@sequenceholdings/orm 2.1.1 → 2.2.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 (68) hide show
  1. package/README.md +3 -1
  2. package/dist/authoring.cjs +284 -99
  3. package/dist/authoring.cjs.map +1 -1
  4. package/dist/authoring.d.cts +31 -8
  5. package/dist/authoring.d.ts +31 -8
  6. package/dist/authoring.js +282 -100
  7. package/dist/authoring.js.map +1 -1
  8. package/dist/canonical.cjs +51 -3
  9. package/dist/canonical.cjs.map +1 -1
  10. package/dist/canonical.d.cts +838 -12
  11. package/dist/canonical.d.ts +838 -12
  12. package/dist/canonical.js +47 -4
  13. package/dist/canonical.js.map +1 -1
  14. package/dist/cli.cjs +5838 -4197
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.js +5838 -4197
  17. package/dist/cli.js.map +1 -1
  18. package/dist/client.cjs +49 -32
  19. package/dist/client.cjs.map +1 -1
  20. package/dist/client.d.cts +49 -6
  21. package/dist/client.d.ts +49 -6
  22. package/dist/client.js +49 -32
  23. package/dist/client.js.map +1 -1
  24. package/dist/compiler.cjs +1237 -263
  25. package/dist/compiler.cjs.map +1 -1
  26. package/dist/compiler.d.cts +57 -3
  27. package/dist/compiler.d.ts +57 -3
  28. package/dist/compiler.js +1230 -264
  29. package/dist/compiler.js.map +1 -1
  30. package/dist/{constants-YoFGjIBM.d.cts → constants-C6EG6Wq7.d.cts} +1 -1
  31. package/dist/{constants-YoFGjIBM.d.ts → constants-C6EG6Wq7.d.ts} +1 -1
  32. package/dist/definition-DjfC7Lll.d.cts +91 -0
  33. package/dist/definition-DjfC7Lll.d.ts +91 -0
  34. package/dist/dynamic-input.cjs +2 -1
  35. package/dist/dynamic-input.cjs.map +1 -1
  36. package/dist/dynamic-input.d.cts +9 -2
  37. package/dist/dynamic-input.d.ts +9 -2
  38. package/dist/dynamic-input.js +2 -1
  39. package/dist/dynamic-input.js.map +1 -1
  40. package/dist/index.cjs +1094 -321
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +479 -19
  43. package/dist/index.d.ts +479 -19
  44. package/dist/index.js +1083 -322
  45. package/dist/index.js.map +1 -1
  46. package/dist/ir.cjs +84 -10
  47. package/dist/ir.cjs.map +1 -1
  48. package/dist/ir.d.cts +138 -13
  49. package/dist/ir.d.ts +138 -13
  50. package/dist/ir.js +81 -11
  51. package/dist/ir.js.map +1 -1
  52. package/dist/predicate-v2.cjs +78 -9
  53. package/dist/predicate-v2.cjs.map +1 -1
  54. package/dist/predicate-v2.d.cts +1 -1
  55. package/dist/predicate-v2.d.ts +1 -1
  56. package/dist/predicate-v2.js +78 -9
  57. package/dist/predicate-v2.js.map +1 -1
  58. package/dist/sdl.cjs +156 -21
  59. package/dist/sdl.cjs.map +1 -1
  60. package/dist/sdl.d.cts +8 -1
  61. package/dist/sdl.d.ts +8 -1
  62. package/dist/sdl.js +156 -21
  63. package/dist/sdl.js.map +1 -1
  64. package/package.json +5 -4
  65. package/src/codegen-gql/server-fns.cjs +2 -2
  66. package/templates/orm-namespace/AGENTS.md +10 -0
  67. package/templates/orm-namespace/CLAUDE.md +1 -84
  68. package/templates/orm-namespace/package.json +1 -1
package/dist/compiler.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createHash } from 'crypto';
2
2
  import { z } from 'zod';
3
3
  import { Kind, print, visit, buildSchema, parse, validate, valueFromASTUntyped } from 'graphql';
4
+ import { PgParser } from '@supabase/pg-parser';
4
5
 
5
6
  // src/compiler-gql/artifact.ts
6
7
 
@@ -8,6 +9,7 @@ import { Kind, print, visit, buildSchema, parse, validate, valueFromASTUntyped }
8
9
  var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
9
10
  var MAX_IDENTIFIER_LENGTH = 63;
10
11
  var CAPABILITY_KEY_PATTERN = /^(?:org\/|app\/[a-z][a-z0-9-]{0,63}\/)?[a-z0-9-]{1,64}$/;
12
+ var QUALIFIED_TABLE_PATTERN = /^[a-z][a-z0-9_]{0,62}\.[a-z][a-z0-9_]{0,62}$/;
11
13
  var ORM_SCHEMA_PREFIX = "orm_";
12
14
  var ormSchemaName = (namespace) => `${ORM_SCHEMA_PREFIX}${namespace}`;
13
15
  var CELL_PARTS = ["at", "by", "rev", "source", "conflicts"];
@@ -33,8 +35,18 @@ function stableJson(value) {
33
35
  const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a < b ? -1 : 1).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableJson(entryValue)}`);
34
36
  return `{${entries.join(",")}}`;
35
37
  }
36
- var quoteIdentifier = (name) => `"${name.replaceAll('"', '""')}"`;
38
+
39
+ // src/compiler/sql.ts
40
+ var CLAIMS_SETTING = "request.jwt.claims";
41
+ var CAPABILITIES_CLAIM_KEY = "capabilities";
42
+ var ROLES_CLAIM_KEY = "roles";
43
+ var quoteIdentifier = (name2) => `"${name2.replaceAll('"', '""')}"`;
37
44
  var quoteLiteral = (value) => `'${value.replaceAll("'", "''")}'`;
45
+ var CLAIMS_SOURCE = `nullif(current_setting('${CLAIMS_SETTING}', true), '')::jsonb`;
46
+ var claimExpression = (key) => `(${CLAIMS_SOURCE} ->> ${quoteLiteral(key)})`;
47
+ var scopedClaimsSource = (namespace) => namespace ? `(CASE WHEN ${CLAIMS_SOURCE} ? 'namespaces' THEN ${CLAIMS_SOURCE} -> 'namespaces' -> ${quoteLiteral(namespace)} ELSE ${CLAIMS_SOURCE} END)` : CLAIMS_SOURCE;
48
+ var capabilityExpression = (key, namespace) => `coalesce(${scopedClaimsSource(namespace)} -> '${CAPABILITIES_CLAIM_KEY}' ? ${quoteLiteral(key)}, false)`;
49
+ var roleExpression = (key, namespace) => `coalesce(${scopedClaimsSource(namespace)} -> '${ROLES_CLAIM_KEY}' ? ${quoteLiteral(key)}, false)`;
38
50
 
39
51
  // src/checks/compile.ts
