@lunora/client 1.0.0-alpha.22 → 1.0.0-alpha.23

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 (25) 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 +41 -4
  4. package/dist/index.d.ts +41 -4
  5. package/dist/index.mjs +5 -4
  6. package/dist/packem_shared/{LunoraClient-Clb118SU.mjs → LunoraClient-BBCQjjbl.mjs} +126 -244
  7. package/dist/packem_shared/{SubscriptionRegistry-D4jfIzZu.mjs → SubscriptionRegistry-CxS_Inha.mjs} +2 -2
  8. package/dist/packem_shared/createLocalStore-BtqUmOQA.mjs +2 -0
  9. package/dist/packem_shared/{createServerClient-Dxemst5C.mjs → createServerClient-CTTAmvMx.mjs} +1 -1
  10. package/dist/packem_shared/{createSnapshotPrecondition-CBwnVz6r.mjs → createSnapshotPrecondition-CxQ1T4ZP.mjs} +3 -3
  11. package/dist/packem_shared/httpStream-BJU-aflc.mjs +159 -0
  12. package/dist/packem_shared/{local-store-DtcIW4c0.mjs → local-store-DIq-UWfD.mjs} +1 -1
  13. package/dist/packem_shared/{lunora-client.d-pw-9sLl0.d.mts → lunora-client.d-JvtVpf8A.d.mts} +120 -18
  14. package/dist/packem_shared/{lunora-client.d-pw-9sLl0.d.ts → lunora-client.d-JvtVpf8A.d.ts} +120 -18
  15. package/dist/packem_shared/{preload.d-BkQr-3Vh.d.ts → preload.d-C4_d_l5v.d.ts} +1 -1
  16. package/dist/packem_shared/{preload.d-6ME5ubgq.d.mts → preload.d-DKbjGN5O.d.mts} +1 -1
  17. package/dist/packem_shared/wire-key-Djie6aaR.mjs +266 -0
  18. package/dist/query/index.d.mts +2 -2
  19. package/dist/query/index.d.ts +2 -2
  20. package/dist/ssr/index.d.mts +3 -3
  21. package/dist/ssr/index.d.ts +3 -3
  22. package/dist/ssr/index.mjs +1 -1
  23. package/package.json +2 -2
  24. package/dist/packem_shared/createLocalStore-BDbbkoXw.mjs +0 -2
  25. package/dist/packem_shared/stable-key-wv6eP48B.mjs +0 -40
@@ -1,4 +1,4 @@
1
- import { SubscriptionRegistry } from './SubscriptionRegistry-D4jfIzZu.mjs';
1
+ import { SubscriptionRegistry } from './SubscriptionRegistry-CxS_Inha.mjs';
2
2
 
3
3
  const foldOptimistic = (base, layers) => {
4
4
  let value = base;
@@ -117,6 +117,55 @@ interface FunctionReference<Kind extends FunctionKind = FunctionKind, Args = unk
117
117
  type ArgsOf<F> = F extends FunctionReference<infer _K, infer A, infer _R> ? A : never;
118
118
  /** Extract the return type from a {@link FunctionReference}. */
119
119
  type ReturnOf<F> = F extends FunctionReference<infer _K, infer _A, infer R> ? R : never;
120
+ /**
121
+ * Typed reference to an HTTP-SSE stream route (`httpRoute.&lt;verb>(path).stream()`)
122
+ * emitted by `@lunora/codegen` as `httpStreams.&lt;namespace>.&lt;name>`.
123
+ *
124
+ * Distinct from {@link FunctionReference}: this is the **HTTP-SSE route stream**
125
+ * (opened with `fetch` + `ReadableStream` against the route's own URL), not the
126
+ * WS procedure stream (`kind: "stream"`). At runtime it carries the HTTP verb
127
+ * and the route path; the phantom marker carries the chunk / searchParams /
128
+ * params types so `httpStream` (and the framework hooks over it) infer the
129
+ * chunk type end-to-end.
130
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
131
+ */
132
+ interface HttpStreamRef<Chunk = unknown, SearchParams = unknown, Params = unknown> {
133
+ /**
134
+ * Phantom marker carrying the `Chunk`/`SearchParams`/`Params` type
135
+ * parameters for inference. Never present at runtime; declared in a
136
+ * covariant (output) position so a concrete reference stays assignable to
137
+ * a widened one.
138
+ */
139
+ readonly __lunoraHttpStream?: {
140
+ chunk: Chunk;
141
+ params: Params;
142
+ searchParams: SearchParams;
143
+ };
144
+ /** HTTP verb the route binds to (uppercased), e.g. `"GET"`. */
145
+ readonly method: string;
146
+ /** The route path as declared, e.g. `/api/tokens/:id` — `:name` segments are filled from `params`. */
147
+ readonly path: string;
148
+ }
149
+ /**
150
+ * The call-side args of an HTTP-SSE stream route: `:name` path params plus URL query params.
151
+ * @experimental Part of the HTTP-SSE stream surface.
152
+ */
153
+ interface HttpStreamCallArgs<SearchParams = unknown, Params = unknown> {
154
+ /** Values for the route path's `:name` segments. */
155
+ params?: Params;
156
+ /** URL query params, appended to the request URL (undefined entries are skipped). */
157
+ searchParams?: SearchParams;
158
+ }
159
+ /**
160
+ * Extract the chunk type from a {@link HttpStreamRef}.
161
+ * @experimental Part of the HTTP-SSE stream surface.
162
+ */
163
+ type HttpStreamChunkOf<R> = R extends HttpStreamRef<infer Chunk, infer _S, infer _P> ? Chunk : never;
164
+ /**
165
+ * Extract the call-side args type from a {@link HttpStreamRef}.
166
+ * @experimental Part of the HTTP-SSE stream surface.
167
+ */
168
+ type HttpStreamArgsOf<R> = R extends HttpStreamRef<infer _C, infer S, infer P> ? HttpStreamCallArgs<S, P> : never;
120
169
  type Unsubscribe = () => void;
121
170
  /**
122
171
  * Serializable result of `preloadQuery`. Produced on the server during SSR,
@@ -309,6 +358,15 @@ interface QueryCacheAdapter {
309
358
  /** Remove one cached query by key. */
310
359
  remove: (key: string) => Promise<void>;
311
360
  }
