@ultimat3/cli 22.15.0 → 23.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 (169) hide show
  1. package/CLAUDE.md +23 -8
  2. package/README.md +28 -5
  3. package/package.json +29 -28
  4. package/src/admin-catalog-keys.ts +16 -0
  5. package/src/admin-registration.ts +192 -0
  6. package/src/api-registration.ts +36 -11
  7. package/src/app-boundaries.ts +6 -20
  8. package/src/app-load.ts +55 -11
  9. package/src/app-manifest.ts +14 -0
  10. package/src/app-permissions.ts +0 -0
  11. package/src/app-reload-graph.ts +7 -30
  12. package/src/app-transport.ts +219 -0
  13. package/src/boundary-findings.ts +16 -4
  14. package/src/browser-transport.ts +160 -0
  15. package/src/budgets.ts +25 -1
  16. package/src/cmd-build-spec.ts +10 -3
  17. package/src/cmd-build.ts +56 -9
  18. package/src/cmd-db.ts +60 -11
  19. package/src/cmd-doctor.ts +35 -11
  20. package/src/cmd-generate.ts +118 -17
  21. package/src/cmd-jobs-spec.ts +19 -3
  22. package/src/cmd-jobs.ts +120 -3
  23. package/src/cmd-new.ts +15 -1
  24. package/src/cmd-routes.ts +15 -1
  25. package/src/cmd-secrets-ring.ts +76 -0
  26. package/src/cmd-secrets-spec.ts +10 -2
  27. package/src/cmd-secrets.ts +48 -12
  28. package/src/cmd-tasks.ts +84 -15
  29. package/src/cmd-verify.ts +10 -2
  30. package/src/comment-safe.ts +16 -0
  31. package/src/coverage-floor.ts +334 -0
  32. package/src/coverage-lcov.ts +193 -0
  33. package/src/coverage-source.ts +136 -0
  34. package/src/db-object-drift.ts +30 -0
  35. package/src/db-replay-engine.ts +115 -0
  36. package/src/db-schema-dump.ts +150 -0
  37. package/src/db-schema-refresh.ts +122 -0
  38. package/src/dev-dashboard.ts +2 -2
  39. package/src/dev-route-table.ts +9 -0
  40. package/src/doctor-coverage.ts +39 -0
  41. package/src/doctor-guards.ts +54 -0
  42. package/src/doctor-sealed.ts +132 -0
  43. package/src/document-graph.ts +82 -0
  44. package/src/error-codes.ts +32 -0
  45. package/src/framework-schema.ts +15 -0
  46. package/src/generate-files.ts +76 -10
  47. package/src/generate-grant-findings.ts +76 -0
  48. package/src/generate-grants.ts +77 -3
  49. package/src/generate-kinds.ts +19 -0
  50. package/src/generate-write.ts +15 -27
  51. package/src/generated-imports.ts +59 -0
  52. package/src/guard-sources.ts +104 -0
  53. package/src/guards.ts +23 -7
  54. package/src/handle-registration.ts +217 -0
  55. package/src/i18n-registration.ts +36 -0
  56. package/src/image-prepare.ts +51 -8
  57. package/src/import-closure.ts +165 -0
  58. package/src/index.ts +11 -2
  59. package/src/island-store.ts +24 -16
  60. package/src/jobs-drain.ts +14 -7
  61. package/src/jobs-json.ts +50 -0
  62. package/src/jobs-report.ts +3 -0
  63. package/src/json-merge.ts +46 -0
  64. package/src/mcp-errors.ts +29 -1
  65. package/src/messages.ts +33 -0
  66. package/src/metrics-endpoint.ts +71 -24
  67. package/src/migration-extensions.ts +29 -0
  68. package/src/module-imports.ts +47 -0
  69. package/src/output.ts +18 -9
  70. package/src/permission-grants.ts +41 -9
  71. package/src/publish-closure.ts +101 -0
  72. package/src/role-load.ts +143 -0
  73. package/src/role-start.ts +18 -1
  74. package/src/role-wake.ts +45 -0
  75. package/src/runtime-admin.ts +141 -0
  76. package/src/runtime-hooks.ts +5 -2
  77. package/src/runtime-queue.ts +8 -1
  78. package/src/runtime-render.ts +6 -0
  79. package/src/runtime-services.ts +2 -1
  80. package/src/scaffold-typecheck.ts +1 -0
  81. package/src/schema-dump-drift.ts +64 -0
  82. package/src/serve-boot.ts +85 -157
  83. package/src/serve-prebuilt-paths.ts +19 -0
  84. package/src/serve-prebuilt.ts +84 -0
  85. package/src/serve-web.ts +164 -0
  86. package/src/serve.ts +6 -9
  87. package/src/server-barrels.ts +54 -0
  88. package/src/source-edit.ts +49 -0
  89. package/src/tasks-facts.ts +4 -1
  90. package/src/templates/action.ts +144 -11
  91. package/src/templates/admin-catalog.ts +58 -0
  92. package/src/templates/admin-page.ts +36 -8
  93. package/src/templates/admin.ts +57 -17
  94. package/src/templates/backfill.ts +121 -87
  95. package/src/templates/entity.ts +140 -44
  96. package/src/templates/guard-animated-layout-property.ts +4 -11
  97. package/src/templates/guard-bare-error.ts +9 -13
  98. package/src/templates/guard-focus-visible.ts +4 -11
  99. package/src/templates/guard-image-dimensions.ts +6 -6
  100. package/src/templates/guard-island-without-states.ts +2 -4
  101. package/src/templates/guard-raw-breakpoint.ts +210 -0
  102. package/src/templates/guard-raw-colour.ts +4 -11
  103. package/src/templates/guard-raw-length.ts +375 -0
  104. package/src/templates/guard-raw-motion.ts +213 -0
  105. package/src/templates/guard-raw-shadow.ts +187 -0
  106. package/src/templates/guard-raw-z-index.ts +166 -0
  107. package/src/templates/guard-repo-raw-sql.ts +345 -0
  108. package/src/templates/guard-semantic-interactive.ts +6 -6
  109. package/src/templates/guard-undeclared-custom-property.ts +217 -0
  110. package/src/templates/guard-undefined-style-class.ts +215 -0
  111. package/src/templates/guard-untranslated-string.ts +6 -6
  112. package/src/templates/guard-unzoned-date.ts +9 -13
  113. package/src/templates/index.ts +3 -2
  114. package/src/templates/island.ts +34 -51
  115. package/src/templates/job-body-test.ts +121 -0
  116. package/src/templates/job.ts +23 -8
  117. package/src/templates/policy.ts +16 -9
  118. package/src/templates/query.ts +242 -26
  119. package/src/templates/resource-create.ts +62 -18
  120. package/src/templates/resource-form-island.ts +58 -80
  121. package/src/templates/resource-page.ts +269 -0
  122. package/src/templates/resource-service.ts +96 -0
  123. package/src/templates/resource.ts +86 -61
  124. package/src/templates/route.ts +38 -29
  125. package/src/templates/scaffold-api.ts +55 -1
  126. package/src/templates/scaffold-app.ts +171 -51
  127. package/src/templates/scaffold-auth.ts +83 -19
  128. package/src/templates/scaffold-browser-client.ts +90 -0
  129. package/src/templates/scaffold-container.ts +16 -0
  130. package/src/templates/scaffold-dashboard-bare.ts +64 -2
  131. package/src/templates/scaffold-dashboard-example.ts +124 -31
  132. package/src/templates/scaffold-dashboard-shared.ts +1 -20
  133. package/src/templates/scaffold-dashboard.ts +1 -2
  134. package/src/templates/scaffold-db-client.ts +142 -0
  135. package/src/templates/scaffold-db-package.ts +90 -8
  136. package/src/templates/scaffold-docs.ts +50 -5
  137. package/src/templates/scaffold-guards.ts +54 -11
  138. package/src/templates/scaffold-http.ts +9 -7
  139. package/src/templates/scaffold-repo.ts +52 -2
  140. package/src/templates/scaffold-roles.ts +56 -9
  141. package/src/templates/scaffold-shell.ts +36 -70
  142. package/src/templates/scaffold-site.ts +45 -19
  143. package/src/templates/scaffold-ui-package.ts +18 -0
  144. package/src/templates/slice-foundation.ts +46 -8
  145. package/src/test-counts.ts +18 -2
  146. package/src/transport-calls.ts +105 -0
  147. package/src/unscanned-admin.ts +54 -0
  148. package/src/verify-checks.ts +26 -7
  149. package/src/verify-coverage-run.ts +190 -0
  150. package/src/verify-deadline.ts +256 -0
  151. package/src/verify-errors.ts +15 -6
  152. package/src/verify-floor.ts +36 -9
  153. package/src/verify-merge.ts +117 -24
  154. package/src/verify-progress.ts +25 -0
  155. package/src/verify-role-load-probe.ts +54 -0
  156. package/src/verify-role-load.ts +148 -0
  157. package/src/verify-run.ts +93 -35
  158. package/src/verify-shard.ts +20 -9
  159. package/src/verify-step.ts +42 -1
  160. package/src/verify-tests.ts +78 -6
  161. package/src/workspace-checks.ts +50 -8
  162. package/src/workspace-dep-edit.ts +127 -0
  163. package/src/cmd-dev-fixture.ts +0 -204
  164. package/src/dev-live-fixture.ts +0 -122
  165. package/src/policy-fixture.ts +0 -78
  166. package/src/realtime-browser-probe-fixture.ts +0 -9
  167. package/src/role-start-fixture.ts +0 -78
  168. package/src/scaffold-fixture.ts +0 -160
  169. package/src/templates/island-fixture.ts +0 -76
