@lunora/client 1.0.0-alpha.232 → 1.0.0-alpha.234

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/auth/index.d.mts +1 -1
  2. package/dist/auth/index.d.ts +1 -1
  3. package/dist/index.d.mts +82 -3
  4. package/dist/index.d.ts +82 -3
  5. package/dist/index.mjs +1 -1
  6. package/dist/packem_shared/LunoraClient-D6Mn3qEX.mjs +1 -0
  7. package/dist/packem_shared/OfflineQueue-BLRESpAe.mjs +1 -0
  8. package/dist/packem_shared/TabCoordinator-DBuR7Dwv.mjs +1 -0
  9. package/dist/packem_shared/client-tuning-C10B2KCO.mjs +1 -0
  10. package/dist/packem_shared/connectLiveStream-CXD8k-Sp.mjs +1 -0
  11. package/dist/packem_shared/{createAsyncStorageQueryCache-f5023fdB.mjs → createAsyncStorageQueryCache-COqH5_SV.mjs} +1 -1
  12. package/dist/packem_shared/{createInMemoryPersistence-Cz2W6XVj.mjs → createInMemoryPersistence-BfgXApi_.mjs} +1 -1
  13. package/dist/packem_shared/{createInMemoryQueryCache-D2uNHLfM.mjs → createInMemoryQueryCache-B5wX-gTy.mjs} +1 -1
  14. package/dist/packem_shared/{createServerClient-BqCdfoE1.mjs → createServerClient-B0TsbJkS.mjs} +1 -1
  15. package/dist/packem_shared/httpStream-DceAMeMf.mjs +9 -0
  16. package/dist/packem_shared/idb-utility-BS7xvou1.mjs +1 -0
  17. package/dist/packem_shared/live-stream-Dil9byXF.mjs +10 -0
  18. package/dist/packem_shared/{liveTopicSubscription-Bs-ULKfs.mjs → liveTopicSubscription-gOmRbuhH.mjs} +1 -1
  19. package/dist/packem_shared/{lunora-client.d-DpmxnYmL.d.ts → lunora-client.d-CZ9TlyaL.d.ts} +207 -19
  20. package/dist/packem_shared/{lunora-client.d-dOEEoF4x.d.mts → lunora-client.d-DZ9gzk9p.d.mts} +207 -19
  21. package/dist/packem_shared/offline-queue-Dt_um8sR.mjs +1 -0
  22. package/dist/packem_shared/{preload.d-XVLTUaah.d.mts → preload.d-BOpEnFP8.d.mts} +1 -1
  23. package/dist/packem_shared/{preload.d-BkXsKMmU.d.ts → preload.d-CXUIU6pE.d.ts} +1 -1
  24. package/dist/packem_shared/stream-idle-C7CsIT_m.mjs +1 -0
  25. package/dist/packem_shared/{wire-errors-Bw04a4ZQ.mjs → wire-errors-BTLdHNoh.mjs} +1 -1
  26. package/dist/query/index.d.mts +2 -2
  27. package/dist/query/index.d.ts +2 -2
  28. package/dist/service.mjs +1 -1
  29. package/dist/ssr/index.d.mts +2 -2
  30. package/dist/ssr/index.d.ts +2 -2
  31. package/dist/ssr/index.mjs +1 -1
  32. package/package.json +1 -1
  33. package/dist/packem_shared/LunoraClient-D6MyuSWy.mjs +0 -1
  34. package/dist/packem_shared/OfflineQueue-BWamzR6p.mjs +0 -1
  35. package/dist/packem_shared/TabCoordinator-CXD7vulJ.mjs +0 -1
  36. package/dist/packem_shared/connectLiveStream-DCgklEjI.mjs +0 -1
  37. package/dist/packem_shared/httpStream-DaqP70Ne.mjs +0 -9
  38. package/dist/packem_shared/idb-utility-C8WS390w.mjs +0 -1
  39. package/dist/packem_shared/live-stream-ct8j7T75.mjs +0 -10
  40. package/dist/packem_shared/offline-queue-D_fr-uQ3.mjs +0 -1
@@ -1,4 +1,4 @@
1
- import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-dOEEoF4x.mjs";
1
+ import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-DZ9gzk9p.mjs";
2
2
  import '@lunora/errors';
3
3
  import "../packem_shared/function-reference.d-Br_hsKje.mjs";
4
4
  import '@lunora/runtime';
@@ -1,4 +1,4 @@
1
- import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-DpmxnYmL.js";
1
+ import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-CZ9TlyaL.js";
2
2
  import '@lunora/errors';
3
3
  import "../packem_shared/function-reference.d-Br_hsKje.js";
