@sequenceholdings/orm 2.5.0 → 2.6.1

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.
package/dist/index.cjs CHANGED
@@ -1260,10 +1260,10 @@ function validateResolvedView({ view, tables, views = [] }) {
1260
1260
  const resolved = view.resolved;
1261
1261
  if (!resolved) return [];
1262
1262
  const errors = [];
1263
- const fail2 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
1263
+ const fail3 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
1264
1264
  const pkColumn = view.primaryKey?.length === 1 ? view.columns.find((c) => c.columnName === view.primaryKey?.[0]) : void 0;
1265
1265
  if (!pkColumn) {
1266
- fail2("requires exactly one primary-key column");
1266
+ fail3("requires exactly one primary-key column");
1267
1267
  return errors;
1268
1268
  }
1269
1269
  const systemNames = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((c) => c.name));
@@ -1273,93 +1273,93 @@ function validateResolvedView({ view, tables, views = [] }) {
1273
1273
  pkColumn,
1274
1274
  dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
1275
1275
  sourceByName: /* @__PURE__ */ new Map(),
1276
- fail: fail2
1276
+ fail: fail3
1277
1277
  };
1278
- if (resolved.sources.filter((source) => source.kind === "manual").length !== 1) fail2("requires exactly one manual source");
1278
+ if (resolved.sources.filter((source) => source.kind === "manual").length !== 1) fail3("requires exactly one manual source");
1279
1279
  const tableByName = new Map(tables.map((table) => [table.table, table]));
1280
1280
  for (const source of resolved.sources) {
1281
- if (scope.sourceByName.has(source.name)) fail2(`duplicate source '${source.name}'`);
1281
+ if (scope.sourceByName.has(source.name)) fail3(`duplicate source '${source.name}'`);
1282
1282
  scope.sourceByName.set(source.name, source);
1283
1283
  const table = tableByName.get(source.table);
1284
- if (!table) fail2(`source '${source.name}' references unknown table '${source.table}'`);
1284
+ if (!table) fail3(`source '${source.name}' references unknown table '${source.table}'`);
1285
1285
  else validateSource({ scope, source, table });
1286
1286
  }
1287
1287
  validateRules(scope);
1288
1288
  validateViewCells(scope);
1289
1289
  if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
1290
1290
  for (const name2 of resolved.deletedBy) {
1291
- if (scope.sourceByName.get(name2)?.kind !== "manual") fail2(`deletedBy '${name2}' must be the manual source`);
1291
+ if (scope.sourceByName.get(name2)?.kind !== "manual") fail3(`deletedBy '${name2}' must be the manual source`);
1292
1292
  }
1293
1293
  return errors;
1294
1294
  }
1295
1295
  function validateSource({ scope, source, table }) {
1296
- const { fail: fail2 } = scope;
1297
- if (source.kind === "etl" && table.source === void 0) fail2(`etl source '${source.name}' must reference a synced table`);
1298
- if (source.kind === "etl" && table.primaryKey?.length === 0) fail2(`etl source '${source.name}' cannot use an unkeyed synced table for resolved row identity`);
1296
+ const { fail: fail3 } = scope;
1297
+ if (source.kind === "etl" && table.source === void 0) fail3(`etl source '${source.name}' must reference a synced table`);
1298
+ if (source.kind === "etl" && table.primaryKey?.length === 0) fail3(`etl source '${source.name}' cannot use an unkeyed synced table for resolved row identity`);
1299
1299
  if (source.kind !== "etl" && (table.internal !== true || table.cellMetadata !== true)) {
1300
- fail2(`${source.kind} source '${source.name}' must reference an internal table with cell metadata`);
1300
+ fail3(`${source.kind} source '${source.name}' must reference an internal table with cell metadata`);
1301
1301
  }
1302
1302
  const hasRev = table.columns.some((column) => column.cell?.part === "rev");
1303
- if (source.kind === "manual" && !hasRev) fail2(`manual source '${source.name}' must carry '__rev' cell columns`);
1304
- if (source.kind === "prefetch" && hasRev) fail2(`prefetch source '${source.name}' must not carry '__rev' cell columns`);
1303
+ if (source.kind === "manual" && !hasRev) fail3(`manual source '${source.name}' must carry '__rev' cell columns`);
1304
+ if (source.kind === "prefetch" && hasRev) fail3(`prefetch source '${source.name}' must not carry '__rev' cell columns`);
1305
1305
  const byName = new Map(table.columns.map((c) => [c.name, c]));
1306
1306
  validateSourceColumns({ scope, source, byName });
1307
1307
  if (source.kind === "etl") {
1308
1308
  const updatedAt = source.updatedAt === void 0 ? void 0 : byName.get(source.updatedAt);
1309
1309
  if (source.updatedAt !== void 0 && updatedAt?.type !== "timestamptz" && updatedAt?.type !== "date") {
1310
- fail2(`etl source '${source.name}' updatedAt must name a timestamptz/date column on '${source.table}'`);
1310
+ fail3(`etl source '${source.name}' updatedAt must name a timestamptz/date column on '${source.table}'`);
1311
1311
  }
1312
1312
  }
1313
1313
  validatePrefetchFields({ scope, source });
1314
1314
  }
