@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
@@ -5,8 +5,8 @@ export { DynamicInputIssue, DynamicInputResult, NormalizedBoolExp, NormalizedOnC
5
5
  export { CompilePolicyInput, CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, ParsedPolicy, PolicyAccess, PolicyColumnInfo, PolicyCompareOp, PolicyCompileError, PolicyCompileErrorCode, PolicyLinkInfo, PolicyNode, PolicyOperand, PolicyParseError, PolicyResolver, PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy } from './predicate-v2.cjs';
6
6
  import { z } from 'zod';
7
7
  export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-BMZKFO0W.cjs';
8
- export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-DjfC7Lll.cjs';
9
- import './constants-Dou1RkTo.cjs';
8
+ export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-B99BsOAO.cjs';
9
+ import './constants-D2o0vK6U.cjs';
10
10
  import './policy-1iZbGnZZ.cjs';
11
11
 
12
12
  type DiagnosticSeverity = 'error' | 'warning';
@@ -5,8 +5,8 @@ export { DynamicInputIssue, DynamicInputResult, NormalizedBoolExp, NormalizedOnC
5
5
  export { CompilePolicyInput, CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, ParsedPolicy, PolicyAccess, PolicyColumnInfo, PolicyCompareOp, PolicyCompileError, PolicyCompileErrorCode, PolicyLinkInfo, PolicyNode, PolicyOperand, PolicyParseError, PolicyResolver, PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy } from './predicate-v2.js';
6
6
  import { z } from 'zod';
7
7
  export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-DUk7AcXs.js';
8
- export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-DjfC7Lll.js';
9
- import './constants-Dou1RkTo.js';
8
+ export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-B99BsOAO.js';
9
+ import './constants-D2o0vK6U.js';
10
10
  import './policy-1iZbGnZZ.js';
11
11
 
12
12
  type DiagnosticSeverity = 'error' | 'warning';
package/dist/compiler.js CHANGED
@@ -264,6 +264,48 @@ var policyExpressionSchema = z.union([
264
264
  variants: z.array(policyVariantSchema).max(10).default([])
265
265
  })
266
266
  ]);
