@lunora/do 1.0.0-alpha.7 → 1.0.0-alpha.9

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 (20) hide show
  1. package/dist/index.d.mts +627 -21
  2. package/dist/index.d.ts +627 -21
  3. package/dist/index.mjs +11 -10
  4. package/dist/packem_shared/{ADMIN_FUNCTIONS-CHcC8fKV.mjs → ADMIN_FUNCTIONS-D_UiYJFk.mjs} +3 -1
  5. package/dist/packem_shared/{CDC_LOG_TABLE-Ctdmxmrv.mjs → CDC_LOG_TABLE-DSycmnDf.mjs} +5 -1
  6. package/dist/packem_shared/{DEFAULT_MAX_RELATION_KEYS-Dou2PWdO.mjs → DEFAULT_MAX_RELATION_KEYS-CHEvKjZt.mjs} +50 -1
  7. package/dist/packem_shared/{NotUniqueError-h_thNFSZ.mjs → NotUniqueError-Cwv7Pe7J.mjs} +9 -7
  8. package/dist/packem_shared/{rank-CrkEIpF4.mjs → RANK_TIEBREAK-CXhdcA1o.mjs} +2 -13
  9. package/dist/packem_shared/{ROOT_DO_SIZE_WARN_BYTES-2DxWrdla.mjs → ROOT_DO_SIZE_WARN_BYTES-DKwBF3Jp.mjs} +1067 -29
  10. package/dist/packem_shared/{ReactiveCache-ByVzgH3d.mjs → ReactiveCache-1hDydFyv.mjs} +1 -28
  11. package/dist/packem_shared/{backfillAggregateIndexes-BbVPvciS.mjs → backfillAggregateIndexes-BZsOqDXP.mjs} +2 -1
  12. package/dist/packem_shared/ctx-db-idempotency-BdcNpvY4.mjs +108 -0
  13. package/dist/packem_shared/ctx-db-shapes-DVoeZpo-.mjs +53 -0
  14. package/dist/packem_shared/{runShardMigrations-PabobOjF.mjs → runShardMigrations-nIwoQeOK.mjs} +5 -3
  15. package/dist/packem_shared/serialize-sql-BlRUoiQe.mjs +14 -0
  16. package/dist/packem_shared/{serveRelationFanout-oxaM6_WL.mjs → serveRelationFanout-C6lDaesn.mjs} +1 -1
  17. package/dist/packem_shared/stableStringify-CyHKJXre.mjs +30 -0
  18. package/package.json +1 -1
  19. package/dist/packem_shared/RANK_TIEBREAK-C6blLR5K.mjs +0 -1
  20. package/dist/packem_shared/ctx-db-idempotency-DkC9rP91.mjs +0 -35
