@lunora/do 1.0.0-alpha.20 → 1.0.0-alpha.22

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 (25) hide show
  1. package/dist/index.d.mts +30 -90
  2. package/dist/index.d.ts +30 -90
  3. package/dist/index.mjs +18 -18
  4. package/dist/packem_shared/{AGGREGATE_SQL_FUNCTION-CFk6adSu.mjs → AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs} +5 -3
  5. package/dist/packem_shared/{CDC_LOG_TABLE-DSycmnDf.mjs → CDC_LOG_TABLE-DZSVKRr7.mjs} +1 -1
  6. package/dist/packem_shared/{ConflictError-C0STs6bU.mjs → ConflictError-CLoq37xH.mjs} +4 -5
  7. package/dist/packem_shared/{CountRlsUnsupportedError-28ZvvwKS.mjs → CountRlsUnsupportedError-BGxj0pgS.mjs} +4 -4
  8. package/dist/packem_shared/{DATA_MIGRATION_STATE_TABLE-DfPxn8I0.mjs → DATA_MIGRATION_STATE_TABLE-DB3IYUR3.mjs} +3 -1
  9. package/dist/packem_shared/{DEFAULT_MAX_RELATION_KEYS-DU-Y4-LJ.mjs → DEFAULT_MAX_RELATION_KEYS-CjM9Y1_G.mjs} +8 -6
  10. package/dist/packem_shared/NotFoundError-C70b9hLw.mjs +9 -0
  11. package/dist/packem_shared/{NotUniqueError-Do5h_jiW.mjs → NotUniqueError-D1U5SBFR.mjs} +63 -55
  12. package/dist/packem_shared/{RLS_UNWRAP_SYMBOL-EtGQdC9d.mjs → RLS_UNWRAP_SYMBOL-DnjkqVgY.mjs} +6 -5
  13. package/dist/packem_shared/{ROOT_DO_SIZE_WARN_BYTES-BbHQKxG6.mjs → ROOT_DO_SIZE_WARN_BYTES-BQoX61l-.mjs} +20 -38
  14. package/dist/packem_shared/{applyOnDelete-BQ-8ZlZ1.mjs → applyOnDelete-CAwZfp-5.mjs} +4 -3
  15. package/dist/packem_shared/{backfillAggregateIndexes-BZsOqDXP.mjs → backfillAggregateIndexes-DDoT-UUI.mjs} +1 -1
  16. package/dist/packem_shared/{compileWhereSql-MW_Lk8nJ.mjs → compileWhereSql-DE6yfRcQ.mjs} +2 -1
  17. package/dist/packem_shared/{createSystemReader-8CzSZP9V.mjs → createSystemReader-D12eNH13.mjs} +4 -2
  18. package/dist/packem_shared/{ctx-db-shapes-0RaIOy7J.mjs → ctx-db-shapes-Cz9dHyh1.mjs} +1 -1
  19. package/dist/packem_shared/{isSourceDue-9mJRJ9Ld.mjs → isSourceDue-BptUN8CR.mjs} +3 -2
  20. package/dist/packem_shared/{materializeExternalRows-Bj6EXy50.mjs → materializeExternalRows-DNcvoLRT.mjs} +2 -2
  21. package/dist/packem_shared/{runShardMigrations-nIwoQeOK.mjs → runShardMigrations-DeZr0KZp.mjs} +1 -1
  22. package/dist/packem_shared/{subscription-delivery-Z6nXHvQN.mjs → subscription-delivery-CK8qga-k.mjs} +8 -1
  23. package/dist/packem_shared/{subscriptionListDeltas-CXwfoEE_.mjs → subscriptionListDeltas-BxygQlXT.mjs} +1 -1
  24. package/package.json +2 -1
  25. package/dist/packem_shared/NotFoundError-CMuMZt81.mjs +0 -10
@@ -1,9 +1,10 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import { sql } from 'drizzle-orm';
2
- import { matchesStaticWhere, aggregateSqlFunction, normalizeCountArgument, throwingScheduler } from './AGGREGATE_SQL_FUNCTION-CFk6adSu.mjs';
3
+ import { matchesStaticWhere, aggregateSqlFunction, normalizeCountArgument, throwingScheduler } from './AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs';
3
4
  import { encodeAggregateKey, foldAggregateTally, aggregateTableName, coerceAggregateNumber, readAggregateValue } from './aggregateTableName-CxNqY1Sl.mjs';
4
- import { mergeWhere, CountRlsUnsupportedError, selectIndexForGroupBy, selectIndexForCount, selectIndexForAggregate } from './CountRlsUnsupportedError-28ZvvwKS.mjs';
5
- import { appendCdcChange } from './CDC_LOG_TABLE-DSycmnDf.mjs';
6
- export { CDC_LOG_TABLE, applyCdcChanges, bumpCdcEpoch, minCdcSeq, readCdcChanges, readCdcCursor, readCdcEpoch, trimCdcChanges } from './CDC_LOG_TABLE-DSycmnDf.mjs';
5
+ import { mergeWhere, CountRlsUnsupportedError, selectIndexForGroupBy, selectIndexForCount, selectIndexForAggregate } from './CountRlsUnsupportedError-BGxj0pgS.mjs';
6
+ import { appendCdcChange } from './CDC_LOG_TABLE-DZSVKRr7.mjs';
7
+ export { CDC_LOG_TABLE, applyCdcChanges, bumpCdcEpoch, minCdcSeq, readCdcChanges, readCdcCursor, readCdcEpoch, trimCdcChanges } from './CDC_LOG_TABLE-DZSVKRr7.mjs';
7
8
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
8
9
  import { i as isFtsAvailable, D as DOC_COLUMN$1, r as rowToDocument, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT, d as aggUpsertSql, j as jsonPathSql, q as quoteIdentifier, t as tableColumns, e as qualifiedJsonPathSql } from './do-sql-BCHCWtrD.mjs';
