@lunora/do 1.0.0-alpha.21 → 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 (23) hide show
  1. package/dist/index.d.mts +28 -90
  2. package/dist/index.d.ts +28 -90
  3. package/dist/index.mjs +17 -17
  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-BfPNp3Jh.mjs → ROOT_DO_SIZE_WARN_BYTES-BQoX61l-.mjs} +18 -37
  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/package.json +2 -1
  23. package/dist/packem_shared/NotFoundError-CMuMZt81.mjs +0 -10
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import { SQL } from 'drizzle-orm';
2
3
  import { DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
3
4
  /**
@@ -110,17 +111,13 @@ interface GroupByEntry {
110
111
  value: AggregateResult;
111
112
  }
112
113
  /**
113
- * Thrown when `count` runs in an RLS-restricted ctx. The structural shape
114
- * (`name: "LunoraError"`, `code`, `status`) lets the runtime's error mapper
115
- * route it without an `instanceof` check, so `@lunora/do` stays free of a
116
- * runtime dependency on `@lunora/server`. Status mirrors the
117
- * `COUNT_RLS_UNSUPPORTED` entry in the `LunoraErrorCode` taxonomy (422):
118
- * the operation is invalid in this context, not malformed.
114
+ * Thrown when `count` runs in an RLS-restricted ctx. A `LunoraError` subclass
115
+ * (`code: "COUNT_RLS_UNSUPPORTED"`, `status: 422`) recognised structurally by the
116
+ * runtime's error mapper (via `isLunoraError`), so `@lunora/do` needs no runtime
117
+ * dependency on `@lunora/server`. 422 = the operation is invalid in this context,
118
+ * not malformed.
119
119
  */
120
- declare class CountRlsUnsupportedError extends Error {
121
- readonly code: string;
122
- override readonly name = "LunoraError";
123
- readonly status: number;
120
+ declare class CountRlsUnsupportedError extends LunoraError {
124
121
  constructor(table?: string);
125
122
  }
126
123
  /**
@@ -761,37 +758,6 @@ declare class ReactiveCache {
761
758
  * everyone. Anonymous/subscription callers pass `null` (their own bucket).
762
759
  */
763
760
  declare const reactiveCacheKey: (functionPath: string, args: Record<string, unknown>, identity: null | string) => string;
764
- /**
765
- * `ctx.db.system` — a best-effort, read-only reader over Lunora's *system*
766
- * tables.
767
- *
768
- * Convex surfaces a handful of read-only system tables (`_scheduled_functions`,
769
- * `_storage`, ...) through `ctx.db.system`. Lunora mirrors that surface, but with
770
- * one load-bearing caveat: **the data these tables expose does not live in the
771
- * shard's SQLite**. Scheduled functions live in the `SchedulerDO`; storage
772
- * objects live in R2. So unlike `ctx.db.&lt;table>` — which reads the same
773
- * transactional SQLite snapshot the mutation writes into — `ctx.db.system`
774
- * reaches across a network/DO boundary on every call.
775
- *
776
- * Consequences callers must keep in mind:
777
- *
778
- * Asynchronous + eventually consistent: each `collect()` / `get()` issues a fresh
779
- * read against the backing source. A row a mutation just scheduled may not yet be
780
- * visible; a job that just fired may still appear pending.
781
- *
782
- * NOT part of the transaction snapshot: reading a system table inside a mutation
783
- * does not pin it; there is no OCC guard and no subscription dependency is
784
- * recorded. Treat the result as a point-in-time best effort.
785
- *
786
- * Read-only: there is no `insert`/`patch`/`delete`. Mutate scheduled jobs via
787
- * `ctx.scheduler`, storage objects via `ctx.storage`.
788
- *
789
- * The reader is intentionally minimal: `query(table).collect()` returns the full
790
- * list (no indexes, no filtering, no pagination) and `get(table, id)` resolves a
791
- * single row. That matches Convex's system-table read surface closely enough for
792
- * the studio / introspection use cases without re-implementing the query
793
- * planner against a remote source.
794
- */
795
761
  /** The system tables `ctx.db.system` can read. */
796
762
  type SystemTableName = "_scheduled_functions" | "_storage";
797
763
  /**
@@ -1893,14 +1859,12 @@ interface DatabaseWriterLike {
1893
1859
  system?: SystemDatabaseReader;
1894
1860
  }
1895
1861
  /**
1896
- * Thrown by `.unique()` when more than one row matches. Like {@link ConflictError}
1897
- * / `NotFoundError`, `code` / `status` are declared as own properties so the
1898
- * cross-package structural error mapper renders it as a 400 without an
1899
- * `instanceof` check against `@lunora/do`.
1862
+ * Thrown by `.unique()` when more than one row matches. A `LunoraError` subclass
1863
+ * (`code: "NOT_UNIQUE"`, `status: 400`) recognised structurally by the
1864
+ * cross-package transport mapper (via `isLunoraError`) without an `instanceof`
1865
+ * check against `@lunora/do`.
1900
1866
  */
1901
- declare class NotUniqueError extends Error {
1902
- readonly code: string;
1903
- readonly status: number;
1867
+ declare class NotUniqueError extends LunoraError {
1904
1868
  constructor(message?: string);
1905
1869
  }
1906
1870
  /**
@@ -3449,13 +3413,11 @@ declare const clearCapturedMail: (sql: SqlExec) => {
3449
3413
  /**
3450
3414
  * Thrown by `findFirstOrThrow` when no document matches the query.
3451
3415
  *
3452
- * Like `ConflictError`, `code` / `status` are declared as own properties
3453
- * so the cross-package structural error mapper maps it to a 404 without an
3454
- * `instanceof` check against `@lunora/do`.
3416
+ * A `LunoraError` subclass (`code: "NOT_FOUND"`, `status: 404`) so the
3417
+ * cross-package transport mapper maps it to a 404 structurally (via
3418
+ * `isLunoraError`) without an `instanceof` check against `@lunora/do`.
3455
3419
  */
3456
- declare class NotFoundError extends Error {
3457
- readonly code: string;
3458
- readonly status: number;
3420
+ declare class NotFoundError extends LunoraError {
3459
3421
  constructor(message?: string);
3460
3422
  }
3461
3423
  /**
@@ -3691,24 +3653,6 @@ interface LogEventInput {
3691
3653
  * that want the raw values.
3692
3654
  */
3693
3655
  /**
3694
- * Secure-by-default write-path guard for `ctx.db`.
3695
- *
3696
- * When a schema is marked `defineSchema(...).rls("required")`, the generated
3697
- * user-facing ctx wraps its writer with {@link guardWriter}. The guard denies
3698
- * every read/write against a PROTECTED table (any table not marked `.public()`)
3699
- * so a procedure that forgot `.use(rls(policies))` fails CLOSED — it can never
3700
- * silently see or mutate a protected table through the unwrapped writer.
3701
- *
3702
- * The guard is transparent to the RLS middleware: it hangs the UNWRAPPED writer
3703
- * off {@link RLS_UNWRAP_SYMBOL}, so `@lunora/server`'s `rls()` middleware
3704
- * recovers the raw writer (via the same `Symbol.for` key — no cross-package
3705
- * import) to evaluate policies and issue policy-filtered reads without tripping
3706
- * the guard. Only procedures that NEVER engaged RLS keep talking to the guard.
3707
- *
3708
- * Admin / migration / studio writers (built from `createShardCtxDb` WITHOUT
3709
- * `enforceRls`) are never guarded — they are trusted system paths.
3710
- */
3711
- /**
3712
3656
  * Well-known symbol the guard hangs the unwrapped writer off of. `Symbol.for`
3713
3657
  * (the cross-realm global registry) lets `@lunora/server`'s RLS middleware read
3714
3658
  * it WITHOUT importing this module — both sides reference the same registered
@@ -3717,14 +3661,12 @@ interface LogEventInput {
3717
3661
  declare const RLS_UNWRAP_SYMBOL: symbol;
3718
3662
  /**
3719
3663
  * Thrown when a raw (non-RLS) handler touches a protected table under a
3720
- * `.rls("required")` schema. `code` / `status` / `table` are own properties (not
3721
- * just inherited prototype state) so structural callers across packages — which
3722
- * deliberately avoid a hard `@lunora/do` runtime dependency — recognise the
3723
- * shape without an `instanceof` check (mirrors `ConflictError`).
3724
- */
3725
- declare class RlsRequiredError extends Error {
3726
- readonly code: string;
3727
- readonly status: number;
3664
+ * `.rls("required")` schema. A `LunoraError` subclass (`code: "RLS_REQUIRED"`,
3665
+ * `status: 403`) recognised structurally across packages (via `isLunoraError`)
3666
+ * which deliberately avoid a hard `@lunora/do` runtime dependency — without an
3667
+ * `instanceof` check. `table` is kept as an own property.
3668
+ */
3669
+ declare class RlsRequiredError extends LunoraError {
3728
3670
  readonly table: string;
3729
3671
  constructor(table: string);
3730
3672
  }
@@ -6587,18 +6529,14 @@ type ConflictKind = "conflict" | "occ" | "restrict" | "trigger" | "unique";
6587
6529
  * Thrown by mutation handlers when an optimistic-concurrency check fails (or a
6588
6530
  * related write guard trips — see {@link ConflictKind}).
6589
6531
  *
6590
- * The runtime maps this to a 409 response so clients can decide whether to
6591
- * refetch + retry or surface the conflict. `code` / `status` / `kind` are
6592
- * declared as own properties (not just inherited prototype state) so structural
6593
- * callers across packages which deliberately avoid taking a hard runtime
6594
- * dependency on `@lunora/do` — can recognise the shape without an `instanceof`
6595
- * check.
6596
- */
6597
- declare class ConflictError extends Error {
6598
- readonly code: string;
6532
+ * A `LunoraError` subclass (`code: "CONFLICT"`, `status: 409`) so the runtime/DO
6533
+ * transport mappers recognise it structurally (via `isLunoraError`) structural
6534
+ * callers across packages still avoid a hard `instanceof` dependency on
6535
+ * `@lunora/do`. `kind` is kept as an own property for the metrics layer.
6536
+ */
6537
+ declare class ConflictError extends LunoraError {
6599
6538
  /** Why the conflict fired; `occ` is the contention signal the metrics layer counts. */
6600
6539
  readonly kind: ConflictKind;
6601
- readonly status: number;
6602
6540
  constructor(message?: string, kind?: ConflictKind);
6603
6541
  }
6604
6542
  /**
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { LunoraError } from '@lunora/errors';
1
2
  import { SQL } from 'drizzle-orm';
2
3
  import { DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
3
4
  /**
@@ -110,17 +111,13 @@ interface GroupByEntry {
110
111
  value: AggregateResult;
111
112
  }
112
113
  /**
113
- * Thrown when `count` runs in an RLS-restricted ctx. The structural shape
114
- * (`name: "LunoraError"`, `code`, `status`) lets the runtime's error mapper
115
- * route it without an `instanceof` check, so `@lunora/do` stays free of a
116
- * runtime dependency on `@lunora/server`. Status mirrors the
117
- * `COUNT_RLS_UNSUPPORTED` entry in the `LunoraErrorCode` taxonomy (422):
118
- * the operation is invalid in this context, not malformed.
114
+ * Thrown when `count` runs in an RLS-restricted ctx. A `LunoraError` subclass
115
+ * (`code: "COUNT_RLS_UNSUPPORTED"`, `status: 422`) recognised structurally by the
116
+ * runtime's error mapper (via `isLunoraError`), so `@lunora/do` needs no runtime
117
+ * dependency on `@lunora/server`. 422 = the operation is invalid in this context,
118
+ * not malformed.
119
119
  */
120
- declare class CountRlsUnsupportedError extends Error {
121
- readonly code: string;
122
- override readonly name = "LunoraError";
123
- readonly status: number;
120
+ declare class CountRlsUnsupportedError extends LunoraError {
124
121
  constructor(table?: string);
125
122
  }
126
123
  /**
@@ -761,37 +758,6 @@ declare class ReactiveCache {
761
758
  * everyone. Anonymous/subscription callers pass `null` (their own bucket).
762
759
  */
763
760
  declare const reactiveCacheKey: (functionPath: string, args: Record<string, unknown>, identity: null | string) => string;
764
- /**
765
- * `ctx.db.system` — a best-effort, read-only reader over Lunora's *system*
766
- * tables.
767
- *
768
- * Convex surfaces a handful of read-only system tables (`_scheduled_functions`,
769
- * `_storage`, ...) through `ctx.db.system`. Lunora mirrors that surface, but with
770
- * one load-bearing caveat: **the data these tables expose does not live in the
771
- * shard's SQLite**. Scheduled functions live in the `SchedulerDO`; storage
772
- * objects live in R2. So unlike `ctx.db.&lt;table>` — which reads the same
773
- * transactional SQLite snapshot the mutation writes into — `ctx.db.system`
774
- * reaches across a network/DO boundary on every call.
775
- *
776
- * Consequences callers must keep in mind:
777
- *
778
- * Asynchronous + eventually consistent: each `collect()` / `get()` issues a fresh
779
- * read against the backing source. A row a mutation just scheduled may not yet be
780
- * visible; a job that just fired may still appear pending.
781
- *
782
- * NOT part of the transaction snapshot: reading a system table inside a mutation
783
- * does not pin it; there is no OCC guard and no subscription dependency is
784
- * recorded. Treat the result as a point-in-time best effort.
785
- *
786
- * Read-only: there is no `insert`/`patch`/`delete`. Mutate scheduled jobs via
787
- * `ctx.scheduler`, storage objects via `ctx.storage`.
788
- *
789
- * The reader is intentionally minimal: `query(table).collect()` returns the full
790
- * list (no indexes, no filtering, no pagination) and `get(table, id)` resolves a
791
- * single row. That matches Convex's system-table read surface closely enough for
792
- * the studio / introspection use cases without re-implementing the query
793
- * planner against a remote source.
794
- */
795
761
  /** The system tables `ctx.db.system` can read. */
796
762
  type SystemTableName = "_scheduled_functions" | "_storage";
797
763
  /**
@@ -1893,14 +1859,12 @@ interface DatabaseWriterLike {
1893
1859
  system?: SystemDatabaseReader;
1894
1860
  }
1895
1861
  /**
1896
- * Thrown by `.unique()` when more than one row matches. Like {@link ConflictError}
1897
- * / `NotFoundError`, `code` / `status` are declared as own properties so the
1898
- * cross-package structural error mapper renders it as a 400 without an
1899
- * `instanceof` check against `@lunora/do`.
1862
+ * Thrown by `.unique()` when more than one row matches. A `LunoraError` subclass
1863
+ * (`code: "NOT_UNIQUE"`, `status: 400`) recognised structurally by the
1864
+ * cross-package transport mapper (via `isLunoraError`) without an `instanceof`
1865
+ * check against `@lunora/do`.
1900
1866
  */
1901
- declare class NotUniqueError extends Error {
1902
- readonly code: string;
1903
- readonly status: number;
1867
+ declare class NotUniqueError extends LunoraError {
1904
1868
  constructor(message?: string);
1905
1869
  }
1906
1870
  /**
@@ -3449,13 +3413,11 @@ declare const clearCapturedMail: (sql: SqlExec) => {
3449
3413
  /**
3450
3414
  * Thrown by `findFirstOrThrow` when no document matches the query.
3451
3415
  *
3452
- * Like `ConflictError`, `code` / `status` are declared as own properties
3453
- * so the cross-package structural error mapper maps it to a 404 without an
3454
- * `instanceof` check against `@lunora/do`.
3416
+ * A `LunoraError` subclass (`code: "NOT_FOUND"`, `status: 404`) so the
3417
+ * cross-package transport mapper maps it to a 404 structurally (via
3418
+ * `isLunoraError`) without an `instanceof` check against `@lunora/do`.
3455
3419
  */
3456
- declare class NotFoundError extends Error {
3457
- readonly code: string;
3458
- readonly status: number;
3420
+ declare class NotFoundError extends LunoraError {
3459
3421
  constructor(message?: string);
3460
3422
  }
3461
3423
  /**
@@ -3691,24 +3653,6 @@ interface LogEventInput {
3691
3653
  * that want the raw values.
3692
3654
  */
3693
3655
  /**
3694
- * Secure-by-default write-path guard for `ctx.db`.
3695
- *
3696
- * When a schema is marked `defineSchema(...).rls("required")`, the generated
3697
- * user-facing ctx wraps its writer with {@link guardWriter}. The guard denies
3698
- * every read/write against a PROTECTED table (any table not marked `.public()`)
3699
- * so a procedure that forgot `.use(rls(policies))` fails CLOSED — it can never
3700
- * silently see or mutate a protected table through the unwrapped writer.
3701
- *
3702
- * The guard is transparent to the RLS middleware: it hangs the UNWRAPPED writer
3703
- * off {@link RLS_UNWRAP_SYMBOL}, so `@lunora/server`'s `rls()` middleware
3704
- * recovers the raw writer (via the same `Symbol.for` key — no cross-package
3705
- * import) to evaluate policies and issue policy-filtered reads without tripping
3706
- * the guard. Only procedures that NEVER engaged RLS keep talking to the guard.
3707
- *
3708
- * Admin / migration / studio writers (built from `createShardCtxDb` WITHOUT
3709
- * `enforceRls`) are never guarded — they are trusted system paths.
3710
- */
3711
- /**
3712
3656
  * Well-known symbol the guard hangs the unwrapped writer off of. `Symbol.for`
3713
3657
  * (the cross-realm global registry) lets `@lunora/server`'s RLS middleware read
3714
3658
  * it WITHOUT importing this module — both sides reference the same registered
@@ -3717,14 +3661,12 @@ interface LogEventInput {
3717
3661
  declare const RLS_UNWRAP_SYMBOL: symbol;
3718
3662
  /**
3719
3663
  * Thrown when a raw (non-RLS) handler touches a protected table under a
3720
- * `.rls("required")` schema. `code` / `status` / `table` are own properties (not
3721
- * just inherited prototype state) so structural callers across packages — which
3722
- * deliberately avoid a hard `@lunora/do` runtime dependency — recognise the
3723
- * shape without an `instanceof` check (mirrors `ConflictError`).
3724
- */
3725
- declare class RlsRequiredError extends Error {
3726
- readonly code: string;
3727
- readonly status: number;
3664
+ * `.rls("required")` schema. A `LunoraError` subclass (`code: "RLS_REQUIRED"`,
3665
+ * `status: 403`) recognised structurally across packages (via `isLunoraError`)
3666
+ * which deliberately avoid a hard `@lunora/do` runtime dependency — without an
3667
+ * `instanceof` check. `table` is kept as an own property.
3668
+ */
3669
+ declare class RlsRequiredError extends LunoraError {
3728
3670
  readonly table: string;
3729
3671
  constructor(table: string);
3730
3672
  }
@@ -6587,18 +6529,14 @@ type ConflictKind = "conflict" | "occ" | "restrict" | "trigger" | "unique";
6587
6529
  * Thrown by mutation handlers when an optimistic-concurrency check fails (or a
6588
6530
  * related write guard trips — see {@link ConflictKind}).
6589
6531
  *
6590
- * The runtime maps this to a 409 response so clients can decide whether to
6591
- * refetch + retry or surface the conflict. `code` / `status` / `kind` are
6592
- * declared as own properties (not just inherited prototype state) so structural
6593
- * callers across packages which deliberately avoid taking a hard runtime
6594
- * dependency on `@lunora/do` — can recognise the shape without an `instanceof`
6595
- * check.
6596
- */
6597
- declare class ConflictError extends Error {
6598
- readonly code: string;
6532
+ * A `LunoraError` subclass (`code: "CONFLICT"`, `status: 409`) so the runtime/DO
6533
+ * transport mappers recognise it structurally (via `isLunoraError`) structural
6534
+ * callers across packages still avoid a hard `instanceof` dependency on
6535
+ * `@lunora/do`. `kind` is kept as an own property for the metrics layer.
6536
+ */
6537
+ declare class ConflictError extends LunoraError {
6599
6538
  /** Why the conflict fired; `occ` is the contention signal the metrics layer counts. */
6600
6539
  readonly kind: ConflictKind;
6601
- readonly status: number;
6602
6540
  constructor(message?: string, kind?: ConflictKind);
6603
6541
  }
6604
6542
  /**
package/dist/index.mjs CHANGED
@@ -1,40 +1,40 @@
1
1
  export { exportShardRows, exportShardTable, importShardRows, parseExportShardArgs, parseImportShardArgs, selectExportTables, validateImportRow } from './packem_shared/exportShardRows-DZEhUeyI.mjs';
2
- export { AGGREGATE_SQL_FUNCTION, aggregateSqlFunction, matchesStaticWhere, normalizeCountArgument, throwingScheduler } from './packem_shared/AGGREGATE_SQL_FUNCTION-CFk6adSu.mjs';
2
+ export { AGGREGATE_SQL_FUNCTION, aggregateSqlFunction, matchesStaticWhere, normalizeCountArgument, throwingScheduler } from './packem_shared/AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs';
3
3
  export { aggregateTableName, coerceAggregateNumber, encodeAggregateKey, foldAggregateTally, readAggregateValue } from './packem_shared/aggregateTableName-CxNqY1Sl.mjs';
4
- export { CountRlsUnsupportedError, mergeWhere, planAggregateLookup, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy } from './packem_shared/CountRlsUnsupportedError-28ZvvwKS.mjs';
4
+ export { CountRlsUnsupportedError, mergeWhere, planAggregateLookup, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy } from './packem_shared/CountRlsUnsupportedError-BGxj0pgS.mjs';
5
5
  export { AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, ensureAuthMetricsTables, readAuthMetrics, recordAuthEvent } from './packem_shared/AUTH_METRICS_BUCKETS_TABLE-CiHHYeJi.mjs';
6
- export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/NotUniqueError-Do5h_jiW.mjs';
7
- export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } from './packem_shared/DATA_MIGRATION_STATE_TABLE-DfPxn8I0.mjs';
6
+ export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/NotUniqueError-D1U5SBFR.mjs';
7
+ export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } from './packem_shared/DATA_MIGRATION_STATE_TABLE-DB3IYUR3.mjs';
8
8
  export { SCAN_DEP, createDependencyTracker, depKey } from './packem_shared/SCAN_DEP-DLJF8dsj.mjs';
9
9
  export { renderSql } from './packem_shared/renderSql-D6eUcn2N.mjs';
10
10
  export { diffExternalSource } from './packem_shared/diffExternalSource-Cx9HUPJj.mjs';
11
- export { materializeExternalRows, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-Bj6EXy50.mjs';
12
- export { isSourceDue, liftSourceId, pullExternalSourceTick } from './packem_shared/isSourceDue-9mJRJ9Ld.mjs';
11
+ export { materializeExternalRows, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-DNcvoLRT.mjs';
12
+ export { isSourceDue, liftSourceId, pullExternalSourceTick } from './packem_shared/isSourceDue-BptUN8CR.mjs';
13
13
  export { FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, ensureFunctionMetricsTables, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, recordFunctionMetric } from './packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
14
14
  export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-DSUQ5fX9.mjs';
15
15
  export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
16
16
  export { MAIL_RETENTION, MAIL_TABLE, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail } from './packem_shared/MAIL_RETENTION-CPpgl-dX.mjs';
17
- export { default as NotFoundError } from './packem_shared/NotFoundError-CMuMZt81.mjs';
17
+ export { default as NotFoundError } from './packem_shared/NotFoundError-C70b9hLw.mjs';
18
18
  export { armRestore, readBookmark } from './packem_shared/armRestore-BJk53Ro8.mjs';
19
19
  export { applySelect, buildSeekWhere, decodeCursor, encodeCursor, normalizeOrderKeys, softDeleteScope } from './packem_shared/applySelect-BvZdFUBT.mjs';
20
20
  export { RANK_TIEBREAK, encodePartitionKey, matchesRankStaticWhere, rankTableName, resolveRankPartition, sortColumnName } from './packem_shared/RANK_TIEBREAK-CXhdcA1o.mjs';
21
21
  export { ReactiveCache, reactiveCacheKey } from './packem_shared/ReactiveCache-BYlSGY0N.mjs';
22
22
  export { serveRelationFanout } from './packem_shared/serveRelationFanout-C5axmGDj.mjs';
23
- export { DEFAULT_MAX_RELATION_KEYS, assertFlatPredicate, assertShapeShardable, containsRelationPredicate, isRelationPredicate, resolveRelationPredicates } from './packem_shared/DEFAULT_MAX_RELATION_KEYS-DU-Y4-LJ.mjs';
24
- export { applyOnDelete, fanOutScalarCounts, resolveWith, runRowValidators } from './packem_shared/applyOnDelete-BQ-8ZlZ1.mjs';
25
- export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_shared/RLS_UNWRAP_SYMBOL-EtGQdC9d.mjs';
23
+ export { DEFAULT_MAX_RELATION_KEYS, assertFlatPredicate, assertShapeShardable, containsRelationPredicate, isRelationPredicate, resolveRelationPredicates } from './packem_shared/DEFAULT_MAX_RELATION_KEYS-CjM9Y1_G.mjs';
24
+ export { applyOnDelete, fanOutScalarCounts, resolveWith, runRowValidators } from './packem_shared/applyOnDelete-CAwZfp-5.mjs';
25
+ export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_shared/RLS_UNWRAP_SYMBOL-DnjkqVgY.mjs';
26
26
  export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/buildFtsMatch-BLEMawrp.mjs';
27
27
  export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
28
28
  export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-ilPZsVwu.mjs';
29
- export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-BfPNp3Jh.mjs';
29
+ export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-BQoX61l-.mjs';
30
30
  export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-BsAbi5Mn.mjs';
31
31
  export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/MAX_SQL_ROWS-dDcFE1YZ.mjs';
32
- export { createSystemReader } from './packem_shared/createSystemReader-8CzSZP9V.mjs';
33
- export { ConflictError } from './packem_shared/ConflictError-C0STs6bU.mjs';
32
+ export { createSystemReader } from './packem_shared/createSystemReader-D12eNH13.mjs';
33
+ export { ConflictError } from './packem_shared/ConflictError-CLoq37xH.mjs';
34
34
  export { hasTrigger, runTriggers } from './packem_shared/hasTrigger-5N6_Fx0A.mjs';
35
- export { compileWhereSql } from './packem_shared/compileWhereSql-MW_Lk8nJ.mjs';
36
- export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-DSycmnDf.mjs';
37
- export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-BZsOqDXP.mjs';
38
- export { runShardMigrations } from './packem_shared/runShardMigrations-nIwoQeOK.mjs';
35
+ export { compileWhereSql } from './packem_shared/compileWhereSql-DE6yfRcQ.mjs';
36
+ export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-DZSVKRr7.mjs';
37
+ export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-DDoT-UUI.mjs';
38
+ export { runShardMigrations } from './packem_shared/runShardMigrations-DeZr0KZp.mjs';
39
39
  export { stableStringify } from './packem_shared/stableStringify-MydiuScU.mjs';
40
40
  export { s as subscriptionListDeltas } from './packem_shared/subscription-delivery-CK8qga-k.mjs';
@@ -1,3 +1,5 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const COUNT_OPTION_KEYS = /* @__PURE__ */ new Set(["baseWhere", "relationBaseWhere", "restrictsCounts", "where"]);
2
4
  const matchesStaticWhere = (document, predicate) => {
3
5
  for (const [field, expected] of Object.entries(predicate)) {
@@ -38,16 +40,16 @@ const AGGREGATE_SQL_FUNCTION = { avg: "AVG", count: "COUNT", max: "MAX", min: "M
38
40
  const aggregateSqlFunction = (op) => {
39
41
  const sqlFunction = AGGREGATE_SQL_FUNCTION[op];
40
42
  if (sqlFunction === void 0) {
41
- throw new Error(`unknown aggregate op "${op}": expected one of ${Object.keys(AGGREGATE_SQL_FUNCTION).join(", ")}`);
43
+ throw new LunoraError("INTERNAL", `unknown aggregate op "${op}": expected one of ${Object.keys(AGGREGATE_SQL_FUNCTION).join(", ")}`);
42
44
  }
43
45
  return sqlFunction;
44
46
  };
45
47
  const throwingScheduler = {
46
48
  runAfter: () => {
47
- throw new Error("ctx.scheduler: no scheduler configured for triggers. Pass `scheduler` to the ctx-db factory.");
49
+ throw new LunoraError("INTERNAL", "ctx.scheduler: no scheduler configured for triggers. Pass `scheduler` to the ctx-db factory.");
48
50
  },
49
51
  runAt: () => {
50
- throw new Error("ctx.scheduler: no scheduler configured for triggers. Pass `scheduler` to the ctx-db factory.");
52
+ throw new LunoraError("INTERNAL", "ctx.scheduler: no scheduler configured for triggers. Pass `scheduler` to the ctx-db factory.");
51
53
  }
52
54
  };
53
55
 
@@ -1,6 +1,6 @@
1
1
  import { sql } from 'drizzle-orm';
2
2
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
- import { ConflictError } from './ConflictError-C0STs6bU.mjs';
3
+ import { ConflictError } from './ConflictError-CLoq37xH.mjs';
4
4
 
5
5
  const CDC_LOG_TABLE = "__cdc_log";
6
6
  const migrateCdcLog = (sql$1) => {
@@ -1,11 +1,10 @@
1
- class ConflictError extends Error {
2
- code = "CONFLICT";
1
+ import { LunoraError } from '@lunora/errors';
2
+
3
+ class ConflictError extends LunoraError {
3
4
  /** Why the conflict fired; `occ` is the contention signal the metrics layer counts. */
4
5
  kind;
5
- status = 409;
6
6
  constructor(message = "Optimistic concurrency conflict", kind = "conflict") {
7
- super(message);
8
- this.name = "ConflictError";
7
+ super("CONFLICT", message, { name: "ConflictError" });
9
8
  this.kind = kind;
10
9
  }
11
10
  }
@@ -1,9 +1,9 @@
1
- class CountRlsUnsupportedError extends Error {
2
- code = "COUNT_RLS_UNSUPPORTED";
3
- name = "LunoraError";
4
- status = 422;
1
+ import { LunoraError } from '@lunora/errors';
2
+
3
+ class CountRlsUnsupportedError extends LunoraError {
5
4
  constructor(table) {
6
5
  super(
6
+ "COUNT_RLS_UNSUPPORTED",
7
7
  table === void 0 ? "count() is not supported in an RLS-restricted context" : `count() is not supported on table "${table}" inside an RLS-restricted context`
8
8
  );
9
9
  }
@@ -1,3 +1,5 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
1
3
  const DATA_MIGRATION_STATE_TABLE = "__lunora_migrations";
2
4
  const DEFAULT_BATCH_SIZE = 100;
3
5
  const STALE_CLAIM_TIMEOUT_MS = 3e4;
@@ -128,7 +130,7 @@ const runDataMigration = async (options) => {
128
130
  const batchSize = options.batchSize ?? migration.batchSize ?? DEFAULT_BATCH_SIZE;
129
131
  const transform = direction === "up" ? migration.up : migration.down;
130
132
  if (!transform) {
131
- throw new Error(`data migration "${migration.id}" has no \`${direction}\` transform`);
133
+ throw new LunoraError("INTERNAL", `data migration "${migration.id}" has no \`${direction}\` transform`);
132
134
  }
133
135
  let cursor = null;
134
136
  let processed = 0;
@@ -1,4 +1,5 @@
1
- import { distinctValues } from './applyOnDelete-BQ-8ZlZ1.mjs';
1
+ import { LunoraError } from '@lunora/errors';
2
+ import { distinctValues } from './applyOnDelete-CAwZfp-5.mjs';
2
3
 
3
4
  const RELATION_EXISTS_KEY = "__relationExists";
4
5
 
@@ -43,7 +44,7 @@ const containsRelationPredicate = (where, schema, tableName) => {
43
44
  };
44
45
  const assertFlatPredicate = (where, schema, tableName, op) => {
45
46
  if (where && containsRelationPredicate(where, schema, tableName)) {
46
- throw new Error(`relation-crossing predicates are not supported in ${op}() — use them in findMany/findFirst or an RLS read policy`);
47
+ throw new LunoraError("INTERNAL", `relation-crossing predicates are not supported in ${op}() — use them in findMany/findFirst or an RLS read policy`);
47
48
  }
48
49
  };
49
50
  const projectChildKeys = async (relation, childWhere, projectField, context, escalatable) => {
@@ -58,7 +59,8 @@ const projectChildKeys = async (relation, childWhere, projectField, context, esc
58
59
  if (escalatable) {
59
60
  return KEY_OVERFLOW;
60
61
  }
61
- throw new Error(
62
+ throw new LunoraError(
63
+ "INTERNAL",
62
64
  `relation predicate on "${relation.table}" matched ${String(keys.length)} rows, exceeding the ${String(context.maxRelationKeys)}-key limit; narrow the predicate (a same-shard EXISTS push-down lifts this cap)`
63
65
  );
64
66
  }
@@ -67,7 +69,7 @@ const projectChildKeys = async (relation, childWhere, projectField, context, esc
67
69
  const compileOperator = async (operator, relation, childWhere, context, escalatable) => {
68
70
  const meta = RELATION_OPERATOR_META[operator];
69
71
  if (!meta) {
70
- throw new Error(`unknown relation operator "${operator}"`);
72
+ throw new LunoraError("INTERNAL", `unknown relation operator "${operator}"`);
71
73
  }
72
74
  const { clause, project } = joinColumns(relation);
73
75
  const keys = await projectChildKeys(relation, meta.negateChild ? { NOT: childWhere } : childWhere, project, context, escalatable);
@@ -85,7 +87,7 @@ const compileOperator = async (operator, relation, childWhere, context, escalata
85
87
  const buildExistsMarker = async (operator, relation, childWhere, parentTable, context) => {
86
88
  const meta = RELATION_OPERATOR_META[operator];
87
89
  if (!meta) {
88
- throw new Error(`unknown relation operator "${operator}"`);
90
+ throw new LunoraError("INTERNAL", `unknown relation operator "${operator}"`);
89
91
  }
90
92
  const base = context.relationBaseWhere?.(relation.table);
91
93
  const predicatePart = meta.negateChild ? { NOT: childWhere } : childWhere;
@@ -97,7 +99,7 @@ const buildExistsMarker = async (operator, relation, childWhere, parentTable, co
97
99
  const assertCardinality = (operator, name, relation) => {
98
100
  const meta = RELATION_OPERATOR_META[operator];
99
101
  if (meta && meta.kind !== relation.kind) {
100
- throw new Error(`relation operator "${operator}" requires a to-${meta.kind} relation, but "${name}" is to-${relation.kind}`);
102
+ throw new LunoraError("INTERNAL", `relation operator "${operator}" requires a to-${meta.kind} relation, but "${name}" is to-${relation.kind}`);
101
103
  }
102
104
  };
103
105
  const resolveRelationNode = async (name, relation, predicate, parentTable, context) => {
@@ -0,0 +1,9 @@
1
+ import { LunoraError } from '@lunora/errors';
2
+
3
+ class NotFoundError extends LunoraError {
4
+ constructor(message = "Document not found") {
5
+ super("NOT_FOUND", message, { name: "NotFoundError" });
6
+ }
7
+ }
8
+
9
+ export { NotFoundError as default };
@@ -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
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") {
@@ -4009,27 +4010,11 @@ class ShardDO {
4009
4010
  */
4010
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
4011
4012
  errorToResponse(error) {
4012
- if (error instanceof ConflictError) {
4013
- return jsonResponse({ error: { code: error.code, message: error.message } }, error.status);
4014
- }
4015
- if (error && typeof error === "object" && error.name === "ValidationError") {
4016
- const message = error instanceof Error ? error.message : "validation failed";
4017
- return jsonResponse({ error: { code: "VALIDATION_ERROR", message } }, 400);
4018
- }
4019
- if (error && typeof error === "object" && error.name === "LunoraError") {
4020
- const lunoraError = error;
4021
- const status = typeof lunoraError.status === "number" ? lunoraError.status : 500;
4022
- const body = {
4023
- code: lunoraError.code ?? "INTERNAL",
4024
- message: lunoraError.message ?? "internal error"
4025
- };
4026
- if (lunoraError.data !== void 0) {
4027
- body.data = encodeWire(lunoraError.data);
4028
- }
4029
- 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);
4030
4016
  }
4031
- console.error("[@lunora/do] unhandled RPC error:", error);
4032
- return jsonResponse({ error: { code: "RPC_FAILED", message: "internal error" } }, 500);
4017
+ return jsonResponse({ error: body }, status);
4033
4018
  }
4034
4019
  /**
4035
4020
  * Batch dispatch (plan 088). Applies each `calls[]` entry through the SAME
@@ -5007,7 +4992,6 @@ class ShardDO {
5007
4992
  * 4. On normal completion send `{type:"complete"}`; on throw send
5008
4993
  * `{type:"error"}`. Either way drop the controller.
5009
4994
  */
5010
- // 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
5011
4995
  async handleStream(ws, id, functionPath, args) {
5012
4996
  const iterable = this.executeStream(functionPath, args);
5013
4997
  if (!iterable) {
@@ -5047,16 +5031,13 @@ class ShardDO {
5047
5031
  ws.send(JSON.stringify({ id, type: "complete" }));
5048
5032
  }
5049
5033
  } catch (error) {
5050
- const { code } = error;
5051
- const isStructured = typeof code === "string";
5052
- if (!isStructured) {
5034
+ const { body, redacted } = toErrorBody(error, { fallbackCode: "INTERNAL_SERVER_ERROR", redactedMessage: "internal error" });
5035
+ if (redacted) {
5053
5036
  console.error("[@lunora/do] unhandled stream error:", error);
5054
5037
  }
5055
- const rawMessage = error instanceof Error ? error.message : String(error);
5056
- const message = isStructured ? rawMessage : "internal error";
5057
5038
  ws.send(
5058
5039
  JSON.stringify({
5059
- error: { code: isStructured ? code : "INTERNAL_SERVER_ERROR", message },
5040
+ error: { code: body.code, message: body.message },
5060
5041
  id,
5061
5042
  type: "error"
5062
5043
  })
@@ -5338,8 +5319,8 @@ class ShardDO {
5338
5319
  resolved = this.resolveShape(shape.name, shape.args ?? {}, identity);
5339
5320
  } catch (error) {
5340
5321
  this.recordShapeError(`shape:seed:${subId}`, error);
5341
- const code = typeof error.code === "string" ? error.code : "SHAPE_RESOLVE_FAILED";
5342
- 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 };
5343
5324
  }
5344
5325
  if (!resolved) {
5345
5326
  return { code: "SHAPE_NOT_FOUND", message: `shape "${shape.name}" not found or not permitted` };
@@ -5351,8 +5332,8 @@ class ShardDO {
5351
5332
  return await this.seedOpLogShape(ws, subId, shape, resolved);
5352
5333
  } catch (error) {
5353
5334
  this.recordShapeError(`shape:seed:${subId}`, error);
5354
- const code = typeof error.code === "string" ? error.code : "SHAPE_SEED_FAILED";
5355
- 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 };
5356
5337
  }
5357
5338
  }
5358
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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/do",
3
- "version": "1.0.0-alpha.21",
3
+ "version": "1.0.0-alpha.22",
4
4
  "description": "Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -46,6 +46,7 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
+ "@lunora/errors": "1.0.0-alpha.1",
49
50
  "@visulima/redact": "3.0.0-alpha.14",
50
51
  "drizzle-orm": "^0.45.2"
51
52
  },
@@ -1,10 +0,0 @@
1
- class NotFoundError extends Error {
2
- code = "NOT_FOUND";
3
- status = 404;
4
- constructor(message = "Document not found") {
5
- super(message);
6
- this.name = "NotFoundError";
7
- }
8
- }
9
-
10
- export { NotFoundError as default };