@lunora/client 1.0.0-alpha.23 → 1.0.0-alpha.25
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 +10 -10
- package/dist/auth/index.d.ts +10 -10
- package/dist/index.d.mts +344 -357
- package/dist/index.d.ts +344 -357
- package/dist/index.mjs +5 -5
- package/dist/packem_shared/{LunoraClient-BBCQjjbl.mjs → LunoraClient-D3h4P7hg.mjs} +18 -4
- package/dist/packem_shared/{OfflineQueue-B4HUF7rt.mjs → OfflineQueue-BgarnAub.mjs} +1 -1
- 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/{createServerClient-CTTAmvMx.mjs → createServerClient-DzeC2J3A.mjs} +1 -1
- package/dist/packem_shared/{httpStream-BJU-aflc.mjs → httpStream-DIdL8NEw.mjs} +33 -24
- package/dist/packem_shared/lunora-client.d-C4ud8bej.d.mts +2834 -0
- package/dist/packem_shared/lunora-client.d-C4ud8bej.d.ts +2834 -0
- package/dist/packem_shared/{offline-queue-CF4_Co5k.mjs → offline-queue-N-1JvYb4.mjs} +14 -2
- package/dist/packem_shared/preload.d-B6-lqUf2.d.ts +20 -0
- package/dist/packem_shared/preload.d-Dvk8zg6m.d.mts +20 -0
- package/dist/pagination/index.d.mts +42 -42
- package/dist/pagination/index.d.ts +42 -42
- package/dist/query/index.d.mts +42 -42
- package/dist/query/index.d.ts +42 -42
- package/dist/ssr/index.d.mts +79 -79
- package/dist/ssr/index.d.ts +79 -79
- package/dist/ssr/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/lunora-client.d-JvtVpf8A.d.mts +0 -2824
- package/dist/packem_shared/lunora-client.d-JvtVpf8A.d.ts +0 -2824
- package/dist/packem_shared/preload.d-C4_d_l5v.d.ts +0 -20
- package/dist/packem_shared/preload.d-DKbjGN5O.d.mts +0 -20
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export { createAsyncStoragePersistence } from './packem_shared/createAsyncStoragePersistence-1Z5BZ8RC.mjs';
|
|
2
2
|
export { default as createInMemoryBookmarkStorage } from './packem_shared/createInMemoryBookmarkStorage-BoN7a7TH.mjs';
|
|
3
|
-
export { createClientQuery } from './packem_shared/createClientQuery-
|
|
4
|
-
export { TabCoordinator } from './packem_shared/TabCoordinator-
|
|
3
|
+
export { createClientQuery } from './packem_shared/createClientQuery-dJZg1ohm.mjs';
|
|
4
|
+
export { TabCoordinator } from './packem_shared/TabCoordinator-D_5oNTTt.mjs';
|
|
5
5
|
export { applyDelta, isMutationDelta } from './packem_shared/applyDelta-CRKZ1PBt.mjs';
|
|
6
6
|
export { CONFLICT_ERROR_CODE, getErrorCode, getRetryAfterMs, isConflictError, isForbiddenError, isRateLimitedError, isUnauthorizedError } from './packem_shared/CONFLICT_ERROR_CODE-B8gQ8tyU.mjs';
|
|
7
|
-
export { httpStream } from './packem_shared/httpStream-
|
|
7
|
+
export { httpStream } from './packem_shared/httpStream-DIdL8NEw.mjs';
|
|
8
8
|
export { c as createLocalStore } from './packem_shared/local-store-DIq-UWfD.mjs';
|
|
9
|
-
export { LunoraClient } from './packem_shared/LunoraClient-
|
|
9
|
+
export { LunoraClient } from './packem_shared/LunoraClient-D3h4P7hg.mjs';
|
|
10
10
|
export { createMutationRunner } from './packem_shared/createMutationRunner-BqsavzvG.mjs';
|
|
11
11
|
export { createMutatorRunner } from './packem_shared/createMutatorRunner-BETvCd0p.mjs';
|
|
12
|
-
export { O as OfflineQueue } from './packem_shared/offline-queue-
|
|
12
|
+
export { O as OfflineQueue } from './packem_shared/offline-queue-N-1JvYb4.mjs';
|
|
13
13
|
export { createInMemoryPersistence, createIndexedDbPersistence } from './packem_shared/createInMemoryPersistence-DZ2VHWgm.mjs';
|
|
14
14
|
export { preloadQuery, preloadedQueryResult } from './packem_shared/preloadQuery-lobFkD2Z.mjs';
|
|
15
15
|
export { createInMemoryQueryCache, createIndexedDbQueryCache, queryCacheKey } from './packem_shared/createInMemoryQueryCache-DiaGZkA2.mjs';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
2
|
import { s as stableWireKey, d as decodeWire, e as encodeWire } from './wire-key-Djie6aaR.mjs';
|
|
3
3
|
import createInMemoryBookmarkStorage from './createInMemoryBookmarkStorage-BoN7a7TH.mjs';
|
|
4
|
-
import { ClientQueryStore } from './createClientQuery-
|
|
5
|
-
import { TabCoordinator } from './TabCoordinator-
|
|
4
|
+
import { ClientQueryStore } from './createClientQuery-dJZg1ohm.mjs';
|
|
5
|
+
import { TabCoordinator } from './TabCoordinator-D_5oNTTt.mjs';
|
|
6
6
|
import { isMutationDelta, applyDelta } from './applyDelta-CRKZ1PBt.mjs';
|
|
7
|
-
import { httpStream } from './httpStream-
|
|
7
|
+
import { httpStream } from './httpStream-DIdL8NEw.mjs';
|
|
8
8
|
import { f as foldOptimistic, a as applyOptimisticLayer, d as dropConfirmedLayers, n as notifySubscription, c as createLocalStore } from './local-store-DIq-UWfD.mjs';
|
|
9
|
-
import { O as OfflineQueue, n as nextId, i as isStaleVersion, r as reportPersistenceError } from './offline-queue-
|
|
9
|
+
import { O as OfflineQueue, n as nextId, i as isStaleVersion, r as reportPersistenceError } from './offline-queue-N-1JvYb4.mjs';
|
|
10
10
|
import { resolvePersistenceAdapter } from './createInMemoryPersistence-DZ2VHWgm.mjs';
|
|
11
11
|
import { resolveQueryCacheAdapter, queryCacheKey } from './createInMemoryQueryCache-DiaGZkA2.mjs';
|
|
12
12
|
import { createReconnect } from './createReconnect-Di_-oHH7.mjs';
|
|
@@ -888,6 +888,20 @@ class LunoraClient {
|
|
|
888
888
|
onMutationSettled(listener) {
|
|
889
889
|
return this.mutationSettledListeners.add(listener);
|
|
890
890
|
}
|
|
891
|
+
/**
|
|
892
|
+
* The `WebSocket` implementation this client was constructed with (an
|
|
893
|
+
* explicit `options.WebSocket`, or the ambient global on platforms that have
|
|
894
|
+
* one) — `undefined` if neither is available. This is the seam a feature
|
|
895
|
+
* that opens its OWN socket outside the client's multiplexed connection
|
|
896
|
+
* (e.g. a voice-agent hook) should default to, instead of reaching for
|
|
897
|
+
* `globalThis.WebSocket` directly: on React Native the client wraps this
|
|
898
|
+
* constructor to inject the auth-headers factory's credential onto the
|
|
899
|
+
* upgrade request (`createLunoraClient`'s `withAuthWebSocket`), which a raw
|
|
900
|
+
* `new globalThis.WebSocket(url)` would silently bypass.
|
|
901
|
+
*/
|
|
902
|
+
getWebSocketImpl() {
|
|
903
|
+
return this.WebSocketImpl;
|
|
904
|
+
}
|
|
891
905
|
// --- Client Query (local state) ----------------------------------------
|
|
892
906
|
/**
|
|
893
907
|
* Read the current value for a {@link ClientQueryRef}. Returns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { O as OfflineQueue, n as nextId, r as reportPersistenceError } from './offline-queue-
|
|
1
|
+
export { O as OfflineQueue, n as nextId, r as reportPersistenceError } from './offline-queue-N-1JvYb4.mjs';
|
|
@@ -142,21 +142,11 @@ class TabCoordinator {
|
|
|
142
142
|
handleMessage(message) {
|
|
143
143
|
switch (message.type) {
|
|
144
144
|
case "claim-leadership": {
|
|
145
|
-
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
if (this.leader) {
|
|
149
|
-
this.broadcast({ type: "heartbeat", tabId: this.tabId, ts: Date.now() });
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
if (this.knownLeader === void 0 && message.tabId < this.tabId) {
|
|
153
|
-
this.knownLeader = message.tabId;
|
|
154
|
-
}
|
|
145
|
+
this.handleClaimLeadership(message);
|
|
155
146
|
break;
|
|
156
147
|
}
|
|
157
148
|
case "heartbeat": {
|
|
158
|
-
this.
|
|
159
|
-
this.knownLeader = message.tabId;
|
|
149
|
+
this.handleHeartbeat(message);
|
|
160
150
|
break;
|
|
161
151
|
}
|
|
162
152
|
case "subscription-data": {
|
|
@@ -181,6 +171,52 @@ class TabCoordinator {
|
|
|
181
171
|
}
|
|
182
172
|
}
|
|
183
173
|
}
|
|
174
|
+
handleClaimLeadership(message) {
|
|
175
|
+
if (message.tabId === this.tabId) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (this.leader) {
|
|
179
|
+
this.broadcast({ type: "heartbeat", tabId: this.tabId, ts: Date.now() });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (this.knownLeader === void 0 && message.tabId < this.tabId) {
|
|
183
|
+
this.knownLeader = message.tabId;
|
|
184
|
+
this.lastHeartbeat = message.ts;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
handleHeartbeat(message) {
|
|
188
|
+
if (message.tabId === this.tabId) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (this.leader) {
|
|
192
|
+
this.resolveLeaderVsLeaderTieBreak(message);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.lastHeartbeat = message.ts;
|
|
196
|
+
this.knownLeader = message.tabId;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Resolve two leaders existing at once — e.g. this tab was backgrounded
|
|
200
|
+
* and its heartbeat/health timers were throttled while a foreground
|
|
201
|
+
* follower's were not, so the follower's `checkLeaderHealth` timed out
|
|
202
|
+
* the (still-alive) leader and self-promoted. `BroadcastChannel` message
|
|
203
|
+
* delivery isn't subject to the same timer-throttling clamp, so even a
|
|
204
|
+
* backgrounded leader eventually observes the pretender's heartbeat here
|
|
205
|
+
* — resolve the split-brain deterministically with the same
|
|
206
|
+
* lexicographically-smaller-tabId rule used at claim-adoption. If the
|
|
207
|
+
* other tab wins, step down; if we win, reassert immediately so the
|
|
208
|
+
* pretender demotes itself the moment it processes our heartbeat.
|
|
209
|
+
*/
|
|
210
|
+
resolveLeaderVsLeaderTieBreak(message) {
|
|
211
|
+
if (message.tabId < this.tabId) {
|
|
212
|
+
this.leader = false;
|
|
213
|
+
this.knownLeader = message.tabId;
|
|
214
|
+
this.lastHeartbeat = message.ts;
|
|
215
|
+
this.onStopBeingLeader?.();
|
|
216
|
+
} else {
|
|
217
|
+
this.broadcast({ type: "heartbeat", tabId: this.tabId, ts: Date.now() });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
184
220
|
becomeLeader() {
|
|
185
221
|
if (this.leader) {
|
|
186
222
|
return;
|
package/dist/packem_shared/{createClientQuery-CQ51bWAE.mjs → createClientQuery-dJZg1ohm.mjs}
RENAMED
|
@@ -16,11 +16,16 @@ class ClientQueryStore {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Set a new value for `ref` and notify every subscriber. Pass `undefined`
|
|
19
|
-
* to reset the slot to `ref.defaultValue
|
|
19
|
+
* to reset the slot to `ref.defaultValue` — delegates to {@link reset} so
|
|
20
|
+
* `get` reports the default rather than a stored `undefined`.
|
|
20
21
|
*/
|
|
21
22
|
set(ref, value) {
|
|
23
|
+
if (value === void 0) {
|
|
24
|
+
this.reset(ref);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
22
27
|
this.values.set(ref.key, value);
|
|
23
|
-
this.notify(ref.key);
|
|
28
|
+
this.notify(ref.key, value);
|
|
24
29
|
}
|
|
25
30
|
/**
|
|
26
31
|
* Delete the stored value for `ref`, resetting to `ref.defaultValue` and
|
|
@@ -28,7 +33,7 @@ class ClientQueryStore {
|
|
|
28
33
|
*/
|
|
29
34
|
reset(ref) {
|
|
30
35
|
this.values.delete(ref.key);
|
|
31
|
-
this.notify(ref.key);
|
|
36
|
+
this.notify(ref.key, ref.defaultValue);
|
|
32
37
|
}
|
|
33
38
|
/**
|
|
34
39
|
* Subscribe to changes for `ref`. The callback is NOT invoked on
|
|
@@ -49,13 +54,17 @@ class ClientQueryStore {
|
|
|
49
54
|
}
|
|
50
55
|
};
|
|
51
56
|
}
|
|
52
|
-
/**
|
|
53
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Notify every subscriber of a value change for the given key with the
|
|
59
|
+
* resolved value — the caller passes the just-stored value on `set` or
|
|
60
|
+
* `ref.defaultValue` on `reset`, so a subscriber always sees the same value
|
|
61
|
+
* {@link get} would return (never a stale re-read after the store mutation).
|
|
62
|
+
*/
|
|
63
|
+
notify(key, value) {
|
|
54
64
|
const subs = this.subscribers.get(key);
|
|
55
65
|
if (!subs) {
|
|
56
66
|
return;
|
|
57
67
|
}
|
|
58
|
-
const value = this.values.get(key);
|
|
59
68
|
for (const callback of subs) {
|
|
60
69
|
try {
|
|
61
70
|
callback(value);
|
|
@@ -104,19 +104,22 @@ const httpStream = (route, args, options = {}) => {
|
|
|
104
104
|
}
|
|
105
105
|
const url = buildHttpStreamUrl(route, args ?? {}, options.baseUrl ?? "");
|
|
106
106
|
const ac = new AbortController();
|
|
107
|
+
let onExternalAbort;
|
|
107
108
|
if (options.signal) {
|
|
108
109
|
if (options.signal.aborted) {
|
|
109
110
|
ac.abort();
|
|
110
111
|
} else {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
},
|
|
116
|
-
{ once: true }
|
|
117
|
-
);
|
|
112
|
+
onExternalAbort = () => {
|
|
113
|
+
ac.abort();
|
|
114
|
+
};
|
|
115
|
+
options.signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
118
116
|
}
|
|
119
117
|
}
|
|
118
|
+
const detachExternalAbort = () => {
|
|
119
|
+
if (onExternalAbort) {
|
|
120
|
+
options.signal?.removeEventListener("abort", onExternalAbort);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
120
123
|
const { handle, iterable } = createStream({
|
|
121
124
|
maxBuffer: options.maxBuffer,
|
|
122
125
|
onCancel: () => {
|
|
@@ -124,24 +127,30 @@ const httpStream = (route, args, options = {}) => {
|
|
|
124
127
|
}
|
|
125
128
|
});
|
|
126
129
|
(async () => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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();
|
|
143
153
|
}
|
|
144
|
-
await pumpSseBody(response.body, handle);
|
|
145
154
|
})().catch((error) => {
|
|
146
155
|
if (ac.signal.aborted) {
|
|
147
156
|
handle.complete();
|