@lunora/client 1.0.0-alpha.22 → 1.0.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/index.d.mts +66 -4
- package/dist/index.d.ts +66 -4
- package/dist/index.mjs +8 -7
- package/dist/packem_shared/{LunoraClient-Clb118SU.mjs → LunoraClient-D3h4P7hg.mjs} +143 -247
- package/dist/packem_shared/{OfflineQueue-B4HUF7rt.mjs → OfflineQueue-BgarnAub.mjs} +1 -1
- package/dist/packem_shared/{SubscriptionRegistry-D4jfIzZu.mjs → SubscriptionRegistry-CxS_Inha.mjs} +2 -2
- package/dist/packem_shared/{TabCoordinator-BwRR8H06.mjs → TabCoordinator-D_5oNTTt.mjs} +48 -12
- package/dist/packem_shared/{createClientQuery-CQ51bWAE.mjs → createClientQuery-dJZg1ohm.mjs} +15 -6
- package/dist/packem_shared/createLocalStore-BtqUmOQA.mjs +2 -0
- package/dist/packem_shared/{createServerClient-Dxemst5C.mjs → createServerClient-DzeC2J3A.mjs} +1 -1
- package/dist/packem_shared/{createSnapshotPrecondition-CBwnVz6r.mjs → createSnapshotPrecondition-CxQ1T4ZP.mjs} +3 -3
- package/dist/packem_shared/httpStream-DIdL8NEw.mjs +168 -0
- package/dist/packem_shared/{local-store-DtcIW4c0.mjs → local-store-DIq-UWfD.mjs} +1 -1
- package/dist/packem_shared/{lunora-client.d-pw-9sLl0.d.mts → lunora-client.d-B8bdwHLr.d.mts} +132 -18
- package/dist/packem_shared/{lunora-client.d-pw-9sLl0.d.ts → lunora-client.d-B8bdwHLr.d.ts} +132 -18
- package/dist/packem_shared/{offline-queue-CF4_Co5k.mjs → offline-queue-N-1JvYb4.mjs} +14 -2
- package/dist/packem_shared/{preload.d-BkQr-3Vh.d.ts → preload.d-BPy9qajK.d.ts} +1 -1
- package/dist/packem_shared/{preload.d-6ME5ubgq.d.mts → preload.d-Ccrgw2z0.d.mts} +1 -1
- package/dist/packem_shared/wire-key-Djie6aaR.mjs +266 -0
- package/dist/query/index.d.mts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/ssr/index.d.mts +3 -3
- package/dist/ssr/index.d.ts +3 -3
- package/dist/ssr/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/createLocalStore-BDbbkoXw.mjs +0 -2
- package/dist/packem_shared/stable-key-wv6eP48B.mjs +0 -40
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
import { createStream } from './DEFAULT_MAX_BUFFER-7hFnzNk9.mjs';
|
|
3
|
+
|
|
4
|
+
const SSE_FIELD_SPACE_RE = /^ /u;
|
|
5
|
+
const parameterToString = (value) => typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
6
|
+
const parseSseFrame = (raw) => {
|
|
7
|
+
let event = "";
|
|
8
|
+
const dataLines = [];
|
|
9
|
+
for (const line of raw.split("\n")) {
|
|
10
|
+
if (line.startsWith("event:")) {
|
|
11
|
+
event = line.slice("event:".length).replace(SSE_FIELD_SPACE_RE, "");
|
|
12
|
+
} else if (line.startsWith("data:")) {
|
|
13
|
+
dataLines.push(line.slice("data:".length).replace(SSE_FIELD_SPACE_RE, ""));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return { data: dataLines.join("\n"), event };
|
|
17
|
+
};
|
|
18
|
+
const buildHttpStreamUrl = (route, args, baseUrl) => {
|
|
19
|
+
const parameters = args.params ?? {};
|
|
20
|
+
const path = route.path.split("/").map((segment) => {
|
|
21
|
+
if (!segment.startsWith(":")) {
|
|
22
|
+
return segment;
|
|
23
|
+
}
|
|
24
|
+
const name = segment.slice(1);
|
|
25
|
+
const value = parameters[name];
|
|
26
|
+
if (value === void 0) {
|
|
27
|
+
throw new LunoraError("HTTP_STREAM_MISSING_PARAM", `httpStream: missing path param ":${name}" for route ${route.path}`);
|
|
28
|
+
}
|
|
29
|
+
return encodeURIComponent(parameterToString(value));
|
|
30
|
+
}).join("/");
|
|
31
|
+
const search = new URLSearchParams();
|
|
32
|
+
for (const [key, value] of Object.entries(args.searchParams ?? {})) {
|
|
33
|
+
if (value !== void 0) {
|
|
34
|
+
search.set(key, parameterToString(value));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const query = search.toString();
|
|
38
|
+
const trimmedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
39
|
+
return `${trimmedBase}${path}${query === "" ? "" : `?${query}`}`;
|
|
40
|
+
};
|
|
41
|
+
const handleSseFrame = (frame, handle) => {
|
|
42
|
+
if (frame.event === "complete") {
|
|
43
|
+
handle.complete();
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (frame.event === "error") {
|
|
47
|
+
let payload = {};
|
|
48
|
+
try {
|
|
49
|
+
payload = JSON.parse(frame.data);
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
const message = typeof payload.message === "string" ? payload.message : "stream error";
|
|
53
|
+
const code = typeof payload.code === "string" ? payload.code : "HTTP_STREAM_ERROR";
|
|
54
|
+
handle.fail(new LunoraError(code, message));
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if ((frame.event === "" || frame.event === "message") && frame.data !== "") {
|
|
58
|
+
try {
|
|
59
|
+
handle.push(JSON.parse(frame.data));
|
|
60
|
+
} catch {
|
|
61
|
+
handle.fail(new LunoraError("HTTP_STREAM_BAD_CHUNK", "httpStream: malformed SSE chunk (invalid JSON)"));
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
};
|
|
67
|
+
const pumpSseBody = async (body, handle) => {
|
|
68
|
+
const reader = body.getReader();
|
|
69
|
+
const decoder = new TextDecoder();
|
|
70
|
+
let buffer = "";
|
|
71
|
+
const drainFrames = () => {
|
|
72
|
+
let separatorIndex = buffer.indexOf("\n\n");
|
|
73
|
+
while (separatorIndex !== -1) {
|
|
74
|
+
const frame = parseSseFrame(buffer.slice(0, separatorIndex));
|
|
75
|
+
buffer = buffer.slice(separatorIndex + 2);
|
|
76
|
+
if (handleSseFrame(frame, handle)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
separatorIndex = buffer.indexOf("\n\n");
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
};
|
|
83
|
+
for (; ; ) {
|
|
84
|
+
const { done, value } = await reader.read();
|
|
85
|
+
if (done) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
buffer += decoder.decode(value, { stream: true }).replaceAll("\r\n", "\n");
|
|
89
|
+
if (drainFrames()) {
|
|
90
|
+
await reader.cancel().catch(() => {
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
buffer += decoder.decode().replaceAll("\r\n", "\n");
|
|
96
|
+
if (!drainFrames()) {
|
|
97
|
+
handle.fail(new LunoraError("HTTP_STREAM_INTERRUPTED", "httpStream: stream ended without a complete frame"));
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const httpStream = (route, args, options = {}) => {
|
|
101
|
+
const fetchImpl = options.fetch ?? (typeof fetch === "function" ? fetch.bind(globalThis) : void 0);
|
|
102
|
+
if (!fetchImpl) {
|
|
103
|
+
throw new LunoraError("INTERNAL", "httpStream: no `fetch` implementation available");
|
|
104
|
+
}
|
|
105
|
+
const url = buildHttpStreamUrl(route, args ?? {}, options.baseUrl ?? "");
|
|
106
|
+
const ac = new AbortController();
|
|
107
|
+
let onExternalAbort;
|
|
108
|
+
if (options.signal) {
|
|
109
|
+
if (options.signal.aborted) {
|
|
110
|
+
ac.abort();
|
|
111
|
+
} else {
|
|
112
|
+
onExternalAbort = () => {
|
|
113
|
+
ac.abort();
|
|
114
|
+
};
|
|
115
|
+
options.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const detachExternalAbort = () => {
|
|
119
|
+
if (onExternalAbort) {
|
|
120
|
+
options.signal?.removeEventListener("abort", onExternalAbort);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const { handle, iterable } = createStream({
|
|
124
|
+
maxBuffer: options.maxBuffer,
|
|
125
|
+
onCancel: () => {
|
|
126
|
+
ac.abort();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
(async () => {
|
|
130
|
+
try {
|
|
131
|
+
const response = await fetchImpl(url, {
|
|
132
|
+
headers: { accept: "text/event-stream", ...options.headers },
|
|
133
|
+
method: route.method,
|
|
134
|
+
signal: ac.signal
|
|
135
|
+
});
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
await response.body?.cancel().catch(() => {
|
|
138
|
+
});
|
|
139
|
+
handle.fail(
|
|
140
|
+
new LunoraError("HTTP_STREAM_STATUS", `httpStream: request failed (status ${response.status.toString()})`, {
|
|
141
|
+
status: response.status
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!response.body) {
|
|
147
|
+
handle.fail(new LunoraError("HTTP_STREAM_NO_BODY", "httpStream: response has no body"));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
await pumpSseBody(response.body, handle);
|
|
151
|
+
} finally {
|
|
152
|
+
detachExternalAbort();
|
|
153
|
+
}
|
|
154
|
+
})().catch((error) => {
|
|
155
|
+
if (ac.signal.aborted) {
|
|
156
|
+
handle.complete();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (error instanceof Error && "code" in error) {
|
|
160
|
+
handle.fail(error);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
handle.fail(new LunoraError("HTTP_STREAM_TRANSPORT", error instanceof Error ? error.message : String(error), { cause: error }));
|
|
164
|
+
});
|
|
165
|
+
return iterable;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export { httpStream };
|
package/dist/packem_shared/{lunora-client.d-pw-9sLl0.d.mts → lunora-client.d-B8bdwHLr.d.mts}
RENAMED
|
@@ -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.<verb>(path).stream()`)
|
|
122
|
+
* emitted by `@lunora/codegen` as `httpStreams.<namespace>.<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
|
-
*
|
|
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
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
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-
|
|
1001
|
-
* optimistic-update fan-out can compare against a
|
|
1002
|
-
* re-serializing every subscription's args on
|
|
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,
|
|
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.).
|
|
1543
|
-
*
|
|
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
|
|
@@ -1655,6 +1724,18 @@ declare class LunoraClient {
|
|
|
1655
1724
|
*/
|
|
1656
1725
|
onMutationSettled(listener: (event: MutationSettledEvent) => void): Unsubscribe;
|
|
1657
1726
|
/**
|
|
1727
|
+
* The `WebSocket` implementation this client was constructed with (an
|
|
1728
|
+
* explicit `options.WebSocket`, or the ambient global on platforms that have
|
|
1729
|
+
* one) — `undefined` if neither is available. This is the seam a feature
|
|
1730
|
+
* that opens its OWN socket outside the client's multiplexed connection
|
|
1731
|
+
* (e.g. a voice-agent hook) should default to, instead of reaching for
|
|
1732
|
+
* `globalThis.WebSocket` directly: on React Native the client wraps this
|
|
1733
|
+
* constructor to inject the auth-headers factory's credential onto the
|
|
1734
|
+
* upgrade request (`createLunoraClient`'s `withAuthWebSocket`), which a raw
|
|
1735
|
+
* `new globalThis.WebSocket(url)` would silently bypass.
|
|
1736
|
+
*/
|
|
1737
|
+
getWebSocketImpl(): typeof WebSocket | undefined;
|
|
1738
|
+
/**
|
|
1658
1739
|
* Read the current value for a {@link ClientQueryRef}. Returns
|
|
1659
1740
|
* `ref.defaultValue` when no value has been explicitly set.
|
|
1660
1741
|
*/
|
|
@@ -1869,8 +1950,10 @@ declare class LunoraClient {
|
|
|
1869
1950
|
* Subscribe to the live scheduled-jobs list over the SchedulerDO's admin
|
|
1870
1951
|
* WebSocket. `onJobs` fires with the full list on connect and on every
|
|
1871
1952
|
* change (schedule / cancel / alarm-fire). Reconnects with the client's
|
|
1872
|
-
* configured backoff. Requires `wsToken` to be set to
|
|
1873
|
-
* browser can't send an `Authorization` header on a WS)
|
|
1953
|
+
* configured backoff. Requires `wsToken` to be set to an admin credential
|
|
1954
|
+
* (the browser can't send an `Authorization` header on a WS) — the master
|
|
1955
|
+
* token, or preferably a {@link WsTokenProvider} minting the ephemeral
|
|
1956
|
+
* sub-token so the master credential stays out of the URL. Returns an
|
|
1874
1957
|
* unsubscribe function that closes the socket and stops reconnecting.
|
|
1875
1958
|
*/
|
|
1876
1959
|
subscribeScheduledJobs(onJobs: (jobs: ScheduleRecord[]) => void): Unsubscribe;
|
|
@@ -2370,6 +2453,25 @@ declare class LunoraClient {
|
|
|
2370
2453
|
maxBuffer?: number;
|
|
2371
2454
|
shardKey?: string;
|
|
2372
2455
|
}): StreamIterable<ReturnOf<F>>;
|
|
2456
|
+
/**
|
|
2457
|
+
* Open a typed **HTTP-SSE route stream** (`httpRoute.<verb>(path).stream()`).
|
|
2458
|
+
* Distinct from {@link LunoraClient.stream}, which consumes the WS procedure
|
|
2459
|
+
* stream (`kind: "stream"`): this one opens the route's own URL with `fetch`
|
|
2460
|
+
* and parses the Server-Sent Events framing the route pump writes (`data:`
|
|
2461
|
+
* chunks, a final `event: complete`, an `event: error` on throw).
|
|
2462
|
+
*
|
|
2463
|
+
* The reference comes from the generated `httpStreams.*` registry, so the
|
|
2464
|
+
* yielded chunk type is the route handler's yielded type. Cancelling the
|
|
2465
|
+
* returned iterable (or aborting `options.signal`) aborts the fetch, which
|
|
2466
|
+
* the server handler observes via its `signal`. The client's bearer token
|
|
2467
|
+
* (when set) rides as an `authorization` header.
|
|
2468
|
+
* @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
|
|
2469
|
+
*/
|
|
2470
|
+
httpStream<Ref extends HttpStreamRef>(route: Ref, args?: HttpStreamArgsOf<Ref>, options?: {
|
|
2471
|
+
headers?: Record<string, string>;
|
|
2472
|
+
maxBuffer?: number;
|
|
2473
|
+
signal?: AbortSignal;
|
|
2474
|
+
}): StreamIterable<HttpStreamChunkOf<Ref>>;
|
|
2373
2475
|
close(): void;
|
|
2374
2476
|
/**
|
|
2375
2477
|
* Persist a mutation that can't go out on the wire right now (offline, or
|
|
@@ -2515,6 +2617,18 @@ declare class LunoraClient {
|
|
|
2515
2617
|
*/
|
|
2516
2618
|
private resendShapeSubscriptions;
|
|
2517
2619
|
private ensureSocket;
|
|
2620
|
+
/**
|
|
2621
|
+
* Resolve the {@link WsTokenProvider} and open the shard socket with the
|
|
2622
|
+
* minted token. The connection is already in the `connecting` state, so the
|
|
2623
|
+
* async gap is race-guarded: a client `close()`, a `setWsToken` bounce, or a
|
|
2624
|
+
* competing connect that landed first all abandon this attempt. A provider
|
|
2625
|
+
* failure fails the attempt through {@link handleDisconnect}, which arms the
|
|
2626
|
+
* normal reconnect backoff — a broken mint endpoint degrades to retries, not
|
|
2627
|
+
* a silent tokenless socket the admin gate would reject.
|
|
2628
|
+
*/
|
|
2629
|
+
private openSocketWithProvidedToken;
|
|
2630
|
+
/** Construct the shard socket and wire its lifecycle handlers. The connection must already be in the `connecting` state. */
|
|
2631
|
+
private openSocket;
|
|
2518
2632
|
private handleDisconnect;
|
|
2519
2633
|
/**
|
|
2520
2634
|
* Begin the keepalive heartbeat on an open connection. Each tick sends a
|
|
@@ -2719,4 +2833,4 @@ declare class LunoraClient {
|
|
|
2719
2833
|
*/
|
|
2720
2834
|
private settleReplayBatchSlots;
|
|
2721
2835
|
}
|
|
2722
|
-
export {
|
|
2836
|
+
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.<verb>(path).stream()`)
|
|
122
|
+
* emitted by `@lunora/codegen` as `httpStreams.<namespace>.<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
|
-
*
|
|
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
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
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-
|
|
1001
|
-
* optimistic-update fan-out can compare against a
|
|
1002
|
-
* re-serializing every subscription's args on
|
|
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,
|
|
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.).
|
|
1543
|
-
*
|
|
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
|
|
@@ -1655,6 +1724,18 @@ declare class LunoraClient {
|
|
|
1655
1724
|
*/
|
|
1656
1725
|
onMutationSettled(listener: (event: MutationSettledEvent) => void): Unsubscribe;
|
|
1657
1726
|
/**
|
|
1727
|
+
* The `WebSocket` implementation this client was constructed with (an
|
|
1728
|
+
* explicit `options.WebSocket`, or the ambient global on platforms that have
|
|
1729
|
+
* one) — `undefined` if neither is available. This is the seam a feature
|
|
1730
|
+
* that opens its OWN socket outside the client's multiplexed connection
|
|
1731
|
+
* (e.g. a voice-agent hook) should default to, instead of reaching for
|
|
1732
|
+
* `globalThis.WebSocket` directly: on React Native the client wraps this
|
|
1733
|
+
* constructor to inject the auth-headers factory's credential onto the
|
|
1734
|
+
* upgrade request (`createLunoraClient`'s `withAuthWebSocket`), which a raw
|
|
1735
|
+
* `new globalThis.WebSocket(url)` would silently bypass.
|
|
1736
|
+
*/
|
|
1737
|
+
getWebSocketImpl(): typeof WebSocket | undefined;
|
|
1738
|
+
/**
|
|
1658
1739
|
* Read the current value for a {@link ClientQueryRef}. Returns
|
|
1659
1740
|
* `ref.defaultValue` when no value has been explicitly set.
|
|
1660
1741
|
*/
|
|
@@ -1869,8 +1950,10 @@ declare class LunoraClient {
|
|
|
1869
1950
|
* Subscribe to the live scheduled-jobs list over the SchedulerDO's admin
|
|
1870
1951
|
* WebSocket. `onJobs` fires with the full list on connect and on every
|
|
1871
1952
|
* change (schedule / cancel / alarm-fire). Reconnects with the client's
|
|
1872
|
-
* configured backoff. Requires `wsToken` to be set to
|
|
1873
|
-
* browser can't send an `Authorization` header on a WS)
|
|
1953
|
+
* configured backoff. Requires `wsToken` to be set to an admin credential
|
|
1954
|
+
* (the browser can't send an `Authorization` header on a WS) — the master
|
|
1955
|
+
* token, or preferably a {@link WsTokenProvider} minting the ephemeral
|
|
1956
|
+
* sub-token so the master credential stays out of the URL. Returns an
|
|
1874
1957
|
* unsubscribe function that closes the socket and stops reconnecting.
|
|
1875
1958
|
*/
|
|
1876
1959
|
subscribeScheduledJobs(onJobs: (jobs: ScheduleRecord[]) => void): Unsubscribe;
|
|
@@ -2370,6 +2453,25 @@ declare class LunoraClient {
|
|
|
2370
2453
|
maxBuffer?: number;
|
|
2371
2454
|
shardKey?: string;
|
|
2372
2455
|
}): StreamIterable<ReturnOf<F>>;
|
|
2456
|
+
/**
|
|
2457
|
+
* Open a typed **HTTP-SSE route stream** (`httpRoute.<verb>(path).stream()`).
|
|
2458
|
+
* Distinct from {@link LunoraClient.stream}, which consumes the WS procedure
|
|
2459
|
+
* stream (`kind: "stream"`): this one opens the route's own URL with `fetch`
|
|
2460
|
+
* and parses the Server-Sent Events framing the route pump writes (`data:`
|
|
2461
|
+
* chunks, a final `event: complete`, an `event: error` on throw).
|
|
2462
|
+
*
|
|
2463
|
+
* The reference comes from the generated `httpStreams.*` registry, so the
|
|
2464
|
+
* yielded chunk type is the route handler's yielded type. Cancelling the
|
|
2465
|
+
* returned iterable (or aborting `options.signal`) aborts the fetch, which
|
|
2466
|
+
* the server handler observes via its `signal`. The client's bearer token
|
|
2467
|
+
* (when set) rides as an `authorization` header.
|
|
2468
|
+
* @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
|
|
2469
|
+
*/
|
|
2470
|
+
httpStream<Ref extends HttpStreamRef>(route: Ref, args?: HttpStreamArgsOf<Ref>, options?: {
|
|
2471
|
+
headers?: Record<string, string>;
|
|
2472
|
+
maxBuffer?: number;
|
|
2473
|
+
signal?: AbortSignal;
|
|
2474
|
+
}): StreamIterable<HttpStreamChunkOf<Ref>>;
|
|
2373
2475
|
close(): void;
|
|
2374
2476
|
/**
|
|
2375
2477
|
* Persist a mutation that can't go out on the wire right now (offline, or
|
|
@@ -2515,6 +2617,18 @@ declare class LunoraClient {
|
|
|
2515
2617
|
*/
|
|
2516
2618
|
private resendShapeSubscriptions;
|
|
2517
2619
|
private ensureSocket;
|
|
2620
|
+
/**
|
|
2621
|
+
* Resolve the {@link WsTokenProvider} and open the shard socket with the
|
|
2622
|
+
* minted token. The connection is already in the `connecting` state, so the
|
|
2623
|
+
* async gap is race-guarded: a client `close()`, a `setWsToken` bounce, or a
|
|
2624
|
+
* competing connect that landed first all abandon this attempt. A provider
|
|
2625
|
+
* failure fails the attempt through {@link handleDisconnect}, which arms the
|
|
2626
|
+
* normal reconnect backoff — a broken mint endpoint degrades to retries, not
|
|
2627
|
+
* a silent tokenless socket the admin gate would reject.
|
|
2628
|
+
*/
|
|
2629
|
+
private openSocketWithProvidedToken;
|
|
2630
|
+
/** Construct the shard socket and wire its lifecycle handlers. The connection must already be in the `connecting` state. */
|
|
2631
|
+
private openSocket;
|
|
2518
2632
|
private handleDisconnect;
|
|
2519
2633
|
/**
|
|
2520
2634
|
* Begin the keepalive heartbeat on an open connection. Each tick sends a
|
|
@@ -2719,4 +2833,4 @@ declare class LunoraClient {
|
|
|
2719
2833
|
*/
|
|
2720
2834
|
private settleReplayBatchSlots;
|
|
2721
2835
|
}
|
|
2722
|
-
export {
|
|
2836
|
+
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 };
|