@sequenceholdings/orm 2.8.3 → 2.8.5

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 (52) hide show
  1. package/dist/authoring.cjs +154 -45
  2. package/dist/authoring.cjs.map +1 -1
  3. package/dist/authoring.d.cts +12 -2
  4. package/dist/authoring.d.ts +12 -2
  5. package/dist/authoring.js +154 -45
  6. package/dist/authoring.js.map +1 -1
  7. package/dist/canonical.cjs +64 -14
  8. package/dist/canonical.cjs.map +1 -1
  9. package/dist/canonical.d.cts +12 -7
  10. package/dist/canonical.d.ts +12 -7
  11. package/dist/canonical.js +64 -14
  12. package/dist/canonical.js.map +1 -1
  13. package/dist/cli.cjs +78 -20
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +78 -20
  16. package/dist/cli.js.map +1 -1
  17. package/dist/compiler.cjs +77 -20
  18. package/dist/compiler.cjs.map +1 -1
  19. package/dist/compiler.d.cts +4 -4
  20. package/dist/compiler.d.ts +4 -4
  21. package/dist/compiler.js +77 -20
  22. package/dist/compiler.js.map +1 -1
  23. package/dist/index.cjs +161 -47
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +11 -11
  26. package/dist/index.d.ts +11 -11
  27. package/dist/index.js +161 -47
  28. package/dist/index.js.map +1 -1
  29. package/dist/ir.cjs +71 -16
  30. package/dist/ir.cjs.map +1 -1
  31. package/dist/ir.d.cts +9 -9
  32. package/dist/ir.d.ts +9 -9
  33. package/dist/ir.js +71 -16
  34. package/dist/ir.js.map +1 -1
  35. package/dist/{naming-CU9ThP_E.d.cts → naming-BMZKFO0W.d.cts} +1 -2
  36. package/dist/{naming-CPfx7EfF.d.ts → naming-DUk7AcXs.d.ts} +1 -2
  37. package/dist/predicate-v2.cjs +71 -16
  38. package/dist/predicate-v2.cjs.map +1 -1
  39. package/dist/predicate-v2.d.cts +3 -3
  40. package/dist/predicate-v2.d.ts +3 -3
  41. package/dist/predicate-v2.js +71 -16
  42. package/dist/predicate-v2.js.map +1 -1
  43. package/dist/sdl.cjs +71 -16
  44. package/dist/sdl.cjs.map +1 -1
  45. package/dist/sdl.d.cts +2 -2
  46. package/dist/sdl.d.ts +2 -2
  47. package/dist/sdl.js +71 -16
  48. package/dist/sdl.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/codegen-gql/apollo-hooks.cjs +1 -1
  51. package/src/codegen-gql/server-fns.cjs +1 -1
  52. package/templates/orm-namespace/graphql/example/queries.ts +1 -1
package/dist/sdl.cjs CHANGED
@@ -240,6 +240,48 @@ var policyExpressionSchema = zod.z.union([
240
240
  variants: zod.z.array(policyVariantSchema).max(10).default([])
241
241
  })
242
242
  ]);
