@lunora/do 1.0.0-alpha.2 → 1.0.0-alpha.21

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 (39) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +1162 -114
  3. package/dist/index.d.ts +1162 -114
  4. package/dist/index.mjs +27 -22
  5. package/dist/packem_shared/{ADMIN_FUNCTION_PREFIX-Dzdqq5J2.mjs → ADMIN_FUNCTIONS-DSUQ5fX9.mjs} +49 -3
  6. package/dist/packem_shared/{applyCdcChanges-Ctdmxmrv.mjs → CDC_LOG_TABLE-DSycmnDf.mjs} +5 -1
  7. package/dist/packem_shared/{DATA_MIGRATION_STATE_TABLE-PTtTiQ7U.mjs → DATA_MIGRATION_STATE_TABLE-DfPxn8I0.mjs} +5 -1
  8. package/dist/packem_shared/{assertFlatPredicate-DyVYReuT.mjs → DEFAULT_MAX_RELATION_KEYS-DU-Y4-LJ.mjs} +51 -2
  9. package/dist/packem_shared/{assertValidClientId-CBZ1zC96.mjs → NotUniqueError-Do5h_jiW.mjs} +127 -37
  10. package/dist/packem_shared/{rank-CrkEIpF4.mjs → RANK_TIEBREAK-CXhdcA1o.mjs} +2 -13
  11. package/dist/packem_shared/{guardWriter-u3UlnCH5.mjs → RLS_UNWRAP_SYMBOL-EtGQdC9d.mjs} +6 -2
  12. package/dist/packem_shared/{ROOT_DO_SIZE_WARN_BYTES-DQkmGiCS.mjs → ROOT_DO_SIZE_WARN_BYTES-BfPNp3Jh.mjs} +2342 -200
  13. package/dist/packem_shared/{ReactiveCache-ByVzgH3d.mjs → ReactiveCache-BYlSGY0N.mjs} +1 -28
  14. package/dist/packem_shared/{applyOnDelete-CMif2RKw.mjs → applyOnDelete-BQ-8ZlZ1.mjs} +19 -9
  15. package/dist/packem_shared/{buildSeekWhere-lVsNXSLy.mjs → applySelect-BvZdFUBT.mjs} +18 -1
  16. package/dist/packem_shared/{backfillAggregateIndexes-BF5eL7kW.mjs → backfillAggregateIndexes-BZsOqDXP.mjs} +3 -2
  17. package/dist/packem_shared/{compileWhereSql-CXrhFA3G.mjs → compileWhereSql-MW_Lk8nJ.mjs} +3 -2
  18. package/dist/packem_shared/ctx-db-idempotency-BdcNpvY4.mjs +108 -0
  19. package/dist/packem_shared/ctx-db-shapes-0RaIOy7J.mjs +53 -0
  20. package/dist/packem_shared/diffExternalSource-Cx9HUPJj.mjs +44 -0
  21. package/dist/packem_shared/isSourceDue-9mJRJ9Ld.mjs +40 -0
  22. package/dist/packem_shared/materializeExternalRows-Bj6EXy50.mjs +23 -0
  23. package/dist/packem_shared/{runShardMigrations-C3bn5r93.mjs → runShardMigrations-nIwoQeOK.mjs} +6 -4
  24. package/dist/packem_shared/serialize-sql-BlRUoiQe.mjs +14 -0
  25. package/dist/packem_shared/{serveRelationFanout-Clr1a05L.mjs → serveRelationFanout-C5axmGDj.mjs} +1 -1
  26. package/dist/packem_shared/stableStringify-MydiuScU.mjs +40 -0
  27. package/dist/packem_shared/subscription-delivery-CK8qga-k.mjs +347 -0
  28. package/dist/packem_shared/subscriptionListDeltas-BxygQlXT.mjs +1 -0
  29. package/package.json +2 -2
  30. package/dist/packem_shared/ctx-db-idempotency-DkC9rP91.mjs +0 -35
  31. package/dist/packem_shared/encodePartitionKey-C6blLR5K.mjs +0 -1
  32. /package/dist/packem_shared/{matchesStaticWhere-CFk6adSu.mjs → AGGREGATE_SQL_FUNCTION-CFk6adSu.mjs} +0 -0
  33. /package/dist/packem_shared/{AUTH_METRICS_BUCKET_MS-CiHHYeJi.mjs → AUTH_METRICS_BUCKETS_TABLE-CiHHYeJi.mjs} +0 -0
  34. /package/dist/packem_shared/{ensureFunctionMetricsTables-UDNVD7FS.mjs → FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs} +0 -0
  35. /package/dist/packem_shared/{clearCapturedMail-CPpgl-dX.mjs → MAIL_RETENTION-CPpgl-dX.mjs} +0 -0
  36. /package/dist/packem_shared/{assertReadonly-dDcFE1YZ.mjs → MAX_SQL_ROWS-dDcFE1YZ.mjs} +0 -0
  37. /package/dist/packem_shared/{buildSecurityAudit-CCAvoFlr.mjs → MIN_ADMIN_TOKEN_LENGTH-CCAvoFlr.mjs} +0 -0
  38. /package/dist/packem_shared/{ftsTableName-BLEMawrp.mjs → buildFtsMatch-BLEMawrp.mjs} +0 -0
  39. /package/dist/packem_shared/{runTriggers-5N6_Fx0A.mjs → hasTrigger-5N6_Fx0A.mjs} +0 -0
package/dist/index.d.mts CHANGED
@@ -195,10 +195,17 @@ interface RelationDefinitionLike {
195
195
  readonly references: string;
196
196
  readonly table: string;
197
197
  }
198
- /** Per-relation refinements: filter / order / cap / recurse into the children. */
198
+ /** Per-relation refinements: filter / order / cap / project / recurse into the children. */
199
199
  interface NestedWith {
200
200
  limit?: number;
201
201
  orderBy?: OrderByInput[];
202
+ /**
203
+ * Project each loaded child down to these fields (like the top-level
204
+ * `findMany` `select`). Applied AFTER grouping, so the join key stays
205
+ * available to map children to parents; `_id`/`_creationTime` and any deeper
206
+ * `with` relations are always retained.
207
+ */
208
+ select?: ReadonlyArray<string>;
202
209
  where?: WhereInput;
203
210
  with?: WithInput;
204
211
  }
@@ -212,8 +219,17 @@ interface WithInput {
212
219
  _count?: Record<string, true>;
213
220
  }
