@lunora/client 1.0.0-alpha.193 → 1.0.0-alpha.195

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 (31) 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 +17 -17
  4. package/dist/index.d.ts +17 -17
  5. package/dist/index.mjs +1 -1
  6. package/dist/packem_shared/LunoraClient-BIzBh60k.mjs +1 -0
  7. package/dist/packem_shared/SubscriptionRegistry-5CHmk3d8.mjs +1 -0
  8. package/dist/packem_shared/applyDelta-5zZaiLB7.mjs +1 -0
  9. package/dist/packem_shared/createLocalStore-CIh8bYs-.mjs +1 -0
  10. package/dist/packem_shared/{createServerClient-HdISrPXa.mjs → createServerClient-D7zkixlG.mjs} +1 -1
  11. package/dist/packem_shared/delta-merge-Cu-cN5Cb.mjs +1 -0
  12. package/dist/packem_shared/local-store-SJVMlM2Z.mjs +1 -0
  13. package/dist/packem_shared/{lunora-client.d-DDVPWV7D.d.mts → lunora-client.d-SHCNd1Cn.d.mts} +78 -117
  14. package/dist/packem_shared/{lunora-client.d-C6lZEDyl.d.ts → lunora-client.d-YjgrrB49.d.ts} +78 -117
  15. package/dist/packem_shared/{preload.d-CdYylQeH.d.ts → preload.d-BTB4TAd_.d.ts} +1 -1
  16. package/dist/packem_shared/{preload.d-Cf8tiYKi.d.mts → preload.d-Cenry5u_.d.mts} +1 -1
  17. package/dist/packem_shared/wire-errors-8iZTTKRN.mjs +1 -0
  18. package/dist/query/index.d.mts +2 -2
  19. package/dist/query/index.d.ts +2 -2
  20. package/dist/service.mjs +1 -1
  21. package/dist/ssr/index.d.mts +2 -2
  22. package/dist/ssr/index.d.ts +2 -2
  23. package/dist/ssr/index.mjs +1 -1
  24. package/package.json +1 -1
  25. package/dist/packem_shared/LunoraClient-sL0hnKnB.mjs +0 -1
  26. package/dist/packem_shared/SubscriptionRegistry-CxKugiaA.mjs +0 -1
  27. package/dist/packem_shared/applyDelta-CK4TguD-.mjs +0 -1
  28. package/dist/packem_shared/createLocalStore-Di9RGozS.mjs +0 -1
  29. package/dist/packem_shared/delta-merge-BCAy1Q0H.mjs +0 -1
  30. package/dist/packem_shared/local-store-C9mvzb5T.mjs +0 -1
  31. package/dist/packem_shared/replay-CXXDZ1_h.mjs +0 -1
@@ -1,41 +1,6 @@
1
1
  import { LunoraErrorCodeInput, LunoraErrorCode } from '@lunora/errors';
2
2
  import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./function-reference.d-Br_hsKje.mjs";
3
3
  import { CronJobInfo, VectorIndexSummary, VectorQueryMatch, PipelineLogQuery, PipelineLogPage, KvNamespaceSummary, KvKeyListResult, KvValueResult, AuthUser, AuthPage, AuthImpersonation, AuthCapabilities, AuthConfigInfo, AuthSession } from '@lunora/runtime';
4
- /**
5
- * Reactive key-value store for local-only client state.
6
- *
7
- * Unlike a server {@link SubscriptionState} (which tracks a live WS connection,
8
- * an `acked` flag, `serverBase`, optimistic layers, and the full subscription
9
- * machinery), a `ClientQueryRef` is purely local — no server round-trip, no
10
- * WebSocket, no persistence. It exists so framework adapters can offer a
11
- * `useClientQuery` hook whose values survive component remounts and are shared
12
- * across every consumer of the same ref, with none of the ceremony or coupling
13
- * of a dedicated context provider.
14
- *
15
- * The store lives inside `LunoraClient` (a private field) and is surfaced through
16
- * `client.getClientQuery(ref)` / `setClientQuery(ref, value)` /
17
- * `subscribeClientQuery(ref, callback)`.
18
- */
19
- /** Opaque handle for a typed client-local query slot. */
20
- interface ClientQueryRef<T = unknown> {
21
- /** Default value when no value has been set explicitly. */
22
- readonly defaultValue: T;
23
- /** Stable identity for the slot. Must be unique within a client instance. */
24
- readonly key: string;
25
- }
26
- /**
27
- * Create a typed {@link ClientQueryRef}. Call once per slot at module scope
28
- * (or inside a component module) — the ref object is the stable identity.
29
- * @example
30
- * ```ts
31
- * // lunora/client-queries.ts
32
- * import { createClientQuery } from "@lunora/client";
33
- *
34
- * export const sidebarOpen = createClientQuery("sidebarOpen", true);
35
- * export const selectedMessageId = createClientQuery("selectedMessageId", undefined as string | undefined);
36
- * ```
37
- */
38
- declare const createClientQuery: <T>(key: string, defaultValue: T) => ClientQueryRef<T>;
39
4
  /**
40
5
  * Typed reference to an HTTP-SSE stream route (`httpRoute.<verb>(path).stream()`)
41
6
  * emitted by `@lunora/codegen` as `httpStreams.<namespace>.<name>`.
@@ -1397,6 +1362,80 @@ declare class SubscriptionRegistry {
1397
1362
  */
1398
1363
  clear(): void;
1399
1364
  }
