Skip to main content

Tooling and Scripts

See also: Local Development · Testing · Data Model · Wiki Publishing

Ground rules

  • Bun runs everything. Workspace scripts are bun run <script>; tools are Bun TypeScript (bun run tools/foo.ts), not Node.
  • Use bunx nx <target> <project> (or bunx <tool> directly), never a bare nx.
  • Nx version is 22.7.5; the daemon is disabled inside the tool scripts (NX_DAEMON=false).

Windows / VS Code terminal quirks

Two env vars VS Code injects break tooling on Windows:

SymptomFix
nx fails with an ESM import errorunset NX_WORKSPACE_ROOT_PATH — VS Code injects a lowercase drive path that breaks nx's ESM import. Also make sure your cwd uses an uppercase drive letter (C:\... not c:\...).
Cypress dies with bad option: --smoke-testunset ELECTRON_RUN_AS_NODE — VS Code sets it, which makes the Cypress Electron binary boot as plain Node.

The e2e wrapper scripts (tools/e2e-emulate.ts, tools/cypress-e2e.ts) and the pre-push hook already strip both — the manual unset is only needed when running bunx nx ... / bunx cypress ... directly.

The tools/ directory

ScriptEntry pointWhat it does
tools/setup-env.tsbun run setupOne-time onboarding: firebase login (Secret Manager access for the emulator, see Secrets Management), optional STORYBOOK_FUNCTIONS_BASE_URL OS env var.
tools/emulate.tsbun run emulateEmulator with emulator-data/ import/export persistence + seeded test user. See Local Development.
tools/serve-emulated.tsbun run start:emulated / mobile:start:emulatedOne-command emulated dev: reuses a running emulator (and leaves it up on exit) or builds Functions + starts one with persistence + seed, then serves the app; Ctrl+C stops both gracefully. See Local Development.
tools/emulator-lifecycle.ts(shared)Emulator process plumbing: persistence args, graceful export-aware shutdown, orphaned-export recovery, Functions build for the emulator, port/process kill helpers. Used by emulate, serve-emulated, storybook-emulated.
tools/emulator-seed.ts(shared)waitForAuthEmulator() + seedTestUser() (test@test.com / testtest), used by the emulator scripts above.
tools/storybook-emulated.tsbun run storybook:emulatedBuilds Functions, installs their deps into dist/, starts emulators, seeds, launches Storybook against them.
tools/storybook-e2e.tsbun run storybook:e2e / bunx nx e2e storybook-e2eServes dist/storybook on :6199 and runs @storybook/test-runner (Playwright). See Testing.
tools/e2e-emulate.tsbun run e2e:emulated / e2e:mobile:emulatedFresh emulator (no import/export) + Cypress suite with CYPRESS_EMULATED=1. See Testing.
tools/cypress-e2e.ts(via e2e nx targets)Serve → wait → cypress run; replaces the broken @nx/cypress:cypress executor; honors external CYPRESS_BASE_URL. See Testing.
tools/bump-version.tsbun run bump:versionPatch-bumps the app version everywhere (see below).
tools/generate-types-erd.tsbun run types:erdRegenerates the Data Model ER diagram (see below).
tools/generate-remote-config.tsbun run firebase:remote-config:generate / :checkGenerates/validates checked-in Firebase Remote Config BOOLEAN templates and the Feature Flags catalog; never contacts Firebase.
tools/mobile-setup.tsbun run mobile:setupOne-time user-local JDK 21 + Android SDK install (Windows/macOS). See Mobile App.
tools/mobile-apk.tsbun run mobile:apk:{dev,prod}Local debug-signed APK; --install adb-sideloads it. See Mobile App.
tools/mobile-machine-paths.ts(shared)User-local JDK/SDK install paths for the two mobile scripts.
tools/build-wiki.tsbun run wiki:build / wiki:check / wiki:bundle:check / wiki:testRegenerates + gates the wiki's generated pages (Storybook, Test Coverage, Production Bundle, the Data Model ERD block) and link-checks every page. See Wiki Publishing.
tools/wiki/bun run wiki:serve / wiki:siteServes the wiki as a local site on :4201 (per-request render, vendored mermaid) / static export to dist/wiki. The page generators live here too. See Wiki Publishing.
tools/build-stories.tsbun run stories:build / stories:checkValidates the compact stories/*.json catalog + regenerates catalog.json. Authoring-only — no runtime consumer (the /stories/map viewer was removed 2026-07-29); NOT the app's stories (see below + Story Engine).
tools/wide-gamut-colors.tsbun run tools/wide-gamut-colors.tsGenerates the vivid OKLCH (Display-P3) accent maps + per-gradient interpolation spaces for foundations/{colors,gradients}.scss — rerun after any accent/pair change.
tools/check-bundle-size.tsbun run bundle:checkTransfer-size gate (CI + pre-push, right after the prod build): brotli-11 of main.js ≤ 250 kB — what Firebase Hosting actually serves. Angular budgets (project.json, raw bytes) guard gross regressions; this guards the wire size. To find a regression: bunx nx build stretched -- --stats-json, group stats.json main inputs by package.
tools/serve-pwa.tsbun run pwa:emulatedFull PWA (ngsw service worker + chunk prefetch) against the local emulator: builds the pwa-emulated configuration and serves it statically on :4280 (own port so the SW never hijacks the :4200 dev loop). See Local Development § PWA.
tools/check-design-tokens.tsbun run tokens:check / tokens:update / :testToken-discipline RATCHET (CI + pre-push): scans SCSS + inline styles: blocks for hand-written values the foundations cover (raw colors, px/rem spacing+widths, font-weights, line-heights, z-index, durations). design-token-baseline.json pins legacy counts per file — a file GAINING violations fails; improving requires tokens:update + commit. token-ok comment = documented exception; @container conditions auto-exempt.
tools/responsive-audit.tsbun run responsive:auditDrives every WEB_ROUTES path through the supported widths (320 design floor → 1600) and fails on horizontal overflow (document scroll or elements past the viewport edge; overflow-x: auto containers exempt — that pattern is by design). Needs a build in dist/apps/stretched/browser first (pwa-emulated works backend-free).
tools/storybook-visual-diff.tsbun run storybook:visual (storybook:visual:test)Screenshot-diffs every story base-ref vs working tree (Playwright + pixelmatch), most-different-first HTML report, TS-import-graph change detection. See Testing.

Generators

types:erd — Data Model ER diagram

bun run types:erd # regenerate the mermaid erDiagram in libs/stretched-types/README.md
bun run types:erd:check # CI gate: exit 1 if the committed diagram is stale
bun run types:erd:test # unit tests for the generator itself

tools/generate-types-erd.ts parses every exported object type in @stretched/types (ts-morph) and emits a mermaid erDiagram between the ERD:START / ERD:END markers in that lib's README. It derives entities, solid relationship edges (direct / array / Record / nested inline types, with cardinality from optional/array/map), and attribute blocks automatically. Two JSDoc tags steer it:

  • @ref <Entity> — on a bare string field (e.g. story_REF: string) declares a reference the tool can't infer; drawn as a dashed edge.
  • @erd-ignore — excludes a type from the diagram (API/DTO namespaces like *Api and the files listed in SKIP_FILES are excluded wholesale).

The diagram is written into libs/stretched-types/README.md; the copy on Data Model is propagated from there by build-wiki.ts. So after changing a type, run both bun run types:erd and bun run wiki:build, and commit both files — CI's types:erd:check gates the README copy and wiki:check gates the wiki copy.

firebase:remote-config:generate / firebase:remote-config:check

bun run firebase:remote-config:generate # regenerate dev/prod templates + wiki flag table
bun run firebase:remote-config:check # fail when the checked-in output is stale
bun run firebase:remote-config:test # focused generator/validator tests

tools/generate-remote-config.ts uses the current runtime ALL_STORIES, the web footer registry, and the web-app launch-flag registry to derive the only supported Remote Config parameters. It rejects duplicate/normalization-colliding keys and writes native Boolean defaults to separate templates for stretched-dev and stretched-2c16a; environment-specific defaults may differ. It is an offline generator, not deployment automation. See Feature Flags for Firebase console setup, rollout, and rollback procedure.

bump:version

bun run bump:version increments the patch segment of the app version in all six environment files (apps/stretched/src/environments/* and apps/stretched-mobile/src/environments/*), and bumps the native shells too: Android versionCode/versionName in apps/stretched-mobile/android/app/build.gradle and iOS CURRENT_PROJECT_VERSION/MARKETING_VERSION in the Xcode project (store build numbers must strictly increase per upload). Bump minor/major by hand across all files.

wiki:build / wiki:check / wiki:serve

The wiki/ folder is the wiki itself (no GitHub wiki). wiki:build regenerates the pages under wiki/generated/ + the Data Model ERD block and link-checks everything; wiki:check is the staleness gate; wiki:bundle:check gates the Production Bundle page right after the production build; wiki:serve renders it all as a local site on :4201. Full details on Wiki Publishing.

stories:build / stories:check — the story-graph catalog (NOT the app's stories)

bun run stories:build # validate stories/*.json + (re)write catalog.json (+ the served copy)
bun run stories:check # CI/pre-push gate: exit 1 if the committed catalog.json is stale
bun run stories:test # unit tests for the validator

tools/build-stories.ts validates the compact stories/*.json authoring files (unique names, valid categories, acyclic dependsOn, resolvable refs) and compiles them into the repo-root catalog.json + a served copy at apps/stretched/public/catalog.json. ⚠️ This catalog is authoring-only — its /stories/map viewer was removed 2026-07-29 and nothing reads it at runtime; it is NOT what users play. The stories users actually play are hand-authored runtime Story objects (libs/stretched-components/.../story-teller/foundation-stories.ts + cost-stories.ts); there is no compiler between the two. See Story Engine.

Git hooks (husky)

  • .husky/pre-push — prettier --write over the repo (if it changed anything, it commits chore: prettier auto-format and aborts the push — push again to include it), then affected lint, affected unit tests with --coverage (must match CI), Functions tests, the doc-currency gates (nx test stretched-types, types:erd:test + :check, wiki:check, stories:test + :check), a production build, and a Storybook build. Mirrors the CI test + docs jobs. The auto-commit behavior is why the .gitignore append rule on Secrets Management matters.
  • .husky/pre-commit — regenerates the story catalog.json (+ served copy) and stages it whenever a commit touches stories/ or tools/build-stories.ts, so the catalog is always fresh in the commit (you rarely run stories:build by hand). Blocks the commit if a story is invalid.
  • .husky/post-merge — runs bun install after a merge/pull so deps stay in sync.