@sequenceholdings/orm 2.8.4 → 2.9.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 (60) hide show
  1. package/dist/authoring.cjs +142 -56
  2. package/dist/authoring.cjs.map +1 -1
  3. package/dist/authoring.d.cts +7 -3
  4. package/dist/authoring.d.ts +7 -3
  5. package/dist/authoring.js +142 -56
  6. package/dist/authoring.js.map +1 -1
  7. package/dist/canonical.cjs +63 -13
  8. package/dist/canonical.cjs.map +1 -1
  9. package/dist/canonical.d.cts +77 -72
  10. package/dist/canonical.d.ts +77 -72
  11. package/dist/canonical.js +63 -13
  12. package/dist/canonical.js.map +1 -1
  13. package/dist/cli.cjs +170 -72
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.d.cts +23 -1
  16. package/dist/cli.d.ts +23 -1
  17. package/dist/cli.js +167 -73
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client.cjs.map +1 -1
  20. package/dist/client.d.cts +1 -1
  21. package/dist/client.d.ts +1 -1
  22. package/dist/client.js.map +1 -1
  23. package/dist/compiler.cjs +77 -20
  24. package/dist/compiler.cjs.map +1 -1
  25. package/dist/compiler.d.cts +2 -2
  26. package/dist/compiler.d.ts +2 -2
  27. package/dist/compiler.js +77 -20
  28. package/dist/compiler.js.map +1 -1
  29. package/dist/{constants-Dou1RkTo.d.cts → constants-D2o0vK6U.d.cts} +8 -1
  30. package/dist/{constants-Dou1RkTo.d.ts → constants-D2o0vK6U.d.ts} +8 -1
  31. package/dist/dynamic-input.d.cts +1 -1
  32. package/dist/dynamic-input.d.ts +1 -1
  33. package/dist/index.cjs +193 -82
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +62 -59
  36. package/dist/index.d.ts +62 -59
  37. package/dist/index.js +192 -83
  38. package/dist/index.js.map +1 -1
  39. package/dist/ir.cjs +70 -15
  40. package/dist/ir.cjs.map +1 -1
  41. package/dist/ir.d.cts +76 -76
  42. package/dist/ir.d.ts +76 -76
  43. package/dist/ir.js +70 -15
  44. package/dist/ir.js.map +1 -1
  45. package/dist/predicate-v2.cjs +70 -15
  46. package/dist/predicate-v2.cjs.map +1 -1
  47. package/dist/predicate-v2.d.cts +1 -1
  48. package/dist/predicate-v2.d.ts +1 -1
  49. package/dist/predicate-v2.js +70 -15
  50. package/dist/predicate-v2.js.map +1 -1
  51. package/dist/sdl.cjs +70 -15
  52. package/dist/sdl.cjs.map +1 -1
  53. package/dist/sdl.d.cts +1 -1
  54. package/dist/sdl.d.ts +1 -1
  55. package/dist/sdl.js +70 -15
  56. package/dist/sdl.js.map +1 -1
  57. package/package.json +3 -3
  58. package/templates/orm-namespace/package.json +2 -1
  59. package/dist/{definition-DjfC7Lll.d.cts → definition-B99BsOAO.d.cts} +4 -4
  60. package/dist/{definition-DjfC7Lll.d.ts → definition-B99BsOAO.d.ts} +4 -4
@@ -254,6 +254,48 @@ var policyExpressionSchema = zod.z.union([
254
254
  variants: zod.z.array(policyVariantSchema).max(10).default([])
255
255
  })
256
256
  ]);