1315
1315
  function validatePrefetchFields({ scope, source }) {
1316
- const { fail: fail2, pkColumn } = scope;
1316
+ const { fail: fail3, pkColumn } = scope;
1317
1317
  if (source.kind !== "prefetch") {
1318
1318
  if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
1319
- fail2(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
1319
+ fail3(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
1320
1320
  }
1321
1321
  return;
1322
1322
  }
1323
1323
  if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
1324
- fail2(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
1324
+ fail3(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
1325
1325
  } else if (source.key !== pkColumn.name && !source.columns.includes(source.key)) {
1326
- fail2(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
1326
+ fail3(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
1327
1327
  }
1328
1328
  for (const column of source.writeback?.columns ?? []) {
1329
- if (!source.columns.includes(column)) fail2(`prefetch source '${source.name}' writes back '${column}', which it does not supply`);
1329
+ if (!source.columns.includes(column)) fail3(`prefetch source '${source.name}' writes back '${column}', which it does not supply`);
1330
1330
  }
1331
1331
  }
1332
1332
  function validateSourceColumns({ scope, source, byName }) {
1333
- const { fail: fail2, pkColumn, dataColumns } = scope;
1333
+ const { fail: fail3, pkColumn, dataColumns } = scope;
1334
1334
  for (const column of [pkColumn.name, ...source.columns]) {
1335
1335
  const own = column === pkColumn.name ? pkColumn : dataColumns.get(column);
1336
1336
  const theirs = byName.get(column);
1337
- if (!own) fail2(`source '${source.name}' supplies unknown column '${column}'`);
1338
- else if (!theirs) fail2(`source '${source.name}' table '${source.table}' has no column '${column}'`);
1339
- else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail2(`column '${column}' has a different type on source '${source.name}'`);
1337
+ if (!own) fail3(`source '${source.name}' supplies unknown column '${column}'`);
1338
+ else if (!theirs) fail3(`source '${source.name}' table '${source.table}' has no column '${column}'`);
1339
+ else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail3(`column '${column}' has a different type on source '${source.name}'`);
1340
1340
  }
1341
1341
  }
1342
- function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fail2 }) {
1342
+ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fail3 }) {
1343
1343
  const ruled = /* @__PURE__ */ new Set();
1344
1344
  for (const rule of resolved.resolve) {
1345
1345
  if (!dataColumns.has(rule.column) || rule.column === pkColumn.name) {
1346
- fail2(`resolve rule targets unknown column '${rule.column}'`);
1346
+ fail3(`resolve rule targets unknown column '${rule.column}'`);
1347
1347
  continue;
1348
1348
  }
1349
- if (ruled.has(rule.column)) fail2(`column '${rule.column}' has more than one resolve rule`);
1349
+ if (ruled.has(rule.column)) fail3(`column '${rule.column}' has more than one resolve rule`);
1350
1350
  ruled.add(rule.column);
1351
1351
  if (rule.comparator === "custom" !== (rule.orderBy !== void 0)) {
1352
- fail2(`column '${rule.column}': orderBy is required for custom comparators and only allowed there`);
1352
+ fail3(`column '${rule.column}': orderBy is required for custom comparators and only allowed there`);
1353
1353
  }
1354
- if (new Set(rule.candidates).size !== rule.candidates.length) fail2(`column '${rule.column}' repeats a candidate`);
1354
+ if (new Set(rule.candidates).size !== rule.candidates.length) fail3(`column '${rule.column}' repeats a candidate`);
1355
1355
  for (const candidate of rule.candidates) {
1356
1356
  const source = sourceByName.get(candidate);
1357
- if (!source) fail2(`column '${rule.column}' names unknown candidate source '${candidate}'`);
1358
- else if (!source.columns.includes(rule.column)) fail2(`column '${rule.column}' candidate '${candidate}' does not supply it`);
1357
+ if (!source) fail3(`column '${rule.column}' names unknown candidate source '${candidate}'`);
1358
+ else if (!source.columns.includes(rule.column)) fail3(`column '${rule.column}' candidate '${candidate}' does not supply it`);
1359
1359
  }
1360
1360
  }
1361
1361
  for (const column of dataColumns.keys()) {
1362
- if (column !== pkColumn.name && !ruled.has(column)) fail2(`column '${column}' has no resolve rule`);
1362
+ if (column !== pkColumn.name && !ruled.has(column)) fail3(`column '${column}' has no resolve rule`);
1363
1363
  }
1364
1364
  }
1365
1365
  var VIEW_CELL_TYPES = {
@@ -1368,7 +1368,7 @@ var VIEW_CELL_TYPES = {
1368
1368
  by: "text",
1369
1369
  conflicts: "jsonb"
1370
1370
  };
1371
- function validateViewCells({ view, dataColumns, pkColumn, fail: fail2 }) {
1371
+ function validateViewCells({ view, dataColumns, pkColumn, fail: fail3 }) {
1372
1372
  const expected = /* @__PURE__ */ new Map();
1373
1373
  for (const column of dataColumns.values()) {
1374
1374
  if (column.name === pkColumn.name) continue;
@@ -1380,34 +1380,34 @@ function validateViewCells({ view, dataColumns, pkColumn, fail: fail2 }) {
1380
1380
  if (!column.cell) continue;
1381
1381
  const want = expected.get(column.name);
1382
1382
  if (!want || want.part !== column.cell.part || want.columnName !== column.columnName || cellColumnName(column.cell.of, column.cell.part) !== column.name) {
1383
- fail2(`unexpected cell column '${column.name}'`);
1383
+ fail3(`unexpected cell column '${column.name}'`);
1384
1384
  continue;
1385
1385
  }
1386
- if (column.type !== VIEW_CELL_TYPES[column.cell.part]) fail2(`cell column '${column.name}' must be ${VIEW_CELL_TYPES[column.cell.part]}`);
1386
+ if (column.type !== VIEW_CELL_TYPES[column.cell.part]) fail3(`cell column '${column.name}' must be ${VIEW_CELL_TYPES[column.cell.part]}`);
1387
1387
  expected.delete(column.name);
1388
1388
  }
1389
- for (const name2 of expected.keys()) fail2(`missing cell column '${name2}'`);
1389
+ for (const name2 of expected.keys()) fail3(`missing cell column '${name2}'`);
1390
1390
  }
1391
1391
  var columnSignature = (columns) => JSON.stringify(columns.map((column) => [column.name, column.columnName, column.type]));
1392
1392
  function validateMaterialized({ scope, tableByName, views }) {
1393
- const { view, resolved, fail: fail2 } = scope;
1393
+ const { view, resolved, fail: fail3 } = scope;
1394
1394
  const { table: tableName, compute: computeName } = resolved.materialized;
1395
1395
  const table = tableByName.get(tableName);
1396
1396
  if (!table || table.internal !== true || table.materializes !== view.view) {
1397
- fail2(`materialized table '${tableName}' must be an internal table that materializes '${view.view}'`);
1397
+ fail3(`materialized table '${tableName}' must be an internal table that materializes '${view.view}'`);
1398
1398
  } else {
1399
- if (columnSignature(table.columns) !== columnSignature(view.columns)) fail2(`materialized table '${tableName}' must have exactly the view's columns`);
1400
- if (JSON.stringify(table.primaryKey ?? []) !== JSON.stringify(view.primaryKey ?? [])) fail2(`materialized table '${tableName}' must share the view's primary key`);
1399
+ if (columnSignature(table.columns) !== columnSignature(view.columns)) fail3(`materialized table '${tableName}' must have exactly the view's columns`);
1400
+ if (JSON.stringify(table.primaryKey ?? []) !== JSON.stringify(view.primaryKey ?? [])) fail3(`materialized table '${tableName}' must share the view's primary key`);
1401
1401
  }
1402
1402
  const compute = views.find((candidate) => candidate.view === computeName);
1403
- if (!compute || compute.internal !== true) fail2(`compute view '${computeName}' must be an internal view`);
1404
- else if (columnSignature(compute.columns) !== columnSignature(view.columns)) fail2(`compute view '${computeName}' must have exactly the view's columns`);
1403
+ if (!compute || compute.internal !== true) fail3(`compute view '${computeName}' must be an internal view`);
1404
+ else if (columnSignature(compute.columns) !== columnSignature(view.columns)) fail3(`compute view '${computeName}' must have exactly the view's columns`);
1405
1405
  for (const source of resolved.sources) {
1406
1406
  if (source.kind !== "etl") continue;
1407
1407
  const read = tableByName.get(source.table)?.policies?.read;
1408
1408
  const unconditional = typeof read === "string" ? read.trim() === "true" : read !== void 0 && read.default.trim() === "true" && read.variants.length === 0;
1409
1409
  if (!unconditional) {
1410
- fail2(`etl source '${source.name}' must have read policy 'true' to be materialized (the copy is shared by every caller)`);
1410
+ fail3(`etl source '${source.name}' must have read policy 'true' to be materialized (the copy is shared by every caller)`);
1411
1411
  }
1412
1412
  }
1413
1413
  }
@@ -1782,26 +1782,26 @@ var canonicalTableSchema = zod.z.object({
1782
1782
  materializes: identifier2.optional()
1783
1783
  }).superRefine((table, ctx) => {
1784
1784
  if (table.primaryKey?.length !== 0) return;
1785
- const fail2 = (message, path = ["primaryKey"]) => ctx.addIssue({ code: "custom", path, message: `unkeyed table '${table.table}' ${message}` });
1785
+ const fail3 = (message, path = ["primaryKey"]) => ctx.addIssue({ code: "custom", path, message: `unkeyed table '${table.table}' ${message}` });
1786
1786
  if (table.source === void 0 || table.columns.length === 0) {
1787
- fail2("requires a typed synced source");
1787
+ fail3("requires a typed synced source");
1788
1788
  }
1789
1789
  if ([table.policies.write, table.policies.insert, table.policies.update].some((policy) => policy !== void 0)) {
1790
- fail2("is read-only and cannot declare write policies", ["policies"]);
1790
+ fail3("is read-only and cannot declare write policies", ["policies"]);
1791
1791
  }
1792
1792
  const ownedCollections = [table.indexes, table.actions, table.checks, table.foreignKeys, table.cowMutations];
1793
1793
  const ownedMetadata = [table.cellMetadata, table.internal, table.materializes, table.renamedFrom];
1794
1794
  if (ownedCollections.some((items) => items !== void 0 && items.length > 0) || ownedMetadata.some((value) => value !== void 0)) {
1795
- fail2("cannot declare owned storage, constraints or identity operations");
1795
+ fail3("cannot declare owned storage, constraints or identity operations");
1796
1796
  }
1797
1797
  for (const [index3, column] of table.columns.entries()) {
1798
1798
  const ownedColumnMetadata = [column.default, column.defaultFn, column.onUpdateFn, column.renamedFrom, column.cast, column.cell];
1799
1799
  if (column.unique || ownedColumnMetadata.some((value) => value !== void 0)) {
1800
- fail2("cannot declare column uniqueness, mutation defaults or migration metadata", ["columns", index3]);
1800
+ fail3("cannot declare column uniqueness, mutation defaults or migration metadata", ["columns", index3]);
1801
1801
  }
1802
1802
  }
1803
1803
  for (const [index3, link] of table.links.entries()) {
1804
- if (link.enforce || link.kind === "toMany") fail2("cannot provide an identity for this link", ["links", index3]);
1804
+ if (link.enforce || link.kind === "toMany") fail3("cannot provide an identity for this link", ["links", index3]);
1805
1805
  }
1806
1806
  });
1807
1807
  var MAX_VIEW_QUERY = 1e5;
@@ -1892,49 +1892,49 @@ function trackedCellColumns(table) {
1892
1892
  }
1893
1893
  var SUFFIXES = Object.values(CELL_PART_SUFFIX);
1894
1894
  function validateCellMetadata(table, index3, ctx) {
1895
- const fail2 = (message, path = ["tables", index3]) => ctx.addIssue({ code: "custom", path, message });
1895
+ const fail3 = (message, path = ["tables", index3]) => ctx.addIssue({ code: "custom", path, message });
1896
1896
  const cells = table.columns.flatMap((column, position) => column.cell ? [{ column, position, cell: column.cell }] : []);
1897
1897
  if (table.materializes !== void 0) return;
1898
1898
  if (table.cellMetadata !== true) {
1899
- for (const { position } of cells) fail2(`cell-metadata columns require cellMetadata on table '${table.table}'`, ["tables", index3, "columns", position]);
1899
+ for (const { position } of cells) fail3(`cell-metadata columns require cellMetadata on table '${table.table}'`, ["tables", index3, "columns", position]);
1900
1900
  return;
1901
1901
  }
1902
1902
  if (table.source !== void 0) {
1903
- fail2(`synced table '${table.table}' cannot track cell metadata`);
1903
+ fail3(`synced table '${table.table}' cannot track cell metadata`);
1904
1904
  return;
1905
1905
  }
1906
1906
  const tracked = trackedCellColumns(table);
1907
1907
  const byName = new Map(tracked.map((column) => [column.name, column]));
1908
1908
  const withRev = cells.some(({ cell }) => cell.part === "rev");
1909
- if (withRev && table.internal !== true) fail2(`only a resolved table's internal sources carry '__rev' cell columns ('${table.table}')`);
1909
+ if (withRev && table.internal !== true) fail3(`only a resolved table's internal sources carry '__rev' cell columns ('${table.table}')`);
1910
1910
  const seen = /* @__PURE__ */ new Set();
1911
1911
  for (const { column, position, cell } of cells) {
1912
1912
  const path = ["tables", index3, "columns", position];
1913
1913
  const owner = byName.get(cell.of);
1914
1914
  if (!owner) {
1915
- fail2(`cell column '${column.name}' belongs to '${cell.of}', which is not a tracked column`, path);
1915
+ fail3(`cell column '${column.name}' belongs to '${cell.of}', which is not a tracked column`, path);
1916
1916
  continue;
1917
1917
  }
1918
- if (cell.part !== "at" && cell.part !== "by" && cell.part !== "rev") fail2(`a table cell column must be part at/by/rev, not '${cell.part}'`, path);
1918
+ 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);
1919
1919
  if (column.name !== cellColumnName(owner.name, cell.part) || column.columnName !== cellColumnName(owner.columnName, cell.part)) {
1920
- fail2(`cell column for '${owner.name}' ${cell.part} must be named '${cellColumnName(owner.name, cell.part)}' / '${cellColumnName(owner.columnName, cell.part)}'`, path);
1920
+ fail3(`cell column for '${owner.name}' ${cell.part} must be named '${cellColumnName(owner.name, cell.part)}' / '${cellColumnName(owner.columnName, cell.part)}'`, path);
1921
1921
  }
1922
1922
  if (column.type !== CELL_PART_TYPES[cell.part] || !column.nullable || column.unique || column.default !== void 0 || column.defaultFn !== void 0) {
1923
- fail2(`cell column '${column.name}' must be a nullable, default-less ${CELL_PART_TYPES[cell.part]}`, path);
1923
+ fail3(`cell column '${column.name}' must be a nullable, default-less ${CELL_PART_TYPES[cell.part]}`, path);
1924
1924
  }
1925
1925
  const key = `${cell.of}:${cell.part}`;
1926
- if (seen.has(key)) fail2(`duplicate cell column '${column.name}'`, path);
1926
+ if (seen.has(key)) fail3(`duplicate cell column '${column.name}'`, path);
1927
1927
  seen.add(key);
1928
1928
  }
1929
1929
  for (const column of tracked) {
1930
1930
  if (column.columnName.length > MAX_TRACKED_COLUMN_NAME) {
1931
- fail2(`column '${column.columnName}' is too long to track cell metadata (max ${MAX_TRACKED_COLUMN_NAME} characters)`);
1931
+ fail3(`column '${column.columnName}' is too long to track cell metadata (max ${MAX_TRACKED_COLUMN_NAME} characters)`);
1932
1932
  }
1933
1933
  if (SUFFIXES.some((suffix) => column.columnName.endsWith(suffix))) {
1934
- fail2(`column '${column.columnName}' ends in a reserved cell-metadata suffix (${SUFFIXES.join(", ")})`);
1934
+ fail3(`column '${column.columnName}' ends in a reserved cell-metadata suffix (${SUFFIXES.join(", ")})`);
1935
1935
  }
1936
1936
  for (const part of withRev ? ["at", "by", "rev"] : ["at", "by"]) {
1937
- if (!seen.has(`${column.name}:${part}`)) fail2(`tracked column '${column.name}' is missing its '${cellColumnName(column.columnName, part)}' cell column`);
1937
+ if (!seen.has(`${column.name}:${part}`)) fail3(`tracked column '${column.name}' is missing its '${cellColumnName(column.columnName, part)}' cell column`);
1938
1938
  }
1939
1939
  }
1940
1940
  }
@@ -2002,38 +2002,38 @@ var localDefinitionSchema = zod.z.object({
2002
2002
  }
2003
2003
  }
2004
2004
  for (const [index3, check] of (table.checks ?? []).entries()) {
2005
- const fail2 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "checks", index3], message });
2006
- if (table.source) fail2("synced tables cannot declare checks");
2007
- if (names.has(check.name)) fail2(`duplicate constraint '${check.name}'`);
2005
+ const fail3 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "checks", index3], message });
2006
+ if (table.source) fail3("synced tables cannot declare checks");
2007
+ if (names.has(check.name)) fail3(`duplicate constraint '${check.name}'`);
2008
2008
  names.add(check.name);