4
4
  import '@lunora/runtime';
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { R as ReconnectOptions, c as PersistenceAdapter, Q as QueryCacheAdapter, B as BookmarkStorage, C as ConnectionStatus, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, O as OfflineQueueOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-dOEEoF4x.mjs";
2
- export { type A as ActionCallOptions, type g as BatchSlot, type h as CachedQuery, type i as ClientDebugShard, type j as ClientDebugSnapshot, type k as ClientDebugSubscription, type l as ClientMessage, type m as ClientQueryRef, type n as ClientShapeSubscribeMessage, type o as ClientShapeUnsubscribeMessage, D as DEFAULT_MAX_BUFFER, type F as FunctionArgumentDescriptor, type p as FunctionDescriptor, type G as GlobalFacetResult, type q as GlobalFacetValue, type r as GlobalFilterClause, type s as GlobalTableInfo, type t as GlobalTablePage, type u as HttpStreamCallArgs, type v as LunoraClientError, type w as LunoraClientOptions, type M as MutationCallOptions, type x as MutationSettledEvent, type y as OptimisticLocalStore, type z as OptimisticUpdate, type E as OutboxMutation, type I as OutboxSink, type J as PersistedMutation, type P as Preloaded, type K as ReplayCredential, type N as ReplayIdentityVerdict, type T as RoomRef, type V as RowOp, type W as RpcEnvelope, type X as RpcResponseBody, type Y as ScheduleRecord, type Z as ScheduleRetryPolicy, 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 StoredQuery, type a9 as StreamHandle, type aa as SubscriptionCallback, type b as SubscriptionErrorCallback, ab as SubscriptionRegistry, type ac as SubscriptionState, type ad as SyncWatermark, type a as Unsubscribe, type U as User, type ae as WorkflowInstanceAction, type af as WorkflowInstanceDetail, type ag as WorkflowInstancePage, type ah as WorkflowInstanceStatus, type ai as WorkflowInstanceSummary, type aj as WorkflowStepDetail, type ak as WsTokenProvider, al as createClientQuery, am as createLocalStore, an as createStream } from "./packem_shared/lunora-client.d-dOEEoF4x.mjs";
1
+ import { R as ReconnectOptions, c as PersistenceAdapter, Q as QueryCacheAdapter, B as BookmarkStorage, C as ConnectionStatus, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, O as OfflineQueueOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-DZ9gzk9p.mjs";
2
+ export { type A as ActionCallOptions, type g as BatchSlot, type h as CachedQuery, type i as ClientDebugShard, type j as ClientDebugSnapshot, type k as ClientDebugSubscription, type l as ClientMessage, type m as ClientQueryRef, type n as ClientShapeSubscribeMessage, type o as ClientShapeUnsubscribeMessage, D as DEFAULT_MAX_BUFFER, type F as FunctionArgumentDescriptor, type p as FunctionDescriptor, type G as GlobalFacetResult, type q as GlobalFacetValue, type r as GlobalFilterClause, type s as GlobalTableInfo, type t as GlobalTablePage, type u as HttpStreamCallArgs, type v as LunoraClientError, type w as LunoraClientOptions, type M as MutationCallOptions, type x as MutationSettledEvent, type y as OnlineOnlyOption, type z as OptimisticLocalStore, type E as OptimisticUpdate, type I as OutboxMutation, type J as OutboxSink, type K as PersistedMutation, type P as Preloaded, type N as ReplayCredential, type T as ReplayIdentityVerdict, type V as RoomRef, type W as RowOp, type X as RpcEnvelope, type Y as RpcResponseBody, type Z as ScheduleRecord, type _ as ScheduleRetryPolicy, type $ as SchedulerPoolStatus, type a0 as SchedulerStatus, type a1 as ServerMessage, type a2 as ServerPokeEndMessage, type a3 as ServerPokePartMessage, type a4 as ServerPokeStartMessage, type a5 as ShardTrafficEntry, type a6 as ShardTrafficResult, type a7 as StorageListPage, type a8 as StorageObject, type a9 as StoredQuery, type aa as StreamHandle, type ab as SubscriptionCallback, type b as SubscriptionErrorCallback, ac as SubscriptionRegistry, type ad as SubscriptionState, type ae as SyncStatus, type af as SyncWatermark, type a as Unsubscribe, type U as User, type ag as WorkflowInstanceAction, type ah as WorkflowInstanceDetail, type ai as WorkflowInstancePage, type aj as WorkflowInstanceStatus, type ak as WorkflowInstanceSummary, type al as WorkflowStepDetail, type am as WsTokenProvider, an as createClientQuery, ao as createLocalStore, ap as createStream } from "./packem_shared/lunora-client.d-DZ9gzk9p.mjs";
3
3
  import { LunoraError, LunoraErrorCode } from '@lunora/errors';
4
4
  export type { LunoraErrorCode } from '@lunora/errors';
5
- export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-XVLTUaah.mjs";
5
+ export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-BOpEnFP8.mjs";
6
6
  import { F as FunctionReference } from "./packem_shared/function-reference.d-Br_hsKje.mjs";
7
7
  export type { A as ArgsOf, R as ReturnOf } from "./packem_shared/function-reference.d-Br_hsKje.mjs";
8
8
  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';
@@ -395,6 +395,13 @@ declare class TabCoordinator {
395
395
  private promotionPending;
396
396
  /** Timestamp of the most recent leader heartbeat. */
397
397
  private lastHeartbeat;
398
+ /**
399
+ * Snapshots waiting for the end of the current tick, latest per key. A
400
+ * `subscription-data` frame is a full snapshot, so a burst for one key within
401
+ * a tick only needs its last frame posted — each post structured-clones the
402
+ * whole value for every tab on the channel.
403
+ */
404
+ private readonly pendingData;
398
405
  private heartbeatTimer;
399
406
  private leaderCheckTimer;
400
407
  /** Callbacks set via constructor options. */
@@ -488,6 +495,8 @@ declare class TabCoordinator {
488
495
  */
489
496
  broadcastConnectionStatus(status: ConnectionStatus, identity?: string | null): void;
490
497
  private broadcast;
498
+ /** Post the snapshots queued this tick — unless leadership was lost meanwhile, when the new leader's own snapshots supersede them. */
499
+ private flushPendingData;
491
500
  private handleMessage;
492
501
  private handleClaimLeadership;
493
502
  private handleHeartbeat;
@@ -565,6 +574,15 @@ interface HttpStreamOptions {
565
574
  fetch?: typeof fetch;
566
575
  /** Extra request headers (e.g. `authorization`). `accept: text/event-stream` is always sent. */
567
576
  headers?: Record<string, string>;
577
+ /**
578
+ * Treat a connection that delivers nothing — no chunk, no keepalive comment —
579
+ * for this many milliseconds as dead: it is cancelled, then reconnected with
580
+ * {@link HttpStreamOptions.reconnect} or failed with `STREAM_IDLE`. Off by
581
+ * default, because a route may be silent for as long as its handler works
582
+ * (a model thinking); set it to a few multiples of the route's keepalive
583
+ * interval when the route sends one.
584
+ */
585
+ idleTimeoutMs?: number;
568
586
  /**
569
587
  * Seed the first request's `Last-Event-ID` header, resuming a stream from a
570
588
  * position a previous consumer persisted. Once frames arrive this is
@@ -611,6 +629,14 @@ interface ConnectLiveStreamOptions {
611
629
  fetch?: typeof fetch;
612
630
  /** Extra headers sent on both the stream and the control requests. */
613
631
  headers?: Record<string, string>;
632
+ /**
633
+ * Treat a connection that delivers nothing — not even a keepalive — for this many milliseconds as
634
+ * dead, and reconnect. Every target that serves live streams (Cloudflare Workers, the Node host
635
+ * and celld) runs the same `ShardDO` live handlers, and they write a `:ka` comment every 25s
636
+ * while a stream is open, so the default is `75000`. Set `0` to disable the bound when a proxy
637
+ * or a server of your own in front of the shard does not forward comment frames.
638
+ */
639
+ idleTimeoutMs?: number;
614
640
  /** Ceiling for the reconnect backoff, in ms. Defaults to `30000`. */
615
641
  maxDelayMs?: number;
616
642
  /** Base reconnect delay in ms. Defaults to `1000`. */
@@ -775,6 +801,13 @@ declare const createMutatorRunner: <TArgs>(handle: MutatorHandle<TArgs>, sinks:
775
801
  reset: () => void;
776
802
  };
777
803
  interface QueuedMutation<T = unknown> {
804
+ /**
805
+ * Set on a write that was queued because its direct send failed in a way that
806
+ * says nothing about its fate (see `LunoraClient.mutation`). Its caller is still
807
+ * waiting and was never told it failed, so the replay counts its transient
808
+ * failures and rejects it after a few, rather than retrying for ever.
809
+ */
810
+ readonly ambiguousSend?: boolean;
778
811
  readonly args: Record<string, unknown>;
779
812
  /**
780
813
  * CDC cursor this write was composed against (see
@@ -803,6 +836,13 @@ interface QueuedMutation<T = unknown> {
803
836
  * a reload and a requeue.
804
837
  */
805
838
  identity?: string | null;
839
+ /**
840
+ * Position in the order this session ISSUED its writes, taken before the first send. A write whose
841
+ * direct send failed ambiguously is queued only once that send gives up, behind writes issued (and
842
+ * queued) while it was in flight; this puts it back ahead of them. Absent on hydrated records,
843
+ * which are older than anything issued this session.
844
+ */
845
+ readonly issueSeq?: number;
806
846
  /**
807
847
  * `true` when a live caller is still awaiting this write's `mutation()`
808
848
  * Promise; `false`/absent for a write restored from durable storage after a
@@ -852,6 +892,8 @@ type EvictHandler = (entry: QueuedMutation, error: Error & {
852
892
  interface OfflineQueueDeps {
853
893
  /** Invoked when an entry is discarded on capacity overflow (carries `OFFLINE_QUEUE_OVERFLOW`). */
854
894
  onEvict?: EvictHandler;
895
+ /** Invoked when a write that was out for replay settles, which `onSizeChange` does not cover: the queue was already shorter. */
896
+ onReplayingChange?: () => void;
855
897
  /** Invoked with the new depth after any size change (drives the client's pending-sync count). */
856
898
  onSizeChange?: (size: number) => void;
857
899
  /** Durable store; when present, writes are mirrored and restored across reloads. */
@@ -879,12 +921,44 @@ declare class OfflineQueue {
879
921
  private readonly persistence;
880
922
  private readonly onEvict;
881
923
  private readonly onSizeChange;
924
+ private readonly onReplayingChange;
882
925
  /** App/schema version stamped on persisted writes; mismatched records are purged on hydrate. */
883
926
  private readonly version;
884
927
  private readonly items;
928
+ /** Ids of writes a flush has {@link OfflineQueue.drain}ed and neither requeued nor discarded yet, which are on the wire or about to be. */
929
+ private readonly replayingIds;
930
+ /** Appends still in flight, by write id, each resolving to whether the record landed. Anything that touches a record durably waits on its append first. */
931
+ private readonly appending;
932
+ /**
933
+ * Writes enqueued this session and not yet settled (see {@link OfflineQueue.discard}). Unlike `items`
934
+ * this survives a replay temporarily draining the queue, so a failed append is tracked for as long as
935
+ * the write is unsettled, and a retry derives its record from the entry as it is NOW.
936
+ */
937
+ private readonly unsettled;
938
+ /** Ids whose append was rejected (a quota error, a closed connection), kept to be tried again — see {@link OfflineQueue.flushUnpersisted}. */
939
+ private readonly unpersisted;
885
940
  constructor(options?: OfflineQueueOptions, deps?: OfflineQueueDeps);
886
941
  get size(): number;
942
+ /** Writes drained for replay and not yet settled or requeued: out of {@link OfflineQueue.size}, but still unsettled. */
943
+ get replaying(): number;
887
944
  enqueue<T>(entry: QueuedMutation<T>): void;
945
+ /**
946
+ * Remove a settled write's durable record — after its append, if that is
947
+ * still in flight. A write that replays quickly can settle before its append
948
+ * has landed; a `remove` that overtook it found nothing, and the record then
949
+ * landed behind it and replayed in the next session.
950
+ */
951
+ discard(id: string): void;
952
+ /**
953
+ * Try again to persist every write whose append was rejected. Called when the
954
+ * page is being hidden: the last chance to get a still-queued write into
955
+ * durable storage before a reload or a discard of the tab loses it.
956
+ *
957
+ * A write whose append is still in flight is skipped: `visibilitychange` and `pagehide` fire
958
+ * back to back, and a second append for the same id would hit a unique-id constraint (or store
959
+ * the record twice) and leave the write wrongly tracked as unpersisted.
960
+ */
961
+ flushUnpersisted(): void;
888
962
  /**
889
963
  * Restore mutations persisted in a prior session and re-queue them in FIFO
890
964
  * order. Restored entries already live in durable storage, so they are not
@@ -956,6 +1030,10 @@ declare class OfflineQueue {
956
1030
  */
957
1031
  drainConflict(): QueuedMutation[];
958
1032
  clear(): void;
1033
+ /** The durable form of a queued write. */
1034
+ private recordOf;
1035
+ /** Append `record`, remembering it as unpersisted if the append is rejected. */
1036
+ private persist;
959
1037
  /**
960
1038
  * The durable half of {@link OfflineQueue.restampIdentity}: rewrite the
961
1039
  * persisted record under the new identity stamp.
@@ -986,6 +1064,7 @@ declare class OfflineQueue {
986
1064
  * always drops the same way regardless of which caller triggered it.
987
1065
  */
988
1066
  private evictOverflow;
1067
+ private markReplaying;
989
1068
  /** Notify the size observer (the client's pending-sync count) after any change. */
990
1069
  private notifySize;
991
1070
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { R as ReconnectOptions, c as PersistenceAdapter, Q as QueryCacheAdapter, B as BookmarkStorage, C as ConnectionStatus, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, O as OfflineQueueOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-DpmxnYmL.js";
2
- export { type A as ActionCallOptions, type g as BatchSlot, type h as CachedQuery, type i as ClientDebugShard, type j as ClientDebugSnapshot, type k as ClientDebugSubscription, type l as ClientMessage, type m as ClientQueryRef, type n as ClientShapeSubscribeMessage, type o as ClientShapeUnsubscribeMessage, D as DEFAULT_MAX_BUFFER, type F as FunctionArgumentDescriptor, type p as FunctionDescriptor, type G as GlobalFacetResult, type q as GlobalFacetValue, type r as GlobalFilterClause, type s as GlobalTableInfo, type t as GlobalTablePage, type u as HttpStreamCallArgs, type v as LunoraClientError, type w as LunoraClientOptions, type M as MutationCallOptions, type x as MutationSettledEvent, type y as OptimisticLocalStore, type z as OptimisticUpdate, type E as OutboxMutation, type I as OutboxSink, type J as PersistedMutation, type P as Preloaded, type K as ReplayCredential, type N as ReplayIdentityVerdict, type T as RoomRef, type V as RowOp, type W as RpcEnvelope, type X as RpcResponseBody, type Y as ScheduleRecord, type Z as ScheduleRetryPolicy, 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 StoredQuery, type a9 as StreamHandle, type aa as SubscriptionCallback, type b as SubscriptionErrorCallback, ab as SubscriptionRegistry, type ac as SubscriptionState, type ad as SyncWatermark, type a as Unsubscribe, type U as User, type ae as WorkflowInstanceAction, type af as WorkflowInstanceDetail, type ag as WorkflowInstancePage, type ah as WorkflowInstanceStatus, type ai as WorkflowInstanceSummary, type aj as WorkflowStepDetail, type ak as WsTokenProvider, al as createClientQuery, am as createLocalStore, an as createStream } from "./packem_shared/lunora-client.d-DpmxnYmL.js";
1
+ import { R as ReconnectOptions, c as PersistenceAdapter, Q as QueryCacheAdapter, B as BookmarkStorage, C as ConnectionStatus, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, O as OfflineQueueOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-CZ9TlyaL.js";
2
+ export { type A as ActionCallOptions, type g as BatchSlot, type h as CachedQuery, type i as ClientDebugShard, type j as ClientDebugSnapshot, type k as ClientDebugSubscription, type l as ClientMessage, type m as ClientQueryRef, type n as ClientShapeSubscribeMessage, type o as ClientShapeUnsubscribeMessage, D as DEFAULT_MAX_BUFFER, type F as FunctionArgumentDescriptor, type p as FunctionDescriptor, type G as GlobalFacetResult, type q as GlobalFacetValue, type r as GlobalFilterClause, type s as GlobalTableInfo, type t as GlobalTablePage, type u as HttpStreamCallArgs, type v as LunoraClientError, type w as LunoraClientOptions, type M as MutationCallOptions, type x as MutationSettledEvent, type y as OnlineOnlyOption, type z as OptimisticLocalStore, type E as OptimisticUpdate, type I as OutboxMutation, type J as OutboxSink, type K as PersistedMutation, type P as Preloaded, type N as ReplayCredential, type T as ReplayIdentityVerdict, type V as RoomRef, type W as RowOp, type X as RpcEnvelope, type Y as RpcResponseBody, type Z as ScheduleRecord, type _ as ScheduleRetryPolicy, type $ as SchedulerPoolStatus, type a0 as SchedulerStatus, type a1 as ServerMessage, type a2 as ServerPokeEndMessage, type a3 as ServerPokePartMessage, type a4 as ServerPokeStartMessage, type a5 as ShardTrafficEntry, type a6 as ShardTrafficResult, type a7 as StorageListPage, type a8 as StorageObject, type a9 as StoredQuery, type aa as StreamHandle, type ab as SubscriptionCallback, type b as SubscriptionErrorCallback, ac as SubscriptionRegistry, type ad as SubscriptionState, type ae as SyncStatus, type af as SyncWatermark, type a as Unsubscribe, type U as User, type ag as WorkflowInstanceAction, type ah as WorkflowInstanceDetail, type ai as WorkflowInstancePage, type aj as WorkflowInstanceStatus, type ak as WorkflowInstanceSummary, type al as WorkflowStepDetail, type am as WsTokenProvider, an as createClientQuery, ao as createLocalStore, ap as createStream } from "./packem_shared/lunora-client.d-CZ9TlyaL.js";
3
3
  import { LunoraError, LunoraErrorCode } from '@lunora/errors';
4
4
  export type { LunoraErrorCode } from '@lunora/errors';
5
- export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-BkXsKMmU.js";
5
+ export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-CXUIU6pE.js";
6
6
  import { F as FunctionReference } from "./packem_shared/function-reference.d-Br_hsKje.js";
7
7
  export type { A as ArgsOf, R as ReturnOf } from "./packem_shared/function-reference.d-Br_hsKje.js";
8
8
  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';
@@ -395,6 +395,13 @@ declare class TabCoordinator {
395
395
  private promotionPending;
396
396
  /** Timestamp of the most recent leader heartbeat. */
397
397
  private lastHeartbeat;
398
+ /**
399
+ * Snapshots waiting for the end of the current tick, latest per key. A
400
+ * `subscription-data` frame is a full snapshot, so a burst for one key within
401
+ * a tick only needs its last frame posted — each post structured-clones the
402
+ * whole value for every tab on the channel.
403
+ */
404
+ private readonly pendingData;
398
405
  private heartbeatTimer;
399
406
  private leaderCheckTimer;
400
407
  /** Callbacks set via constructor options. */
@@ -488,6 +495,8 @@ declare class TabCoordinator {
488
495
  */
489
496
  broadcastConnectionStatus(status: ConnectionStatus, identity?: string | null): void;
490
497
  private broadcast;
498
+ /** Post the snapshots queued this tick — unless leadership was lost meanwhile, when the new leader's own snapshots supersede them. */
499
+ private flushPendingData;
491
500
  private handleMessage;
492
501
  private handleClaimLeadership;
493
502
  private handleHeartbeat;
@@ -565,6 +574,15 @@ interface HttpStreamOptions {
565
574
  fetch?: typeof fetch;
566
575
  /** Extra request headers (e.g. `authorization`). `accept: text/event-stream` is always sent. */
567
576
  headers?: Record<string, string>;
577
+ /**
578
+ * Treat a connection that delivers nothing — no chunk, no keepalive comment —
579
+ * for this many milliseconds as dead: it is cancelled, then reconnected with
580
+ * {@link HttpStreamOptions.reconnect} or failed with `STREAM_IDLE`. Off by
581
+ * default, because a route may be silent for as long as its handler works
582
+ * (a model thinking); set it to a few multiples of the route's keepalive
583
+ * interval when the route sends one.
584
+ */
585
+ idleTimeoutMs?: number;
568
586
  /**
569
587
  * Seed the first request's `Last-Event-ID` header, resuming a stream from a
570
588
  * position a previous consumer persisted. Once frames arrive this is
@@ -611,6 +629,14 @@ interface ConnectLiveStreamOptions {
611
629
  fetch?: typeof fetch;
612
630
  /** Extra headers sent on both the stream and the control requests. */
613
631
  headers?: Record<string, string>;
632
+ /**
633
+ * Treat a connection that delivers nothing — not even a keepalive — for this many milliseconds as
634
+ * dead, and reconnect. Every target that serves live streams (Cloudflare Workers, the Node host
635
+ * and celld) runs the same `ShardDO` live handlers, and they write a `:ka` comment every 25s
636
+ * while a stream is open, so the default is `75000`. Set `0` to disable the bound when a proxy
637
+ * or a server of your own in front of the shard does not forward comment frames.
638
+ */
639
+ idleTimeoutMs?: number;
614
640
  /** Ceiling for the reconnect backoff, in ms. Defaults to `30000`. */
615
641
  maxDelayMs?: number;
616
642
  /** Base reconnect delay in ms. Defaults to `1000`. */
@@ -775,6 +801,13 @@ declare const createMutatorRunner: <TArgs>(handle: MutatorHandle<TArgs>, sinks:
775
801
  reset: () => void;
776
802
  };
777
803
  interface QueuedMutation<T = unknown> {
804
+ /**
805
+ * Set on a write that was queued because its direct send failed in a way that
806
+ * says nothing about its fate (see `LunoraClient.mutation`). Its caller is still
807
+ * waiting and was never told it failed, so the replay counts its transient
808
+ * failures and rejects it after a few, rather than retrying for ever.
809
+ */
810
+ readonly ambiguousSend?: boolean;
778
811
  readonly args: Record<string, unknown>;
779
812
  /**
780
813
  * CDC cursor this write was composed against (see
@@ -803,6 +836,13 @@ interface QueuedMutation<T = unknown> {
803
836
  * a reload and a requeue.
804
837
  */
805
838
  identity?: string | null;
839
+ /**
840
+ * Position in the order this session ISSUED its writes, taken before the first send. A write whose
841
+ * direct send failed ambiguously is queued only once that send gives up, behind writes issued (and
842
+ * queued) while it was in flight; this puts it back ahead of them. Absent on hydrated records,
843
+ * which are older than anything issued this session.
844
+ */
845
+ readonly issueSeq?: number;
806
846
  /**
807
847
  * `true` when a live caller is still awaiting this write's `mutation()`
808
848
  * Promise; `false`/absent for a write restored from durable storage after a
@@ -852,6 +892,8 @@ type EvictHandler = (entry: QueuedMutation, error: Error & {
852
892
  interface OfflineQueueDeps {
853
893
  /** Invoked when an entry is discarded on capacity overflow (carries `OFFLINE_QUEUE_OVERFLOW`). */
854
894
  onEvict?: EvictHandler;
895
+ /** Invoked when a write that was out for replay settles, which `onSizeChange` does not cover: the queue was already shorter. */
896
+ onReplayingChange?: () => void;
855
897
  /** Invoked with the new depth after any size change (drives the client's pending-sync count). */
856
898
  onSizeChange?: (size: number) => void;
857
899
  /** Durable store; when present, writes are mirrored and restored across reloads. */
@@ -879,12 +921,44 @@ declare class OfflineQueue {
879
921
  private readonly persistence;
880
922
  private readonly onEvict;
881
923
  private readonly onSizeChange;
924
+ private readonly onReplayingChange;
882
925
  /** App/schema version stamped on persisted writes; mismatched records are purged on hydrate. */
883
926
  private readonly version;
884
927
  private readonly items;
928
+ /** Ids of writes a flush has {@link OfflineQueue.drain}ed and neither requeued nor discarded yet, which are on the wire or about to be. */
929
+ private readonly replayingIds;
930
+ /** Appends still in flight, by write id, each resolving to whether the record landed. Anything that touches a record durably waits on its append first. */
931
+ private readonly appending;
932
+ /**
933
+ * Writes enqueued this session and not yet settled (see {@link OfflineQueue.discard}). Unlike `items`
934
+ * this survives a replay temporarily draining the queue, so a failed append is tracked for as long as
935
+ * the write is unsettled, and a retry derives its record from the entry as it is NOW.
936
+ */
937
+ private readonly unsettled;
938
+ /** Ids whose append was rejected (a quota error, a closed connection), kept to be tried again — see {@link OfflineQueue.flushUnpersisted}. */
939
+ private readonly unpersisted;
885
940
  constructor(options?: OfflineQueueOptions, deps?: OfflineQueueDeps);
886
941
  get size(): number;
942
+ /** Writes drained for replay and not yet settled or requeued: out of {@link OfflineQueue.size}, but still unsettled. */
943
+ get replaying(): number;
887
944
  enqueue<T>(entry: QueuedMutation<T>): void;
945
+ /**
946
+ * Remove a settled write's durable record — after its append, if that is
947
+ * still in flight. A write that replays quickly can settle before its append
948
+ * has landed; a `remove` that overtook it found nothing, and the record then
949
+ * landed behind it and replayed in the next session.
950
+ */
951
+ discard(id: string): void;
952
+ /**
953
+ * Try again to persist every write whose append was rejected. Called when the
954
+ * page is being hidden: the last chance to get a still-queued write into
955
+ * durable storage before a reload or a discard of the tab loses it.
956
+ *
957
+ * A write whose append is still in flight is skipped: `visibilitychange` and `pagehide` fire
958
+ * back to back, and a second append for the same id would hit a unique-id constraint (or store
959
+ * the record twice) and leave the write wrongly tracked as unpersisted.
960
+ */
961
+ flushUnpersisted(): void;
888
962
  /**
889
963
  * Restore mutations persisted in a prior session and re-queue them in FIFO
890
964
  * order. Restored entries already live in durable storage, so they are not
@@ -956,6 +1030,10 @@ declare class OfflineQueue {
956
1030
  */
957
1031
  drainConflict(): QueuedMutation[];
958
1032
  clear(): void;
1033
+ /** The durable form of a queued write. */
1034
+ private recordOf;
1035
+ /** Append `record`, remembering it as unpersisted if the append is rejected. */
1036
+ private persist;
959
1037
  /**
960
1038
  * The durable half of {@link OfflineQueue.restampIdentity}: rewrite the
961
1039
  * persisted record under the new identity stamp.
@@ -986,6 +1064,7 @@ declare class OfflineQueue {
986
1064
  * always drops the same way regardless of which caller triggered it.
987
1065
  */
988
1066
  private evictOverflow;
1067
+ private markReplaying;
989
1068
  /** Notify the size observer (the client's pending-sync count) after any change. */
990
1069
  private notifySize;
991
1070
  }
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-DoEHtuR8.mjs";import{createAsyncStoragePersistence as i}from"./packem_shared/createAsyncStoragePersistence-6FCvqc6C.mjs";import{createAsyncStorageQueryCache as n}from"./packem_shared/createAsyncStorageQueryCache-f5023fdB.mjs";import{default as s}from"./packem_shared/createInMemoryBookmarkStorage-BooZhW0n.mjs";import{createCallRunner as x}from"./packem_shared/createCallRunner-Bwcrfh76.mjs";import{createClientQuery as l}from"./packem_shared/createClientQuery-B8Nfj-7o.mjs";import{TabCoordinator as d}from"./packem_shared/TabCoordinator-CXD7vulJ.mjs";import{a as R,i as C}from"./packem_shared/delta-merge-Ls3DED4g.mjs";import{CONFLICT_ERROR_CODE as A,TransportError as b,getErrorCode as h,getRetryAfterMs as D,isAuthReplayFailure as L,isConflictError as Q,isForbiddenError as T,isRateLimitedError as _,isUnauthorizedError as F}from"./packem_shared/CONFLICT_ERROR_CODE-B7qNZMMk.mjs";import{httpStream as M}from"./packem_shared/httpStream-DaqP70Ne.mjs";import{c as O}from"./packem_shared/live-stream-ct8j7T75.mjs";import{liveTopicSubscription as U,subscribeLiveTopic as v}from"./packem_shared/liveTopicSubscription-Bs-ULKfs.mjs";import{c as k}from"./packem_shared/local-store-fqWV2n_Z.mjs";import{LunoraClient as N}from"./packem_shared/LunoraClient-D6MyuSWy.mjs";import{createMutatorRunner as K}from"./packem_shared/createMutatorRunner-CuMRJpUx.mjs";import{O as X}from"./packem_shared/offline-queue-D_fr-uQ3.mjs";import{createInMemoryPersistence as w,createIndexedDbPersistence as G}from"./packem_shared/createInMemoryPersistence-Cz2W6XVj.mjs";import{preloadQuery as J,preloadedQueryResult as W}from"./packem_shared/preloadQuery-uFy24PCR.mjs";import{createInMemoryQueryCache as Z,createIndexedDbQueryCache as $,queryCacheKey as ee}from"./packem_shared/createInMemoryQueryCache-D2uNHLfM.mjs";import{createReconnect as oe}from"./packem_shared/createReconnect-CjTmjJDH.mjs";import{default as ae}from"./packem_shared/createSnapshotPrecondition-CLkxVeN1.mjs";import{DEFAULT_MAX_BUFFER as ie,createStream as pe}from"./packem_shared/DEFAULT_MAX_BUFFER-Bd-_iyiV.mjs";import{SubscriptionRegistry as me}from"./packem_shared/SubscriptionRegistry-DKIXE7M7.mjs";export{A as CONFLICT_ERROR_CODE,ie as DEFAULT_MAX_BUFFER,N as LunoraClient,X as OfflineQueue,o as RETIRE_AFTER_DURABLE_SEQ_ADVANCE,me as SubscriptionRegistry,d as TabCoordinator,b as TransportError,R as applyDelta,O as connectLiveStream,i as createAsyncStoragePersistence,n as createAsyncStorageQueryCache,x as createCallRunner,l as createClientQuery,s as createInMemoryBookmarkStorage,w as createInMemoryPersistence,Z as createInMemoryQueryCache,G as createIndexedDbPersistence,$ as createIndexedDbQueryCache,k as createLocalStore,K as createMutatorRunner,oe as createReconnect,ae as createSnapshotPrecondition,pe as createStream,h as getErrorCode,D as getRetryAfterMs,M as httpStream,L as isAuthReplayFailure,Q as isConflictError,T as isForbiddenError,C as isMutationDelta,_ as isRateLimitedError,F as isUnauthorizedError,U as liveTopicSubscription,t as maxSeq,J as preloadQuery,W as preloadedQueryResult,ee as queryCacheKey,a as reconcileOptimistic,v as subscribeLiveTopic};
1
+ import{RETIRE_AFTER_DURABLE_SEQ_ADVANCE as o,maxSeq as t,reconcileOptimistic as a}from"./packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-DoEHtuR8.mjs";import{createAsyncStoragePersistence as i}from"./packem_shared/createAsyncStoragePersistence-6FCvqc6C.mjs";import{createAsyncStorageQueryCache as n}from"./packem_shared/createAsyncStorageQueryCache-COqH5_SV.mjs";import{default as s}from"./packem_shared/createInMemoryBookmarkStorage-BooZhW0n.mjs";import{createCallRunner as x}from"./packem_shared/createCallRunner-Bwcrfh76.mjs";import{createClientQuery as l}from"./packem_shared/createClientQuery-B8Nfj-7o.mjs";import{TabCoordinator as d}from"./packem_shared/TabCoordinator-DBuR7Dwv.mjs";import{a as R,i as C}from"./packem_shared/delta-merge-Ls3DED4g.mjs";import{CONFLICT_ERROR_CODE as A,TransportError as b,getErrorCode as h,getRetryAfterMs as D,isAuthReplayFailure as L,isConflictError as Q,isForbiddenError as T,isRateLimitedError as _,isUnauthorizedError as F}from"./packem_shared/CONFLICT_ERROR_CODE-B7qNZMMk.mjs";import{httpStream as M}from"./packem_shared/httpStream-DceAMeMf.mjs";import{c as O}from"./packem_shared/live-stream-Dil9byXF.mjs";import{liveTopicSubscription as U,subscribeLiveTopic as v}from"./packem_shared/liveTopicSubscription-gOmRbuhH.mjs";import{c as k}from"./packem_shared/local-store-fqWV2n_Z.mjs";import{LunoraClient as N}from"./packem_shared/LunoraClient-D6Mn3qEX.mjs";import{createMutatorRunner as K}from"./packem_shared/createMutatorRunner-CuMRJpUx.mjs";import{O as X}from"./packem_shared/offline-queue-Dt_um8sR.mjs";import{createInMemoryPersistence as w,createIndexedDbPersistence as G}from"./packem_shared/createInMemoryPersistence-BfgXApi_.mjs";import{preloadQuery as J,preloadedQueryResult as W}from"./packem_shared/preloadQuery-uFy24PCR.mjs";import{createInMemoryQueryCache as Z,createIndexedDbQueryCache as $,queryCacheKey as ee}from"./packem_shared/createInMemoryQueryCache-B5wX-gTy.mjs";import{createReconnect as oe}from"./packem_shared/createReconnect-CjTmjJDH.mjs";import{default as ae}from"./packem_shared/createSnapshotPrecondition-CLkxVeN1.mjs";import{DEFAULT_MAX_BUFFER as ie,createStream as pe}from"./packem_shared/DEFAULT_MAX_BUFFER-Bd-_iyiV.mjs";import{SubscriptionRegistry as me}from"./packem_shared/SubscriptionRegistry-DKIXE7M7.mjs";export{A as CONFLICT_ERROR_CODE,ie as DEFAULT_MAX_BUFFER,N as LunoraClient,X as OfflineQueue,o as RETIRE_AFTER_DURABLE_SEQ_ADVANCE,me as SubscriptionRegistry,d as TabCoordinator,b as TransportError,R as applyDelta,O as connectLiveStream,i as createAsyncStoragePersistence,n as createAsyncStorageQueryCache,x as createCallRunner,l as createClientQuery,s as createInMemoryBookmarkStorage,w as createInMemoryPersistence,Z as createInMemoryQueryCache,G as createIndexedDbPersistence,$ as createIndexedDbQueryCache,k as createLocalStore,K as createMutatorRunner,oe as createReconnect,ae as createSnapshotPrecondition,pe as createStream,h as getErrorCode,D as getRetryAfterMs,M as httpStream,L as isAuthReplayFailure,Q as isConflictError,T as isForbiddenError,C as isMutationDelta,_ as isRateLimitedError,F as isUnauthorizedError,U as liveTopicSubscription,t as maxSeq,J as preloadQuery,W as preloadedQueryResult,ee as queryCacheKey,a as reconcileOptimistic,v as subscribeLiveTopic};