1365
+ /**
1366
+ * An `Error` carrying the server's machine-readable `code` and (for a
1367
+ * `LunoraError`) structured `data`, plus an optional actionable `hint` (Markdown)
1368
+ * and `docsUrl` resolved from the central error catalog. The client's public
1369
+ * error contract for RPC/batch failures — a UI can render `hint`/`docsUrl` to
1370
+ * tell the user how to fix the error. The `(string & {})` arm keeps
1371
+ * forward-compat/unknown server codes assignable without losing autocomplete on
1372
+ * the known `LunoraErrorCode` union.
1373
+ */
1374
+ type LunoraClientError = Error & {
1375
+ code?: LunoraErrorCode | (string & {});
1376
+ data?: unknown;
1377
+ docsUrl?: string;
1378
+ hint?: string | string[];
1379
+ };
1380
+ /** One demuxed result slot of a `LunoraClient.batch` call. */
1381
+ type BatchSlot = {
1382
+ error: LunoraClientError;
1383
+ ok: false;
1384
+ } | {
1385
+ ok: true;
1386
+ value: unknown;
1387
+ };
1388
+ /**
1389
+ * Reactive key-value store for local-only client state.
1390
+ *
1391
+ * Unlike a server {@link SubscriptionState} (which tracks a live WS connection,
1392
+ * an `acked` flag, `serverBase`, optimistic layers, and the full subscription
1393
+ * machinery), a `ClientQueryRef` is purely local — no server round-trip, no
1394
+ * WebSocket, no persistence. It exists so framework adapters can offer a
1395
+ * `useClientQuery` hook whose values survive component remounts and are shared
1396
+ * across every consumer of the same ref, with none of the ceremony or coupling
1397
+ * of a dedicated context provider.
1398
+ *
1399
+ * The store lives inside `LunoraClient` (a private field) and is surfaced through
1400
+ * `client.getClientQuery(ref)` / `setClientQuery(ref, value)` /
1401
+ * `subscribeClientQuery(ref, callback)`.
1402
+ */
1403
+ /** Opaque handle for a typed client-local query slot. */
1404
+ interface ClientQueryRef<T = unknown> {
1405
+ /** Default value when no value has been set explicitly. */
1406
+ readonly defaultValue: T;
1407
+ /** Stable identity for the slot. Must be unique within a client instance. */
1408
+ readonly key: string;
1409
+ }
1410
+ /**
1411
+ * Create a typed {@link ClientQueryRef}. Call once per slot at module scope
1412
+ * (or inside a component module) — the ref object is the stable identity.
1413
+ * @example
1414
+ * ```ts
1415
+ * // lunora/client-queries.ts
1416
+ * import { createClientQuery } from "@lunora/client";
1417
+ *
1418
+ * export const sidebarOpen = createClientQuery("sidebarOpen", true);
1419
+ * export const selectedMessageId = createClientQuery("selectedMessageId", undefined as string | undefined);
1420
+ * ```
1421
+ */
1422
+ declare const createClientQuery: <T>(key: string, defaultValue: T) => ClientQueryRef<T>;
1423
+ type WSState = "idle" | "connecting" | "open" | "closed";
1424
+ /**
1425
+ * Aggregate live-socket health across every shard connection, for a UI status
1426
+ * indicator. `idle` = no socket opened yet; `connecting` = at least one socket
1427
+ * is (re)connecting and none is open; `connected` = at least one socket is open;
1428
+ * `polling` = no socket would open, so live queries are being refreshed over HTTP
1429
+ * instead (see `polling-fallback.ts` — live but slower, and shapes
1430
+ * / streams / whispers are dark; mutations go over HTTP as they do while
1431
+ * connected); `offline` = sockets exist but all are down (between reconnect
1432
+ * attempts), and a poll could not reach the origin either.
1433
+ *
1434
+ * `polling` outranks `connecting`: while the fallback is running a reconnect is
1435
+ * still armed in the background, and reporting that attempt would flicker the
1436
+ * indicator between two states while data is in fact arriving on the slow path.
1437
+ */
1438
+ type ConnectionStatus = "connected" | "connecting" | "idle" | "offline" | "polling";
1400
1439
  /**
1401
1440
  * Read/write handle over the client's live query cache, handed to a mutation's
1402
1441
  * `withOptimisticUpdate` callback so a single mutation can optimistically patch
@@ -1450,6 +1489,8 @@ declare const createLocalStore: (subscriptions: SubscriptionRegistry, shardKey:
1450
1489
  rollbacks: (() => void)[];
1451
1490
  store: OptimisticLocalStore;
1452
1491
  };
1492
+ /** Callback a shape subscription invokes with its materialized rowset on every applied poke. */
1493
+ type ShapeCallback = (rows: Record<string, unknown>[]) => void;
1453
1494
  declare const DEFAULT_MAX_BUFFER = 1024;
