@metaobjectsdev/codegen-ts 1.0.3 → 1.0.4
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.
- package/dist/catalog-gates.d.ts +52 -0
- package/dist/catalog-gates.d.ts.map +1 -0
- package/dist/catalog-gates.js +183 -0
- package/dist/catalog-gates.js.map +1 -0
- package/dist/constants.d.ts +24 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +26 -0
- package/dist/constants.js.map +1 -1
- package/dist/generator-registry.d.ts +73 -0
- package/dist/generator-registry.d.ts.map +1 -1
- package/dist/generator-registry.js +174 -40
- package/dist/generator-registry.js.map +1 -1
- package/dist/generator.d.ts +17 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js.map +1 -1
- package/dist/generators/trace-helper-file.d.ts.map +1 -1
- package/dist/generators/trace-helper-file.js +54 -11
- package/dist/generators/trace-helper-file.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/metaobjects-config.d.ts +0 -15
- package/dist/metaobjects-config.d.ts.map +1 -1
- package/dist/metaobjects-config.js.map +1 -1
- package/dist/projection/extract-view-spec.d.ts.map +1 -1
- package/dist/projection/extract-view-spec.js +111 -4
- package/dist/projection/extract-view-spec.js.map +1 -1
- package/dist/relation-resolver.d.ts.map +1 -1
- package/dist/relation-resolver.js +11 -9
- package/dist/relation-resolver.js.map +1 -1
- package/dist/routes-expose.d.ts +30 -0
- package/dist/routes-expose.d.ts.map +1 -1
- package/dist/routes-expose.js +46 -0
- package/dist/routes-expose.js.map +1 -1
- package/dist/runner.d.ts +20 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +23 -0
- package/dist/runner.js.map +1 -1
- package/dist/templates/entity-file.js +2 -2
- package/dist/templates/entity-file.js.map +1 -1
- package/dist/templates/queries-file.js +5 -5
- package/dist/templates/queries-file.js.map +1 -1
- package/dist/templates/routes-file-hono.d.ts.map +1 -1
- package/dist/templates/routes-file-hono.js +10 -3
- package/dist/templates/routes-file-hono.js.map +1 -1
- package/dist/templates/routes-file.d.ts.map +1 -1
- package/dist/templates/routes-file.js +21 -4
- package/dist/templates/routes-file.js.map +1 -1
- package/dist/templates/value-object-file.js +2 -2
- package/dist/templates/value-object-file.js.map +1 -1
- package/package.json +6 -6
- package/src/catalog-gates.ts +222 -0
- package/src/constants.ts +27 -0
- package/src/generator-registry.ts +245 -44
- package/src/generator.ts +17 -0
- package/src/generators/trace-helper-file.ts +55 -12
- package/src/index.ts +17 -3
- package/src/metaobjects-config.ts +0 -15
- package/src/projection/extract-view-spec.ts +126 -5
- package/src/reference/entity.ts +2 -2
- package/src/reference/queries.ts +2 -2
- package/src/relation-resolver.ts +14 -8
- package/src/routes-expose.ts +57 -0
- package/src/runner.ts +49 -0
- package/src/templates/entity-file.ts +2 -2
- package/src/templates/queries-file.ts +5 -5
- package/src/templates/routes-file-hono.ts +10 -3
- package/src/templates/routes-file.ts +21 -4
- 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.
|
|
@@ -40,13 +49,55 @@ import {
|
|
|
40
49
|
templateGenerator,
|
|
41
50
|
traceHelperFile,
|
|
42
51
|
sharedModelFile,
|
|
52
|
+
requirementTests,
|
|
43
53
|
} from "./generators/index.js";
|
|
44
54
|
|
|
45
55
|
export type GeneratorTier = "native" | "neutral";
|
|
46
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
|
+
|
|
47
91
|
export interface GeneratorRegistryEntry {
|
|
48
92
|
/** Stable, cross-port-consistent id. Equals the registry map key. */
|
|
49
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;
|
|
50
101
|
/** One-line (no newline) human description for `--list`. */
|
|
51
102
|
description: string;
|
|
52
103
|
/** "native" = recommended `meta gen` suite; "neutral" = `meta docs`-owned. */
|
|
@@ -55,10 +106,55 @@ export interface GeneratorRegistryEntry {
|
|
|
55
106
|
factory: () => Generator;
|
|
56
107
|
/** Optional one-line options summary for `--list`. */
|
|
57
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;
|
|
58
149
|
/** Optional note — used to point neutral entries at their canonical door. */
|
|
59
150
|
note?: string;
|
|
60
151
|
}
|
|
61
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
|
+
|
|
62
158
|
// The `template` generator is a PRIMITIVE: callers supply { name, walk,
|
|
63
159
|
// template }. For registry identity + `--list` we expose a no-op default so the
|
|
64
160
|
// factory constructs a valid Generator without throwing; real use passes opts
|
|
@@ -73,139 +169,244 @@ function templatePrimitive(): Generator {
|
|
|
73
169
|
}
|
|
74
170
|
|
|
75
171
|
export const generatorRegistry: Record<string, GeneratorRegistryEntry> = {
|
|
76
|
-
// -----
|
|
172
|
+
// ----- model ------------------------------------------------------------
|
|
77
173
|
entity: {
|
|
78
174
|
name: "entity",
|
|
175
|
+
kind: "generator",
|
|
176
|
+
layer: "model",
|
|
79
177
|
description: "Per-entity Drizzle table + typed model module (the entity module).",
|
|
80
178
|
tier: "native",
|
|
81
179
|
factory: () => entityFile(),
|
|
82
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"),
|
|
83
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 ------------------------------------------------------
|
|
84
213
|
queries: {
|
|
85
214
|
name: "queries",
|
|
215
|
+
kind: "generator",
|
|
216
|
+
layer: "persistence",
|
|
86
217
|
description: "Per-entity typed query helpers (findById/create/...).",
|
|
87
218
|
tier: "native",
|
|
88
219
|
factory: () => queriesFile(),
|
|
89
220
|
options: "filter?, target?",
|
|
221
|
+
requires: ["entity"],
|
|
222
|
+
runtimePeers: ["drizzle-orm"],
|
|
223
|
+
configKeys: ["dialect", "extStyle", "pluralizeCollections", "collectionNameOverrides"],
|
|
224
|
+
ejectable: ejectable("queries"),
|
|
90
225
|
},
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
description: "Per-entity callable/service surface wrapping the query helpers.",
|
|
94
|
-
tier: "native",
|
|
95
|
-
factory: () => callableFile(),
|
|
96
|
-
options: "filter?, target?",
|
|
97
|
-
},
|
|
226
|
+
|
|
227
|
+
// ----- api --------------------------------------------------------------
|
|
98
228
|
routes: {
|
|
99
229
|
name: "routes",
|
|
230
|
+
kind: "generator",
|
|
231
|
+
layer: "api",
|
|
100
232
|
description: "Per-entity Fastify CRUD routes (drizzle-fastify mountCrudRoutes).",
|
|
101
233
|
tier: "native",
|
|
102
234
|
factory: () => routesFile(),
|
|
103
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"),
|
|
104
242
|
},
|
|
105
243
|
"routes-hono": {
|
|
106
244
|
name: "routes-hono",
|
|
245
|
+
kind: "generator",
|
|
246
|
+
layer: "api",
|
|
107
247
|
description: "Per-entity Hono CRUD routes (runtime-ts/hono mountCrudRoutes).",
|
|
108
248
|
tier: "native",
|
|
109
249
|
factory: () => routesFileHono(),
|
|
110
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"),
|
|
111
257
|
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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).",
|
|
115
266
|
tier: "native",
|
|
116
|
-
factory: () =>
|
|
117
|
-
options: "target?",
|
|
267
|
+
factory: () => apiDocsFile(),
|
|
268
|
+
options: "filter?, target?",
|
|
269
|
+
ejectable: ejectable("api-docs"),
|
|
118
270
|
},
|
|
119
|
-
|
|
120
|
-
name: "
|
|
121
|
-
|
|
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.",
|
|
122
300
|
tier: "native",
|
|
123
|
-
factory: () =>
|
|
301
|
+
factory: () => callableFile(),
|
|
124
302
|
options: "filter?, target?",
|
|
303
|
+
requires: ["entity"],
|
|
304
|
+
runtimePeers: ["drizzle-orm"],
|
|
305
|
+
configKeys: ["columnNamingStrategy"],
|
|
306
|
+
ejectable: ejectable("callable"),
|
|
125
307
|
},
|
|
126
308
|
"prompt-render": {
|
|
127
309
|
name: "prompt-render",
|
|
310
|
+
kind: "generator",
|
|
311
|
+
layer: "capability",
|
|
128
312
|
description: "Per-template prompt-render helper over the render engine.",
|
|
129
313
|
tier: "native",
|
|
130
314
|
factory: () => promptRender(),
|
|
131
315
|
options: "filter?, target?",
|
|
316
|
+
runtimePackages: ["@metaobjectsdev/render"],
|
|
317
|
+
ejectable: ejectable("prompt-render"),
|
|
132
318
|
},
|
|
133
319
|
"output-parser": {
|
|
134
320
|
name: "output-parser",
|
|
321
|
+
kind: "generator",
|
|
322
|
+
layer: "capability",
|
|
135
323
|
description: "Per-template tolerant output parser (recover-on-receipt).",
|
|
136
324
|
tier: "native",
|
|
137
325
|
factory: () => outputParser(),
|
|
138
326
|
options: "filter?, target?",
|
|
327
|
+
runtimePackages: ["@metaobjectsdev/metadata", "@metaobjectsdev/render", "@metaobjectsdev/runtime-ts"],
|
|
328
|
+
runtimePeers: ["zod"],
|
|
329
|
+
ejectable: ejectable("output-parser"),
|
|
139
330
|
},
|
|
140
331
|
extractor: {
|
|
141
332
|
name: "extractor",
|
|
333
|
+
kind: "generator",
|
|
334
|
+
layer: "capability",
|
|
142
335
|
description: "Per-template typed extract<Name> helper (strict payload extraction).",
|
|
143
336
|
tier: "native",
|
|
144
337
|
factory: () => extractor(),
|
|
145
338
|
options: "filter?, target?",
|
|
339
|
+
requires: ["entity", "output-parser"],
|
|
340
|
+
runtimePackages: ["@metaobjectsdev/metadata", "@metaobjectsdev/render"],
|
|
341
|
+
ejectable: ejectable("extractor"),
|
|
146
342
|
},
|
|
147
343
|
"output-prompt": {
|
|
148
344
|
name: "output-prompt",
|
|
345
|
+
kind: "generator",
|
|
346
|
+
layer: "capability",
|
|
149
347
|
description: "Per-template output-format prompt fragment generator.",
|
|
150
348
|
tier: "native",
|
|
151
349
|
factory: () => outputPrompt(),
|
|
152
350
|
options: "filter?, target?",
|
|
351
|
+
runtimePackages: ["@metaobjectsdev/render"],
|
|
352
|
+
ejectable: ejectable("output-prompt"),
|
|
153
353
|
},
|
|
154
354
|
"render-helper": {
|
|
155
355
|
name: "render-helper",
|
|
356
|
+
kind: "generator",
|
|
357
|
+
layer: "capability",
|
|
156
358
|
description: "Per-template.output render helper (document/email typed wrappers).",
|
|
157
359
|
tier: "native",
|
|
158
360
|
factory: () => renderHelper(),
|
|
159
361
|
options: "filter?, target?",
|
|
362
|
+
runtimePackages: ["@metaobjectsdev/render"],
|
|
363
|
+
configKeys: ["extStyle"],
|
|
364
|
+
ejectable: ejectable("render-helper"),
|
|
160
365
|
},
|
|
161
366
|
template: {
|
|
162
367
|
name: "template",
|
|
368
|
+
kind: "generator",
|
|
369
|
+
layer: "capability",
|
|
163
370
|
description: "Generic Mustache template primitive (walk + template → files).",
|
|
164
371
|
tier: "native",
|
|
165
372
|
factory: () => templatePrimitive(),
|
|
166
373
|
options: "name, walk, template, format?, filter?, provider?, target?",
|
|
374
|
+
ejectable: ejectable("template"),
|
|
167
375
|
},
|
|
168
|
-
"api-docs": {
|
|
169
|
-
name: "api-docs",
|
|
170
|
-
description:
|
|
171
|
-
"Per-entity/template SDK API reference (the generated code's API, human + agent forms).",
|
|
172
|
-
tier: "native",
|
|
173
|
-
factory: () => apiDocsFile(),
|
|
174
|
-
options: "filter?, target?",
|
|
175
|
-
},
|
|
176
|
-
|
|
177
376
|
"trace-helper": {
|
|
178
377
|
name: "trace-helper",
|
|
179
|
-
|
|
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).",
|
|
180
382
|
tier: "native",
|
|
181
383
|
factory: () => traceHelperFile(),
|
|
182
384
|
options: "outDir?, target?",
|
|
385
|
+
ejectable: ejectable("trace-helper"),
|
|
386
|
+
},
|
|
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"),
|
|
183
396
|
},
|
|
184
397
|
"shared-model": {
|
|
185
398
|
name: "shared-model",
|
|
186
|
-
|
|
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`.",
|
|
187
403
|
tier: "native",
|
|
188
404
|
// `name`/`include` are required at run time (an empty include matches
|
|
189
405
|
// everything, so a placeholder here constructs without throwing — real use
|
|
190
406
|
// always supplies both, same as `template`'s templatePrimitive() above).
|
|
191
407
|
factory: () => sharedModelFile({ name: "shared-model", include: [] }),
|
|
192
408
|
options: "name, include, exclude?, files?, version?, target?",
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// ----- Tier-2 neutral (owned by the `meta docs` engine — D1 / ADR-0020) ---
|
|
196
|
-
docs: {
|
|
197
|
-
name: "docs",
|
|
198
|
-
description: "Neutral per-entity / per-template Markdown documentation pages.",
|
|
199
|
-
tier: "neutral",
|
|
200
|
-
factory: () => docsFile(),
|
|
201
|
-
note: "neutral artifact — use `meta docs` (the single docs door, ADR-0021 D1); not part of the recommended `meta gen` native suite.",
|
|
202
|
-
},
|
|
203
|
-
"mermaid-er": {
|
|
204
|
-
name: "mermaid-er",
|
|
205
|
-
description: "Mermaid ER diagram of the entity/relationship model.",
|
|
206
|
-
tier: "neutral",
|
|
207
|
-
factory: () => mermaidErDiagram(),
|
|
208
|
-
note: "neutral artifact owned by the docs engine (ADR-0020); surfaced via `meta docs`, not the recommended `meta gen` native suite.",
|
|
409
|
+
ejectable: ejectable("shared-model"),
|
|
209
410
|
},
|
|
210
411
|
};
|
|
211
412
|
|
package/src/generator.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
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";
|
|
@@ -65,6 +66,22 @@ export interface GenContext {
|
|
|
65
66
|
* own `files` option does).
|
|
66
67
|
*/
|
|
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[];
|
|
68
85
|
warn: (msg: string) => void;
|
|
69
86
|
}
|
|
70
87
|
|
|
@@ -28,9 +28,11 @@ import {
|
|
|
28
28
|
TEMPLATE_ATTR_TEXT_REF,
|
|
29
29
|
} from "@metaobjectsdev/metadata";
|
|
30
30
|
import { responseFormatOf } from "../templates/find-inbound.js";
|
|
31
|
-
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
31
|
+
import type { MetaData, MetaObject } from "@metaobjectsdev/metadata";
|
|
32
|
+
import { libraryManifests } from "@metaobjectsdev/metadata/library";
|
|
32
33
|
import {
|
|
33
34
|
type EmittedFile,
|
|
35
|
+
type GenContext,
|
|
34
36
|
type Generator,
|
|
35
37
|
type GeneratorFactory,
|
|
36
38
|
perEntity,
|
|
@@ -39,8 +41,40 @@ import { generatePayloadInterfacesBatch } from "../payload-codegen.js";
|
|
|
39
41
|
import { GENERATED_HEADER } from "../constants.js";
|
|
40
42
|
import { tphDiscriminatorPin } from "../templates/zod-validators.js";
|
|
41
43
|
|
|
42
|
-
/**
|
|
43
|
-
const
|
|
44
|
+
/** This generator's stable name — the key a library manifest declares its anchor under. */
|
|
45
|
+
const STABLE_NAME = "trace-helper";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The FQNs this generator keys on, from the LIBRARY MANIFESTS rather than a constant
|
|
49
|
+
* here (FR-043 §6).
|
|
50
|
+
*
|
|
51
|
+
* What it replaces: `const LLM_CALL_BASE = "LlmCallBase"`, compared against `.name`
|
|
52
|
+
* anywhere in the super chain — so any adopter entity called `LlmCallBase`, in any
|
|
53
|
+
* package, triggered the generator. An anchor is a fully-qualified node the library
|
|
54
|
+
* declares, and it is resolved to a node and compared by identity below.
|
|
55
|
+
*
|
|
56
|
+
* `ctx.libraries` undefined means the caller never said which libraries are selected
|
|
57
|
+
* (a programmatic `runGen()`); every shipped manifest's anchor is then a candidate,
|
|
58
|
+
* which is still FQN-anchored. An EMPTY array is the opposite and is honoured: the
|
|
59
|
+
* caller looked, this project opted into none, and the generator matches nothing.
|
|
60
|
+
*/
|
|
61
|
+
function anchorFqns(ctx: GenContext): string[] {
|
|
62
|
+
const manifests = ctx.libraries ?? Object.values(libraryManifests());
|
|
63
|
+
const out: string[] = [];
|
|
64
|
+
for (const manifest of manifests) {
|
|
65
|
+
for (const g of manifest.generators ?? []) {
|
|
66
|
+
if (g.name === STABLE_NAME && g.anchor !== undefined) out.push(g.anchor);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Resolve each anchor FQN to the node it names, skipping any the model does not hold. */
|
|
73
|
+
function anchorNodes(ctx: GenContext): MetaData[] {
|
|
74
|
+
const wanted = new Set(anchorFqns(ctx));
|
|
75
|
+
if (wanted.size === 0) return [];
|
|
76
|
+
return ctx.loadedRoot.children().filter((n) => wanted.has(n.resolutionKey()));
|
|
77
|
+
}
|
|
44
78
|
|
|
45
79
|
export interface TraceHelperOpts {
|
|
46
80
|
/** Output directory prefix relative to the target's outDir. Default: "" (root). */
|
|
@@ -49,12 +83,17 @@ export interface TraceHelperOpts {
|
|
|
49
83
|
target?: string;
|
|
50
84
|
}
|
|
51
85
|
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Walk the super chain looking for one of the anchor NODES.
|
|
88
|
+
*
|
|
89
|
+
* Node identity, not `.name` and not even the FQN string: the anchors were resolved
|
|
90
|
+
* against this run's own loaded root, so an entity whose chain reaches one reaches
|
|
91
|
+
* exactly the node the library declared.
|
|
92
|
+
*/
|
|
93
|
+
function extendsAnchor(obj: MetaObject, anchors: readonly MetaData[]): boolean {
|
|
94
|
+
if (anchors.length === 0) return false;
|
|
95
|
+
for (let cur = obj.superResolved; cur !== undefined; cur = cur.superResolved) {
|
|
96
|
+
if (anchors.includes(cur)) return true;
|
|
58
97
|
}
|
|
59
98
|
return false;
|
|
60
99
|
}
|
|
@@ -66,12 +105,16 @@ function pascal(s: string): string {
|
|
|
66
105
|
|
|
67
106
|
export const traceHelperFile = function traceHelperFile(opts?: TraceHelperOpts): Generator {
|
|
68
107
|
const dirPrefix = opts?.outDir ? `${opts.outDir.replace(/\/$/, "")}/` : "";
|
|
108
|
+
// Resolved once per run, on first use: `perEntity` calls back per entity and the
|
|
109
|
+
// anchor set is a property of the run, not of the entity.
|
|
110
|
+
let anchors: MetaData[] | undefined;
|
|
69
111
|
const generator: Generator = {
|
|
70
|
-
name:
|
|
112
|
+
name: STABLE_NAME,
|
|
71
113
|
generate: perEntity((entity, ctx) => {
|
|
72
|
-
|
|
114
|
+
anchors ??= anchorNodes(ctx);
|
|
115
|
+
// Only concrete entities derived from a library's declared anchor.
|
|
73
116
|
if (entity.isAbstract) return [];
|
|
74
|
-
if (!
|
|
117
|
+
if (!extendsAnchor(entity, anchors)) return [];
|
|
75
118
|
|
|
76
119
|
// Find the nested template.prompt.
|
|
77
120
|
// ADR-0039: resolving — a concrete trace entity may inherit its
|
package/src/index.ts
CHANGED
|
@@ -38,8 +38,22 @@ export {
|
|
|
38
38
|
generatorRegistry,
|
|
39
39
|
listGenerators,
|
|
40
40
|
getGenerator,
|
|
41
|
+
GENERATOR_LAYERS,
|
|
41
42
|
} from "./generator-registry.js";
|
|
42
|
-
export type {
|
|
43
|
+
export type {
|
|
44
|
+
GeneratorRegistryEntry,
|
|
45
|
+
GeneratorTier,
|
|
46
|
+
GeneratorFramework,
|
|
47
|
+
Layer,
|
|
48
|
+
} from "./generator-registry.js";
|
|
49
|
+
|
|
50
|
+
// The post-selection audits `meta gen` runs over a wired suite, and the impl-name →
|
|
51
|
+
// stable-name resolver both of them go through. Public so the CLI can gate it.
|
|
52
|
+
export {
|
|
53
|
+
warnUnsatisfiedRequires,
|
|
54
|
+
warnMixedApiFrameworks,
|
|
55
|
+
stableNameIndex,
|
|
56
|
+
} from "./catalog-gates.js";
|
|
43
57
|
|
|
44
58
|
export type { MetaobjectsGenConfig, NormalizedMetaobjectsGenConfig, ResolvedGenConfig, Dialect, ExtStyle, ColumnNamingStrategy, MetaDataTypeProvider, GeneratorSpec, DocsConfig, ResolvedDocsConfig, DocsSurface, ApiSurface, VerifyConfig } from "./metaobjects-config.js";
|
|
45
59
|
export { defineConfig, normalizeConfig, resolveGenerators, resolveDocsConfig } from "./metaobjects-config.js";
|
|
@@ -82,7 +96,7 @@ export { decideAndWrite, GitMissingError, WRITE_STATUSES } from "./overwrite-pol
|
|
|
82
96
|
export { contentHash, readGeneratedHash, listGeneratedPaths } from "./overwrite-policy.js";
|
|
83
97
|
|
|
84
98
|
export { CodegenError } from "./errors.js";
|
|
85
|
-
export { GENERATED_HEADER, NAMES_FILE_SUFFIX, DEFAULT_OUT_DIR, RETIRED_CODEGEN_ATTRS, type RetiredCodegenAttr } from "./constants.js";
|
|
99
|
+
export { GENERATED_HEADER, NAMES_FILE_SUFFIX, DEFAULT_OUT_DIR, RETIRED_CODEGEN_ATTRS, sidecarLine, type RetiredCodegenAttr } from "./constants.js";
|
|
86
100
|
export { warnRetiredCodegenAttrs } from "./retired-codegen-attrs.js";
|
|
87
101
|
|
|
88
102
|
export { formatTs } from "./format.js";
|
|
@@ -210,7 +224,7 @@ export { renderEntityConstants, resourcePath } from "./templates/entity-constant
|
|
|
210
224
|
export { renderQueriesFile } from "./templates/queries-file.js";
|
|
211
225
|
// #348 — which CRUD verbs a generated routes file mounts. Public because an OWNED
|
|
212
226
|
// routes generator (ADR-0034) composes the same render call and needs the same option.
|
|
213
|
-
export { CRUD_VERBS, TPH_POLYMORPHIC_VERBS, resolveExpose, intersectExpose, exposeLine } from "./routes-expose.js";
|
|
227
|
+
export { CRUD_VERBS, TPH_POLYMORPHIC_VERBS, resolveExpose, intersectExpose, exposeLine, authSeamJsDoc } from "./routes-expose.js";
|
|
214
228
|
export type { CrudVerb, ExposeOption } from "./routes-expose.js";
|
|
215
229
|
export { renderRoutesFile } from "./templates/routes-file.js";
|
|
216
230
|
export { renderRoutesFileHono } from "./templates/routes-file-hono.js";
|
|
@@ -198,21 +198,6 @@ export interface MetaobjectsGenConfig extends Omit<ResolvedGenConfig, "dbImport"
|
|
|
198
198
|
* loader. Composed AFTER the default core+forge bundle.
|
|
199
199
|
*/
|
|
200
200
|
providers?: readonly MetaDataTypeProvider[];
|
|
201
|
-
/**
|
|
202
|
-
* MetaObjects-shipped library packages this project loads alongside its own metadata —
|
|
203
|
-
* `["ai"]` makes `extends: "metaobjects::ai::LlmCallBase"` resolve.
|
|
204
|
-
*
|
|
205
|
-
* Sits beside `providers` because it answers the same shape of question: what does this
|
|
206
|
-
* project's model need in scope beyond the files it declares. Opt-in, because a library
|
|
207
|
-
* registers real top-level nodes and a project that never references one should not find
|
|
208
|
-
* them in its model, its generated output or its docs.
|
|
209
|
-
*
|
|
210
|
-
* Threaded to `loadMemory` by every CLI command that loads metadata. Before it existed,
|
|
211
|
-
* `librarySources` was reachable only from `MetaDataLoader.fromDirectory` — which the
|
|
212
|
-
* CLI does not use — so a generator that consumes a library was registered FOR the CLI
|
|
213
|
-
* while its input was unreachable THROUGH it (#333).
|
|
214
|
-
*/
|
|
215
|
-
libraries?: readonly string[];
|
|
216
201
|
}
|
|
217
202
|
|
|
218
203
|
/** MetaobjectsGenConfig after applying defaults. All fields required.
|