361
+ /**
362
+ * Resolves the WS `?token=` credential fresh at every (re)connect — the channel
363
+ * for short-lived tokens (e.g. the ephemeral admin sub-token the worker mints
364
+ * at `POST /_lunora/admin/ws-token`) instead of a static secret in the URL.
365
+ * May return the token synchronously or as a Promise; returning `undefined`
366
+ * connects without a token. A thrown error / rejected Promise fails that
367
+ * connect attempt, and the client retries with its normal reconnect backoff.
368
+ */
369
+ type WsTokenProvider = () => Promise<string | undefined> | string | undefined;
312
370
  interface LunoraClientOptions {
313
371
  /**
314
372
  * Base path the worker mounts better-auth at, used by the client's
@@ -421,15 +479,21 @@ interface LunoraClientOptions {
421
479
  url: string;
422
480
  WebSocket?: typeof WebSocket;
423
481
  /**
424
- * Token appended to the WebSocket URL as `?token=…`. The server matches it
425
- * against `LUNORA_WS_BEARER` (to clear the upgrade gate) and/or
482
+ * Credential appended to the WebSocket URL as `?token=…`. The server matches
483
+ * it against `LUNORA_WS_BEARER` (to clear the upgrade gate) and/or
426
484
  * `LUNORA_ADMIN_TOKEN` (to authorize `__lunora_admin__:*` subscriptions —
427
- * what the studio sets it to). Browsers can't set headers on the
428
- * `WebSocket` constructor, so the query parameter is the only channel; it
429
- * ends up in server logs and history, so prefer a short-lived rotating
430
- * token in production.
485
+ * what the studio supplies). Browsers can't set headers on the `WebSocket`
486
+ * constructor, so the query parameter is the only channel; it ends up in
487
+ * server logs and history, so prefer a short-lived rotating token in
488
+ * production over a static secret.
489
+ *
490
+ * Pass a {@link WsTokenProvider} function to resolve the token fresh at
491
+ * every (re)connect — the channel for short-lived credentials such as the
492
+ * ephemeral admin sub-token minted by `POST /_lunora/admin/ws-token`: the
493
+ * provider re-mints on each reconnect, including the one following a `4001`
494
+ * token-expired drop, so a static master token never has to ride the URL.
431
495
  */
432
- wsToken?: string;
496
+ wsToken?: string | WsTokenProvider;
433
497
  wsUrl?: string;
434
498
  }
435
499
  /** Wire envelope sent on `POST /_lunora/rpc`. */
