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>(orbunx <tool>directly), never a barenx. - 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:
| Symptom | Fix |
|---|---|
nx fails with an ESM import error | unset 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-test | unset 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
| Script | Entry point | What it does |
|---|---|---|
tools/setup-env.ts | bun run setup | One-time onboarding: firebase login (Secret Manager access for the emulator, see Secrets Management), optional STORYBOOK_FUNCTIONS_BASE_URL OS env var. |
tools/emulate.ts | bun run emulate | Emulator with emulator-data/ import/export persistence + seeded test user. See Local Development. |
tools/serve-emulated.ts | bun run start:emulated / mobile:start:emulated | One-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.ts | bun run storybook:emulated | Builds Functions, installs their deps into dist/, starts emulators, seeds, launches Storybook against them. |
tools/storybook-e2e.ts | bun run storybook:e2e / bunx nx e2e storybook-e2e | Serves dist/storybook on :6199 and runs @storybook/test-runner (Playwright). See Testing. |
tools/e2e-emulate.ts | bun run e2e:emulated / e2e:mobile:emulated | Fresh 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.ts | bun run bump:version | Patch-bumps the app version everywhere (see below). |
tools/generate-types-erd.ts | bun run types:erd | Regenerates the Data Model ER diagram (see below). |
tools/generate-remote-config.ts | bun run firebase:remote-config:generate / :check | Generates/validates checked-in Firebase Remote Config BOOLEAN templates and the Feature Flags catalog; never contacts Firebase. |
tools/mobile-setup.ts | bun run mobile:setup | One-time user-local JDK 21 + Android SDK install (Windows/macOS). See Mobile App. |
tools/mobile-apk.ts | bun 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.ts | bun run wiki:build / wiki:check / wiki:bundle:check / wiki:test | Regenerates + 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:site | Serves 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.ts | bun run stories:build / stories:check | Validates 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.ts | bun run tools/wide-gamut-colors.ts | Generates 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.ts | bun run bundle:check | Transfer-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.ts | bun run pwa:emulated | Full 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.ts | bun run tokens:check / tokens:update / :test | Token-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.ts | bun run responsive:audit | Drives 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.ts | bun 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*Apiand the files listed inSKIP_FILESare 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--writeover the repo (if it changed anything, it commitschore: prettier auto-formatand 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 CItest+docsjobs. The auto-commit behavior is why the.gitignoreappend rule on Secrets Management matters..husky/pre-commit— regenerates the storycatalog.json(+ served copy) and stages it whenever a commit touchesstories/ortools/build-stories.ts, so the catalog is always fresh in the commit (you rarely runstories:buildby hand). Blocks the commit if a story is invalid..husky/post-merge— runsbun installafter a merge/pull so deps stay in sync.