1454
1495
  interface StreamHandle<T = unknown> {
1455
1496
  /** Mark the stream complete (no more chunks); resolves any pending consumer to `done:true`. */
@@ -1482,22 +1523,6 @@ declare const createStream: <T>(options: {
1482
1523
  handle: StreamHandle<T>;
1483
1524
  iterable: StreamIterable<T>;
1484
1525
  };
1485
- type WSState = "idle" | "connecting" | "open" | "closed";
1486
- /**
1487
- * Aggregate live-socket health across every shard connection, for a UI status
1488
- * indicator. `idle` = no socket opened yet; `connecting` = at least one socket
1489
- * is (re)connecting and none is open; `connected` = at least one socket is open;
1490
- * `polling` = no socket would open, so live queries are being refreshed over HTTP
1491
- * instead (see {@link file://./polling-fallback.ts} — live but slower, and shapes
1492
- * / streams / whispers are dark; mutations go over HTTP as they do while
1493
- * connected); `offline` = sockets exist but all are down (between reconnect
1494
- * attempts), and a poll could not reach the origin either.
1495
- *
1496
- * `polling` outranks `connecting`: while the fallback is running a reconnect is
1497
- * still armed in the background, and reporting that attempt would flicker the
1498
- * indicator between two states while data is in fact arriving on the slow path.
1499
- */
1500
- type ConnectionStatus = "connected" | "connecting" | "idle" | "offline" | "polling";
1501
1526
  /** One shard's socket + watermark state in a {@link LunoraClient.debug} snapshot. */
1502
1527
  interface ClientDebugShard {
1503
1528
  /**
@@ -1702,31 +1727,6 @@ type ReplayIdentityVerdict = {
1702
1727
  } | {
1703
1728
  verdict: "unknown";
1704
1729
  };
1705
- /** Callback a shape subscription invokes with its materialized rowset on every applied poke. */
1706
- type ShapeCallback = (rows: Record<string, unknown>[]) => void;
1707
- /**
1708
- * An `Error` carrying the server's machine-readable `code` and (for a
1709
- * `LunoraError`) structured `data`, plus an optional actionable `hint` (Markdown)
1710
- * and `docsUrl` resolved from the central error catalog. The client's public
1711
- * error contract for RPC/batch failures — a UI can render `hint`/`docsUrl` to
1712
- * tell the user how to fix the error. The `(string & {})` arm keeps
1713
- * forward-compat/unknown server codes assignable without losing autocomplete on
1714
- * the known {@link LunoraErrorCode} union.
1715
- */
1716
- type LunoraClientError = Error & {
1717
- code?: LunoraErrorCode | (string & {});
1718
- data?: unknown;
1719
- docsUrl?: string;
1720
- hint?: string | string[];
1721
- };
1722
- /** One demuxed result slot of a {@link LunoraClient.batch} call (plan 088). */
1723
- type BatchSlot = {
1724
- error: LunoraClientError;
1725
- ok: false;
1726
- } | {
1727
- ok: true;
1728
- value: unknown;
1729
- };
1730
1730
  /**
1731
1731
  * Lunora browser/edge client. Talks RPC over HTTP and real-time deltas over
1732
1732
  * a single multiplexed WebSocket.
@@ -1761,6 +1761,8 @@ declare class LunoraClient {
1761
1761
  private readonly connectTimeoutMs;
1762
1762
  /** Keepalive cadence (ms); `0` disables the heartbeat. See {@link LunoraClientOptions.heartbeatIntervalMs}. */
1763
1763
  private readonly heartbeatIntervalMs;
1764
+ /** The socket-lifecycle inputs, built once after the constructor assigns them (none is reassigned later). */
1765
+ private readonly managedSocketOptions;
1764
1766
  /** Failed-open run that engages the HTTP polling fallback; `0` disables it. */
1765
1767
  private readonly pollingFallbackAfterFailedAttempts;
1766
1768
  /** Polling-fallback cadence (ms); `0` disables it. See {@link LunoraClientOptions.pollingFallback}. */
@@ -3921,26 +3923,6 @@ declare class LunoraClient {
3921
3923
  * a silent tokenless socket the admin gate would reject.
3922
3924
  */
3923
3925
  private openSocketWithProvidedToken;
3924
- /**
3925
- * Construct one WebSocket connection attempt and wire the shared
3926
- * lifecycle guarantees around it — the fail-fast connect-timeout, the
3927
- * identity guard that stops a superseded attempt's late `open`/`message`/
3928
- * `close`/`error` from touching a connection a newer attempt already
3929
- * owns, and (once open) the keepalive heartbeat with its half-open
3930
- * watchdog (plan 217). One call opens ONE attempt; the caller owns
3931
- * reconnect scheduling from `onClose` — mirrors the shard's existing
3932
- * `ensureSocket` / `handleDisconnect` split, now shared with
3933
- * `subscribeScheduledJobs` so it stops re-living the bug that split
3934
- * already fixed once (CLIENT-05).
3935
- *
3936
- * The identity guard is `conn.socket !== socket`, re-checked before every
3937
- * action below. `conn.socket` is reassigned to a new attempt's socket
3938
- * synchronously — right here, before `open` ever fires — so an older
3939
- * attempt's guard trips the instant it's superseded, even if its
3940
- * underlying socket only fires its real `close`/`error` much later. This
3941
- * ordering is load-bearing: preserve it exactly.
3942
- */
3943
- private openManagedSocket;
3944
3926
  /** Construct the shard socket and wire its lifecycle handlers. The connection must already be in the `connecting` state. */
3945
3927
  private openSocket;
3946
3928
  /**
@@ -3949,27 +3931,6 @@ declare class LunoraClient {
3949
3931
  */
3950
3932
  private cancelStream;
3951
3933
  private handleDisconnect;
3952
- /**
3953
- * Begin the keepalive heartbeat on an open connection attempt — the only
3954
- * caller is {@link openManagedSocket}'s own `open` handler, so both the
3955
- * shard socket and `subscribeScheduledJobs` share this one implementation
3956
- * instead of each hand-rolling their own (plan 217, generalized).
3957
- *
3958
- * Each tick first checks the half-open watchdog (see
3959
- * {@link ManagedSocketState.lastFrameAt}): if no frame at all has arrived
3960
- * within `heartbeatIntervalMs * 2.5`, the far end has gone quiet without
3961
- * the socket ever firing `close` — force it closed and report it through
3962
- * `onWatchdogTrip` (the caller's `onClose`) so the normal reconnect/backoff
3963
- * takes over instead of every live query on it silently staling forever.
3964
- * Otherwise it sends a {@link WS_KEEPALIVE_PING} text frame the server
3965
- * answers from its hibernation auto-response without waking the DO. A
3966
- * no-op when the heartbeat is disabled (an interval of zero or less);
3967
- * idempotent — any existing timer is cleared first so a reconnect can't
3968
- * leak intervals.
3969
- */
3970
- private startHeartbeat;
3971
- /** Clear a connection's keepalive timer, if any. Safe to call repeatedly. */
3972
- private stopHeartbeat;
3973
3934
  /** Mark every subscription bound to `shardKey` as needing a fresh ack. */
3974
3935
  private markShardPendingAck;
3975
3936
  /**
@@ -1,41 +1,6 @@
1
1
  import { LunoraErrorCodeInput, LunoraErrorCode } from '@lunora/errors';
2
2
  import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./function-reference.d-Br_hsKje.js";
3
3
  import { CronJobInfo, VectorIndexSummary, VectorQueryMatch, PipelineLogQuery, PipelineLogPage, KvNamespaceSummary, KvKeyListResult, KvValueResult, AuthUser, AuthPage, AuthImpersonation, AuthCapabilities, AuthConfigInfo, AuthSession } from '@lunora/runtime';
4
- /**
5
- * Reactive key-value store for local-only client state.
6
- *
7
- * Unlike a server {@link SubscriptionState} (which tracks a live WS connection,
8
- * an `acked` flag, `serverBase`, optimistic layers, and the full subscription
9
- * machinery), a `ClientQueryRef` is purely local — no server round-trip, no
10
- * WebSocket, no persistence. It exists so framework adapters can offer a
11
- * `useClientQuery` hook whose values survive component remounts and are shared
12
- * across every consumer of the same ref, with none of the ceremony or coupling
13
- * of a dedicated context provider.
14
- *
15
- * The store lives inside `LunoraClient` (a private field) and is surfaced through
16
- * `client.getClientQuery(ref)` / `setClientQuery(ref, value)` /
17
- * `subscribeClientQuery(ref, callback)`.
18
- */
19
- /** Opaque handle for a typed client-local query slot. */
20
- interface ClientQueryRef<T = unknown> {
21
- /** Default value when no value has been set explicitly. */
22
- readonly defaultValue: T;
23
- /** Stable identity for the slot. Must be unique within a client instance. */
24
- readonly key: string;
25
- }
26
- /**
27
- * Create a typed {@link ClientQueryRef}. Call once per slot at module scope
28
- * (or inside a component module) — the ref object is the stable identity.
29
- * @example
30
- * ```ts
31
- * // lunora/client-queries.ts
32
- * import { createClientQuery } from "@lunora/client";
33
- *
34
- * export const sidebarOpen = createClientQuery("sidebarOpen", true);
35
- * export const selectedMessageId = createClientQuery("selectedMessageId", undefined as string | undefined);
36
- * ```
37
- */
38
- declare const createClientQuery: <T>(key: string, defaultValue: T) => ClientQueryRef<T>;
39
4
  /**
40
5
  * Typed reference to an HTTP-SSE stream route (`httpRoute.<verb>(path).stream()`)
41
6
  * emitted by `@lunora/codegen` as `httpStreams.<namespace>.<name>`.
@@ -1397,6 +1362,80 @@ declare class SubscriptionRegistry {
1397
1362
  */
1398
1363
  clear(): void;
1399
1364
  }
1365
+ /**
1366
+ * An `Error` carrying the server's machine-readable `code` and (for a
1367
+ * `LunoraError`) structured `data`, plus an optional actionable `hint` (Markdown)
1368
+ * and `docsUrl` resolved from the central error catalog. The client's public
1369
+ * error contract for RPC/batch failures — a UI can render `hint`/`docsUrl` to
1370
+ * tell the user how to fix the error. The `(string & {})` arm keeps
1371
+ * forward-compat/unknown server codes assignable without losing autocomplete on
1372
+ * the known `LunoraErrorCode` union.
1373
+ */
1374
+ type LunoraClientError = Error & {
1375
+ code?: LunoraErrorCode | (string & {});
1376
+ data?: unknown;
1377
+ docsUrl?: string;
1378
+ hint?: string | string[];
1379
+ };
1380
+ /** One demuxed result slot of a `LunoraClient.batch` call. */
1381
+ type BatchSlot = {
1382
+ error: LunoraClientError;
1383
+ ok: false;
1384
+ } | {
1385
+ ok: true;
1386
+ value: unknown;
1387
+ };
1388
+ /**
1389
+ * Reactive key-value store for local-only client state.
1390
+ *
1391
+ * Unlike a server {@link SubscriptionState} (which tracks a live WS connection,
1392
+ * an `acked` flag, `serverBase`, optimistic layers, and the full subscription
1393
+ * machinery), a `ClientQueryRef` is purely local — no server round-trip, no
1394
+ * WebSocket, no persistence. It exists so framework adapters can offer a
1395
+ * `useClientQuery` hook whose values survive component remounts and are shared
1396
+ * across every consumer of the same ref, with none of the ceremony or coupling
1397
+ * of a dedicated context provider.
1398
+ *
1399
+ * The store lives inside `LunoraClient` (a private field) and is surfaced through
1400
+ * `client.getClientQuery(ref)` / `setClientQuery(ref, value)` /
1401
+ * `subscribeClientQuery(ref, callback)`.
1402
+ */
1403
+ /** Opaque handle for a typed client-local query slot. */
1404
+ interface ClientQueryRef<T = unknown> {
1405
+ /** Default value when no value has been set explicitly. */
1406
+ readonly defaultValue: T;
1407
+ /** Stable identity for the slot. Must be unique within a client instance. */
1408
+ readonly key: string;
1409
+ }
1410
+ /**
1411
+ * Create a typed {@link ClientQueryRef}. Call once per slot at module scope
1412
+ * (or inside a component module) — the ref object is the stable identity.
1413
+ * @example
1414
+ * ```ts
1415
+ * // lunora/client-queries.ts
1416
+ * import { createClientQuery } from "@lunora/client";
1417
+ *
1418
+ * export const sidebarOpen = createClientQuery("sidebarOpen", true);
1419
+ * export const selectedMessageId = createClientQuery("selectedMessageId", undefined as string | undefined);
1420
+ * ```
1421
+ */
1422
+ declare const createClientQuery: <T>(key: string, defaultValue: T) => ClientQueryRef<T>;
1423
+ type WSState = "idle" | "connecting" | "open" | "closed";
1424
+ /**
1425
+ * Aggregate live-socket health across every shard connection, for a UI status
1426
+ * indicator. `idle` = no socket opened yet; `connecting` = at least one socket
1427
+ * is (re)connecting and none is open; `connected` = at least one socket is open;
1428
+ * `polling` = no socket would open, so live queries are being refreshed over HTTP
1429
+ * instead (see `polling-fallback.ts` — live but slower, and shapes
1430
+ * / streams / whispers are dark; mutations go over HTTP as they do while
1431
+ * connected); `offline` = sockets exist but all are down (between reconnect
1432
+ * attempts), and a poll could not reach the origin either.
1433
+ *
1434
+ * `polling` outranks `connecting`: while the fallback is running a reconnect is
1435
+ * still armed in the background, and reporting that attempt would flicker the
1436
+ * indicator between two states while data is in fact arriving on the slow path.
1437
+ */
1438
+ type ConnectionStatus = "connected" | "connecting" | "idle" | "offline" | "polling";
1400
1439
  /**
1401
1440
  * Read/write handle over the client's live query cache, handed to a mutation's
1402
1441
  * `withOptimisticUpdate` callback so a single mutation can optimistically patch
@@ -1450,6 +1489,8 @@ declare const createLocalStore: (subscriptions: SubscriptionRegistry, shardKey:
1450
1489
  rollbacks: (() => void)[];
1451
1490
  store: OptimisticLocalStore;
1452
1491
  };
1492
+ /** Callback a shape subscription invokes with its materialized rowset on every applied poke. */
1493
+ type ShapeCallback = (rows: Record<string, unknown>[]) => void;
1453
1494
  declare const DEFAULT_MAX_BUFFER = 1024;
1454
1495
  interface StreamHandle<T = unknown> {
1455
1496
  /** Mark the stream complete (no more chunks); resolves any pending consumer to `done:true`. */
@@ -1482,22 +1523,6 @@ declare const createStream: <T>(options: {
1482
1523
  handle: StreamHandle<T>;
1483
1524
  iterable: StreamIterable<T>;
1484
1525
  };
1485
- type WSState = "idle" | "connecting" | "open" | "closed";
1486
- /**
1487
- * Aggregate live-socket health across every shard connection, for a UI status
1488
- * indicator. `idle` = no socket opened yet; `connecting` = at least one socket
1489
- * is (re)connecting and none is open; `connected` = at least one socket is open;
1490
- * `polling` = no socket would open, so live queries are being refreshed over HTTP
1491
- * instead (see {@link file://./polling-fallback.ts} — live but slower, and shapes
1492
- * / streams / whispers are dark; mutations go over HTTP as they do while
1493
- * connected); `offline` = sockets exist but all are down (between reconnect
1494
- * attempts), and a poll could not reach the origin either.
1495
- *
1496
- * `polling` outranks `connecting`: while the fallback is running a reconnect is
1497
- * still armed in the background, and reporting that attempt would flicker the
1498
- * indicator between two states while data is in fact arriving on the slow path.
1499
- */
1500
- type ConnectionStatus = "connected" | "connecting" | "idle" | "offline" | "polling";
1501
1526
  /** One shard's socket + watermark state in a {@link LunoraClient.debug} snapshot. */
1502
1527
  interface ClientDebugShard {
1503
1528
  /**
@@ -1702,31 +1727,6 @@ type ReplayIdentityVerdict = {
1702
1727
  } | {
1703
1728
  verdict: "unknown";
1704
1729
  };
1705
- /** Callback a shape subscription invokes with its materialized rowset on every applied poke. */
1706
- type ShapeCallback = (rows: Record<string, unknown>[]) => void;
1707
- /**
1708
- * An `Error` carrying the server's machine-readable `code` and (for a
1709
- * `LunoraError`) structured `data`, plus an optional actionable `hint` (Markdown)
1710
- * and `docsUrl` resolved from the central error catalog. The client's public
1711
- * error contract for RPC/batch failures — a UI can render `hint`/`docsUrl` to
1712
- * tell the user how to fix the error. The `(string & {})` arm keeps
1713
- * forward-compat/unknown server codes assignable without losing autocomplete on
1714
- * the known {@link LunoraErrorCode} union.
1715
- */
1716
- type LunoraClientError = Error & {
1717
- code?: LunoraErrorCode | (string & {});
1718
- data?: unknown;
1719
- docsUrl?: string;
1720
- hint?: string | string[];
1721
- };
1722
- /** One demuxed result slot of a {@link LunoraClient.batch} call (plan 088). */
1723
- type BatchSlot = {
1724
- error: LunoraClientError;
1725
- ok: false;
1726
- } | {
1727
- ok: true;
1728
- value: unknown;
1729
- };
1730
1730
  /**
1731
1731
  * Lunora browser/edge client. Talks RPC over HTTP and real-time deltas over
1732
1732
  * a single multiplexed WebSocket.
@@ -1761,6 +1761,8 @@ declare class LunoraClient {
1761
1761
  private readonly connectTimeoutMs;
1762
1762
  /** Keepalive cadence (ms); `0` disables the heartbeat. See {@link LunoraClientOptions.heartbeatIntervalMs}. */
1763
1763
  private readonly heartbeatIntervalMs;
1764
+ /** The socket-lifecycle inputs, built once after the constructor assigns them (none is reassigned later). */
1765
+ private readonly managedSocketOptions;
1764
1766
  /** Failed-open run that engages the HTTP polling fallback; `0` disables it. */
1765
1767
  private readonly pollingFallbackAfterFailedAttempts;
1766
1768
  /** Polling-fallback cadence (ms); `0` disables it. See {@link LunoraClientOptions.pollingFallback}. */
@@ -3921,26 +3923,6 @@ declare class LunoraClient {
3921
3923
  * a silent tokenless socket the admin gate would reject.
3922
3924
  */
3923
3925
  private openSocketWithProvidedToken;
3924
- /**
3925
- * Construct one WebSocket connection attempt and wire the shared
3926
- * lifecycle guarantees around it — the fail-fast connect-timeout, the
3927
- * identity guard that stops a superseded attempt's late `open`/`message`/
3928
- * `close`/`error` from touching a connection a newer attempt already
3929
- * owns, and (once open) the keepalive heartbeat with its half-open
3930
- * watchdog (plan 217). One call opens ONE attempt; the caller owns
3931
- * reconnect scheduling from `onClose` — mirrors the shard's existing
3932
- * `ensureSocket` / `handleDisconnect` split, now shared with
3933
- * `subscribeScheduledJobs` so it stops re-living the bug that split
3934
- * already fixed once (CLIENT-05).
3935
- *
3936
- * The identity guard is `conn.socket !== socket`, re-checked before every
3937
- * action below. `conn.socket` is reassigned to a new attempt's socket
3938
- * synchronously — right here, before `open` ever fires — so an older
3939
- * attempt's guard trips the instant it's superseded, even if its
3940
- * underlying socket only fires its real `close`/`error` much later. This
3941
- * ordering is load-bearing: preserve it exactly.
3942
- */
3943
- private openManagedSocket;
3944
3926
  /** Construct the shard socket and wire its lifecycle handlers. The connection must already be in the `connecting` state. */
3945
3927
  private openSocket;
3946
3928
  /**
@@ -3949,27 +3931,6 @@ declare class LunoraClient {
3949
3931
  */
3950
3932
  private cancelStream;
3951
3933
  private handleDisconnect;
3952
- /**
3953
- * Begin the keepalive heartbeat on an open connection attempt — the only
3954
- * caller is {@link openManagedSocket}'s own `open` handler, so both the
3955
- * shard socket and `subscribeScheduledJobs` share this one implementation
3956
- * instead of each hand-rolling their own (plan 217, generalized).
3957
- *
3958
- * Each tick first checks the half-open watchdog (see
3959
- * {@link ManagedSocketState.lastFrameAt}): if no frame at all has arrived
3960
- * within `heartbeatIntervalMs * 2.5`, the far end has gone quiet without
3961
- * the socket ever firing `close` — force it closed and report it through
3962
- * `onWatchdogTrip` (the caller's `onClose`) so the normal reconnect/backoff
3963
- * takes over instead of every live query on it silently staling forever.
3964
- * Otherwise it sends a {@link WS_KEEPALIVE_PING} text frame the server
3965
- * answers from its hibernation auto-response without waking the DO. A
3966
- * no-op when the heartbeat is disabled (an interval of zero or less);
3967
- * idempotent — any existing timer is cleared first so a reconnect can't
3968
- * leak intervals.
3969
- */
3970
- private startHeartbeat;
3971
- /** Clear a connection's keepalive timer, if any. Safe to call repeatedly. */
3972
- private stopHeartbeat;
3973
3934
  /** Mark every subscription bound to `shardKey` as needing a fresh ack. */
3974
3935
  private markShardPendingAck;
3975
3936
  /**
@@ -1,4 +1,4 @@
1
- import { L as LunoraClient, P as Preloaded } from "./lunora-client.d-C6lZEDyl.js";
1
+ import { L as LunoraClient, P as Preloaded } from "./lunora-client.d-YjgrrB49.js";
2
2
  import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./function-reference.d-Br_hsKje.js";
3
3
  /**
4
4
  * Run a query once on the server (during SSR) and capture its result in a
@@ -1,4 +1,4 @@
1
- import { L as LunoraClient, P as Preloaded } from "./lunora-client.d-DDVPWV7D.mjs";
1
+ import { L as LunoraClient, P as Preloaded } from "./lunora-client.d-SHCNd1Cn.mjs";
2
2
  import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./function-reference.d-Br_hsKje.mjs";
3
3
  /**
4
4
  * Run a query once on the server (during SSR) and capture its result in a
@@ -0,0 +1 @@
1
+ import{LunoraError as i}from"@lunora/errors";import{d as c}from"./wire-codec-3Mj5UgNj.mjs";import{getRetryAfterMs as d,TransportError as u}from"./CONFLICT_ERROR_CODE-B7qNZMMk.mjs";const f=r=>{if(r===null)return;const t=Number(r);if(Number.isFinite(t))return t>0?t*1e3:void 0;const e=Date.parse(r);if(Number.isNaN(e))return;const o=e-Date.now();return o>0?o:void 0},v=(r,t)=>{const e=f(t);if(!(e===void 0||d(r)!==void 0))return r.data===void 0?{retryAfterMs:e}:typeof r.data=="object"&&r.data!==null&&!Array.isArray(r.data)?{...r.data,retryAfterMs:e}:void 0},l=r=>{const t=r?.error;return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0},m=()=>new u("LunoraClient: batch slot carried no error envelope"),s=(r,t)=>{const e=r.error,o=typeof e?.code=="string"?e.code:void 0,a=typeof e?.message=="string"?e.message:void 0;return{code:o,messageText:(typeof r.message=="string"?r.message:void 0)??a??t}},g=r=>{const{code:t,messageText:e}=s(r,"stream error");return t===void 0?new Error(e):new i(t,e)},A=r=>{const{code:t,messageText:e}=s(r,"subscription error");return{message:e,...t===void 0?{}:{code:t}}},n=r=>{const t=new Error(r.message??"request failed");if(t.code=r.code,r.data!==void 0)try{t.data=c(r.data)}catch{}return r.hint!==void 0&&(t.hint=r.hint),r.docsUrl!==void 0&&(t.docsUrl=r.docsUrl),t},y=(r,t)=>{const e=n(r),o=v(e,t);return o!==void 0&&(e.data=o),e},h=r=>{const t=l(r);return t===void 0?m():n(t)};export{n as a,y as b,g as c,A as d,l as e,f as r,h as s};
@@ -1,4 +1,4 @@
1
- import { S as SubscriptionError, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DDVPWV7D.mjs";
1
+ import { S as SubscriptionError, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-SHCNd1Cn.mjs";
2
2
  export type {
3
3
  /**
4
4
  * `@lunora/client/query` — the framework-neutral live-query state machine shared
@@ -10,7 +10,7 @@ export type {
10
10
  * framework — each adapter supplies thin sinks that write into its own ref /
11
11
  * store / signal / cache.
12
12
  */
13
- b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DDVPWV7D.mjs";
13
+ b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-SHCNd1Cn.mjs";
14
14
  import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "../packem_shared/function-reference.d-Br_hsKje.mjs";
15
15
  import '@lunora/errors';
16
16
  import '@lunora/runtime';
@@ -1,4 +1,4 @@
1
- import { S as SubscriptionError, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-C6lZEDyl.js";
1
+ import { S as SubscriptionError, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-YjgrrB49.js";
2
2
  export type {
3
3
  /**
4
4
  * `@lunora/client/query` — the framework-neutral live-query state machine shared
@@ -10,7 +10,7 @@ export type {
10
10
  * framework — each adapter supplies thin sinks that write into its own ref /
11
11
  * store / signal / cache.
12
12
  */
13
- b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-C6lZEDyl.js";
13
+ b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-YjgrrB49.js";
14
14
  import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "../packem_shared/function-reference.d-Br_hsKje.js";
15
15
  import '@lunora/errors';
16
16
  import '@lunora/runtime';
package/dist/service.mjs CHANGED
@@ -1 +1 @@
1
- import{LunoraError as i}from"@lunora/errors";import{e as p,d}from"./packem_shared/wire-codec-3Mj5UgNj.mjs";import{e as w}from"./packem_shared/replay-CXXDZ1_h.mjs";const v="/_lunora/rpc",N="https://lunora.internal",$=e=>{const t=new Error(e.message??"request failed");return t.code=e.code,e.data!==void 0&&(t.data=d(e.data)),e.hint!==void 0&&(t.hint=e.hint),e.docsUrl!==void 0&&(t.docsUrl=e.docsUrl),t},c=async(e,t,n,s,u)=>{const o=n.__lunoraRef,f=JSON.stringify({args:p("@lunora/client/service",o,s??{}),functionPath:o,...u?.shardKey===void 0?{}:{shardKey:u.shardKey}}),a=await e.fetch(new Request(`${N}${v}`,{body:f,headers:{"content-type":"application/json"},method:"POST"}));let r;try{r=await a.json()}catch{throw new i("INTERNAL",`@lunora/client/service: ${t} "${o}" got a non-JSON response (status ${a.status.toString()}). Check the service binding points at the Lunora Worker that declares this function.`,{status:a.status})}if(r===null||typeof r!="object")throw new i("INTERNAL",`@lunora/client/service: ${t} "${o}" returned a JSON body that is not an object (status ${a.status.toString()}). Check the service binding points at the Lunora Worker that declares this function.`,{status:a.status});const l=r,h=w(l);if(h!==void 0)throw $(h);if(!a.ok)throw new i("INTERNAL",`@lunora/client/service: ${t} "${o}" failed with status ${a.status.toString()} and no error envelope.`,{status:a.status});return d(l.result)},E=e=>({action:async(t,n,s)=>await c(e,"action",t,n,s),mutation:async(t,n,s)=>await c(e,"mutation",t,n,s),query:async(t,n,s)=>await c(e,"query",t,n,s)});export{v as RPC_PATH,E as createServiceClient};
1
+ import{LunoraError as i}from"@lunora/errors";import{e as p,d}from"./packem_shared/wire-codec-3Mj5UgNj.mjs";import{e as w}from"./packem_shared/wire-errors-8iZTTKRN.mjs";const v="/_lunora/rpc",N="https://lunora.internal",$=e=>{const t=new Error(e.message??"request failed");if(t.code=e.code,e.data!==void 0)try{t.data=d(e.data)}catch{}return e.hint!==void 0&&(t.hint=e.hint),e.docsUrl!==void 0&&(t.docsUrl=e.docsUrl),t},c=async(e,t,n,s,u)=>{const o=n.__lunoraRef,f=JSON.stringify({args:p("@lunora/client/service",o,s??{}),functionPath:o,...u?.shardKey===void 0?{}:{shardKey:u.shardKey}}),a=await e.fetch(new Request(`${N}${v}`,{body:f,headers:{"content-type":"application/json"},method:"POST"}));let r;try{r=await a.json()}catch{throw new i("INTERNAL",`@lunora/client/service: ${t} "${o}" got a non-JSON response (status ${a.status.toString()}). Check the service binding points at the Lunora Worker that declares this function.`,{status:a.status})}if(r===null||typeof r!="object")throw new i("INTERNAL",`@lunora/client/service: ${t} "${o}" returned a JSON body that is not an object (status ${a.status.toString()}). Check the service binding points at the Lunora Worker that declares this function.`,{status:a.status});const h=r,l=w(h);if(l!==void 0)throw $(l);if(!a.ok)throw new i("INTERNAL",`@lunora/client/service: ${t} "${o}" failed with status ${a.status.toString()} and no error envelope.`,{status:a.status});return d(h.result)},E=e=>({action:async(t,n,s)=>await c(e,"action",t,n,s),mutation:async(t,n,s)=>await c(e,"mutation",t,n,s),query:async(t,n,s)=>await c(e,"query",t,n,s)});export{v as RPC_PATH,E as createServiceClient};
@@ -1,4 +1,4 @@
1
- import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DDVPWV7D.mjs";
1
+ import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-SHCNd1Cn.mjs";
2
2
  export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/function-reference.d-Br_hsKje.mjs";
3
3
  export {
4
4
  /**
@@ -20,7 +20,7 @@ p as preloadQuery,
20
20
  * WebSocket or touches a UI framework, so it is safe to import from any SSR
21
21
  * loader.
22
22
  */
23
- a as preloadedQueryResult } from "../packem_shared/preload.d-Cf8tiYKi.mjs";
23
+ a as preloadedQueryResult } from "../packem_shared/preload.d-Cenry5u_.mjs";
24
24
  import '@lunora/errors';
25
25
  import '@lunora/runtime';
26
26
  /**
@@ -1,4 +1,4 @@
1
- import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-C6lZEDyl.js";
1
+ import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-YjgrrB49.js";
2
2
  export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/function-reference.d-Br_hsKje.js";
3
3
  export {
4
4
  /**
@@ -20,7 +20,7 @@ p as preloadQuery,
20
20
  * WebSocket or touches a UI framework, so it is safe to import from any SSR
21
21
  * loader.
22
22
  */
23
- a as preloadedQueryResult } from "../packem_shared/preload.d-CdYylQeH.js";
23
+ a as preloadedQueryResult } from "../packem_shared/preload.d-BTB4TAd_.js";
24
24
  import '@lunora/errors';
25
25
  import '@lunora/runtime';
26
26
  /**
@@ -1 +1 @@
1
- import{getServerSession as o}from"../packem_shared/getServerSession-BaOoo1p6.mjs";import{deserializePreloaded as l,serializePreloaded as t}from"../packem_shared/deserializePreloaded-CEQci6cB.mjs";import{createServerClient as i}from"../packem_shared/createServerClient-HdISrPXa.mjs";import{preloadQuery as s,preloadedQueryResult as f}from"../packem_shared/preloadQuery-uFy24PCR.mjs";export{i as createServerClient,l as deserializePreloaded,o as getServerSession,s as preloadQuery,f as preloadedQueryResult,t as serializePreloaded};
1
+ import{getServerSession as o}from"../packem_shared/getServerSession-BaOoo1p6.mjs";import{deserializePreloaded as l,serializePreloaded as t}from"../packem_shared/deserializePreloaded-CEQci6cB.mjs";import{createServerClient as i}from"../packem_shared/createServerClient-D7zkixlG.mjs";import{preloadQuery as s,preloadedQueryResult as f}from"../packem_shared/preloadQuery-uFy24PCR.mjs";export{i as createServerClient,l as deserializePreloaded,o as getServerSession,s as preloadQuery,f as preloadedQueryResult,t as serializePreloaded};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/client",
3
- "version": "1.0.0-alpha.193",
3
+ "version": "1.0.0-alpha.195",
4
4
  "description": "Lunora browser SDK: WebSocket transport, optimistic updates, and an offline mutation queue",
5
5
  "keywords": [
6
6
  "cloudflare",