@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.
@@ -1,4 +1,4 @@
1
- import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-DLcz4GRz.mjs";
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. */
@@ -1,4 +1,4 @@
1
- import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-DLcz4GRz.js";
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-DLcz4GRz.mjs";
2
- export { type h as BatchSlot, C as CONFLICT_ERROR_CODE, type i as CachedQuery, type j as ClientDebugShard, type k as ClientDebugSnapshot, type l as ClientDebugSubscription, type m as ClientMessage, type n as ClientQueryRef, type o as ClientShapeSubscribeMessage, type p as ClientShapeUnsubscribeMessage, type q as ConnectionStatus, 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-DLcz4GRz.mjs";
3
- export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-C1M_N5mE.mjs";
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-DLcz4GRz.js";
2
- export { type h as BatchSlot, C as CONFLICT_ERROR_CODE, type i as CachedQuery, type j as ClientDebugShard, type k as ClientDebugSnapshot, type l as ClientDebugSubscription, type m as ClientMessage, type n as ClientQueryRef, type o as ClientShapeSubscribeMessage, type p as ClientShapeUnsubscribeMessage, type q as ConnectionStatus, 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-DLcz4GRz.js";
3
- export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-hkFC0Uno.js";
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 o,maxSeq as t,reconcileOptimistic as a}from"./packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-D96iRJDY.mjs";import{createAsyncStoragePersistence as n}from"./packem_shared/createAsyncStoragePersistence-BRLqeXsC.mjs";import{default as p}from"./packem_shared/createInMemoryBookmarkStorage-DCd5Ej-t.mjs";import{createClientQuery as f}from"./packem_shared/createClientQuery-CrdRoicO.mjs";import{TabCoordinator as s}from"./packem_shared/TabCoordinator-ijLic02x.mjs";import{applyDelta as d,isMutationDelta as l}from"./packem_shared/applyDelta-DXqQORbM.mjs";import{CONFLICT_ERROR_CODE as y,getErrorCode as E,getRetryAfterMs as C,isConflictError as S,isForbiddenError as A,isRateLimitedError as D,isUnauthorizedError as M}from"./packem_shared/CONFLICT_ERROR_CODE-B2SU0CaJ.mjs";import{httpStream as I}from"./packem_shared/httpStream-Cz4_QyjV.mjs";import{c as h}from"./packem_shared/local-store-C53xDvEr.mjs";import{LunoraClient as L}from"./packem_shared/LunoraClient-DRo2LNe9.mjs";import{createMutationRunner as b}from"./packem_shared/createMutationRunner-lQXH8Amk.mjs";import{createMutatorRunner as O}from"./packem_shared/createMutatorRunner-BVzo0FBZ.mjs";import{c as U}from"./packem_shared/offline-queue-b-v6bWWd.mjs";import{createInMemoryPersistence as B,createIndexedDbPersistence as q}from"./packem_shared/createInMemoryPersistence-CQYXy8UP.mjs";import{preloadQuery as v,preloadedQueryResult as w}from"./packem_shared/preloadQuery-DjwSjFwt.mjs";import{createInMemoryQueryCache as K,createIndexedDbQueryCache as V,queryCacheKey as W}from"./packem_shared/createInMemoryQueryCache-DjCKrLjk.mjs";import{createReconnect as j}from"./packem_shared/createReconnect-CFT7YRSR.mjs";import{default as H}from"./packem_shared/createSnapshotPrecondition-DOKRAJ4b.mjs";import{DEFAULT_MAX_BUFFER as Y,createStream as Z}from"./packem_shared/DEFAULT_MAX_BUFFER-TBn2s7Rz.mjs";import{SubscriptionRegistry as ee}from"./packem_shared/SubscriptionRegistry-CdrOD7sZ.mjs";import{ClientServiceWorker as oe}from"./packem_shared/ClientServiceWorker-CRQ5yl7-.mjs";import{createReply as ae,sendToSw as ce}from"./packem_shared/createReply-DF0jRsv-.mjs";export{y as CONFLICT_ERROR_CODE,oe as ClientServiceWorker,Y as DEFAULT_MAX_BUFFER,L as LunoraClient,U as OfflineQueue,o as RETIRE_AFTER_DURABLE_SEQ_ADVANCE,ee as SubscriptionRegistry,s as TabCoordinator,d as applyDelta,n as createAsyncStoragePersistence,f as createClientQuery,p as createInMemoryBookmarkStorage,B as createInMemoryPersistence,K as createInMemoryQueryCache,q as createIndexedDbPersistence,V as createIndexedDbQueryCache,h as createLocalStore,b as createMutationRunner,O as createMutatorRunner,j as createReconnect,ae as createReply,H as createSnapshotPrecondition,Z as createStream,E as getErrorCode,C as getRetryAfterMs,I as httpStream,S as isConflictError,A as isForbiddenError,l as isMutationDelta,D as isRateLimitedError,M as isUnauthorizedError,t as maxSeq,v as preloadQuery,w as preloadedQueryResult,W as queryCacheKey,a as reconcileOptimistic,ce as sendToSw};
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};