@metaobjectsdev/codegen-ts 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/column-mapper.d.ts.map +1 -1
- package/dist/column-mapper.js +123 -46
- package/dist/column-mapper.js.map +1 -1
- package/dist/generators/callable-file.d.ts +8 -0
- package/dist/generators/callable-file.d.ts.map +1 -0
- package/dist/generators/callable-file.js +32 -0
- package/dist/generators/callable-file.js.map +1 -0
- package/dist/generators/docs-data-builder.d.ts.map +1 -1
- package/dist/generators/docs-data-builder.js +5 -2
- package/dist/generators/docs-data-builder.js.map +1 -1
- package/dist/generators/extractor-file.d.ts +9 -0
- package/dist/generators/extractor-file.d.ts.map +1 -0
- package/dist/generators/extractor-file.js +45 -0
- package/dist/generators/extractor-file.js.map +1 -0
- package/dist/generators/index.d.ts +3 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +3 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/render-helper-file.d.ts +9 -0
- package/dist/generators/render-helper-file.d.ts.map +1 -0
- package/dist/generators/render-helper-file.js +58 -0
- package/dist/generators/render-helper-file.js.map +1 -0
- package/dist/payload-codegen.d.ts.map +1 -1
- package/dist/payload-codegen.js +42 -8
- package/dist/payload-codegen.js.map +1 -1
- package/dist/projection/extract-view-spec.d.ts.map +1 -1
- package/dist/projection/extract-view-spec.js +11 -3
- package/dist/projection/extract-view-spec.js.map +1 -1
- package/dist/render-engine/framework-provider.d.ts +6 -5
- package/dist/render-engine/framework-provider.d.ts.map +1 -1
- package/dist/render-engine/framework-provider.js +53 -11
- package/dist/render-engine/framework-provider.js.map +1 -1
- package/dist/templates/callable-file.d.ts +8 -0
- package/dist/templates/callable-file.d.ts.map +1 -0
- package/dist/templates/callable-file.js +98 -0
- package/dist/templates/callable-file.js.map +1 -0
- package/dist/templates/extract-delegate-emitter.d.ts +42 -0
- package/dist/templates/extract-delegate-emitter.d.ts.map +1 -0
- package/dist/templates/extract-delegate-emitter.js +339 -0
- package/dist/templates/extract-delegate-emitter.js.map +1 -0
- package/dist/templates/{recover-schema-emitter.d.ts → extract-schema-emitter.d.ts} +2 -2
- package/dist/templates/extract-schema-emitter.d.ts.map +1 -0
- package/dist/templates/{recover-schema-emitter.js → extract-schema-emitter.js} +37 -20
- package/dist/templates/extract-schema-emitter.js.map +1 -0
- package/dist/templates/extractor.d.ts +9 -0
- package/dist/templates/extractor.d.ts.map +1 -0
- package/dist/templates/extractor.js +296 -0
- package/dist/templates/extractor.js.map +1 -0
- package/dist/templates/field-meta.d.ts.map +1 -1
- package/dist/templates/field-meta.js +2 -1
- package/dist/templates/field-meta.js.map +1 -1
- package/dist/templates/filter-type.d.ts.map +1 -1
- package/dist/templates/filter-type.js +8 -5
- package/dist/templates/filter-type.js.map +1 -1
- package/dist/templates/fr010-field-mapping.d.ts +22 -6
- package/dist/templates/fr010-field-mapping.d.ts.map +1 -1
- package/dist/templates/fr010-field-mapping.js +66 -21
- package/dist/templates/fr010-field-mapping.js.map +1 -1
- package/dist/templates/inferred-types.d.ts +15 -1
- package/dist/templates/inferred-types.d.ts.map +1 -1
- package/dist/templates/inferred-types.js +30 -17
- package/dist/templates/inferred-types.js.map +1 -1
- package/dist/templates/output-parser.d.ts.map +1 -1
- package/dist/templates/output-parser.js +98 -34
- package/dist/templates/output-parser.js.map +1 -1
- package/dist/templates/output-prompt.js +2 -2
- package/dist/templates/render-helper.d.ts +14 -0
- package/dist/templates/render-helper.d.ts.map +1 -0
- package/dist/templates/render-helper.js +180 -0
- package/dist/templates/render-helper.js.map +1 -0
- package/dist/templates/zod-validators.d.ts.map +1 -1
- package/dist/templates/zod-validators.js +59 -3
- package/dist/templates/zod-validators.js.map +1 -1
- package/package.json +10 -4
- package/src/column-mapper.ts +128 -45
- package/src/generators/callable-file.ts +44 -0
- package/src/generators/docs-data-builder.ts +5 -1
- package/src/generators/extractor-file.ts +57 -0
- package/src/generators/index.ts +3 -0
- package/src/generators/render-helper-file.ts +74 -0
- package/src/payload-codegen.ts +52 -7
- package/src/projection/extract-view-spec.ts +11 -3
- package/src/render-engine/framework-provider.ts +53 -16
- package/src/templates/callable-file.ts +122 -0
- package/src/templates/extract-delegate-emitter.ts +370 -0
- package/src/templates/{recover-schema-emitter.ts → extract-schema-emitter.ts} +39 -19
- package/src/templates/extractor.ts +333 -0
- package/src/templates/field-meta.ts +2 -0
- package/src/templates/filter-type.ts +7 -5
- package/src/templates/fr010-field-mapping.ts +71 -18
- package/src/templates/inferred-types.ts +32 -18
- package/src/templates/output-parser.ts +108 -35
- package/src/templates/output-prompt.ts +2 -2
- package/src/templates/render-helper.ts +244 -0
- package/src/templates/zod-validators.ts +51 -4
- package/dist/templates/recover-schema-emitter.d.ts.map +0 -1
- package/dist/templates/recover-schema-emitter.js.map +0 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// downstream (e.g. to LLM tool_use input_schema) lost the nested object shape.
|
|
11
11
|
import { code, joinCode, imp } from "ts-poet";
|
|
12
12
|
import { MetaObject, MetaField } from "@metaobjectsdev/metadata";
|
|
13
|
-
import { FIELD_SUBTYPE_STRING, FIELD_SUBTYPE_INT, FIELD_SUBTYPE_LONG, FIELD_SUBTYPE_CURRENCY, FIELD_SUBTYPE_BOOLEAN, FIELD_SUBTYPE_DOUBLE, FIELD_SUBTYPE_FLOAT, FIELD_SUBTYPE_DATE, FIELD_SUBTYPE_TIME, FIELD_SUBTYPE_TIMESTAMP, FIELD_SUBTYPE_ENUM, FIELD_SUBTYPE_OBJECT, VALIDATOR_SUBTYPE_REQUIRED, VALIDATOR_SUBTYPE_LENGTH, VALIDATOR_SUBTYPE_REGEX, IDENTITY_ATTR_FIELDS, IDENTITY_ATTR_GENERATION, FIELD_ATTR_REQUIRED, FIELD_ATTR_MAX_LENGTH, FIELD_ATTR_DEFAULT, FIELD_ATTR_AUTO_SET, FIELD_ATTR_OBJECT_REF, AUTO_SET_ON_CREATE, AUTO_SET_ON_UPDATE, VALIDATOR_ATTR_MAX, VALIDATOR_ATTR_MIN, VALIDATOR_ATTR_PATTERN, GENERATION_INCREMENT, GENERATION_UUID, } from "@metaobjectsdev/metadata";
|
|
13
|
+
import { FIELD_SUBTYPE_STRING, FIELD_SUBTYPE_INT, FIELD_SUBTYPE_LONG, FIELD_SUBTYPE_CURRENCY, FIELD_SUBTYPE_BOOLEAN, FIELD_SUBTYPE_DOUBLE, FIELD_SUBTYPE_FLOAT, FIELD_SUBTYPE_DATE, FIELD_SUBTYPE_TIME, FIELD_SUBTYPE_TIMESTAMP, FIELD_SUBTYPE_ENUM, FIELD_SUBTYPE_OBJECT, FIELD_SUBTYPE_UUID, VALIDATOR_SUBTYPE_REQUIRED, VALIDATOR_SUBTYPE_LENGTH, VALIDATOR_SUBTYPE_REGEX, VALIDATOR_SUBTYPE_NUMERIC, VALIDATOR_SUBTYPE_ARRAY, IDENTITY_ATTR_FIELDS, IDENTITY_ATTR_GENERATION, FIELD_ATTR_REQUIRED, FIELD_ATTR_MAX_LENGTH, FIELD_ATTR_DEFAULT, FIELD_ATTR_AUTO_SET, FIELD_ATTR_OBJECT_REF, FIELD_ATTR_READ_ONLY, AUTO_SET_ON_CREATE, AUTO_SET_ON_UPDATE, VALIDATOR_ATTR_MAX, VALIDATOR_ATTR_MIN, VALIDATOR_ATTR_PATTERN, GENERATION_INCREMENT, GENERATION_UUID, } from "@metaobjectsdev/metadata";
|
|
14
14
|
import { enumValues, zodEnumExpr } from "../enum-meta.js";
|
|
15
15
|
import { renderDocsFor } from "./jsdoc.js";
|
|
16
16
|
/** Auto-generated PK field names that should be omitted from InsertSchema. */
|
|
@@ -44,6 +44,11 @@ export function renderInsertSchemaOnly(obj) {
|
|
|
44
44
|
for (const child of obj.fields()) {
|
|
45
45
|
if (autoGenPkFields.has(child.name))
|
|
46
46
|
continue;
|
|
47
|
+
// FR-013: @readOnly fields are populated by DB / replication / external
|
|
48
|
+
// owner; the application has no path to write them. Exclude from the
|
|
49
|
+
// create-shape schema entirely.
|
|
50
|
+
if (child.ownAttr(FIELD_ATTR_READ_ONLY) === true)
|
|
51
|
+
continue;
|
|
47
52
|
const autoSet = child.ownAttr(FIELD_ATTR_AUTO_SET);
|
|
48
53
|
if (autoSet === AUTO_SET_ON_CREATE || autoSet === AUTO_SET_ON_UPDATE) {
|
|
49
54
|
insertFieldLines.push(code ` ${child.name}: z.string().optional().transform(() => new Date().toISOString())`);
|
|
@@ -69,6 +74,12 @@ export function renderZodValidators(obj) {
|
|
|
69
74
|
for (const child of obj.fields()) {
|
|
70
75
|
if (autoGenPkFields.has(child.name))
|
|
71
76
|
continue;
|
|
77
|
+
// FR-013: @readOnly fields appear in neither InsertSchema nor UpdateSchema.
|
|
78
|
+
// The DB / trigger / replication owns the write path; the app must not
|
|
79
|
+
// pass these values in POST/PATCH bodies (routesFile enforces the same
|
|
80
|
+
// contract at the boundary with a 400 response).
|
|
81
|
+
if (child.ownAttr(FIELD_ATTR_READ_ONLY) === true)
|
|
82
|
+
continue;
|
|
72
83
|
const autoSet = child.ownAttr(FIELD_ATTR_AUTO_SET);
|
|
73
84
|
// Insert schema: @autoSet fields use transform (always override client input).
|
|
74
85
|
if (autoSet === AUTO_SET_ON_CREATE || autoSet === AUTO_SET_ON_UPDATE) {
|
|
@@ -152,6 +163,7 @@ function zodFieldExpr(field) {
|
|
|
152
163
|
break;
|
|
153
164
|
}
|
|
154
165
|
case FIELD_SUBTYPE_STRING:
|
|
166
|
+
case FIELD_SUBTYPE_UUID:
|
|
155
167
|
default:
|
|
156
168
|
baseStr = "z.string()";
|
|
157
169
|
break;
|
|
@@ -171,12 +183,27 @@ function fieldWillBeOptional(field) {
|
|
|
171
183
|
const hasDefault = field.ownAttr(FIELD_ATTR_DEFAULT) !== undefined;
|
|
172
184
|
return !isRequired || hasDefault;
|
|
173
185
|
}
|
|
174
|
-
/**
|
|
186
|
+
/** Numeric field subtypes whose Zod base is `z.number()` — value bounds apply. */
|
|
187
|
+
const NUMERIC_FIELD_SUBTYPES = new Set([
|
|
188
|
+
FIELD_SUBTYPE_INT, FIELD_SUBTYPE_LONG, FIELD_SUBTYPE_CURRENCY,
|
|
189
|
+
FIELD_SUBTYPE_DOUBLE, FIELD_SUBTYPE_FLOAT,
|
|
190
|
+
]);
|
|
191
|
+
/** Append .min/.max/.regex/.optional() based on field-level validators + required state.
|
|
192
|
+
*
|
|
193
|
+
* Bound semantics by field shape:
|
|
194
|
+
* - string (scalar) → .min/.max = character count (validator.length + @maxLength)
|
|
195
|
+
* - numeric (scalar) → .min/.max = numeric value (validator.numeric)
|
|
196
|
+
* - array (any element) → .min/.max = element count (validator.array)
|
|
197
|
+
*/
|
|
175
198
|
function appendValidatorChain(base, field) {
|
|
176
199
|
let isRequired = field.ownAttr(FIELD_ATTR_REQUIRED) === true;
|
|
177
200
|
let maxLen = field.ownAttr(FIELD_ATTR_MAX_LENGTH);
|
|
178
201
|
let minLen;
|
|
179
202
|
let pattern;
|
|
203
|
+
let numMin;
|
|
204
|
+
let numMax;
|
|
205
|
+
let arrMin;
|
|
206
|
+
let arrMax;
|
|
180
207
|
for (const child of field.validators()) {
|
|
181
208
|
if (child.subType === VALIDATOR_SUBTYPE_REQUIRED)
|
|
182
209
|
isRequired = true;
|
|
@@ -193,9 +220,32 @@ function appendValidatorChain(base, field) {
|
|
|
193
220
|
if (typeof p === "string")
|
|
194
221
|
pattern = p;
|
|
195
222
|
}
|
|
223
|
+
if (child.subType === VALIDATOR_SUBTYPE_NUMERIC) {
|
|
224
|
+
const max = child.ownAttr(VALIDATOR_ATTR_MAX);
|
|
225
|
+
const min = child.ownAttr(VALIDATOR_ATTR_MIN);
|
|
226
|
+
if (typeof max === "number")
|
|
227
|
+
numMax = max;
|
|
228
|
+
if (typeof min === "number")
|
|
229
|
+
numMin = min;
|
|
230
|
+
}
|
|
231
|
+
if (child.subType === VALIDATOR_SUBTYPE_ARRAY) {
|
|
232
|
+
const max = child.ownAttr(VALIDATOR_ATTR_MAX);
|
|
233
|
+
const min = child.ownAttr(VALIDATOR_ATTR_MIN);
|
|
234
|
+
if (typeof max === "number")
|
|
235
|
+
arrMax = max;
|
|
236
|
+
if (typeof min === "number")
|
|
237
|
+
arrMin = min;
|
|
238
|
+
}
|
|
196
239
|
}
|
|
197
240
|
let chain = base;
|
|
198
|
-
|
|
241
|
+
// Array element-count bounds apply to the z.array(...) wrapper regardless of element type.
|
|
242
|
+
if (field.isArray) {
|
|
243
|
+
if (arrMin !== undefined)
|
|
244
|
+
chain = code `${chain}.min(${arrMin})`;
|
|
245
|
+
if (arrMax !== undefined)
|
|
246
|
+
chain = code `${chain}.max(${arrMax})`;
|
|
247
|
+
}
|
|
248
|
+
else if (field.subType === FIELD_SUBTYPE_STRING) {
|
|
199
249
|
if (minLen !== undefined)
|
|
200
250
|
chain = code `${chain}.min(${minLen})`;
|
|
201
251
|
else if (isRequired)
|
|
@@ -205,6 +255,12 @@ function appendValidatorChain(base, field) {
|
|
|
205
255
|
if (pattern !== undefined)
|
|
206
256
|
chain = code `${chain}.regex(new RegExp(${JSON.stringify(pattern)}))`;
|
|
207
257
|
}
|
|
258
|
+
else if (NUMERIC_FIELD_SUBTYPES.has(field.subType)) {
|
|
259
|
+
if (numMin !== undefined)
|
|
260
|
+
chain = code `${chain}.min(${numMin})`;
|
|
261
|
+
if (numMax !== undefined)
|
|
262
|
+
chain = code `${chain}.max(${numMax})`;
|
|
263
|
+
}
|
|
208
264
|
// Fields with DB-level defaults are optional in the InsertSchema: the caller
|
|
209
265
|
// can omit them and the DB will fill in. Otherwise required-with-default
|
|
210
266
|
// would force callers to repeat the default at every call site.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod-validators.js","sourceRoot":"","sources":["../../src/templates/zod-validators.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,mFAAmF;AACnF,kFAAkF;AAClF,0FAA0F;AAC1F,EAAE;AACF,+EAA+E;AAC/E,+DAA+D;AAC/D,6EAA6E;AAC7E,+EAA+E;AAC/E,+EAA+E;AAE/E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAa,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACL,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,sBAAsB,EACnF,qBAAqB,EAAE,oBAAoB,EAAE,mBAAmB,EAChE,kBAAkB,EAAE,kBAAkB,EAAE,uBAAuB,EAC/D,kBAAkB,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"zod-validators.js","sourceRoot":"","sources":["../../src/templates/zod-validators.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/codegen-ts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "TypeScript codegen engine for MetaObjects — emits Drizzle, Zod, and Fastify artifacts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
"bun": "./src/generators/index.ts",
|
|
16
16
|
"types": "./dist/generators/index.d.ts",
|
|
17
17
|
"default": "./dist/generators/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./templates/zod-validators": {
|
|
20
|
+
"bun": "./src/templates/zod-validators.ts",
|
|
21
|
+
"types": "./dist/templates/zod-validators.d.ts",
|
|
22
|
+
"default": "./dist/templates/zod-validators.js"
|
|
18
23
|
}
|
|
19
24
|
},
|
|
20
25
|
"files": ["dist", "src", "templates", "README.md", "LICENSE"],
|
|
@@ -38,8 +43,8 @@
|
|
|
38
43
|
"access": "public"
|
|
39
44
|
},
|
|
40
45
|
"dependencies": {
|
|
41
|
-
"@metaobjectsdev/metadata": "0.
|
|
42
|
-
"@metaobjectsdev/render": "0.
|
|
46
|
+
"@metaobjectsdev/metadata": "0.9.0",
|
|
47
|
+
"@metaobjectsdev/render": "0.9.0",
|
|
43
48
|
"@biomejs/js-api": "^0.7.0",
|
|
44
49
|
"@biomejs/wasm-nodejs": "^1.9.4",
|
|
45
50
|
"ts-poet": "^6.10.0"
|
|
@@ -50,7 +55,8 @@
|
|
|
50
55
|
},
|
|
51
56
|
"devDependencies": {
|
|
52
57
|
"@biomejs/biome": "^1.9.0",
|
|
53
|
-
"@metaobjectsdev/codegen-ts-react": "0.
|
|
58
|
+
"@metaobjectsdev/codegen-ts-react": "0.9.0",
|
|
59
|
+
"@metaobjectsdev/runtime-ts": "0.9.0",
|
|
54
60
|
"bun-types": "latest",
|
|
55
61
|
"drizzle-orm": "^0.36.0",
|
|
56
62
|
"hono": "^4.6.0",
|
package/src/column-mapper.ts
CHANGED
|
@@ -17,14 +17,21 @@ import {
|
|
|
17
17
|
FIELD_SUBTYPE_OBJECT,
|
|
18
18
|
FIELD_SUBTYPE_CLASS,
|
|
19
19
|
FIELD_SUBTYPE_ENUM,
|
|
20
|
+
FIELD_SUBTYPE_UUID,
|
|
20
21
|
VALIDATOR_SUBTYPE_REQUIRED,
|
|
21
22
|
VALIDATOR_SUBTYPE_LENGTH,
|
|
22
23
|
FIELD_ATTR_MAX_LENGTH,
|
|
24
|
+
FIELD_ATTR_PRECISION,
|
|
25
|
+
FIELD_ATTR_SCALE,
|
|
23
26
|
FIELD_ATTR_REQUIRED,
|
|
24
27
|
FIELD_ATTR_UNIQUE,
|
|
25
28
|
FIELD_ATTR_DEFAULT,
|
|
26
29
|
FIELD_ATTR_OBJECT_REF,
|
|
27
30
|
VALIDATOR_ATTR_MAX,
|
|
31
|
+
FIELD_ATTR_DB_COLUMN_TYPE,
|
|
32
|
+
DB_COLUMN_TYPE_UUID,
|
|
33
|
+
DB_COLUMN_TYPE_JSONB,
|
|
34
|
+
DB_COLUMN_TYPE_TIMESTAMP_WITH_TZ,
|
|
28
35
|
} from "@metaobjectsdev/metadata";
|
|
29
36
|
import { columnNameFromField } from "./naming.js";
|
|
30
37
|
import { enumValues } from "./enum-meta.js";
|
|
@@ -76,6 +83,7 @@ function sqliteJsonArrayElementTsType(subType: string): string | undefined {
|
|
|
76
83
|
case FIELD_SUBTYPE_STRING:
|
|
77
84
|
case FIELD_SUBTYPE_ENUM:
|
|
78
85
|
case FIELD_SUBTYPE_CLASS:
|
|
86
|
+
case FIELD_SUBTYPE_UUID:
|
|
79
87
|
case FIELD_SUBTYPE_DATE:
|
|
80
88
|
case FIELD_SUBTYPE_TIME:
|
|
81
89
|
case FIELD_SUBTYPE_TIMESTAMP:
|
|
@@ -137,6 +145,42 @@ export interface ColumnSpec {
|
|
|
137
145
|
| { kind: "objectRef"; name: string; module: string };
|
|
138
146
|
}
|
|
139
147
|
|
|
148
|
+
/**
|
|
149
|
+
* R6 Plan 2b: a physical @dbColumnType override selects the Drizzle COLUMN type
|
|
150
|
+
* instead of the subtype default — mirroring the override-precedence shape in
|
|
151
|
+
* migrate-ts/src/expected-schema.ts (override checked first, wins over the
|
|
152
|
+
* subtype default), so codegen and DDL agree.
|
|
153
|
+
*
|
|
154
|
+
* Postgres-only: uuid/jsonb/timestamptz are Postgres physical column types with
|
|
155
|
+
* no native SQLite analogue, so on SQLite the attribute is ignored (the caller
|
|
156
|
+
* falls through to the subtype default). The native TS binding is keyed on
|
|
157
|
+
* field.subType elsewhere and is unaffected — a `field.string @dbColumnType:uuid`
|
|
158
|
+
* field stays a TS `string`; only the Drizzle column function changes.
|
|
159
|
+
*
|
|
160
|
+
* The loader has already validated the (subtype × value) pairing, so an
|
|
161
|
+
* unrecognized value never reaches here; an unknown value returns undefined
|
|
162
|
+
* (fall through to subtype default).
|
|
163
|
+
*/
|
|
164
|
+
function pgColumnTypeOverride(
|
|
165
|
+
field: MetaField,
|
|
166
|
+
): { fnName: string; fnOptions?: Record<string, unknown> } | undefined {
|
|
167
|
+
const dbColumnType = field.ownAttr(FIELD_ATTR_DB_COLUMN_TYPE);
|
|
168
|
+
if (typeof dbColumnType !== "string") return undefined;
|
|
169
|
+
switch (dbColumnType) {
|
|
170
|
+
case DB_COLUMN_TYPE_UUID:
|
|
171
|
+
return { fnName: "uuid" };
|
|
172
|
+
case DB_COLUMN_TYPE_JSONB:
|
|
173
|
+
return { fnName: "jsonb" };
|
|
174
|
+
case DB_COLUMN_TYPE_TIMESTAMP_WITH_TZ:
|
|
175
|
+
// Drizzle pg-core: timestamp(col, { withTimezone: true }) → timestamptz.
|
|
176
|
+
// mode:"string" for the same reason as the plain-timestamp branch — the
|
|
177
|
+
// schema + wire contract carry timestamps as strings.
|
|
178
|
+
return { fnName: "timestamp", fnOptions: { mode: "string", withTimezone: true } };
|
|
179
|
+
default:
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
140
184
|
/** Resolve max length from validator.length child or @maxLength attr.
|
|
141
185
|
* Uses field.validators() (effective) so inherited validators are seen. */
|
|
142
186
|
function getMaxLength(field: MetaField): number | undefined {
|
|
@@ -206,59 +250,98 @@ export function mapColumnType(
|
|
|
206
250
|
case FIELD_SUBTYPE_ENUM:
|
|
207
251
|
case FIELD_SUBTYPE_CLASS:
|
|
208
252
|
case FIELD_SUBTYPE_OBJECT:
|
|
253
|
+
case FIELD_SUBTYPE_UUID:
|
|
254
|
+
// SQLite has no native uuid type; store as TEXT (string native binding).
|
|
255
|
+
fnName = "text";
|
|
256
|
+
break;
|
|
209
257
|
default:
|
|
210
258
|
fnName = "text";
|
|
211
259
|
break;
|
|
212
260
|
}
|
|
213
261
|
}
|
|
214
262
|
} else {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
263
|
+
// A physical @dbColumnType override wins over the subtype default (Postgres
|
|
264
|
+
// only; SQLite has no native analogue and falls through above). Resolved
|
|
265
|
+
// first so the override-precedence matches migrate-ts's expected-schema.
|
|
266
|
+
const override = pgColumnTypeOverride(field);
|
|
267
|
+
if (override !== undefined) {
|
|
268
|
+
// Override fully determines the physical type; skip the subtype switch.
|
|
269
|
+
fnName = override.fnName;
|
|
270
|
+
fnOptions = override.fnOptions;
|
|
271
|
+
} else {
|
|
272
|
+
switch (subType) {
|
|
273
|
+
case FIELD_SUBTYPE_BOOLEAN:
|
|
274
|
+
fnName = "boolean";
|
|
275
|
+
break;
|
|
276
|
+
case FIELD_SUBTYPE_INT:
|
|
277
|
+
fnName = "integer";
|
|
278
|
+
break;
|
|
279
|
+
case FIELD_SUBTYPE_CURRENCY:
|
|
280
|
+
case FIELD_SUBTYPE_LONG:
|
|
281
|
+
fnName = "bigint";
|
|
282
|
+
fnOptions = { mode: "number" };
|
|
283
|
+
break;
|
|
284
|
+
case FIELD_SUBTYPE_DOUBLE:
|
|
285
|
+
fnName = "doublePrecision";
|
|
286
|
+
break;
|
|
287
|
+
case FIELD_SUBTYPE_FLOAT:
|
|
288
|
+
fnName = "real";
|
|
289
|
+
break;
|
|
290
|
+
case FIELD_SUBTYPE_DATE:
|
|
291
|
+
fnName = "date";
|
|
292
|
+
break;
|
|
293
|
+
case FIELD_SUBTYPE_TIME:
|
|
294
|
+
fnName = "time";
|
|
295
|
+
break;
|
|
296
|
+
case FIELD_SUBTYPE_TIMESTAMP:
|
|
297
|
+
// mode:"string" so the column round-trips ISO-8601 strings — the
|
|
298
|
+
// generated Zod schema validates timestamp fields as z.string() and
|
|
299
|
+
// the cross-port wire format carries timestamps as JSON strings.
|
|
300
|
+
// Drizzle's default timestamp mode is "date" (expects/returns a JS
|
|
301
|
+
// Date and calls value.toISOString() on write), which is internally
|
|
302
|
+
// inconsistent with the string-typed schema + wire contract and
|
|
303
|
+
// throws on a string write. See SP-B api-contract-generated lane.
|
|
304
|
+
fnName = "timestamp";
|
|
305
|
+
fnOptions = { mode: "string" };
|
|
306
|
+
break;
|
|
307
|
+
case FIELD_SUBTYPE_UUID:
|
|
308
|
+
// Postgres native uuid column; native TS binding stays `string`.
|
|
309
|
+
fnName = "uuid";
|
|
310
|
+
break;
|
|
311
|
+
case FIELD_SUBTYPE_DECIMAL: {
|
|
312
|
+
// Drizzle pg `numeric` infers as a TS `string` (precision-exact); the
|
|
313
|
+
// native TS binding for field.decimal is `string` to match. Read the
|
|
314
|
+
// declared @precision/@scale (mirroring migrate-ts/expected-schema so
|
|
315
|
+
// codegen and the DDL agree), falling back to a sane default.
|
|
316
|
+
fnName = "numeric";
|
|
317
|
+
const precision = field.ownAttr(FIELD_ATTR_PRECISION);
|
|
318
|
+
const scale = field.ownAttr(FIELD_ATTR_SCALE);
|
|
319
|
+
if (typeof precision === "number" && typeof scale === "number") {
|
|
320
|
+
fnOptions = { precision, scale };
|
|
321
|
+
} else if (typeof precision === "number") {
|
|
322
|
+
fnOptions = { precision };
|
|
323
|
+
} else {
|
|
324
|
+
fnOptions = { precision: 19, scale: 4 };
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
253
327
|
}
|
|
254
|
-
|
|
328
|
+
case FIELD_SUBTYPE_STRING: {
|
|
329
|
+
const maxLen = getMaxLength(field);
|
|
330
|
+
if (maxLen !== undefined) {
|
|
331
|
+
fnName = "varchar";
|
|
332
|
+
fnOptions = { length: maxLen };
|
|
333
|
+
} else {
|
|
334
|
+
fnName = "text";
|
|
335
|
+
}
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
case FIELD_SUBTYPE_ENUM:
|
|
339
|
+
case FIELD_SUBTYPE_CLASS:
|
|
340
|
+
case FIELD_SUBTYPE_OBJECT:
|
|
341
|
+
default:
|
|
342
|
+
fnName = "text";
|
|
343
|
+
break;
|
|
255
344
|
}
|
|
256
|
-
case FIELD_SUBTYPE_ENUM:
|
|
257
|
-
case FIELD_SUBTYPE_CLASS:
|
|
258
|
-
case FIELD_SUBTYPE_OBJECT:
|
|
259
|
-
default:
|
|
260
|
-
fnName = "text";
|
|
261
|
-
break;
|
|
262
345
|
}
|
|
263
346
|
}
|
|
264
347
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// FR-015 callable-wrapper generator factory.
|
|
2
|
+
//
|
|
3
|
+
// One file per entity backed by a stored procedure or table function:
|
|
4
|
+
// <outDir>/<package>/<Entity>.callable.ts
|
|
5
|
+
//
|
|
6
|
+
// Non-callable entities are skipped (filter narrows the iteration set so
|
|
7
|
+
// vanilla tables/views never emit a noisy empty file).
|
|
8
|
+
|
|
9
|
+
import type { MetaObject } from "@metaobjectsdev/metadata";
|
|
10
|
+
import { perEntity, type Generator, type GeneratorFactory } from "../generator.js";
|
|
11
|
+
import { renderCallableFile, isCallableEntity } from "../templates/callable-file.js";
|
|
12
|
+
import { formatTs } from "../format.js";
|
|
13
|
+
import { entityOutputPath } from "../import-path.js";
|
|
14
|
+
|
|
15
|
+
export interface CallableFileOpts {
|
|
16
|
+
filter?: (entity: MetaObject) => boolean;
|
|
17
|
+
target?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const callableFile = function callableFile(opts?: CallableFileOpts): Generator {
|
|
21
|
+
const userFilter = opts?.filter;
|
|
22
|
+
const filter: (e: MetaObject) => boolean = userFilter
|
|
23
|
+
? (e) => isCallableEntity(e) && userFilter(e)
|
|
24
|
+
: isCallableEntity;
|
|
25
|
+
|
|
26
|
+
const generator: Generator = {
|
|
27
|
+
name: "callable-file",
|
|
28
|
+
filter,
|
|
29
|
+
generate: perEntity(async (entity, ctx) => {
|
|
30
|
+
return {
|
|
31
|
+
path: entityOutputPath(
|
|
32
|
+
ctx.config.outputLayout ?? "flat",
|
|
33
|
+
entity.package,
|
|
34
|
+
`${entity.name}.callable.ts`,
|
|
35
|
+
),
|
|
36
|
+
content: await formatTs(renderCallableFile(entity)),
|
|
37
|
+
};
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
if (opts?.target) {
|
|
41
|
+
generator.target = opts.target;
|
|
42
|
+
}
|
|
43
|
+
return generator;
|
|
44
|
+
} as GeneratorFactory<CallableFileOpts>;
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
FIELD_SUBTYPE_OBJECT,
|
|
26
26
|
FIELD_SUBTYPE_STRING,
|
|
27
27
|
FIELD_SUBTYPE_CLASS,
|
|
28
|
+
FIELD_SUBTYPE_UUID,
|
|
28
29
|
FIELD_SUBTYPE_INT,
|
|
29
30
|
FIELD_SUBTYPE_SHORT,
|
|
30
31
|
FIELD_SUBTYPE_BYTE,
|
|
@@ -78,13 +79,16 @@ export interface BuildDocDataOpts {
|
|
|
78
79
|
const SCALAR_TS_BY_SUBTYPE: Record<string, string> = {
|
|
79
80
|
[FIELD_SUBTYPE_STRING]: "string",
|
|
80
81
|
[FIELD_SUBTYPE_CLASS]: "string",
|
|
82
|
+
[FIELD_SUBTYPE_UUID]: "string",
|
|
81
83
|
[FIELD_SUBTYPE_INT]: "number",
|
|
82
84
|
[FIELD_SUBTYPE_SHORT]: "number",
|
|
83
85
|
[FIELD_SUBTYPE_BYTE]: "number",
|
|
84
86
|
[FIELD_SUBTYPE_LONG]: "number",
|
|
85
87
|
[FIELD_SUBTYPE_DOUBLE]: "number",
|
|
86
88
|
[FIELD_SUBTYPE_FLOAT]: "number",
|
|
87
|
-
|
|
89
|
+
// field.decimal is precision-exact: surfaced as a TS `string` (Drizzle pg
|
|
90
|
+
// `numeric` infers `string`). Keep the docs scalar mapping in lockstep.
|
|
91
|
+
[FIELD_SUBTYPE_DECIMAL]: "string",
|
|
88
92
|
[FIELD_SUBTYPE_CURRENCY]: "number",
|
|
89
93
|
[FIELD_SUBTYPE_BOOLEAN]: "boolean",
|
|
90
94
|
[FIELD_SUBTYPE_DATE]: "string",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// server/typescript/packages/codegen-ts/src/generators/extractor-file.ts
|
|
2
|
+
//
|
|
3
|
+
// Stock generator that emits one <TemplateName>.extractor.ts file per declared template.output
|
|
4
|
+
// node whose @format is json/xml. Wraps renderExtractor() from templates/extractor.ts.
|
|
5
|
+
//
|
|
6
|
+
// The emitted extractor sits over the output-parser's nested-capable extract and turns dirty LLM
|
|
7
|
+
// text into the strict typed payload graph. It imports from the sibling <Name>.output.ts (the
|
|
8
|
+
// output-parser) and ./payloads.ts, so run it alongside outputParser() + a payload generator.
|
|
9
|
+
//
|
|
10
|
+
// Consumer wiring (metaobjects.config.ts):
|
|
11
|
+
// generators: [..., outputParser(), extractor()]
|
|
12
|
+
//
|
|
13
|
+
// Custom output directory:
|
|
14
|
+
// generators: [..., extractor({ outDir: "src/generated/outputs" })]
|
|
15
|
+
|
|
16
|
+
import { TYPE_TEMPLATE, TEMPLATE_SUBTYPE_OUTPUT, TEMPLATE_ATTR_FORMAT } from "@metaobjectsdev/metadata";
|
|
17
|
+
import {
|
|
18
|
+
type EmittedFile,
|
|
19
|
+
type Generator,
|
|
20
|
+
type GeneratorFactory,
|
|
21
|
+
oncePerRun,
|
|
22
|
+
} from "../generator.js";
|
|
23
|
+
import { renderExtractor } from "../templates/extractor.js";
|
|
24
|
+
|
|
25
|
+
export interface ExtractorOpts {
|
|
26
|
+
/** Output directory prefix relative to the target's outDir. Default: "" (root). */
|
|
27
|
+
outDir?: string;
|
|
28
|
+
/** Optional named output target (registry key). Defaults to "default". */
|
|
29
|
+
target?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const extractor = function extractor(opts?: ExtractorOpts): Generator {
|
|
33
|
+
const dirPrefix = opts?.outDir ? `${opts.outDir.replace(/\/$/, "")}/` : "";
|
|
34
|
+
const generator: Generator = {
|
|
35
|
+
name: "extractor",
|
|
36
|
+
generate: oncePerRun((_entities, ctx) => {
|
|
37
|
+
const outputs = ctx.loadedRoot
|
|
38
|
+
.ownChildren()
|
|
39
|
+
.filter((c) => c.type === TYPE_TEMPLATE && c.subType === TEMPLATE_SUBTYPE_OUTPUT);
|
|
40
|
+
const files: EmittedFile[] = [];
|
|
41
|
+
for (const t of outputs) {
|
|
42
|
+
// The extract tier requires the extract API, which only json/xml output-parsers emit.
|
|
43
|
+
const format = ((t.ownAttr(TEMPLATE_ATTR_FORMAT) as string | undefined) ?? "text").toLowerCase();
|
|
44
|
+
if (format !== "json" && format !== "xml") continue;
|
|
45
|
+
files.push({
|
|
46
|
+
path: `${dirPrefix}${t.name}.extractor.ts`,
|
|
47
|
+
content: renderExtractor(ctx.loadedRoot, t.name),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return files;
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
if (opts?.target) {
|
|
54
|
+
generator.target = opts.target;
|
|
55
|
+
}
|
|
56
|
+
return generator;
|
|
57
|
+
} as GeneratorFactory<ExtractorOpts>;
|
package/src/generators/index.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export { entityFile, type EntityFileOpts } from "./entity-file.js";
|
|
2
2
|
export { queriesFile, type QueriesFileOpts } from "./queries-file.js";
|
|
3
|
+
export { callableFile, type CallableFileOpts } from "./callable-file.js";
|
|
3
4
|
export { routesFile, type RoutesFileOpts } from "./routes-file.js";
|
|
4
5
|
export { routesFileHono, type RoutesFileHonoOpts } from "./routes-file-hono.js";
|
|
5
6
|
export { barrel, type BarrelOpts } from "./barrel.js";
|
|
6
7
|
export { mermaidErDiagram, type MermaidErOptions } from "./mermaid-er.js";
|
|
7
8
|
export { promptRender, type PromptRenderOpts } from "./prompt-render-file.js";
|
|
8
9
|
export { outputParser, type OutputParserOpts } from "./output-parser-file.js";
|
|
10
|
+
export { extractor, type ExtractorOpts } from "./extractor-file.js";
|
|
9
11
|
export { outputPrompt, type OutputPromptOpts } from "./output-prompt-file.js";
|
|
12
|
+
export { renderHelper, type RenderHelperOpts } from "./render-helper-file.js";
|
|
10
13
|
export { docsFile, type DocsFileOpts } from "./docs-file.js";
|
|
11
14
|
export {
|
|
12
15
|
templateGenerator,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// server/typescript/packages/codegen-ts/src/generators/render-helper-file.ts
|
|
2
|
+
//
|
|
3
|
+
// Stock generator that emits one `<TemplateName>.render.ts` typed render helper
|
|
4
|
+
// per `template.output` node — `render<Name>(payload, provider)` wrapping the
|
|
5
|
+
// render() engine (document → string, email → EmailDocument). The mustache↔VO
|
|
6
|
+
// drift check (the render lib's verify()) is enforced at BUILD time: this
|
|
7
|
+
// generator resolves each referenced mustache through the codegen-time provider
|
|
8
|
+
// (projectProvider(ctx.projectRoot)) and FAILS codegen when a `{{field}}` isn't
|
|
9
|
+
// on the payload VO. Skips template.output nodes whose @payloadRef doesn't
|
|
10
|
+
// resolve to a value-object (same contract as the prompt/parser generators).
|
|
11
|
+
//
|
|
12
|
+
// Consumer wiring (metaobjects.config.ts):
|
|
13
|
+
// generators: [..., renderHelper()]
|
|
14
|
+
//
|
|
15
|
+
// Custom output directory:
|
|
16
|
+
// generators: [..., renderHelper({ outDir: "src/generated/render" })]
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
TYPE_OBJECT,
|
|
20
|
+
TYPE_TEMPLATE,
|
|
21
|
+
TEMPLATE_SUBTYPE_OUTPUT,
|
|
22
|
+
TEMPLATE_ATTR_PAYLOAD_REF,
|
|
23
|
+
} from "@metaobjectsdev/metadata";
|
|
24
|
+
import {
|
|
25
|
+
type EmittedFile,
|
|
26
|
+
type Generator,
|
|
27
|
+
type GeneratorFactory,
|
|
28
|
+
oncePerRun,
|
|
29
|
+
} from "../generator.js";
|
|
30
|
+
import { projectProvider } from "../render-engine/framework-provider.js";
|
|
31
|
+
import { renderRenderHelper } from "../templates/render-helper.js";
|
|
32
|
+
|
|
33
|
+
export interface RenderHelperOpts {
|
|
34
|
+
/** Output directory prefix relative to the target's outDir. Default: "" (root). */
|
|
35
|
+
outDir?: string;
|
|
36
|
+
/** Optional named output target (registry key). Defaults to "default". */
|
|
37
|
+
target?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const renderHelper = function renderHelper(opts?: RenderHelperOpts): Generator {
|
|
41
|
+
const dirPrefix = opts?.outDir ? `${opts.outDir.replace(/\/$/, "")}/` : "";
|
|
42
|
+
const generator: Generator = {
|
|
43
|
+
name: "render-helper",
|
|
44
|
+
generate: oncePerRun((_entities, ctx) => {
|
|
45
|
+
const root = ctx.loadedRoot;
|
|
46
|
+
// The codegen-time provider — layers the project's templates/ dir over the
|
|
47
|
+
// framework defaults; used to resolve + verify each referenced mustache so
|
|
48
|
+
// the build-time drift gate runs against the same texts render() will see.
|
|
49
|
+
const provider = projectProvider(ctx.projectRoot);
|
|
50
|
+
const outputs = root
|
|
51
|
+
.ownChildren()
|
|
52
|
+
.filter((c) => c.type === TYPE_TEMPLATE && c.subType === TEMPLATE_SUBTYPE_OUTPUT);
|
|
53
|
+
const files: EmittedFile[] = [];
|
|
54
|
+
for (const t of outputs) {
|
|
55
|
+
// @payloadRef must resolve to a value-object (same contract as the parser).
|
|
56
|
+
const payloadRef = t.ownAttr(TEMPLATE_ATTR_PAYLOAD_REF);
|
|
57
|
+
if (typeof payloadRef !== "string") continue;
|
|
58
|
+
const vo = root.ownChildren().find((c) => c.type === TYPE_OBJECT && c.name === payloadRef);
|
|
59
|
+
if (!vo) continue;
|
|
60
|
+
files.push({
|
|
61
|
+
// renderRenderHelper THROWS (fails codegen) on a mustache↔VO drift —
|
|
62
|
+
// intentionally NOT caught: a drifted template is a build error.
|
|
63
|
+
path: `${dirPrefix}${t.name}.render.ts`,
|
|
64
|
+
content: renderRenderHelper(root, t.name, provider),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return files;
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
if (opts?.target) {
|
|
71
|
+
generator.target = opts.target;
|
|
72
|
+
}
|
|
73
|
+
return generator;
|
|
74
|
+
} as GeneratorFactory<RenderHelperOpts>;
|