267
+
268
+ // src/canonical/resolved-rows.ts
269
+ function logicalKey(table) {
270
+ return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
271
+ }
272
+ function sameKey(key, expected) {
273
+ return key.length === expected.length && key.every((name2) => expected.includes(name2));
274
+ }
275
+ function validateRowIdentity(input) {
276
+ validateExistsIn(input);
277
+ if (input.keys.length > 1) validateCompositeKey(input);
278
+ }
279
+ function validateExistsIn({ resolved, sourceByName, fail: fail4 }) {
280
+ const name2 = resolved.existsIn?.[0];
281
+ if (name2 === void 0) return;
282
+ if (sourceByName.get(name2)?.kind !== "etl") fail4(`existsIn '${name2}' must be an etl source`);
283
+ if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
284
+ fail4("existsIn allows only an update policy: rows come from the etl source");
285
+ }
286
+ }
287
+ function validateCompositeKey({ resolved, keys, tableByName, fail: fail4 }) {
288
+ const keyList = `(${keys.join(", ")})`;
289
+ if (resolved.existsIn === void 0) {
290
+ fail4(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
291
+ return;
292
+ }
293
+ const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
294
+ if (writes.length > 0) fail4(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
295
+ if (resolved.materialized) fail4(`composite key ${keyList} cannot be materialized`);
296
+ for (const source of resolved.sources) {
297
+ if (source.kind === "prefetch") {
298
+ fail4(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
299
+ continue;
300
+ }
301
+ const table = tableByName.get(source.table);
302
+ if (table && !sameKey(logicalKey(table), keys)) {
303
+ fail4(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
304
+ }
305
+ }
306
+ }
307
+
308
+ // src/canonical/resolved.ts
267
309
  function parseDurationMs(input) {
268
310
  if (typeof input === "number") return Number.isInteger(input) && input > 0 ? input : void 0;
269
311
  const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(input);
@@ -340,6 +382,13 @@ var canonicalResolvedSchema = z.object({
340
382
  resolve: z.array(canonicalResolveRuleSchema).max(500),
341
383
  /** Sources whose tombstone hides the resolved row (v1: the manual source only). */
342
384
  deletedBy: z.array(identifier).max(1).default([]),
385
+ /**
386
+ * `rows.existsIn`: the one etl source whose rows are the table's rows (read
387
+ * directly as the row source). Only an update policy may write. It is also
388
+ * what admits a composite primary key: a read-only table keyed exactly like
389
+ * that source.
390
+ */
391
+ existsIn: z.array(identifier).length(1).optional(),
343
392
  /**
344
393
  * Materialized: the view reads an indexed internal table (`table`) that the
345
394
  * data plane keeps in step with `compute`, the internal view holding the
@@ -358,8 +407,8 @@ function validateResolvedView({ view, tables, views = [] }) {
358
407
  if (!resolved) return [];
359
408
  const errors = [];
360
409
  const fail4 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
361
- const pkColumn = view.primaryKey?.length === 1 ? view.columns.find((c) => c.columnName === view.primaryKey?.[0]) : void 0;
362
- if (!pkColumn) {
410
+ const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
411
+ if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
363
412
  fail4("requires exactly one primary-key column");
364
413
  return errors;
365
414
  }
@@ -367,7 +416,7 @@ function validateResolvedView({ view, tables, views = [] }) {
367
416
  const scope = {
368
417
  view,
369
418
  resolved,
370
- pkColumn,
419
+ keys: new Map(keyColumns.map((column) => [column.name, column])),
371
420
  dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
372
421
  sourceByName: /* @__PURE__ */ new Map(),
373
422
  fail: fail4
@@ -381,6 +430,7 @@ function validateResolvedView({ view, tables, views = [] }) {
381
430
  if (!table) fail4(`source '${source.name}' references unknown table '${source.table}'`);
382
431
  else validateSource({ scope, source, table });
383
432
  }
433
+ validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail: fail4 });
384
434
  validateRules(scope);
385
435
  validateViewCells(scope);
386
436
  if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
@@ -410,7 +460,7 @@ function validateSource({ scope, source, table }) {
410
460
  validatePrefetchFields({ scope, source });
411
461
  }
412
462
  function validatePrefetchFields({ scope, source }) {
413
- const { fail: fail4, pkColumn } = scope;
463
+ const { fail: fail4, keys } = scope;
414
464
  if (source.kind !== "prefetch") {
415
465
  if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
416
466
  fail4(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
@@ -419,7 +469,7 @@ function validatePrefetchFields({ scope, source }) {
419
469
  }
420
470
  if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
421
471
  fail4(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
422
- } else if (source.key !== pkColumn.name && !source.columns.includes(source.key)) {
472
+ } else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
423
473
  fail4(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
424
474
  }
425
475
  for (const column of source.writeback?.columns ?? []) {
@@ -427,19 +477,19 @@ function validatePrefetchFields({ scope, source }) {
427
477
  }
428
478
  }
429
479
  function validateSourceColumns({ scope, source, byName }) {
430
- const { fail: fail4, pkColumn, dataColumns } = scope;
431
- for (const column of [pkColumn.name, ...source.columns]) {
432
- const own = column === pkColumn.name ? pkColumn : dataColumns.get(column);
480
+ const { fail: fail4, keys, dataColumns } = scope;
481
+ for (const column of [...keys.keys(), ...source.columns]) {
482
+ const own = keys.get(column) ?? dataColumns.get(column);
433
483
  const theirs = byName.get(column);
434
484
  if (!own) fail4(`source '${source.name}' supplies unknown column '${column}'`);
435
485
  else if (!theirs) fail4(`source '${source.name}' table '${source.table}' has no column '${column}'`);
436
486
  else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail4(`column '${column}' has a different type on source '${source.name}'`);
437
487
  }
438
488
  }
439
- function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fail4 }) {
489
+ function validateRules({ resolved, keys, dataColumns, sourceByName, fail: fail4 }) {
440
490
  const ruled = /* @__PURE__ */ new Set();
441
491
  for (const rule of resolved.resolve) {
442
- if (!dataColumns.has(rule.column) || rule.column === pkColumn.name) {
492
+ if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
443
493
  fail4(`resolve rule targets unknown column '${rule.column}'`);
444
494
  continue;
445
495
  }
@@ -456,7 +506,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fa
456
506
  }
457
507
  }
458
508
  for (const column of dataColumns.keys()) {
459
- if (column !== pkColumn.name && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
509
+ if (!keys.has(column) && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
460
510
  }
461
511
  }
462
512
  var VIEW_CELL_TYPES = {
@@ -465,10 +515,10 @@ var VIEW_CELL_TYPES = {
465
515
  by: "text",
466
516
  conflicts: "jsonb"
467
517
  };
468
- function validateViewCells({ view, dataColumns, pkColumn, fail: fail4 }) {
518
+ function validateViewCells({ view, dataColumns, keys, fail: fail4 }) {
469
519
  const expected = /* @__PURE__ */ new Map();
470
520
  for (const column of dataColumns.values()) {
471
- if (column.name === pkColumn.name) continue;
521
+ if (keys.has(column.name)) continue;
472
522
  for (const part of RESOLVED_VIEW_CELL_PARTS) {
473
523
  expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
474
524
  }
@@ -1694,8 +1744,13 @@ var resolvedSourceIrSchema = z.object({
1694
1744
  }).optional()
1695
1745
  });
1696
1746
  var resolvedMetaIrSchema = z.object({
1697
- /** Logical pk column of the resolved view (single-column). */
1698
- pk: z.string(),
1747
+ /**
1748
+ * Logical pk column of the resolved view. Absent for a composite key, which
1749
+ * only a read-only `existsIn` table may declare: it has no writes, prefetch
1750
+ * or materialization (the paths that address one key column), and its reads
1751
+ * use the view's `primaryKey` like any keyed view.
1752
+ */
1753
+ pk: z.string().optional(),
1699
1754
  /** Source name of the manual source. */
1700
1755
  manual: z.string(),
1701
1756
  sources: z.array(resolvedSourceIrSchema),
@@ -3151,7 +3206,7 @@ function buildCompileContext(rawCanonical) {
3151
3206
  appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
3152
3207
  ...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
3153
3208
  ...cells ? { cells } : {},
3154
- ...resolved ? { resolved: resolvedMeta({ resolved, pk: primaryKey[0] }) } : {},
3209
+ ...resolved ? { resolved: resolvedMeta({ resolved, primaryKey }) } : {},
3155
3210
  constraints: isView ? [] : constraints,
3156
3211
  dynamicFilterMaxDepth: 6,
3157
3212
  dynamicFilterMaxNodes: 64
@@ -3433,10 +3488,10 @@ function resolvedWritable(entries) {
3433
3488
  const system = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((column) => column.name));
3434
3489
  return entries.filter((entry) => !entry.deprecated && !system.has(entry.name)).map((entry) => entry.name);
3435
3490
  }
3436
- function resolvedMeta({ resolved, pk }) {
3491
+ function resolvedMeta({ resolved, primaryKey }) {
3437
3492
  const manual = resolved.sources.find((source) => source.kind === "manual");
3438
3493
  return {
3439
- pk,
3494
+ ...primaryKey.length === 1 ? { pk: primaryKey[0] } : {},
3440
3495
  manual: manual?.name ?? "",
3441
3496
  deletedBy: [...resolved.deletedBy],
3442
3497
  ...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
@@ -3562,7 +3617,9 @@ function applyPrefetchDirectives({ node, root, meta }) {
3562
3617
  if (directive.maxStaleness !== void 0 && maxStalenessMs === void 0) {
3563
3618
  fail(`@prefetch(maxStaleness: "${directive.maxStaleness}") must look like '500ms', '30s' or '5m'`, "invalid-source-directive");
3564
3619
  }
3565
- const key = root.kind === "byPk" ? { one: root.pk[0].value } : selectKey({ where: root.where, pk: meta.resolved.pk, source, alias: root.alias });
3620
+ const pk = meta.resolved?.pk;
3621
+ if (pk === void 0) return fail(`@prefetch on '${root.alias}' needs a single-column primary key`, "invalid-source-directive");
3622
+ const key = root.kind === "byPk" ? { one: root.pk[0].value } : selectKey({ where: root.where, pk, source, alias: root.alias });
3566
3623
  return { source: source.name, ...maxStalenessMs !== void 0 ? { maxStalenessMs } : {}, key };
3567
3624
  });
3568
3625
  return { ...root, prefetch: plans };
@@ -7176,7 +7233,7 @@ function compileBuiltinSqlArtifact({ namespace, definitions, read, unbackedObjec
7176
7233
  definitions.map((definition) => [definition.namespace, computeSchemaHash(definition)]).sort()
7177
7234
  ])) };
7178
7235
  return normalizeIr({
7179
- irVersion: RUNTIME_SQL_IR_VERSION,
7236
+ irVersion: usesServicePrincipal(policies) ? SERVICE_PRINCIPAL_IR_VERSION : RUNTIME_SQL_IR_VERSION,
7180
7237
  namespace,
7181
7238
  schemaHash: catalog.identity,
7182
7239
  ownSchemaHash: localHash(anchor),