@@ -997,9 +1061,10 @@ interface SubscriptionState {
997
1061
  acked: boolean;
998
1062
  readonly args: Record<string, unknown>;
999
1063
  /**
1000
- * Stable-stringified `args`, computed once at subscribe time. Cached so the
1001
- * optimistic-update fan-out can compare against a mutation's args key without
1002
- * re-serializing every subscription's args on every mutation.
1064
+ * Stable wire-key of `args` (`stableWireKey`), computed once at subscribe
1065
+ * time. Cached so the optimistic-update fan-out can compare against a
1066
+ * mutation's args key without re-serializing every subscription's args on
1067
+ * every mutation.
1003
1068
  */
1004
1069
  readonly argsKey: string;
1005
1070
  readonly callbacks: Set<SubscriptionCallback>;
@@ -1068,11 +1133,13 @@ interface SubscriptionState {
1068
1133
  }
1069
1134
  /**
1070
1135
  * Active subscription registry. The client keys subscriptions by
1071
- * `(functionPath, stableStringify(args), shardKey)` so duplicate calls share a
1136
+ * `(functionPath, stableWireKey(args), shardKey)` so duplicate calls share a
1072
1137
  * single server-side registration. Args are stably encoded (keys sorted at every
1073
1138
  * depth) so two structurally-equal arg records constructed with a different key
1074
1139
  * order (`{ a, b }` vs `{ b, a }`) collapse to the same key instead of leaking a
1075
- * duplicate subscription.
1140
+ * duplicate subscription. Encoding the args' **wire form** keeps the key
1141
+ * byte-identical for pure-JSON args while giving wire-typed args (`bigint`,
1142
+ * `Date`, bytes, …) distinct stable tokens instead of a throw.
1076
1143
  */
1077
1144
  declare class SubscriptionRegistry {
1078
1145
  static key(functionPath: string, args: Record<string, unknown>, shardKey?: string): string;
@@ -1539,10 +1606,12 @@ declare class LunoraClient {
1539
1606
  * Replace the token appended to WS upgrade URLs as `?token=…` and close
1540
1607
  * every open shard socket so the reconnect picks up the new value. Call
1541
1608
  * this whenever the user's WS credential changes (rotating the admin token
1542
- * in the studio, switching workspaces, etc.). Bearer tokens for HTTP
1543
- * RPC are independentsee {@link setAuthToken}.
1609
+ * in the studio, switching workspaces, etc.). Accepts a static string or a
1610
+ * {@link WsTokenProvider} resolved fresh at every (re)connect the channel
1611
+ * for short-lived credentials like the minted ephemeral admin sub-token.
1612
+ * Bearer tokens for HTTP RPC are independent — see {@link setAuthToken}.
1544
1613
  */
1545
- setWsToken(token: string | undefined): void;
1614
+ setWsToken(token: string | undefined | WsTokenProvider): void;
1546
1615
  /**
1547
1616
  * Register (or clear, with `undefined`) the app context sent in the `connect`
1548
1617
  * envelope for a shard's socket, overriding the client-wide
@@ -1869,8 +1938,10 @@ declare class LunoraClient {
1869
1938
  * Subscribe to the live scheduled-jobs list over the SchedulerDO's admin
1870
1939
  * WebSocket. `onJobs` fires with the full list on connect and on every
1871
1940
  * change (schedule / cancel / alarm-fire). Reconnects with the client's
1872
- * configured backoff. Requires `wsToken` to be set to the admin token (the
1873
- * browser can't send an `Authorization` header on a WS). Returns an
1941
+ * configured backoff. Requires `wsToken` to be set to an admin credential
1942
+ * (the browser can't send an `Authorization` header on a WS) the master
1943
+ * token, or preferably a {@link WsTokenProvider} minting the ephemeral
1944
+ * sub-token so the master credential stays out of the URL. Returns an
1874
1945
  * unsubscribe function that closes the socket and stops reconnecting.
1875
1946
  */
1876
1947
  subscribeScheduledJobs(onJobs: (jobs: ScheduleRecord[]) => void): Unsubscribe;
@@ -2370,6 +2441,25 @@ declare class LunoraClient {
2370
2441
  maxBuffer?: number;
2371
2442
  shardKey?: string;
2372
2443
  }): StreamIterable<ReturnOf<F>>;
2444
+ /**
2445
+ * Open a typed **HTTP-SSE route stream** (`httpRoute.&lt;verb>(path).stream()`).
2446
+ * Distinct from {@link LunoraClient.stream}, which consumes the WS procedure
2447
+ * stream (`kind: "stream"`): this one opens the route's own URL with `fetch`
2448
+ * and parses the Server-Sent Events framing the route pump writes (`data:`
2449
+ * chunks, a final `event: complete`, an `event: error` on throw).
2450
+ *
2451
+ * The reference comes from the generated `httpStreams.*` registry, so the
2452
+ * yielded chunk type is the route handler's yielded type. Cancelling the
2453
+ * returned iterable (or aborting `options.signal`) aborts the fetch, which
2454
+ * the server handler observes via its `signal`. The client's bearer token
2455
+ * (when set) rides as an `authorization` header.
2456
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
2457
+ */
2458
+ httpStream<Ref extends HttpStreamRef>(route: Ref, args?: HttpStreamArgsOf<Ref>, options?: {
2459
+ headers?: Record<string, string>;
2460
+ maxBuffer?: number;
2461
+ signal?: AbortSignal;
2462
+ }): StreamIterable<HttpStreamChunkOf<Ref>>;
2373
2463
  close(): void;
