@ultimat3/cli 23.0.0 → 25.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/CLAUDE.md +28 -13
  2. package/README.md +32 -7
  3. package/package.json +29 -29
  4. package/src/admin-catalog-keys.ts +2 -2
  5. package/src/affected.ts +30 -36
  6. package/src/api-registration.ts +89 -0
  7. package/src/app-artifacts.ts +1 -1
  8. package/src/app-auth.ts +4 -27
  9. package/src/app-boundaries.ts +17 -4
  10. package/src/app-config-load.ts +51 -0
  11. package/src/app-env.ts +26 -25
  12. package/src/app-load.ts +38 -14
  13. package/src/app-manifest.ts +4 -10
  14. package/src/app-mcp.ts +31 -46
  15. package/src/app-name.ts +52 -0
  16. package/src/app-permissions-borrowed.ts +123 -0
  17. package/src/app-permissions-site.ts +48 -0
  18. package/src/app-permissions.ts +7 -17
  19. package/src/app-root.ts +9 -5
  20. package/src/bin.ts +3 -7
  21. package/src/browser-launcher.ts +4 -2
  22. package/src/budgets-charged.ts +60 -0
  23. package/src/budgets-scan.ts +76 -0
  24. package/src/budgets-unmeasured.ts +91 -0
  25. package/src/budgets.ts +117 -154
  26. package/src/cdp-shot-driver.ts +20 -1
  27. package/src/cdp-shot-watch.ts +35 -31
  28. package/src/ci-log.ts +0 -0
  29. package/src/cmd-auth-spec.ts +16 -0
  30. package/src/cmd-auth.ts +65 -0
  31. package/src/cmd-build-spec.ts +7 -1
  32. package/src/cmd-build.ts +151 -22
  33. package/src/cmd-db-backfill.ts +26 -13
  34. package/src/cmd-db-branch.ts +2 -2
  35. package/src/cmd-db.ts +10 -6
  36. package/src/cmd-deploy-compose.ts +94 -0
  37. package/src/cmd-deploy-drain.ts +48 -0
  38. package/src/cmd-deploy-helm.ts +7 -6
  39. package/src/cmd-deploy-start-first.ts +178 -0
  40. package/src/cmd-deploy.ts +158 -36
  41. package/src/cmd-dev.ts +18 -356
  42. package/src/cmd-doctor-spec.ts +2 -9
  43. package/src/cmd-doctor.ts +57 -80
  44. package/src/cmd-env.ts +55 -16
  45. package/src/cmd-generate.ts +89 -32
  46. package/src/cmd-help.ts +19 -6
  47. package/src/cmd-i18n.ts +21 -17
  48. package/src/cmd-jobs-spec.ts +7 -22
  49. package/src/cmd-jobs.ts +33 -140
  50. package/src/cmd-manifest-spec.ts +1 -1
  51. package/src/cmd-manifest.ts +6 -25
  52. package/src/cmd-mcp.ts +71 -22
  53. package/src/cmd-new.ts +36 -8
  54. package/src/cmd-planned.ts +24 -8
  55. package/src/cmd-routes.ts +15 -5
  56. package/src/cmd-secrets.ts +47 -6
  57. package/src/cmd-shot-matrix.ts +22 -7
  58. package/src/cmd-shot-spec.ts +10 -2
  59. package/src/cmd-shot.ts +46 -41
  60. package/src/cmd-tasks.ts +2 -2
  61. package/src/cmd-test-spec.ts +1 -7
  62. package/src/cmd-test.ts +14 -30
  63. package/src/cmd-verify.ts +4 -4
  64. package/src/coverage-lcov.ts +4 -2
  65. package/src/db-backfill.ts +2 -2
  66. package/src/db-replay-engine.ts +4 -10
  67. package/src/db-subscribes.ts +27 -16
  68. package/src/dev-boot.ts +290 -0
  69. package/src/dev-child-watch.ts +63 -12
  70. package/src/dev-dashboard-guard.ts +76 -0
  71. package/src/dev-dashboard.ts +56 -30
  72. package/src/dev-lock.ts +100 -31
  73. package/src/dev-rebuild.ts +80 -0
  74. package/src/dev-restart-retire.ts +46 -0
  75. package/src/dev-route-table.ts +30 -8
  76. package/src/dev-supervisor.ts +114 -22
  77. package/src/dev-traces.ts +53 -20
  78. package/src/dispatch.ts +17 -13
  79. package/src/doctor-app-url.ts +39 -0
  80. package/src/doctor-auth.ts +95 -0
  81. package/src/doctor-offline.ts +6 -5
  82. package/src/document-options.ts +7 -0
  83. package/src/drift-replica-identity.ts +42 -0
  84. package/src/drift.ts +29 -6
  85. package/src/error-catalog.ts +6 -9
  86. package/src/error-codes.ts +29 -3
  87. package/src/error-page-csp.ts +14 -15
  88. package/src/error-unthrown.ts +29 -11
  89. package/src/errors.ts +54 -83
  90. package/src/exec.ts +8 -0
  91. package/src/favicon.ts +2 -2
  92. package/src/fix-command.ts +9 -1
  93. package/src/framework-env.ts +148 -0
  94. package/src/framework-schema-apply.ts +78 -0
  95. package/src/framework-schema-stamp.ts +96 -0
  96. package/src/framework-schema.ts +25 -3
  97. package/src/generate-catalog-locales.ts +79 -0
  98. package/src/generate-files.ts +4 -3
  99. package/src/generate-kinds.ts +18 -0
  100. package/src/generate-plural.ts +50 -0
  101. package/src/generate-shadow.ts +95 -0
  102. package/src/generate-write.ts +47 -13
  103. package/src/gh-target.ts +12 -7
  104. package/src/hold.ts +56 -1
  105. package/src/i18n-audit.ts +25 -9
  106. package/src/i18n-index.ts +5 -6
  107. package/src/i18n-registration.ts +7 -9
  108. package/src/import-scan.ts +28 -3
  109. package/src/index.ts +2 -6
  110. package/src/island-bundle.ts +57 -79
  111. package/src/island-capture.ts +1 -1
  112. package/src/island-duplicate-refusal.ts +57 -0
  113. package/src/island-duplicates.ts +221 -0
  114. package/src/island-entry-missing.ts +64 -0
  115. package/src/island-harness-route.ts +20 -4
  116. package/src/island-harness-script.ts +23 -2
  117. package/src/island-identity.ts +17 -14
  118. package/src/island-package-dedupe.ts +150 -0
  119. package/src/island-realtime.ts +36 -16
  120. package/src/island-routes.ts +2 -2
  121. package/src/island-runtime.ts +241 -0
  122. package/src/island-sources.ts +105 -0
  123. package/src/island-states-load.ts +6 -0
  124. package/src/island-store.ts +90 -6
  125. package/src/jobs-json.ts +0 -26
  126. package/src/jobs-report.ts +36 -20
  127. package/src/local-cli-handoff.ts +90 -0
  128. package/src/loopback-host.ts +13 -0
  129. package/src/manifest-staleness.ts +62 -0
  130. package/src/mcp-errors.ts +20 -4
  131. package/src/mcp-host.ts +22 -14
  132. package/src/mcp-test-run.ts +38 -0
  133. package/src/mcp-ui-inspect.ts +3 -2
  134. package/src/mcp-ui-interact.ts +3 -2
  135. package/src/mcp-ui.ts +8 -7
  136. package/src/measure-scope.ts +15 -10
  137. package/src/messages.ts +29 -16
  138. package/src/metrics-endpoint.ts +28 -3
  139. package/src/migration-extensions.ts +1 -1
  140. package/src/otlp-export.ts +2 -2
  141. package/src/output.ts +24 -0
  142. package/src/page-navigation.ts +7 -23
  143. package/src/page-speculation.ts +19 -31
  144. package/src/parse.ts +0 -8
  145. package/src/permission-grants.ts +3 -3
  146. package/src/posix-path.ts +30 -0
  147. package/src/prerender-locales.ts +6 -4
  148. package/src/prerender-out.ts +34 -7
  149. package/src/prerender-report.ts +58 -0
  150. package/src/prerender-served.ts +52 -0
  151. package/src/prerender.ts +71 -103
  152. package/src/pwa-artifacts.ts +33 -94
  153. package/src/pwa-manifest-locales.ts +9 -9
  154. package/src/registry.ts +2 -0
  155. package/src/retry-memo.ts +1 -1
  156. package/src/revalidated-response.ts +4 -3
  157. package/src/role-realtime.ts +7 -1
  158. package/src/role-replicator.ts +124 -29
  159. package/src/role-start.ts +69 -85
  160. package/src/role-sync.ts +119 -25
  161. package/src/root-env.ts +157 -28
  162. package/src/route-islands.ts +69 -0
  163. package/src/runtime-admin.ts +4 -1
  164. package/src/runtime-assets.ts +26 -9
  165. package/src/runtime-bindings.ts +27 -7
  166. package/src/runtime-cache.ts +46 -55
  167. package/src/runtime-hooks.ts +2 -1
  168. package/src/runtime-idempotency-scope.ts +23 -0
  169. package/src/runtime-isr-outcome.ts +100 -0
  170. package/src/runtime-isr.ts +36 -0
  171. package/src/runtime-jobs.ts +76 -41
  172. package/src/runtime-live-feed.ts +1 -1
  173. package/src/runtime-mail.ts +26 -0
  174. package/src/runtime-mfa-warning.ts +29 -0
  175. package/src/runtime-notify-retention.ts +10 -46
  176. package/src/runtime-overrides.ts +4 -4
  177. package/src/runtime-purge.ts +38 -16
  178. package/src/runtime-queue.ts +43 -22
  179. package/src/runtime-realtime.ts +11 -45
  180. package/src/runtime-render.ts +136 -114
  181. package/src/runtime-replica.ts +3 -8
  182. package/src/runtime-services.ts +49 -26
  183. package/src/runtime-storage-upload.ts +2 -2
  184. package/src/runtime-storage.ts +13 -8
  185. package/src/schema-diff.ts +16 -0
  186. package/src/schema-drift.ts +45 -15
  187. package/src/secrets-rotation.ts +8 -9
  188. package/src/seo-routes.ts +80 -9
  189. package/src/serve-boot.ts +51 -12
  190. package/src/serve-drain.ts +11 -33
  191. package/src/serve-env.ts +9 -6
  192. package/src/serve-retire.ts +95 -0
  193. package/src/serve-web.ts +31 -15
  194. package/src/serve.ts +27 -10
  195. package/src/server-barrels.ts +6 -3
  196. package/src/shot-browser.ts +1 -1
  197. package/src/shot-locale.ts +6 -33
  198. package/src/shot-settle.ts +3 -3
  199. package/src/shot-theme.ts +1 -1
  200. package/src/shot-viewport.ts +152 -0
  201. package/src/signal-shred.ts +26 -12
  202. package/src/site-asset-routes.ts +2 -2
  203. package/src/site-assets.ts +3 -5
  204. package/src/site-config.ts +9 -48
  205. package/src/site-seo.ts +7 -5
  206. package/src/sitemap-lastmod.ts +5 -0
  207. package/src/solid-loader.ts +12 -3
  208. package/src/source-files.ts +19 -3
  209. package/src/static-report.ts +5 -0
  210. package/src/stored-object-headers.ts +43 -0
  211. package/src/style-routes.ts +2 -2
  212. package/src/sw-artifacts.ts +8 -8
  213. package/src/sw-precache-plan.ts +34 -4
  214. package/src/sync-authenticator.ts +3 -3
  215. package/src/sync-url.ts +3 -3
  216. package/src/tasks-facts.ts +3 -29
  217. package/src/templates/action.ts +7 -4
  218. package/src/templates/backfill.ts +4 -4
  219. package/src/templates/entity.ts +2 -2
  220. package/src/templates/github/ci.yml.ts +28 -18
  221. package/src/templates/github/image.yml.ts +92 -0
  222. package/src/templates/github/runner.ts +18 -0
  223. package/src/templates/guard-bare-error.ts +16 -2
  224. package/src/templates/guard-unzoned-date.ts +56 -6
  225. package/src/templates/index.ts +1 -1
  226. package/src/templates/job-body-test.ts +2 -2
  227. package/src/templates/job.ts +6 -6
  228. package/src/templates/naming.ts +28 -0
  229. package/src/templates/query.ts +9 -8
  230. package/src/templates/resource-page.ts +9 -7
  231. package/src/templates/resource-service.ts +2 -2
  232. package/src/templates/route.ts +26 -14
  233. package/src/templates/scaffold-api.ts +2 -3
  234. package/src/templates/scaffold-app.ts +8 -3
  235. package/src/templates/scaffold-auth.ts +205 -70
  236. package/src/templates/scaffold-bin.ts +122 -0
  237. package/src/templates/scaffold-claude-commands.ts +11 -11
  238. package/src/templates/scaffold-container-compose.ts +10 -1
  239. package/src/templates/scaffold-container.ts +16 -8
  240. package/src/templates/scaffold-dashboard-bare.ts +8 -4
  241. package/src/templates/scaffold-dashboard-example-tests.ts +208 -0
  242. package/src/templates/scaffold-dashboard-example.ts +112 -172
  243. package/src/templates/scaffold-dashboard-shared.ts +62 -1
  244. package/src/templates/scaffold-dashboard.ts +9 -5
  245. package/src/templates/scaffold-db-client.ts +4 -2
  246. package/src/templates/scaffold-db-package.ts +2 -2
  247. package/src/templates/scaffold-docs.ts +20 -77
  248. package/src/templates/scaffold-domain-package.ts +0 -4
  249. package/src/templates/scaffold-entries.ts +4 -3
  250. package/src/templates/scaffold-env.ts +6 -3
  251. package/src/templates/scaffold-gitattributes.ts +59 -0
  252. package/src/templates/scaffold-helm-fences.ts +76 -0
  253. package/src/templates/scaffold-helm-templates.ts +100 -19
  254. package/src/templates/scaffold-helm.ts +62 -5
  255. package/src/templates/scaffold-http.ts +1 -1
  256. package/src/templates/scaffold-i18n.ts +15 -2
  257. package/src/templates/scaffold-icon.ts +2 -2
  258. package/src/templates/scaffold-repo.ts +27 -16
  259. package/src/templates/scaffold-shell.ts +55 -37
  260. package/src/test-counts.ts +2 -6
  261. package/src/test-ignores.ts +25 -0
  262. package/src/test-only-shipped.ts +193 -0
  263. package/src/test-passes.ts +0 -15
  264. package/src/test-select.ts +7 -15
  265. package/src/test-shards.ts +40 -95
  266. package/src/test-workers.ts +26 -4
  267. package/src/theme-boot.ts +8 -33
  268. package/src/theme-brand.ts +66 -0
  269. package/src/trusted-hops-error.ts +17 -0
  270. package/src/trusted-hops.ts +49 -0
  271. package/src/unscanned-admin.ts +2 -2
  272. package/src/unthrown-codes.ts +26 -0
  273. package/src/verify-checks.ts +29 -48
  274. package/src/verify-coverage-run.ts +3 -2
  275. package/src/verify-deadline.ts +207 -33
  276. package/src/verify-merge.ts +108 -43
  277. package/src/verify-part-step.ts +98 -0
  278. package/src/verify-role-load-probe.ts +12 -2
  279. package/src/verify-role-load.ts +9 -4
  280. package/src/verify-run.ts +32 -8
  281. package/src/verify-shard.ts +13 -2
  282. package/src/verify-stalled.ts +152 -0
  283. package/src/verify-step.ts +2 -0
  284. package/src/verify-test-run.ts +3 -2
  285. package/src/verify-tests.ts +5 -3
  286. package/src/worker-bundle.ts +2 -2
  287. package/src/workspace-checks.ts +53 -7
  288. package/src/workspace-graph.ts +3 -2
  289. package/src/write-line.ts +82 -7
  290. package/src/browser-launcher-fake-html.ts +0 -80
  291. package/src/browser-launcher-fake.ts +0 -168
  292. package/src/build-errors.ts +0 -54
  293. package/src/jobs-drain.ts +0 -163
  294. package/src/scaffold-typecheck.ts +0 -227
  295. package/src/thrown-by.ts +0 -24
  296. package/src/verify-errors.ts +0 -58