2009
2009
  try {
2010
2010
  compileCheckExpression(check.expression, table.columns);
2011
2011
  } catch (error) {
2012
- fail2(error instanceof Error ? error.message : String(error));
2012
+ fail3(error instanceof Error ? error.message : String(error));
2013
2013
  }
2014
2014
  }
2015
2015
  for (const [index3, fk] of (table.foreignKeys ?? []).entries()) {
2016
- const fail2 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index3], message });
2017
- if (names.has(fk.name)) fail2(`duplicate foreign key '${fk.name}'`);
2016
+ const fail3 = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index3], message });
2017
+ if (names.has(fk.name)) fail3(`duplicate foreign key '${fk.name}'`);
2018
2018
  names.add(fk.name);
2019
2019
  const target = fkTables.get(fk.target);
2020
2020
  if (table.source || !target || target.source) {
2021
- fail2("foreign keys must connect owned tables in the same namespace");
2021
+ fail3("foreign keys must connect owned tables in the same namespace");
2022
2022
  continue;
2023
2023
  }
2024
- if (fk.columns.length !== fk.references.length) fail2("foreign key columns and references must have the same number of columns");
2025
- if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail2("foreign key columns must not repeat");
2024
+ if (fk.columns.length !== fk.references.length) fail3("foreign key columns and references must have the same number of columns");
2025
+ if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail3("foreign key columns must not repeat");
2026
2026
  for (const [position, name2] of fk.columns.entries()) {
2027
2027
  const sourceColumn = table.columns.find((column) => column.columnName === name2);
2028
2028
  const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
2029
- if (!sourceColumn || !targetColumn) fail2("foreign key references an unknown column");
2030
- else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail2("foreign key column types must match");
2029
+ if (!sourceColumn || !targetColumn) fail3("foreign key references an unknown column");
2030
+ else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail3("foreign key column types must match");
2031
2031
  }