2374
2464
  /**
2375
2465
  * Persist a mutation that can't go out on the wire right now (offline, or
@@ -2515,6 +2605,18 @@ declare class LunoraClient {
2515
2605
  */
2516
2606
  private resendShapeSubscriptions;
2517
2607
  private ensureSocket;
2608
+ /**
2609
+ * Resolve the {@link WsTokenProvider} and open the shard socket with the
2610
+ * minted token. The connection is already in the `connecting` state, so the
2611
+ * async gap is race-guarded: a client `close()`, a `setWsToken` bounce, or a
2612
+ * competing connect that landed first all abandon this attempt. A provider
2613
+ * failure fails the attempt through {@link handleDisconnect}, which arms the
2614
+ * normal reconnect backoff — a broken mint endpoint degrades to retries, not
2615
+ * a silent tokenless socket the admin gate would reject.
2616
+ */
2617
+ private openSocketWithProvidedToken;
2618
+ /** Construct the shard socket and wire its lifecycle handlers. The connection must already be in the `connecting` state. */
2619
+ private openSocket;
2518
2620
  private handleDisconnect;
2519
2621
  /**
2520
2622
  * Begin the keepalive heartbeat on an open connection. Each tick sends a
@@ -2719,4 +2821,4 @@ declare class LunoraClient {
2719
2821
  */
2720
2822
  private settleReplayBatchSlots;
2721
2823
  }
2722
- export { StorageObject as $, ArgsOf as A, BookmarkStorage as B, CONFLICT_ERROR_CODE as C, DEFAULT_MAX_BUFFER as D, RowOp as E, FunctionReference as F, GlobalFacetResult as G, RpcEnvelope as H, RpcResponseBody as I, ScheduleRecord as J, SchedulerPoolStatus as K, LunoraClient as L, MutationCallOptions as M, SchedulerStatus as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, ServerMessage as T, User as U, ServerPokeEndMessage as V, ServerPokePartMessage as W, ServerPokeStartMessage as X, ShardTrafficEntry as Y, ShardTrafficResult as Z, StorageListPage as _, Unsubscribe as a, StreamHandle as a0, StreamIterable as a1, SubscriptionCallback as a2, SubscriptionRegistry as a3, SubscriptionState as a4, SyncWatermark as a5, WorkflowInstanceAction as a6, WorkflowInstanceDetail as a7, WorkflowInstancePage as a8, WorkflowInstanceStatus as a9, WorkflowInstanceSummary as aa, WorkflowStepDetail as ab, createClientQuery as ac, createLocalStore as ad, createStream as ae, getErrorCode as af, getRetryAfterMs as ag, isConflictError as ah, isForbiddenError as ai, isRateLimitedError as aj, isUnauthorizedError as ak, SubscriptionErrorCallback as b, PersistenceAdapter as c, ReconnectOptions as d, BatchSlot as e, CachedQuery as f, ClientMessage as g, ClientQueryRef as h, ClientShapeSubscribeMessage as i, ClientShapeUnsubscribeMessage as j, ConnectionStatus as k, FunctionArgumentDescriptor as l, FunctionDescriptor as m, GlobalFacetValue as n, GlobalFilterClause as o, GlobalTableInfo as p, GlobalTablePage as q, LunoraClientError as r, LunoraClientOptions as s, LunoraErrorCode as t, MutationSettledEvent as u, OptimisticLocalStore as v, OptimisticUpdate as w, OutboxMutation as x, OutboxSink as y, PersistedMutation as z };
2824
+ export { ServerPokePartMessage as $, ArgsOf as A, BookmarkStorage as B, CONFLICT_ERROR_CODE as C, DEFAULT_MAX_BUFFER as D, OptimisticUpdate as E, FunctionReference as F, GlobalFacetResult as G, HttpStreamRef as H, OutboxMutation as I, OutboxSink as J, PersistedMutation as K, LunoraClient as L, MutationCallOptions as M, RowOp as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, RpcEnvelope as T, User as U, RpcResponseBody as V, ScheduleRecord as W, SchedulerPoolStatus as X, SchedulerStatus as Y, ServerMessage as Z, ServerPokeEndMessage as _, Unsubscribe as a, ServerPokeStartMessage as a0, ShardTrafficEntry as a1, ShardTrafficResult as a2, StorageListPage as a3, StorageObject as a4, StreamHandle as a5, SubscriptionCallback as a6, SubscriptionRegistry as a7, SubscriptionState as a8, SyncWatermark as a9, WorkflowInstanceAction as aa, WorkflowInstanceDetail as ab, WorkflowInstancePage as ac, WorkflowInstanceStatus as ad, WorkflowInstanceSummary as ae, WorkflowStepDetail as af, WsTokenProvider as ag, createClientQuery as ah, createLocalStore as ai, createStream as aj, getErrorCode as ak, getRetryAfterMs as al, isConflictError as am, isForbiddenError as an, isRateLimitedError as ao, isUnauthorizedError as ap, SubscriptionErrorCallback as b, PersistenceAdapter as c, HttpStreamArgsOf as d, HttpStreamChunkOf as e, StreamIterable as f, ReconnectOptions as g, BatchSlot as h, CachedQuery as i, ClientMessage as j, ClientQueryRef as k, ClientShapeSubscribeMessage as l, ClientShapeUnsubscribeMessage as m, ConnectionStatus as n, FunctionArgumentDescriptor as o, FunctionDescriptor as p, GlobalFacetValue as q, GlobalFilterClause as r, GlobalTableInfo as s, GlobalTablePage as t, HttpStreamCallArgs as u, LunoraClientError as v, LunoraClientOptions as w, LunoraErrorCode as x, MutationSettledEvent as y, OptimisticLocalStore as z };
@@ -117,6 +117,55 @@ interface FunctionReference<Kind extends FunctionKind = FunctionKind, Args = unk
117
117
  type ArgsOf<F> = F extends FunctionReference<infer _K, infer A, infer _R> ? A : never;