package/dist/index.d.mts CHANGED
@@ -596,6 +596,7 @@ declare const rankTableName: (table: string, indexName: string) => string;
596
596
  * - `sortValues[i]` === `serializeSqlValue(doc[index.sortBy[i].field])` — the same transform `syncRankIndexEntry` applies to the stored `__sort_k<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.
597
597
  * - `rowId` === `doc._id`, the `__id__` tiebreak.
598
598
  */
599
+ declare const stableStringify: (value: unknown) => string;
599
600
  /** A single memoized result, the deps it read, and any active subscribers. */
600
601
  interface CacheEntry {
601
602
  /** Approximate serialized size of `result`, charged against `maxBytes`. */
@@ -728,16 +729,6 @@ declare class ReactiveCache {
728
729
  private evict;
729
730
  }
730
731
  /**
731
- * Stable, sorted JSON encoding of `args` for use in a cache key. Object keys
732
- * are visited in lexical order at every depth so `{ a: 1, b: 2 }` and
733
- * `{ b: 2, a: 1 }` hash to the same string. Arrays preserve their order
734
- * (the index IS the key). `undefined` values are skipped at the object level
735
- * so `{ a: undefined }` collides with `{}` — matches Convex behavior and
736
- * avoids spurious cache misses on optional args. Inside arrays `undefined`
737
- * encodes as `null` to keep positional semantics.
738
- */
739
- declare const stableStringify: (value: unknown) => string;
740
- /**
741
732
  * Compose a cache key from a function path, a stably-encoded args object, and
742
733
  * the caller's identity discriminator. Exported so the wiring layer and tests
743
734
  * build identical keys without each side reinventing the format.
@@ -993,7 +984,32 @@ interface SubscriptionQuery {
993
984
  */
994
985
  table?: string;
995
986
  }
987
+ /**
988
+ * A live shape subscription registered on a socket — the partial-replication
989
+ * parallel to {@link SubscriptionQuery}. The client names a `defineShape` shape
990
+ * and supplies validated `args`; the DO resolves it to a table + RLS-composed
991
+ * `effectiveWhere` under the socket's verified identity (never the client's
992
+ * word) and pokes the membership diff. `sinceSeq`/`sinceEpoch` carry the
993
+ * client's last applied checkpoint for resume.
994
+ */
995
+ interface ShapeSubscriptionQuery {
996
+ /** Validated shape arguments (e.g. `{ channelId }`); forwarded to `resolveShape`. */
997
+ args?: Record<string, unknown>;
998
+ /** Registered shape name (the `defineShape` export the codegen subclass resolves). */
999
+ name: string;
1000
+ /** Resume epoch the client persisted alongside {@link ShapeSubscriptionQuery.sinceSeq} (see {@link SubscriptionQuery.sinceEpoch}). */
1001
+ sinceEpoch?: string;
1002
+ /** Resume checkpoint: the `__cdc_log` cursor the client's view of this shape last reflected (see {@link SubscriptionQuery.sinceSeq}). */
1003
+ sinceSeq?: number;
1004
+ }
996
1005
  interface SubscriptionEnvelope {
1006
+ /**
1007
+ * Stable per-client id carried by the `connect` envelope. Recorded on the
1008
+ * socket attachment so a shape poke can echo this client's
1009
+ * `__client_watermark` as its `lastMutationId`. Ignored on other envelope
1010
+ * types; absent for clients that don't use custom mutators.
1011
+ */
1012
+ clientId?: string;
997
1013
  /**
998
1014
  * App-supplied connection context carried by the `connect` envelope (e.g.
999
1015
  * `{ roomId, sessionId }`). Merged into the socket attachment and forwarded
@@ -1008,6 +1024,19 @@ interface SubscriptionEnvelope {
1008
1024
  id: string;
1009
1025
  query?: SubscriptionQuery;
1010
1026
  /**
1027
+ * Shape descriptor of a `shape_subscribe` envelope: the named shape + its
1028
+ * validated args. Carries the client's resume checkpoint via
1029
+ * {@link SubscriptionEnvelope.sinceCheckpoint}/{@link SubscriptionEnvelope.sinceEpoch}.
1030
+ */
1031
+ shape?: {
1032
+ args?: Record<string, unknown>;
1033
+ name: string;
1034
+ };
1035
+ /** Resume checkpoint on a `shape_subscribe` envelope (the `__cdc_log` cursor the client's shape view is at). */
1036
+ sinceCheckpoint?: number;
1037
+ /** CDC epoch the {@link SubscriptionEnvelope.sinceCheckpoint} belongs to. */
1038
+ sinceEpoch?: string;
1039
+ /**
1011
1040
  * Topic of a `whisper`/`whisper_subscribe`/`whisper_unsubscribe` envelope —
1012
1041
  * an app-chosen channel name (e.g. `"room:42:cursors"`) scoped to this shard.
1013
1042
  */
@@ -1025,7 +1054,7 @@ interface SubscriptionEnvelope {
1025
1054
  * this shard with NO SQLite/CDC write (AnyCable-style whispering — typing
1026
1055
  * indicators, live cursors). The sender never receives its own whisper.
1027
1056
  */
1028
- type: "ack" | "connect" | "stream" | "subscribe" | "unsubscribe" | "whisper" | "whisper_subscribe" | "whisper_unsubscribe";
1057
+ type: "ack" | "connect" | "shape_subscribe" | "shape_unsubscribe" | "stream" | "subscribe" | "unsubscribe" | "whisper" | "whisper_subscribe" | "whisper_unsubscribe";
1029
1058
  }
1030
1059
  /**
1031
1060
  * The argument a connection-lifecycle hook receives. Structurally matches
@@ -1100,6 +1129,15 @@ interface SocketAttachment {
1100
1129
  */
1101
1130
  admin?: boolean;
1102
1131
  /**
1132
+ * Stable per-client id from the `connect` envelope (the same id the client
1133
+ * stamps on its custom-mutator pushes). Lets a shape poke echo this client's
1134
+ * `__client_watermark` as the poke's `lastMutationId`, so a `@lunora/db`
1135
+ * collection can drop the optimistic overlay for writes this poke has
1136
+ * synced. Absent for clients that don't use custom mutators. Persisted so it
1137
+ * survives hibernation.
1138
+ */
1139
+ clientId?: string;
1140
+ /**
1103
1141
  * `true` once the socket's `connect` envelope has fired the `onConnect`
1104
1142
  * hooks. Gates the dispatch so a client that re-sends `connect` (or a
1105
1143
  * duplicate frame) can't re-fire the hooks for an already-announced socket —
@@ -1133,6 +1171,14 @@ interface SocketAttachment {
1133
1171
  * hooks so they run under the connecting user.
1134
1172
  */
1135
1173
  identity?: Record<string, unknown>;
1174
+ /**
1175
+ * Live shape subscriptions registered on this socket, keyed by the
1176
+ * client-supplied subscription id. The partial-replication parallel to
1177
+ * {@link SocketAttachment.subs}: the poke protocol fans membership diffs to
1178
+ * these, while `subs` drives the legacy `data`/`delta` re-execution path.
1179
+ * Absent until the socket sends its first `shape_subscribe`.
1180
+ */
1181
+ shapes?: Record<string, ShapeSubscriptionQuery>;
1136
1182
  subs: Record<string, SubscriptionQuery>;
1137
1183
  /**
1138
1184
  * Verified user id resolved at upgrade (from `x-lunora-userid`), or absent
@@ -1189,10 +1235,16 @@ interface CdcChange {
1189
1235
  * Read changelog entries newer than `sinceSeq` in commit order, up to `limit`
1190
1236
  * (clamped to [1, 10000]). Returns the rows plus the cursor to resume from (the
1191
1237
  * last `seq`, or `sinceSeq` when the page is empty).
1238
+ *
1239
+ * The optional `tables` set narrows the page to changes on those tables — the
1240
+ * shape/poke path reads one filtered page per flush so it never scans op-log
1241
+ * entries for tables no live shape is watching. Omit it (or pass an empty set)
1242
+ * for the full, unfiltered page (the existing streaming-export/resume callers).
1192
1243
  */
1193
1244
  declare const readCdcChanges: (sql: SqlExec, options?: {
1194
1245
  limit?: number;
1195
1246
  sinceSeq?: number;
1247
+ tables?: ReadonlySet<string>;
1196
1248
  }) => {
1197
1249
  changes: CdcChange[];
1198
1250
  cursor: number;
@@ -1228,6 +1280,11 @@ declare const applyCdcChanges: (writer: DatabaseWriterLike, changes: ReadonlyArr
1228
1280
  declare const runShardMigrations: (sql: SqlExec, schema: SchemaLike, options?: {
1229
1281
  cdc?: boolean;
1230
1282
  }) => void;
1283
+ /** One shape member: its `_id` key plus the decoded document (id + creationTime merged in). */
1284
+ interface ShapeRow {
1285
+ doc: Record<string, unknown>;
1286
+ id: string;
1287
+ }
1231
1288
  /**
1232
1289
  * Structural projection of `state.storage.sql` (workerd's SqlStorage). We
1233
1290
  * only require the `exec` overload — the cursor it returns is iterable and
@@ -2262,6 +2319,19 @@ declare const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
2262
2319
  */
2263
2320
  declare const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
2264
2321
  /**
2322
+ * Reserved `functionPath` prefix for live feature-flag reads. The React client's
2323
+ * `useFlag`/`useFlags` subscribe to `__lunora_flags__:eval` over the same WS
2324
+ * channel as a user query; `ShardDO` intercepts it before user dispatch and
2325
+ * serves it from the codegen-overridden flag-subscription read hook, which
2326
+ * evaluates the flag through the app's OpenFeature provider under the socket's
2327
+ * verified identity. Like the other reserved prefixes it is NOT admin-gated (a
2328
+ * flag read is public, scoped to the subscriber's own targeting context), and
2329
+ * the `__lunora_` namespace is reserved so a real `&lt;file>:&lt;function>` can't
2330
+ * collide. Re-evaluated on every write-flush so values stay live within a
2331
+ * session (provider-side flips with no intervening write surface on reconnect).
2332
+ */
2333
+ declare const FLAGS_FUNCTION_PREFIX = "__lunora_flags__:";
2334
+ /**
2265
2335
  * Fully-qualified reserved paths the data browser invokes. The
2266
2336
  * `__lunora_admin__:` prefix is spelled out inline rather than interpolated so
2267
2337
  * the values stay emittable under `--isolatedDeclarations`.
@@ -2292,6 +2362,7 @@ declare const ADMIN_FUNCTIONS: {
2292
2362
  readonly getSettings: "__lunora_admin__:getSettings";
2293
2363
  readonly getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus";
2294
2364
  readonly importShard: "__lunora_admin__:importShard";
2365
+ readonly listFlags: "__lunora_admin__:listFlags";
2295
2366
  readonly listQueues: "__lunora_admin__:listQueues";
2296
2367
  readonly listTables: "__lunora_admin__:listTables";
2297
2368
  readonly listWorkflows: "__lunora_admin__:listWorkflows";
@@ -2572,6 +2643,8 @@ interface StorageRulesResult {
2572
2643
  * package's tests and the studio's fails the build if the two key sets diverge.
2573
2644
  */
2574
2645
  interface StudioFeaturesResult {
2646
+ /** `@lunora/flags` / `ctx.flags` is used, or it is a declared dependency. */
2647
+ flags: boolean;
2575
2648
  /** `@lunora/mail` is imported by a `lunora/` source or a declared dependency. */
2576
2649
  mail: boolean;
2577
2650
  /** `@lunora/payment` is used (import or `ctx.payments`) or a declared dependency. */
@@ -2588,6 +2661,40 @@ interface StudioFeaturesResult {
2588
2661
  workflows: boolean;
2589
2662
  }
2590
2663
  /**
2664
+ * One feature flag evaluated under a supplied targeting context, surfaced by
2665
+ * `__lunora_admin__:listFlags` for the studio's read-only Flags page. The `key`
2666
+ * and `type` are statically discovered by `@lunora/codegen` from the app's
2667
+ * `ctx.flags.&lt;type>("key", …)` reads; `value`/`reason`/`variant`/`errorCode`
2668
+ * come from the live OpenFeature evaluation (the codegen subclass overrides the
2669
+ * base `evaluateFlags` hook). `value` is the resolved flag value as JSON.
2670
+ */
2671
+ interface FlagEvaluation {
2672
+ /** OpenFeature `errorCode` when the evaluation failed (the value falls back to the default). */
2673
+ errorCode?: string;
2674
+ /** The discovered flag key (the first argument of a `ctx.flags.&lt;type>(...)` read). */
2675
+ key: string;
2676
+ /** OpenFeature `reason` for the resolution (`TARGETING_MATCH`, `DEFAULT`, `ERROR`, …). */
2677
+ reason?: string;
2678
+ /** The flag's value type, derived from which `ctx.flags.&lt;type>` method read it. */
2679
+ type: "boolean" | "number" | "object" | "string";
2680
+ /** The resolved value (JSON), or the type default when unconfigured / on error. */
2681
+ value: unknown;
2682
+ /** OpenFeature `variant` identifier when the provider reports one. */
2683
+ variant?: string;
2684
+ }
2685
+ /**
2686
+ * Payload of a `__lunora_admin__:listFlags` call: every statically-discovered
2687
+ * flag evaluated under the supplied targeting context. `configured` is `false`
2688
+ * when the app wires no `@lunora/flags` provider (the base hook), so the studio
2689
+ * can distinguish "no flags configured" from "configured but zero flags read".
2690
+ */
2691
+ interface FlagsResult {
2692
+ /** `true` when an `@lunora/flags` provider is wired (the codegen override ran). */
2693
+ configured: boolean;
2694
+ /** Each discovered flag evaluated under the request's targeting context. */
2695
+ flags: FlagEvaluation[];
2696
+ }
2697
+ /**
2591
2698
  * One declared Cloudflare Workflow, surfaced by `__lunora_admin__:listWorkflows`
2592
2699
  * for the studio's Workflows page. Statically discovered by `@lunora/codegen`
2593
2700
  * from `lunora/workflows.ts` (the codegen subclass overrides the base hook);
@@ -3368,6 +3475,16 @@ declare const assertFlatPredicate: (where: WhereInput | undefined, schema: Resol
3368
3475
  * and issues no extra query).
3369
3476
  */
3370
3477
  declare const resolveRelationPredicates: (where: WhereInput | undefined, options: ResolveRelationPredicatesOptions) => Promise<WhereInput | undefined>;
3478
+ /**
3479
+ * Registration-time guard for partial-replication shapes. A live shape can only
3480
+ * be poked from the op-log of its OWN shard Durable Object, so an
3481
+ * `effectiveWhere` that joins to a `.shardBy()` table reaches rows that live in
3482
+ * other DOs the poke loop can never observe. Reject such a shape up front with
3483
+ * the two supported remedies. Called from the generated `resolveShape` override
3484
+ * the moment a socket subscribes (the first point the compiled predicate and the
3485
+ * schema's shard modes are both in hand).
3486
+ */
3487
+ declare const assertShapeShardable: (effectiveWhere: WhereInput | undefined, schema: ResolveContext["schema"], table: string) => void;
3371
3488
  /** Severity of a `ctx.log.*` call, mirroring the console method names (`log` is the default level, distinct from `info`). */
3372
3489
  type ContextLogLevel = "debug" | "error" | "info" | "log" | "warn";
3373
3490
  /** The fields {@link emitLogEvent} ships for one `ctx.log.*` call. */
@@ -3756,6 +3873,13 @@ interface ShardDOState {
3756
3873
  getCurrentBookmark?: () => Promise<string>;
3757
3874
  /** Native PITR: arm a restore to `bookmark` on next restart; returns the undo bookmark. */
3758
3875
  onNextSessionRestoreBookmark?: (bookmark: string) => Promise<string>;
3876
+ /**
3877
+ * Arm the DO's single alarm to fire at `scheduledTime` (ms epoch),
3878
+ * waking {@link ShardDO.alarm}. Used by the global-shape poll loop.
3879
+ * Optional: present on the real runtime, absent in the unit harness
3880
+ * (where the poll loop degrades to seed-only).
3881
+ */
3882
+ setAlarm?: (scheduledTime: Date | number) => Promise<void>;
3759
3883
  sql: {
3760
3884
  [key: string]: unknown;
3761
3885
  /**
@@ -3800,6 +3924,41 @@ interface SubscriptionOutcome {
3800
3924
  tables: Set<string>;
3801
3925
  }
3802
3926
  /**
3927
+ * A shape resolved to its concrete query plan, the return of the
3928
+ * {@link ShardDO.resolveShape} hook. The codegen subclass composes the shape's
3929
+ * own predicate with the caller's RLS read base-where into `effectiveWhere`
3930
+ * under the socket's verified identity (the client never supplies it), so the
3931
+ * membership query the poke protocol runs is RLS-correct by construction.
3932
+ *
3933
+ * `columns`, when present, projects each row-op's `value` to that subset (the
3934
+ * shape's declared column allow-list); absent ⇒ the full document is shipped.
3935
+ */
3936
+ interface ResolvedShape {
3937
+ columns?: ReadonlyArray<string>;
3938
+ effectiveWhere?: WhereInput;
3939
+ /**
3940
+ * `true` when the shape's table is `.global()` (lives in D1, not this DO's
3941
+ * SQLite). A global shape has **no per-DO op-log** to diff, so it is served
3942
+ * by the **latency-tiered poll path** ({@link ShardDO.seedGlobalShape} +
3943
+ * {@link ShardDO.refreshGlobalShape}) instead of the CDC poke path — seeded
3944
+ * from {@link ShardDO.readGlobalShapeRows} and refreshed on an alarm tick.
3945
+ * The codegen subclass sets it from the schema's `shardMode`; absent ⇒ a
3946
+ * shard-local (poke-live) shape.
3947
+ */
3948
+ global?: boolean;
3949
+ table: string;
3950
+ }
3951
+ /**
3952
+ * Classification of a watermarked custom-mutator push against the shard's
3953
+ * `__client_watermark`: `expected` is the next in-order sequence, `kind`
3954
+ * whether the push is a replay (`"already"`), the next one (`"next"`), or an
3955
+ * out-of-order arrival (`"gap"`).
3956
+ */
3957
+ type ClientMutationClass = {
3958
+ expected: number;
3959
+ kind: "already" | "gap" | "next";
3960
+ };
3961
+ /**
3803
3962
  * Identity a subscription query is executed under, threaded EXPLICITLY into
3804
3963
  * `executeSubscription` → `buildCtx` rather than read from the shared,
3805
3964
  * per-request `currentRequestUserId`/`currentRequestIdentity` instance fields.
@@ -4008,6 +4167,28 @@ declare abstract class ShardDO {
4008
4167
  */
4009
4168
  protected static readonly MAX_SUBSCRIPTIONS_PER_SOCKET = 32;
4010
4169
  /**
4170
+ * Poll interval (ms) for `.global()`-table shapes. A global table lives in
4171
+ * D1 with no per-DO op-log, so its shapes can't be poke-live; the DO re-reads
4172
+ * each subscribed global shape's membership from D1 on an alarm every
4173
+ * `GLOBAL_SHAPE_POLL_INTERVAL_MS` and pokes only the diff. This is the
4174
+ * latency floor for a global-shape update — deliberately coarse (seconds, not
4175
+ * the sub-millisecond poke-live path) since the D1 read fans out per tick.
4176
+ */
4177
+ protected static readonly GLOBAL_SHAPE_POLL_INTERVAL_MS = 2e3;
4178
+ /**
4179
+ * Upper bound on a `.global()`-shape's materialized membership. Each global
4180
+ * shape keeps its ENTIRE current membership as a per-socket snapshot
4181
+ * (`Map&lt;rowKey, hash&gt;`) so the poll loop can diff it; that snapshot — and the
4182
+ * read buffer feeding it — scale with the membership size, multiplied by every
4183
+ * subscribed socket. An unbounded membership (a global table with no narrowing
4184
+ * shape predicate or RLS read scope) would grow them without limit and evict
4185
+ * the DO. A shape whose membership exceeds this cap is failed closed (left
4186
+ * empty, logged) rather than retained — the developer must narrow it. Sized
4187
+ * well above any reasonable per-identity replicated set so legitimate shapes
4188
+ * never trip it.
4189
+ */
4190
+ protected static readonly GLOBAL_SHAPE_MAX_ROWS = 5e4;
4191
+ /**
4011
4192
  * Per-socket whisper-topic cap. Topic membership rides the same hibernation
4012
4193
  * attachment as `subs`, so bound it for the same reason — a runaway
4013
4194
  * `whisper_subscribe` loop must not wedge the attachment past the runtime's
@@ -4107,6 +4288,39 @@ declare abstract class ShardDO {
4107
4288
  */
4108
4289
  private currentRequestMutationId;
4109
4290
  /**
4291
+ * Stable per-device client id for the in-flight custom-mutator push,
4292
+ * forwarded via the `x-lunora-client-id` header. Backs the
4293
+ * `__client_watermark` table: the dispatch path classifies the paired
4294
+ * `currentRequestClientSeq` against the stored high-watermark (already
4295
+ * processed / next / out-of-order gap). Absent on legacy mutations and
4296
+ * queries (those keep the `__idempotency` path). Cleared in `fetch`'s
4297
+ * `finally`.
4298
+ */
4299
+ private currentRequestClientId;
4300
+ /**
4301
+ * Monotonic per-client mutation sequence for the in-flight custom-mutator
4302
+ * push, forwarded via the `x-lunora-client-seq` header (numeric). Paired
4303
+ * with `currentRequestClientId` to drive the watermark classification.
4304
+ * `undefined` when absent or non-numeric.
4305
+ */
4306
+ private currentRequestClientSeq;
4307
+ /**
4308
+ * The in-flight push's custom-mutator classification, stashed by `fetch`
4309
+ * before `handleRpc` so the in-transaction bookkeeping ({@link
4310
+ * ShardDO.commitMutationBookkeeping}) can advance the `__client_watermark` for
4311
+ * a `"next"` push inside the same commit as the writes. `undefined` for an
4312
+ * ordinary mutation / non-mutator push. Cleared per request.
4313
+ */
4314
+ private currentMutatorClass;
4315
+ /**
4316
+ * Set once a mutation's replay bookkeeping (idempotency row + watermark
4317
+ * advance) has committed INSIDE the handler transaction, so the post-dispatch
4318
+ * path skips the now-redundant best-effort writes. Cleared per request; stays
4319
+ * `false` for actions/queries (no transaction wrapper) so their dispatch-level
4320
+ * idempotency persist still runs.
4321
+ */
4322
+ private mutationBookkeepingCommitted;
4323
+ /**
4110
4324
  * Wall-clock millis of the last `__idempotency` GC sweep on this warm
4111
4325
  * instance. The dedup write throttles `trimIdempotent` to at most once an
4112
4326
  * hour off this field (in-memory, so a fresh instance just sweeps on its
@@ -4156,6 +4370,39 @@ declare abstract class ShardDO {
4156
4370
  * memo simply forces one re-run and (at most) one redundant push.
4157
4371
  */
4158
4372
  private readonly subMemos;
4373
+ /**
4374
+ * Per-socket poke baseline for shape subscriptions: maps each shape's
4375
+ * subscription id to the `__cdc_log` cursor it has been poked through.
4376
+ * `pokeShapeSubscribers` reads each op page since this cursor and advances
4377
+ * it to the flush watermark. In-memory only (like {@link ShardDO.subMemos});
4378
+ * a cold memo on a reconnected/hibernated socket re-seeds from the client's
4379
+ * `sinceCheckpoint`.
4380
+ */
4381
+ private readonly shapeMemos;
4382
+ /**
4383
+ * Per-socket, per-**global**-shape membership snapshot: maps each global
4384
+ * shape's subscription id to a `key → projected-value JSON` map of the rows
4385
+ * last poked to that socket. A `.global()` (D1) table has no op-log to diff,
4386
+ * so {@link ShardDO.refreshGlobalShape} re-reads the full membership on each
4387
+ * alarm tick and diffs it against this snapshot to compute the poke. Parallel
4388
+ * to {@link ShardDO.shapeMemos} (the cursor baseline for poke-live shapes).
4389
+ *
4390
+ * This is a hot in-memory **cache** over the durable `__global_shape_snapshot`
4391
+ * table (keyed by the socket's `connectionId` + subId): a hibernation eviction
4392
+ * clears the WeakMap, so on the next alarm wake {@link ShardDO.readGlobalSnapshot}
4393
+ * misses and re-loads the baseline from SQLite — without it, the diff would run
4394
+ * against an empty baseline and a row deleted from D1 while the DO slept would
4395
+ * never be poked as a `delete`, lingering on the client as a phantom row.
4396
+ */
4397
+ private readonly globalShapeSnapshots;
4398
+ /**
4399
+ * Whether a global-shape poll alarm is currently armed. Guards
4400
+ * {@link ShardDO.scheduleGlobalPoll} from re-arming on every seed; reset in
4401
+ * {@link ShardDO.alarm} before the poll so a still-subscribed shape re-arms.
4402
+ */
4403
+ private globalPollScheduled;
4404
+ /** Monotonic per-DO poke id source; correlates a poke's `pokeStart`/`pokePart`/`pokeEnd` frames. */
4405
+ private pokeSequence;
4159
4406
  /** Per-socket whisper-rate token bucket (see {@link ShardDO.WHISPER_RATE_BURST}). In-memory; resets on hibernation. */
4160
4407
  private readonly whisperBuckets;
4161
4408
  /**
@@ -4274,6 +4521,15 @@ declare abstract class ShardDO {
4274
4521
  webSocketClose(ws: WebSocket, _code: number, _reason: string, _wasClean: boolean): Promise<void>;
4275
4522
  /** Hibernation API: invoked on socket error. */
4276
4523
  webSocketError(_ws: WebSocket, _error: unknown): void;
4524
+ /**
4525
+ * Durable Object alarm handler — the heartbeat for `.global()`-table shapes.
4526
+ * The runtime wakes this when the poll alarm armed by `scheduleGlobalPoll`
4527
+ * fires; it refreshes every subscribed global shape (diff-poke from the global
4528
+ * backend) and re-arms while any remain. With no global subscribers left, the
4529
+ * alarm is not re-armed and the DO goes idle. A base-only / global-free DO
4530
+ * never arms it, so this stays dormant there.
4531
+ */
4532
+ alarm(): Promise<void>;
4277
4533
  /** Subclasses implement function dispatch. */
4278
4534
  abstract handleRpc(functionPath: string, args: Record<string, unknown>): Promise<unknown>;
4279
4535
  /**
@@ -4520,6 +4776,26 @@ declare abstract class ShardDO {
4520
4776
  */
4521
4777
  protected studioFeatures(): StudioFeaturesResult;
4522
4778
  /**
4779
+ * Evaluate every statically-discovered feature flag under `context` for the
4780
+ * studio's read-only Flags page (`__lunora_admin__:listFlags`). The flag keys
4781
+ * + value types are discovered by `@lunora/codegen` from the app's
4782
+ * `ctx.flags.&lt;type>("key", …)` reads and evaluated through the configured
4783
+ * `@lunora/flags` provider — work only the codegen subclass can do, so it
4784
+ * overrides this. The base class wires no provider and reports
4785
+ * `configured: false` with zero flags (an un-generated `ShardDO` has none).
4786
+ */
4787
+ protected evaluateFlags(_context?: Record<string, unknown>): Promise<FlagsResult>;
4788
+ /**
4789
+ * Serve one reserved {@link FLAGS_FUNCTION_PREFIX} live flag read for the
4790
+ * React client's `useFlag`/`useFlags`. `functionPath` carries the flag key +
4791
+ * type and `args` the per-subscriber targeting context; the codegen subclass
4792
+ * overrides this to evaluate the flag through the app's `@lunora/flags`
4793
+ * provider under `identity` and return the resolved value. The base class
4794
+ * wires no provider, so it returns `null` — `resolveReactiveOutcome` reads
4795
+ * `null` as "nothing to deliver" and the subscriber keeps its default.
4796
+ */
4797
+ protected runFlagSubscriptionRead(_functionPath: string, _arguments: Record<string, unknown>, _identity?: SubscriptionIdentity): Promise<unknown>;
4798
+ /**
4523
4799
  * The Cloudflare Queues declared by this app, surfaced via
4524
4800
  * `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
