@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
package/dist/ir.js CHANGED
@@ -237,6 +237,48 @@ var policyExpressionSchema = z.union([
237
237
  variants: z.array(policyVariantSchema).max(10).default([])
238
238
  })
239
239
  ]);
240
+
241
+ // src/canonical/resolved-rows.ts
242
+ function logicalKey(table) {
243
+ return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
244
+ }
245
+ function sameKey(key, expected) {
246
+ return key.length === expected.length && key.every((name) => expected.includes(name));
247
+ }
248
+ function validateRowIdentity(input) {
249
+ validateExistsIn(input);
250
+ if (input.keys.length > 1) validateCompositeKey(input);
251
+ }
252
+ function validateExistsIn({ resolved, sourceByName, fail }) {
253
+ const name = resolved.existsIn?.[0];
254
+ if (name === void 0) return;
255
+ if (sourceByName.get(name)?.kind !== "etl") fail(`existsIn '${name}' must be an etl source`);
256
+ if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
257
+ fail("existsIn allows only an update policy: rows come from the etl source");
258
+ }
259
+ }
260
+ function validateCompositeKey({ resolved, keys, tableByName, fail }) {
261
+ const keyList = `(${keys.join(", ")})`;
262
+ if (resolved.existsIn === void 0) {
263
+ fail(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
264
+ return;
265
+ }
266
+ const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
267
+ if (writes.length > 0) fail(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
268
+ if (resolved.materialized) fail(`composite key ${keyList} cannot be materialized`);
269
+ for (const source of resolved.sources) {
270
+ if (source.kind === "prefetch") {
271
+ fail(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
272
+ continue;
273
+ }
274
+ const table = tableByName.get(source.table);
275
+ if (table && !sameKey(logicalKey(table), keys)) {
276
+ fail(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
277
+ }
278
+ }
279
+ }
280
+
281
+ // src/canonical/resolved.ts
240
282
  var identifier = z.string().min(1).max(63).regex(/^[a-z][a-z0-9_]*$/);
241
283
  var durationMs = z.number().int().positive().max(24 * 60 * 60 * 1e3);
242
284
  var RESOLVED_VIEW_SYSTEM_COLUMNS = {
@@ -305,6 +347,13 @@ var canonicalResolvedSchema = z.object({
305
347
  resolve: z.array(canonicalResolveRuleSchema).max(500),
306
348
  /** Sources whose tombstone hides the resolved row (v1: the manual source only). */
307
349
  deletedBy: z.array(identifier).max(1).default([]),
350
+ /**
351
+ * `rows.existsIn`: the one etl source whose rows are the table's rows (read
352
+ * directly as the row source). Only an update policy may write. It is also
353
+ * what admits a composite primary key: a read-only table keyed exactly like
354
+ * that source.
355
+ */
356
+ existsIn: z.array(identifier).length(1).optional(),
308
357
  /**
309
358
  * Materialized: the view reads an indexed internal table (`table`) that the
310
359
  * data plane keeps in step with `compute`, the internal view holding the
@@ -323,8 +372,8 @@ function validateResolvedView({ view, tables, views = [] }) {
323
372
  if (!resolved) return [];
324
373
  const errors = [];
325
374
  const fail = (message) => errors.push(`resolved table '${view.view}': ${message}`);
326
- const pkColumn = view.primaryKey?.length === 1 ? view.columns.find((c) => c.columnName === view.primaryKey?.[0]) : void 0;
327
- if (!pkColumn) {
375
+ const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
376
+ if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
328
377
  fail("requires exactly one primary-key column");
329
378
  return errors;
330
379
  }
@@ -332,7 +381,7 @@ function validateResolvedView({ view, tables, views = [] }) {
332
381
  const scope = {
333
382
  view,
334
383
  resolved,
335
- pkColumn,
384
+ keys: new Map(keyColumns.map((column) => [column.name, column])),
336
385
  dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
337
386
  sourceByName: /* @__PURE__ */ new Map(),
338
387
  fail
@@ -346,6 +395,7 @@ function validateResolvedView({ view, tables, views = [] }) {
346
395
  if (!table) fail(`source '${source.name}' references unknown table '${source.table}'`);
347
396
  else validateSource({ scope, source, table });
348
397
  }
398
+ validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail });
349
399
  validateRules(scope);
350
400
  validateViewCells(scope);
351
401
  if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
@@ -375,7 +425,7 @@ function validateSource({ scope, source, table }) {
375
425
  validatePrefetchFields({ scope, source });
376
426
  }
377
427
  function validatePrefetchFields({ scope, source }) {
378
- const { fail, pkColumn } = scope;
428
+ const { fail, keys } = scope;
379
429
  if (source.kind !== "prefetch") {
380
430
  if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
381
431
  fail(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
@@ -384,7 +434,7 @@ function validatePrefetchFields({ scope, source }) {
384
434
  }
385
435
  if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
386
436
  fail(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
387
- } else if (source.key !== pkColumn.name && !source.columns.includes(source.key)) {
437
+ } else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
388
438
  fail(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
389
439
  }
390
440
  for (const column of source.writeback?.columns ?? []) {
@@ -392,19 +442,19 @@ function validatePrefetchFields({ scope, source }) {
392
442
  }
393
443
  }
394
444
  function validateSourceColumns({ scope, source, byName }) {
395
- const { fail, pkColumn, dataColumns } = scope;
396
- for (const column of [pkColumn.name, ...source.columns]) {
397
- const own = column === pkColumn.name ? pkColumn : dataColumns.get(column);
445
+ const { fail, keys, dataColumns } = scope;
446
+ for (const column of [...keys.keys(), ...source.columns]) {
447
+ const own = keys.get(column) ?? dataColumns.get(column);
398
448
  const theirs = byName.get(column);
399
449
  if (!own) fail(`source '${source.name}' supplies unknown column '${column}'`);
400
450
  else if (!theirs) fail(`source '${source.name}' table '${source.table}' has no column '${column}'`);
401
451
  else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail(`column '${column}' has a different type on source '${source.name}'`);
402
452
  }
403
453
  }
404
- function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail }) {
454
+ function validateRules({ resolved, keys, dataColumns, sourceByName, fail }) {
405
455
  const ruled = /* @__PURE__ */ new Set();
406
456
  for (const rule of resolved.resolve) {
407
- if (!dataColumns.has(rule.column) || rule.column === pkColumn.name) {
457
+ if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
408
458
  fail(`resolve rule targets unknown column '${rule.column}'`);
409
459
  continue;
410
460
  }
@@ -421,7 +471,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail })
421
471
  }
422
472
  }
423
473
  for (const column of dataColumns.keys()) {
424
- if (column !== pkColumn.name && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
474
+ if (!keys.has(column) && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
425
475
  }
426
476
  }
427
477
  var VIEW_CELL_TYPES = {
@@ -430,10 +480,10 @@ var VIEW_CELL_TYPES = {
430
480
  by: "text",
431
481
  conflicts: "jsonb"
432
482
  };
433
- function validateViewCells({ view, dataColumns, pkColumn, fail }) {
483
+ function validateViewCells({ view, dataColumns, keys, fail }) {
434
484
  const expected = /* @__PURE__ */ new Map();
435
485
  for (const column of dataColumns.values()) {
436
- if (column.name === pkColumn.name) continue;
486
+ if (keys.has(column.name)) continue;
437
487
  for (const part of RESOLVED_VIEW_CELL_PARTS) {
438
488
  expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
439
489
  }
@@ -1452,8 +1502,13 @@ var resolvedSourceIrSchema = z.object({
1452
1502
  }).optional()
1453
1503
  });
1454
1504
  var resolvedMetaIrSchema = z.object({
1455
- /** Logical pk column of the resolved view (single-column). */
1456
- pk: z.string(),
1505
+ /**
1506
+ * Logical pk column of the resolved view. Absent for a composite key, which
1507
+ * only a read-only `existsIn` table may declare: it has no writes, prefetch
1508
+ * or materialization (the paths that address one key column), and its reads
1509
+ * use the view's `primaryKey` like any keyed view.
1510
+ */
1511
+ pk: z.string().optional(),
1457
1512
  /** Source name of the manual source. */
1458
1513
  manual: z.string(),
1459
1514
  sources: z.array(resolvedSourceIrSchema),