2032
2032
  if (fk.onDelete === "set null" || fk.onUpdate === "set null") {
2033
- if (fk.columns.some((name2) => table.columns.find((column) => column.columnName === name2)?.nullable === false || table.primaryKey?.includes(name2))) fail2("SET NULL requires nullable source columns");
2033
+ if (fk.columns.some((name2) => table.columns.find((column) => column.columnName === name2)?.nullable === false || table.primaryKey?.includes(name2))) fail3("SET NULL requires nullable source columns");
2034
2034
  }
2035
2035
  const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index4) => index4.unique).map((index4) => index4.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
2036
- if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail2("foreign key target columns must form a unique key");
2036
+ 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");
2037
2037
  }
2038
2038
  }
2039
2039
  for (const [viewIndex, view] of (definition.views ?? []).entries()) {
@@ -2221,8 +2221,8 @@ function cloneColumn({ builder, property, notNull, primaryKey: primaryKey2 }) {
2221
2221
  return clone;
2222
2222
  }
2223
2223
  function planSource(input) {
2224
- const { sourceName, input: source, fail: fail2 } = input;
2225
- if (!IDENTIFIER_PATTERN.test(sourceName)) fail2(`source name "${sourceName}" must match ${IDENTIFIER_PATTERN}`);
2224
+ const { sourceName, input: source, fail: fail3 } = input;
2225
+ if (!IDENTIFIER_PATTERN.test(sourceName)) fail3(`source name "${sourceName}" must match ${IDENTIFIER_PATTERN}`);
2226
2226
  switch (source.kind) {
2227
2227
  case "manual":
2228
2228
  return {
@@ -2262,27 +2262,27 @@ function assertColumns(input, list2, what) {
2262
2262
  for (const column of list2) if (!input.dataColumns.includes(column)) input.fail(`${what} names unknown column "${column}"`);
2263
2263
  }
2264
2264
  function planEtl(input, source) {
2265
- const { sourceName, pk, dataColumns, fail: fail2 } = input;
2265
+ const { sourceName, pk, dataColumns, fail: fail3 } = input;
2266
2266
  const table = typeof source.table === "function" ? source.table() : source.table;
2267
- if (!isSeqTable(table) || table.source === void 0) fail2(`etl source "${sourceName}" must reference a defineSyncedTable`);
2267
+ if (!isSeqTable(table) || table.source === void 0) fail3(`etl source "${sourceName}" must reference a defineSyncedTable`);
2268
2268
  const synced = table;
2269
- if (synced.config.primaryKey?.length === 0) fail2(`etl source "${sourceName}" cannot use an unkeyed synced table for resolved row identity`);
2270
- if (!(pk in synced.columns)) fail2(`etl source "${sourceName}" table "${synced.tableName}" has no "${pk}" column`);
2269
+ if (synced.config.primaryKey?.length === 0) fail3(`etl source "${sourceName}" cannot use an unkeyed synced table for resolved row identity`);
2270
+ if (!(pk in synced.columns)) fail3(`etl source "${sourceName}" table "${synced.tableName}" has no "${pk}" column`);
2271
2271
  const supplied = source.columns ? [...source.columns] : dataColumns.filter((column) => column in synced.columns);
2272
2272
  assertColumns(input, supplied, `etl source "${sourceName}"`);
2273
2273
  if (source.updatedAt !== void 0 && !(source.updatedAt in synced.columns)) {
2274
- fail2(`etl source "${sourceName}" updatedAt "${source.updatedAt}" is not a column of "${synced.tableName}"`);
2274
+ fail3(`etl source "${sourceName}" updatedAt "${source.updatedAt}" is not a column of "${synced.tableName}"`);
2275
2275
  }
2276
2276
  return { name: sourceName, kind: "etl", table: synced, columns: supplied, ...source.updatedAt ? { updatedAt: source.updatedAt } : {} };
2277
2277
  }
2278
2278
  function planPrefetch(input, source) {
2279
- const { sourceName, pk, fail: fail2 } = input;
2279
+ const { sourceName, pk, fail: fail3 } = input;
2280
2280
  assertColumns(input, source.columns, `prefetch source "${sourceName}"`);
2281
2281
  if (source.key !== pk && !source.columns.includes(source.key)) {
2282
- fail2(`prefetch source "${sourceName}" key "${source.key}" must be the primary key or one of its columns`);
2282
+ fail3(`prefetch source "${sourceName}" key "${source.key}" must be the primary key or one of its columns`);
2283
2283
  }
2284
2284
  for (const column of source.writeback?.columns ?? []) {
2285
- if (!source.columns.includes(column)) fail2(`prefetch source "${sourceName}" writes back "${column}", which it does not supply`);
2285
+ if (!source.columns.includes(column)) fail3(`prefetch source "${sourceName}" writes back "${column}", which it does not supply`);
2286
2286
  }
2287
2287
  const writeback = source.writeback;
2288
2288
  return {
@@ -2488,34 +2488,34 @@ var SYSTEM_VIEW_COLUMNS = {
2488
2488
  };
2489
2489
  function defineResolvedTable(name2, columns, config) {
2490
2490
  const file = activeAuthoringContext()?.currentFile;
2491
- const fail2 = (message) => {
2491
+ const fail3 = (message) => {
2492
2492
  throw new OrmDefinitionError(`resolved table "${name2}": ${message}`, file);
2493
2493
  };
2494
- if (!IDENTIFIER_PATTERN.test(name2)) fail2(`name must match ${IDENTIFIER_PATTERN}`);
2494
+ if (!IDENTIFIER_PATTERN.test(name2)) fail3(`name must match ${IDENTIFIER_PATTERN}`);
2495
2495
  for (const property of Object.keys(columns)) {
2496
2496
  if (RESERVED_RESOLVED_COLUMN_NAMES.has(property) || RESERVED_RESOLVED_COLUMN_NAMES.has(columns[property].columnNameFor(property))) {
2497
- fail2(`column "${property}" collides with a reserved system column`);
2497
+ fail3(`column "${property}" collides with a reserved system column`);
2498
2498
  }
2499
2499
  }
2500
2500
  for (const property of Object.keys(columns)) {
2501
2501
  if (columns[property].columnNameFor(property).length > MAX_TRACKED_COLUMN_NAME) {
2502
- fail2(`column "${property}" is too long for a resolved table (max ${MAX_TRACKED_COLUMN_NAME} characters, room for cell parts)`);
2502
+ fail3(`column "${property}" is too long for a resolved table (max ${MAX_TRACKED_COLUMN_NAME} characters, room for cell parts)`);
2503
2503
  }
2504
2504
  }
2505
2505
  const pkEntries = Object.entries(columns).filter(([, builder]) => builder.isPrimaryKey);
2506
- if (pkEntries.length !== 1) fail2("declare exactly one .primaryKey() column");
2506
+ if (pkEntries.length !== 1) fail3("declare exactly one .primaryKey() column");
2507
2507
  const [pk, pkBuilder] = pkEntries[0];
2508
2508
  const dataColumns = Object.keys(columns).filter((property) => property !== pk);
2509
- if (dataColumns.length === 0) fail2("declare at least one non-key column");
2509
+ if (dataColumns.length === 0) fail3("declare at least one non-key column");
2510
2510
  const sourceEntries = Object.entries(config.sources);
2511
- if (sourceEntries.filter(([, source]) => source.kind === "manual").length !== 1) fail2("declare exactly one manualSource()");
2511
+ if (sourceEntries.filter(([, source]) => source.kind === "manual").length !== 1) fail3("declare exactly one manualSource()");
2512
2512
  const plans = sourceEntries.map(
2513
- ([sourceName, input]) => planSource({ viewName: name2, sourceName, input, columns, pk, pkBuilder, dataColumns, fail: fail2 })
2513
+ ([sourceName, input]) => planSource({ viewName: name2, sourceName, input, columns, pk, pkBuilder, dataColumns, fail: fail3 })
2514
2514
  );
2515
- const rules = planRules({ config, plans, dataColumns, fail: fail2 });
2515
+ const rules = planRules({ config, plans, dataColumns, fail: fail3 });
2516
2516
  const deletedBy = [...config.rows?.deletedBy ?? []];
2517
2517
  for (const source of deletedBy) {
2518
- if (plans.find((plan) => plan.name === source)?.kind !== "manual") fail2(`rows.deletedBy "${source}" must be the manual source`);
2518
+ if (plans.find((plan) => plan.name === source)?.kind !== "manual") fail3(`rows.deletedBy "${source}" must be the manual source`);
2519
2519
  }
2520
2520
  const partType = { source: t.text, at: t.timestamptz, by: t.text, conflicts: t.jsonb };
2521
2521
  const viewColumns = {};
@@ -2578,11 +2578,11 @@ function defineResolvedTable(name2, columns, config) {
2578
2578
  }
2579
2579
  });
2580
2580
  }
2581
- function planRules({ config, plans, dataColumns, fail: fail2 }) {
2581
+ function planRules({ config, plans, dataColumns, fail: fail3 }) {
2582
2582
  const kindOrder = { manual: 0, prefetch: 1, etl: 2 };
2583
2583
  const fallback = config.default ?? priority([...plans].sort((a, b) => kindOrder[a.kind] - kindOrder[b.kind]).map((plan) => plan.name));
2584
2584
  for (const column of Object.keys(config.resolve ?? {})) {
2585
- if (!dataColumns.includes(column)) fail2(`resolve names unknown column "${column}"`);
2585
+ if (!dataColumns.includes(column)) fail3(`resolve names unknown column "${column}"`);
2586
2586
  }
2587
2587
  return dataColumns.map((column) => {
2588
2588
  const explicit = config.resolve?.[column];
@@ -2590,11 +2590,11 @@ function planRules({ config, plans, dataColumns, fail: fail2 }) {
2590
2590
  const candidates = explicit ? [...explicit.candidates] : comparator.candidates.filter((name2) => plans.find((plan) => plan.name === name2)?.columns.includes(column));
2591
2591
  for (const candidate of candidates) {
2592
2592
  const plan = plans.find((entry) => entry.name === candidate);
2593
- if (!plan) fail2(`column "${column}" names unknown source "${candidate}"`);
2594
- else if (!plan.columns.includes(column)) fail2(`column "${column}" candidate "${candidate}" does not supply it`);
2593
+ if (!plan) fail3(`column "${column}" names unknown source "${candidate}"`);
2594
+ else if (!plan.columns.includes(column)) fail3(`column "${column}" candidate "${candidate}" does not supply it`);
2595
2595
  }
2596
- if (candidates.length === 0) fail2(`column "${column}" has no candidate source`);
2597
- if (comparator.kind === "custom") assertSafeOrderBy({ orderBy: comparator.orderBy ?? "", column, fail: fail2 });
2596
+ if (candidates.length === 0) fail3(`column "${column}" has no candidate source`);
2597
+ if (comparator.kind === "custom") assertSafeOrderBy({ orderBy: comparator.orderBy ?? "", column, fail: fail3 });
2598
2598
  return {
2599
2599
  column,
2600
2600
  comparator: comparator.kind,
@@ -2645,18 +2645,18 @@ var ORDER_BY_KEYWORDS = /* @__PURE__ */ new Set([
2645
2645
  ]);
2646
2646
  var ALLOWED_FUNCTIONS = new Set(VIEW_QUERY_FUNCTION_ALLOWLIST);
2647
2647
  var ORDER_BY_TOKEN = /\s+|'(?:[^']|'')*'|\d+(?:\.\d+)?|[a-z_][a-z0-9_]*|::|<=|>=|<>|!=|[-+*/%<>=(),|]/giy;
2648
- function assertSafeOrderBy({ orderBy, column, fail: fail2 }) {
2648
+ function assertSafeOrderBy({ orderBy, column, fail: fail3 }) {
2649
2649
  ORDER_BY_TOKEN.lastIndex = 0;
2650
2650
  let consumed = 0;
2651
2651
  for (let match = ORDER_BY_TOKEN.exec(orderBy); match; match = ORDER_BY_TOKEN.exec(orderBy)) {
2652
2652
  consumed = ORDER_BY_TOKEN.lastIndex;
2653
2653
  const token = match[0].toLowerCase();
2654
2654
  if (/^[a-z_]/.test(token) && !ORDER_BY_KEYWORDS.has(token) && !ALLOWED_FUNCTIONS.has(token)) {
2655
- fail2(`column "${column}" custom orderBy uses "${match[0]}" \u2014 only value, updated_at, source, actor, ord, SQL keywords and allowlisted functions`);
2655
+ fail3(`column "${column}" custom orderBy uses "${match[0]}" \u2014 only value, updated_at, source, actor, ord, SQL keywords and allowlisted functions`);
2656
2656
  }
2657
2657
  }
2658
2658
  if (consumed !== orderBy.length || orderBy.trim() === "") {
2659
- fail2(`column "${column}" custom orderBy contains an unsupported character near "${orderBy.slice(consumed, consumed + 10)}"`);
2659
+ fail3(`column "${column}" custom orderBy contains an unsupported character near "${orderBy.slice(consumed, consumed + 10)}"`);
2660
2660
  }
2661
2661
  }
2662
2662
  function stableJson(value) {
@@ -3184,17 +3184,17 @@ function buildCanonicalLink({
3184
3184
  }) {
3185
3185
  const ownerKind = relationKind(relation);
3186
3186
  const ownerName = relationName(relation);
3187
- const fail2 = (message) => {
3187
+ const fail3 = (message) => {
3188
3188
  throw new OrmDefinitionError(`${ownerKind} "${ownerName}": link "${linkName}" ${message}`, relation.file);
3189
3189
  };
3190
3190
  if (link.enforce && link.linkKind !== "toOne") {
3191
- return fail2("sets enforce: true, which is only supported on toOne links");
3191
+ return fail3("sets enforce: true, which is only supported on toOne links");
3192
3192
  }
3193
3193
  if ((link.onDelete !== void 0 || link.onUpdate !== void 0) && !link.enforce) {
3194
- return fail2("sets a referential action without enforce: true");
3194
+ return fail3("sets a referential action without enforce: true");
3195
3195
  }
3196
3196
  if (link.linkKind === "toPlatformUser") {
3197
- if (link.target !== void 0) return fail2("is toPlatformUser and must not declare a target table");
3197
+ if (link.target !== void 0) return fail3("is toPlatformUser and must not declare a target table");
3198
3198
  return {
3199
3199
  name: linkName,
3200
3200
  kind: "toPlatformUser",
@@ -3205,7 +3205,7 @@ function buildCanonicalLink({
3205
3205
  };
3206
3206
  }
3207
3207
  if (typeof link.target === "object" && link.target?.kind === "seq-external-object") {
3208
- if (link.enforce) return fail2("cannot enforce a cross-namespace physical foreign key; use an ORM link without enforce");
3208
+ if (link.enforce) return fail3("cannot enforce a cross-namespace physical foreign key; use an ORM link without enforce");
3209
3209
  return {
3210
3210
  name: linkName,
3211
3211
  kind: link.linkKind,
@@ -3217,13 +3217,13 @@ function buildCanonicalLink({
3217
3217
  ...link.description !== void 0 ? { description: link.description } : {}
3218
3218
  };
3219
3219
  }
3220
- const target = resolveRelationRef({ ref: link.target, fail: fail2 });
3220
+ const target = resolveRelationRef({ ref: link.target, fail: fail3 });
3221
3221
  const targetName = relationName(target);
3222
3222
  if (!relationsByName.has(targetName)) {
3223
- return fail2(`targets ${relationKind(target)} "${targetName}" which is not part of this namespace`);
3223
+ return fail3(`targets ${relationKind(target)} "${targetName}" which is not part of this namespace`);
3224
3224
  }
3225
3225
  if (link.enforce && (!isSeqTable(relation) || !isSeqTable(target))) {
3226
- return fail2("cannot enforce a PostgreSQL foreign key involving a view");
3226
+ return fail3("cannot enforce a PostgreSQL foreign key involving a view");
3227
3227
  }
3228
3228
  const owningSide = link.linkKind === "toOne" ? relation : target;
3229
3229
  const referencedSide = link.linkKind === "toOne" ? target : relation;
@@ -3239,19 +3239,19 @@ function buildCanonicalLink({
3239
3239
  ...link.description !== void 0 ? { description: link.description } : {}
3240
3240
  };
3241
3241
  }
3242
- function resolveRelationRef({ ref, fail: fail2 }) {
3243
- if (ref === void 0) return fail2("declares no target relation");
3242
+ function resolveRelationRef({ ref, fail: fail3 }) {
3243
+ if (ref === void 0) return fail3("declares no target relation");
3244
3244
  if (typeof ref !== "function") return ref;
3245
3245
  let resolved;
3246
3246
  try {
3247
3247
  resolved = ref();
3248
3248
  } catch (error) {
3249
- return fail2(
3249
+ return fail3(
3250
3250
  `target thunk threw (${error instanceof Error ? error.message : String(error)}) \u2014 lazy refs resolve at serialize time; make sure the target module ran`
3251
3251
  );
3252
3252
  }
3253
3253
  if (!isSeqTable(resolved) && !isSeqView(resolved)) {
3254
- return fail2("target thunk did not return a relation defined with defineTable/defineView");
3254
+ return fail3("target thunk did not return a relation defined with defineTable/defineView");
3255
3255
  }
3256
3256
  return resolved;
3257
3257
  }
@@ -4123,19 +4123,19 @@ function compileComparison(op, left, right, columnTypes) {
4123
4123
  }
4124
4124
  return `${compileOperand(left, right, columnTypes)} ${SQL_COMPARE_OPS[op]} ${compileOperand(right, left, columnTypes)}`;
4125
4125
  }
4126
- function compilePredicate(predicate, columnTypes) {
4126
+ function compilePredicate(predicate, columnTypes, options = {}) {
4127
4127
  switch (predicate.kind) {
4128
4128
  case "and":
4129
4129
  case "or": {
4130
- const joined = predicate.operands.map((operand) => compilePredicate(operand, columnTypes)).join(` ${predicate.kind.toUpperCase()} `);
4130
+ const joined = predicate.operands.map((operand) => compilePredicate(operand, columnTypes, options)).join(` ${predicate.kind.toUpperCase()} `);
4131
4131
  return `(${joined})`;
4132
4132
  }
4133
4133
  case "not":
4134
- return `(NOT ${compilePredicate(predicate.operand, columnTypes)})`;
4134
+ return `(NOT ${compilePredicate(predicate.operand, columnTypes, options)})`;
4135
4135
  case "compare":
4136
4136
  return `(${compileComparison(predicate.op, predicate.left, predicate.right, columnTypes)})`;
4137
4137
  case "capability":
4138
- return `(${capabilityExpression(predicate.key)})`;
4138
+ return `(${options.statementClaimChecks ? "SELECT " : ""}${capabilityExpression(predicate.key)})`;
4139
4139
  }
4140
4140
  }
4141
4141
 
@@ -4221,9 +4221,9 @@ function compilePredIrToSql(pred, options = {}) {
4221
4221
  case "cmp":
4222
4222
  return compileCmp(pred.op, pred.column, pred.value, options);
4223
4223
  case "capability":
4224
- return `(${capabilityExpression(pred.key, pred.namespace ?? options.namespace)})`;
4224
+ return `(${options.statementClaimChecks ? "SELECT " : ""}${capabilityExpression(pred.key, pred.namespace ?? options.namespace)})`;
4225
4225
  case "role":
4226
- return `(${roleExpression(pred.key, pred.namespace ?? options.namespace)})`;
4226
+ return `(${options.statementClaimChecks ? "SELECT " : ""}${roleExpression(pred.key, pred.namespace ?? options.namespace)})`;
4227
4227
  case "exists":
4228
4228
  if (options.exists) return options.exists(pred);
4229
4229
  throw new OrmDefinitionError("synced view read policy cannot use 'exists' without a relationship resolver");
@@ -4799,7 +4799,9 @@ function syncedViewStatements({
4799
4799
  columnNamesByTable,
4800
4800
  columnTypesByTable,
4801
4801
  executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE,
4802
- backingPrivileges = "grant"
4802
+ backingPrivileges = "grant",
4803
+ viewPrivileges = "grant",
4804
+ statementClaimChecks = true
4803
4805
  }) {
4804
4806
  const schema = quoteIdentifier(ormSchemaName(definition.namespace));
4805
4807
  const role = quoteIdentifier(consumerRoleName(definition.namespace));
@@ -4842,9 +4844,10 @@ function syncedViewStatements({
4842
4844
  const readPolicy = table.policies?.read;
4843
4845
  const policyClause = readPolicyIr ? ` WHERE ${compilePredIrToSql(readPolicyIr, {
4844
4846
  namespace: definition.namespace,
4847
+ statementClaimChecks,
4845
4848
  columnTypes,
4846
4849
  columnNames: columnNamesByTable?.get(table.table)
4847
- })}` : readPolicy ? ` WHERE ${compilePredicate(readPolicy.ast, narrowToV1ColumnTypes(columnTypes))}` : " WHERE false";
4850
+ })}` : readPolicy ? ` WHERE ${compilePredicate(readPolicy.ast, narrowToV1ColumnTypes(columnTypes), { statementClaimChecks })}` : " WHERE false";
4848
4851
  if (backingPrivileges === "grant") {
4849
4852
  const selectPrivilege = backing.selectColumns ? `SELECT (${backing.selectColumns.map(quoteIdentifier).join(", ")})` : "SELECT";
4850
4853
  const hasSelectPrivilege = backing.selectColumns ? backing.selectColumns.map((column) => `pg_catalog.has_column_privilege(${quoteLiteral(executionOwnerRole)}, ${quoteLiteral(physicalTarget)}, ${quoteLiteral(column)}, 'SELECT')`).join("\n AND ") : `pg_catalog.has_table_privilege(${quoteLiteral(executionOwnerRole)}, ${quoteLiteral(physicalTarget)}, 'SELECT')`;
@@ -4861,7 +4864,7 @@ END`)}`);
4861
4864
  statements.push(
4862
4865
  `CREATE OR REPLACE VIEW ${viewName} AS SELECT ${projection} FROM ${physicalTarget}${whereClause}`
4863
4866
  );
4864
- statements.push(`GRANT SELECT ON ${viewName} TO ${role}`);
4867
+ if (viewPrivileges === "grant") statements.push(`GRANT SELECT ON ${viewName} TO ${role}`);
4865
4868
  }
4866
4869
  return statements;
4867
4870
  }
@@ -5199,15 +5202,15 @@ var sqlValues = /* @__PURE__ */ new Set([
5199
5202
  async function validateManagedViewQueries({ canonical }) {
5200
5203
  for (const definition of [canonical, ...canonical.imports ?? []]) {
5201
5204
  for (const view of definition.views ?? []) {
5202
- const fail2 = (message) => {
5205
+ const fail3 = (message) => {
5203
5206
  throw new OrmDefinitionError(`managed view '${definition.namespace}/${view.view}': ${message}`);
5204
5207
  };
5205
5208
  parser ??= new pgParser.PgParser({ version: 17 });
5206
5209
  const parsed = await parser.parse(view.query);
5207
- if (parsed.error || !parsed.tree) fail2("invalid SELECT query");
5210
+ if (parsed.error || !parsed.tree) fail3("invalid SELECT query");
5208
5211
  const statements = list(obj(parsed.tree).stmts);
5209
- if (statements.length !== 1 || !obj(obj(statements[0]).stmt).SelectStmt) fail2("one SELECT query is required");
5210
- validateTree({ tree: parsed.tree, schema: ormSchemaName(definition.namespace), enums: definition.enums.map((entry) => entry.name), fail: fail2 });
5212
+ if (statements.length !== 1 || !obj(obj(statements[0]).stmt).SelectStmt) fail3("one SELECT query is required");
5213
+ validateTree({ tree: parsed.tree, schema: ormSchemaName(definition.namespace), enums: definition.enums.map((entry) => entry.name), fail: fail3 });
5211
5214
  }
5212
5215
  }
5213
5216
  }
@@ -5236,29 +5239,29 @@ function validateTree({ tree, ...scope }) {
5236
5239
  }
5237
5240
  }
5238
5241
  function validateNode({ kind, child, scope }) {
5239
- const { fail: fail2 } = scope;
5240
- if (kind.endsWith("Stmt") && kind !== "SelectStmt") fail2("only SELECT statements are permitted");
5242
+ const { fail: fail3 } = scope;
5243
+ if (kind.endsWith("Stmt") && kind !== "SelectStmt") fail3("only SELECT statements are permitted");
5241
5244
  if (kind === "FuncCall") {
5242
5245
  const call = obj(child);
5243
5246
  const parts = strings(call.funcname);
5244
5247
  const name2 = reviewedFunctionName({ parts, format: call.funcformat });
5245
- if (!name2 || !functions.has(name2)) fail2(`only explicitly allowlisted pg_catalog functions are permitted (found '${parts.join(".")}')`);
5248
+ if (!name2 || !functions.has(name2)) fail3(`only explicitly allowlisted pg_catalog functions are permitted (found '${parts.join(".")}')`);
5246
5249
  }
5247
5250
  if (kind === "typeName" || kind === "TypeName") validateType({ node: obj(child), ...scope });
5248
- if (kind === "A_Expr" && pgCatalogName(strings(obj(child).name)) === void 0) fail2("only pg_catalog operators are permitted");
5249
- if (kind === "SortBy" && list(obj(child).useOp).length && pgCatalogName(strings(obj(child).useOp)) === void 0) fail2("only pg_catalog sort operators are permitted");
5250
- if (kind === "SQLValueFunction" && !sqlValues.has(String(obj(child).op))) fail2("unsupported SQL value expression");
5251
- if (kind === "ParamRef") fail2("view queries cannot contain parameters");
5251
+ if (kind === "A_Expr" && pgCatalogName(strings(obj(child).name)) === void 0) fail3("only pg_catalog operators are permitted");
5252
+ if (kind === "SortBy" && list(obj(child).useOp).length && pgCatalogName(strings(obj(child).useOp)) === void 0) fail3("only pg_catalog sort operators are permitted");
5253
+ if (kind === "SQLValueFunction" && !sqlValues.has(String(obj(child).op))) fail3("unsupported SQL value expression");
5254
+ if (kind === "ParamRef") fail3("view queries cannot contain parameters");
5252
5255
  if (kind === "A_Indirection" && list(obj(child).indirection).some((entry) => !obj(entry).A_Indices)) {
5253
- fail2("composite field notation is not permitted in managed views");
5256
+ fail3("composite field notation is not permitted in managed views");
5254
5257
  }
5255
5258
  }
5256
- function validateType({ node, schema, enums, fail: fail2 }) {
5259
+ function validateType({ node, schema, enums, fail: fail3 }) {
5257
5260
  const parts = strings(node.names);
5258
5261
  const builtin = pgCatalogName(parts);
5259
5262
  const managedEnum = parts.length === 1 && enums.includes(parts[0]) || parts.length === 2 && parts[0] === schema && enums.includes(parts[1]);
5260
5263
  if (node.setof || node.pct_type || !managedEnum && (!builtin || !scalarTypes.has(builtin))) {
5261
- fail2(`casts may only use supported pg_catalog scalar types or declared enums (found '${parts.join(".")}')`);
5264
+ fail3(`casts may only use supported pg_catalog scalar types or declared enums (found '${parts.join(".")}')`);
5262
5265
  }
5263
5266
  }
5264
5267
 
@@ -6470,6 +6473,118 @@ async function guardedRun(run) {
6470
6473
  process.exit = originalExit;
6471
6474
  }
6472
6475
  }
6476
+ var parser2;
6477
+ var locations = /* @__PURE__ */ new Set(["location", "stmt_location", "stmt_len"]);
6478
+ async function assertSyncedViewDefinitionMatches({
6479
+ viewDefinition,
6480
+ expectedCreateStatements
6481
+ }) {
6482
+ if (!expectedCreateStatements.length) fail2();
6483
+ const expected = [];
6484
+ for (const statement of expectedCreateStatements) {
6485
+ const node = await parseStatement(statement);
6486
+ const view = obj(node.ViewStmt);
6487
+ requireKeys(view, ["view", "query", "replace", "withCheckOption"]);
6488
+ if (view.replace !== true || view.withCheckOption !== "NO_CHECK_OPTION") fail2();
6489
+ backingOf(obj(view.view));
6490
+ expected.push(canonicalSelect(obj(obj(view.query).SelectStmt)));
6491
+ }
6492
+ const actual = await parseStatement(viewDefinition);
6493
+ if (!expected.includes(canonicalSelect(obj(actual.SelectStmt)))) fail2();
6494
+ }
6495
+ function fail2() {
6496
+ throw new OrmDefinitionError("synced view definition does not match the compiler");
6497
+ }
6498
+ async function parseStatement(sql7) {
6499
+ if (!sql7.length || sql7.length > 262144) fail2();
6500
+ parser2 ??= new pgParser.PgParser({ version: 17 });
6501
+ const parsed = await parser2.parse(sql7);
6502
+ if (parsed.error || !parsed.tree) fail2();
6503
+ const statements = list(obj(parsed.tree).stmts);
6504
+ if (statements.length !== 1) fail2();
6505
+ return obj(obj(statements[0]).stmt);
6506
+ }
6507
+ function requireKeys(node, allowed) {
6508
+ for (const key of Object.keys(node)) if (!locations.has(key) && !allowed.includes(key)) fail2();
6509
+ }
6510
+ function backingOf(node) {
6511
+ requireKeys(node, ["catalogname", "schemaname", "relname", "inh", "relpersistence"]);
6512
+ if (node.catalogname !== "" || node.inh !== true || node.relpersistence !== "p") fail2();
6513
+ const schema = str(node.schemaname);
6514
+ const relation = str(node.relname);
6515
+ if (!schema || !relation) fail2();
6516
+ return { schema, relation };
6517
+ }
6518
+ function validateSelect(select, scalar2) {
6519
+ requireKeys(select, ["targetList", "fromClause", "whereClause", "groupDistinct", "limitOption", "op", "all"]);
6520
+ if (select.groupDistinct !== false || select.limitOption !== "LIMIT_OPTION_DEFAULT" || select.op !== "SETOP_NONE" || select.all !== false) fail2();
6521
+ if (scalar2 && (list(select.fromClause).length || select.whereClause !== void 0 || list(select.targetList).length !== 1)) fail2();
6522
+ }
6523
+ function canonicalSelect(select) {
6524
+ validateSelect(select, false);
6525
+ const from = list(select.fromClause);
6526
+ if (from.length !== 1) fail2();
6527
+ const backing = backingOf(obj(obj(from[0]).RangeVar));
6528
+ const targets = list(select.targetList);
6529
+ if (!targets.length) fail2();
6530
+ for (const target of targets) {
6531
+ const node = obj(obj(target).ResTarget);
6532
+ requireKeys(node, ["name", "val"]);
6533
+ if (node.name !== "" || !obj(node.val).ColumnRef) fail2();
6534
+ }
6535
+ return JSON.stringify(normalize({ value: select, backing, scalar: false }));
6536
+ }
6537
+ function normalize({ value, backing, scalar: scalar2 }) {
6538
+ if (Array.isArray(value)) return value.map((entry) => normalize({ value: entry, backing, scalar: scalar2 }));
6539
+ if (!value || typeof value !== "object") return value;
6540
+ const node = obj(value);
6541
+ if (node.ColumnRef) return normalizeColumnRef({ ref: obj(node.ColumnRef), backing, scalar: scalar2 });
6542
+ if (node.TypeCast && isLiteralTextCast(obj(node.TypeCast))) {
6543
+ return normalize({ value: obj(node.TypeCast).arg, backing, scalar: scalar2 });
6544
+ }
6545
+ if (node.SubLink) return normalizeScalarSubLink({ sublink: obj(node.SubLink), backing });
6546
+ const result = {};
6547
+ for (const key of Object.keys(node).sort()) {
6548
+ if (locations.has(key)) continue;
6549
+ if (key === "ParamRef" || key === "RangeSubselect" || key === "JoinExpr" || key.endsWith("Stmt") && key !== "SelectStmt" || scalar2 && key === "RangeVar") fail2();
6550
+ result[key] = normalize({ value: node[key], backing, scalar: scalar2 });
6551
+ }
6552
+ return result;
6553
+ }
6554
+ function normalizeColumnRef({ ref, backing, scalar: scalar2 }) {
6555
+ if (scalar2) fail2();
6556
+ requireKeys(ref, ["fields"]);
6557
+ const fields = strings(ref.fields);
6558
+ const qualified = fields.length === 2 && fields[0] === backing.relation || fields.length === 3 && fields[0] === backing.schema && fields[1] === backing.relation;
6559
+ if (fields.length !== 1 && !qualified) fail2();
6560
+ const column = fields.at(-1);
6561
+ if (!column) fail2();
6562
+ return { ColumnRef: { fields: [{ String: { sval: column } }] } };
6563
+ }
6564
+ function normalizeScalarSubLink({ sublink, backing }) {
6565
+ requireKeys(sublink, ["subLinkType", "subLinkId", "subselect"]);
6566
+ if (sublink.subLinkType !== "EXPR_SUBLINK" || sublink.subLinkId !== 0) fail2();
6567
+ const select = obj(obj(sublink.subselect).SelectStmt);
6568
+ validateSelect(select, true);
6569
+ const target = obj(obj(list(select.targetList)[0]).ResTarget);
6570
+ requireKeys(target, ["name", "val"]);
6571
+ const normalizedSelect = { ...select, targetList: [{ ResTarget: { ...target, name: "" } }] };
6572
+ return { SubLink: normalize({ value: { ...sublink, subselect: { SelectStmt: normalizedSelect } }, backing, scalar: true }) };
6573
+ }
6574
+ function isLiteralTextCast(cast) {
6575
+ requireKeys(cast, ["arg", "typeName"]);
6576
+ const arg = obj(cast.arg);
6577
+ if (!arg.A_Const) return false;
6578
+ const constant = obj(arg.A_Const);
6579
+ if (constant.isnull !== false || !constant.sval || typeof obj(constant.sval).sval !== "string") return false;
6580
+ const type = obj(cast.typeName);
6581
+ const names = strings(type.names);
6582
+ if (!(names.length === 1 && names[0] === "text" || names.length === 2 && names[0] === "pg_catalog" && names[1] === "text")) return false;
6583
+ if (list(type.arrayBounds).length || list(type.typmods).length) return false;
6584
+ requireKeys(type, ["names", "typeOid", "setof", "pct_type", "typemod"]);
6585
+ if (type.typeOid !== 0 || type.setof !== false || type.pct_type !== false || type.typemod !== -1) return false;
6586
+ return true;
6587
+ }
6473
6588
 
6474
6589
  // src/engine/index.ts
6475
6590
  var STATEMENT_KINDS = ["hint", "ddl", "security", "function", "synced_view"];
@@ -6539,6 +6654,11 @@ function renderOrmMigrationExport(exported) {
6539
6654
  if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
6540
6655
  return text4;
6541
6656
  }
6657
+
6658
+ // src/client/sql.ts
6659
+ var BUILTIN_SQL_READ_HASH = "sha256:a50c477c952a5e0fe68ed158a4530e4aab573c09bcbb1a8ac454dc88a39a6258";
6660
+
6661
+ // src/ir/index.ts
6542
6662
  var IR_VERSION = 5;
6543
6663
  var LEGACY_IR_VERSION = 1;
6544
6664
  var GUARDED_IR_VERSION = 2;
@@ -6871,7 +6991,8 @@ var compiledOperationSchema = zod.z.object({
6871
6991
  }).superRefine((operation, ctx) => {
6872
6992
  if (operation.language === "sql") {
6873
6993
  const validPlan = operation.sqlQuery ? !operation.sql && operation.irVersion === RUNTIME_SQL_IR_VERSION : !!operation.sql && operation.irVersion === SQL_IR_VERSION;
6874
- if (!validPlan || operation.kind !== "query" || operation.roots.length !== 0 || !operation.requires?.capability) {
6994
+ const builtin = operation.name === "BuiltinSqlRead" && operation.opHash === BUILTIN_SQL_READ_HASH && operation.requires === null;
6995
+ if (!validPlan || operation.kind !== "query" || operation.roots.length !== 0 || !builtin && !operation.requires?.capability) {
6875
6996
  ctx.addIssue({ code: "custom", message: "SQL operations require a query-only v4 plan or v5 runtime contract, capability, and no GraphQL roots" });
6876
6997
  }
6877
6998
  } else if (operation.sql || operation.sqlQuery || operation.roots.length === 0) {
@@ -8343,6 +8464,7 @@ exports.actionsInterfaceName = actionsInterfaceName;
8343
8464
  exports.activeAuthoringContext = activeAuthoringContext;
8344
8465
  exports.applyMigrationHints = applyMigrationHints;
8345
8466
  exports.applyMigrationHintsV2 = applyMigrationHintsV2;
8467
+ exports.assertSyncedViewDefinitionMatches = assertSyncedViewDefinitionMatches;
8346
8468
  exports.beginAuthoringContext = beginAuthoringContext;
8347
8469
  exports.buildActionParamsSchema = buildActionParamsSchema;
8348
8470
  exports.buildDrizzleEntities = buildDrizzleEntities;