@metaobjectsdev/codegen-ts 0.21.5 → 0.22.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 (50) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +12 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/projection/build-projection-views.d.ts.map +1 -1
  6. package/dist/projection/build-projection-views.js +6 -4
  7. package/dist/projection/build-projection-views.js.map +1 -1
  8. package/dist/projection/extract-view-spec.d.ts.map +1 -1
  9. package/dist/projection/extract-view-spec.js +3 -3
  10. package/dist/projection/extract-view-spec.js.map +1 -1
  11. package/dist/projection/projection-detector.d.ts.map +1 -1
  12. package/dist/projection/projection-detector.js +7 -3
  13. package/dist/projection/projection-detector.js.map +1 -1
  14. package/dist/runner.d.ts.map +1 -1
  15. package/dist/runner.js +5 -2
  16. package/dist/runner.js.map +1 -1
  17. package/dist/source-detect.d.ts.map +1 -1
  18. package/dist/source-detect.js +17 -15
  19. package/dist/source-detect.js.map +1 -1
  20. package/dist/templates/callable-file.d.ts.map +1 -1
  21. package/dist/templates/callable-file.js +4 -2
  22. package/dist/templates/callable-file.js.map +1 -1
  23. package/dist/templates/entity-file.d.ts.map +1 -1
  24. package/dist/templates/entity-file.js +4 -1
  25. package/dist/templates/entity-file.js.map +1 -1
  26. package/dist/templates/projection-decl.d.ts.map +1 -1
  27. package/dist/templates/projection-decl.js +8 -4
  28. package/dist/templates/projection-decl.js.map +1 -1
  29. package/dist/templates/view-decl.d.ts +9 -0
  30. package/dist/templates/view-decl.d.ts.map +1 -1
  31. package/dist/templates/view-decl.js +14 -6
  32. package/dist/templates/view-decl.js.map +1 -1
  33. package/dist/templates/zod-validators.d.ts +4 -0
  34. package/dist/templates/zod-validators.d.ts.map +1 -1
  35. package/dist/templates/zod-validators.js +3 -2
  36. package/dist/templates/zod-validators.js.map +1 -1
  37. package/package.json +6 -6
  38. package/src/index.ts +14 -0
  39. package/src/projection/build-projection-views.ts +10 -9
  40. package/src/projection/extract-view-spec.ts +4 -5
  41. package/src/projection/projection-detector.ts +7 -7
  42. package/src/reference/entity.ts +6 -1
  43. package/src/reference/queries.ts +6 -1
  44. package/src/runner.ts +5 -2
  45. package/src/source-detect.ts +18 -11
  46. package/src/templates/callable-file.ts +5 -2
  47. package/src/templates/entity-file.ts +4 -1
  48. package/src/templates/projection-decl.ts +8 -4
  49. package/src/templates/view-decl.ts +23 -7
  50. package/src/templates/zod-validators.ts +3 -2
@@ -12,7 +12,7 @@ import { FIELD_ATTR_OBJECT_REF } 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";
15
- import { renderZodValidators, isTphSubtype } from "./zod-validators.js";
15
+ import { renderZodValidators, isTphSubtype, primaryIdentityFieldNames } from "./zod-validators.js";
16
16
  import { renderEntityConstants } from "./entity-constants.js";
17
17
  import { renderFilterAllowlist, renderSortAllowlist } from "./filter-allowlist.js";
18
18
  import { renderFilterType } from "./filter-type.js";
@@ -143,6 +143,9 @@ export function renderEntityFile(
143
143
  };
144
144
  const viewOpts = {
145
145
  dialect: ctx.dialect, columnNamingStrategy: ctx.columnNamingStrategy, timestampMode: ctx.timestampMode, voRef,
146
+ // PK fields type non-null in the replica-view decl + read schema even
147
+ // without @required (a PK is never NULL; see ViewDeclOpts.pkFieldNames).
148
+ pkFieldNames: new Set(primaryIdentityFieldNames(entity)) as ReadonlySet<string>,
146
149
  };
147
150
  const z = imp("z@zod");
148
151
  const docs = renderDocsFor(entity);
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { code, imp, joinCode, type Code } from "ts-poet";
11
11
  import {
12
- MetaField, MetaObject, type MetaRoot,
12
+ MetaField, MetaObject, type MetaRoot, isMetaObject,
13
13
  FIELD_ATTR_OBJECT_REF, stripPackage,
14
14
  } from "@metaobjectsdev/metadata";
15
15
  import { projectionViewName } from "../projection/extract-view-spec.js";
@@ -22,6 +22,7 @@ import { renderFilterAllowlist, renderSortAllowlist } from "./filter-allowlist.j
22
22
  import { renderFilterType } from "./filter-type.js";
23
23
  import { inferViewKind, currencyMetaFor, labelFor } from "./field-meta.js";
24
24
  import { renderExistingViewDecl, renderViewReadZodObject } from "./view-decl.js";
25
+ import { primaryIdentityFieldNames } from "./zod-validators.js";
25
26
 
26
27
  // ---------------------------------------------------------------------------
27
28
  // Public interface
