@lunora/do 1.0.0-alpha.6 → 1.0.0-alpha.8
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 +153 -43
- package/dist/index.d.ts +153 -43
- package/dist/index.mjs +6 -4
- package/dist/packem_shared/{ADMIN_FUNCTIONS-CHcC8fKV.mjs → ADMIN_FUNCTIONS-D_UiYJFk.mjs} +3 -1
- package/dist/packem_shared/{ROOT_DO_SIZE_WARN_BYTES-DfwcxW8F.mjs → ROOT_DO_SIZE_WARN_BYTES-BCz6GIDw.mjs} +130 -129
- package/dist/packem_shared/{ReactiveCache-ByVzgH3d.mjs → ReactiveCache-1hDydFyv.mjs} +1 -28
- package/dist/packem_shared/{serveRelationFanout-oxaM6_WL.mjs → serveRelationFanout-C6lDaesn.mjs} +1 -1
- package/dist/packem_shared/stableStringify-CyHKJXre.mjs +30 -0
- package/dist/packem_shared/subscriptionListDeltas-ce84gpwL.mjs +111 -0
- package/package.json +1 -1
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.
|
|
@@ -2262,6 +2253,19 @@ declare const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
|
|
|
2262
2253
|
*/
|
|
2263
2254
|
declare const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
|
|
2264
2255
|
/**
|
|
2256
|
+
* Reserved `functionPath` prefix for live feature-flag reads. The React client's
|
|
2257
|
+
* `useFlag`/`useFlags` subscribe to `__lunora_flags__:eval` over the same WS
|
|
2258
|
+
* channel as a user query; `ShardDO` intercepts it before user dispatch and
|
|
2259
|
+
* serves it from the codegen-overridden flag-subscription read hook, which
|
|
2260
|
+
* evaluates the flag through the app's OpenFeature provider under the socket's
|
|
2261
|
+
* verified identity. Like the other reserved prefixes it is NOT admin-gated (a
|
|
2262
|
+
* flag read is public, scoped to the subscriber's own targeting context), and
|
|
2263
|
+
* the `__lunora_` namespace is reserved so a real `<file>:<function>` can't
|
|
2264
|
+
* collide. Re-evaluated on every write-flush so values stay live within a
|
|
2265
|
+
* session (provider-side flips with no intervening write surface on reconnect).
|
|
2266
|
+
*/
|
|
2267
|
+
declare const FLAGS_FUNCTION_PREFIX = "__lunora_flags__:";
|
|
2268
|
+
/**
|
|
2265
2269
|
* Fully-qualified reserved paths the data browser invokes. The
|
|
2266
2270
|
* `__lunora_admin__:` prefix is spelled out inline rather than interpolated so
|
|
2267
2271
|
* the values stay emittable under `--isolatedDeclarations`.
|
|
@@ -2292,6 +2296,7 @@ declare const ADMIN_FUNCTIONS: {
|
|
|
2292
2296
|
readonly getSettings: "__lunora_admin__:getSettings";
|
|
2293
2297
|
readonly getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus";
|
|
2294
2298
|
readonly importShard: "__lunora_admin__:importShard";
|
|
2299
|
+
readonly listFlags: "__lunora_admin__:listFlags";
|
|
2295
2300
|
readonly listQueues: "__lunora_admin__:listQueues";
|
|
2296
2301
|
readonly listTables: "__lunora_admin__:listTables";
|
|
2297
2302
|
readonly listWorkflows: "__lunora_admin__:listWorkflows";
|
|
@@ -2572,6 +2577,8 @@ interface StorageRulesResult {
|
|
|
2572
2577
|
* package's tests and the studio's fails the build if the two key sets diverge.
|
|
2573
2578
|
*/
|
|
2574
2579
|
interface StudioFeaturesResult {
|
|
2580
|
+
/** `@lunora/flags` / `ctx.flags` is used, or it is a declared dependency. */
|
|
2581
|
+
flags: boolean;
|
|
2575
2582
|
/** `@lunora/mail` is imported by a `lunora/` source or a declared dependency. */
|
|
2576
2583
|
mail: boolean;
|
|
2577
2584
|
/** `@lunora/payment` is used (import or `ctx.payments`) or a declared dependency. */
|
|
@@ -2588,6 +2595,40 @@ interface StudioFeaturesResult {
|
|
|
2588
2595
|
workflows: boolean;
|
|
2589
2596
|
}
|
|
2590
2597
|
/**
|
|
2598
|
+
* One feature flag evaluated under a supplied targeting context, surfaced by
|
|
2599
|
+
* `__lunora_admin__:listFlags` for the studio's read-only Flags page. The `key`
|
|
2600
|
+
* and `type` are statically discovered by `@lunora/codegen` from the app's
|
|
2601
|
+
* `ctx.flags.<type>("key", …)` reads; `value`/`reason`/`variant`/`errorCode`
|
|
2602
|
+
* come from the live OpenFeature evaluation (the codegen subclass overrides the
|
|
2603
|
+
* base `evaluateFlags` hook). `value` is the resolved flag value as JSON.
|
|
2604
|
+
*/
|
|
2605
|
+
interface FlagEvaluation {
|
|
2606
|
+
/** OpenFeature `errorCode` when the evaluation failed (the value falls back to the default). */
|
|
2607
|
+
errorCode?: string;
|
|
2608
|
+
/** The discovered flag key (the first argument of a `ctx.flags.<type>(...)` read). */
|
|
2609
|
+
key: string;
|
|
2610
|
+
/** OpenFeature `reason` for the resolution (`TARGETING_MATCH`, `DEFAULT`, `ERROR`, …). */
|
|
2611
|
+
reason?: string;
|
|
2612
|
+
/** The flag's value type, derived from which `ctx.flags.<type>` method read it. */
|
|
2613
|
+
type: "boolean" | "number" | "object" | "string";
|
|
2614
|
+
/** The resolved value (JSON), or the type default when unconfigured / on error. */
|
|
2615
|
+
value: unknown;
|
|
2616
|
+
/** OpenFeature `variant` identifier when the provider reports one. */
|
|
2617
|
+
variant?: string;
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Payload of a `__lunora_admin__:listFlags` call: every statically-discovered
|
|
2621
|
+
* flag evaluated under the supplied targeting context. `configured` is `false`
|
|
2622
|
+
* when the app wires no `@lunora/flags` provider (the base hook), so the studio
|
|
2623
|
+
* can distinguish "no flags configured" from "configured but zero flags read".
|
|
2624
|
+
*/
|
|
2625
|
+
interface FlagsResult {
|
|
2626
|
+
/** `true` when an `@lunora/flags` provider is wired (the codegen override ran). */
|
|
2627
|
+
configured: boolean;
|
|
2628
|
+
/** Each discovered flag evaluated under the request's targeting context. */
|
|
2629
|
+
flags: FlagEvaluation[];
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2591
2632
|
* One declared Cloudflare Workflow, surfaced by `__lunora_admin__:listWorkflows`
|
|
2592
2633
|
* for the studio's Workflows page. Statically discovered by `@lunora/codegen`
|
|
2593
2634
|
* from `lunora/workflows.ts` (the codegen subclass overrides the base hook);
|
|
@@ -3666,6 +3707,44 @@ declare class SessionDO {
|
|
|
3666
3707
|
private handleRevoke;
|
|
3667
3708
|
}
|
|
3668
3709
|
/**
|
|
3710
|
+
* Diff the previously-sent list snapshot (`previousJson`, the memo's
|
|
3711
|
+
* `lastJson`) against the new query result and produce per-row
|
|
3712
|
+
* {@link MutationDelta}s the client can merge in place via `applyDelta` —
|
|
3713
|
+
* Convex-parity live-pagination deltas (server half of gap #20).
|
|
3714
|
+
*
|
|
3715
|
+
* Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
|
|
3716
|
+
* unless ALL of these hold:
|
|
3717
|
+
*
|
|
3718
|
+
* 1. `previousJson` parses to an array (there IS a previous list to diff against).
|
|
3719
|
+
* 2. `nextResult` is also an array.
|
|
3720
|
+
* 3. Every row in both arrays is a plain object carrying a string `_id`.
|
|
3721
|
+
* 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
|
|
3722
|
+
* 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
|
|
3723
|
+
*
|
|
3724
|
+
* Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
|
|
3725
|
+
* `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
|
|
3726
|
+
* full new `row`; delete omits it (matching the wire contract `@lunora/client`
|
|
3727
|
+
* parses). Deltas are ordered deletes-then-inserts/updates so the client never
|
|
3728
|
+
* sees a transient over-length page.
|
|
3729
|
+
*
|
|
3730
|
+
* Per-row serialization is done exactly **once** per refresh (finding #6). Each
|
|
3731
|
+
* row is stringified a single time into a fingerprint reused for both the
|
|
3732
|
+
* `prev !== next` change-detection compare and — when the caller passes the
|
|
3733
|
+
* optional `frames` sink — the pre-serialized delta frame body. The returned
|
|
3734
|
+
* `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
|
|
3735
|
+
* exact `JSON.stringify(delta)` string for each returned delta, in the same
|
|
3736
|
+
* order, so the caller can splice it straight into the `{type:"delta"}` frame
|
|
3737
|
+
* without serializing the delta (and the row inside it) a second time.
|
|
3738
|
+
* @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
|
|
3739
|
+
*/
|
|
3740
|
+
declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
|
|
3741
|
+
/**
|
|
3742
|
+
* Send one WebSocket frame, reporting whether it left the socket. A throw from
|
|
3743
|
+
* `ws.send` (socket closed mid-flush, outbound buffer gone) is the only
|
|
3744
|
+
* delivery-failure signal the runtime exposes; callers use the boolean to decide
|
|
3745
|
+
* whether to advance a subscription's delivered-diff baseline.
|
|
3746
|
+
*/
|
|
3747
|
+
/**
|
|
3669
3748
|
* Optional programmatic log sink, resolved from `createShardDO({ observability })`.
|
|
3670
3749
|
* Structurally a subset of `@lunora/runtime`'s `ObservabilitySink`, so a user can
|
|
3671
3750
|
* pass the SAME sink object to `createWorker` (which drives `onRpc`) and
|
|
@@ -3918,38 +3997,6 @@ interface RunShardRankPageArgs {
|
|
|
3918
3997
|
take?: number;
|
|
3919
3998
|
}
|
|
3920
3999
|
/**
|
|
3921
|
-
* Diff the previously-sent list snapshot (`previousJson`, the memo's
|
|
3922
|
-
* `lastJson`) against the new query result and produce per-row
|
|
3923
|
-
* {@link MutationDelta}s the client can merge in place via `applyDelta` —
|
|
3924
|
-
* Convex-parity live-pagination deltas (server half of gap #20).
|
|
3925
|
-
*
|
|
3926
|
-
* Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
|
|
3927
|
-
* unless ALL of these hold:
|
|
3928
|
-
*
|
|
3929
|
-
* 1. `previousJson` parses to an array (there IS a previous list to diff against).
|
|
3930
|
-
* 2. `nextResult` is also an array.
|
|
3931
|
-
* 3. Every row in both arrays is a plain object carrying a string `_id`.
|
|
3932
|
-
* 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
|
|
3933
|
-
* 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
|
|
3934
|
-
*
|
|
3935
|
-
* Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
|
|
3936
|
-
* `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
|
|
3937
|
-
* full new `row`; delete omits it (matching the wire contract `@lunora/client`
|
|
3938
|
-
* parses). Deltas are ordered deletes-then-inserts/updates so the client never
|
|
3939
|
-
* sees a transient over-length page.
|
|
3940
|
-
*
|
|
3941
|
-
* Per-row serialization is done exactly **once** per refresh (finding #6). Each
|
|
3942
|
-
* row is stringified a single time into a fingerprint reused for both the
|
|
3943
|
-
* `prev !== next` change-detection compare and — when the caller passes the
|
|
3944
|
-
* optional `frames` sink — the pre-serialized delta frame body. The returned
|
|
3945
|
-
* `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
|
|
3946
|
-
* exact `JSON.stringify(delta)` string for each returned delta, in the same
|
|
3947
|
-
* order, so the caller can splice it straight into the `{type:"delta"}` frame
|
|
3948
|
-
* without serializing the delta (and the row inside it) a second time.
|
|
3949
|
-
* @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
|
|
3950
|
-
*/
|
|
3951
|
-
declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
|
|
3952
|
-
/**
|
|
3953
4000
|
* Threshold at which a `__root__` DO triggers the size warning. 1 GiB —
|
|
3954
4001
|
* exactly 10% of the 10 GiB per-DO SQLite ceiling, leaving plenty of runway
|
|
3955
4002
|
* to plan a `.shardBy()` migration before the wall hits.
|
|
@@ -4131,6 +4178,18 @@ declare abstract class ShardDO {
|
|
|
4131
4178
|
*/
|
|
4132
4179
|
private pendingChangedTables;
|
|
4133
4180
|
/**
|
|
4181
|
+
* Coalesced set of tables awaiting a subscription-refresh pass, merged
|
|
4182
|
+
* across every {@link ShardDO.flushChangedTables} call that lands while a
|
|
4183
|
+
* pass is already draining. The single drain loop
|
|
4184
|
+
* ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
|
|
4185
|
+
* writes to the same table therefore collapses into one (or two) refresh
|
|
4186
|
+
* passes instead of N, so each affected subscription's handler re-runs once
|
|
4187
|
+
* per burst rather than once per write. `undefined` when nothing is pending.
|
|
4188
|
+
*/
|
|
4189
|
+
private pendingRefreshTables;
|
|
4190
|
+
/** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
|
|
4191
|
+
private refreshInFlight;
|
|
4192
|
+
/**
|
|
4134
4193
|
* Last pushed result per `(socket, subId)`, keyed by socket. Lets
|
|
4135
4194
|
* `refreshSubscriptions` skip re-running queries whose tables were
|
|
4136
4195
|
* untouched and suppress pushes when the re-run result is unchanged. Held
|
|
@@ -4502,6 +4561,26 @@ declare abstract class ShardDO {
|
|
|
4502
4561
|
*/
|
|
4503
4562
|
protected studioFeatures(): StudioFeaturesResult;
|
|
4504
4563
|
/**
|
|
4564
|
+
* Evaluate every statically-discovered feature flag under `context` for the
|
|
4565
|
+
* studio's read-only Flags page (`__lunora_admin__:listFlags`). The flag keys
|
|
4566
|
+
* + value types are discovered by `@lunora/codegen` from the app's
|
|
4567
|
+
* `ctx.flags.<type>("key", …)` reads and evaluated through the configured
|
|
4568
|
+
* `@lunora/flags` provider — work only the codegen subclass can do, so it
|
|
4569
|
+
* overrides this. The base class wires no provider and reports
|
|
4570
|
+
* `configured: false` with zero flags (an un-generated `ShardDO` has none).
|
|
4571
|
+
*/
|
|
4572
|
+
protected evaluateFlags(_context?: Record<string, unknown>): Promise<FlagsResult>;
|
|
4573
|
+
/**
|
|
4574
|
+
* Serve one reserved {@link FLAGS_FUNCTION_PREFIX} live flag read for the
|
|
4575
|
+
* React client's `useFlag`/`useFlags`. `functionPath` carries the flag key +
|
|
4576
|
+
* type and `args` the per-subscriber targeting context; the codegen subclass
|
|
4577
|
+
* overrides this to evaluate the flag through the app's `@lunora/flags`
|
|
4578
|
+
* provider under `identity` and return the resolved value. The base class
|
|
4579
|
+
* wires no provider, so it returns `null` — `resolveReactiveOutcome` reads
|
|
4580
|
+
* `null` as "nothing to deliver" and the subscriber keeps its default.
|
|
4581
|
+
*/
|
|
4582
|
+
protected runFlagSubscriptionRead(_functionPath: string, _arguments: Record<string, unknown>, _identity?: SubscriptionIdentity): Promise<unknown>;
|
|
4583
|
+
/**
|
|
4505
4584
|
* The Cloudflare Queues declared by this app, surfaced via
|
|
4506
4585
|
* `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
|
|
4507
4586
|
* Durable Objects and hold no shard state, so this is pure declaration
|
|
@@ -5013,6 +5092,16 @@ declare abstract class ShardDO {
|
|
|
5013
5092
|
*/
|
|
5014
5093
|
private handleGetWorkflowInstanceStatus;
|
|
5015
5094
|
/**
|
|
5095
|
+
* Serve `__lunora_admin__:listFlags` — the studio's read-only Flags page.
|
|
5096
|
+
* Evaluates every statically-discovered feature flag under an optional
|
|
5097
|
+
* `args.context` targeting context (the studio's editable context editor)
|
|
5098
|
+
* via the {@link evaluateFlags} hook, which the codegen subclass overrides
|
|
5099
|
+
* with live OpenFeature evaluation. Read-only: a flag lookup mutates no shard
|
|
5100
|
+
* state, so nothing is flushed or audited. Admin-gated by `handleAdminRpc`'s
|
|
5101
|
+
* caller.
|
|
5102
|
+
*/
|
|
5103
|
+
private handleListFlags;
|
|
5104
|
+
/**
|
|
5016
5105
|
* Run `run()` with the per-request identity pinned to (`userId`, `identity`),
|
|
5017
5106
|
* then restore the prior values in a `finally` (even if `run()` throws), so the
|
|
5018
5107
|
* forced identity can never leak into a later dispatch on this DO instance. The
|
|
@@ -5272,6 +5361,16 @@ declare abstract class ShardDO {
|
|
|
5272
5361
|
*/
|
|
5273
5362
|
private executeAdminSubscription;
|
|
5274
5363
|
/**
|
|
5364
|
+
* Resolve one subscription (seed or refresh) to its {@link SubscriptionOutcome}
|
|
5365
|
+
* by routing the `functionPath` to the right read path — shared by
|
|
5366
|
+
* {@link seedSubscription} and {@link refreshSubscriptions} so both branch
|
|
5367
|
+
* identically:
|
|
5368
|
+
* - `__lunora_admin__:*` → {@link executeAdminSubscription} (raw SQLite read).
|
|
5369
|
+
* - {@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`).
|
|
5370
|
+
* - everything else → {@link executeSubscription} (the user query, under the socket's own by-value identity).
|
|
5371
|
+
*/
|
|
5372
|
+
private resolveReactiveOutcome;
|
|
5373
|
+
/**
|
|
5275
5374
|
* Constant-time bearer check against `env.LUNORA_ADMIN_TOKEN`. Returns
|
|
5276
5375
|
* `false` (closed) when the token is unset so admin introspection is
|
|
5277
5376
|
* opt-in rather than exposed by default.
|
|
@@ -5303,6 +5402,17 @@ declare abstract class ShardDO {
|
|
|
5303
5402
|
*/
|
|
5304
5403
|
private flushChangedTables;
|
|
5305
5404
|
/**
|
|
5405
|
+
* Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
|
|
5406
|
+
* until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
|
|
5407
|
+
* Tables merged by a `flushChangedTables` that lands mid-pass are picked up
|
|
5408
|
+
* by the next loop iteration, so every committed write is observed by a
|
|
5409
|
+
* refresh that runs after it — bursts simply share a pass. The post-write
|
|
5410
|
+
* high-watermark and live-socket set are re-read inside each
|
|
5411
|
+
* `refreshSubscriptions` call, so a later batch always reflects the latest
|
|
5412
|
+
* committed state.
|
|
5413
|
+
*/
|
|
5414
|
+
private drainSubscriptionRefreshes;
|
|
5415
|
+
/**
|
|
5306
5416
|
* For every live subscription whose query reads one of `changed`, re-run
|
|
5307
5417
|
* the query and push a fresh `{ type: "data" }` frame when the result
|
|
5308
5418
|
* differs from the last one sent. Subscriptions with no `functionPath`
|
|
@@ -5689,4 +5799,4 @@ interface WhereSqlStrategy {
|
|
|
5689
5799
|
* `undefined` when the input imposes no constraint (empty `where`).
|
|
5690
5800
|
*/
|
|
5691
5801
|
declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
|
|
5692
|
-
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 };
|
|
5802
|
+
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, 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 };
|
package/dist/index.d.ts
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.
|
|
@@ -2262,6 +2253,19 @@ declare const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
|
|
|
2262
2253
|
*/
|
|
2263
2254
|
declare const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
|
|
2264
2255
|
/**
|
|
2256
|
+
* Reserved `functionPath` prefix for live feature-flag reads. The React client's
|
|
2257
|
+
* `useFlag`/`useFlags` subscribe to `__lunora_flags__:eval` over the same WS
|
|
2258
|
+
* channel as a user query; `ShardDO` intercepts it before user dispatch and
|
|
2259
|
+
* serves it from the codegen-overridden flag-subscription read hook, which
|
|
2260
|
+
* evaluates the flag through the app's OpenFeature provider under the socket's
|
|
2261
|
+
* verified identity. Like the other reserved prefixes it is NOT admin-gated (a
|
|
2262
|
+
* flag read is public, scoped to the subscriber's own targeting context), and
|
|
2263
|
+
* the `__lunora_` namespace is reserved so a real `<file>:<function>` can't
|
|
2264
|
+
* collide. Re-evaluated on every write-flush so values stay live within a
|
|
2265
|
+
* session (provider-side flips with no intervening write surface on reconnect).
|
|
2266
|
+
*/
|
|
2267
|
+
declare const FLAGS_FUNCTION_PREFIX = "__lunora_flags__:";
|
|
2268
|
+
/**
|
|
2265
2269
|
* Fully-qualified reserved paths the data browser invokes. The
|
|
2266
2270
|
* `__lunora_admin__:` prefix is spelled out inline rather than interpolated so
|
|
2267
2271
|
* the values stay emittable under `--isolatedDeclarations`.
|
|
@@ -2292,6 +2296,7 @@ declare const ADMIN_FUNCTIONS: {
|
|
|
2292
2296
|
readonly getSettings: "__lunora_admin__:getSettings";
|
|
2293
2297
|
readonly getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus";
|
|
2294
2298
|
readonly importShard: "__lunora_admin__:importShard";
|
|
2299
|
+
readonly listFlags: "__lunora_admin__:listFlags";
|
|
2295
2300
|
readonly listQueues: "__lunora_admin__:listQueues";
|
|
2296
2301
|
readonly listTables: "__lunora_admin__:listTables";
|
|
2297
2302
|
readonly listWorkflows: "__lunora_admin__:listWorkflows";
|
|
@@ -2572,6 +2577,8 @@ interface StorageRulesResult {
|
|
|
2572
2577
|
* package's tests and the studio's fails the build if the two key sets diverge.
|
|
2573
2578
|
*/
|
|
2574
2579
|
interface StudioFeaturesResult {
|
|
2580
|
+
/** `@lunora/flags` / `ctx.flags` is used, or it is a declared dependency. */
|
|
2581
|
+
flags: boolean;
|
|
2575
2582
|
/** `@lunora/mail` is imported by a `lunora/` source or a declared dependency. */
|
|
2576
2583
|
mail: boolean;
|
|
2577
2584
|
/** `@lunora/payment` is used (import or `ctx.payments`) or a declared dependency. */
|
|
@@ -2588,6 +2595,40 @@ interface StudioFeaturesResult {
|
|
|
2588
2595
|
workflows: boolean;
|
|
2589
2596
|
}
|
|
2590
2597
|
/**
|
|
2598
|
+
* One feature flag evaluated under a supplied targeting context, surfaced by
|
|
2599
|
+
* `__lunora_admin__:listFlags` for the studio's read-only Flags page. The `key`
|
|
2600
|
+
* and `type` are statically discovered by `@lunora/codegen` from the app's
|
|
2601
|
+
* `ctx.flags.<type>("key", …)` reads; `value`/`reason`/`variant`/`errorCode`
|
|
2602
|
+
* come from the live OpenFeature evaluation (the codegen subclass overrides the
|
|
2603
|
+
* base `evaluateFlags` hook). `value` is the resolved flag value as JSON.
|
|
2604
|
+
*/
|
|
2605
|
+
interface FlagEvaluation {
|
|
2606
|
+
/** OpenFeature `errorCode` when the evaluation failed (the value falls back to the default). */
|
|
2607
|
+
errorCode?: string;
|
|
2608
|
+
/** The discovered flag key (the first argument of a `ctx.flags.<type>(...)` read). */
|
|
2609
|
+
key: string;
|
|
2610
|
+
/** OpenFeature `reason` for the resolution (`TARGETING_MATCH`, `DEFAULT`, `ERROR`, …). */
|
|
2611
|
+
reason?: string;
|
|
2612
|
+
/** The flag's value type, derived from which `ctx.flags.<type>` method read it. */
|
|
2613
|
+
type: "boolean" | "number" | "object" | "string";
|
|
2614
|
+
/** The resolved value (JSON), or the type default when unconfigured / on error. */
|
|
2615
|
+
value: unknown;
|
|
2616
|
+
/** OpenFeature `variant` identifier when the provider reports one. */
|
|
2617
|
+
variant?: string;
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Payload of a `__lunora_admin__:listFlags` call: every statically-discovered
|
|
2621
|
+
* flag evaluated under the supplied targeting context. `configured` is `false`
|
|
2622
|
+
* when the app wires no `@lunora/flags` provider (the base hook), so the studio
|
|
2623
|
+
* can distinguish "no flags configured" from "configured but zero flags read".
|
|
2624
|
+
*/
|
|
2625
|
+
interface FlagsResult {
|
|
2626
|
+
/** `true` when an `@lunora/flags` provider is wired (the codegen override ran). */
|
|
2627
|
+
configured: boolean;
|
|
2628
|
+
/** Each discovered flag evaluated under the request's targeting context. */
|
|
2629
|
+
flags: FlagEvaluation[];
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2591
2632
|
* One declared Cloudflare Workflow, surfaced by `__lunora_admin__:listWorkflows`
|
|
2592
2633
|
* for the studio's Workflows page. Statically discovered by `@lunora/codegen`
|
|
2593
2634
|
* from `lunora/workflows.ts` (the codegen subclass overrides the base hook);
|
|
@@ -3666,6 +3707,44 @@ declare class SessionDO {
|
|
|
3666
3707
|
private handleRevoke;
|
|
3667
3708
|
}
|
|
3668
3709
|
/**
|
|
3710
|
+
* Diff the previously-sent list snapshot (`previousJson`, the memo's
|
|
3711
|
+
* `lastJson`) against the new query result and produce per-row
|
|
3712
|
+
* {@link MutationDelta}s the client can merge in place via `applyDelta` —
|
|
3713
|
+
* Convex-parity live-pagination deltas (server half of gap #20).
|
|
3714
|
+
*
|
|
3715
|
+
* Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
|
|
3716
|
+
* unless ALL of these hold:
|
|
3717
|
+
*
|
|
3718
|
+
* 1. `previousJson` parses to an array (there IS a previous list to diff against).
|
|
3719
|
+
* 2. `nextResult` is also an array.
|
|
3720
|
+
* 3. Every row in both arrays is a plain object carrying a string `_id`.
|
|
3721
|
+
* 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
|
|
3722
|
+
* 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
|
|
3723
|
+
*
|
|
3724
|
+
* Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
|
|
3725
|
+
* `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
|
|
3726
|
+
* full new `row`; delete omits it (matching the wire contract `@lunora/client`
|
|
3727
|
+
* parses). Deltas are ordered deletes-then-inserts/updates so the client never
|
|
3728
|
+
* sees a transient over-length page.
|
|
3729
|
+
*
|
|
3730
|
+
* Per-row serialization is done exactly **once** per refresh (finding #6). Each
|
|
3731
|
+
* row is stringified a single time into a fingerprint reused for both the
|
|
3732
|
+
* `prev !== next` change-detection compare and — when the caller passes the
|
|
3733
|
+
* optional `frames` sink — the pre-serialized delta frame body. The returned
|
|
3734
|
+
* `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
|
|
3735
|
+
* exact `JSON.stringify(delta)` string for each returned delta, in the same
|
|
3736
|
+
* order, so the caller can splice it straight into the `{type:"delta"}` frame
|
|
3737
|
+
* without serializing the delta (and the row inside it) a second time.
|
|
3738
|
+
* @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
|
|
3739
|
+
*/
|
|
3740
|
+
declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
|
|
3741
|
+
/**
|
|
3742
|
+
* Send one WebSocket frame, reporting whether it left the socket. A throw from
|
|
3743
|
+
* `ws.send` (socket closed mid-flush, outbound buffer gone) is the only
|
|
3744
|
+
* delivery-failure signal the runtime exposes; callers use the boolean to decide
|
|
3745
|
+
* whether to advance a subscription's delivered-diff baseline.
|
|
3746
|
+
*/
|
|
3747
|
+
/**
|
|
3669
3748
|
* Optional programmatic log sink, resolved from `createShardDO({ observability })`.
|
|
3670
3749
|
* Structurally a subset of `@lunora/runtime`'s `ObservabilitySink`, so a user can
|
|
3671
3750
|
* pass the SAME sink object to `createWorker` (which drives `onRpc`) and
|
|
@@ -3918,38 +3997,6 @@ interface RunShardRankPageArgs {
|
|
|
3918
3997
|
take?: number;
|
|
3919
3998
|
}
|
|
3920
3999
|
/**
|
|
3921
|
-
* Diff the previously-sent list snapshot (`previousJson`, the memo's
|
|
3922
|
-
* `lastJson`) against the new query result and produce per-row
|
|
3923
|
-
* {@link MutationDelta}s the client can merge in place via `applyDelta` —
|
|
3924
|
-
* Convex-parity live-pagination deltas (server half of gap #20).
|
|
3925
|
-
*
|
|
3926
|
-
* Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
|
|
3927
|
-
* unless ALL of these hold:
|
|
3928
|
-
*
|
|
3929
|
-
* 1. `previousJson` parses to an array (there IS a previous list to diff against).
|
|
3930
|
-
* 2. `nextResult` is also an array.
|
|
3931
|
-
* 3. Every row in both arrays is a plain object carrying a string `_id`.
|
|
3932
|
-
* 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
|
|
3933
|
-
* 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
|
|
3934
|
-
*
|
|
3935
|
-
* Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
|
|
3936
|
-
* `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
|
|
3937
|
-
* full new `row`; delete omits it (matching the wire contract `@lunora/client`
|
|
3938
|
-
* parses). Deltas are ordered deletes-then-inserts/updates so the client never
|
|
3939
|
-
* sees a transient over-length page.
|
|
3940
|
-
*
|
|
3941
|
-
* Per-row serialization is done exactly **once** per refresh (finding #6). Each
|
|
3942
|
-
* row is stringified a single time into a fingerprint reused for both the
|
|
3943
|
-
* `prev !== next` change-detection compare and — when the caller passes the
|
|
3944
|
-
* optional `frames` sink — the pre-serialized delta frame body. The returned
|
|
3945
|
-
* `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
|
|
3946
|
-
* exact `JSON.stringify(delta)` string for each returned delta, in the same
|
|
3947
|
-
* order, so the caller can splice it straight into the `{type:"delta"}` frame
|
|
3948
|
-
* without serializing the delta (and the row inside it) a second time.
|
|
3949
|
-
* @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
|
|
3950
|
-
*/
|
|
3951
|
-
declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
|
|
3952
|
-
/**
|
|
3953
4000
|
* Threshold at which a `__root__` DO triggers the size warning. 1 GiB —
|
|
3954
4001
|
* exactly 10% of the 10 GiB per-DO SQLite ceiling, leaving plenty of runway
|
|
3955
4002
|
* to plan a `.shardBy()` migration before the wall hits.
|
|
@@ -4131,6 +4178,18 @@ declare abstract class ShardDO {
|
|
|
4131
4178
|
*/
|
|
4132
4179
|
private pendingChangedTables;
|
|
4133
4180
|
/**
|
|
4181
|
+
* Coalesced set of tables awaiting a subscription-refresh pass, merged
|
|
4182
|
+
* across every {@link ShardDO.flushChangedTables} call that lands while a
|
|
4183
|
+
* pass is already draining. The single drain loop
|
|
4184
|
+
* ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
|
|
4185
|
+
* writes to the same table therefore collapses into one (or two) refresh
|
|
4186
|
+
* passes instead of N, so each affected subscription's handler re-runs once
|
|
4187
|
+
* per burst rather than once per write. `undefined` when nothing is pending.
|
|
4188
|
+
*/
|
|
4189
|
+
private pendingRefreshTables;
|
|
4190
|
+
/** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
|
|
4191
|
+
private refreshInFlight;
|
|
4192
|
+
/**
|
|
4134
4193
|
* Last pushed result per `(socket, subId)`, keyed by socket. Lets
|
|
4135
4194
|
* `refreshSubscriptions` skip re-running queries whose tables were
|
|
4136
4195
|
* untouched and suppress pushes when the re-run result is unchanged. Held
|
|
@@ -4502,6 +4561,26 @@ declare abstract class ShardDO {
|
|
|
4502
4561
|
*/
|
|
4503
4562
|
protected studioFeatures(): StudioFeaturesResult;
|
|
4504
4563
|
/**
|
|
4564
|
+
* Evaluate every statically-discovered feature flag under `context` for the
|
|
4565
|
+
* studio's read-only Flags page (`__lunora_admin__:listFlags`). The flag keys
|
|
4566
|
+
* + value types are discovered by `@lunora/codegen` from the app's
|
|
4567
|
+
* `ctx.flags.<type>("key", …)` reads and evaluated through the configured
|
|
4568
|
+
* `@lunora/flags` provider — work only the codegen subclass can do, so it
|
|
4569
|
+
* overrides this. The base class wires no provider and reports
|
|
4570
|
+
* `configured: false` with zero flags (an un-generated `ShardDO` has none).
|
|
4571
|
+
*/
|
|
4572
|
+
protected evaluateFlags(_context?: Record<string, unknown>): Promise<FlagsResult>;
|
|
4573
|
+
/**
|
|
4574
|
+
* Serve one reserved {@link FLAGS_FUNCTION_PREFIX} live flag read for the
|
|
4575
|
+
* React client's `useFlag`/`useFlags`. `functionPath` carries the flag key +
|
|
4576
|
+
* type and `args` the per-subscriber targeting context; the codegen subclass
|
|
4577
|
+
* overrides this to evaluate the flag through the app's `@lunora/flags`
|
|
4578
|
+
* provider under `identity` and return the resolved value. The base class
|
|
4579
|
+
* wires no provider, so it returns `null` — `resolveReactiveOutcome` reads
|
|
4580
|
+
* `null` as "nothing to deliver" and the subscriber keeps its default.
|
|
4581
|
+
*/
|
|
4582
|
+
protected runFlagSubscriptionRead(_functionPath: string, _arguments: Record<string, unknown>, _identity?: SubscriptionIdentity): Promise<unknown>;
|
|
4583
|
+
/**
|
|
4505
4584
|
* The Cloudflare Queues declared by this app, surfaced via
|
|
4506
4585
|
* `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
|
|
4507
4586
|
* Durable Objects and hold no shard state, so this is pure declaration
|
|
@@ -5013,6 +5092,16 @@ declare abstract class ShardDO {
|
|
|
5013
5092
|
*/
|
|
5014
5093
|
private handleGetWorkflowInstanceStatus;
|
|
5015
5094
|
/**
|
|
5095
|
+
* Serve `__lunora_admin__:listFlags` — the studio's read-only Flags page.
|
|
5096
|
+
* Evaluates every statically-discovered feature flag under an optional
|
|
5097
|
+
* `args.context` targeting context (the studio's editable context editor)
|
|
5098
|
+
* via the {@link evaluateFlags} hook, which the codegen subclass overrides
|
|
5099
|
+
* with live OpenFeature evaluation. Read-only: a flag lookup mutates no shard
|
|
5100
|
+
* state, so nothing is flushed or audited. Admin-gated by `handleAdminRpc`'s
|
|
5101
|
+
* caller.
|
|
5102
|
+
*/
|
|
5103
|
+
private handleListFlags;
|
|
5104
|
+
/**
|
|
5016
5105
|
* Run `run()` with the per-request identity pinned to (`userId`, `identity`),
|
|
5017
5106
|
* then restore the prior values in a `finally` (even if `run()` throws), so the
|
|
5018
5107
|
* forced identity can never leak into a later dispatch on this DO instance. The
|
|
@@ -5272,6 +5361,16 @@ declare abstract class ShardDO {
|
|
|
5272
5361
|
*/
|
|
5273
5362
|
private executeAdminSubscription;
|
|
5274
5363
|
/**
|
|
5364
|
+
* Resolve one subscription (seed or refresh) to its {@link SubscriptionOutcome}
|
|
5365
|
+
* by routing the `functionPath` to the right read path — shared by
|
|
5366
|
+
* {@link seedSubscription} and {@link refreshSubscriptions} so both branch
|
|
5367
|
+
* identically:
|
|
5368
|
+
* - `__lunora_admin__:*` → {@link executeAdminSubscription} (raw SQLite read).
|
|
5369
|
+
* - {@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`).
|
|
5370
|
+
* - everything else → {@link executeSubscription} (the user query, under the socket's own by-value identity).
|
|
5371
|
+
*/
|
|
5372
|
+
private resolveReactiveOutcome;
|
|
5373
|
+
/**
|
|
5275
5374
|
* Constant-time bearer check against `env.LUNORA_ADMIN_TOKEN`. Returns
|
|
5276
5375
|
* `false` (closed) when the token is unset so admin introspection is
|
|
5277
5376
|
* opt-in rather than exposed by default.
|
|
@@ -5303,6 +5402,17 @@ declare abstract class ShardDO {
|
|
|
5303
5402
|
*/
|
|
5304
5403
|
private flushChangedTables;
|
|
5305
5404
|
/**
|
|
5405
|
+
* Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
|
|
5406
|
+
* until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
|
|
5407
|
+
* Tables merged by a `flushChangedTables` that lands mid-pass are picked up
|
|
5408
|
+
* by the next loop iteration, so every committed write is observed by a
|
|
5409
|
+
* refresh that runs after it — bursts simply share a pass. The post-write
|
|
5410
|
+
* high-watermark and live-socket set are re-read inside each
|
|
5411
|
+
* `refreshSubscriptions` call, so a later batch always reflects the latest
|
|
5412
|
+
* committed state.
|
|
5413
|
+
*/
|
|
5414
|
+
private drainSubscriptionRefreshes;
|
|
5415
|
+
/**
|
|
5306
5416
|
* For every live subscription whose query reads one of `changed`, re-run
|
|
5307
5417
|
* the query and push a fresh `{ type: "data" }` frame when the result
|
|
5308
5418
|
* differs from the last one sent. Subscriptions with no `functionPath`
|
|
@@ -5689,4 +5799,4 @@ interface WhereSqlStrategy {
|
|
|
5689
5799
|
* `undefined` when the input imposes no constraint (empty `where`).
|
|
5690
5800
|
*/
|
|
5691
5801
|
declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
|
|
5692
|
-
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 };
|
|
5802
|
+
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, 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 };
|
package/dist/index.mjs
CHANGED
|
@@ -8,22 +8,22 @@ export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } fro
|
|
|
8
8
|
export { SCAN_DEP, createDependencyTracker, depKey } from './packem_shared/SCAN_DEP-DLJF8dsj.mjs';
|
|
9
9
|
export { renderSql } from './packem_shared/renderSql-D6eUcn2N.mjs';
|
|
10
10
|
export { FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, ensureFunctionMetricsTables, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, recordFunctionMetric } from './packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
|
|
11
|
-
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-
|
|
11
|
+
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-D_UiYJFk.mjs';
|
|
12
12
|
export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
|
|
13
13
|
export { MAIL_RETENTION, MAIL_TABLE, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail } from './packem_shared/MAIL_RETENTION-CPpgl-dX.mjs';
|
|
14
14
|
export { default as NotFoundError } from './packem_shared/NotFoundError-CMuMZt81.mjs';
|
|
15
15
|
export { armRestore, readBookmark } from './packem_shared/armRestore-BJk53Ro8.mjs';
|
|
16
16
|
export { applySelect, buildSeekWhere, decodeCursor, encodeCursor, normalizeOrderKeys, softDeleteScope } from './packem_shared/applySelect-BvZdFUBT.mjs';
|
|
17
17
|
export { R as RANK_TIEBREAK, e as encodePartitionKey, m as matchesRankStaticWhere, r as rankTableName, a as resolveRankPartition, s as sortColumnName } from './packem_shared/rank-CrkEIpF4.mjs';
|
|
18
|
-
export { ReactiveCache, reactiveCacheKey
|
|
19
|
-
export { serveRelationFanout } from './packem_shared/serveRelationFanout-
|
|
18
|
+
export { ReactiveCache, reactiveCacheKey } from './packem_shared/ReactiveCache-1hDydFyv.mjs';
|
|
19
|
+
export { serveRelationFanout } from './packem_shared/serveRelationFanout-C6lDaesn.mjs';
|
|
20
20
|
export { DEFAULT_MAX_RELATION_KEYS, assertFlatPredicate, containsRelationPredicate, isRelationPredicate, resolveRelationPredicates } from './packem_shared/DEFAULT_MAX_RELATION_KEYS-Dou2PWdO.mjs';
|
|
21
21
|
export { applyOnDelete, resolveWith, runRowValidators } from './packem_shared/applyOnDelete-sA7o1CqD.mjs';
|
|
22
22
|
export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_shared/RLS_UNWRAP_SYMBOL-EtGQdC9d.mjs';
|
|
23
23
|
export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/buildFtsMatch-BLEMawrp.mjs';
|
|
24
24
|
export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
|
|
25
25
|
export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-ilPZsVwu.mjs';
|
|
26
|
-
export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO
|
|
26
|
+
export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-BCz6GIDw.mjs';
|
|
27
27
|
export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-BsAbi5Mn.mjs';
|
|
28
28
|
export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/MAX_SQL_ROWS-dDcFE1YZ.mjs';
|
|
29
29
|
export { createSystemReader } from './packem_shared/createSystemReader-8CzSZP9V.mjs';
|
|
@@ -33,3 +33,5 @@ export { compileWhereSql } from './packem_shared/compileWhereSql-CXrhFA3G.mjs';
|
|
|
33
33
|
export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-Ctdmxmrv.mjs';
|
|
34
34
|
export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-BbVPvciS.mjs';
|
|
35
35
|
export { runShardMigrations } from './packem_shared/runShardMigrations-PabobOjF.mjs';
|
|
36
|
+
export { stableStringify } from './packem_shared/stableStringify-CyHKJXre.mjs';
|
|
37
|
+
export { subscriptionListDeltas } from './packem_shared/subscriptionListDeltas-ce84gpwL.mjs';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const ADMIN_FUNCTION_PREFIX = "__lunora_admin__:";
|
|
2
2
|
const RELATION_FUNCTION_PREFIX = "__lunora_relation__:";
|
|
3
|
+
const FLAGS_FUNCTION_PREFIX = "__lunora_flags__:";
|
|
3
4
|
const ADMIN_FUNCTIONS = {
|
|
4
5
|
applyCdc: "__lunora_admin__:applyCdc",
|
|
5
6
|
cdcSync: "__lunora_admin__:cdcSync",
|
|
@@ -27,6 +28,7 @@ const ADMIN_FUNCTIONS = {
|
|
|
27
28
|
// eslint-disable-next-line no-secrets/no-secrets -- reserved admin RPC path constant, not a credential
|
|
28
29
|
getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus",
|
|
29
30
|
importShard: "__lunora_admin__:importShard",
|
|
31
|
+
listFlags: "__lunora_admin__:listFlags",
|
|
30
32
|
listQueues: "__lunora_admin__:listQueues",
|
|
31
33
|
listTables: "__lunora_admin__:listTables",
|
|
32
34
|
listWorkflows: "__lunora_admin__:listWorkflows",
|
|
@@ -311,4 +313,4 @@ const summarizeSubscriptions = (attachments) => {
|
|
|
311
313
|
return { connections, totalConnections: connections.length, totalSubscriptions };
|
|
312
314
|
};
|
|
313
315
|
|
|
314
|
-
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, MAX_PAGE_SIZE, RELATION_FUNCTION_PREFIX, facetColumn, findStorageReferences, listTables, readTablePage, selectMatchingIds, summarizeSubscriptions };
|
|
316
|
+
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, MAX_PAGE_SIZE, RELATION_FUNCTION_PREFIX, facetColumn, findStorageReferences, listTables, readTablePage, selectMatchingIds, summarizeSubscriptions };
|
|
@@ -4,14 +4,15 @@ import { recordAuthEvent, readAuthMetrics } from './AUTH_METRICS_BUCKETS_TABLE-C
|
|
|
4
4
|
import { DATA_MIGRATION_STATE_TABLE, readMigrationStatus } from './DATA_MIGRATION_STATE_TABLE-PTtTiQ7U.mjs';
|
|
5
5
|
import { SCAN_DEP, createDependencyTracker, tableFromDepKey } from './SCAN_DEP-DLJF8dsj.mjs';
|
|
6
6
|
import { readFunctionMetricsTotals, readFunctionMetricIndexHits, recordFunctionMetric, mergeScanAttribution, readFunctionMetrics, readFunctionMetricBuckets } from './FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
|
|
7
|
-
import { ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, readTablePage, facetColumn, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-
|
|
7
|
+
import { ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, readTablePage, facetColumn, FLAGS_FUNCTION_PREFIX, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-D_UiYJFk.mjs';
|
|
8
8
|
import { LogBuffer } from './LogBuffer-B_Ezju_N.mjs';
|
|
9
9
|
import { recordCapturedMail, clearCapturedMail, readCapturedMail, MAIL_TABLE } from './MAIL_RETENTION-CPpgl-dX.mjs';
|
|
10
10
|
import { readBookmark, armRestore } from './armRestore-BJk53Ro8.mjs';
|
|
11
|
-
import { ReactiveCache, reactiveCacheKey } from './ReactiveCache-
|
|
11
|
+
import { ReactiveCache, reactiveCacheKey } from './ReactiveCache-1hDydFyv.mjs';
|
|
12
12
|
import { redact, standardRules } from '@visulima/redact';
|
|
13
13
|
import { i as isDevEnvironment, c as buildSettings, b as buildSecurityAudit } from './security-audit-CucgBice.mjs';
|
|
14
14
|
import { runReadonlySql } from './MAX_SQL_ROWS-dDcFE1YZ.mjs';
|
|
15
|
+
import { trySendFrame, subscriptionListDeltas, sendDeltaFrames } from './subscriptionListDeltas-ce84gpwL.mjs';
|
|
15
16
|
import { ConflictError } from './ConflictError-C0STs6bU.mjs';
|
|
16
17
|
import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-Ctdmxmrv.mjs';
|
|
17
18
|
import { r as readIdempotent, w as writeIdempotent, t as trimIdempotent } from './ctx-db-idempotency-DkC9rP91.mjs';
|
|
@@ -413,97 +414,7 @@ const findDanglingReferences = (sql, storageColumns, liveKeys) => {
|
|
|
413
414
|
|
|
414
415
|
const WS_KEEPALIVE_PING = "lunora-ping";
|
|
415
416
|
const WS_KEEPALIVE_PONG = "lunora-pong";
|
|
416
|
-
const
|
|
417
|
-
const DELTA_FALLBACK_TABLE = "__lunora__";
|
|
418
|
-
const readRowId = (row) => {
|
|
419
|
-
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
420
|
-
return void 0;
|
|
421
|
-
}
|
|
422
|
-
const id = row[ROW_ID_FIELD];
|
|
423
|
-
return typeof id === "string" ? id : void 0;
|
|
424
|
-
};
|
|
425
|
-
const indexRowsById = (rows) => {
|
|
426
|
-
const byId = /* @__PURE__ */ new Map();
|
|
427
|
-
const order = [];
|
|
428
|
-
for (const row of rows) {
|
|
429
|
-
const id = readRowId(row);
|
|
430
|
-
if (id === void 0 || byId.has(id)) {
|
|
431
|
-
return void 0;
|
|
432
|
-
}
|
|
433
|
-
byId.set(id, row);
|
|
434
|
-
order.push(id);
|
|
435
|
-
}
|
|
436
|
-
return { byId, order };
|
|
437
|
-
};
|
|
438
|
-
const survivorsKeepOrder = (previous, next) => {
|
|
439
|
-
const survivingPrevious = previous.order.filter((id) => next.byId.has(id));
|
|
440
|
-
const survivingNext = next.order.filter((id) => previous.byId.has(id));
|
|
441
|
-
if (survivingPrevious.length !== survivingNext.length) {
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
444
|
-
return survivingPrevious.every((id, index) => survivingNext[index] === id);
|
|
445
|
-
};
|
|
446
|
-
const collectDeleteDeltas = (previous, next, deltaTable, tableJson) => {
|
|
447
|
-
const out = [];
|
|
448
|
-
for (const id of previous.order) {
|
|
449
|
-
if (!next.byId.has(id)) {
|
|
450
|
-
out.push({
|
|
451
|
-
delta: { key: id, op: "delete", table: deltaTable },
|
|
452
|
-
frame: `{"key":${JSON.stringify(id)},"op":"delete","table":${tableJson}}`
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return out;
|
|
457
|
-
};
|
|
458
|
-
const collectUpsertDeltas = (previous, next, deltaTable, tableJson) => {
|
|
459
|
-
const out = [];
|
|
460
|
-
for (const id of next.order) {
|
|
461
|
-
const nextRow = next.byId.get(id);
|
|
462
|
-
const previousRow = previous.byId.get(id);
|
|
463
|
-
const nextFingerprint = JSON.stringify(nextRow);
|
|
464
|
-
const previousFingerprint = previousRow === void 0 ? void 0 : JSON.stringify(previousRow);
|
|
465
|
-
if (previousFingerprint === nextFingerprint) {
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
const op = previousFingerprint === void 0 ? "insert" : "update";
|
|
469
|
-
out.push({
|
|
470
|
-
delta: { key: id, op, row: nextRow, table: deltaTable },
|
|
471
|
-
frame: `{"key":${JSON.stringify(id)},"op":"${op}","row":${nextFingerprint},"table":${tableJson}}`
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
return out;
|
|
475
|
-
};
|
|
476
|
-
const subscriptionListDeltas = (previousJson, nextResult, table, frames) => {
|
|
477
|
-
let parsed;
|
|
478
|
-
try {
|
|
479
|
-
parsed = JSON.parse(previousJson);
|
|
480
|
-
} catch {
|
|
481
|
-
return void 0;
|
|
482
|
-
}
|
|
483
|
-
if (!Array.isArray(parsed) || !Array.isArray(nextResult)) {
|
|
484
|
-
return void 0;
|
|
485
|
-
}
|
|
486
|
-
const previous = indexRowsById(parsed);
|
|
487
|
-
const next = indexRowsById(nextResult);
|
|
488
|
-
if (previous === void 0 || next === void 0) {
|
|
489
|
-
return void 0;
|
|
490
|
-
}
|
|
491
|
-
if (!survivorsKeepOrder(previous, next)) {
|
|
492
|
-
return void 0;
|
|
493
|
-
}
|
|
494
|
-
const deltaTable = table === "" ? DELTA_FALLBACK_TABLE : table;
|
|
495
|
-
const tableJson = JSON.stringify(deltaTable);
|
|
496
|
-
const framed = [...collectDeleteDeltas(previous, next, deltaTable, tableJson), ...collectUpsertDeltas(previous, next, deltaTable, tableJson)];
|
|
497
|
-
if (framed.length > next.order.length) {
|
|
498
|
-
return void 0;
|
|
499
|
-
}
|
|
500
|
-
if (frames !== void 0) {
|
|
501
|
-
for (const { frame } of framed) {
|
|
502
|
-
frames.push(frame);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
return framed.map(({ delta }) => delta);
|
|
506
|
-
};
|
|
417
|
+
const UNDELIVERED_BASELINE = "<undelivered>";
|
|
507
418
|
const ROOT_DO_SIZE_WARN_BYTES = 1073741824;
|
|
508
419
|
const CDC_RESUME_SCAN_LIMIT = 1e4;
|
|
509
420
|
const IDEMPOTENCY_RETENTION_MS = 864e5;
|
|
@@ -1133,6 +1044,18 @@ class ShardDO {
|
|
|
1133
1044
|
* the common read-only path allocates nothing.
|
|
1134
1045
|
*/
|
|
1135
1046
|
pendingChangedTables = void 0;
|
|
1047
|
+
/**
|
|
1048
|
+
* Coalesced set of tables awaiting a subscription-refresh pass, merged
|
|
1049
|
+
* across every {@link ShardDO.flushChangedTables} call that lands while a
|
|
1050
|
+
* pass is already draining. The single drain loop
|
|
1051
|
+
* ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
|
|
1052
|
+
* writes to the same table therefore collapses into one (or two) refresh
|
|
1053
|
+
* passes instead of N, so each affected subscription's handler re-runs once
|
|
1054
|
+
* per burst rather than once per write. `undefined` when nothing is pending.
|
|
1055
|
+
*/
|
|
1056
|
+
pendingRefreshTables = void 0;
|
|
1057
|
+
/** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
|
|
1058
|
+
refreshInFlight = false;
|
|
1136
1059
|
/**
|
|
1137
1060
|
* Last pushed result per `(socket, subId)`, keyed by socket. Lets
|
|
1138
1061
|
* `refreshSubscriptions` skip re-running queries whose tables were
|
|
@@ -1853,7 +1776,33 @@ class ShardDO {
|
|
|
1853
1776
|
*/
|
|
1854
1777
|
// eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass overrides this with the statically-discovered feature flags
|
|
1855
1778
|
studioFeatures() {
|
|
1856
|
-
return { mail: false, payments: false, queues: false, scheduler: false, storage: false, vectors: false, workflows: false };
|
|
1779
|
+
return { flags: false, mail: false, payments: false, queues: false, scheduler: false, storage: false, vectors: false, workflows: false };
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Evaluate every statically-discovered feature flag under `context` for the
|
|
1783
|
+
* studio's read-only Flags page (`__lunora_admin__:listFlags`). The flag keys
|
|
1784
|
+
* + value types are discovered by `@lunora/codegen` from the app's
|
|
1785
|
+
* `ctx.flags.<type>("key", …)` reads and evaluated through the configured
|
|
1786
|
+
* `@lunora/flags` provider — work only the codegen subclass can do, so it
|
|
1787
|
+
* overrides this. The base class wires no provider and reports
|
|
1788
|
+
* `configured: false` with zero flags (an un-generated `ShardDO` has none).
|
|
1789
|
+
*/
|
|
1790
|
+
// eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass overrides this with live OpenFeature evaluation over the discovered flag keys
|
|
1791
|
+
evaluateFlags(_context) {
|
|
1792
|
+
return Promise.resolve({ configured: false, flags: [] });
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Serve one reserved {@link FLAGS_FUNCTION_PREFIX} live flag read for the
|
|
1796
|
+
* React client's `useFlag`/`useFlags`. `functionPath` carries the flag key +
|
|
1797
|
+
* type and `args` the per-subscriber targeting context; the codegen subclass
|
|
1798
|
+
* overrides this to evaluate the flag through the app's `@lunora/flags`
|
|
1799
|
+
* provider under `identity` and return the resolved value. The base class
|
|
1800
|
+
* wires no provider, so it returns `null` — `resolveReactiveOutcome` reads
|
|
1801
|
+
* `null` as "nothing to deliver" and the subscriber keeps its default.
|
|
1802
|
+
*/
|
|
1803
|
+
// eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass overrides this to evaluate the flag through the configured provider
|
|
1804
|
+
runFlagSubscriptionRead(_functionPath, _arguments, _identity) {
|
|
1805
|
+
return Promise.resolve(null);
|
|
1857
1806
|
}
|
|
1858
1807
|
/**
|
|
1859
1808
|
* The Cloudflare Queues declared by this app, surfaced via
|
|
@@ -2249,10 +2198,7 @@ class ShardDO {
|
|
|
2249
2198
|
if (!this.matchesSubscription(query, delta)) {
|
|
2250
2199
|
continue;
|
|
2251
2200
|
}
|
|
2252
|
-
|
|
2253
|
-
ws.send(`{"type":"delta","id":${JSON.stringify(subId)},"delta":${deltaJson}}`);
|
|
2254
|
-
} catch {
|
|
2255
|
-
}
|
|
2201
|
+
trySendFrame(ws, `{"type":"delta","id":${JSON.stringify(subId)},"delta":${deltaJson}}`);
|
|
2256
2202
|
}
|
|
2257
2203
|
}
|
|
2258
2204
|
}
|
|
@@ -2808,6 +2754,9 @@ class ShardDO {
|
|
|
2808
2754
|
if (functionPath === ADMIN_FUNCTIONS.getWorkflowInstanceStatus) {
|
|
2809
2755
|
return this.handleGetWorkflowInstanceStatus(args);
|
|
2810
2756
|
}
|
|
2757
|
+
if (functionPath === ADMIN_FUNCTIONS.listFlags) {
|
|
2758
|
+
return this.handleListFlags(args);
|
|
2759
|
+
}
|
|
2811
2760
|
return this.handlePitrAdminOp(functionPath, args);
|
|
2812
2761
|
}
|
|
2813
2762
|
/**
|
|
@@ -2928,6 +2877,21 @@ class ShardDO {
|
|
|
2928
2877
|
return jsonResponse({ result }, 200);
|
|
2929
2878
|
}
|
|
2930
2879
|
/* eslint-enable no-secrets/no-secrets */
|
|
2880
|
+
/**
|
|
2881
|
+
* Serve `__lunora_admin__:listFlags` — the studio's read-only Flags page.
|
|
2882
|
+
* Evaluates every statically-discovered feature flag under an optional
|
|
2883
|
+
* `args.context` targeting context (the studio's editable context editor)
|
|
2884
|
+
* via the {@link evaluateFlags} hook, which the codegen subclass overrides
|
|
2885
|
+
* with live OpenFeature evaluation. Read-only: a flag lookup mutates no shard
|
|
2886
|
+
* state, so nothing is flushed or audited. Admin-gated by `handleAdminRpc`'s
|
|
2887
|
+
* caller.
|
|
2888
|
+
*/
|
|
2889
|
+
async handleListFlags(args) {
|
|
2890
|
+
const rawContext = args.context;
|
|
2891
|
+
const context = typeof rawContext === "object" && rawContext !== null && !Array.isArray(rawContext) ? rawContext : void 0;
|
|
2892
|
+
const result = await this.evaluateFlags(context);
|
|
2893
|
+
return jsonResponse({ result }, 200);
|
|
2894
|
+
}
|
|
2931
2895
|
/**
|
|
2932
2896
|
* Run `run()` with the per-request identity pinned to (`userId`, `identity`),
|
|
2933
2897
|
* then restore the prior values in a `finally` (even if `run()` throws), so the
|
|
@@ -3469,6 +3433,25 @@ class ShardDO {
|
|
|
3469
3433
|
const read = this.readAdminOp(functionPath, args);
|
|
3470
3434
|
return read ? { result: read.result, tables: read.tables } : null;
|
|
3471
3435
|
}
|
|
3436
|
+
/**
|
|
3437
|
+
* Resolve one subscription (seed or refresh) to its {@link SubscriptionOutcome}
|
|
3438
|
+
* by routing the `functionPath` to the right read path — shared by
|
|
3439
|
+
* {@link seedSubscription} and {@link refreshSubscriptions} so both branch
|
|
3440
|
+
* identically:
|
|
3441
|
+
* - `__lunora_admin__:*` → {@link executeAdminSubscription} (raw SQLite read).
|
|
3442
|
+
* - {@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`).
|
|
3443
|
+
* - everything else → {@link executeSubscription} (the user query, under the socket's own by-value identity).
|
|
3444
|
+
*/
|
|
3445
|
+
async resolveReactiveOutcome(functionPath, args, isAdmin, identity) {
|
|
3446
|
+
if (isAdmin) {
|
|
3447
|
+
return this.executeAdminSubscription(functionPath, args);
|
|
3448
|
+
}
|
|
3449
|
+
if (functionPath.startsWith(FLAGS_FUNCTION_PREFIX)) {
|
|
3450
|
+
const result = await this.runFlagSubscriptionRead(functionPath, args, identity);
|
|
3451
|
+
return result === null ? null : { result, tables: /* @__PURE__ */ new Set([ADMIN_WILDCARD]) };
|
|
3452
|
+
}
|
|
3453
|
+
return this.executeSubscription(functionPath, args, identity);
|
|
3454
|
+
}
|
|
3472
3455
|
/**
|
|
3473
3456
|
* Constant-time bearer check against `env.LUNORA_ADMIN_TOKEN`. Returns
|
|
3474
3457
|
* `false` (closed) when the token is unset so admin introspection is
|
|
@@ -3567,11 +3550,47 @@ class ShardDO {
|
|
|
3567
3550
|
if (!changed || changed.size === 0) {
|
|
3568
3551
|
return;
|
|
3569
3552
|
}
|
|
3553
|
+
if (this.pendingRefreshTables) {
|
|
3554
|
+
for (const table of changed) {
|
|
3555
|
+
this.pendingRefreshTables.add(table);
|
|
3556
|
+
}
|
|
3557
|
+
} else {
|
|
3558
|
+
this.pendingRefreshTables = changed;
|
|
3559
|
+
}
|
|
3560
|
+
if (this.refreshInFlight) {
|
|
3561
|
+
return;
|
|
3562
|
+
}
|
|
3570
3563
|
if (typeof this.state.waitUntil === "function") {
|
|
3571
|
-
this.state.waitUntil(this.
|
|
3564
|
+
this.state.waitUntil(this.drainSubscriptionRefreshes());
|
|
3572
3565
|
return;
|
|
3573
3566
|
}
|
|
3574
|
-
await this.
|
|
3567
|
+
await this.drainSubscriptionRefreshes();
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
|
|
3571
|
+
* until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
|
|
3572
|
+
* Tables merged by a `flushChangedTables` that lands mid-pass are picked up
|
|
3573
|
+
* by the next loop iteration, so every committed write is observed by a
|
|
3574
|
+
* refresh that runs after it — bursts simply share a pass. The post-write
|
|
3575
|
+
* high-watermark and live-socket set are re-read inside each
|
|
3576
|
+
* `refreshSubscriptions` call, so a later batch always reflects the latest
|
|
3577
|
+
* committed state.
|
|
3578
|
+
*/
|
|
3579
|
+
async drainSubscriptionRefreshes() {
|
|
3580
|
+
if (this.refreshInFlight) {
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
this.refreshInFlight = true;
|
|
3584
|
+
try {
|
|
3585
|
+
let batch = this.pendingRefreshTables;
|
|
3586
|
+
while (batch && batch.size > 0) {
|
|
3587
|
+
this.pendingRefreshTables = void 0;
|
|
3588
|
+
await this.refreshSubscriptions(batch);
|
|
3589
|
+
batch = this.pendingRefreshTables;
|
|
3590
|
+
}
|
|
3591
|
+
} finally {
|
|
3592
|
+
this.refreshInFlight = false;
|
|
3593
|
+
}
|
|
3575
3594
|
}
|
|
3576
3595
|
/**
|
|
3577
3596
|
* For every live subscription whose query reads one of `changed`, re-run
|
|
@@ -3649,15 +3668,10 @@ class ShardDO {
|
|
|
3649
3668
|
continue;
|
|
3650
3669
|
}
|
|
3651
3670
|
try {
|
|
3652
|
-
const outcome =
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
// per-request identity fields. Without it an `rls()` / `ctx.auth`
|
|
3657
|
-
// scoped live query would evaluate anonymous and return zero rows.
|
|
3658
|
-
// eslint-disable-next-line no-await-in-loop -- subscriptions on a socket re-run sequentially; each shares the single SQLite handle
|
|
3659
|
-
await this.executeSubscription(functionPath, query.args ?? {}, { identity: attachment.identity, userId: attachment.userId })
|
|
3660
|
-
);
|
|
3671
|
+
const outcome = await this.resolveReactiveOutcome(functionPath, query.args ?? {}, isAdmin, {
|
|
3672
|
+
identity: attachment.identity,
|
|
3673
|
+
userId: attachment.userId
|
|
3674
|
+
});
|
|
3661
3675
|
if (!outcome) {
|
|
3662
3676
|
continue;
|
|
3663
3677
|
}
|
|
@@ -3699,7 +3713,10 @@ class ShardDO {
|
|
|
3699
3713
|
async seedSubscription(ws, subId, query, functionPath, isAdmin) {
|
|
3700
3714
|
const seedArgs = query.args ?? {};
|
|
3701
3715
|
const attachment = this.readAttachment(ws);
|
|
3702
|
-
const outcome =
|
|
3716
|
+
const outcome = await this.resolveReactiveOutcome(functionPath, seedArgs, isAdmin, {
|
|
3717
|
+
identity: attachment.identity,
|
|
3718
|
+
userId: attachment.userId
|
|
3719
|
+
});
|
|
3703
3720
|
if (!outcome) {
|
|
3704
3721
|
return;
|
|
3705
3722
|
}
|
|
@@ -3763,21 +3780,8 @@ class ShardDO {
|
|
|
3763
3780
|
}
|
|
3764
3781
|
const deltaFrames = [];
|
|
3765
3782
|
const deltas = existing === void 0 ? void 0 : subscriptionListDeltas(existing.lastJson, outcome.result, outcome.tables.values().next().value ?? "", deltaFrames);
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
const idJson = JSON.stringify(subId);
|
|
3769
|
-
for (const deltaBody of deltaFrames) {
|
|
3770
|
-
try {
|
|
3771
|
-
ws.send(`{"type":"delta","id":${idJson},"delta":${deltaBody}${cursorSuffix}}`);
|
|
3772
|
-
} catch {
|
|
3773
|
-
}
|
|
3774
|
-
}
|
|
3775
|
-
return;
|
|
3776
|
-
}
|
|
3777
|
-
try {
|
|
3778
|
-
ws.send(`{"type":"data","id":${JSON.stringify(subId)},"data":${json}${cursorSuffix}}`);
|
|
3779
|
-
} catch {
|
|
3780
|
-
}
|
|
3783
|
+
const delivered = deltas === void 0 ? trySendFrame(ws, `{"type":"data","id":${JSON.stringify(subId)},"data":${json}${cursorSuffix}}`) : sendDeltaFrames(ws, subId, deltaFrames, cursorSuffix);
|
|
3784
|
+
memos.set(subId, { lastJson: delivered ? json : existing?.lastJson ?? UNDELIVERED_BASELINE, tables: outcome.tables });
|
|
3781
3785
|
}
|
|
3782
3786
|
/**
|
|
3783
3787
|
* Gate the upgrade request against two complementary controls:
|
|
@@ -3999,10 +4003,7 @@ class ShardDO {
|
|
|
3999
4003
|
if (ws === sender || this.readAttachment(ws).whispers?.includes(topic) !== true) {
|
|
4000
4004
|
continue;
|
|
4001
4005
|
}
|
|
4002
|
-
|
|
4003
|
-
ws.send(frame);
|
|
4004
|
-
} catch {
|
|
4005
|
-
}
|
|
4006
|
+
trySendFrame(ws, frame);
|
|
4006
4007
|
}
|
|
4007
4008
|
}
|
|
4008
4009
|
// eslint-disable-next-line class-methods-use-this -- cohesive DO instance method grouped with the hibernation/attachment helpers; reads only the socket
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
+
import { stableStringify } from './stableStringify-CyHKJXre.mjs';
|
|
1
2
|
import { depKey, SCAN_DEP } from './SCAN_DEP-DLJF8dsj.mjs';
|
|
2
3
|
|
|
3
|
-
const compareKeys = (a, b) => {
|
|
4
|
-
if (a < b) {
|
|
5
|
-
return -1;
|
|
6
|
-
}
|
|
7
|
-
return a > b ? 1 : 0;
|
|
8
|
-
};
|
|
9
4
|
const DEFAULT_MAX_ENTRIES = 1e3;
|
|
10
5
|
const DEFAULT_MAX_BYTES = 4 * 1024 * 1024;
|
|
11
6
|
const estimateBytes = (value) => {
|
|
@@ -232,28 +227,6 @@ class ReactiveCache {
|
|
|
232
227
|
}
|
|
233
228
|
}
|
|
234
229
|
}
|
|
235
|
-
const stableStringify = (value) => {
|
|
236
|
-
if (value === void 0) {
|
|
237
|
-
return "null";
|
|
238
|
-
}
|
|
239
|
-
if (value === null || typeof value !== "object") {
|
|
240
|
-
return JSON.stringify(value);
|
|
241
|
-
}
|
|
242
|
-
if (Array.isArray(value)) {
|
|
243
|
-
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
244
|
-
}
|
|
245
|
-
const record = value;
|
|
246
|
-
const keys = Object.keys(record).toSorted(compareKeys);
|
|
247
|
-
const parts = [];
|
|
248
|
-
for (const key of keys) {
|
|
249
|
-
const raw = record[key];
|
|
250
|
-
if (raw === void 0) {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
parts.push(`${JSON.stringify(key)}:${stableStringify(raw)}`);
|
|
254
|
-
}
|
|
255
|
-
return `{${parts.join(",")}}`;
|
|
256
|
-
};
|
|
257
230
|
const reactiveCacheKey = (functionPath, args, identity) => `${identity ?? "\0anon"}\0${functionPath}:${stableStringify(args)}`;
|
|
258
231
|
|
|
259
232
|
export { ReactiveCache, reactiveCacheKey, stableStringify };
|
package/dist/packem_shared/{serveRelationFanout-oxaM6_WL.mjs → serveRelationFanout-C6lDaesn.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-
|
|
1
|
+
import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-D_UiYJFk.mjs';
|
|
2
2
|
|
|
3
3
|
const serveRelationFanout = async (schema, database, functionPath, args) => {
|
|
4
4
|
const table = typeof args["table"] === "string" ? args["table"] : "";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const compareKeys = (a, b) => {
|
|
2
|
+
if (a < b) {
|
|
3
|
+
return -1;
|
|
4
|
+
}
|
|
5
|
+
return a > b ? 1 : 0;
|
|
6
|
+
};
|
|
7
|
+
const stableStringify = (value) => {
|
|
8
|
+
if (value === void 0) {
|
|
9
|
+
return "null";
|
|
10
|
+
}
|
|
11
|
+
if (value === null || typeof value !== "object") {
|
|
12
|
+
return JSON.stringify(value);
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
16
|
+
}
|
|
17
|
+
const record = value;
|
|
18
|
+
const keys = Object.keys(record).toSorted(compareKeys);
|
|
19
|
+
const parts = [];
|
|
20
|
+
for (const key of keys) {
|
|
21
|
+
const raw = record[key];
|
|
22
|
+
if (raw === void 0) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
parts.push(`${JSON.stringify(key)}:${stableStringify(raw)}`);
|
|
26
|
+
}
|
|
27
|
+
return `{${parts.join(",")}}`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { stableStringify };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const ROW_ID_FIELD = "_id";
|
|
2
|
+
const DELTA_FALLBACK_TABLE = "__lunora__";
|
|
3
|
+
const readRowId = (row) => {
|
|
4
|
+
if (typeof row !== "object" || row === null || Array.isArray(row)) {
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
const id = row[ROW_ID_FIELD];
|
|
8
|
+
return typeof id === "string" ? id : void 0;
|
|
9
|
+
};
|
|
10
|
+
const indexRowsById = (rows) => {
|
|
11
|
+
const byId = /* @__PURE__ */ new Map();
|
|
12
|
+
const order = [];
|
|
13
|
+
for (const row of rows) {
|
|
14
|
+
const id = readRowId(row);
|
|
15
|
+
if (id === void 0 || byId.has(id)) {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
byId.set(id, row);
|
|
19
|
+
order.push(id);
|
|
20
|
+
}
|
|
21
|
+
return { byId, order };
|
|
22
|
+
};
|
|
23
|
+
const survivorsKeepOrder = (previous, next) => {
|
|
24
|
+
const survivingPrevious = previous.order.filter((id) => next.byId.has(id));
|
|
25
|
+
const survivingNext = next.order.filter((id) => previous.byId.has(id));
|
|
26
|
+
if (survivingPrevious.length !== survivingNext.length) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return survivingPrevious.every((id, index) => survivingNext[index] === id);
|
|
30
|
+
};
|
|
31
|
+
const collectDeleteDeltas = (previous, next, deltaTable, tableJson) => {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const id of previous.order) {
|
|
34
|
+
if (!next.byId.has(id)) {
|
|
35
|
+
out.push({
|
|
36
|
+
delta: { key: id, op: "delete", table: deltaTable },
|
|
37
|
+
frame: `{"key":${JSON.stringify(id)},"op":"delete","table":${tableJson}}`
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
};
|
|
43
|
+
const collectUpsertDeltas = (previous, next, deltaTable, tableJson) => {
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const id of next.order) {
|
|
46
|
+
const nextRow = next.byId.get(id);
|
|
47
|
+
const previousRow = previous.byId.get(id);
|
|
48
|
+
const nextFingerprint = JSON.stringify(nextRow);
|
|
49
|
+
const previousFingerprint = previousRow === void 0 ? void 0 : JSON.stringify(previousRow);
|
|
50
|
+
if (previousFingerprint === nextFingerprint) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const op = previousFingerprint === void 0 ? "insert" : "update";
|
|
54
|
+
out.push({
|
|
55
|
+
delta: { key: id, op, row: nextRow, table: deltaTable },
|
|
56
|
+
frame: `{"key":${JSON.stringify(id)},"op":"${op}","row":${nextFingerprint},"table":${tableJson}}`
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
};
|
|
61
|
+
const subscriptionListDeltas = (previousJson, nextResult, table, frames) => {
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(previousJson);
|
|
65
|
+
} catch {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(parsed) || !Array.isArray(nextResult)) {
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
const previous = indexRowsById(parsed);
|
|
72
|
+
const next = indexRowsById(nextResult);
|
|
73
|
+
if (previous === void 0 || next === void 0) {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
if (!survivorsKeepOrder(previous, next)) {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
const deltaTable = table === "" ? DELTA_FALLBACK_TABLE : table;
|
|
80
|
+
const tableJson = JSON.stringify(deltaTable);
|
|
81
|
+
const framed = [...collectDeleteDeltas(previous, next, deltaTable, tableJson), ...collectUpsertDeltas(previous, next, deltaTable, tableJson)];
|
|
82
|
+
if (framed.length > next.order.length) {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
if (frames !== void 0) {
|
|
86
|
+
for (const { frame } of framed) {
|
|
87
|
+
frames.push(frame);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return framed.map(({ delta }) => delta);
|
|
91
|
+
};
|
|
92
|
+
const trySendFrame = (ws, frame) => {
|
|
93
|
+
try {
|
|
94
|
+
ws.send(frame);
|
|
95
|
+
return true;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const sendDeltaFrames = (ws, subId, deltaFrames, cursorSuffix) => {
|
|
101
|
+
const idJson = JSON.stringify(subId);
|
|
102
|
+
let delivered = true;
|
|
103
|
+
for (const deltaBody of deltaFrames) {
|
|
104
|
+
if (!trySendFrame(ws, `{"type":"delta","id":${idJson},"delta":${deltaBody}${cursorSuffix}}`)) {
|
|
105
|
+
delivered = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return delivered;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export { sendDeltaFrames, subscriptionListDeltas, trySendFrame };
|