118
118
  /** Extract the return type from a {@link FunctionReference}. */
119
119
  type ReturnOf<F> = F extends FunctionReference<infer _K, infer _A, infer R> ? R : never;
120
+ /**
121
+ * Typed reference to an HTTP-SSE stream route (`httpRoute.&lt;verb>(path).stream()`)
122
+ * emitted by `@lunora/codegen` as `httpStreams.&lt;namespace>.&lt;name>`.
123
+ *
124
+ * Distinct from {@link FunctionReference}: this is the **HTTP-SSE route stream**
125
+ * (opened with `fetch` + `ReadableStream` against the route's own URL), not the
126
+ * WS procedure stream (`kind: "stream"`). At runtime it carries the HTTP verb
127
+ * and the route path; the phantom marker carries the chunk / searchParams /
128
+ * params types so `httpStream` (and the framework hooks over it) infer the
129
+ * chunk type end-to-end.
130
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
131
+ */
132
+ interface HttpStreamRef<Chunk = unknown, SearchParams = unknown, Params = unknown> {
133
+ /**
134
+ * Phantom marker carrying the `Chunk`/`SearchParams`/`Params` type
135
+ * parameters for inference. Never present at runtime; declared in a
136
+ * covariant (output) position so a concrete reference stays assignable to
137
+ * a widened one.
138
+ */
139
+ readonly __lunoraHttpStream?: {
140
+ chunk: Chunk;
141
+ params: Params;
142
+ searchParams: SearchParams;
143
+ };
144
+ /** HTTP verb the route binds to (uppercased), e.g. `"GET"`. */
145
+ readonly method: string;
146
+ /** The route path as declared, e.g. `/api/tokens/:id` — `:name` segments are filled from `params`. */
147
+ readonly path: string;
148
+ }
149
+ /**
150
+ * The call-side args of an HTTP-SSE stream route: `:name` path params plus URL query params.
151
+ * @experimental Part of the HTTP-SSE stream surface.
152
+ */
153
+ interface HttpStreamCallArgs<SearchParams = unknown, Params = unknown> {
154
+ /** Values for the route path's `:name` segments. */
155
+ params?: Params;
156
+ /** URL query params, appended to the request URL (undefined entries are skipped). */
157
+ searchParams?: SearchParams;
158
+ }
159
+ /**
160
+ * Extract the chunk type from a {@link HttpStreamRef}.
161
+ * @experimental Part of the HTTP-SSE stream surface.
162
+ */
163
+ type HttpStreamChunkOf<R> = R extends HttpStreamRef<infer Chunk, infer _S, infer _P> ? Chunk : never;
164
+ /**
165
+ * Extract the call-side args type from a {@link HttpStreamRef}.
166
+ * @experimental Part of the HTTP-SSE stream surface.
167
+ */
168
+ type HttpStreamArgsOf<R> = R extends HttpStreamRef<infer _C, infer S, infer P> ? HttpStreamCallArgs<S, P> : never;
120
169
  type Unsubscribe = () => void;
121
170
  /**
122
171
  * Serializable result of `preloadQuery`. Produced on the server during SSR,
@@ -309,6 +358,15 @@ interface QueryCacheAdapter {
309
358
  /** Remove one cached query by key. */
310
359
  remove: (key: string) => Promise<void>;
311
360
  }
