@metaobjectsdev/codegen-ts 0.24.5 → 1.0.0-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.
- package/README.md +37 -12
- package/dist/api-surface.d.ts +29 -0
- package/dist/api-surface.d.ts.map +1 -1
- package/dist/api-surface.js +47 -0
- package/dist/api-surface.js.map +1 -1
- package/dist/column-mapper.d.ts +12 -0
- package/dist/column-mapper.d.ts.map +1 -1
- package/dist/column-mapper.js +1 -0
- package/dist/column-mapper.js.map +1 -1
- package/dist/constants.d.ts +24 -11
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +54 -19
- package/dist/constants.js.map +1 -1
- package/dist/db-emitting.d.ts +6 -0
- package/dist/db-emitting.d.ts.map +1 -0
- package/dist/db-emitting.js +26 -0
- package/dist/db-emitting.js.map +1 -0
- package/dist/generator-registry.d.ts.map +1 -1
- package/dist/generator-registry.js +14 -1
- package/dist/generator-registry.js.map +1 -1
- package/dist/generator.d.ts +5 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js.map +1 -1
- package/dist/generators/agent-docs-file.d.ts +19 -0
- package/dist/generators/agent-docs-file.d.ts.map +1 -0
- package/dist/generators/agent-docs-file.js +257 -0
- package/dist/generators/agent-docs-file.js.map +1 -0
- package/dist/generators/agent-requirements-page.d.ts +22 -0
- package/dist/generators/agent-requirements-page.d.ts.map +1 -0
- package/dist/generators/agent-requirements-page.js +175 -0
- package/dist/generators/agent-requirements-page.js.map +1 -0
- package/dist/generators/agent-schema-input.d.ts +69 -0
- package/dist/generators/agent-schema-input.d.ts.map +1 -0
- package/dist/generators/agent-schema-input.js +23 -0
- package/dist/generators/agent-schema-input.js.map +1 -0
- package/dist/generators/agent-schema-page.d.ts +21 -0
- package/dist/generators/agent-schema-page.d.ts.map +1 -0
- package/dist/generators/agent-schema-page.js +241 -0
- package/dist/generators/agent-schema-page.js.map +1 -0
- package/dist/generators/agent-ui-page.d.ts +22 -0
- package/dist/generators/agent-ui-page.d.ts.map +1 -0
- package/dist/generators/agent-ui-page.js +253 -0
- package/dist/generators/agent-ui-page.js.map +1 -0
- package/dist/generators/api-docs-file.d.ts.map +1 -1
- package/dist/generators/api-docs-file.js +13 -6
- package/dist/generators/api-docs-file.js.map +1 -1
- package/dist/generators/api-model.d.ts +6 -0
- package/dist/generators/api-model.d.ts.map +1 -1
- package/dist/generators/api-model.js +54 -35
- package/dist/generators/api-model.js.map +1 -1
- package/dist/generators/callable-file.js +1 -1
- package/dist/generators/callable-file.js.map +1 -1
- package/dist/generators/docs-data-builder.d.ts.map +1 -1
- package/dist/generators/docs-data-builder.js +47 -2
- package/dist/generators/docs-data-builder.js.map +1 -1
- package/dist/generators/docs-data.d.ts +6 -2
- package/dist/generators/docs-data.d.ts.map +1 -1
- package/dist/generators/index.d.ts +3 -8
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +19 -14
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/names-file.d.ts +17 -0
- package/dist/generators/names-file.d.ts.map +1 -0
- package/dist/generators/names-file.js +95 -0
- package/dist/generators/names-file.js.map +1 -0
- package/dist/generators/requirements-markdown.d.ts +18 -1
- package/dist/generators/requirements-markdown.d.ts.map +1 -1
- package/dist/generators/requirements-markdown.js +11 -9
- package/dist/generators/requirements-markdown.js.map +1 -1
- package/dist/generators/routes-file-hono.d.ts +17 -2
- package/dist/generators/routes-file-hono.d.ts.map +1 -1
- package/dist/generators/routes-file-hono.js +9 -11
- package/dist/generators/routes-file-hono.js.map +1 -1
- package/dist/generators/routes-file.d.ts +17 -2
- package/dist/generators/routes-file.d.ts.map +1 -1
- package/dist/generators/routes-file.js +10 -8
- package/dist/generators/routes-file.js.map +1 -1
- package/dist/generators/template-generator.d.ts.map +1 -1
- package/dist/generators/template-generator.js +20 -6
- package/dist/generators/template-generator.js.map +1 -1
- package/dist/index.d.ts +20 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -4
- package/dist/index.js.map +1 -1
- package/dist/metaobjects-config.d.ts +38 -1
- package/dist/metaobjects-config.d.ts.map +1 -1
- package/dist/metaobjects-config.js +9 -1
- package/dist/metaobjects-config.js.map +1 -1
- package/dist/names.d.ts +275 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +433 -0
- package/dist/names.js.map +1 -0
- package/dist/overwrite-policy.d.ts.map +1 -1
- package/dist/overwrite-policy.js +15 -3
- package/dist/overwrite-policy.js.map +1 -1
- package/dist/projection/extract-view-spec.d.ts +25 -1
- package/dist/projection/extract-view-spec.d.ts.map +1 -1
- package/dist/projection/extract-view-spec.js +79 -6
- package/dist/projection/extract-view-spec.js.map +1 -1
- package/dist/reference-templates.d.ts +1 -1
- package/dist/reference-templates.d.ts.map +1 -1
- package/dist/reference-templates.js +1 -1
- package/dist/reference-templates.js.map +1 -1
- package/dist/render-context.d.ts +12 -1
- package/dist/render-context.d.ts.map +1 -1
- package/dist/render-context.js +1 -0
- package/dist/render-context.js.map +1 -1
- package/dist/retired-codegen-attrs.d.ts +32 -0
- package/dist/retired-codegen-attrs.d.ts.map +1 -0
- package/dist/retired-codegen-attrs.js +47 -0
- package/dist/retired-codegen-attrs.js.map +1 -0
- package/dist/routes-expose.d.ts +63 -0
- package/dist/routes-expose.d.ts.map +1 -0
- package/dist/routes-expose.js +57 -0
- package/dist/routes-expose.js.map +1 -0
- package/dist/runner.d.ts +18 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +109 -7
- package/dist/runner.js.map +1 -1
- package/dist/templates/callable-file.d.ts +7 -2
- package/dist/templates/callable-file.d.ts.map +1 -1
- package/dist/templates/callable-file.js +54 -9
- package/dist/templates/callable-file.js.map +1 -1
- package/dist/templates/drizzle-schema.d.ts.map +1 -1
- package/dist/templates/drizzle-schema.js +236 -65
- package/dist/templates/drizzle-schema.js.map +1 -1
- package/dist/templates/entity-constants.d.ts +15 -6
- package/dist/templates/entity-constants.d.ts.map +1 -1
- package/dist/templates/entity-constants.js +85 -161
- package/dist/templates/entity-constants.js.map +1 -1
- package/dist/templates/entity-file.d.ts.map +1 -1
- package/dist/templates/entity-file.js +38 -4
- package/dist/templates/entity-file.js.map +1 -1
- package/dist/templates/entity-meta-file.d.ts +15 -1
- package/dist/templates/entity-meta-file.d.ts.map +1 -1
- package/dist/templates/entity-meta-file.js +12 -2
- package/dist/templates/entity-meta-file.js.map +1 -1
- package/dist/templates/entity-ui-descriptor.d.ts +112 -0
- package/dist/templates/entity-ui-descriptor.d.ts.map +1 -0
- package/dist/templates/entity-ui-descriptor.js +238 -0
- package/dist/templates/entity-ui-descriptor.js.map +1 -0
- package/dist/templates/field-meta.d.ts +46 -9
- package/dist/templates/field-meta.d.ts.map +1 -1
- package/dist/templates/field-meta.js +95 -21
- package/dist/templates/field-meta.js.map +1 -1
- package/dist/templates/names-decl.d.ts +38 -0
- package/dist/templates/names-decl.d.ts.map +1 -0
- package/dist/templates/names-decl.js +104 -0
- package/dist/templates/names-decl.js.map +1 -0
- package/dist/templates/projection-decl.d.ts +21 -0
- package/dist/templates/projection-decl.d.ts.map +1 -1
- package/dist/templates/projection-decl.js +53 -29
- package/dist/templates/projection-decl.js.map +1 -1
- package/dist/templates/queries-file.d.ts.map +1 -1
- package/dist/templates/queries-file.js +17 -2
- package/dist/templates/queries-file.js.map +1 -1
- package/dist/templates/requirement-test.d.ts.map +1 -1
- package/dist/templates/requirement-test.js +13 -2
- package/dist/templates/requirement-test.js.map +1 -1
- package/dist/templates/routes-file-hono.d.ts +2 -1
- package/dist/templates/routes-file-hono.d.ts.map +1 -1
- package/dist/templates/routes-file-hono.js +11 -2
- package/dist/templates/routes-file-hono.js.map +1 -1
- package/dist/templates/routes-file.d.ts +2 -1
- package/dist/templates/routes-file.d.ts.map +1 -1
- package/dist/templates/routes-file.js +54 -21
- package/dist/templates/routes-file.js.map +1 -1
- package/dist/templates/value-object-file.d.ts.map +1 -1
- package/dist/templates/value-object-file.js +15 -1
- package/dist/templates/value-object-file.js.map +1 -1
- package/dist/templates/view-decl.d.ts +37 -1
- package/dist/templates/view-decl.d.ts.map +1 -1
- package/dist/templates/view-decl.js +27 -3
- package/dist/templates/view-decl.js.map +1 -1
- package/dist/templates/zod-validators.d.ts +21 -0
- package/dist/templates/zod-validators.d.ts.map +1 -1
- package/dist/templates/zod-validators.js +40 -5
- package/dist/templates/zod-validators.js.map +1 -1
- package/dist/view-context.d.ts +24 -0
- package/dist/view-context.d.ts.map +1 -0
- package/dist/view-context.js +81 -0
- package/dist/view-context.js.map +1 -0
- package/package.json +6 -6
- package/src/api-surface.ts +53 -0
- package/src/column-mapper.ts +13 -0
- package/src/constants.ts +91 -16
- package/src/db-emitting.ts +31 -0
- package/src/generator-registry.ts +14 -4
- package/src/generator.ts +5 -0
- package/src/generators/agent-docs-file.ts +297 -0
- package/src/generators/agent-requirements-page.ts +199 -0
- package/src/generators/agent-schema-input.ts +88 -0
- package/src/generators/agent-schema-page.ts +297 -0
- package/src/generators/agent-ui-page.ts +290 -0
- package/src/generators/api-docs-file.ts +13 -6
- package/src/generators/api-model.ts +63 -34
- package/src/generators/callable-file.ts +1 -1
- package/src/generators/docs-data-builder.ts +46 -2
- package/src/generators/docs-data.ts +6 -2
- package/src/generators/index.ts +25 -14
- package/src/generators/names-file.ts +98 -0
- package/src/generators/requirements-markdown.ts +36 -2
- package/src/generators/routes-file-hono.ts +19 -10
- package/src/generators/routes-file.ts +20 -7
- package/src/generators/template-generator.ts +20 -6
- package/src/index.ts +64 -4
- package/src/metaobjects-config.ts +48 -2
- package/src/names.ts +629 -0
- package/src/overwrite-policy.ts +15 -3
- package/src/projection/extract-view-spec.ts +85 -6
- package/src/reference/entity.ts +21 -2
- package/src/reference/names.ts +125 -0
- package/src/reference/queries.ts +19 -3
- package/src/reference/routes-hono.ts +19 -9
- package/src/reference/routes.ts +25 -9
- package/src/reference-templates.ts +1 -1
- package/src/render-context.ts +13 -1
- package/src/retired-codegen-attrs.ts +52 -0
- package/src/routes-expose.ts +86 -0
- package/src/runner.ts +122 -11
- package/src/templates/callable-file.ts +60 -8
- package/src/templates/drizzle-schema.ts +248 -60
- package/src/templates/entity-constants.ts +95 -184
- package/src/templates/entity-file.ts +46 -4
- package/src/templates/entity-meta-file.ts +16 -3
- package/src/templates/entity-ui-descriptor.ts +305 -0
- package/src/templates/field-meta.ts +100 -19
- package/src/templates/names-decl.ts +165 -0
- package/src/templates/projection-decl.ts +72 -31
- package/src/templates/queries-file.ts +17 -2
- package/src/templates/requirement-test.ts +13 -2
- package/src/templates/routes-file-hono.ts +14 -2
- package/src/templates/routes-file.ts +69 -24
- package/src/templates/value-object-file.ts +15 -1
- package/src/templates/view-decl.ts +53 -3
- package/src/templates/zod-validators.ts +41 -5
- package/src/view-context.ts +88 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MetaField, MetaView } from "@metaobjectsdev/metadata";
|
|
2
|
+
/**
|
|
3
|
+
* The surface a generated artifact renders, and therefore the `name` its
|
|
4
|
+
* emitter looks for among a field's views.
|
|
5
|
+
*
|
|
6
|
+
* These are the only two surfaces the packaged generators render. An owned
|
|
7
|
+
* generator (FR-040) targeting a third may pass its own name — `viewForContext`
|
|
8
|
+
* takes any string — but it then owns telling its authors what to name.
|
|
9
|
+
*/
|
|
10
|
+
export declare const VIEW_CONTEXT_FORM = "form";
|
|
11
|
+
/** Both grid tiers (TanStack and Angular) render the same surface, so both ask for this. */
|
|
12
|
+
export declare const VIEW_CONTEXT_GRID = "grid";
|
|
13
|
+
/**
|
|
14
|
+
* The view a field declares for `context`, or `undefined` when it declares none
|
|
15
|
+
* at all (the caller's existing "no view" path — an inferred default — applies).
|
|
16
|
+
*
|
|
17
|
+
* Uses the RESOLVING accessor (ADR-0039): a view inherited through `extends` is
|
|
18
|
+
* as much the field's view as one declared on it.
|
|
19
|
+
*
|
|
20
|
+
* @throws CodegenError when the field declares several views and none is named
|
|
21
|
+
* for `context`. The remedy is in the message: name one of them.
|
|
22
|
+
*/
|
|
23
|
+
export declare function viewForContext(field: MetaField, context: string): MetaView | undefined;
|
|
24
|
+
//# sourceMappingURL=view-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-context.d.ts","sourceRoot":"","sources":["../src/view-context.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGpE;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,4FAA4F;AAC5F,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAexC;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAiBtF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Selecting a field's view by the SURFACE that renders it (#356).
|
|
2
|
+
//
|
|
3
|
+
// A field may legally declare more than one `view.*` child and every one of them
|
|
4
|
+
// survives the load, so an emitter reading `field.views()[0]` let DECLARATION
|
|
5
|
+
// ORDER decide its output — and because several emitters read that same list,
|
|
6
|
+
// one declaration drove three unrelated surfaces at once. Declaring a
|
|
7
|
+
// `view.text` so a grid cell rendered as text silently degraded the generated
|
|
8
|
+
// FORM to an `<input>`, and swapping two lines of JSON with no semantic change
|
|
9
|
+
// flipped the form's `<select>` back. That is not a modelling mistake; it is the
|
|
10
|
+
// only possible outcome when one declaration serves three readers.
|
|
11
|
+
//
|
|
12
|
+
// The selector is the view's `name` — a reserved structural key already legal on
|
|
13
|
+
// every node — so this needs no new attribute, no provider and no
|
|
14
|
+
// `metamodelVersion` move (ADR-0037 step 0: the vocabulary existed, only the
|
|
15
|
+
// read was wrong). The same shape as #353's resolution.
|
|
16
|
+
//
|
|
17
|
+
// Two rules, and the second is the load-bearing one:
|
|
18
|
+
//
|
|
19
|
+
// 1. A field declaring ONE view keeps its exact current behaviour: that view
|
|
20
|
+
// applies to every surface, whatever it is named (or unnamed). Existing
|
|
21
|
+
// models are untouched by construction — a `name` is how a view is
|
|
22
|
+
// ADDRESSED by `extends` (ADR-0029 `Customer.priceCents.display`), and
|
|
23
|
+
// re-reading those names as surface names would break addressing.
|
|
24
|
+
//
|
|
25
|
+
// 2. A field declaring SEVERAL views must name one for the surface being
|
|
26
|
+
// rendered. No match is a hard error, not a fallback: falling back to
|
|
27
|
+
// `views()[0]` reinstates the positional read for exactly the multi-view
|
|
28
|
+
// case this exists to fix, and falling back to the inferred default turns a
|
|
29
|
+
// `name` typo ("forms") into a silently degraded control. A field cannot
|
|
30
|
+
// declare two views with the SAME name — the loader merges same-(type,
|
|
31
|
+
// name) siblings into one node — so a match is never ambiguous.
|
|
32
|
+
import { CodegenError } from "./errors.js";
|
|
33
|
+
/**
|
|
34
|
+
* The surface a generated artifact renders, and therefore the `name` its
|
|
35
|
+
* emitter looks for among a field's views.
|
|
36
|
+
*
|
|
37
|
+
* These are the only two surfaces the packaged generators render. An owned
|
|
38
|
+
* generator (FR-040) targeting a third may pass its own name — `viewForContext`
|
|
39
|
+
* takes any string — but it then owns telling its authors what to name.
|
|
40
|
+
*/
|
|
41
|
+
export const VIEW_CONTEXT_FORM = "form";
|
|
42
|
+
/** Both grid tiers (TanStack and Angular) render the same surface, so both ask for this. */
|
|
43
|
+
export const VIEW_CONTEXT_GRID = "grid";
|
|
44
|
+
/** Render `view.dropdown name="form"` / `view.text (no name)` for a diagnostic. */
|
|
45
|
+
function describe(view) {
|
|
46
|
+
return view.name.length > 0
|
|
47
|
+
? `view.${view.subType} name="${view.name}"`
|
|
48
|
+
: `view.${view.subType} (no name)`;
|
|
49
|
+
}
|
|
50
|
+
/** `Entity.field`, or just the field name when the field has no parent object. */
|
|
51
|
+
function fieldPath(field) {
|
|
52
|
+
const owner = field.parent?.name;
|
|
53
|
+
return owner !== undefined && owner.length > 0 ? `${owner}.${field.name}` : field.name;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The view a field declares for `context`, or `undefined` when it declares none
|
|
57
|
+
* at all (the caller's existing "no view" path — an inferred default — applies).
|
|
58
|
+
*
|
|
59
|
+
* Uses the RESOLVING accessor (ADR-0039): a view inherited through `extends` is
|
|
60
|
+
* as much the field's view as one declared on it.
|
|
61
|
+
*
|
|
62
|
+
* @throws CodegenError when the field declares several views and none is named
|
|
63
|
+
* for `context`. The remedy is in the message: name one of them.
|
|
64
|
+
*/
|
|
65
|
+
export function viewForContext(field, context) {
|
|
66
|
+
const declared = field.views();
|
|
67
|
+
// One view (or none) is unambiguous: it applies to every surface, exactly as
|
|
68
|
+
// it did before this selector existed.
|
|
69
|
+
if (declared.length <= 1)
|
|
70
|
+
return declared[0];
|
|
71
|
+
const match = declared.find((v) => v.name === context);
|
|
72
|
+
if (match !== undefined)
|
|
73
|
+
return match;
|
|
74
|
+
throw new CodegenError(`Field "${fieldPath(field)}" declares ${declared.length} views ` +
|
|
75
|
+
`(${declared.map(describe).join(", ")}) and none is named "${context}", so codegen ` +
|
|
76
|
+
`cannot tell which one renders the ${context}. Name one of them "${context}" — a ` +
|
|
77
|
+
`view's \`name\` selects the surface it renders ("${VIEW_CONTEXT_FORM}" or ` +
|
|
78
|
+
`"${VIEW_CONTEXT_GRID}"). A field declaring a SINGLE view needs no name: that view ` +
|
|
79
|
+
`applies to every surface.`);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=view-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-context.js","sourceRoot":"","sources":["../src/view-context.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,8EAA8E;AAC9E,sEAAsE;AACtE,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,mEAAmE;AACnE,EAAE;AACF,iFAAiF;AACjF,kEAAkE;AAClE,6EAA6E;AAC7E,wDAAwD;AACxD,EAAE;AACF,qDAAqD;AACrD,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,wEAAwE;AACxE,4EAA4E;AAC5E,uEAAuE;AACvE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,iFAAiF;AACjF,8EAA8E;AAC9E,4EAA4E;AAC5E,qEAAqE;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAExC,mFAAmF;AACnF,SAAS,QAAQ,CAAC,IAAc;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QACzB,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,UAAU,IAAI,CAAC,IAAI,GAAG;QAC5C,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,YAAY,CAAC;AACvC,CAAC;AAED,kFAAkF;AAClF,SAAS,SAAS,CAAC,KAAgB;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;IACjC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;AACzF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgB,EAAE,OAAe;IAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAC/B,6EAA6E;IAC7E,uCAAuC;IACvC,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IACvD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEtC,MAAM,IAAI,YAAY,CACpB,UAAU,SAAS,CAAC,KAAK,CAAC,cAAc,QAAQ,CAAC,MAAM,SAAS;QAC9D,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,OAAO,gBAAgB;QACpF,qCAAqC,OAAO,uBAAuB,OAAO,QAAQ;QAClF,oDAAoD,iBAAiB,OAAO;QAC5E,IAAI,iBAAiB,+DAA+D;QACpF,2BAA2B,CAC9B,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/codegen-ts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
4
|
"description": "TypeScript codegen engine for MetaObjects — emits Drizzle, Zod, and Fastify artifacts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -111,8 +111,8 @@
|
|
|
111
111
|
"access": "public"
|
|
112
112
|
},
|
|
113
113
|
"dependencies": {
|
|
114
|
-
"@metaobjectsdev/metadata": "0.
|
|
115
|
-
"@metaobjectsdev/render": "0.
|
|
114
|
+
"@metaobjectsdev/metadata": "1.0.0-rc.1",
|
|
115
|
+
"@metaobjectsdev/render": "1.0.0-rc.1",
|
|
116
116
|
"@biomejs/js-api": "^0.7.0",
|
|
117
117
|
"@biomejs/wasm-nodejs": "^1.9.4",
|
|
118
118
|
"@toon-format/toon": "^2.3.0",
|
|
@@ -124,9 +124,9 @@
|
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
126
126
|
"@biomejs/biome": "^1.9.0",
|
|
127
|
-
"@metaobjectsdev/codegen-ts-react": "0.
|
|
128
|
-
"@metaobjectsdev/migrate-ts": "0.
|
|
129
|
-
"@metaobjectsdev/runtime-ts": "0.
|
|
127
|
+
"@metaobjectsdev/codegen-ts-react": "1.0.0-rc.1",
|
|
128
|
+
"@metaobjectsdev/migrate-ts": "1.0.0-rc.1",
|
|
129
|
+
"@metaobjectsdev/runtime-ts": "1.0.0-rc.1",
|
|
130
130
|
"bun-types": "latest",
|
|
131
131
|
"drizzle-orm": "^0.38.0",
|
|
132
132
|
"hono": "^4.6.0",
|
package/src/api-surface.ts
CHANGED
|
@@ -20,7 +20,16 @@
|
|
|
20
20
|
|
|
21
21
|
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
22
22
|
import { isAbstract } from "./instance-artifacts.js";
|
|
23
|
+
import { isProjection } from "./projection/projection-detector.js";
|
|
23
24
|
import { hasAnyRdbSource, hasWritableRdbSource } from "./source-detect.js";
|
|
25
|
+
import { resourcePath, restPath } from "./templates/entity-ui-descriptor.js";
|
|
26
|
+
import {
|
|
27
|
+
declaresTphDiscriminator,
|
|
28
|
+
isTphSubtype,
|
|
29
|
+
tphDiscriminatorBase,
|
|
30
|
+
tphDiscriminatorPin,
|
|
31
|
+
} from "./templates/zod-validators.js";
|
|
32
|
+
import { tphRouteSegment } from "./templates/tph-discriminator.js";
|
|
24
33
|
|
|
25
34
|
/**
|
|
26
35
|
* True when the object is served by a generated READ endpoint — so a hook has
|
|
@@ -43,3 +52,47 @@ export function servesReadApi(entity: MetaObject): boolean {
|
|
|
43
52
|
export function servesWriteApi(entity: MetaObject): boolean {
|
|
44
53
|
return !isAbstract(entity) && hasWritableRdbSource(entity);
|
|
45
54
|
}
|
|
55
|
+
|
|
56
|
+
// `restPath` MOVED to templates/entity-ui-descriptor.ts, beside `resourcePath` and the
|
|
57
|
+
// descriptor that emits `$path` from it. Re-exported here because this module is where
|
|
58
|
+
// the package barrel and `generators/agent-ui-page.ts` import it from, and neither should
|
|
59
|
+
// have to care that the composition moved.
|
|
60
|
+
export { restPath } from "./templates/entity-ui-descriptor.js";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The address the generated routes actually SERVE an object at: the mount prefix plus the
|
|
64
|
+
* object's REST path.
|
|
65
|
+
*
|
|
66
|
+
* One function, because getting this wrong is the defect it exists to prevent, twice over.
|
|
67
|
+
* `agent/ui.md` documented `/authors` for routes mounted at `/api/authors`; the API
|
|
68
|
+
* reference did the same, separately, in two more places — three doors composing one
|
|
69
|
+
* address, each free to drift from the others. `templates/routes-file-hono.ts` and
|
|
70
|
+
* `routes-file.ts` compose the same thing into GENERATED code and remain the runtime
|
|
71
|
+
* authority; this is the documentation side mirroring them.
|
|
72
|
+
*/
|
|
73
|
+
export function servedPath(entity: MetaObject, apiPrefix: string): string {
|
|
74
|
+
return `${apiPrefix}${restPath(entity)}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* True when a FORM is generated for this object.
|
|
79
|
+
*
|
|
80
|
+
* `servesWriteApi` is NOT the same question, and the difference is a TPH hierarchy. The
|
|
81
|
+
* discriminator BASE has a writable SOURCE — which is all `servesWriteApi` asks — but no
|
|
82
|
+
* write ENDPOINT: `routes-file.ts` mounts it with `expose: ["list", "get"]`, because the
|
|
83
|
+
* discriminated union has no single writable shape. So it gets no form, and there would be
|
|
84
|
+
* nowhere to submit one. Each concrete SUBTYPE gets a form, even though it owns no writable
|
|
85
|
+
* source of its own. A read-only projection gets none: it is instantiable for read, never
|
|
86
|
+
* for write.
|
|
87
|
+
*
|
|
88
|
+
* This is the form generator's own filter, hoisted so `agent/ui.md` can say "there is
|
|
89
|
+
* deliberately no form here" for the same set of objects the generator skips. Asking
|
|
90
|
+
* `servesWriteApi` on the page instead announced a form for every discriminator base.
|
|
91
|
+
*/
|
|
92
|
+
export function hasGeneratedForm(entity: MetaObject): boolean {
|
|
93
|
+
if (isTphSubtype(entity)) return true; // per-subtype form
|
|
94
|
+
// A discriminator base is never form-rendered directly; `entity` is already known not
|
|
95
|
+
// to be a subtype.
|
|
96
|
+
if (declaresTphDiscriminator(entity)) return false;
|
|
97
|
+
return servesWriteApi(entity) && !isProjection(entity);
|
|
98
|
+
}
|
package/src/column-mapper.ts
CHANGED
|
@@ -264,6 +264,18 @@ export interface ColumnSpec {
|
|
|
264
264
|
leadingComment?: string;
|
|
265
265
|
/** Optional CHECK constraint expression for the column (e.g., `status IN ('A', 'B')`). */
|
|
266
266
|
checkConstraint?: string;
|
|
267
|
+
/**
|
|
268
|
+
* The member list of {@link checkConstraint}, without the leading column name — e.g.
|
|
269
|
+
* `'A', 'B'` or `0, 5, 9`.
|
|
270
|
+
*
|
|
271
|
+
* Carried separately so the RENDERER can compose the constraint against the column's
|
|
272
|
+
* `<Entity>Names` constant instead of the literal `dbName` baked in above. Splitting it
|
|
273
|
+
* here rather than re-deriving it there keeps ONE builder for the member list, which is
|
|
274
|
+
* the half that must match `migrate-ts`'s `buildChecks` exactly or `meta verify` reports
|
|
275
|
+
* permanent drift. `checkConstraint` stays the composed literal, for the documented arm
|
|
276
|
+
* where no names artifact is in the run.
|
|
277
|
+
*/
|
|
278
|
+
checkConstraintValues?: string;
|
|
267
279
|
/**
|
|
268
280
|
* Optional `.$type<...>()` chain target. Renderer (drizzle-schema.ts) emits
|
|
269
281
|
* it ahead of the modifiers chain, using ts-poet `imp()` for objectRef
|
|
@@ -799,6 +811,7 @@ export function mapColumnType(
|
|
|
799
811
|
list = values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
800
812
|
}
|
|
801
813
|
result.checkConstraint = `${dbName} IN (${list})`;
|
|
814
|
+
result.checkConstraintValues = list;
|
|
802
815
|
}
|
|
803
816
|
}
|
|
804
817
|
|
package/src/constants.ts
CHANGED
|
@@ -1,28 +1,103 @@
|
|
|
1
1
|
// Local constants for codegen-ts.
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* The header line every emitter puts at the top of a generated file.
|
|
5
|
+
*
|
|
6
|
+
* Informational ON THIS PORT. It used to be documented here as the thing that "drives
|
|
7
|
+
* the overwrite policy", and it does not: `overwrite-policy.ts` decides from the
|
|
8
|
+
* `.gen-state` snapshot body and the committed hash manifest and never reads this
|
|
9
|
+
* string (grep it — no non-template module imports it). Deleting the header from a
|
|
10
|
+
* generated file does not take ownership of it here; it just changes the content, so
|
|
11
|
+
* the hash stops matching and the file is refused like any other edit.
|
|
12
|
+
*
|
|
13
|
+
* The JVM ports are the opposite: there the marker IS the decision
|
|
14
|
+
* (`GeneratedFileWriter`), and deleting it is exactly how you take ownership. Both
|
|
15
|
+
* behaviours are stated in `docs/features/own-your-codegen.md` — do not collapse them
|
|
16
|
+
* into one sentence, because the gesture that protects a file differs per port.
|
|
17
|
+
*/
|
|
4
18
|
export const GENERATED_HEADER = "@generated by @metaobjectsdev/codegen-ts";
|
|
5
19
|
|
|
6
|
-
|
|
7
|
-
|
|
20
|
+
// `EXTRA_SUFFIX = ".extra"` was removed here. It was a public export of this package
|
|
21
|
+
// wired to nothing: no generator, orphan sweep or write path ever compared a path
|
|
22
|
+
// against it, so its presence implied tool support for a name that is pure convention.
|
|
23
|
+
// A `<Entity>.extra.ts` sibling is safe because codegen only writes the paths it
|
|
24
|
+
// records in `.gen-state/.hashes.json` — not because of its name — and the generated
|
|
25
|
+
// barrel is built from the model, so it does not re-export it. Say that in prose;
|
|
26
|
+
// do not re-add a constant that suggests the engine knows the name.
|
|
8
27
|
|
|
9
28
|
/** Default outDir used by tests + as a sane default for generate(). */
|
|
10
29
|
export const DEFAULT_OUT_DIR = "./src/db/entities";
|
|
11
30
|
|
|
12
31
|
// ---------------------------------------------------------------------------
|
|
13
|
-
//
|
|
32
|
+
// Retired codegen-control attributes.
|
|
14
33
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
34
|
+
// `@emitRoutes` / `@emitTanstack` / `@emitForm` / `@emitGrid` / `@emitAngular`
|
|
35
|
+
// were read off metadata by generator filters and were NEVER registered
|
|
36
|
+
// metamodel vocabulary. The strict loader rejects every one of them with
|
|
37
|
+
// ERR_UNKNOWN_ATTR — and `meta verify` loads strict — while `meta gen` loads
|
|
38
|
+
// non-strict and honoured them. So an adopter who authored the documented
|
|
39
|
+
// per-entity opt-out got the behaviour AND a red `meta verify`: half-working,
|
|
40
|
+
// which is worse than either half, and exactly the class ADR-0023 §2 names.
|
|
41
|
+
//
|
|
42
|
+
// Registering them was refused. That moves `metamodelVersion` and forces a
|
|
43
|
+
// four-registry publish for a TypeScript-only generator kill switch that four
|
|
44
|
+
// of five ports would never read. The replacement already exists and is
|
|
45
|
+
// verified end to end: decide per generator what you consume — wire only the
|
|
46
|
+
// generators whose output you actually import, and narrow one with its
|
|
47
|
+
// `filter` option. The one clause that was opt-IN rather than opt-out (a TPH
|
|
48
|
+
// subtype's own per-subtype grid) cannot be a `filter`, since a filter is
|
|
49
|
+
// ANDed with the built-in gates and can only NARROW; it is the
|
|
50
|
+
// `tphSubtypeGrids` option on tanstackGrid()/tanstackGridHook() instead.
|
|
51
|
+
//
|
|
52
|
+
// No generator reads any of these names now. They survive HERE and nowhere
|
|
53
|
+
// else, so `meta gen` can recognise a stale one still sitting in an adopter's
|
|
54
|
+
// metadata and say what replaced it (see retired-codegen-attrs.ts). Keeping
|
|
55
|
+
// the name and its replacement in ONE record is the point: a warning whose
|
|
56
|
+
// text lives apart from the list it is warning about drifts from it.
|
|
19
57
|
// ---------------------------------------------------------------------------
|
|
20
58
|
|
|
21
|
-
/**
|
|
22
|
-
export
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
59
|
+
/** One retired codegen-control attribute, paired with what an adopter does instead. */
|
|
60
|
+
export interface RetiredCodegenAttr {
|
|
61
|
+
/** The bare attribute name, as it appears in metadata (no `@` sigil). */
|
|
62
|
+
readonly name: string;
|
|
63
|
+
/** What to do instead, phrased as an instruction and naming a real API. */
|
|
64
|
+
readonly replacement: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Every retired codegen-control attribute. Read ONLY by the `meta gen` warning. */
|
|
68
|
+
export const RETIRED_CODEGEN_ATTRS: readonly RetiredCodegenAttr[] = [
|
|
69
|
+
{
|
|
70
|
+
name: "emitRoutes",
|
|
71
|
+
replacement:
|
|
72
|
+
'drop routesFile() / routesFileHono() from `generators` in metaobjects.config.ts if ' +
|
|
73
|
+
'you do not consume REST routes, or narrow it — routesFile({ filter: (e) => e.name !== "Ledger" })',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "emitTanstack",
|
|
77
|
+
replacement:
|
|
78
|
+
'drop tanstackQuery() / tanstackGrid() / tanstackGridHook() from `generators` in ' +
|
|
79
|
+
'metaobjects.config.ts if you do not consume their output, or narrow one — ' +
|
|
80
|
+
'tanstackQuery({ filter: (e) => e.name !== "InternalAudit" })',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "emitForm",
|
|
84
|
+
replacement:
|
|
85
|
+
'drop formFile() from `generators` in metaobjects.config.ts if you do not consume ' +
|
|
86
|
+
'generated forms, or narrow it — formFile({ filter: (e) => e.name !== "InternalAudit" })',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "emitGrid",
|
|
90
|
+
replacement:
|
|
91
|
+
'pass the `tphSubtypeGrids` option — tanstackGrid({ tphSubtypeGrids: (e) => e.name === ' +
|
|
92
|
+
'"CopayAuth" }) — and pass the SAME predicate to tanstackGridHook(), or the emitted ' +
|
|
93
|
+
'<Sub>.grid.ts has no <Sub>.columns.tsx to import. This one is an opt-IN, so a `filter` ' +
|
|
94
|
+
'cannot express it: a filter is ANDed with the built-in gates and can only narrow',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "emitAngular",
|
|
98
|
+
replacement:
|
|
99
|
+
'drop angularServiceFile() / angularFormFile() / angularGridFile() from `generators` in ' +
|
|
100
|
+
'metaobjects.config.ts if you do not consume Angular output, or narrow one — ' +
|
|
101
|
+
'angularServiceFile({ filter: (e) => e.name !== "InternalAudit" })',
|
|
102
|
+
},
|
|
103
|
+
] as const;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Which objects make a project a DATABASE project — and therefore require a `dialect`.
|
|
2
|
+
//
|
|
3
|
+
// THIS IS THE ONE ANSWER, and it is hoisted here because two commands need it and a second
|
|
4
|
+
// copy would be a second answer. `runGen` throws when a model emits database code and the
|
|
5
|
+
// config declares no dialect (see `missingDialectMessage`), and it throws BEFORE
|
|
6
|
+
// `normalizeConfig` fills its defaults on purpose: `DEFAULT_DIALECT` is INERT, existing
|
|
7
|
+
// only so a value-object-only project need not name one. A DB project that forgot it gets
|
|
8
|
+
// a named error rather than silently-defaulted output — "a Postgres project quietly
|
|
9
|
+
// emitting sqlite", as the runner's own comment puts it.
|
|
10
|
+
//
|
|
11
|
+
// `meta docs` needs the same answer for `agent/schema.md`. Reading `?? DEFAULT_DIALECT`
|
|
12
|
+
// there instead would document a sqlite schema for a project `meta gen` refuses to build
|
|
13
|
+
// — asserting an answer the toolchain never gave.
|
|
14
|
+
|
|
15
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
16
|
+
import { hasAnyRdbSource } from "./source-detect.js";
|
|
17
|
+
|
|
18
|
+
/** The concrete objects a run would emit database code for. Empty ⇒ no dialect needed. */
|
|
19
|
+
export function dbEmittingObjects(entities: readonly MetaObject[]): MetaObject[] {
|
|
20
|
+
return entities.filter((e) => !e.isAbstract && hasAnyRdbSource(e));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The one wording for "this model needs a dialect and the config has none". */
|
|
24
|
+
export function missingDialectMessage(dbEmitting: readonly MetaObject[]): string {
|
|
25
|
+
const names = dbEmitting.map((e) => e.name).join(", ");
|
|
26
|
+
return (
|
|
27
|
+
`codegen config is missing dialect — required because this model ` +
|
|
28
|
+
`generates database code for: ${names}. Set dialect in ` +
|
|
29
|
+
`metaobjects.config.ts. (A model of only value objects and/or sourceless projections may omit it.)`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -18,13 +18,17 @@
|
|
|
18
18
|
// door for documentation is `meta docs` (D1).
|
|
19
19
|
|
|
20
20
|
import type { Generator } from "./generator.js";
|
|
21
|
+
// The four ADR-0034 ownable generators are no longer exported from ./generators/index.js
|
|
22
|
+
// (1.0 removed them — see that file's header). They remain the engine's internal composers
|
|
23
|
+
// and the stable-name registry still constructs them, so import them from their own modules.
|
|
24
|
+
import { entityFile } from "./generators/entity-file.js";
|
|
25
|
+
import { queriesFile } from "./generators/queries-file.js";
|
|
26
|
+
import { routesFile } from "./generators/routes-file.js";
|
|
27
|
+
import { barrel } from "./generators/barrel.js";
|
|
21
28
|
import {
|
|
22
|
-
entityFile,
|
|
23
|
-
queriesFile,
|
|
24
29
|
callableFile,
|
|
25
|
-
routesFile,
|
|
26
30
|
routesFileHono,
|
|
27
|
-
|
|
31
|
+
namesFile,
|
|
28
32
|
mermaidErDiagram,
|
|
29
33
|
promptRender,
|
|
30
34
|
outputParser,
|
|
@@ -111,6 +115,12 @@ export const generatorRegistry: Record<string, GeneratorRegistryEntry> = {
|
|
|
111
115
|
factory: () => barrel(),
|
|
112
116
|
options: "target?",
|
|
113
117
|
},
|
|
118
|
+
names: {
|
|
119
|
+
name: "names",
|
|
120
|
+
description: "Per-entity physical database name constants (table/view, schema, columns).",
|
|
121
|
+
tier: "native",
|
|
122
|
+
factory: () => namesFile(),
|
|
123
|
+
},
|
|
114
124
|
"prompt-render": {
|
|
115
125
|
name: "prompt-render",
|
|
116
126
|
description: "Per-template prompt-render helper over the render engine.",
|
package/src/generator.ts
CHANGED
|
@@ -63,6 +63,11 @@ export interface Generator {
|
|
|
63
63
|
* that Hono routes are actually being emitted and document them — rather than
|
|
64
64
|
* silently omitting the Hono CRUD registrars whenever the variant is wired. */
|
|
65
65
|
emitsHonoRoutes?: boolean;
|
|
66
|
+
/** §A6 — marks the generator that emits the <Entity>Names artifact. The runner
|
|
67
|
+
* aggregates this across the suite into ResolvedGenConfig.includeNames, which the
|
|
68
|
+
* entity generator reads to decide whether it may reference those constants.
|
|
69
|
+
* Same mechanism as emitsHonoRoutes/includeHonoRoutes. */
|
|
70
|
+
emitsNames?: boolean;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
export type GeneratorFactory<TOpts = void> = TOpts extends void
|