9
10
  import { param } from './renderSql-D6eUcn2N.mjs';
@@ -12,18 +13,18 @@ import { stringifySearchText, ftsTableName, tokenizeSearch, buildFtsMatch, score
12
13
  import { s as serializeSqlValue } from './serialize-sql-BlRUoiQe.mjs';
13
14
  import { SCAN_DEP } from './SCAN_DEP-DLJF8dsj.mjs';
14
15
  import { decodeCursor, normalizeOrderKeys, buildSeekWhere, applySelect, encodeCursor, softDeleteScope, buildSeekBeforeWhere } from './applySelect-BvZdFUBT.mjs';
15
- import NotFoundError from './NotFoundError-CMuMZt81.mjs';
16
- import { assertFlatPredicate, resolveRelationPredicates } from './DEFAULT_MAX_RELATION_KEYS-DU-Y4-LJ.mjs';
17
- import { runRowValidators, resolveWith, applyOnDelete, fanOutScalarCounts } from './applyOnDelete-BQ-8ZlZ1.mjs';
18
- import { guardWriter } from './RLS_UNWRAP_SYMBOL-EtGQdC9d.mjs';
19
- import { createSystemReader } from './createSystemReader-8CzSZP9V.mjs';
20
- import { ConflictError } from './ConflictError-C0STs6bU.mjs';
16
+ import NotFoundError from './NotFoundError-C70b9hLw.mjs';
17
+ import { assertFlatPredicate, resolveRelationPredicates } from './DEFAULT_MAX_RELATION_KEYS-CjM9Y1_G.mjs';
18
+ import { runRowValidators, resolveWith, applyOnDelete, fanOutScalarCounts } from './applyOnDelete-CAwZfp-5.mjs';
19
+ import { guardWriter } from './RLS_UNWRAP_SYMBOL-DnjkqVgY.mjs';
20
+ import { createSystemReader } from './createSystemReader-D12eNH13.mjs';
21
+ import { ConflictError } from './ConflictError-CLoq37xH.mjs';
21
22
  import { runTriggers } from './hasTrigger-5N6_Fx0A.mjs';
22
- import { compileWhereSql } from './compileWhereSql-MW_Lk8nJ.mjs';
23
- export { backfillAggregateIndexes, backfillRankIndexes } from './backfillAggregateIndexes-BZsOqDXP.mjs';
23
+ import { compileWhereSql } from './compileWhereSql-DE6yfRcQ.mjs';
24
+ export { backfillAggregateIndexes, backfillRankIndexes } from './backfillAggregateIndexes-DDoT-UUI.mjs';
24
25
  export { C as CLIENT_WATERMARK_TABLE, G as GLOBAL_SHAPE_SNAPSHOT_TABLE, I as IDEMPOTENCY_TABLE, c as advanceClientWatermark, d as deleteGlobalShapeSnapshot, e as deleteGlobalShapeSnapshotsForConnection, m as migrateClientWatermark, b as migrateGlobalShapeSnapshot, r as readClientWatermark, f as readGlobalShapeSnapshot, g as readIdempotent, t as trimIdempotent, w as writeGlobalShapeSnapshot, h as writeIdempotent } from './ctx-db-idempotency-BdcNpvY4.mjs';
25
- export { runShardMigrations } from './runShardMigrations-nIwoQeOK.mjs';
26
- export { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-0RaIOy7J.mjs';
26
+ export { runShardMigrations } from './runShardMigrations-DeZr0KZp.mjs';
27
+ export { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
27
28
 
28
29
  const rankIndexFieldsUnchanged = (index, previous, next) => {
29
30
  const fields = [...index.partitionBy ?? [], ...index.sortBy.map((key) => key.field), ...index.where ? Object.keys(index.where) : []];
@@ -422,11 +423,11 @@ const computeRankPage = (deps, tableName, indexName, rankPageOptions) => {
422
423
  const { assertRankPartitionLocal, ensureRankBackfilled, onRead, schema } = deps;
423
424
  const definition = schema.tables[tableName];
424
425
  if (!definition) {
425
- throw new Error(`unknown table: ${tableName}`);
426
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
426
427
  }
427
428
  const index = definition.rankIndexes?.find((i) => i.name === indexName);
428
429
  if (!index) {
429
- throw new Error(`unknown rankIndex "${indexName}" on table "${tableName}"`);
430
+ throw new LunoraError("INTERNAL", `unknown rankIndex "${indexName}" on table "${tableName}"`);
430
431
  }
431
432
  assertRankPartitionLocal(tableName, definition, index);
432
433
  onRead(tableName, SCAN_DEP);
@@ -474,7 +475,7 @@ const computeRankPage = (deps, tableName, indexName, rankPageOptions) => {
474
475
  const CLIENT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
475
476
  const assertValidClientId = (clientId) => {
476
477
  if (!CLIENT_ID_PATTERN.test(clientId)) {
477
- throw new Error(`invalid clientId ${JSON.stringify(clientId)}: a client-supplied row id must be a UUID`);
478
+ throw new LunoraError("INTERNAL", `invalid clientId ${JSON.stringify(clientId)}: a client-supplied row id must be a UUID`);
478
479
  }
479
480
  };
480
481
  const MAX_TRIGGER_DEPTH = 50;
@@ -518,14 +519,17 @@ const createSearchBuilder = (search, tableName) => {
518
519
  const builder = {
519
520
  eq: (field, value) => {
520
521
  if (!search.definition.filterFields?.includes(field)) {
521
- throw new Error(`field "${field}" is not a filter field of search index "${search.indexName}" on table "${tableName}"`);
522
+ throw new LunoraError("INTERNAL", `field "${field}" is not a filter field of search index "${search.indexName}" on table "${tableName}"`);
522
523
  }
523
524
  search.filters.push({ field, value });
524
525
  return builder;
525
526
  },
526
527
  search: (field, query) => {
527
528
  if (field !== search.definition.field) {
528
- throw new Error(`search index "${search.indexName}" on table "${tableName}" indexes "${search.definition.field}", not "${field}"`);
529
+ throw new LunoraError(
530
+ "INTERNAL",
531
+ `search index "${search.indexName}" on table "${tableName}" indexes "${search.definition.field}", not "${field}"`
532
+ );
529
533
  }
530
534
  const stage = search;
531
535
  stage.field = field;
@@ -706,19 +710,16 @@ const paginateStage = (sql$1, tableName, stage, options, scopeCondition) => {
706
710
  page
707
711
  };
708
712
  };
709
- class NotUniqueError extends Error {
710
- code = "NOT_UNIQUE";
711
- status = 400;
713
+ class NotUniqueError extends LunoraError {
712
714
  constructor(message = "unique() found more than one matching document") {
713
- super(message);
714
- this.name = "NotUniqueError";
715
+ super("NOT_UNIQUE", message, { name: "NotUniqueError" });
715
716
  }
716
717
  }
717
718
  const ID_WHITESPACE_PATTERN = /\s/u;
718
719
  const NUL_CHARACTER = String.fromCodePoint(0);
719
720
  const normalizeIdStructurally = (schema, tableName, id) => {
720
721
  if (!schema.tables[tableName]) {
721
- throw new Error(`unknown table: ${tableName}`);
722
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
722
723
  }
723
724
  if (typeof id !== "string" || id.length === 0 || ID_WHITESPACE_PATTERN.test(id) || id.includes(NUL_CHARACTER)) {
724
725
  return null;
@@ -728,7 +729,7 @@ const normalizeIdStructurally = (schema, tableName, id) => {
728
729
  const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
729
730
  const tableDefinition = schema.tables[tableName];
730
731
  if (!tableDefinition) {
731
- throw new Error(`unknown table: ${tableName}`);
732
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
732
733
  }
733
734
  const scopeWhere = softDeleteScope(tableDefinition.softDeleteMode, void 0);
734
735
  const scopeCondition = scopeWhere ? compileWhereSql(scopeWhere, doWhereSqlStrategy) : void 0;
@@ -742,7 +743,7 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
742
743
  const runSearchFetch = (limit) => {
743
744
  const { search } = stage;
744
745
  if (!search) {
745
- throw new Error("runSearchFetch called without a staged search");
746
+ throw new LunoraError("INTERNAL", "runSearchFetch called without a staged search");
746
747
  }
747
748
  const filtered = stage.inMemoryFilters.length > 0;
748
749
  const engineLimit = filtered ? void 0 : limit;
@@ -825,7 +826,7 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
825
826
  // eslint-disable-next-line @typescript-eslint/require-await -- TableReaderLike returns Promises (the D1 twin awaits real I/O); the DO impl is synchronous over local SQLite
826
827
  async paginate(options) {
827
828
  if (stage.search) {
828
- throw new Error("pagination is not supported on search queries; use .take(n) or .collect()");
829
+ throw new LunoraError("INTERNAL", "pagination is not supported on search queries; use .take(n) or .collect()");
829
830
  }
830
831
  return paginateStage(sql$1, tableName, stage, options, scopeCondition);
831
832
  },
@@ -844,7 +845,7 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
844
845
  withIndex(indexName, range) {
845
846
  const definition = tableDefinition.indexes.find((index) => index.name === indexName);
846
847
  if (!definition) {
847
- throw new Error(`unknown index "${indexName}" on table "${tableName}"`);
848
+ throw new LunoraError("INTERNAL", `unknown index "${indexName}" on table "${tableName}"`);
848
849
  }
849
850
  onIndexUse(tableName, indexName, "index");
850
851
  stage.indexName = indexName;
@@ -857,7 +858,7 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
857
858
  withSearchIndex(indexName, search) {
858
859
  const definition = (tableDefinition.searchIndexes ?? []).find((index) => index.name === indexName);
859
860
  if (!definition) {
860
- throw new Error(`unknown search index "${indexName}" on table "${tableName}"`);
861
+ throw new LunoraError("INTERNAL", `unknown search index "${indexName}" on table "${tableName}"`);
861
862
  }
862
863
  onIndexUse(tableName, indexName, "search");
863
864
  const searchStage = {
@@ -871,7 +872,7 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
871
872
  stage.search = searchStage;
872
873
  search(createSearchBuilder(searchStage, tableName));
873
874
  if (!searchStage.hasQuery) {
874
- throw new Error(`search index "${indexName}" on table "${tableName}" requires a .search(field, query) call`);
875
+ throw new LunoraError("INTERNAL", `search index "${indexName}" on table "${tableName}" requires a .search(field, query) call`);
875
876
  }
876
877
  return reader;
877
878
  }
@@ -913,7 +914,10 @@ const applyOnUpdate = (definition, provided, target, auth) => {
913
914
  const assertNoExplicitUndefined = (op, document) => {
914
915
  for (const field of Object.keys(document)) {
915
916
  if (document[field] === void 0) {
916
- throw new Error(`Cannot ${op} field '${field}' to undefined — use null to clear a nullable field, or omit the key to leave it unchanged.`);
917
+ throw new LunoraError(
918
+ "INTERNAL",
919
+ `Cannot ${op} field '${field}' to undefined — use null to clear a nullable field, or omit the key to leave it unchanged.`
920
+ );
917
921
  }
918
922
  }
919
923
  };
@@ -993,7 +997,10 @@ const createShardCtxDb = (options) => {
993
997
  const routeBackend = (table, op) => {
994
998
  if (isGlobalTable(table)) {
995
999
  if (!globalDb) {
996
- throw new Error(`cross-backend ${op} for global table '${table}' requires a globalDb writer — pass one to createShardCtxDb({ globalDb })`);
1000
+ throw new LunoraError(
1001
+ "INTERNAL",
1002
+ `cross-backend ${op} for global table '${table}' requires a globalDb writer — pass one to createShardCtxDb({ globalDb })`
1003
+ );
997
1004
  }
998
1005
  return globalDb;
999
1006
  }
@@ -1005,7 +1012,7 @@ const createShardCtxDb = (options) => {
1005
1012
  return void 0;
1006
1013
  }
1007
1014
  if (!globalDb) {
1008
- throw new Error(`${op} on global table '${tableName}' requires a globalDb writer — pass one to createShardCtxDb({ globalDb })`);
1015
+ throw new LunoraError("INTERNAL", `${op} on global table '${tableName}' requires a globalDb writer — pass one to createShardCtxDb({ globalDb })`);
1009
1016
  }
1010
1017
  return globalDb;
1011
1018
  };
@@ -1036,7 +1043,7 @@ const createShardCtxDb = (options) => {
1036
1043
  }
1037
1044
  const definition = schema.tables[relationTable];
1038
1045
  if (!definition) {
1039
- throw new Error(`unknown table: ${relationTable}`);
1046
+ throw new LunoraError("INTERNAL", `unknown table: ${relationTable}`);
1040
1047
  }
1041
1048
  onRead(relationTable, SCAN_DEP);
1042
1049
  const softScope = softDeleteScope(definition.softDeleteMode, void 0);
@@ -1153,7 +1160,7 @@ const createShardCtxDb = (options) => {
1153
1160
  }
1154
1161
  const definition = schema.tables[tableName];
1155
1162
  if (!definition) {
1156
- throw new Error(`unknown table: ${tableName}`);
1163
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1157
1164
  }
1158
1165
  aggregateSqlFunction(aggOptions.op);
1159
1166
  if (aggOptions.op === "count") {
@@ -1165,7 +1172,7 @@ const createShardCtxDb = (options) => {
1165
1172
  });
1166
1173
  }
1167
1174
  if (!aggOptions.field) {
1168
- throw new Error(`aggregate(${tableName}, { op: "${aggOptions.op}" }): "field" is required for non-count reducers`);
1175
+ throw new LunoraError("INTERNAL", `aggregate(${tableName}, { op: "${aggOptions.op}" }): "field" is required for non-count reducers`);
1169
1176
  }
1170
1177
  onRead(tableName, SCAN_DEP);
1171
1178
  const aggScope = softDeleteScope(definition.softDeleteMode, void 0);
@@ -1207,7 +1214,7 @@ const createShardCtxDb = (options) => {
1207
1214
  }
1208
1215
  const definition = schema.tables[tableName];
1209
1216
  if (!definition) {
1210
- throw new Error(`unknown table: ${tableName}`);
1217
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1211
1218
  }
1212
1219
  const countOptions = normalizeCountArgument(whereOrOptions);
1213
1220
  if (countOptions.restrictsCounts) {
@@ -1338,7 +1345,7 @@ const createShardCtxDb = (options) => {
1338
1345
  }
1339
1346
  const findManyDefinition = schema.tables[tableName];
1340
1347
  if (!findManyDefinition) {
1341
- throw new Error(`unknown table: ${tableName}`);
1348
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1342
1349
  }
1343
1350
  const isFullScan = !args.where && !args.baseWhere;
1344
1351
  if (isFullScan) {
@@ -1451,13 +1458,13 @@ const createShardCtxDb = (options) => {
1451
1458
  }
1452
1459
  const definition = schema.tables[tableName];
1453
1460
  if (!definition) {
1454
- throw new Error(`unknown table: ${tableName}`);
1461
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1455
1462
  }
1456
1463
  onRead(tableName, SCAN_DEP);
1457
1464
  const agg = groupOptions.agg ?? { op: "count" };
1458
1465
  aggregateSqlFunction(agg.op);
1459
1466
  if (agg.op !== "count" && !agg.field) {
1460
- throw new Error(`groupBy(${tableName}, { agg: { op: "${agg.op}" } }): "field" is required for non-count reducers`);
1467
+ throw new LunoraError("INTERNAL", `groupBy(${tableName}, { agg: { op: "${agg.op}" } }): "field" is required for non-count reducers`);
1461
1468
  }
1462
1469
  const groupScope = softDeleteScope(definition.softDeleteMode, void 0);
1463
1470
  const effective = mergeWhere(mergeWhere(groupOptions.baseWhere, groupOptions.where), groupScope);
@@ -1502,7 +1509,7 @@ const createShardCtxDb = (options) => {
1502
1509
  } else {
1503
1510
  const { field } = agg;
1504
1511
  if (field === void 0) {
1505
- throw new Error(`groupBy(${tableName}, { agg: { op: "${agg.op}" } }): "field" is required for non-count reducers`);
1512
+ throw new LunoraError("INTERNAL", `groupBy(${tableName}, { agg: { op: "${agg.op}" } }): "field" is required for non-count reducers`);
1506
1513
  }
1507
1514
  select.push(sql`${sql.raw(aggregateSqlFunction(agg.op))}(${jsonPathSql(field)}) AS value`);
1508
1515
  }
@@ -1535,7 +1542,7 @@ const createShardCtxDb = (options) => {
1535
1542
  }
1536
1543
  const definition = schema.tables[tableName];
1537
1544
  if (!definition) {
1538
- throw new Error(`unknown table: ${tableName}`);
1545
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1539
1546
  }
1540
1547
  const withDefaults = applyInsertDefaults(definition, document, auth);
1541
1548
  runRowValidators(definition, withDefaults);
@@ -1584,7 +1591,7 @@ const createShardCtxDb = (options) => {
1584
1591
  }
1585
1592
  const definition = schema.tables[tableName];
1586
1593
  if (!definition) {
1587
- throw new Error(`unknown table: ${tableName}`);
1594
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1588
1595
  }
1589
1596
  ensureBackfilledForTable(tableName);
1590
1597
  ensureRankBackfilledForTable(tableName);
@@ -1624,12 +1631,12 @@ const createShardCtxDb = (options) => {
1624
1631
  await global.patch(id, patch);
1625
1632
  return;
1626
1633
  }
1627
- throw new Error(`document not found: ${id}`);
1634
+ throw new LunoraError("INTERNAL", `document not found: ${id}`);
1628
1635
  }
1629
1636
  const { docJson: existingJson, row: existing, tableName } = located;
1630
1637
  const tableDefinition = schema.tables[tableName];
1631
1638
  if (!tableDefinition) {
1632
- throw new Error(`unknown table: ${tableName}`);
1639
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1633
1640
  }
1634
1641
  onRead(tableName, id);
1635
1642
  assertNoExplicitUndefined("patch", patch);
@@ -1681,11 +1688,11 @@ const createShardCtxDb = (options) => {
1681
1688
  onIndexUse(tableName, indexName, "rank");
1682
1689
  const definition = schema.tables[tableName];
1683
1690
  if (!definition) {
1684
- throw new Error(`unknown table: ${tableName}`);
1691
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1685
1692
  }
1686
1693
  const index = definition.rankIndexes?.find((i) => i.name === indexName);
1687
1694
  if (!index) {
1688
- throw new Error(`unknown rankIndex "${indexName}" on table "${tableName}"`);
1695
+ throw new LunoraError("INTERNAL", `unknown rankIndex "${indexName}" on table "${tableName}"`);
1689
1696
  }
1690
1697
  assertRankPartitionLocal(tableName, definition, index);
1691
1698
  if (rankOptions.restrictsCounts) {
@@ -1726,17 +1733,18 @@ const createShardCtxDb = (options) => {
1726
1733
  // eslint-disable-next-line @typescript-eslint/require-await -- DatabaseWriterLike returns Promises (the D1 twin awaits I/O); the body is synchronous SQLite
1727
1734
  async rankBefore(tableName, indexName, rankBeforeOptions) {
1728
1735
  if (isGlobalTable(tableName)) {
1729
- throw new Error(
1736
+ throw new LunoraError(
1737
+ "INTERNAL",
1730
1738
  `rankBefore is not supported on the global (.global()) table '${tableName}' — cross-shard rank cursors apply only to sharded tables`
1731
1739
  );
1732
1740
  }
1733
1741
  const definition = schema.tables[tableName];
1734
1742
  if (!definition) {
1735
- throw new Error(`unknown table: ${tableName}`);
1743
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1736
1744
  }
1737
1745
  const index = definition.rankIndexes?.find((i) => i.name === indexName);
1738
1746
  if (!index) {
1739
- throw new Error(`unknown rankIndex "${indexName}" on table "${tableName}"`);
1747
+ throw new LunoraError("INTERNAL", `unknown rankIndex "${indexName}" on table "${tableName}"`);
1740
1748
  }
1741
1749
  if (rankBeforeOptions.restrictsCounts) {
1742
1750
  throw new CountRlsUnsupportedError(tableName);
@@ -1774,11 +1782,11 @@ const createShardCtxDb = (options) => {
1774
1782
  await global.restore(id);
1775
1783
  return;
1776
1784
  }
1777
- throw new Error(`document not found: ${id}`);
1785
+ throw new LunoraError("INTERNAL", `document not found: ${id}`);
1778
1786
  }
1779
1787
  const field = schema.tables[located.tableName]?.softDeleteMode?.field;
1780
1788
  if (!field) {
1781
- throw new Error(`ctx.db.restore: table "${located.tableName}" is not a .softDelete() table`);
1789
+ throw new LunoraError("INTERNAL", `ctx.db.restore: table "${located.tableName}" is not a .softDelete() table`);
1782
1790
  }
1783
1791
  const wasDeleted = located.row[field] !== null && located.row[field] !== void 0;
1784
1792
  await writer.patch(id, { [field]: null }, expectedTable);
@@ -1794,12 +1802,12 @@ const createShardCtxDb = (options) => {
1794
1802
  await global.replace(id, document);
1795
1803
  return;
1796
1804
  }
1797
- throw new Error(`document not found: ${id}`);
1805
+ throw new LunoraError("INTERNAL", `document not found: ${id}`);
1798
1806
  }
1799
1807
  const { docJson: existingJson, row: previous, tableName } = located;
1800
1808
  const tableDefinition = schema.tables[tableName];
1801
1809
  if (!tableDefinition) {
1802
- throw new Error(`unknown table: ${tableName}`);
1810
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
1803
1811
  }
1804
1812
  assertNoExplicitUndefined("replace", document);
1805
1813
  const creationTime = typeof document["_creationTime"] === "number" ? document["_creationTime"] : clock();
@@ -1,13 +1,14 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const RLS_UNWRAP_SYMBOL = /* @__PURE__ */ Symbol.for("lunora.ctxdb.rls-unwrap");
2
- class RlsRequiredError extends Error {
3
- code = "RLS_REQUIRED";
4
- status = 403;
4
+ class RlsRequiredError extends LunoraError {
5
5
  table;
6
6
  constructor(table) {
7
7
  super(
8
- `ctx.db access to "${table}" is denied: the schema is marked .rls("required"), so this table is protected. Apply RLS with .use(rls(policies)) in the procedure, or mark the table .public() to opt it out.`
8
+ "RLS_REQUIRED",
9
+ `ctx.db access to "${table}" is denied: the schema is marked .rls("required"), so this table is protected. Apply RLS with .use(rls(policies)) in the procedure, or mark the table .public() to opt it out.`,
10
+ { name: "RlsRequiredError" }
9
11
  );
10
- this.name = "RlsRequiredError";
11
12
  this.table = table;
12
13
  }
13
14
  }
@@ -1,8 +1,9 @@
1
+ import { LunoraError, toErrorBody } from '@lunora/errors';
1
2
  import { drizzle } from 'drizzle-orm/durable-sqlite';
2
- import { e as encodeWire, a as awaitWsDrain, t as trySendFrame, d as decodeWire, s as subscriptionListDeltas, b as sendDeltaFrames } from './subscription-delivery-Z6nXHvQN.mjs';
3
+ import { e as encodeWire, a as awaitWsDrain, t as trySendFrame, d as decodeWire, s as subscriptionListDeltas, b as sendDeltaFrames } from './subscription-delivery-CK8qga-k.mjs';
3
4
  import { parseExportShardArgs, parseImportShardArgs } from './exportShardRows-DZEhUeyI.mjs';
4
5
  import { recordAuthEvent, readAuthMetrics } from './AUTH_METRICS_BUCKETS_TABLE-CiHHYeJi.mjs';
5
- import { DATA_MIGRATION_STATE_TABLE, readMigrationStatus } from './DATA_MIGRATION_STATE_TABLE-DfPxn8I0.mjs';
6
+ import { DATA_MIGRATION_STATE_TABLE, readMigrationStatus } from './DATA_MIGRATION_STATE_TABLE-DB3IYUR3.mjs';
6
7
  import { SCAN_DEP, createDependencyTracker, tableFromDepKey } from './SCAN_DEP-DLJF8dsj.mjs';
7
8
  import { readFunctionMetricsTotals, readFunctionMetricIndexHits, recordFunctionMetric, mergeScanAttribution, readFunctionMetrics, readFunctionMetricBuckets } from './FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
8
9
  import { createFanoutCounters, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, summarizeFanoutTopics, readTablePage, facetColumn, FLAGS_FUNCTION_PREFIX, recordFanoutPass, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-DSUQ5fX9.mjs';
@@ -14,10 +15,10 @@ import { stableStringify } from './stableStringify-MydiuScU.mjs';
14
15
  import { redact, standardRules } from '@visulima/redact';
15
16
  import { i as isDevEnvironment, c as buildSettings, b as buildSecurityAudit } from './security-audit-CucgBice.mjs';
16
17
  import { runReadonlySql } from './MAX_SQL_ROWS-dDcFE1YZ.mjs';
17
- import { ConflictError } from './ConflictError-C0STs6bU.mjs';
18
+ import { ConflictError } from './ConflictError-CLoq37xH.mjs';
18
19
  import { e as deleteGlobalShapeSnapshotsForConnection, g as readIdempotent, h as writeIdempotent, t as trimIdempotent, r as readClientWatermark, m as migrateClientWatermark, c as advanceClientWatermark, d as deleteGlobalShapeSnapshot, f as readGlobalShapeSnapshot, w as writeGlobalShapeSnapshot } from './ctx-db-idempotency-BdcNpvY4.mjs';
19
- import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-DSycmnDf.mjs';
20
- import { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-0RaIOy7J.mjs';
20
+ import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-DZSVKRr7.mjs';
21
+ import { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
21
22
 
22
23
  const MAX_BATCH_ENTRIES = 500;
23
24
 
@@ -199,7 +200,7 @@ const shapeRoutingKey = (name, args) => stableStringify({ args: args ?? {}, name
199
200
  const DEFAULT_PROMOTION_THRESHOLDS = { tDown: 4e3, tUp: 8e3 };
200
201
  const nextPromotionState = (current, subscribers, thresholds = DEFAULT_PROMOTION_THRESHOLDS) => {
201
202
  if (thresholds.tDown >= thresholds.tUp) {
202
- throw new Error(`invalid promotion thresholds: tDown (${String(thresholds.tDown)}) must be < tUp (${String(thresholds.tUp)})`);
203
+ throw new LunoraError("INTERNAL", `invalid promotion thresholds: tDown (${String(thresholds.tDown)}) must be < tUp (${String(thresholds.tUp)})`);
203
204
  }
204
205
  if (current === "owned") {
205
206
  return subscribers >= thresholds.tUp ? "promoted" : "owned";
@@ -305,7 +306,7 @@ const envPositiveInt = (env, key, fallback) => {
305
306
  };
306
307
  const RELAY_MULTICAST_IDENTITY = {};
307
308
  const assertNeverFrame = (frame) => {
308
- throw new Error(`unhandled relay frame: ${JSON.stringify(frame)}`);
309
+ throw new LunoraError("INTERNAL", `unhandled relay frame: ${JSON.stringify(frame)}`);
309
310
  };
310
311
  const asRelayNamespace = (value) => {
311
312
  if (value === null || typeof value !== "object") {
@@ -2834,6 +2835,7 @@ class ShardDO {
2834
2835
  return {
2835
2836
  analytics: false,
2836
2837
  auth: false,
2838
+ containers: false,
2837
2839
  flags: false,
2838
2840
  kv: false,
2839
2841
  mail: false,
@@ -4008,27 +4010,11 @@ class ShardDO {
4008
4010
  */
4009
4011
  // eslint-disable-next-line class-methods-use-this -- cohesive DO instance method (groups with the request handlers); kept non-static so subclasses can override the error mapping
4010
4012
  errorToResponse(error) {
4011
- if (error instanceof ConflictError) {
4012
- return jsonResponse({ error: { code: error.code, message: error.message } }, error.status);
4013
- }
4014
- if (error && typeof error === "object" && error.name === "ValidationError") {
4015
- const message = error instanceof Error ? error.message : "validation failed";
4016
- return jsonResponse({ error: { code: "VALIDATION_ERROR", message } }, 400);
4017
- }
4018
- if (error && typeof error === "object" && error.name === "LunoraError") {
4019
- const lunoraError = error;
4020
- const status = typeof lunoraError.status === "number" ? lunoraError.status : 500;
4021
- const body = {
4022
- code: lunoraError.code ?? "INTERNAL",
4023
- message: lunoraError.message ?? "internal error"
4024
- };
4025
- if (lunoraError.data !== void 0) {
4026
- body.data = encodeWire(lunoraError.data);
4027
- }
4028
- return jsonResponse({ error: body }, status);
4013
+ const { body, redacted, status } = toErrorBody(error, { encodeData: encodeWire, fallbackCode: "RPC_FAILED", redactedMessage: "internal error" });
4014
+ if (redacted) {
4015
+ console.error("[@lunora/do] internal error:", error);
4029
4016
  }
4030
- console.error("[@lunora/do] unhandled RPC error:", error);
4031
- return jsonResponse({ error: { code: "RPC_FAILED", message: "internal error" } }, 500);
4017
+ return jsonResponse({ error: body }, status);
4032
4018
  }
4033
4019
  /**
4034
4020
  * Batch dispatch (plan 088). Applies each `calls[]` entry through the SAME
@@ -5006,7 +4992,6 @@ class ShardDO {
5006
4992
  * 4. On normal completion send `{type:"complete"}`; on throw send
5007
4993
  * `{type:"error"}`. Either way drop the controller.
5008
4994
  */
5009
- // eslint-disable-next-line sonarjs/cognitive-complexity -- the stream lifecycle (ack → chunk pump → complete/error) plus the structured-vs-redacted error branch is the wire protocol and reads clearer inline than split across helpers sharing the controller + socket
5010
4995
  async handleStream(ws, id, functionPath, args) {
5011
4996
  const iterable = this.executeStream(functionPath, args);
5012
4997
  if (!iterable) {
@@ -5046,16 +5031,13 @@ class ShardDO {
5046
5031
  ws.send(JSON.stringify({ id, type: "complete" }));
5047
5032
  }
5048
5033
  } catch (error) {
5049
- const { code } = error;
5050
- const isStructured = typeof code === "string";
5051
- if (!isStructured) {
5034
+ const { body, redacted } = toErrorBody(error, { fallbackCode: "INTERNAL_SERVER_ERROR", redactedMessage: "internal error" });
5035
+ if (redacted) {
5052
5036
  console.error("[@lunora/do] unhandled stream error:", error);
5053
5037
  }
5054
- const rawMessage = error instanceof Error ? error.message : String(error);
5055
- const message = isStructured ? rawMessage : "internal error";
5056
5038
  ws.send(
5057
5039
  JSON.stringify({
5058
- error: { code: isStructured ? code : "INTERNAL_SERVER_ERROR", message },
5040
+ error: { code: body.code, message: body.message },
5059
5041
  id,
5060
5042
  type: "error"
5061
5043
  })
@@ -5337,8 +5319,8 @@ class ShardDO {
5337
5319
  resolved = this.resolveShape(shape.name, shape.args ?? {}, identity);
5338
5320
  } catch (error) {
5339
5321
  this.recordShapeError(`shape:seed:${subId}`, error);
5340
- const code = typeof error.code === "string" ? error.code : "SHAPE_RESOLVE_FAILED";
5341
- return { code, message: error instanceof Error ? error.message : "shape resolution failed" };
5322
+ const { body } = toErrorBody(error, { fallbackCode: "SHAPE_RESOLVE_FAILED", redactedMessage: "shape resolution failed" });
5323
+ return { code: body.code, message: body.message };
5342
5324
  }
5343
5325
  if (!resolved) {
5344
5326
  return { code: "SHAPE_NOT_FOUND", message: `shape "${shape.name}" not found or not permitted` };
@@ -5350,8 +5332,8 @@ class ShardDO {
5350
5332
  return await this.seedOpLogShape(ws, subId, shape, resolved);
5351
5333
  } catch (error) {
5352
5334
  this.recordShapeError(`shape:seed:${subId}`, error);
5353
- const code = typeof error.code === "string" ? error.code : "SHAPE_SEED_FAILED";
5354
- return { code, message: error instanceof Error ? error.message : "shape seed failed" };
5335
+ const { body } = toErrorBody(error, { fallbackCode: "SHAPE_SEED_FAILED", redactedMessage: "shape seed failed" });
5336
+ return { code: body.code, message: body.message };
5355
5337
  }
5356
5338
  }
5357
5339
  /**
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import { applySelect } from './applySelect-BvZdFUBT.mjs';
2
3
 
3
4
  const projectChildren = (documents, nested) => nested.select ? applySelect(documents, nested.select, nested.with) : documents;
@@ -27,13 +28,13 @@ const resolveWith = async (options) => {
27
28
  }
28
29
  const parentDefinition = schema.tables[tableName];
29
30
  if (!parentDefinition) {
30
- throw new Error(`unknown table: ${tableName}`);
31
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
31
32
  }
32
33
  const relationMap = parentDefinition.relationMap ?? {};
33
34
  const requireRelation = (name) => {
34
35
  const relation = relationMap[name];
35
36
  if (!relation) {
36
- throw new Error(`unknown relation "${name}" on table "${tableName}"`);
37
+ throw new LunoraError("INTERNAL", `unknown relation "${name}" on table "${tableName}"`);
37
38
  }
38
39
  return relation;
39
40
  };
@@ -145,7 +146,7 @@ const applyRelationOnDelete = async (options, holderTable, relation) => {
145
146
  const applyOnDelete = async (options) => {
146
147
  const { schema, tableName } = options;
147
148
  if (!schema.tables[tableName]) {
148
- throw new Error(`unknown table: ${tableName}`);
149
+ throw new LunoraError("INTERNAL", `unknown table: ${tableName}`);
149
150
  }
150
151
  for (const [holderTable, holderDefinition] of Object.entries(schema.tables)) {
151
152
  const relations = holderDefinition.relationMap;
@@ -1,5 +1,5 @@
1
1
  import { sql } from 'drizzle-orm';
2
- import { matchesStaticWhere } from './AGGREGATE_SQL_FUNCTION-CFk6adSu.mjs';
2
+ import { matchesStaticWhere } from './AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs';
3
3
  import { encodeAggregateKey, foldAggregateTally, aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
4
4
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
5
5
  import { D as DOC_COLUMN, r as rowToDocument, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT } from './do-sql-BCHCWtrD.mjs';
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import { sql } from 'drizzle-orm';
2
3
 
3
4
  const RELATION_EXISTS_KEY = "__relationExists";
@@ -94,7 +95,7 @@ const STRUCTURAL_KEYS = /* @__PURE__ */ new Set(["AND", "NOT", "OR", RELATION_EX
94
95
  const compileStructuralKey = (key, value, strategy) => {
95
96
  if (key === RELATION_EXISTS_KEY) {
96
97
  if (!strategy.relationExists) {
97
- throw new Error("encountered a relation EXISTS marker without a relationExists strategy hook");
98
+ throw new LunoraError("INTERNAL", "encountered a relation EXISTS marker without a relationExists strategy hook");
98
99
  }
99
100
  return strategy.relationExists(value);
100
101
  }
@@ -1,3 +1,5 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const toScheduledFunctionDoc = (record) => {
2
4
  const doc = {
3
5
  args: record["args"] ?? {},
@@ -40,13 +42,13 @@ const toStorageMetadata = (object) => {
40
42
  const createSystemReader = (options = {}) => {
41
43
  const requireScheduler = () => {
42
44
  if (!options.scheduler) {
43
- throw new Error('ctx.db.system.query("_scheduled_functions"): no scheduler configured. Pass `scheduler` to createShardDO().');
45
+ throw new LunoraError("INTERNAL", 'ctx.db.system.query("_scheduled_functions"): no scheduler configured. Pass `scheduler` to createShardDO().');
44
46
  }
45
47
  return options.scheduler;
46
48
  };
47
49
  const requireStorage = () => {
48
50
  if (!options.storage) {
49
- throw new Error('ctx.db.system.query("_storage"): no storage configured. Pass `storage` to createShardDO().');
51
+ throw new LunoraError("INTERNAL", 'ctx.db.system.query("_storage"): no storage configured. Pass `storage` to createShardDO().');
50
52
  }
51
53
  return options.storage;
52
54
  };
@@ -1,7 +1,7 @@
1
1
  import { sql } from 'drizzle-orm';
2
2
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
3
  import { D as DOC_COLUMN, r as rowToDocument, j as jsonPathSql } from './do-sql-BCHCWtrD.mjs';
4
- import { compileWhereSql } from './compileWhereSql-MW_Lk8nJ.mjs';
4
+ import { compileWhereSql } from './compileWhereSql-DE6yfRcQ.mjs';
5
5
  import { s as serializeSqlValue } from './serialize-sql-BlRUoiQe.mjs';
6
6
 
7
7
  const shapeWhereStrategy = { fieldRef: jsonPathSql, serialize: serializeSqlValue };
@@ -1,10 +1,11 @@
1
- import { runExternalSourceTick } from './materializeExternalRows-Bj6EXy50.mjs';
1
+ import { LunoraError } from '@lunora/errors';
2
+ import { runExternalSourceTick } from './materializeExternalRows-DNcvoLRT.mjs';
2
3
 
3
4
  const liftSourceId = (row, options = {}) => {
4
5
  const { idColumn = "id", map } = options;
5
6
  const idValue = row[idColumn];
6
7
  if (idValue === void 0 || idValue === null) {
7
- throw new Error(`external-source: row is missing id column "${idColumn}"`);
8
+ throw new LunoraError("INTERNAL", `external-source: row is missing id column "${idColumn}"`);
8
9
  }
9
10
  if (typeof idValue !== "string" && typeof idValue !== "number" && typeof idValue !== "bigint") {
10
11
  throw new TypeError(`external-source: id column "${idColumn}" must be a string or number`);
@@ -1,5 +1,5 @@
1
- import { applyCdcChanges } from './CDC_LOG_TABLE-DSycmnDf.mjs';
2
- import { s as selectShapeRows } from './ctx-db-shapes-0RaIOy7J.mjs';
1
+ import { applyCdcChanges } from './CDC_LOG_TABLE-DZSVKRr7.mjs';
2
+ import { s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
3
3
  import { diffExternalSource, projectExternalSourceRow } from './diffExternalSource-Cx9HUPJj.mjs';
4
4
  import { stableStringify } from './stableStringify-MydiuScU.mjs';
5
5
 
@@ -1,6 +1,6 @@
1
1
  import { sql } from 'drizzle-orm';
2
2
  import { aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
3
- import { migrateCdcLog, migrateCdcMeta } from './CDC_LOG_TABLE-DSycmnDf.mjs';
3
+ import { migrateCdcLog, migrateCdcMeta } from './CDC_LOG_TABLE-DZSVKRr7.mjs';
4
4
  import { m as migrateClientWatermark, a as migrateIdempotency, b as migrateGlobalShapeSnapshot } from './ctx-db-idempotency-BdcNpvY4.mjs';
5
5
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
6
6
  import { D as DOC_COLUMN, j as jsonPathSql, c as createIndexSql, t as tableColumns, i as isFtsAvailable, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT } from './do-sql-BCHCWtrD.mjs';