@metaobjectsdev/codegen-ts 0.16.0 → 0.17.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.
Files changed (66) hide show
  1. package/dist/column-mapper.d.ts +10 -0
  2. package/dist/column-mapper.d.ts.map +1 -1
  3. package/dist/column-mapper.js +21 -2
  4. package/dist/column-mapper.js.map +1 -1
  5. package/dist/naming.d.ts +4 -0
  6. package/dist/naming.d.ts.map +1 -1
  7. package/dist/naming.js +6 -0
  8. package/dist/naming.js.map +1 -1
  9. package/dist/projection/build-projection-views.d.ts +5 -1
  10. package/dist/projection/build-projection-views.d.ts.map +1 -1
  11. package/dist/projection/build-projection-views.js +165 -47
  12. package/dist/projection/build-projection-views.js.map +1 -1
  13. package/dist/projection/extract-view-spec.d.ts +8 -1
  14. package/dist/projection/extract-view-spec.d.ts.map +1 -1
  15. package/dist/projection/extract-view-spec.js +430 -29
  16. package/dist/projection/extract-view-spec.js.map +1 -1
  17. package/dist/projection/view-ddl-emit.d.ts.map +1 -1
  18. package/dist/projection/view-ddl-emit.js +142 -25
  19. package/dist/projection/view-ddl-emit.js.map +1 -1
  20. package/dist/projection/view-spec.d.ts +101 -3
  21. package/dist/projection/view-spec.d.ts.map +1 -1
  22. package/dist/templates/drizzle-schema.d.ts.map +1 -1
  23. package/dist/templates/drizzle-schema.js +9 -0
  24. package/dist/templates/drizzle-schema.js.map +1 -1
  25. package/dist/templates/entity-file.d.ts.map +1 -1
  26. package/dist/templates/entity-file.js +39 -3
  27. package/dist/templates/entity-file.js.map +1 -1
  28. package/dist/templates/inferred-types.d.ts +1 -1
  29. package/dist/templates/inferred-types.d.ts.map +1 -1
  30. package/dist/templates/inferred-types.js +13 -1
  31. package/dist/templates/inferred-types.js.map +1 -1
  32. package/dist/templates/projection-decl.d.ts.map +1 -1
  33. package/dist/templates/projection-decl.js +9 -70
  34. package/dist/templates/projection-decl.js.map +1 -1
  35. package/dist/templates/queries-file.d.ts.map +1 -1
  36. package/dist/templates/queries-file.js +117 -43
  37. package/dist/templates/queries-file.js.map +1 -1
  38. package/dist/templates/queries.d.ts +21 -4
  39. package/dist/templates/queries.d.ts.map +1 -1
  40. package/dist/templates/queries.js +48 -12
  41. package/dist/templates/queries.js.map +1 -1
  42. package/dist/templates/view-decl.d.ts +28 -0
  43. package/dist/templates/view-decl.d.ts.map +1 -0
  44. package/dist/templates/view-decl.js +107 -0
  45. package/dist/templates/view-decl.js.map +1 -0
  46. package/dist/templates/zod-validators.d.ts +6 -0
  47. package/dist/templates/zod-validators.d.ts.map +1 -1
  48. package/dist/templates/zod-validators.js +53 -7
  49. package/dist/templates/zod-validators.js.map +1 -1
  50. package/package.json +6 -6
  51. package/src/column-mapper.ts +26 -1
  52. package/src/naming.ts +7 -0
  53. package/src/projection/build-projection-views.ts +211 -50
  54. package/src/projection/extract-view-spec.ts +468 -29
  55. package/src/projection/view-ddl-emit.ts +158 -24
  56. package/src/projection/view-spec.ts +104 -3
  57. package/src/reference/entity.ts +11 -1
  58. package/src/reference/queries.ts +4 -1
  59. package/src/templates/drizzle-schema.ts +7 -0
  60. package/src/templates/entity-file.ts +46 -3
  61. package/src/templates/inferred-types.ts +18 -1
  62. package/src/templates/projection-decl.ts +8 -74
  63. package/src/templates/queries-file.ts +133 -48
  64. package/src/templates/queries.ts +50 -11
  65. package/src/templates/view-decl.ts +128 -0
  66. package/src/templates/zod-validators.ts +54 -9
@@ -73,6 +73,19 @@ export function isTphSubtype(obj: MetaObject): boolean {
73
73
  return tphDiscriminatorPin(obj) !== undefined;
74
74
  }
75
75
 