@@ -1,160 +0,0 @@
1
- // What the scaffold drift gate compiles: every documented `x new` invocation, with every `x g`
2
- // generator run on top of the one that carries an app. Separate from the compiler harness next to
3
- // it, because "which scaffolds exist" is a fact about the CLI's surface, not about running `tsc`.
4
-
5
- import type { GenerateOptions } from './cmd-generate';
6
- import { dedupe, generate } from './cmd-generate';
7
- import { planNewApp } from './cmd-new';
8
- import type { GeneratedFile } from './templates';
9
-
10
- /** The app the fixture scaffolds. Kebab, multi-word: single-word names hide casing bugs. */
11
- export const FIXTURE_APP = 'ledger-demo';
12
-
13
- /**
14
- * An `errors.ts` an author wrote: it declares what the slice throws, and not the generated name.
15
- * The generator's INPUT only — the sandbox's own `errors.ts` is the one the resource wrote — so it
16
- * carries no `X_*` code: a literal here would be one shipped source hands a reader, and the
17
- * registry rule would ask where it was registered.
18
- */
19
- export const HANDWRITTEN_ERRORS = `import { UltimateError } from '@ultimat3/core';
20
-
21
- export class LedgerClosedError extends UltimateError {}
22
- `;
23
-
24
- /**
25
- * A feature slice whose entity is NOT tenant-scoped — the shape `x g entity`'s own comment
26
- * describes for a single-tenant app, and never a shape the generator writes itself. The
27
- * generator's INPUT only, the same way `HANDWRITTEN_ERRORS` above is: `x g job`/`x g task` read
28
- * this from a real `entity.ts`, and a fixture that never exercised it compiled only the shape
29
- * that never failed.
30
- */
31
- export const HANDWRITTEN_ENTITY_NO_TENANT = `import { entity, text, uuid } from '@ultimat3/entity';
32
-
33
- export const shortLink = entity('short_links', {
34
- columns: { id: uuid().primaryKey(), url: text({ max: 2000 }) },
35
- });
36
-
37
- export type ShortLink = typeof shortLink.$row;
38
- `;
39
-
40
- /** The paired `repo.ts`: no `byId`, no `listByOrg` — nothing this slice's job may call. */
41
- export const HANDWRITTEN_REPO_NO_TENANT = `import { db, sql } from '@ultimat3/db';
42
- import type { ShortLink } from './entity';
43
-
44
- export async function list(limit = 50): Promise<readonly ShortLink[]> {
45
- return db().query<ShortLink>(sql\`select * from short_links order by url limit \${limit}\`);
46
- }
47
- `;
48
-
49
- /**
50
- * The `invoice` resource's own `entity.ts`, as `x g` would read it off disk after the resource
51
- * below ran — the generators writing INTO that slice are handed it, because a slice with no entity
52
- * now gets the neutral body and no invented table.
53
- */
54
- export const INVOICE_ENTITY = String(
55
- generate({ kind: 'entity', name: 'invoice' }).find((file) => file.path.endsWith('/entity.ts'))
56
- ?.contents,
57
- );
58
-
59
- /**
60
- * One realistic invocation of every generator, on top of `x new --example`. Names differ from
61
- * their feature on purpose: `x g query invoice --feature invoice` would collide with the entity
62
- * import, and a fixture that trips over its own naming stops testing the templates.
63
- *
64
- * `admin: true` on the resource is not decoration: `apps/web/app/<name>/admin/resource.ts` and its
65
- * test are templates no other invocation emits, and they cost nothing extra here because they land
66
- * in a sandbox that is compiled anyway.
67
- */
68
- export const FIXTURE_GENERATORS: readonly GenerateOptions[] = [
69
- { kind: 'resource', name: 'invoice', admin: true },
70
- { kind: 'entity', name: 'credit-note', feature: 'credit-note' },
71
- { kind: 'policy', name: 'credit-note', feature: 'credit-note' },
72
- { kind: 'action', name: 'send-invoice', feature: 'invoice', sliceEntity: INVOICE_ENTITY },
73
- { kind: 'mutator', name: 'rename-invoice', feature: 'invoice', sliceEntity: INVOICE_ENTITY },
74
- // The other shape both templates have: a slice whose `errors.ts` is the author's and declares
75
- // no `InvoiceNotFoundError`. The resource's own `errors.ts` still lands in the sandbox (it does
76
- // declare one), which is the point — this compiles the file `x g action` writes when it must
77
- // not import that class, beside the one it writes when it may.
78
- {
79
- kind: 'action',
80
- name: 'ping-invoice',
81
- feature: 'invoice',
82
- sliceErrors: HANDWRITTEN_ERRORS,
83
- sliceEntity: INVOICE_ENTITY,
84
- },
85
- {
86
- kind: 'mutator',
87
- name: 'touch-invoice',
88
- feature: 'invoice',
89
- sliceErrors: HANDWRITTEN_ERRORS,
90
- sliceEntity: INVOICE_ENTITY,
91
- },
92
- { kind: 'query', name: 'invoice-search', feature: 'invoice' },
93
- { kind: 'query', name: 'invoice-feed', feature: 'invoice', live: true },
94
- { kind: 'job', name: 'sweep-invoices', feature: 'invoice', sliceEntity: INVOICE_ENTITY },
95
- { kind: 'backfill', name: 'reindex-invoices', feature: 'invoice' },
96
- { kind: 'task', name: 'nightly-sweep', feature: 'invoice', sliceEntity: INVOICE_ENTITY },
97
- // The other shape both templates have: a feature whose entity names no tenant column, so the
98
- // job/task must not assume one — compiled here beside the tenant-scoped pair above, exactly as
99
- // `ping-invoice`/`touch-invoice` compile the action's other shape beside `send-invoice`.
100
- {
101
- kind: 'job',
102
- name: 'purge-orphans',
103
- feature: 'short-link',
104
- sliceEntity: HANDWRITTEN_ENTITY_NO_TENANT,
105
- sliceRepo: HANDWRITTEN_REPO_NO_TENANT,
106
- },
107
- {
108
- kind: 'task',
109
- name: 'nightly-purge',
110
- feature: 'short-link',
111
- sliceEntity: HANDWRITTEN_ENTITY_NO_TENANT,
112
- sliceRepo: HANDWRITTEN_REPO_NO_TENANT,
113
- },
114
- { kind: 'route', name: 'pricing', surface: 'site' },
115
- { kind: 'route', name: 'billing', surface: 'app' },
116
- // `--at`, pointed at the `site/` route above: an island's whole reason to exist is a 0kb page
117
- // that needs one interactive control, so the fixture places it where that is true.
118
- { kind: 'island', name: 'currency-picker', at: 'apps/web/site/pricing' },
119
- // The one screen the admin derives from nothing — and the one generator that must NOT emit a
120
- // `defineRoute`, so compiling it is how that stays true.
121
- { kind: 'admin:page', name: 'reconcile', permission: 'ledger:reconcile' },
122
- // The app's own convention. It is the only generated file that imports `@ultimat3/cli` for its
123
- // types, so compiling it is what proves a scaffolded app can actually write one.
124
- { kind: 'guard', name: 'migration-safety' },
125
- ];
126
-
127
- /** The whole scaffolded surface: a new app, then every generator run inside it. */
128
- export function scaffoldFixture(): readonly GeneratedFile[] {
129
- return dedupe([
130
- ...planNewApp({ name: FIXTURE_APP, example: true }),
131
- ...FIXTURE_GENERATORS.flatMap((options) => generate(options)),
132
- ]);
133
- }
134
-
135
- export interface ScaffoldVariant {
136
- /** Names the invocation in a failure, so a red gate says which `x new` broke. */
137
- readonly name: string;
138
- /** What this variant emits that no other one does — why it earns its own compile. */
139
- readonly why: string;
140
- readonly files: readonly GeneratedFile[];
141
- }
142
-
143
- /**
144
- * Every scaffold a user can ask for, each compiled on its own. One variant per *file set*, not per
145
- * flag: `--no-example` writes a different `packages/db` than `--example` does, and compiling only
146
- * the example app is what let `x new --no-example` ship a `schema.ts` importing a slice that
147
- * invocation never writes.
148
- */
149
- export const scaffoldVariants = (): readonly ScaffoldVariant[] => [
150
- {
151
- name: 'x new',
152
- why: 'the example slice, plus one run of every generator on top of it',
153
- files: scaffoldFixture(),
154
- },
155
- {
156
- name: 'x new --no-example',
157
- why: 'an empty app/: no entity, so schema, seed and the initial migration have nothing to name',
158
- files: planNewApp({ name: FIXTURE_APP, example: false }),
159
- },
160
- ];
@@ -1,76 +0,0 @@
1
- // TEST-ONLY. An app root on disk holding emitted files, with the packages an island imports
2
- // resolvable BY SPECIFIER the way a real app resolves them — so a template that emits
3
- // `import { Button } from '@ultimat3/ui'` is proven by a build that actually resolves it, and not
4
- // by a string assertion that the import is present.
5
-
6
- // `node:` by necessity, and SYNC by necessity: `[Symbol.dispose]` cannot await, so the teardown
7
- // half has to be synchronous — and Bun ships neither a path API nor a `symlink`.
8
- import { mkdirSync, rmSync, symlinkSync } from 'node:fs';
9
- import { dirname, join } from 'node:path';
10
- import type { GeneratedFile } from './naming';
11
-
12
- /** `packages/cli/src/templates` → the repo root, four hops up. */
13
- const REPO_ROOT = join(import.meta.dir, '..', '..', '..', '..');
14
-
15
- /**
16
- * INSIDE the checkout, and measured rather than chosen: the identical fixture under `os.tmpdir()`
17
- * fails every `@ultimat3/*` and every relative import inside it with `Could not resolve`, because
18
- * `Bun.build`'s resolver is scoped to the project `bun test` was started in and an app root outside
19
- * it cannot reach its own `node_modules`. `.prerender-fixture` is the same shape for the same
20
- * reason. The leading dot keeps it out of every `tsc` wildcard include.
21
- */
22
- const FIXTURE_ROOT = join(REPO_ROOT, 'packages', 'cli', '.island-fixture');
23
-
24
- /** The package the fixture lives inside. Linking it would aim a symlink at its own ancestor. */
25
- const SELF = 'cli';
26
-
27
- export interface FixtureApp extends Disposable {
28
- /** Absolute path of the app root — what `buildIslands` globs from. */
29
- readonly path: string;
30
- }
31
-
32
- /**
33
- * Symlinks rather than a `bun install`: the emitted island must resolve THIS working copy of
34
- * `@ultimat3/ui`, and an install in a fixture directory would fetch the registry's last release
35
- * and quietly prove nothing about the change under test. Every workspace is linked, not a chosen
36
- * few — a template that grows an import should build, not fail on a list nobody updated.
37
- */
38
- function linkDependencies(root: string): void {
39
- const scope = join(root, 'node_modules', '@ultimat3');
40
- mkdirSync(scope, { recursive: true });
41
- const packages = join(REPO_ROOT, 'packages');
42
- for (const entry of new Bun.Glob('*/package.json').scanSync({ cwd: packages })) {
43
- const name = entry.slice(0, entry.indexOf('/'));
44
- if (name === SELF) continue;
45
- symlinkSync(join(packages, name), join(scope, name), 'dir');
46
- }
47
- // Resolved, never spelled as a path: the installer's layout is its own business and a hardcoded
48
- // `node_modules/solid-js` is a fixture that breaks on a linker change rather than on a real one.
49
- symlinkSync(
50
- dirname(Bun.resolveSync('solid-js/package.json', REPO_ROOT)),
51
- join(root, 'node_modules', 'solid-js'),
52
- 'dir',
53
- );
54
- }
55
-
56
- /**
57
- * `Disposable`, so the idiom is `using root = await fixtureAppRoot(label, files)`. `label` is the
58
- * caller's, and is what keeps two test FILES off one directory: the path is fixed rather than
59
- * random, because a random one cannot be named in `.gitignore` and a crashed run leaves it behind.
60
- */
61
- export async function fixtureAppRoot(
62
- label: string,
63
- files: readonly GeneratedFile[],
64
- ): Promise<FixtureApp> {
65
- const path = join(FIXTURE_ROOT, label);
66
- rmSync(path, { recursive: true, force: true });
67
- mkdirSync(path, { recursive: true });
68
- linkDependencies(path);
69
- for (const file of files) await Bun.write(join(path, file.path), String(file.contents));
70
- return {
71
- path,
72
- [Symbol.dispose]: (): void => {
73
- rmSync(path, { recursive: true, force: true });
74
- },
75
- };
76
- }