361
+ /**
362
+ * Resolves the WS `?token=` credential fresh at every (re)connect — the channel
363
+ * for short-lived tokens (e.g. the ephemeral admin sub-token the worker mints
364
+ * at `POST /_lunora/admin/ws-token`) instead of a static secret in the URL.
365
+ * May return the token synchronously or as a Promise; returning `undefined`
366
+ * connects without a token. A thrown error / rejected Promise fails that
367
+ * connect attempt, and the client retries with its normal reconnect backoff.
368
+ */
369
+ type WsTokenProvider = () => Promise<string | undefined> | string | undefined;
312
370
  interface LunoraClientOptions {
313
371
  /**
314
372
  * Base path the worker mounts better-auth at, used by the client's
@@ -421,15 +479,21 @@ interface LunoraClientOptions {
421
479
  url: string;
422
480
  WebSocket?: typeof WebSocket;
423
481
  /**
424
- * Token appended to the WebSocket URL as `?token=…`. The server matches it
425
- * against `LUNORA_WS_BEARER` (to clear the upgrade gate) and/or
482
+ * Credential appended to the WebSocket URL as `?token=…`. The server matches
483
+ * it against `LUNORA_WS_BEARER` (to clear the upgrade gate) and/or
426
484
  * `LUNORA_ADMIN_TOKEN` (to authorize `__lunora_admin__:*` subscriptions —
427
- * what the studio sets it to). Browsers can't set headers on the
428
- * `WebSocket` constructor, so the query parameter is the only channel; it
429
- * ends up in server logs and history, so prefer a short-lived rotating
430
- * token in production.
485
+ * what the studio supplies). Browsers can't set headers on the `WebSocket`
486
+ * constructor, so the query parameter is the only channel; it ends up in
487
+ * server logs and history, so prefer a short-lived rotating token in
488
+ * production over a static secret.
489
+ *
490
+ * Pass a {@link WsTokenProvider} function to resolve the token fresh at
491
+ * every (re)connect — the channel for short-lived credentials such as the
492
+ * ephemeral admin sub-token minted by `POST /_lunora/admin/ws-token`: the
493
+ * provider re-mints on each reconnect, including the one following a `4001`
494
+ * token-expired drop, so a static master token never has to ride the URL.
431
495
  */
432
- wsToken?: string;
496
+ wsToken?: string | WsTokenProvider;
433
497
  wsUrl?: string;
434
498
  }
435
499
  /** Wire envelope sent on `POST /_lunora/rpc`. */
@@ -997,9 +1061,10 @@ interface SubscriptionState {
997
1061
  acked: boolean;
998
1062
  readonly args: Record<string, unknown>;
999
1063
  /**
1000
- * Stable-stringified `args`, computed once at subscribe time. Cached so the
1001
- * optimistic-update fan-out can compare against a mutation's args key without
1002
- * re-serializing every subscription's args on every mutation.
1064
+ * Stable wire-key of `args` (`stableWireKey`), computed once at subscribe
1065
+ * time. Cached so the optimistic-update fan-out can compare against a
1066
+ * mutation's args key without re-serializing every subscription's args on
1067
+ * every mutation.
1003
1068
  */
1004
1069
  readonly argsKey: string;
1005
1070
  readonly callbacks: Set<SubscriptionCallback>;
@@ -1068,11 +1133,13 @@ interface SubscriptionState {
1068
1133
  }
1069
1134
  /**
1070
1135
  * Active subscription registry. The client keys subscriptions by
1071
- * `(functionPath, stableStringify(args), shardKey)` so duplicate calls share a
1136
+ * `(functionPath, stableWireKey(args), shardKey)` so duplicate calls share a
1072
1137
  * single server-side registration. Args are stably encoded (keys sorted at every
1073
1138
  * depth) so two structurally-equal arg records constructed with a different key
1074
1139
  * order (`{ a, b }` vs `{ b, a }`) collapse to the same key instead of leaking a
1075
- * duplicate subscription.
1140
+ * duplicate subscription. Encoding the args' **wire form** keeps the key
1141
+ * byte-identical for pure-JSON args while giving wire-typed args (`bigint`,
1142
+ * `Date`, bytes, …) distinct stable tokens instead of a throw.
1076
1143
  */
1077
1144
  declare class SubscriptionRegistry {
1078
1145
  static key(functionPath: string, args: Record<string, unknown>, shardKey?: string): string;
@@ -1539,10 +1606,12 @@ declare class LunoraClient {
1539
1606
  * Replace the token appended to WS upgrade URLs as `?token=…` and close
1540
1607
  * every open shard socket so the reconnect picks up the new value. Call
1541
1608
  * this whenever the user's WS credential changes (rotating the admin token
1542
- * in the studio, switching workspaces, etc.). Bearer tokens for HTTP
1543
- * RPC are independentsee {@link setAuthToken}.
1609
+ * in the studio, switching workspaces, etc.). Accepts a static string or a
1610
+ * {@link WsTokenProvider} resolved fresh at every (re)connect the channel
1611
+ * for short-lived credentials like the minted ephemeral admin sub-token.
1612
+ * Bearer tokens for HTTP RPC are independent — see {@link setAuthToken}.
1544
1613
  */
1545
- setWsToken(token: string | undefined): void;
1614
+ setWsToken(token: string | undefined | WsTokenProvider): void;
1546
1615
  /**
1547
1616
  * Register (or clear, with `undefined`) the app context sent in the `connect`
1548
1617
  * envelope for a shard's socket, overriding the client-wide
@@ -1869,8 +1938,10 @@ declare class LunoraClient {
1869
1938
  * Subscribe to the live scheduled-jobs list over the SchedulerDO's admin
1870
1939
  * WebSocket. `onJobs` fires with the full list on connect and on every
1871
1940
  * change (schedule / cancel / alarm-fire). Reconnects with the client's
1872
- * configured backoff. Requires `wsToken` to be set to the admin token (the
1873
- * browser can't send an `Authorization` header on a WS). Returns an
1941
+ * configured backoff. Requires `wsToken` to be set to an admin credential
1942
+ * (the browser can't send an `Authorization` header on a WS) the master
1943
+ * token, or preferably a {@link WsTokenProvider} minting the ephemeral
1944
+ * sub-token so the master credential stays out of the URL. Returns an
1874
1945
  * unsubscribe function that closes the socket and stops reconnecting.
1875
1946
  */
1876
1947
  subscribeScheduledJobs(onJobs: (jobs: ScheduleRecord[]) => void): Unsubscribe;
@@ -2370,6 +2441,25 @@ declare class LunoraClient {
2370
2441
  maxBuffer?: number;
2371
2442
  shardKey?: string;
2372
2443
  }): StreamIterable<ReturnOf<F>>;