4525
4801
  * Durable Objects and hold no shard state, so this is pure declaration
@@ -4691,16 +4967,105 @@ declare abstract class ShardDO {
4691
4967
  * unless the request carried an `x-lunora-mutation-id` header (queries and
4692
4968
  * legacy clients leave `currentRequestMutationId` undefined).
4693
4969
  *
4694
- * Called on the live dispatch path right after the handler's writes have
4695
- * auto-committed, through the same `this.sql` handle, so the dedup row is
4696
- * durable iff those writes are. (The DO has no ambient BEGIN/COMMIT around a
4697
- * mutation `handleRpc` invokes the user handler directly so this can't
4698
- * piggyback on a surrounding transaction; it commits as its own statement
4699
- * immediately after.) `INSERT OR IGNORE` keeps a concurrent double-dispatch
4700
- * of the same id idempotent. Also runs the throttled dedup-table GC.
4970
+ * For a mutation this runs INSIDE the handler's transaction (via
4971
+ * {@link ShardDO.commitMutationBookkeeping}, which `handleRpc` invokes before
4972
+ * the transaction commits), so the dedup row is durable iff the writes are —
4973
+ * closing the crash window where the writes commit but the replay guard does
4974
+ * not. Actions/queries aren't transaction-wrapped, so they call this on the
4975
+ * live dispatch path right after the handler resolves, through the same
4976
+ * `this.sql` handle. `INSERT OR IGNORE` keeps a concurrent double-dispatch (or
4977
+ * the now-skipped post-dispatch call) of the same id idempotent. Also runs the
4978
+ * throttled dedup-table GC.
4701
4979
  */
