@lunora/do 1.0.0-alpha.114 → 1.0.0-alpha.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +129 -45
- package/dist/index.d.ts +129 -45
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-PRr5ltyV.mjs +15 -0
- package/dist/packem_shared/{SESSION_DO_TTL_DEFAULT-C4oEfgFy.mjs → SESSION_DO_TTL_DEFAULT-BbU25BWj.mjs} +1 -1
- package/dist/packem_shared/{SHARD_REGISTRY_DO_NAME-CF4ion0i.mjs → SHARD_REGISTRY_DO_NAME-DObo9_01.mjs} +1 -1
- package/dist/packem_shared/json-response-0Bq2ky0N.mjs +1 -0
- package/package.json +5 -5
- package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-B4sEEdp9.mjs +0 -16
- package/dist/packem_shared/json-response-wrh9TBPw.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -879,13 +879,52 @@ declare abstract class ShardDO {
|
|
|
879
879
|
*/
|
|
880
880
|
protected static readonly MAX_STREAMS_PER_SOCKET = 8;
|
|
881
881
|
/**
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
882
|
+
* The runtime's hard ceiling on one hibernation attachment.
|
|
883
|
+
*
|
|
884
|
+
* MEASURED against workerd rather than taken from a doc page: a
|
|
885
|
+
* `serializeAttachment` of 16385 bytes throws
|
|
886
|
+
* `A WebSocket 'attachment' cannot be larger than 16384 bytes.`, and 8192
|
|
887
|
+
* succeeds. The measurement lives in
|
|
888
|
+
* `__tests__/shard-do.subscription-cap.test.ts`; the workerd half is
|
|
889
|
+
* `__tests__/workerd/shard-do.workerd.test.ts`.
|
|
890
|
+
*
|
|
891
|
+
* This is the bound that actually binds, and the runtime is the only thing
|
|
892
|
+
* that enforces it — deliberately. A pre-flight size check here would need a
|
|
893
|
+
* second size model, and `JSON.stringify` is not it: an attachment
|
|
894
|
+
* legitimately holds the decoded wire types (`bigint`, `Date`, bytes), and
|
|
895
|
+
* stringifying a `bigint` throws. So `subscribe`/`shapeSubscribe` let the
|
|
896
|
+
* runtime refuse, roll the registry back, and spend this constant on saying
|
|
897
|
+
* WHY — the hibernation API makes them swallow the throw itself, and
|
|
898
|
+
* "failed to persist subscription attachment" is not something an app can
|
|
899
|
+
* act on.
|
|
900
|
+
*/
|
|
901
|
+
protected static readonly MAX_ATTACHMENT_BYTES = 16384;
|
|
902
|
+
/**
|
|
903
|
+
* Per-socket cap on `subs` + `shapes` together — a coarse backstop on
|
|
904
|
+
* per-poke fan-out work, NOT the storage bound.
|
|
905
|
+
*
|
|
906
|
+
* The storage bound is {@link ShardDO.MAX_ATTACHMENT_BYTES}, and it is the
|
|
907
|
+
* one that can actually stop a legitimate app: every registration is
|
|
908
|
+
* persisted in the hibernation attachment as `{functionPath, table, args,
|
|
909
|
+
* sinceSeq, sinceEpoch}` beside `connectionId`/`userId`/`identity`/
|
|
910
|
+
* `clientId`/`context`/`whispers`, and `args` is the client's to choose, so
|
|
911
|
+
* no fixed count can bound it.
|
|
912
|
+
*
|
|
913
|
+
* 32 against the measured numbers: a realistic registration (a function
|
|
914
|
+
* path, one id argument, a limit, a cursor and an epoch uuid) costs ~218
|
|
915
|
+
* bytes, and a fully decorated socket's fixed fields — identity claims, app
|
|
916
|
+
* `context`, whisper topics — about 550. 32 of them is ~7.5 KB, under half
|
|
917
|
+
* the 16384-byte ceiling, so the count cap never fires before the byte
|
|
918
|
+
* budget for a record of that shape. It fires only for registrations small
|
|
919
|
+
* enough that 32 of them are cheap, which is where a fan-out backstop
|
|
920
|
+
* belongs.
|
|
921
|
+
*
|
|
922
|
+
* This was briefly 8, derived from a 2048-byte attachment budget that the
|
|
923
|
+
* runtime does not impose — 8× too small, and low enough to break an app
|
|
924
|
+
* holding a dozen live queries on one socket at runtime, which is the
|
|
925
|
+
* failure this number exists to avoid.
|
|
926
|
+
*
|
|
927
|
+
* Both numbers are asserted in `__tests__/shard-do.subscription-cap.test.ts`.
|
|
889
928
|
*/
|
|
890
929
|
protected static readonly MAX_SUBSCRIPTIONS_PER_SOCKET = 32;
|
|
891
930
|
/**
|
|
@@ -1513,14 +1552,6 @@ declare abstract class ShardDO {
|
|
|
1513
1552
|
* the scan attribution. Stamped by `getCtxDbIndexUseHook`.
|
|
1514
1553
|
*/
|
|
1515
1554
|
private currentIndexHits;
|
|
1516
|
-
/**
|
|
1517
|
-
* Resource meter for the in-flight dispatch. Created per request alongside
|
|
1518
|
-
* the scanned-table capture and handed to `createShardCtxDb` by the codegen
|
|
1519
|
-
* subclass, so one runaway mutation fails with a
|
|
1520
|
-
* `TRANSACTION_LIMIT_EXCEEDED` instead of taking the whole shard's isolate
|
|
1521
|
-
* down with it.
|
|
1522
|
-
*/
|
|
1523
|
-
private currentTransactionHeadroom;
|
|
1524
1555
|
/**
|
|
1525
1556
|
* Per-DISTINCT-statement SQL samples collected during the current `/rpc`
|
|
1526
1557
|
* dispatch by the instrumented `sql` getter, keyed by the raw query text.
|
|
@@ -1642,14 +1673,13 @@ declare abstract class ShardDO {
|
|
|
1642
1673
|
* `headroom` is an optional BY-VALUE override, mirroring
|
|
1643
1674
|
* {@link ShardDO.runShardWrite}'s pattern: the main `/rpc` dispatch
|
|
1644
1675
|
* (`handleFetchCloudflare`) captures its freshly-minted tracker in a LOCAL and
|
|
1645
|
-
* passes it here explicitly, so the ctx this dispatch builds
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1651
|
-
*
|
|
1652
|
-
* before this parameter existed.
|
|
1676
|
+
* passes it here explicitly, so the ctx this dispatch builds is metered
|
|
1677
|
+
* against ITS OWN tracker however the (possibly `await`-interleaved) handler
|
|
1678
|
+
* interleaves with a concurrent one. Callers that dispatch
|
|
1679
|
+
* through here without minting their own tracker (`dispatchLifecycle`,
|
|
1680
|
+
* `handleRunAs`) omit it and the codegen subclass falls back to
|
|
1681
|
+
* `this.transactionHeadroom()`, which mints a FRESH per-ctx budget — never
|
|
1682
|
+
* the in-flight dispatch's.
|
|
1653
1683
|
*
|
|
1654
1684
|
* `scope` is the same shape of BY-VALUE thread for the reactive cache: the
|
|
1655
1685
|
* `/rpc` query path routes through {@link ShardDO.runCachedQuery}, which
|
|
@@ -2116,9 +2146,10 @@ declare abstract class ShardDO {
|
|
|
2116
2146
|
*
|
|
2117
2147
|
* The single seam every writer-routed single-row write goes through — a studio
|
|
2118
2148
|
* row edit, a bulk row op, a TTL expiry. `headroom` is an optional BY-VALUE
|
|
2119
|
-
* meter:
|
|
2120
|
-
* `this.transactionHeadroom()`,
|
|
2121
|
-
*
|
|
2149
|
+
* meter: an admin caller omits it and the override falls back to
|
|
2150
|
+
* `this.transactionHeadroom()`, which mints a fresh per-call budget, while
|
|
2151
|
+
* {@link ShardDO.pollTtlSweeps} (an alarm work item draining many rows under
|
|
2152
|
+
* ONE ceiling) passes its own tracker explicitly.
|
|
2122
2153
|
*/
|
|
2123
2154
|
protected runShardWrite(args: RunShardWriteArgs, _headroom?: TransactionHeadroomTracker): Promise<RunShardWriteResult>;
|
|
2124
2155
|
/**
|
|
@@ -2491,16 +2522,28 @@ declare abstract class ShardDO {
|
|
|
2491
2522
|
* `applyCdcChanges(writer, args.changes)`.
|
|
2492
2523
|
*/
|
|
2493
2524
|
protected runShardApplyCdc(_args: RunShardApplyCdcArgs): Promise<RunShardApplyCdcResult>;
|
|
2525
|
+
/**
|
|
2526
|
+
* Whether `functionPath` is a paid (`.x402({ price })`) procedure. The paywall
|
|
2527
|
+
* lives at the origin worker (`/_lunora/rpc`, REST, `serverQuery`), which a
|
|
2528
|
+
* WebSocket subscription never crosses — so the shard must refuse to seed or
|
|
2529
|
+
* poke a paid query itself, or it is served free. The base class has no
|
|
2530
|
+
* function registry, so the default is `false`; the codegen-generated
|
|
2531
|
+
* subclass overrides it with the real `LUNORA_FUNCTIONS` lookup.
|
|
2532
|
+
*/
|
|
2533
|
+
protected isPaidFunction(_functionPath: string): boolean;
|
|
2494
2534
|
/**
|
|
2495
2535
|
* Register a subscription on the given socket. Stored via
|
|
2496
2536
|
* `ws.serializeAttachment` so it survives hibernation.
|
|
2497
2537
|
*
|
|
2498
2538
|
* Returns a status so the caller can surface a structured error frame
|
|
2499
|
-
* when the cap is hit or the attachment fails to
|
|
2500
|
-
* throw out of this path — the WS hibernation API
|
|
2501
|
-
* `webSocketMessage` as a fatal-channel error.
|
|
2539
|
+
* when the query is paid, the cap is hit or the attachment fails to
|
|
2540
|
+
* serialize. We never throw out of this path — the WS hibernation API
|
|
2541
|
+
* treats a thrown `webSocketMessage` as a fatal-channel error.
|
|
2542
|
+
*
|
|
2543
|
+
* The `paid` refusal sits here rather than at the envelope so every
|
|
2544
|
+
* registration path — not only the `subscribe` frame — goes through it.
|
|
2502
2545
|
*/
|
|
2503
|
-
protected subscribe(ws: ShardSocketLike, subId: string, query: SubscriptionQuery): "ok" | "serialize_failed" | "too_many";
|
|
2546
|
+
protected subscribe(ws: ShardSocketLike, subId: string, query: SubscriptionQuery): "ok" | "paid" | "serialize_failed" | "too_many";
|
|
2504
2547
|
protected unsubscribe(ws: ShardSocketLike, subId: string): void;
|
|
2505
2548
|
/**
|
|
2506
2549
|
* Register a live shape subscription on a socket — the partial-replication
|
|
@@ -2747,8 +2790,16 @@ declare abstract class ShardDO {
|
|
|
2747
2790
|
* The deferred-iterator shape (`(signal) => AsyncIterable<unknown>`) keeps
|
|
2748
2791
|
* the cancel signal pluggable per-call without coupling this signature to
|
|
2749
2792
|
* the wire-frame loop in `handleStream`.
|
|
2793
|
+
*
|
|
2794
|
+
* `identity` is the socket's verified identity, threaded BY VALUE exactly as
|
|
2795
|
+
* {@link ShardDO.executeSubscription} threads it and for the same reason: a
|
|
2796
|
+
* `stream` frame is dispatched fire-and-forget and its iterator is pulled
|
|
2797
|
+
* long after, interleaved with unrelated `/rpc` dispatches, so reading the
|
|
2798
|
+
* shared per-request identity fields instead would run an `rls()` /
|
|
2799
|
+
* `ctx.auth`-scoped stream as nobody while the shard is idle — and as
|
|
2800
|
+
* whoever else is mid-flight while it is not.
|
|
2750
2801
|
*/
|
|
2751
|
-
protected executeStream(_functionPath: string, _args: Record<string, unknown
|
|
2802
|
+
protected executeStream(_functionPath: string, _args: Record<string, unknown>, _identity?: SubscriptionIdentity): null | {
|
|
2752
2803
|
durable?: {
|
|
2753
2804
|
ttlMs?: number;
|
|
2754
2805
|
};
|
|
@@ -2901,11 +2952,24 @@ declare abstract class ShardDO {
|
|
|
2901
2952
|
*/
|
|
2902
2953
|
protected transactionLimits(): Partial<TransactionLimits>;
|
|
2903
2954
|
/**
|
|
2904
|
-
*
|
|
2905
|
-
*
|
|
2906
|
-
*
|
|
2955
|
+
* A fresh budget for a dispatch that brought none of its own.
|
|
2956
|
+
*
|
|
2957
|
+
* This used to hand back an INSTANCE FIELD stamped by `beginDispatch` — "the
|
|
2958
|
+
* meter of whichever `/rpc` is in flight". Nothing that reaches it is that
|
|
2959
|
+
* dispatch: the `/rpc` path value-threads its own tracker into `handleRpc`
|
|
2960
|
+
* and never consults this. What reached it were the out-of-band callers —
|
|
2961
|
+
* `dispatchLifecycle`'s `onConnect`/`onDisconnect` hooks, `handleRunAs`, the
|
|
2962
|
+
* admin `runShardWrite` behind the studio's row editor — which either
|
|
2963
|
+
* charged their writes to an unrelated in-flight mutation's budget (failing
|
|
2964
|
+
* one of the two with a ceiling neither caused) or, with no dispatch in
|
|
2965
|
+
* flight, ran completely unmetered.
|
|
2966
|
+
*
|
|
2967
|
+
* So: mint one, the same by-value answer {@link ShardDO.subscriptionHeadroom}
|
|
2968
|
+
* and {@link ShardDO.alarmHeadroom} give their own out-of-band callers, and
|
|
2969
|
+
* for the same reason — an ambient field says "a dispatch is in flight", not
|
|
2970
|
+
* "this caller is that dispatch".
|
|
2907
2971
|
*/
|
|
2908
|
-
protected transactionHeadroom(): TransactionHeadroomTracker
|
|
2972
|
+
protected transactionHeadroom(): TransactionHeadroomTracker;
|
|
2909
2973
|
/**
|
|
2910
2974
|
* A fresh budget for one deferred subscription re-run.
|
|
2911
2975
|
*
|
|
@@ -4101,6 +4165,27 @@ declare abstract class ShardDO {
|
|
|
4101
4165
|
*/
|
|
4102
4166
|
private recordSubscriptionRefreshError;
|
|
4103
4167
|
private refreshSubscriptions;
|
|
4168
|
+
/**
|
|
4169
|
+
* Run {@link ShardDO.seedSubscription} and fail the ONE subscription — never
|
|
4170
|
+
* the socket — when its handler throws.
|
|
4171
|
+
*
|
|
4172
|
+
* The seed dispatches the user's query: it re-validates the args and runs
|
|
4173
|
+
* the procedure's auth/RLS middleware, so an anonymous socket subscribing to
|
|
4174
|
+
* an `authQuery`, a bad argument, or a handler `NOT_FOUND` rejects here.
|
|
4175
|
+
* Under the WS hibernation API a throw out of `webSocketMessage` is a
|
|
4176
|
+
* FATAL-CHANNEL error (see the analysis on `webSocketError`): the runtime
|
|
4177
|
+
* tears the socket down, taking every OTHER live subscription on it with
|
|
4178
|
+
* it — and the client already saw this subscribe's `ack`, which resets its
|
|
4179
|
+
* reconnect backoff, so it reconnects, resubscribes, throws again, and
|
|
4180
|
+
* spins at the initial delay for the life of the page.
|
|
4181
|
+
*
|
|
4182
|
+
* So: drop the just-registered subscription from the attachment and answer
|
|
4183
|
+
* with a structured `error` frame carrying the thrown error's code. Mirrors
|
|
4184
|
+
* `refreshSubscriptions`' per-`(socket, sub)` catch on the write-flush half
|
|
4185
|
+
* of the same path, and `handleShapeSubscribe`'s rollback-then-error on a
|
|
4186
|
+
* failed shape seed.
|
|
4187
|
+
*/
|
|
4188
|
+
private seedSubscriptionGuarded;
|
|
4104
4189
|
/**
|
|
4105
4190
|
* Seed a freshly-registered subscription with its first value. Runs the
|
|
4106
4191
|
* query once, then takes one of two paths.
|
|
@@ -4115,6 +4200,10 @@ declare abstract class ShardDO {
|
|
|
4115
4200
|
*
|
|
4116
4201
|
* Either way the fresh result memoises this socket's diff baseline so later
|
|
4117
4202
|
* write-flushes ({@link refreshSubscriptions}) can emit incremental deltas.
|
|
4203
|
+
*
|
|
4204
|
+
* MAY THROW: it runs the real handler (arg re-validation plus the whole
|
|
4205
|
+
* auth/RLS middleware chain). Every caller goes through
|
|
4206
|
+
* {@link ShardDO.seedSubscriptionGuarded}, which owns that failure.
|
|
4118
4207
|
*/
|
|
4119
4208
|
private seedSubscription;
|
|
4120
4209
|
/**
|
|
@@ -4139,8 +4228,8 @@ declare abstract class ShardDO {
|
|
|
4139
4228
|
* thrown `webSocketMessage` is fatal to the hibernating socket).
|
|
4140
4229
|
*/
|
|
4141
4230
|
private handleShapeSubscribe;
|
|
4142
|
-
/** Send a structured `error` frame for a failed `shape_subscribe`, swallowing a send on an already-closed socket. */
|
|
4143
|
-
private
|
|
4231
|
+
/** Send a structured `error` frame for a failed `subscribe`/`shape_subscribe`, swallowing a send on an already-closed socket. */
|
|
4232
|
+
private sendSubscriptionError;
|
|
4144
4233
|
/**
|
|
4145
4234
|
* Seed a freshly-registered shape subscription. Resolves the shape under the
|
|
4146
4235
|
* socket's verified identity, then ships either:
|
|
@@ -4363,8 +4452,8 @@ declare abstract class ShardDO {
|
|
|
4363
4452
|
private scheduleGlobalPoll;
|
|
4364
4453
|
/**
|
|
4365
4454
|
* Delete one expired row through {@link ShardDO.runShardWrite}, passing this
|
|
4366
|
-
* sweep's own by-value meter (
|
|
4367
|
-
*
|
|
4455
|
+
* sweep's own by-value meter (the override's `this.transactionHeadroom()`
|
|
4456
|
+
* fallback would mint a fresh budget per row, bounding nothing),
|
|
4368
4457
|
* absorbing a `TRANSACTION_LIMIT_EXCEEDED` as "batch full" rather than
|
|
4369
4458
|
* letting it propagate — split out of {@link ShardDO.pollTtlSweeps} to keep
|
|
4370
4459
|
* that method's own complexity down. Returns `true` when the limit was hit
|
|
@@ -4435,12 +4524,7 @@ declare abstract class ShardDO {
|
|
|
4435
4524
|
tables: string[];
|
|
4436
4525
|
} | undefined>;
|
|
4437
4526
|
private beginDispatch;
|
|
4438
|
-
/**
|
|
4439
|
-
* Clear every per-request field {@link ShardDO.beginDispatch} stamped.
|
|
4440
|
-
*
|
|
4441
|
-
* `dispatchHeadroom` is passed rather than read off `this` so the headroom
|
|
4442
|
-
* clear stays identity-guarded — see the comment on it.
|
|
4443
|
-
*/
|
|
4527
|
+
/** Clear every per-request field {@link ShardDO.beginDispatch} stamped. */
|
|
4444
4528
|
private endDispatch;
|
|
4445
4529
|
/**
|
|
4446
4530
|
* Open one `.global()` poll tick: ask the global changelog what moved since
|
package/dist/index.d.ts
CHANGED
|
@@ -879,13 +879,52 @@ declare abstract class ShardDO {
|
|
|
879
879
|
*/
|
|
880
880
|
protected static readonly MAX_STREAMS_PER_SOCKET = 8;
|
|
881
881
|
/**
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
882
|
+
* The runtime's hard ceiling on one hibernation attachment.
|
|
883
|
+
*
|
|
884
|
+
* MEASURED against workerd rather than taken from a doc page: a
|
|
885
|
+
* `serializeAttachment` of 16385 bytes throws
|
|
886
|
+
* `A WebSocket 'attachment' cannot be larger than 16384 bytes.`, and 8192
|
|
887
|
+
* succeeds. The measurement lives in
|
|
888
|
+
* `__tests__/shard-do.subscription-cap.test.ts`; the workerd half is
|
|
889
|
+
* `__tests__/workerd/shard-do.workerd.test.ts`.
|
|
890
|
+
*
|
|
891
|
+
* This is the bound that actually binds, and the runtime is the only thing
|
|
892
|
+
* that enforces it — deliberately. A pre-flight size check here would need a
|
|
893
|
+
* second size model, and `JSON.stringify` is not it: an attachment
|
|
894
|
+
* legitimately holds the decoded wire types (`bigint`, `Date`, bytes), and
|
|
895
|
+
* stringifying a `bigint` throws. So `subscribe`/`shapeSubscribe` let the
|
|
896
|
+
* runtime refuse, roll the registry back, and spend this constant on saying
|
|
897
|
+
* WHY — the hibernation API makes them swallow the throw itself, and
|
|
898
|
+
* "failed to persist subscription attachment" is not something an app can
|
|
899
|
+
* act on.
|
|
900
|
+
*/
|
|
901
|
+
protected static readonly MAX_ATTACHMENT_BYTES = 16384;
|
|
902
|
+
/**
|
|
903
|
+
* Per-socket cap on `subs` + `shapes` together — a coarse backstop on
|
|
904
|
+
* per-poke fan-out work, NOT the storage bound.
|
|
905
|
+
*
|
|
906
|
+
* The storage bound is {@link ShardDO.MAX_ATTACHMENT_BYTES}, and it is the
|
|
907
|
+
* one that can actually stop a legitimate app: every registration is
|
|
908
|
+
* persisted in the hibernation attachment as `{functionPath, table, args,
|
|
909
|
+
* sinceSeq, sinceEpoch}` beside `connectionId`/`userId`/`identity`/
|
|
910
|
+
* `clientId`/`context`/`whispers`, and `args` is the client's to choose, so
|
|
911
|
+
* no fixed count can bound it.
|
|
912
|
+
*
|
|
913
|
+
* 32 against the measured numbers: a realistic registration (a function
|
|
914
|
+
* path, one id argument, a limit, a cursor and an epoch uuid) costs ~218
|
|
915
|
+
* bytes, and a fully decorated socket's fixed fields — identity claims, app
|
|
916
|
+
* `context`, whisper topics — about 550. 32 of them is ~7.5 KB, under half
|
|
917
|
+
* the 16384-byte ceiling, so the count cap never fires before the byte
|
|
918
|
+
* budget for a record of that shape. It fires only for registrations small
|
|
919
|
+
* enough that 32 of them are cheap, which is where a fan-out backstop
|
|
920
|
+
* belongs.
|
|
921
|
+
*
|
|
922
|
+
* This was briefly 8, derived from a 2048-byte attachment budget that the
|
|
923
|
+
* runtime does not impose — 8× too small, and low enough to break an app
|
|
924
|
+
* holding a dozen live queries on one socket at runtime, which is the
|
|
925
|
+
* failure this number exists to avoid.
|
|
926
|
+
*
|
|
927
|
+
* Both numbers are asserted in `__tests__/shard-do.subscription-cap.test.ts`.
|
|
889
928
|
*/
|
|
890
929
|
protected static readonly MAX_SUBSCRIPTIONS_PER_SOCKET = 32;
|
|
891
930
|
/**
|
|
@@ -1513,14 +1552,6 @@ declare abstract class ShardDO {
|
|
|
1513
1552
|
* the scan attribution. Stamped by `getCtxDbIndexUseHook`.
|
|
1514
1553
|
*/
|
|
1515
1554
|
private currentIndexHits;
|
|
1516
|
-
/**
|
|
1517
|
-
* Resource meter for the in-flight dispatch. Created per request alongside
|
|
1518
|
-
* the scanned-table capture and handed to `createShardCtxDb` by the codegen
|
|
1519
|
-
* subclass, so one runaway mutation fails with a
|
|
1520
|
-
* `TRANSACTION_LIMIT_EXCEEDED` instead of taking the whole shard's isolate
|
|
1521
|
-
* down with it.
|
|
1522
|
-
*/
|
|
1523
|
-
private currentTransactionHeadroom;
|
|
1524
1555
|
/**
|
|
1525
1556
|
* Per-DISTINCT-statement SQL samples collected during the current `/rpc`
|
|
1526
1557
|
* dispatch by the instrumented `sql` getter, keyed by the raw query text.
|
|
@@ -1642,14 +1673,13 @@ declare abstract class ShardDO {
|
|
|
1642
1673
|
* `headroom` is an optional BY-VALUE override, mirroring
|
|
1643
1674
|
* {@link ShardDO.runShardWrite}'s pattern: the main `/rpc` dispatch
|
|
1644
1675
|
* (`handleFetchCloudflare`) captures its freshly-minted tracker in a LOCAL and
|
|
1645
|
-
* passes it here explicitly, so the ctx this dispatch builds
|
|
1646
|
-
*
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1651
|
-
*
|
|
1652
|
-
* before this parameter existed.
|
|
1676
|
+
* passes it here explicitly, so the ctx this dispatch builds is metered
|
|
1677
|
+
* against ITS OWN tracker however the (possibly `await`-interleaved) handler
|
|
1678
|
+
* interleaves with a concurrent one. Callers that dispatch
|
|
1679
|
+
* through here without minting their own tracker (`dispatchLifecycle`,
|
|
1680
|
+
* `handleRunAs`) omit it and the codegen subclass falls back to
|
|
1681
|
+
* `this.transactionHeadroom()`, which mints a FRESH per-ctx budget — never
|
|
1682
|
+
* the in-flight dispatch's.
|
|
1653
1683
|
*
|
|
1654
1684
|
* `scope` is the same shape of BY-VALUE thread for the reactive cache: the
|
|
1655
1685
|
* `/rpc` query path routes through {@link ShardDO.runCachedQuery}, which
|
|
@@ -2116,9 +2146,10 @@ declare abstract class ShardDO {
|
|
|
2116
2146
|
*
|
|
2117
2147
|
* The single seam every writer-routed single-row write goes through — a studio
|
|
2118
2148
|
* row edit, a bulk row op, a TTL expiry. `headroom` is an optional BY-VALUE
|
|
2119
|
-
* meter:
|
|
2120
|
-
* `this.transactionHeadroom()`,
|
|
2121
|
-
*
|
|
2149
|
+
* meter: an admin caller omits it and the override falls back to
|
|
2150
|
+
* `this.transactionHeadroom()`, which mints a fresh per-call budget, while
|
|
2151
|
+
* {@link ShardDO.pollTtlSweeps} (an alarm work item draining many rows under
|
|
2152
|
+
* ONE ceiling) passes its own tracker explicitly.
|
|
2122
2153
|
*/
|
|
2123
2154
|
protected runShardWrite(args: RunShardWriteArgs, _headroom?: TransactionHeadroomTracker): Promise<RunShardWriteResult>;
|
|
2124
2155
|
/**
|
|
@@ -2491,16 +2522,28 @@ declare abstract class ShardDO {
|
|
|
2491
2522
|
* `applyCdcChanges(writer, args.changes)`.
|
|
2492
2523
|
*/
|
|
2493
2524
|
protected runShardApplyCdc(_args: RunShardApplyCdcArgs): Promise<RunShardApplyCdcResult>;
|
|
2525
|
+
/**
|
|
2526
|
+
* Whether `functionPath` is a paid (`.x402({ price })`) procedure. The paywall
|
|
2527
|
+
* lives at the origin worker (`/_lunora/rpc`, REST, `serverQuery`), which a
|
|
2528
|
+
* WebSocket subscription never crosses — so the shard must refuse to seed or
|
|
2529
|
+
* poke a paid query itself, or it is served free. The base class has no
|
|
2530
|
+
* function registry, so the default is `false`; the codegen-generated
|
|
2531
|
+
* subclass overrides it with the real `LUNORA_FUNCTIONS` lookup.
|
|
2532
|
+
*/
|
|
2533
|
+
protected isPaidFunction(_functionPath: string): boolean;
|
|
2494
2534
|
/**
|
|
2495
2535
|
* Register a subscription on the given socket. Stored via
|
|
2496
2536
|
* `ws.serializeAttachment` so it survives hibernation.
|
|
2497
2537
|
*
|
|
2498
2538
|
* Returns a status so the caller can surface a structured error frame
|
|
2499
|
-
* when the cap is hit or the attachment fails to
|
|
2500
|
-
* throw out of this path — the WS hibernation API
|
|
2501
|
-
* `webSocketMessage` as a fatal-channel error.
|
|
2539
|
+
* when the query is paid, the cap is hit or the attachment fails to
|
|
2540
|
+
* serialize. We never throw out of this path — the WS hibernation API
|
|
2541
|
+
* treats a thrown `webSocketMessage` as a fatal-channel error.
|
|
2542
|
+
*
|
|
2543
|
+
* The `paid` refusal sits here rather than at the envelope so every
|
|
2544
|
+
* registration path — not only the `subscribe` frame — goes through it.
|
|
2502
2545
|
*/
|
|
2503
|
-
protected subscribe(ws: ShardSocketLike, subId: string, query: SubscriptionQuery): "ok" | "serialize_failed" | "too_many";
|
|
2546
|
+
protected subscribe(ws: ShardSocketLike, subId: string, query: SubscriptionQuery): "ok" | "paid" | "serialize_failed" | "too_many";
|
|
2504
2547
|
protected unsubscribe(ws: ShardSocketLike, subId: string): void;
|
|
2505
2548
|
/**
|
|
2506
2549
|
* Register a live shape subscription on a socket — the partial-replication
|
|
@@ -2747,8 +2790,16 @@ declare abstract class ShardDO {
|
|
|
2747
2790
|
* The deferred-iterator shape (`(signal) => AsyncIterable<unknown>`) keeps
|
|
2748
2791
|
* the cancel signal pluggable per-call without coupling this signature to
|
|
2749
2792
|
* the wire-frame loop in `handleStream`.
|
|
2793
|
+
*
|
|
2794
|
+
* `identity` is the socket's verified identity, threaded BY VALUE exactly as
|
|
2795
|
+
* {@link ShardDO.executeSubscription} threads it and for the same reason: a
|
|
2796
|
+
* `stream` frame is dispatched fire-and-forget and its iterator is pulled
|
|
2797
|
+
* long after, interleaved with unrelated `/rpc` dispatches, so reading the
|
|
2798
|
+
* shared per-request identity fields instead would run an `rls()` /
|
|
2799
|
+
* `ctx.auth`-scoped stream as nobody while the shard is idle — and as
|
|
2800
|
+
* whoever else is mid-flight while it is not.
|
|
2750
2801
|
*/
|
|
2751
|
-
protected executeStream(_functionPath: string, _args: Record<string, unknown
|
|
2802
|
+
protected executeStream(_functionPath: string, _args: Record<string, unknown>, _identity?: SubscriptionIdentity): null | {
|
|
2752
2803
|
durable?: {
|
|
2753
2804
|
ttlMs?: number;
|
|
2754
2805
|
};
|
|
@@ -2901,11 +2952,24 @@ declare abstract class ShardDO {
|
|
|
2901
2952
|
*/
|
|
2902
2953
|
protected transactionLimits(): Partial<TransactionLimits>;
|
|
2903
2954
|
/**
|
|
2904
|
-
*
|
|
2905
|
-
*
|
|
2906
|
-
*
|
|
2955
|
+
* A fresh budget for a dispatch that brought none of its own.
|
|
2956
|
+
*
|
|
2957
|
+
* This used to hand back an INSTANCE FIELD stamped by `beginDispatch` — "the
|
|
2958
|
+
* meter of whichever `/rpc` is in flight". Nothing that reaches it is that
|
|
2959
|
+
* dispatch: the `/rpc` path value-threads its own tracker into `handleRpc`
|
|
2960
|
+
* and never consults this. What reached it were the out-of-band callers —
|
|
2961
|
+
* `dispatchLifecycle`'s `onConnect`/`onDisconnect` hooks, `handleRunAs`, the
|
|
2962
|
+
* admin `runShardWrite` behind the studio's row editor — which either
|
|
2963
|
+
* charged their writes to an unrelated in-flight mutation's budget (failing
|
|
2964
|
+
* one of the two with a ceiling neither caused) or, with no dispatch in
|
|
2965
|
+
* flight, ran completely unmetered.
|
|
2966
|
+
*
|
|
2967
|
+
* So: mint one, the same by-value answer {@link ShardDO.subscriptionHeadroom}
|
|
2968
|
+
* and {@link ShardDO.alarmHeadroom} give their own out-of-band callers, and
|
|
2969
|
+
* for the same reason — an ambient field says "a dispatch is in flight", not
|
|
2970
|
+
* "this caller is that dispatch".
|
|
2907
2971
|
*/
|
|
2908
|
-
protected transactionHeadroom(): TransactionHeadroomTracker
|
|
2972
|
+
protected transactionHeadroom(): TransactionHeadroomTracker;
|
|
2909
2973
|
/**
|
|
2910
2974
|
* A fresh budget for one deferred subscription re-run.
|
|
2911
2975
|
*
|
|
@@ -4101,6 +4165,27 @@ declare abstract class ShardDO {
|
|
|
4101
4165
|
*/
|
|
4102
4166
|
private recordSubscriptionRefreshError;
|
|
4103
4167
|
private refreshSubscriptions;
|
|
4168
|
+
/**
|
|
4169
|
+
* Run {@link ShardDO.seedSubscription} and fail the ONE subscription — never
|
|
4170
|
+
* the socket — when its handler throws.
|
|
4171
|
+
*
|
|
4172
|
+
* The seed dispatches the user's query: it re-validates the args and runs
|
|
4173
|
+
* the procedure's auth/RLS middleware, so an anonymous socket subscribing to
|
|
4174
|
+
* an `authQuery`, a bad argument, or a handler `NOT_FOUND` rejects here.
|
|
4175
|
+
* Under the WS hibernation API a throw out of `webSocketMessage` is a
|
|
4176
|
+
* FATAL-CHANNEL error (see the analysis on `webSocketError`): the runtime
|
|
4177
|
+
* tears the socket down, taking every OTHER live subscription on it with
|
|
4178
|
+
* it — and the client already saw this subscribe's `ack`, which resets its
|
|
4179
|
+
* reconnect backoff, so it reconnects, resubscribes, throws again, and
|
|
4180
|
+
* spins at the initial delay for the life of the page.
|
|
4181
|
+
*
|
|
4182
|
+
* So: drop the just-registered subscription from the attachment and answer
|
|
4183
|
+
* with a structured `error` frame carrying the thrown error's code. Mirrors
|
|
4184
|
+
* `refreshSubscriptions`' per-`(socket, sub)` catch on the write-flush half
|
|
4185
|
+
* of the same path, and `handleShapeSubscribe`'s rollback-then-error on a
|
|
4186
|
+
* failed shape seed.
|
|
4187
|
+
*/
|
|
4188
|
+
private seedSubscriptionGuarded;
|
|
4104
4189
|
/**
|
|
4105
4190
|
* Seed a freshly-registered subscription with its first value. Runs the
|
|
4106
4191
|
* query once, then takes one of two paths.
|
|
@@ -4115,6 +4200,10 @@ declare abstract class ShardDO {
|
|
|
4115
4200
|
*
|
|
4116
4201
|
* Either way the fresh result memoises this socket's diff baseline so later
|
|
4117
4202
|
* write-flushes ({@link refreshSubscriptions}) can emit incremental deltas.
|
|
4203
|
+
*
|
|
4204
|
+
* MAY THROW: it runs the real handler (arg re-validation plus the whole
|
|
4205
|
+
* auth/RLS middleware chain). Every caller goes through
|
|
4206
|
+
* {@link ShardDO.seedSubscriptionGuarded}, which owns that failure.
|
|
4118
4207
|
*/
|
|
4119
4208
|
private seedSubscription;
|
|
4120
4209
|
/**
|
|
@@ -4139,8 +4228,8 @@ declare abstract class ShardDO {
|
|
|
4139
4228
|
* thrown `webSocketMessage` is fatal to the hibernating socket).
|
|
4140
4229
|
*/
|
|
4141
4230
|
private handleShapeSubscribe;
|
|
4142
|
-
/** Send a structured `error` frame for a failed `shape_subscribe`, swallowing a send on an already-closed socket. */
|
|
4143
|
-
private
|
|
4231
|
+
/** Send a structured `error` frame for a failed `subscribe`/`shape_subscribe`, swallowing a send on an already-closed socket. */
|
|
4232
|
+
private sendSubscriptionError;
|
|
4144
4233
|
/**
|
|
4145
4234
|
* Seed a freshly-registered shape subscription. Resolves the shape under the
|
|
4146
4235
|
* socket's verified identity, then ships either:
|
|
@@ -4363,8 +4452,8 @@ declare abstract class ShardDO {
|
|
|
4363
4452
|
private scheduleGlobalPoll;
|
|
4364
4453
|
/**
|
|
4365
4454
|
* Delete one expired row through {@link ShardDO.runShardWrite}, passing this
|
|
4366
|
-
* sweep's own by-value meter (
|
|
4367
|
-
*
|
|
4455
|
+
* sweep's own by-value meter (the override's `this.transactionHeadroom()`
|
|
4456
|
+
* fallback would mint a fresh budget per row, bounding nothing),
|
|
4368
4457
|
* absorbing a `TRANSACTION_LIMIT_EXCEEDED` as "batch full" rather than
|
|
4369
4458
|
* letting it propagate — split out of {@link ShardDO.pollTtlSweeps} to keep
|
|
4370
4459
|
* that method's own complexity down. Returns `true` when the limit was hit
|
|
@@ -4435,12 +4524,7 @@ declare abstract class ShardDO {
|
|
|
4435
4524
|
tables: string[];
|
|
4436
4525
|
} | undefined>;
|
|
4437
4526
|
private beginDispatch;
|
|
4438
|
-
/**
|
|
4439
|
-
* Clear every per-request field {@link ShardDO.beginDispatch} stamped.
|
|
4440
|
-
*
|
|
4441
|
-
* `dispatchHeadroom` is passed rather than read off `this` so the headroom
|
|
4442
|
-
* clear stays identity-guarded — see the comment on it.
|
|
4443
|
-
*/
|
|
4527
|
+
/** Clear every per-request field {@link ShardDO.beginDispatch} stamped. */
|
|
4444
4528
|
private endDispatch;
|
|
4445
4529
|
/**
|
|
4446
4530
|
* Open one `.global()` poll tick: ask the global changelog what moved since
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{serveRelationFanout as a}from"./packem_shared/serveRelationFanout-DcSjzAZv.mjs";import{SESSION_DO_TTL_DEFAULT as t,SessionDO as c}from"./packem_shared/SESSION_DO_TTL_DEFAULT-
|
|
1
|
+
import{serveRelationFanout as a}from"./packem_shared/serveRelationFanout-DcSjzAZv.mjs";import{SESSION_DO_TTL_DEFAULT as t,SessionDO as c}from"./packem_shared/SESSION_DO_TTL_DEFAULT-BbU25BWj.mjs";import{ROOT_DO_SIZE_WARN_BYTES as i,ROOT_SHARD_NAME as s,ShardDO as n}from"./packem_shared/ROOT_DO_SIZE_WARN_BYTES-PRr5ltyV.mjs";import{SHARD_REGISTRY_DO_NAME as R,ShardRegistryDO as d}from"./packem_shared/SHARD_REGISTRY_DO_NAME-DObo9_01.mjs";import{createShardAlarms as h,createShardDirectory as D,createShardHost as O,createShardKvStore as _,createShardPlatform as E,createSocketHost as m,createWorkerPlatform as u}from"@lunora/platform-cloudflare";import{REPROJECTION_MIGRATION_PREFIX as x,UNVOUCHABLE_DEP as I,applyCdcChanges as f,assertShapeShardable as A,backfillSearchIndexes as b,buildReprojectionMigration as M,clearMemoryTables as g,countLegacyRows as N,createReadFootprint as k,createShardCtxDb as y,exportShardRows as C,importShardRows as H,isSourceDue as L,markUnvouchableReads as P,pullExternalSourceIncrementalTick as F,pullExternalSourceTick as U,reprojectionMigrationId as j,reprojectionTables as v,runDataMigration as w,runShardMigrations as B,subscriptionListDeltas as G}from"@lunora/shard-engine";export{x as REPROJECTION_MIGRATION_PREFIX,i as ROOT_DO_SIZE_WARN_BYTES,s as ROOT_SHARD_NAME,t as SESSION_DO_TTL_DEFAULT,R as SHARD_REGISTRY_DO_NAME,c as SessionDO,n as ShardDO,d as ShardRegistryDO,I as UNVOUCHABLE_DEP,f as applyCdcChanges,A as assertShapeShardable,b as backfillSearchIndexes,M as buildReprojectionMigration,g as clearMemoryTables,N as countLegacyRows,k as createReadFootprint,h as createShardAlarms,y as createShardCtxDb,D as createShardDirectory,O as createShardHost,_ as createShardKvStore,E as createShardPlatform,m as createSocketHost,u as createWorkerPlatform,C as exportShardRows,H as importShardRows,L as isSourceDue,P as markUnvouchableReads,F as pullExternalSourceIncrementalTick,U as pullExternalSourceTick,j as reprojectionMigrationId,v as reprojectionTables,w as runDataMigration,B as runShardMigrations,a as serveRelationFanout,G as subscriptionListDeltas};
|