2444
+ /**
2445
+ * Open a typed **HTTP-SSE route stream** (`httpRoute.&lt;verb>(path).stream()`).
2446
+ * Distinct from {@link LunoraClient.stream}, which consumes the WS procedure
2447
+ * stream (`kind: "stream"`): this one opens the route's own URL with `fetch`
2448
+ * and parses the Server-Sent Events framing the route pump writes (`data:`
2449
+ * chunks, a final `event: complete`, an `event: error` on throw).
2450
+ *
2451
+ * The reference comes from the generated `httpStreams.*` registry, so the
2452
+ * yielded chunk type is the route handler's yielded type. Cancelling the
2453
+ * returned iterable (or aborting `options.signal`) aborts the fetch, which
2454
+ * the server handler observes via its `signal`. The client's bearer token
2455
+ * (when set) rides as an `authorization` header.
2456
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
2457
+ */
2458
+ httpStream<Ref extends HttpStreamRef>(route: Ref, args?: HttpStreamArgsOf<Ref>, options?: {
2459
+ headers?: Record<string, string>;
2460
+ maxBuffer?: number;
2461
+ signal?: AbortSignal;
2462
+ }): StreamIterable<HttpStreamChunkOf<Ref>>;
2373
2463
  close(): void;
2374
2464
  /**
2375
2465
  * Persist a mutation that can't go out on the wire right now (offline, or
@@ -2515,6 +2605,18 @@ declare class LunoraClient {
2515
2605
  */
2516
2606
  private resendShapeSubscriptions;
2517
2607
  private ensureSocket;
2608
+ /**
2609
+ * Resolve the {@link WsTokenProvider} and open the shard socket with the
2610
+ * minted token. The connection is already in the `connecting` state, so the
2611
+ * async gap is race-guarded: a client `close()`, a `setWsToken` bounce, or a
2612
+ * competing connect that landed first all abandon this attempt. A provider
2613
+ * failure fails the attempt through {@link handleDisconnect}, which arms the
2614
+ * normal reconnect backoff — a broken mint endpoint degrades to retries, not
2615
+ * a silent tokenless socket the admin gate would reject.
2616
+ */
2617
+ private openSocketWithProvidedToken;
2618
+ /** Construct the shard socket and wire its lifecycle handlers. The connection must already be in the `connecting` state. */
2619
+ private openSocket;
2518
2620
  private handleDisconnect;
2519
2621
  /**
2520
2622
  * Begin the keepalive heartbeat on an open connection. Each tick sends a
@@ -2719,4 +2821,4 @@ declare class LunoraClient {
2719
2821
  */
2720
2822
  private settleReplayBatchSlots;
2721
2823
  }
