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