257
+
258
+ // src/canonical/resolved-rows.ts
259
+ function logicalKey(table) {
260
+ return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
261
+ }
262
+ function sameKey(key, expected) {
263
+ return key.length === expected.length && key.every((name) => expected.includes(name));
264
+ }
265
+ function validateRowIdentity(input) {
266
+ validateExistsIn(input);
267
+ if (input.keys.length > 1) validateCompositeKey(input);
268
+ }
269
+ function validateExistsIn({ resolved, sourceByName, fail }) {
270
+ const name = resolved.existsIn?.[0];
271
+ if (name === void 0) return;
272
+ if (sourceByName.get(name)?.kind !== "etl") fail(`existsIn '${name}' must be an etl source`);
273
+ if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
274
+ fail("existsIn allows only an update policy: rows come from the etl source");
275
+ }
276
+ }
277
+ function validateCompositeKey({ resolved, keys, tableByName, fail }) {
278
+ const keyList = `(${keys.join(", ")})`;
279
+ if (resolved.existsIn === void 0) {
280
+ fail(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
281
+ return;
282
+ }
283
+ const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
284
+ if (writes.length > 0) fail(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
285
+ if (resolved.materialized) fail(`composite key ${keyList} cannot be materialized`);
286
+ for (const source of resolved.sources) {
287
+ if (source.kind === "prefetch") {
288
+ fail(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
289
+ continue;
290
+ }
291
+ const table = tableByName.get(source.table);
292
+ if (table && !sameKey(logicalKey(table), keys)) {
293
+ fail(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
294
+ }
295
+ }
296
+ }
297
+
298
+ // src/canonical/resolved.ts
257
299
  function parseDurationMs(input) {
258
300
  if (typeof input === "number") return Number.isInteger(input) && input > 0 ? input : void 0;
259
301
  const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(input);
@@ -331,6 +373,13 @@ var canonicalResolvedSchema = zod.z.object({
331
373
  resolve: zod.z.array(canonicalResolveRuleSchema).max(500),
332
374
  /** Sources whose tombstone hides the resolved row (v1: the manual source only). */
333
375
  deletedBy: zod.z.array(identifier).max(1).default([]),
376
+ /**
377
+ * `rows.existsIn`: the one etl source whose rows are the table's rows (read
378
+ * directly as the row source). Only an update policy may write. It is also
379
+ * what admits a composite primary key: a read-only table keyed exactly like
380
+ * that source.
381
+ */
382
+ existsIn: zod.z.array(identifier).length(1).optional(),
334
383
  /**
335
384
  * Materialized: the view reads an indexed internal table (`table`) that the
336
385
  * data plane keeps in step with `compute`, the internal view holding the
@@ -349,8 +398,8 @@ function validateResolvedView({ view, tables, views = [] }) {
349
398
  if (!resolved) return [];
350
399
  const errors = [];
351
400
  const fail = (message) => errors.push(`resolved table '${view.view}': ${message}`);
352
- const pkColumn = view.primaryKey?.length === 1 ? view.columns.find((c) => c.columnName === view.primaryKey?.[0]) : void 0;
353
- if (!pkColumn) {
401
+ const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
402
+ if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
354
403
  fail("requires exactly one primary-key column");
355
404
  return errors;
356
405
  }
@@ -358,7 +407,7 @@ function validateResolvedView({ view, tables, views = [] }) {
358
407
  const scope = {
359
408
  view,
360
409
  resolved,
361
- pkColumn,
410
+ keys: new Map(keyColumns.map((column) => [column.name, column])),
362
411
  dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
363
412
  sourceByName: /* @__PURE__ */ new Map(),
364
413
  fail
@@ -372,6 +421,7 @@ function validateResolvedView({ view, tables, views = [] }) {
372
421
  if (!table) fail(`source '${source.name}' references unknown table '${source.table}'`);
373
422
  else validateSource({ scope, source, table });
374
423
  }
424
+ validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail });
375
425
  validateRules(scope);
376
426
  validateViewCells(scope);
377
427
  if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
@@ -401,7 +451,7 @@ function validateSource({ scope, source, table }) {
401
451
  validatePrefetchFields({ scope, source });
402
452
  }
403
453
  function validatePrefetchFields({ scope, source }) {
404
- const { fail, pkColumn } = scope;
454
+ const { fail, keys } = scope;
405
455
  if (source.kind !== "prefetch") {
406
456
  if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
407
457
  fail(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
@@ -410,7 +460,7 @@ function validatePrefetchFields({ scope, source }) {
410
460
  }
411
461
  if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
412
462
  fail(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
413
- } else if (source.key !== pkColumn.name && !source.columns.includes(source.key)) {
463
+ } else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
414
464
  fail(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
415
465
  }
416
466
  for (const column of source.writeback?.columns ?? []) {
@@ -418,19 +468,19 @@ function validatePrefetchFields({ scope, source }) {
418
468
  }
419
469
  }
420
470
  function validateSourceColumns({ scope, source, byName }) {
421
- const { fail, pkColumn, dataColumns } = scope;
422
- for (const column of [pkColumn.name, ...source.columns]) {
423
- const own = column === pkColumn.name ? pkColumn : dataColumns.get(column);
471
+ const { fail, keys, dataColumns } = scope;
472
+ for (const column of [...keys.keys(), ...source.columns]) {
473
+ const own = keys.get(column) ?? dataColumns.get(column);
424
474
  const theirs = byName.get(column);
425
475
  if (!own) fail(`source '${source.name}' supplies unknown column '${column}'`);
426
476
  else if (!theirs) fail(`source '${source.name}' table '${source.table}' has no column '${column}'`);
427
477
  else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail(`column '${column}' has a different type on source '${source.name}'`);
428
478
  }
429
479
  }
430
- function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail }) {
480
+ function validateRules({ resolved, keys, dataColumns, sourceByName, fail }) {
431
481
  const ruled = /* @__PURE__ */ new Set();
432
482
  for (const rule of resolved.resolve) {
433
- if (!dataColumns.has(rule.column) || rule.column === pkColumn.name) {
483
+ if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
434
484
  fail(`resolve rule targets unknown column '${rule.column}'`);
435
485
  continue;
436
486
  }
@@ -447,7 +497,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail })
447
497
  }
448
498
  }
449
499
  for (const column of dataColumns.keys()) {
450
- if (column !== pkColumn.name && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
500
+ if (!keys.has(column) && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
451
501
  }
452
502
  }
453
503
  var VIEW_CELL_TYPES = {
@@ -456,10 +506,10 @@ var VIEW_CELL_TYPES = {
456
506
  by: "text",
457
507
  conflicts: "jsonb"
458
508
  };
459
- function validateViewCells({ view, dataColumns, pkColumn, fail }) {
509
+ function validateViewCells({ view, dataColumns, keys, fail }) {
460
510
  const expected = /* @__PURE__ */ new Map();
461
511
  for (const column of dataColumns.values()) {
462
- if (column.name === pkColumn.name) continue;
512
+ if (keys.has(column.name)) continue;
463
513
  for (const part of RESOLVED_VIEW_CELL_PARTS) {
464
514
  expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
465
515
  }