2722
- export { StorageObject as $, ArgsOf as A, BookmarkStorage as B, CONFLICT_ERROR_CODE as C, DEFAULT_MAX_BUFFER as D, RowOp as E, FunctionReference as F, GlobalFacetResult as G, RpcEnvelope as H, RpcResponseBody as I, ScheduleRecord as J, SchedulerPoolStatus as K, LunoraClient as L, MutationCallOptions as M, SchedulerStatus as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, ServerMessage as T, User as U, ServerPokeEndMessage as V, ServerPokePartMessage as W, ServerPokeStartMessage as X, ShardTrafficEntry as Y, ShardTrafficResult as Z, StorageListPage as _, Unsubscribe as a, StreamHandle as a0, StreamIterable as a1, SubscriptionCallback as a2, SubscriptionRegistry as a3, SubscriptionState as a4, SyncWatermark as a5, WorkflowInstanceAction as a6, WorkflowInstanceDetail as a7, WorkflowInstancePage as a8, WorkflowInstanceStatus as a9, WorkflowInstanceSummary as aa, WorkflowStepDetail as ab, createClientQuery as ac, createLocalStore as ad, createStream as ae, getErrorCode as af, getRetryAfterMs as ag, isConflictError as ah, isForbiddenError as ai, isRateLimitedError as aj, isUnauthorizedError as ak, SubscriptionErrorCallback as b, PersistenceAdapter as c, ReconnectOptions as d, BatchSlot as e, CachedQuery as f, ClientMessage as g, ClientQueryRef as h, ClientShapeSubscribeMessage as i, ClientShapeUnsubscribeMessage as j, ConnectionStatus as k, FunctionArgumentDescriptor as l, FunctionDescriptor as m, GlobalFacetValue as n, GlobalFilterClause as o, GlobalTableInfo as p, GlobalTablePage as q, LunoraClientError as r, LunoraClientOptions as s, LunoraErrorCode as t, MutationSettledEvent as u, OptimisticLocalStore as v, OptimisticUpdate as w, OutboxMutation as x, OutboxSink as y, PersistedMutation as z };
2824
+ export { ServerPokePartMessage as $, ArgsOf as A, BookmarkStorage as B, CONFLICT_ERROR_CODE as C, DEFAULT_MAX_BUFFER as D, OptimisticUpdate as E, FunctionReference as F, GlobalFacetResult as G, HttpStreamRef as H, OutboxMutation as I, OutboxSink as J, PersistedMutation as K, LunoraClient as L, MutationCallOptions as M, RowOp as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, RpcEnvelope as T, User as U, RpcResponseBody as V, ScheduleRecord as W, SchedulerPoolStatus as X, SchedulerStatus as Y, ServerMessage as Z, ServerPokeEndMessage as _, Unsubscribe as a, ServerPokeStartMessage as a0, ShardTrafficEntry as a1, ShardTrafficResult as a2, StorageListPage as a3, StorageObject as a4, StreamHandle as a5, SubscriptionCallback as a6, SubscriptionRegistry as a7, SubscriptionState as a8, SyncWatermark as a9, WorkflowInstanceAction as aa, WorkflowInstanceDetail as ab, WorkflowInstancePage as ac, WorkflowInstanceStatus as ad, WorkflowInstanceSummary as ae, WorkflowStepDetail as af, WsTokenProvider as ag, createClientQuery as ah, createLocalStore as ai, createStream as aj, getErrorCode as ak, getRetryAfterMs as al, isConflictError as am, isForbiddenError as an, isRateLimitedError as ao, isUnauthorizedError as ap, SubscriptionErrorCallback as b, PersistenceAdapter as c, HttpStreamArgsOf as d, HttpStreamChunkOf as e, StreamIterable as f, ReconnectOptions as g, BatchSlot as h, CachedQuery as i, ClientMessage as j, ClientQueryRef as k, ClientShapeSubscribeMessage as l, ClientShapeUnsubscribeMessage as m, ConnectionStatus as n, FunctionArgumentDescriptor as o, FunctionDescriptor as p, GlobalFacetValue as q, GlobalFilterClause as r, GlobalTableInfo as s, GlobalTablePage as t, HttpStreamCallArgs as u, LunoraClientError as v, LunoraClientOptions as w, LunoraErrorCode as x, MutationSettledEvent as y, OptimisticLocalStore as z };
@@ -1,4 +1,4 @@
1
- import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-pw-9sLl0.js";
1
+ import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-JvtVpf8A.js";
2
2
  /**
3
3
  * Run a query once on the server (during SSR) and capture its result in a
4
4
  * serializable {@link Preloaded} token. Embed the token in the rendered HTML and
@@ -1,4 +1,4 @@
1
- import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-pw-9sLl0.mjs";
1
+ import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-JvtVpf8A.mjs";
2
2
  /**
3
3
  * Run a query once on the server (during SSR) and capture its result in a
4
4
  * serializable {@link Preloaded} token. Embed the token in the rendered HTML and