@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
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
// name: "email",
|
|
13
13
|
// label: "Email Address", // from @label on the view, falls back to humanized field name
|
|
14
14
|
// view: "text", // MetaView subtype
|
|
15
|
-
// htmlType: "email", // optional; only when
|
|
16
|
-
// placeholder: "you@example.com", // optional; only when @placeholder is set on the view
|
|
17
|
-
// helpText: "We never share this.", // optional; only when @helpText is set
|
|
15
|
+
// htmlType: "email", // optional; only when the view maps to a real HTML input type
|
|
18
16
|
// rules: { // optional; derived from validator children
|
|
19
17
|
// required: "Email is required",
|
|
20
18
|
// maxLength: { value: 255, message: "Must be 255 characters or fewer" },
|
|
@@ -28,205 +26,110 @@
|
|
|
28
26
|
// per-attribute access (placeholder, rules, etc.) by hand — the helper
|
|
29
27
|
// picks them up from this object automatically.
|
|
30
28
|
|
|
31
|
-
import { code, type Code } from "ts-poet";
|
|
32
|
-
import
|
|
33
|
-
import {
|
|
29
|
+
import { code, joinCode, type Code } from "ts-poet";
|
|
30
|
+
import { MetaObject } from "@metaobjectsdev/metadata";
|
|
31
|
+
import { resolveTableName } from "@metaobjectsdev/metadata";
|
|
32
|
+
import { physicalNameExpr } from "../names.js";
|
|
34
33
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
VIEW_SUBTYPE_PASSWORD,
|
|
41
|
-
VIEW_SUBTYPE_HIDDEN,
|
|
42
|
-
VIEW_SUBTYPE_DROPDOWN,
|
|
43
|
-
VIEW_SUBTYPE_RADIO,
|
|
44
|
-
VALIDATOR_SUBTYPE_REQUIRED,
|
|
45
|
-
VALIDATOR_SUBTYPE_LENGTH,
|
|
46
|
-
VALIDATOR_SUBTYPE_REGEX,
|
|
47
|
-
VALIDATOR_ATTR_MIN,
|
|
48
|
-
VALIDATOR_ATTR_MAX,
|
|
49
|
-
VALIDATOR_ATTR_PATTERN,
|
|
50
|
-
FIELD_ATTR_MAX_LENGTH,
|
|
51
|
-
FIELD_ATTR_REQUIRED,
|
|
52
|
-
} from "@metaobjectsdev/metadata";
|
|
53
|
-
import { resolveTableName, pluralize, toSnakeCase } from "@metaobjectsdev/metadata";
|
|
54
|
-
import { inferViewKind, currencyMetaFor, labelFor } from "./field-meta.js";
|
|
34
|
+
buildEntityUiDescriptor,
|
|
35
|
+
resourcePath,
|
|
36
|
+
type UiFieldDescriptor,
|
|
37
|
+
type UiRule,
|
|
38
|
+
} from "./entity-ui-descriptor.js";
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.replace(/_/g, " ")
|
|
61
|
-
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
62
|
-
.trim();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* REST resource path for an entity. Pluralized + snake_cased + lowercased.
|
|
67
|
-
* "Subscriber" → "/subscribers"
|
|
68
|
-
* "WorkoutEvent" → "/workout_events"
|
|
69
|
-
*/
|
|
70
|
-
export function resourcePath(entity: MetaData): string {
|
|
71
|
-
// ADR-0039: resolving — a concrete entity may inherit @routePath via extends.
|
|
72
|
-
const overrideAttr = entity.attr("routePath");
|
|
73
|
-
if (typeof overrideAttr === "string" && overrideAttr.length > 0) {
|
|
74
|
-
return overrideAttr.startsWith("/") ? overrideAttr : `/${overrideAttr}`;
|
|
75
|
-
}
|
|
76
|
-
return `/${pluralize(toSnakeCase(entity.name))}`;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Resolve the view subtype: explicit `view` child (own or inherited) wins, else inferred from field subType. */
|
|
80
|
-
function resolveView(field: MetaField): { view: string; viewNode?: MetaData } {
|
|
81
|
-
const viewChild = field.views()[0];
|
|
82
|
-
if (viewChild) {
|
|
83
|
-
return { view: viewChild.subType, viewNode: viewChild };
|
|
84
|
-
}
|
|
85
|
-
return { view: inferViewKind(field) };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Map a MetaView subtype to a real HTML <input type=...> value. Returns
|
|
90
|
-
* undefined for views that don't map to <input> at all (textarea, dropdown,
|
|
91
|
-
* radio) — consumers render the right element type themselves.
|
|
92
|
-
*/
|
|
93
|
-
function htmlTypeFromView(view: string, override?: string): string | undefined {
|
|
94
|
-
if (typeof override === "string" && override.length > 0) return override;
|
|
95
|
-
switch (view) {
|
|
96
|
-
case VIEW_SUBTYPE_TEXT:
|
|
97
|
-
return "text";
|
|
98
|
-
case VIEW_SUBTYPE_NUMBER:
|
|
99
|
-
return "number";
|
|
100
|
-
case VIEW_SUBTYPE_DATE:
|
|
101
|
-
return "date";
|
|
102
|
-
case VIEW_SUBTYPE_PASSWORD:
|
|
103
|
-
return "password";
|
|
104
|
-
case VIEW_SUBTYPE_CHECKBOX:
|
|
105
|
-
return "checkbox";
|
|
106
|
-
case VIEW_SUBTYPE_HIDDEN:
|
|
107
|
-
return "hidden";
|
|
108
|
-
case VIEW_SUBTYPE_RADIO:
|
|
109
|
-
return "radio";
|
|
110
|
-
case "month":
|
|
111
|
-
return "month";
|
|
112
|
-
case "email":
|
|
113
|
-
return "email";
|
|
114
|
-
case VIEW_SUBTYPE_TEXTAREA:
|
|
115
|
-
case VIEW_SUBTYPE_DROPDOWN:
|
|
116
|
-
return undefined;
|
|
117
|
-
default:
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
40
|
+
// `resourcePath` moved to entity-ui-descriptor.ts with the rest of the form derivation.
|
|
41
|
+
// Re-exported here because it is part of this module's published surface (the package
|
|
42
|
+
// barrel re-exports it from this path, and api-model.ts imports it from here).
|
|
43
|
+
export { resourcePath };
|
|
121
44
|
|
|
122
45
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
46
|
+
* Render one rule entry as the RHF-shaped code fragment the descriptor emits.
|
|
47
|
+
*
|
|
48
|
+
* Order is the descriptor's, which is declaration order — see the ordering note in
|
|
49
|
+
* entity-ui-descriptor.ts for why that is not incidental.
|
|
127
50
|
*/
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const min = child.attr(VALIDATOR_ATTR_MIN);
|
|
143
|
-
const max = child.attr(VALIDATOR_ATTR_MAX);
|
|
144
|
-
if (typeof min === "number") {
|
|
145
|
-
const msg = (child.attr("minMessage") as string | undefined) ?? `Must be at least ${min} characters`;
|
|
146
|
-
ruleParts.push(`minLength: { value: ${min}, message: ${JSON.stringify(msg)} }`);
|
|
147
|
-
}
|
|
148
|
-
if (typeof max === "number") {
|
|
149
|
-
const msg = (child.attr("maxMessage") as string | undefined) ?? `Must be ${max} characters or fewer`;
|
|
150
|
-
ruleParts.push(`maxLength: { value: ${max}, message: ${JSON.stringify(msg)} }`);
|
|
151
|
-
hasMaxLength = true;
|
|
152
|
-
}
|
|
153
|
-
} else if (child.subType === VALIDATOR_SUBTYPE_REGEX) {
|
|
154
|
-
const pattern = child.attr(VALIDATOR_ATTR_PATTERN);
|
|
155
|
-
if (typeof pattern === "string") {
|
|
156
|
-
const msg = (child.attr("message") as string | undefined) ?? "Invalid format";
|
|
157
|
-
// Emit as RegExp literal /.../ — `as const` preserves the value-ref.
|
|
158
|
-
// Forward-slash inside the pattern is escaped so the literal closes correctly.
|
|
159
|
-
const safe = pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
|
|
160
|
-
ruleParts.push(`pattern: { value: /${safe}/, message: ${JSON.stringify(msg)} }`);
|
|
161
|
-
}
|
|
51
|
+
function renderRule(r: UiRule): string {
|
|
52
|
+
switch (r.kind) {
|
|
53
|
+
case "required":
|
|
54
|
+
return `required: ${JSON.stringify(r.message)}`;
|
|
55
|
+
case "minLength":
|
|
56
|
+
return `minLength: { value: ${r.value}, message: ${JSON.stringify(r.message)} }`;
|
|
57
|
+
case "maxLength":
|
|
58
|
+
return `maxLength: { value: ${r.value}, message: ${JSON.stringify(r.message)} }`;
|
|
59
|
+
case "pattern": {
|
|
60
|
+
// Emit as a RegExp literal /.../ — `as const` preserves the value-ref. The
|
|
61
|
+
// descriptor carries the RAW pattern; forward-slash and backslash are escaped
|
|
62
|
+
// HERE, because closing a TypeScript regex literal is an emission concern.
|
|
63
|
+
const safe = r.pattern.replace(/\\/g, "\\\\").replace(/\//g, "\\/");
|
|
64
|
+
return `pattern: { value: /${safe}/, message: ${JSON.stringify(r.message)} }`;
|
|
162
65
|
}
|
|
163
66
|
}
|
|
164
|
-
|
|
165
|
-
// Field-level @required attr (if not already covered by validator).
|
|
166
|
-
if (!hasRequired && field.attr(FIELD_ATTR_REQUIRED) === true) {
|
|
167
|
-
ruleParts.push(`required: ${JSON.stringify(`${humanize(field.name)} is required`)}`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Field-level @maxLength attr (if not already covered).
|
|
171
|
-
const maxLenAttr = field.attr(FIELD_ATTR_MAX_LENGTH);
|
|
172
|
-
if (!hasMaxLength && typeof maxLenAttr === "number") {
|
|
173
|
-
ruleParts.push(
|
|
174
|
-
`maxLength: { value: ${maxLenAttr}, message: ${JSON.stringify(`Must be ${maxLenAttr} characters or fewer`)} }`,
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (ruleParts.length === 0) return undefined;
|
|
179
|
-
return `{ ${ruleParts.join(", ")} }`;
|
|
180
67
|
}
|
|
181
68
|
|
|
182
69
|
/** Build one nested field-object entry like `email: { name, label, ... },`. */
|
|
183
|
-
function renderFieldEntry(
|
|
184
|
-
const { view, viewNode } = resolveView(field);
|
|
185
|
-
const label = labelFor(field);
|
|
186
|
-
// ADR-0039: resolving — a view node may inherit @placeholder/@helpText/@htmlType via extends.
|
|
187
|
-
const placeholder = viewNode?.attr("placeholder") as string | undefined;
|
|
188
|
-
const helpText = viewNode?.attr("helpText") as string | undefined;
|
|
189
|
-
const htmlType = htmlTypeFromView(view, viewNode?.attr("htmlType") as string | undefined);
|
|
190
|
-
const rules = renderFieldRules(field);
|
|
191
|
-
|
|
70
|
+
function renderFieldEntry(f: UiFieldDescriptor): string {
|
|
192
71
|
const entries: string[] = [
|
|
193
|
-
`name: ${JSON.stringify(
|
|
194
|
-
`label: ${JSON.stringify(label)}`,
|
|
195
|
-
`view: ${JSON.stringify(view)}`,
|
|
72
|
+
`name: ${JSON.stringify(f.name)}`,
|
|
73
|
+
`label: ${JSON.stringify(f.label)}`,
|
|
74
|
+
`view: ${JSON.stringify(f.view)}`,
|
|
196
75
|
];
|
|
197
|
-
if (htmlType !== undefined) entries.push(`htmlType: ${JSON.stringify(htmlType)}`);
|
|
198
|
-
if (
|
|
199
|
-
if (helpText !== undefined) entries.push(`helpText: ${JSON.stringify(helpText)}`);
|
|
200
|
-
if (rules !== undefined) entries.push(`rules: ${rules}`);
|
|
76
|
+
if (f.htmlType !== undefined) entries.push(`htmlType: ${JSON.stringify(f.htmlType)}`);
|
|
77
|
+
if (f.rules.length > 0) entries.push(`rules: { ${f.rules.map(renderRule).join(", ")} }`);
|
|
201
78
|
|
|
202
79
|
// Currency-specific keys: only emitted for currency-subtype fields.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
entries.push(`
|
|
206
|
-
entries.push(`locale: ${JSON.stringify(currencyMeta.locale)}`);
|
|
80
|
+
if (f.currency !== undefined) {
|
|
81
|
+
entries.push(`currency: ${JSON.stringify(f.currency.currency)}`);
|
|
82
|
+
entries.push(`locale: ${JSON.stringify(f.currency.locale)}`);
|
|
207
83
|
}
|
|
208
84
|
|
|
209
|
-
return ` ${
|
|
85
|
+
return ` ${f.name}: {\n ${entries.join(",\n ")},\n }`;
|
|
210
86
|
}
|
|
211
87
|
|
|
212
|
-
export function renderEntityConstants(
|
|
213
|
-
|
|
88
|
+
export function renderEntityConstants(
|
|
89
|
+
obj: MetaObject,
|
|
90
|
+
/**
|
|
91
|
+
* @deprecated Accepted and IGNORED. The API base URL left the descriptor: generated
|
|
92
|
+
* client artifacts emit entity-relative paths and the base is supplied once at the
|
|
93
|
+
* client provider (`baseUrl`), because where the browser sends a request is a
|
|
94
|
+
* deployment fact rather than something to freeze at `meta gen` time.
|
|
95
|
+
*
|
|
96
|
+
* It must STAY in the signature: `src/reference/entity.ts` is copied verbatim into
|
|
97
|
+
* adopter repos (ADR-0034) and calls this positionally, so removing the parameter
|
|
98
|
+
* would fail to compile in every ejected copy. Removal is a separate later break.
|
|
99
|
+
*/
|
|
100
|
+
_apiPrefix = "",
|
|
101
|
+
// §A6. OPTIONAL, and it must stay optional: `src/reference/entity.ts` is copied
|
|
102
|
+
// verbatim into adopter repos by ADR-0034 scaffold-and-own and calls this with two
|
|
103
|
+
// arguments. A required parameter would fail to compile in every ejected copy.
|
|
104
|
+
names?: { readonly name: string; readonly symbol: Code } | undefined,
|
|
105
|
+
): Code {
|
|
106
|
+
// ONE derivation of the form surface, shared with the `agent/ui.md` docs page — see
|
|
107
|
+
// entity-ui-descriptor.ts. The descriptor already walks the RESOLVING fields(), so
|
|
108
|
+
// inherited fields (from extends:/super:) appear here as they always have.
|
|
109
|
+
const descriptor = buildEntityUiDescriptor(obj);
|
|
110
|
+
const entityName = descriptor.entity;
|
|
214
111
|
const tableName = resolveTableName(obj);
|
|
215
|
-
const path =
|
|
216
|
-
|
|
217
|
-
const fieldEntries: string[] =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
112
|
+
const path = descriptor.path;
|
|
113
|
+
|
|
114
|
+
const fieldEntries: string[] = descriptor.fields.map(renderFieldEntry);
|
|
115
|
+
|
|
116
|
+
// A6/B2 — reference the constant whenever the artifact is in the run. No equality
|
|
117
|
+
// guard: primaryRdbSource (@metaobjectsdev/metadata) refuses any object whose
|
|
118
|
+
// @role: primary sources disagree on a physical name, and both the constant and the
|
|
119
|
+
// literal below resolve through it, so a reference here is the single spelling. `physicalNameExpr` accepts
|
|
120
|
+
// any `{ symbol }`-shaped value, so it works with this function's `{ name, symbol }`
|
|
121
|
+
// parameter as well as `namesRef`'s own `{ resolved, symbol }`.
|
|
122
|
+
const tableLine: Code = code` $table: ${physicalNameExpr(names, tableName, obj)}`;
|
|
123
|
+
|
|
124
|
+
const body = joinCode(
|
|
125
|
+
[
|
|
126
|
+
code` $entity: ${JSON.stringify(entityName)}`,
|
|
127
|
+
tableLine,
|
|
128
|
+
code` $path: ${JSON.stringify(path)}`,
|
|
129
|
+
...fieldEntries.map((e) => code`${e}`),
|
|
130
|
+
],
|
|
131
|
+
{ on: ",\n" },
|
|
132
|
+
);
|
|
230
133
|
|
|
231
134
|
return code`
|
|
232
135
|
/**
|
|
@@ -234,9 +137,17 @@ export function renderEntityConstants(obj: MetaObject, apiPrefix = ""): Code {
|
|
|
234
137
|
*
|
|
235
138
|
* Use these instead of magic strings so TS catches typos and refactors stay
|
|
236
139
|
* coherent. Each non-dollar-prefixed key is a per-field object carrying
|
|
237
|
-
* name, label, view,
|
|
140
|
+
* name, label, view, an htmlType where the field's view maps to one, and the
|
|
238
141
|
* RHF-shaped validation rules derived from the field's validator children.
|
|
239
142
|
*
|
|
143
|
+
* placeholder and helpText are NOT emitted — no provider registers an attribute
|
|
144
|
+
* for them, so nothing here could derive one. (htmlType is emitted, but derived
|
|
145
|
+
* from the view subtype; the @htmlType override was removed for the same reason.)
|
|
146
|
+
* useEntityForm still honours placeholder if you add it: this file is generated,
|
|
147
|
+
* hand edits inside it survive regeneration through the three-way merge, and the
|
|
148
|
+
* consumers read this const rather than the metadata. That is the intended way
|
|
149
|
+
* to set it.
|
|
150
|
+
*
|
|
240
151
|
* Typical usage with the metaobjects React form helper:
|
|
241
152
|
*
|
|
242
153
|
* import { useEntityForm } from '@metaobjectsdev/react';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { code, imp, joinCode, type Code } from "ts-poet";
|
|
10
10
|
import type { MetaObject, MetaField } from "@metaobjectsdev/metadata";
|
|
11
|
-
import { FIELD_ATTR_OBJECT_REF } from "@metaobjectsdev/metadata";
|
|
11
|
+
import { FIELD_ATTR_OBJECT_REF, SOURCE_ROLE_REPLICA } from "@metaobjectsdev/metadata";
|
|
12
12
|
import { fieldDeclaringPackage, type RenderContext } from "../render-context.js";
|
|
13
13
|
import { renderDrizzleSchema } from "./drizzle-schema.js";
|
|
14
14
|
import { renderInferredTypes, renderEnumTypeAliases } from "./inferred-types.js";
|
|
@@ -20,10 +20,13 @@ import { renderTphDiscriminatorUnion, isTphDiscriminatorBase } from "./tph-discr
|
|
|
20
20
|
import { GENERATED_HEADER } from "../constants.js";
|
|
21
21
|
import { isProjection, isWriteThrough } from "../projection/projection-detector.js";
|
|
22
22
|
import { renderProjectionDecl } from "./projection-decl.js";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
projectionViewName, projectionViewSchema, projectionViewRole,
|
|
25
|
+
} from "../projection/extract-view-spec.js";
|
|
24
26
|
import { renderExistingViewDecl, renderViewReadZodObject } from "./view-decl.js";
|
|
25
27
|
import { renderDocsFor } from "./jsdoc.js";
|
|
26
28
|
import { valueObjectModuleSpecifier } from "../import-path.js";
|
|
29
|
+
import { namesRef, namesConstArg, physicalNameExpr, sourceSchemaExpr } from "../names.js";
|
|
27
30
|
import { hasWritableRdbSource } from "../source-detect.js";
|
|
28
31
|
import { renderValueObjectFile } from "./value-object-file.js";
|
|
29
32
|
import { isAbstract } from "../instance-artifacts.js";
|
|
@@ -76,6 +79,10 @@ export function renderEntityFile(
|
|
|
76
79
|
// Projections intentionally get the z.enum() validator but NOT a named enum
|
|
77
80
|
// type alias — emitting aliases here is a deliberate v1 scope decision.
|
|
78
81
|
if (isProjection(entity)) {
|
|
82
|
+
// §A6/§B2 — same `namesRef` pair drizzle-schema.ts builds, so the projection's view
|
|
83
|
+
// name + per-field dbCol reference the exact constant the names artifact exports
|
|
84
|
+
// (undefined when the artifact is not in this run — see names.ts). `namesRef`'s
|
|
85
|
+
// `{ resolved, symbol }` return is exactly the shape `ProjectionDeclOpts.names` wants.
|
|
79
86
|
return renderProjectionDecl(entity, ctx.loadedRoot, {
|
|
80
87
|
columnNamingStrategy: ctx.columnNamingStrategy,
|
|
81
88
|
dialect: ctx.dialect,
|
|
@@ -85,6 +92,7 @@ export function renderEntityFile(
|
|
|
85
92
|
ctx,
|
|
86
93
|
// Contract target drops the Drizzle .existing() view decl + drizzle-orm import.
|
|
87
94
|
includeViewDecl: runtime,
|
|
95
|
+
names: namesRef(entity, ctx),
|
|
88
96
|
});
|
|
89
97
|
}
|
|
90
98
|
|
|
@@ -141,17 +149,46 @@ export function renderEntityFile(
|
|
|
141
149
|
const module = valueObjectModuleSpecifier(name, ctx.packageOf, entity.package, ctx.outputLayout, ctx.extStyle);
|
|
142
150
|
return { name, module };
|
|
143
151
|
};
|
|
152
|
+
// ONE selection of the replica source: `projectionViewName` names it, and this is the
|
|
153
|
+
// role its entry has in <Entity>Names.sources. Hardcoding "replica" here would be a
|
|
154
|
+
// second derivation of the very thing extract-view-spec.ts exists to decide once.
|
|
155
|
+
const viewRole = projectionViewRole(entity) ?? SOURCE_ROLE_REPLICA;
|
|
156
|
+
const entityNames = namesRef(entity, ctx);
|
|
144
157
|
const viewOpts = {
|
|
145
158
|
dialect: ctx.dialect, columnNamingStrategy: ctx.columnNamingStrategy, timestampMode: ctx.timestampMode, voRef,
|
|
146
159
|
// PK fields type non-null in the replica-view decl + read schema even
|
|
147
160
|
// without @required (a PK is never NULL; see ViewDeclOpts.pkFieldNames).
|
|
148
161
|
pkFieldNames: new Set(primaryIdentityFieldNames(entity)) as ReadonlySet<string>,
|
|
162
|
+
// §A6 — the entity's names artifact supplies this view's COLUMN constants. A
|
|
163
|
+
// replica view exposes the entity's own fields, so `<Entity>Names.fields.<f>.column`
|
|
164
|
+
// is the same physical name the table side binds; a derived field the artifact
|
|
165
|
+
// does not carry falls back to the literal via `columnExpr`. The view's own NAME
|
|
166
|
+
// is passed separately below and stays a literal — the artifact holds the primary
|
|
167
|
+
// (table) source's name, not this one.
|
|
168
|
+
names: entityNames,
|
|
169
|
+
// The replica view's OWN @schema, from the same source node projectionViewName picks
|
|
170
|
+
// — never the entity's, which is the WRITE TABLE's and would qualify this view with a
|
|
171
|
+
// schema that belongs to something else. A view and the table it replicates need not
|
|
172
|
+
// live in the same schema, and a name paired with someone else's schema is worse than
|
|
173
|
+
// no schema at all.
|
|
174
|
+
//
|
|
175
|
+
// Both this and the view's NAME are now CONSTANTS. They were literals for a real
|
|
176
|
+
// reason that has stopped being true: <Entity>Names carried the primary source only,
|
|
177
|
+
// so a write-through entity's two physical names had one slot between them and the
|
|
178
|
+
// second was emitted in full — the same escape C#'s AppDbContext had, in a different
|
|
179
|
+
// language, which is what showed it was the artifact's shape and not the binding.
|
|
180
|
+
schema: sourceSchemaExpr(entityNames, viewRole) ?? projectionViewSchema(entity),
|
|
149
181
|
};
|
|
150
182
|
const z = imp("z@zod");
|
|
151
183
|
const docs = renderDocsFor(entity);
|
|
152
184
|
const docsPrefix = docs ? `${docs}\n` : "";
|
|
153
185
|
viewSections.push(
|
|
154
|
-
renderExistingViewDecl(
|
|
186
|
+
renderExistingViewDecl(
|
|
187
|
+
fields,
|
|
188
|
+
physicalNameExpr(entityNames, projectionViewName(entity, ctx.columnNamingStrategy), entity, viewRole),
|
|
189
|
+
`${camel}View`,
|
|
190
|
+
viewOpts,
|
|
191
|
+
),
|
|
155
192
|
code`
|
|
156
193
|
export const ${entity.name}Schema = ${renderViewReadZodObject(fields, viewOpts)};
|
|
157
194
|
`,
|
|
@@ -161,13 +198,18 @@ ${docsPrefix}export type ${entity.name} = ${z}.infer<typeof ${entity.name}Schema
|
|
|
161
198
|
);
|
|
162
199
|
}
|
|
163
200
|
|
|
201
|
+
// §A6/§B2 — same `namesRef` pair drizzle-schema.ts (and this file's own projection
|
|
202
|
+
// branch above) build, so the descriptor's $table references the exact constant the
|
|
203
|
+
// names artifact exports (undefined when the artifact is not in this run).
|
|
204
|
+
const constantsNames = namesRef(entity, ctx);
|
|
205
|
+
|
|
164
206
|
const sections: Code[] = [
|
|
165
207
|
renderDrizzleSchema(entity, ctx),
|
|
166
208
|
...viewSections,
|
|
167
209
|
renderInferredTypes(entity, tphBase, ctx, writeThrough /* skipRow — read type is the view schema */),
|
|
168
210
|
...(enumAliases !== null ? [enumAliases] : []),
|
|
169
211
|
renderZodValidators(entity, ctx),
|
|
170
|
-
renderEntityConstants(entity, ctx.apiPrefix),
|
|
212
|
+
renderEntityConstants(entity, ctx.apiPrefix, namesConstArg(constantsNames)),
|
|
171
213
|
...(allowlists ? [renderFilterAllowlist(entity, undefined, ctx), renderSortAllowlist(entity)] : []),
|
|
172
214
|
renderFilterType(entity),
|
|
173
215
|
...(tphBlock !== null ? [tphBlock] : []),
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// consumer having wired an extra generator, since the entity generator is
|
|
21
21
|
// scaffold-and-own (ADR-0034) and cannot be changed from the package.
|
|
22
22
|
|
|
23
|
-
import { code } from "ts-poet";
|
|
23
|
+
import { code, type Code } from "ts-poet";
|
|
24
24
|
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
25
25
|
import { GENERATED_HEADER } from "../constants.js";
|
|
26
26
|
import { renderEntityConstants } from "./entity-constants.js";
|
|
@@ -35,7 +35,20 @@ export function entityMetaSpecifier(entityName: string, extStyle: string | undef
|
|
|
35
35
|
return `./${entityName}.meta${extStyle === "js" ? ".js" : ""}`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function renderEntityMetaFile(
|
|
38
|
+
export function renderEntityMetaFile(
|
|
39
|
+
entity: MetaObject,
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated Accepted and IGNORED — see `renderEntityConstants`. The API base URL
|
|
42
|
+
* left the descriptor; the client provider's `baseUrl` supplies it at runtime.
|
|
43
|
+
*
|
|
44
|
+
* §A6 fix round 3. OPTIONAL, and it must stay optional: this function is called
|
|
45
|
+
* from codegen-ts-tanstack's reference/hooks.ts and reference/grid-hook.ts, both
|
|
46
|
+
* copied verbatim into adopter repos (ADR-0034). A required parameter would fail
|
|
47
|
+
* to compile in every ejected copy.
|
|
48
|
+
*/
|
|
49
|
+
_apiPrefix = "",
|
|
50
|
+
names?: { readonly name: string; readonly symbol: Code } | undefined,
|
|
51
|
+
): string {
|
|
39
52
|
const body = code`
|
|
40
53
|
// ${GENERATED_HEADER} — DO NOT EDIT.
|
|
41
54
|
// Source metadata: ${entity.name}
|
|
@@ -45,7 +58,7 @@ export function renderEntityMetaFile(entity: MetaObject, apiPrefix = ""): string
|
|
|
45
58
|
// from here so a client bundle never pulls the ORM in. \`${entity.name}.ts\` also
|
|
46
59
|
// exports this descriptor, for server-side and pre-existing consumers.
|
|
47
60
|
|
|
48
|
-
${renderEntityConstants(entity,
|
|
61
|
+
${renderEntityConstants(entity, "", names)}
|
|
49
62
|
`;
|
|
50
63
|
return body.toString();
|
|
51
64
|
}
|