@sequenceholdings/orm 0.1.3 → 2.1.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.
Files changed (59) hide show
  1. package/README.md +108 -38
  2. package/dist/authoring.cjs +128 -16
  3. package/dist/authoring.cjs.map +1 -1
  4. package/dist/authoring.d.cts +21 -4
  5. package/dist/authoring.d.ts +21 -4
  6. package/dist/authoring.js +128 -16
  7. package/dist/authoring.js.map +1 -1
  8. package/dist/canonical.cjs +83 -9
  9. package/dist/canonical.cjs.map +1 -1
  10. package/dist/canonical.d.cts +47 -1
  11. package/dist/canonical.d.ts +47 -1
  12. package/dist/canonical.js +83 -10
  13. package/dist/canonical.js.map +1 -1
  14. package/dist/cli.cjs +816 -134
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.js +818 -136
  17. package/dist/cli.js.map +1 -1
  18. package/dist/client.cjs.map +1 -1
  19. package/dist/client.js.map +1 -1
  20. package/dist/compiler.cjs +488 -96
  21. package/dist/compiler.cjs.map +1 -1
  22. package/dist/compiler.d.cts +34 -2
  23. package/dist/compiler.d.ts +34 -2
  24. package/dist/compiler.js +489 -97
  25. package/dist/compiler.js.map +1 -1
  26. package/dist/index.cjs +420 -71
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +266 -9
  29. package/dist/index.d.ts +266 -9
  30. package/dist/index.js +410 -73
  31. package/dist/index.js.map +1 -1
  32. package/dist/ir.cjs +146 -12
  33. package/dist/ir.cjs.map +1 -1
  34. package/dist/ir.d.cts +161 -5
  35. package/dist/ir.d.ts +161 -5
  36. package/dist/ir.js +144 -13
  37. package/dist/ir.js.map +1 -1
  38. package/dist/{naming-CTvWSRTf.d.ts → naming-DCn94LJ4.d.ts} +15 -1
  39. package/dist/{naming-DRyinU4u.d.cts → naming-DPglwzkB.d.cts} +15 -1
  40. package/dist/predicate-v2.cjs +136 -11
  41. package/dist/predicate-v2.cjs.map +1 -1
  42. package/dist/predicate-v2.d.cts +3 -0
  43. package/dist/predicate-v2.d.ts +3 -0
  44. package/dist/predicate-v2.js +136 -11
  45. package/dist/predicate-v2.js.map +1 -1
  46. package/dist/sdl.cjs +460 -13
  47. package/dist/sdl.cjs.map +1 -1
  48. package/dist/sdl.d.cts +1 -1
  49. package/dist/sdl.d.ts +1 -1
  50. package/dist/sdl.js +459 -14
  51. package/dist/sdl.js.map +1 -1
  52. package/package.json +6 -4
  53. package/src/codegen-gql/preset.cjs +64 -0
  54. package/src/codegen-gql/server-fns.cjs +11 -6
  55. package/templates/orm-namespace/codegen.preset.cjs +17 -0
  56. package/templates/orm-namespace/codegen.ts +1 -1
  57. package/templates/orm-namespace/package.json +3 -0
  58. package/templates/orm-namespace/schema/tables.ts +1 -1
  59. package/templates/orm-namespace/sequence.config.ts +1 -1
package/dist/ir.cjs CHANGED
@@ -3,9 +3,15 @@
3
3
  var zod = require('zod');
4
4
 
5
5
  // src/ir/index.ts
6
+
7
+ // src/constants.ts
8
+ var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
9
+
10
+ // src/canonical/v2.ts
6
11
  var CANONICAL_FORMAT = 2;
7
12
  var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
8
13
  var identifier = zod.z.string().min(1).max(63).regex(IDENTIFIER_RE);
