@metaobjectsdev/codegen-ts 1.0.2 → 1.0.4-rc.1

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 (94) hide show
  1. package/dist/catalog-gates.d.ts +52 -0
  2. package/dist/catalog-gates.d.ts.map +1 -0
  3. package/dist/catalog-gates.js +183 -0
  4. package/dist/catalog-gates.js.map +1 -0
  5. package/dist/constants.d.ts +24 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +26 -0
  8. package/dist/constants.js.map +1 -1
  9. package/dist/enum-shared.d.ts +9 -2
  10. package/dist/enum-shared.d.ts.map +1 -1
  11. package/dist/enum-shared.js +12 -3
  12. package/dist/enum-shared.js.map +1 -1
  13. package/dist/generator-registry.d.ts +73 -0
  14. package/dist/generator-registry.d.ts.map +1 -1
  15. package/dist/generator-registry.js +182 -38
  16. package/dist/generator-registry.js.map +1 -1
  17. package/dist/generator.d.ts +48 -1
  18. package/dist/generator.d.ts.map +1 -1
  19. package/dist/generator.js.map +1 -1
  20. package/dist/generators/entity-file.d.ts.map +1 -1
  21. package/dist/generators/entity-file.js +4 -1
  22. package/dist/generators/entity-file.js.map +1 -1
  23. package/dist/generators/index.d.ts +1 -0
  24. package/dist/generators/index.d.ts.map +1 -1
  25. package/dist/generators/index.js +5 -0
  26. package/dist/generators/index.js.map +1 -1
  27. package/dist/generators/shared-model-file.d.ts +23 -0
  28. package/dist/generators/shared-model-file.d.ts.map +1 -0
  29. package/dist/generators/shared-model-file.js +235 -0
  30. package/dist/generators/shared-model-file.js.map +1 -0
  31. package/dist/generators/trace-helper-file.d.ts.map +1 -1
  32. package/dist/generators/trace-helper-file.js +54 -11
  33. package/dist/generators/trace-helper-file.js.map +1 -1
  34. package/dist/index.d.ts +5 -4
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +6 -3
  37. package/dist/index.js.map +1 -1
  38. package/dist/metaobjects-config.d.ts +0 -15
  39. package/dist/metaobjects-config.d.ts.map +1 -1
  40. package/dist/metaobjects-config.js.map +1 -1
  41. package/dist/projection/extract-view-spec.d.ts.map +1 -1
  42. package/dist/projection/extract-view-spec.js +111 -4
  43. package/dist/projection/extract-view-spec.js.map +1 -1
  44. package/dist/relation-resolver.d.ts.map +1 -1
  45. package/dist/relation-resolver.js +11 -9
  46. package/dist/relation-resolver.js.map +1 -1
  47. package/dist/routes-expose.d.ts +30 -0
  48. package/dist/routes-expose.d.ts.map +1 -1
  49. package/dist/routes-expose.js +46 -0
  50. package/dist/routes-expose.js.map +1 -1
  51. package/dist/runner.d.ts +29 -0
  52. package/dist/runner.d.ts.map +1 -1
  53. package/dist/runner.js +38 -1
  54. package/dist/runner.js.map +1 -1
  55. package/dist/templates/entity-file.js +2 -2
  56. package/dist/templates/entity-file.js.map +1 -1
  57. package/dist/templates/enums-file.d.ts +11 -1
  58. package/dist/templates/enums-file.d.ts.map +1 -1
  59. package/dist/templates/enums-file.js +10 -2
  60. package/dist/templates/enums-file.js.map +1 -1
  61. package/dist/templates/queries-file.js +5 -5
  62. package/dist/templates/queries-file.js.map +1 -1
  63. package/dist/templates/routes-file-hono.d.ts.map +1 -1
  64. package/dist/templates/routes-file-hono.js +10 -3
  65. package/dist/templates/routes-file-hono.js.map +1 -1
  66. package/dist/templates/routes-file.d.ts.map +1 -1
  67. package/dist/templates/routes-file.js +21 -4
  68. package/dist/templates/routes-file.js.map +1 -1
  69. package/dist/templates/value-object-file.js +2 -2
  70. package/dist/templates/value-object-file.js.map +1 -1
  71. package/package.json +6 -6
  72. package/src/catalog-gates.ts +222 -0
  73. package/src/constants.ts +27 -0
  74. package/src/enum-shared.ts +17 -3
  75. package/src/generator-registry.ts +253 -41
  76. package/src/generator.ts +48 -1
  77. package/src/generators/entity-file.ts +7 -1
  78. package/src/generators/index.ts +11 -0
  79. package/src/generators/shared-model-file.ts +292 -0
  80. package/src/generators/trace-helper-file.ts +55 -12
  81. package/src/index.ts +17 -3
  82. package/src/metaobjects-config.ts +0 -15
  83. package/src/projection/extract-view-spec.ts +126 -5
  84. package/src/reference/entity.ts +12 -3
  85. package/src/reference/queries.ts +2 -2
  86. package/src/relation-resolver.ts +14 -8
  87. package/src/routes-expose.ts +57 -0
  88. package/src/runner.ts +77 -1
  89. package/src/templates/entity-file.ts +2 -2
  90. package/src/templates/enums-file.ts +13 -2
  91. package/src/templates/queries-file.ts +5 -5
  92. package/src/templates/routes-file-hono.ts +10 -3
  93. package/src/templates/routes-file.ts +21 -4
  94. package/src/templates/value-object-file.ts +2 -2