@@ -1,227 +0,0 @@
1
- // Typechecks what `x new` and `x g` write, the way the user's own `tsc` will: real files on disk,
2
- // the real workspace packages, the real compiler. A template that merely parses has not been
3
- // checked — it has moved its failure from this gate to the first command the user runs.
4
-
5
- // `node:` and not Bun: Bun has no API for a temporary directory (`mkdtempSync` + `tmpdir`), for a
6
- // symlink (`symlinkSync`, how the sandbox borrows the workspace's node_modules), or for a
7
- // recursive delete (`rmSync`). `node:path` comes with them — `Bun.write` takes the joined path,
8
- // but only `resolve`/`sep` can prove that path stayed inside the sandbox.
9
- import { mkdtempSync, rmSync, symlinkSync } from 'node:fs';
10
- import { tmpdir } from 'node:os';
11
- import { join, resolve, sep } from 'node:path';
12
- import { ScaffoldPathEscapeError } from './errors';
13
- import type { Runner } from './exec';
14
- import { exec } from './exec';
15
- import { FIXTURE_APP, scaffoldFixture } from './scaffold-fixture';
16
- import type { GeneratedFile } from './templates';
17
-
18
- /** Derived from this file, never from cwd, so the harness works from any working directory. */
19
- export const workspaceRoot = (): string => resolve(import.meta.dir, '..', '..', '..');
20
-
21
- export interface TypeDiagnostic {
22
- /** Sandbox-relative path, or '' for a diagnostic the compiler raised about the project itself. */
23
- readonly file: string;
24
- readonly line: number;
25
- readonly code: string;
26
- readonly message: string;
27
- }
28
-
29
- const AT_FILE = /^(.+?)\((\d+),(\d+)\): error (TS\d+): (.*)$/;
30
- const PROJECT_WIDE = /^error (TS\d+): (.*)$/;
31
-
32
- /**
33
- * Config errors carry no file, so both shapes are collected: a silent harness is a green lie.
34
- * Split on `\r?\n`, because a trailing `\r` lands inside the message capture and no `KNOWN_GAPS`
35
- * entry would match it — a CRLF `tsc` would turn every pinned gap into an unexplained failure.
36
- */
37
- export function parseDiagnostics(output: string): readonly TypeDiagnostic[] {
38
- const found: TypeDiagnostic[] = [];
39
- for (const line of output.split(/\r?\n/)) {
40
- const at = AT_FILE.exec(line);
41
- if (at !== null) {
42
- found.push({
43
- file: at[1] ?? '',
44
- line: Number.parseInt(at[2] ?? '0', 10),
45
- code: at[4] ?? '',
46
- message: at[5] ?? '',
47
- });
48
- continue;
49
- }
50
- const wide = PROJECT_WIDE.exec(line.trim());
51
- if (wide !== null)
52
- found.push({ file: '', line: 0, code: wide[1] ?? '', message: wide[2] ?? '' });
53
- }
54
- return found;
55
- }
56
-
57
- export interface KnownGap {
58
- /** `ScaffoldVariant.name`. A pin bought by one invocation may not excuse another. */
59
- readonly variant: string;
60
- readonly code: string;
61
- /** Exact sandbox-relative path. A pattern would absolve the same bug in a file nobody pinned. */
62
- readonly file: string;
63
- /** Exact `tsc` message. Matched literally so a near-miss regression is not absorbed. */
64
- readonly message: string;
65
- /** Who fixes it, and why the template cannot. */
66
- readonly owner: string;
67
- }
68
-
69
- /**
70
- * Diagnostics a template cannot fix, pinned one occurrence at a time. Pinned, never ignored:
71
- * `unexpectedIn` fails on anything not listed here — including a second copy of a listed
72
- * diagnostic, because each entry is consumed by exactly one match — and `staleGapsIn` fails when
73
- * a listed entry stops reproducing, so an entry cannot outlive the bug it describes.
74
- *
75
- * Empty, and that is the point: `invariants:` is one callback over the whole list, so `c` is typed
76
- * from the entity's own columns and the six `TS18048 'c.title' is possibly 'undefined'` pins this
77
- * list used to carry are gone. A new entry here is a template shipping a diagnostic, which needs
78
- * an owner who can remove it — not a permanent exemption.
79
- */
80
- export const KNOWN_GAPS: readonly KnownGap[] = [];
81
-
82
- /**
83
- * The pins one invocation may spend. Another variant's pins are not its to spend. `gaps` is a
84
- * parameter for the same reason it is on `unexpectedIn` and `staleGapsIn`: with `KNOWN_GAPS`
85
- * legitimately empty, the bookkeeping is only testable against a list a test supplies.
86
- */
87
- export const gapsFor = (
88
- variant: string,
89
- gaps: readonly KnownGap[] = KNOWN_GAPS,
90
- ): readonly KnownGap[] => gaps.filter((gap) => gap.variant === variant);
91
-
92
- const matches = (diagnostic: TypeDiagnostic, gap: KnownGap): boolean =>
93
- diagnostic.code === gap.code &&
94
- diagnostic.file === gap.file &&
95
- diagnostic.message === gap.message;
96
-
97
- export interface GapPartition {
98
- /** Diagnostics no unconsumed `KNOWN_GAPS` entry accounts for. */
99
- readonly unexpected: readonly TypeDiagnostic[];
100
- /** Entries that found no match — the bug is fixed and the pin has to go. */
101
- readonly stale: readonly KnownGap[];
102
- }
103
-
104
- /**
105
- * One pass, first-fit, each pin spent once. Counting matters: two occurrences of a diagnostic
106
- * pinned once means a new regression is hiding behind an old bug, so the surplus is unexpected.
107
- */
108
- export function partitionDiagnostics(
109
- diagnostics: readonly TypeDiagnostic[],
110
- gaps: readonly KnownGap[] = KNOWN_GAPS,
111
- ): GapPartition {
112
- const budget = gaps.map((gap) => ({ gap, spent: false }));
113
- const unexpected: TypeDiagnostic[] = [];
114
- for (const entry of diagnostics) {
115
- const slot = budget.find((pin) => !pin.spent && matches(entry, pin.gap));
116
- if (slot === undefined) unexpected.push(entry);
117
- else slot.spent = true;
118
- }
119
- return { unexpected, stale: budget.filter((pin) => !pin.spent).map((pin) => pin.gap) };
120
- }
121
-
122
- /** Everything the gate refuses: a diagnostic no unconsumed `KNOWN_GAPS` entry accounts for. */
123
- export const unexpectedIn = (
124
- diagnostics: readonly TypeDiagnostic[],
125
- gaps: readonly KnownGap[] = KNOWN_GAPS,
126
- ): readonly TypeDiagnostic[] => partitionDiagnostics(diagnostics, gaps).unexpected;
127
-
128
- /** Entries that no longer reproduce — the bug is fixed and the pin has to go. */
129
- export const staleGapsIn = (
130
- diagnostics: readonly TypeDiagnostic[],
131
- gaps: readonly KnownGap[] = KNOWN_GAPS,
132
- ): readonly KnownGap[] => partitionDiagnostics(diagnostics, gaps).stale;
133
-
134
- /** Written beside the app's own tsconfig so the gate inherits every flag the app ships with. */
135
- const OVERLAY = 'tsconfig.scaffold-check.json';
136
-
137
- /**
138
- * The one thing the sandbox may change about the generated project: where its imports resolve.
139
- * `@ultimat3/*` points at workspace source instead of a published tarball, and the app's own
140
- * workspace packages resolve without a `bun install` that a sealed test could never run.
141
- */
142
- const overlay = (root: string, app: string): string =>
143
- `${JSON.stringify(
144
- {
145
- extends: './tsconfig.json',
146
- compilerOptions: {
147
- noEmit: true,
148
- paths: {
149
- // The subpath exports a generated app reaches for, spelled the way the packages' own
150
- // `exports` maps spell them — the wildcard below would read `ui/icons/zap` as a package
151
- // name, and `cli/serve` as `packages/cli/serve`. Longest prefix wins, so these are
152
- // consulted first. `@ultimat3/cli/serve` is what `x new`'s `apps/web/server.ts` imports.
153
- '@ultimat3/ui/icons/*': [`${root}/packages/ui/src/icons/glyphs/*`],
154
- '@ultimat3/query/client': [`${root}/packages/query/src/client`],
155
- '@ultimat3/render/server': [`${root}/packages/render/src/server`],
156
- '@ultimat3/cli/serve': [`${root}/packages/cli/src/serve-entry`],
157
- '@ultimat3/testing/test-types': [`${root}/packages/testing/src/test-types`],
158
- '@ultimat3/*': [`${root}/packages/*/src`],
159
- [`@${app}/web/*`]: ['./apps/web/*'],
160
- [`@${app}/admin/*`]: ['./apps/admin/*'],
161
- [`@${app}/*`]: ['./packages/*/src'],
162
- },
163
- },
164
- },
165
- null,
166
- 2,
167
- )}\n`;
168
-
169
- export interface TypecheckOptions {
170
- readonly files?: readonly GeneratedFile[];
171
- readonly app?: string;
172
- readonly runner?: Runner;
173
- /** Leave the sandbox on disk. For debugging a red gate by hand, never for the gate itself. */
174
- readonly keep?: boolean;
175
- }
176
-
177
- export interface TypecheckReport {
178
- readonly dir: string;
179
- readonly fileCount: number;
180
- readonly diagnostics: readonly TypeDiagnostic[];
181
- readonly output: string;
182
- }
183
-
184
- /**
185
- * `GeneratedFile.path` is documented as relative-POSIX, not enforced as it. A `..` segment would
186
- * put template output on the developer's real disk, so the sandbox proves containment before it
187
- * writes rather than after.
188
- */
189
- export function sandboxPath(dir: string, path: string): string {
190
- const target = resolve(dir, path);
191
- if (target !== dir && !target.startsWith(`${dir}${sep}`))
192
- throw new ScaffoldPathEscapeError({ path, dir });
193
- return target;
194
- }
195
-
196
- export async function typecheckScaffold(options: TypecheckOptions = {}): Promise<TypecheckReport> {
197
- const root = workspaceRoot();
198
- const app = options.app ?? FIXTURE_APP;
199
- const files = options.files ?? scaffoldFixture();
200
- const dir = mkdtempSync(join(tmpdir(), 'x-scaffold-'));
201
- try {
202
- for (const file of files) await Bun.write(sandboxPath(dir, file.path), file.contents);
203
- // The sandbox borrows the workspace's installed dependencies. The gate is about the
204
- // templates; whether a registry install succeeds is a different question, and a sealed
205
- // test cannot ask it.
206
- symlinkSync(join(root, 'node_modules'), join(dir, 'node_modules'), 'dir');
207
- await Bun.write(join(dir, OVERLAY), overlay(root, app));
208
- const result = await (options.runner ?? exec)(
209
- [join(root, 'node_modules', '.bin', 'tsc'), '--noEmit', '--pretty', 'false', '-p', OVERLAY],
210
- { cwd: dir },
211
- );
212
- const output = [result.stdout, result.stderr].filter((part) => part.length > 0).join('\n');
213
- return { dir, fileCount: files.length, diagnostics: parseDiagnostics(output), output };
214
- } finally {
215
- if (options.keep !== true) rmSync(dir, { recursive: true, force: true });
216
- }
217
- }
218
-
219
- /** One diagnostic per line, in the shape `tsc` prints — a failed gate is a runnable bug report. */
220
- export const formatDiagnostics = (diagnostics: readonly TypeDiagnostic[]): string =>
221
- diagnostics
222
- .map((entry) =>
223
- entry.file === ''
224
- ? `error ${entry.code}: ${entry.message}`
225
- : `${entry.file}:${entry.line} ${entry.code}: ${entry.message}`,
226
- )
227
- .join('\n');
package/src/thrown-by.ts DELETED
@@ -1,24 +0,0 @@
1
- // One `thrownBy` for every test in this package. What a caller acts on is the thrown *shape* —
2
- // the code, the cause and the fix — never the class, so a test that asserts on the class would
3
- // pass while the fix line rots. Shared rather than copied: a second copy asserts less, silently.
4
-
5
- import { expect } from 'bun:test';
6
-
7
- /** The three fields every `UltimateError` carries, as a test reads them off the thrown value. */
8
- export interface ThrownShape {
9
- readonly code?: string;
10
- readonly cause?: string;
11
- readonly fix?: string;
12
- }
13
-
14
- export function thrownBy(call: () => unknown): ThrownShape {
15
- try {
16
- call();
17
- } catch (error) {
18
- return error as ThrownShape;
19
- }
20
- // Not `throw new Error(...)`: a bare throw carries no code and no fix, and this is the one
21
- // failure a test author most needs named. `expect.unreachable` fails through the runner, which
22
- // prints the assertion the caller wrote instead of a stack from inside this helper.
23
- return expect.unreachable('expected a throw');
24
- }
@@ -1,58 +0,0 @@
1
- // The gate's sizing and CI-split refusals: a bad budget env var, an unsplittable `--shard`, and a
2
- // `x verify merge` part that is not a verify document. Split from errors.ts at its size ceiling.
3
-
4
- import { UltimateError } from '@ultimat3/core';
5
-
6
- /**
7
- * `ULTIMATE_TEST_MEMORY_BUDGET` or `ULTIMATE_TEST_MAX_WORKERS` set to something that does not
8
- * parse. Refused rather than ignored: a typo read as "the default" is a run sized for a machine
9
- * the caller said this is not.
10
- */
11
- export class TestBudgetInvalidError extends UltimateError {
12
- constructor(name: string, value: string) {
13
- super({
14
- code: 'X_TEST_BUDGET_INVALID',
15
- cause: `${name}=${JSON.stringify(value)} does not parse — ${
16
- name === 'ULTIMATE_TEST_MAX_WORKERS'
17
- ? 'expected a positive integer'
18
- : 'expected a size such as 3g, 512m or 4GiB'
19
- }`,
20
- fix:
21
- name === 'ULTIMATE_TEST_MAX_WORKERS'
22
- ? 'ULTIMATE_TEST_MAX_WORKERS=4 x verify'
23
- : 'ULTIMATE_TEST_MEMORY_BUDGET=3g x verify',
24
- });
25
- }
26
- }
27
-
28
- /**
29
- * The gate as an app types it. A refusal raised from another entry — `bun run verify` at the
30
- * framework root, where `x verify` answers `X_NOT_IN_APP` — passes its own spelling, so the
31
- * `fix:` runs where it was raised.
32
- */
33
- const GATE = 'x verify';
34
-
35
- /** `x verify --shard` asked for a split the gate cannot make. */
36
- export class VerifyShardInvalidError extends UltimateError {
37
- constructor(input: { reason: string; fix?: string; command?: string }) {
38
- const command = input.command ?? GATE;
39
- super({
40
- code: 'X_VERIFY_SHARD_INVALID',
41
- cause: `${command} --shard: ${input.reason}`,
42
- fix: input.fix ?? `${command} --only unit --shard 1/4 --json`,
43
- });
44
- }
45
- }
46
-
47
- /** A part handed to `x verify merge` that is not an `x verify --json` document. */
48
- export class VerifyMergeInputError extends UltimateError {
49
- constructor(input: { file: string; reason: string; sourceError?: unknown; command?: string }) {
50
- const command = input.command ?? GATE;
51
- super({
52
- code: 'X_VERIFY_MERGE_INPUT',
53
- cause: `${command} merge: ${input.file} ${input.reason}`,
54
- fix: `${command} --only unit --json > part.json # one document per part, the last line of stdout`,
55
- ...(input.sourceError === undefined ? {} : { sourceError: input.sourceError }),
56
- });
57
- }
58
- }