4702
4980
  protected persistIdempotentResult(result: unknown): void;
4703
4981
  /**
4982
+ * Whether `functionPath` names a registered custom mutator (a `defineMutator`
4983
+ * declaration) rather than an ordinary `mutation`. The base class knows of no
4984
+ * mutators, so the default is `false`; the codegen-generated subclass
4985
+ * overrides this to consult its mutator registry. When `true` (and the push
4986
+ * carries a `clientId`/`clientSeq`), the dispatch path applies the
4987
+ * `__client_watermark` ordering semantics instead of the legacy idempotency
4988
+ * dedup.
4989
+ */
4990
+ protected isCustomMutator(_functionPath: string): boolean;
4991
+ /**
4992
+ * Classify an in-flight custom-mutator push against the shard's stored
4993
+ * high-watermark for `currentRequestClientId`. The watermark is the highest
4994
+ * per-client sequence the DO has applied, so the push is exactly one of:
4995
+ *
4996
+ * - `"already"` — `seq &lt;= watermark`: a replay of a confirmed (or in-flight,
4997
+ * now-resent) mutation. The handler must NOT re-run; the dispatch path returns
4998
+ * a benign ack so the client drops the pending overlay.
4999
+ * - `"next"` — `seq == watermark + 1`: the next mutation in order. Run the
5000
+ * authoritative `server` impl and advance the watermark in the same commit.
5001
+ * - `"gap"` — `seq > watermark + 1`: an out-of-order arrival (an earlier push
5002
+ * was lost). Halt: the client must resend from `watermark + 1`.
5003
+ *
5004
+ * Returns `undefined` when the push is not a watermarked custom mutator
5005
+ * (missing client id/seq, or a stub `sql` handle without the table) so the
5006
+ * caller falls through to the legacy idempotency path.
5007
+ */
5008
+ protected classifyClientMutation(): ClientMutationClass | undefined;
5009
+ /**
5010
+ * Terminal response for a watermarked custom-mutator push that is NOT the
5011
+ * next-in-order mutation — an idempotent replay ack (`"already"`) or an
5012
+ * out-of-order halt (`"gap"`). Returns `undefined` for an ordinary mutation
5013
+ * or a `"next"` push so `fetch` proceeds to the authoritative handler. Records
5014
+ * the function call on the short-circuit paths so metrics stay attributed.
5015
+ */
5016
+ protected rejectNonNextMutation(functionPath: string, mutatorClass: ClientMutationClass | undefined, dispatchStartedAt: number): Response | undefined;
5017
+ /**
5018
+ * Respond to a dispatch that hit the `(identity, mutationId)` idempotency
5019
+ * cache. Records the (zero-work) function call, then: for a `"next"` custom
5020
+ * mutator whose handler already committed but whose watermark advance was
5021
+ * lost to a crash in between, re-advance and echo `lastMutationId` exactly as
5022
+ * the post-commit path does (otherwise the cached branch returns a bare
5023
+ * result with a stale watermark and the client reports every later seq as a
5024
+ * gap forever); for everything else, return the bare cached `{ result }`.
5025
+ */
5026
+ protected respondFromIdempotencyCache(functionPath: string, dispatchStartedAt: number, mutatorClass: ClientMutationClass | undefined, cachedValue: unknown): Response;
5027
+ /**
5028
+ * Build the success response for a dispatched RPC. A `"next"` custom-mutator
5029
+ * push echoes the applied `lastMutationId` so the client drops the pending
5030
+ * optimistic overlay as soon as the ack lands; ordinary calls return the bare
5031
+ * `{ result }` envelope unchanged.
5032
+ */
5033
+ protected buildDispatchResponse(mutatorClass: ClientMutationClass | undefined, result: unknown): Response;
5034
+ /**
5035
+ * Commit a mutation's replay bookkeeping — the `(identity, mutationId)`
5036
+ * idempotency dedup row and, for a `"next"` custom-mutator push, the
5037
+ * `__client_watermark` advance — INSIDE the handler's transaction. Called by
5038
+ * the generated `handleRpc` mutation branch after the user handler resolves
5039
+ * but before the transaction commits, so the writes, the dedup row, and the
5040
+ * watermark land in one atomic commit: a crash can't leave the writes durable
5041
+ * without the replay guard (which a re-dispatch would otherwise re-run) nor
5042
+ * without the watermark. Sets {@link ShardDO.mutationBookkeepingCommitted} so
5043
+ * `fetch` skips the redundant post-dispatch persist.
5044
+ */
5045
+ protected commitMutationBookkeeping(result: unknown): void;
5046
+ /**
5047
+ * Best-effort replay bookkeeping for the live dispatch path, run after
5048
+ * `handleRpc` returns. A generated mutation already committed it atomically
5049
+ * inside its transaction (via {@link ShardDO.commitMutationBookkeeping}, which
5050
+ * sets the flag), so this skips. Actions/queries aren't transaction-wrapped,
5051
+ * so they record their dedup row here (a no-op without an `x-lunora-mutation-id`),
5052
+ * and a `"next"` push advances its watermark (the gap self-heals on replay).
5053
+ */
5054
+ protected recordPostDispatchBookkeeping(result: unknown, mutatorClass: ClientMutationClass | undefined): void;
5055
+ /**
5056
+ * Advance the stored high-watermark for the in-flight custom mutator to
5057
+ * `currentRequestClientSeq` through the same `this.sql` handle. On the
5058
+ * transactional path ({@link ShardDO.commitMutationBookkeeping}, `strict`) it
5059
+ * runs inside the handler's commit, so the watermark is durable iff the writes
5060
+ * are; a failure rethrows to roll the mutation back. On the best-effort
5061
+ * cache-hit recovery path (`strict` omitted) a missing table is swallowed —
5062
+ * the replay re-runs and re-advances (the read side treats a missing row as
5063
+ * watermark 0), so the gap self-heals.
5064
+ */
5065
+ protected advanceClientMutationWatermark(options?: {
5066
+ strict?: boolean;
5067
+ }): void;
5068
+ /**
4704
5069
  * Replay a batch of CDC changes into this shard (point-in-time recovery).
4705
5070
  * Schema-aware — it builds a `createShardCtxDb` writer — so the base class
4706
5071
  * can't implement it; the codegen-generated subclass overrides this to call
@@ -4719,6 +5084,17 @@ declare abstract class ShardDO {
4719
5084
  protected subscribe(ws: WebSocket, subId: string, query: SubscriptionQuery): "ok" | "serialize_failed" | "too_many";
4720
5085
  protected unsubscribe(ws: WebSocket, subId: string): void;
4721
5086
  /**
5087
+ * Register a live shape subscription on a socket — the partial-replication
5088
+ * parallel to {@link ShardDO.subscribe}. Stores the descriptor in the
5089
+ * attachment's `shapes` registry (created lazily) so it survives
5090
+ * hibernation, sharing the per-socket cap with `subs`. Returns a status the
5091
+ * caller surfaces as a structured error frame; never throws (a thrown
5092
+ * `webSocketMessage` is a fatal-channel error under the hibernation API).
5093
+ */
5094
+ protected shapeSubscribe(ws: WebSocket, subId: string, shape: ShapeSubscriptionQuery): "ok" | "serialize_failed" | "too_many";
5095
+ /** Remove a shape subscription and its poke baseline. Mirrors {@link ShardDO.unsubscribe}'s rollback-on-serialize-failure contract. */
5096
+ protected shapeUnsubscribe(ws: WebSocket, subId: string): void;
5097
+ /**
4722
5098
  * Decide whether a single subscription is interested in a mutation
4723
5099
  * delta. The default implementation checks the table name, then runs a
4724
5100
  * shallow-equality predicate over `query.args` against `delta.row`. A
@@ -4756,6 +5132,40 @@ declare abstract class ShardDO {
4756
5132
  */