14
+ var columnIdentifier = zod.z.string().min(1).max(63).regex(COLUMN_IDENTIFIER_PATTERN);
9
15
  var COLUMN_TYPES = [
10
16
  "uuid",
11
17
  "text",
@@ -26,18 +32,33 @@ var canonicalEnumSchema = zod.z.object({
26
32
  var canonicalColumnSchema = zod.z.object({
27
33
  /** Property name in TS (camelCase); columnName is the Postgres name. */
28
34
  name: zod.z.string().min(1).max(63),
29
- columnName: identifier,
35
+ columnName: columnIdentifier,
30
36
  type: zod.z.union([columnTypeSchema, zod.z.object({ enum: identifier })]),
31
37
  nullable: zod.z.boolean().default(true),
32
38
  unique: zod.z.boolean().default(false),
33
39
  /** Literal default, or one of the allowlisted SQL functions. */
34
40
  default: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]).optional(),
35
41
  defaultFn: zod.z.enum(["gen_random_uuid()", "now()"]).optional(),
42
+ /**
43
+ * Stamp this column from the database clock on UPDATE (`now()`). The
44
+ * column stays insertable (defaults fill gaps) but is omitted from
45
+ * SetInput / settable so callers cannot backdate it.
46
+ */
47
+ onUpdateFn: zod.z.enum(["now()"]).optional(),
36
48
  description: zod.z.string().max(500).optional(),
37
49
  /** Migration hints — same semantics as v1. */
38
- renamedFrom: identifier.optional(),
50
+ renamedFrom: columnIdentifier.optional(),
39
51
  cast: zod.z.string().max(200).optional(),
40
52
  deprecated: zod.z.boolean().default(false)
53
+ }).superRefine((column, ctx) => {
54
+ if (column.onUpdateFn === void 0) return;
55
+ if (typeof column.type !== "string" || column.type !== "timestamptz" && column.type !== "date") {
56
+ ctx.addIssue({
57
+ code: "custom",
58
+ path: ["onUpdateFn"],
59
+ message: `onUpdateFn '${column.onUpdateFn}' is only valid on timestamptz/date columns`
60
+ });
61
+ }
41
62
  });
42
63
  var canonicalLinkSchema = zod.z.object({
43
64
  name: zod.z.string().min(1).max(63),
@@ -45,15 +66,15 @@ var canonicalLinkSchema = zod.z.object({
45
66
  /** Target table name (namespace-local). Absent for toPlatformUser. */
46
67
  target: identifier.optional(),
47
68
  /** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
48
- on: identifier,
69
+ on: columnIdentifier,
49
70
  /** Referenced column on the target (default: target's PK / platform user `sub`). */
50
- references: identifier.optional(),
71
+ references: columnIdentifier.optional(),
51
72
  /** enforce: true emits a real FK constraint (same-namespace toOne only). */
52
73
  enforce: zod.z.boolean().default(false),
53
74
  description: zod.z.string().max(500).optional()
54
75
  });
55
76
  var canonicalIndexSchema = zod.z.object({
56
- columns: zod.z.array(identifier).min(1),
77
+ columns: zod.z.array(columnIdentifier).min(1),
57
78
  unique: zod.z.boolean().default(false),
58
79
  name: identifier.optional()
59
80
  });
@@ -144,6 +165,13 @@ var canonicalCowMutationSchema = zod.z.object({
144
165
  }
145
166
  }
146
167
  });
168
+ var canonicalForeignKeySchema = zod.z.object({
169
+ name: identifier,
170
+ columns: zod.z.array(columnIdentifier).min(1).max(8),
171
+ target: identifier,
172
+ references: zod.z.array(columnIdentifier).min(1).max(8),
173
+ deferrable: zod.z.enum(["initially-immediate", "initially-deferred"]).optional()
174
+ }).strict();
147
175
  var canonicalTableSchema = zod.z.object({
148
176
  table: identifier,
149
177
  description: zod.z.string().max(2e3).optional(),
@@ -152,7 +180,7 @@ var canonicalTableSchema = zod.z.object({
152
180
  /** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
153
181
  source: zod.z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
154
182
  columns: zod.z.array(canonicalColumnSchema).default([]),
155
- primaryKey: zod.z.array(identifier).min(1).max(8).optional(),
183
+ primaryKey: zod.z.array(columnIdentifier).min(1).max(8).optional(),
156
184
  links: zod.z.array(canonicalLinkSchema).default([]),
157
185
  indexes: zod.z.array(canonicalIndexSchema).default([]),
158
186
  policies: canonicalPoliciesSchema.default({}),
@@ -163,14 +191,16 @@ var canonicalTableSchema = zod.z.object({
163
191
  */
164
192
  actions: zod.z.array(canonicalActionSchema).default([]),
165
193
  /** Optional (not defaulted) so pre-CoW canonical documents keep byte-identical hashes. */
166
- cowMutations: zod.z.array(canonicalCowMutationSchema).optional()
194
+ cowMutations: zod.z.array(canonicalCowMutationSchema).optional(),
195
+ /** Omitted by old definitions, preserving their existing content hashes. */
196
+ foreignKeys: zod.z.array(canonicalForeignKeySchema).max(100).optional()
167
197
  });
168
198
  var canonicalViewSchema = zod.z.object({
169
199
  view: identifier,
170
200
  description: zod.z.string().max(2e3).optional(),
171
201
  columns: zod.z.array(canonicalColumnSchema).min(1),
172
202
  /** Logical identity for GraphQL only; PostgreSQL views have no PK constraint. */
173
- primaryKey: zod.z.array(identifier).min(1).max(8).optional(),
203
+ primaryKey: zod.z.array(columnIdentifier).min(1).max(8).optional(),
174
204
  links: zod.z.array(canonicalLinkSchema).default([]),
175
205
  policies: zod.z.object({ read: policyExpressionSchema }).strict(),
176
206
  query: zod.z.string().min(1).max(1e5).refine(isSingleViewQuerySql, {
@@ -183,6 +213,9 @@ var canonicalViewSchema = zod.z.object({
183
213
  if (column.default !== void 0 || column.defaultFn !== void 0) {
184
214
  ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare a default` });
185
215
  }
216
+ if (column.onUpdateFn !== void 0) {
217
+ ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare onUpdateFn` });
218
+ }
186
219
  if (column.unique) {
187
220
  ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare unique` });
188
221
  }
@@ -216,6 +249,45 @@ zod.z.object({
216
249
  ctx.addIssue({ code: "custom", path: ["tables", index, "table"], message: `duplicate relation '${table.table}'` });
217
250
  }
218
251
  seen.add(table.table);
252
+ if (table.source !== void 0) {
253
+ for (const [columnIndex, column] of table.columns.entries()) {
254
+ if (column.onUpdateFn !== void 0) {
255
+ ctx.addIssue({
256
+ code: "custom",
257
+ path: ["tables", index, "columns", columnIndex],
258
+ message: `synced table '${table.table}' column '${column.name}' cannot declare onUpdateFn`
259
+ });
260
+ }
261
+ }
262
+ }
263
+ }
264
+ const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
265
+ for (const [tableIndex, table] of definition.tables.entries()) {
266
+ const names = /* @__PURE__ */ new Set([
267
+ ...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
268
+ ...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
269
+ ...table.links.filter((link) => link.enforce).map((link) => `fk_${table.table}_${link.name}`)
270
+ ]);
271
+ for (const [index, fk] of (table.foreignKeys ?? []).entries()) {
272
+ const fail = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index], message });
273
+ if (names.has(fk.name)) fail(`duplicate foreign key '${fk.name}'`);
274
+ names.add(fk.name);
275
+ const target = fkTables.get(fk.target);
276
+ if (table.source || !target || target.source) {
277
+ fail("foreign keys must connect owned tables in the same namespace");
278
+ continue;
279
+ }
280
+ if (fk.columns.length !== fk.references.length) fail("foreign key columns and references must have the same number of columns");
281
+ if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail("foreign key columns must not repeat");
282
+ for (const [position, name] of fk.columns.entries()) {
283
+ const sourceColumn = table.columns.find((column) => column.columnName === name);
284
+ const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
285
+ if (!sourceColumn || !targetColumn) fail("foreign key references an unknown column");
286
+ else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail("foreign key column types must match");
287
+ }
288
+ const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index2) => index2.unique).map((index2) => index2.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
289
+ if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail("foreign key target columns must form a unique key");
290
+ }
219
291
  }
220
292
  const tableByName = new Map(definition.tables.map((table) => [table.table, table]));
221
293
  for (const [tableIndex, table] of definition.tables.entries()) {
@@ -334,7 +406,18 @@ zod.z.object({
334
406
  });
335
407
 
336
408
  // src/ir/index.ts
337
- var IR_VERSION = 1;
409
+ var IR_VERSION = 2;
410
+ var LEGACY_IR_VERSION = 1;
411
+ var irVersionSchema = zod.z.union([zod.z.literal(LEGACY_IR_VERSION), zod.z.literal(IR_VERSION)]);
412
+ var operationVersionSchema = zod.z.object({ irVersion: irVersionSchema });
413
+ function requiredArtifactIrVersion(operations) {
414
+ let required = LEGACY_IR_VERSION;
415
+ for (const operation of operations) {
416
+ const { irVersion } = operationVersionSchema.parse(operation);
417
+ if (irVersion > required) required = irVersion;
418
+ }
419
+ return required;
420
+ }
338
421
  var CLAIMS_CONTRACT = {
339
422
  sub: "string",
340
423
  org: "string?",
@@ -461,6 +544,33 @@ var aggregateRootSchema = zod.z.object({
461
544
  nodes: selectionIrSchema.optional(),
462
545
  includeIf: includeIfSchema.optional()
463
546
  });
547
+ var DATE_BUCKETS = ["year", "quarter", "month", "week", "day", "hour"];
548
+ var groupByKeyIrSchema = zod.z.object({
549
+ column: zod.z.string(),
550
+ bucket: zod.z.enum(DATE_BUCKETS).optional()
551
+ });
552
+ var aggregateGroupsRootSchema = zod.z.object({
553
+ kind: zod.z.literal("aggregateGroups"),
554
+ alias: zod.z.string(),
555
+ table: zod.z.string(),
556
+ /** Composed at compile time: AND(policy, documentWhere?, dynamicFilter?). */
557
+ where: predIrSchema,
558
+ groupBy: zod.z.array(groupByKeyIrSchema).min(1),
559
+ /**
560
+ * The `key { … }` projection: which group columns appear in each entry's key
561
+ * object, in selection order. A subset of `groupBy` (the compiler rejects a
562
+ * key field that is not a group key — it would be indeterminate within the
563
+ * group). Empty when the document selects only `aggregate`.
564
+ */
565
+ key: zod.z.array(zod.z.object({ alias: zod.z.string(), column: zod.z.string() })).default([]),
566
+ count: zod.z.object({ columns: zod.z.array(zod.z.string()).optional(), distinct: zod.z.boolean().optional() }).optional(),
567
+ sum: zod.z.array(zod.z.string()).optional(),
568
+ avg: zod.z.array(zod.z.string()).optional(),
569
+ /** Bounds the GROUP count, not the scanned rows — see MAX_GROUPS. */
570
+ limit: limitIrSchema,
571
+ offset: zod.z.object({ variable: zod.z.string().optional(), value: zod.z.number().int().optional() }).optional(),
572
+ includeIf: includeIfSchema.optional()
573
+ });
464
574
  var writeValuesSchema = zod.z.union([
465
575
  zod.z.object({ slot: zod.z.string() }),
466
576
  zod.z.object({ entries: zod.z.array(zod.z.object({ column: zod.z.string(), value: valueIrSchema })) })
@@ -506,12 +616,15 @@ var mutationRootSchema = zod.z.object({
506
616
  insertCheck: predIrSchema.optional(),
507
617
  returning: selectionIrSchema.nullable(),
508
618
  affectedRows: zod.z.boolean().default(false),
619
+ /** v2: abort the transaction when the actual write count differs, regardless of selection. */
620
+ assertAffectedRows: zod.z.number().int().min(0).max(2147483647).optional(),
509
621
  includeIf: includeIfSchema.optional()
510
622
  });
511
623
  var rootIrSchema = zod.z.discriminatedUnion("kind", [
512
624
  selectRootSchema,
513
625
  byPkRootSchema,
514
626
  aggregateRootSchema,
627
+ aggregateGroupsRootSchema,
515
628
  mutationRootSchema
516
629
  ]);
517
630
  var INPUT_REF_KINDS = [
@@ -541,7 +654,7 @@ var variableIrSchema = zod.z.object({
541
654
  defaultValue: variableDefaultIrSchema.optional()
542
655
  });
543
656
  var compiledOperationSchema = zod.z.object({
544
- irVersion: zod.z.literal(IR_VERSION),
657
+ irVersion: irVersionSchema,
545
658
  name: zod.z.string(),
546
659
  opHash: zod.z.string().regex(/^sha256:[0-9a-f]{64}$/),
547
660
  kind: zod.z.enum(["query", "mutation"]),
@@ -553,6 +666,13 @@ var compiledOperationSchema = zod.z.object({
553
666
  /** Every table the op touches — data-token scoping + audit. */
554
667
  tableSet: zod.z.array(zod.z.string()).min(1),
555
668
  roots: zod.z.array(rootIrSchema).min(1)
669
+ }).superRefine((operation, ctx) => {
670
+ for (const [index, root] of operation.roots.entries()) {
671
+ if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
672
+ if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
673
+ ctx.addIssue({ code: "custom", path: ["roots", index], message: "affected-row assertions require unconditional mutation roots and IR v2" });
674
+ }
675
+ }
556
676
  });
557
677
  var tableMetaIrSchema = zod.z.object({
558
678
  table: zod.z.string(),
@@ -589,18 +709,29 @@ var tableMetaIrSchema = zod.z.object({
589
709
  insertable: zod.z.array(zod.z.string()),
590
710
  settable: zod.z.array(zod.z.string()),
591
711
  appendable: zod.z.array(zod.z.string()),
712
+ /**
713
+ * Logical columns stamped with Postgres `now()` on UPDATE (canonical
714
+ * `onUpdateFn`). Omitted when empty so pre-onUpdate artifacts still parse.
715
+ */
716
+ touchOnUpdate: zod.z.array(zod.z.string()).optional(),
592
717
  constraints: zod.z.array(zod.z.object({ name: zod.z.string(), columns: zod.z.array(zod.z.string()) })),
593
718
  /** Depth/node caps for dynamic filter trees on this table. */
594
719
  dynamicFilterMaxDepth: zod.z.number().int().positive().default(6),
595
720
  dynamicFilterMaxNodes: zod.z.number().int().positive().default(64)
596
721
  });
597
722
  var compiledArtifactSchema = zod.z.object({
598
- irVersion: zod.z.literal(IR_VERSION),
723
+ irVersion: irVersionSchema,
599
724
  namespace: zod.z.string(),
600
725
  schemaHash: zod.z.string(),
601
726
  opsHash: zod.z.string(),
602
727
  tables: zod.z.record(zod.z.string(), tableMetaIrSchema),
603
728
  operations: zod.z.record(zod.z.string(), compiledOperationSchema)
729
+ }).superRefine((artifact, ctx) => {
730
+ for (const [name, operation] of Object.entries(artifact.operations)) {
731
+ if (operation.irVersion > artifact.irVersion) {
732
+ ctx.addIssue({ code: "custom", path: ["operations", name, "irVersion"], message: "operation requires a newer IR version than its artifact" });
733
+ }
734
+ }
604
735
  });
605
736
  var IrVersionError = class extends Error {
606
737
  constructor(found) {
@@ -611,7 +742,7 @@ var IrVersionError = class extends Error {
611
742
  function normalizeIr(raw) {
612
743
  if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
613
744
  const version = raw.irVersion;
614
- if (version !== IR_VERSION) throw new IrVersionError(version);
745
+ if (version !== LEGACY_IR_VERSION && version !== IR_VERSION) throw new IrVersionError(version);
615
746
  return compiledArtifactSchema.parse(raw);
616
747
  }
617
748
  function indexByHash(artifact) {
@@ -622,9 +753,11 @@ function indexByHash(artifact) {
622
753
 
623
754
  exports.CLAIMS_CONTRACT = CLAIMS_CONTRACT;
624
755
  exports.CMP_OPS = CMP_OPS;
756
+ exports.DATE_BUCKETS = DATE_BUCKETS;
625
757
  exports.INPUT_REF_KINDS = INPUT_REF_KINDS;
626
758
  exports.IR_VERSION = IR_VERSION;
627
759
  exports.IrVersionError = IrVersionError;
760
+ exports.LEGACY_IR_VERSION = LEGACY_IR_VERSION;
628
761
  exports.compiledArtifactSchema = compiledArtifactSchema;
629
762
  exports.compiledOperationSchema = compiledOperationSchema;
630
763
  exports.indexByHash = indexByHash;
@@ -632,6 +765,7 @@ exports.limitIrSchema = limitIrSchema;
632
765
  exports.normalizeIr = normalizeIr;
633
766
  exports.orderIrSchema = orderIrSchema;
634
767
  exports.predIrSchema = predIrSchema;
768
+ exports.requiredArtifactIrVersion = requiredArtifactIrVersion;
635
769
  exports.rootIrSchema = rootIrSchema;
636
770
  exports.selectionIrSchema = selectionIrSchema;
637
771
  exports.tableMetaIrSchema = tableMetaIrSchema;