@sequenceholdings/orm 2.9.0 → 2.11.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 (50) hide show
  1. package/README.md +71 -0
  2. package/dist/authoring.cjs +796 -7
  3. package/dist/authoring.cjs.map +1 -1
  4. package/dist/authoring.d.cts +11 -2
  5. package/dist/authoring.d.ts +11 -2
  6. package/dist/authoring.js +796 -7
  7. package/dist/authoring.js.map +1 -1
  8. package/dist/canonical.cjs +777 -6
  9. package/dist/canonical.cjs.map +1 -1
  10. package/dist/canonical.d.cts +43 -43
  11. package/dist/canonical.d.ts +43 -43
  12. package/dist/canonical.js +777 -6
  13. package/dist/canonical.js.map +1 -1
  14. package/dist/cli.cjs +7585 -7512
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.d.cts +10 -1
  17. package/dist/cli.d.ts +10 -1
  18. package/dist/cli.js +7584 -7513
  19. package/dist/cli.js.map +1 -1
  20. package/dist/compiler.cjs +1059 -50
  21. package/dist/compiler.cjs.map +1 -1
  22. package/dist/compiler.d.cts +182 -2
  23. package/dist/compiler.d.ts +182 -2
  24. package/dist/compiler.js +1054 -51
  25. package/dist/compiler.js.map +1 -1
  26. package/dist/index.cjs +3720 -3447
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +96 -31
  29. package/dist/index.d.ts +96 -31
  30. package/dist/index.js +3714 -3446
  31. package/dist/index.js.map +1 -1
  32. package/dist/ir.cjs +787 -14
  33. package/dist/ir.cjs.map +1 -1
  34. package/dist/ir.d.cts +70 -59
  35. package/dist/ir.d.ts +70 -59
  36. package/dist/ir.js +787 -15
  37. package/dist/ir.js.map +1 -1
  38. package/dist/predicate-v2.cjs +802 -30
  39. package/dist/predicate-v2.cjs.map +1 -1
  40. package/dist/predicate-v2.d.cts +2 -0
  41. package/dist/predicate-v2.d.ts +2 -0
  42. package/dist/predicate-v2.js +802 -30
  43. package/dist/predicate-v2.js.map +1 -1
  44. package/dist/sdl.cjs +794 -24
  45. package/dist/sdl.cjs.map +1 -1
  46. package/dist/sdl.js +794 -24
  47. package/dist/sdl.js.map +1 -1
  48. package/package.json +1 -1
  49. package/dist/{definition-B99BsOAO.d.cts → definition-CevES7lM.d.cts} +2 -2
  50. package/dist/{definition-B99BsOAO.d.ts → definition-CevES7lM.d.ts} +2 -2
package/dist/compiler.js CHANGED
@@ -7,16 +7,21 @@ import { PgParser } from '@supabase/pg-parser';
7
7
 
8
8
  // src/constants.ts
9
9
  var NAMESPACE_PATTERN = /^(?!(?:events|native_sources)$)[a-z][a-z0-9_]{0,40}$/;
10
+ var IDENTIFIER_PATTERN = /^[a-z][a-z0-9_]*$/;
10
11
  var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
11
12
  var MAX_IDENTIFIER_LENGTH = 63;
12
13
  var CAPABILITY_KEY_PATTERN = /^(?:org\/|app\/[a-z][a-z0-9-]{0,63}\/)?[a-z0-9-]{1,64}$/;
13
14
  var MAX_PERSISTED_READ_ROWS = 50001;
15
+ var CLAIM_KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
14
16
  var QUALIFIED_TABLE_PATTERN = /^[a-z][a-z0-9_]{0,62}\.[a-z][a-z0-9_]{0,62}$/;
17
+ var SYNCED_SOURCE_PATTERN = QUALIFIED_TABLE_PATTERN;
15
18
  var ORM_SCHEMA_PREFIX = "orm_";
16
19
  var ormSchemaName = (namespace) => {
17
20
  if (!NAMESPACE_PATTERN.test(namespace)) throw new Error(`invalid or reserved ORM namespace '${namespace}'`);
18
21
  return `${ORM_SCHEMA_PREFIX}${namespace}`;
19
22
  };
23
+ var RESERVED_TABLE_NAMES = /* @__PURE__ */ new Set(["actions", "query"]);
24
+ var RESERVED_COLUMN_NAMES = /* @__PURE__ */ new Set(["select", "order", "limit", "offset"]);
20
25
  var CELL_PARTS = ["at", "by", "rev", "source", "conflicts"];