4757
5133
  protected executeSubscription(_functionPath: string, _args: Record<string, unknown>, _identity?: SubscriptionIdentity): Promise<SubscriptionOutcome | null>;
4758
5134
  /**
5135
+ * Resolve a named shape to its concrete query plan for `identity`. The base
5136
+ * class has no shape registry, so it returns `undefined` — partial
5137
+ * replication is disabled and a `shape_subscribe` is rejected. The
5138
+ * codegen-generated subclass overrides this to look the shape up in the
5139
+ * project's `defineShape` registry, evaluate its `where(ctx, args)` under the
5140
+ * subscriber's verified identity, and AND-compose it with the table's RLS
5141
+ * read base-where into {@link ResolvedShape.effectiveWhere}.
5142
+ *
5143
+ * `identity` is the socket's OWN verified identity (the same unforgeable
5144
+ * value `refreshSubscriptions` threads), passed by value so this never reads
5145
+ * the mutable per-request identity fields. Returning `undefined` is the
5146
+ * fail-closed signal — an unknown shape, or an RLS-required table with no
5147
+ * policy resolving for this identity, yields no subscription rather than
5148
+ * leaking rows.
5149
+ */
5150
+ protected resolveShape(_name: string, _args: Record<string, unknown>, _identity?: SubscriptionIdentity): ResolvedShape | undefined;
5151
+ /**
5152
+ * Read the FULL current membership of a `.global()`-table shape from its D1
5153
+ * (or Hyperdrive) backend — the seed/poll source for the latency-tiered
5154
+ * global shape path. A `.global()` table lives in another store with no
5155
+ * per-DO op-log, so this is the only way to learn its rows from inside the
5156
+ * shard DO; {@link ShardDO.seedGlobalShape} calls it once on subscribe and
5157
+ * {@link ShardDO.refreshGlobalShape} on every alarm tick, diffing the result
5158
+ * against the per-socket snapshot to compute the poke.
5159
+ *
5160
+ * The base class has no global backend, so it returns `[]` (a base-only DO,
5161
+ * or a project with no global tables, never resolves a global shape). The
5162
+ * codegen subclass overrides it to drain `globalDb.findMany(table, { where:
5163
+ * effectiveWhere })` under the socket's verified `identity` — the same
5164
+ * unforgeable value `resolveShape` composed the RLS predicate with, so the
5165
+ * D1 read is identity-scoped exactly like the poke-live path.
5166
+ */
5167
+ protected readGlobalShapeRows(_resolved: ResolvedShape, _identity?: SubscriptionIdentity): Promise<ShapeRow[]>;
5168
+ /**
4759
5169
  * Look up a streaming-query function and return a thunk that produces the
4760
5170
  * `AsyncIterable&lt;unknown>` when handed an {@link AbortSignal}. The codegen
4761
5171
  * subclass overrides this to dispatch via `LUNORA_FUNCTIONS`; the base
@@ -5031,6 +5441,16 @@ declare abstract class ShardDO {
5031
5441
  */
