@lunora/client 1.0.0-alpha.34 → 1.0.0-alpha.36
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/auth/index.d.mts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/index.d.mts +130 -8
- package/dist/index.d.ts +130 -8
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/LunoraClient-NUIJBf6Z.mjs +1 -0
- package/dist/packem_shared/OfflineQueue-y4mymc5n.mjs +1 -0
- package/dist/packem_shared/TabCoordinator-CMFYQCL-.mjs +1 -0
- package/dist/packem_shared/anyApi-BD4dgvDY.mjs +1 -0
- package/dist/packem_shared/{createServerClient-DX7Dk2w2.mjs → createServerClient-Bc6O-zYd.mjs} +1 -1
- package/dist/packem_shared/{lunora-client.d-DLcz4GRz.d.mts → lunora-client.d-CQ4K1Ls8.d.mts} +127 -6
- package/dist/packem_shared/{lunora-client.d-DLcz4GRz.d.ts → lunora-client.d-CQ4K1Ls8.d.ts} +127 -6
- package/dist/packem_shared/offline-queue-BeWay5u5.mjs +1 -0
- package/dist/packem_shared/{preload.d-C1M_N5mE.d.mts → preload.d-CrqgaKH9.d.mts} +1 -1
- package/dist/packem_shared/{preload.d-hkFC0Uno.d.ts → preload.d-DZf_x6mY.d.ts} +1 -1
- package/dist/query/index.d.mts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/ssr/index.d.mts +3 -3
- package/dist/ssr/index.d.ts +3 -3
- package/dist/ssr/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/LunoraClient-DRo2LNe9.mjs +0 -1
- package/dist/packem_shared/OfflineQueue-l0_y9TmK.mjs +0 -1
- package/dist/packem_shared/TabCoordinator-ijLic02x.mjs +0 -1
- package/dist/packem_shared/offline-queue-b-v6bWWd.mjs +0 -1
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-CQ4K1Ls8.mjs";
|
|
2
2
|
import '@lunora/runtime';
|
|
3
3
|
interface IdentityStore {
|
|
4
4
|
/** Read the current resolved user, or `null` when signed out / not yet resolved. */
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-CQ4K1Ls8.js";
|
|
2
2
|
import '@lunora/runtime';
|
|
3
3
|
interface IdentityStore {
|
|
4
4
|
/** Read the current resolved user, or `null` when signed out / not yet resolved. */
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { c as PersistenceAdapter, B as BookmarkStorage, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, g as ReconnectOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-
|
|
2
|
-
export { type h as BatchSlot,
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-
|
|
1
|
+
import { c as PersistenceAdapter, B as BookmarkStorage, C as ConnectionStatus, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, g as ReconnectOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-CQ4K1Ls8.mjs";
|
|
2
|
+
export { type h as BatchSlot, i as CONFLICT_ERROR_CODE, type j as CachedQuery, type k as ClientDebugShard, type l as ClientDebugSnapshot, type m as ClientDebugSubscription, type n as ClientMessage, type o as ClientQueryRef, type p as ClientShapeSubscribeMessage, type q as ClientShapeUnsubscribeMessage, D as DEFAULT_MAX_BUFFER, type r as FunctionArgumentDescriptor, type s as FunctionDescriptor, type G as GlobalFacetResult, type t as GlobalFacetValue, type u as GlobalFilterClause, type v as GlobalTableInfo, type w as GlobalTablePage, type x as HttpStreamCallArgs, type y as LunoraClientError, type z as LunoraClientOptions, type E as LunoraErrorCode, type I as MutationSettledEvent, type J as OptimisticLocalStore, type K as OptimisticUpdate, type N as OutboxMutation, type T as OutboxSink, type V as PersistedMutation, type P as Preloaded, type W as RowOp, type X as RpcEnvelope, type Y as RpcResponseBody, type Z as ScheduleRecord, type _ as SchedulerPoolStatus, type $ as SchedulerStatus, type a0 as ServerMessage, type a1 as ServerPokeEndMessage, type a2 as ServerPokePartMessage, type a3 as ServerPokeStartMessage, type a4 as ShardTrafficEntry, type a5 as ShardTrafficResult, type a6 as StorageListPage, type a7 as StorageObject, type a8 as StreamHandle, type a9 as SubscriptionCallback, type b as SubscriptionErrorCallback, aa as SubscriptionRegistry, type ab as SubscriptionState, type ac as SyncWatermark, type a as Unsubscribe, type U as User, type ad as WorkflowInstanceAction, type ae as WorkflowInstanceDetail, type af as WorkflowInstancePage, type ag as WorkflowInstanceStatus, type ah as WorkflowInstanceSummary, type ai as WorkflowStepDetail, type aj as WsTokenProvider, ak as createClientQuery, al as createLocalStore, am as createStream, an as getErrorCode, ao as getRetryAfterMs, ap as isConflictError, aq as isForbiddenError, ar as isRateLimitedError, as as isUnauthorizedError } from "./packem_shared/lunora-client.d-CQ4K1Ls8.mjs";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-CrqgaKH9.mjs";
|
|
4
4
|
export type { AuthCapabilities, AuthConfigInfo, AuthImpersonation, AuthPage, AuthSession, AuthUser, AuthUserFieldSpec, CronJobInfo, KvKeyEntry, KvKeyListResult, KvNamespaceSummary, KvValueResult, PipelineLogColumnMap, PipelineLogCursor, PipelineLogField, PipelineLogPage, PipelineLogQuery, PipelineLogRow, VectorIndexSummary, VectorQueryMatch } from '@lunora/runtime';
|
|
5
|
+
declare const anyApi: Record<string, Record<string, unknown>>;
|
|
5
6
|
/**
|
|
6
7
|
* Optimistic-reconcile logic shared by every `@lunora/*` framework adapter's
|
|
7
8
|
* agent-chat surface (`@lunora/react` `useAgentChat`, plus the `@lunora/vue`,
|
|
@@ -139,18 +140,69 @@ interface TabCoordinatorOptions {
|
|
|
139
140
|
* Called when this tab becomes the leader (should open WS connections).
|
|
140
141
|
*/
|
|
141
142
|
onBecomeLeader?: () => void;
|
|
143
|
+
/**
|
|
144
|
+
* Called when the leader broadcasts its aggregate `ConnectionStatus`
|
|
145
|
+
* (see `LunoraClient.emitConnectionStatus`) — a follower owns no socket
|
|
146
|
+
* of its own, so this is its only truthful signal for a status indicator
|
|
147
|
+
* or the offline-queue gate. Sent on every leader-side status change and
|
|
148
|
+
* once more right after a new leader takes over, so a follower already
|
|
149
|
+
* mid-mirroring isn't stuck on a stale value from the PREVIOUS leader.
|
|
150
|
+
*
|
|
151
|
+
* `identity` is the leader's own `identityFingerprint()` (`null` = signed
|
|
152
|
+
* out) — the channel-name scoping (see `LunoraClient.createTabCoordinator`)
|
|
153
|
+
* is the primary defence against a stale frame from a since-changed
|
|
154
|
+
* identity, but `setAuthToken` in one tab can move it to a new channel
|
|
155
|
+
* while this frame is already queued in another tab's message task queue.
|
|
156
|
+
* A follower drops the frame when `identity` is present and doesn't match
|
|
157
|
+
* its own; an **absent** `identity` (mixed-version leader) is accepted —
|
|
158
|
+
* today's behavior, and the channel split already separates version groups.
|
|
159
|
+
*/
|
|
160
|
+
onConnectionStatus?: (status: ConnectionStatus, identity?: string | null) => void;
|
|
142
161
|
/**
|
|
143
162
|
* Called when this tab loses leadership (should close WS connections).
|
|
144
163
|
*/
|
|
145
164
|
onStopBeingLeader?: () => void;
|
|
146
165
|
/**
|
|
147
|
-
* Called when the leader broadcasts subscription data.
|
|
166
|
+
* Called when the leader broadcasts subscription data. `cursor`/`epoch`
|
|
167
|
+
* ride along when the leader is CLIENT-01-aware, so the follower can drop
|
|
168
|
+
* its own confirmed optimistic layers instead of just displaying the raw
|
|
169
|
+
* value; both are absent on a mixed-version leader that hasn't shipped the
|
|
170
|
+
* cursor yet (the follower falls back to its historical behavior — see
|
|
171
|
+
* `lunora-client.ts`'s `onSubscriptionData` wiring).
|
|
172
|
+
*
|
|
173
|
+
* `identity` is the leader's `identityFingerprint()` (see
|
|
174
|
+
* `onConnectionStatus`'s docblock for the full rationale and the
|
|
175
|
+
* absent-field mixed-version rule — identical here).
|
|
148
176
|
*/
|
|
149
|
-
onSubscriptionData?: (key: string, data: unknown) => void;
|
|
177
|
+
onSubscriptionData?: (key: string, data: unknown, cursor?: number, epoch?: string, identity?: string | null) => void;
|
|
150
178
|
/**
|
|
151
179
|
* Called when the leader broadcasts a subscription error.
|
|
152
180
|
*/
|
|
153
181
|
onSubscriptionError?: (key: string, error: SubscriptionError) => void;
|
|
182
|
+
/**
|
|
183
|
+
* Called when the leader broadcasts a `settled` frame's checkpoint advance
|
|
184
|
+
* (no value change, but the resume cursor moved). A follower needs this
|
|
185
|
+
* too — otherwise a `setQuery`/per-call optimistic overlay that a
|
|
186
|
+
* byte-identical write just confirmed stays masked on follower tabs until
|
|
187
|
+
* the next VISIBLE data frame arrives, even though the leader already
|
|
188
|
+
* dropped it.
|
|
189
|
+
*
|
|
190
|
+
* `lastMutationId` is the LEADER's own per-client `__client_watermark` —
|
|
191
|
+
* scoped server-side to the socket's announced `clientId` — so it only
|
|
192
|
+
* means anything to a follower whose `clientId` matches the leader's
|
|
193
|
+
* (`clientId` rides along for exactly that comparison; see
|
|
194
|
+
* `LunoraClient`'s wiring). An **absent** `clientId` (a mixed-version
|
|
195
|
+
* leader that hasn't shipped this field yet) also skips the
|
|
196
|
+
* `mutationId` half — safe, because the follower's own gates still
|
|
197
|
+
* resolve via its own RPC-ack watermark path and the `CheckpointRegistry`
|
|
198
|
+
* bounded fallback. The checkpoint (cursor) half of this callback fires
|
|
199
|
+
* unconditionally regardless of `clientId` — only the `mutationId` half
|
|
200
|
+
* is scoped.
|
|
201
|
+
*
|
|
202
|
+
* `identity` is the leader's `identityFingerprint()` — see
|
|
203
|
+
* `onConnectionStatus`'s docblock for the drop rule (identical here).
|
|
204
|
+
*/
|
|
205
|
+
onSubscriptionSettled?: (key: string, cursor?: number, epoch?: string, lastMutationId?: number, clientId?: string, identity?: string | null) => void;
|
|
154
206
|
}
|
|
155
207
|
declare class TabCoordinator {
|
|
156
208
|
private readonly bc;
|
|
@@ -163,6 +215,12 @@ declare class TabCoordinator {
|
|
|
163
215
|
private leader;
|
|
164
216
|
/** `true` once `start()` has been called. */
|
|
165
217
|
private running;
|
|
218
|
+
/**
|
|
219
|
+
* `true` while a `claimAndPromote()` claim window is open. Guards the yield
|
|
220
|
+
* handler and `checkLeaderHealth`'s belt-and-braces else-branch so the two
|
|
221
|
+
* promotion paths can't both arm a `becomeLeader` timeout for the same gap.
|
|
222
|
+
*/
|
|
223
|
+
private promotionPending;
|
|
166
224
|
/** Timestamp of the most recent leader heartbeat. */
|
|
167
225
|
private lastHeartbeat;
|
|
168
226
|
private heartbeatTimer;
|
|
@@ -170,8 +228,10 @@ declare class TabCoordinator {
|
|
|
170
228
|
/** Callbacks set via constructor options. */
|
|
171
229
|
private readonly onBecomeLeader;
|
|
172
230
|
private readonly onStopBeingLeader;
|
|
231
|
+
private readonly onConnectionStatus;
|
|
173
232
|
private readonly onSubscriptionData;
|
|
174
233
|
private readonly onSubscriptionError;
|
|
234
|
+
private readonly onSubscriptionSettled;
|
|
175
235
|
constructor(options?: TabCoordinatorOptions);
|
|
176
236
|
/**
|
|
177
237
|
* Start the coordinator: attempt to claim leadership and begin the
|
|
@@ -185,6 +245,23 @@ declare class TabCoordinator {
|
|
|
185
245
|
stop(): void;
|
|
186
246
|
/** `true` when this tab is the current WebSocket leader. */
|
|
187
247
|
isLeader(): boolean;
|
|
248
|
+
/**
|
|
249
|
+
* Force this (freshly `start()`-ed) tab to become leader immediately,
|
|
250
|
+
* skipping the normal claim-then-`leaderTimeout` dance. Safe to call
|
|
251
|
+
* right after `start()` when the caller already knows this tab was the
|
|
252
|
+
* SOLE leader of the group it's replacing (e.g. `LunoraClient`'s
|
|
253
|
+
* identity-change coordinator restart — waiting out the full
|
|
254
|
+
* `leaderTimeout` there would freeze every live query for no reason,
|
|
255
|
+
* since this tab is overwhelmingly likely to be alone on the freshly
|
|
256
|
+
* derived channel). A sibling tab going through the normal `start()`
|
|
257
|
+
* dance for the SAME transition observes this tab's `becomeLeader`
|
|
258
|
+
* heartbeat and defers before its own claim-timeout fires; if another
|
|
259
|
+
* tab ALSO force-promotes at the same moment (e.g. two tabs both
|
|
260
|
+
* transitioning to the same new identity), the existing
|
|
261
|
+
* `resolveLeaderVsLeaderTieBreak` resolves the rare double-promotion
|
|
262
|
+
* the same way it resolves any other split-brain.
|
|
263
|
+
*/
|
|
264
|
+
promoteImmediately(): void;
|
|
188
265
|
/** The tab id of the current leader, or `undefined` if unknown / no leader. */
|
|
189
266
|
get leaderTabId(): string | undefined;
|
|
190
267
|
/** The id of this tab. */
|
|
@@ -193,14 +270,42 @@ declare class TabCoordinator {
|
|
|
193
270
|
get isRunning(): boolean;
|
|
194
271
|
/**
|
|
195
272
|
* Broadcast subscription data to all follower tabs. Only the leader should
|
|
196
|
-
* call this.
|
|
273
|
+
* call this. `cursor`/`epoch` are omitted from the wire frame when
|
|
274
|
+
* `undefined` (e.g. a CDC-off shard) — a follower on ANY version treats a
|
|
275
|
+
* missing `cursor` as "no confirmed-layer drop this frame", so omitting it
|
|
276
|
+
* here is equivalent to sending it as `undefined`. `identity` is this
|
|
277
|
+
* (leader) tab's own `identityFingerprint()` (`null` = signed out) —
|
|
278
|
+
* stamped so a follower can drop a frame from a since-changed identity
|
|
279
|
+
* (see the `onSubscriptionData` docblock); spread-included whenever
|
|
280
|
+
* supplied, since a fixed leader always knows its own identity (never
|
|
281
|
+
* omits it) and `null` must round-trip distinctly from "field absent".
|
|
197
282
|
*/
|
|
198
|
-
broadcastSubscriptionData(key: string, data: unknown): void;
|
|
283
|
+
broadcastSubscriptionData(key: string, data: unknown, cursor?: number, epoch?: string, identity?: string | null): void;
|
|
199
284
|
/**
|
|
200
285
|
* Broadcast a subscription error to all follower tabs. Only the leader
|
|
201
286
|
* should call this.
|
|
202
287
|
*/
|
|
203
288
|
broadcastSubscriptionError(key: string, error: SubscriptionError): void;
|
|
289
|
+
/**
|
|
290
|
+
* Broadcast a `settled` frame's checkpoint advance to all follower tabs
|
|
291
|
+
* (no value change, but the resume cursor/epoch moved — see
|
|
292
|
+
* `LunoraClient.handleSettledMessage`). `clientId` is this (leader) tab's
|
|
293
|
+
* own client id, stamped so a follower can tell whether the echoed
|
|
294
|
+
* `lastMutationId` watermark is genuinely its own (see the
|
|
295
|
+
* `onSubscriptionSettled` docblock). `identity` is this tab's own
|
|
296
|
+
* `identityFingerprint()` — see `broadcastSubscriptionData`'s docblock for
|
|
297
|
+
* the same round-tripping rule. Only the leader should call this.
|
|
298
|
+
*/
|
|
299
|
+
broadcastSubscriptionSettled(key: string, cursor?: number, epoch?: string, lastMutationId?: number, clientId?: string, identity?: string | null): void;
|
|
300
|
+
/**
|
|
301
|
+
* Broadcast this tab's aggregate `ConnectionStatus` to follower tabs, so
|
|
302
|
+
* they can mirror a truthful status without a socket of their own (see
|
|
303
|
+
* `LunoraClient.computeStatus`/`emitConnectionStatus`). `identity` is this
|
|
304
|
+
* tab's own `identityFingerprint()` — see `broadcastSubscriptionData`'s
|
|
305
|
+
* docblock for the same round-tripping rule. Only the leader should call
|
|
306
|
+
* this.
|
|
307
|
+
*/
|
|
308
|
+
broadcastConnectionStatus(status: ConnectionStatus, identity?: string | null): void;
|
|
204
309
|
private broadcast;
|
|
205
310
|
private handleMessage;
|
|
206
311
|
private handleClaimLeadership;
|
|
@@ -221,6 +326,14 @@ declare class TabCoordinator {
|
|
|
221
326
|
private becomeLeader;
|
|
222
327
|
private sendHeartbeat;
|
|
223
328
|
private checkLeaderHealth;
|
|
329
|
+
/**
|
|
330
|
+
* Broadcast a leadership claim and arm a `becomeLeader` fallback: if no
|
|
331
|
+
* other tab has asserted itself as leader by the time the window elapses,
|
|
332
|
+
* self-promote. Shared by `checkLeaderHealth`'s stale-leader path and the
|
|
333
|
+
* `yield-leadership` handler so both promotion triggers use one claim
|
|
334
|
+
* window instead of each arming its own timeout.
|
|
335
|
+
*/
|
|
336
|
+
private claimAndPromote;
|
|
224
337
|
}
|
|
225
338
|
/**
|
|
226
339
|
* Client-side incremental merging of structured mutation deltas.
|
|
@@ -532,6 +645,15 @@ declare class OfflineQueue {
|
|
|
532
645
|
*/
|
|
533
646
|
drainConflict(): QueuedMutation[];
|
|
534
647
|
clear(): void;
|
|
648
|
+
/**
|
|
649
|
+
* Evict entries from the FRONT of `items` (the oldest — FIFO order) until
|
|
650
|
+
* the queue is at or under `maxItems`, rejecting each with
|
|
651
|
+
* `OFFLINE_QUEUE_OVERFLOW`, un-persisting it, and firing `onEvict`. Shared
|
|
652
|
+
* by `enqueue` (a live write pushes past capacity) and `hydrate` (a durable
|
|
653
|
+
* store restored more than `maxItems` records — CLIENT-03) so an overflow
|
|
654
|
+
* always drops the same way regardless of which caller triggered it.
|
|
655
|
+
*/
|
|
656
|
+
private evictOverflow;
|
|
535
657
|
/** Notify the size observer (the client's pending-sync count) after any change. */
|
|
536
658
|
private notifySize;
|
|
537
659
|
}
|
|
@@ -718,4 +840,4 @@ declare const sendToSw: (sw: ServiceWorker | null, message: ClientToSwMessage, e
|
|
|
718
840
|
* Create a reply for a client message (call from inside the SW).
|
|
719
841
|
*/
|
|
720
842
|
declare const createReply: (original: ClientToSwMessage, payload?: unknown) => SwToClientMessage;
|
|
721
|
-
export { type ArgsOf, type AsyncStorageLike, type AsyncStoragePersistenceOptions, type BookmarkStorage, ClientServiceWorker, type ClientSwOptions, type ClientToSwMessage, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamOptions, type HttpStreamRef, type IndexedDbPersistenceOptions, type IndexedDbQueryCacheOptions, LunoraClient, type MutationCallOptions, type MutationDelta, type MutationRunnerSinks, type MutatorHandle, type MutatorRunnerSinks, type MutatorTransaction, OfflineQueue, type OfflineQueueOptions, type OptimisticMessage, type PersistenceAdapter, type QueryCacheAdapter, type QueuedMutation, RETIRE_AFTER_DURABLE_SEQ_ADVANCE, type ReconcileDurableMessage, type ReconnectCalculator, type ReconnectOptions, type ReturnOf, type ServiceWorkerStatus, type StreamIterable, type SubscriptionError, type SwToClientMessage, TabCoordinator, applyDelta, createAsyncStoragePersistence, createInMemoryBookmarkStorage, createInMemoryPersistence, createInMemoryQueryCache, createIndexedDbPersistence, createIndexedDbQueryCache, createMutationRunner, createMutatorRunner, createReconnect, createReply, createSnapshotPrecondition, httpStream, isMutationDelta, maxSeq, queryCacheKey, reconcileOptimistic, sendToSw };
|
|
843
|
+
export { type ArgsOf, type AsyncStorageLike, type AsyncStoragePersistenceOptions, type BookmarkStorage, ClientServiceWorker, type ClientSwOptions, type ClientToSwMessage, type ConnectionStatus, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamOptions, type HttpStreamRef, type IndexedDbPersistenceOptions, type IndexedDbQueryCacheOptions, LunoraClient, type MutationCallOptions, type MutationDelta, type MutationRunnerSinks, type MutatorHandle, type MutatorRunnerSinks, type MutatorTransaction, OfflineQueue, type OfflineQueueOptions, type OptimisticMessage, type PersistenceAdapter, type QueryCacheAdapter, type QueuedMutation, RETIRE_AFTER_DURABLE_SEQ_ADVANCE, type ReconcileDurableMessage, type ReconnectCalculator, type ReconnectOptions, type ReturnOf, type ServiceWorkerStatus, type StreamIterable, type SubscriptionError, type SwToClientMessage, TabCoordinator, anyApi, applyDelta, createAsyncStoragePersistence, createInMemoryBookmarkStorage, createInMemoryPersistence, createInMemoryQueryCache, createIndexedDbPersistence, createIndexedDbQueryCache, createMutationRunner, createMutatorRunner, createReconnect, createReply, createSnapshotPrecondition, httpStream, isMutationDelta, maxSeq, queryCacheKey, reconcileOptimistic, sendToSw };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { c as PersistenceAdapter, B as BookmarkStorage, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, g as ReconnectOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-
|
|
2
|
-
export { type h as BatchSlot,
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-
|
|
1
|
+
import { c as PersistenceAdapter, B as BookmarkStorage, C as ConnectionStatus, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, g as ReconnectOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-CQ4K1Ls8.js";
|
|
2
|
+
export { type h as BatchSlot, i as CONFLICT_ERROR_CODE, type j as CachedQuery, type k as ClientDebugShard, type l as ClientDebugSnapshot, type m as ClientDebugSubscription, type n as ClientMessage, type o as ClientQueryRef, type p as ClientShapeSubscribeMessage, type q as ClientShapeUnsubscribeMessage, D as DEFAULT_MAX_BUFFER, type r as FunctionArgumentDescriptor, type s as FunctionDescriptor, type G as GlobalFacetResult, type t as GlobalFacetValue, type u as GlobalFilterClause, type v as GlobalTableInfo, type w as GlobalTablePage, type x as HttpStreamCallArgs, type y as LunoraClientError, type z as LunoraClientOptions, type E as LunoraErrorCode, type I as MutationSettledEvent, type J as OptimisticLocalStore, type K as OptimisticUpdate, type N as OutboxMutation, type T as OutboxSink, type V as PersistedMutation, type P as Preloaded, type W as RowOp, type X as RpcEnvelope, type Y as RpcResponseBody, type Z as ScheduleRecord, type _ as SchedulerPoolStatus, type $ as SchedulerStatus, type a0 as ServerMessage, type a1 as ServerPokeEndMessage, type a2 as ServerPokePartMessage, type a3 as ServerPokeStartMessage, type a4 as ShardTrafficEntry, type a5 as ShardTrafficResult, type a6 as StorageListPage, type a7 as StorageObject, type a8 as StreamHandle, type a9 as SubscriptionCallback, type b as SubscriptionErrorCallback, aa as SubscriptionRegistry, type ab as SubscriptionState, type ac as SyncWatermark, type a as Unsubscribe, type U as User, type ad as WorkflowInstanceAction, type ae as WorkflowInstanceDetail, type af as WorkflowInstancePage, type ag as WorkflowInstanceStatus, type ah as WorkflowInstanceSummary, type ai as WorkflowStepDetail, type aj as WsTokenProvider, ak as createClientQuery, al as createLocalStore, am as createStream, an as getErrorCode, ao as getRetryAfterMs, ap as isConflictError, aq as isForbiddenError, ar as isRateLimitedError, as as isUnauthorizedError } from "./packem_shared/lunora-client.d-CQ4K1Ls8.js";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-DZf_x6mY.js";
|
|
4
4
|
export type { AuthCapabilities, AuthConfigInfo, AuthImpersonation, AuthPage, AuthSession, AuthUser, AuthUserFieldSpec, CronJobInfo, KvKeyEntry, KvKeyListResult, KvNamespaceSummary, KvValueResult, PipelineLogColumnMap, PipelineLogCursor, PipelineLogField, PipelineLogPage, PipelineLogQuery, PipelineLogRow, VectorIndexSummary, VectorQueryMatch } from '@lunora/runtime';
|
|
5
|
+
declare const anyApi: Record<string, Record<string, unknown>>;
|
|
5
6
|
/**
|
|
6
7
|
* Optimistic-reconcile logic shared by every `@lunora/*` framework adapter's
|
|
7
8
|
* agent-chat surface (`@lunora/react` `useAgentChat`, plus the `@lunora/vue`,
|
|
@@ -139,18 +140,69 @@ interface TabCoordinatorOptions {
|
|
|
139
140
|
* Called when this tab becomes the leader (should open WS connections).
|
|
140
141
|
*/
|
|
141
142
|
onBecomeLeader?: () => void;
|
|
143
|
+
/**
|
|
144
|
+
* Called when the leader broadcasts its aggregate `ConnectionStatus`
|
|
145
|
+
* (see `LunoraClient.emitConnectionStatus`) — a follower owns no socket
|
|
146
|
+
* of its own, so this is its only truthful signal for a status indicator
|
|
147
|
+
* or the offline-queue gate. Sent on every leader-side status change and
|
|
148
|
+
* once more right after a new leader takes over, so a follower already
|
|
149
|
+
* mid-mirroring isn't stuck on a stale value from the PREVIOUS leader.
|
|
150
|
+
*
|
|
151
|
+
* `identity` is the leader's own `identityFingerprint()` (`null` = signed
|
|
152
|
+
* out) — the channel-name scoping (see `LunoraClient.createTabCoordinator`)
|
|
153
|
+
* is the primary defence against a stale frame from a since-changed
|
|
154
|
+
* identity, but `setAuthToken` in one tab can move it to a new channel
|
|
155
|
+
* while this frame is already queued in another tab's message task queue.
|
|
156
|
+
* A follower drops the frame when `identity` is present and doesn't match
|
|
157
|
+
* its own; an **absent** `identity` (mixed-version leader) is accepted —
|
|
158
|
+
* today's behavior, and the channel split already separates version groups.
|
|
159
|
+
*/
|
|
160
|
+
onConnectionStatus?: (status: ConnectionStatus, identity?: string | null) => void;
|
|
142
161
|
/**
|
|
143
162
|
* Called when this tab loses leadership (should close WS connections).
|
|
144
163
|
*/
|
|
145
164
|
onStopBeingLeader?: () => void;
|
|
146
165
|
/**
|
|
147
|
-
* Called when the leader broadcasts subscription data.
|
|
166
|
+
* Called when the leader broadcasts subscription data. `cursor`/`epoch`
|
|
167
|
+
* ride along when the leader is CLIENT-01-aware, so the follower can drop
|
|
168
|
+
* its own confirmed optimistic layers instead of just displaying the raw
|
|
169
|
+
* value; both are absent on a mixed-version leader that hasn't shipped the
|
|
170
|
+
* cursor yet (the follower falls back to its historical behavior — see
|
|
171
|
+
* `lunora-client.ts`'s `onSubscriptionData` wiring).
|
|
172
|
+
*
|
|
173
|
+
* `identity` is the leader's `identityFingerprint()` (see
|
|
174
|
+
* `onConnectionStatus`'s docblock for the full rationale and the
|
|
175
|
+
* absent-field mixed-version rule — identical here).
|
|
148
176
|
*/
|
|
149
|
-
onSubscriptionData?: (key: string, data: unknown) => void;
|
|
177
|
+
onSubscriptionData?: (key: string, data: unknown, cursor?: number, epoch?: string, identity?: string | null) => void;
|
|
150
178
|
/**
|
|
151
179
|
* Called when the leader broadcasts a subscription error.
|
|
152
180
|
*/
|
|
153
181
|
onSubscriptionError?: (key: string, error: SubscriptionError) => void;
|
|
182
|
+
/**
|
|
183
|
+
* Called when the leader broadcasts a `settled` frame's checkpoint advance
|
|
184
|
+
* (no value change, but the resume cursor moved). A follower needs this
|
|
185
|
+
* too — otherwise a `setQuery`/per-call optimistic overlay that a
|
|
186
|
+
* byte-identical write just confirmed stays masked on follower tabs until
|
|
187
|
+
* the next VISIBLE data frame arrives, even though the leader already
|
|
188
|
+
* dropped it.
|
|
189
|
+
*
|
|
190
|
+
* `lastMutationId` is the LEADER's own per-client `__client_watermark` —
|
|
191
|
+
* scoped server-side to the socket's announced `clientId` — so it only
|
|
192
|
+
* means anything to a follower whose `clientId` matches the leader's
|
|
193
|
+
* (`clientId` rides along for exactly that comparison; see
|
|
194
|
+
* `LunoraClient`'s wiring). An **absent** `clientId` (a mixed-version
|
|
195
|
+
* leader that hasn't shipped this field yet) also skips the
|
|
196
|
+
* `mutationId` half — safe, because the follower's own gates still
|
|
197
|
+
* resolve via its own RPC-ack watermark path and the `CheckpointRegistry`
|
|
198
|
+
* bounded fallback. The checkpoint (cursor) half of this callback fires
|
|
199
|
+
* unconditionally regardless of `clientId` — only the `mutationId` half
|
|
200
|
+
* is scoped.
|
|
201
|
+
*
|
|
202
|
+
* `identity` is the leader's `identityFingerprint()` — see
|
|
203
|
+
* `onConnectionStatus`'s docblock for the drop rule (identical here).
|
|
204
|
+
*/
|
|
205
|
+
onSubscriptionSettled?: (key: string, cursor?: number, epoch?: string, lastMutationId?: number, clientId?: string, identity?: string | null) => void;
|
|
154
206
|
}
|
|
155
207
|
declare class TabCoordinator {
|
|
156
208
|
private readonly bc;
|
|
@@ -163,6 +215,12 @@ declare class TabCoordinator {
|
|
|
163
215
|
private leader;
|
|
164
216
|
/** `true` once `start()` has been called. */
|
|
165
217
|
private running;
|
|
218
|
+
/**
|
|
219
|
+
* `true` while a `claimAndPromote()` claim window is open. Guards the yield
|
|
220
|
+
* handler and `checkLeaderHealth`'s belt-and-braces else-branch so the two
|
|
221
|
+
* promotion paths can't both arm a `becomeLeader` timeout for the same gap.
|
|
222
|
+
*/
|
|
223
|
+
private promotionPending;
|
|
166
224
|
/** Timestamp of the most recent leader heartbeat. */
|
|
167
225
|
private lastHeartbeat;
|
|
168
226
|
private heartbeatTimer;
|
|
@@ -170,8 +228,10 @@ declare class TabCoordinator {
|
|
|
170
228
|
/** Callbacks set via constructor options. */
|
|
171
229
|
private readonly onBecomeLeader;
|
|
172
230
|
private readonly onStopBeingLeader;
|
|
231
|
+
private readonly onConnectionStatus;
|
|
173
232
|
private readonly onSubscriptionData;
|
|
174
233
|
private readonly onSubscriptionError;
|
|
234
|
+
private readonly onSubscriptionSettled;
|
|
175
235
|
constructor(options?: TabCoordinatorOptions);
|
|
176
236
|
/**
|
|
177
237
|
* Start the coordinator: attempt to claim leadership and begin the
|
|
@@ -185,6 +245,23 @@ declare class TabCoordinator {
|
|
|
185
245
|
stop(): void;
|
|
186
246
|
/** `true` when this tab is the current WebSocket leader. */
|
|
187
247
|
isLeader(): boolean;
|
|
248
|
+
/**
|
|
249
|
+
* Force this (freshly `start()`-ed) tab to become leader immediately,
|
|
250
|
+
* skipping the normal claim-then-`leaderTimeout` dance. Safe to call
|
|
251
|
+
* right after `start()` when the caller already knows this tab was the
|
|
252
|
+
* SOLE leader of the group it's replacing (e.g. `LunoraClient`'s
|
|
253
|
+
* identity-change coordinator restart — waiting out the full
|
|
254
|
+
* `leaderTimeout` there would freeze every live query for no reason,
|
|
255
|
+
* since this tab is overwhelmingly likely to be alone on the freshly
|
|
256
|
+
* derived channel). A sibling tab going through the normal `start()`
|
|
257
|
+
* dance for the SAME transition observes this tab's `becomeLeader`
|
|
258
|
+
* heartbeat and defers before its own claim-timeout fires; if another
|
|
259
|
+
* tab ALSO force-promotes at the same moment (e.g. two tabs both
|
|
260
|
+
* transitioning to the same new identity), the existing
|
|
261
|
+
* `resolveLeaderVsLeaderTieBreak` resolves the rare double-promotion
|
|
262
|
+
* the same way it resolves any other split-brain.
|
|
263
|
+
*/
|
|
264
|
+
promoteImmediately(): void;
|
|
188
265
|
/** The tab id of the current leader, or `undefined` if unknown / no leader. */
|
|
189
266
|
get leaderTabId(): string | undefined;
|
|
190
267
|
/** The id of this tab. */
|
|
@@ -193,14 +270,42 @@ declare class TabCoordinator {
|
|
|
193
270
|
get isRunning(): boolean;
|
|
194
271
|
/**
|
|
195
272
|
* Broadcast subscription data to all follower tabs. Only the leader should
|
|
196
|
-
* call this.
|
|
273
|
+
* call this. `cursor`/`epoch` are omitted from the wire frame when
|
|
274
|
+
* `undefined` (e.g. a CDC-off shard) — a follower on ANY version treats a
|
|
275
|
+
* missing `cursor` as "no confirmed-layer drop this frame", so omitting it
|
|
276
|
+
* here is equivalent to sending it as `undefined`. `identity` is this
|
|
277
|
+
* (leader) tab's own `identityFingerprint()` (`null` = signed out) —
|
|
278
|
+
* stamped so a follower can drop a frame from a since-changed identity
|
|
279
|
+
* (see the `onSubscriptionData` docblock); spread-included whenever
|
|
280
|
+
* supplied, since a fixed leader always knows its own identity (never
|
|
281
|
+
* omits it) and `null` must round-trip distinctly from "field absent".
|
|
197
282
|
*/
|
|
198
|
-
broadcastSubscriptionData(key: string, data: unknown): void;
|
|
283
|
+
broadcastSubscriptionData(key: string, data: unknown, cursor?: number, epoch?: string, identity?: string | null): void;
|
|
199
284
|
/**
|
|
200
285
|
* Broadcast a subscription error to all follower tabs. Only the leader
|
|
201
286
|
* should call this.
|
|
202
287
|
*/
|
|
203
288
|
broadcastSubscriptionError(key: string, error: SubscriptionError): void;
|
|
289
|
+
/**
|
|
290
|
+
* Broadcast a `settled` frame's checkpoint advance to all follower tabs
|
|
291
|
+
* (no value change, but the resume cursor/epoch moved — see
|
|
292
|
+
* `LunoraClient.handleSettledMessage`). `clientId` is this (leader) tab's
|
|
293
|
+
* own client id, stamped so a follower can tell whether the echoed
|
|
294
|
+
* `lastMutationId` watermark is genuinely its own (see the
|
|
295
|
+
* `onSubscriptionSettled` docblock). `identity` is this tab's own
|
|
296
|
+
* `identityFingerprint()` — see `broadcastSubscriptionData`'s docblock for
|
|
297
|
+
* the same round-tripping rule. Only the leader should call this.
|
|
298
|
+
*/
|
|
299
|
+
broadcastSubscriptionSettled(key: string, cursor?: number, epoch?: string, lastMutationId?: number, clientId?: string, identity?: string | null): void;
|
|
300
|
+
/**
|
|
301
|
+
* Broadcast this tab's aggregate `ConnectionStatus` to follower tabs, so
|
|
302
|
+
* they can mirror a truthful status without a socket of their own (see
|
|
303
|
+
* `LunoraClient.computeStatus`/`emitConnectionStatus`). `identity` is this
|
|
304
|
+
* tab's own `identityFingerprint()` — see `broadcastSubscriptionData`'s
|
|
305
|
+
* docblock for the same round-tripping rule. Only the leader should call
|
|
306
|
+
* this.
|
|
307
|
+
*/
|
|
308
|
+
broadcastConnectionStatus(status: ConnectionStatus, identity?: string | null): void;
|
|
204
309
|
private broadcast;
|
|
205
310
|
private handleMessage;
|
|
206
311
|
private handleClaimLeadership;
|
|
@@ -221,6 +326,14 @@ declare class TabCoordinator {
|
|
|
221
326
|
private becomeLeader;
|
|
222
327
|
private sendHeartbeat;
|
|
223
328
|
private checkLeaderHealth;
|
|
329
|
+
/**
|
|
330
|
+
* Broadcast a leadership claim and arm a `becomeLeader` fallback: if no
|
|
331
|
+
* other tab has asserted itself as leader by the time the window elapses,
|
|
332
|
+
* self-promote. Shared by `checkLeaderHealth`'s stale-leader path and the
|
|
333
|
+
* `yield-leadership` handler so both promotion triggers use one claim
|
|
334
|
+
* window instead of each arming its own timeout.
|
|
335
|
+
*/
|
|
336
|
+
private claimAndPromote;
|
|
224
337
|
}
|
|
225
338
|
/**
|
|
226
339
|
* Client-side incremental merging of structured mutation deltas.
|
|
@@ -532,6 +645,15 @@ declare class OfflineQueue {
|
|
|
532
645
|
*/
|
|
533
646
|
drainConflict(): QueuedMutation[];
|
|
534
647
|
clear(): void;
|
|
648
|
+
/**
|
|
649
|
+
* Evict entries from the FRONT of `items` (the oldest — FIFO order) until
|
|
650
|
+
* the queue is at or under `maxItems`, rejecting each with
|
|
651
|
+
* `OFFLINE_QUEUE_OVERFLOW`, un-persisting it, and firing `onEvict`. Shared
|
|
652
|
+
* by `enqueue` (a live write pushes past capacity) and `hydrate` (a durable
|
|
653
|
+
* store restored more than `maxItems` records — CLIENT-03) so an overflow
|
|
654
|
+
* always drops the same way regardless of which caller triggered it.
|
|
655
|
+
*/
|
|
656
|
+
private evictOverflow;
|
|
535
657
|
/** Notify the size observer (the client's pending-sync count) after any change. */
|
|
536
658
|
private notifySize;
|
|
537
659
|
}
|
|
@@ -718,4 +840,4 @@ declare const sendToSw: (sw: ServiceWorker | null, message: ClientToSwMessage, e
|
|
|
718
840
|
* Create a reply for a client message (call from inside the SW).
|
|
719
841
|
*/
|
|
720
842
|
declare const createReply: (original: ClientToSwMessage, payload?: unknown) => SwToClientMessage;
|
|
721
|
-
export { type ArgsOf, type AsyncStorageLike, type AsyncStoragePersistenceOptions, type BookmarkStorage, ClientServiceWorker, type ClientSwOptions, type ClientToSwMessage, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamOptions, type HttpStreamRef, type IndexedDbPersistenceOptions, type IndexedDbQueryCacheOptions, LunoraClient, type MutationCallOptions, type MutationDelta, type MutationRunnerSinks, type MutatorHandle, type MutatorRunnerSinks, type MutatorTransaction, OfflineQueue, type OfflineQueueOptions, type OptimisticMessage, type PersistenceAdapter, type QueryCacheAdapter, type QueuedMutation, RETIRE_AFTER_DURABLE_SEQ_ADVANCE, type ReconcileDurableMessage, type ReconnectCalculator, type ReconnectOptions, type ReturnOf, type ServiceWorkerStatus, type StreamIterable, type SubscriptionError, type SwToClientMessage, TabCoordinator, applyDelta, createAsyncStoragePersistence, createInMemoryBookmarkStorage, createInMemoryPersistence, createInMemoryQueryCache, createIndexedDbPersistence, createIndexedDbQueryCache, createMutationRunner, createMutatorRunner, createReconnect, createReply, createSnapshotPrecondition, httpStream, isMutationDelta, maxSeq, queryCacheKey, reconcileOptimistic, sendToSw };
|
|
843
|
+
export { type ArgsOf, type AsyncStorageLike, type AsyncStoragePersistenceOptions, type BookmarkStorage, ClientServiceWorker, type ClientSwOptions, type ClientToSwMessage, type ConnectionStatus, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamOptions, type HttpStreamRef, type IndexedDbPersistenceOptions, type IndexedDbQueryCacheOptions, LunoraClient, type MutationCallOptions, type MutationDelta, type MutationRunnerSinks, type MutatorHandle, type MutatorRunnerSinks, type MutatorTransaction, OfflineQueue, type OfflineQueueOptions, type OptimisticMessage, type PersistenceAdapter, type QueryCacheAdapter, type QueuedMutation, RETIRE_AFTER_DURABLE_SEQ_ADVANCE, type ReconcileDurableMessage, type ReconnectCalculator, type ReconnectOptions, type ReturnOf, type ServiceWorkerStatus, type StreamIterable, type SubscriptionError, type SwToClientMessage, TabCoordinator, anyApi, applyDelta, createAsyncStoragePersistence, createInMemoryBookmarkStorage, createInMemoryPersistence, createInMemoryQueryCache, createIndexedDbPersistence, createIndexedDbQueryCache, createMutationRunner, createMutatorRunner, createReconnect, createReply, createSnapshotPrecondition, httpStream, isMutationDelta, maxSeq, queryCacheKey, reconcileOptimistic, sendToSw };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RETIRE_AFTER_DURABLE_SEQ_ADVANCE as
|
|
1
|
+
import{anyApi as o}from"./packem_shared/anyApi-BD4dgvDY.mjs";import{RETIRE_AFTER_DURABLE_SEQ_ADVANCE as a,maxSeq as n,reconcileOptimistic as p}from"./packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-D96iRJDY.mjs";import{createAsyncStoragePersistence as m}from"./packem_shared/createAsyncStoragePersistence-BRLqeXsC.mjs";import{default as f}from"./packem_shared/createInMemoryBookmarkStorage-DCd5Ej-t.mjs";import{createClientQuery as s}from"./packem_shared/createClientQuery-CrdRoicO.mjs";import{TabCoordinator as d}from"./packem_shared/TabCoordinator-CMFYQCL-.mjs";import{applyDelta as y,isMutationDelta as R}from"./packem_shared/applyDelta-DXqQORbM.mjs";import{CONFLICT_ERROR_CODE as C,getErrorCode as S,getRetryAfterMs as A,isConflictError as D,isForbiddenError as M,isRateLimitedError as _,isUnauthorizedError as h}from"./packem_shared/CONFLICT_ERROR_CODE-B2SU0CaJ.mjs";import{httpStream as Q}from"./packem_shared/httpStream-Cz4_QyjV.mjs";import{c as L}from"./packem_shared/local-store-C53xDvEr.mjs";import{LunoraClient as b}from"./packem_shared/LunoraClient-NUIJBf6Z.mjs";import{createMutationRunner as O}from"./packem_shared/createMutationRunner-lQXH8Amk.mjs";import{createMutatorRunner as U}from"./packem_shared/createMutatorRunner-BVzo0FBZ.mjs";import{h as B}from"./packem_shared/offline-queue-BeWay5u5.mjs";import{createInMemoryPersistence as N,createIndexedDbPersistence as v}from"./packem_shared/createInMemoryPersistence-CQYXy8UP.mjs";import{preloadQuery as z,preloadedQueryResult as K}from"./packem_shared/preloadQuery-DjwSjFwt.mjs";import{createInMemoryQueryCache as W,createIndexedDbQueryCache as X,queryCacheKey as j}from"./packem_shared/createInMemoryQueryCache-DjCKrLjk.mjs";import{createReconnect as H}from"./packem_shared/createReconnect-CFT7YRSR.mjs";import{default as Y}from"./packem_shared/createSnapshotPrecondition-DOKRAJ4b.mjs";import{DEFAULT_MAX_BUFFER as $,createStream as ee}from"./packem_shared/DEFAULT_MAX_BUFFER-TBn2s7Rz.mjs";import{SubscriptionRegistry as oe}from"./packem_shared/SubscriptionRegistry-CdrOD7sZ.mjs";import{ClientServiceWorker as ae}from"./packem_shared/ClientServiceWorker-CRQ5yl7-.mjs";import{createReply as pe,sendToSw as ce}from"./packem_shared/createReply-DF0jRsv-.mjs";export{C as CONFLICT_ERROR_CODE,ae as ClientServiceWorker,$ as DEFAULT_MAX_BUFFER,b as LunoraClient,B as OfflineQueue,a as RETIRE_AFTER_DURABLE_SEQ_ADVANCE,oe as SubscriptionRegistry,d as TabCoordinator,o as anyApi,y as applyDelta,m as createAsyncStoragePersistence,s as createClientQuery,f as createInMemoryBookmarkStorage,N as createInMemoryPersistence,W as createInMemoryQueryCache,v as createIndexedDbPersistence,X as createIndexedDbQueryCache,L as createLocalStore,O as createMutationRunner,U as createMutatorRunner,H as createReconnect,pe as createReply,Y as createSnapshotPrecondition,ee as createStream,S as getErrorCode,A as getRetryAfterMs,Q as httpStream,D as isConflictError,M as isForbiddenError,R as isMutationDelta,_ as isRateLimitedError,h as isUnauthorizedError,n as maxSeq,z as preloadQuery,K as preloadedQueryResult,j as queryCacheKey,p as reconcileOptimistic,ce as sendToSw};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as d}from"@lunora/errors";import{t as I,c as w,f as W}from"./wire-key-D_zOxXK4.mjs";import D from"./createInMemoryBookmarkStorage-DCd5Ej-t.mjs";import{ClientQueryStore as j}from"./createClientQuery-CrdRoicO.mjs";import{TabCoordinator as H}from"./TabCoordinator-CMFYQCL-.mjs";import{isMutationDelta as V,applyDelta as J}from"./applyDelta-DXqQORbM.mjs";import{httpStream as X}from"./httpStream-Cz4_QyjV.mjs";import{d as N,n as E,f as L,a as Y,c as Z}from"./local-store-C53xDvEr.mjs";import{h as tt,u as $,d as et,s as st}from"./offline-queue-BeWay5u5.mjs";import{resolvePersistenceAdapter as it}from"./createInMemoryPersistence-CQYXy8UP.mjs";import{resolveQueryCacheAdapter as nt,queryCacheKey as _}from"./createInMemoryQueryCache-DjCKrLjk.mjs";import{createReconnect as K}from"./createReconnect-CFT7YRSR.mjs";import{createStream as rt}from"./DEFAULT_MAX_BUFFER-TBn2s7Rz.mjs";import{SubscriptionRegistry as k}from"./SubscriptionRegistry-CdrOD7sZ.mjs";const F=500,ot=(h,t)=>{if(h.size<t)return;const e=h.keys().next().value;e!==void 0&&h.delete(e)};class C{listeners=new Set;add(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}emit(...t){const[e]=t;for(const s of this.listeners)try{s(e)}catch{}}clear(){this.listeners.clear()}}const at="/_lunora/rpc",q="/_lunora/rpc-batch",ct="/_lunora/ws",P=h=>h===void 0||h===""?"":`&bucket=${encodeURIComponent(h)}`,M=h=>{for(let t=h.length-1;t>=0;t-=1)h[t]?.()},ht=(h,t)=>{for(const e of t)e.op==="delete"?h.delete(e.key):e.value!==void 0&&h.set(e.key,e.value)},dt="lunora-ping",ut=3e4,lt=1e4,mt=250,pt=64,ft="/_lunora/admin/shard-traffic",yt="/_lunora/admin/scheduled",gt="/_lunora/admin/scheduled/status",wt="/_lunora/admin/scheduled/ws",bt="/_lunora/admin/scheduled/cancel",vt="/_lunora/admin/scheduled/dead",St="/_lunora/admin/scheduled/dead/retry",kt="/_lunora/admin/scheduled/dead/cancel",Ct="/_lunora/admin/workflows/instances",Tt="/_lunora/admin/workflows/instance",It="/_lunora/admin/workflows/status",A="/_lunora/admin/storage",Et="/_lunora/admin/storage/url",Lt="/_lunora/admin/storage/buckets",At="/_lunora/admin/functions",Rt="/_lunora/admin/cron-jobs",Ot="/_lunora/admin/cron-jobs/run",Nt="/_lunora/admin/openapi",_t="/_lunora/admin/openrpc",Ft="/_lunora/admin/global/tables",Pt="/_lunora/admin/global/table",Mt="/_lunora/admin/global/facet",xt="/_lunora/admin/vector/indexes",$t="/_lunora/admin/vector/query",Kt="/_lunora/admin/logs/archive",qt="/_lunora/admin/kv/namespaces",Ut="/_lunora/admin/kv/keys",x="/_lunora/admin/kv/value",Qt="/_lunora/admin/auth/users",U="/_lunora/admin/auth/sessions",Bt="/_lunora/admin/auth/users/create",Wt="/_lunora/admin/auth/users/role",zt="/_lunora/admin/auth/users/ban",Gt="/_lunora/admin/auth/users/unban",Dt="/_lunora/admin/auth/users/password",jt="/_lunora/admin/auth/users/remove",Ht="/_lunora/admin/auth/users/impersonate",Vt="/_lunora/admin/auth/sessions/revoke",Jt="/_lunora/admin/auth/sessions/revoke-all",Xt="/_lunora/admin/auth/capabilities",Yt="/_lunora/admin/auth/users/update",Zt="/_lunora/admin/auth/accounts",te="/_lunora/admin/auth/accounts/unlink",ee="/_lunora/admin/auth/passkeys",se="/_lunora/admin/auth/passkeys/delete",ie="/_lunora/admin/auth/two-factor/disable",ne="/_lunora/admin/auth/organizations",re="/_lunora/admin/auth/organizations/members",oe="/_lunora/admin/auth/organizations/invitations",ae="/_lunora/admin/auth/organizations/members/remove",ce="/_lunora/admin/auth/organizations/invitations/cancel",he="/_lunora/admin/auth/config",de="/_lunora/admin/auth/organizations/create",ue="/_lunora/admin/auth/organizations/update",le="/_lunora/admin/auth/organizations/remove",me="/_lunora/admin/auth/organizations/members/add",pe="/_lunora/admin/auth/organizations/members/invite",fe="/_lunora/admin/auth/organizations/members/role",ye="/_lunora/admin/auth/organizations/teams",ge="/_lunora/admin/auth/organizations/teams/create",we="/_lunora/admin/auth/organizations/teams/update",be="/_lunora/admin/auth/organizations/teams/remove",ve="/_lunora/admin/auth/organizations/teams/members",Se="/_lunora/admin/auth/organizations/teams/members/add",ke="/_lunora/admin/auth/organizations/teams/members/remove",Ce="/_lunora/admin/auth/organizations/roles",Te="/_lunora/admin/auth/organizations/roles/create",Ie="/_lunora/admin/auth/organizations/roles/update",Ee="/_lunora/admin/auth/organizations/roles/remove",Le="/api/auth",Ae="/get-session",Q=h=>h.startsWith("https://")?`wss://${h.slice(8)}`:h.startsWith("http://")?`ws://${h.slice(7)}`:h,v=(h,t)=>`${h.endsWith("/")?h.slice(0,-1):h}${t}`,g=(h,t)=>{const e=new URLSearchParams;for(const[i,n]of Object.entries(t))n!==void 0&&n!==""&&e.set(i,String(n));const s=e.toString();return s===""?h:`${h}?${s}`},p=h=>h??"",Re=h=>{const t=h.error,e=typeof t?.code=="string"?t.code:void 0,s=typeof t?.message=="string"?t.message:void 0,i=(typeof h.message=="string"?h.message:void 0)??s??"stream error";return e===void 0?new Error(i):new d(e,i)},B=h=>{const t=h.error,e=typeof t?.code=="string"?t.code:void 0,s=typeof t?.message=="string"?t.message:void 0;return{message:(typeof h.message=="string"?h.message:void 0)??s??"subscription error",...e===void 0?{}:{code:e}}},R=(h,t)=>{for(const e of h)try{e(t)}catch{}},Oe=new TextDecoder,Ne=h=>{if(typeof h=="string")return h;if(h instanceof ArrayBuffer)return Oe.decode(h)},f=(h,t)=>{if(!h.socket||h.wsState!=="open")return!1;try{return h.socket.send(JSON.stringify(t)),!0}catch{return!1}},T=h=>{const t=new Error(h.message??"request failed");return t.code=h.code,h.data!==void 0&&(t.data=w(h.data)),h.hint!==void 0&&(t.hint=h.hint),h.docsUrl!==void 0&&(t.docsUrl=h.docsUrl),t},S=(h,t)=>{try{return W(h)}catch(e){const s=e instanceof Error?e.message:String(e);throw new TypeError(`LunoraClient: cannot encode ${t} — ${s}`,e instanceof Error?{cause:e}:void 0)}},_e=(h,t)=>{const e=Array.from({length:t});for(const s of h){if(typeof s.id!="number"||s.id<0||s.id>=t)continue;const i=s.body;e[s.id]=i&&"error"in i&&i.error?{error:T(i.error),ok:!1}:{ok:!0,value:w(i?.result)}}return e.map(s=>s??{error:new Error("batch call returned no result"),ok:!1})},Fe=new Set(["SHARD_ERROR","SHARD_UNAVAILABLE"]),Pe=Promise.resolve();class z{static MAX_POKE_BUFFERS=256;static createClientQuery(t,e){return{defaultValue:e,key:t}}url;wsUrl;clientQueryStore;wsToken;authBasePath;fetchImpl;WebSocketImpl;bookmark;reconnectOptions;connectTimeoutMs;heartbeatIntervalMs;offlineQueue;outbox;clientId;readyResolved=!1;readyResolve;readyPromise;clientWatermarks=new Map;outboxMutationCounter=0;onPersistenceError;persistence;persistenceVersion;outboxLeaderRelease;queryCache;hydratedQueryCache=new Map;pendingCacheWrites=new Map;cacheFlushTimer;subscriptions=new k;tabCoordinator;leaderStatus;leaderWasEverConnected=!1;connections=new Map;defaultConnectionContext;connectionContexts=new Map;connectionContextHolders=new Map;authToken=null;authSubject=void 0;queuedIdentities=new Map;queuedOfflineShardKeys=new Set;closed=!1;authTokenListeners=new C;statusListeners=new C;tokenExpiredListeners=new C;mutationSettledListeners=new C;pendingChangeListeners=new C;whisperHandlers=new Map;lastStatus="idle";nextSubId=0;nextStreamId=0;streams=new Map;shapeSubscriptions=new Map;pokeBuffers=new Map;nextShapeId=0;constructor(t){this.url=t.url,this.wsUrl=t.wsUrl??v(Q(t.url),ct),this.wsToken=t.wsToken;const e=t.authBasePath??Le;this.authBasePath=e.endsWith("/")?e.slice(0,-1):e,this.fetchImpl=t.fetch??(typeof fetch=="function"?fetch.bind(globalThis):void 0),this.WebSocketImpl=t.WebSocket??(typeof WebSocket=="function"?WebSocket:void 0),this.bookmark=t.bookmarkStorage??D(),this.reconnectOptions=t.reconnect,this.heartbeatIntervalMs=t.heartbeatIntervalMs??ut,this.connectTimeoutMs=t.connectTimeoutMs??lt,this.defaultConnectionContext=t.connectionContext,this.persistence=it(t.persistence,t.outbox===void 0),this.persistenceVersion=t.persistenceVersion,this.queryCache=nt(t.queryCache),this.onPersistenceError=t.offlineQueue?.onPersistenceError,this.clientQueryStore=new j,t.crossTabSync?(this.tabCoordinator=this.createTabCoordinator(),this.tabCoordinator.start()):this.tabCoordinator=void 0,t.hydrateOnStart&&this.queryCache?this.readyPromise=new Promise(s=>{this.readyResolve=s}):(this.readyResolved=!0,this.readyPromise=Pe),this.offlineQueue=new tt(t.offlineQueue,{onEvict:(s,i)=>{this.emitItemSettled(s,"rejected",i)},onSizeChange:s=>{this.pendingChangeListeners.emit(s)},persistence:this.persistence,version:t.persistenceVersion}),this.outbox=t.outbox,this.clientId=t.clientId??`client-${$()}`,this.persistence&&queueMicrotask(()=>{this.hydrateAsOutboxLeader()}),this.queryCache&&queueMicrotask(()=>{(async()=>{try{await this.hydrateQueryCache()}catch{}finally{this.readyResolved=!0,this.readyResolve?.()}})().catch(()=>{})})}setAuthToken(t,e){const s=this.authToken!==t,i=this.identityFingerprint();this.authToken=t,e!==void 0&&(this.authSubject=e);const n=this.identityFingerprint();if(n!==i&&(s?this.rejectQueuedForIdentityChange():this.restampQueuedIdentity(i,n),this.tabCoordinator)){const r=this.tabCoordinator.isLeader();this.tabCoordinator.stop(),this.tabCoordinator=this.createTabCoordinator(),this.tabCoordinator.start(),r&&this.tabCoordinator.promoteImmediately()}s&&this.authTokenListeners.emit(t)}getAuthToken(){return this.authToken}currentIdentity(){return this.identityFingerprint()}clientIdentifier(){return this.clientId}confirmedMutationWatermark(t){return this.clientWatermarks.get(t??"")??0}async callMutator(t,e,s){const i=s?.clientSeq;if(i!==void 0&&(!Number.isInteger(i)||i<=0))throw new d("INTERNAL",`callMutator: clientSeq must be a positive integer, got ${String(i)}`);const n=s?.shardKey??"";let r;const o=await this.rpc(t,e,s?.shardKey,{captureBookmark:!0,clientId:this.clientId,clientSeq:i,onMutationAck:a=>{r=a}});return r!==void 0&&r>(this.clientWatermarks.get(n)??0)&&this.clientWatermarks.set(n,r),{applied:r===void 0||r===i,result:o}}onAuthTokenChange(t){return this.authTokenListeners.add(t)}async getCurrentUser(){if(this.closed||!this.fetchImpl)return null;const t={};this.authToken&&(t.authorization=`Bearer ${this.authToken}`);try{const e=await this.fetchImpl(v(this.url,`${this.authBasePath}${Ae}`),{credentials:"include",headers:t,method:"GET"});return e.ok?(await e.json())?.user??null:null}catch{return null}}setWsToken(t){if(this.wsToken!==t){this.wsToken=t;for(const e of this.connections.values())if(e.socket)try{e.socket.close()}catch{}}}setConnectionContext(t,e={}){const s=p(e.shardKey);t===void 0?this.connectionContexts.delete(s):this.connectionContexts.set(s,t),this.refreshConnectionContext(s)}acquireConnectionContext(t,e={}){const s=p(e.shardKey),i={context:t},n=this.connectionContextHolders.get(s);n?n.push(i):this.connectionContextHolders.set(s,[i]),this.refreshConnectionContext(s);let r=!1;return()=>{if(r)return;r=!0;const o=this.connectionContextHolders.get(s);if(!o)return;const a=o.indexOf(i);a!==-1&&o.splice(a,1),o.length===0&&this.connectionContextHolders.delete(s),this.refreshConnectionContext(s)}}whisperSubscribe(t,e,s={}){const i=p(s.shardKey);let n=this.whisperHandlers.get(i);n||(n=new Map,this.whisperHandlers.set(i,n));let r=n.get(t);const o=r===void 0;if(r||(r=new Set,n.set(t,r)),r.add(e),this.ensureSocket(s.shardKey),o){const a=this.getConnection(s.shardKey);a&&f(a,{topic:t,type:"whisper_subscribe"})}return()=>{const a=this.whisperHandlers.get(i),c=a?.get(t);if(!c?.delete(e)||c.size>0)return;a?.delete(t),a?.size===0&&this.whisperHandlers.delete(i);const u=this.getConnection(s.shardKey);u&&f(u,{topic:t,type:"whisper_unsubscribe"})}}whisper(t,e,s={}){this.ensureSocket(s.shardKey);const i=this.getConnection(s.shardKey);i&&f(i,{data:S(e??null,`whisper data for topic '${t}'`),topic:t,type:"whisper"})}onTokenExpired(t){return this.tokenExpiredListeners.add(t)}connectionStatus(){return this.computeStatus()}onConnectionStatus(t){const e=this.statusListeners.add(t);return t(this.computeStatus()),e}pendingCount(){return this.offlineQueue.size}debug(){const t=[],e=new Set([...this.connections.keys(),...this.clientWatermarks.keys()]);for(const i of e){const n=this.connections.get(i);t.push({confirmedMutationWatermark:this.clientWatermarks.get(i)??0,hasSocket:n?.socket!==void 0,shardKey:i===""?void 0:i,wasEverConnected:n?.wasEverConnected??!1,wsState:n?.wsState??"idle"})}const s=this.subscriptions.all().map(i=>({acked:i.acked,functionPath:i.fn.__lunoraRef,id:i.id,kind:"query",lastMutationId:i.lastMutationId,pendingOptimisticLayers:i.optimisticLayers.length,serverCursor:i.serverCursor,shardKey:i.shardKey,subscriberCount:i.callbacks.size}));for(const i of this.shapeSubscriptions.values())s.push({acked:i.serverCursor!==void 0,functionPath:`shape:${i.name}`,id:i.id,kind:"shape",lastMutationId:i.lastMutationId,pendingOptimisticLayers:0,rowCount:i.rows.size,serverCursor:i.serverCursor,shardKey:i.shardKey,subscriberCount:i.callbacks.size});return{clientId:this.clientId,closed:this.closed,connectionStatus:this.computeStatus(),pendingWrites:this.offlineQueue.size,shards:t.toSorted((i,n)=>(i.shardKey??"").localeCompare(n.shardKey??"")),subscriptions:s.toSorted((i,n)=>i.functionPath.localeCompare(n.functionPath))}}onPendingChange(t){const e=this.pendingChangeListeners.add(t);return t(this.offlineQueue.size),e}onMutationSettled(t){return this.mutationSettledListeners.add(t)}getWebSocketImpl(){return this.WebSocketImpl}getClientQuery(t){return this.clientQueryStore.get(t)}setClientQuery(t,e){this.clientQueryStore.set(t,e)}subscribeClientQuery(t,e){return this.clientQueryStore.subscribe(t,e)}resetClientQuery(t){this.clientQueryStore.reset(t)}snapshotPrecondition(t,e,s){const i=this.peekActiveQueryValue(t.__lunoraRef,e,s),n=i===void 0?void 0:I(i);return()=>{const r=this.peekActiveQueryValue(t.__lunoraRef,e,s);return n===void 0&&r===void 0?!0:n===void 0||r===void 0?!1:I(r)===n}}whenReady(){return this.readyPromise}get isReady(){return this.readyResolved}peekHydratedQuery(t,e,s){if(!this.readyResolved)return;const i=I(e),n=_(t,i,s),r=this.hydratedQueryCache.get(n);if(r!==void 0)return r.identity===this.identityFingerprint()?r.value:void 0}peekActiveQueryValue(t,e,s){const i=k.key(t,e,s);return this.subscriptions.get(i)?.lastValue}async query(t,e,s={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return await this.rpc(t.__lunoraRef,e,s.shardKey,{attachBookmark:!0})}async batch(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");if(!this.fetchImpl)throw new d("INTERNAL","LunoraClient: no `fetch` implementation available");if(t.length===0)return[];const e=await this.fetchImpl(v(this.url,q),{body:JSON.stringify({calls:t.map((n,r)=>({args:S(n.args??{},`args for batch call '${n.fn.__lunoraRef}'`),functionPath:n.fn.__lunoraRef,id:r,shardKey:n.shardKey}))}),headers:this.rpcRequestHeaders({attachBookmark:!0}),method:"POST"}),s=e.headers.get("x-d1-bookmark");s&&this.bookmark.set(s);let i;try{i=await e.json()}catch{throw new d("INTERNAL",`LunoraClient: batch response was not JSON (status ${e.status.toString()})`)}if(!e.ok||i.error&&!i.results)throw i.error?T(i.error):new d("INTERNAL",`LunoraClient: batch request failed (status ${e.status.toString()})`);return _e(i.results??[],t.length)}async mutation(t,e,s={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const i=e,n=s.mutationId??$(),{confirms:r,rollbacks:o}=this.applyOptimisticUpdates(t.__lunoraRef,i,s.shardKey,s.optimistic);s.optimisticUpdate&&this.applyOptimisticUpdate(s.optimisticUpdate,e,s.shardKey,o,r);const{hasSocket:a,wasEverConnected:c,wsState:u}=this.connectionGateState(s.shardKey),{queueBeforeFirstConnect:l}=this.offlineQueue,m=c||l,y=this.WebSocketImpl!==void 0&&m;if(u!=="open"&&!a&&y||u==="connecting"&&m)return this.enqueueOfflineMutation(t,i,s.shardKey,n,o,r,s.precondition);try{let b;const G=await this.rpc(t.__lunoraRef,i,s.shardKey,{captureBookmark:!0,mutationId:n,onCommitCursor:O=>{b=O}});for(const O of r)O(b);return G}catch(b){throw M(o),b}}async action(t,e,s={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return await this.rpc(t.__lunoraRef,e,s.shardKey)}async importRows(t,e,s={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const i=Math.max(1,Math.trunc(s.chunkSize??500)),n=s.toArgs??(c=>({rows:c})),r=e.length;let o=0,a=0;for(let c=0;c<r;c+=i){const u=e.slice(c,c+i),l=a;await this.mutation(t,n(u),{...s.importId===void 0?{}:{mutationId:`${s.importId}:${String(l)}`},shardKey:s.shardKey}),a+=1,o+=u.length,s.onProgress?.({done:o,total:r})}return{chunks:a,imported:o}}async shardTraffic(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=await this.adminFetch(ft,"POST",{table:t});return{failed:e.failed??0,ok:e.ok??0,shards:e.shards??[]}}async listScheduledJobs(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(yt,"GET")).records??[]}async schedulerStatus(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const t=await this.adminFetch(gt,"GET");return{backlog:t.backlog??0,inFlight:t.inFlight??0,pools:t.pools??[]}}async cancelScheduledJob(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return{cancelled:(await this.adminFetch(bt,"POST",{id:t})).cancelled===!0}}async listDeadJobs(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(vt,"GET")).records??[]}async retryDeadJob(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return{retried:(await this.adminFetch(St,"POST",{id:t})).retried===!0}}async removeDeadJob(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return{removed:(await this.adminFetch(kt,"POST",{id:t})).removed===!0}}async listWorkflowInstances(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=new URLSearchParams({name:t.name});t.status!==void 0&&e.set("status",t.status),t.page!==void 0&&e.set("page",String(t.page)),t.perPage!==void 0&&e.set("perPage",String(t.perPage));const s=await this.adminFetch(`${Ct}?${e.toString()}`,"GET");return{configured:s.configured,instances:s.instances??[],page:s.page??1,perPage:s.perPage??t.perPage??0,totalCount:s.totalCount}}async getWorkflowInstance(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=new URLSearchParams({id:t.id,name:t.name}),s=await this.adminFetch(`${Tt}?${e.toString()}`,"GET");return{createdOn:s.createdOn,endedOn:s.endedOn,error:s.error,id:s.id??t.id,output:s.output,params:s.params,startedOn:s.startedOn,status:s.status??"unknown",steps:s.steps??[]}}async setWorkflowInstanceStatus(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return{status:(await this.adminFetch(It,"POST",{action:t.action,id:t.id,name:t.name})).status??"unknown"}}subscribeScheduledJobs(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");if(this.WebSocketImpl===void 0)return()=>{};const e=v(Q(this.url),wt),s=K(this.reconnectOptions),i={connectTimer:void 0,heartbeatTimer:void 0,lastFrameAt:0,socket:void 0};let n,r=!1;const o=()=>{r||(n=setTimeout(u,s.next()))},a=l=>{if(r||this.WebSocketImpl===void 0)return;const m=l===void 0?e:`${e}?token=${encodeURIComponent(l)}`;this.openManagedSocket(i,m,this.connectTimeoutMs,{onClose:()=>{o()},onMessage:y=>{try{const b=JSON.parse(typeof y.data=="string"?y.data:"");b.type==="jobs"&&Array.isArray(b.records)&&t(b.records)}catch{}},onOpen:()=>{s.reset()}})},c=async l=>{let m;try{m=await l()}catch{o();return}a(m)},u=()=>{if(r||this.WebSocketImpl===void 0)return;const{wsToken:l}=this;if(typeof l=="function"){c(l).catch(()=>{});return}a(l)};return u(),()=>{r=!0,n!==void 0&&clearTimeout(n),i.connectTimer!==void 0&&(clearTimeout(i.connectTimer),i.connectTimer=void 0),this.stopHeartbeat(i),i.socket?.close()}}async listFunctions(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(At,"GET")).functions??[]}async getCronJobs(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(Rt,"GET")).jobs??[]}async runCronJob(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=await this.adminFetch(Ot,"POST",{name:t});return{name:e.name??t,ran:e.ran===!0}}async fetchOpenApi(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return await this.adminFetch(Nt,"GET")}async fetchOpenRpc(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return await this.adminFetch(_t,"GET")}async listStorageObjects(t={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=new URLSearchParams;t.prefix!==void 0&&t.prefix!==""&&e.set("prefix",t.prefix),t.cursor!==void 0&&t.cursor!==""&&e.set("cursor",t.cursor),t.limit!==void 0&&e.set("limit",String(t.limit)),t.bucket!==void 0&&t.bucket!==""&&e.set("bucket",t.bucket);const s=e.toString(),i=s===""?A:`${A}?${s}`,n=await this.adminFetch(i,"GET");return{cursor:n.cursor,objects:n.objects??[]}}async deleteStorageObject(t,e){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const s=`${A}?key=${encodeURIComponent(t)}${P(e?.bucket)}`,i=await this.adminFetch(s,"DELETE");return{deleted:i.deleted??!0,key:i.key??t}}async listStorageBuckets(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(Lt,"GET")).buckets??[]}async uploadStorageObject(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=`${A}?key=${encodeURIComponent(t.key)}${P(t.bucket)}`,s=await this.adminFetch(e,"PUT",t.body,t.contentType);return{etag:s.etag,key:s.key??t.key}}async signedStorageUrl(t,e){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const s=e?.expiresInSeconds,i=s===void 0?"":`&expiresIn=${encodeURIComponent(s.toString())}`,n=`${Et}?key=${encodeURIComponent(t)}${i}${P(e?.bucket)}`,r=await this.adminFetch(n,"GET");if(typeof r.url!="string")throw new TypeError("LunoraClient: storage URL endpoint returned no `url`");return r.url}async listGlobalTables(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return await this.adminFetch(Ft,"GET")}async readGlobalTablePage(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=new URLSearchParams({table:t.table});return t.limit!==void 0&&e.set("limit",String(t.limit)),t.offset!==void 0&&e.set("offset",String(t.offset)),t.filters!==void 0&&t.filters.length>0&&e.set("filters",JSON.stringify(t.filters)),await this.adminFetch(`${Pt}?${e.toString()}`,"GET")}async facetGlobalColumn(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=new URLSearchParams({column:t.column,table:t.table});return t.limit!==void 0&&e.set("limit",String(t.limit)),t.filters!==void 0&&t.filters.length>0&&e.set("filters",JSON.stringify(t.filters)),await this.adminFetch(`${Mt}?${e.toString()}`,"GET")}async listVectorIndexes(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(xt,"GET")).indexes??[]}async queryVectorIndex(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch($t,"POST",t)).matches??[]}async queryLogArchive(t={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=await this.adminFetch(Kt,"POST",t);return{nextCursor:e.nextCursor,rows:e.rows??[]}}async listKvNamespaces(){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");return(await this.adminFetch(qt,"GET")).namespaces??[]}async listKvKeys(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=g(Ut,{cursor:t.cursor,limit:t.limit,namespace:t.namespace,prefix:t.prefix});return await this.adminFetch(e,"GET")}async getKvValue(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=g(x,{key:t.key,namespace:t.namespace});return await this.adminFetch(e,"GET")}async putKvValue(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");await this.adminFetch(x,"PUT",t)}async deleteKvKey(t){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=g(x,{key:t.key,namespace:t.namespace});await this.adminFetch(e,"DELETE")}async listAuthUsers(t={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=g(Qt,{filterField:t.filterField,filterValue:t.filterValue,limit:t.limit,offset:t.offset,search:t.search,searchField:t.searchField,sortBy:t.sortBy,sortDirection:t.sortDirection});return await this.adminFetch(e,"GET")}async createAuthUser(t){return await this.adminFetch(Bt,"POST",t)}async setAuthUserRole(t){return await this.adminFetch(Wt,"POST",t)}async banAuthUser(t){return await this.adminFetch(zt,"POST",t)}async unbanAuthUser(t){return await this.adminFetch(Gt,"POST",t)}async setAuthUserPassword(t){await this.adminFetch(Dt,"POST",t)}async removeAuthUser(t){await this.adminFetch(jt,"POST",t)}async impersonateAuthUser(t){return await this.adminFetch(Ht,"POST",t)}async revokeAuthSession(t){await this.adminFetch(Vt,"POST",t)}async revokeAuthUserSessions(t){await this.adminFetch(Jt,"POST",t)}async getAuthCapabilities(){return await this.adminFetch(Xt,"GET")}async updateAuthUser(t){return await this.adminFetch(Yt,"POST",t)}async listAuthAccounts(t){return await this.adminFetch(g(Zt,{userId:t.userId}),"GET")}async unlinkAuthAccount(t){await this.adminFetch(te,"POST",t)}async listAuthPasskeys(t){return await this.adminFetch(g(ee,{userId:t.userId}),"GET")}async deleteAuthPasskey(t){await this.adminFetch(se,"POST",t)}async disableAuthTwoFactor(t){await this.adminFetch(ie,"POST",t)}async listAuthOrganizations(t={}){return await this.adminFetch(g(ne,{limit:t.limit,offset:t.offset}),"GET")}async listAuthOrgMembers(t){const e=g(re,{limit:t.limit,offset:t.offset,organizationId:t.organizationId});return await this.adminFetch(e,"GET")}async listAuthOrgInvitations(t){const e=g(oe,{limit:t.limit,offset:t.offset,organizationId:t.organizationId});return await this.adminFetch(e,"GET")}async removeAuthOrgMember(t){await this.adminFetch(ae,"POST",t)}async cancelAuthOrgInvitation(t){await this.adminFetch(ce,"POST",t)}async getAuthConfig(){return await this.adminFetch(he,"GET")}async createAuthOrganization(t){return await this.adminFetch(de,"POST",t)}async updateAuthOrganization(t){return await this.adminFetch(ue,"POST",t)}async deleteAuthOrganization(t){await this.adminFetch(le,"POST",t)}async addAuthOrgMember(t){return await this.adminFetch(me,"POST",t)}async inviteAuthOrgMember(t){return await this.adminFetch(pe,"POST",t)}async setAuthOrgMemberRole(t){return await this.adminFetch(fe,"POST",t)}async listAuthOrgTeams(t){const e=g(ye,{limit:t.limit,offset:t.offset,organizationId:t.organizationId});return await this.adminFetch(e,"GET")}async createAuthOrgTeam(t){return await this.adminFetch(ge,"POST",t)}async updateAuthOrgTeam(t){return await this.adminFetch(we,"POST",t)}async removeAuthOrgTeam(t){await this.adminFetch(be,"POST",t)}async listAuthOrgTeamMembers(t){const e=g(ve,{limit:t.limit,offset:t.offset,teamId:t.teamId});return await this.adminFetch(e,"GET")}async addAuthOrgTeamMember(t){return await this.adminFetch(Se,"POST",t)}async removeAuthOrgTeamMember(t){await this.adminFetch(ke,"POST",t)}async listAuthOrgRoles(t){const e=g(Ce,{limit:t.limit,offset:t.offset,organizationId:t.organizationId});return await this.adminFetch(e,"GET")}async createAuthOrgRole(t){return await this.adminFetch(Te,"POST",t)}async updateAuthOrgRole(t){return await this.adminFetch(Ie,"POST",t)}async deleteAuthOrgRole(t){await this.adminFetch(Ee,"POST",t)}async listAuthSessions(t={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const e=new URLSearchParams;t.userId!==void 0&&t.userId!==""&&e.set("userId",t.userId),t.limit!==void 0&&e.set("limit",String(t.limit)),t.offset!==void 0&&e.set("offset",String(t.offset));const s=e.toString();return await this.adminFetch(s===""?U:`${U}?${s}`,"GET")}subscribe(t,e,s,i={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");const n=e??{},r=k.key(t.__lunoraRef,n,i.shardKey);let o=this.subscriptions.get(r);const a=s,c=i.onError;if(!o){this.nextSubId+=1;const l=`sub_${this.nextSubId.toString()}`,m=I(n),y=this.takeHydratedCache(t.__lunoraRef,m,i.shardKey);o={acked:!1,args:n,argsKey:m,callbacks:new Set,checkpointCallbacks:new Set,errorCallbacks:new Set,fn:t,id:l,lastValue:y?.value,optimisticLayers:[],serverBase:y?.value,serverCursor:y?.serverCursor,shardKey:i.shardKey,...y?.serverEpoch===void 0?{}:{serverEpoch:y.serverEpoch}},this.subscriptions.add(o)}if(o.callbacks.add(a),c&&o.errorCallbacks.add(c),i.onCheckpoint&&o.checkpointCallbacks.add(i.onCheckpoint),o.lastValue!==void 0)try{a(o.lastValue)}catch{}this.ensureSocket(i.shardKey),this.sendSubscribeIfOpen(o);const u=o;return()=>{if(u.callbacks.delete(a),c&&u.errorCallbacks.delete(c),i.onCheckpoint&&u.checkpointCallbacks.delete(i.onCheckpoint),u.callbacks.size===0){const l=this.getConnection(u.shardKey);!(l&&f(l,{id:u.id,type:"unsubscribe"}))&&l&&l.pendingUnsubscribes.push({id:u.id,type:"unsubscribe"}),this.subscriptions.remove(u)}}}subscribeShape(t,e,s={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");this.nextShapeId+=1;const i=`shape_${this.nextShapeId.toString()}`,n={args:t.args,callbacks:new Set([e]),errorCallbacks:s.onError?new Set([s.onError]):new Set,id:i,name:t.name,onCheckpoint:s.onCheckpoint,rows:new Map,shardKey:s.shardKey,wireArgs:t.args===void 0?void 0:S(t.args,`shape args for '${t.name}'`)};return this.shapeSubscriptions.set(i,n),this.ensureSocket(s.shardKey),this.sendShapeSubscribeIfOpen(n),()=>{this.shapeSubscriptions.delete(i);const r=this.getConnection(n.shardKey);!(r&&f(r,{id:i,type:"shape_unsubscribe"}))&&r&&r.pendingUnsubscribes.push({id:i,type:"shape_unsubscribe"})}}stream(t,e,s={}){if(this.closed)throw new d("INTERNAL","LunoraClient is closed");if(this.WebSocketImpl===void 0)throw new d("INTERNAL","LunoraClient: streams require a WebSocket implementation");this.nextStreamId+=1;const i=`stream_${this.nextStreamId.toString()}`,{shardKey:n}=s,r=e??{},{handle:o,iterable:a}=rt({maxBuffer:s.maxBuffer,onCancel:()=>{const l=this.getConnection(n);l&&f(l,{id:i,type:"unsubscribe"}),this.streams.delete(i)}});this.streams.set(i,{handle:o,shardKey:n}),this.ensureSocket(n);const c=this.getConnection(n),u={id:i,query:{args:S(r,`stream args for '${t.__lunoraRef}'`),functionPath:t.__lunoraRef,shardKey:n},type:"stream"};if(!(c?.wsState==="open"&&f(c,u))&&c){for(c.pendingStreams=c.pendingStreams??[];c.pendingStreams.length>=pt;){const l=c.pendingStreams.shift()?.id,m=l?this.streams.get(l):void 0;m&&(m.handle.fail(new d("STREAM_QUEUE_OVERFLOW","stream-start frame evicted while socket was unreachable")),this.streams.delete(l))}c.pendingStreams.push(u)}return a}httpStream(t,e,s={}){if(this.closed)throw new d("CLIENT_CLOSED","LunoraClient is closed");if(!this.fetchImpl)throw new d("INTERNAL","LunoraClient: no `fetch` implementation available");const i={...this.authToken?{authorization:`Bearer ${this.authToken}`}:{},...s.headers};return X(t,e,{baseUrl:this.url,fetch:this.fetchImpl,headers:i,maxBuffer:s.maxBuffer,signal:s.signal})}close(){this.closed=!0,this.outboxLeaderRelease?.(),this.outboxLeaderRelease=void 0;for(const t of this.streams.values())t.handle.fail(new d("CLIENT_CLOSED","LunoraClient closed"));this.streams.clear();for(const t of this.connections.values())this.teardownConnection(t);this.offlineQueue.clear(),this.queuedIdentities.clear(),this.cacheFlushTimer!==void 0&&(clearTimeout(this.cacheFlushTimer),this.cacheFlushTimer=void 0),this.pendingCacheWrites.size>0&&this.flushQueryCacheWrites().catch(()=>{}),this.authTokenListeners.clear(),this.statusListeners.clear(),this.tokenExpiredListeners.clear(),this.mutationSettledListeners.clear(),this.pendingChangeListeners.clear(),this.whisperHandlers.clear(),this.shapeSubscriptions.clear(),this.pokeBuffers.clear(),this.tabCoordinator?.stop(),this.tabCoordinator=void 0}teardownConnection(t){if(t.reconnectTimer!==void 0&&(clearTimeout(t.reconnectTimer),t.reconnectTimer=void 0),t.connectTimer!==void 0&&(clearTimeout(t.connectTimer),t.connectTimer=void 0),this.stopHeartbeat(t),t.socket){try{t.socket.close()}catch{}t.socket=void 0}t.wsState="closed"}createTabCoordinator(){const t=`lunora-bridge::${this.url}::${this.identityFingerprint()??"anon"}`;return new H({channelName:t,onBecomeLeader:()=>{for(const e of this.subscriptions.all())this.ensureSocket(e.shardKey),this.sendSubscribeIfOpen(e);this.tabCoordinator?.broadcastConnectionStatus(this.computeStatus(),this.identityFingerprint())},onStopBeingLeader:()=>{for(const[e,s]of this.connections)this.teardownConnection(s),this.connections.delete(e);this.leaderStatus=void 0,this.leaderWasEverConnected=!1},onConnectionStatus:(e,s)=>{if(s!==void 0&&s!==this.identityFingerprint())return;const i=e==="connected"&&this.leaderStatus!=="connected";this.leaderStatus=e,e==="connected"&&(this.leaderWasEverConnected=!0),this.emitConnectionStatus(),i&&this.flushAllOfflineQueues()},onSubscriptionData:(e,s,i,n,r)=>{if(r!==void 0&&r!==this.identityFingerprint())return;const o=this.subscriptions.get(e);if(!o)return;if(o.serverBase=s,i!==void 0){o.serverCursor=i,n!==void 0&&(o.serverEpoch=n),N(o,i),E(o,o.optimisticLayers.length===0?s:L(s,o.optimisticLayers));return}const a=o.optimisticLayers.length===0?s:L(s,o.optimisticLayers);E(o,a)},onSubscriptionError:(e,s)=>{const i=this.subscriptions.get(e);i&&R(i.errorCallbacks,s)},onSubscriptionSettled:(e,s,i,n,r,o)=>{if(o!==void 0&&o!==this.identityFingerprint())return;const a=this.subscriptions.get(e);if(a){this.ackAndAdvanceCursor(a,s,i),n!==void 0&&r===this.clientId&&(a.lastMutationId=Math.max(a.lastMutationId??0,n));for(const c of a.checkpointCallbacks)c({checkpoint:a.serverCursor,mutationId:a.lastMutationId})}}})}async enqueueOfflineMutation(t,e,s,i,n,r,o){const a=this.identityFingerprint();if(this.outbox){this.outboxMutationCounter+=1;const c=this.outboxMutationCounter;try{await this.outbox.enqueue({args:e,clientId:this.clientId,functionPath:t.__lunoraRef,idempotencyKey:`${this.clientId}:${String(c)}`,identity:a,mutationId:c,shardKey:s})}catch(u){throw M(n),u instanceof Error?u:new Error(String(u))}for(const u of r)u(void 0);return}return new Promise((c,u)=>{const l={args:e,functionPath:t.__lunoraRef,liveAwaiter:!0,id:i,identity:a,precondition:o,onCommit:m=>{for(const y of r)y(m)},reject:m=>{this.queuedIdentities.delete(i),M(n),u(m instanceof Error?m:new Error(String(m)))},resolve:c,shardKey:s};this.offlineQueue.enqueue(l),this.queuedOfflineShardKeys.add(s),l.id!==void 0&&this.queuedIdentities.set(l.id,a)})}async hydratePersistedQueue(){try{const t=await this.offlineQueue.hydrate();for(const e of t)this.queuedOfflineShardKeys.add(e),this.ensureSocket(e)}catch{}}hydrateAsOutboxLeader(){const t=()=>{this.hydratePersistedQueue().catch(()=>{})},e=globalThis.navigator?.locks;if(!e){t();return}e.request(`lunora:outbox-leader:${this.url}`,()=>(this.closed||t(),new Promise(s=>{if(this.closed){s();return}this.outboxLeaderRelease=s}))).catch(t)}async hydrateQueryCache(){if(this.queryCache)try{const t=await this.queryCache.load();for(const{key:e,...s}of t){if(et(this.persistenceVersion,s.version)){this.queryCache.remove(e).catch(()=>{});continue}this.hydratedQueryCache.set(e,s)}}catch{}}takeHydratedCache(t,e,s){const i=_(t,e,s),n=this.hydratedQueryCache.get(i);if(n!==void 0)return this.hydratedQueryCache.delete(i),n.identity===this.identityFingerprint()?n:void 0}persistQueryValue(t){const e=t.serverBase;if(!this.queryCache||e===void 0)return;const s=_(t.fn.__lunoraRef,t.argsKey,t.shardKey);this.pendingCacheWrites.set(s,{identity:this.identityFingerprint(),serverCursor:t.serverCursor,ts:Date.now(),value:e,...t.serverEpoch===void 0?{}:{serverEpoch:t.serverEpoch},...this.persistenceVersion===void 0?{}:{version:this.persistenceVersion}}),this.cacheFlushTimer??=setTimeout(()=>{this.flushQueryCacheWrites().catch(()=>{})},mt)}async flushQueryCacheWrites(){this.cacheFlushTimer=void 0;const{queryCache:t}=this;if(!t){this.pendingCacheWrites.clear();return}const e=[...this.pendingCacheWrites.entries()];this.pendingCacheWrites.clear(),await Promise.allSettled(e.map(([s,i])=>t.put(s,i)))}computeStatus(){if(this.tabCoordinator&&!this.tabCoordinator.isLeader())return this.leaderStatus??"idle";const t=[...this.connections.values()];return t.length===0?"idle":t.some(e=>e.wsState==="open")?"connected":t.some(e=>e.wsState==="connecting")?"connecting":"offline"}emitConnectionStatus(){const t=this.computeStatus();t!==this.lastStatus&&(this.lastStatus=t,this.statusListeners.emit(t),this.tabCoordinator?.isLeader()&&this.tabCoordinator.broadcastConnectionStatus(t,this.identityFingerprint()))}emitItemSettled(t,e,s){this.mutationSettledListeners.emit({args:t.args,code:s===void 0?void 0:s.code,error:s,functionPath:t.functionPath,hadAwaiter:t.liveAwaiter??!1,id:t.id??"",shardKey:t.shardKey,status:e})}applyOptimisticUpdates(t,e,s,i){const n=[],r=[];if(!i)return{confirms:n,rollbacks:r};const o=k.key(t,e,s),a=this.subscriptions.get(o);if(a){const c=Y(a,i);c&&(n.push(c.confirm),r.push(c.rollback))}return{confirms:n,rollbacks:r}}applyOptimisticUpdate(t,e,s,i,n){const{confirms:r,rollbacks:o,store:a}=Z(this.subscriptions,s);try{t(a,e)}catch{for(let c=o.length-1;c>=0;c-=1)o[c]?.();return}i.push(...o),n.push(...r)}getConnection(t){return this.connections.get(p(t))}connectionGateState(t){if(this.tabCoordinator&&!this.tabCoordinator.isLeader()){let s="idle";return this.leaderStatus==="connected"?s="open":this.leaderStatus==="connecting"&&(s="connecting"),{hasSocket:!1,wasEverConnected:this.leaderWasEverConnected,wsState:s}}const e=this.getConnection(t);return{hasSocket:e?.socket!==void 0,wasEverConnected:e?.wasEverConnected??!1,wsState:e?.wsState??"idle"}}getOrCreateConnection(t){const e=p(t);let s=this.connections.get(e);return s||(s={connectTimer:void 0,heartbeatTimer:void 0,lastFrameAt:0,pendingUnsubscribes:[],reconnect:K(this.reconnectOptions),reconnectTimer:void 0,shardKey:t,socket:void 0,wasEverConnected:!1,wsState:"idle"},this.connections.set(e,s)),s}wsUrlFor(t,e){const s=[];if(t!==void 0&&s.push(`shard=${encodeURIComponent(t)}`),e!==void 0&&s.push(`token=${encodeURIComponent(e)}`),s.length===0)return this.wsUrl;const i=this.wsUrl.includes("?")?"&":"?";return`${this.wsUrl}${i}${s.join("&")}`}rpcRequestHeaders(t){const e={"content-type":"application/json"};if(this.authToken&&(e.authorization=`Bearer ${this.authToken}`),t.mutationId&&(e["x-lunora-mutation-id"]=t.mutationId),t.clientId!==void 0&&(e["x-lunora-client-id"]=t.clientId),t.clientSeq!==void 0&&(e["x-lunora-client-seq"]=t.clientSeq.toString()),t.attachBookmark){const s=this.bookmark.get();s&&(e["x-d1-bookmark"]=s)}return e}async rpc(t,e,s,i={}){if(!this.fetchImpl)throw new d("INTERNAL","LunoraClient: no `fetch` implementation available");const n=this.rpcRequestHeaders(i),r=await this.fetchImpl(v(this.url,at),{body:JSON.stringify({args:S(e,`args for '${t}'`),functionPath:t,shardKey:s}),headers:n,method:"POST"});if(i.captureBookmark){const a=r.headers.get("x-d1-bookmark");a&&this.bookmark.set(a)}let o;try{o=await r.json()}catch{const a=r.statusText?` ${r.statusText}`:"";throw new d("INTERNAL",`LunoraClient: response was not JSON (status ${r.status.toString()}${a})`)}if("error"in o)throw T(o.error);if(!r.ok){const a=r.statusText?` ${r.statusText}`:"";throw new d("INTERNAL",`LunoraClient: request failed (status ${r.status.toString()}${a})`)}return i.onMutationAck?.(o.lastMutationId),i.onCommitCursor?.(o.commitCursor),w(o.result)}async adminFetch(t,e,s,i){if(!this.fetchImpl)throw new d("INTERNAL","LunoraClient: no `fetch` implementation available");const n={};this.authToken&&(n.authorization=`Bearer ${this.authToken}`);const r=s instanceof ArrayBuffer||s instanceof Blob;let o;s===void 0?o=void 0:r?(o=s,i!==void 0&&(n["content-type"]=i)):(o=JSON.stringify(s),n["content-type"]="application/json");const a=await this.fetchImpl(v(this.url,t),{body:o,headers:n,method:e});let c;try{c=await a.json()}catch{const u=a.statusText?` ${a.statusText}`:"";throw new d("INTERNAL",`LunoraClient: response was not JSON (status ${a.status.toString()}${u})`)}if(typeof c=="object"&&c!==null&&"error"in c){const u=c.error,l=new Error(u.message??"admin request failed");throw l.code=u.code,l}if(!a.ok){const u=a.statusText?` ${a.statusText}`:"";throw new d("INTERNAL",`LunoraClient: admin request failed (status ${a.status.toString()}${u})`)}return c}effectiveConnectionContext(t){const e=this.connectionContextHolders.get(t);return e&&e.length>0?e[e.length-1]?.context:this.connectionContexts.get(t)??this.defaultConnectionContext}refreshConnectionContext(t){const e=this.connections.get(t);e?.wsState==="open"&&this.sendConnectEnvelope(e)}sendConnectEnvelope(t){const e=this.effectiveConnectionContext(p(t.shardKey));f(t,{clientId:this.clientId,id:"connect",type:"connect",...e===void 0?{}:{context:e}})}resendShapeSubscriptions(t){for(const e of this.shapeSubscriptions.values())p(e.shardKey)===p(t)&&this.sendShapeSubscribeIfOpen(e)}ensureSocket(t){if(this.closed||this.WebSocketImpl===void 0||this.tabCoordinator&&!this.tabCoordinator.isLeader())return;const e=this.getOrCreateConnection(t);if(!(e.wsState==="open"||e.wsState==="connecting")){if(e.wsState="connecting",this.emitConnectionStatus(),typeof this.wsToken=="function"){this.openSocketWithProvidedToken(e,t,this.wsToken).catch(()=>{});return}this.openSocket(e,t,this.wsToken)}}async openSocketWithProvidedToken(t,e,s){let i;try{i=await s()}catch{this.handleDisconnect(t);return}this.closed||this.WebSocketImpl===void 0||t.wsState!=="connecting"||t.socket!==void 0||this.openSocket(t,e,i)}openManagedSocket(t,e,s,i){const{WebSocketImpl:n}=this;if(n===void 0)throw new d("INTERNAL","no WebSocket implementation available");const r=new n(e);t.socket=r;const o=()=>{this.stopHeartbeat(t),t.connectTimer!==void 0&&(clearTimeout(t.connectTimer),t.connectTimer=void 0),t.socket===r&&(t.socket=void 0)},a=c=>{o(),i.onClose(c)};s>0&&(t.connectTimer=setTimeout(()=>{if(t.connectTimer=void 0,t.socket===r){try{r.close()}catch{}a()}},s)),r.addEventListener("open",()=>{t.socket===r&&(t.connectTimer!==void 0&&(clearTimeout(t.connectTimer),t.connectTimer=void 0),t.lastFrameAt=Date.now(),i.onOpen(),this.startHeartbeat(t,a))}),r.addEventListener("message",c=>{t.socket===r&&(t.lastFrameAt=Date.now(),i.onMessage(c))}),r.addEventListener("close",c=>{t.socket===r&&a(c)}),r.addEventListener("error",()=>{t.socket===r&&a()})}openSocket(t,e,s){this.WebSocketImpl!==void 0&&this.openManagedSocket(t,this.wsUrlFor(e,s),this.connectTimeoutMs,{onClose:i=>{i?.code===4001&&this.notifyTokenExpired(),this.handleDisconnect(t)},onMessage:i=>{this.handleServerMessage(i.data,e)},onOpen:()=>{t.wsState="open",t.wasEverConnected=!0,t.reconnect.reset(),this.emitConnectionStatus(),this.sendConnectEnvelope(t),this.markShardPendingAck(e);for(const n of this.subscriptions.all())p(n.shardKey)===p(e)&&this.sendSubscribeIfOpen(n);if(this.resendShapeSubscriptions(e),t.pendingUnsubscribes.length>0){const n=t.pendingUnsubscribes;t.pendingUnsubscribes=[];for(const{id:r,type:o}of n)f(t,{id:r,type:o})}if(t.pendingStreams&&t.pendingStreams.length>0){const n=t.pendingStreams;t.pendingStreams=[];for(const r of n)f(t,r)}const i=this.whisperHandlers.get(p(e));if(i)for(const n of i.keys())f(t,{topic:n,type:"whisper_subscribe"});this.flushOfflineQueue(e).catch(()=>{})}})}handleDisconnect(t){if(this.closed||t.wsState==="idle"||t.wsState==="closed")return;this.stopHeartbeat(t),t.connectTimer!==void 0&&(clearTimeout(t.connectTimer),t.connectTimer=void 0),t.socket=void 0,t.wsState="idle",this.emitConnectionStatus(),this.markShardPendingAck(t.shardKey);const e=new Set((t.pendingStreams??[]).map(i=>i.id));for(const[i,n]of this.streams)p(n.shardKey)===p(t.shardKey)&&!e.has(i)&&(n.handle.fail(new d("STREAM_DISCONNECTED","stream terminated: WebSocket disconnected")),this.streams.delete(i));if(this.WebSocketImpl===void 0)return;const s=t.reconnect.next();t.reconnectTimer=setTimeout(()=>{t.reconnectTimer=void 0,this.ensureSocket(t.shardKey)},s)}startHeartbeat(t,e){this.stopHeartbeat(t),!(this.heartbeatIntervalMs<=0)&&(t.heartbeatTimer=setInterval(()=>{if(!t.socket)return;const{socket:s}=t;if(Date.now()-t.lastFrameAt>this.heartbeatIntervalMs*2.5){try{s.close()}catch{}e();return}try{s.send(dt)}catch{}},this.heartbeatIntervalMs))}stopHeartbeat(t){t.heartbeatTimer!==void 0&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=void 0)}markShardPendingAck(t){const e=p(t);for(const s of this.subscriptions.all())p(s.shardKey)===e&&(s.acked=!1)}sendSubscribeIfOpen(t){const e=this.getConnection(t.shardKey);if(e?.wsState!=="open"||t.acked)return;const s=t.fn.__lunoraTable??t.fn.__lunoraRef;f(e,{id:t.id,query:{args:W(t.args),functionPath:t.fn.__lunoraRef,table:s,...t.serverCursor===void 0?{}:{sinceSeq:t.serverCursor},...t.serverEpoch===void 0?{}:{sinceEpoch:t.serverEpoch}},type:"subscribe"})}sendShapeSubscribeIfOpen(t){const e=this.getConnection(t.shardKey);e?.wsState==="open"&&f(e,{id:t.id,shape:{name:t.name,...t.wireArgs===void 0?{}:{args:t.wireArgs}},type:"shape_subscribe",...t.serverCursor===void 0?{}:{sinceCheckpoint:t.serverCursor},...t.serverEpoch===void 0?{}:{sinceEpoch:t.serverEpoch}})}handleServerMessage(t,e){const s=Ne(t);if(s===void 0)return;let i;try{i=JSON.parse(s)}catch{return}switch(i.type){case"ack":{const n=this.subscriptions.getById(i.id);n&&(n.acked=!0);return}case"chunk":{const{data:n,id:r}=i;this.streams.get(r)?.handle.push(w(n));return}case"complete":{this.handleCompleteMessage(i.id);return}case"data":case"delta":{this.handleDataMessage(i);return}case"error":{this.handleErrorMessage(i);break}case"pokeEnd":{this.handlePokeEnd(i);break}case"pokePart":{this.handlePokePart(i);break}case"pokeStart":{this.handlePokeStart(i);break}case"resume":{this.handleResumeMessage(i);break}case"settled":{this.handleSettledMessage(i);break}case"whisper":{this.dispatchWhisper(i,e);break}}}handleErrorMessage(t){if(t.error?.code==="TOKEN_EXPIRED"){this.notifyTokenExpired();return}const{id:e}=t,s=e===void 0?void 0:this.streams.get(e);if(s&&e!==void 0){s.handle.fail(Re(t)),this.streams.delete(e);return}const i=e===void 0?void 0:this.subscriptions.getById(e);if(i){R(i.errorCallbacks,B(t));return}const n=e===void 0?void 0:this.shapeSubscriptions.get(e);n&&R(n.errorCallbacks,B(t))}handlePokeStart(t){ot(this.pokeBuffers,z.MAX_POKE_BUFFERS),this.pokeBuffers.set(t.pokeId,{baseCheckpoint:t.baseCheckpoint,epoch:t.epoch,lastMutationId:new Map,parts:new Map})}handlePokePart(t){const e=this.pokeBuffers.get(t.pokeId);if(!e)return;const s=e.parts.get(t.shapeId)??[];for(const i of t.rowsPatch)s.push(i.value===void 0?i:{...i,value:w(i.value)});e.parts.set(t.shapeId,s),t.lastMutationId!==void 0&&e.lastMutationId.set(t.shapeId,t.lastMutationId)}handlePokeEnd(t){const e=this.pokeBuffers.get(t.pokeId);if(e){this.pokeBuffers.delete(t.pokeId);for(const[s,i]of e.parts){const n=this.shapeSubscriptions.get(s);if(!n)continue;const r=e.epoch!==void 0&&n.serverEpoch!==void 0&&e.epoch!==n.serverEpoch,o=e.baseCheckpoint!==void 0&&n.serverCursor!==void 0&&n.serverCursor!==e.baseCheckpoint;if(r||o){n.rows.clear(),n.serverCursor=void 0,n.serverEpoch=void 0,this.emitShapeRows(n),this.sendShapeSubscribeIfOpen(n);continue}ht(n.rows,i),t.checkpoint!==void 0&&(n.serverCursor=t.checkpoint),t.epoch!==void 0&&(n.serverEpoch=t.epoch);const a=e.lastMutationId.get(s);a!==void 0&&(n.lastMutationId=a),this.emitShapeRows(n),n.onCheckpoint?.({checkpoint:n.serverCursor,mutationId:n.lastMutationId})}}}emitShapeRows(t){const e=[...t.rows.values()];for(const s of t.callbacks)try{s(e)}catch{}}handleDataMessage(t){const{id:e}=t,s=e?this.subscriptions.getById(e):void 0;if(!s)return;const i=this.resolveDataPayload(t,s);if(s.serverBase=i,t.cursor!==void 0&&(s.serverCursor=t.cursor),t.epoch!==void 0&&(s.serverEpoch=t.epoch),t.lastMutationId!==void 0){s.lastMutationId=Math.max(s.lastMutationId??0,t.lastMutationId);for(const n of s.checkpointCallbacks)n({checkpoint:s.serverCursor,mutationId:s.lastMutationId})}if(this.persistQueryValue(s),N(s,s.serverCursor),E(s,s.optimisticLayers.length===0?i:L(i,s.optimisticLayers)),this.tabCoordinator?.isLeader()){const n=k.key(s.fn.__lunoraRef,s.args,s.shardKey);this.tabCoordinator.broadcastSubscriptionData(n,i,s.serverCursor,s.serverEpoch,this.identityFingerprint())}}handleResumeMessage(t){const e=this.subscriptions.getById(t.id);e&&this.ackAndAdvanceCursor(e,t.cursor,t.epoch)}handleSettledMessage(t){const e=this.subscriptions.getById(t.id);if(e){this.ackAndAdvanceCursor(e,t.cursor,t.epoch),t.lastMutationId!==void 0&&(e.lastMutationId=t.lastMutationId);for(const s of e.checkpointCallbacks)s({checkpoint:e.serverCursor,mutationId:e.lastMutationId});if(this.tabCoordinator?.isLeader()){const s=k.key(e.fn.__lunoraRef,e.args,e.shardKey);this.tabCoordinator.broadcastSubscriptionSettled(s,e.serverCursor,e.serverEpoch,e.lastMutationId,this.clientId,this.identityFingerprint())}}}ackAndAdvanceCursor(t,e,s){t.acked=!0,(e!==void 0&&e!==t.serverCursor||s!==void 0&&s!==t.serverEpoch)&&(e!==void 0&&(t.serverCursor=e),s!==void 0&&(t.serverEpoch=s),this.persistQueryValue(t),N(t,t.serverCursor)&&E(t,L(t.serverBase,t.optimisticLayers)))}resolveDataPayload(t,e){if("data"in t&&t.data!==void 0)return w(t.data);const s=w(t.delta);if(V(s)&&e.serverBase!==void 0){const i=J(e.serverBase,s);if(i!==void 0)return i}return s}dispatchWhisper(t,e){const s=this.whisperHandlers.get(p(e))?.get(t.topic);if(!s)return;const i=w(t.data);for(const n of s)try{n(i,t.from)}catch{}}notifyTokenExpired(){this.tokenExpiredListeners.emit()}handleCompleteMessage(t){const e=this.streams.get(t);if(e){e.handle.complete(),this.streams.delete(t);return}const s=this.subscriptions.getById(t);s&&(s.acked=!1,R(s.errorCallbacks,{code:"SUBSCRIPTION_CANCELLED",message:"subscription was cancelled by the server"}))}unpersist(t){t&&this.persistence?.remove(t).catch(e=>{st(this.onPersistenceError,"remove",e,t)})}identityFingerprint(){if(this.authSubject!==void 0)return this.authSubject===null?null:`subj:${this.authSubject}`;const t=this.authToken;return t===null?null:this.hashToken(t)}hashToken(t){let e=2166136261,s=5381;for(let i=0;i<t.length;i+=1){const n=t.charCodeAt(i);e^=n,e=Math.imul(e,16777619),s=Math.imul(s,33)+n}return`${t.length.toString(36)}:${(e>>>0).toString(36)}:${(s>>>0).toString(36)}`}isSameCredentialUnderTokenHash(t){if(t===null||t.startsWith("subj:"))return!1;const e=this.authToken;return e===null?!1:this.hashToken(e)===t}rejectQueuedForIdentityChange(){const t=this.offlineQueue.drain();for(const e of t){this.queuedIdentities.delete(e.id??""),this.unpersist(e.id);const s=new Error("offline mutation discarded: auth identity changed before replay");s.code="OFFLINE_IDENTITY_CHANGED",e.reject(s),this.emitItemSettled(e,"rejected",s)}this.clearQueryCacheForIdentityChange()}restampQueuedIdentity(t,e){for(const[s,i]of this.queuedIdentities)i===t&&this.queuedIdentities.set(s,e)}clearQueryCacheForIdentityChange(){this.cacheFlushTimer!==void 0&&(clearTimeout(this.cacheFlushTimer),this.cacheFlushTimer=void 0),this.pendingCacheWrites.clear(),this.hydratedQueryCache.clear(),this.queryCache?.clear().catch(()=>{})}flushAllOfflineQueues(){for(const t of this.queuedOfflineShardKeys)this.flushOfflineQueue(t).catch(()=>{})}async flushOfflineQueue(t){const e=this.offlineQueue.drainConflict();for(const c of e){this.unpersist(c.id),this.queuedIdentities.delete(c.id??"");const u=new Error("offline mutation skipped: precondition failed before replay");u.code="OFFLINE_PRECONDITION_FAILED",this.emitItemSettled(c,"rejected",u)}const s=p(t),i=this.offlineQueue.drain(c=>p(c.shardKey)===s);if(i.length===0)return;const n=this.identityFingerprint(),r=[];for(const c of i)this.passesReplayIdentityGate(c,n)&&r.push(c);if(r.length===0)return;const o=this.encodableOrSettleTerminal(r);if(o.length===0)return;if(o.length===1){await this.replaySequential(o);return}const a=[];for(let c=0;c<o.length;c+=F){const u=o.slice(c,c+F),l=await this.replayBatched(u);if(a.push(...l.requeue),l.stop){a.push(...o.slice(c+F));break}}a.length>0&&this.offlineQueue.requeue(a)}encodableOrSettleTerminal(t){const e=[];for(const s of t)try{S(s.args,`args for '${s.functionPath}'`),e.push(s)}catch(i){this.settleReplayTerminal(s,i instanceof Error?i:new Error(String(i)))}return e}passesReplayIdentityGate(t,e){const s=t.id===void 0?void 0:this.queuedIdentities.get(t.id),i=s===void 0?t.identity:s;if(i!==void 0&&i!==e&&!this.isSameCredentialUnderTokenHash(i)){this.queuedIdentities.delete(t.id??""),this.unpersist(t.id);const n=new Error("offline mutation skipped: auth identity changed before replay");return n.code="OFFLINE_IDENTITY_CHANGED",t.reject(n),this.emitItemSettled(t,"rejected",n),!1}return this.queuedIdentities.delete(t.id??""),!0}settleReplaySuccess(t,e,s){this.unpersist(t.id),t.onCommit?.(s),t.resolve(e),this.emitItemSettled(t,"committed")}settleReplayTerminal(t,e){this.unpersist(t.id),t.reject(e),this.emitItemSettled(t,"rejected",e)}async replaySequential(t){for(let e=0;e<t.length;e+=1){const s=t[e];if(s)try{let i;const n=await this.rpc(s.functionPath,s.args,s.shardKey,{captureBookmark:!0,mutationId:s.id,onCommitCursor:r=>{i=r}});this.settleReplaySuccess(s,n,i)}catch(i){if(i.code!==void 0){this.settleReplayTerminal(s,i);continue}this.offlineQueue.requeue(t.slice(e));return}}}async replayBatched(t){if(!this.fetchImpl)return{requeue:t,stop:!0};let e;try{e=await this.fetchImpl(v(this.url,q),{body:JSON.stringify({calls:t.map((n,r)=>({args:S(n.args,`args for '${n.functionPath}'`),functionPath:n.functionPath,id:r,mutationId:n.id,shardKey:n.shardKey}))}),headers:this.rpcRequestHeaders({attachBookmark:!0}),method:"POST"})}catch{return{requeue:t,stop:!0}}const s=e.headers.get("x-d1-bookmark");s&&this.bookmark.set(s);let i;try{i=await e.json()}catch{return{requeue:t,stop:!0}}if(!i.results){if(i.error){const n=T(i.error);for(const r of t)this.settleReplayTerminal(r,n);return{requeue:[],stop:!1}}return{requeue:t,stop:!0}}return{requeue:this.settleReplayBatchSlots(t,i.results),stop:!1}}settleReplayBatchSlots(t,e){const s=new Map;for(const n of e)typeof n.id=="number"&&n.body!==void 0&&s.set(n.id,n.body);const i=[];for(const[n,r]of t.entries()){const o=s.get(n);o===void 0?i.push(r):"error"in o?Fe.has(o.error.code)?i.push(r):this.settleReplayTerminal(r,T(o.error)):this.settleReplaySuccess(r,w(o.result),o.commitCursor)}return i}}export{z as LunoraClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{h as s,u as o,s as t}from"./offline-queue-BeWay5u5.mjs";export{s as OfflineQueue,o as nextId,t as reportPersistenceError};
|