@@ -10,6 +10,13 @@
10
10
  // factory-array config keeps working unchanged — the registry powers `--list`
11
11
  // and a stable identity, it does not replace the config path.
12
12
  //
13
+ // **This is a SLICE, not the whole catalog.** `codegen-ts` cannot import
14
+ // `codegen-ts-react` / `codegen-ts-tanstack` (dependency direction), so those two
15
+ // packages export their own slices and the CLI unions all three
16
+ // (`cli/src/lib/catalog.ts`). Set equality against the cross-port manifest is a
17
+ // property of the COMPOSED catalog and is asserted there; this package's own
18
+ // conformance test can only check one direction — no rogue names.
19
+ //
13
20
  // Tiering (ADR-0020 / ADR-0021 D1):
14
21
  // - "native" — the recommended Tier-1 `meta gen` suite (idiomatic emission).
15
22
  // - "neutral" — Tier-2 artifacts owned by the neutral docs engine. `docs` and
@@ -18,6 +25,8 @@
18
25
  // door for documentation is `meta docs` (D1).
19
26
 
20
27
  import type { Generator } from "./generator.js";
28
+ import type { MetaobjectsGenConfig } from "./metaobjects-config.js";
29
+ import { REFERENCE_GENERATOR_NAMES } from "./reference-templates.js";
21
30
  // The four ADR-0034 ownable generators are no longer exported from ./generators/index.js
22
31
  // (1.0 removed them — see that file's header). They remain the engine's internal composers
23
32
  // and the stable-name registry still constructs them, so import them from their own modules.
@@ -39,13 +48,56 @@ import {
39
48
  docsFile,
40
49
  templateGenerator,
41
50
  traceHelperFile,
51
+ sharedModelFile,
52
+ requirementTests,
42
53
  } from "./generators/index.js";
43
54
 
44
55
  export type GeneratorTier = "native" | "neutral";
45
56
 