5032
5442
  private handleGetWorkflowInstanceStatus;
5033
5443
  /**
5444
+ * Serve `__lunora_admin__:listFlags` — the studio's read-only Flags page.
5445
+ * Evaluates every statically-discovered feature flag under an optional
5446
+ * `args.context` targeting context (the studio's editable context editor)
5447
+ * via the {@link evaluateFlags} hook, which the codegen subclass overrides
5448
+ * with live OpenFeature evaluation. Read-only: a flag lookup mutates no shard
5449
+ * state, so nothing is flushed or audited. Admin-gated by `handleAdminRpc`'s
5450
+ * caller.
5451
+ */
5452
+ private handleListFlags;
5453
+ /**
5034
5454
  * Run `run()` with the per-request identity pinned to (`userId`, `identity`),
5035
5455
  * then restore the prior values in a `finally` (even if `run()` throws), so the
5036
5456
  * forced identity can never leak into a later dispatch on this DO instance. The
@@ -5290,6 +5710,16 @@ declare abstract class ShardDO {
5290
5710
  */
5291
5711
  private executeAdminSubscription;
5292
5712
  /**
5713
+ * Resolve one subscription (seed or refresh) to its {@link SubscriptionOutcome}
5714
+ * by routing the `functionPath` to the right read path — shared by
5715
+ * {@link seedSubscription} and {@link refreshSubscriptions} so both branch
5716
+ * identically:
5717
+ * - `__lunora_admin__:*` → {@link executeAdminSubscription} (raw SQLite read).
5718
+ * - {@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`).
5719
+ * - everything else → {@link executeSubscription} (the user query, under the socket's own by-value identity).
5720
+ */
5721
+ private resolveReactiveOutcome;
5722
+ /**
5293
5723
  * Constant-time bearer check against `env.LUNORA_ADMIN_TOKEN`. Returns
5294
5724
  * `false` (closed) when the token is unset so admin introspection is
5295
5725
  * opt-in rather than exposed by default.
@@ -5327,8 +5757,8 @@ declare abstract class ShardDO {
5327
5757
  * by the next loop iteration, so every committed write is observed by a
5328
5758
  * refresh that runs after it — bursts simply share a pass. The post-write
5329
5759
  * high-watermark and live-socket set are re-read inside each
5330
- * `refreshSubscriptions` call, so a later batch always reflects the latest
5331
- * committed state.
5760
+ * `refreshSubscriptions` / `pokeShapeSubscribers` call, so a later batch
5761
+ * always reflects the latest committed state.
5332
5762
  */