@@ -125,7 +126,7 @@ export function renderProjectionDecl(
125
126
  const superName = superModel?.name ?? projection.superRef;
126
127
  if (superName) {
127
128
  const baseObj =
128
- superModel instanceof MetaObject ? superModel : root.findObject(superName);
129
+ isMetaObject(superModel) ? superModel : root.findObject(superName);
129
130
  if (baseObj) {
130
131
  // fields() returns effective fields, so inherited fields (from extends:/super:) are included.
131
132
  for (const f of baseObj.fields()) allFields.push(f);
@@ -152,15 +153,18 @@ export function renderProjectionDecl(
152
153
  const camelName = projName.charAt(0).toLowerCase() + projName.slice(1);
153
154
  const path = pathFromProjectionName(projName);
154
155
 
156
+ // The projection's primary-identity fields type non-null in the view decl +
157
+ // read schema even without @required (a PK is never NULL; see ViewDeclOpts).
158
+ const pkFieldNames: ReadonlySet<string> = new Set(primaryIdentityFieldNames(projection));
155
159
  const sections: Code[] = [
156
160
  ...(includeViewDecl
157
161
  ? [renderExistingViewDecl(allFields, viewName, `${camelName}View`, {
158
- dialect, columnNamingStrategy, timestampMode, voRef,
162
+ dialect, columnNamingStrategy, timestampMode, voRef, pkFieldNames,
159
163
  })]
160
164
  : []),
161
165
  code`
162
166
  export const ${projName}Schema = ${renderViewReadZodObject(allFields, {
163
- dialect, columnNamingStrategy, timestampMode, voRef,
167
+ dialect, columnNamingStrategy, timestampMode, voRef, pkFieldNames,
164
168
  })};
165
169
  `,
166
170
  code`
@@ -29,6 +29,15 @@ export interface ViewDeclOpts {
29
29
  * `RenderContext.resolveValueObjectName` + `valueObjectModuleSpecifier`.
30
30
  */
31
31
  readonly voRef: (field: MetaField) => { name: string; module: string };
32
+ /**
33
+ * Field names of the owning object's PRIMARY identity (see
34
+ * `primaryIdentityFieldNames` in zod-validators). A PK column sits on the FROM
35
+ * side of every synthesized view, so it is never NULL even when the field
36
+ * carries no `@required` — the view column gets `.notNull()` and the read
37
+ * schema stays non-nullable, matching the generated api-docs, queries and
38
+ * UpdateSchema, all of which already treat the PK as non-null.
39
+ */
40
+ readonly pkFieldNames: ReadonlySet<string>;
32
41
  }
33
42
 
34
43
  /**
@@ -49,9 +58,13 @@ function viewColumnLine(f: MetaField, opts: ViewDeclOpts): Code {
49
58
  // typing) and `.notNull()` (shapes the SELECT type) carry to an existing view;
50
59
  // `.primaryKey()`/`.default()`/`.references()`/`.unique()` are table-DDL concerns
51
60
  // and invalid on a `.existing()` view declaration. Preserve the canonical order.
52
- const viewModifiers = spec.modifiers
53
- .filter((m) => m === ".array()" || m === ".notNull()")
54
- .join("");
61
+ // A primary-identity column additionally gains `.notNull()`: on the table side
62
+ // its non-null comes from `.primaryKey()` (which the view cannot carry), so
63
+ // without this the view SELECT type — and the Zod read schema derived from it —
64
+ // typed the PK `T | null` for a column that can never be NULL.
65
+ const kept = spec.modifiers.filter((m) => m === ".array()" || m === ".notNull()");
66
+ if (opts.pkFieldNames.has(f.name) && !kept.includes(".notNull()")) kept.push(".notNull()");
67
+ const viewModifiers = kept.join("");
55
68
  // Narrow the column to its resolved element/value type — `.$type<…>()` — mirroring
56
69
  // the entity column so the read row is typed (not `unknown`) and an array column
57
70
  // reads as `T[]`, not `T`.
@@ -113,10 +126,13 @@ export function renderViewReadZodObject(fields: readonly MetaField[], opts: View
113
126
  const { dialect, columnNamingStrategy, timestampMode } = opts;
114
127
  const z = imp("z@zod");
115
128
  const lines: Code[] = fields.map((f) => {
116
- const nullable =
117
- mapColumnType(f, dialect, columnNamingStrategy, timestampMode).modifiers.includes(".notNull()")
118
- ? ""
119
- : ".nullable()";
129
+ // Non-null when the column is `.notNull()` OR it is a primary-identity
130
+ // field — a PK is never NULL, but its table-side non-null is expressed via
131
+ // `.primaryKey()`, not a `.notNull()` modifier (see viewColumnLine above).
132
+ const nonNull =
133
+ mapColumnType(f, dialect, columnNamingStrategy, timestampMode).modifiers.includes(".notNull()") ||
134
+ opts.pkFieldNames.has(f.name);
135
+ const nullable = nonNull ? "" : ".nullable()";
120
136
  const hasObjectRef =
121
137
  f.subType === FIELD_SUBTYPE_OBJECT &&
122
138
  typeof f.attr(FIELD_ATTR_OBJECT_REF) === "string" &&
@@ -129,8 +129,9 @@ ${joinCode(fieldLines, { on: ",\n" })}
129
129
  }
130
130
 
131
131
  /** Field names participating in the object's PRIMARY identity, normalized to a
132
- * list. Empty when the object has no primary identity. */
133
- function primaryIdentityFieldNames(obj: MetaObject): string[] {
132
+ * list. Empty when the object has no primary identity. Exported for the
133
+ * view-decl read-schema path, which must type PK columns non-null too. */
134
+ export function primaryIdentityFieldNames(obj: MetaObject): string[] {
134
135
  const primary = obj.primaryIdentity();
135
136
  if (!primary) return [];
136
137
  const fields = primary.attr(IDENTITY_ATTR_FIELDS);