57
+ /**
58
+ * The six layers a generator can belong to — the axis an adopter SELECTS BY.
59
+ *
60
+ * Cross-port: `layer` is gated by every port's registry-conformance test against
61
+ * `fixtures/generator-registry-conformance/registry.json`, exactly as `tier` is, so a
62
+ * polyglot agent groups the catalog by the same words everywhere.
63
+ *
64
+ * Six, not ten. The first four are app-shape decisions a builder makes; `docs` is on by
65
+ * default (`meta docs`); `capability` holds the ones the MODEL has already made — nobody
66
+ * picks `prompt-render` by browsing a taxonomy, they pick it because they declared a
67
+ * `template.prompt`, which `meta gen --list --probe` reports with a real file count. A
68
+ * layer with one member does no grouping work, so do not split `capability` to tidy it.
69
+ */
70
+ export const GENERATOR_LAYERS = [
71
+ "model",
72
+ "persistence",
73
+ "api",
74
+ "client",
75
+ "docs",
76
+ "capability",
77
+ ] as const;
78
+ export type Layer = (typeof GENERATOR_LAYERS)[number];
79
+
80
+ /**
81
+ * The framework a generator's OUTPUT targets. Absent = framework-neutral.
82
+ *
83
+ * Exclusivity is an ADVISORY and only on the `api` layer (spec §8a): `routes` and
84
+ * `routes-hono` emit to different paths, so wiring both is legal and silently produces
85
+ * two HTTP surfaces — worth a warning, never an error, because migrating between them is
86
+ * legitimate. There is NO general per-layer rule: `@metaobjectsdev/tanstack` peers on
87
+ * `react`, so `form` (react) + `hooks`/`grid` (tanstack) is the intended composition.
88
+ */
89
+ export type GeneratorFramework = "fastify" | "hono" | "react" | "tanstack";
90
+
46
91
  export interface GeneratorRegistryEntry {
47
92
  /** Stable, cross-port-consistent id. Equals the registry map key. */
48
93
  name: string;
94
+ /**
95
+ * Catalog discriminator. Today every entry is a generator; FR-043 adds
96
+ * `kind: "library"` rows to the same table rather than building a parallel one.
97
+ */
98
+ kind: "generator";
99
+ /** The selection axis — see {@link GENERATOR_LAYERS}. Gated cross-port. */
100
+ layer: Layer;
49
101
  /** One-line (no newline) human description for `--list`. */
50
102
  description: string;
51
103
  /** "native" = recommended `meta gen` suite; "neutral" = `meta docs`-owned. */
@@ -54,10 +106,55 @@ export interface GeneratorRegistryEntry {
54
106
  factory: () => Generator;
55
107
  /** Optional one-line options summary for `--list`. */
56
108
  options?: string;
109
+ /** The framework this generator's OUTPUT targets. Absent = framework-neutral. */
110
+ framework?: GeneratorFramework;
111
+ /**
112
+ * Stable names whose emitted output THIS generator's output imports.
113
+ *
114
+ * Resolved, not trusted: a gate dry-runs every generator, resolves each emitted
115
+ * RELATIVE import back to whichever generator emits that path, and asserts the result
116
+ * is a subset of this list — so a generator that quietly starts depending on `entity`
117
+ * cannot ship claiming it depends on nothing.
118
+ */
119
+ requires?: readonly string[];
120
+ /**
121
+ * The `@metaobjectsdev` runtime packages the EMITTED code imports.
122
+ *
123
+ * PLURAL, against the design's first sketch, because the emitted code is: an
124
+ * `output-parser` module imports `@metaobjectsdev/metadata`, `/render` AND
125
+ * `/runtime-ts`, and a `hooks` module imports `/runtime-web` and `/tanstack`. A
126
+ * singular field would have forced two of the three to go undeclared, which is the
127
+ * exact under-declaration the gate exists to catch. Resolved, not trusted — the
128
+ * emitted files are read back and every `@metaobjectsdev` import must appear here.
129
+ */
130
+ runtimePackages?: readonly string[];
131
+ /**
132
+ * Third-party packages the EMITTED code imports.
133
+ *
134
+ * Per generator rather than derived from {@link runtimePackages}, because
135
+ * `@metaobjectsdev/runtime-ts`'s peers are a union (drizzle-orm, fastify, hono, kysely,
136
+ * zod) — deriving per package would tell someone ejecting `entity` to install both
137
+ * Fastify and Hono. Version RANGES are read from the runtime package's own
138
+ * `peerDependencies`, never written here. Gated the same way `requires` is.
139
+ */
140
+ runtimePeers?: readonly string[];
141
+ /**
142
+ * Config keys this generator reads. Typed against the real config so a renamed key
143
+ * breaks the build instead of going quietly stale — the same "resolved, not trusted"
144
+ * doctrine as `requires`, obtained here for free from the type system.
145
+ */
146
+ configKeys?: readonly (keyof MetaobjectsGenConfig)[];
147
+ /** True iff this package ships a `src/reference/<name>.ts` for `meta eject`. */
148
+ ejectable: boolean;
57
149
  /** Optional note — used to point neutral entries at their canonical door. */
58
150
  note?: string;
59
151
  }
60
152
 
153
+ /** True iff this package ships a copyable reference template for `name`. */
154
+ function ejectable(name: string): boolean {
155
+ return (REFERENCE_GENERATOR_NAMES as readonly string[]).includes(name);
156
+ }
157
+
61
158
  // The `template` generator is a PRIMITIVE: callers supply { name, walk,
62
159
  // template }. For registry identity + `--list` we expose a no-op default so the
63
160
  // factory constructs a valid Generator without throwing; real use passes opts
@@ -72,129 +169,244 @@ function templatePrimitive(): Generator {
72
169
  }
73
170
 
74
171
  export const generatorRegistry: Record<string, GeneratorRegistryEntry> = {
75
- // ----- Tier-1 native suite (idiomatic per-port emission) -----------------
172
+ // ----- model ------------------------------------------------------------
76
173
  entity: {
77
174
  name: "entity",
175
+ kind: "generator",
176
+ layer: "model",
78
177
  description: "Per-entity Drizzle table + typed model module (the entity module).",
79
178
  tier: "native",
80
179
  factory: () => entityFile(),
81
180
  options: "filter?, target?",
181
+ requires: [],
182
+ runtimePackages: ["@metaobjectsdev/runtime-ts"],
183
+ runtimePeers: ["drizzle-orm", "zod"],
184
+ configKeys: ["dialect", "extStyle", "outputLayout", "columnNamingStrategy",
185
+ "pluralizeCollections", "collectionNameOverrides", "providedEnumModule", "apiPrefix"],
186
+ ejectable: ejectable("entity"),
187
+ },
188
+ names: {
189
+ name: "names",
190
+ kind: "generator",
191
+ layer: "model",
192
+ description: "Per-entity physical database name constants (table/view, schema, columns).",
193
+ tier: "native",
194
+ factory: () => namesFile(),
195
+ options: "filter?, target?",
196
+ configKeys: ["columnNamingStrategy"],
197
+ ejectable: ejectable("names"),
82
198
  },
199
+ barrel: {
200
+ name: "barrel",
201
+ kind: "generator",
202
+ layer: "model",
203
+ description: "Single index.ts re-exporting every generated entity module.",
204
+ tier: "native",
205
+ factory: () => barrel(),
206
+ options: "target?",
207
+ requires: ["entity"],
208
+ configKeys: ["extStyle"],
209
+ ejectable: ejectable("barrel"),
210
+ },
211
+
212
+ // ----- persistence ------------------------------------------------------
83
213
  queries: {
84
214
  name: "queries",
215
+ kind: "generator",
216
+ layer: "persistence",
85
217
  description: "Per-entity typed query helpers (findById/create/...).",
86
218
  tier: "native",
87
219
  factory: () => queriesFile(),
88
220
  options: "filter?, target?",
221
+ requires: ["entity"],
222
+ runtimePeers: ["drizzle-orm"],
223
+ configKeys: ["dialect", "extStyle", "pluralizeCollections", "collectionNameOverrides"],
224
+ ejectable: ejectable("queries"),
89
225
  },
90
- callable: {
91
- name: "callable",
92
- description: "Per-entity callable/service surface wrapping the query helpers.",
93
- tier: "native",
94
- factory: () => callableFile(),
95
- options: "filter?, target?",
96
- },
226
+
227
+ // ----- api --------------------------------------------------------------
97
228
  routes: {
98
229
  name: "routes",
230
+ kind: "generator",
231
+ layer: "api",
99
232
  description: "Per-entity Fastify CRUD routes (drizzle-fastify mountCrudRoutes).",
100
233
  tier: "native",
101
234
  factory: () => routesFile(),
102
235
  options: "filter?, target?",
236
+ framework: "fastify",
237
+ requires: ["entity"],
238
+ runtimePackages: ["@metaobjectsdev/runtime-ts"],
239
+ runtimePeers: ["fastify"],
240
+ configKeys: ["dbImport", "apiPrefix", "dialect", "extStyle", "outputLayout", "columnNamingStrategy"],
241
+ ejectable: ejectable("routes"),
103
242
  },
104
243
  "routes-hono": {
105
244
  name: "routes-hono",
245
+ kind: "generator",
246
+ layer: "api",
106
247
  description: "Per-entity Hono CRUD routes (runtime-ts/hono mountCrudRoutes).",
107
248
  tier: "native",
108
249
  factory: () => routesFileHono(),
109
250
  options: "filter?, target?",
251
+ framework: "hono",
252
+ requires: ["entity"],
253
+ runtimePackages: ["@metaobjectsdev/runtime-ts"],
254
+ runtimePeers: ["hono"],
255
+ configKeys: ["dbImport", "apiPrefix", "dialect", "extStyle"],
256
+ ejectable: ejectable("routes-hono"),
110
257
  },
111
- barrel: {
112
- name: "barrel",
113
- description: "Single index.ts re-exporting every generated entity module.",
258
+
259
+ // ----- docs -------------------------------------------------------------
260
+ "api-docs": {
261
+ name: "api-docs",
262
+ kind: "generator",
263
+ layer: "docs",
264
+ description:
265
+ "Per-entity/template SDK API reference (the generated code's API, human + agent forms).",
114
266
  tier: "native",
115
- factory: () => barrel(),
116
- options: "target?",
267
+ factory: () => apiDocsFile(),
268
+ options: "filter?, target?",
269
+ ejectable: ejectable("api-docs"),
117
270
  },
118
- names: {
119
- name: "names",
120
- description: "Per-entity physical database name constants (table/view, schema, columns).",
271
+ docs: {
272
+ name: "docs",
273
+ kind: "generator",
274
+ layer: "docs",
275
+ description: "Neutral per-entity / per-template Markdown documentation pages.",
276
+ tier: "neutral",
277
+ factory: () => docsFile(),
278
+ ejectable: ejectable("docs"),
279
+ note: "neutral artifact — use `meta docs` (the single docs door, ADR-0021 D1); not part of the recommended `meta gen` native suite.",
280
+ },
281
+ "mermaid-er": {
282
+ name: "mermaid-er",
283
+ kind: "generator",
284
+ layer: "docs",
285
+ description: "Mermaid ER diagram of the entity/relationship model.",
286
+ tier: "neutral",
287
+ factory: () => mermaidErDiagram(),
288
+ ejectable: ejectable("mermaid-er"),
289
+ note: "neutral artifact owned by the docs engine (ADR-0020); surfaced via `meta docs`, not the recommended `meta gen` native suite.",
290
+ },
291
+
292
+ // ----- capability -------------------------------------------------------
293
+ // Not chosen by browsing this list — chosen because the MODEL already asks for
294
+ // them. `meta gen --list --probe` reports a real file count per entry.
295
+ callable: {
296
+ name: "callable",
297
+ kind: "generator",
298
+ layer: "capability",
299
+ description: "Per-entity callable/service surface wrapping the query helpers.",
121
300
  tier: "native",
122
- factory: () => namesFile(),
301
+ factory: () => callableFile(),
123
302
  options: "filter?, target?",
303
+ requires: ["entity"],
304
+ runtimePeers: ["drizzle-orm"],
305
+ configKeys: ["columnNamingStrategy"],
306
+ ejectable: ejectable("callable"),
124
307
  },
125
308
  "prompt-render": {
126
309
  name: "prompt-render",
310
+ kind: "generator",
311
+ layer: "capability",
127
312
  description: "Per-template prompt-render helper over the render engine.",
128
313
  tier: "native",
129
314
  factory: () => promptRender(),
130
315
  options: "filter?, target?",
316
+ runtimePackages: ["@metaobjectsdev/render"],
317
+ ejectable: ejectable("prompt-render"),
131
318
  },
132
319
  "output-parser": {
133
320
  name: "output-parser",
321
+ kind: "generator",
322
+ layer: "capability",
134
323
  description: "Per-template tolerant output parser (recover-on-receipt).",
135
324
  tier: "native",
136
325
  factory: () => outputParser(),
137
326
  options: "filter?, target?",
327
+ runtimePackages: ["@metaobjectsdev/metadata", "@metaobjectsdev/render", "@metaobjectsdev/runtime-ts"],
328
+ runtimePeers: ["zod"],
329
+ ejectable: ejectable("output-parser"),
138
330
  },
139
331
  extractor: {
140
332
  name: "extractor",
333
+ kind: "generator",
334
+ layer: "capability",
141
335
  description: "Per-template typed extract<Name> helper (strict payload extraction).",
142
336
  tier: "native",
143
337
  factory: () => extractor(),
144
338
  options: "filter?, target?",
339
+ requires: ["entity", "output-parser"],
340
+ runtimePackages: ["@metaobjectsdev/metadata", "@metaobjectsdev/render"],
341
+ ejectable: ejectable("extractor"),
145
342
  },
146
343
  "output-prompt": {
147
344
  name: "output-prompt",
345
+ kind: "generator",
346
+ layer: "capability",
148
347
  description: "Per-template output-format prompt fragment generator.",
149
348
  tier: "native",
150
349
  factory: () => outputPrompt(),
151
350
  options: "filter?, target?",
351
+ runtimePackages: ["@metaobjectsdev/render"],
352
+ ejectable: ejectable("output-prompt"),
152
353
  },
153
354
  "render-helper": {
154
355
  name: "render-helper",
356
+ kind: "generator",
357
+ layer: "capability",
155
358
  description: "Per-template.output render helper (document/email typed wrappers).",
156
359
  tier: "native",
157
360
  factory: () => renderHelper(),
158
361
  options: "filter?, target?",
362
+ runtimePackages: ["@metaobjectsdev/render"],
363
+ configKeys: ["extStyle"],
364
+ ejectable: ejectable("render-helper"),
159
365
  },
160
366
  template: {
161
367
  name: "template",
368
+ kind: "generator",
369
+ layer: "capability",
162
370
  description: "Generic Mustache template primitive (walk + template → files).",
163
371
  tier: "native",
164
372
  factory: () => templatePrimitive(),
165
373
  options: "name, walk, template, format?, filter?, provider?, target?",
374
+ ejectable: ejectable("template"),
166
375
  },
167
- "api-docs": {
168
- name: "api-docs",
169
- description:
170
- "Per-entity/template SDK API reference (the generated code's API, human + agent forms).",
171
- tier: "native",
172
- factory: () => apiDocsFile(),
173
- options: "filter?, target?",
174
- },
175
-
176
376
  "trace-helper": {
177
377
  name: "trace-helper",
178
- description: "Per-entity typed record<Entity>/call<Entity> trace helpers (extract + buildLlmCallRow + persist; LlmCallBase-derived entities only).",
378
+ kind: "generator",
379
+ layer: "capability",
380
+ description:
381
+ "Per-entity typed record<Entity>/call<Entity> trace helpers (extract + buildLlmCallRow + persist; LlmCallBase-derived entities only).",
179
382
  tier: "native",
180
383
  factory: () => traceHelperFile(),
181
384
  options: "outDir?, target?",
385
+ ejectable: ejectable("trace-helper"),
182
386
  },
183
-
184
- // ----- Tier-2 neutral (owned by the `meta docs` engine — D1 / ADR-0020) ---
185
- docs: {
186
- name: "docs",
187
- description: "Neutral per-entity / per-template Markdown documentation pages.",
188
- tier: "neutral",
189
- factory: () => docsFile(),
190
- note: "neutral artifact — use `meta docs` (the single docs door, ADR-0021 D1); not part of the recommended `meta gen` native suite.",
387
+ "requirement-tests": {
388
+ name: "requirement-tests",
389
+ kind: "generator",
390
+ layer: "capability",
391
+ description: "Per-requirement test stub, one per requirement.functional claim in the ledger.",
392
+ tier: "native",
393
+ factory: () => requirementTests(),
394
+ options: "filter?, target?",
395
+ ejectable: ejectable("requirement-tests"),
191
396
  },
192
- "mermaid-er": {
193
- name: "mermaid-er",
194
- description: "Mermaid ER diagram of the entity/relationship model.",
195
- tier: "neutral",
196
- factory: () => mermaidErDiagram(),
197
- note: "neutral artifact owned by the docs engine (ADR-0020); surfaced via `meta docs`, not the recommended `meta gen` native suite.",
397
+ "shared-model": {
398
+ name: "shared-model",
399
+ kind: "generator",
400
+ layer: "capability",
401
+ description:
402
+ "FR-023: a publisher's flattened shared-model artifact + manifest for a consumer's `meta deps sync`.",
403
+ tier: "native",
404
+ // `name`/`include` are required at run time (an empty include matches
405
+ // everything, so a placeholder here constructs without throwing — real use
406
+ // always supplies both, same as `template`'s templatePrimitive() above).
407
+ factory: () => sharedModelFile({ name: "shared-model", include: [] }),
408
+ options: "name, include, exclude?, files?, version?, target?",
409
+ ejectable: ejectable("shared-model"),
198
410
  },
199
411
  };
200
412
 
package/src/generator.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { MetaObject, MetaRoot } from "@metaobjectsdev/metadata";
1
+ import type { MetaObject, MetaRoot, TypeRegistry } from "@metaobjectsdev/metadata";
2
+ import type { LibraryManifest } from "@metaobjectsdev/metadata/library";
2
3
  import type { RenderContext } from "./render-context.js";
3
4
  import type { ResolvedGenConfig } from "./metaobjects-config.js";
4
5
  import type { OrphanPolicy } from "./reconcile-orphans.js";
@@ -20,6 +21,16 @@ export interface GenContext {
20
21
  * filter is set). Always call this from helpers; do not call generator.filter
21
22
  * directly. */
22
23
  matches: (entity: MetaObject) => boolean;
24
+ /** FR-023 §11.1 item 2 — the model-wide output-scope predicate over a node's
25
+ * `resolutionKey()` (the runner's own `RunGenOpts.scope`, verbatim). A
26
+ * DIFFERENT knob from `matches`: `matches` is the per-generator `filter`
27
+ * (ANDed into what THIS generator emits), while `select` is the whole-model
28
+ * scope every generator shares — for a template that renders once over
29
+ * `ctx.loadedRoot` rather than per-entity (the shared-enums module is the
30
+ * first such template), `matches` never runs at all, so that template must
31
+ * read `select` directly to honour the same exclusion. Undefined ⇒ every
32
+ * node is in scope, byte-identical to a project with no `scope` declared. */
33
+ select?: (fqn: string) => boolean;
23
34
  config: ResolvedGenConfig;
24
35
  /** Pre-built by the runner for built-in generators that wrap existing
25
36
  * templates. Third-party generators typically don't need this. Always
@@ -35,6 +46,42 @@ export interface GenContext {
35
46
  * in a sub-directory. Undefined only when the runner was driven
36
47
  * programmatically without an explicit projectRoot. */
37
48
  projectRoot?: string;
49
+ /**
50
+ * FR-023 §4.3 — the run's composed registry (core providers plus whatever this
51
+ * project's `metaobjects.config.ts` `providers` adds), filled by the runner. The
52
+ * SAME vocabulary `opts.metadata` was loaded with. `sharedModelFile()`'s
53
+ * standalone re-loads of a `files` subset use this so a publisher project's own
54
+ * consumer-supplied vocabulary is honoured identically to the main load. Optional
55
+ * on the type so tests and custom callers don't need a placeholder; always
56
+ * present at run time when invoked via `runGen()`.
57
+ */
58
+ registry?: TypeRegistry;
59
+ /**
60
+ * FR-023 §4.3 — the collection's own source files (never a dependency's
61
+ * artifact), filled by the runner from `RunGenOpts.sourceFiles`. Distinct from
62
+ * `loadedRoot`, which is already fully loaded and merged: `sharedModelFile()`
63
+ * needs the raw file LIST so it can re-load a `files:`-narrowed subset of it
64
+ * standalone. Undefined when the caller never supplied `RunGenOpts.sourceFiles`
65
+ * (a generator relying on it should default sensibly, as `sharedModelFile()`'s
66
+ * own `files` option does).
67
+ */
68
+ sourceFiles?: readonly string[];
69
+ /**
70
+ * FR-043 §6 — the manifests of the libraries this project opted into, filled by the
71
+ * runner from `RunGenOpts.libraries`.
72
+ *
73
+ * What a generator reads it FOR is the `anchor`: the library node it keys on. That
74
+ * retires the hard-coded entity name a library-aware generator would otherwise carry
75
+ * (`trace-helper` compared `.name === "LlmCallBase"`, so ANY adopter entity of that
76
+ * name in ANY package triggered it) in favour of resolving the manifest's anchor to a
77
+ * node and comparing by node identity.
78
+ *
79
+ * Undefined means the caller never said — a programmatic `runGen()` rather than
80
+ * `meta gen`. A generator reading it should fall back to every SHIPPED manifest's
81
+ * anchors, which is still FQN-anchored and still not a bare name. An empty array is
82
+ * the opposite and is meaningful: the caller looked, and this project opted into none.
83
+ */
84
+ libraries?: readonly LibraryManifest[];
38
85
  warn: (msg: string) => void;
39
86
  }
40
87
 
@@ -59,7 +59,13 @@ export const entityFile = function entityFile(opts?: EntityFileOpts): Generator
59
59
  // FR-019: emit the shared-enums module ONCE per run, into the entity-module
60
60
  // target root. Returns null (no file) when the model uses no materialized
61
61
  // shared enums — keeping the inline-enum default byte-identical (no new file).
62
- const sharedEnums = renderSharedEnumsFile(ctx.loadedRoot);
62
+ // FR-023 §11.1 item 2: `select` (never `ctx.matches` — this renders from the
63
+ // WHOLE root, not this generator's filtered subset) excludes an enum used
64
+ // only by an imported, out-of-scope entity.
65
+ const sharedEnums = renderSharedEnumsFile(
66
+ ctx.loadedRoot,
67
+ ctx.select !== undefined ? { select: ctx.select } : undefined,
68
+ );
63
69
  if (sharedEnums !== null) {
64
70
  files.push({
65
71
  path: `${SHARED_ENUMS_BASENAME}.ts`,
@@ -35,6 +35,17 @@ export {
35
35
  type TemplateFormat,
36
36
  } from "./template-generator.js";
37
37
  export { traceHelperFile, type TraceHelperOpts } from "./trace-helper-file.js";
38
+ // FR-023 §4.3 — the publisher's flattened shared-model artifact + manifest generator.
39
+ // The three tool-file constants are re-exported alongside it purely so a `cli`
40
+ // test can assert they stay byte-equal to sdk's canonical copies (see that
41
+ // module's own comment for why codegen-ts can't just import sdk directly).
42
+ export {
43
+ sharedModelFile,
44
+ type SharedModelFileOpts,
45
+ MANIFEST_FILE as SHARED_MODEL_MANIFEST_FILE,
46
+ ARTIFACT_SUFFIX as SHARED_MODEL_ARTIFACT_SUFFIX,
47
+ INTEGRITY_PREFIX as SHARED_MODEL_INTEGRITY_PREFIX,
48
+ } from "./shared-model-file.js";
38
49
  export type {
39
50
  EntityDocData,
40
51
  StorageFieldDoc,