21
26
  var CELL_PART_SUFFIX = {
22
27
  at: "__at",
@@ -248,6 +253,748 @@ var Parser = class {
248
253
  function compileCheckExpression(source, columns) {
249
254
  return new Parser(tokenize(source), columns).parse();
250
255
  }
256
+
257
+ // src/predicate/ast.ts
258
+ var COMPARE_OPS = ["=", "!=", "<", "<=", ">", ">="];
259
+ function walkPredicate(predicate, visit2) {
260
+ visit2(predicate);
261
+ if (predicate.kind === "and" || predicate.kind === "or") {
262
+ for (const operand of predicate.operands) walkPredicate(operand, visit2);
263
+ }
264
+ if (predicate.kind === "not") walkPredicate(predicate.operand, visit2);
265
+ }
266
+ function predicateOperands(predicate) {
267
+ const operands = [];
268
+ walkPredicate(predicate, (node) => {
269
+ if (node.kind === "compare") operands.push(node.left, node.right);
270
+ });
271
+ return operands;
272
+ }
273
+ function referencedColumns(predicate) {
274
+ const names = predicateOperands(predicate).flatMap(
275
+ (operand) => operand.kind === "column" ? [operand.name] : []
276
+ );
277
+ return [...new Set(names)];
278
+ }
279
+ var identifierSchema = z.string().max(MAX_IDENTIFIER_LENGTH).regex(IDENTIFIER_PATTERN, "must be a lowercase identifier (a-z, 0-9, _)");
280
+ var namespaceSchema = z.string().regex(NAMESPACE_PATTERN, "must be lowercase, start with a letter, and be at most 41 characters");
281
+ var tableReferenceSchema = z.string().refine(
282
+ (value) => IDENTIFIER_PATTERN.test(value) || QUALIFIED_TABLE_PATTERN.test(value),
283
+ "must be a table name or a schema-qualified table reference"
284
+ );
285
+
286
+ // src/schema/columns.ts
287
+ var COLUMN_TYPES = [
288
+ "uuid",
289
+ "text",
290
+ "integer",
291
+ "bigint",
292
+ "numeric",
293
+ "double",
294
+ "boolean",
295
+ "timestamptz",
296
+ "date",
297
+ "jsonb"
298
+ ];
299
+ var columnTypeSchema = z.enum(COLUMN_TYPES);
300
+ var FUNCTION_DEFAULTS = {
301
+ "gen_random_uuid()": ["uuid"],
302
+ "now()": ["timestamptz", "date"]
303
+ };
304
+ var CAST_PATTERN = /^([a-z][a-z0-9_]*)::([a-z]+)$/;
305
+ function defaultIssue(value, type) {
306
+ if (typeof value === "boolean") return type === "boolean" ? null : `boolean default is invalid for type '${type}'`;
307
+ if (typeof value === "number") {
308
+ if (!["integer", "bigint", "numeric", "double"].includes(type)) {
309
+ return `numeric default is invalid for type '${type}'`;
310
+ }
311
+ if (["integer", "bigint"].includes(type)) {
312
+ if (!Number.isInteger(value)) return `default for type '${type}' must be an integer`;
313
+ if (!Number.isSafeInteger(value)) {
314
+ return `default for type '${type}' exceeds the safe integer range \u2014 YAML numbers lose precision past 2^53`;
315
+ }
316
+ }
317
+ if (type === "numeric" && Math.abs(value) > Number.MAX_SAFE_INTEGER) {
318
+ return "numeric default exceeds the safe float range \u2014 write it as a string (e.g. '9007199254740993') to keep exact precision";
319
+ }
320
+ return null;
321
+ }
322
+ const allowedTypes = FUNCTION_DEFAULTS[value];
323
+ if (allowedTypes) return allowedTypes.includes(type) ? null : `default '${value}' is invalid for type '${type}'`;
324
+ return null;
325
+ }
326
+ var columnSchema = z.strictObject({
327
+ type: columnTypeSchema,
328
+ primary_key: z.boolean().optional(),
329
+ nullable: z.boolean().optional(),
330
+ unique: z.boolean().optional(),
331
+ default: z.union([z.string(), z.number(), z.boolean()]).optional(),
332
+ description: z.string().optional(),
333
+ renamed_from: identifierSchema.optional(),
334
+ cast: z.string().optional(),
335
+ deprecated: z.boolean().optional()
336
+ }).superRefine((column, ctx) => {
337
+ if (column.default !== void 0) {
338
+ const issue = defaultIssue(column.default, column.type);
339
+ if (issue) ctx.addIssue({ code: "custom", message: issue, path: ["default"] });
340
+ }
341
+ if (column.primary_key && column.nullable) {
342
+ ctx.addIssue({ code: "custom", message: "a primary key column cannot be nullable", path: ["nullable"] });
343
+ }
344
+ if (column.primary_key && column.deprecated) {
345
+ ctx.addIssue({ code: "custom", message: "a primary key column cannot be deprecated", path: ["deprecated"] });
346
+ }
347
+ if (column.cast && !CAST_PATTERN.test(column.cast)) {
348
+ ctx.addIssue({ code: "custom", message: "cast must have the shape '<column>::<type>'", path: ["cast"] });
349
+ }
350
+ });
351
+ function castParts(cast) {
352
+ const match = CAST_PATTERN.exec(cast);
353
+ if (!match) return null;
354
+ return { column: match[1], type: match[2] };
355
+ }
356
+
357
+ // src/errors.ts
358
+ var OrmDefinitionError = class extends Error {
359
+ constructor(message, file) {
360
+ super(file ? `${file}: ${message}` : message);
361
+ this.file = file;
362
+ }
363
+ name = "OrmDefinitionError";
364
+ };
365
+ var PredicateParseError = class extends Error {
366
+ constructor(message, position) {
367
+ super(`${message} (at position ${position})`);
368
+ this.position = position;
369
+ }
370
+ name = "PredicateParseError";
371
+ };
372
+
373
+ // src/predicate/parse.ts
374
+ var KEYWORDS = /* @__PURE__ */ new Set(["and", "or", "not", "true", "false", "null", "claim", "capability"]);
375
+ var OPERATOR_PATTERN = /^(!=|<=|>=|<>|=|<|>)/;
376
+ var IDENTIFIER_START = /[a-z_]/;
377
+ var IDENTIFIER_CHAR = /[a-z0-9_.-]/;
378
+ var NUMBER_PATTERN = /^-?\d+(\.\d+)?/;
379
+ function tokenize2(source) {
380
+ const tokens = [];
381
+ let position = 0;
382
+ while (position < source.length) {
383
+ const char = source[position];
384
+ if (/\s/.test(char)) {
385
+ position += 1;
386
+ continue;
387
+ }
388
+ if (char === "(") {
389
+ tokens.push({ kind: "lparen", value: char, position });
390
+ position += 1;
391
+ continue;
392
+ }
393
+ if (char === ")") {
394
+ tokens.push({ kind: "rparen", value: char, position });
395
+ position += 1;
396
+ continue;
397
+ }
398
+ if (char === ",") {
399
+ tokens.push({ kind: "comma", value: char, position });
400
+ position += 1;
401
+ continue;
402
+ }
403
+ if (char === "'") {
404
+ const { value, end } = readString(source, position);
405
+ tokens.push({ kind: "string", value, position });
406
+ position = end;
407
+ continue;
408
+ }
409
+ const operator = OPERATOR_PATTERN.exec(source.slice(position));
410
+ if (operator) {
411
+ const value = operator[0] === "<>" ? "!=" : operator[0];
412
+ tokens.push({ kind: "operator", value, position });
413
+ position += operator[0].length;
414
+ continue;
415
+ }
416
+ const number = NUMBER_PATTERN.exec(source.slice(position));
417
+ if (number && !IDENTIFIER_START.test(char)) {
418
+ tokens.push({ kind: "number", value: number[0], position });
419
+ position += number[0].length;
420
+ continue;
421
+ }
422
+ if (IDENTIFIER_START.test(char)) {
423
+ let end = position;
424
+ while (end < source.length && IDENTIFIER_CHAR.test(source[end])) end += 1;
425
+ tokens.push({ kind: "identifier", value: source.slice(position, end), position });
426
+ position = end;
427
+ continue;
428
+ }
429
+ throw new PredicateParseError(`unexpected character '${char}'`, position);
430
+ }
431
+ tokens.push({ kind: "end", value: "", position: source.length });
432
+ return tokens;
433
+ }
434
+ function readString(source, start) {
435
+ let value = "";
436
+ let position = start + 1;
437
+ while (position < source.length) {
438
+ const char = source[position];
439
+ if (char !== "'") {
440
+ value += char;
441
+ position += 1;
442
+ continue;
443
+ }
444
+ if (source[position + 1] === "'") {
445
+ value += "'";
446
+ position += 2;
447
+ continue;
448
+ }
449
+ return { value, end: position + 1 };
450
+ }
451
+ throw new PredicateParseError("unterminated string literal", start);
452
+ }
453
+ var Parser2 = class {
454
+ constructor(tokens) {
455
+ this.tokens = tokens;
456
+ }
457
+ index = 0;
458
+ parse() {
459
+ const predicate = this.parseOr();
460
+ this.expect("end", "end of expression");
461
+ return predicate;
462
+ }
463
+ parseOr() {
464
+ const operands = [this.parseAnd()];
465
+ while (this.matchKeyword("or")) operands.push(this.parseAnd());
466
+ return operands.length === 1 ? operands[0] : { kind: "or", operands };
467
+ }
468
+ parseAnd() {
469
+ const operands = [this.parseNot()];
470
+ while (this.matchKeyword("and")) operands.push(this.parseNot());
471
+ return operands.length === 1 ? operands[0] : { kind: "and", operands };
472
+ }
473
+ parseNot() {
474
+ if (this.matchKeyword("not")) return { kind: "not", operand: this.parseNot() };
475
+ return this.parsePrimary();
476
+ }
477
+ parsePrimary() {
478
+ if (this.peek().kind === "lparen") {
479
+ this.advance();
480
+ const inner = this.parseOr();
481
+ this.expect("rparen", "')'");
482
+ return inner;
483
+ }
484
+ if (this.peekKeyword("capability")) {
485
+ const capability = this.parseCapability();
486
+ const next = this.peek();
487
+ if (next.kind === "operator") {
488
+ throw new PredicateParseError("capability() is boolean-valued and cannot appear in a comparison", next.position);
489
+ }
490
+ return capability;
491
+ }
492
+ return this.parseComparison();
493
+ }
494
+ parseCapability() {
495
+ this.advance();
496
+ const key = this.parseCallArgument("capability");
497
+ if (!CAPABILITY_KEY_PATTERN.test(key)) {
498
+ throw new PredicateParseError(`invalid capability key '${key}'`, this.peek().position);
499
+ }
500
+ return { kind: "capability", key };
501
+ }
502
+ parseComparison() {
503
+ const left = this.parseOperand();
504
+ const operator = this.expect("operator", "a comparison operator");
505
+ if (!COMPARE_OPS.includes(operator.value)) {
506
+ throw new PredicateParseError(`unsupported operator '${operator.value}'`, operator.position);
507
+ }
508
+ const right = this.parseOperand();
509
+ return { kind: "compare", op: operator.value, left, right };
510
+ }
511
+ parseOperand() {
512
+ const token = this.peek();
513
+ if (token.kind === "string") {
514
+ this.advance();
515
+ return { kind: "literal", value: token.value };
516
+ }
517
+ if (token.kind === "number") {
518
+ this.advance();
519
+ const value = Number(token.value);
520
+ if (!Number.isFinite(value) || Number.isInteger(value) && !Number.isSafeInteger(value)) {
521
+ throw new PredicateParseError(
522
+ `numeric literal '${token.value}' exceeds safe integer precision \u2014 quote it as a string`,
523
+ token.position
524
+ );
525
+ }
526
+ return { kind: "literal", value };
527
+ }
528
+ if (token.kind !== "identifier") {
529
+ throw new PredicateParseError(`expected an operand, got '${token.value || "end"}'`, token.position);
530
+ }
531
+ this.advance();
532
+ if (token.value === "true" || token.value === "false") {
533
+ return { kind: "literal", value: token.value === "true" };
534
+ }
535
+ if (token.value === "null") return { kind: "literal", value: null };
536
+ if (token.value === "claim") {
537
+ const key = this.parseCallArgument("claim");
538
+ if (!CLAIM_KEY_PATTERN.test(key)) {
539
+ throw new PredicateParseError(`invalid claim key '${key}'`, token.position);
540
+ }
541
+ return { kind: "claim", key };
542
+ }
543
+ if (token.value === "capability") {
544
+ throw new PredicateParseError("capability() is boolean-valued and cannot appear in a comparison", token.position);
545
+ }
546
+ if (KEYWORDS.has(token.value) || !IDENTIFIER_PATTERN.test(token.value)) {
547
+ throw new PredicateParseError(`invalid column reference '${token.value}'`, token.position);
548
+ }
549
+ return { kind: "column", name: token.value };
550
+ }
551
+ parseCallArgument(fn) {
552
+ this.expect("lparen", `'(' after ${fn}`);
553
+ const argument = this.expect("string", `a quoted key in ${fn}(...)`);
554
+ this.expect("rparen", `')' after ${fn} key`);
555
+ return argument.value;
556
+ }
557
+ peek() {
558
+ return this.tokens[this.index];
559
+ }
560
+ peekKeyword(keyword) {
561
+ const token = this.peek();
562
+ return token.kind === "identifier" && token.value === keyword;
563
+ }
564
+ matchKeyword(keyword) {
565
+ if (!this.peekKeyword(keyword)) return false;
566
+ this.advance();
567
+ return true;
568
+ }
569
+ advance() {
570
+ const token = this.peek();
571
+ if (token.kind !== "end") this.index += 1;
572
+ return token;
573
+ }
574
+ expect(kind, description) {
575
+ const token = this.peek();
576
+ if (token.kind !== kind) {
577
+ throw new PredicateParseError(`expected ${description}, got '${token.value || "end"}'`, token.position);
578
+ }
579
+ return this.advance();
580
+ }
581
+ };
582
+ function parsePredicate(source) {
583
+ const trimmed = source.trim();
584
+ if (trimmed.length === 0) throw new PredicateParseError("empty predicate", 0);
585
+ return { source: trimmed, ast: new Parser2(tokenize2(trimmed)).parse() };
586
+ }
587
+
588
+ // src/schema/predicates.ts
589
+ var parsedPredicateSchema = z.string().transform((source, ctx) => {
590
+ try {
591
+ return parsePredicate(source);
592
+ } catch (error) {
593
+ ctx.addIssue({ code: "custom", message: error instanceof Error ? error.message : String(error) });
594
+ return z.NEVER;
595
+ }
596
+ });
597
+
598
+ // src/schema/actions.ts
599
+ var EXECUTES_VERB_PATTERN = /^\s*(insert|update|delete)\b/i;
600
+ var DOLLAR_QUOTE_PATTERN = /\$[a-zA-Z0-9_]*\$/;
601
+ var RETURNING_PATTERN = /\breturning\b/i;
602
+ var ESCAPE_STRING_PATTERN = /(?<![a-zA-Z0-9_])[Ee]'/;
603
+ var MASKED_CLAIM_CALL_PATTERN = /\b(?:claim|capability)\(\s*''\s*\)/g;
604
+ var BARE_CLAIM_CALL_PATTERN = /\b(?:claim|capability)\s*\(/;
605
+ var EXISTS_ENTRY_PATTERN = /^exists\(\s*'([^']+)'\s*,\s*([a-z][a-z0-9_]*)\s*\)$/;
606
+ var NAMED_PARAM_PATTERN = /(?<!:):([a-z][a-z0-9_]*)/g;
607
+ var QUOTED_STRING_PATTERN = /'(?:[^']|'')*'/g;
608
+ var SQL_COMMENT_PATTERN = /--|\/\*/;
609
+ var actionParamSchema = z.strictObject({
610
+ type: columnTypeSchema,
611
+ optional: z.boolean().optional(),
612
+ description: z.string().optional()
613
+ });
614
+ var validatesEntrySchema = z.string().transform((source, ctx) => {
615
+ const match = EXISTS_ENTRY_PATTERN.exec(source.trim());
616
+ if (!match) {
617
+ ctx.addIssue({ code: "custom", message: "validates entries must have the shape exists('<table>', <param>)" });
618
+ return z.NEVER;
619
+ }
620
+ const table = tableReferenceSchema.safeParse(match[1]);
621
+ if (!table.success) {
622
+ ctx.addIssue({ code: "custom", message: `invalid table reference '${match[1]}'` });
623
+ return z.NEVER;
624
+ }
625
+ return { source: source.trim(), table: table.data, param: match[2] };
626
+ });
627
+ var AUDIT_ACTIONS = ["create", "update", "delete"];
628
+ var auditSchema = z.strictObject({
629
+ action: z.enum(AUDIT_ACTIONS),
630
+ resource: identifierSchema.optional()
631
+ });
632
+ var actionSchema = z.strictObject({
633
+ description: z.string().optional(),
634
+ requires: parsedPredicateSchema.optional(),
635
+ params: z.record(identifierSchema, actionParamSchema).optional(),
636
+ validates: z.array(validatesEntrySchema).optional(),
637
+ // One statement, or an ordered list run atomically in the action function.
638
+ executes: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]),
639
+ // Declares the columns the action's RETURNING yields. Present → the
640
+ // function returns those rows (typed in the client); absent → the action
641
+ // reports rows_affected only. The column names/types must match the
642
+ // RETURNING list, which Postgres enforces at CREATE FUNCTION.
643
+ returns: z.record(identifierSchema, columnTypeSchema).optional(),
644
+ audit: auditSchema.optional()
645
+ }).superRefine((action, ctx) => {
646
+ const params = Object.keys(action.params ?? {});
647
+ if (action.requires && referencedColumns(action.requires.ast).length > 0) {
648
+ ctx.addIssue({
649
+ code: "custom",
650
+ message: "requires may reference claims and capabilities only, not columns",
651
+ path: ["requires"]
652
+ });
653
+ }
654
+ for (const issue of executesIssues(action.executes, new Set(params), action.returns !== void 0)) {
655
+ ctx.addIssue({ code: "custom", message: issue, path: ["executes"] });
656
+ }
657
+ if (action.returns && Object.keys(action.returns).length === 0) {
658
+ ctx.addIssue({ code: "custom", message: "returns must declare at least one column", path: ["returns"] });
659
+ }
660
+ for (const [index, entry] of (action.validates ?? []).entries()) {
661
+ if (!params.includes(entry.param)) {
662
+ ctx.addIssue({
663
+ code: "custom",
664
+ message: `validates references undeclared param '${entry.param}'`,
665
+ path: ["validates", index]
666
+ });
667
+ }
668
+ }
669
+ });
670
+ function extractNamedParams(sql) {
671
+ const withoutStrings = sql.replace(QUOTED_STRING_PATTERN, "''");
672
+ const names = [...withoutStrings.matchAll(NAMED_PARAM_PATTERN)].map((match) => match[1]);
673
+ return [...new Set(names)];
674
+ }
675
+ function actionStatementSources(executes) {
676
+ return (Array.isArray(executes) ? executes : [executes]).map((statement) => statement.trim());
677
+ }
678
+ function executesIssues(executes, declaredParams, hasReturns) {
679
+ const issues = [];
680
+ const statements = actionStatementSources(executes);
681
+ statements.forEach((statement, index) => {
682
+ const withoutStrings = statement.replace(QUOTED_STRING_PATTERN, "''");
683
+ const at = statements.length > 1 ? ` (statement ${index + 1})` : "";
684
+ if (!EXECUTES_VERB_PATTERN.test(statement)) issues.push(`executes must be INSERT, UPDATE, or DELETE${at}`);
685
+ if (withoutStrings.replace(/;\s*$/, "").includes(";")) issues.push(`each executes entry must be a single statement${at}`);
686
+ if (SQL_COMMENT_PATTERN.test(withoutStrings)) issues.push(`executes must not contain SQL comments${at}`);
687
+ if (DOLLAR_QUOTE_PATTERN.test(withoutStrings)) issues.push(`executes must not use dollar-quoted strings${at}`);
688
+ if (ESCAPE_STRING_PATTERN.test(withoutStrings)) issues.push(`executes must not use escape strings (E'\u2026')${at}`);
689
+ if (withoutStrings.includes('"')) issues.push(`executes must not use double-quoted identifiers${at}`);
690
+ if (BARE_CLAIM_CALL_PATTERN.test(withoutStrings.replace(MASKED_CLAIM_CALL_PATTERN, ""))) {
691
+ issues.push(`executes has a malformed claim()/capability() call \u2014 use e.g. claim('sub')${at}`);
692
+ }
693
+ if (RETURNING_PATTERN.test(withoutStrings) && !(hasReturns && index === statements.length - 1)) {
694
+ issues.push(
695
+ hasReturns ? `only the last executes statement may use RETURNING${at}` : `executes must not use RETURNING unless the action declares returns${at}`
696
+ );
697
+ }
698
+ });
699
+ const last = statements.at(-1);
700
+ if (hasReturns && last && !RETURNING_PATTERN.test(last.replace(QUOTED_STRING_PATTERN, "''"))) {
701
+ issues.push("the last executes statement must use RETURNING to populate the declared returns columns");
702
+ }
703
+ const undeclared = extractNamedParams(statements.join("\n")).filter((name2) => !declaredParams.has(name2));
704
+ if (undeclared.length > 0) issues.push(`executes references undeclared params: ${undeclared.join(", ")}`);
705
+ return issues;
706
+ }
707
+
708
+ // src/schema/table.ts
709
+ var linkSchema = z.strictObject({
710
+ to: tableReferenceSchema,
711
+ on: identifierSchema,
712
+ enforce: z.boolean().optional(),
713
+ description: z.string().optional()
714
+ });
715
+ var indexSchema = z.union([
716
+ z.array(identifierSchema).min(1),
717
+ z.strictObject({
718
+ columns: z.array(identifierSchema).min(1),
719
+ unique: z.boolean().optional(),
720
+ name: identifierSchema.optional()
721
+ })
722
+ ]).transform((index) => Array.isArray(index) ? { columns: index } : index);
723
+ var policiesSchema = z.strictObject({
724
+ read: parsedPredicateSchema.optional(),
725
+ write: parsedPredicateSchema.optional(),
726
+ insert: parsedPredicateSchema.optional(),
727
+ update: parsedPredicateSchema.optional()
728
+ });
729
+ z.strictObject({
730
+ namespace: namespaceSchema,
731
+ table: identifierSchema,
732
+ description: z.string().optional(),
733
+ columns: z.record(identifierSchema, columnSchema).optional(),
734
+ source: z.string().regex(SYNCED_SOURCE_PATTERN, "source must be a qualified lowercase schema.table identifier").optional(),
735
+ links: z.record(identifierSchema, linkSchema).optional(),
736
+ indexes: z.array(indexSchema).optional(),
737
+ policies: policiesSchema.optional(),
738
+ actions: z.record(identifierSchema, actionSchema).optional()
739
+ }).superRefine((table, ctx) => {
740
+ if (RESERVED_TABLE_NAMES.has(table.table)) {
741
+ ctx.addIssue({ code: "custom", message: `'${table.table}' is a reserved table name`, path: ["table"] });
742
+ }
743
+ for (const name2 of Object.keys(table.columns ?? {})) {
744
+ if (RESERVED_COLUMN_NAMES.has(name2)) {
745
+ ctx.addIssue({
746
+ code: "custom",
747
+ message: `column '${name2}' collides with the Data API's reserved query parameters (select, order, limit, offset) \u2014 it could never be filtered`,
748
+ path: ["columns", name2]
749
+ });
750
+ }
751
+ }
752
+ if (table.source) validateSyncedTable(table, ctx);
753
+ else validateColumnTable(table, ctx);
754
+ validateLinks(table, ctx);
755
+ validatePolicyColumns(table, ctx);
756
+ validateDerivedNameLengths(table, ctx);
757
+ });
758
+ var indexName = (table, definition) => definition.name ?? `ix_${table}_${definition.columns.join("_")}`;
759
+ var policyName = (table, suffix) => `${table}_${suffix}`;
760
+ var foreignKeyName = (table, link) => `fk_${table}_${link}`;
761
+ function validateSyncedTable(table, ctx) {
762
+ const forbidden = [
763
+ ["actions", "synced tables are read-only; actions are not allowed"],
764
+ ["indexes", "v1 synced tables cannot declare indexes; declare them with defineSyncedTable({ indexes }) in a v2 definition"]
765
+ ];
766
+ for (const [field, message] of forbidden) {
767
+ if (table[field] !== void 0) ctx.addIssue({ code: "custom", message, path: [field] });
768
+ }
769
+ if (table.policies?.write) {
770
+ ctx.addIssue({ code: "custom", message: "synced tables are read-only", path: ["policies", "write"] });
771
+ }
772
+ if (table.policies?.insert) {
773
+ ctx.addIssue({ code: "custom", message: "synced tables are read-only", path: ["policies", "insert"] });
774
+ }
775
+ if (table.policies?.update) {
776
+ ctx.addIssue({ code: "custom", message: "synced tables are read-only", path: ["policies", "update"] });
777
+ }
778
+ const columns = table.columns ?? {};
779
+ const names = Object.keys(columns);
780
+ if (names.length === 0) return;
781
+ const primaryKeys = names.filter((name2) => columns[name2]?.primary_key);
782
+ if (primaryKeys.length !== 1) {
783
+ ctx.addIssue({ code: "custom", message: "exactly one column must be primary_key", path: ["columns"] });
784
+ }
785
+ validateRenames(columns, names, ctx);
786
+ for (const name2 of names) {
787
+ const column = columns[name2];
788
+ if (!column) continue;
789
+ const cast = column.cast ? castParts(column.cast) : null;
790
+ if (column.cast && cast?.column !== name2) {
791
+ ctx.addIssue({
792
+ code: "custom",
793
+ message: "cast must reference the column it is declared on",
794
+ path: ["columns", name2, "cast"]
795
+ });
796
+ }
797
+ if (cast && cast.type !== column.type) {
798
+ ctx.addIssue({
799
+ code: "custom",
800
+ message: `cast target '${cast.type}' must match the declared type '${column.type}'`,
801
+ path: ["columns", name2, "cast"]
802
+ });
803
+ }
804
+ }
805
+ }
806
+ function validateRenames(columns, names, ctx) {
807
+ const renameSources = /* @__PURE__ */ new Map();
808
+ for (const name2 of names) {
809
+ const renamedFrom = columns[name2]?.renamed_from;
810
+ if (!renamedFrom) continue;
811
+ if (renamedFrom === name2 || names.includes(renamedFrom)) {
812
+ ctx.addIssue({
813
+ code: "custom",
814
+ message: "renamed_from must name a column that no longer exists",
815
+ path: ["columns", name2, "renamed_from"]
816
+ });
817
+ }
818
+ const claimed = renameSources.get(renamedFrom);
819
+ if (claimed) {
820
+ ctx.addIssue({
821
+ code: "custom",
822
+ message: `renamed_from '${renamedFrom}' is already claimed by '${claimed}'`,
823
+ path: ["columns", name2, "renamed_from"]
824
+ });
825
+ }
826
+ renameSources.set(renamedFrom, name2);
827
+ }
828
+ }
829
+ function validateColumnTable(table, ctx) {
830
+ const columns = table.columns ?? {};
831
+ const names = Object.keys(columns);
832
+ if (names.length === 0) {
833
+ ctx.addIssue({ code: "custom", message: "a table must declare columns or a source", path: ["columns"] });
834
+ return;
835
+ }
836
+ const primaryKeys = names.filter((name2) => columns[name2]?.primary_key);
837
+ if (primaryKeys.length !== 1) {
838
+ ctx.addIssue({ code: "custom", message: "exactly one column must be primary_key", path: ["columns"] });
839
+ }
840
+ validateRenames(columns, names, ctx);
841
+ for (const name2 of names) {
842
+ const column = columns[name2];
843
+ if (!column) continue;
844
+ const cast = column.cast ? castParts(column.cast) : null;
845
+ if (column.cast && cast?.column !== name2) {
846
+ ctx.addIssue({
847
+ code: "custom",
848
+ message: "cast must reference the column it is declared on",
849
+ path: ["columns", name2, "cast"]
850
+ });
851
+ }
852
+ if (cast && cast.type !== column.type) {
853
+ ctx.addIssue({
854
+ code: "custom",
855
+ message: `cast target '${cast.type}' must match the declared type '${column.type}'`,
856
+ path: ["columns", name2, "cast"]
857
+ });
858
+ }
859
+ }
860
+ for (const [position, index] of (table.indexes ?? []).entries()) {
861
+ if (new Set(index.columns).size !== index.columns.length) {
862
+ ctx.addIssue({
863
+ code: "custom",
864
+ message: "index names a column more than once",
865
+ path: ["indexes", position]
866
+ });
867
+ }
868
+ for (const column of index.columns) {
869
+ if (!names.includes(column)) {
870
+ ctx.addIssue({
871
+ code: "custom",
872
+ message: `index references unknown column '${column}'`,
873
+ path: ["indexes", position]
874
+ });
875
+ }
876
+ }
877
+ }
878
+ }
879
+ function validateLinks(table, ctx) {
880
+ const columnNames = Object.keys(table.columns ?? {});
881
+ for (const [name2, link] of Object.entries(table.links ?? {})) {
882
+ if (columnNames.length > 0 && !columnNames.includes(link.on)) {
883
+ ctx.addIssue({ code: "custom", message: `link references unknown column '${link.on}'`, path: ["links", name2, "on"] });
884
+ }
885
+ if (link.enforce && link.to.includes(".")) {
886
+ ctx.addIssue({
887
+ code: "custom",
888
+ message: "enforce is only valid for links inside the namespace; cross-boundary links stay soft",
889
+ path: ["links", name2, "enforce"]
890
+ });
891
+ }
892
+ if (link.enforce && table.source) {
893
+ ctx.addIssue({
894
+ code: "custom",
895
+ message: "a synced table cannot enforce foreign keys \u2014 its DDL is owned by the sync",
896
+ path: ["links", name2, "enforce"]
897
+ });
898
+ }
899
+ }
900
+ }
901
+ function validateDerivedNameLengths(table, ctx) {
902
+ const worstPolicy = policyName(table.table, "insert");
903
+ if (!table.source && worstPolicy.length > MAX_IDENTIFIER_LENGTH) {
904
+ ctx.addIssue({
905
+ code: "custom",
906
+ message: `table name is too long \u2014 the derived policy name '${worstPolicy}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit`,
907
+ path: ["table"]
908
+ });
909
+ }
910
+ for (const [position, index] of (table.indexes ?? []).entries()) {
911
+ const name2 = indexName(table.table, index);
912
+ if (name2.length > MAX_IDENTIFIER_LENGTH) {
913
+ ctx.addIssue({
914
+ code: "custom",
915
+ message: `derived index name '${name2}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 set a shorter explicit name`,
916
+ path: ["indexes", position]
917
+ });
918
+ }
919
+ }
920
+ for (const [name2, link] of Object.entries(table.links ?? {})) {
921
+ const constraint = foreignKeyName(table.table, name2);
922
+ if (link.enforce && constraint.length > MAX_IDENTIFIER_LENGTH) {
923
+ ctx.addIssue({
924
+ code: "custom",
925
+ message: `derived foreign-key name '${constraint}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 shorten the link or table name`,
926
+ path: ["links", name2]
927
+ });
928
+ }
929
+ }
930
+ for (const name2 of Object.keys(table.actions ?? {})) {
931
+ const fnName = `action_${name2}`;
932
+ if (fnName.length > MAX_IDENTIFIER_LENGTH) {
933
+ ctx.addIssue({
934
+ code: "custom",
935
+ message: `derived action function name '${fnName}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 shorten the action name`,
936
+ path: ["actions", name2]
937
+ });
938
+ }
939
+ }
940
+ }
941
+ var TYPE_GROUPS = {
942
+ integer: "numeric",
943
+ bigint: "numeric",
944
+ numeric: "numeric",
945
+ double: "numeric",
946
+ text: "text",
947
+ uuid: "uuid",
948
+ date: "temporal",
949
+ timestamptz: "temporal",
950
+ boolean: "boolean",
951
+ jsonb: "jsonb"
952
+ };
953
+ function validatePolicyColumns(table, ctx) {
954
+ const columns = table.columns ?? {};
955
+ const columnNames = Object.keys(columns);
956
+ if (columnNames.length === 0) return;
957
+ for (const kind of ["read", "write", "insert", "update"]) {
958
+ const predicate = table.policies?.[kind];
959
+ if (!predicate) continue;
960
+ for (const column of referencedColumns(predicate.ast)) {
961
+ if (!columnNames.includes(column)) {
962
+ ctx.addIssue({
963
+ code: "custom",
964
+ message: `policy references unknown column '${column}'`,
965
+ path: ["policies", kind]
966
+ });
967
+ }
968
+ }
969
+ walkPredicate(predicate.ast, (node) => {
970
+ if (node.kind !== "compare") return;
971
+ const issue = comparisonTypeIssue(node.left, node.right, columns);
972
+ if (issue) ctx.addIssue({ code: "custom", message: issue, path: ["policies", kind] });
973
+ });
974
+ }
975
+ }
976
+ function comparisonTypeIssue(left, right, columns) {
977
+ const groupOf = (operand) => operand.kind === "column" ? columns[operand.name] ? TYPE_GROUPS[columns[operand.name].type] : null : null;
978
+ if (left.kind === "column" && right.kind === "column") {
979
+ const leftGroup = groupOf(left);
980
+ const rightGroup = groupOf(right);
981
+ if (leftGroup && rightGroup && leftGroup !== rightGroup) {
982
+ return `policy compares '${left.name}' (${columns[left.name].type}) with '${right.name}' (${columns[right.name].type}) \u2014 incomparable types`;
983
+ }
984
+ return null;
985
+ }
986
+ const column = left.kind === "column" ? left : right.kind === "column" ? right : null;
987
+ const literal = left.kind === "literal" ? left : right.kind === "literal" ? right : null;
988
+ if (!column || !literal || literal.value === null) return null;
989
+ const columnGroup = groupOf(column);
990
+ if (!columnGroup) return null;
991
+ if (typeof literal.value === "string") return null;
992
+ const literalGroup = typeof literal.value === "number" ? "numeric" : "boolean";
993
+ if (literalGroup !== columnGroup) {
994
+ return `policy compares '${column.name}' (${columns[column.name].type}) with a ${typeof literal.value} literal \u2014 incomparable types`;
995
+ }
996
+ return null;
997
+ }
251
998
  var policyVariantSchema = z.object({
252
999
  /** Claims-only guard (capability()/role()/claim() comparisons — no columns). */
253
1000
  when: z.string().min(1).max(1e3),
@@ -564,7 +1311,7 @@ var CANONICAL_FORMAT = 2;
564
1311
  var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
565
1312
  var identifier2 = z.string().min(1).max(63).regex(IDENTIFIER_RE);
566
1313
  var columnIdentifier = z.string().min(1).max(63).regex(COLUMN_IDENTIFIER_PATTERN);
567
- var COLUMN_TYPES = [
1314
+ var COLUMN_TYPES2 = [
568
1315
  "uuid",
569
1316
  "text",
570
1317
  "integer",
@@ -576,7 +1323,7 @@ var COLUMN_TYPES = [
576
1323
  "date",
577
1324
  "jsonb"
578
1325
  ];
579
- var columnTypeSchema = z.enum(COLUMN_TYPES);
1326
+ var columnTypeSchema2 = z.enum(COLUMN_TYPES2);
580
1327
  var canonicalEnumSchema = z.object({
581
1328
  name: identifier2,
582
1329
  values: z.array(z.string().min(1).max(63)).min(1).max(100)
@@ -585,7 +1332,7 @@ var canonicalColumnSchema = z.object({
585
1332
  /** Property name in TS (camelCase); columnName is the Postgres name. */
586
1333
  name: z.string().min(1).max(63),
587
1334
  columnName: columnIdentifier,
588
- type: z.union([columnTypeSchema, z.object({ enum: identifier2 })]),
1335
+ type: z.union([columnTypeSchema2, z.object({ enum: identifier2 })]),
589
1336
  nullable: z.boolean().default(true),
590
1337
  unique: z.boolean().default(false),
591
1338
  /** Literal default, or one of the allowlisted SQL functions. */
@@ -835,7 +1582,7 @@ var canonicalActionSchema = z.object({
835
1582
  name: identifier2,
836
1583
  requires: z.string().max(1e3).optional(),
837
1584
  params: z.record(z.string(), z.object({
838
- type: columnTypeSchema,
1585
+ type: columnTypeSchema2,
839
1586
  nullable: z.boolean().default(false)
840
1587
  })).default({}),
841
1588
  executes: z.string().min(1).max(1e4),
@@ -936,7 +1683,8 @@ var canonicalTableSchema = z.object({
936
1683
  if ([table.policies.write, table.policies.insert, table.policies.update].some((policy) => policy !== void 0)) {
937
1684
  fail4("is read-only and cannot declare write policies", ["policies"]);
938
1685
  }
939
- const ownedCollections = [table.indexes, table.actions, table.checks, table.foreignKeys, table.cowMutations];
1686
+ const ownedIndexes = table.source === void 0 ? table.indexes : table.indexes.filter((index) => index.unique);
1687
+ const ownedCollections = [ownedIndexes, table.actions, table.checks, table.foreignKeys, table.cowMutations];
940
1688
  const ownedMetadata = [table.cellMetadata, table.internal, table.materializes, table.renamedFrom];
941
1689
  if (ownedCollections.some((items) => items !== void 0 && items.length > 0) || ownedMetadata.some((value) => value !== void 0)) {
942
1690
  fail4("cannot declare owned storage, constraints or identity operations");
@@ -1038,6 +1786,23 @@ function trackedCellColumns(table) {
1038
1786
  return table.columns.filter((column) => column.cell === void 0 && !pk.has(column.columnName) && column.onUpdateFn === void 0);
1039
1787
  }
1040
1788
  var SUFFIXES = Object.values(CELL_PART_SUFFIX);
1789
+ function validateSyncedIndexes(table, tableIndex, ctx) {
1790
+ if (table.source === void 0) return;
1791
+ const columns = new Set(table.columns.map((column) => column.columnName));
1792
+ for (const [position, index] of table.indexes.entries()) {
1793
+ const fail4 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "indexes", position], message: `synced table '${table.table}' ${message}` });
1794
+ if (columns.size === 0) fail4("must declare columns to declare indexes");
1795
+ if (index.unique) fail4("cannot declare a unique index");
1796
+ if (new Set(index.columns).size !== index.columns.length) fail4("index names a column more than once");
1797
+ for (const column of index.columns) {
1798
+ if (columns.size > 0 && !columns.has(column)) fail4(`index references unknown column '${column}'`);
1799
+ }
1800
+ const name2 = indexName(table.table, index);
1801
+ if (name2.length > MAX_IDENTIFIER_LENGTH) {
1802
+ fail4(`derived index name '${name2}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 set a shorter explicit name`);
1803
+ }
1804
+ }
1805
+ }
1041
1806
  function validateCellMetadata(table, index, ctx) {
1042
1807
  const fail4 = (message, path = ["tables", index]) => ctx.addIssue({ code: "custom", path, message });
1043
1808
  const cells = table.columns.flatMap((column, position) => column.cell ? [{ column, position, cell: column.cell }] : []);
@@ -1104,12 +1869,24 @@ var localDefinitionSchema = z.object({
1104
1869
  }).default({ uses: [] })
1105
1870
  }).superRefine((definition, ctx) => {
1106
1871
  const seen = /* @__PURE__ */ new Set();
1872
+ const syncedIndexOwners = /* @__PURE__ */ new Map();
1107
1873
  for (const [index, table] of definition.tables.entries()) {
1108
1874
  if (seen.has(table.table)) {
1109
1875
  ctx.addIssue({ code: "custom", path: ["tables", index, "table"], message: `duplicate relation '${table.table}'` });
1110
1876
  }
1111
1877
  seen.add(table.table);
1112
1878
  validateCellMetadata(table, index, ctx);
1879
+ validateSyncedIndexes(table, index, ctx);
1880
+ if (table.source !== void 0) {
1881
+ for (const [position, entry] of table.indexes.entries()) {
1882
+ const name2 = indexName(table.table, entry);
1883
+ const owner = syncedIndexOwners.get(name2);
1884
+ if (owner !== void 0) {
1885
+ ctx.addIssue({ code: "custom", path: ["tables", index, "indexes", position], message: `synced index '${name2}' is also declared on '${owner}'` });
1886
+ }
1887
+ syncedIndexOwners.set(name2, table.table);
1888
+ }
1889
+ }
1113
1890
  if (table.source !== void 0) {
1114
1891
  for (const [columnIndex, column] of table.columns.entries()) {
1115
1892
  if (column.onUpdateFn !== void 0) {
@@ -1651,7 +2428,7 @@ var variableDefaultIrSchema = z.object({
1651
2428
  var variableIrSchema = z.object({
1652
2429
  name: z.string(),
1653
2430
  type: z.union([
1654
- z.object({ kind: z.literal("scalar"), scalar: z.enum(COLUMN_TYPES) }),
2431
+ z.object({ kind: z.literal("scalar"), scalar: z.enum(COLUMN_TYPES2) }),
1655
2432
  /** `values` is the declared membership whitelist — the executor validates against it. */
1656
2433
  z.object({ kind: z.literal("enum"), name: z.string(), values: z.array(z.string()).min(1) }),
1657
2434
  /** §4f dynamic slot: validated against `tables[table]` whitelists at runtime. */
@@ -1663,19 +2440,20 @@ var variableIrSchema = z.object({
1663
2440
  defaultValue: variableDefaultIrSchema.optional()
1664
2441
  });
1665
2442
  var sqlColumnSchema = z.object({ name: z.string(), type: z.enum(["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz", "jsonb"]), nullable: z.boolean() });
2443
+ var sqlObjectIrSchema = z.object({
2444
+ ref: z.string(),
2445
+ table: z.string(),
2446
+ columns: z.array(sqlColumnSchema.extend({ type: z.string() })).min(1),
2447
+ scan: z.string().min(1),
2448
+ read: z.array(z.string()).min(1)
2449
+ }).strict();
1666
2450
  var compiledOperationSchema = z.object({
1667
2451
  language: z.literal("sql").optional(),
1668
2452
  /** Server-compiled object catalog and permission ceiling, never caller SQL. */
1669
2453
  sqlQuery: z.object({
1670
2454
  catalog: z.object({
1671
2455
  identity: z.string(),
1672
- objects: z.array(z.object({
1673
- ref: z.string(),
1674
- table: z.string(),
1675
- columns: z.array(sqlColumnSchema.extend({ type: z.string() })).min(1),
1676
- scan: z.string().min(1),
1677
- read: z.array(z.string()).min(1)
1678
- }).strict()).min(1).max(256)
2456
+ objects: z.array(sqlObjectIrSchema).min(1).max(256)
1679
2457
  }).strict(),
1680
2458
  read: z.array(z.string()).min(1).max(256),
1681
2459
  maxRows: z.number().int().min(1).max(1e4)
@@ -1783,7 +2561,7 @@ var tableMetaIrSchema = z.object({
1783
2561
  columns: z.array(z.object({
1784
2562
  name: z.string(),
1785
2563
  columnName: z.string(),
1786
- type: z.union([z.enum(COLUMN_TYPES), z.object({ enum: z.string(), values: z.array(z.string()) })]),
2564
+ type: z.union([z.enum(COLUMN_TYPES2), z.object({ enum: z.string(), values: z.array(z.string()) })]),
1787
2565
  nullable: z.boolean()
1788
2566
  })),
1789
2567
  /** Empty for keyless read-only views; keyed tables and views list logical columns. */
@@ -1920,7 +2698,7 @@ function graphqlScalarFor(type) {
1920
2698
  }
1921
2699
  var COLUMN_TYPE_BY_SCALAR = new Map([
1922
2700
  ["ID", "text"],
1923
- ...COLUMN_TYPES.map((type) => [graphqlScalarFor(type), type])
2701
+ ...COLUMN_TYPES2.map((type) => [graphqlScalarFor(type), type])
1924
2702
  ]);
1925
2703
  function columnTypeForScalar(scalarName) {
1926
2704
  return COLUMN_TYPE_BY_SCALAR.get(scalarName);
@@ -2021,14 +2799,14 @@ var PolicyCompileError = class extends Error {
2021
2799
  };
2022
2800
 
2023
2801
  // src/predicate-v2/parse.ts
2024
- var KEYWORDS = /* @__PURE__ */ new Set(["and", "or", "not", "true", "false", "null", "claim", "capability", "role", "exists"]);
2025
- var OPERATOR_PATTERN = /^(!=|<=|>=|<>|=|<|>)/;
2026
- var IDENTIFIER_START = /[a-zA-Z_]/;
2027
- var IDENTIFIER_CHAR = /[a-zA-Z0-9_]/;
2028
- var NUMBER_PATTERN = /^-?\d+(\.\d+)?/;
2802
+ var KEYWORDS2 = /* @__PURE__ */ new Set(["and", "or", "not", "true", "false", "null", "claim", "capability", "role", "exists"]);
2803
+ var OPERATOR_PATTERN2 = /^(!=|<=|>=|<>|=|<|>)/;
2804
+ var IDENTIFIER_START2 = /[a-zA-Z_]/;
2805
+ var IDENTIFIER_CHAR2 = /[a-zA-Z0-9_]/;
2806
+ var NUMBER_PATTERN2 = /^-?\d+(\.\d+)?/;
2029
2807
  var SCALAR_CLAIM_KEYS = Object.entries(CLAIMS_CONTRACT).filter(([, type]) => !type.endsWith("[]")).map(([key]) => key);
2030
2808
  var ARRAY_CLAIM_KEYS = Object.entries(CLAIMS_CONTRACT).filter(([, type]) => type.endsWith("[]")).map(([key]) => key);
2031
- function tokenize2(source) {
2809
+ function tokenize3(source) {
2032
2810
  const tokens = [];
2033
2811
  let position = 0;
2034
2812
  while (position < source.length) {
@@ -2058,27 +2836,27 @@ function tokenize2(source) {
2058
2836
  continue;
2059
2837
  }
2060
2838
  if (char === "'") {
2061
- const { value, end } = readString(source, position);
2839
+ const { value, end } = readString2(source, position);
2062
2840
  tokens.push({ kind: "string", value, position });
2063
2841
  position = end;
2064
2842
  continue;
2065
2843
  }
2066
- const operator = OPERATOR_PATTERN.exec(source.slice(position));
2844
+ const operator = OPERATOR_PATTERN2.exec(source.slice(position));
2067
2845
  if (operator) {
2068
2846
  const value = operator[0] === "<>" ? "!=" : operator[0];
2069
2847
  tokens.push({ kind: "operator", value, position });
2070
2848
  position += operator[0].length;
2071
2849
  continue;
2072
2850
  }
2073
- const number = NUMBER_PATTERN.exec(source.slice(position));
2074
- if (number && !IDENTIFIER_START.test(char)) {
2851
+ const number = NUMBER_PATTERN2.exec(source.slice(position));
2852
+ if (number && !IDENTIFIER_START2.test(char)) {
2075
2853
  tokens.push({ kind: "number", value: number[0], position });
2076
2854
  position += number[0].length;
2077
2855
  continue;
2078
2856
  }
2079
- if (IDENTIFIER_START.test(char)) {
2857
+ if (IDENTIFIER_START2.test(char)) {
2080
2858
  let end = position;
2081
- while (end < source.length && IDENTIFIER_CHAR.test(source[end])) end += 1;
2859
+ while (end < source.length && IDENTIFIER_CHAR2.test(source[end])) end += 1;
2082
2860
  tokens.push({ kind: "identifier", value: source.slice(position, end), position });
2083
2861
  position = end;
2084
2862
  continue;
@@ -2088,7 +2866,7 @@ function tokenize2(source) {
2088
2866
  tokens.push({ kind: "end", value: "", position: source.length });
2089
2867
  return tokens;
2090
2868
  }
2091
- function readString(source, start) {
2869
+ function readString2(source, start) {
2092
2870
  let value = "";
2093
2871
  let position = start + 1;
2094
2872
  while (position < source.length) {
@@ -2107,7 +2885,7 @@ function readString(source, start) {
2107
2885
  }
2108
2886
  throw new PolicyParseError("unterminated string literal", start);
2109
2887
  }
2110
- var Parser2 = class {
2888
+ var Parser3 = class {
2111
2889
  constructor(tokens) {
2112
2890
  this.tokens = tokens;
2113
2891
  }
@@ -2249,7 +3027,7 @@ var Parser2 = class {
2249
3027
  /** `ident(.ident)*` — first segment must not be a keyword; later segments may be (e.g. `author.role`). */
2250
3028
  parsePath(description) {
2251
3029
  const first = this.peek();
2252
- if (first.kind !== "identifier" || KEYWORDS.has(first.value)) {
3030
+ if (first.kind !== "identifier" || KEYWORDS2.has(first.value)) {
2253
3031
  throw new PolicyParseError(`expected ${description}, got '${first.value || "end"}'`, first.position);
2254
3032
  }
2255
3033
  this.advance();
@@ -2295,7 +3073,7 @@ var Parser2 = class {
2295
3073
  function parsePolicy(source) {
2296
3074
  const trimmed = source.trim();
2297
3075
  if (trimmed.length === 0) throw new PolicyParseError("empty policy expression", 0);
2298
- return { source: trimmed, ast: new Parser2(tokenize2(trimmed)).parse() };
3076
+ return { source: trimmed, ast: new Parser3(tokenize3(trimmed)).parse() };
2299
3077
  }
2300
3078
 
2301
3079
  // src/predicate-v2/compile.ts
@@ -2335,6 +3113,7 @@ var FLIPPED = {
2335
3113
  ">=": "<="
2336
3114
  };
2337
3115
  function resolveTable(resolver, table) {
3116
+ resolver.onTable?.(table);
2338
3117
  const info = resolver.tables[table];
2339
3118
  if (!info) {
2340
3119
  throw new PolicyCompileError(`unknown table '${table}'`, "unknown-table", Object.keys(resolver.tables));
@@ -2979,15 +3758,6 @@ function classifyInputType(name2, tableByTypename) {
2979
3758
  return void 0;
2980
3759
  }
2981
3760
 
2982
- // src/errors.ts
2983
- var OrmDefinitionError = class extends Error {
2984
- constructor(message, file) {
2985
- super(file ? `${file}: ${message}` : message);
2986
- this.file = file;
2987
- }
2988
- name = "OrmDefinitionError";
2989
- };
2990
-
2991
3761
  // src/canonical/composition.ts
2992
3762
  function localContract(definition) {
2993
3763
  return normalizeCanonical(localDefinitionSchema.parse(definition));
@@ -3558,12 +4328,12 @@ function computeOpHash({
3558
4328
  definition,
3559
4329
  schemaHash
3560
4330
  }) {
3561
- const digest = createHash("sha256").update(OP_HASH_DOMAIN).update(`${COMPILER_MAJOR}\0${schemaHash}\0`).update(canonicalOperationText(definition)).digest("hex");
3562
- return `sha256:${digest}`;
4331
+ const digest2 = createHash("sha256").update(OP_HASH_DOMAIN).update(`${COMPILER_MAJOR}\0${schemaHash}\0`).update(canonicalOperationText(definition)).digest("hex");
4332
+ return `sha256:${digest2}`;
3563
4333
  }
3564
4334
  function computeOpsHash(opHashes) {
3565
- const digest = createHash("sha256").update(OPS_HASH_DOMAIN).update([...opHashes].sort().join("\n")).digest("hex");
3566
- return `sha256:${digest}`;
4335
+ const digest2 = createHash("sha256").update(OPS_HASH_DOMAIN).update([...opHashes].sort().join("\n")).digest("hex");
4336
+ return `sha256:${digest2}`;
3567
4337
  }
3568
4338
  var SOURCE_DIRECTIVES = /* @__PURE__ */ new Set(["prefetch", "writeback", "localOnly"]);
3569
4339
  var fail = (message, code) => {
@@ -5018,8 +5788,8 @@ function restrictOperation({
5018
5788
  // src/compiler-gql/artifact.ts
5019
5789
  var SCHEMA_HASH_DOMAIN = "sequence-orm-canonical-v2\0";
5020
5790
  function computeSchemaHash(canonical) {
5021
- const digest = createHash("sha256").update(SCHEMA_HASH_DOMAIN).update(stableJson(canonical)).digest("hex");
5022
- return `sha256:${digest}`;
5791
+ const digest2 = createHash("sha256").update(SCHEMA_HASH_DOMAIN).update(stableJson(canonical)).digest("hex");
5792
+ return `sha256:${digest2}`;
5023
5793
  }
5024
5794
  function compileNamespace({ canonical, sdl, documents }) {
5025
5795
  const { ctx, diagnostics } = buildCompileContext(canonical);
@@ -6473,10 +7243,13 @@ function sqlHash(value) {
6473
7243
  return `sha256:${createHash("sha256").update(value).digest("hex")}`;
6474
7244
  }
6475
7245
  function createSqlCatalog({ canonical, publishedObjects, executionTarget }) {
6476
- if (!publishedObjects.length || new Set(publishedObjects).size !== publishedObjects.length) throw new Error("SQL objects must be explicitly declared and unique");
6477
7246
  const { ctx, diagnostics } = buildCompileContext(canonical);
6478
7247
  if (diagnostics.some((d) => d.severity === "error")) throw new Error(diagnostics.map((d) => d.message).join("; "));
6479
- const identity = sqlHash(`ontology-sql-v1\0${computeSchemaHash(canonical)}\0${executionTarget}`);
7248
+ return createSqlCatalogFromContext({ canonical, publishedObjects, executionTarget, ctx });
7249
+ }
7250
+ function createSqlCatalogFromContext({ canonical, publishedObjects, executionTarget, ctx, identity: suppliedIdentity }) {
7251
+ if (!publishedObjects.length || new Set(publishedObjects).size !== publishedObjects.length) throw new Error("SQL objects must be explicitly declared and unique");
7252
+ const identity = suppliedIdentity ?? sqlHash(`ontology-sql-v1\0${computeSchemaHash(canonical)}\0${executionTarget}`);
6480
7253
  const logicalNames = new Map(Object.entries(ctx.tables).map(([key, meta]) => [meta, key]));
6481
7254
  const refOf = (meta) => `${meta.namespace ?? canonical.namespace}/${logicalNames.get(meta)}`;
6482
7255
  const definitions = [canonical, ...canonical.imports ?? []];
@@ -6613,9 +7386,10 @@ var SqlEmitter = class {
6613
7386
  only(cte, ["ctename", "ctequery", "ctematerialized", "location"]);
6614
7387
  const name2 = str(cte.ctename);
6615
7388
  if (scope.ctes.has(name2)) fail3("duplicate CTE");
7389
+ const materialization = cte.ctematerialized === "CTEMaterializeAlways" ? "MATERIALIZED " : cte.ctematerialized === "CTEMaterializeNever" ? "NOT MATERIALIZED " : cte.ctematerialized === "CTEMaterializeDefault" ? "" : fail3("unsupported CTE materialization");
6616
7390
  const result2 = this.select(obj(cte.ctequery), { ...parent, ctes: scope.ctes });
6617
7391
  scope.ctes.set(name2, result2);
6618
- ctes.push(`${quoteIdentifier(name2)} AS (${result2.text})`);
7392
+ ctes.push(`${quoteIdentifier(name2)} AS ${materialization}(${result2.text})`);
6619
7393
  }
6620
7394
  }
6621
7395
  const result = node.op === "SETOP_UNION" ? this.union(node, scope) : this.projection(node, scope);
@@ -7221,7 +7995,7 @@ function compileBuiltinSqlArtifact({ namespace, definitions, read, unbackedObjec
7221
7995
  if (owner !== namespace && selected.some((identity) => !definition.exports?.[identity.slice(owner.length + 1)]?.read)) {
7222
7996
  throw new Error("SQL object is not exported");
7223
7997
  }
7224
- objects.push(...createSqlCatalog({ canonical: definition, publishedObjects: selected, executionTarget }).objects);
7998
+ objects.push(...createSqlCatalogFromContext({ canonical: definition, publishedObjects: selected, executionTarget, ctx }).objects);
7225
7999
  }
7226
8000
  if (objects.length !== read.length) throw new Error("SQL object is unavailable");
7227
8001
  const actualRead = [...new Set(objects.flatMap((object) => object.read))].sort();
@@ -7259,6 +8033,177 @@ function compileBuiltinSqlArtifact({ namespace, definitions, read, unbackedObjec
7259
8033
  } }
7260
8034
  });
7261
8035
  }
8036
+ var OBJECT_METADATA_FORMAT = 1;
8037
+ var digest = z.string().regex(/^sha256:[a-f0-9]{64}$/u);
8038
+ var objectSqlBundleSchema = z.object({
8039
+ format: z.literal(OBJECT_METADATA_FORMAT),
8040
+ dependencies: z.record(z.string(), digest),
8041
+ object: sqlObjectIrSchema,
8042
+ tables: z.record(z.string(), tableMetaIrSchema),
8043
+ readPolicies: z.record(z.string(), predIrSchema)
8044
+ }).strict();
8045
+ function contractHashes(canonical) {
8046
+ const hashes = /* @__PURE__ */ new Map();
8047
+ for (const owner of [canonical, ...canonical.imports ?? []]) {
8048
+ for (const relation of [...owner.tables, ...owner.views ?? []]) {
8049
+ const name2 = "table" in relation ? relation.table : relation.view;
8050
+ hashes.set(`${owner.namespace}/${name2}`, sqlHash(stableJson([
8051
+ "orm-object-contract-v1",
8052
+ owner.namespace,
8053
+ relation,
8054
+ owner.enums,
8055
+ owner.exports?.[name2]
8056
+ ])));
8057
+ }
8058
+ }
8059
+ return hashes;
8060
+ }
8061
+ function objectClosure({ roots, tables, policies, sourceDependencies }) {
8062
+ const required = /* @__PURE__ */ new Set(), links = /* @__PURE__ */ new Map();
8063
+ const walk = (pred, table) => {
8064
+ if (!pred) return;
8065
+ if (pred.kind === "and" || pred.kind === "or") for (const operand of pred.operands) walk(operand, table);
8066
+ else if (pred.kind === "not") walk(pred.operand, table);
8067
+ else if (pred.kind === "exists" || pred.kind === "aggPredicate") {
8068
+ const link = tables[table]?.links.find((candidate) => candidate.name === pred.link);
8069
+ if (!link) throw new Error("object policy dependency is unavailable");
8070
+ const used = links.get(table) ?? /* @__PURE__ */ new Set();
8071
+ used.add(link.name);
8072
+ links.set(table, used);
8073
+ visit2(link.target);
8074
+ walk(pred.where, link.target);
8075
+ }
8076
+ };
8077
+ const visit2 = (ref) => {
8078
+ if (required.has(ref)) return;
8079
+ const meta = tables[ref];
8080
+ if (!meta) throw new Error("object dependency is unavailable");
8081
+ required.add(ref);
8082
+ if (required.size > 1024) throw new Error("object dependency budget exceeded");
8083
+ walk(policies[ref], ref);
8084
+ for (const dependency of sourceDependencies.get(ref) ?? []) visit2(dependency);
8085
+ for (const backing of meta.dependencies ?? []) visit2(backing);
8086
+ };
8087
+ for (const root of roots) visit2(root);
8088
+ return { required: [...required].sort(), links };
8089
+ }
8090
+ async function compileObjectMetadata({ canonical: raw }) {
8091
+ const canonical = normalizeCanonical(canonicalDefinitionSchema.parse(raw));
8092
+ await validateManagedViewQueries({ canonical });
8093
+ const context = buildCompileContext(canonical);
8094
+ if (context.diagnostics.some((item) => item.severity === "error")) throw new Error("invalid object metadata definition");
8095
+ const { ctx } = context;
8096
+ const hashes = contractHashes(canonical);
8097
+ const ref = (key) => {
8098
+ const meta = ctx.tables[key];
8099
+ if (!meta) throw new Error("object dependency is unavailable");
8100
+ return `${meta.namespace ?? canonical.namespace}/${key}`;
8101
+ };
8102
+ const tables = {}, policies = {};
8103
+ const sourceDependencies = /* @__PURE__ */ new Map();
8104
+ const relations = [canonical, ...canonical.imports ?? []].flatMap(canonicalRelations);
8105
+ const expressions = new Map(relations.map((relation) => [relation.table, relation.policies.read]));
8106
+ for (const [key, meta] of Object.entries(ctx.tables)) {
8107
+ tables[ref(key)] = {
8108
+ ...meta,
8109
+ namespace: meta.namespace ?? canonical.namespace,
8110
+ links: meta.links.map((link) => ({ ...link, target: ref(link.target) })),
8111
+ ...meta.dependencies ? { dependencies: meta.dependencies.map(ref) } : {},
8112
+ ...meta.foreignReferences ? { foreignReferences: meta.foreignReferences.map((link) => ({ ...link, target: ref(link.target) })) } : {}
8113
+ };
8114
+ const policy = ctx.readPolicies.get(key);
8115
+ if (policy) policies[ref(key)] = policy;
8116
+ const expression = expressions.get(key);
8117
+ if (expression !== void 0) {
8118
+ const consulted = /* @__PURE__ */ new Set();
8119
+ compilePolicy({ expression, table: key, access: "read", resolver: {
8120
+ tables: ctx.tables,
8121
+ onTable: (table) => {
8122
+ consulted.add(ref(table));
8123
+ },
8124
+ readPolicy: (table) => expressions.get(table),
8125
+ policyNamespace: (table) => ctx.tables[table]?.namespace
8126
+ } });
8127
+ sourceDependencies.set(ref(key), [...consulted]);
8128
+ }
8129
+ }
8130
+ const objects = [];
8131
+ for (const relation of [...canonical.tables, ...canonical.views ?? []]) {
8132
+ const name2 = "table" in relation ? relation.table : relation.view;
8133
+ const identity = `${canonical.namespace}/${name2}`, contractHash = hashes.get(identity);
8134
+ let bundle = null;
8135
+ if (!relation.internal) {
8136
+ const object = createSqlCatalogFromContext({
8137
+ canonical,
8138
+ ctx,
8139
+ identity: contractHash,
8140
+ publishedObjects: [identity],
8141
+ executionTarget: "default"
8142
+ }).objects[0];
8143
+ const closure = objectClosure({ roots: object.read, tables, policies, sourceDependencies });
8144
+ bundle = objectSqlBundleSchema.parse({
8145
+ format: OBJECT_METADATA_FORMAT,
8146
+ object,
8147
+ // platformUsers is compiler-owned metadata, pinned by the format above.
8148
+ dependencies: Object.fromEntries(closure.required.filter((key) => hashes.has(key)).map((key) => [key, hashes.get(key)])),
8149
+ tables: Object.fromEntries(closure.required.map((key) => {
8150
+ const { foreignReferences: _writeReferences, ...meta } = tables[key];
8151
+ return [key, { ...meta, links: meta.links.filter((link) => closure.links.get(key)?.has(link.name)) }];
8152
+ })),
8153
+ readPolicies: Object.fromEntries(closure.required.filter((key) => policies[key]).map((key) => [key, policies[key]]))
8154
+ });
8155
+ }
8156
+ objects.push({ name: name2, contractHash, bundle });
8157
+ }
8158
+ return {
8159
+ format: OBJECT_METADATA_FORMAT,
8160
+ objects,
8161
+ relations: Object.values(ctx.tables).map(({ schema, table }) => ({ schema, table }))
8162
+ };
8163
+ }
8164
+ function composeObjectSqlArtifact({ namespace, bundles }) {
8165
+ const tables = {}, readPolicies = {};
8166
+ const pins = /* @__PURE__ */ new Map();
8167
+ const objects = bundles.map((bundle) => {
8168
+ if (!bundle.object.ref.startsWith(`${namespace}/`) && !bundle.tables[bundle.object.ref]?.exportRead) throw new Error("SQL object is not exported");
8169
+ for (const [ref, hash] of Object.entries(bundle.dependencies)) {
8170
+ if (pins.has(ref) && pins.get(ref) !== hash) throw new Error("inconsistent object contracts");
8171
+ pins.set(ref, hash);
8172
+ }
8173
+ for (const [ref, meta] of Object.entries(bundle.tables)) {
8174
+ const previous = tables[ref];
8175
+ const links = new Map([...previous?.links ?? [], ...meta.links].map((link) => [link.name, link]));
8176
+ tables[ref] = { ...meta, links: [...links.values()] };
8177
+ }
8178
+ Object.assign(readPolicies, bundle.readPolicies);
8179
+ return bundle.object;
8180
+ });
8181
+ const read = [...new Set(objects.flatMap((object) => object.read))].sort();
8182
+ const identity = sqlHash(stableJson(["orm-object-sql-v1", namespace, bundles]));
8183
+ return normalizeIr({
8184
+ irVersion: usesServicePrincipal(readPolicies) ? SERVICE_PRINCIPAL_IR_VERSION : RUNTIME_SQL_IR_VERSION,
8185
+ namespace,
8186
+ schemaHash: identity,
8187
+ opsHash: BUILTIN_SQL_READ_HASH,
8188
+ tables,
8189
+ readPolicies,
8190
+ operations: { BuiltinSqlRead: {
8191
+ irVersion: RUNTIME_SQL_IR_VERSION,
8192
+ name: "BuiltinSqlRead",
8193
+ opHash: BUILTIN_SQL_READ_HASH,
8194
+ language: "sql",
8195
+ kind: "query",
8196
+ requires: null,
8197
+ roots: [],
8198
+ tableSet: read,
8199
+ variables: [
8200
+ { name: "sql", type: { kind: "scalar", scalar: "text" }, nullable: false, list: false },
8201
+ { name: "parameters", type: { kind: "scalar", scalar: "jsonb" }, nullable: true, list: false }
8202
+ ],
8203
+ sqlQuery: { catalog: { identity, objects }, read, maxRows: 1e3 }
8204
+ } }
8205
+ });
8206
+ }
7262
8207
  var DATASET_PREVIEW_LIMIT = 100;
7263
8208
  function compileDatasetPreviews(input) {
7264
8209
  const canonical = normalizeCanonical(input);
@@ -7304,6 +8249,64 @@ function compileDatasetPreviews(input) {
7304
8249
  return operations;
7305
8250
  }
7306
8251
 
7307
- 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, bindNamedParameters, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileBuiltinSqlArtifact, compileDatasetPreviews, compileDefaultOperations, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, generateDefaultOperations, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, referencedSqlObjects, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateManagedViewQueries, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
8252
+ // src/compiler-gql/operation-metadata.ts
8253
+ var OPERATION_METADATA_FORMAT = 1;
8254
+ function projectOperationMetadata({ artifact, operation }) {
8255
+ const required = /* @__PURE__ */ new Set(), dynamic = /* @__PURE__ */ new Set();
8256
+ const visit2 = (table) => {
8257
+ if (required.has(table)) return;
8258
+ const meta = artifact.tables[table];
8259
+ if (!meta) throw new Error(`operation metadata references missing table '${table}'`);
8260
+ required.add(table);
8261
+ walk(artifact.readPolicies?.[table], table);
8262
+ for (const dependency of meta.dependencies ?? []) visit2(dependency);
8263
+ };
8264
+ const walk = (value, table) => {
8265
+ if (Array.isArray(value)) {
8266
+ for (const child of value) walk(child, table);
8267
+ return;
8268
+ }
8269
+ if (!value || typeof value !== "object") return;
8270
+ const node = value;
8271
+ let scope = typeof node.table === "string" && artifact.tables[node.table] ? node.table : table;
8272
+ if (scope !== table) visit2(scope);
8273
+ if (typeof node.link === "string") {
8274
+ const link = artifact.tables[scope]?.links.find((candidate) => candidate.name === node.link);
8275
+ if (!link) throw new Error("operation metadata has an unresolved relationship");
8276
+ scope = link.target;
8277
+ visit2(scope);
8278
+ }
8279
+ for (const [key, child] of Object.entries(node)) {
8280
+ if (["value", "objects", "set", "inc", "append", "pk", "onConflict"].includes(key)) continue;
8281
+ walk(child, scope);
8282
+ }
8283
+ };
8284
+ const visitDynamic = (table) => {
8285
+ if (dynamic.has(table)) return;
8286
+ dynamic.add(table);
8287
+ visit2(table);
8288
+ for (const link of artifact.tables[table].links) visitDynamic(link.target);
8289
+ };
8290
+ for (const table of operation.tableSet) visit2(table);
8291
+ for (const root of operation.roots) {
8292
+ visit2(root.table);
8293
+ walk(root, root.table);
8294
+ if (root.kind === "mutation" && !["delete", "deleteByPk", "cowRevert"].includes(root.op)) {
8295
+ for (const reference of artifact.tables[root.table].foreignReferences ?? []) visit2(reference.target);
8296
+ }
8297
+ }
8298
+ for (const variable of operation.variables) if (variable.type.kind === "inputRef") {
8299
+ visit2(variable.type.table);
8300
+ if (["boolExp", "orderBy"].includes(variable.type.input)) visitDynamic(variable.type.table);
8301
+ }
8302
+ return normalizeIr({
8303
+ ...artifact,
8304
+ tables: Object.fromEntries([...required].sort().map((table) => [table, artifact.tables[table]])),
8305
+ readPolicies: Object.fromEntries([...required].filter((table) => artifact.readPolicies?.[table]).map((table) => [table, artifact.readPolicies[table]])),
8306
+ operations: { [operation.name]: operation }
8307
+ });
8308
+ }
8309
+
8310
+ export { COMPILER_MAJOR, CONST_TRUE, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, OBJECT_METADATA_FORMAT, OPERATION_METADATA_FORMAT, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, bindNamedParameters, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileBuiltinSqlArtifact, compileDatasetPreviews, compileDefaultOperations, compileNamespace, compileNamespaceOrThrow, compileObjectMetadata, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, composeObjectSqlArtifact, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, generateDefaultOperations, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, objectSqlBundleSchema, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, projectOperationMetadata, queryRootNames, referencedSqlObjects, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateManagedViewQueries, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
7308
8311
  //# sourceMappingURL=compiler.js.map
7309
8312
  //# sourceMappingURL=compiler.js.map