214
221
  interface ResolveWithOptions {
215
- counter: (tableName: string, where?: WhereInput) => Promise<number>;
216
222
  fetcher: (tableName: string, args: QueryArgs) => Promise<QueryPage>;
223
+ /**
224
+ * Grouped aggregate: for every FK value in `values`, return the count of
225
+ * child rows in `tableName` whose `whereField` equals that value,
226
+ * optionally AND-ing in `policyWhere` (the child table's RLS read filter).
227
+ * Returns a `Map` keyed by FK value with the per-group count — missing
228
+ * keys (groups with zero children) are not included; callers default to 0.
229
+ * A single `GROUP BY :whereField … WHERE :whereField IN (values)` query
230
+ * replaces the former one-query-per-distinct-value loop.
231
+ */
232
+ groupedCounter: (tableName: string, whereField: string, values: unknown[], policyWhere?: WhereInput) => Promise<Map<unknown, number>>;
217
233
  parents: Record<string, unknown>[];
218
234
  /**
219
235
  * Per-target-table read filter (RLS) applied to each relation fetch/count and
@@ -227,7 +243,18 @@ interface ResolveWithOptions {
227
243
  tableName: string;
228
244
  with: WithInput;
229
245
  }
246
+ /**
247
+ * Cross-backend fan-out for grouped `_count` on backends whose `groupedCounter`
248
+ * must fall back to scalar calls (e.g. a DO's global-D1 child or the sql-store's
249
+ * cross-shard reverse direction). Issues one `counter(table, where)` per FK
250
+ * value in parallel and collects the results into a Map.
251
+ *
252
+ * Used by both the DO and sql-store `relationGroupedCounter` implementations so
253
+ * the parallel fan-out logic isn't duplicated.
254
+ */
255
+ declare const fanOutScalarCounts: (counter: (tableName: string, where?: WhereInput) => Promise<number>, tableName: string, whereField: string, values: unknown[], policyWhere: WhereInput | undefined) => Promise<Map<unknown, number>>;
230
256
  /** Distinct, non-nullish values of `field` across `rows`, preserving first-seen order. */
257
+
231
258
  /**
232
259
  * Resolve every requested relation on `parents` (a single already-fetched
233
260
  * page), mutating each parent in place: `one` → `Doc | null`, `many` →
@@ -289,9 +316,9 @@ declare const applyOnDelete: (options: ApplyOnDeleteOptions) => Promise<void>;
289
316
  * fakes (which never carry a runtime parser) keep working.
290
317
  */
291
318
  declare const runRowValidators: (definition: TableDefinitionLike, document: Record<string, unknown>) => void;
292
- type SortDirection$1 = "asc" | "desc";
319
+ type SortDirection = "asc" | "desc";
293
320
  /** A single `{ field: "asc" | "desc" }` entry; `orderBy` is an ordered list of these. */
294
- type OrderByInput = Record<string, SortDirection$1>;
321
+ type OrderByInput = Record<string, SortDirection>;
295
322
  interface QueryArgs {
296
323
  /**
297
324
  * Predicate injected by the runtime (e.g. by `@lunora/server`'s RLS
@@ -304,6 +331,12 @@ interface QueryArgs {
304
331
  */
305
332
  baseWhere?: WhereInput;
306
333
  cursor?: null | string;
334
+ /**
335
+ * Opt a list read OUT of soft-delete scoping: when `true`, rows whose
336
+ * soft-delete column is set are INCLUDED. Has no effect on a table without
337
+ * `.softDelete()`. Default (absent/false) hides soft-deleted rows.
338
+ */
339
+ includeDeleted?: boolean;
307
340
  limit?: number;
308
341
  orderBy?: OrderByInput[];
309
342
  /**
@@ -324,6 +357,15 @@ interface QueryArgs {
324
357
  * reads (`findMany`/`findFirst`) — this flag specifically guards `count`.
325
358
  */
326
359
  restrictsCounts?: boolean;
360
+ /**
361
+ * Project each returned row down to these fields. The system fields `_id` and
362
+ * `_creationTime` are always retained (cursors + by-id reuse depend on them),
363
+ * and any relations attached via `with` (their relation keys and `_count`)
364
+ * survive the trim. Applied AFTER the rows are read and relations resolved, so
365
+ * read-dependency tracking and cursor encoding see the full row — only the
366
+ * payload returned to the caller is narrowed. Omit for the full document.
367
+ */
368
+ select?: ReadonlyArray<string>;
327
369
  where?: WhereInput;
328
370
  with?: WithInput;
329
371
  }
@@ -341,7 +383,7 @@ interface QueryPage {
341
383
  splitCursor?: null | string;
342
384
  }
343
385
  interface OrderKey {
344
- direction: SortDirection$1;
386
+ direction: SortDirection;
345
387
  field: string;
346
388
  }
347
389
  /**
@@ -376,6 +418,26 @@ declare const buildSeekWhere: (keys: OrderKey[], cursorValues: unknown[]) => Whe
376
418
  * the page it terminates. Reactive pagination uses this for a page's fixed end
377
419
  * cursor; the shared compiler renders it per dialect.
378
420
  */
421
+
422
+ /**
423
+ * Project `page` rows down to `select` — plus the always-kept system fields and
424
+ * the relation/`_count` keys attached by a `with` load (passed as `withInput`,
425
+ * the same object handed to `findMany`). Returns the page unchanged when
426
+ * `select` is undefined. Pure; callers apply it AFTER relation resolution +
427
+ * cursor encoding so only the returned payload is trimmed (dependency tracking +
428
+ * the cursor still see the full row).
429
+ */
430
+ declare const applySelect: (page: Record<string, unknown>[], select: ReadonlyArray<string> | undefined, withInput?: Record<string, unknown>) => Record<string, unknown>[];
431
+ /**
432
+ * The read-scope predicate that hides soft-deleted rows — `{ [field]: { isNull:
433
+ * true } }` matching the live rows whose soft-delete column is null/absent — or
434
+ * `undefined` when the table isn't `.softDelete()` or the read opted in via
435
+ * `includeDeleted`. AND-merge it into a list read's `where` (the by-id path
436
+ * never calls this, so `get`/`patch`/`replace`/`restore` still address the row).
437
+ */
438
+ declare const softDeleteScope: (softDeleteMode: {
439
+ field: string;
440
+ } | undefined, includeDeleted: boolean | undefined) => undefined | WhereInput;
379
441
  type RankDirection = "asc" | "desc";
380
442
  interface RankSortKeyLike {
381
443
  readonly direction: RankDirection;
@@ -554,6 +616,7 @@ declare const rankTableName: (table: string, indexName: string) => string;
554
616
  * - `sortValues[i]` === `serializeSqlValue(doc[index.sortBy[i].field])` — the same transform `syncRankIndexEntry` applies to the stored `__sort_k&lt;i>__` column, so the comparison is byte-for-byte (and JSON-safe for the cross-shard wire) regardless of which shard owns the row. `rankBefore` re-applies it idempotently, so a direct caller passing raw values still works.
555
617
  * - `rowId` === `doc._id`, the `__id__` tiebreak.
556
618
  */
619
+ declare const stableStringify: (value: unknown) => string;
557
620
  /** A single memoized result, the deps it read, and any active subscribers. */
558
621
  interface CacheEntry {
559
622
  /** Approximate serialized size of `result`, charged against `maxBytes`. */
@@ -686,16 +749,6 @@ declare class ReactiveCache {
686
749
  private evict;
687
750
  }
688
751
  /**
689
- * Stable, sorted JSON encoding of `args` for use in a cache key. Object keys
690
- * are visited in lexical order at every depth so `{ a: 1, b: 2 }` and
691
- * `{ b: 2, a: 1 }` hash to the same string. Arrays preserve their order
692
- * (the index IS the key). `undefined` values are skipped at the object level
693
- * so `{ a: undefined }` collides with `{}` — matches Convex behavior and
694
- * avoids spurious cache misses on optional args. Inside arrays `undefined`
695
- * encodes as `null` to keep positional semantics.
696
- */
697
- declare const stableStringify: (value: unknown) => string;
698
- /**
699
752
  * Compose a cache key from a function path, a stably-encoded args object, and
700
753
  * the caller's identity discriminator. Exported so the wiring layer and tests
701
754
  * build identical keys without each side reinventing the format.
@@ -951,7 +1004,32 @@ interface SubscriptionQuery {
951
1004
  */
952
1005
  table?: string;
953
1006
  }
1007
+ /**
1008
+ * A live shape subscription registered on a socket — the partial-replication
1009
+ * parallel to {@link SubscriptionQuery}. The client names a `defineShape` shape
1010
+ * and supplies validated `args`; the DO resolves it to a table + RLS-composed
1011
+ * `effectiveWhere` under the socket's verified identity (never the client's
1012
+ * word) and pokes the membership diff. `sinceSeq`/`sinceEpoch` carry the
1013
+ * client's last applied checkpoint for resume.
1014
+ */
1015
+ interface ShapeSubscriptionQuery {
1016
+ /** Validated shape arguments (e.g. `{ channelId }`); forwarded to `resolveShape`. */
1017
+ args?: Record<string, unknown>;
1018
+ /** Registered shape name (the `defineShape` export the codegen subclass resolves). */
1019
+ name: string;
1020
+ /** Resume epoch the client persisted alongside {@link ShapeSubscriptionQuery.sinceSeq} (see {@link SubscriptionQuery.sinceEpoch}). */
1021
+ sinceEpoch?: string;
1022
+ /** Resume checkpoint: the `__cdc_log` cursor the client's view of this shape last reflected (see {@link SubscriptionQuery.sinceSeq}). */
1023
+ sinceSeq?: number;
1024
+ }
954
1025
  interface SubscriptionEnvelope {
1026
+ /**
1027
+ * Stable per-client id carried by the `connect` envelope. Recorded on the
1028
+ * socket attachment so a shape poke can echo this client's
1029
+ * `__client_watermark` as its `lastMutationId`. Ignored on other envelope
1030
+ * types; absent for clients that don't use custom mutators.
1031
+ */
1032
+ clientId?: string;
955
1033
  /**
956
1034
  * App-supplied connection context carried by the `connect` envelope (e.g.
957
1035
  * `{ roomId, sessionId }`). Merged into the socket attachment and forwarded
@@ -966,6 +1044,19 @@ interface SubscriptionEnvelope {
966
1044
  id: string;
967
1045
  query?: SubscriptionQuery;
968
1046
  /**
1047
+ * Shape descriptor of a `shape_subscribe` envelope: the named shape + its
1048
+ * validated args. Carries the client's resume checkpoint via
1049
+ * {@link SubscriptionEnvelope.sinceCheckpoint}/{@link SubscriptionEnvelope.sinceEpoch}.
1050
+ */
1051
+ shape?: {
1052
+ args?: Record<string, unknown>;
1053
+ name: string;
1054
+ };
1055
+ /** Resume checkpoint on a `shape_subscribe` envelope (the `__cdc_log` cursor the client's shape view is at). */
1056
+ sinceCheckpoint?: number;
1057
+ /** CDC epoch the {@link SubscriptionEnvelope.sinceCheckpoint} belongs to. */
1058
+ sinceEpoch?: string;
1059
+ /**
969
1060
  * Topic of a `whisper`/`whisper_subscribe`/`whisper_unsubscribe` envelope —
970
1061
  * an app-chosen channel name (e.g. `"room:42:cursors"`) scoped to this shard.
971
1062
  */
@@ -983,7 +1074,7 @@ interface SubscriptionEnvelope {
983
1074
  * this shard with NO SQLite/CDC write (AnyCable-style whispering — typing
984
1075
  * indicators, live cursors). The sender never receives its own whisper.
985
1076
  */
986
- type: "ack" | "connect" | "stream" | "subscribe" | "unsubscribe" | "whisper" | "whisper_subscribe" | "whisper_unsubscribe";
1077
+ type: "ack" | "connect" | "shape_subscribe" | "shape_unsubscribe" | "stream" | "subscribe" | "unsubscribe" | "whisper" | "whisper_subscribe" | "whisper_unsubscribe";
987
1078
  }
988
1079
  /**
989
1080
  * The argument a connection-lifecycle hook receives. Structurally matches
@@ -1058,6 +1149,15 @@ interface SocketAttachment {
1058
1149
  */
1059
1150
  admin?: boolean;
1060
1151
  /**
1152
+ * Stable per-client id from the `connect` envelope (the same id the client
1153
+ * stamps on its custom-mutator pushes). Lets a shape poke echo this client's
1154
+ * `__client_watermark` as the poke's `lastMutationId`, so a `@lunora/db`
1155
+ * collection can drop the optimistic overlay for writes this poke has
1156
+ * synced. Absent for clients that don't use custom mutators. Persisted so it
1157
+ * survives hibernation.
1158
+ */
1159
+ clientId?: string;
1160
+ /**
1061
1161
  * `true` once the socket's `connect` envelope has fired the `onConnect`
1062
1162
  * hooks. Gates the dispatch so a client that re-sends `connect` (or a
1063
1163
  * duplicate frame) can't re-fire the hooks for an already-announced socket —
@@ -1091,6 +1191,14 @@ interface SocketAttachment {
1091
1191
  * hooks so they run under the connecting user.
1092
1192
  */
1093
1193
  identity?: Record<string, unknown>;
1194
+ /**
1195
+ * Live shape subscriptions registered on this socket, keyed by the
1196
+ * client-supplied subscription id. The partial-replication parallel to
1197
+ * {@link SocketAttachment.subs}: the poke protocol fans membership diffs to
1198
+ * these, while `subs` drives the legacy `data`/`delta` re-execution path.
1199
+ * Absent until the socket sends its first `shape_subscribe`.
1200
+ */
1201
+ shapes?: Record<string, ShapeSubscriptionQuery>;
1094
1202
  subs: Record<string, SubscriptionQuery>;
1095
1203
  /**
1096
1204
  * Verified user id resolved at upgrade (from `x-lunora-userid`), or absent
@@ -1106,6 +1214,36 @@ interface SocketAttachment {
1106
1214
  whispers?: string[];
1107
1215
  }
1108
1216
  /**
1217
+ * A `shape_subscribe`'s resolved query: which `table` to replicate, the
1218
+ * identity-scoped `effectiveWhere` (the shape predicate AND-merged with the
1219
+ * table's RLS read base-where), the optional projected `columns` allow-list,
1220
+ * and whether the table is `.global()` (served by the latency-tiered poll path
1221
+ * rather than the CDC poke path). The codegen subclass's `resolveShape` builds
1222
+ * it under the socket's verified identity, so the membership query the poke
1223
+ * protocol runs is RLS-correct by construction.
1224
+ */
1225
+ interface ResolvedShape {
1226
+ columns?: ReadonlyArray<string>;
1227
+ effectiveWhere?: WhereInput;
1228
+ /** `true` when the shape's table is `.global()` (lives in D1, not this DO's SQLite) — no per-DO op-log to diff, so served by the poll path. */
1229
+ global?: boolean;
1230
+ table: string;
1231
+ }
1232
+ /**
1233
+ * Identity a subscription/shape query is executed under, threaded EXPLICITLY
1234
+ * into the codegen `resolveShape`/`buildCtx` rather than read from the shared,
1235
+ * per-request identity fields. The value passed is the socket's OWN verified
1236
+ * identity (stamped on the {@link SocketAttachment} at the WS upgrade from the
1237
+ * runtime-minted `x-lunora-userid`/`x-lunora-identity` headers the client can't
1238
+ * forge), passed BY VALUE so a deferred refresh or interleaved RPC can't clobber
1239
+ * it. An anonymous socket leaves both fields `undefined`, so an RLS/`ctx.auth`
1240
+ * query fails closed (empty/denied) rather than leaking another user's data.
1241
+ */
1242
+ interface SubscriptionIdentity {
1243
+ identity?: Record<string, unknown>;
1244
+ userId?: string;
1245
+ }
1246
+ /**
1109
1247
  * One-shot backfill of every declared aggregate index. Used by tests and
1110
1248
  * production hosts that want to populate counters up-front instead of on first
1111
1249
  * read. Idempotent: counter rows that already exist are left alone, so it's
@@ -1147,10 +1285,16 @@ interface CdcChange {
1147
1285
  * Read changelog entries newer than `sinceSeq` in commit order, up to `limit`
1148
1286
  * (clamped to [1, 10000]). Returns the rows plus the cursor to resume from (the
1149
1287
  * last `seq`, or `sinceSeq` when the page is empty).
1288
+ *
1289
+ * The optional `tables` set narrows the page to changes on those tables — the
1290
+ * shape/poke path reads one filtered page per flush so it never scans op-log
1291
+ * entries for tables no live shape is watching. Omit it (or pass an empty set)
1292
+ * for the full, unfiltered page (the existing streaming-export/resume callers).
1150
1293
  */
1151
1294
  declare const readCdcChanges: (sql: SqlExec, options?: {
1152
1295
  limit?: number;
1153
1296
  sinceSeq?: number;
1297
+ tables?: ReadonlySet<string>;
1154
1298
  }) => {
1155
1299
  changes: CdcChange[];
1156
1300
  cursor: number;
@@ -1186,6 +1330,11 @@ declare const applyCdcChanges: (writer: DatabaseWriterLike, changes: ReadonlyArr
1186
1330
  declare const runShardMigrations: (sql: SqlExec, schema: SchemaLike, options?: {
1187
1331
  cdc?: boolean;
1188
1332
  }) => void;
1333
+ /** One shape member: its `_id` key plus the decoded document (id + creationTime merged in). */
1334
+ interface ShapeRow {
1335
+ doc: Record<string, unknown>;
1336
+ id: string;
1337
+ }
1189
1338
  /**
1190
1339
  * Structural projection of `state.storage.sql` (workerd's SqlStorage). We
1191
1340
  * only require the `exec` overload — the cursor it returns is iterable and
@@ -1233,6 +1382,16 @@ interface TableDefinitionLike {
1233
1382
  field?: string;
1234
1383
  kind: "global" | "root" | "shardBy";
1235
1384
  };
1385
+ /**
1386
+ * Mirror of `@lunora/server`'s `TableDefinition.softDeleteMode` (set by
1387
+ * `.softDelete()`). When present, `delete()` flips the `field` column to a
1388
+ * timestamp instead of physically removing the row (cascading as a soft
1389
+ * delete), and list reads scope out rows whose `field` is set unless
1390
+ * `includeDeleted` is passed. By-id reads/writes are unaffected.
1391
+ */
1392
+ readonly softDeleteMode?: {
1393
+ field: string;
1394
+ };
1236
1395
  readonly triggerMap?: Record<string, TriggerDefinitionLike>;
1237
1396
  }
1238
1397
  interface IndexDefinitionLike {
@@ -1525,7 +1684,16 @@ interface DatabaseWriterLike {
1525
1684
  * RLS-aware ctx seam from §3.2).
1526
1685
  */
1527
1686
  count: (tableName: string, where?: RestrictableQueryOptions | WhereInput) => Promise<number>;
1528
- delete: (id: string, expectedTable?: string) => Promise<void>;
1687
+ /**
1688
+ * Delete a row by id. On a `.softDelete()` table this flips the marker column
1689
+ * (cascading as a soft delete) instead of removing the row; pass
1690
+ * `options.hard` to force a physical removal (which cascades as a physical
1691
+ * delete, reaching already-soft-deleted children too). Non-soft tables ignore
1692
+ * `options.hard` — they always delete physically.
1693
+ */
1694
+ delete: (id: string, expectedTable?: string, options?: {
1695
+ hard?: boolean;
1696
+ }) => Promise<void>;
1529
1697
  /**
1530
1698
  * Delete many rows by id in one call (a loop over `delete()`). The returned
1531
1699
  * `deleted` is the number of ids **requested**, not rows actually removed (an
@@ -1702,6 +1870,14 @@ interface DatabaseWriterLike {
1702
1870
  rankPageRows?: (tableName: string, indexName: string, options?: RankPageOptions) => Promise<ShardRankPageResult>;
1703
1871
  replace: (id: string, document: Record<string, unknown>, expectedTable?: string) => Promise<void>;
1704
1872
  /**
1873
+ * Un-soft-delete a row: clears the `.softDelete()` marker column (a by-id
1874
+ * UPDATE, so it works on a row that list reads currently hide). Throws when
1875
+ * the row's table isn't `.softDelete()`. Optional on the interface — the DO
1876
+ * writer implements it; the `.global()` twin does too, so a restore on a
1877
+ * global table routes through the DO writer's global fallback.
1878
+ */
1879
+ restore?: (id: string, expectedTable?: string) => Promise<void>;
1880
+ /**
1705
1881
  * Best-effort, read-only reader over Lunora's system tables
1706
1882
  * (`_scheduled_functions`, `_storage`). Eventually consistent and **not**
1707
1883
  * part of the shard's transaction snapshot — see {@link SystemDatabaseReader}.
@@ -1918,6 +2094,22 @@ declare const readAggregateValue: (op: string, row: {
1918
2094
  * string.
1919
2095
  */
1920
2096
  declare const encodeAggregateKey: (by: ReadonlyArray<string>, source: Record<string, unknown>) => string;
2097
+ /** One recorded admin operation, in monotonic `seq` order. */
2098
+ interface AuditEntry {
2099
+ /** JSON-decoded extra context (the acting user, op-specific counts, …); absent when none was recorded. */
2100
+ detail?: Record<string, unknown>;
2101
+ /** Primary key of the affected row, when the op targets one. */
2102
+ id?: string;
2103
+ /** Short op identifier, e.g. `writeRow` or `runMigration`. */
2104
+ op: string;
2105
+ /** Monotonic per-shard cursor — strictly increasing, never reused. */
2106
+ seq: number;
2107
+ /** Affected table, when the op targets one. */
2108
+ table?: string;
2109
+ /** Wall-clock millis when the op was recorded. */
2110
+ ts: number;
2111
+ }
2112
+ /** Fields accepted when appending one audit entry; `seq` is assigned by the table. */
1921
2113
  /** Reserved single-row auth accumulator table. Auto-hidden from the data browser by the `__lunora` prefix. */
1922
2114
  declare const AUTH_METRICS_TABLE = "__lunora_auth_metrics";
1923
2115
  /** Reserved coarse time-series table: app-wide auth attempt/failure counts bucketed by a fixed window. */
@@ -2155,6 +2347,112 @@ interface RenderedSql {
2155
2347
  * identifiers quoted and placeholders numbered the way that engine expects.
2156
2348
  */
2157
2349
  declare const renderSql: (engine: SqlEngine, query: SQL) => RenderedSql;
2350
+ /** The result of {@link diffExternalSource}: the changes to replay, and the baseline the next tick diffs from. */
2351
+ interface ExternalSourceDiffResult {
2352
+ /** Ordered for `applyCdcChanges`: upserts in pulled order, then deletes in baseline order. */
2353
+ changes: CdcChange[];
2354
+ /** `id → canonical-value JSON` — pass back as the `baseline` next tick (or persist for an incremental cursor). */
2355
+ nextBaseline: Map<string, string>;
2356
+ }
2357
+ /**
2358
+ * Project a row to the document the ingest loop stores + compares on: `_id` plus
2359
+ * either the `columns` allow-list or every field except the framework-assigned
2360
+ * `_creationTime` (which the source never supplies). Returned as a plain object;
2361
+ * key order is irrelevant because {@link stableStringify} sorts keys. Used for BOTH
2362
+ * the pulled side here and the local baseline, so the two are byte-identical for an
2363
+ * unchanged row.
2364
+ */
2365
+
2366
+ /**
2367
+ * Diff a sourced table's freshly-pulled membership against the local baseline.
2368
+ * Returns the `CdcChange[]` to apply (in stable order: upserts in pulled order,
2369
+ * then deletes in baseline order) and the next baseline (`id → canonical JSON`).
2370
+ */
2371
+ declare const diffExternalSource: (pulled: ReadonlyArray<Record<string, unknown>>, baseline: ReadonlyMap<string, string>, options: {
2372
+ columns?: ReadonlyArray<string>;
2373
+ table: string;
2374
+ }) => ExternalSourceDiffResult;
2375
+ /** The outcome of one materialize pass: how many changes were applied, and the baseline the next tick diffs from. */
2376
+ interface MaterializeResult {
2377
+ /** Number of `CdcChange`s applied (inserts + updates + deletes). Zero on a steady-state tick. */
2378
+ applied: number;
2379
+ /** `id → projected-value JSON` — the post-tick membership, to feed back as `baseline` next tick. */
2380
+ nextBaseline: Map<string, string>;
2381
+ }
2382
+ /**
2383
+ * Diff `pulled` against `baseline` and apply the delta to `writer`. Returns the
2384
+ * applied count and the next baseline. A steady-state tick (membership unchanged)
2385
+ * applies nothing and returns `applied: 0`.
2386
+ */
2387
+ declare const materializeExternalRows: (writer: DatabaseWriterLike, pulled: ReadonlyArray<Record<string, unknown>>, baseline: ReadonlyMap<string, string>, options: {
2388
+ columns?: ReadonlyArray<string>;
2389
+ table: string;
2390
+ }) => Promise<MaterializeResult>;
2391
+ /**
2392
+ * Read the materialized table's current membership as the canonical full-pull
2393
+ * baseline (`id → canonical JSON`). Reuses the shape scanner (`selectShapeRows`)
2394
+ * and the SAME {@link projectExternalSourceRow} + {@link stableStringify} the diff
2395
+ * uses, so a stored row (with `_creationTime` + arbitrary key order) compares
2396
+ * byte-identical to its freshly-pulled source counterpart — an unchanged row
2397
+ * produces no spurious update.
2398
+ */
2399
+ declare const readExternalSourceBaseline: (sql: SqlExec, table: string, columns?: ReadonlyArray<string>) => Map<string, string>;
2400
+ /**
2401
+ * Run one full-pull materialize tick: read the table's current membership as the
2402
+ * baseline, diff the freshly-pulled rows against it, and apply the delta. This is
2403
+ * the system-driven loop body the DO poll alarm calls — the table IS the baseline
2404
+ * (design §1 Fact A), so no separate snapshot is kept. Pass both the read handle
2405
+ * (`sql`) and the validated `writer` (the DO has both); they must address the same
2406
+ * table.
2407
+ */
2408
+ declare const runExternalSourceTick: (sql: SqlExec, writer: DatabaseWriterLike, pulled: ReadonlyArray<Record<string, unknown>>, options: {
2409
+ columns?: ReadonlyArray<string>;
2410
+ table: string;
2411
+ }) => Promise<MaterializeResult>;
2412
+ /** The minimal SqlClient surface the poll loop calls (mirrors `@lunora/hyperdrive`'s `SqlClient`). */
2413
+ interface SourceClientLike {
2414
+ query: <Row = Record<string, unknown>>(text: string, parameters?: ReadonlyArray<unknown>) => Promise<Row[]>;
2415
+ }
2416
+ /** Poll cadence: `"manual"` (never auto-poll) or a minimum interval between polls. */
2417
+ type SourceRefresh = "manual" | {
2418
+ everyMs: number;
2419
+ };
2420
+ /** The runtime `.source(...)` config the poll loop reads — a structural mirror of `@lunora/server`'s `ExternalSourceDefinition` (only the fields the tick uses). */
2421
+ interface ExternalSourceLike {
2422
+ binding: string;
2423
+ columns?: ReadonlyArray<string>;
2424
+ idColumn?: string;
2425
+ map?: (row: Record<string, unknown>) => Record<string, unknown>;
2426
+ query: string;
2427
+ refresh?: SourceRefresh;
2428
+ tenantBy?: (shardKey: string) => ReadonlyArray<unknown>;
2429
+ }
2430
+ /**
2431
+ * Lift an external row to a Lunora document: the `idColumn` value becomes a
2432
+ * stringified `_id`, then either `map` shapes the body or every other column is
2433
+ * copied verbatim. Throws on a missing/null id, and on a non-scalar id, so a
2434
+ * misconfigured query fails loudly instead of materializing rows under the literal
2435
+ * id `"undefined"` (or collapsing many rows onto one id). Shared with
2436
+ * `@lunora/hyperdrive`'s `projectSourceRow`.
2437
+ */
2438
+ declare const liftSourceId: (row: Record<string, unknown>, options?: {
2439
+ idColumn?: string;
2440
+ map?: (row: Record<string, unknown>) => Record<string, unknown>;
2441
+ }) => Record<string, unknown>;
2442
+ /**
2443
+ * Whether a source should poll on this alarm tick. `"manual"` never auto-polls;
2444
+ * `{ everyMs }` polls at most once per interval (the alarm floor still bounds it
2445
+ * from below); an omitted `refresh` polls every tick. `lastPolledMs` is `undefined`
2446
+ * before the first poll (always due).
2447
+ */
2448
+ declare const isSourceDue: (refresh: SourceRefresh | undefined, lastPolledMs: number | undefined, nowMs: number) => boolean;
2449
+ /**
2450
+ * Pull a sourced table's tenant slice from `client`, project each row through
2451
+ * {@link liftSourceId}, and materialize it via {@link runExternalSourceTick}
2452
+ * (read local baseline → diff → apply through the validated CDC writer). The
2453
+ * per-table body the DO poll alarm runs; `shardKey` binds into `tenantBy`.
2454
+ */
2455
+ declare const pullExternalSourceTick: (sql: SqlExec, writer: DatabaseWriterLike, client: SourceClientLike, table: string, source: ExternalSourceLike, shardKey: string) => Promise<MaterializeResult>;
2158
2456
  /**
2159
2457
  * Reserved `functionPath` prefix for admin introspection RPCs. These travel
2160
2458
  * over the same `/_lunora/rpc` → shard `/rpc` path as ordinary functions, but
@@ -2177,6 +2475,19 @@ declare const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
2177
2475
  */
2178
2476
  declare const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
2179
2477
  /**
2478
+ * Reserved `functionPath` prefix for live feature-flag reads. The React client's
2479
+ * `useFlag`/`useFlags` subscribe to `__lunora_flags__:eval` over the same WS
2480
+ * channel as a user query; `ShardDO` intercepts it before user dispatch and
2481
+ * serves it from the codegen-overridden flag-subscription read hook, which
2482
+ * evaluates the flag through the app's OpenFeature provider under the socket's
2483
+ * verified identity. Like the other reserved prefixes it is NOT admin-gated (a
2484
+ * flag read is public, scoped to the subscriber's own targeting context), and
2485
+ * the `__lunora_` namespace is reserved so a real `&lt;file>:&lt;function>` can't
2486
+ * collide. Re-evaluated on every write-flush so values stay live within a
2487
+ * session (provider-side flips with no intervening write surface on reconnect).
2488
+ */
2489
+ declare const FLAGS_FUNCTION_PREFIX = "__lunora_flags__:";
2490
+ /**
2180
2491
  * Fully-qualified reserved paths the data browser invokes. The
2181
2492
  * `__lunora_admin__:` prefix is spelled out inline rather than interpolated so
2182
2493
  * the values stay emittable under `--isolatedDeclarations`.
@@ -2196,6 +2507,7 @@ declare const ADMIN_FUNCTIONS: {
2196
2507
  readonly getAuditLog: "__lunora_admin__:getAuditLog";
2197
2508
  readonly getAuthMetrics: "__lunora_admin__:getAuthMetrics";
2198
2509
  readonly getCapturedMail: "__lunora_admin__:getCapturedMail";
2510
+ readonly getFanoutMetrics: "__lunora_admin__:getFanoutMetrics";
2199
2511
  readonly getFunctionStats: "__lunora_admin__:getFunctionStats";
2200
2512
  readonly listSubscriptions: "__lunora_admin__:listSubscriptions";
2201
2513
  readonly listTableIndexes: "__lunora_admin__:listTableIndexes";
@@ -2207,6 +2519,8 @@ declare const ADMIN_FUNCTIONS: {
2207
2519
  readonly getSettings: "__lunora_admin__:getSettings";
2208
2520
  readonly getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus";
2209
2521
  readonly importShard: "__lunora_admin__:importShard";
2522
+ readonly listFlags: "__lunora_admin__:listFlags";
2523
+ readonly listQueues: "__lunora_admin__:listQueues";
2210
2524
  readonly listTables: "__lunora_admin__:listTables";
2211
2525
  readonly listWorkflows: "__lunora_admin__:listWorkflows";
2212
2526
  readonly maskPolicies: "__lunora_admin__:maskPolicies";
@@ -2234,30 +2548,6 @@ interface TableInfo {
2234
2548
  name: string;
2235
2549
  rowCount: number;
2236
2550
  }
2237
- /**
2238
- * One recorded admin operation served by `__lunora_admin__:getAuditLog`, sourced
2239
- * from the reserved `__lunora_audit__` table (see `audit-log.ts`). Unlike the
2240
- * in-memory `getMetrics`/`getFunctionStats` counters, the audit log is durable —
2241
- * it survives hibernation/restart and is bounded only by a retention cap. `seq`
2242
- * is a monotonic per-shard cursor the studio pages through; `op` is the short
2243
- * op name (`writeRow`, `runMigration`, `importShard`, `applyCdc`); `table`/`id`
2244
- * are present when the op targets one; `detail` carries op-specific context
2245
- * (notably the acting `userId`).
2246
- */
2247
- interface AuditEntry {
2248
- /** JSON extra context (acting user, op-specific counts, …); absent when none was recorded. */
2249
- detail?: Record<string, unknown>;
2250
- /** Primary key of the affected row, when the op targets one. */
2251
- id?: string;
2252
- /** Short op identifier, e.g. `writeRow`. */
2253
- op: string;
2254
- /** Monotonic per-shard cursor — strictly increasing, never reused. */
2255
- seq: number;
2256
- /** Affected table, when the op targets one. */
2257
- table?: string;
2258
- /** Epoch-ms the op was recorded. */
2259
- ts: number;
2260
- }
2261
2551
  /** Payload of a `__lunora_admin__:getAuditLog` call: the recorded entries, newest first. */
2262
2552
  interface AuditLogResult {
2263
2553
  entries: AuditEntry[];
@@ -2510,20 +2800,66 @@ interface StorageRulesResult {
2510
2800
  * package's tests and the studio's fails the build if the two key sets diverge.
2511
2801
  */
2512
2802
  interface StudioFeaturesResult {
2803
+ /** `@lunora/bindings/analytics` / `ctx.analytics` is used, or it is a declared dependency. */
2804
+ analytics: boolean;
2805
+ /** `@lunora/auth` is a declared dependency (backs the Users / Sessions / Organizations / Configuration pages). */
2806
+ auth: boolean;
2807
+ /** `@lunora/container` / `ctx.containers` is used, the app declares containers, or it is a declared dependency. */
2808
+ containers: boolean;
2809
+ /** `@lunora/flags` / `ctx.flags` is used, or it is a declared dependency. */
2810
+ flags: boolean;
2811
+ /** `@lunora/bindings/kv` / `ctx.kv` is used, or it is a declared dependency. */
2812
+ kv: boolean;
2513
2813
  /** `@lunora/mail` is imported by a `lunora/` source or a declared dependency. */
2514
2814
  mail: boolean;
2515
2815
  /** `@lunora/payment` is used (import or `ctx.payments`) or a declared dependency. */
2516
2816
  payments: boolean;
2817
+ /** `@lunora/queue` / `ctx.queues` is used, the app declares queues, or it is a declared dependency. */
2818
+ queues: boolean;
2517
2819
  /** `@lunora/scheduler` / `ctx.scheduler` is used, the app declares crons, or it is a declared dependency. */
2518
2820
  scheduler: boolean;
2519
2821
  /** `@lunora/storage` / `ctx.storage` is used, the schema declares storage columns/rules, or it is a declared dependency. */
2520
2822
  storage: boolean;
2521
- /** The schema declares vector indexes, `@lunora/vectors` / `ctx.vectors` is used, or it is a declared dependency. */
2823
+ /** The schema declares vector indexes, `@lunora/bindings/vectors` / `ctx.vectors` is used, or it is a declared dependency. */
2522
2824
  vectors: boolean;
2523
2825
  /** `@lunora/workflow` / `ctx.workflows` is used, the app declares workflows, or it is a declared dependency. */
2524
2826
  workflows: boolean;
2525
2827
  }
2526
2828
  /**
2829
+ * One feature flag evaluated under a supplied targeting context, surfaced by
2830
+ * `__lunora_admin__:listFlags` for the studio's read-only Flags page. The `key`
2831
+ * and `type` are statically discovered by `@lunora/codegen` from the app's
2832
+ * `ctx.flags.&lt;type>("key", …)` reads; `value`/`reason`/`variant`/`errorCode`
2833
+ * come from the live OpenFeature evaluation (the codegen subclass overrides the
2834
+ * base `evaluateFlags` hook). `value` is the resolved flag value as JSON.
2835
+ */
2836
+ interface FlagEvaluation {
2837
+ /** OpenFeature `errorCode` when the evaluation failed (the value falls back to the default). */
2838
+ errorCode?: string;
2839
+ /** The discovered flag key (the first argument of a `ctx.flags.&lt;type>(...)` read). */
2840
+ key: string;
2841
+ /** OpenFeature `reason` for the resolution (`TARGETING_MATCH`, `DEFAULT`, `ERROR`, …). */
2842
+ reason?: string;
2843
+ /** The flag's value type, derived from which `ctx.flags.&lt;type>` method read it. */
2844
+ type: "boolean" | "number" | "object" | "string";
2845
+ /** The resolved value (JSON), or the type default when unconfigured / on error. */
2846
+ value: unknown;
2847
+ /** OpenFeature `variant` identifier when the provider reports one. */
2848
+ variant?: string;
2849
+ }
2850
+ /**
2851
+ * Payload of a `__lunora_admin__:listFlags` call: every statically-discovered
2852
+ * flag evaluated under the supplied targeting context. `configured` is `false`
2853
+ * when the app wires no `@lunora/flags` provider (the base hook), so the studio
2854
+ * can distinguish "no flags configured" from "configured but zero flags read".
2855
+ */
2856
+ interface FlagsResult {
2857
+ /** `true` when an `@lunora/flags` provider is wired (the codegen override ran). */
2858
+ configured: boolean;
2859
+ /** Each discovered flag evaluated under the request's targeting context. */
2860
+ flags: FlagEvaluation[];
2861
+ }
2862
+ /**
2527
2863
  * One declared Cloudflare Workflow, surfaced by `__lunora_admin__:listWorkflows`
2528
2864
  * for the studio's Workflows page. Statically discovered by `@lunora/codegen`
2529
2865
  * from `lunora/workflows.ts` (the codegen subclass overrides the base hook);
@@ -2544,6 +2880,28 @@ interface WorkflowsResult {
2544
2880
  workflows: WorkflowMetadata[];
2545
2881
  }
2546
2882
  /**
2883
+ * One declared Cloudflare Queue, surfaced by `__lunora_admin__:listQueues` for
2884
+ * the studio's Queues page. Statically discovered by `@lunora/codegen` from
2885
+ * `lunora/queues.ts` (the codegen subclass overrides the base hook); queues are
2886
+ * not Durable Objects and carry no runtime state in the shard, so this is pure
2887
+ * declaration metadata. `binding` is the generated `QUEUE_*` producer binding,
2888
+ * `name` the deployed `queues.producers[].queue`, `exportName` the
2889
+ * `lunora/queues.ts` export (`ctx.queues.&lt;exportName>`), `mode` whether the
2890
+ * queue is consumed by a worker (`push`) or polled externally (`pull`), and
2891
+ * `deadLetterQueue` the optional DLQ a push consumer dead-letters to.
2892
+ */
2893
+ interface QueueMetadata {
2894
+ binding: string;
2895
+ deadLetterQueue?: string;
2896
+ exportName: string;
2897
+ mode: "pull" | "push";
2898
+ name: string;
2899
+ }
2900
+ /** Payload of a `__lunora_admin__:listQueues` call: every declared queue, sorted by export name. */
2901
+ interface QueuesResult {
2902
+ queues: QueueMetadata[];
2903
+ }
2904
+ /**
2547
2905
  * Lifecycle state of a workflow instance, mirrored from `@lunora/workflow`'s
2548
2906
  * `WorkflowInstanceStatus` so `@lunora/do` carries no dependency on the workflow
2549
2907
  * package. Returned by `getWorkflowInstanceStatus` and `createWorkflowInstance`.
@@ -2617,7 +2975,12 @@ interface TablePage {
2617
2975
  */
2618
2976
  refs?: Record<string, string>;
2619
2977
  rows: Record<string, unknown>[];
2620
- total: number;
2978
+ /**
2979
+ * Total rows matching the predicate. Absent when the read passed
2980
+ * `skipCount: true` (the caller sources the count from a separate,
2981
+ * predicate-keyed read instead of recomputing it per page).
2982
+ */
2983
+ total?: number;
2621
2984
  }
2622
2985
  /** Comparison a {@link FilterClause} applies. `contains` is a case-sensitive substring (LIKE); the rest are direct SQL comparisons. */
2623
2986
  type FilterOperator = "contains" | "eq" | "gt" | "gte" | "lt" | "lte" | "ne";
@@ -2633,8 +2996,6 @@ interface FilterClause {
2633
2996
  operator: FilterOperator;
2634
2997
  value?: unknown;
2635
2998
  }
2636
- /** Sort direction for an {@link OrderByClause}. */
2637
- type SortDirection = "asc" | "desc";
2638
2999
  /**
2639
3000
  * A server-side sort over one displayed column. `column` resolves the same way a
2640
3001
  * {@link FilterClause}'s does — a physical/meta column orders by its identifier, a
@@ -2675,6 +3036,14 @@ interface ReadTablePageOptions {
2675
3036
  * Empty/whitespace is treated as no filter.
2676
3037
  */
2677
3038
  search?: string;
3039
+ /**
3040
+ * Skip the `SELECT COUNT(*)` and return the page with `total` absent. The
3041
+ * data browser splits the row count into a separate predicate-keyed read (one
3042
+ * that excludes `offset`), so paging never re-counts; the page read passes
3043
+ * this to avoid recomputing the same total per offset. Unset → the COUNT runs
3044
+ * (today's behavior) and `total` is populated.
3045
+ */
3046
+ skipCount?: boolean;
2678
3047
  table: string;
2679
3048
  }
2680
3049
  /**
@@ -3284,6 +3653,16 @@ declare const assertFlatPredicate: (where: WhereInput | undefined, schema: Resol
3284
3653
  * and issues no extra query).
3285
3654
  */
3286
3655
  declare const resolveRelationPredicates: (where: WhereInput | undefined, options: ResolveRelationPredicatesOptions) => Promise<WhereInput | undefined>;
3656
+ /**
3657
+ * Registration-time guard for partial-replication shapes. A live shape can only
3658
+ * be poked from the op-log of its OWN shard Durable Object, so an
3659
+ * `effectiveWhere` that joins to a `.shardBy()` table reaches rows that live in
3660
+ * other DOs the poke loop can never observe. Reject such a shape up front with
3661
+ * the two supported remedies. Called from the generated `resolveShape` override
3662
+ * the moment a socket subscribes (the first point the compiled predicate and the
3663
+ * schema's shard modes are both in hand).
3664
+ */
3665
+ declare const assertShapeShardable: (effectiveWhere: WhereInput | undefined, schema: ResolveContext["schema"], table: string) => void;
3287
3666
  /** Severity of a `ctx.log.*` call, mirroring the console method names (`log` is the default level, distinct from `info`). */
3288
3667
  type ContextLogLevel = "debug" | "error" | "info" | "log" | "warn";
3289
3668
  /** The fields {@link emitLogEvent} ships for one `ctx.log.*` call. */
@@ -3582,6 +3961,44 @@ declare class SessionDO {
3582
3961
  private handleRevoke;
3583
3962
  }
3584
3963
  /**
3964
+ * Diff the previously-sent list snapshot (`previousJson`, the memo's
3965
+ * `lastJson`) against the new query result and produce per-row
3966
+ * {@link MutationDelta}s the client can merge in place via `applyDelta` —
3967
+ * Convex-parity live-pagination deltas (server half of gap #20).
3968
+ *
3969
+ * Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
3970
+ * unless ALL of these hold:
3971
+ *
3972
+ * 1. `previousJson` parses to an array (there IS a previous list to diff against).
3973
+ * 2. `nextResult` is also an array.
3974
+ * 3. Every row in both arrays is a plain object carrying a string `_id`.
3975
+ * 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
3976
+ * 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
3977
+ *
3978
+ * Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
3979
+ * `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
3980
+ * full new `row`; delete omits it (matching the wire contract `@lunora/client`
3981
+ * parses). Deltas are ordered deletes-then-inserts/updates so the client never
3982
+ * sees a transient over-length page.
3983
+ *
3984
+ * Per-row serialization is done exactly **once** per refresh (finding #6). Each
3985
+ * row is stringified a single time into a fingerprint reused for both the
3986
+ * `prev !== next` change-detection compare and — when the caller passes the
3987
+ * optional `frames` sink — the pre-serialized delta frame body. The returned
3988
+ * `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
3989
+ * exact `JSON.stringify(delta)` string for each returned delta, in the same
3990
+ * order, so the caller can splice it straight into the `{type:"delta"}` frame
3991
+ * without serializing the delta (and the row inside it) a second time.
3992
+ * @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
3993
+ */
3994
+ declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
3995
+ /**
3996
+ * Send one WebSocket frame, reporting whether it left the socket. A throw from
3997
+ * `ws.send` (socket closed mid-flush, outbound buffer gone) is the only
3998
+ * delivery-failure signal the runtime exposes; callers use the boolean to decide
3999
+ * whether to advance a subscription's delivered-diff baseline.
4000
+ */
4001
+ /**
3585
4002
  * Optional programmatic log sink, resolved from `createShardDO({ observability })`.
3586
4003
  * Structurally a subset of `@lunora/runtime`'s `ObservabilitySink`, so a user can
3587
4004
  * pass the SAME sink object to `createWorker` (which drives `onRpc`) and
@@ -3634,6 +4051,13 @@ interface ShardDOState {
3634
4051
  getCurrentBookmark?: () => Promise<string>;
3635
4052
  /** Native PITR: arm a restore to `bookmark` on next restart; returns the undo bookmark. */
3636
4053
  onNextSessionRestoreBookmark?: (bookmark: string) => Promise<string>;
4054
+ /**
4055
+ * Arm the DO's single alarm to fire at `scheduledTime` (ms epoch),
4056
+ * waking {@link ShardDO.alarm}. Used by the global-shape poll loop.
4057
+ * Optional: present on the real runtime, absent in the unit harness
4058
+ * (where the poll loop degrades to seed-only).
4059
+ */
4060
+ setAlarm?: (scheduledTime: Date | number) => Promise<void>;
3637
4061
  sql: {
3638
4062
  [key: string]: unknown;
3639
4063
  /**
@@ -3678,34 +4102,15 @@ interface SubscriptionOutcome {
3678
4102
  tables: Set<string>;
3679
4103
  }
3680
4104
  /**
3681
- * Identity a subscription query is executed under, threaded EXPLICITLY into
3682
- * `executeSubscription` `buildCtx` rather than read from the shared,
3683
- * per-request `currentRequestUserId`/`currentRequestIdentity` instance fields.
3684
- *
3685
- * The value passed is the socket's OWN verified identity, captured at the WS
3686
- * upgrade from the runtime-forwarded, server-minted `x-lunora-userid` /
3687
- * `x-lunora-identity` headers (the client cannot forge them — the runtime
3688
- * strips any client-supplied copies) and stamped on the {@link SocketAttachment}.
3689
- * `seedSubscription` and `refreshSubscriptions` read it off the attachment and
3690
- * pass it here BY VALUE — never by reading the mutable per-request
3691
- * `currentRequestUserId`/`currentRequestIdentity` instance fields, which a
3692
- * deferred (`waitUntil`) refresh or a concurrently-interleaved RPC could be
3693
- * mutating. That value-passing is what keeps a subscription re-run from
3694
- * observing or clobbering an in-flight RPC's identity.
3695
- *
3696
- * Developer-facing consequence: a query that authorizes or filters on the
3697
- * caller's identity — via `.use(rls(...))` or by reading `ctx.auth.userId` —
3698
- * evaluates over the live channel under the CONNECTING user, so its seed and
3699
- * every write-driven refresh return that user's rows, matching the one-shot
3700
- * `fetch` RPC. An anonymous socket (no identity resolved at upgrade) leaves
3701
- * both fields `undefined`, so such a query fails closed (empty/denied) rather
3702
- * than leaking another user's data. See the lunora-realtime skill
3703
- * ("Authorization & live queries").
4105
+ * Classification of a watermarked custom-mutator push against the shard's
4106
+ * `__client_watermark`: `expected` is the next in-order sequence, `kind`
4107
+ * whether the push is a replay (`"already"`), the next one (`"next"`), or an
4108
+ * out-of-order arrival (`"gap"`).
3704
4109
  */
3705
- interface SubscriptionIdentity {
3706
- identity?: Record<string, unknown>;
3707
- userId?: string;
3708
- }
4110
+ type ClientMutationClass = {
4111
+ expected: number;
4112
+ kind: "already" | "gap" | "next";
4113
+ };
3709
4114
  /**
3710
4115
  * Optional shard-level configuration passed through `super(state, env, …)`.
3711
4116
  * Reserved as a bag rather than positional args so subclasses don't break
@@ -3834,38 +4239,6 @@ interface RunShardRankPageArgs {
3834
4239
  take?: number;
3835
4240
  }
3836
4241
  /**
3837
- * Diff the previously-sent list snapshot (`previousJson`, the memo's
3838
- * `lastJson`) against the new query result and produce per-row
3839
- * {@link MutationDelta}s the client can merge in place via `applyDelta` —
3840
- * Convex-parity live-pagination deltas (server half of gap #20).
3841
- *
3842
- * Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
3843
- * unless ALL of these hold:
3844
- *
3845
- * 1. `previousJson` parses to an array (there IS a previous list to diff against).
3846
- * 2. `nextResult` is also an array.
3847
- * 3. Every row in both arrays is a plain object carrying a string `_id`.
3848
- * 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
3849
- * 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
3850
- *
3851
- * Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
3852
- * `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
3853
- * full new `row`; delete omits it (matching the wire contract `@lunora/client`
3854
- * parses). Deltas are ordered deletes-then-inserts/updates so the client never
3855
- * sees a transient over-length page.
3856
- *
3857
- * Per-row serialization is done exactly **once** per refresh (finding #6). Each
3858
- * row is stringified a single time into a fingerprint reused for both the
3859
- * `prev !== next` change-detection compare and — when the caller passes the
3860
- * optional `frames` sink — the pre-serialized delta frame body. The returned
3861
- * `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
3862
- * exact `JSON.stringify(delta)` string for each returned delta, in the same
3863
- * order, so the caller can splice it straight into the `{type:"delta"}` frame
3864
- * without serializing the delta (and the row inside it) a second time.
3865
- * @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
3866
- */
3867
- declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
3868
- /**
3869
4242
  * Threshold at which a `__root__` DO triggers the size warning. 1 GiB —
3870
4243
  * exactly 10% of the 10 GiB per-DO SQLite ceiling, leaving plenty of runway
3871
4244
  * to plan a `.shardBy()` migration before the wall hits.
@@ -3918,6 +4291,28 @@ declare abstract class ShardDO {
3918
4291
  */
3919
4292
  protected static readonly MAX_SUBSCRIPTIONS_PER_SOCKET = 32;
3920
4293
  /**
4294
+ * Poll interval (ms) for `.global()`-table shapes. A global table lives in
4295
+ * D1 with no per-DO op-log, so its shapes can't be poke-live; the DO re-reads
4296
+ * each subscribed global shape's membership from D1 on an alarm every
4297
+ * `GLOBAL_SHAPE_POLL_INTERVAL_MS` and pokes only the diff. This is the
4298
+ * latency floor for a global-shape update — deliberately coarse (seconds, not
4299
+ * the sub-millisecond poke-live path) since the D1 read fans out per tick.
4300
+ */
4301
+ protected static readonly GLOBAL_SHAPE_POLL_INTERVAL_MS = 2e3;
4302
+ /**
4303
+ * Upper bound on a `.global()`-shape's materialized membership. Each global
4304
+ * shape keeps its ENTIRE current membership as a per-socket snapshot
4305
+ * (`Map&lt;rowKey, hash&gt;`) so the poll loop can diff it; that snapshot — and the
4306
+ * read buffer feeding it — scale with the membership size, multiplied by every
4307
+ * subscribed socket. An unbounded membership (a global table with no narrowing
4308
+ * shape predicate or RLS read scope) would grow them without limit and evict
4309
+ * the DO. A shape whose membership exceeds this cap is failed closed (left
4310
+ * empty, logged) rather than retained — the developer must narrow it. Sized
4311
+ * well above any reasonable per-identity replicated set so legitimate shapes
4312
+ * never trip it.
4313
+ */
4314
+ protected static readonly GLOBAL_SHAPE_MAX_ROWS = 5e4;
4315
+ /**
3921
4316
  * Per-socket whisper-topic cap. Topic membership rides the same hibernation
3922
4317
  * attachment as `subs`, so bound it for the same reason — a runaway
3923
4318
  * `whisper_subscribe` loop must not wedge the attachment past the runtime's
@@ -4017,6 +4412,39 @@ declare abstract class ShardDO {
4017
4412
  */
4018
4413
  private currentRequestMutationId;
4019
4414
  /**
4415
+ * Stable per-device client id for the in-flight custom-mutator push,
4416
+ * forwarded via the `x-lunora-client-id` header. Backs the
4417
+ * `__client_watermark` table: the dispatch path classifies the paired
4418
+ * `currentRequestClientSeq` against the stored high-watermark (already
4419
+ * processed / next / out-of-order gap). Absent on legacy mutations and
4420
+ * queries (those keep the `__idempotency` path). Cleared in `fetch`'s
4421
+ * `finally`.
4422
+ */
4423
+ private currentRequestClientId;
4424
+ /**
4425
+ * Monotonic per-client mutation sequence for the in-flight custom-mutator
4426
+ * push, forwarded via the `x-lunora-client-seq` header (numeric). Paired
4427
+ * with `currentRequestClientId` to drive the watermark classification.
4428
+ * `undefined` when absent or non-numeric.
4429
+ */
4430
+ private currentRequestClientSeq;
4431
+ /**
4432
+ * The in-flight push's custom-mutator classification, stashed by `fetch`
4433
+ * before `handleRpc` so the in-transaction bookkeeping ({@link
4434
+ * ShardDO.commitMutationBookkeeping}) can advance the `__client_watermark` for
4435
+ * a `"next"` push inside the same commit as the writes. `undefined` for an
4436
+ * ordinary mutation / non-mutator push. Cleared per request.
4437
+ */
4438
+ private currentMutatorClass;
4439
+ /**
4440
+ * Set once a mutation's replay bookkeeping (idempotency row + watermark
4441
+ * advance) has committed INSIDE the handler transaction, so the post-dispatch
4442
+ * path skips the now-redundant best-effort writes. Cleared per request; stays
4443
+ * `false` for actions/queries (no transaction wrapper) so their dispatch-level
4444
+ * idempotency persist still runs.
4445
+ */
4446
+ private mutationBookkeepingCommitted;
4447
+ /**
4020
4448
  * Wall-clock millis of the last `__idempotency` GC sweep on this warm
4021
4449
  * instance. The dedup write throttles `trimIdempotent` to at most once an
4022
4450
  * hour off this field (in-memory, so a fresh instance just sweeps on its
@@ -4047,6 +4475,18 @@ declare abstract class ShardDO {
4047
4475
  */
4048
4476
  private pendingChangedTables;
4049
4477
  /**
4478
+ * Coalesced set of tables awaiting a subscription-refresh pass, merged
4479
+ * across every {@link ShardDO.flushChangedTables} call that lands while a
4480
+ * pass is already draining. The single drain loop
4481
+ * ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
4482
+ * writes to the same table therefore collapses into one (or two) refresh
4483
+ * passes instead of N, so each affected subscription's handler re-runs once
4484
+ * per burst rather than once per write. `undefined` when nothing is pending.
4485
+ */
4486
+ private pendingRefreshTables;
4487
+ /** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
4488
+ private refreshInFlight;
4489
+ /**
4050
4490
  * Last pushed result per `(socket, subId)`, keyed by socket. Lets
4051
4491
  * `refreshSubscriptions` skip re-running queries whose tables were
4052
4492
  * untouched and suppress pushes when the re-run result is unchanged. Held
@@ -4054,6 +4494,39 @@ declare abstract class ShardDO {
4054
4494
  * memo simply forces one re-run and (at most) one redundant push.
4055
4495
  */
4056
4496
  private readonly subMemos;
4497
+ /**
4498
+ * Per-socket poke baseline for shape subscriptions: maps each shape's
4499
+ * subscription id to the `__cdc_log` cursor it has been poked through.
4500
+ * `pokeShapeSubscribers` reads each op page since this cursor and advances
4501
+ * it to the flush watermark. In-memory only (like {@link ShardDO.subMemos});
4502
+ * a cold memo on a reconnected/hibernated socket re-seeds from the client's
4503
+ * `sinceCheckpoint`.
4504
+ */
4505
+ private readonly shapeMemos;
4506
+ /**
4507
+ * Per-socket, per-**global**-shape membership snapshot: maps each global
4508
+ * shape's subscription id to a `key → projected-value JSON` map of the rows
4509
+ * last poked to that socket. A `.global()` (D1) table has no op-log to diff,
4510
+ * so {@link ShardDO.refreshGlobalShape} re-reads the full membership on each
4511
+ * alarm tick and diffs it against this snapshot to compute the poke. Parallel
4512
+ * to {@link ShardDO.shapeMemos} (the cursor baseline for poke-live shapes).
4513
+ *
4514
+ * This is a hot in-memory **cache** over the durable `__global_shape_snapshot`
4515
+ * table (keyed by the socket's `connectionId` + subId): a hibernation eviction
4516
+ * clears the WeakMap, so on the next alarm wake {@link ShardDO.readGlobalSnapshot}
4517
+ * misses and re-loads the baseline from SQLite — without it, the diff would run
4518
+ * against an empty baseline and a row deleted from D1 while the DO slept would
4519
+ * never be poked as a `delete`, lingering on the client as a phantom row.
4520
+ */
4521
+ private readonly globalShapeSnapshots;
4522
+ /**
4523
+ * Whether a global-shape poll alarm is currently armed. Guards
4524
+ * {@link ShardDO.scheduleGlobalPoll} from re-arming on every seed; reset in
4525
+ * {@link ShardDO.alarm} before the poll so a still-subscribed shape re-arms.
4526
+ */
4527
+ private globalPollScheduled;
4528
+ /** Monotonic per-DO poke id source; correlates a poke's `pokeStart`/`pokePart`/`pokeEnd` frames. */
4529
+ private pokeSequence;
4057
4530
  /** Per-socket whisper-rate token bucket (see {@link ShardDO.WHISPER_RATE_BURST}). In-memory; resets on hibernation. */
4058
4531
  private readonly whisperBuckets;
4059
4532
  /**
@@ -4072,6 +4545,35 @@ declare abstract class ShardDO {
4072
4545
  */
4073
4546
  private readonly metrics;
4074
4547
  /**
4548
+ * Running fan-out cost counters surfaced by the
4549
+ * `__lunora_admin__:getFanoutMetrics` RPC — one tally for the reactive
4550
+ * shape-poke path (`pokeShapeSubscribers`) and one for the whisper broadcast
4551
+ * path (`broadcastWhisper`). Each pass records the sockets it iterated (the
4552
+ * O(subscribers) cost) and delivered to. In-memory and reset on
4553
+ * hibernation/restart, sharing `metrics.sinceMs` as the "since this instance
4554
+ * woke" epoch. This is the observability half of plan 075's auto-elastic
4555
+ * relay tier (Phase 1): measure the per-flush fan-out cost so the promotion
4556
+ * threshold is grounded in real numbers, with no behavior change.
4557
+ */
4558
+ private readonly fanout;
4559
+ /**
4560
+ * The runtime's Durable Object namespace binding name (e.g. `"SHARD"`),
4561
+ * forwarded as `x-lunora-shard-binding` on every request so a DO can address
4562
+ * its siblings (`this.env[binding].getByName(...)`) for the relay hub. Absent
4563
+ * in single-DO mode / the unit harness — when absent, the relay tier is inert
4564
+ * and whispers stay shard-local (no behavior change). In-memory; re-learned per
4565
+ * request.
4566
+ */
4567
+ private shardBinding;
4568
+ /**
4569
+ * The auto-elastic fan-out relay collaborator (plan 075) — an {@link OwnerRelay}
4570
+ * or {@link RelayMember} chosen ONCE from this DO's name, or `undefined` for an
4571
+ * unnamed (single-DO) DO where the relay tier is inert. All relay state +
4572
+ * transport lives on it, reached back through the {@link RelayHost} adapter, so
4573
+ * owner-only state can never sit next to relay-only state on this class.
4574
+ */
4575
+ private readonly relay;
4576
+ /**
4075
4577
  * Declared indexes (`table:index`) a query has exercised since this instance
4076
4578
  * woke, stamped by `getCtxDbIndexUseHook`. In-memory and reset on
4077
4579
  * hibernation/restart — drives the `unused_index` runtime advisory.
@@ -4172,6 +4674,15 @@ declare abstract class ShardDO {
4172
4674
  webSocketClose(ws: WebSocket, _code: number, _reason: string, _wasClean: boolean): Promise<void>;
4173
4675
  /** Hibernation API: invoked on socket error. */
4174
4676
  webSocketError(_ws: WebSocket, _error: unknown): void;
4677
+ /**
4678
+ * Durable Object alarm handler — the heartbeat for `.global()`-table shapes.
4679
+ * The runtime wakes this when the poll alarm armed by `scheduleGlobalPoll`
4680
+ * fires; it refreshes every subscribed global shape (diff-poke from the global
4681
+ * backend) and re-arms while any remain. With no global subscribers left, the
4682
+ * alarm is not re-armed and the DO goes idle. A base-only / global-free DO
4683
+ * never arms it, so this stays dormant there.
4684
+ */
4685
+ alarm(): Promise<void>;
4175
4686
  /** Subclasses implement function dispatch. */
4176
4687
  abstract handleRpc(functionPath: string, args: Record<string, unknown>): Promise<unknown>;
4177
4688
  /**
@@ -4418,6 +4929,35 @@ declare abstract class ShardDO {
4418
4929
  */
4419
4930
  protected studioFeatures(): StudioFeaturesResult;
4420
4931
  /**
4932
+ * Evaluate every statically-discovered feature flag under `context` for the
4933
+ * studio's read-only Flags page (`__lunora_admin__:listFlags`). The flag keys
4934
+ * + value types are discovered by `@lunora/codegen` from the app's
4935
+ * `ctx.flags.&lt;type>("key", …)` reads and evaluated through the configured
4936
+ * `@lunora/flags` provider — work only the codegen subclass can do, so it
4937
+ * overrides this. The base class wires no provider and reports
4938
+ * `configured: false` with zero flags (an un-generated `ShardDO` has none).
4939
+ */
4940
+ protected evaluateFlags(_context?: Record<string, unknown>): Promise<FlagsResult>;
4941
+ /**
4942
+ * Serve one reserved {@link FLAGS_FUNCTION_PREFIX} live flag read for the
4943
+ * React client's `useFlag`/`useFlags`. `functionPath` carries the flag key +
4944
+ * type and `args` the per-subscriber targeting context; the codegen subclass
4945
+ * overrides this to evaluate the flag through the app's `@lunora/flags`
4946
+ * provider under `identity` and return the resolved value. The base class
4947
+ * wires no provider, so it returns `null` — `resolveReactiveOutcome` reads
4948
+ * `null` as "nothing to deliver" and the subscriber keeps its default.
4949
+ */
4950
+ protected runFlagSubscriptionRead(_functionPath: string, _arguments: Record<string, unknown>, _identity?: SubscriptionIdentity): Promise<unknown>;
4951
+ /**
4952
+ * The Cloudflare Queues declared by this app, surfaced via
4953
+ * `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
4954
+ * Durable Objects and hold no shard state, so this is pure declaration
4955
+ * metadata statically discovered by `@lunora/codegen` from `lunora/queues.ts`
4956
+ * and emitted into the generated subclass, which overrides this. The base
4957
+ * class can't see the user's project, so it reports none.
4958
+ */
4959
+ protected queuesMetadata(): QueuesResult;
4960
+ /**
4421
4961
  * The Cloudflare Workflows declared by this app, surfaced via
4422
4962
  * `__lunora_admin__:listWorkflows` for the studio's Workflows page. Workflows
4423
4963
  * are NOT Durable Objects and hold no shard state, so this is pure
@@ -4580,16 +5120,117 @@ declare abstract class ShardDO {
4580
5120
  * unless the request carried an `x-lunora-mutation-id` header (queries and
4581
5121
  * legacy clients leave `currentRequestMutationId` undefined).
4582
5122
  *
4583
- * Called on the live dispatch path right after the handler's writes have
4584
- * auto-committed, through the same `this.sql` handle, so the dedup row is
4585
- * durable iff those writes are. (The DO has no ambient BEGIN/COMMIT around a
4586
- * mutation `handleRpc` invokes the user handler directly so this can't
4587
- * piggyback on a surrounding transaction; it commits as its own statement
4588
- * immediately after.) `INSERT OR IGNORE` keeps a concurrent double-dispatch
4589
- * of the same id idempotent. Also runs the throttled dedup-table GC.
5123
+ * For a mutation this runs INSIDE the handler's transaction (via
5124
+ * {@link ShardDO.commitMutationBookkeeping}, which `handleRpc` invokes before
5125
+ * the transaction commits), so the dedup row is durable iff the writes are —
5126
+ * closing the crash window where the writes commit but the replay guard does
5127
+ * not. Actions/queries aren't transaction-wrapped, so they call this on the
5128
+ * live dispatch path right after the handler resolves, through the same
5129
+ * `this.sql` handle. `INSERT OR IGNORE` keeps a concurrent double-dispatch (or
5130
+ * the now-skipped post-dispatch call) of the same id idempotent. Also runs the
5131
+ * throttled dedup-table GC.
4590
5132
  */
4591
5133
  protected persistIdempotentResult(result: unknown): void;
4592
5134
  /**
5135
+ * Whether `functionPath` names a registered custom mutator (a `defineMutator`
5136
+ * declaration) rather than an ordinary `mutation`. The base class knows of no
5137
+ * mutators, so the default is `false`; the codegen-generated subclass
5138
+ * overrides this to consult its mutator registry. When `true` (and the push
5139
+ * carries a `clientId`/`clientSeq`), the dispatch path applies the
5140
+ * `__client_watermark` ordering semantics instead of the legacy idempotency
5141
+ * dedup.
5142
+ */
5143
+ protected isCustomMutator(_functionPath: string): boolean;
5144
+ /**
5145
+ * Classify an in-flight custom-mutator push against the shard's stored
5146
+ * high-watermark for `currentRequestClientId`. The watermark is the highest
5147
+ * per-client sequence the DO has applied, so the push is exactly one of:
5148
+ *
5149
+ * - `"already"` — `seq &lt;= watermark`: a replay of a confirmed (or in-flight,
5150
+ * now-resent) mutation. The handler must NOT re-run; the dispatch path returns
5151
+ * a benign ack so the client drops the pending overlay.
5152
+ * - `"next"` — `seq == watermark + 1`: the next mutation in order. Run the
5153
+ * authoritative `server` impl and advance the watermark in the same commit.
5154
+ * - `"gap"` — `seq > watermark + 1`: an out-of-order arrival (an earlier push
5155
+ * was lost). Halt: the client must resend from `watermark + 1`.
5156
+ *
5157
+ * Returns `undefined` when the push is not a watermarked custom mutator
5158
+ * (missing client id/seq, or a stub `sql` handle without the table) so the
5159
+ * caller falls through to the legacy idempotency path.
5160
+ */
5161
+ protected classifyClientMutation(): ClientMutationClass | undefined;
5162
+ /**
5163
+ * Terminal response for a watermarked custom-mutator push that is NOT the
5164
+ * next-in-order mutation — an idempotent replay ack (`"already"`) or an
5165
+ * out-of-order halt (`"gap"`). Returns `undefined` for an ordinary mutation
5166
+ * or a `"next"` push so `fetch` proceeds to the authoritative handler. Records
5167
+ * the function call on the short-circuit paths so metrics stay attributed.
5168
+ */
5169
+ protected rejectNonNextMutation(functionPath: string, mutatorClass: ClientMutationClass | undefined, dispatchStartedAt: number): Response | undefined;
5170
+ /**
5171
+ * Respond to a dispatch that hit the `(identity, mutationId)` idempotency
5172
+ * cache. Records the (zero-work) function call, then: for a `"next"` custom
5173
+ * mutator whose handler already committed but whose watermark advance was
5174
+ * lost to a crash in between, re-advance and echo `lastMutationId` exactly as
5175
+ * the post-commit path does (otherwise the cached branch returns a bare
5176
+ * result with a stale watermark and the client reports every later seq as a
5177
+ * gap forever); for everything else, return the bare cached `{ result }`.
5178
+ */
5179
+ protected respondFromIdempotencyCache(functionPath: string, dispatchStartedAt: number, mutatorClass: ClientMutationClass | undefined, cachedValue: unknown): Response;
5180
+ /**
5181
+ * The CDC cursor a just-committed plain mutation landed at — the post-write
5182
+ * high-watermark, on the same scale as the `cursor` on `data`/`delta` frames.
5183
+ * The client drops a pending per-call optimistic overlay once it sees a frame
5184
+ * with `cursor >= commitCursor` (gapless reconciliation that keeps mutations
5185
+ * concurrent, without the serialized custom-mutator watermark). Scoped to a
5186
+ * mutation (carries an `x-lunora-mutation-id`) on a CDC-enabled shard;
5187
+ * `undefined` otherwise, leaving the wire byte-identical for queries/actions
5188
+ * and CDC-off shards.
5189
+ */
5190
+ protected mutationCommitCursor(): number | undefined;
5191
+ /**
5192
+ * Build the success response for a dispatched RPC. A `"next"` custom-mutator
5193
+ * push echoes the applied `lastMutationId` so the client drops the pending
5194
+ * optimistic overlay as soon as the ack lands; a plain mutation echoes
5195
+ * `commitCursor` (see {@link mutationCommitCursor}); other calls return the
5196
+ * bare `{ result }` envelope unchanged.
5197
+ */
5198
+ protected buildDispatchResponse(mutatorClass: ClientMutationClass | undefined, result: unknown): Response;
5199
+ /**
5200
+ * Commit a mutation's replay bookkeeping — the `(identity, mutationId)`
5201
+ * idempotency dedup row and, for a `"next"` custom-mutator push, the
5202
+ * `__client_watermark` advance — INSIDE the handler's transaction. Called by
5203
+ * the generated `handleRpc` mutation branch after the user handler resolves
5204
+ * but before the transaction commits, so the writes, the dedup row, and the
5205
+ * watermark land in one atomic commit: a crash can't leave the writes durable
5206
+ * without the replay guard (which a re-dispatch would otherwise re-run) nor
5207
+ * without the watermark. Sets {@link ShardDO.mutationBookkeepingCommitted} so
5208
+ * `fetch` skips the redundant post-dispatch persist.
5209
+ */
5210
+ protected commitMutationBookkeeping(result: unknown): void;
5211
+ /**
5212
+ * Best-effort replay bookkeeping for the live dispatch path, run after
5213
+ * `handleRpc` returns. A generated mutation already committed it atomically
5214
+ * inside its transaction (via {@link ShardDO.commitMutationBookkeeping}, which
5215
+ * sets the flag), so this skips. Actions/queries aren't transaction-wrapped,
5216
+ * so they record their dedup row here (a no-op without an `x-lunora-mutation-id`),
5217
+ * and a `"next"` push advances its watermark (the gap self-heals on replay).
5218
+ */
5219
+ protected recordPostDispatchBookkeeping(result: unknown, mutatorClass: ClientMutationClass | undefined): void;
5220
+ /**
5221
+ * Advance the stored high-watermark for the in-flight custom mutator to
5222
+ * `currentRequestClientSeq` through the same `this.sql` handle. On the
5223
+ * transactional path ({@link ShardDO.commitMutationBookkeeping}, `strict`) it
5224
+ * runs inside the handler's commit, so the watermark is durable iff the writes
5225
+ * are; a failure rethrows to roll the mutation back. On the best-effort
5226
+ * cache-hit recovery path (`strict` omitted) a missing table is swallowed —
5227
+ * the replay re-runs and re-advances (the read side treats a missing row as
5228
+ * watermark 0), so the gap self-heals.
5229
+ */
5230
+ protected advanceClientMutationWatermark(options?: {
5231
+ strict?: boolean;
5232
+ }): void;
5233
+ /**
4593
5234
  * Replay a batch of CDC changes into this shard (point-in-time recovery).
4594
5235
  * Schema-aware — it builds a `createShardCtxDb` writer — so the base class
4595
5236
  * can't implement it; the codegen-generated subclass overrides this to call
@@ -4608,6 +5249,17 @@ declare abstract class ShardDO {
4608
5249
  protected subscribe(ws: WebSocket, subId: string, query: SubscriptionQuery): "ok" | "serialize_failed" | "too_many";
4609
5250
  protected unsubscribe(ws: WebSocket, subId: string): void;
4610
5251
  /**
5252
+ * Register a live shape subscription on a socket — the partial-replication
5253
+ * parallel to {@link ShardDO.subscribe}. Stores the descriptor in the
5254
+ * attachment's `shapes` registry (created lazily) so it survives
5255
+ * hibernation, sharing the per-socket cap with `subs`. Returns a status the
5256
+ * caller surfaces as a structured error frame; never throws (a thrown
5257
+ * `webSocketMessage` is a fatal-channel error under the hibernation API).
5258
+ */
5259
+ protected shapeSubscribe(ws: WebSocket, subId: string, shape: ShapeSubscriptionQuery): "ok" | "serialize_failed" | "too_many";
5260
+ /** Remove a shape subscription and its poke baseline. Mirrors {@link ShardDO.unsubscribe}'s rollback-on-serialize-failure contract. */
5261
+ protected shapeUnsubscribe(ws: WebSocket, subId: string): void;
5262
+ /**
4611
5263
  * Decide whether a single subscription is interested in a mutation
4612
5264
  * delta. The default implementation checks the table name, then runs a
4613
5265
  * shallow-equality predicate over `query.args` against `delta.row`. A
@@ -4645,6 +5297,74 @@ declare abstract class ShardDO {
4645
5297
  */
4646
5298
  protected executeSubscription(_functionPath: string, _args: Record<string, unknown>, _identity?: SubscriptionIdentity): Promise<SubscriptionOutcome | null>;
4647
5299
  /**
5300
+ * Resolve a named shape to its concrete query plan for `identity`. The base
5301
+ * class has no shape registry, so it returns `undefined` — partial
5302
+ * replication is disabled and a `shape_subscribe` is rejected. The
5303
+ * codegen-generated subclass overrides this to look the shape up in the
5304
+ * project's `defineShape` registry, evaluate its `where(ctx, args)` under the
5305
+ * subscriber's verified identity, and AND-compose it with the table's RLS
5306
+ * read base-where into {@link ResolvedShape.effectiveWhere}.
5307
+ *
5308
+ * `identity` is the socket's OWN verified identity (the same unforgeable
5309
+ * value `refreshSubscriptions` threads), passed by value so this never reads
5310
+ * the mutable per-request identity fields. Returning `undefined` is the
5311
+ * fail-closed signal — an unknown shape, or an RLS-required table with no
5312
+ * policy resolving for this identity, yields no subscription rather than
5313
+ * leaking rows.
5314
+ */
5315
+ protected resolveShape(_name: string, _args: Record<string, unknown>, _identity?: SubscriptionIdentity): ResolvedShape | undefined;
5316
+ /**
5317
+ * The RLS-uniform gate (plan 075 Phase 3): whether a reactive shape may be
5318
+ * relay-multicast — i.e. one delta is correct for **every** subscriber. The owner
5319
+ * decides it (see {@link OwnerRelay.isShapeRelayUniform} — a static RLS read-policy
5320
+ * guard plus claim-exhaustive `Proxy` probes, fail-closed); this thin delegation
5321
+ * is the seam the gate test exercises. A non-owner DO is never relay-uniform.
5322
+ */
5323
+ protected isShapeRelayUniform(name: string, args: Record<string, unknown>): boolean;
5324
+ /**
5325
+ * Read the FULL current membership of a `.global()`-table shape from its D1
5326
+ * (or Hyperdrive) backend — the seed/poll source for the latency-tiered
5327
+ * global shape path. A `.global()` table lives in another store with no
5328
+ * per-DO op-log, so this is the only way to learn its rows from inside the
5329
+ * shard DO; {@link ShardDO.seedGlobalShape} calls it once on subscribe and
5330
+ * {@link ShardDO.refreshGlobalShape} on every alarm tick, diffing the result
5331
+ * against the per-socket snapshot to compute the poke.
5332
+ *
5333
+ * The base class has no global backend, so it returns `[]` (a base-only DO,
5334
+ * or a project with no global tables, never resolves a global shape). The
5335
+ * codegen subclass overrides it to drain `globalDb.findMany(table, { where:
5336
+ * effectiveWhere })` under the socket's verified `identity` — the same
5337
+ * unforgeable value `resolveShape` composed the RLS predicate with, so the
5338
+ * D1 read is identity-scoped exactly like the poke-live path.
5339
+ */
5340
+ protected readGlobalShapeRows(_resolved: ResolvedShape, _identity?: SubscriptionIdentity): Promise<ShapeRow[]>;
5341
+ /**
5342
+ * Poll external-source (`.source(...)`) tables once (plan 077): materialize
5343
+ * each sourced table's freshly-pulled tenant slice into this DO's SQLite. The
5344
+ * base `ShardDO` has no sourced tables, so it returns `0` and the ingest tier
5345
+ * stays dormant — zero behavior change for every existing DO. The codegen
5346
+ * subclass overrides it to, per sourced table, build a `createShardCtxDb`
5347
+ * writer, read the tenant slice from Hyperdrive under this DO's shard key, and
5348
+ * run `runExternalSourceTick` (read local baseline → diff → apply via the
5349
+ * validated CDC writer). Returns the number of sourced tables still being
5350
+ * polled, so the shared poll alarm ({@link ShardDO.alarm}) re-arms while ingest
5351
+ * is active.
5352
+ */
5353
+ protected pollExternalSources(): Promise<number>;
5354
+ /**
5355
+ * Arm the shared poll alarm for external-source ingest (plan 077). The alarm is
5356
+ * shared with the global-shape poll tier; the codegen subclass calls this once
5357
+ * (on construction / first sourced write) so a sourced DO starts its ingest
5358
+ * loop, after which {@link ShardDO.alarm} re-arms itself while
5359
+ * {@link ShardDO.pollExternalSources} reports remaining work. Idempotent; a
5360
+ * no-op when the runtime exposes no `setAlarm` (unit harness).
5361
+ */
5362
+ protected scheduleSourcePoll(): Promise<void>;
5363
+ /** This DO's shard key (its DO name), or `__root__` for the single-DO default. The `tenantBy` mapper binds it into the source query. */
5364
+ protected currentShardKey(): string;
5365
+ /** Record a contained external-source ingest failure (one sourced table's poll) into the log ring without aborting the others. */
5366
+ protected recordExternalSourceError(table: string, error: unknown): void;
5367
+ /**
4648
5368
  * Look up a streaming-query function and return a thunk that produces the
4649
5369
  * `AsyncIterable&lt;unknown>` when handed an {@link AbortSignal}. The codegen
4650
5370
  * subclass overrides this to dispatch via `LUNORA_FUNCTIONS`; the base
@@ -4837,6 +5557,26 @@ declare abstract class ShardDO {
4837
5557
  */
4838
5558
  private errorToResponse;
4839
5559
  /**
5560
+ * Batch dispatch (plan 088). Applies each `calls[]` entry through the SAME
5561
+ * single-call `/rpc` path (via a nested `this.fetch`), **sequentially**, so
5562
+ * the per-`(identity, mutationId)` idempotency dedup and the per-client
5563
+ * `__client_watermark` ordering are enforced entry-by-entry exactly as for an
5564
+ * individual call — no duplication of the dispatch core, no reordering.
5565
+ *
5566
+ * Failures are **per-slot, not fail-fast**: an entry that throws (or a
5567
+ * custom-mutator `OUT_OF_ORDER` gap) is captured in its own result slot and
5568
+ * later entries still run. Ordering is still safe — a later same-client
5569
+ * mutator after a gap re-classifies as a gap too (the watermark never
5570
+ * advanced), so it cannot apply out of order; unrelated entries/queries are
5571
+ * independent. The response is `{ results: [{ id, status, body }] }` in
5572
+ * request order; each `body` is the untouched single-call envelope (its
5573
+ * `result` already wire-encoded), so the client demuxes + decodes each
5574
+ * exactly as one call.
5575
+ */
5576
+ private handleBatchRpc;
5577
+ /** Dispatch one batch entry through the single-call `/rpc` path and capture its envelope (plan 088). */
5578
+ private dispatchBatchEntry;
5579
+ /**
4840
5580
  * Serve a reserved admin-introspection RPC (`__lunora_admin__:*`) for the
4841
5581
  * data browser. Gated by `env.LUNORA_ADMIN_TOKEN`: introspection is
4842
5582
  * **disabled unless the token is configured**, and when it is, the request
@@ -4920,6 +5660,16 @@ declare abstract class ShardDO {
4920
5660
  */
4921
5661
  private handleGetWorkflowInstanceStatus;
4922
5662
  /**
5663
+ * Serve `__lunora_admin__:listFlags` — the studio's read-only Flags page.
5664
+ * Evaluates every statically-discovered feature flag under an optional
5665
+ * `args.context` targeting context (the studio's editable context editor)
5666
+ * via the {@link evaluateFlags} hook, which the codegen subclass overrides
5667
+ * with live OpenFeature evaluation. Read-only: a flag lookup mutates no shard
5668
+ * state, so nothing is flushed or audited. Admin-gated by `handleAdminRpc`'s
5669
+ * caller.
5670
+ */
5671
+ private handleListFlags;
5672
+ /**
4923
5673
  * Run `run()` with the per-request identity pinned to (`userId`, `identity`),
4924
5674
  * then restore the prior values in a `finally` (even if `run()` throws), so the
4925
5675
  * forced identity can never leak into a later dispatch on this DO instance. The
@@ -5111,6 +5861,20 @@ declare abstract class ShardDO {
5111
5861
  * Read-only: it touches no SQLite and mutates no socket state.
5112
5862
  */
5113
5863
  private collectSubscriptions;
5864
+ /**
5865
+ * Assemble the `__lunora_admin__:getFanoutMetrics` payload for the Studio
5866
+ * fan-out observability panel (plan 075 Phase 1). The point-in-time topic
5867
+ * subscriber counts are folded live from each socket's attachment via
5868
+ * {@link summarizeFanoutTopics}; the running per-path cost counters are the
5869
+ * in-memory {@link ShardDO.fanout} tallies, sharing `metrics.sinceMs` as the
5870
+ * "since this instance woke" epoch. Touches no SQLite and mutates no socket
5871
+ * state; it does call `relay.relayCount()`, which advances the promotion
5872
+ * latch — safe here because that transition is a pure, monotonic function of
5873
+ * the live socket count (DOs are single-threaded), so a metrics poll only ever
5874
+ * drives the latch to the same state the routing path would compute for the
5875
+ * same count, never a divergent one.
5876
+ */
5877
+ private collectFanoutMetrics;
5114
5878
  /** Resolve a `getAuditLog` admin read, parsing the optional `limit`/`sinceSeq` cursor args and ensuring the reserved table first. */
5115
5879
  private readAdminAuditLog;
5116
5880
  /**
@@ -5179,6 +5943,49 @@ declare abstract class ShardDO {
5179
5943
  */
5180
5944
  private executeAdminSubscription;
5181
5945
  /**
5946
+ * Resolve one subscription (seed or refresh) to its {@link SubscriptionOutcome}
5947
+ * by routing the `functionPath` to the right read path — shared by
5948
+ * {@link seedSubscription} and {@link refreshSubscriptions} so both branch
5949
+ * identically:
5950
+ * - `__lunora_admin__:*` → {@link executeAdminSubscription} (raw SQLite read).
5951
+ * - {@link FLAGS_FUNCTION_PREFIX} → {@link runFlagSubscriptionRead} (the codegen subclass evaluates the flag through the configured provider). The value isn't bound to any table, so it is tagged with the {@link ADMIN_WILDCARD} dep — re-evaluated on every write-flush so a live `useFlag` stays current within a session. A `null` read means "nothing to deliver" (no provider, or a flag that resolved to `null`).
5952
+ * - everything else → {@link executeSubscription} (the user query, under the socket's own by-value identity).
5953
+ */
5954
+ private resolveReactiveOutcome;
5955
+ /**
5956
+ * SECURITY BOUNDARY for cross-socket reactive dedup. A read is
5957
+ * identity-INDEPENDENT only when its result cannot vary by the caller's
5958
+ * verified identity — i.e. the admin/reserved introspection reads, which
5959
+ * route to {@link executeAdminSubscription} and ignore the
5960
+ * {@link SubscriptionIdentity} entirely.
5961
+ *
5962
+ * Everything else is identity-DEPENDENT and must NEVER be shared across
5963
+ * sockets: a user query may be `rls()` / `ctx.auth`-scoped (different rows
5964
+ * per identity), and a flag read ({@link FLAGS_FUNCTION_PREFIX}) evaluates
5965
+ * the provider with the subscriber's identity (per-user targeting). Sharing
5966
+ * one socket's result with another would leak one identity's rows/flags to a
5967
+ * different identity, so this predicate gates {@link resolveReactiveOutcomeDeduped}
5968
+ * shut for them.
5969
+ */
5970
+ protected isIdentityIndependent(functionPath: string): boolean;
5971
+ /**
5972
+ * Memoizing wrapper over {@link resolveReactiveOutcome}: flush-local sharing across sockets.
5973
+ * Within a single {@link refreshSubscriptions} pass, N sockets subscribed to
5974
+ * the SAME identity-independent `(functionPath, args)` re-run the query N
5975
+ * times today (see the Case-6 fan-out characterization). When the read is
5976
+ * identity-independent (admin/reserved — see {@link isIdentityIndependent})
5977
+ * its result is the same for every socket, so the first run is cached (by its
5978
+ * in-flight Promise, since the bounded worker pool runs sockets in parallel)
5979
+ * and shared with the rest — collapsing N runs to ONE.
5980
+ *
5981
+ * Identity-DEPENDENT reads are passed straight through, UNCACHED: each socket
5982
+ * must evaluate under its own by-value identity (RLS / `ctx.auth` / per-user
5983
+ * flags), so they never share a result. The `cache` is created fresh per
5984
+ * flush by the caller, so a result is never reused across passes (it would go
5985
+ * stale after the next write).
5986
+ */
5987
+ private resolveReactiveOutcomeDeduped;
5988
+ /**
5182
5989
  * Constant-time bearer check against `env.LUNORA_ADMIN_TOKEN`. Returns
5183
5990
  * `false` (closed) when the token is unset so admin introspection is
5184
5991
  * opt-in rather than exposed by default.
@@ -5210,6 +6017,17 @@ declare abstract class ShardDO {
5210
6017
  */
5211
6018
  private flushChangedTables;
5212
6019
  /**
6020
+ * Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
6021
+ * until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
6022
+ * Tables merged by a `flushChangedTables` that lands mid-pass are picked up
6023
+ * by the next loop iteration, so every committed write is observed by a
6024
+ * refresh that runs after it — bursts simply share a pass. The post-write
6025
+ * high-watermark and live-socket set are re-read inside each
6026
+ * `refreshSubscriptions` / `pokeShapeSubscribers` call, so a later batch
6027
+ * always reflects the latest committed state.
6028
+ */
6029
+ private drainSubscriptionRefreshes;
6030
+ /**
5213
6031
  * For every live subscription whose query reads one of `changed`, re-run
5214
6032
  * the query and push a fresh `{ type: "data" }` frame when the result
5215
6033
  * differs from the last one sent. Subscriptions with no `functionPath`
@@ -5282,6 +6100,216 @@ declare abstract class ShardDO {
5282
6100
  */
5283
6101
  private seedSubscription;
5284
6102
  /**
6103
+ * Drive the full `shape_subscribe` flow as one failure-aware unit: persist the
6104
+ * attachment, seed the shape, and ack ONLY once both succeed. A persist
6105
+ * rejection (`too_many`/`serialize_failed`) or a seed that can't resolve the
6106
+ * shape (unknown / RLS-denied / cross-shard-invalid) rolls the attachment back
6107
+ * and sends an `error` frame instead of acking — so a client is never left
6108
+ * acked but subscribed to a shape that will never deliver. Never throws (a
6109
+ * thrown `webSocketMessage` is fatal to the hibernating socket).
6110
+ */
6111
+ private handleShapeSubscribe;
6112
+ /** Send a structured `error` frame for a failed `shape_subscribe`, swallowing a send on an already-closed socket. */
6113
+ private sendShapeSubscribeError;
6114
+ /**
6115
+ * Seed a freshly-registered shape subscription. Resolves the shape under the
6116
+ * socket's verified identity, then ships either:
6117
+ *
6118
+ * - a **catch-up** poke (the membership diff in `(sinceCheckpoint, cursor]`)
6119
+ * when the client supplied a still-current checkpoint within the CDC retention
6120
+ * window and on this epoch — the cheap reconnect path; or
6121
+ * - a **full** insert-poke of the shape's entire current membership — a
6122
+ * first-time subscribe, or a reconnect that fell outside retention / forked
6123
+ * epoch.
6124
+ *
6125
+ * Either way the per-socket shape memo advances to the flush watermark so
6126
+ * later `pokeShapeSubscribers` passes diff from the right point.
6127
+ *
6128
+ * Returns `"ok"` once the shape resolved and its seed poke was attempted, or a
6129
+ * `{ code, message }` failure when the shape can't be resolved — an unknown /
6130
+ * RLS-denied shape (a base class with no registry resolves nothing), or a
6131
+ * `resolveShape` that threw (e.g. a cross-shard-join guard). The caller rolls
6132
+ * back the persisted attachment and errors instead of acking, so a client is
6133
+ * never left subscribed to a shape that will never deliver.
6134
+ */
6135
+ private seedShapeSubscription;
6136
+ /**
6137
+ * Seed a non-`.global()` (op-log-backed) shape: either a catch-up diff over
6138
+ * `(sinceSeq, cursor]` when the client supplied a still-current checkpoint on
6139
+ * this epoch within the CDC retention window, or a full membership insert-poke
6140
+ * otherwise. The memo advances to `cursor` only once the poke is delivered, so
6141
+ * a failed send re-diffs from the prior point rather than skipping rows. May
6142
+ * throw (a stub `sql` handle, a membership probe failure); the caller converts
6143
+ * it to a structured `shape_subscribe` error.
6144
+ */
6145
+ private seedOpLogShape;
6146
+ /**
6147
+ * Compute an op-log shape seed (cursor, epoch, the resume base, and the
6148
+ * membership `rowsPatch`) WITHOUT sending — the shared core of
6149
+ * {@link ShardDO.seedOpLogShape} (sends to a local socket) and the owner relay's
6150
+ * `buildShapeSeedFrames` (serializes the frames for a relay to deliver, plan 075
6151
+ * Phase 3, via the {@link RelayHost} seam). Resume only when CDC is on, the client is on this
6152
+ * epoch, its checkpoint doesn't run ahead of ours, and the log still covers it;
6153
+ * else a full re-seed. A fully-compacted log only proves "nothing missed" when
6154
+ * the client is already at `cursor`.
6155
+ * @returns the cursor/epoch, the resume base (`baseCheckpoint`), and the membership patch
6156
+ */
6157
+ private computeOpLogShapeSeed;
6158
+ /**
6159
+ * Fan the membership diff of every shape affected by this flush to its
6160
+ * subscribers — the partial-replication parallel to
6161
+ * {@link ShardDO.refreshSubscriptions}, called alongside it from
6162
+ * {@link ShardDO.flushChangedTables}. For each socket (bounded fan-out, same
6163
+ * concurrency + `awaitWsDrain` backpressure as the subscription path) it
6164
+ * resolves each shape under the socket's identity, diffs only the shapes
6165
+ * whose table changed in `(memoCursor, frameCursor]`, and emits one poke
6166
+ * carrying a part per changed shape. No-op when no socket holds a shape.
6167
+ */
6168
+ private pokeShapeSubscribers;
6169
+ /**
6170
+ * Diff every op-log-backed shape a socket holds against this flush, splitting
6171
+ * the results into the poke parts to send and the per-shape memo advances. A
6172
+ * `.global()` shape (driven by the alarm poll loop, not this flush) and a shape
6173
+ * whose table didn't change are skipped; a shape whose resolve/diff throws is
6174
+ * logged and skipped with its memo unadvanced so a later flush retries. Empty
6175
+ * diffs advance unconditionally; part-bearing shapes advance only once the
6176
+ * caller confirms the poke was delivered.
6177
+ */
6178
+ private collectShapePokeParts;
6179
+ /**
6180
+ * Drain the op-log range `(sinceSeq, upTo]` for `table` into the latest op per
6181
+ * row id (collapsing multiple ops on the same row to the newest). Within one
6182
+ * flush, every shape over the SAME `(table, sinceSeq, upTo)` reads the
6183
+ * identical changelog slice, so the drained map is memoized in the
6184
+ * caller-supplied `cache` (created fresh per flush) — N shapes on a table
6185
+ * share ONE changelog drain instead of re-scanning it per shape. The
6186
+ * per-shape membership probe still runs per shape (its predicate is
6187
+ * identity/args-specific), so only the shared op read is collapsed.
6188
+ */
6189
+ private readShapeOpRange;
6190
+ /**
6191
+ * Read one page of the `__cdc_log` for a shape diff (table-scoped). A thin
6192
+ * protected seam over {@link readCdcChanges}: it isolates the single
6193
+ * changelog read that {@link readShapeOpRange} memoizes per flush, and gives
6194
+ * tests a point to count the reads the op-range cache collapses.
6195
+ */
6196
+ protected readShapeCdcPage(sql: SqlExec, sinceSeq: number, tables: ReadonlySet<string>): {
6197
+ changes: CdcChange[];
6198
+ cursor: number;
6199
+ };
6200
+ /**
6201
+ * Build the row-ops for a shape over the op range `(sinceSeq, upTo]`. Reads
6202
+ * the changelog (drained across pages via {@link readShapeOpRange}, shared
6203
+ * across same-range shapes in a flush), collapses to the latest op per row,
6204
+ * then runs ONE membership probe ({@link selectShapeMemberIds}) over the
6205
+ * changed ids: a row still in the set → upsert with its post-image doc
6206
+ * (projected to the shape's columns); a row that left the set, or any delete,
6207
+ * → `delete(key)` (a delete carries no post-image, so membership is
6208
+ * unknowable from the op alone — the client no-ops an unknown key).
6209
+ */
6210
+ private buildShapeDiff;
6211
+ /** Build the full insert-poke of a shape's current membership — the first-seed/full-reseed rowset. */
6212
+ private buildShapeSeed;
6213
+ /**
6214
+ * Seed a `.global()`-table shape: read its full membership from D1, ship it
6215
+ * as one insert-poke, record the membership snapshot the alarm poll loop will
6216
+ * diff against, and arm the poll alarm. A global shape has no op-log cursor,
6217
+ * so the poke is stamped at this DO's current cursor (informational only) and
6218
+ * carries no resume base — a reconnect always re-seeds full.
6219
+ */
6220
+ private seedGlobalShape;
6221
+ /**
6222
+ * Re-read a global shape's membership from D1 and poke only the diff against
6223
+ * the socket's last snapshot: a new key → `insert`, a changed projected value
6224
+ * → `update`, a vanished key → `delete`. The snapshot advances to the fresh
6225
+ * membership even when the diff is empty, so the next tick compares from here.
6226
+ * No frame is sent when nothing changed (the common steady-state tick).
6227
+ */
6228
+ private refreshGlobalShape;
6229
+ /**
6230
+ * Read a socket's global-shape baseline, preferring the hot in-memory cache
6231
+ * and falling back to the durable `__global_shape_snapshot` table on a miss (a
6232
+ * cold socket after a hibernation eviction). The loaded baseline repopulates
6233
+ * the cache so subsequent ticks in this wake hit memory. An empty
6234
+ * `connectionId` (a socket that never went through the lifecycle-aware upgrade,
6235
+ * e.g. a unit harness) skips the durable read and behaves as in-memory-only.
6236
+ */
6237
+ private readGlobalSnapshot;
6238
+ /** Record a socket's latest global-shape membership snapshot in the in-memory cache (creating the per-socket map lazily). */
6239
+ private recordGlobalSnapshot;
6240
+ /**
6241
+ * Load a durable global-shape baseline from SQLite, or an empty map when none
6242
+ * is stored / the durable path is unavailable. A stub `sql` handle (unit
6243
+ * harness) or a missing table degrades to in-memory-only behavior rather than
6244
+ * failing the poll tick.
6245
+ */
6246
+ private loadGlobalSnapshot;
6247
+ /**
6248
+ * Persist a socket's global-shape baseline to SQLite so the poll-loop diff
6249
+ * survives hibernation. A no-op for a connection-id-less socket or a stub
6250
+ * `sql` handle (the in-memory cache then carries the baseline for the DO's
6251
+ * lifetime, matching the pre-durable behavior).
6252
+ */
6253
+ private saveGlobalSnapshot;
6254
+ /**
6255
+ * Arm the poll alarm for `.global()` shapes if one isn't already pending.
6256
+ * Idempotent — every global-shape seed calls it, but only the first arms the
6257
+ * alarm. Degrades to a no-op when the runtime exposes no `setAlarm` (the unit
6258
+ * harness): a global shape is then seed-only, which the poll-loop tests assert
6259
+ * by driving {@link ShardDO.alarm} directly.
6260
+ */
6261
+ private scheduleGlobalPoll;
6262
+ /**
6263
+ * Record a contained shape-tier error (poll / poke / seed) into the DO's log
6264
+ * ring without aborting the rest of the pass. The shape pipeline is a
6265
+ * best-effort fan-out: one socket's read or one shape's resolve failing must
6266
+ * never take down the others — so callers swallow the throw and surface it
6267
+ * here for diagnosis. `context` is a synthetic `shape:phase:subId` path.
6268
+ */
6269
+ private recordShapeError;
6270
+ /**
6271
+ * Guard a global shape's materialized membership against {@link
6272
+ * ShardDO.GLOBAL_SHAPE_MAX_ROWS}. Returns `true` when the row count is within
6273
+ * the cap; otherwise records a diagnosable error and returns `false` so the
6274
+ * caller fails the shape closed (no snapshot retained, no poke sent) rather
6275
+ * than risking a DO eviction on an unbounded global table. The transient read
6276
+ * buffer is bounded by the same gate — an over-cap membership is dropped, not
6277
+ * snapshotted per socket.
6278
+ */
6279
+ private withinGlobalShapeBound;
6280
+ /**
6281
+ * Refresh every `.global()`-table shape held across all live sockets, one
6282
+ * diff-poke per (socket, shape). Returns the number of global shapes still
6283
+ * subscribed so {@link ShardDO.alarm} knows whether to re-arm. Expired sockets
6284
+ * are dropped in passing (mirrors {@link ShardDO.pokeShapeSubscribers}).
6285
+ */
6286
+ private pollGlobalShapes;
6287
+ /**
6288
+ * Refresh one socket's `.global()`-table shapes, containing per-shape
6289
+ * failures so a single throw never aborts the poll tick (and with it the
6290
+ * re-arm). Returns the count of global shapes still subscribed on this socket
6291
+ * — a failed `resolveShape`/read keeps its shape counted so the alarm keeps
6292
+ * polling and retries next tick.
6293
+ */
6294
+ private pollSocketGlobalShapes;
6295
+ /**
6296
+ * Send one poke (`pokeStart` → `pokePart` per shape → `pokeEnd`) to a socket.
6297
+ * All parts apply atomically at `pokeEnd`. Returns `true` when every frame was
6298
+ * handed to the socket, `false` when a send threw mid-poke (the socket closed)
6299
+ * — callers must NOT advance their shape baselines on a `false` so the client
6300
+ * re-receives the rows on its next flush/reconnect instead of losing them.
6301
+ */
6302
+ private sendPoke;
6303
+ /**
6304
+ * The recipient client's `__client_watermark` for stamping a poke's
6305
+ * `lastMutationId`, or `undefined` when the socket announced no `clientId`
6306
+ * (a client that doesn't use custom mutators — nothing to drop an overlay
6307
+ * for). Read off the attachment so it survives hibernation.
6308
+ */
6309
+ private socketClientWatermark;
6310
+ /** Record a shape's poke baseline cursor on a socket (creating the per-socket map lazily). */
6311
+ private recordShapeMemo;
6312
+ /**
5285
6313
  * Record `outcome` as this socket's diff baseline for `subId` without
5286
6314
  * sending a frame. Used by the resume fast-path, where the client keeps its
5287
6315
  * cached value but the server still needs a baseline so the next
@@ -5353,6 +6381,15 @@ declare abstract class ShardDO {
5353
6381
  * on older runtimes, where it degrades to a no-op.
5354
6382
  */
5355
6383
  private armWebSocketKeepalive;
6384
+ /**
6385
+ * Route the non-RPC requests `fetch` handles before the shard-local RPC
6386
+ * endpoint: a WebSocket upgrade, and the internal `/_lunora/relay` owner↔relay
6387
+ * control channel (never reachable by a client — the runtime forwards only
6388
+ * worker-internal traffic there). Returns `undefined` for an RPC request, which
6389
+ * `fetch` then dispatches.
6390
+ * @returns the routed response, or `undefined` when this is an RPC request
6391
+ */
6392
+ private routeNonRpc;
5356
6393
  private handleWebSocketUpgrade;
5357
6394
  /**
5358
6395
  * Whether this shard has a `__cdc_log` table. The single source of the
@@ -5400,6 +6437,14 @@ declare abstract class ShardDO {
5400
6437
  * but per-topic auth does not exist here; see `whisperSubscribe` on the client.
5401
6438
  */
5402
6439
  private broadcastWhisper;
6440
+ /**
6441
+ * Deliver an already-serialized whisper `frame` to every local socket joined to
6442
+ * `topic`, excluding `exclude` (the sender, or `undefined` for a frame the relay
6443
+ * hub forwarded in — its sender lives on another DO). Records the fan-out pass
6444
+ * for `getFanoutMetrics` (plan 075 Phase 1). Pure delivery — no SQLite, no CDC.
6445
+ * @returns the number of sockets the frame was sent to
6446
+ */
6447
+ private deliverWhisperLocal;
5403
6448
  private readAttachment;
5404
6449
  }
5405
6450
  /**
@@ -5573,8 +6618,11 @@ interface WhereSqlStrategy {
5573
6618
  fieldRef: FieldRefSql;
5574
6619
  /**
5575
6620
  * Dialect `contains` rendering given the field reference and the (already
5576
- * bound) search term. Absent ⇒ the portable `… LIKE '%' || term || '%'`
5577
- * concat form (SQLite/Postgres); MySQL supplies a `CONCAT(...)` variant.
6621
+ * bound, already wildcard-escaped) search term. Absent ⇒ the portable
6622
+ * `… LIKE '%' || term || '%' ESCAPE '\'` concat form (SQLite/Postgres); MySQL
6623
+ * supplies a `CONCAT(...)` variant. The term is escaped by
6624
+ * {@link compileContains}, so an implementation MUST pair it with
6625
+ * `ESCAPE '\'` for the literal-match to hold.
5578
6626
  */
5579
6627
  likeContains?: (reference: SQL, term: SQL) => SQL;
5580
6628
  /**
@@ -5596,4 +6644,4 @@ interface WhereSqlStrategy {
5596
6644
  * `undefined` when the input imposes no constraint (empty `where`).
5597
6645
  */
5598
6646
  declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
5599
- export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, AGGREGATE_SQL_FUNCTION, AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, type AdvisoriesResult, type AdvisoryFinding, type AggregateIndexDefinitionLike, type AggregateOp, type AggregateOptions, type AggregateResult, type AggregateTally, type ApplyOnDeleteOptions, type AuditEntry, type AuditLogResult, type AuthMetrics, type AuthMetricsBucket, type BroadcastDelta, CDC_LOG_TABLE, type CacheEntry, type CapturedMailRow, type CdcChange, type Clock, type ColumnMeta, type ColumnMetaLike, ConflictError, type CountArgs, CountRlsUnsupportedError, type CtxDbOptions, DATA_MIGRATION_STATE_TABLE, DEFAULT_MAX_RELATION_KEYS, type DataMigrationDocument, type DataMigrationLike, type DataMigrationTransform, type DatabaseWriterLike, type DependencyTracker, type DeployInfo, type ExportRow, type ExportShardAdminArgs, type ExportShardArgs, FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, type FacetColumnOptions, type FacetColumnResult, type FacetValue, type FieldOperators, type FunctionCallStat, type FunctionMetricBucket, type FunctionMetricIndexHit, type FunctionStatsResult, type GroupByEntry, type GroupByOptions, type HibernatableWebSocket, type IdGenerator, type ImportError, type ImportShardAdminArgs, type ImportShardArgs, type ImportShardResult, type IndexDefinitionLike, type IndexRangeBuilderLike, LogBuffer, type LogEntry, type LogEventInput, type LogLevel, type LogSink, MAIL_RETENTION, MAIL_TABLE, MAX_SQL_ROWS, MIN_ADMIN_TOKEN_LENGTH, MIN_AUTH_SECRET_LENGTH, type MaskColumnMetadata, type MaskPoliciesResult, type MigrationDirection, type MigrationRunResult, type MigrationStatus, type MigrationStatusRow, type MutationDelta, type NestedWith, NotFoundError, NotUniqueError, type OnDeleteActionLike, type OrderByInput, type OrderKey, type PaginationOptions, type PitrBookmarkResult, type PitrRestoreArgs, type PitrRestoreResult, type PitrStorage, type QueryArgs, type QueryPage, RANK_TIEBREAK, RELATION_FUNCTION_PREFIX, RLS_UNWRAP_SYMBOL, ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, type RankDirection, type RankIndexDefinitionLike, type RankOptions, type RankPage, type RankPageOptions, type RankPageRow, type RankPageRowKey, type RankResult, type RankSortKeyLike, ReactiveCache, type ReactiveCacheOptions, type ReadHook, type ReadTablePageOptions, type RecordAuthEventInput, type RecordFunctionMetricInput, type RecordMailInput, type RelationDefinitionLike, type RenderedSql, type ResolveRelationPredicatesOptions, type ResolveWithOptions, type RestrictableQueryOptions, type RlsPoliciesResult, type RlsPolicyMetadata, RlsRequiredError, type RlsRoleMetadata, type RpcRequest, type RunDataMigrationOptions, type RunShardApplyCdcArgs, type RunShardApplyCdcResult, type RunShardBulkDeleteArgs, type RunShardBulkDeleteResult, type RunShardExportArgs, type RunShardImportArgs, type RunShardMigrationArgs, type RunShardRankBeforeArgs, type RunShardRankPageArgs, type RunShardWriteArgs, type RunShardWriteResult, type RunTriggersOptions, SCAN_DEP, SESSION_DO_TTL_DEFAULT, SHARD_REGISTRY_DO_NAME, type ScheduledFunctionDoc, type SchedulerLike, type SchemaLike, type SearchFilterBuilderLike, type SecurityAuditResult, type SecurityFinding, type SecurityFindingKind, type SecurityFindingLevel, type SelectMatchingIdsOptions, type ServerDefaultContextLike, SessionDO, type SessionRecord, type SettingEntry, type SettingKind, type SettingsResult, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection$1 as SortDirection, type SqlConsoleResult, type SqlCursor, type SqlEngine, type SqlExec, type StorageRuleMetadata, type StorageRulesResult, type StudioFeaturesResult, type SubscriptionEnvelope, type SubscriptionOutcome, type SubscriptionQuery, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemReaderOptions, type SystemReaderSchedulerLike, type SystemReaderStorageLike, type SystemTableName, type TableColumnsResult, type TableDefinitionLike, type TableIndexInfo, type TableIndexesResult, type TableInfo, type TablePage, type TableReaderLike, type TablesColumnsResult, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type ValidatorLike, type WhereInput, type WhereSqlStrategy, type WithInput, type WorkflowMetadata, type WorkflowsResult, type WriteEvent, type WriteHook, aggregateSqlFunction, aggregateTableName, applyCdcChanges, applyOnDelete, armRestore, assertFlatPredicate, assertReadonly, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, listTables, matchesRankStaticWhere, matchesStaticWhere, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
6647
+ export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, AGGREGATE_SQL_FUNCTION, AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, type AdvisoriesResult, type AdvisoryFinding, type AggregateIndexDefinitionLike, type AggregateOp, type AggregateOptions, type AggregateResult, type AggregateTally, type ApplyOnDeleteOptions, type AuditEntry, type AuditLogResult, type AuthMetrics, type AuthMetricsBucket, type BroadcastDelta, CDC_LOG_TABLE, type CacheEntry, type CapturedMailRow, type CdcChange, type Clock, type ColumnMeta, type ColumnMetaLike, ConflictError, type CountArgs, CountRlsUnsupportedError, type CtxDbOptions, DATA_MIGRATION_STATE_TABLE, DEFAULT_MAX_RELATION_KEYS, type DataMigrationDocument, type DataMigrationLike, type DataMigrationTransform, type DatabaseWriterLike, type DependencyTracker, type DeployInfo, type ExportRow, type ExportShardAdminArgs, type ExportShardArgs, type ExternalSourceDiffResult, type ExternalSourceLike, FLAGS_FUNCTION_PREFIX, FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, type FacetColumnOptions, type FacetColumnResult, type FacetValue, type FieldOperators, type FlagEvaluation, type FlagsResult, type FunctionCallStat, type FunctionMetricBucket, type FunctionMetricIndexHit, type FunctionStatsResult, type GroupByEntry, type GroupByOptions, type HibernatableWebSocket, type IdGenerator, type ImportError, type ImportShardAdminArgs, type ImportShardArgs, type ImportShardResult, type IndexDefinitionLike, type IndexRangeBuilderLike, LogBuffer, type LogEntry, type LogEventInput, type LogLevel, type LogSink, MAIL_RETENTION, MAIL_TABLE, MAX_SQL_ROWS, MIN_ADMIN_TOKEN_LENGTH, MIN_AUTH_SECRET_LENGTH, type MaskColumnMetadata, type MaskPoliciesResult, type MaterializeResult, type MigrationDirection, type MigrationRunResult, type MigrationStatus, type MigrationStatusRow, type MutationDelta, type NestedWith, NotFoundError, NotUniqueError, type OnDeleteActionLike, type OrderByInput, type OrderKey, type PaginationOptions, type PitrBookmarkResult, type PitrRestoreArgs, type PitrRestoreResult, type PitrStorage, type QueryArgs, type QueryPage, type QueueMetadata, type QueuesResult, RANK_TIEBREAK, RELATION_FUNCTION_PREFIX, RLS_UNWRAP_SYMBOL, ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, type RankDirection, type RankIndexDefinitionLike, type RankOptions, type RankPage, type RankPageOptions, type RankPageRow, type RankPageRowKey, type RankResult, type RankSortKeyLike, ReactiveCache, type ReactiveCacheOptions, type ReadHook, type ReadTablePageOptions, type RecordAuthEventInput, type RecordFunctionMetricInput, type RecordMailInput, type RelationDefinitionLike, type RenderedSql, type ResolveRelationPredicatesOptions, type ResolveWithOptions, type RestrictableQueryOptions, type RlsPoliciesResult, type RlsPolicyMetadata, RlsRequiredError, type RlsRoleMetadata, type RpcRequest, type RunDataMigrationOptions, type RunShardApplyCdcArgs, type RunShardApplyCdcResult, type RunShardBulkDeleteArgs, type RunShardBulkDeleteResult, type RunShardExportArgs, type RunShardImportArgs, type RunShardMigrationArgs, type RunShardRankBeforeArgs, type RunShardRankPageArgs, type RunShardWriteArgs, type RunShardWriteResult, type RunTriggersOptions, SCAN_DEP, SESSION_DO_TTL_DEFAULT, SHARD_REGISTRY_DO_NAME, type ScheduledFunctionDoc, type SchedulerLike, type SchemaLike, type SearchFilterBuilderLike, type SecurityAuditResult, type SecurityFinding, type SecurityFindingKind, type SecurityFindingLevel, type SelectMatchingIdsOptions, type ServerDefaultContextLike, SessionDO, type SessionRecord, type SettingEntry, type SettingKind, type SettingsResult, type ShapeSubscriptionQuery, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, type SourceClientLike, type SourceRefresh, type SqlConsoleResult, type SqlCursor, type SqlEngine, type SqlExec, type StorageRuleMetadata, type StorageRulesResult, type StudioFeaturesResult, type SubscriptionEnvelope, type SubscriptionOutcome, type SubscriptionQuery, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemReaderOptions, type SystemReaderSchedulerLike, type SystemReaderStorageLike, type SystemTableName, type TableColumnsResult, type TableDefinitionLike, type TableIndexInfo, type TableIndexesResult, type TableInfo, type TablePage, type TableReaderLike, type TablesColumnsResult, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type ValidatorLike, type WhereInput, type WhereSqlStrategy, type WithInput, type WorkflowMetadata, type WorkflowsResult, type WriteEvent, type WriteHook, aggregateSqlFunction, aggregateTableName, applyCdcChanges, applyOnDelete, applySelect, armRestore, assertFlatPredicate, assertReadonly, assertShapeShardable, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, diffExternalSource, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pullExternalSourceTick, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readExternalSourceBaseline, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runExternalSourceTick, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };