diff --git a/.github/renovate.json b/.github/renovate.json index d7160ad..12d03e2 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -13,7 +13,7 @@ }, "customManagers": [ { - "description": "The @lvbt/* packages install from git tags of repository-tooling with a path: suffix that the npm manager does not parse, and the Claude Code marketplace pins the same tag. Track both as one dependency on the repository's tags.", + "description": "The @lasvegasfortransit/* packages install from git tags of repository-tooling with a path: suffix that the npm manager does not parse, and the Claude Code marketplace pins the same tag. Track both as one dependency on the repository's tags.", "customType": "regex", "managerFilePatterns": ["/(^|/)package\\.json$/", "/(^|/)\\.claude/settings\\.json$/"], "matchStrings": [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6696fdb..34022c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,8 @@ jobs: env: LVBT_MIGRATION_BROWSER_ACCEPTANCE: '1' run: >- - pnpm --filter @lvbt/labs-cli exec vitest run tests/migration-export-acceptance.test.ts + pnpm --filter @lasvegasfortransit/labs-cli exec vitest run + tests/migration-export-acceptance.test.ts - name: Isolated archive tests run: pnpm test:archive diff --git a/AGENTS.md b/AGENTS.md index f7ed295..fe3bcd8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,7 +43,7 @@ change crosses boundaries; never invent one for a feature, file, task, or role. ## The repository standard -Lint, format, TypeScript, and test settings extend the `@lvbt/*` packages from +Lint, format, TypeScript, and test settings extend the `@lasvegasfortransit/*` packages from `LasVegasForTransit/repository-tooling`. Change a shared rule there, not here. ## Documentation ownership diff --git a/apps/home/astro.config.ts b/apps/home/astro.config.ts index 120dbf6..cd06218 100644 --- a/apps/home/astro.config.ts +++ b/apps/home/astro.config.ts @@ -1,7 +1,7 @@ import sitemap from '@astrojs/sitemap'; import tailwindcss from '@tailwindcss/vite'; import lvbtAnalytics from '@lasvegasfortransit/analytics/astro'; -import { LABS_SITE } from '@lvbt/brand/analytics'; +import { LABS_SITE } from '@lasvegasfortransit/brand/analytics'; import { defineConfig } from 'astro/config'; export default defineConfig({ diff --git a/apps/home/package.json b/apps/home/package.json index caf9e2d..c42dd82 100644 --- a/apps/home/package.json +++ b/apps/home/package.json @@ -1,5 +1,5 @@ { - "name": "@lvbt/lab-home", + "name": "@lasvegasfortransit/lab-home", "version": "0.0.0", "private": true, "type": "module", @@ -17,9 +17,9 @@ "dependencies": { "@astrojs/sitemap": "catalog:", "@lasvegasfortransit/analytics": "catalog:", - "@lvbt/brand": "workspace:*", - "@lvbt/labs-cli": "workspace:*", - "@lvbt/ui": "workspace:*", + "@lasvegasfortransit/brand": "workspace:*", + "@lasvegasfortransit/labs-cli": "workspace:*", + "@lasvegasfortransit/ui": "workspace:*", "@tailwindcss/vite": "catalog:", "astro": "catalog:", "tailwindcss": "catalog:" diff --git a/apps/home/src/pages/index.astro b/apps/home/src/pages/index.astro index c67d4da..a72d769 100644 --- a/apps/home/src/pages/index.astro +++ b/apps/home/src/pages/index.astro @@ -1,8 +1,8 @@ --- import path from 'node:path'; -import { discoverLabs } from '@lvbt/labs-cli/manifest'; -import { isListedLab } from '@lvbt/labs-cli/catalog'; -import LabLifecycleNotice from '@lvbt/ui/astro/lifecycle-notice'; +import { discoverLabs } from '@lasvegasfortransit/labs-cli/manifest'; +import { isListedLab } from '@lasvegasfortransit/labs-cli/catalog'; +import LabLifecycleNotice from '@lasvegasfortransit/ui/astro/lifecycle-notice'; import BaseLayout from '../layouts/BaseLayout.astro'; const root = path.resolve(process.cwd(), '../..'); diff --git a/apps/home/src/styles/global.css b/apps/home/src/styles/global.css index 361630e..0a8e359 100644 --- a/apps/home/src/styles/global.css +++ b/apps/home/src/styles/global.css @@ -1,5 +1,5 @@ @import 'tailwindcss'; -@import '@lvbt/brand/tokens.css'; +@import '@lasvegasfortransit/brand/tokens.css'; :root { --page-pad: clamp(1.25rem, 4vw, 3rem); diff --git a/apps/home/tests/astro-config.test.ts b/apps/home/tests/astro-config.test.ts index e90ba57..dc2b2dc 100644 --- a/apps/home/tests/astro-config.test.ts +++ b/apps/home/tests/astro-config.test.ts @@ -1,6 +1,6 @@ import { expect, it, vi } from 'vitest'; -import { LABS_SITE } from '@lvbt/brand/analytics'; +import { LABS_SITE } from '@lasvegasfortransit/brand/analytics'; const { lvbtAnalytics } = vi.hoisted(() => ({ lvbtAnalytics: vi.fn(() => ({ name: '@lasvegasfortransit/analytics', hooks: {} })), diff --git a/apps/home/tests/e2e/catalog-links.spec.ts b/apps/home/tests/e2e/catalog-links.spec.ts index 5044de2..54a1b7b 100644 --- a/apps/home/tests/e2e/catalog-links.spec.ts +++ b/apps/home/tests/e2e/catalog-links.spec.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; import { fileURLToPath } from 'node:url'; -import { discoverLabs } from '@lvbt/labs-cli/manifest'; -import { isListedLab } from '@lvbt/labs-cli/catalog'; +import { discoverLabs } from '@lasvegasfortransit/labs-cli/manifest'; +import { isListedLab } from '@lasvegasfortransit/labs-cli/catalog'; const labsHost = 'labs.lasvegasfortransit.org'; diff --git a/apps/home/tests/e2e/home.spec.ts b/apps/home/tests/e2e/home.spec.ts index 8e0aec5..4ceceb1 100644 --- a/apps/home/tests/e2e/home.spec.ts +++ b/apps/home/tests/e2e/home.spec.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; import { fileURLToPath } from 'node:url'; -import { discoverLabs } from '@lvbt/labs-cli/manifest'; -import { isListedLab } from '@lvbt/labs-cli/catalog'; +import { discoverLabs } from '@lasvegasfortransit/labs-cli/manifest'; +import { isListedLab } from '@lasvegasfortransit/labs-cli/catalog'; test('presents the Labs projects clearly without overflowing', async ({ page }) => { await page.goto('/'); diff --git a/apps/transit-funding/package.json b/apps/transit-funding/package.json index db7f69c..169cf6f 100644 --- a/apps/transit-funding/package.json +++ b/apps/transit-funding/package.json @@ -1,5 +1,5 @@ { - "name": "@lvbt/lab-transit-funding", + "name": "@lasvegasfortransit/lab-transit-funding", "version": "0.0.0", "private": true, "type": "module", @@ -16,13 +16,13 @@ }, "dependencies": { "@lasvegasfortransit/analytics": "catalog:", - "@lvbt/brand": "workspace:*", - "@lvbt/ui": "workspace:*", + "@lasvegasfortransit/brand": "workspace:*", + "@lasvegasfortransit/ui": "workspace:*", "react": "catalog:", "react-dom": "catalog:" }, "devDependencies": { - "@lvbt/lab-runtime": "workspace:*", + "@lasvegasfortransit/lab-runtime": "workspace:*", "@lasvegasfortransit/eslint-config": "file:../../.lvbt/web-platform/packages/eslint-config", "@lasvegasfortransit/playwright-config": "file:../../.lvbt/web-platform/packages/playwright-config", "@lasvegasfortransit/typescript-config": "file:../../.lvbt/web-platform/packages/typescript-config", diff --git a/apps/transit-funding/src/App.tsx b/apps/transit-funding/src/App.tsx index b040e42..7285b0f 100644 --- a/apps/transit-funding/src/App.tsx +++ b/apps/transit-funding/src/App.tsx @@ -1,5 +1,5 @@ import { ActOne } from './story/ActOne.tsx'; -import { LabLifecycleNotice } from '@lvbt/ui'; +import { LabLifecycleNotice } from '@lasvegasfortransit/ui'; import manifest from '../lab.config'; export function App() { diff --git a/apps/transit-funding/src/main.tsx b/apps/transit-funding/src/main.tsx index 0a60ab7..bc86a30 100644 --- a/apps/transit-funding/src/main.tsx +++ b/apps/transit-funding/src/main.tsx @@ -1,6 +1,6 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; -import { initLabsAnalytics } from '@lvbt/brand/analytics'; +import { initLabsAnalytics } from '@lasvegasfortransit/brand/analytics'; import { App } from './App.tsx'; import './ui/app.css'; diff --git a/apps/transit-funding/src/ui/app.css b/apps/transit-funding/src/ui/app.css index 18fa257..0863826 100644 --- a/apps/transit-funding/src/ui/app.css +++ b/apps/transit-funding/src/ui/app.css @@ -1,6 +1,6 @@ @import 'tailwindcss'; -@import '@lvbt/brand/tokens.css'; -@import '@lvbt/ui/lifecycle.css'; +@import '@lasvegasfortransit/brand/tokens.css'; +@import '@lasvegasfortransit/ui/lifecycle.css'; @import '../theme/tokens.css'; * { diff --git a/apps/transit-funding/tests/e2e/archive/read-only.spec.ts b/apps/transit-funding/tests/e2e/archive/read-only.spec.ts index 5da8af8..b2beb24 100644 --- a/apps/transit-funding/tests/e2e/archive/read-only.spec.ts +++ b/apps/transit-funding/tests/e2e/archive/read-only.spec.ts @@ -1,5 +1,8 @@ import { expect, test } from '@playwright/test'; -import { createArchiveContext, readProjectArchiveFiles } from '@lvbt/lab-runtime/archive'; +import { + createArchiveContext, + readProjectArchiveFiles, +} from '@lasvegasfortransit/lab-runtime/archive'; import { expectNoAccessibilityViolations } from '@lasvegasfortransit/playwright-config/accessibility'; test('reads the funding story with all live services unavailable', async ({ diff --git a/docs/development/how-to/create-a-lab.md b/docs/development/how-to/create-a-lab.md index b628c9f..c5c4a31 100644 --- a/docs/development/how-to/create-a-lab.md +++ b/docs/development/how-to/create-a-lab.md @@ -55,10 +55,11 @@ Run `pnpm check`, then inspect the app at its production path with `pnpm lab pre listed project appears on home only after its status changes from `draft` to `active` and the project Worker passes the public route check. -Run `pnpm exec turbo run test:archive --filter=@lvbt/lab-` to build and inspect the read-only -archive with live services blocked. The generated suite checks the initial project content at -desktop and mobile sizes. Extend it with the project's primary workflows as those workflows take -shape. Archive tests use `playwright.archive.config.ts`; normal preview tests exclude that suite. +Run `pnpm exec turbo run test:archive --filter=@lasvegasfortransit/lab-` to build and inspect +the read-only archive with live services blocked. The generated suite checks the initial project +content at desktop and mobile sizes. Extend it with the project's primary workflows as those +workflows take shape. Archive tests use `playwright.archive.config.ts`; normal preview tests exclude +that suite. Inspect the Worker deployment before publishing: diff --git a/docs/development/how-to/deprecate-and-retire-a-lab.md b/docs/development/how-to/deprecate-and-retire-a-lab.md index 271d78c..bd0068f 100644 --- a/docs/development/how-to/deprecate-and-retire-a-lab.md +++ b/docs/development/how-to/deprecate-and-retire-a-lab.md @@ -31,11 +31,11 @@ project runs through `pnpm exec turbo run test:archive --filter= Update screenshot baselines only after inspecting the rendered difference: ```sh -pnpm --filter @lvbt/lab- exec playwright test --update-snapshots +pnpm --filter @lasvegasfortransit/lab- exec playwright test --update-snapshots ``` Inspect every changed desktop and mobile image, then rerun the same suite without diff --git a/docs/development/reference/brand-and-ui.md b/docs/development/reference/brand-and-ui.md index 665359c..734573b 100644 --- a/docs/development/reference/brand-and-ui.md +++ b/docs/development/reference/brand-and-ui.md @@ -34,9 +34,9 @@ Components consume semantic roles rather than raw hexadecimal values. ## Shared UI boundary -`@lvbt/ui` contains accessible buttons, links, fields, toggles, menus, tabs, dialogs, notices, -metadata blocks, and lifecycle banners. Astro and React exports share tokens and behavior without -forcing a common renderer. +`@lasvegasfortransit/ui` contains accessible buttons, links, fields, toggles, menus, tabs, dialogs, +notices, metadata blocks, and lifecycle banners. Astro and React exports share tokens and behavior +without forcing a common renderer. The package does not contain page shells, dashboards, project navigation, domain forms, maps, charts, or application state. Repetition enters the shared package only after two projects need the @@ -53,10 +53,11 @@ project content. ### Lifecycle imports -React apps import `LabLifecycleNotice` from `@lvbt/ui` and include -`@import '@lvbt/ui/lifecycle.css';` in their application stylesheet. Astro sites import the default -component from `@lvbt/ui/astro/lifecycle-notice`; that component includes its stylesheet. Both -receive the project manifest as the `manifest` prop. Generated projects include these imports. +React apps import `LabLifecycleNotice` from `@lasvegasfortransit/ui` and include +`@import '@lasvegasfortransit/ui/lifecycle.css';` in their application stylesheet. Astro sites +import the default component from `@lasvegasfortransit/ui/astro/lifecycle-notice`; that component +includes its stylesheet. Both receive the project manifest as the `manifest` prop. Generated +projects include these imports. The notice renders only for deprecated and retired projects. Deprecated projects include the sunset date; retired archives omit future-tense retirement copy. Missing required lifecycle metadata fails @@ -68,5 +69,5 @@ separately. This changes token availability, not brand values. Brand metadata includes the LVBT organization name, canonical hostname, favicons, social card defaults, attribution, and the Labs analytics site. The brand package doesn't depend on any framework: Astro projects pass its `LABS_SITE` to the integration from -`@lasvegasfortransit/analytics/astro`, and Vite and React projects use `@lvbt/brand/analytics`. -Preview, local, and archive builds omit the analytics client. +`@lasvegasfortransit/analytics/astro`, and Vite and React projects use +`@lasvegasfortransit/brand/analytics`. Preview, local, and archive builds omit the analytics client. diff --git a/docs/development/tutorials/create-first-lab.md b/docs/development/tutorials/create-first-lab.md index 5031520..c0ee97e 100644 --- a/docs/development/tutorials/create-first-lab.md +++ b/docs/development/tutorials/create-first-lab.md @@ -63,9 +63,9 @@ Run the complete project bar: ```sh pnpm check -pnpm --filter @lvbt/lab-curb-space-notes build -pnpm --filter @lvbt/lab-curb-space-notes build:archive -pnpm --filter @lvbt/lab-curb-space-notes test:e2e +pnpm --filter @lasvegasfortransit/lab-curb-space-notes build +pnpm --filter @lasvegasfortransit/lab-curb-space-notes build:archive +pnpm --filter @lasvegasfortransit/lab-curb-space-notes test:e2e ``` These commands run the repository checks, production and archive builds, and Playwright scenarios. diff --git a/docs/operations/how-to/provision.md b/docs/operations/how-to/provision.md index 0ff0e9d..cd904b3 100644 --- a/docs/operations/how-to/provision.md +++ b/docs/operations/how-to/provision.md @@ -28,8 +28,9 @@ creates them, so provisioning and deploys keep working after that person leaves. the Super Administrator role on the LVBT account. Wrangler accepts an account API token because the workflows and provisioning also set `CLOUDFLARE_ACCOUNT_ID`. -1. In the Cloudflare dashboard, choose the LVBT account, **Las Vegans for Better Transit**, and go - to **Manage Account → Account API Tokens** +1. In the Cloudflare dashboard, choose the LVBT account. Check that its name is **Las Vegans for + Better Transit**; if it still says "Las Vegas for Better Transit", correct it under the account's + settings before continuing. Go to **Manage Account → Account API Tokens** (). Click **Create Token**, then **Create Custom Token**. 2. Name it `labs deploy (GitHub Actions)`. Under **Permissions**, add exactly these rows, which are @@ -40,30 +41,38 @@ workflows and provisioning also set `CLOUDFLARE_ACCOUNT_ID`. more than read access; - **Zone · Workers Routes · Edit**: the project routes on `lasvegasfortransit.org`; - **Zone · Zone · Read**: the zone check provisioning makes before any write. -3. Under **Zone Resources**, choose **Include → Specific zone** and select `lasvegasfortransit.org`, - the zone `.lvbt/infrastructure.config.ts` names. +3. Under **Account Resources**, choose **Include** and the LVBT account by name. Leave **All + accounts** off so the token cannot act on another account. Under **Zone Resources**, choose + **Include → Specific zone** and select `lasvegasfortransit.org`, the zone + `.lvbt/infrastructure.config.ts` names. Leave **All zones** off so it cannot change another zone. 4. Leave the expiration empty so deploys keep working, click **Continue to summary**, then **Create Token**. Copy the token; Cloudflare shows it only once. -5. Before copying anything else, paste it in both places it goes: as the GitHub **environment - secret** `CLOUDFLARE_API_TOKEN` on the `production` environment (repository → Settings → - Environments → `production` → Environment secrets → Add environment secret), and as - `CLOUDFLARE_API_TOKEN` in your own shell's environment for `pnpm provision --apply`. Never put it - in a command argument. +5. Before copying anything else, run + `gh secret set CLOUDFLARE_API_TOKEN --env production --repo LasVegasForTransit/labs`, paste the + token at the prompt, and press Enter. Check that `CLOUDFLARE_API_TOKEN` now appears under + repository → **Settings → Environments → production → Environment secrets**. Add the same value + to your local shell's `CLOUDFLARE_API_TOKEN` environment for `pnpm provision --apply`; do not put + it in a command argument or tracked file. 6. Create the preview token the same way, named `labs preview (GitHub Actions)`, with only these rows: **Account · Workers Scripts · Edit** (upload and delete pull-request preview Workers) and **Zone · Workers Routes · Read** (the cleanup checks that a preview Worker has no routes before - deleting it). With read-only routes, a preview can never change production traffic. Choose the - same zone, leave the expiration empty, create it, and copy it. -7. Before copying anything else, paste it in both places: the `preview` environment secret - `CLOUDFLARE_PREVIEW_API_TOKEN`, and `CLOUDFLARE_PREVIEW_API_TOKEN` in your shell. + deleting it). Read-only route access prevents this token from changing production routing; its + Workers Scripts permission can still edit Worker code, so restrict who can use the token. Select + only the LVBT account and `lasvegasfortransit.org` zone, leave the expiration empty, create it, + and copy it. +7. Before copying anything else, run + `gh secret set CLOUDFLARE_PREVIEW_API_TOKEN --env preview --repo LasVegasForTransit/labs`, paste + the token at the prompt, and press Enter. Check that it appears under **Settings → Environments → + preview → Environment secrets**. Add the same value to your local shell's + `CLOUDFLARE_PREVIEW_API_TOKEN` environment for provisioning. If a token ever leaks, roll it on the same Account API Tokens page, then paste the new value in both of its places before doing anything else. -`CLOUDFLARE_ACCOUNT_ID` (`2557b5c2e166292ded0f8425b73075e9`, the LVBT account, **Las Vegans for -Better Transit**) is also a `production` environment secret. Provisioning reads the account and zone -IDs from `.lvbt/infrastructure.config.ts` and writes the matching repository variables itself; you -do not set those by hand. +`CLOUDFLARE_ACCOUNT_ID` (`2557b5c2e166292ded0f8425b73075e9`, the LVBT account) and +`CLOUDFLARE_ZONE_ID` are repository variables, not secrets. Provisioning reads both IDs from +`.lvbt/infrastructure.config.ts` and writes the matching variables itself; you do not set them by +hand. Authenticate the local CLIs without placing credentials in shell arguments: @@ -72,10 +81,11 @@ gh auth login pnpm exec wrangler login ``` -Set `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_PREVIEW_API_TOKEN` in the command environment before -applying changes; see [Get Cloudflare tokens ready](#get-cloudflare-tokens-ready-first-time-only) if -you do not have them yet. GitHub CLI reads both tokens from standard input; neither appears in a -command argument. +Set `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_PREVIEW_API_TOKEN` in the local command environment +before applying changes; see +[Get Cloudflare tokens ready](#get-cloudflare-tokens-ready-first-time-only) if you do not have them +yet. The `gh secret set` commands above read token values from standard input; neither token belongs +in a command argument. Run a read-only comparison: diff --git a/docs/operations/reference/analytics.md b/docs/operations/reference/analytics.md index f1ede49..8433c27 100644 --- a/docs/operations/reference/analytics.md +++ b/docs/operations/reference/analytics.md @@ -12,10 +12,11 @@ Control and Do Not Track disable page and event measurement. ## Application contract Astro projects add `lvbtAnalytics({ site: LABS_SITE })` to their integration list, importing the -integration from `@lasvegasfortransit/analytics/astro` and `LABS_SITE` from `@lvbt/brand/analytics`. -Vite and React projects call `initLabsAnalytics()` from `@lvbt/brand/analytics` before rendering. -The brand package fixes the site identity to `labs.lasvegasfortransit.org`; applications never copy -the token, collector URL, privacy checks, or beacon setup. +integration from `@lasvegasfortransit/analytics/astro` and `LABS_SITE` from +`@lasvegasfortransit/brand/analytics`. Vite and React projects call `initLabsAnalytics()` from +`@lasvegasfortransit/brand/analytics` before rendering. The brand package fixes the site identity to +`labs.lasvegasfortransit.org`; applications never copy the token, collector URL, privacy checks, or +beacon setup. Production builds receive `PUBLIC_LVBT_CWA_TOKEN` and `LVBT_REQUIRE_ANALYTICS=1`. The public token identifies the Cloudflare property and is stored as a GitHub production environment variable. A diff --git a/packages/brand/README.md b/packages/brand/README.md index 868c889..b713602 100644 --- a/packages/brand/README.md +++ b/packages/brand/README.md @@ -1,8 +1,8 @@ # LVBT brand -`@lvbt/brand` publishes the shared Public Sans font, LVBT marks, and semantic color tokens used by -every lab. Applications own their composition and import the foundation through -`@lvbt/brand/tokens.css`. +`@lasvegasfortransit/brand` publishes the shared Public Sans font, LVBT marks, and semantic color +tokens used by every lab. Applications own their composition and import the foundation through +`@lasvegasfortransit/brand/tokens.css`. The brand package owns design tokens, Public Sans, shared assets, metadata defaults, and attribution. It establishes a recognizable LVBT identity without prescribing application layout or diff --git a/packages/brand/package.json b/packages/brand/package.json index 701d1a0..180d554 100644 --- a/packages/brand/package.json +++ b/packages/brand/package.json @@ -1,5 +1,5 @@ { - "name": "@lvbt/brand", + "name": "@lasvegasfortransit/brand", "version": "0.0.0", "private": true, "type": "module", diff --git a/packages/lab-runtime/README.md b/packages/lab-runtime/README.md index c988e37..f4053d9 100644 --- a/packages/lab-runtime/README.md +++ b/packages/lab-runtime/README.md @@ -1,12 +1,13 @@ # Lab runtime -`@lvbt/lab-runtime` provides manifest validation and isolated archive testing for independent labs. -It contains no repository-management commands and does not depend on the home catalog or another -application. +`@lasvegasfortransit/lab-runtime` provides manifest validation and isolated archive testing for +independent labs. It contains no repository-management commands and does not depend on the home +catalog or another application. -Import `LabManifestV1Schema` and `validateManifestForDirectory` from `@lvbt/lab-runtime/manifest`. -Archive browser suites use `createArchiveContext` and `readProjectArchiveFiles` from -`@lvbt/lab-runtime/archive`. File-only checks import `@lvbt/lab-runtime/archive-files`. +Import `LabManifestV1Schema` and `validateManifestForDirectory` from +`@lasvegasfortransit/lab-runtime/manifest`. Archive browser suites use `createArchiveContext` and +`readProjectArchiveFiles` from `@lasvegasfortransit/lab-runtime/archive`. File-only checks import +`@lasvegasfortransit/lab-runtime/archive-files`. The package travels with a project during migration. Repository discovery, deployment ownership, -provisioning, and lifecycle commands remain in `@lvbt/labs-cli`. +provisioning, and lifecycle commands remain in `@lasvegasfortransit/labs-cli`. diff --git a/packages/lab-runtime/package.json b/packages/lab-runtime/package.json index 52bb3c3..bd6d29b 100644 --- a/packages/lab-runtime/package.json +++ b/packages/lab-runtime/package.json @@ -1,5 +1,5 @@ { - "name": "@lvbt/lab-runtime", + "name": "@lasvegasfortransit/lab-runtime", "private": true, "type": "module", "exports": { diff --git a/packages/labs-cli/README.md b/packages/labs-cli/README.md index 0d2cc2f..d5f026f 100644 --- a/packages/labs-cli/README.md +++ b/packages/labs-cli/README.md @@ -1,8 +1,8 @@ # Labs operations -`@lvbt/labs-cli` owns the Labs repository's `pnpm lab` interface, catalog discovery, project -generation, lifecycle transitions, deployment policy, and provider target definitions. It contains -rules that apply specifically to `labs.lasvegasfortransit.org` and the Labs monorepo. +`@lasvegasfortransit/labs-cli` owns the Labs repository's `pnpm lab` interface, catalog discovery, +project generation, lifecycle transitions, deployment policy, and provider target definitions. It +contains rules that apply specifically to `labs.lasvegasfortransit.org` and the Labs monorepo. Human prompts and complete non-interactive flags call the same operations. Remote and destructive commands support dry-run mode, and every command can return structured JSON for agents and CI. diff --git a/packages/labs-cli/package.json b/packages/labs-cli/package.json index 469a15f..cd30e95 100644 --- a/packages/labs-cli/package.json +++ b/packages/labs-cli/package.json @@ -1,5 +1,5 @@ { - "name": "@lvbt/labs-cli", + "name": "@lasvegasfortransit/labs-cli", "version": "0.0.0", "private": true, "type": "module", @@ -15,7 +15,7 @@ "test:e2e": "playwright test" }, "dependencies": { - "@lvbt/lab-runtime": "workspace:*", + "@lasvegasfortransit/lab-runtime": "workspace:*", "@lasvegasfortransit/web-platform": "file:../../.lvbt/web-platform/packages/web-platform", "zod": "catalog:" }, diff --git a/packages/labs-cli/src/archive-browser.ts b/packages/labs-cli/src/archive-browser.ts index 114fbb9..46f342e 100644 --- a/packages/labs-cli/src/archive-browser.ts +++ b/packages/labs-cli/src/archive-browser.ts @@ -1 +1 @@ -export * from '@lvbt/lab-runtime/archive'; +export * from '@lasvegasfortransit/lab-runtime/archive'; diff --git a/packages/labs-cli/src/archive-files.ts b/packages/labs-cli/src/archive-files.ts index 1c93b65..2a66942 100644 --- a/packages/labs-cli/src/archive-files.ts +++ b/packages/labs-cli/src/archive-files.ts @@ -1 +1 @@ -export * from '@lvbt/lab-runtime/archive-files'; +export * from '@lasvegasfortransit/lab-runtime/archive-files'; diff --git a/packages/labs-cli/src/cli.ts b/packages/labs-cli/src/cli.ts index 6878a95..acea123 100644 --- a/packages/labs-cli/src/cli.ts +++ b/packages/labs-cli/src/cli.ts @@ -95,7 +95,7 @@ export function parseLabCommand(arguments_: readonly string[]): ParsedLabCommand } export function projectFilter(slug: string): string { - return `@lvbt/lab-${slug}`; + return `@lasvegasfortransit/lab-${slug}`; } async function loadManifest(root: string, slug: string): Promise { diff --git a/packages/labs-cli/src/create-archive.ts b/packages/labs-cli/src/create-archive.ts index 8c30b29..8d89624 100644 --- a/packages/labs-cli/src/create-archive.ts +++ b/packages/labs-cli/src/create-archive.ts @@ -10,7 +10,7 @@ export default defineConfig({ }); `, 'tests/e2e/archive/read-only.spec.ts': `import { expect, test } from '@playwright/test'; -import { createArchiveContext, readProjectArchiveFiles } from '@lvbt/lab-runtime/archive'; +import { createArchiveContext, readProjectArchiveFiles } from '@lasvegasfortransit/lab-runtime/archive'; import { expectNoAccessibilityViolations } from '@lasvegasfortransit/playwright-config/accessibility'; import { monitorPageHealth } from '@lasvegasfortransit/playwright-config/page-health'; import manifest from '../../../lab.config'; diff --git a/packages/labs-cli/src/create.ts b/packages/labs-cli/src/create.ts index 39a23b9..0ece5a0 100644 --- a/packages/labs-cli/src/create.ts +++ b/packages/labs-cli/src/create.ts @@ -30,10 +30,10 @@ async function templatePackage(reference: string, slug: string, site: boolean) { devDependencies: Record; scripts: Record; }; - pkg.name = `@lvbt/lab-${slug}`; - pkg.dependencies['@lvbt/lab-runtime'] = 'workspace:*'; - pkg.dependencies['@lvbt/brand'] = 'workspace:*'; - pkg.dependencies['@lvbt/ui'] = 'workspace:*'; + pkg.name = `@lasvegasfortransit/lab-${slug}`; + pkg.dependencies['@lasvegasfortransit/lab-runtime'] = 'workspace:*'; + pkg.dependencies['@lasvegasfortransit/brand'] = 'workspace:*'; + pkg.dependencies['@lasvegasfortransit/ui'] = 'workspace:*'; const platformScope = '@lasvegasfortransit/'; for (const name of Object.keys(pkg.devDependencies).filter((name) => name.startsWith(platformScope), @@ -53,7 +53,7 @@ async function templatePackage(reference: string, slug: string, site: boolean) { function browserTest(base: string): string { return `import { expect, test } from '@playwright/test'; -import { LabManifestV1Schema } from '@lvbt/lab-runtime/manifest'; +import { LabManifestV1Schema } from '@lasvegasfortransit/lab-runtime/manifest'; import { expectNoAccessibilityViolations } from '@lasvegasfortransit/playwright-config/accessibility'; import { monitorPageHealth } from '@lasvegasfortransit/playwright-config/page-health'; import config from '../../lab.config'; @@ -123,7 +123,7 @@ export async function createLab(root: string, args: string[]): Promise { ...archiveTemplate(), ...(await visualBaselines()), 'src/styles.css': - "@import 'tailwindcss';\n@import '@lvbt/brand/tokens.css';\n@import '@lvbt/ui/lifecycle.css';\nbody { margin: 0; font-family: var(--font-sans); color: var(--color-on-surface); background: var(--color-surface); }\nmain { max-width: 64rem; margin-inline: auto; padding: 2rem 1.5rem; }\nh1 { font-size: 2rem; font-weight: 800; }\n", + "@import 'tailwindcss';\n@import '@lasvegasfortransit/brand/tokens.css';\n@import '@lasvegasfortransit/ui/lifecycle.css';\nbody { margin: 0; font-family: var(--font-sans); color: var(--color-on-surface); background: var(--color-surface); }\nmain { max-width: 64rem; margin-inline: auto; padding: 2rem 1.5rem; }\nh1 { font-size: 2rem; font-weight: 800; }\n", 'package.json': JSON.stringify(pkg, null, 2), 'lab.config.ts': `export default ${JSON.stringify(manifest, null, 2)} as const;\n`, 'README.md': `# ${manifest.title}\n\n${manifest.summary}\n\nRun \`pnpm lab dev ${manifest.slug}\`.\n\n[Project documentation](docs/README.md) contains ownership and operation details.\n`, @@ -153,7 +153,7 @@ export async function createLab(root: string, args: string[]): Promise { null, 2, ), - 'tests/manifest.test.ts': `import { expect, test } from 'vitest';\nimport { LabManifestV1Schema } from '@lvbt/lab-runtime/manifest';\nimport manifest from '../lab.config';\ntest('declares project ownership', () => { expect(LabManifestV1Schema.parse(manifest).slug).toBe(${JSON.stringify(manifest.slug)}); });\n`, + 'tests/manifest.test.ts': `import { expect, test } from 'vitest';\nimport { LabManifestV1Schema } from '@lasvegasfortransit/lab-runtime/manifest';\nimport manifest from '../lab.config';\ntest('declares project ownership', () => { expect(LabManifestV1Schema.parse(manifest).slug).toBe(${JSON.stringify(manifest.slug)}); });\n`, 'src/worker.ts': `export default { fetch(request: Request, env: { ASSETS: { fetch(request: Request): Promise } }) { const url = new URL(request.url); url.pathname = url.pathname.replace(/^\\/${manifest.slug}(?:\\/|$)/, '/'); return env.ASSETS.fetch(new Request(url, request)); } };\n`, 'playwright.config.ts': `import { defineConfig } from '@playwright/test';\nimport { sharedConfig } from '@lasvegasfortransit/playwright-config';\nexport default defineConfig({ ...sharedConfig, testIgnore: ['**/archive/**'], use: { ...sharedConfig.use, baseURL: 'http://127.0.0.1:8899' }, webServer: { command: 'pnpm build && pnpm exec wrangler dev --port 8899', url: 'http://127.0.0.1:8899${base}', reuseExistingServer: false } });\n`, 'tests/e2e/home.spec.ts': browserTest(base), @@ -171,7 +171,7 @@ export async function createLab(root: string, args: string[]): Promise { files['index.html'] = 'Lab
\n'; files['src/main.tsx'] = - `import { createRoot } from 'react-dom/client';\nimport { LabLifecycleNotice } from '@lvbt/ui';\nimport './styles.css';\nimport manifest from '../lab.config';\ndocument.title = manifest.title;\nconst root = document.getElementById('root');\nif (root) createRoot(root).render(

{manifest.title}

{manifest.summary}

);\n`; + `import { createRoot } from 'react-dom/client';\nimport { LabLifecycleNotice } from '@lasvegasfortransit/ui';\nimport './styles.css';\nimport manifest from '../lab.config';\ndocument.title = manifest.title;\nconst root = document.getElementById('root');\nif (root) createRoot(root).render(

{manifest.title}

{manifest.summary}

);\n`; } if (apply) await writeProject(directory, files); process.stdout.write( diff --git a/packages/labs-cli/src/deployment-plan.ts b/packages/labs-cli/src/deployment-plan.ts index d2c4a2b..2ab013d 100644 --- a/packages/labs-cli/src/deployment-plan.ts +++ b/packages/labs-cli/src/deployment-plan.ts @@ -40,12 +40,38 @@ function baseCatalogRecord(input: unknown, slug: string) { return record; } +// Workspace packages moved from the `@lvbt/` scope to `@lasvegasfortransit/`. A base commit from +// before that rename still has the old scope committed, so base processing accepts either scope +// while head processing (the current scope) requires the new one. `scopeFor` resolves which scope +// a legacy-tolerant snapshot actually used, for building matching synthetic dependency names. +const currentScope = '@lasvegasfortransit'; +const legacyScope = '@lvbt'; + +function labPackageName(scope: string, slug: string): string { + return `${scope}/lab-${slug}`; +} + +function cliPackageName(scope: string): string { + return `${scope}/labs-cli`; +} + +function scopeFor(name: string, slug: string, allowLegacyScope: boolean): string | undefined { + if (name === labPackageName(currentScope, slug)) return currentScope; + if (!allowLegacyScope) return undefined; + return name === labPackageName(legacyScope, slug) ? legacyScope : undefined; +} + function addCatalogRecords( blobs: Map, read: (file: string) => string, - { names, readRecord }: { names: Set; readRecord: RecordReader }, + { + names, + readRecord, + allowLegacyScope, + }: { names: Set; readRecord: RecordReader; allowLegacyScope: boolean }, projects: WorkspaceProject[], ) { + const scope = allowLegacyScope ? legacyScope : currentScope; for (const file of [...blobs.keys()] .filter((file) => /^catalog\/[^/]+\.json$/.test(file)) .sort()) { @@ -54,12 +80,12 @@ function addCatalogRecords( if (projects.some((project) => project.slug === slug)) throw new Error(`Duplicate app and catalog ownership for ${slug}.`); if (manifest.status !== 'retired') continue; - if (names.has(`@lvbt/lab-${slug}`)) - throw new Error(`Duplicate deployment identity for ${slug}.`); + const name = labPackageName(scope, slug); + if (names.has(name)) throw new Error(`Duplicate deployment identity for ${slug}.`); projects.push({ - name: `@lvbt/lab-${slug}`, + name, directory: `retired/${slug}`, - dependencies: ['@lvbt/labs-cli'], + dependencies: [cliPackageName(scope)], slug, status: 'retired', archive: true, @@ -88,6 +114,7 @@ function workspaceAt( root: string, commit: string, readRecord: RecordReader = validateCatalogRecord, + allowLegacyScope = false, ): WorkspaceProject[] { const entries = git(root, [ 'ls-tree', @@ -133,18 +160,18 @@ function workspaceAt( if (directory.startsWith('apps/')) { const slug = directory.slice('apps/'.length); const { manifest } = parseManifestSource(read(`${directory}/lab.config.ts`), slug); - if (pkg.name !== `@lvbt/lab-${slug}`) - throw new Error(`Package name must match lab slug ${slug}.`); + const scope = scopeFor(pkg.name, slug, allowLegacyScope); + if (scope === undefined) throw new Error(`Package name must match lab slug ${slug}.`); project.slug = slug; project.status = manifest.status; if (manifest.status === 'retired') { project.archive = true; - project.dependencies = ['@lvbt/labs-cli']; + project.dependencies = [cliPackageName(scope)]; } } return project; }); - addCatalogRecords(blobs, read, { names, readRecord }, projects); + addCatalogRecords(blobs, read, { names, readRecord, allowLegacyScope }, projects); applyMigrationHandoffs(blobs, read, projects); return projects; } @@ -153,7 +180,7 @@ export function deploymentPlan(root: string, refs: { base?: string; head?: strin const head = resolveCommit(root, refs.head ?? 'HEAD'); const base = refs.base === undefined ? undefined : resolveCommit(root, refs.base); const current = workspaceAt(root, head); - const previous = base === undefined ? [] : workspaceAt(root, base, baseCatalogRecord); + const previous = base === undefined ? [] : workspaceAt(root, base, baseCatalogRecord, true); const files = base === undefined ? git(root, ['ls-tree', '-rz', '--name-only', head]).split('\0').filter(Boolean) diff --git a/packages/labs-cli/src/manifest.ts b/packages/labs-cli/src/manifest.ts index 22db465..fc32658 100644 --- a/packages/labs-cli/src/manifest.ts +++ b/packages/labs-cli/src/manifest.ts @@ -1,2 +1,2 @@ export { discoverLabs } from './discovery.js'; -export * from '@lvbt/lab-runtime/manifest'; +export * from '@lasvegasfortransit/lab-runtime/manifest'; diff --git a/packages/labs-cli/src/migration-source.ts b/packages/labs-cli/src/migration-source.ts index b2513ef..2e3d25f 100644 --- a/packages/labs-cli/src/migration-source.ts +++ b/packages/labs-cli/src/migration-source.ts @@ -54,9 +54,9 @@ function localDependency( specifier: string, packages: Map, ) { - if (name === '@lvbt/labs-cli') + if (name === '@lasvegasfortransit/labs-cli') throw new Error( - 'Replace repository-management tooling with @lvbt/lab-runtime before migration.', + 'Replace repository-management tooling with @lasvegasfortransit/lab-runtime before migration.', ); if (specifier.startsWith('workspace:')) { if (!/^workspace:[*^~]$/.test(specifier)) diff --git a/packages/labs-cli/src/preview.ts b/packages/labs-cli/src/preview.ts index edcf324..bc4d347 100644 --- a/packages/labs-cli/src/preview.ts +++ b/packages/labs-cli/src/preview.ts @@ -78,7 +78,7 @@ function startWorker(target: PreviewTarget): ChildProcess { 'pnpm', [ '--filter', - `@lvbt/lab-${target.slug}`, + `@lasvegasfortransit/lab-${target.slug}`, 'exec', 'wrangler', 'dev', diff --git a/packages/labs-cli/src/provision-providers.ts b/packages/labs-cli/src/provision-providers.ts index 51cef17..a69081d 100644 --- a/packages/labs-cli/src/provision-providers.ts +++ b/packages/labs-cli/src/provision-providers.ts @@ -153,7 +153,9 @@ export async function provisionWorkerResources( const worker = `lvbt-labs-${lab.slug}`; resources.push( provisionWorkerPresence({ name: worker }, readWorkers, async () => { - await run('pnpm', ['--filter', `@lvbt/lab-${lab.slug}`, 'build'], { cwd: root }); + await run('pnpm', ['--filter', `@lasvegasfortransit/lab-${lab.slug}`, 'build'], { + cwd: root, + }); await run( 'pnpm', [ diff --git a/packages/labs-cli/templates/generated-project/site-index.astro b/packages/labs-cli/templates/generated-project/site-index.astro index eff5cd2..ffb5f58 100644 --- a/packages/labs-cli/templates/generated-project/site-index.astro +++ b/packages/labs-cli/templates/generated-project/site-index.astro @@ -1,6 +1,6 @@ --- import '../styles.css'; -import LabLifecycleNotice from '@lvbt/ui/astro/lifecycle-notice'; +import LabLifecycleNotice from '@lasvegasfortransit/ui/astro/lifecycle-notice'; import manifest from '../../lab.config'; --- diff --git a/packages/labs-cli/test-support/migration-fixture.ts b/packages/labs-cli/test-support/migration-fixture.ts index 6ccbfa7..81cde02 100644 --- a/packages/labs-cli/test-support/migration-fixture.ts +++ b/packages/labs-cli/test-support/migration-fixture.ts @@ -53,12 +53,12 @@ export async function withMigrationFixture(run: (root: string) => void | Promise scripts: Record; [key: string]: unknown; }; - examplePackage.name = '@lvbt/lab-migration-example'; + examplePackage.name = '@lasvegasfortransit/lab-migration-example'; examplePackage.dependencies = { ...examplePackage.dependencies, - '@lvbt/brand': 'workspace:*', - '@lvbt/lab-runtime': 'workspace:*', - '@lvbt/ui': 'workspace:*', + '@lasvegasfortransit/brand': 'workspace:*', + '@lasvegasfortransit/lab-runtime': 'workspace:*', + '@lasvegasfortransit/ui': 'workspace:*', }; for (const dependency of [ '@lasvegasfortransit/eslint-config', @@ -80,7 +80,7 @@ import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; export default defineConfig({ base: '/migration-example/', plugins: [react(), tailwindcss()] }); `, - [`${app}/src/App.tsx`]: `import { LabLifecycleNotice } from '@lvbt/ui'; + [`${app}/src/App.tsx`]: `import { LabLifecycleNotice } from '@lasvegasfortransit/ui'; import manifest from '../lab.config'; @@ -132,7 +132,7 @@ export default defineConfig({ }); `, [`${app}/tests/e2e/archive/read-only.spec.ts`]: `import { expect, test } from '@playwright/test'; -import { createArchiveContext, readProjectArchiveFiles } from '@lvbt/lab-runtime/archive'; +import { createArchiveContext, readProjectArchiveFiles } from '@lasvegasfortransit/lab-runtime/archive'; import { expectNoAccessibilityViolations } from '@lasvegasfortransit/playwright-config/accessibility'; import { monitorPageHealth } from '@lasvegasfortransit/playwright-config/page-health'; diff --git a/packages/labs-cli/tests/affected.test.ts b/packages/labs-cli/tests/affected.test.ts index 4fc270d..16bf3ff 100644 --- a/packages/labs-cli/tests/affected.test.ts +++ b/packages/labs-cli/tests/affected.test.ts @@ -3,24 +3,28 @@ import { expect, test } from 'vitest'; import { affectedProjects, type WorkspaceProject } from '../src/affected.js'; const projects: WorkspaceProject[] = [ - { name: '@lvbt/brand', directory: 'packages/brand', dependencies: [] }, - { name: '@lvbt/ui', directory: 'packages/ui', dependencies: ['@lvbt/brand'] }, + { name: '@lasvegasfortransit/brand', directory: 'packages/brand', dependencies: [] }, { - name: '@lvbt/lab-home', + name: '@lasvegasfortransit/ui', + directory: 'packages/ui', + dependencies: ['@lasvegasfortransit/brand'], + }, + { + name: '@lasvegasfortransit/lab-home', directory: 'apps/home', - dependencies: ['@lvbt/brand'], + dependencies: ['@lasvegasfortransit/brand'], slug: 'home', status: 'active', }, { - name: '@lvbt/lab-map', + name: '@lasvegasfortransit/lab-map', directory: 'apps/map', - dependencies: ['@lvbt/ui'], + dependencies: ['@lasvegasfortransit/ui'], slug: 'map', status: 'active', }, { - name: '@lvbt/lab-map-two', + name: '@lasvegasfortransit/lab-map-two', directory: 'apps/map-two', dependencies: [], slug: 'map-two', @@ -30,7 +34,12 @@ const projects: WorkspaceProject[] = [ test('shared changes rebuild direct and transitive dependents', () => { const result = affectedProjects(projects, ['packages/brand/src/tokens.css']); - expect(result.packages).toEqual(['@lvbt/brand', '@lvbt/lab-home', '@lvbt/lab-map', '@lvbt/ui']); + expect(result.packages).toEqual([ + '@lasvegasfortransit/brand', + '@lasvegasfortransit/lab-home', + '@lasvegasfortransit/lab-map', + '@lasvegasfortransit/ui', + ]); expect(result.deploy).toEqual(['map', 'home']); }); @@ -45,7 +54,7 @@ test('manifest changes rebuild home and keep drafts out of deployment', () => { }); test('a removed package still invalidates its surviving dependents', () => { - const current = projects.filter((project) => project.name !== '@lvbt/ui'); + const current = projects.filter((project) => project.name !== '@lasvegasfortransit/ui'); expect(affectedProjects(current, ['packages/ui/src/control.tsx'], projects).deploy).toEqual([ 'map', ]); @@ -70,7 +79,9 @@ test('root documentation does not trigger deployment', () => { test('cycles terminate and include each dependent once', () => { const cycle = projects.map((project) => - project.name === '@lvbt/brand' ? { ...project, dependencies: ['@lvbt/ui'] } : project, + project.name === '@lasvegasfortransit/brand' + ? { ...project, dependencies: ['@lasvegasfortransit/ui'] } + : project, ); expect(affectedProjects(cycle, ['packages/ui/src/control.tsx']).deploy).toEqual(['map', 'home']); }); @@ -78,7 +89,7 @@ test('cycles terminate and include each dependent once', () => { test('a paused migration remains buildable but is not deployed by Labs', () => { const paused: WorkspaceProject[] = [ { - name: '@lvbt/lab-example', + name: '@lasvegasfortransit/lab-example', directory: 'apps/example', dependencies: [], slug: 'example', @@ -86,7 +97,7 @@ test('a paused migration remains buildable but is not deployed by Labs', () => { deploymentOwner: 'standalone', }, { - name: '@lvbt/lab-home', + name: '@lasvegasfortransit/lab-home', directory: 'apps/home', dependencies: [], slug: 'home', @@ -95,7 +106,7 @@ test('a paused migration remains buildable but is not deployed by Labs', () => { ]; expect(affectedProjects(paused, ['migrations/example.json'])).toEqual({ - packages: ['@lvbt/lab-example'], + packages: ['@lasvegasfortransit/lab-example'], apps: ['example'], deploy: [], }); diff --git a/packages/labs-cli/tests/cache-inputs.test.ts b/packages/labs-cli/tests/cache-inputs.test.ts index d81c0a5..8d1987f 100644 --- a/packages/labs-cli/tests/cache-inputs.test.ts +++ b/packages/labs-cli/tests/cache-inputs.test.ts @@ -17,7 +17,11 @@ function hashes(root: string) { ); return dryRun .parse(JSON.parse(output)) - .tasks.filter((task) => ['@lvbt/lab-home#build', '@lvbt/labs-cli#test'].includes(task.taskId)); + .tasks.filter((task) => + ['@lasvegasfortransit/lab-home#build', '@lasvegasfortransit/labs-cli#test'].includes( + task.taskId, + ), + ); } test('catalog records and app manifests invalidate home builds and catalog validation', async () => { @@ -35,7 +39,10 @@ test('catalog records and app manifests invalidate home builds and catalog valid await mkdir(directory, { recursive: true }); await writeFile( path.join(directory, 'package.json'), - JSON.stringify({ name: `@lvbt/${name}`, scripts: { build: 'true', test: 'true' } }), + JSON.stringify({ + name: `@lasvegasfortransit/${name}`, + scripts: { build: 'true', test: 'true' }, + }), ); } const original = hashes(root); diff --git a/packages/labs-cli/tests/cli.test.ts b/packages/labs-cli/tests/cli.test.ts index f4e2f63..e12ff0b 100644 --- a/packages/labs-cli/tests/cli.test.ts +++ b/packages/labs-cli/tests/cli.test.ts @@ -162,7 +162,7 @@ describe('parseLabCommand', () => { describe('projectFilter', () => { it('derives the workspace package name from the permanent slug', () => { - expect(projectFilter('transit-funding')).toBe('@lvbt/lab-transit-funding'); + expect(projectFilter('transit-funding')).toBe('@lasvegasfortransit/lab-transit-funding'); }); }); diff --git a/packages/labs-cli/tests/cloudflare-deployment.test.ts b/packages/labs-cli/tests/cloudflare-deployment.test.ts index e5efc39..07b6a44 100644 --- a/packages/labs-cli/tests/cloudflare-deployment.test.ts +++ b/packages/labs-cli/tests/cloudflare-deployment.test.ts @@ -205,7 +205,7 @@ test('waits for the new release marker before verifying the stable URL', async ( }, }); const result = await deployProjects( - { packages: ['@lvbt/lab-home'], deploy: ['home'] }, + { packages: ['@lasvegasfortransit/lab-home'], deploy: ['home'] }, operations, ); expect(result.ok).toBe(true); @@ -259,7 +259,7 @@ test.each([1, 2])( }, }); const result = await deployProjects( - { packages: ['@lvbt/lab-home'], deploy: ['home'] }, + { packages: ['@lasvegasfortransit/lab-home'], deploy: ['home'] }, operations, ); expect(result.ok).toBe(false); diff --git a/packages/labs-cli/tests/deployment-plan.test.ts b/packages/labs-cli/tests/deployment-plan.test.ts index 6308499..ed6130c 100644 --- a/packages/labs-cli/tests/deployment-plan.test.ts +++ b/packages/labs-cli/tests/deployment-plan.test.ts @@ -28,15 +28,15 @@ function commit( } const files = { - 'packages/brand/package.json': JSON.stringify({ name: '@lvbt/brand' }), + 'packages/brand/package.json': JSON.stringify({ name: '@lasvegasfortransit/brand' }), 'apps/home/package.json': JSON.stringify({ - name: '@lvbt/lab-home', - dependencies: { '@lvbt/brand': 'workspace:*' }, + name: '@lasvegasfortransit/lab-home', + dependencies: { '@lasvegasfortransit/brand': 'workspace:*' }, }), 'apps/home/lab.config.ts': `export default ${JSON.stringify(home)} as const;`, 'apps/map/package.json': JSON.stringify({ - name: '@lvbt/lab-map', - dependencies: { '@lvbt/brand': 'workspace:*' }, + name: '@lasvegasfortransit/lab-map', + dependencies: { '@lasvegasfortransit/brand': 'workspace:*' }, }), 'apps/map/lab.config.ts': `export default ${JSON.stringify({ ...home, slug: 'map' })} as const;`, }; @@ -59,6 +59,36 @@ test('plans from committed trees, resolving refs and rebuilding shared dependent } }); +test('plans across the @lvbt to @lasvegasfortransit package rename without failing on the pre-rename base', async () => { + const root = await mkdtemp(path.join(os.tmpdir(), 'lab-deployment-plan-rename-')); + try { + execFileSync('git', ['init', '--quiet'], { cwd: root }); + const base = commit(root, { + 'packages/brand/package.json': JSON.stringify({ name: '@lvbt/brand' }), + 'apps/home/package.json': JSON.stringify({ + name: '@lvbt/lab-home', + dependencies: { '@lvbt/brand': 'workspace:*' }, + }), + 'apps/home/lab.config.ts': `export default ${JSON.stringify(home)} as const;`, + }); + const head = commit( + root, + { + 'packages/brand/package.json': JSON.stringify({ name: '@lasvegasfortransit/brand' }), + 'apps/home/package.json': JSON.stringify({ + name: '@lasvegasfortransit/lab-home', + dependencies: { '@lasvegasfortransit/brand': 'workspace:*' }, + }), + }, + base, + ); + const plan = deploymentPlan(root, { base, head }); + expect(plan.deploy).toEqual(['home']); + } finally { + await rm(root, { recursive: true, force: true }); + } +}); + test('does not execute historical manifests while inspecting their status', async () => { const root = await mkdtemp(path.join(os.tmpdir(), 'lab-deployment-plan-')); try { @@ -96,7 +126,7 @@ test('deploys retired archives after source removal without inventing a build pa ); const plan = deploymentPlan(root, { base, head }); expect(plan.deploy).toEqual(['map', 'home']); - expect(plan.packages).not.toContain('@lvbt/lab-map'); + expect(plan.packages).not.toContain('@lasvegasfortransit/lab-map'); const update = commit(root, { 'retired/map/site/index.html': '

Corrected map

' }, head); expect(deploymentPlan(root, { base: head, head: update }).deploy).toEqual(['map', 'home']); const docs = commit(root, { 'docs/guide.md': 'Updated guide' }, update); @@ -145,7 +175,7 @@ test('deploys the archive before removing retired project source', async () => { execFileSync('git', ['init', '--quiet'], { cwd: root }); const base = commit(root, { ...files, - 'packages/labs-cli/package.json': JSON.stringify({ name: '@lvbt/labs-cli' }), + 'packages/labs-cli/package.json': JSON.stringify({ name: '@lasvegasfortransit/labs-cli' }), }); const manifest = { ...home, @@ -164,7 +194,7 @@ test('deploys the archive before removing retired project source', async () => { ); const plan = deploymentPlan(root, { base, head }); expect(plan.deploy).toEqual(['map', 'home']); - expect(plan.packages).not.toContain('@lvbt/lab-map'); + expect(plan.packages).not.toContain('@lasvegasfortransit/lab-map'); const update = commit( root, { 'retired/map/site/index.html': '

Updated archive

' }, @@ -205,7 +235,7 @@ test('a committed migration handoff pauses only that lab deployment', async () = expect(deploymentPlan(root, { base, head })).toMatchObject({ files: ['migrations/map.json'], - packages: ['@lvbt/lab-map'], + packages: ['@lasvegasfortransit/lab-map'], apps: ['map'], deploy: [], }); diff --git a/packages/labs-cli/tests/deployment.test.ts b/packages/labs-cli/tests/deployment.test.ts index 560f042..90c16e4 100644 --- a/packages/labs-cli/tests/deployment.test.ts +++ b/packages/labs-cli/tests/deployment.test.ts @@ -21,7 +21,11 @@ function operations(events: string[], failure?: string): DeploymentOperations { }; } const plan = { - packages: ['@lvbt/lab-home', '@lvbt/lab-map', '@lvbt/lab-budget'], + packages: [ + '@lasvegasfortransit/lab-home', + '@lasvegasfortransit/lab-map', + '@lasvegasfortransit/lab-budget', + ], deploy: ['home', 'map', 'budget'], }; @@ -30,7 +34,7 @@ test('builds all affected artifacts before deploying and verifying apps, with ho const result = await deployProjects(plan, operations(events)); expect(result.ok).toBe(true); expect(events).toEqual([ - 'build:@lvbt/lab-home,@lvbt/lab-map,@lvbt/lab-budget', + 'build:@lasvegasfortransit/lab-home,@lasvegasfortransit/lab-map,@lasvegasfortransit/lab-budget', 'deploy:map', 'verify:map', 'deploy:budget', diff --git a/packages/labs-cli/tests/migration-source.test.ts b/packages/labs-cli/tests/migration-source.test.ts index e8bd66b..184ad50 100644 --- a/packages/labs-cli/tests/migration-source.test.ts +++ b/packages/labs-cli/tests/migration-source.test.ts @@ -10,15 +10,18 @@ async function fixture(dependencies: Record, run: (root: string) try { execFileSync('git', ['init', '--quiet'], { cwd: root }); const files = { - 'apps/map/package.json': JSON.stringify({ name: '@lvbt/lab-map', dependencies }), + 'apps/map/package.json': JSON.stringify({ + name: '@lasvegasfortransit/lab-map', + dependencies, + }), 'apps/map/icon.bin': 'binary asset', - 'apps/other/package.json': JSON.stringify({ name: '@lvbt/lab-other' }), + 'apps/other/package.json': JSON.stringify({ name: '@lasvegasfortransit/lab-other' }), 'packages/ui/package.json': JSON.stringify({ - name: '@lvbt/ui', - dependencies: { '@lvbt/brand': 'workspace:*' }, + name: '@lasvegasfortransit/ui', + dependencies: { '@lasvegasfortransit/brand': 'workspace:*' }, }), - 'packages/brand/package.json': JSON.stringify({ name: '@lvbt/brand' }), - 'packages/unused/package.json': JSON.stringify({ name: '@lvbt/unused' }), + 'packages/brand/package.json': JSON.stringify({ name: '@lasvegasfortransit/brand' }), + 'packages/unused/package.json': JSON.stringify({ name: '@lasvegasfortransit/unused' }), }; let input = 'commit refs/heads/fixture\ncommitter Test 1 +0000\ndata 7\nfixture\n'; @@ -33,7 +36,7 @@ async function fixture(dependencies: Record, run: (root: string) } test('reads committed source and includes the transitive shared-package closure', async () => { - await fixture({ '@lvbt/ui': 'workspace:*' }, (root) => { + await fixture({ '@lasvegasfortransit/ui': 'workspace:*' }, (root) => { const source = migrationSource(root); expect(source.commit).toMatch(/^[a-f0-9]{40}$/); expect(source.read('apps/map/icon.bin').toString()).toBe('binary asset'); @@ -42,8 +45,8 @@ test('reads committed source and includes the transitive shared-package closure' }); test.each([ - { '@lvbt/lab-other': 'workspace:*' }, - { '@lvbt/labs-cli': 'workspace:*' }, + { '@lasvegasfortransit/lab-other': 'workspace:*' }, + { '@lasvegasfortransit/labs-cli': 'workspace:*' }, { outside: 'file:../../../outside' }, ])('rejects nonportable dependencies: %j', async (dependencies) => { await fixture(dependencies, (root) => { diff --git a/packages/labs-cli/tests/pr-preview-command.test.ts b/packages/labs-cli/tests/pr-preview-command.test.ts index 6227bce..d6f9e1b 100644 --- a/packages/labs-cli/tests/pr-preview-command.test.ts +++ b/packages/labs-cli/tests/pr-preview-command.test.ts @@ -69,7 +69,7 @@ test('plans version previews for existing Workers and temporary Workers for new { deploymentPlan: () => ({ head: commit, - packages: ['@lvbt/lab-map'], + packages: ['@lasvegasfortransit/lab-map'], deploy: ['map', 'home'], }), deployedWorkers: () => Promise.resolve(['lvbt-labs-home']), @@ -110,7 +110,11 @@ test('discovers Durable Object projects only when they declare isolated staging' test('publishes only in apply mode and preserves the machine-readable plan', async () => { let publishes = 0; const dependencies = { - deploymentPlan: () => ({ head: commit, packages: ['@lvbt/lab-home'], deploy: ['home'] }), + deploymentPlan: () => ({ + head: commit, + packages: ['@lasvegasfortransit/lab-home'], + deploy: ['home'], + }), deployedWorkers: () => Promise.resolve(['lvbt-labs-home']), statefulProjects: () => Promise.resolve([]), publish: () => { diff --git a/packages/labs-cli/tests/pr-preview-deployment.test.ts b/packages/labs-cli/tests/pr-preview-deployment.test.ts index 2ca98f9..67f5455 100644 --- a/packages/labs-cli/tests/pr-preview-deployment.test.ts +++ b/packages/labs-cli/tests/pr-preview-deployment.test.ts @@ -137,7 +137,7 @@ test('builds every affected package before uploading and verifying previews', as const result = await publishPullRequestPreviews({ root: '/repo', identity: { ...identity, accountId: 'abc123' }, - plan: { head: commit, packages: ['@lvbt/lab-home'], deploy: ['home'] }, + plan: { head: commit, packages: ['@lasvegasfortransit/lab-home'], deploy: ['home'] }, targets: [target], read: { get: () => Promise.resolve({}), list: () => Promise.resolve([]) }, dependencies: { @@ -170,7 +170,7 @@ test('builds every affected package before uploading and verifying previews', as expect(result.ok).toBe(true); expect(events).toEqual([ 'run:check', - 'run:exec turbo run build --filter=@lvbt/lab-home', + 'run:exec turbo run build --filter=@lasvegasfortransit/lab-home', 'assert', 'prepare', 'assert', @@ -201,7 +201,7 @@ test('publishes a stateful project through its dedicated staging Worker', async const result = await publishPullRequestPreviews({ root: '/repo', identity: { ...identity, accountId: 'abc123' }, - plan: { head: commit, packages: ['@lvbt/lab-map'], deploy: ['map'] }, + plan: { head: commit, packages: ['@lasvegasfortransit/lab-map'], deploy: ['map'] }, targets: [target], read: { get: () => Promise.resolve({}), list: () => Promise.resolve([]) }, dependencies: { diff --git a/packages/labs-cli/tests/provision-worker-resources.test.ts b/packages/labs-cli/tests/provision-worker-resources.test.ts index 70ed54c..85837c1 100644 --- a/packages/labs-cli/tests/provision-worker-resources.test.ts +++ b/packages/labs-cli/tests/provision-worker-resources.test.ts @@ -60,7 +60,7 @@ test('plans and uploads each missing source-backed published Worker once', async expect(commands).toEqual([ { command: 'pnpm', - args: ['--filter', '@lvbt/lab-home', 'build'], + args: ['--filter', '@lasvegasfortransit/lab-home', 'build'], cwd: root, }, { diff --git a/packages/labs-cli/tests/runtime-boundary.test.ts b/packages/labs-cli/tests/runtime-boundary.test.ts index aa4adc1..28e078f 100644 --- a/packages/labs-cli/tests/runtime-boundary.test.ts +++ b/packages/labs-cli/tests/runtime-boundary.test.ts @@ -10,8 +10,8 @@ test('independent apps use the shared runtime without repository-management tool devDependencies: Record; }; const dependencies = { ...pkg.dependencies, ...pkg.devDependencies }; - expect(dependencies['@lvbt/labs-cli'], entry.name).toBeUndefined(); - expect(dependencies['@lvbt/lab-runtime'], entry.name).toBe('workspace:*'); + expect(dependencies['@lasvegasfortransit/labs-cli'], entry.name).toBeUndefined(); + expect(dependencies['@lasvegasfortransit/lab-runtime'], entry.name).toBe('workspace:*'); } }); diff --git a/packages/labs-cli/tests/workspace.test.ts b/packages/labs-cli/tests/workspace.test.ts index 547beb5..c67b065 100644 --- a/packages/labs-cli/tests/workspace.test.ts +++ b/packages/labs-cli/tests/workspace.test.ts @@ -20,6 +20,8 @@ describe('validateWorkspaceImport', () => { }); it('accepts imports from a shared package', () => { - expect(validateWorkspaceImport('apps/home/src/page.ts', '@lvbt/brand/tokens.css')).toBeNull(); + expect( + validateWorkspaceImport('apps/home/src/page.ts', '@lasvegasfortransit/brand/tokens.css'), + ).toBeNull(); }); }); diff --git a/packages/ui/README.md b/packages/ui/README.md index 86fc7ea..9f95070 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -1,7 +1,7 @@ # LVBT UI -`@lvbt/ui` contains small accessibility and lifecycle primitives shared across Labs. It does not own -navigation, page composition, application state, or business logic. +`@lasvegasfortransit/ui` contains small accessibility and lifecycle primitives shared across Labs. +It does not own navigation, page composition, application state, or business logic. The UI package provides accessible foundational controls for Astro and React. Controls share interaction behavior and brand styling while leaving navigation, state, workflows, and domain logic diff --git a/packages/ui/package.json b/packages/ui/package.json index 22d468e..4a74c95 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,5 +1,5 @@ { - "name": "@lvbt/ui", + "name": "@lasvegasfortransit/ui", "version": "0.0.0", "private": true, "type": "module", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3375852..7f0d25e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -391,13 +391,13 @@ importers: '@lasvegasfortransit/analytics': specifier: 'catalog:' version: 0.1.0(@playwright/test@1.62.1)(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.13.3)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react@19.2.8)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) - '@lvbt/brand': + '@lasvegasfortransit/brand': specifier: workspace:* version: link:../../packages/brand - '@lvbt/labs-cli': + '@lasvegasfortransit/labs-cli': specifier: workspace:* version: link:../../packages/labs-cli - '@lvbt/ui': + '@lasvegasfortransit/ui': specifier: workspace:* version: link:../../packages/ui '@tailwindcss/vite': @@ -452,10 +452,10 @@ importers: '@lasvegasfortransit/analytics': specifier: 'catalog:' version: 0.1.0(@playwright/test@1.62.1)(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.13.3)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))(react@19.2.8)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0)) - '@lvbt/brand': + '@lasvegasfortransit/brand': specifier: workspace:* version: link:../../packages/brand - '@lvbt/ui': + '@lasvegasfortransit/ui': specifier: workspace:* version: link:../../packages/ui react: @@ -468,6 +468,9 @@ importers: '@lasvegasfortransit/eslint-config': specifier: file:../../.lvbt/web-platform/packages/eslint-config version: file:.lvbt/web-platform/packages/eslint-config(eslint@10.9.1(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(turbo@2.10.12)(typescript@6.0.3) + '@lasvegasfortransit/lab-runtime': + specifier: workspace:* + version: link:../../packages/lab-runtime '@lasvegasfortransit/playwright-config': specifier: file:../../.lvbt/web-platform/packages/playwright-config version: file:.lvbt/web-platform/packages/playwright-config(@playwright/test@1.62.1) @@ -477,9 +480,6 @@ importers: '@lasvegasfortransit/vitest-config': specifier: file:../../.lvbt/web-platform/packages/vitest-config version: file:.lvbt/web-platform/packages/vitest-config(vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)(yaml@2.9.0))) - '@lvbt/lab-runtime': - specifier: workspace:* - version: link:../../packages/lab-runtime '@playwright/test': specifier: 'catalog:' version: 1.62.1 @@ -578,12 +578,12 @@ importers: packages/labs-cli: dependencies: + '@lasvegasfortransit/lab-runtime': + specifier: workspace:* + version: link:../lab-runtime '@lasvegasfortransit/web-platform': specifier: file:../../.lvbt/web-platform/packages/web-platform version: file:.lvbt/web-platform/packages/web-platform - '@lvbt/lab-runtime': - specifier: workspace:* - version: link:../lab-runtime zod: specifier: 'catalog:' version: 4.5.4 diff --git a/turbo.json b/turbo.json index 355bf54..ae9135d 100644 --- a/turbo.json +++ b/turbo.json @@ -54,8 +54,11 @@ "dependsOn": ["^build"], "outputs": ["dist-archive/**"] }, - "@lvbt/labs-cli#test:e2e": { - "dependsOn": ["@lvbt/lab-home#build", "@lvbt/lab-transit-funding#build"], + "@lasvegasfortransit/labs-cli#test:e2e": { + "dependsOn": [ + "@lasvegasfortransit/lab-home#build", + "@lasvegasfortransit/lab-transit-funding#build" + ], "cache": false, "outputs": ["playwright-report/**", "test-results/**"] }