@metaobjectsdev/codegen-ts 0.21.6 → 0.22.0-rc.2
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/projection/build-projection-views.d.ts.map +1 -1
- package/dist/projection/build-projection-views.js +6 -4
- package/dist/projection/build-projection-views.js.map +1 -1
- package/dist/projection/extract-view-spec.d.ts.map +1 -1
- package/dist/projection/extract-view-spec.js +3 -3
- package/dist/projection/extract-view-spec.js.map +1 -1
- package/dist/projection/projection-detector.d.ts.map +1 -1
- package/dist/projection/projection-detector.js +7 -3
- package/dist/projection/projection-detector.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +5 -2
- package/dist/runner.js.map +1 -1
- package/dist/source-detect.d.ts.map +1 -1
- package/dist/source-detect.js +17 -15
- package/dist/source-detect.js.map +1 -1
- package/dist/templates/callable-file.d.ts.map +1 -1
- package/dist/templates/callable-file.js +4 -2
- package/dist/templates/callable-file.js.map +1 -1
- package/dist/templates/projection-decl.js +2 -2
- package/dist/templates/projection-decl.js.map +1 -1
- package/dist/templates/zod-validators.d.ts +2 -0
- package/dist/templates/zod-validators.d.ts.map +1 -1
- package/dist/templates/zod-validators.js +69 -12
- package/dist/templates/zod-validators.js.map +1 -1
- package/package.json +6 -6
- package/src/projection/build-projection-views.ts +10 -9
- package/src/projection/extract-view-spec.ts +4 -5
- package/src/projection/projection-detector.ts +7 -7
- package/src/runner.ts +5 -2
- package/src/source-detect.ts +18 -11
- package/src/templates/callable-file.ts +5 -2
- package/src/templates/projection-decl.ts +2 -2
- package/src/templates/zod-validators.ts +75 -12
|
@@ -159,6 +159,44 @@ function primaryKeyFieldNames(obj: MetaObject): Set<string> {
|
|
|
159
159
|
return new Set(primaryIdentityFieldNames(obj));
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/** PK field names the CALLER must supply on insert: an ASSIGNED primary key —
|
|
163
|
+
* a natural key or an externally-issued id, i.e. a primary identity carrying no
|
|
164
|
+
* @generation — that also has no @default to fill it.
|
|
165
|
+
*
|
|
166
|
+
* These are insert-REQUIRED regardless of @required, and that is a typing
|
|
167
|
+
* necessity rather than a policy choice: the Drizzle column for such a PK is
|
|
168
|
+
* `text("id").primaryKey()` with no default, so its inferred insert type
|
|
169
|
+
* requires the value. Deriving the PK's optionality from @required like any
|
|
170
|
+
* other field emitted `id: z.string().optional()`, and the generated
|
|
171
|
+
* `create<Entity>` — which pipes `InsertSchema.parse(data)` straight into
|
|
172
|
+
* `.values()` — then failed to compile (TS2769). It was also a semantic hole:
|
|
173
|
+
* the schema accepted a create payload with no primary key at all.
|
|
174
|
+
*
|
|
175
|
+
* Excluded, because each is already insert-optional for a real reason:
|
|
176
|
+
* • @generation increment|uuid PKs — omitted from the schema entirely
|
|
177
|
+
* (autoGenPkFieldNames), the caller never supplies them;
|
|
178
|
+
* • a PK carrying @default — the column gets that default, so omitting is
|
|
179
|
+
* legal and forcing it required would make callers repeat the default.
|
|
180
|
+
* Mirrors primaryKeyFieldNames' reasoning for the UpdateSchema: a PK column is
|
|
181
|
+
* never NULL, so PK optionality can never be read off @required alone. */
|
|
182
|
+
function assignedPkFieldNames(obj: MetaObject): Set<string> {
|
|
183
|
+
const autoGen = autoGenPkFieldNames(obj);
|
|
184
|
+
const out = new Set<string>();
|
|
185
|
+
for (const name of primaryIdentityFieldNames(obj)) {
|
|
186
|
+
if (autoGen.has(name)) continue;
|
|
187
|
+
out.add(name);
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** True when this field is an assigned PK with no @default — see
|
|
193
|
+
* assignedPkFieldNames. Kept as one predicate so the emitters and the
|
|
194
|
+
* documented-shape function (insertSchemaFields) cannot drift. */
|
|
195
|
+
function isInsertRequiredPk(field: MetaField, assignedPk: Set<string>): boolean {
|
|
196
|
+
if (!assignedPk.has(field.name)) return false;
|
|
197
|
+
return field.attr(FIELD_ATTR_DEFAULT) === undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
162
200
|
/**
|
|
163
201
|
* Emit ONLY the `<Name>InsertSchema`. Used by the value-object file emitter
|
|
164
202
|
* for metaobjects with no writable source.rdb — those have no PATCH/update
|
|
@@ -171,6 +209,7 @@ function primaryKeyFieldNames(obj: MetaObject): Set<string> {
|
|
|
171
209
|
export function renderInsertSchemaOnly(obj: MetaObject, ctx?: RenderContext): Code {
|
|
172
210
|
const z = imp("z@zod");
|
|
173
211
|
const autoGenPkFields = autoGenPkFieldNames(obj);
|
|
212
|
+
const assignedPkFields = assignedPkFieldNames(obj);
|
|
174
213
|
const tphPin = tphDiscriminatorPin(obj);
|
|
175
214
|
|
|
176
215
|
const insertFieldLines: Code[] = [];
|
|
@@ -204,7 +243,9 @@ export function renderInsertSchemaOnly(obj: MetaObject, ctx?: RenderContext): Co
|
|
|
204
243
|
: code` ${child.name}: z.string().optional().transform(() => new Date().toISOString())`,
|
|
205
244
|
);
|
|
206
245
|
} else {
|
|
207
|
-
insertFieldLines.push(
|
|
246
|
+
insertFieldLines.push(
|
|
247
|
+
code` ${child.name}: ${zodFieldExpr(child, obj, ctx, isInsertRequiredPk(child, assignedPkFields))}`,
|
|
248
|
+
);
|
|
208
249
|
}
|
|
209
250
|
}
|
|
210
251
|
|
|
@@ -246,11 +287,14 @@ export interface SchemaFieldShape {
|
|
|
246
287
|
* • @readOnly fields are omitted (DB / replication owns the write path);
|
|
247
288
|
* • a TPH subtype's @discriminator field is a pinned `z.literal(value)`;
|
|
248
289
|
* • @autoSet fields are present but optional (server fills them);
|
|
290
|
+
* • an ASSIGNED primary key with no @default is required (see
|
|
291
|
+
* assignedPkFieldNames — its Drizzle column has no default);
|
|
249
292
|
* • every other field's optionality is `fieldWillBeOptional` (not required, or
|
|
250
293
|
* carries a @default).
|
|
251
294
|
*/
|
|
252
295
|
export function insertSchemaFields(obj: MetaObject): SchemaFieldShape[] {
|
|
253
296
|
const autoGenPkFields = autoGenPkFieldNames(obj);
|
|
297
|
+
const assignedPkFields = assignedPkFieldNames(obj);
|
|
254
298
|
const tphPin = tphDiscriminatorPin(obj);
|
|
255
299
|
const out: SchemaFieldShape[] = [];
|
|
256
300
|
for (const child of obj.fields()) {
|
|
@@ -266,7 +310,10 @@ export function insertSchemaFields(obj: MetaObject): SchemaFieldShape[] {
|
|
|
266
310
|
if (autoSet === AUTO_SET_ON_CREATE || autoSet === AUTO_SET_ON_UPDATE) {
|
|
267
311
|
out.push({ name: child.name, optional: true, autoSet: true });
|
|
268
312
|
} else {
|
|
269
|
-
out.push({
|
|
313
|
+
out.push({
|
|
314
|
+
name: child.name,
|
|
315
|
+
optional: isInsertRequiredPk(child, assignedPkFields) ? false : fieldWillBeOptional(child),
|
|
316
|
+
});
|
|
270
317
|
}
|
|
271
318
|
}
|
|
272
319
|
return out;
|
|
@@ -311,6 +358,7 @@ export function renderZodValidators(obj: MetaObject, ctx?: RenderContext): Code
|
|
|
311
358
|
const z = imp("z@zod");
|
|
312
359
|
const autoGenPkFields = autoGenPkFieldNames(obj);
|
|
313
360
|
const pkFields = primaryKeyFieldNames(obj);
|
|
361
|
+
const assignedPkFields = assignedPkFieldNames(obj);
|
|
314
362
|
const tphPin = tphDiscriminatorPin(obj);
|
|
315
363
|
|
|
316
364
|
const insertFieldLines: Code[] = [];
|
|
@@ -358,7 +406,9 @@ export function renderZodValidators(obj: MetaObject, ctx?: RenderContext): Code
|
|
|
358
406
|
// field expr) so an import/restore keeps the caller's original timestamp.
|
|
359
407
|
preservingFieldLines.push(code` ${child.name}: ${zodFieldExpr(child, obj, ctx)}`);
|
|
360
408
|
} else {
|
|
361
|
-
|
|
409
|
+
// The preserving schema is an INSERT shape (import / restore / replication),
|
|
410
|
+
// so an assigned PK is required there for the same typing reason.
|
|
411
|
+
const fieldLine = code` ${child.name}: ${zodFieldExpr(child, obj, ctx, isInsertRequiredPk(child, assignedPkFields))}`;
|
|
362
412
|
insertFieldLines.push(fieldLine);
|
|
363
413
|
preservingFieldLines.push(fieldLine);
|
|
364
414
|
}
|
|
@@ -452,7 +502,14 @@ function zodScalarFor(subType: string): string {
|
|
|
452
502
|
return "z.string()"; // string/uuid/date/time/timestamp/decimal/enum on the wire
|
|
453
503
|
}
|
|
454
504
|
|
|
455
|
-
function zodFieldExpr(
|
|
505
|
+
function zodFieldExpr(
|
|
506
|
+
field: MetaField,
|
|
507
|
+
owner?: MetaObject,
|
|
508
|
+
ctx?: RenderContext,
|
|
509
|
+
/** Suppress the trailing `.optional()` — see assignedPkFieldNames. Set ONLY
|
|
510
|
+
* by the insert-shape emitters; the update/read shapes must stay optional. */
|
|
511
|
+
forceRequired = false,
|
|
512
|
+
): Code {
|
|
456
513
|
// `@dbColumnType: jsonb` on a scalar (legal only on field.string) is the
|
|
457
514
|
// sanctioned "open JSON bag" escape hatch — a genuinely untyped JSON column
|
|
458
515
|
// with no value-object to reference. Its Drizzle column is a bare `jsonb()`,
|
|
@@ -465,7 +522,7 @@ function zodFieldExpr(field: MetaField, owner?: MetaObject, ctx?: RenderContext)
|
|
|
465
522
|
if (field.attr(FIELD_ATTR_DB_COLUMN_TYPE) === DB_COLUMN_TYPE_JSONB) {
|
|
466
523
|
let base: Code = code`z.unknown()`;
|
|
467
524
|
if (field.resolvedIsArray()) base = code`z.array(${base})`;
|
|
468
|
-
return appendValidatorChain(base, field);
|
|
525
|
+
return appendValidatorChain(base, field, forceRequired);
|
|
469
526
|
}
|
|
470
527
|
|
|
471
528
|
// FIELD_SUBTYPE_OBJECT: emit z.array(<Ref>InsertSchema) / <Ref>InsertSchema
|
|
@@ -490,13 +547,13 @@ function zodFieldExpr(field: MetaField, owner?: MetaObject, ctx?: RenderContext)
|
|
|
490
547
|
const refImp = imp(`${refName}InsertSchema@${moduleSpec}`);
|
|
491
548
|
let base: Code = code`${refImp}`;
|
|
492
549
|
if (field.resolvedIsArray()) base = code`z.array(${base})`;
|
|
493
|
-
return appendValidatorChain(base, field);
|
|
550
|
+
return appendValidatorChain(base, field, forceRequired);
|
|
494
551
|
}
|
|
495
552
|
// No resolvable @objectRef — fall through to z.unknown(); downstream code
|
|
496
553
|
// can still pass a value through but loses validation.
|
|
497
554
|
let base: Code = code`z.unknown()`;
|
|
498
555
|
if (field.resolvedIsArray()) base = code`z.array(${base})`;
|
|
499
|
-
return appendValidatorChain(base, field);
|
|
556
|
+
return appendValidatorChain(base, field, forceRequired);
|
|
500
557
|
}
|
|
501
558
|
|
|
502
559
|
// field.map → z.record(z.string(), V): value is a VO's InsertSchema (@objectRef)
|
|
@@ -509,10 +566,10 @@ function zodFieldExpr(field: MetaField, owner?: MetaObject, ctx?: RenderContext)
|
|
|
509
566
|
? valueObjectModuleSpecifier(refName, ctx.packageOf, owner.package, ctx.outputLayout, ctx.extStyle)
|
|
510
567
|
: `./${refName}.js`;
|
|
511
568
|
const refImp = imp(`${refName}InsertSchema@${moduleSpec}`);
|
|
512
|
-
return appendValidatorChain(code`z.record(z.string(), ${refImp})`, field);
|
|
569
|
+
return appendValidatorChain(code`z.record(z.string(), ${refImp})`, field, forceRequired);
|
|
513
570
|
}
|
|
514
571
|
const vt = field.attr(FIELD_ATTR_VALUE_TYPE);
|
|
515
|
-
return appendValidatorChain(code`z.record(z.string(), ${zodScalarFor(typeof vt === "string" ? vt : "string")})`, field);
|
|
572
|
+
return appendValidatorChain(code`z.record(z.string(), ${zodScalarFor(typeof vt === "string" ? vt : "string")})`, field, forceRequired);
|
|
516
573
|
}
|
|
517
574
|
|
|
518
575
|
let baseStr: string;
|
|
@@ -573,7 +630,7 @@ function zodFieldExpr(field: MetaField, owner?: MetaObject, ctx?: RenderContext)
|
|
|
573
630
|
const sharedConst = imp(`${constName}@${spec}`);
|
|
574
631
|
let base: Code = code`${sharedConst}`;
|
|
575
632
|
if (field.resolvedIsArray()) base = code`z.array(${base})`;
|
|
576
|
-
return appendValidatorChain(base, field);
|
|
633
|
+
return appendValidatorChain(base, field, forceRequired);
|
|
577
634
|
}
|
|
578
635
|
}
|
|
579
636
|
baseStr = zodEnumExpr(values);
|
|
@@ -612,7 +669,7 @@ function zodFieldExpr(field: MetaField, owner?: MetaObject, ctx?: RenderContext)
|
|
|
612
669
|
}
|
|
613
670
|
|
|
614
671
|
if (field.resolvedIsArray()) baseStr = `z.array(${baseStr})`;
|
|
615
|
-
return appendValidatorChain(code`${baseStr}`, field);
|
|
672
|
+
return appendValidatorChain(code`${baseStr}`, field, forceRequired);
|
|
616
673
|
}
|
|
617
674
|
|
|
618
675
|
/** Mirrors the optional-or-not decision inside appendValidatorChain so the update-schema
|
|
@@ -635,7 +692,7 @@ const NUMERIC_FIELD_SUBTYPES = new Set<string>([
|
|
|
635
692
|
* - numeric (scalar) → .min/.max = numeric value (validator.numeric)
|
|
636
693
|
* - array (any element) → .min/.max = element count (validator.array)
|
|
637
694
|
*/
|
|
638
|
-
function appendValidatorChain(base: Code, field: MetaField): Code {
|
|
695
|
+
function appendValidatorChain(base: Code, field: MetaField, forceRequired = false): Code {
|
|
639
696
|
let isRequired = field.attr(FIELD_ATTR_REQUIRED) === true;
|
|
640
697
|
let maxLen: number | undefined = field.attr(FIELD_ATTR_MAX_LENGTH) as number | undefined;
|
|
641
698
|
let minLen: number | undefined;
|
|
@@ -709,6 +766,12 @@ function appendValidatorChain(base: Code, field: MetaField): Code {
|
|
|
709
766
|
if (numMax !== undefined) chain = code`${chain}.max(${numMax})`;
|
|
710
767
|
}
|
|
711
768
|
|
|
769
|
+
// An assigned PK is insert-REQUIRED whatever @required says — its Drizzle
|
|
770
|
+
// column has no default, so an optional value cannot typecheck into
|
|
771
|
+
// `.values()`. Only the caller knows this (the update/read shapes must stay
|
|
772
|
+
// optional), hence the explicit opt-in. See assignedPkFieldNames.
|
|
773
|
+
if (forceRequired) return chain;
|
|
774
|
+
|
|
712
775
|
// Fields with DB-level defaults are optional in the InsertSchema: the caller
|
|
713
776
|
// can omit them and the DB will fill in. Otherwise required-with-default
|
|
714
777
|
// would force callers to repeat the default at every call site.
|