Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion apps/home/astro.config.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand Down
8 changes: 4 additions & 4 deletions apps/home/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@lvbt/lab-home",
"name": "@lasvegasfortransit/lab-home",
"version": "0.0.0",
"private": true,
"type": "module",
Expand All @@ -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:"
Expand Down
6 changes: 3 additions & 3 deletions apps/home/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -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(), '../..');
Expand Down
2 changes: 1 addition & 1 deletion apps/home/src/styles/global.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import 'tailwindcss';
@import '@lvbt/brand/tokens.css';
@import '@lasvegasfortransit/brand/tokens.css';

:root {
--page-pad: clamp(1.25rem, 4vw, 3rem);
Expand Down
2 changes: 1 addition & 1 deletion apps/home/tests/astro-config.test.ts
Original file line number Diff line number Diff line change
@@ -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: {} })),
Expand Down
4 changes: 2 additions & 2 deletions apps/home/tests/e2e/catalog-links.spec.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
4 changes: 2 additions & 2 deletions apps/home/tests/e2e/home.spec.ts
Original file line number Diff line number Diff line change
@@ -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('/');

Expand Down
8 changes: 4 additions & 4 deletions apps/transit-funding/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@lvbt/lab-transit-funding",
"name": "@lasvegasfortransit/lab-transit-funding",
"version": "0.0.0",
"private": true,
"type": "module",
Expand All @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion apps/transit-funding/src/App.tsx
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion apps/transit-funding/src/main.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
4 changes: 2 additions & 2 deletions apps/transit-funding/src/ui/app.css
Original file line number Diff line number Diff line change
@@ -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';

* {
Expand Down
5 changes: 4 additions & 1 deletion apps/transit-funding/tests/e2e/archive/read-only.spec.ts
Original file line number Diff line number Diff line change
@@ -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 ({
Expand Down
9 changes: 5 additions & 4 deletions docs/development/how-to/create-a-lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-<slug>` 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-<slug>` 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:

Expand Down
10 changes: 5 additions & 5 deletions docs/development/how-to/deprecate-and-retire-a-lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ project runs through `pnpm exec turbo run test:archive --filter=<project-package

Archive tests live under `tests/e2e/archive/` and use a separate `playwright.archive.config.ts`
without a preview server. Import `readProjectArchiveFiles` and `createArchiveContext` from
`@lvbt/lab-runtime/archive`, read the archive, and create the isolated context with the lab slug and
the test's viewport. The context serves captured files directly: no upstream server receives
requests. Uncaptured paths, external requests, writes, and WebSockets are denied; service workers
are blocked. Assert that `archive.failures` is empty after the workflow, and close `archive.context`
in a `finally` block.
`@lasvegasfortransit/lab-runtime/archive`, read the archive, and create the isolated context with
the lab slug and the test's viewport. The context serves captured files directly: no upstream server
receives requests. Uncaptured paths, external requests, writes, and WebSockets are denied; service
workers are blocked. Assert that `archive.failures` is empty after the workflow, and close
`archive.context` in a `finally` block.

The page cannot open WebRTC or WebTransport connections. This harness verifies offline behavior; it
is not a security sandbox for untrusted code.
Expand Down
2 changes: 1 addition & 1 deletion docs/development/how-to/migrate-a-lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ directory must exist outside Labs.

Resolve every missing workspace dependency before exporting. A standalone project never imports
another Labs app or the Labs management CLI. Portable manifest and archive helpers come from
`@lvbt/lab-runtime`.
`@lasvegasfortransit/lab-runtime`.

### Export and validate

Expand Down
2 changes: 1 addition & 1 deletion docs/development/how-to/update-and-deploy-a-lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pnpm lab preview <slug>
Update screenshot baselines only after inspecting the rendered difference:

```sh
pnpm --filter @lvbt/lab-<slug> exec playwright test --update-snapshots
pnpm --filter @lasvegasfortransit/lab-<slug> exec playwright test --update-snapshots
```

Inspect every changed desktop and mobile image, then rerun the same suite without
Expand Down
19 changes: 10 additions & 9 deletions docs/development/reference/brand-and-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.
6 changes: 3 additions & 3 deletions docs/development/tutorials/create-first-lab.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
52 changes: 31 additions & 21 deletions docs/operations/how-to/provision.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
(<https://dash.cloudflare.com/2557b5c2e166292ded0f8425b73075e9/api-tokens>). Click **Create
Token**, then **Create Custom Token**.
2. Name it `labs deploy (GitHub Actions)`. Under **Permissions**, add exactly these rows, which are
Expand All @@ -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:

Expand All @@ -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:

Expand Down
9 changes: 5 additions & 4 deletions docs/operations/reference/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions packages/brand/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading
Loading