40
52
  function tokenize(source) {
@@ -340,10 +352,10 @@ function validateResolvedView({ view, tables, views = [] }) {
340
352
  const resolved = view.resolved;
341
353
  if (!resolved) return [];
342
354
  const errors = [];
343
- const fail3 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
355
+ const fail4 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
344
356
  const pkColumn = view.primaryKey?.length === 1 ? view.columns.find((c) => c.columnName === view.primaryKey?.[0]) : void 0;
345
357
  if (!pkColumn) {
346
- fail3("requires exactly one primary-key column");
358
+ fail4("requires exactly one primary-key column");
347
359
  return errors;
348
360
  }
349
361
  const systemNames = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((c) => c.name));
@@ -353,92 +365,92 @@ function validateResolvedView({ view, tables, views = [] }) {
353
365
  pkColumn,
354
366
  dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
355
367
  sourceByName: /* @__PURE__ */ new Map(),
356
- fail: fail3
368
+ fail: fail4
357
369
  };
358
- if (resolved.sources.filter((source) => source.kind === "manual").length !== 1) fail3("requires exactly one manual source");
370
+ if (resolved.sources.filter((source) => source.kind === "manual").length !== 1) fail4("requires exactly one manual source");
359
371
  const tableByName = new Map(tables.map((table) => [table.table, table]));
360
372
  for (const source of resolved.sources) {
361
- if (scope.sourceByName.has(source.name)) fail3(`duplicate source '${source.name}'`);
373
+ if (scope.sourceByName.has(source.name)) fail4(`duplicate source '${source.name}'`);
362
374
  scope.sourceByName.set(source.name, source);
363
375
  const table = tableByName.get(source.table);
364
- if (!table) fail3(`source '${source.name}' references unknown table '${source.table}'`);
376
+ if (!table) fail4(`source '${source.name}' references unknown table '${source.table}'`);
365
377
  else validateSource({ scope, source, table });
366
378
  }
367
379
  validateRules(scope);
368
380
  validateViewCells(scope);
369
381
  if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
370
- for (const name of resolved.deletedBy) {
371
- if (scope.sourceByName.get(name)?.kind !== "manual") fail3(`deletedBy '${name}' must be the manual source`);
382
+ for (const name2 of resolved.deletedBy) {
383
+ if (scope.sourceByName.get(name2)?.kind !== "manual") fail4(`deletedBy '${name2}' must be the manual source`);
372
384
  }
373
385
  return errors;
374
386
  }
375
387
  function validateSource({ scope, source, table }) {
376
- const { fail: fail3 } = scope;
377
- if (source.kind === "etl" && table.source === void 0) fail3(`etl source '${source.name}' must reference a synced table`);
388
+ const { fail: fail4 } = scope;
389
+ if (source.kind === "etl" && table.source === void 0) fail4(`etl source '${source.name}' must reference a synced table`);
378
390
  if (source.kind !== "etl" && (table.internal !== true || table.cellMetadata !== true)) {
379
- fail3(`${source.kind} source '${source.name}' must reference an internal table with cell metadata`);
391
+ fail4(`${source.kind} source '${source.name}' must reference an internal table with cell metadata`);
380
392
  }
381
393
  const hasRev = table.columns.some((column) => column.cell?.part === "rev");
382
- if (source.kind === "manual" && !hasRev) fail3(`manual source '${source.name}' must carry '__rev' cell columns`);
383
- if (source.kind === "prefetch" && hasRev) fail3(`prefetch source '${source.name}' must not carry '__rev' cell columns`);
394
+ if (source.kind === "manual" && !hasRev) fail4(`manual source '${source.name}' must carry '__rev' cell columns`);
395
+ if (source.kind === "prefetch" && hasRev) fail4(`prefetch source '${source.name}' must not carry '__rev' cell columns`);
384
396
  const byName = new Map(table.columns.map((c) => [c.name, c]));
385
397
  validateSourceColumns({ scope, source, byName });
386
398
  if (source.kind === "etl") {
387
399
  const updatedAt = source.updatedAt === void 0 ? void 0 : byName.get(source.updatedAt);
388
400
  if (source.updatedAt !== void 0 && updatedAt?.type !== "timestamptz" && updatedAt?.type !== "date") {
389
- fail3(`etl source '${source.name}' updatedAt must name a timestamptz/date column on '${source.table}'`);
401
+ fail4(`etl source '${source.name}' updatedAt must name a timestamptz/date column on '${source.table}'`);
390
402
  }
391
403
  }
392
404
  validatePrefetchFields({ scope, source });
393
405
  }
394
406
  function validatePrefetchFields({ scope, source }) {
395
- const { fail: fail3, pkColumn } = scope;
407
+ const { fail: fail4, pkColumn } = scope;
396
408
  if (source.kind !== "prefetch") {
397
409
  if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
398
- fail3(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
410
+ fail4(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
399
411
  }
400
412
  return;
401
413
  }
402
414
  if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
403
- fail3(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
415
+ fail4(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
404
416
  } else if (source.key !== pkColumn.name && !source.columns.includes(source.key)) {
405
- fail3(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
417
+ fail4(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
406
418
  }
407
419
  for (const column of source.writeback?.columns ?? []) {
408
- if (!source.columns.includes(column)) fail3(`prefetch source '${source.name}' writes back '${column}', which it does not supply`);
420
+ if (!source.columns.includes(column)) fail4(`prefetch source '${source.name}' writes back '${column}', which it does not supply`);
409
421
  }
410
422
  }
411
423
  function validateSourceColumns({ scope, source, byName }) {
412
- const { fail: fail3, pkColumn, dataColumns } = scope;
424
+ const { fail: fail4, pkColumn, dataColumns } = scope;
413
425
  for (const column of [pkColumn.name, ...source.columns]) {
414
426
  const own = column === pkColumn.name ? pkColumn : dataColumns.get(column);
415
427
  const theirs = byName.get(column);
416
- if (!own) fail3(`source '${source.name}' supplies unknown column '${column}'`);
417
- else if (!theirs) fail3(`source '${source.name}' table '${source.table}' has no column '${column}'`);
418
- else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail3(`column '${column}' has a different type on source '${source.name}'`);
428
+ if (!own) fail4(`source '${source.name}' supplies unknown column '${column}'`);
429
+ else if (!theirs) fail4(`source '${source.name}' table '${source.table}' has no column '${column}'`);
430
+ else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail4(`column '${column}' has a different type on source '${source.name}'`);
419
431
  }
420
432
  }
421
- function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fail3 }) {
433
+ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fail4 }) {
422
434
  const ruled = /* @__PURE__ */ new Set();
423
435
  for (const rule of resolved.resolve) {
424
436
  if (!dataColumns.has(rule.column) || rule.column === pkColumn.name) {
425
- fail3(`resolve rule targets unknown column '${rule.column}'`);
437
+ fail4(`resolve rule targets unknown column '${rule.column}'`);
426
438
  continue;
427
439
  }
428
- if (ruled.has(rule.column)) fail3(`column '${rule.column}' has more than one resolve rule`);
440
+ if (ruled.has(rule.column)) fail4(`column '${rule.column}' has more than one resolve rule`);
429
441
  ruled.add(rule.column);
430
442
  if (rule.comparator === "custom" !== (rule.orderBy !== void 0)) {
431
- fail3(`column '${rule.column}': orderBy is required for custom comparators and only allowed there`);
443
+ fail4(`column '${rule.column}': orderBy is required for custom comparators and only allowed there`);
432
444
  }
433
- if (new Set(rule.candidates).size !== rule.candidates.length) fail3(`column '${rule.column}' repeats a candidate`);
445
+ if (new Set(rule.candidates).size !== rule.candidates.length) fail4(`column '${rule.column}' repeats a candidate`);
434
446
  for (const candidate of rule.candidates) {
435
447
  const source = sourceByName.get(candidate);
436
- if (!source) fail3(`column '${rule.column}' names unknown candidate source '${candidate}'`);
437
- else if (!source.columns.includes(rule.column)) fail3(`column '${rule.column}' candidate '${candidate}' does not supply it`);
448
+ if (!source) fail4(`column '${rule.column}' names unknown candidate source '${candidate}'`);
449
+ else if (!source.columns.includes(rule.column)) fail4(`column '${rule.column}' candidate '${candidate}' does not supply it`);
438
450
  }
439
451
  }
440
452
  for (const column of dataColumns.keys()) {
441
- if (column !== pkColumn.name && !ruled.has(column)) fail3(`column '${column}' has no resolve rule`);
453
+ if (column !== pkColumn.name && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
442
454
  }
443
455
  }
444
456
  var VIEW_CELL_TYPES = {
@@ -447,7 +459,7 @@ var VIEW_CELL_TYPES = {
447
459
  by: "text",
448
460
  conflicts: "jsonb"
449
461
  };
450
- function validateViewCells({ view, dataColumns, pkColumn, fail: fail3 }) {
462
+ function validateViewCells({ view, dataColumns, pkColumn, fail: fail4 }) {
451
463
  const expected = /* @__PURE__ */ new Map();
452
464
  for (const column of dataColumns.values()) {
453
465
  if (column.name === pkColumn.name) continue;
@@ -459,34 +471,34 @@ function validateViewCells({ view, dataColumns, pkColumn, fail: fail3 }) {
459
471
  if (!column.cell) continue;
460
472
  const want = expected.get(column.name);
461
473
  if (!want || want.part !== column.cell.part || want.columnName !== column.columnName || cellColumnName(column.cell.of, column.cell.part) !== column.name) {
462
- fail3(`unexpected cell column '${column.name}'`);
474
+ fail4(`unexpected cell column '${column.name}'`);
463
475
  continue;
464
476
  }
465
- if (column.type !== VIEW_CELL_TYPES[column.cell.part]) fail3(`cell column '${column.name}' must be ${VIEW_CELL_TYPES[column.cell.part]}`);
477
+ if (column.type !== VIEW_CELL_TYPES[column.cell.part]) fail4(`cell column '${column.name}' must be ${VIEW_CELL_TYPES[column.cell.part]}`);
466
478
  expected.delete(column.name);
467
479
  }
468
- for (const name of expected.keys()) fail3(`missing cell column '${name}'`);
480
+ for (const name2 of expected.keys()) fail4(`missing cell column '${name2}'`);
469
481
  }
470
482
  var columnSignature = (columns) => JSON.stringify(columns.map((column) => [column.name, column.columnName, column.type]));
471
483
  function validateMaterialized({ scope, tableByName, views }) {
472
- const { view, resolved, fail: fail3 } = scope;
484
+ const { view, resolved, fail: fail4 } = scope;
473
485
  const { table: tableName, compute: computeName } = resolved.materialized;
474
486
  const table = tableByName.get(tableName);
475
487
  if (!table || table.internal !== true || table.materializes !== view.view) {
476
- fail3(`materialized table '${tableName}' must be an internal table that materializes '${view.view}'`);
488
+ fail4(`materialized table '${tableName}' must be an internal table that materializes '${view.view}'`);
477
489
  } else {
478
- if (columnSignature(table.columns) !== columnSignature(view.columns)) fail3(`materialized table '${tableName}' must have exactly the view's columns`);
479
- if (JSON.stringify(table.primaryKey ?? []) !== JSON.stringify(view.primaryKey ?? [])) fail3(`materialized table '${tableName}' must share the view's primary key`);
490
+ if (columnSignature(table.columns) !== columnSignature(view.columns)) fail4(`materialized table '${tableName}' must have exactly the view's columns`);
491
+ if (JSON.stringify(table.primaryKey ?? []) !== JSON.stringify(view.primaryKey ?? [])) fail4(`materialized table '${tableName}' must share the view's primary key`);
480
492
  }
481
493
  const compute = views.find((candidate) => candidate.view === computeName);
482
- if (!compute || compute.internal !== true) fail3(`compute view '${computeName}' must be an internal view`);
483
- else if (columnSignature(compute.columns) !== columnSignature(view.columns)) fail3(`compute view '${computeName}' must have exactly the view's columns`);
494
+ if (!compute || compute.internal !== true) fail4(`compute view '${computeName}' must be an internal view`);
495
+ else if (columnSignature(compute.columns) !== columnSignature(view.columns)) fail4(`compute view '${computeName}' must have exactly the view's columns`);
484
496
  for (const source of resolved.sources) {
485
497
  if (source.kind !== "etl") continue;
486
498
  const read = tableByName.get(source.table)?.policies?.read;
487
499
  const unconditional = typeof read === "string" ? read.trim() === "true" : read !== void 0 && read.default.trim() === "true" && read.variants.length === 0;
488
500
  if (!unconditional) {
489
- fail3(`etl source '${source.name}' must have read policy 'true' to be materialized (the copy is shared by every caller)`);
501
+ fail4(`etl source '${source.name}' must have read policy 'true' to be materialized (the copy is shared by every caller)`);
490
502
  }
491
503
  }
492
504
  }
@@ -556,6 +568,8 @@ var canonicalLinkSchema = z.object({
556
568
  kind: z.enum(["toOne", "toMany", "toPlatformUser"]),
557
569
  /** Target table name (namespace-local). Absent for toPlatformUser. */
558
570
  target: identifier2.optional(),
571
+ /** Owning namespace of an imported target; omitted for local links. */
572
+ namespace: identifier2.max(41).optional(),
559
573
  /** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
560
574
  on: columnIdentifier,
561
575
  /** Referenced column on the target (default: target's PK / platform user `sub`). */
@@ -657,11 +671,12 @@ var canonicalForeignKeySchema = z.object({
657
671
  }).strict();
658
672
  var canonicalTableSchema = z.object({
659
673
  table: identifier2,
674
+ displayName: z.string().trim().min(1).max(120).optional(),
660
675
  description: z.string().max(2e3).optional(),
661
676
  /** Table-level rename hint (new in v2). */
662
677
  renamedFrom: identifier2.optional(),
663
- /** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
664
- source: z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
678
+ /** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
679
+ source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
665
680
  columns: z.array(canonicalColumnSchema).default([]),
666
681
  primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
667
682
  links: z.array(canonicalLinkSchema).default([]),
@@ -768,53 +783,55 @@ function trackedCellColumns(table) {
768
783
  }
769
784
  var SUFFIXES = Object.values(CELL_PART_SUFFIX);
770
785
  function validateCellMetadata(table, index, ctx) {
771
- const fail3 = (message, path = ["tables", index]) => ctx.addIssue({ code: "custom", path, message });
786
+ const fail4 = (message, path = ["tables", index]) => ctx.addIssue({ code: "custom", path, message });
772
787
  const cells = table.columns.flatMap((column, position) => column.cell ? [{ column, position, cell: column.cell }] : []);
773
788
  if (table.materializes !== void 0) return;
774
789
  if (table.cellMetadata !== true) {
775
- for (const { position } of cells) fail3(`cell-metadata columns require cellMetadata on table '${table.table}'`, ["tables", index, "columns", position]);
790
+ for (const { position } of cells) fail4(`cell-metadata columns require cellMetadata on table '${table.table}'`, ["tables", index, "columns", position]);
776
791
  return;
777
792
  }
778
793
  if (table.source !== void 0) {
779
- fail3(`synced table '${table.table}' cannot track cell metadata`);
794
+ fail4(`synced table '${table.table}' cannot track cell metadata`);
780
795
  return;
781
796
  }
782
797
  const tracked = trackedCellColumns(table);
783
798
  const byName = new Map(tracked.map((column) => [column.name, column]));
784
799
  const withRev = cells.some(({ cell }) => cell.part === "rev");
785
- if (withRev && table.internal !== true) fail3(`only a resolved table's internal sources carry '__rev' cell columns ('${table.table}')`);
800
+ if (withRev && table.internal !== true) fail4(`only a resolved table's internal sources carry '__rev' cell columns ('${table.table}')`);
786
801
  const seen = /* @__PURE__ */ new Set();
787
802
  for (const { column, position, cell } of cells) {
788
803
  const path = ["tables", index, "columns", position];
789
804
  const owner = byName.get(cell.of);
790
805
  if (!owner) {
791
- fail3(`cell column '${column.name}' belongs to '${cell.of}', which is not a tracked column`, path);
806
+ fail4(`cell column '${column.name}' belongs to '${cell.of}', which is not a tracked column`, path);
792
807
  continue;
793
808
  }
794
- if (cell.part !== "at" && cell.part !== "by" && cell.part !== "rev") fail3(`a table cell column must be part at/by/rev, not '${cell.part}'`, path);
809
+ if (cell.part !== "at" && cell.part !== "by" && cell.part !== "rev") fail4(`a table cell column must be part at/by/rev, not '${cell.part}'`, path);
795
810
  if (column.name !== cellColumnName(owner.name, cell.part) || column.columnName !== cellColumnName(owner.columnName, cell.part)) {
796
- fail3(`cell column for '${owner.name}' ${cell.part} must be named '${cellColumnName(owner.name, cell.part)}' / '${cellColumnName(owner.columnName, cell.part)}'`, path);
811
+ fail4(`cell column for '${owner.name}' ${cell.part} must be named '${cellColumnName(owner.name, cell.part)}' / '${cellColumnName(owner.columnName, cell.part)}'`, path);
797
812
  }
798
813
  if (column.type !== CELL_PART_TYPES[cell.part] || !column.nullable || column.unique || column.default !== void 0 || column.defaultFn !== void 0) {
799
- fail3(`cell column '${column.name}' must be a nullable, default-less ${CELL_PART_TYPES[cell.part]}`, path);
814
+ fail4(`cell column '${column.name}' must be a nullable, default-less ${CELL_PART_TYPES[cell.part]}`, path);
800
815
  }
801
816
  const key = `${cell.of}:${cell.part}`;
802
- if (seen.has(key)) fail3(`duplicate cell column '${column.name}'`, path);
817
+ if (seen.has(key)) fail4(`duplicate cell column '${column.name}'`, path);
803
818
  seen.add(key);
804
819
  }
805
820
  for (const column of tracked) {
806
821
  if (column.columnName.length > MAX_TRACKED_COLUMN_NAME) {
807
- fail3(`column '${column.columnName}' is too long to track cell metadata (max ${MAX_TRACKED_COLUMN_NAME} characters)`);
822
+ fail4(`column '${column.columnName}' is too long to track cell metadata (max ${MAX_TRACKED_COLUMN_NAME} characters)`);
808
823
  }
809
824
  if (SUFFIXES.some((suffix) => column.columnName.endsWith(suffix))) {
810
- fail3(`column '${column.columnName}' ends in a reserved cell-metadata suffix (${SUFFIXES.join(", ")})`);
825
+ fail4(`column '${column.columnName}' ends in a reserved cell-metadata suffix (${SUFFIXES.join(", ")})`);
811
826
  }
812
827
  for (const part of withRev ? ["at", "by", "rev"] : ["at", "by"]) {
813
- if (!seen.has(`${column.name}:${part}`)) fail3(`tracked column '${column.name}' is missing its '${cellColumnName(column.columnName, part)}' cell column`);
828
+ if (!seen.has(`${column.name}:${part}`)) fail4(`tracked column '${column.name}' is missing its '${cellColumnName(column.columnName, part)}' cell column`);
814
829
  }
815
830
  }
816
831
  }
817
- var canonicalDefinitionSchema = z.object({
832
+ var exportGateSchema = z.union([z.literal(true), z.object({ capability: z.string().min(1) }).strict(), z.object({ role: z.string().min(1) }).strict()]);
833
+ var relationExportSchema = z.object({ read: exportGateSchema, write: exportGateSchema.optional() }).strict();
834
+ var localDefinitionSchema = z.object({
818
835
  definition_format: z.literal(CANONICAL_FORMAT),
819
836
  namespace: identifier2.max(41),
820
837
  description: z.string().max(2e3).optional(),
@@ -822,6 +839,8 @@ var canonicalDefinitionSchema = z.object({
822
839
  tables: z.array(canonicalTableSchema).min(0).max(500),
823
840
  /** Optional to preserve byte-identical hashes for pre-view canonical v2 documents. */
824
841
  views: z.array(canonicalViewSchema).max(500).optional(),
842
+ /** Target-owned cross-namespace access contract, using existing namespace grants. */
843
+ exports: z.record(identifier2, relationExportSchema).optional(),
825
844
  /** Verbatim custom-roles grammar (@sequenceholdings/custom-roles-manifest). */
826
845
  capabilities: z.object({
827
846
  roles: z.unknown().optional(),
@@ -848,7 +867,16 @@ var canonicalDefinitionSchema = z.object({
848
867
  }
849
868
  }
850
869
  const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
870
+ const relations = /* @__PURE__ */ new Set([...fkTables.keys(), ...(definition.views ?? []).map((view) => view.view)]);
871
+ for (const name2 of Object.keys(definition.exports ?? {})) {
872
+ if (!relations.has(name2)) ctx.addIssue({ code: "custom", path: ["exports", name2], message: `export '${name2}' is not a local object` });
873
+ }
851
874
  for (const [tableIndex, table] of definition.tables.entries()) {
875
+ for (const [linkIndex, link] of table.links.entries()) {
876
+ if (link.enforce && link.namespace !== void 0 && link.namespace !== definition.namespace) {
877
+ ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "links", linkIndex], message: "physical foreign keys require a local target; use an ORM link without enforce" });
878
+ }
879
+ }
852
880
  const names = /* @__PURE__ */ new Set([
853
881
  ...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
854
882
  ...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
@@ -864,38 +892,38 @@ var canonicalDefinitionSchema = z.object({
864
892
  }
865
893
  }
866
894
  for (const [index, check] of (table.checks ?? []).entries()) {
867
- const fail3 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "checks", index], message });
868
- if (table.source) fail3("synced tables cannot declare checks");
869
- if (names.has(check.name)) fail3(`duplicate constraint '${check.name}'`);
895
+ const fail4 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "checks", index], message });
896
+ if (table.source) fail4("synced tables cannot declare checks");
897
+ if (names.has(check.name)) fail4(`duplicate constraint '${check.name}'`);
870
898
  names.add(check.name);
871
899
  try {
872
900
  compileCheckExpression(check.expression, table.columns);
873
901
  } catch (error) {
874
- fail3(error instanceof Error ? error.message : String(error));
902
+ fail4(error instanceof Error ? error.message : String(error));
875
903
  }
876
904
  }
877
905
  for (const [index, fk] of (table.foreignKeys ?? []).entries()) {
878
- const fail3 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index], message });
879
- if (names.has(fk.name)) fail3(`duplicate foreign key '${fk.name}'`);
906
+ const fail4 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index], message });
907
+ if (names.has(fk.name)) fail4(`duplicate foreign key '${fk.name}'`);
880
908
  names.add(fk.name);
881
909
  const target = fkTables.get(fk.target);
882
910
  if (table.source || !target || target.source) {
883
- fail3("foreign keys must connect owned tables in the same namespace");
911
+ fail4("foreign keys must connect owned tables in the same namespace");
884
912
  continue;
885
913
  }
886
- if (fk.columns.length !== fk.references.length) fail3("foreign key columns and references must have the same number of columns");
887
- if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail3("foreign key columns must not repeat");
888
- for (const [position, name] of fk.columns.entries()) {
889
- const sourceColumn = table.columns.find((column) => column.columnName === name);
914
+ if (fk.columns.length !== fk.references.length) fail4("foreign key columns and references must have the same number of columns");
915
+ if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail4("foreign key columns must not repeat");
916
+ for (const [position, name2] of fk.columns.entries()) {
917
+ const sourceColumn = table.columns.find((column) => column.columnName === name2);
890
918
  const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
891
- if (!sourceColumn || !targetColumn) fail3("foreign key references an unknown column");
892
- else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail3("foreign key column types must match");
919
+ if (!sourceColumn || !targetColumn) fail4("foreign key references an unknown column");
920
+ else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail4("foreign key column types must match");
893
921
  }
894
922
  if (fk.onDelete === "set null" || fk.onUpdate === "set null") {
895
- if (fk.columns.some((name) => table.columns.find((column) => column.columnName === name)?.nullable === false || table.primaryKey?.includes(name))) fail3("SET NULL requires nullable source columns");
923
+ if (fk.columns.some((name2) => table.columns.find((column) => column.columnName === name2)?.nullable === false || table.primaryKey?.includes(name2))) fail4("SET NULL requires nullable source columns");
896
924
  }
897
925
  const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index2) => index2.unique).map((index2) => index2.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
898
- if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail3("foreign key target columns must form a unique key");
926
+ if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail4("foreign key target columns must form a unique key");
899
927
  }
900
928
  }
901
929
  for (const [viewIndex, view] of (definition.views ?? []).entries()) {
@@ -997,27 +1025,31 @@ var canonicalDefinitionSchema = z.object({
997
1025
  const visiting = /* @__PURE__ */ new Set();
998
1026
  const visited = /* @__PURE__ */ new Set();
999
1027
  let cycleReported = false;
1000
- const visit2 = (name, path) => {
1001
- if (visited.has(name) || cycleReported) return;
1002
- if (visiting.has(name)) {
1028
+ const visit2 = (name2, path) => {
1029
+ if (visited.has(name2) || cycleReported) return;
1030
+ if (visiting.has(name2)) {
1003
1031
  cycleReported = true;
1004
1032
  ctx.addIssue({
1005
1033
  code: "custom",
1006
1034
  path: ["views"],
1007
- message: `view dependencies form a cycle: ${[...path, name].join(" -> ")}`
1035
+ message: `view dependencies form a cycle: ${[...path, name2].join(" -> ")}`
1008
1036
  });
1009
1037
  return;
1010
1038
  }
1011
- visiting.add(name);
1012
- const view = viewByName.get(name);
1039
+ visiting.add(name2);
1040
+ const view = viewByName.get(name2);
1013
1041
  for (const dependency of view?.dependencies ?? []) {
1014
- if (viewByName.has(dependency)) visit2(dependency, [...path, name]);
1042
+ if (viewByName.has(dependency)) visit2(dependency, [...path, name2]);
1015
1043
  }
1016
- visiting.delete(name);
1017
- visited.add(name);
1044
+ visiting.delete(name2);
1045
+ visited.add(name2);
1018
1046
  };
1019
1047
  for (const view of views) visit2(view.view, []);
1020
1048
  });
1049
+ var canonicalDefinitionSchema = localDefinitionSchema.safeExtend({
1050
+ /** Flattened, pinned snapshots. Imported relations never participate in this namespace's DDL. */
1051
+ imports: z.array(localDefinitionSchema).max(32).optional()
1052
+ });
1021
1053
  function canonicalRelations(definition) {
1022
1054
  const views = (definition.views ?? []).map((view) => ({
1023
1055
  table: view.view,
@@ -1061,13 +1093,22 @@ function normalizeCanonical(definition) {
1061
1093
  dependencies: [...view.dependencies].sort()
1062
1094
  }));
1063
1095
  }
1096
+ if (definition.imports?.length) {
1097
+ normalized.imports = definition.imports.map(normalizeCanonical).sort((a, b) => a.namespace.localeCompare(b.namespace));
1098
+ } else {
1099
+ delete normalized.imports;
1100
+ }
1064
1101
  return normalized;
1065
1102
  }
1066
1103
 
1067
1104
  // src/ir/index.ts
1068
- var IR_VERSION = 2;
1105
+ var IR_VERSION = 5;
1069
1106
  var LEGACY_IR_VERSION = 1;
1070
- var irVersionSchema = z.union([z.literal(LEGACY_IR_VERSION), z.literal(IR_VERSION)]);
1107
+ var GUARDED_IR_VERSION = 2;
1108
+ var COMPOSED_IR_VERSION = 3;
1109
+ var SQL_IR_VERSION = 4;
1110
+ var RUNTIME_SQL_IR_VERSION = 5;
1111
+ var irVersionSchema = z.union([z.literal(LEGACY_IR_VERSION), z.literal(GUARDED_IR_VERSION), z.literal(COMPOSED_IR_VERSION), z.literal(SQL_IR_VERSION), z.literal(RUNTIME_SQL_IR_VERSION)]);
1071
1112
  var operationVersionSchema = z.object({ irVersion: irVersionSchema });
1072
1113
  function requiredArtifactIrVersion(operations) {
1073
1114
  let required = LEGACY_IR_VERSION;
@@ -1119,8 +1160,8 @@ var predIrSchema = z.lazy(
1119
1160
  value: z.number(),
1120
1161
  where: predIrSchema.optional()
1121
1162
  }),
1122
- z.object({ kind: z.literal("capability"), key: z.string() }),
1123
- z.object({ kind: z.literal("role"), key: z.string() }),
1163
+ z.object({ kind: z.literal("capability"), key: z.string(), namespace: z.string().optional() }),
1164
+ z.object({ kind: z.literal("role"), key: z.string(), namespace: z.string().optional() }),
1124
1165
  z.object({ kind: z.literal("dynamicFilter"), slot: z.string() }),
1125
1166
  z.object({ kind: z.literal("const"), value: z.boolean() })
1126
1167
  ])
@@ -1347,7 +1388,31 @@ var variableIrSchema = z.object({
1347
1388
  /** Absent = no default declared; see {@link variableDefaultIrSchema}. */
1348
1389
  defaultValue: variableDefaultIrSchema.optional()
1349
1390
  });
1391
+ var sqlColumnSchema = z.object({ name: z.string(), type: z.enum(["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz"]), nullable: z.boolean() });
1350
1392
  var compiledOperationSchema = z.object({
1393
+ language: z.literal("sql").optional(),
1394
+ /** Server-compiled object catalog and permission ceiling, never caller SQL. */
1395
+ sqlQuery: z.object({
1396
+ catalog: z.object({
1397
+ identity: z.string(),
1398
+ objects: z.array(z.object({
1399
+ ref: z.string(),
1400
+ table: z.string(),
1401
+ columns: z.array(sqlColumnSchema.extend({ type: z.string() })).min(1),
1402
+ scan: z.string().min(1),
1403
+ read: z.array(z.string()).min(1)
1404
+ }).strict()).min(1).max(256)
1405
+ }).strict(),
1406
+ read: z.array(z.string()).min(1).max(256),
1407
+ maxRows: z.number().int().min(1).max(1e4)
1408
+ }).strict().optional(),
1409
+ sql: z.object({
1410
+ text: z.string().min(1).max(1048576),
1411
+ columns: z.array(sqlColumnSchema).min(1).max(256),
1412
+ parameters: z.array(z.string()).max(128),
1413
+ read: z.array(z.string()).min(1).max(256),
1414
+ maxRows: z.number().int().min(1).max(1e4)
1415
+ }).strict().optional(),
1351
1416
  irVersion: irVersionSchema,
1352
1417
  name: z.string(),
1353
1418
  opHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
@@ -1359,8 +1424,16 @@ var compiledOperationSchema = z.object({
1359
1424
  variables: z.array(variableIrSchema),
1360
1425
  /** Every table the op touches — data-token scoping + audit. */
1361
1426
  tableSet: z.array(z.string()).min(1),
1362
- roots: z.array(rootIrSchema).min(1)
1427
+ roots: z.array(rootIrSchema)
1363
1428
  }).superRefine((operation, ctx) => {
1429
+ if (operation.language === "sql") {
1430
+ const validPlan = operation.sqlQuery ? !operation.sql && operation.irVersion === RUNTIME_SQL_IR_VERSION : !!operation.sql && operation.irVersion === SQL_IR_VERSION;
1431
+ if (!validPlan || operation.kind !== "query" || operation.roots.length !== 0 || !operation.requires?.capability) {
1432
+ ctx.addIssue({ code: "custom", message: "SQL operations require a query-only v4 plan or v5 runtime contract, capability, and no GraphQL roots" });
1433
+ }
1434
+ } else if (operation.sql || operation.sqlQuery || operation.roots.length === 0) {
1435
+ ctx.addIssue({ code: "custom", message: "GraphQL operations require roots and cannot carry SQL" });
1436
+ }
1364
1437
  for (const [index, root] of operation.roots.entries()) {
1365
1438
  if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
1366
1439
  if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
@@ -1407,11 +1480,16 @@ var resolvedMetaIrSchema = z.object({
1407
1480
  });
1408
1481
  var tableMetaIrSchema = z.object({
1409
1482
  table: z.string(),
1483
+ namespace: z.string().optional(),
1484
+ dependencies: z.array(z.string()).optional(),
1485
+ foreignReferences: z.array(z.object({ column: z.string(), target: z.string(), targetColumn: z.string() })).optional(),
1486
+ exportRead: z.union([z.literal(true), z.object({ capability: z.string() }), z.object({ role: z.string() })]).optional(),
1487
+ exportWrite: z.union([z.literal(true), z.object({ capability: z.string() }), z.object({ role: z.string() })]).optional(),
1410
1488
  schema: z.string(),
1411
1489
  typename: z.string(),
1412
1490
  /**
1413
1491
  * True when statements over this relation evaluate a claims-gated synced
1414
- * view: synced twins (`source: gold.*`) materialize as views whose baked
1492
+ * view: synced twins (`source: schema.table`) materialize as views whose baked
1415
1493
  * policy reads `current_setting('request.jwt.claims')`, and namespace views
1416
1494
  * inherit the gate transitively through their declared dependencies. The
1417
1495
  * executor must stamp that GUC (transaction-scoped) before any statement
@@ -1470,13 +1548,21 @@ var compiledArtifactSchema = z.object({
1470
1548
  irVersion: irVersionSchema,
1471
1549
  namespace: z.string(),
1472
1550
  schemaHash: z.string(),
1551
+ ownSchemaHash: z.string().optional(),
1552
+ dependencies: z.record(z.string(), z.string()).optional(),
1553
+ readPolicies: z.record(z.string(), predIrSchema).optional(),
1473
1554
  opsHash: z.string(),
1555
+ /** Apply-observed synced objects without a serving backing; not part of the authored schema hash. */
1556
+ unbackedTables: z.array(z.string()).optional(),
1474
1557
  tables: z.record(z.string(), tableMetaIrSchema),
1475
1558
  operations: z.record(z.string(), compiledOperationSchema)
1476
1559
  }).superRefine((artifact, ctx) => {
1477
- for (const [name, operation] of Object.entries(artifact.operations)) {
1560
+ if (Object.keys(artifact.dependencies ?? {}).length > 0 && artifact.irVersion < COMPOSED_IR_VERSION) {
1561
+ ctx.addIssue({ code: "custom", path: ["irVersion"], message: "cross-namespace dependencies require IR v3" });
1562
+ }
1563
+ for (const [name2, operation] of Object.entries(artifact.operations)) {
1478
1564
  if (operation.irVersion > artifact.irVersion) {
1479
- ctx.addIssue({ code: "custom", path: ["operations", name, "irVersion"], message: "operation requires a newer IR version than its artifact" });
1565
+ ctx.addIssue({ code: "custom", path: ["operations", name2, "irVersion"], message: "operation requires a newer IR version than its artifact" });
1480
1566
  }
1481
1567
  }
1482
1568
  });
@@ -1489,20 +1575,20 @@ var IrVersionError = class extends Error {
1489
1575
  function normalizeIr(raw) {
1490
1576
  if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
1491
1577
  const version = raw.irVersion;
1492
- if (version !== LEGACY_IR_VERSION && version !== IR_VERSION) throw new IrVersionError(version);
1578
+ if (!irVersionSchema.safeParse(version).success) throw new IrVersionError(version);
1493
1579
  return compiledArtifactSchema.parse(raw);
1494
1580
  }
1495
1581
 
1496
1582
  // src/sdl/naming.ts
1497
- function camelCase(name) {
1498
- const segments = name.split("_").filter((segment) => segment.length > 0);
1583
+ function camelCase(name2) {
1584
+ const segments = name2.split("_").filter((segment) => segment.length > 0);
1499
1585
  const first = segments[0];
1500
- if (first === void 0) return name;
1586
+ if (first === void 0) return name2;
1501
1587
  const head = first.charAt(0).toLowerCase() + first.slice(1);
1502
1588
  return head + segments.slice(1).map(capitalizeFirst).join("");
1503
1589
  }
1504
- function pascalCase(name) {
1505
- return capitalizeFirst(camelCase(name));
1590
+ function pascalCase(name2) {
1591
+ return capitalizeFirst(camelCase(name2));
1506
1592
  }
1507
1593
  function capitalizeFirst(segment) {
1508
1594
  return segment.charAt(0).toUpperCase() + segment.slice(1);
@@ -1522,8 +1608,8 @@ function typeName(table) {
1522
1608
  if (last === void 0) return pascalCase(table);
1523
1609
  return [...segments.slice(0, -1), singularizeWord(last)].map(capitalizeFirst).join("");
1524
1610
  }
1525
- function enumTypeName(name) {
1526
- return pascalCase(name);
1611
+ function enumTypeName(name2) {
1612
+ return pascalCase(name2);
1527
1613
  }
1528
1614
  var SCALAR_FOR_COLUMN_TYPE = {
1529
1615
  uuid: "uuid",
@@ -2314,7 +2400,7 @@ var PolicyInjector = class {
2314
2400
  const updatePolicy = this.update(table);
2315
2401
  const where = andPred(updatePolicy, root.where ? this.injectDocPred(root.where, table) : void 0);
2316
2402
  if (root.op === "update" || root.op === "updateByPk") {
2317
- const check = predContainsExists(updatePolicy) ? {} : { insertCheck: updatePolicy };
2403
+ const check = predContainsExists(updatePolicy) && !this.ctx.composed ? {} : { insertCheck: updatePolicy };
2318
2404
  return { ...root, where, ...check, returning };
2319
2405
  }
2320
2406
  if (root.op === "cowOverride") {
@@ -2328,7 +2414,7 @@ var PolicyInjector = class {
2328
2414
  const pins = (this.ctx.pins.get(table) ?? []).map(
2329
2415
  (pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
2330
2416
  );
2331
- const check = predContainsExists(updatePolicy) ? {} : { insertCheck: updatePolicy };
2417
+ const check = predContainsExists(updatePolicy) && !this.ctx.composed ? {} : { insertCheck: updatePolicy };
2332
2418
  return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, ...check, returning };
2333
2419
  }
2334
2420
  return { ...root, where, returning };
@@ -2479,16 +2565,92 @@ var NAME_KEY_BY_KIND = {
2479
2565
  function inputTypeName(table, kind) {
2480
2566
  return tableNames(table)[NAME_KEY_BY_KIND[kind]];
2481
2567
  }
2482
- function classifyInputType(name, tableByTypename) {
2568
+ function classifyInputType(name2, tableByTypename) {
2483
2569
  for (const [suffix, input] of INPUT_TYPE_SUFFIXES) {
2484
- if (!name.endsWith(suffix) || name.length <= suffix.length) continue;
2485
- const table = tableByTypename.get(name.slice(0, -suffix.length));
2570
+ if (!name2.endsWith(suffix) || name2.length <= suffix.length) continue;
2571
+ const table = tableByTypename.get(name2.slice(0, -suffix.length));
2486
2572
  if (table !== void 0) return { input, table };
2487
2573
  }
2488
2574
  return void 0;
2489
2575
  }
2490
2576
 
2577
+ // src/errors.ts
2578
+ var OrmDefinitionError = class extends Error {
2579
+ constructor(message, file) {
2580
+ super(file ? `${file}: ${message}` : message);
2581
+ this.file = file;
2582
+ }
2583
+ name = "OrmDefinitionError";
2584
+ };
2585
+
2586
+ // src/canonical/composition.ts
2587
+ function localContract(definition) {
2588
+ return normalizeCanonical(localDefinitionSchema.parse(definition));
2589
+ }
2590
+ function composeNamespace(definition) {
2591
+ const all = [localContract(definition), ...definition.imports ?? []];
2592
+ const owners = /* @__PURE__ */ new Map();
2593
+ const namespaces = /* @__PURE__ */ new Set();
2594
+ const enums = /* @__PURE__ */ new Map();
2595
+ for (const owner of all) {
2596
+ if (namespaces.has(owner.namespace)) throw new OrmDefinitionError(`duplicate imported namespace '${owner.namespace}'`);
2597
+ namespaces.add(owner.namespace);
2598
+ for (const value of owner.enums) {
2599
+ const previous = enums.get(value.name);
2600
+ if (previous && stableJson(previous) !== stableJson(value)) {
2601
+ throw new OrmDefinitionError(`conflicting imported enum '${value.name}'`);
2602
+ }
2603
+ enums.set(value.name, value);
2604
+ }
2605
+ for (const relation of canonicalRelations(owner)) {
2606
+ if (owners.has(relation.table)) throw new OrmDefinitionError(`ambiguous imported relation '${relation.table}'`);
2607
+ owners.set(relation.table, owner);
2608
+ }
2609
+ }
2610
+ for (const owner of definition.imports?.length ? all : []) {
2611
+ for (const relation of canonicalRelations(owner)) {
2612
+ for (const link of relation.links) {
2613
+ if (link.kind === "toPlatformUser") continue;
2614
+ const target = owners.get(link.target ?? "");
2615
+ if (!target || target.namespace !== (link.namespace ?? owner.namespace)) {
2616
+ throw new OrmDefinitionError(`link '${owner.namespace}/${relation.table}.${link.name}' has no matching imported target`);
2617
+ }
2618
+ if (target.namespace !== owner.namespace && !target.exports?.[link.target ?? ""]) {
2619
+ throw new OrmDefinitionError(`target '${target.namespace}/${link.target}' is not exported`);
2620
+ }
2621
+ }
2622
+ }
2623
+ }
2624
+ return {
2625
+ owners,
2626
+ definition: {
2627
+ ...localContract(definition),
2628
+ tables: all.flatMap((item) => item.tables),
2629
+ views: all.flatMap((item) => item.views ?? []),
2630
+ enums: [...enums.values()]
2631
+ }
2632
+ };
2633
+ }
2634
+
2491
2635
  // src/compiler-gql/context.ts
2636
+ function scopePolicy(pred, namespace) {
2637
+ switch (pred.kind) {
2638
+ case "capability":
2639
+ case "role":
2640
+ return { ...pred, namespace };
2641
+ case "and":
2642
+ case "or":
2643
+ return { ...pred, operands: pred.operands.map((p) => scopePolicy(p, namespace)) };
2644
+ case "not":
2645
+ return { ...pred, operand: scopePolicy(pred.operand, namespace) };
2646
+ case "exists":
2647
+ return { ...pred, where: scopePolicy(pred.where, namespace) };
2648
+ case "aggPredicate":
2649
+ return pred.where ? { ...pred, where: scopePolicy(pred.where, namespace) } : pred;
2650
+ default:
2651
+ return pred;
2652
+ }
2653
+ }
2492
2654
  function platformUserMeta() {
2493
2655
  const columns = [
2494
2656
  // `sub` is the view's key (auth0_id, filtered NOT NULL in the view).
@@ -2553,7 +2715,8 @@ function logicalColumn(entries, ref) {
2553
2715
  return entries.find((c) => c.name === ref || c.columnName === ref)?.name;
2554
2716
  }
2555
2717
  function buildCompileContext(rawCanonical) {
2556
- const canonical = normalizeCanonical(canonicalDefinitionSchema.parse(rawCanonical));
2718
+ const normalized = normalizeCanonical(canonicalDefinitionSchema.parse(rawCanonical));
2719
+ const { definition: canonical, owners } = composeNamespace(normalized);
2557
2720
  const diagnostics = [];
2558
2721
  const enums = new Map(canonical.enums.map((e) => [e.name, e]));
2559
2722
  const relations = canonicalRelations(canonical);
@@ -2566,17 +2729,18 @@ function buildCompileContext(rawCanonical) {
2566
2729
  const syncedTableNames = new Set(canonical.tables.filter((t) => t.source !== void 0).map((t) => t.table));
2567
2730
  const viewDependencies = new Map((canonical.views ?? []).map((view) => [view.view, view.dependencies]));
2568
2731
  const claimsGatedMemo = /* @__PURE__ */ new Map();
2569
- const relationIsClaimsGated = (name) => {
2570
- if (syncedTableNames.has(name)) return true;
2571
- const memo = claimsGatedMemo.get(name);
2732
+ const relationIsClaimsGated = (name2) => {
2733
+ if (syncedTableNames.has(name2)) return true;
2734
+ const memo = claimsGatedMemo.get(name2);
2572
2735
  if (memo !== void 0) return memo;
2573
- claimsGatedMemo.set(name, false);
2574
- const gated = (viewDependencies.get(name) ?? []).some(relationIsClaimsGated);
2575
- claimsGatedMemo.set(name, gated);
2736
+ claimsGatedMemo.set(name2, false);
2737
+ const gated = (viewDependencies.get(name2) ?? []).some(relationIsClaimsGated);
2738
+ claimsGatedMemo.set(name2, gated);
2576
2739
  return gated;
2577
2740
  };
2578
2741
  for (const table of relations) {
2579
2742
  const isView = viewNames.has(table.table);
2743
+ const owner = owners.get(table.table);
2580
2744
  const entries = resolveColumnEntries(table, enums, diagnostics);
2581
2745
  columnEntries.set(table.table, entries);
2582
2746
  const pkRefs = table.primaryKey ?? (isView ? [] : ["id"]);
@@ -2614,7 +2778,10 @@ function buildCompileContext(rawCanonical) {
2614
2778
  }
2615
2779
  tables[table.table] = {
2616
2780
  table: table.table,
2617
- schema: ormSchemaName(canonical.namespace),
2781
+ schema: ormSchemaName(owner.namespace),
2782
+ namespace: owner.namespace,
2783
+ ...viewDependencies.has(table.table) ? { dependencies: viewDependencies.get(table.table) } : {},
2784
+ ...owner.exports?.[table.table] ? { exportRead: owner.exports[table.table].read, exportWrite: owner.exports[table.table].write } : {},
2618
2785
  typename: typeNameForTable(table.table),
2619
2786
  // Synced twins (and views reading them, resolved transitively below)
2620
2787
  // are claims-gated — see tableMetaIrSchema.claimsGated.
@@ -2625,7 +2792,7 @@ function buildCompileContext(rawCanonical) {
2625
2792
  filterable: data.map((c) => c.name),
2626
2793
  orderable: data.filter((c) => c.type !== "jsonb").map((c) => c.name),
2627
2794
  insertable: resolved ? resolvedWritable(data) : isView ? [] : active.map((c) => c.name),
2628
- settable: resolved ? resolvedWritable(data).filter((name) => !primaryKey.includes(name)) : isView ? [] : active.filter((c) => !c.onUpdateFn).map((c) => c.name),
2795
+ settable: resolved ? resolvedWritable(data).filter((name2) => !primaryKey.includes(name2)) : isView ? [] : active.filter((c) => !c.onUpdateFn).map((c) => c.name),
2629
2796
  appendable: isView ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
2630
2797
  ...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
2631
2798
  ...cells ? { cells } : {},
@@ -2641,7 +2808,7 @@ function buildCompileContext(rawCanonical) {
2641
2808
  for (const link of table.links) {
2642
2809
  const targetName = link.kind === "toPlatformUser" ? PLATFORM_USER_TABLE : link.target;
2643
2810
  const target = targetName === void 0 ? void 0 : tables[targetName];
2644
- if (!targetName || !target) {
2811
+ if (!targetName || !target || link.namespace !== void 0 && link.namespace !== target.namespace) {
2645
2812
  diagnostics.push({
2646
2813
  severity: "error",
2647
2814
  code: "unknown-link-target",
@@ -2666,6 +2833,11 @@ function buildCompileContext(rawCanonical) {
2666
2833
  continue;
2667
2834
  }
2668
2835
  meta.links.push({ name: link.name, kind: link.kind, target: targetName, on, references });
2836
+ if (link.kind !== "toPlatformUser" && meta.namespace !== target.namespace) {
2837
+ const fkOwner = link.kind === "toMany" ? target : meta;
2838
+ const referenced = link.kind === "toMany" ? table.table : targetName;
2839
+ (fkOwner.foreignReferences ??= []).push({ column: on, target: referenced, targetColumn: references });
2840
+ }
2669
2841
  }
2670
2842
  }
2671
2843
  const readPolicies = /* @__PURE__ */ new Map();
@@ -2683,7 +2855,8 @@ function buildCompileContext(rawCanonical) {
2683
2855
  return;
2684
2856
  }
2685
2857
  try {
2686
- store.set(table.table, compilePolicy({ expression, table: table.table, resolver }));
2858
+ const pred = compilePolicy({ expression, table: table.table, resolver });
2859
+ store.set(table.table, normalized.imports?.length ? scopePolicy(pred, owners.get(table.table).namespace) : pred);
2687
2860
  } catch (error) {
2688
2861
  if (error instanceof PolicyCompileError || error instanceof PolicyParseError) {
2689
2862
  diagnostics.push({
@@ -2737,6 +2910,42 @@ function buildCompileContext(rawCanonical) {
2737
2910
  updatePolicies.set(PLATFORM_USER_TABLE, { kind: "const", value: false });
2738
2911
  pins.set(PLATFORM_USER_TABLE, []);
2739
2912
  }
2913
+ if (normalized.imports?.length) {
2914
+ const originalReads = new Map(readPolicies);
2915
+ const protect = (pred, table, path) => {
2916
+ switch (pred.kind) {
2917
+ case "and":
2918
+ case "or":
2919
+ return { ...pred, operands: pred.operands.map((p) => protect(p, table, path)) };
2920
+ case "not":
2921
+ return { ...pred, operand: protect(pred.operand, table, path) };
2922
+ case "exists":
2923
+ case "aggPredicate": {
2924
+ const target = tables[table]?.links.find((link) => link.name === pred.link)?.target;
2925
+ if (!target) return pred;
2926
+ let where = pred.where ? protect(pred.where, target, path) : CONST_TRUE;
2927
+ if (tables[table]?.namespace !== tables[target]?.namespace) {
2928
+ if (path.includes(target)) {
2929
+ diagnostics.push({
2930
+ severity: "error",
2931
+ code: "cyclic-cross-namespace-policy",
2932
+ table,
2933
+ message: `cross-namespace read policies form a cycle through '${target}'`
2934
+ });
2935
+ return CONST_FALSE;
2936
+ }
2937
+ where = andPred(where, protect(originalReads.get(target) ?? CONST_TRUE, target, [...path, target]));
2938
+ }
2939
+ return { ...pred, where };
2940
+ }
2941
+ default:
2942
+ return pred;
2943
+ }
2944
+ };
2945
+ for (const policies of [readPolicies, writePolicies, insertPolicies, updatePolicies]) {
2946
+ for (const [table, policy] of policies) policies.set(table, protect(policy, table, [table]));
2947
+ }
2948
+ }
2740
2949
  const tableByTypename = new Map(Object.entries(tables).map(([key, meta]) => [meta.typename, key]));
2741
2950
  const queryRoots = /* @__PURE__ */ new Map();
2742
2951
  const mutationRoots = /* @__PURE__ */ new Map();
@@ -2813,6 +3022,7 @@ function buildCompileContext(rawCanonical) {
2813
3022
  return {
2814
3023
  ctx: {
2815
3024
  namespace: canonical.namespace,
3025
+ composed: !!normalized.imports?.length,
2816
3026
  tables,
2817
3027
  tableByTypename,
2818
3028
  queryRoots,
@@ -2881,18 +3091,18 @@ function flattenSelectionSet(selectionSet, fragments, stack) {
2881
3091
  continue;
2882
3092
  }
2883
3093
  if (selection.kind === Kind.FRAGMENT_SPREAD) {
2884
- const name = selection.name.value;
3094
+ const name2 = selection.name.value;
2885
3095
  if (selection.directives && selection.directives.length > 0) {
2886
- throw new DocumentCompileError(`directives on fragment spread '...${name}' are not supported`, "fragment-directive");
3096
+ throw new DocumentCompileError(`directives on fragment spread '...${name2}' are not supported`, "fragment-directive");
2887
3097
  }
2888
- if (stack.includes(name)) {
2889
- throw new DocumentCompileError(`fragment cycle detected through '${name}'`, "fragment-cycle");
3098
+ if (stack.includes(name2)) {
3099
+ throw new DocumentCompileError(`fragment cycle detected through '${name2}'`, "fragment-cycle");
2890
3100
  }
2891
- const fragment = fragments.get(name);
3101
+ const fragment = fragments.get(name2);
2892
3102
  if (!fragment) {
2893
- throw new DocumentCompileError(`unknown fragment '${name}'`, "unknown-fragment");
3103
+ throw new DocumentCompileError(`unknown fragment '${name2}'`, "unknown-fragment");
2894
3104
  }
2895
- const inlined2 = flattenSelectionSet(fragment.selectionSet, fragments, [...stack, name]);
3105
+ const inlined2 = flattenSelectionSet(fragment.selectionSet, fragments, [...stack, name2]);
2896
3106
  selections.push(...inlined2.selections);
2897
3107
  continue;
2898
3108
  }
@@ -2932,40 +3142,40 @@ var SOURCE_DIRECTIVES = /* @__PURE__ */ new Set(["prefetch", "writeback", "local
2932
3142
  var fail = (message, code) => {
2933
3143
  throw new DocumentCompileError(message, code);
2934
3144
  };
2935
- function readArgs(node, name) {
2936
- return (node.directives ?? []).filter((directive) => directive.name.value === name).map((directive) => {
3145
+ function readArgs(node, name2) {
3146
+ return (node.directives ?? []).filter((directive) => directive.name.value === name2).map((directive) => {
2937
3147
  const args = {};
2938
3148
  for (const arg of directive.arguments ?? []) {
2939
3149
  const key = arg.name.value;
2940
3150
  if (key === "source" || key === "maxStaleness") {
2941
- if (arg.value.kind !== Kind.STRING) fail(`@${name}(${key}:) must be a string literal`, "invalid-source-directive");
3151
+ if (arg.value.kind !== Kind.STRING) fail(`@${name2}(${key}:) must be a string literal`, "invalid-source-directive");
2942
3152
  args[key] = arg.value.value;
2943
- } else if (key === "mode" && name === "writeback") {
3153
+ } else if (key === "mode" && name2 === "writeback") {
2944
3154
  if (arg.value.kind !== Kind.ENUM || !["SYNC", "ASYNC"].includes(arg.value.value)) {
2945
3155
  fail("@writeback(mode:) must be SYNC or ASYNC", "invalid-source-directive");
2946
3156
  }
2947
3157
  args.mode = arg.value.value === "SYNC" ? "sync" : "async";
2948
3158
  } else {
2949
- fail(`@${name} does not take argument '${key}'`, "invalid-source-directive");
3159
+ fail(`@${name2} does not take argument '${key}'`, "invalid-source-directive");
2950
3160
  }
2951
3161
  }
2952
- if (!args.source) fail(`@${name} needs a source: argument`, "invalid-source-directive");
3162
+ if (!args.source) fail(`@${name2} needs a source: argument`, "invalid-source-directive");
2953
3163
  return args;
2954
3164
  });
2955
3165
  }
2956
- function prefetchSourceFor({ resolved, name, directive, table }) {
3166
+ function prefetchSourceFor({ resolved, name: name2, directive, table }) {
2957
3167
  if (!resolved) return fail(`@${directive} is only valid on resolved tables \u2014 '${table}' is not one`, "invalid-source-directive");
2958
- const source = resolved.sources.find((entry) => entry.name === name);
3168
+ const source = resolved.sources.find((entry) => entry.name === name2);
2959
3169
  if (!source || source.kind !== "prefetch") {
2960
3170
  const options = resolved.sources.filter((entry) => entry.kind === "prefetch").map((entry) => entry.name);
2961
- return fail(`@${directive}(source: "${name}") on '${table}' must name a prefetch source (${options.join(", ") || "none declared"})`, "invalid-source-directive");
3171
+ return fail(`@${directive}(source: "${name2}") on '${table}' must name a prefetch source (${options.join(", ") || "none declared"})`, "invalid-source-directive");
2962
3172
  }
2963
3173
  return source;
2964
3174
  }
2965
3175
  function applyPrefetchDirectives({ node, root, meta }) {
2966
3176
  const directives = readArgs(node, "prefetch");
2967
- for (const name of ["writeback", "localOnly"]) {
2968
- if (readArgs(node, name).length > 0) fail(`@${name} is only valid on resolved-table mutation roots`, "invalid-source-directive");
3177
+ for (const name2 of ["writeback", "localOnly"]) {
3178
+ if (readArgs(node, name2).length > 0) fail(`@${name2} is only valid on resolved-table mutation roots`, "invalid-source-directive");
2969
3179
  }
2970
3180
  if (directives.length === 0) return root;
2971
3181
  if (root.kind !== "select" && root.kind !== "byPk") {
@@ -3126,13 +3336,13 @@ var OperationLowerer = class {
3126
3336
  return this.ctx.tables[table];
3127
3337
  }
3128
3338
  /** A data column — cell-metadata columns are reachable only through `cells { … }`. */
3129
- column(table, name) {
3339
+ column(table, name2) {
3130
3340
  const meta = this.meta(table);
3131
- const column = meta.columns.find((c) => c.name === name);
3132
- return column && !isCellColumn(meta, name) ? column : void 0;
3341
+ const column = meta.columns.find((c) => c.name === name2);
3342
+ return column && !isCellColumn(meta, name2) ? column : void 0;
3133
3343
  }
3134
- link(table, name) {
3135
- return this.meta(table).links.find((l) => l.name === name);
3344
+ link(table, name2) {
3345
+ return this.meta(table).links.find((l) => l.name === name2);
3136
3346
  }
3137
3347
  fieldCandidates(table) {
3138
3348
  const meta = this.meta(table);
@@ -3144,12 +3354,12 @@ var OperationLowerer = class {
3144
3354
  ];
3145
3355
  }
3146
3356
  /** Validate that a whole-variable argument is the declared §4f slot for this table. */
3147
- slot(name, input, table) {
3148
- const variable = this.variables.get(name);
3357
+ slot(name2, input, table) {
3358
+ const variable = this.variables.get(name2);
3149
3359
  if (!variable || variable.type.kind !== "inputRef" || variable.type.input !== input || variable.type.table !== table) {
3150
- fail2(`variable $${name} must be declared as the ${input} input type for table '${table}'`, "slot-mismatch");
3360
+ fail2(`variable $${name2} must be declared as the ${input} input type for table '${table}'`, "slot-mismatch");
3151
3361
  }
3152
- return name;
3362
+ return name2;
3153
3363
  }
3154
3364
  // -------------------------------------------------------------------------
3155
3365
  // Values
@@ -3325,13 +3535,13 @@ var OperationLowerer = class {
3325
3535
  });
3326
3536
  }
3327
3537
  lowerLimit(node) {
3328
- const base = { default: DEFAULT_LIMIT, max: MAX_LIMIT };
3329
- if (!node) return base;
3330
- if (node.kind === Kind.VARIABLE) return { ...base, variable: node.name.value };
3538
+ const base2 = { default: DEFAULT_LIMIT, max: MAX_LIMIT };
3539
+ if (!node) return base2;
3540
+ if (node.kind === Kind.VARIABLE) return { ...base2, variable: node.name.value };
3331
3541
  if (node.kind === Kind.INT) {
3332
3542
  const value = Number(node.value);
3333
3543
  if (value < 1) fail2("limit must be a positive integer", "invalid-limit");
3334
- return { ...base, value: Math.min(value, MAX_LIMIT) };
3544
+ return { ...base2, value: Math.min(value, MAX_LIMIT) };
3335
3545
  }
3336
3546
  fail2("limit must be an integer literal or a variable", "invalid-limit");
3337
3547
  }
@@ -3352,28 +3562,28 @@ var OperationLowerer = class {
3352
3562
  if (node.directives && node.directives.length > 0) {
3353
3563
  fail2(`@include/@skip are supported on root fields only ('${node.name.value}' is nested)`, "nested-directive");
3354
3564
  }
3355
- const name = node.name.value;
3356
- if (name === "__typename") continue;
3357
- const alias = node.alias?.value ?? name;
3358
- const column = this.column(table, name);
3565
+ const name2 = node.name.value;
3566
+ if (name2 === "__typename") continue;
3567
+ const alias = node.alias?.value ?? name2;
3568
+ const column = this.column(table, name2);
3359
3569
  if (column) {
3360
3570
  if (node.arguments !== void 0 && node.arguments.length > 0 || node.selectionSet !== void 0) {
3361
- fail2(`column '${table}.${name}' takes no arguments or sub-selection`, "invalid-selection");
3571
+ fail2(`column '${table}.${name2}' takes no arguments or sub-selection`, "invalid-selection");
3362
3572
  }
3363
- selection.columns.push({ alias, column: name });
3573
+ selection.columns.push({ alias, column: name2 });
3364
3574
  continue;
3365
3575
  }
3366
- if (name === CELLS_FIELD && meta.cells) {
3576
+ if (name2 === CELLS_FIELD && meta.cells) {
3367
3577
  if (selection.cells) fail2(`'${CELLS_FIELD}' may be selected once per object`, "invalid-selection");
3368
3578
  selection.cells = this.lowerCells(node, table, alias);
3369
3579
  continue;
3370
3580
  }
3371
- const link = this.link(table, name);
3581
+ const link = this.link(table, name2);
3372
3582
  if (link) {
3373
- if (!node.selectionSet) fail2(`link '${table}.${name}' needs a selection set`, "invalid-selection");
3583
+ if (!node.selectionSet) fail2(`link '${table}.${name2}' needs a selection set`, "invalid-selection");
3374
3584
  const relation = {
3375
3585
  alias,
3376
- link: name,
3586
+ link: name2,
3377
3587
  selection: this.lowerSelection(node.selectionSet, link.target)
3378
3588
  };
3379
3589
  if (link.kind === "toMany") {
@@ -3389,15 +3599,15 @@ var OperationLowerer = class {
3389
3599
  if (offset) relation.offset = offset;
3390
3600
  }
3391
3601
  } else if (node.arguments && node.arguments.length > 0) {
3392
- fail2(`to-one link '${table}.${name}' takes no arguments`, "invalid-selection");
3602
+ fail2(`to-one link '${table}.${name2}' takes no arguments`, "invalid-selection");
3393
3603
  }
3394
3604
  selection.relations.push(relation);
3395
3605
  continue;
3396
3606
  }
3397
- if (name.endsWith("Aggregate")) {
3398
- const aggLink = this.link(table, name.slice(0, -"Aggregate".length));
3607
+ if (name2.endsWith("Aggregate")) {
3608
+ const aggLink = this.link(table, name2.slice(0, -"Aggregate".length));
3399
3609
  if (aggLink && aggLink.kind === "toMany") {
3400
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
3610
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
3401
3611
  assertKnownArgs(node, ["where"]);
3402
3612
  const whereArg = argsByName(node).get("where");
3403
3613
  const aggregate = this.lowerAggregateSelection(node.selectionSet, aggLink.target);
@@ -3410,7 +3620,7 @@ var OperationLowerer = class {
3410
3620
  continue;
3411
3621
  }
3412
3622
  }
3413
- fail2(`unknown field '${name}' on '${table}'`, "unknown-field", this.fieldCandidates(table));
3623
+ fail2(`unknown field '${name2}' on '${table}'`, "unknown-field", this.fieldCandidates(table));
3414
3624
  }
3415
3625
  return selection;
3416
3626
  }
@@ -3466,13 +3676,13 @@ var OperationLowerer = class {
3466
3676
  const result = {};
3467
3677
  let sawAggregate = false;
3468
3678
  for (const node of aggregateBlockFields(selectionSet)) {
3469
- const name = node.name.value;
3470
- if (name === "nodes") {
3679
+ const name2 = node.name.value;
3680
+ if (name2 === "nodes") {
3471
3681
  if (!node.selectionSet) fail2("'nodes' needs a selection set", "invalid-aggregate");
3472
3682
  result.nodes = this.lowerSelection(node.selectionSet, table);
3473
3683
  continue;
3474
3684
  }
3475
- if (name !== "aggregate") fail2(`unknown aggregate field '${name}'`, "invalid-aggregate", ["aggregate", "nodes"]);
3685
+ if (name2 !== "aggregate") fail2(`unknown aggregate field '${name2}'`, "invalid-aggregate", ["aggregate", "nodes"]);
3476
3686
  if (sawAggregate) fail2("one aggregate block per field \u2014 alias the root instead", "invalid-aggregate");
3477
3687
  sawAggregate = true;
3478
3688
  if (!node.selectionSet) fail2("'aggregate' needs a selection set", "invalid-aggregate");
@@ -3493,18 +3703,18 @@ var OperationLowerer = class {
3493
3703
  let sawAggregate = false;
3494
3704
  let sawKey = false;
3495
3705
  for (const node of aggregateBlockFields(selectionSet)) {
3496
- const name = node.name.value;
3497
- if (name === "key") {
3706
+ const name2 = node.name.value;
3707
+ if (name2 === "key") {
3498
3708
  if (sawKey) fail2("one key block per grouped aggregate", "invalid-aggregate");
3499
3709
  sawKey = true;
3500
3710
  if (!node.selectionSet) fail2("'key' needs a selection set", "invalid-aggregate");
3501
3711
  result.key = this.lowerGroupKeySelection(node.selectionSet, table, groupColumns);
3502
3712
  continue;
3503
3713
  }
3504
- if (name === "nodes") {
3714
+ if (name2 === "nodes") {
3505
3715
  fail2("'nodes' is not available on a grouped aggregate \u2014 query the rows separately", "invalid-aggregate", ["key", "aggregate"]);
3506
3716
  }
3507
- if (name !== "aggregate") fail2(`unknown grouped-aggregate field '${name}'`, "invalid-aggregate", ["key", "aggregate"]);
3717
+ if (name2 !== "aggregate") fail2(`unknown grouped-aggregate field '${name2}'`, "invalid-aggregate", ["key", "aggregate"]);
3508
3718
  if (sawAggregate) fail2("one aggregate block per field \u2014 alias the root instead", "invalid-aggregate");
3509
3719
  sawAggregate = true;
3510
3720
  if (!node.selectionSet) fail2("'aggregate' needs a selection set", "invalid-aggregate");
@@ -3530,19 +3740,19 @@ var OperationLowerer = class {
3530
3740
  const key = [];
3531
3741
  for (const node of selectionSet.selections) {
3532
3742
  if (node.kind !== Kind.FIELD) fail2("fragments must be flattened before lowering", "internal");
3533
- const name = node.name.value;
3534
- if (name === "__typename") continue;
3535
- if (!this.column(table, name)) {
3536
- fail2(`unknown key column '${name}' on '${table}'`, "unknown-column", [...groupColumns]);
3743
+ const name2 = node.name.value;
3744
+ if (name2 === "__typename") continue;
3745
+ if (!this.column(table, name2)) {
3746
+ fail2(`unknown key column '${name2}' on '${table}'`, "unknown-column", [...groupColumns]);
3537
3747
  }
3538
- if (!groupColumns.has(name)) {
3748
+ if (!groupColumns.has(name2)) {
3539
3749
  fail2(
3540
- `key column '${name}' is not one of this query's groupBy keys \u2014 it has no single value within a group`,
3750
+ `key column '${name2}' is not one of this query's groupBy keys \u2014 it has no single value within a group`,
3541
3751
  "invalid-group-key",
3542
3752
  [...groupColumns]
3543
3753
  );
3544
3754
  }
3545
- key.push({ alias: node.alias?.value ?? name, column: name });
3755
+ key.push({ alias: node.alias?.value ?? name2, column: name2 });
3546
3756
  }
3547
3757
  return key;
3548
3758
  }
@@ -3638,35 +3848,35 @@ var OperationLowerer = class {
3638
3848
  let includeIf;
3639
3849
  let omit = false;
3640
3850
  for (const directive of node.directives ?? []) {
3641
- const name = directive.name.value;
3642
- if (name === "assertAffectedRows") continue;
3643
- if (SOURCE_DIRECTIVES.has(name)) continue;
3644
- if (name !== "include" && name !== "skip") {
3645
- fail2(`directive @${name} is not supported on fields`, "unknown-directive", ["include", "skip"]);
3851
+ const name2 = directive.name.value;
3852
+ if (name2 === "assertAffectedRows") continue;
3853
+ if (SOURCE_DIRECTIVES.has(name2)) continue;
3854
+ if (name2 !== "include" && name2 !== "skip") {
3855
+ fail2(`directive @${name2} is not supported on fields`, "unknown-directive", ["include", "skip"]);
3646
3856
  }
3647
3857
  const ifArg = directive.arguments?.find((a) => a.name.value === "if");
3648
- if (!ifArg) fail2(`@${name} needs an if: argument`, "invalid-directive");
3858
+ if (!ifArg) fail2(`@${name2} needs an if: argument`, "invalid-directive");
3649
3859
  if (ifArg.value.kind === Kind.BOOLEAN) {
3650
- const keep = name === "include" ? ifArg.value.value : !ifArg.value.value;
3860
+ const keep = name2 === "include" ? ifArg.value.value : !ifArg.value.value;
3651
3861
  if (!keep) omit = true;
3652
3862
  continue;
3653
3863
  }
3654
- if (ifArg.value.kind !== Kind.VARIABLE) fail2(`@${name}(if:) must be a boolean literal or variable`, "invalid-directive");
3864
+ if (ifArg.value.kind !== Kind.VARIABLE) fail2(`@${name2}(if:) must be a boolean literal or variable`, "invalid-directive");
3655
3865
  if (includeIf) fail2("at most one of @include/@skip per root field", "invalid-directive");
3656
- includeIf = { variable: ifArg.value.name.value, negated: name === "skip" };
3866
+ includeIf = { variable: ifArg.value.name.value, negated: name2 === "skip" };
3657
3867
  }
3658
3868
  return omit ? { omit: true } : { includeIf, omit: false };
3659
3869
  }
3660
3870
  lowerQueryRoot(node) {
3661
- const name = node.name.value;
3662
- const alias = node.alias?.value ?? name;
3663
- const rootInfo = this.ctx.queryRoots.get(name);
3664
- if (!rootInfo) fail2(`unknown query root '${name}'`, "unknown-root", [...this.ctx.queryRoots.keys()]);
3871
+ const name2 = node.name.value;
3872
+ const alias = node.alias?.value ?? name2;
3873
+ const rootInfo = this.ctx.queryRoots.get(name2);
3874
+ if (!rootInfo) fail2(`unknown query root '${name2}'`, "unknown-root", [...this.ctx.queryRoots.keys()]);
3665
3875
  const { table } = rootInfo;
3666
3876
  const args = argsByName(node);
3667
3877
  if (rootInfo.kind === "select") {
3668
3878
  assertKnownArgs(node, ["where", "orderBy", "distinctOn", "limit", "offset"]);
3669
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
3879
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
3670
3880
  const where = args.get("where");
3671
3881
  const orderBy = args.get("orderBy");
3672
3882
  const distinctOn = args.get("distinctOn");
@@ -3685,7 +3895,7 @@ var OperationLowerer = class {
3685
3895
  };
3686
3896
  }
3687
3897
  if (rootInfo.kind === "byPk") {
3688
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
3898
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
3689
3899
  const pk = this.lowerPkArgs(node, table);
3690
3900
  return { kind: "byPk", alias, table, selection: this.lowerSelection(node.selectionSet, table), pk, where: CONST_TRUE };
3691
3901
  }
@@ -3693,7 +3903,7 @@ var OperationLowerer = class {
3693
3903
  return this.lowerAggregateGroupsRoot(node, { alias, table });
3694
3904
  }
3695
3905
  assertKnownArgs(node, ["where"]);
3696
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
3906
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
3697
3907
  const whereArg = args.get("where");
3698
3908
  const aggregate = this.lowerAggregateSelection(node.selectionSet, table);
3699
3909
  return {
@@ -3796,13 +4006,13 @@ var OperationLowerer = class {
3796
4006
  }
3797
4007
  /** Like lowerLimit, but bounded by MAX_GROUPS and defaulting to it. */
3798
4008
  lowerGroupLimit(node) {
3799
- const base = { default: MAX_GROUPS, max: MAX_GROUPS };
3800
- if (!node) return base;
3801
- if (node.kind === Kind.VARIABLE) return { ...base, variable: node.name.value };
4009
+ const base2 = { default: MAX_GROUPS, max: MAX_GROUPS };
4010
+ if (!node) return base2;
4011
+ if (node.kind === Kind.VARIABLE) return { ...base2, variable: node.name.value };
3802
4012
  if (node.kind === Kind.INT) {
3803
4013
  const value = Number(node.value);
3804
4014
  if (value < 1) fail2("limit must be a positive integer", "invalid-limit");
3805
- return { ...base, value: Math.min(value, MAX_GROUPS) };
4015
+ return { ...base2, value: Math.min(value, MAX_GROUPS) };
3806
4016
  }
3807
4017
  fail2("limit must be an integer literal or a variable", "invalid-limit");
3808
4018
  }
@@ -3821,10 +4031,10 @@ var OperationLowerer = class {
3821
4031
  // Mutations
3822
4032
  // -------------------------------------------------------------------------
3823
4033
  lowerMutationRoot(node) {
3824
- const name = node.name.value;
3825
- const alias = node.alias?.value ?? name;
3826
- const rootInfo = this.ctx.mutationRoots.get(name);
3827
- if (!rootInfo) fail2(`unknown mutation root '${name}'`, "unknown-root", [...this.ctx.mutationRoots.keys()]);
4034
+ const name2 = node.name.value;
4035
+ const alias = node.alias?.value ?? name2;
4036
+ const rootInfo = this.ctx.mutationRoots.get(name2);
4037
+ if (!rootInfo) fail2(`unknown mutation root '${name2}'`, "unknown-root", [...this.ctx.mutationRoots.keys()]);
3828
4038
  if (rootInfo.op === "cowOverride" || rootInfo.op === "cowRevert") {
3829
4039
  return this.lowerCowRoot(node, rootInfo);
3830
4040
  }
@@ -3833,16 +4043,16 @@ var OperationLowerer = class {
3833
4043
  }
3834
4044
  const { op, table } = rootInfo;
3835
4045
  const args = argsByName(node);
3836
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
3837
- const base = { kind: "mutation", op, alias, table, pinnedColumns: [], affectedRows: false };
4046
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
4047
+ const base2 = { kind: "mutation", op, alias, table, pinnedColumns: [], affectedRows: false };
3838
4048
  switch (op) {
3839
4049
  case "insert": {
3840
4050
  assertKnownArgs(node, ["objects", "onConflict"]);
3841
4051
  const objects = args.get("objects");
3842
- if (!objects) fail2(`'${name}' needs objects:`, "missing-argument");
4052
+ if (!objects) fail2(`'${name2}' needs objects:`, "missing-argument");
3843
4053
  const response = this.lowerMutationResponse(node.selectionSet, table);
3844
4054
  return {
3845
- ...base,
4055
+ ...base2,
3846
4056
  ...response,
3847
4057
  objects: this.lowerInsertObjects(objects.value, table, { list: true }),
3848
4058
  ...this.lowerOnConflictArg(args.get("onConflict"), table)
@@ -3851,9 +4061,9 @@ var OperationLowerer = class {
3851
4061
  case "insertOne": {
3852
4062
  assertKnownArgs(node, ["object", "onConflict"]);
3853
4063
  const object = args.get("object");
3854
- if (!object) fail2(`'${name}' needs object:`, "missing-argument");
4064
+ if (!object) fail2(`'${name2}' needs object:`, "missing-argument");
3855
4065
  return {
3856
- ...base,
4066
+ ...base2,
3857
4067
  objects: this.lowerInsertObjects(object.value, table, { list: false }),
3858
4068
  ...this.lowerOnConflictArg(args.get("onConflict"), table),
3859
4069
  returning: this.lowerSelection(node.selectionSet, table)
@@ -3862,9 +4072,9 @@ var OperationLowerer = class {
3862
4072
  case "update": {
3863
4073
  assertKnownArgs(node, ["where", "_set", "_append", "_inc"]);
3864
4074
  const where = args.get("where");
3865
- if (!where) fail2(`'${name}' needs where: (bulk updates without a filter are not allowed)`, "missing-argument");
4075
+ if (!where) fail2(`'${name2}' needs where: (bulk updates without a filter are not allowed)`, "missing-argument");
3866
4076
  return {
3867
- ...base,
4077
+ ...base2,
3868
4078
  ...this.lowerMutationResponse(node.selectionSet, table),
3869
4079
  where: this.lowerBoolExp(where.value, table),
3870
4080
  ...this.lowerWriteValues(args, table)
@@ -3873,9 +4083,9 @@ var OperationLowerer = class {
3873
4083
  case "updateByPk": {
3874
4084
  assertKnownArgs(node, ["pkColumns", "_set", "_append", "_inc"]);
3875
4085
  const pkColumns = args.get("pkColumns");
3876
- if (!pkColumns) fail2(`'${name}' needs pkColumns:`, "missing-argument");
4086
+ if (!pkColumns) fail2(`'${name2}' needs pkColumns:`, "missing-argument");
3877
4087
  return {
3878
- ...base,
4088
+ ...base2,
3879
4089
  pk: this.lowerPkColumnsArg(pkColumns.value, table),
3880
4090
  ...this.lowerWriteValues(args, table),
3881
4091
  returning: this.lowerSelection(node.selectionSet, table)
@@ -3884,16 +4094,16 @@ var OperationLowerer = class {
3884
4094
  case "delete": {
3885
4095
  assertKnownArgs(node, ["where"]);
3886
4096
  const where = args.get("where");
3887
- if (!where) fail2(`'${name}' needs where: (bulk deletes without a filter are not allowed)`, "missing-argument");
4097
+ if (!where) fail2(`'${name2}' needs where: (bulk deletes without a filter are not allowed)`, "missing-argument");
3888
4098
  return {
3889
- ...base,
4099
+ ...base2,
3890
4100
  ...this.lowerMutationResponse(node.selectionSet, table),
3891
4101
  where: this.lowerBoolExp(where.value, table)
3892
4102
  };
3893
4103
  }
3894
4104
  case "deleteByPk": {
3895
4105
  return {
3896
- ...base,
4106
+ ...base2,
3897
4107
  pk: this.lowerPkArgs(node, table),
3898
4108
  returning: this.lowerSelection(node.selectionSet, table)
3899
4109
  };
@@ -3908,16 +4118,16 @@ var OperationLowerer = class {
3908
4118
  * the structured fields, never from authored statements.
3909
4119
  */
3910
4120
  lowerCowRoot(node, rootInfo) {
3911
- const name = node.name.value;
3912
- const alias = node.alias?.value ?? name;
4121
+ const name2 = node.name.value;
4122
+ const alias = node.alias?.value ?? name2;
3913
4123
  const { table } = rootInfo;
3914
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
4124
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
3915
4125
  const meta = this.meta(table);
3916
4126
  const args = argsByName(node);
3917
4127
  const pkArg = args.get(rootInfo.pk);
3918
- if (!pkArg) fail2(`'${name}' is missing primary-key argument '${rootInfo.pk}'`, "missing-pk-argument", [rootInfo.pk]);
4128
+ if (!pkArg) fail2(`'${name2}' is missing primary-key argument '${rootInfo.pk}'`, "missing-pk-argument", [rootInfo.pk]);
3919
4129
  const pk = [{ column: rootInfo.pk, value: this.lowerValue(pkArg.value) }];
3920
- const base = {
4130
+ const base2 = {
3921
4131
  kind: "mutation",
3922
4132
  op: rootInfo.op,
3923
4133
  alias,
@@ -3930,14 +4140,14 @@ var OperationLowerer = class {
3930
4140
  };
3931
4141
  if (rootInfo.op === "cowRevert") {
3932
4142
  assertKnownArgs(node, [rootInfo.pk]);
3933
- return base;
4143
+ return base2;
3934
4144
  }
3935
4145
  assertKnownArgs(node, [rootInfo.pk, ...rootInfo.set]);
3936
4146
  const entries = rootInfo.set.map((column) => {
3937
4147
  const arg = args.get(column);
3938
4148
  if (!arg) {
3939
4149
  fail2(
3940
- `'${name}' is missing set argument '${column}' \u2014 a cow override writes every declared set column (pass null to clear a nullable one)`,
4150
+ `'${name2}' is missing set argument '${column}' \u2014 a cow override writes every declared set column (pass null to clear a nullable one)`,
3941
4151
  "missing-argument",
3942
4152
  [...rootInfo.set]
3943
4153
  );
@@ -3947,7 +4157,7 @@ var OperationLowerer = class {
3947
4157
  }
3948
4158
  return { column, value: this.lowerValue(arg.value) };
3949
4159
  });
3950
- return { ...base, set: { entries }, cowCopyColumns: [...rootInfo.copyColumns] };
4160
+ return { ...base2, set: { entries }, cowCopyColumns: [...rootInfo.copyColumns] };
3951
4161
  }
3952
4162
  /**
3953
4163
  * Resolved-table roots: insertOne(object), updateByPk(pkColumns, _set),
@@ -3955,12 +4165,12 @@ var OperationLowerer = class {
3955
4165
  * the touched cells into the manual source; `returning` reads the view.
3956
4166
  */
3957
4167
  lowerResolvedRoot(node, rootInfo) {
3958
- const name = node.name.value;
3959
- const alias = node.alias?.value ?? name;
4168
+ const name2 = node.name.value;
4169
+ const alias = node.alias?.value ?? name2;
3960
4170
  const { table, action } = rootInfo;
3961
- if (!node.selectionSet) fail2(`'${name}' needs a selection set`, "invalid-selection");
4171
+ if (!node.selectionSet) fail2(`'${name2}' needs a selection set`, "invalid-selection");
3962
4172
  const args = argsByName(node);
3963
- const base = {
4173
+ const base2 = {
3964
4174
  kind: "mutation",
3965
4175
  op: "resolvedWrite",
3966
4176
  resolvedAction: action,
@@ -3974,32 +4184,32 @@ var OperationLowerer = class {
3974
4184
  case "insert": {
3975
4185
  assertKnownArgs(node, ["object"]);
3976
4186
  const object = args.get("object");
3977
- if (!object) fail2(`'${name}' needs object:`, "missing-argument");
3978
- return { ...base, objects: this.lowerInsertObjects(object.value, table, { list: false }) };
4187
+ if (!object) fail2(`'${name2}' needs object:`, "missing-argument");
4188
+ return { ...base2, objects: this.lowerInsertObjects(object.value, table, { list: false }) };
3979
4189
  }
3980
4190
  case "update": {
3981
4191
  assertKnownArgs(node, ["pkColumns", "_set"]);
3982
4192
  const pkColumns = args.get("pkColumns");
3983
- if (!pkColumns || !args.get("_set")) fail2(`'${name}' needs pkColumns: and _set:`, "missing-argument");
3984
- return { ...base, pk: this.lowerPkColumnsArg(pkColumns.value, table), ...this.lowerWriteValues(args, table) };
4193
+ if (!pkColumns || !args.get("_set")) fail2(`'${name2}' needs pkColumns: and _set:`, "missing-argument");
4194
+ return { ...base2, pk: this.lowerPkColumnsArg(pkColumns.value, table), ...this.lowerWriteValues(args, table) };
3985
4195
  }
3986
4196
  case "delete":
3987
- return { ...base, pk: this.lowerPkArgs(node, table) };
4197
+ return { ...base2, pk: this.lowerPkArgs(node, table) };
3988
4198
  case "revert": {
3989
4199
  assertKnownArgs(node, ["pkColumns", "columns"]);
3990
4200
  const pkColumns = args.get("pkColumns");
3991
4201
  const columns = args.get("columns");
3992
- if (!pkColumns || !columns) fail2(`'${name}' needs pkColumns: and columns:`, "missing-argument");
4202
+ if (!pkColumns || !columns) fail2(`'${name2}' needs pkColumns: and columns:`, "missing-argument");
3993
4203
  const items = columns.value.kind === Kind.LIST ? columns.value.values : [columns.value];
3994
4204
  const meta = this.meta(table);
3995
4205
  const revertColumns = items.map((item) => {
3996
4206
  if (item.kind !== Kind.ENUM || !meta.settable.includes(item.value)) {
3997
- fail2(`'${name}' columns: must list update-column enum values written in the document`, "invalid-revert-columns", meta.settable);
4207
+ fail2(`'${name2}' columns: must list update-column enum values written in the document`, "invalid-revert-columns", meta.settable);
3998
4208
  }
3999
4209
  return item.value;
4000
4210
  });
4001
- if (revertColumns.length === 0) fail2(`'${name}' columns: must name at least one column`, "invalid-revert-columns");
4002
- return { ...base, pk: this.lowerPkColumnsArg(pkColumns.value, table), revertColumns: [...new Set(revertColumns)] };
4211
+ if (revertColumns.length === 0) fail2(`'${name2}' columns: must name at least one column`, "invalid-revert-columns");
4212
+ return { ...base2, pk: this.lowerPkColumnsArg(pkColumns.value, table), revertColumns: [...new Set(revertColumns)] };
4003
4213
  }
4004
4214
  }
4005
4215
  }
@@ -4008,24 +4218,24 @@ var OperationLowerer = class {
4008
4218
  let returning = null;
4009
4219
  for (const node of selectionSet.selections) {
4010
4220
  if (node.kind !== Kind.FIELD) fail2("fragments must be flattened before lowering", "internal");
4011
- const name = node.name.value;
4012
- if (name === "__typename") continue;
4013
- if (name === "affectedRows") {
4221
+ const name2 = node.name.value;
4222
+ if (name2 === "__typename") continue;
4223
+ if (name2 === "affectedRows") {
4014
4224
  affectedRows = true;
4015
4225
  continue;
4016
4226
  }
4017
- if (name === "returning") {
4227
+ if (name2 === "returning") {
4018
4228
  if (!node.selectionSet) fail2("'returning' needs a selection set", "invalid-selection");
4019
4229
  returning = this.lowerSelection(node.selectionSet, table);
4020
4230
  continue;
4021
4231
  }
4022
- fail2(`unknown mutation-response field '${name}'`, "invalid-selection", ["affectedRows", "returning"]);
4232
+ fail2(`unknown mutation-response field '${name2}'`, "invalid-selection", ["affectedRows", "returning"]);
4023
4233
  }
4024
4234
  return { affectedRows, returning };
4025
4235
  }
4026
- lowerInsertObjects(node, table, { list }) {
4236
+ lowerInsertObjects(node, table, { list: list2 }) {
4027
4237
  if (node.kind === Kind.VARIABLE) return { slot: this.slot(node.name.value, "insertInput", table) };
4028
- if (list) {
4238
+ if (list2) {
4029
4239
  if (node.kind !== Kind.LIST) fail2(`objects: must be a list literal or a variable`, "invalid-insert");
4030
4240
  return { rows: node.values.map((item) => this.lowerInsertRow(item, table)) };
4031
4241
  }
@@ -4132,9 +4342,9 @@ function lowerOperation({ restricted, ctx }) {
4132
4342
  const lowerer = new OperationLowerer(ctx, restricted);
4133
4343
  return lowerer.lowerRoots(restricted.definition, restricted.kind);
4134
4344
  }
4135
- function isCellColumn(meta, name) {
4345
+ function isCellColumn(meta, name2) {
4136
4346
  if (!meta.cells) return false;
4137
- return Object.values(meta.cells).some((parts) => Object.values(parts).includes(name));
4347
+ return Object.values(meta.cells).some((parts) => Object.values(parts).includes(name2));
4138
4348
  }
4139
4349
  function fieldNodes(selectionSet) {
4140
4350
  const out = [];
@@ -4191,7 +4401,7 @@ function parseDocuments({
4191
4401
  }
4192
4402
  function unwrapType(node, variable) {
4193
4403
  let nullable = true;
4194
- let list = false;
4404
+ let list2 = false;
4195
4405
  let current = node;
4196
4406
  for (; ; ) {
4197
4407
  if (current.kind === Kind.NON_NULL_TYPE) {
@@ -4200,17 +4410,17 @@ function unwrapType(node, variable) {
4200
4410
  continue;
4201
4411
  }
4202
4412
  if (current.kind === Kind.LIST_TYPE) {
4203
- if (list) {
4413
+ if (list2) {
4204
4414
  throw new DocumentCompileError(`variable $${variable}: nested list types are not supported`, "unsupported-variable-type");
4205
4415
  }
4206
- list = true;
4416
+ list2 = true;
4207
4417
  current = current.type;
4208
4418
  continue;
4209
4419
  }
4210
- return { named: current.name.value, list, nullable };
4420
+ return { named: current.name.value, list: list2, nullable };
4211
4421
  }
4212
4422
  }
4213
- function classifyVariableType({ named, list }, variable, ctx) {
4423
+ function classifyVariableType({ named, list: list2 }, variable, ctx) {
4214
4424
  const inputRef = classifyInputType(named, ctx.tableByTypename);
4215
4425
  if (inputRef) {
4216
4426
  const listable = {
@@ -4223,13 +4433,13 @@ function classifyVariableType({ named, list }, variable, ctx) {
4223
4433
  insertInput: "either"
4224
4434
  };
4225
4435
  const rule = listable[inputRef.input];
4226
- if (rule === "must" && !list) {
4436
+ if (rule === "must" && !list2) {
4227
4437
  throw new DocumentCompileError(
4228
4438
  `variable $${variable}: ${named} must be used as a list (e.g. [${named}!])`,
4229
4439
  "unsupported-variable-type"
4230
4440
  );
4231
4441
  }
4232
- if (rule === "never" && list) {
4442
+ if (rule === "never" && list2) {
4233
4443
  throw new DocumentCompileError(
4234
4444
  `variable $${variable}: ${named} cannot be used as a list`,
4235
4445
  "unsupported-variable-type"
@@ -4266,7 +4476,7 @@ function restrictDefault({
4266
4476
  node,
4267
4477
  variable,
4268
4478
  type,
4269
- list,
4479
+ list: list2,
4270
4480
  nullable
4271
4481
  }) {
4272
4482
  if (type.kind === "inputRef") {
@@ -4286,7 +4496,7 @@ function restrictDefault({
4286
4496
  return { value: null };
4287
4497
  }
4288
4498
  if (Array.isArray(value)) {
4289
- if (!list) {
4499
+ if (!list2) {
4290
4500
  throw new DocumentCompileError(
4291
4501
  `variable $${variable}: default for a non-list variable must not be a list`,
4292
4502
  "unsupported-variable-default"
@@ -4294,7 +4504,7 @@ function restrictDefault({
4294
4504
  }
4295
4505
  return { value: value.map((item) => restrictDefaultLiteral(item, variable, type)) };
4296
4506
  }
4297
- if (list) {
4507
+ if (list2) {
4298
4508
  throw new DocumentCompileError(
4299
4509
  `variable $${variable}: default for a list variable must be a list`,
4300
4510
  "unsupported-variable-default"
@@ -4329,8 +4539,8 @@ function restrictOperation({
4329
4539
  if (operation.operation === "subscription") {
4330
4540
  throw new DocumentCompileError("subscriptions are not supported in v2.0 \u2014 poll a query instead", "subscription-unsupported");
4331
4541
  }
4332
- const name = operation.name?.value;
4333
- if (!name) {
4542
+ const name2 = operation.name?.value;
4543
+ if (!name2) {
4334
4544
  throw new DocumentCompileError(
4335
4545
  "anonymous operations are not supported \u2014 every operation must be named (the name keys the registry)",
4336
4546
  "anonymous-operation"
@@ -4339,14 +4549,14 @@ function restrictOperation({
4339
4549
  const variables = (operation.variableDefinitions ?? []).map((definition) => {
4340
4550
  const variable = definition.variable.name.value;
4341
4551
  const unwrapped = unwrapType(definition.type, variable);
4342
- const { list, nullable } = unwrapped;
4552
+ const { list: list2, nullable } = unwrapped;
4343
4553
  const type = classifyVariableType(unwrapped, variable, ctx);
4344
- const ir = { name: variable, type, nullable, list };
4554
+ const ir = { name: variable, type, nullable, list: list2 };
4345
4555
  if (definition.defaultValue === void 0) return ir;
4346
- return { ...ir, defaultValue: restrictDefault({ node: definition.defaultValue, variable, type, list, nullable }) };
4556
+ return { ...ir, defaultValue: restrictDefault({ node: definition.defaultValue, variable, type, list: list2, nullable }) };
4347
4557
  });
4348
4558
  return {
4349
- name,
4559
+ name: name2,
4350
4560
  kind: operation.operation === "mutation" ? "mutation" : "query",
4351
4561
  requires: parseRequires(operation),
4352
4562
  variables,
@@ -4384,8 +4594,20 @@ function compileNamespace({ canonical, sdl, documents }) {
4384
4594
  }
4385
4595
  const loweredRoots = lowerOperation({ restricted: { ...restricted, definition: flattened }, ctx });
4386
4596
  const { roots, tableSet } = injectPolicies({ roots: loweredRoots, ctx });
4597
+ for (const table of tableSet) {
4598
+ const meta = ctx.tables[table];
4599
+ if (meta.namespace && meta.namespace !== ctx.namespace && !meta.exportRead) {
4600
+ throw new DocumentCompileError(`object '${meta.namespace}/${table}' is not exported for reads`, "unexported-object");
4601
+ }
4602
+ }
4603
+ for (const root of roots) {
4604
+ const meta = ctx.tables[root.table];
4605
+ if (root.kind === "mutation" && meta.namespace && meta.namespace !== ctx.namespace && !meta.exportWrite) {
4606
+ throw new DocumentCompileError(`object '${meta.namespace}/${root.table}' is not exported for writes`, "unexported-object");
4607
+ }
4608
+ }
4387
4609
  operations[restricted.name] = {
4388
- irVersion: roots.some((root) => root.kind === "mutation" && root.assertAffectedRows !== void 0) ? IR_VERSION : LEGACY_IR_VERSION,
4610
+ irVersion: canonical.imports?.length ? COMPOSED_IR_VERSION : roots.some((root) => root.kind === "mutation" && root.assertAffectedRows !== void 0) ? GUARDED_IR_VERSION : LEGACY_IR_VERSION,
4389
4611
  name: restricted.name,
4390
4612
  opHash: computeOpHash({ definition: flattened, schemaHash }),
4391
4613
  kind: restricted.kind,
@@ -4412,9 +4634,14 @@ function compileNamespace({ canonical, sdl, documents }) {
4412
4634
  }
4413
4635
  if (diagnostics.some((d) => d.severity === "error")) return { ok: false, diagnostics };
4414
4636
  const artifact = {
4415
- irVersion: requiredArtifactIrVersion(Object.values(operations)),
4637
+ irVersion: canonical.imports?.length ? COMPOSED_IR_VERSION : requiredArtifactIrVersion(Object.values(operations)),
4416
4638
  namespace: ctx.namespace,
4417
4639
  schemaHash,
4640
+ ownSchemaHash: computeSchemaHash(localContract(canonical)),
4641
+ ...canonical.imports?.length ? {
4642
+ dependencies: Object.fromEntries(canonical.imports.map((owner) => [owner.namespace, computeSchemaHash(localContract(owner))]))
4643
+ } : {},
4644
+ readPolicies: Object.fromEntries(ctx.readPolicies),
4418
4645
  opsHash: computeOpsHash(Object.values(operations).map((op) => op.opHash)),
4419
4646
  tables: ctx.tables,
4420
4647
  operations
@@ -4477,9 +4704,10 @@ function allowedOps(type) {
4477
4704
  case "text":
4478
4705
  return ["eq", "neq", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike", "isNull"];
4479
4706
  case "uuid":
4480
- return ["eq", "neq", "in", "nin", "isNull"];
4707
+ return ["eq", "neq", "in", "nin", "like", "ilike", "isNull"];
4481
4708
  case "integer":
4482
4709
  case "bigint":
4710
+ return ["eq", "neq", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike", "isNull"];
4483
4711
  case "numeric":
4484
4712
  case "double":
4485
4713
  case "timestamptz":
@@ -4697,12 +4925,12 @@ function validateSelectColumns({ table, value }) {
4697
4925
  function validateInsertInput({
4698
4926
  table,
4699
4927
  value,
4700
- list,
4928
+ list: list2,
4701
4929
  maxRows
4702
4930
  }) {
4703
4931
  const collector = new IssueCollector();
4704
4932
  const rowCap = maxRows ?? 1e3;
4705
- const rows = list ? value : [value];
4933
+ const rows = list2 ? value : [value];
4706
4934
  if (!Array.isArray(rows)) {
4707
4935
  collector.add("invalid-shape", "$", "objects must be an array of rows");
4708
4936
  return { ok: false, issues: collector.issues };
@@ -4713,7 +4941,7 @@ function validateInsertInput({
4713
4941
  }
4714
4942
  const normalized = [];
4715
4943
  rows.forEach((row, index) => {
4716
- const path = list ? `$[${index}]` : "$";
4944
+ const path = list2 ? `$[${index}]` : "$";
4717
4945
  if (!isPlainObject(row)) {
4718
4946
  collector.add("invalid-shape", path, "insert rows must be objects");
4719
4947
  return;
@@ -4823,6 +5051,744 @@ function validateOnConflictInput({
4823
5051
  return finish(collector, { constraint, updateColumns });
4824
5052
  }
4825
5053
 
4826
- export { COMPILER_MAJOR, CONST_TRUE, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileNamespace, compileNamespaceOrThrow, compilePolicy, compilePredicateV2, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, enumTypeName, flattenOperation, injectPolicies, inputTypeName, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
5054
+ // src/engine/types.ts
5055
+ var SQL_TYPE_NAMES = {
5056
+ double: "double precision",
5057
+ timestamptz: "timestamp with time zone"
5058
+ };
5059
+ var sqlTypeName = (type) => SQL_TYPE_NAMES[type] ?? type;
5060
+
5061
+ // src/compiler/pred-ir-sql.ts
5062
+ function compilePredIrToSql(pred, options = {}) {
5063
+ switch (pred.kind) {
5064
+ case "const":
5065
+ return pred.value ? "TRUE" : "FALSE";
5066
+ case "and":
5067
+ return `(${pred.operands.map((operand) => compilePredIrToSql(operand, options)).join(" AND ")})`;
5068
+ case "or":
5069
+ return `(${pred.operands.map((operand) => compilePredIrToSql(operand, options)).join(" OR ")})`;
5070
+ case "not":
5071
+ return `(NOT ${compilePredIrToSql(pred.operand, options)})`;
5072
+ case "cmp":
5073
+ return compileCmp(pred.op, pred.column, pred.value, options);
5074
+ case "capability":
5075
+ return `(${capabilityExpression(pred.key, pred.namespace ?? options.namespace)})`;
5076
+ case "role":
5077
+ return `(${roleExpression(pred.key, pred.namespace ?? options.namespace)})`;
5078
+ case "exists":
5079
+ if (options.exists) return options.exists(pred);
5080
+ throw new OrmDefinitionError("synced view read policy cannot use 'exists' without a relationship resolver");
5081
+ case "aggPredicate":
5082
+ case "dynamicFilter":
5083
+ throw new OrmDefinitionError(
5084
+ `synced view read policy cannot use '${pred.kind}' \u2014 use row filters and claim/capability/role checks only`
5085
+ );
5086
+ default: {
5087
+ const exhausted = pred;
5088
+ throw new OrmDefinitionError(`unsupported synced view predicate kind '${String(exhausted)}'`);
5089
+ }
5090
+ }
5091
+ }
5092
+ function physicalColumn(logical, options) {
5093
+ return options.columnNames?.[logical] ?? logical;
5094
+ }
5095
+ function compileCmp(op, column, value, options) {
5096
+ const physical = physicalColumn(column, options);
5097
+ const declared = options.columnTypes?.[physical] ?? options.columnTypes?.[column];
5098
+ const isEnum = typeof declared === "object" && declared !== null;
5099
+ const qualified = options.alias ? `${quoteIdentifier(options.alias)}.${quoteIdentifier(physical)}` : quoteIdentifier(physical);
5100
+ const col = isEnum ? `${qualified}::text` : qualified;
5101
+ const columnType = isEnum ? "text" : declared;
5102
+ switch (op) {
5103
+ case "eq":
5104
+ return compileEquality(col, value, columnType, false);
5105
+ case "neq":
5106
+ return compileEquality(col, value, columnType, true);
5107
+ case "gt":
5108
+ return `${col} > ${compileValue(value, columnType)}`;
5109
+ case "gte":
5110
+ return `${col} >= ${compileValue(value, columnType)}`;
5111
+ case "lt":
5112
+ return `${col} < ${compileValue(value, columnType)}`;
5113
+ case "lte":
5114
+ return `${col} <= ${compileValue(value, columnType)}`;
5115
+ case "in":
5116
+ return `${col} = ANY(${compileArrayValue(value, columnType)})`;
5117
+ case "nin":
5118
+ return `${col} <> ALL(${compileArrayValue(value, columnType)})`;
5119
+ case "like":
5120
+ return `${col} LIKE ${compileValue(value, columnType)}`;
5121
+ case "ilike":
5122
+ return `${col} ILIKE ${compileValue(value, columnType)}`;
5123
+ case "isNull":
5124
+ return value.kind === "literal" && value.value === true ? `${col} IS NULL` : `${col} IS NOT NULL`;
5125
+ case "contains":
5126
+ case "hasKey":
5127
+ throw new OrmDefinitionError(`synced view read policy cannot use json operator '${op}'`);
5128
+ default: {
5129
+ const exhausted = op;
5130
+ throw new OrmDefinitionError(`unsupported synced view comparison '${String(exhausted)}'`);
5131
+ }
5132
+ }
5133
+ }
5134
+ function compileEquality(col, value, columnType, negated, options) {
5135
+ if (value.kind === "literal" && value.value === null) {
5136
+ return negated ? `${col} IS NOT NULL` : `${col} IS NULL`;
5137
+ }
5138
+ const rhs = compileValue(value, columnType);
5139
+ return negated ? `${col} <> ${rhs}` : `${col} = ${rhs}`;
5140
+ }
5141
+ function compileValue(value, columnType, _options) {
5142
+ switch (value.kind) {
5143
+ case "literal": {
5144
+ const literal = value.value;
5145
+ if (literal === null) return "NULL";
5146
+ if (typeof literal === "string") return quoteLiteral(literal);
5147
+ if (typeof literal === "boolean") return literal ? "TRUE" : "FALSE";
5148
+ return String(literal);
5149
+ }
5150
+ case "claim": {
5151
+ const expression = claimExpression(value.key);
5152
+ return columnType && columnType !== "text" ? `(${expression})::${sqlTypeName(columnType)}` : expression;
5153
+ }
5154
+ case "variable":
5155
+ throw new OrmDefinitionError("synced view read policy cannot reference operation variables");
5156
+ default: {
5157
+ const exhausted = value;
5158
+ throw new OrmDefinitionError(`unsupported synced view value kind '${String(exhausted)}'`);
5159
+ }
5160
+ }
5161
+ }
5162
+ function compileArrayValue(value, columnType, _options) {
5163
+ if (value.kind !== "literal" || !Array.isArray(value.value)) {
5164
+ throw new OrmDefinitionError("synced view IN/NIN comparisons require literal arrays");
5165
+ }
5166
+ const entries = value.value.map((entry) => {
5167
+ if (entry === null) return "NULL";
5168
+ if (typeof entry === "string") return quoteLiteral(entry);
5169
+ if (typeof entry === "boolean") return entry ? "TRUE" : "FALSE";
5170
+ return String(entry);
5171
+ });
5172
+ const array = `ARRAY[${entries.join(", ")}]`;
5173
+ return columnType ? `${array}::${sqlTypeName(columnType)}[]` : array;
5174
+ }
5175
+
5176
+ // src/sql/catalog.ts
5177
+ function sqlHash(value) {
5178
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
5179
+ }
5180
+ function createSqlCatalog({ canonical, publishedObjects, executionTarget }) {
5181
+ if (!publishedObjects.length || new Set(publishedObjects).size !== publishedObjects.length) throw new Error("SQL objects must be explicitly declared and unique");
5182
+ const { ctx, diagnostics } = buildCompileContext(canonical);
5183
+ if (diagnostics.some((d) => d.severity === "error")) throw new Error(diagnostics.map((d) => d.message).join("; "));
5184
+ const identity = sqlHash(`ontology-sql-v1\0${computeSchemaHash(canonical)}\0${executionTarget}`);
5185
+ const refOf = (meta) => `${meta.namespace ?? canonical.namespace}/${meta.table}`;
5186
+ const definitions = [canonical, ...canonical.imports ?? []];
5187
+ const internal = new Set(definitions.flatMap((owner) => [
5188
+ ...owner.tables.filter((t) => t.internal).map((t) => `${owner.namespace}/${t.table}`),
5189
+ ...(owner.views ?? []).filter((v) => v.internal).map((v) => `${owner.namespace}/${v.view}`)
5190
+ ]));
5191
+ const objects = publishedObjects.map((ref) => {
5192
+ const meta = Object.values(ctx.tables).find((table) => refOf(table) === ref);
5193
+ if (!meta || internal.has(ref) || meta.namespace !== canonical.namespace && !meta.exportRead) throw new Error(`SQL object not published: ${ref}`);
5194
+ const reads = /* @__PURE__ */ new Set([ref]);
5195
+ let sequence = 0;
5196
+ const predicate = (table, pred, alias) => compilePredIrToSql(pred, {
5197
+ alias,
5198
+ namespace: table.namespace ?? canonical.namespace,
5199
+ columnNames: Object.fromEntries(table.columns.map((c) => [c.name, c.columnName])),
5200
+ columnTypes: Object.fromEntries(table.columns.map((c) => [c.name, c.type])),
5201
+ exists: (node) => {
5202
+ const link = table.links.find((candidate) => candidate.name === node.link);
5203
+ const target = link?.target ? ctx.tables[link.target] : void 0;
5204
+ if (!link || !target) throw new Error("SQL policy link is unavailable");
5205
+ reads.add(refOf(target));
5206
+ const child = `p${++sequence}`;
5207
+ const col = (t, a, name2) => {
5208
+ const c = t.columns.find((c2) => c2.name === name2 || c2.columnName === name2);
5209
+ if (!c) throw new Error("SQL policy column is unavailable");
5210
+ return `${quoteIdentifier(a)}.${quoteIdentifier(c.columnName)}`;
5211
+ };
5212
+ const join = link.kind === "toMany" ? `${col(target, child, link.on)} = ${col(table, alias, link.references)}` : `${col(table, alias, link.on)} = ${col(target, child, link.references)}`;
5213
+ return `EXISTS (SELECT 1 FROM ${quoteIdentifier(target.schema)}.${quoteIdentifier(target.table)} AS ${quoteIdentifier(child)} WHERE ${join} AND (${predicate(target, node.where, child)}))`;
5214
+ }
5215
+ });
5216
+ const columns = meta.columns.filter((c) => meta.filterable.includes(c.name)).map((c) => ({
5217
+ name: c.name,
5218
+ type: typeof c.type === "string" ? c.type : "text",
5219
+ nullable: c.nullable
5220
+ }));
5221
+ const projection = columns.map((c) => {
5222
+ const physical = meta.columns.find((m) => m.name === c.name);
5223
+ return `${quoteIdentifier("p0")}.${quoteIdentifier(physical.columnName)}${typeof physical.type === "object" ? "::text" : ""} AS ${quoteIdentifier(c.name)}`;
5224
+ }).join(", ");
5225
+ const policy = ctx.readPolicies.get(meta.table) ?? { kind: "const", value: true };
5226
+ const scan = `SELECT ${projection} FROM ${quoteIdentifier(meta.schema)}.${quoteIdentifier(meta.table)} AS ${quoteIdentifier("p0")} WHERE ${predicate(meta, policy, "p0")}`;
5227
+ return { ref, table: meta.table, columns, scan, read: [...reads].sort() };
5228
+ });
5229
+ return { identity, objects };
5230
+ }
5231
+
5232
+ // src/sql/ast.ts
5233
+ function obj(value) {
5234
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("unsupported SQL node");
5235
+ return value;
5236
+ }
5237
+ function list(value) {
5238
+ return value === void 0 ? [] : Array.isArray(value) ? value : fail3("expected SQL list");
5239
+ }
5240
+ function str(value) {
5241
+ return typeof value === "string" ? value : fail3("expected SQL name");
5242
+ }
5243
+ function fail3(message) {
5244
+ throw new Error(message);
5245
+ }
5246
+ function strings(value) {
5247
+ return list(value).map((v) => str(obj(obj(v).String).sval));
5248
+ }
5249
+ function only(node, keys) {
5250
+ for (const [key, value] of Object.entries(node)) {
5251
+ if (!keys.includes(key) && value !== void 0 && value !== null && value !== false && value !== "" && value !== 0 && !(Array.isArray(value) && value.length === 0)) {
5252
+ fail3(`unsupported SQL clause: ${key}`);
5253
+ }
5254
+ }
5255
+ }
5256
+
5257
+ // src/sql/compiler.ts
5258
+ var parser;
5259
+ var SQL_TYPES = ["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz"];
5260
+ var pgType = (type) => type === "double" ? "double precision" : SQL_TYPES.some((t) => t === type) ? type : fail3("unsupported SQL type");
5261
+ async function compileOntologySql({ catalog, sql, parameterTypes = [], maxRows = 1e3 }) {
5262
+ if (!Number.isInteger(maxRows) || maxRows < 1 || maxRows > 1e4 || Buffer.byteLength(sql) > 65536) fail3("SQL limit exceeded");
5263
+ parser ??= new PgParser({ version: 17 });
5264
+ const parsed = await parser.parse(sql);
5265
+ if (parsed.error || !parsed.tree) fail3("invalid SQL");
5266
+ const statements = list(obj(parsed.tree).stmts);
5267
+ if (statements.length !== 1) fail3("one SQL SELECT is required");
5268
+ const emitter = new SqlEmitter({ catalog, parameterTypes });
5269
+ const result = emitter.select(obj(obj(statements[0]).stmt), { relations: [], ctes: /* @__PURE__ */ new Map() });
5270
+ if (emitter.used.size !== parameterTypes.length) fail3("SQL parameters must be used exactly as declared");
5271
+ if (!emitter.read.size) fail3("SQL operation must read an ontology object");
5272
+ return {
5273
+ ...result,
5274
+ read: [...emitter.read].sort(),
5275
+ maxRows,
5276
+ queryHash: sqlHash(JSON.stringify(["ontology-sql-v1", catalog.identity, result.text, parameterTypes, maxRows]))
5277
+ };
5278
+ }
5279
+ var SqlEmitter = class {
5280
+ constructor(input) {
5281
+ this.input = input;
5282
+ }
5283
+ read = /* @__PURE__ */ new Set();
5284
+ used = /* @__PURE__ */ new Set();
5285
+ nodes = 0;
5286
+ depth = 0;
5287
+ budget() {
5288
+ if (++this.nodes > 5e3 || this.depth > 48) fail3("SQL complexity limit exceeded");
5289
+ }
5290
+ select(wrapper, parent, resolveUnknown = true) {
5291
+ this.budget();
5292
+ this.depth++;
5293
+ const node = obj(wrapper.SelectStmt);
5294
+ only(node, ["targetList", "fromClause", "whereClause", "groupClause", "havingClause", "sortClause", "limitCount", "limitOffset", "limitOption", "op", "withClause", "all", "larg", "rarg"]);
5295
+ if (!["SETOP_NONE", "SETOP_UNION"].includes(str(node.op))) fail3("unsupported SQL set operation");
5296
+ if (node.op === "SETOP_NONE" && (node.all || node.larg || node.rarg)) fail3("invalid SQL set operation");
5297
+ if (node.limitOption && !["LIMIT_OPTION_DEFAULT", "LIMIT_OPTION_COUNT"].includes(str(node.limitOption))) fail3("unsupported SQL limit");
5298
+ const scope = { relations: [], parent, ctes: new Map(parent.ctes) };
5299
+ const ctes = [];
5300
+ if (node.withClause) {
5301
+ const withNode = obj(node.withClause);
5302
+ only(withNode, ["ctes", "location"]);
5303
+ for (const entry of list(withNode.ctes)) {
5304
+ const cte = obj(obj(entry).CommonTableExpr);
5305
+ only(cte, ["ctename", "ctequery", "ctematerialized", "location"]);
5306
+ const name2 = str(cte.ctename);
5307
+ if (scope.ctes.has(name2)) fail3("duplicate CTE");
5308
+ const result2 = this.select(obj(cte.ctequery), { ...parent, ctes: scope.ctes });
5309
+ scope.ctes.set(name2, result2);
5310
+ ctes.push(`${quoteIdentifier(name2)} AS (${result2.text})`);
5311
+ }
5312
+ }
5313
+ const result = node.op === "SETOP_UNION" ? this.union(node, scope) : this.projection(node, scope);
5314
+ const { columns } = result;
5315
+ let text = `${ctes.length ? `WITH ${ctes.join(", ")} ` : ""}${result.text}`;
5316
+ if (list(node.sortClause).length) text += ` ORDER BY ${list(node.sortClause).map((n) => {
5317
+ const sort = obj(obj(n).SortBy);
5318
+ only(sort, ["node", "sortby_dir", "sortby_nulls", "location"]);
5319
+ const direction = sort.sortby_dir === "SORTBY_DESC" ? "DESC" : sort.sortby_dir === "SORTBY_ASC" || sort.sortby_dir === "SORTBY_DEFAULT" ? "ASC" : fail3("unsupported SQL sort");
5320
+ const nulls = sort.sortby_nulls === "SORTBY_NULLS_FIRST" ? " NULLS FIRST" : sort.sortby_nulls === "SORTBY_NULLS_LAST" ? " NULLS LAST" : "";
5321
+ const value = obj(sort.node);
5322
+ const reference = value.ColumnRef ? strings(obj(value.ColumnRef).fields) : [];
5323
+ const projected = reference.length === 1 ? columns.filter((c) => c.name === reference[0]) : [];
5324
+ return `${projected.length === 1 ? quoteIdentifier(projected[0].name) : this.expr(value, scope).text} ${direction}${nulls}`;
5325
+ }).join(", ")}`;
5326
+ for (const [key, keyword] of [["limitCount", "LIMIT"], ["limitOffset", "OFFSET"]]) {
5327
+ if (node[key]) text += ` ${keyword} ${this.expr(obj(node[key]), scope).text}`;
5328
+ }
5329
+ this.depth--;
5330
+ return { text, columns: resolveUnknown ? columns.map((c) => c.type === "unknown" ? { ...c, type: "text" } : c) : columns };
5331
+ }
5332
+ union(node, scope) {
5333
+ for (const key of ["targetList", "fromClause", "whereClause", "groupClause", "havingClause"]) {
5334
+ if (node[key]) fail3("unsupported UNION clause");
5335
+ }
5336
+ const left = this.select({ SelectStmt: obj(node.larg) }, scope, false);
5337
+ const right = this.select({ SelectStmt: obj(node.rarg) }, scope, false);
5338
+ if (left.columns.length !== right.columns.length) fail3("UNION branches must have the same column count");
5339
+ const numeric = ["integer", "bigint", "numeric", "double"];
5340
+ const columns = left.columns.map((column, index) => {
5341
+ const other = right.columns[index];
5342
+ const type = column.type === "unknown" && other.type === "unknown" ? "text" : column.type === "unknown" ? other.type : other.type === "unknown" ? column.type : column.type === other.type ? column.type : numeric.includes(column.type) && numeric.includes(other.type) ? numeric[Math.max(numeric.indexOf(column.type), numeric.indexOf(other.type))] : fail3("UNION columns require compatible types; use explicit casts");
5343
+ return { name: column.name, type, nullable: column.nullable || other.nullable };
5344
+ });
5345
+ return { text: `(${left.text}) UNION${node.all ? " ALL" : ""} (${right.text})`, columns };
5346
+ }
5347
+ projection(node, scope) {
5348
+ const from = list(node.fromClause).map((n) => this.from(obj(n), scope)).join(", ");
5349
+ const columns = [];
5350
+ const targets = [];
5351
+ for (const item of list(node.targetList)) {
5352
+ const target = obj(obj(item).ResTarget);
5353
+ only(target, ["name", "val", "location"]);
5354
+ const value = obj(target.val);
5355
+ const fields = value.ColumnRef ? list(obj(value.ColumnRef).fields) : [];
5356
+ if (fields.length && obj(fields[fields.length - 1]).A_Star) {
5357
+ const matches = fields.length === 1 ? scope.relations : scope.relations.filter((r) => r.alias === strings(fields.slice(0, -1))[0]);
5358
+ if (!matches.length || fields.length > 2 || target.name) fail3("invalid wildcard");
5359
+ for (const relation of matches) for (const column of relation.columns) {
5360
+ columns.push(column);
5361
+ targets.push(`${quoteIdentifier(relation.alias)}.${quoteIdentifier(column.name)}`);
5362
+ }
5363
+ } else {
5364
+ const expression = this.expr(value, scope);
5365
+ const name2 = typeof target.name === "string" && target.name ? target.name : expression.name ?? `column${columns.length + 1}`;
5366
+ columns.push({ name: name2, type: expression.type, nullable: true });
5367
+ targets.push(`${expression.text} AS ${quoteIdentifier(name2)}`);
5368
+ }
5369
+ }
5370
+ if (!targets.length || targets.length > 256) fail3("invalid SQL projection");
5371
+ let text = `SELECT ${targets.join(", ")}${from ? ` FROM ${from}` : ""}`;
5372
+ if (node.whereClause) text += ` WHERE ${this.expr(obj(node.whereClause), scope).text}`;
5373
+ if (list(node.groupClause).length) text += ` GROUP BY ${list(node.groupClause).map((n) => this.expr(obj(n), scope).text).join(", ")}`;
5374
+ if (node.havingClause) text += ` HAVING ${this.expr(obj(node.havingClause), scope).text}`;
5375
+ return { text, columns };
5376
+ }
5377
+ from(node, scope) {
5378
+ this.budget();
5379
+ this.depth++;
5380
+ try {
5381
+ return this.relation(node, scope);
5382
+ } finally {
5383
+ this.depth--;
5384
+ }
5385
+ }
5386
+ relation(node, scope) {
5387
+ if (node.RangeVar) {
5388
+ const range = obj(node.RangeVar);
5389
+ only(range, ["schemaname", "relname", "inh", "relpersistence", "alias", "location"]);
5390
+ if (range.inh !== true) fail3("ONLY is not supported for ontology objects");
5391
+ const name2 = str(range.relname);
5392
+ const alias = range.alias ? str(obj(range.alias).aliasname) : name2;
5393
+ if (range.alias) only(obj(range.alias), ["aliasname"]);
5394
+ if (scope.relations.some((r) => r.alias === alias)) fail3("duplicate SQL alias");
5395
+ const cte = !range.schemaname ? scope.ctes.get(name2) : void 0;
5396
+ if (cte) {
5397
+ scope.relations.push({ alias, columns: cte.columns });
5398
+ return `${quoteIdentifier(name2)} AS ${quoteIdentifier(alias)}`;
5399
+ }
5400
+ const object = this.input.catalog.objects.find((o) => o.ref === `${range.schemaname}/${name2}`);
5401
+ if (!object) fail3("SQL object is not declared or available");
5402
+ for (const ref of object.read) this.read.add(ref);
5403
+ scope.relations.push({ alias, columns: object.columns });
5404
+ const source = `(WITH ${quoteIdentifier("__orm_scan")} AS MATERIALIZED (${object.scan}) SELECT * FROM ${quoteIdentifier("__orm_scan")})`;
5405
+ return `${source} AS ${quoteIdentifier(alias)}`;
5406
+ }
5407
+ if (node.JoinExpr) {
5408
+ const join = obj(node.JoinExpr);
5409
+ only(join, ["jointype", "larg", "rarg", "quals"]);
5410
+ if (!["JOIN_INNER", "JOIN_LEFT", "JOIN_RIGHT", "JOIN_FULL"].includes(str(join.jointype))) fail3("unsupported SQL join");
5411
+ const start = scope.relations.length;
5412
+ const left = this.from(obj(join.larg), scope);
5413
+ const middle = scope.relations.length;
5414
+ const right = this.from(obj(join.rarg), scope);
5415
+ const nullable = (relations) => {
5416
+ for (const relation of relations) relation.columns = relation.columns.map((c) => ({ ...c, nullable: true }));
5417
+ };
5418
+ if (["JOIN_RIGHT", "JOIN_FULL"].includes(str(join.jointype))) nullable(scope.relations.slice(start, middle));
5419
+ if (["JOIN_LEFT", "JOIN_FULL"].includes(str(join.jointype))) nullable(scope.relations.slice(middle));
5420
+ const kind = str(join.jointype).slice("JOIN_".length);
5421
+ return `(${left} ${kind} JOIN ${right} ON ${join.quals ? this.expr(obj(join.quals), scope).text : "TRUE"})`;
5422
+ }
5423
+ if (node.RangeSubselect) {
5424
+ const range = obj(node.RangeSubselect);
5425
+ only(range, ["subquery", "alias"]);
5426
+ const alias = str(obj(range.alias).aliasname);
5427
+ only(obj(range.alias), ["aliasname"]);
5428
+ if (scope.relations.some((r) => r.alias === alias)) fail3("duplicate SQL alias");
5429
+ const result = this.select(obj(range.subquery), { relations: [], ctes: scope.ctes });
5430
+ scope.relations.push({ alias, columns: result.columns });
5431
+ return `(${result.text}) AS ${quoteIdentifier(alias)}`;
5432
+ }
5433
+ return fail3("unsupported SQL relation");
5434
+ }
5435
+ column(fields, scope) {
5436
+ if (fields.length !== 1 && fields.length !== 2) fail3("unsupported column reference");
5437
+ const name2 = fields[fields.length - 1];
5438
+ const matches = scope.relations.flatMap((r) => fields.length === 2 && r.alias !== fields[0] ? [] : r.columns.filter((c) => c.name === name2).map((c) => ({ r, c })));
5439
+ if (matches.length > 1) fail3("ambiguous SQL column");
5440
+ if (matches.length === 1) return { text: `${quoteIdentifier(matches[0].r.alias)}.${quoteIdentifier(name2)}`, type: matches[0].c.type, name: name2 };
5441
+ if (scope.parent && !(fields.length === 2 && scope.relations.some((r) => r.alias === fields[0]))) return this.column(fields, scope.parent);
5442
+ return fail3("unknown ontology property");
5443
+ }
5444
+ expr(node, scope) {
5445
+ this.budget();
5446
+ this.depth++;
5447
+ try {
5448
+ return this.expression(node, scope);
5449
+ } finally {
5450
+ this.depth--;
5451
+ }
5452
+ }
5453
+ expression(node, scope) {
5454
+ if (node.ColumnRef) return this.column(strings(obj(node.ColumnRef).fields), scope);
5455
+ if (node.ParamRef) {
5456
+ const number = obj(node.ParamRef).number;
5457
+ if (typeof number !== "number" || !Number.isInteger(number) || number < 1 || number > this.input.parameterTypes.length) fail3("undeclared SQL parameter");
5458
+ this.used.add(number);
5459
+ const type = this.input.parameterTypes[number - 1];
5460
+ return { text: `$${number}::${pgType(type)}`, type };
5461
+ }
5462
+ if (node.A_Const) {
5463
+ const literal = obj(node.A_Const);
5464
+ if (literal.isnull) return { text: "NULL", type: "unknown" };
5465
+ if (literal.sval) return { text: quoteLiteral(str(obj(literal.sval).sval)), type: "unknown" };
5466
+ if (literal.boolval) return { text: obj(literal.boolval).boolval ? "TRUE" : "FALSE", type: "boolean" };
5467
+ const value = literal.ival ? obj(literal.ival).ival : obj(literal.fval).fval;
5468
+ if (!/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(String(value))) fail3("unsupported SQL constant");
5469
+ return { text: String(value), type: literal.ival ? "integer" : "numeric" };
5470
+ }
5471
+ if (node.A_Expr) {
5472
+ const value = obj(node.A_Expr);
5473
+ only(value, ["kind", "name", "lexpr", "rexpr", "location"]);
5474
+ const names = strings(value.name);
5475
+ if (names.length !== 1 || value.kind !== "AEXPR_OP") fail3("unsupported SQL operator");
5476
+ const operator = names[0];
5477
+ if (!["=", "<>", "!=", "<", ">", "<=", ">=", "+", "-", "*", "/"].includes(operator)) fail3("unsupported SQL operator");
5478
+ const left = this.expr(obj(value.lexpr), scope), right = this.expr(obj(value.rexpr), scope);
5479
+ const numericTypes = ["integer", "bigint", "numeric", "double"];
5480
+ const arithmetic = ["+", "-", "*", "/"].includes(operator);
5481
+ if (arithmetic && (!numericTypes.includes(left.type) || !numericTypes.includes(right.type))) fail3("arithmetic requires numeric operands");
5482
+ const type = arithmetic ? numericTypes[Math.max(numericTypes.indexOf(left.type), numericTypes.indexOf(right.type))] : "boolean";
5483
+ return { text: `(${left.text} OPERATOR(pg_catalog.${operator}) ${right.text})`, type };
5484
+ }
5485
+ if (node.BoolExpr) {
5486
+ const value = obj(node.BoolExpr);
5487
+ const args = list(value.args).map((a) => this.expr(obj(a), scope).text);
5488
+ if (value.boolop === "NOT_EXPR" && args.length === 1) return { text: `(NOT ${args[0]})`, type: "boolean" };
5489
+ if (!["AND_EXPR", "OR_EXPR"].includes(str(value.boolop))) fail3("unsupported boolean expression");
5490
+ return { text: `(${args.join(value.boolop === "AND_EXPR" ? " AND " : " OR ")})`, type: "boolean" };
5491
+ }
5492
+ if (node.NullTest) {
5493
+ const value = obj(node.NullTest);
5494
+ only(value, ["arg", "nulltesttype", "location"]);
5495
+ if (!["IS_NULL", "IS_NOT_NULL"].includes(str(value.nulltesttype))) fail3("invalid null comparison");
5496
+ return { text: `(${this.expr(obj(value.arg), scope).text} IS ${value.nulltesttype === "IS_NOT_NULL" ? "NOT " : ""}NULL)`, type: "boolean" };
5497
+ }
5498
+ if (node.SubLink) {
5499
+ const value = obj(node.SubLink);
5500
+ only(value, ["subLinkType", "subselect", "location"]);
5501
+ if (value.subLinkType !== "EXISTS_SUBLINK") fail3("only EXISTS subqueries are supported");
5502
+ return { text: `EXISTS (${this.select(obj(value.subselect), scope).text})`, type: "boolean" };
5503
+ }
5504
+ if (node.TypeCast) {
5505
+ const value = obj(node.TypeCast), typeName2 = obj(value.typeName);
5506
+ only(typeName2, ["names", "typemod", "location"]);
5507
+ const names = strings(typeName2.names);
5508
+ if (names.length > 2 || names.length === 2 && names[0] !== "pg_catalog") fail3("unsupported SQL cast");
5509
+ const aliases = { int4: "integer", int8: "bigint", float8: "double", bool: "boolean" };
5510
+ const name2 = names[names.length - 1], type = aliases[name2] ?? name2;
5511
+ return { text: `(${this.expr(obj(value.arg), scope).text})::${pgType(type)}`, type };
5512
+ }
5513
+ if (node.FuncCall) {
5514
+ const value = obj(node.FuncCall);
5515
+ only(value, ["funcname", "args", "agg_star", "agg_distinct", "funcformat", "location"]);
5516
+ const names = strings(value.funcname);
5517
+ if (names.length !== 1) fail3("SQL function qualification is not supported");
5518
+ const name2 = names[0];
5519
+ if (!["count", "sum", "avg", "min", "max", "lower", "upper", "length"].includes(name2)) fail3("SQL function is not allowed");
5520
+ const args = list(value.args).map((a) => this.expr(obj(a), scope));
5521
+ if (value.agg_star && (name2 !== "count" || args.length) || !value.agg_star && args.length !== 1) fail3("unsupported SQL function arguments");
5522
+ const argType = args[0]?.type ?? "integer";
5523
+ if (["lower", "upper", "length"].includes(name2) && !["text", "unknown"].includes(argType)) fail3("text function requires text");
5524
+ if (["sum", "avg"].includes(name2) && !["integer", "bigint", "numeric", "double"].includes(argType)) fail3("aggregate requires numeric input");
5525
+ const type = name2 === "count" ? "bigint" : name2 === "length" ? "integer" : ["lower", "upper"].includes(name2) ? "text" : name2 === "avg" ? argType === "double" ? "double" : "numeric" : name2 === "sum" ? argType === "integer" ? "bigint" : argType === "bigint" ? "numeric" : argType : argType;
5526
+ return { text: `pg_catalog.${name2}(${value.agg_distinct ? "DISTINCT " : ""}${value.agg_star ? "*" : args.map((a) => a.text).join(", ")})`, type, name: name2 };
5527
+ }
5528
+ return fail3(`unsupported SQL expression: ${Object.keys(node).join(",")}`);
5529
+ }
5530
+ };
5531
+ var sqlScalarSchema = z.enum(["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz"]);
5532
+ var name = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(63).refine((value) => !["__proto__", "prototype", "constructor"].includes(value), "reserved name");
5533
+ var base = z.object({
5534
+ name,
5535
+ namespace: name,
5536
+ language: z.literal("sql"),
5537
+ kind: z.literal("query"),
5538
+ requires: z.object({ capability: z.string().min(1).max(128) }).strict(),
5539
+ ontology: z.object({ read: z.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*\/[A-Za-z_][A-Za-z0-9_]*$/)).min(1).max(256) }).strict(),
5540
+ maxRows: z.number().int().min(1).max(1e4).default(1e3)
5541
+ });
5542
+ var fixed = base.extend({
5543
+ parameters: z.record(name, z.object({ type: sqlScalarSchema, required: z.boolean().default(true) }).strict()).default({}),
5544
+ sql: z.string().min(1).max(65536)
5545
+ }).strict();
5546
+ var runtime = base.extend({ mode: z.literal("runtime") }).strict();
5547
+ var sqlOperationDefinitionSchema = z.union([fixed, runtime]);
5548
+ function defineOperation(input) {
5549
+ return sqlOperationDefinitionSchema.parse(input);
5550
+ }
5551
+
5552
+ // src/sql/parameters.ts
5553
+ function bindNamedParameters({ sql, names }) {
5554
+ let result = "", i = 0;
5555
+ while (i < sql.length) {
5556
+ const start = i;
5557
+ if (sql.startsWith("--", i)) {
5558
+ i = sql.indexOf("\n", i);
5559
+ if (i < 0) i = sql.length;
5560
+ } else if (sql.startsWith("/*", i)) {
5561
+ i += 2;
5562
+ let depth = 1;
5563
+ while (i < sql.length && depth) {
5564
+ if (sql.startsWith("/*", i)) {
5565
+ depth++;
5566
+ i += 2;
5567
+ } else if (sql.startsWith("*/", i)) {
5568
+ depth--;
5569
+ i += 2;
5570
+ } else i++;
5571
+ }
5572
+ if (depth) throw new Error("unterminated SQL comment");
5573
+ } else if (sql[i] === "'" || sql[i] === '"') {
5574
+ const escapes = sql[i] === "'" && /[eE]/.test(sql[i - 1] ?? "") && !/[A-Za-z0-9_$]/.test(sql[i - 2] ?? "");
5575
+ const quote = sql[i++];
5576
+ while (i < sql.length) {
5577
+ if (sql[i] === quote) {
5578
+ if (sql[i + 1] === quote) i += 2;
5579
+ else {
5580
+ i++;
5581
+ break;
5582
+ }
5583
+ } else if (escapes && sql[i] === "\\") i += 2;
5584
+ else i++;
5585
+ }
5586
+ } else if (sql[i] === "$" && /^\$(?:[A-Za-z_][A-Za-z0-9_]*)?\$/.test(sql.slice(i))) {
5587
+ const marker = /^\$(?:[A-Za-z_][A-Za-z0-9_]*)?\$/.exec(sql.slice(i))[0];
5588
+ const end = sql.indexOf(marker, i + marker.length);
5589
+ if (end < 0) throw new Error("unterminated SQL string");
5590
+ i = end + marker.length;
5591
+ } else if (sql[i] === ":" && sql[i - 1] !== ":" && sql[i + 1] !== ":") {
5592
+ const name2 = /^[A-Za-z_][A-Za-z0-9_]*/.exec(sql.slice(i + 1))?.[0];
5593
+ if (!name2 || !names.includes(name2)) throw new Error("undeclared named SQL parameter");
5594
+ result += `$${names.indexOf(name2) + 1}`;
5595
+ i += name2.length + 1;
5596
+ continue;
5597
+ } else i++;
5598
+ result += sql.slice(start, i);
5599
+ }
5600
+ return result;
5601
+ }
5602
+
5603
+ // src/sql/operations.ts
5604
+ function isSqlOperationDocument(path, contents) {
5605
+ if (path.endsWith(".operation.json")) return true;
5606
+ if (!contents?.trimStart().startsWith("{")) return false;
5607
+ try {
5608
+ const value = JSON.parse(contents);
5609
+ return !!value && typeof value === "object" && "language" in value && value.language === "sql";
5610
+ } catch {
5611
+ return false;
5612
+ }
5613
+ }
5614
+ async function compileOperationDocuments(input) {
5615
+ const sqlDocuments = input.documents.filter((d) => isSqlOperationDocument(d.path, d.contents));
5616
+ const result = compileNamespace({ ...input, documents: input.documents.filter((d) => !isSqlOperationDocument(d.path, d.contents)) });
5617
+ if (!result.ok || !sqlDocuments.length) return result;
5618
+ for (const document of sqlDocuments) {
5619
+ try {
5620
+ const definition = defineOperation(JSON.parse(document.contents));
5621
+ if (definition.namespace !== input.canonical.namespace) throw new Error("SQL operation namespace does not match its owner");
5622
+ if (result.artifact.operations[definition.name]) throw new Error("duplicate operation name");
5623
+ const roles = input.canonical.capabilities.roles;
5624
+ const localCapabilities = roles && typeof roles === "object" && "capabilities" in roles ? roles.capabilities : void 0;
5625
+ const declaredLocally = localCapabilities && typeof localCapabilities === "object" && Object.hasOwn(localCapabilities, definition.requires.capability);
5626
+ if (!declaredLocally && !input.canonical.capabilities.uses.includes(definition.requires.capability)) throw new Error("SQL operation capability is not declared by its namespace");
5627
+ const read = [...definition.ontology.read].sort();
5628
+ const catalog = createSqlCatalog({ canonical: input.canonical, publishedObjects: read, executionTarget: "registered-operation" });
5629
+ if ("mode" in definition) {
5630
+ if (catalog.objects.some((object) => object.read.some((ref) => !read.includes(ref)))) throw new Error("SQL policy accesses an undeclared ontology object");
5631
+ const opHash2 = sqlHash(JSON.stringify([
5632
+ "sequence-runtime-sql-operation-v1",
5633
+ definition.name,
5634
+ definition.namespace,
5635
+ definition.requires.capability,
5636
+ read,
5637
+ catalog.identity,
5638
+ definition.maxRows
5639
+ ]));
5640
+ result.artifact.operations[definition.name] = compiledOperationSchema.parse({
5641
+ irVersion: RUNTIME_SQL_IR_VERSION,
5642
+ language: "sql",
5643
+ name: definition.name,
5644
+ opHash: opHash2,
5645
+ kind: "query",
5646
+ requires: definition.requires,
5647
+ variables: [
5648
+ { name: "sql", type: { kind: "scalar", scalar: "text" }, nullable: false, list: false },
5649
+ { name: "parameters", type: { kind: "scalar", scalar: "jsonb" }, nullable: true, list: false }
5650
+ ],
5651
+ roots: [],
5652
+ tableSet: read.map((ref) => ref.split("/")[1]).sort(),
5653
+ sqlQuery: { catalog, read, maxRows: definition.maxRows }
5654
+ });
5655
+ result.manifest.operations[definition.name] = { opHash: opHash2, kind: "query", requires: definition.requires };
5656
+ continue;
5657
+ }
5658
+ const names = Object.keys(definition.parameters).sort();
5659
+ if (names.length > 128) throw new Error("too many SQL parameters");
5660
+ const plan = await compileOntologySql({
5661
+ catalog,
5662
+ sql: bindNamedParameters({ sql: definition.sql, names }),
5663
+ parameterTypes: names.map((name2) => definition.parameters[name2].type),
5664
+ maxRows: definition.maxRows
5665
+ });
5666
+ if (plan.read.some((ref) => !read.includes(ref))) throw new Error("SQL policy/query accesses an undeclared ontology object");
5667
+ const variables = names.map((name2) => ({
5668
+ name: name2,
5669
+ type: { kind: "scalar", scalar: definition.parameters[name2].type },
5670
+ nullable: !definition.parameters[name2].required,
5671
+ list: false
5672
+ }));
5673
+ const opHash = sqlHash(JSON.stringify([
5674
+ "sequence-sql-operation-v1",
5675
+ definition.name,
5676
+ definition.namespace,
5677
+ definition.requires.capability,
5678
+ read,
5679
+ variables,
5680
+ plan.queryHash
5681
+ ]));
5682
+ const operation = compiledOperationSchema.parse({
5683
+ irVersion: SQL_IR_VERSION,
5684
+ language: "sql",
5685
+ name: definition.name,
5686
+ opHash,
5687
+ kind: "query",
5688
+ requires: definition.requires,
5689
+ variables,
5690
+ roots: [],
5691
+ tableSet: plan.read.map((ref) => ref.split("/")[1]).sort(),
5692
+ sql: { text: plan.text, columns: plan.columns, parameters: names, read, maxRows: plan.maxRows }
5693
+ });
5694
+ result.artifact.operations[definition.name] = operation;
5695
+ result.manifest.operations[definition.name] = { opHash, kind: "query", requires: definition.requires };
5696
+ } catch (error) {
5697
+ return { ok: false, diagnostics: [...result.diagnostics, {
5698
+ severity: "error",
5699
+ code: "invalid-sql-operation",
5700
+ document: document.path,
5701
+ message: error instanceof Error ? error.message : "invalid SQL operation"
5702
+ }] };
5703
+ }
5704
+ }
5705
+ const required = requiredArtifactIrVersion(Object.values(result.artifact.operations));
5706
+ if (required > result.artifact.irVersion) result.artifact.irVersion = required;
5707
+ result.artifact.opsHash = computeOpsHash(Object.values(result.artifact.operations).map((op) => op.opHash));
5708
+ result.manifest.opsHash = result.artifact.opsHash;
5709
+ return { ...result, artifact: normalizeIr(result.artifact) };
5710
+ }
5711
+ var parameterName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(63).refine((value) => !["__proto__", "prototype", "constructor"].includes(value), "reserved parameter name");
5712
+ var parameter = z.discriminatedUnion("type", [
5713
+ z.object({ type: z.literal("text"), value: z.string().nullable() }).strict(),
5714
+ z.object({ type: z.literal("uuid"), value: z.guid().nullable() }).strict(),
5715
+ z.object({ type: z.literal("date"), value: z.string().nullable() }).strict(),
5716
+ z.object({ type: z.literal("timestamptz"), value: z.string().nullable() }).strict(),
5717
+ z.object({ type: z.literal("numeric"), value: z.string().regex(/^-?\d+(\.\d+)?$/).nullable() }).strict(),
5718
+ z.object({ type: z.literal("bigint"), value: z.string().regex(/^-?\d+$/).nullable() }).strict(),
5719
+ z.object({ type: z.literal("integer"), value: z.number().int().min(-2147483648).max(2147483647).nullable() }).strict(),
5720
+ z.object({ type: z.literal("double"), value: z.number().nullable() }).strict(),
5721
+ z.object({ type: z.literal("boolean"), value: z.boolean().nullable() }).strict()
5722
+ ]);
5723
+ var sqlQueryInputSchema = z.object({
5724
+ sql: z.string().min(1).max(65536).refine((value) => Buffer.byteLength(value) <= 65536, "SQL exceeds 64 KiB"),
5725
+ parameters: z.record(parameterName, parameter).refine((value) => Object.keys(value).length <= 128, "too many SQL parameters").default({})
5726
+ }).strict();
5727
+ async function compileRuntimeSql({ operation, input }) {
5728
+ const contract = operation.sqlQuery;
5729
+ if (!contract) throw new Error("missing runtime SQL contract");
5730
+ const names = Object.keys(input.parameters).sort();
5731
+ const plan = await compileOntologySql({
5732
+ catalog: contract.catalog,
5733
+ sql: bindNamedParameters({ sql: input.sql, names }),
5734
+ parameterTypes: names.map((name2) => input.parameters[name2].type),
5735
+ maxRows: contract.maxRows
5736
+ });
5737
+ if (plan.read.some((ref) => !contract.read.includes(ref))) throw new Error("SQL accesses an undeclared ontology object");
5738
+ return compiledOperationSchema.parse({
5739
+ ...operation,
5740
+ sqlQuery: void 0,
5741
+ irVersion: SQL_IR_VERSION,
5742
+ variables: names.map((name2) => ({ name: name2, type: { kind: "scalar", scalar: input.parameters[name2].type }, nullable: true, list: false })),
5743
+ tableSet: plan.read.map((ref) => ref.split("/")[1]).sort(),
5744
+ sql: { text: plan.text, columns: plan.columns, parameters: names, read: plan.read, maxRows: plan.maxRows }
5745
+ });
5746
+ }
5747
+ var DATASET_PREVIEW_LIMIT = 100;
5748
+ function compileDatasetPreviews(input) {
5749
+ const canonical = normalizeCanonical(input);
5750
+ const { ctx, diagnostics } = buildCompileContext(canonical);
5751
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) throw new NamespaceCompileError(diagnostics);
5752
+ const schemaHash = computeSchemaHash(canonical);
5753
+ const operations = {};
5754
+ for (const relation of canonicalRelations(canonical)) {
5755
+ if (relation.internal || relation.source !== void 0) continue;
5756
+ const columns = relation.columns.filter((column) => column.cell === void 0);
5757
+ const { roots, tableSet } = injectPolicies({ ctx, roots: [{
5758
+ kind: "select",
5759
+ table: relation.table,
5760
+ alias: "rows",
5761
+ where: { kind: "const", value: true },
5762
+ orderBy: [],
5763
+ limit: { value: DATASET_PREVIEW_LIMIT, default: DATASET_PREVIEW_LIMIT, max: DATASET_PREVIEW_LIMIT },
5764
+ selection: {
5765
+ typename: ctx.tables[relation.table].typename,
5766
+ columns: columns.map((column) => ({ column: column.name, alias: column.name })),
5767
+ relations: [],
5768
+ aggregates: []
5769
+ }
5770
+ }] });
5771
+ for (const table of tableSet) {
5772
+ const meta = ctx.tables[table];
5773
+ if (meta.namespace && meta.namespace !== ctx.namespace && !meta.exportRead) {
5774
+ throw new DocumentCompileError(`object '${meta.namespace}/${table}' is not exported for reads`, "unexported-object");
5775
+ }
5776
+ }
5777
+ const hash = createHash("sha256").update("sequence-orm-dataset-preview-v1\0").update(stableJson({ schemaHash, roots, tableSet })).digest("hex");
5778
+ operations[relation.table] = compiledOperationSchema.parse({
5779
+ irVersion: canonical.imports?.length ? COMPOSED_IR_VERSION : LEGACY_IR_VERSION,
5780
+ name: `DatasetPreview_${relation.table}`,
5781
+ opHash: `sha256:${hash}`,
5782
+ kind: "query",
5783
+ requires: null,
5784
+ variables: [],
5785
+ tableSet,
5786
+ roots
5787
+ });
5788
+ }
5789
+ return operations;
5790
+ }
5791
+
5792
+ export { COMPILER_MAJOR, CONST_TRUE, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
4827
5793
  //# sourceMappingURL=compiler.js.map
4828
5794
  //# sourceMappingURL=compiler.js.map