243
+
244
+ // src/canonical/resolved-rows.ts
245
+ function logicalKey(table) {
246
+ return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
247
+ }
248
+ function sameKey(key, expected) {
249
+ return key.length === expected.length && key.every((name) => expected.includes(name));
250
+ }
251
+ function validateRowIdentity(input) {
252
+ validateExistsIn(input);
253
+ if (input.keys.length > 1) validateCompositeKey(input);
254
+ }
255
+ function validateExistsIn({ resolved, sourceByName, fail }) {
256
+ const name = resolved.existsIn?.[0];
257
+ if (name === void 0) return;
258
+ if (sourceByName.get(name)?.kind !== "etl") fail(`existsIn '${name}' must be an etl source`);
259
+ if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
260
+ fail("existsIn allows only an update policy: rows come from the etl source");
261
+ }
262
+ }
263
+ function validateCompositeKey({ resolved, keys, tableByName, fail }) {
264
+ const keyList = `(${keys.join(", ")})`;
265
+ if (resolved.existsIn === void 0) {
266
+ fail(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
267
+ return;
268
+ }
269
+ const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
270
+ if (writes.length > 0) fail(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
271
+ if (resolved.materialized) fail(`composite key ${keyList} cannot be materialized`);
272
+ for (const source of resolved.sources) {
273
+ if (source.kind === "prefetch") {
274
+ fail(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
275
+ continue;
276
+ }
277
+ const table = tableByName.get(source.table);
278
+ if (table && !sameKey(logicalKey(table), keys)) {
279
+ fail(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
280
+ }
281
+ }
282
+ }
283
+
284
+ // src/canonical/resolved.ts
243
285
  var identifier = zod.z.string().min(1).max(63).regex(/^[a-z][a-z0-9_]*$/);
244
286
  var durationMs = zod.z.number().int().positive().max(24 * 60 * 60 * 1e3);
245
287
  var RESOLVED_VIEW_SYSTEM_COLUMNS = {
@@ -308,6 +350,13 @@ var canonicalResolvedSchema = zod.z.object({
308
350
  resolve: zod.z.array(canonicalResolveRuleSchema).max(500),
309
351
  /** Sources whose tombstone hides the resolved row (v1: the manual source only). */
310
352
  deletedBy: zod.z.array(identifier).max(1).default([]),
353
+ /**
354
+ * `rows.existsIn`: the one etl source whose rows are the table's rows (read
355
+ * directly as the row source). Only an update policy may write. It is also
356
+ * what admits a composite primary key: a read-only table keyed exactly like
357
+ * that source.
358
+ */
359
+ existsIn: zod.z.array(identifier).length(1).optional(),
311
360
  /**
312
361
  * Materialized: the view reads an indexed internal table (`table`) that the
313
362
  * data plane keeps in step with `compute`, the internal view holding the
@@ -326,8 +375,8 @@ function validateResolvedView({ view, tables, views = [] }) {
326
375
  if (!resolved) return [];
327
376
  const errors = [];
328
377
  const fail = (message) => errors.push(`resolved table '${view.view}': ${message}`);
329
- const pkColumn = view.primaryKey?.length === 1 ? view.columns.find((c) => c.columnName === view.primaryKey?.[0]) : void 0;
330
- if (!pkColumn) {
378
+ const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
379
+ if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
331
380
  fail("requires exactly one primary-key column");
332
381
  return errors;
333
382
  }
@@ -335,7 +384,7 @@ function validateResolvedView({ view, tables, views = [] }) {
335
384
  const scope = {
336
385
  view,
337
386
  resolved,
338
- pkColumn,
387
+ keys: new Map(keyColumns.map((column) => [column.name, column])),
339
388
  dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
340
389
  sourceByName: /* @__PURE__ */ new Map(),
341
390
  fail
@@ -349,6 +398,7 @@ function validateResolvedView({ view, tables, views = [] }) {
349
398
  if (!table) fail(`source '${source.name}' references unknown table '${source.table}'`);
350
399
  else validateSource({ scope, source, table });
351
400
  }
401
+ validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail });
352
402
  validateRules(scope);
353
403
  validateViewCells(scope);
354
404
  if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
@@ -378,7 +428,7 @@ function validateSource({ scope, source, table }) {
378
428
  validatePrefetchFields({ scope, source });
379
429
  }
380
430
  function validatePrefetchFields({ scope, source }) {
381
- const { fail, pkColumn } = scope;
431
+ const { fail, keys } = scope;
382
432
  if (source.kind !== "prefetch") {
383
433
  if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
384
434
  fail(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
@@ -387,7 +437,7 @@ function validatePrefetchFields({ scope, source }) {
387
437
  }
388
438
  if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
389
439
  fail(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
390
- } else if (source.key !== pkColumn.name && !source.columns.includes(source.key)) {
440
+ } else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
391
441
  fail(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
392
442
  }
393
443
  for (const column of source.writeback?.columns ?? []) {
@@ -395,19 +445,19 @@ function validatePrefetchFields({ scope, source }) {
395
445
  }
396
446
  }
397
447
  function validateSourceColumns({ scope, source, byName }) {
398
- const { fail, pkColumn, dataColumns } = scope;
399
- for (const column of [pkColumn.name, ...source.columns]) {
400
- const own = column === pkColumn.name ? pkColumn : dataColumns.get(column);
448
+ const { fail, keys, dataColumns } = scope;
449
+ for (const column of [...keys.keys(), ...source.columns]) {
450
+ const own = keys.get(column) ?? dataColumns.get(column);
401
451
  const theirs = byName.get(column);
402
452
  if (!own) fail(`source '${source.name}' supplies unknown column '${column}'`);
403
453
  else if (!theirs) fail(`source '${source.name}' table '${source.table}' has no column '${column}'`);
404
454
  else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail(`column '${column}' has a different type on source '${source.name}'`);
405
455
  }
406
456
  }
407
- function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail }) {
457
+ function validateRules({ resolved, keys, dataColumns, sourceByName, fail }) {
408
458
  const ruled = /* @__PURE__ */ new Set();
409
459
  for (const rule of resolved.resolve) {
410
- if (!dataColumns.has(rule.column) || rule.column === pkColumn.name) {
460
+ if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
411
461
  fail(`resolve rule targets unknown column '${rule.column}'`);
412
462
  continue;
413
463
  }
@@ -424,7 +474,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail })
424
474
  }
425
475
  }
426
476
  for (const column of dataColumns.keys()) {
427
- if (column !== pkColumn.name && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
477
+ if (!keys.has(column) && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
428
478
  }
429
479
  }
430
480
  var VIEW_CELL_TYPES = {
@@ -433,10 +483,10 @@ var VIEW_CELL_TYPES = {
433
483
  by: "text",
434
484
  conflicts: "jsonb"
435
485
  };
436
- function validateViewCells({ view, dataColumns, pkColumn, fail }) {
486
+ function validateViewCells({ view, dataColumns, keys, fail }) {
437
487
  const expected = /* @__PURE__ */ new Map();
438
488
  for (const column of dataColumns.values()) {
439
- if (column.name === pkColumn.name) continue;
489
+ if (keys.has(column.name)) continue;
440
490
  for (const part of RESOLVED_VIEW_CELL_PARTS) {
441
491
  expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
442
492
  }
@@ -647,7 +697,7 @@ var canonicalTableSchema = zod.z.object({
647
697
  table: identifier2,
648
698
  displayName: zod.z.string().trim().min(1).max(120).optional(),
649
699
  description: zod.z.string().max(2e3).optional(),
650
- /** Table-level rename hint (new in v2). */
700
+ /** Table-level rename hint. */
651
701
  renamedFrom: identifier2.optional(),
652
702
  /** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
653
703
  source: zod.z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
@@ -1613,8 +1663,13 @@ var resolvedSourceIrSchema = zod.z.object({
1613
1663
  }).optional()
1614
1664
  });
1615
1665
  var resolvedMetaIrSchema = zod.z.object({
1616
- /** Logical pk column of the resolved view (single-column). */
1617
- pk: zod.z.string(),
1666
+ /**
1667
+ * Logical pk column of the resolved view. Absent for a composite key, which
1668
+ * only a read-only `existsIn` table may declare: it has no writes, prefetch
1669
+ * or materialization (the paths that address one key column), and its reads
1670
+ * use the view's `primaryKey` like any keyed view.
1671
+ */
1672
+ pk: zod.z.string().optional(),
1618
1673
  /** Source name of the manual source. */
1619
1674
  manual: zod.z.string(),
1620
1675
  sources: zod.z.array(resolvedSourceIrSchema),