76
+ /** True when this object declares at least one @autoSet timestamp field
77
+ * (onCreate or onUpdate). Drives whether codegen emits the #203
78
+ * `<Entity>InsertPreservingSchema` + `insertPreserving<Entity>` escape hatch —
79
+ * a plain entity has nothing to preserve, so both are omitted. Resolving
80
+ * (ADR-0039): honors an @autoSet inherited via extends. */
81
+ export function hasAutoSetFields(obj: MetaObject): boolean {
82
+ for (const child of obj.fields()) {
83
+ const autoSet = child.attr(FIELD_ATTR_AUTO_SET);
84
+ if (autoSet === AUTO_SET_ON_CREATE || autoSet === AUTO_SET_ON_UPDATE) return true;
85
+ }
86
+ return false;
87
+ }
88
+
76
89
  /**
77
90
  * FR-017 Tier 2 — the per-subtype FULL read schema `<Sub>Schema`. Unlike the
78
91
  * insert schema, this includes every effective field (PK included) so a raw DB
@@ -162,7 +175,9 @@ export function renderInsertSchemaOnly(obj: MetaObject, ctx?: RenderContext): Co
162
175
  // FR-013: @readOnly fields are populated by DB / replication / external
163
176
  // owner; the application has no path to write them. Exclude from the
164
177
  // create-shape schema entirely.
165
- if (child.attr(FIELD_ATTR_READ_ONLY) === true) continue;
178
+ // #213 — a derived (origin-bearing) field is read-only (FR-024 §7 / ADR-0028):
179
+ // excluded from the Insert/Update/preserving schemas, exactly like @readOnly.
180
+ if (child.attr(FIELD_ATTR_READ_ONLY) === true || child.isDerived()) continue;
166
181
 
167
182
  // FR-017 Tier 1: TPH subtype pins its discriminator field to z.literal(...).
168
183
  if (tphPin !== undefined && child.name === tphPin.fieldName) {
@@ -226,7 +241,9 @@ export function insertSchemaFields(obj: MetaObject): SchemaFieldShape[] {
226
241
  const out: SchemaFieldShape[] = [];
227
242
  for (const child of obj.fields()) {
228
243
  if (autoGenPkFields.has(child.name)) continue;
229
- if (child.attr(FIELD_ATTR_READ_ONLY) === true) continue;
244
+ // #213 — a derived (origin-bearing) field is read-only (FR-024 §7 / ADR-0028):
245
+ // excluded from the Insert/Update/preserving schemas, exactly like @readOnly.
246
+ if (child.attr(FIELD_ATTR_READ_ONLY) === true || child.isDerived()) continue;
230
247
  if (tphPin !== undefined && child.name === tphPin.fieldName) {
231
248
  out.push({ name: child.name, optional: false, pinnedLiteral: tphPin.value });
232
249
  continue;
@@ -256,7 +273,9 @@ export function updateSchemaFields(obj: MetaObject): SchemaFieldShape[] {
256
273
  const out: SchemaFieldShape[] = [];
257
274
  for (const child of obj.fields()) {
258
275
  if (autoGenPkFields.has(child.name)) continue;
259
- if (child.attr(FIELD_ATTR_READ_ONLY) === true) continue;
276
+ // #213 — a derived (origin-bearing) field is read-only (FR-024 §7 / ADR-0028):
277
+ // excluded from the Insert/Update/preserving schemas, exactly like @readOnly.
278
+ if (child.attr(FIELD_ATTR_READ_ONLY) === true || child.isDerived()) continue;
260
279
  // TPH subtype discriminator: omitted from the update schema entirely.
261
280
  if (tphPin !== undefined && child.name === tphPin.fieldName) continue;
262
281
  const autoSet = child.attr(FIELD_ATTR_AUTO_SET);
@@ -282,22 +301,29 @@ export function renderZodValidators(obj: MetaObject, ctx?: RenderContext): Code
282
301
 
283
302
  const insertFieldLines: Code[] = [];
284
303
  const updateFieldLines: Code[] = [];
304
+ // #203 — the `insertPreserving` escape hatch's schema: same shape as the insert
305
+ // schema, but @autoSet columns are written VERBATIM (no create-time now()
306
+ // transform). Only emitted when the entity declares @autoSet fields.
307
+ const preservingFieldLines: Code[] = [];
308
+ const emitPreserving = hasAutoSetFields(obj);
285
309
  for (const child of obj.fields()) {
286
310
  if (autoGenPkFields.has(child.name)) continue;
287
311
  // FR-013: @readOnly fields appear in neither InsertSchema nor UpdateSchema.
288
312
  // The DB / trigger / replication owns the write path; the app must not
289
313
  // pass these values in POST/PATCH bodies (routesFile enforces the same
290
314
  // contract at the boundary with a 400 response).
291
- if (child.attr(FIELD_ATTR_READ_ONLY) === true) continue;
315
+ // #213 — a derived (origin-bearing) field is read-only (FR-024 §7 / ADR-0028):
316
+ // excluded from the Insert/Update/preserving schemas, exactly like @readOnly.
317
+ if (child.attr(FIELD_ATTR_READ_ONLY) === true || child.isDerived()) continue;
292
318
 
293
319
  // FR-017 Tier 1: TPH subtype pins its discriminator field to z.literal(...).
294
320
  // The discriminator is implicit on subtype rows (controlled by URL / insert
295
321
  // path) — the app never writes it via the body and never updates it.
296
322
  // Insert: pinned literal. Update: omitted entirely (clients can't change subtype).
297
323
  if (tphPin !== undefined && child.name === tphPin.fieldName) {
298
- insertFieldLines.push(
299
- code` ${child.name}: z.literal(${JSON.stringify(tphPin.value)})`,
300
- );
324
+ const litLine = code` ${child.name}: z.literal(${JSON.stringify(tphPin.value)})`;
325
+ insertFieldLines.push(litLine);
326
+ preservingFieldLines.push(litLine);
301
327
  continue;
302
328
  }
303
329
 
@@ -308,8 +334,13 @@ export function renderZodValidators(obj: MetaObject, ctx?: RenderContext): Code
308
334
  insertFieldLines.push(
309
335
  code` ${child.name}: z.string().optional().transform(() => new Date().toISOString())`,
310
336
  );
337
+ // Preserving schema: the @autoSet column is validated verbatim (its natural
338
+ // field expr) so an import/restore keeps the caller's original timestamp.
339
+ preservingFieldLines.push(code` ${child.name}: ${zodFieldExpr(child, obj, ctx)}`);
311
340
  } else {
312
- insertFieldLines.push(code` ${child.name}: ${zodFieldExpr(child, obj, ctx)}`);
341
+ const fieldLine = code` ${child.name}: ${zodFieldExpr(child, obj, ctx)}`;
342
+ insertFieldLines.push(fieldLine);
343
+ preservingFieldLines.push(fieldLine);
313
344
  }
314
345
 
315
346
  // Update schema: @autoSet onCreate → omit entirely; onUpdate → transform
@@ -339,10 +370,24 @@ export function renderZodValidators(obj: MetaObject, ctx?: RenderContext): Code
339
370
 
340
371
  const insertSchemaName = `${obj.name}InsertSchema`;
341
372
  const updateSchemaName = `${obj.name}UpdateSchema`;
373
+ const preservingSchemaName = `${obj.name}InsertPreservingSchema`;
342
374
 
343
375
  const docs = renderDocsFor(obj);
344
376
  const docsPrefix = docs ? `${docs}\n` : "";
345
377
 
378
+ // #203 — emit the preserving insert-shape only for @autoSet entities (nothing to
379
+ // preserve otherwise). Backs `insertPreserving<Entity>` (import/restore/replication).
380
+ const preservingBlock = emitPreserving
381
+ ? code`
382
+
383
+ /** Insert-shape for import / restore / replication of ${obj.name}: identical to
384
+ * ${insertSchemaName}, but the @autoSet timestamp columns are written VERBATIM
385
+ * (no create-time now() stamp) so the caller's original values are preserved. */
386
+ export const ${preservingSchemaName} = ${z}.object({
387
+ ${joinCode(preservingFieldLines, { on: ",\n" })}
388
+ });`
389
+ : code``;
390
+
346
391
  return code`
347
392
  ${docsPrefix}export const ${insertSchemaName} = ${z}.object({
348
393
  ${joinCode(insertFieldLines, { on: ",\n" })}
@@ -354,7 +399,7 @@ ${joinCode(updateFieldLines, { on: ",\n" })}
354
399
 
355
400
  /** Typed patch shape for ${obj.name}: every settable field, optional (FR-035 PATCH). A
356
401
  * renamed/dropped field is a compile error at every \`update${obj.name}\` call site. */
357
- export type ${obj.name}Patch = ${z}.input<typeof ${updateSchemaName}>;
402
+ export type ${obj.name}Patch = ${z}.input<typeof ${updateSchemaName}>;${preservingBlock}
358
403
  `;
359
404
  }
360
405