5333
5763
  private drainSubscriptionRefreshes;
5334
5764
  /**
@@ -5404,6 +5834,182 @@ declare abstract class ShardDO {
5404
5834
  */
5405
5835
  private seedSubscription;
5406
5836
  /**
5837
+ * Drive the full `shape_subscribe` flow as one failure-aware unit: persist the
5838
+ * attachment, seed the shape, and ack ONLY once both succeed. A persist
5839
+ * rejection (`too_many`/`serialize_failed`) or a seed that can't resolve the
5840
+ * shape (unknown / RLS-denied / cross-shard-invalid) rolls the attachment back
5841
+ * and sends an `error` frame instead of acking — so a client is never left
5842
+ * acked but subscribed to a shape that will never deliver. Never throws (a
5843
+ * thrown `webSocketMessage` is fatal to the hibernating socket).
5844
+ */
5845
+ private handleShapeSubscribe;
5846
+ /** Send a structured `error` frame for a failed `shape_subscribe`, swallowing a send on an already-closed socket. */
5847
+ private sendShapeSubscribeError;
5848
+ /**
5849
+ * Seed a freshly-registered shape subscription. Resolves the shape under the
5850
+ * socket's verified identity, then ships either:
5851
+ *
5852
+ * - a **catch-up** poke (the membership diff in `(sinceCheckpoint, cursor]`)
5853
+ * when the client supplied a still-current checkpoint within the CDC retention
5854
+ * window and on this epoch — the cheap reconnect path; or
5855
+ * - a **full** insert-poke of the shape's entire current membership — a
5856
+ * first-time subscribe, or a reconnect that fell outside retention / forked
5857
+ * epoch.
5858
+ *
5859
+ * Either way the per-socket shape memo advances to the flush watermark so
5860
+ * later `pokeShapeSubscribers` passes diff from the right point.
5861
+ *
5862
+ * Returns `"ok"` once the shape resolved and its seed poke was attempted, or a
5863
+ * `{ code, message }` failure when the shape can't be resolved — an unknown /
5864
+ * RLS-denied shape (a base class with no registry resolves nothing), or a
5865
+ * `resolveShape` that threw (e.g. a cross-shard-join guard). The caller rolls
5866
+ * back the persisted attachment and errors instead of acking, so a client is
5867
+ * never left subscribed to a shape that will never deliver.
5868
+ */
5869
+ private seedShapeSubscription;
5870
+ /**
5871
+ * Seed a non-`.global()` (op-log-backed) shape: either a catch-up diff over
5872
+ * `(sinceSeq, cursor]` when the client supplied a still-current checkpoint on
5873
+ * this epoch within the CDC retention window, or a full membership insert-poke
5874
+ * otherwise. The memo advances to `cursor` only once the poke is delivered, so
5875
+ * a failed send re-diffs from the prior point rather than skipping rows. May
5876
+ * throw (a stub `sql` handle, a membership probe failure); the caller converts
5877
+ * it to a structured `shape_subscribe` error.
5878
+ */
5879
+ private seedOpLogShape;
5880
+ /**
5881
+ * Fan the membership diff of every shape affected by this flush to its
5882
+ * subscribers — the partial-replication parallel to
5883
+ * {@link ShardDO.refreshSubscriptions}, called alongside it from
5884
+ * {@link ShardDO.flushChangedTables}. For each socket (bounded fan-out, same
5885
+ * concurrency + `awaitWsDrain` backpressure as the subscription path) it
5886
+ * resolves each shape under the socket's identity, diffs only the shapes
5887
+ * whose table changed in `(memoCursor, frameCursor]`, and emits one poke
5888
+ * carrying a part per changed shape. No-op when no socket holds a shape.
5889
+ */
5890
+ private pokeShapeSubscribers;
5891
+ /**
5892
+ * Diff every op-log-backed shape a socket holds against this flush, splitting
5893
+ * the results into the poke parts to send and the per-shape memo advances. A
5894
+ * `.global()` shape (driven by the alarm poll loop, not this flush) and a shape
5895
+ * whose table didn't change are skipped; a shape whose resolve/diff throws is
5896
+ * logged and skipped with its memo unadvanced so a later flush retries. Empty
5897
+ * diffs advance unconditionally; part-bearing shapes advance only once the
5898
+ * caller confirms the poke was delivered.
5899
+ */
5900
+ private collectShapePokeParts;
5901
+ /**
5902
+ * Build the row-ops for a shape over the op range `(sinceSeq, upTo]`. Reads
5903
+ * the changelog (drained across pages), collapses to the latest op per row,
5904
+ * then runs ONE membership probe ({@link selectShapeMemberIds}) over the
5905
+ * changed ids: a row still in the set → upsert with its post-image doc
5906
+ * (projected to the shape's columns); a row that left the set, or any delete,
5907
+ * → `delete(key)` (a delete carries no post-image, so membership is
5908
+ * unknowable from the op alone — the client no-ops an unknown key).
5909
+ */
5910
+ private buildShapeDiff;
5911
+ /** Build the full insert-poke of a shape's current membership — the first-seed/full-reseed rowset. */
5912
+ private buildShapeSeed;
5913
+ /**
5914
+ * Seed a `.global()`-table shape: read its full membership from D1, ship it
5915
+ * as one insert-poke, record the membership snapshot the alarm poll loop will
5916
+ * diff against, and arm the poll alarm. A global shape has no op-log cursor,
5917
+ * so the poke is stamped at this DO's current cursor (informational only) and
5918
+ * carries no resume base — a reconnect always re-seeds full.
5919
+ */
5920
+ private seedGlobalShape;
5921
+ /**
5922
+ * Re-read a global shape's membership from D1 and poke only the diff against
5923
+ * the socket's last snapshot: a new key → `insert`, a changed projected value
5924
+ * → `update`, a vanished key → `delete`. The snapshot advances to the fresh
5925
+ * membership even when the diff is empty, so the next tick compares from here.
5926
+ * No frame is sent when nothing changed (the common steady-state tick).
5927
+ */
5928
+ private refreshGlobalShape;
5929
+ /**
5930
+ * Read a socket's global-shape baseline, preferring the hot in-memory cache
5931
+ * and falling back to the durable `__global_shape_snapshot` table on a miss (a
5932
+ * cold socket after a hibernation eviction). The loaded baseline repopulates
5933
+ * the cache so subsequent ticks in this wake hit memory. An empty
5934
+ * `connectionId` (a socket that never went through the lifecycle-aware upgrade,
5935
+ * e.g. a unit harness) skips the durable read and behaves as in-memory-only.
5936
+ */
5937
+ private readGlobalSnapshot;
5938
+ /** Record a socket's latest global-shape membership snapshot in the in-memory cache (creating the per-socket map lazily). */
5939
+ private recordGlobalSnapshot;
5940
+ /**
5941
+ * Load a durable global-shape baseline from SQLite, or an empty map when none
5942
+ * is stored / the durable path is unavailable. A stub `sql` handle (unit
5943
+ * harness) or a missing table degrades to in-memory-only behavior rather than
5944
+ * failing the poll tick.
5945
+ */
5946
+ private loadGlobalSnapshot;
5947
+ /**
5948
+ * Persist a socket's global-shape baseline to SQLite so the poll-loop diff
5949
+ * survives hibernation. A no-op for a connection-id-less socket or a stub
5950
+ * `sql` handle (the in-memory cache then carries the baseline for the DO's
5951
+ * lifetime, matching the pre-durable behavior).
5952
+ */
5953
+ private saveGlobalSnapshot;
5954
+ /**
5955
+ * Arm the poll alarm for `.global()` shapes if one isn't already pending.
5956
+ * Idempotent — every global-shape seed calls it, but only the first arms the
5957
+ * alarm. Degrades to a no-op when the runtime exposes no `setAlarm` (the unit
5958
+ * harness): a global shape is then seed-only, which the poll-loop tests assert
5959
+ * by driving {@link ShardDO.alarm} directly.
5960
+ */
5961
+ private scheduleGlobalPoll;
5962
+ /**
5963
+ * Record a contained shape-tier error (poll / poke / seed) into the DO's log
5964
+ * ring without aborting the rest of the pass. The shape pipeline is a
5965
+ * best-effort fan-out: one socket's read or one shape's resolve failing must
5966
+ * never take down the others — so callers swallow the throw and surface it
5967
+ * here for diagnosis. `context` is a synthetic `shape:phase:subId` path.
5968
+ */
5969
+ private recordShapeError;
5970
+ /**
5971
+ * Guard a global shape's materialized membership against {@link
5972
+ * ShardDO.GLOBAL_SHAPE_MAX_ROWS}. Returns `true` when the row count is within
5973
+ * the cap; otherwise records a diagnosable error and returns `false` so the
5974
+ * caller fails the shape closed (no snapshot retained, no poke sent) rather
5975
+ * than risking a DO eviction on an unbounded global table. The transient read
5976
+ * buffer is bounded by the same gate — an over-cap membership is dropped, not
5977
+ * snapshotted per socket.
5978
+ */
5979
+ private withinGlobalShapeBound;
5980
+ /**
5981
+ * Refresh every `.global()`-table shape held across all live sockets, one
5982
+ * diff-poke per (socket, shape). Returns the number of global shapes still
5983
+ * subscribed so {@link ShardDO.alarm} knows whether to re-arm. Expired sockets
5984
+ * are dropped in passing (mirrors {@link ShardDO.pokeShapeSubscribers}).
5985
+ */
5986
+ private pollGlobalShapes;
5987
+ /**
5988
+ * Refresh one socket's `.global()`-table shapes, containing per-shape
5989
+ * failures so a single throw never aborts the poll tick (and with it the
5990
+ * re-arm). Returns the count of global shapes still subscribed on this socket
5991
+ * — a failed `resolveShape`/read keeps its shape counted so the alarm keeps
5992
+ * polling and retries next tick.
5993
+ */
5994
+ private pollSocketGlobalShapes;
5995
+ /**
5996
+ * Send one poke (`pokeStart` → `pokePart` per shape → `pokeEnd`) to a socket.
5997
+ * All parts apply atomically at `pokeEnd`. Returns `true` when every frame was
5998
+ * handed to the socket, `false` when a send threw mid-poke (the socket closed)
5999
+ * — callers must NOT advance their shape baselines on a `false` so the client
6000
+ * re-receives the rows on its next flush/reconnect instead of losing them.
6001
+ */
6002
+ private sendPoke;
6003
+ /**
6004
+ * The recipient client's `__client_watermark` for stamping a poke's
6005
+ * `lastMutationId`, or `undefined` when the socket announced no `clientId`
6006
+ * (a client that doesn't use custom mutators — nothing to drop an overlay
6007
+ * for). Read off the attachment so it survives hibernation.
6008
+ */
6009
+ private socketClientWatermark;
6010
+ /** Record a shape's poke baseline cursor on a socket (creating the per-socket map lazily). */
6011
+ private recordShapeMemo;
6012
+ /**
5407
6013
  * Record `outcome` as this socket's diff baseline for `subId` without
5408
6014
  * sending a frame. Used by the resume fast-path, where the client keeps its
5409
6015
  * cached value but the server still needs a baseline so the next
@@ -5718,4 +6324,4 @@ interface WhereSqlStrategy {
5718
6324
  * `undefined` when the input imposes no constraint (empty `where`).
5719
6325
  */
5720
6326
  declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
5721
- 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, 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, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type 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, applySelect, 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, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
6327
+ 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, 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 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 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, 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, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };