dynamodb-reactive 0.1.22 → 0.2.2

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 (46) hide show
  1. package/README.md +437 -153
  2. package/dist/client-r_tnzwqi.d.ts +224 -0
  3. package/dist/client.cjs +1023 -0
  4. package/dist/client.cjs.map +1 -0
  5. package/dist/client.d.cts +6 -0
  6. package/dist/client.d.ts +4 -70
  7. package/dist/client.js +1013 -1
  8. package/dist/client.js.map +1 -1
  9. package/dist/core.cjs +232 -0
  10. package/dist/core.cjs.map +1 -0
  11. package/dist/core.d.cts +4 -0
  12. package/dist/core.d.ts +3 -3
  13. package/dist/core.js +222 -1
  14. package/dist/core.js.map +1 -1
  15. package/dist/index.cjs +232 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +157 -0
  18. package/dist/index.d.ts +103 -56
  19. package/dist/index.js +222 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/infra.cjs +634 -0
  22. package/dist/infra.cjs.map +1 -0
  23. package/dist/infra.d.cts +126 -0
  24. package/dist/infra.d.ts +57 -102
  25. package/dist/infra.js +385 -147
  26. package/dist/infra.js.map +1 -1
  27. package/dist/react.cjs +1250 -0
  28. package/dist/react.cjs.map +1 -0
  29. package/dist/react.d.cts +68 -0
  30. package/dist/react.d.ts +68 -3
  31. package/dist/react.js +1237 -1
  32. package/dist/react.js.map +1 -1
  33. package/dist/server.cjs +2382 -0
  34. package/dist/server.cjs.map +1 -0
  35. package/dist/server.d.cts +364 -0
  36. package/dist/server.d.ts +58 -313
  37. package/dist/server.js +1882 -1204
  38. package/dist/server.js.map +1 -1
  39. package/dist/{table-tN3D4Tm-.d.ts → table-BijJ73pX.d.ts} +2 -34
  40. package/dist/{types-ISMJfyIv.d.ts → types-LMLk8JVC.d.ts} +56 -30
  41. package/package.json +79 -41
  42. package/dist/chunk-2KC44Q5I.js +0 -1103
  43. package/dist/chunk-2KC44Q5I.js.map +0 -1
  44. package/dist/chunk-ZAJA4KBE.js +0 -150
  45. package/dist/chunk-ZAJA4KBE.js.map +0 -1
  46. package/dist/react-D_NLHuuO.d.ts +0 -556
package/dist/react.cjs ADDED
@@ -0,0 +1,1250 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsonpatch = require('fast-json-patch');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var jsonpatch__default = /*#__PURE__*/_interopDefault(jsonpatch);
10
+
11
+ // ../client/src/react.tsx
12
+
13
+ // ../client/src/canonical.ts
14
+ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
15
+ if (value === null) return "null";
16
+ switch (typeof value) {
17
+ case "string":
18
+ case "boolean":
19
+ return JSON.stringify(value);
20
+ case "number":
21
+ return Number.isFinite(value) ? JSON.stringify(value) : "null";
22
+ case "undefined":
23
+ return arrayItem ? "null" : void 0;
24
+ case "object": {
25
+ if (applyToJSON && "toJSON" in value && typeof value.toJSON === "function") {
26
+ return canonicalize(value.toJSON(key), seen, key, arrayItem, false);
27
+ }
28
+ if (seen.has(value)) {
29
+ throw new TypeError("Query input must not contain circular references");
30
+ }
31
+ seen.add(value);
32
+ try {
33
+ if (Array.isArray(value)) {
34
+ const items = [];
35
+ for (let index = 0; index < value.length; index++) {
36
+ items.push(
37
+ canonicalize(value[index], seen, String(index), true) ?? "null"
38
+ );
39
+ }
40
+ return `[${items.join(",")}]`;
41
+ }
42
+ const object = value;
43
+ const entries = [];
44
+ for (const key2 of Object.keys(object).sort()) {
45
+ const item = canonicalize(object[key2], seen, key2);
46
+ if (item !== void 0) {
47
+ entries.push(`${JSON.stringify(key2)}:${item}`);
48
+ }
49
+ }
50
+ return `{${entries.join(",")}}`;
51
+ } finally {
52
+ seen.delete(value);
53
+ }
54
+ }
55
+ default:
56
+ throw new TypeError(`Unsupported query input value: ${typeof value}`);
57
+ }
58
+ }
59
+ function getQueryKey(path, input) {
60
+ return `${JSON.stringify(path)}:${canonicalize(input, /* @__PURE__ */ new Set(), "") ?? "undefined"}`;
61
+ }
62
+ var { applyPatch } = jsonpatch__default.default;
63
+ function applyPatches(document2, patches) {
64
+ if (patches.length === 0) {
65
+ return document2;
66
+ }
67
+ const operations = patches.map((patch) => {
68
+ const op = {
69
+ op: patch.op,
70
+ path: patch.path
71
+ };
72
+ if ("value" in patch && patch.value !== void 0) {
73
+ op.value = patch.value;
74
+ }
75
+ if ("from" in patch && patch.from !== void 0) {
76
+ op.from = patch.from;
77
+ }
78
+ return op;
79
+ });
80
+ try {
81
+ const result = applyPatch(
82
+ structuredClone(document2),
83
+ operations,
84
+ true,
85
+ // Validate operations
86
+ false
87
+ // Don't mutate the original
88
+ );
89
+ return result.newDocument;
90
+ } catch (error2) {
91
+ throw new Error("Failed to apply patches", { cause: error2 });
92
+ }
93
+ }
94
+
95
+ // ../client/src/log.ts
96
+ function debug(...values) {
97
+ if (globalThis.__DYNAMODB_REACTIVE_DEBUG__) {
98
+ console.debug(...values);
99
+ }
100
+ }
101
+ function warn(...values) {
102
+ console.warn(...values);
103
+ }
104
+ function error(...values) {
105
+ console.error(...values);
106
+ }
107
+
108
+ // ../client/src/websocket.ts
109
+ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
110
+ function withDefaults(config) {
111
+ return {
112
+ autoReconnect: true,
113
+ connectionTimeout: 1e4,
114
+ handshakeTimeout: 1e4,
115
+ maxReconnectAttempts: 10,
116
+ reconnectDelay: 1e3,
117
+ ...config
118
+ };
119
+ }
120
+ var WebSocketManager = class {
121
+ config;
122
+ ws = null;
123
+ state = "disconnected";
124
+ reconnectAttempts = 0;
125
+ reconnectTimer = null;
126
+ handshakeTimer = null;
127
+ heartbeatTimer = null;
128
+ messageHandlers = /* @__PURE__ */ new Set();
129
+ stateHandlers = /* @__PURE__ */ new Set();
130
+ handshakeHandlers = /* @__PURE__ */ new Set();
131
+ _connectionId = null;
132
+ connectionWaiters = /* @__PURE__ */ new Set();
133
+ socketGeneration = 0;
134
+ intentionalDisconnect = false;
135
+ constructor(config) {
136
+ this.config = withDefaults(config);
137
+ }
138
+ /** Apply settings used by future requests/events without replacing the socket. */
139
+ configure(config) {
140
+ this.config = withDefaults(config);
141
+ }
142
+ get connectionId() {
143
+ return this._connectionId;
144
+ }
145
+ get generation() {
146
+ return this.socketGeneration;
147
+ }
148
+ waitForConnectionId(options = {}) {
149
+ if (this._connectionId && this.state === "connected") {
150
+ return Promise.resolve(this._connectionId);
151
+ }
152
+ if (options.signal?.aborted) {
153
+ return Promise.reject(new Error("Connection wait aborted"));
154
+ }
155
+ const timeoutMs = options.timeoutMs ?? this.config.connectionTimeout;
156
+ return new Promise((resolve, reject) => {
157
+ let timeout;
158
+ const abort = () => finish(new Error("Connection wait aborted"));
159
+ const waiter = {
160
+ resolve,
161
+ reject,
162
+ cleanup: () => {
163
+ if (timeout) clearTimeout(timeout);
164
+ options.signal?.removeEventListener("abort", abort);
165
+ }
166
+ };
167
+ const finish = (failure) => {
168
+ if (!this.connectionWaiters.delete(waiter)) return;
169
+ waiter.cleanup();
170
+ reject(failure);
171
+ };
172
+ this.connectionWaiters.add(waiter);
173
+ options.signal?.addEventListener("abort", abort, { once: true });
174
+ timeout = setTimeout(
175
+ () => finish(new Error(`Connection timed out after ${timeoutMs}ms`)),
176
+ timeoutMs
177
+ );
178
+ });
179
+ }
180
+ getState() {
181
+ return this.state;
182
+ }
183
+ async connect() {
184
+ if (this.state === "connected") return;
185
+ if (this.state === "connecting") {
186
+ await this.waitForConnectionId();
187
+ return;
188
+ }
189
+ this.intentionalDisconnect = false;
190
+ const generation = ++this.socketGeneration;
191
+ this.setState("connecting");
192
+ let socket;
193
+ try {
194
+ const url = await this.buildUrl();
195
+ if (generation !== this.socketGeneration || this.intentionalDisconnect) {
196
+ return;
197
+ }
198
+ const nextSocket = new WebSocket(url);
199
+ socket = nextSocket;
200
+ this.ws = nextSocket;
201
+ this.startHandshakeTimer(nextSocket, generation);
202
+ nextSocket.onopen = () => {
203
+ if (!this.isCurrent(nextSocket, generation)) return;
204
+ nextSocket.send(JSON.stringify({ type: "init" }));
205
+ };
206
+ nextSocket.onclose = () => {
207
+ if (!this.isCurrent(nextSocket, generation)) return;
208
+ this.clearConnectionTimers();
209
+ this.ws = null;
210
+ this._connectionId = null;
211
+ this.setState("disconnected");
212
+ this.invokeLifecycleCallback(
213
+ "onDisconnect",
214
+ this.config.onDisconnect
215
+ );
216
+ if (this.config.autoReconnect && !this.intentionalDisconnect) {
217
+ this.scheduleReconnect();
218
+ } else {
219
+ this.rejectWaiters(new Error("WebSocket disconnected"));
220
+ }
221
+ };
222
+ nextSocket.onerror = () => {
223
+ if (!this.isCurrent(nextSocket, generation)) return;
224
+ this.invokeLifecycleCallback(
225
+ "onError",
226
+ this.config.onError,
227
+ new Error("WebSocket error")
228
+ );
229
+ };
230
+ nextSocket.onmessage = (event) => {
231
+ if (!this.isCurrent(nextSocket, generation)) return;
232
+ this.receiveMessage(nextSocket, generation, event.data);
233
+ };
234
+ } catch (cause) {
235
+ if (generation !== this.socketGeneration) return;
236
+ const failure = cause instanceof Error ? cause : new Error(String(cause));
237
+ this.clearConnectionTimers();
238
+ if (socket && this.ws === socket) socket.close();
239
+ this.ws = null;
240
+ this._connectionId = null;
241
+ this.setState("disconnected");
242
+ this.invokeLifecycleCallback("onError", this.config.onError, failure);
243
+ if (this.config.autoReconnect && !this.intentionalDisconnect) {
244
+ this.scheduleReconnect();
245
+ await this.waitForConnectionId();
246
+ return;
247
+ }
248
+ this.rejectWaiters(failure);
249
+ throw failure;
250
+ }
251
+ await this.waitForConnectionId();
252
+ }
253
+ disconnect() {
254
+ this.intentionalDisconnect = true;
255
+ ++this.socketGeneration;
256
+ this.clearTimers();
257
+ const socket = this.ws;
258
+ this.ws = null;
259
+ socket?.close(1e3, "Client disconnect");
260
+ this._connectionId = null;
261
+ this.setState("disconnected");
262
+ this.rejectWaiters(new Error("WebSocket disconnected"));
263
+ }
264
+ forceReconnect() {
265
+ this.intentionalDisconnect = false;
266
+ ++this.socketGeneration;
267
+ this.clearTimers();
268
+ const socket = this.ws;
269
+ this.ws = null;
270
+ socket?.close(1e3, "Force reconnect");
271
+ this._connectionId = null;
272
+ this.reconnectAttempts = 0;
273
+ this.setState("disconnected");
274
+ void this.connect().catch(() => void 0);
275
+ }
276
+ onMessage(handler) {
277
+ this.messageHandlers.add(handler);
278
+ return () => this.messageHandlers.delete(handler);
279
+ }
280
+ onStateChange(handler) {
281
+ this.stateHandlers.add(handler);
282
+ return () => this.stateHandlers.delete(handler);
283
+ }
284
+ onHandshake(handler) {
285
+ this.handshakeHandlers.add(handler);
286
+ return () => this.handshakeHandlers.delete(handler);
287
+ }
288
+ async buildUrl() {
289
+ let url = this.config.url;
290
+ if (!this.config.auth) return url;
291
+ const token = typeof this.config.auth === "function" ? await this.config.auth() : this.config.auth;
292
+ const separator = url.includes("?") ? "&" : "?";
293
+ url = `${url}${separator}token=${encodeURIComponent(token)}`;
294
+ return url;
295
+ }
296
+ receiveMessage(socket, generation, rawData) {
297
+ try {
298
+ const message = JSON.parse(String(rawData));
299
+ if (message.type === "connected") {
300
+ if (this._connectionId) return;
301
+ this._connectionId = message.connectionId;
302
+ socket.send(JSON.stringify({ type: "ping" }));
303
+ return;
304
+ }
305
+ if (message.type === "pong") {
306
+ if (!this._connectionId || this.state === "connected") return;
307
+ this.clearHandshakeTimer();
308
+ this.reconnectAttempts = 0;
309
+ this.setState("connected");
310
+ this.startHeartbeat(socket, generation);
311
+ this.invokeLifecycleCallback("onConnect", this.config.onConnect);
312
+ this.resolveWaiters(this._connectionId);
313
+ for (const handler of this.handshakeHandlers) handler(generation);
314
+ return;
315
+ }
316
+ for (const handler of this.messageHandlers) handler(message);
317
+ } catch (cause) {
318
+ error("Failed to parse WebSocket message:", cause);
319
+ }
320
+ }
321
+ startHandshakeTimer(socket, generation) {
322
+ this.clearHandshakeTimer();
323
+ this.handshakeTimer = setTimeout(() => {
324
+ if (!this.isCurrent(socket, generation) || this.state === "connected") {
325
+ return;
326
+ }
327
+ const failure = new Error("WebSocket handshake timed out");
328
+ this.invokeLifecycleCallback("onError", this.config.onError, failure);
329
+ socket.close(4e3, "Handshake timeout");
330
+ }, this.config.handshakeTimeout);
331
+ }
332
+ startHeartbeat(socket, generation) {
333
+ this.clearHeartbeatTimer();
334
+ this.heartbeatTimer = setInterval(() => {
335
+ if (!this.isCurrent(socket, generation) || this.state !== "connected" || socket.readyState !== WebSocket.OPEN) {
336
+ return;
337
+ }
338
+ socket.send(JSON.stringify({ type: "ping" }));
339
+ }, HEARTBEAT_INTERVAL_MS);
340
+ }
341
+ isCurrent(socket, generation) {
342
+ return this.ws === socket && this.socketGeneration === generation;
343
+ }
344
+ setState(state) {
345
+ if (this.state === state) return;
346
+ this.state = state;
347
+ for (const handler of this.stateHandlers) handler(state);
348
+ }
349
+ scheduleReconnect() {
350
+ if (this.reconnectTimer || this.intentionalDisconnect) return;
351
+ if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
352
+ const failure = new Error("Max reconnection attempts reached");
353
+ error(failure.message);
354
+ this.setState("disconnected");
355
+ this.rejectWaiters(failure);
356
+ this.invokeLifecycleCallback("onError", this.config.onError, failure);
357
+ return;
358
+ }
359
+ this.setState("reconnecting");
360
+ this.reconnectAttempts++;
361
+ const delay = Math.min(
362
+ this.config.reconnectDelay * 2 ** (this.reconnectAttempts - 1),
363
+ 3e4
364
+ ) * (0.5 + Math.random() * 0.5);
365
+ this.reconnectTimer = setTimeout(() => {
366
+ this.reconnectTimer = null;
367
+ void this.connect().catch(() => void 0);
368
+ }, delay);
369
+ }
370
+ resolveWaiters(connectionId) {
371
+ const waiters = [...this.connectionWaiters];
372
+ this.connectionWaiters.clear();
373
+ for (const waiter of waiters) {
374
+ waiter.cleanup();
375
+ waiter.resolve(connectionId);
376
+ }
377
+ }
378
+ invokeLifecycleCallback(name, callback, ...args) {
379
+ if (!callback) return;
380
+ try {
381
+ callback(...args);
382
+ } catch (cause) {
383
+ try {
384
+ error(`[dynamodb-reactive] ${name} callback failed:`, cause);
385
+ } catch {
386
+ }
387
+ }
388
+ }
389
+ rejectWaiters(failure) {
390
+ const waiters = [...this.connectionWaiters];
391
+ this.connectionWaiters.clear();
392
+ for (const waiter of waiters) {
393
+ waiter.cleanup();
394
+ waiter.reject(failure);
395
+ }
396
+ }
397
+ clearHandshakeTimer() {
398
+ if (!this.handshakeTimer) return;
399
+ clearTimeout(this.handshakeTimer);
400
+ this.handshakeTimer = null;
401
+ }
402
+ clearHeartbeatTimer() {
403
+ if (!this.heartbeatTimer) return;
404
+ clearInterval(this.heartbeatTimer);
405
+ this.heartbeatTimer = null;
406
+ }
407
+ clearConnectionTimers() {
408
+ this.clearHandshakeTimer();
409
+ this.clearHeartbeatTimer();
410
+ }
411
+ clearTimers() {
412
+ this.clearConnectionTimers();
413
+ if (!this.reconnectTimer) return;
414
+ clearTimeout(this.reconnectTimer);
415
+ this.reconnectTimer = null;
416
+ }
417
+ };
418
+
419
+ // ../client/src/client.ts
420
+ var DEFAULT_HTTP_URL = "/api/reactive";
421
+ function generateId() {
422
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
423
+ }
424
+ var ReactiveClient = class {
425
+ wsManager;
426
+ httpUrl;
427
+ authGetter;
428
+ headersGetter;
429
+ subscriptionsById = /* @__PURE__ */ new Map();
430
+ subscriptionsByKey = /* @__PURE__ */ new Map();
431
+ connectionState = "disconnected";
432
+ stateListeners = /* @__PURE__ */ new Set();
433
+ constructor(config) {
434
+ this.wsManager = new WebSocketManager(config);
435
+ this.httpUrl = config.httpUrl ?? DEFAULT_HTTP_URL;
436
+ this.setAuth(config.auth);
437
+ this.setInitialHeaders(config.headers);
438
+ this.wsManager.onMessage((message) => this.handleMessage(message));
439
+ this.wsManager.onStateChange((state) => {
440
+ this.connectionState = state;
441
+ this.notifyStateListeners();
442
+ if (state === "disconnected" || state === "reconnecting") {
443
+ this.markSubscriptionsStale();
444
+ }
445
+ });
446
+ this.wsManager.onHandshake((socketGeneration) => {
447
+ this.registerActiveSubscriptions(socketGeneration);
448
+ });
449
+ }
450
+ /** Apply live configuration without replacing subscription caches or the socket. */
451
+ configure(config) {
452
+ this.httpUrl = config.httpUrl ?? DEFAULT_HTTP_URL;
453
+ this.authGetter = this.toAuthGetter(config.auth);
454
+ this.headersGetter = this.toHeadersGetter(config.headers);
455
+ this.wsManager.configure(config);
456
+ }
457
+ async connect() {
458
+ await this.wsManager.connect();
459
+ }
460
+ disconnect() {
461
+ this.wsManager.disconnect();
462
+ }
463
+ forceReconnect() {
464
+ this.wsManager.forceReconnect();
465
+ }
466
+ getConnectionState() {
467
+ return this.connectionState;
468
+ }
469
+ onConnectionStateChange(listener) {
470
+ this.stateListeners.add(listener);
471
+ return () => this.stateListeners.delete(listener);
472
+ }
473
+ subscribe(path, options = {}) {
474
+ const key = getQueryKey(path, options.input);
475
+ let shared = this.subscriptionsByKey.get(key);
476
+ if (!shared) {
477
+ shared = {
478
+ id: generateId(),
479
+ key,
480
+ path,
481
+ input: options.input,
482
+ authoritativeData: void 0,
483
+ data: void 0,
484
+ loading: true,
485
+ stale: true,
486
+ error: void 0,
487
+ listeners: /* @__PURE__ */ new Set(),
488
+ observers: /* @__PURE__ */ new Map(),
489
+ active: true,
490
+ hasRegistered: false,
491
+ generation: 0,
492
+ registrationId: void 0,
493
+ registrationConnectionId: void 0,
494
+ registrationSocketGeneration: void 0,
495
+ registrationPromise: void 0,
496
+ version: void 0,
497
+ patchBuffer: /* @__PURE__ */ new Map(),
498
+ appliedMessageIds: /* @__PURE__ */ new Set(),
499
+ resyncPromise: void 0,
500
+ resyncGeneration: void 0,
501
+ optimisticLayers: /* @__PURE__ */ new Map(),
502
+ settlementRevision: 0,
503
+ registrationSettlementRevision: 0,
504
+ revision: 0
505
+ };
506
+ this.subscriptionsByKey.set(key, shared);
507
+ this.subscriptionsById.set(shared.id, shared);
508
+ if (this.connectionState === "connected") {
509
+ void this.ensureRegistered(
510
+ shared,
511
+ this.wsManager.generation
512
+ ).catch(() => void 0);
513
+ }
514
+ }
515
+ const leaseId = generateId();
516
+ shared.observers.set(leaseId, {
517
+ onData: options.onData ? (data) => options.onData?.(data) : void 0,
518
+ onError: options.onError
519
+ });
520
+ if (this.connectionState === "connected" && (!shared.hasRegistered || shared.stale) && !shared.registrationPromise) {
521
+ void this.ensureRegistered(shared, this.wsManager.generation).catch(
522
+ () => void 0
523
+ );
524
+ }
525
+ const state = shared;
526
+ let released = false;
527
+ return {
528
+ id: state.id,
529
+ key: state.key,
530
+ get data() {
531
+ return state.data;
532
+ },
533
+ get loading() {
534
+ return state.loading;
535
+ },
536
+ get stale() {
537
+ return state.stale;
538
+ },
539
+ get error() {
540
+ return state.error;
541
+ },
542
+ unsubscribe: () => {
543
+ if (released) return;
544
+ released = true;
545
+ this.releaseSubscription(state, leaseId);
546
+ },
547
+ refetch: () => this.refetch(state)
548
+ };
549
+ }
550
+ async call(path, input, options) {
551
+ const mutationId = generateId();
552
+ const layers = [];
553
+ try {
554
+ this.createMutationLayers(mutationId, input, options, layers);
555
+ const connectionId = await this.wsManager.waitForConnectionId();
556
+ const headers = await this.buildHeaders();
557
+ const response = await fetch(this.httpUrl, {
558
+ method: "POST",
559
+ headers,
560
+ body: JSON.stringify({ type: "call", connectionId, path, input })
561
+ });
562
+ if (!response.ok) {
563
+ throw new Error(`Call failed: ${response.status} ${response.statusText}`);
564
+ }
565
+ const result = await response.json();
566
+ if (result.type === "error") {
567
+ throw new Error(result.message ?? "Unknown error");
568
+ }
569
+ const data = result.data;
570
+ this.settleMutationLayers(mutationId, input, data, options, layers);
571
+ return data;
572
+ } catch (cause) {
573
+ this.removeMutationLayers(mutationId, layers);
574
+ throw cause;
575
+ }
576
+ }
577
+ addSubscriptionListener(id, listener) {
578
+ const state = this.subscriptionsById.get(id);
579
+ if (!state) return () => void 0;
580
+ state.listeners.add(listener);
581
+ return () => state.listeners.delete(listener);
582
+ }
583
+ getSubscriptionRevision(id) {
584
+ return this.subscriptionsById.get(id)?.revision ?? -1;
585
+ }
586
+ applyOptimisticUpdate(subscriptionId, updater) {
587
+ const state = this.subscriptionsById.get(subscriptionId);
588
+ if (!state || state.data === void 0) return;
589
+ state.optimisticLayers.set(`local-${generateId()}`, {
590
+ updater
591
+ });
592
+ this.recomputeData(state);
593
+ this.notifySubscription(state, true);
594
+ }
595
+ setAuth(auth) {
596
+ this.authGetter = this.toAuthGetter(auth);
597
+ }
598
+ setInitialHeaders(headers) {
599
+ this.headersGetter = this.toHeadersGetter(headers);
600
+ }
601
+ toAuthGetter(auth) {
602
+ if (!auth) return void 0;
603
+ return typeof auth === "function" ? auth : () => auth;
604
+ }
605
+ toHeadersGetter(headers) {
606
+ if (!headers) return void 0;
607
+ return typeof headers === "function" ? headers : () => headers;
608
+ }
609
+ registerActiveSubscriptions(socketGeneration) {
610
+ for (const state of this.subscriptionsByKey.values()) {
611
+ if (state.active) {
612
+ void this.ensureRegistered(state, socketGeneration).catch(
613
+ () => void 0
614
+ );
615
+ }
616
+ }
617
+ }
618
+ ensureRegistered(state, socketGeneration, force = false) {
619
+ if (!force && state.registrationSocketGeneration === socketGeneration && state.registrationPromise) {
620
+ return state.registrationPromise;
621
+ }
622
+ state.generation++;
623
+ state.registrationSettlementRevision = state.settlementRevision;
624
+ state.registrationId = generateId();
625
+ state.registrationSocketGeneration = socketGeneration;
626
+ state.registrationConnectionId = void 0;
627
+ state.registrationPromise = void 0;
628
+ state.resyncPromise = void 0;
629
+ state.resyncGeneration = void 0;
630
+ state.hasRegistered = false;
631
+ state.version = void 0;
632
+ state.patchBuffer.clear();
633
+ state.appliedMessageIds.clear();
634
+ state.loading = true;
635
+ state.stale = true;
636
+ state.error = void 0;
637
+ this.notifySubscription(state);
638
+ const generation = state.generation;
639
+ const registrationId = state.registrationId;
640
+ const isCurrent = () => state.active && state.generation === generation && state.registrationId === registrationId && state.registrationSocketGeneration === socketGeneration && this.wsManager.generation === socketGeneration;
641
+ let requestIssued = false;
642
+ let registrationConnectionId;
643
+ let request;
644
+ request = (async () => {
645
+ try {
646
+ const connectionId = await this.wsManager.waitForConnectionId();
647
+ if (!isCurrent()) return;
648
+ registrationConnectionId = connectionId;
649
+ state.registrationConnectionId = connectionId;
650
+ const headers = await this.buildHeaders();
651
+ if (!isCurrent()) return;
652
+ requestIssued = true;
653
+ const response = await fetch(this.httpUrl, {
654
+ method: "POST",
655
+ headers,
656
+ body: JSON.stringify({
657
+ type: "subscribe",
658
+ connectionId,
659
+ subscriptionId: state.id,
660
+ generation,
661
+ registrationId,
662
+ path: state.path,
663
+ input: state.input
664
+ })
665
+ });
666
+ if (!response.ok) {
667
+ throw new Error(
668
+ `Subscribe failed: ${response.status} ${response.statusText}`
669
+ );
670
+ }
671
+ const result = await response.json();
672
+ if (result.type === "error") {
673
+ throw new Error(result.message);
674
+ }
675
+ if (!isCurrent()) return;
676
+ this.handleSnapshot(result);
677
+ if (isCurrent()) state.hasRegistered = true;
678
+ } catch (cause) {
679
+ if (isCurrent()) {
680
+ const failure = cause instanceof Error ? cause : new Error(String(cause));
681
+ state.error = failure;
682
+ state.loading = this.hasSettledLayers(state);
683
+ this.notifySubscription(state, false, failure);
684
+ }
685
+ throw cause;
686
+ } finally {
687
+ if ((!state.active || !isCurrent()) && requestIssued) {
688
+ void this.sendUnsubscribe(
689
+ registrationConnectionId,
690
+ state.id,
691
+ generation,
692
+ registrationId
693
+ );
694
+ }
695
+ if (state.registrationPromise === request) {
696
+ state.registrationPromise = void 0;
697
+ }
698
+ }
699
+ })();
700
+ state.registrationPromise = request;
701
+ return request;
702
+ }
703
+ releaseSubscription(state, leaseId) {
704
+ state.observers.delete(leaseId);
705
+ if (state.observers.size > 0) return;
706
+ state.active = false;
707
+ this.subscriptionsById.delete(state.id);
708
+ this.subscriptionsByKey.delete(state.key);
709
+ if (state.registrationId && state.registrationConnectionId && state.hasRegistered) {
710
+ void this.sendUnsubscribe(
711
+ state.registrationConnectionId,
712
+ state.id,
713
+ state.generation,
714
+ state.registrationId
715
+ );
716
+ }
717
+ }
718
+ async sendUnsubscribe(connectionId, subscriptionId, generation, registrationId) {
719
+ if (!connectionId) return;
720
+ try {
721
+ const headers = await this.buildHeaders();
722
+ await fetch(this.httpUrl, {
723
+ method: "POST",
724
+ headers,
725
+ body: JSON.stringify({
726
+ type: "unsubscribe",
727
+ connectionId,
728
+ subscriptionId,
729
+ generation,
730
+ registrationId
731
+ })
732
+ });
733
+ } catch {
734
+ }
735
+ }
736
+ async refetch(state) {
737
+ if (!state.active) return;
738
+ if (!state.registrationId) {
739
+ await this.ensureRegistered(state, this.wsManager.generation);
740
+ return;
741
+ }
742
+ await this.requestResync(state);
743
+ }
744
+ handleMessage(message) {
745
+ switch (message.type) {
746
+ case "patch":
747
+ this.handlePatch(message);
748
+ break;
749
+ case "resync": {
750
+ const state = this.subscriptionsById.get(message.subscriptionId);
751
+ if (state?.generation === message.generation) {
752
+ void this.requestResync(state).catch(() => void 0);
753
+ }
754
+ break;
755
+ }
756
+ }
757
+ }
758
+ handleSnapshot(message) {
759
+ const state = this.subscriptionsById.get(message.subscriptionId);
760
+ if (!state || message.generation !== state.generation) return;
761
+ if (state.version !== void 0 && message.version <= state.version) return;
762
+ state.authoritativeData = message.data;
763
+ state.version = message.version;
764
+ state.error = void 0;
765
+ const coveredRevision = state.registrationSettlementRevision;
766
+ for (const [layerId, layer] of state.optimisticLayers) {
767
+ if (layer.settledRevision !== void 0 && layer.settledRevision <= coveredRevision) {
768
+ state.optimisticLayers.delete(layerId);
769
+ }
770
+ }
771
+ const awaitingReconciliation = this.hasSettledLayers(state);
772
+ state.loading = awaitingReconciliation;
773
+ state.stale = awaitingReconciliation;
774
+ this.recomputeData(state);
775
+ this.discardStalePatches(state);
776
+ this.notifySubscription(state, true);
777
+ this.drainPatchBuffer(state);
778
+ }
779
+ handlePatch(message) {
780
+ const state = this.subscriptionsById.get(message.subscriptionId);
781
+ if (!state || message.generation !== state.generation) return;
782
+ if (state.appliedMessageIds.has(message.messageId) || state.patchBuffer.has(message.messageId)) {
783
+ return;
784
+ }
785
+ if (state.version !== void 0 && message.toVersion <= state.version) {
786
+ state.appliedMessageIds.add(message.messageId);
787
+ return;
788
+ }
789
+ state.patchBuffer.set(message.messageId, message);
790
+ if (state.version === void 0) return;
791
+ this.drainPatchBuffer(state);
792
+ }
793
+ drainPatchBuffer(state) {
794
+ if (state.version === void 0 || state.authoritativeData === void 0) {
795
+ return;
796
+ }
797
+ this.discardStalePatches(state);
798
+ while (true) {
799
+ const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion)[0];
800
+ if (!next) break;
801
+ state.patchBuffer.delete(next.messageId);
802
+ try {
803
+ state.authoritativeData = applyPatches(
804
+ state.authoritativeData,
805
+ next.patches
806
+ );
807
+ state.version = next.toVersion;
808
+ state.appliedMessageIds.add(next.messageId);
809
+ const awaitingReconciliation = this.hasSettledLayers(state);
810
+ state.loading = awaitingReconciliation;
811
+ state.stale = awaitingReconciliation;
812
+ state.error = void 0;
813
+ this.recomputeData(state);
814
+ this.notifySubscription(state, true);
815
+ this.discardStalePatches(state);
816
+ } catch {
817
+ void this.requestResync(state).catch(() => void 0);
818
+ return;
819
+ }
820
+ }
821
+ if ([...state.patchBuffer.values()].some(
822
+ (patch) => patch.toVersion > state.version && patch.fromVersion !== state.version
823
+ )) {
824
+ void this.requestResync(state).catch(() => void 0);
825
+ }
826
+ }
827
+ discardStalePatches(state) {
828
+ if (state.version === void 0) return;
829
+ for (const [messageId, patch] of state.patchBuffer) {
830
+ if (patch.toVersion <= state.version) {
831
+ state.patchBuffer.delete(messageId);
832
+ state.appliedMessageIds.add(messageId);
833
+ }
834
+ }
835
+ }
836
+ requestResync(state) {
837
+ if (state.resyncPromise && state.resyncGeneration === state.generation) {
838
+ return state.resyncPromise;
839
+ }
840
+ if (!state.active || !state.registrationId) return Promise.resolve();
841
+ state.stale = true;
842
+ state.loading = true;
843
+ this.notifySubscription(state);
844
+ let request;
845
+ request = this.ensureRegistered(
846
+ state,
847
+ this.wsManager.generation,
848
+ true
849
+ ).finally(() => {
850
+ if (state.resyncPromise === request) {
851
+ state.resyncPromise = void 0;
852
+ state.resyncGeneration = void 0;
853
+ }
854
+ }).then(() => {
855
+ if (state.active && this.hasSettledLayers(state)) {
856
+ return this.requestResync(state);
857
+ }
858
+ return void 0;
859
+ });
860
+ state.resyncPromise = request;
861
+ state.resyncGeneration = state.generation;
862
+ return request;
863
+ }
864
+ createMutationLayers(mutationId, input, options, layered) {
865
+ const targets = this.findMutationTargets(options);
866
+ for (const state of targets) {
867
+ const updater = this.createOptimisticUpdater(input, options);
868
+ if (!updater || state.authoritativeData === void 0) continue;
869
+ state.optimisticLayers.set(mutationId, {
870
+ updater
871
+ });
872
+ layered.push(state);
873
+ this.recomputeData(state);
874
+ this.notifySubscription(state, true);
875
+ }
876
+ }
877
+ findMutationTargets(options) {
878
+ if (options?.queryKey) {
879
+ const exact = this.subscriptionsByKey.get(options.queryKey);
880
+ return exact ? [exact] : [];
881
+ }
882
+ if (typeof options?.invalidates === "object") {
883
+ const exact = this.subscriptionsByKey.get(
884
+ getQueryKey(options.invalidates.path, options.invalidates.input)
885
+ );
886
+ return exact ? [exact] : [];
887
+ }
888
+ if (typeof options?.invalidates === "string") {
889
+ return [...this.subscriptionsByKey.values()].filter(
890
+ (state) => state.path === options.invalidates
891
+ );
892
+ }
893
+ return [];
894
+ }
895
+ createOptimisticUpdater(input, options, result, settled = false) {
896
+ const update = options?.optimisticUpdate;
897
+ if (typeof update === "function") {
898
+ return (data) => Array.isArray(data) ? update(data, input, result) : data;
899
+ }
900
+ const itemId = typeof input === "object" && input !== null ? input.id : void 0;
901
+ if (update === "remove" && itemId !== void 0) {
902
+ return (data) => Array.isArray(data) ? data.filter((item) => item.id !== itemId) : data;
903
+ }
904
+ if (update !== "merge") return void 0;
905
+ if (itemId !== void 0) {
906
+ const resultFields = settled && typeof result === "object" && result !== null ? result : {};
907
+ return (data) => {
908
+ if (!Array.isArray(data)) return data;
909
+ return data.map(
910
+ (item) => item.id === itemId ? { ...item, ...input, ...resultFields } : item
911
+ );
912
+ };
913
+ }
914
+ if (options?.optimisticData === void 0) return void 0;
915
+ return (data) => {
916
+ if (!Array.isArray(data)) return data;
917
+ const optimistic = settled ? result === void 0 ? typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData : result : typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData;
918
+ if (settled && optimistic !== null && typeof optimistic === "object") {
919
+ const optimisticId = optimistic.id;
920
+ if (optimisticId !== void 0 && data.some(
921
+ (item) => typeof item === "object" && item !== null && item.id === optimisticId
922
+ )) {
923
+ return data;
924
+ }
925
+ }
926
+ return optimistic === void 0 ? data : [optimistic, ...data];
927
+ };
928
+ }
929
+ settleMutationLayers(mutationId, input, result, options, states) {
930
+ for (const state of states) {
931
+ const layer = state.optimisticLayers.get(mutationId);
932
+ const updater = this.createOptimisticUpdater(
933
+ input,
934
+ options,
935
+ result,
936
+ true
937
+ );
938
+ if (!layer || !updater) continue;
939
+ layer.updater = updater;
940
+ layer.settledRevision = ++state.settlementRevision;
941
+ state.loading = true;
942
+ state.stale = true;
943
+ this.recomputeData(state);
944
+ this.notifySubscription(state, true);
945
+ void this.requestResync(state).catch(() => void 0);
946
+ }
947
+ }
948
+ removeMutationLayers(mutationId, states) {
949
+ for (const state of states) {
950
+ if (!state.optimisticLayers.delete(mutationId)) continue;
951
+ this.recomputeData(state);
952
+ this.notifySubscription(state, true);
953
+ }
954
+ }
955
+ recomputeData(state) {
956
+ let data = state.authoritativeData;
957
+ for (const layer of state.optimisticLayers.values()) {
958
+ data = layer.updater(data);
959
+ }
960
+ state.data = data;
961
+ }
962
+ hasSettledLayers(state) {
963
+ for (const layer of state.optimisticLayers.values()) {
964
+ if (layer.settledRevision !== void 0) return true;
965
+ }
966
+ return false;
967
+ }
968
+ async buildHeaders() {
969
+ const headers = {
970
+ "Content-Type": "application/json"
971
+ };
972
+ if (this.headersGetter) Object.assign(headers, await this.headersGetter());
973
+ if (this.authGetter) {
974
+ headers["Authorization"] = `Bearer ${await this.authGetter()}`;
975
+ }
976
+ return headers;
977
+ }
978
+ markSubscriptionsStale() {
979
+ for (const state of this.subscriptionsByKey.values()) {
980
+ if (!state.active) continue;
981
+ state.stale = true;
982
+ state.loading = true;
983
+ this.notifySubscription(state);
984
+ }
985
+ }
986
+ notifySubscription(state, dataChanged = false, failure) {
987
+ if (!state.active) return;
988
+ state.revision++;
989
+ for (const listener of state.listeners) listener();
990
+ if (dataChanged) {
991
+ for (const observer of state.observers.values()) {
992
+ observer.onData?.(state.data);
993
+ }
994
+ }
995
+ if (failure) {
996
+ for (const observer of state.observers.values()) {
997
+ observer.onError?.(failure);
998
+ }
999
+ }
1000
+ }
1001
+ notifyStateListeners() {
1002
+ for (const listener of this.stateListeners) {
1003
+ listener(this.connectionState);
1004
+ }
1005
+ }
1006
+ };
1007
+ var ReactiveClientContext = react.createContext(null);
1008
+ function createManagedClient(externalClient, config) {
1009
+ if (externalClient) return { client: externalClient, owned: false };
1010
+ if (!config?.url) return null;
1011
+ return {
1012
+ client: new ReactiveClient(config),
1013
+ owned: true,
1014
+ scope: getClientScope(config)
1015
+ };
1016
+ }
1017
+ function sameRecord(left, right) {
1018
+ if (left === right) return true;
1019
+ if (!left || !right) return false;
1020
+ const keys = Object.keys(left);
1021
+ return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
1022
+ }
1023
+ function getClientScope(config) {
1024
+ return {
1025
+ url: config.url,
1026
+ httpUrl: config.httpUrl ?? "/api/reactive",
1027
+ auth: typeof config.auth === "string" ? config.auth : void 0,
1028
+ dynamicAuth: typeof config.auth === "function",
1029
+ headers: typeof config.headers === "object" ? { ...config.headers } : void 0,
1030
+ dynamicHeaders: typeof config.headers === "function"
1031
+ };
1032
+ }
1033
+ function sameScope(left, right) {
1034
+ if (!left) return false;
1035
+ return left.url === right.url && left.httpUrl === right.httpUrl && left.auth === right.auth && left.dynamicAuth === right.dynamicAuth && left.dynamicHeaders === right.dynamicHeaders && sameRecord(left.headers, right.headers);
1036
+ }
1037
+ function ReactiveClientProvider({
1038
+ children,
1039
+ config,
1040
+ client: externalClient
1041
+ }) {
1042
+ const [managed, setManaged] = react.useState(
1043
+ () => createManagedClient(externalClient, config)
1044
+ );
1045
+ let renderedManaged = managed;
1046
+ if (externalClient) {
1047
+ if (managed?.owned || managed?.client !== externalClient) {
1048
+ renderedManaged = { client: externalClient, owned: false };
1049
+ }
1050
+ } else if (!config?.url) {
1051
+ renderedManaged = null;
1052
+ } else {
1053
+ const scope = getClientScope(config);
1054
+ if (!managed?.owned || !sameScope(managed.scope, scope)) {
1055
+ renderedManaged = {
1056
+ client: new ReactiveClient(config),
1057
+ owned: true,
1058
+ scope
1059
+ };
1060
+ }
1061
+ }
1062
+ if (renderedManaged !== managed) setManaged(renderedManaged);
1063
+ react.useEffect(() => {
1064
+ if (renderedManaged?.owned && config) {
1065
+ renderedManaged.client.configure(config);
1066
+ }
1067
+ }, [config, renderedManaged]);
1068
+ react.useEffect(() => {
1069
+ if (!renderedManaged?.owned) return;
1070
+ void renderedManaged.client.connect().catch(() => void 0);
1071
+ return () => {
1072
+ renderedManaged.client.disconnect();
1073
+ };
1074
+ }, [renderedManaged]);
1075
+ react.useEffect(() => {
1076
+ if (typeof window === "undefined" || typeof document === "undefined" || !renderedManaged?.owned) {
1077
+ return;
1078
+ }
1079
+ const attemptReconnect = () => {
1080
+ const state = renderedManaged.client.getConnectionState();
1081
+ if (state === "connected" || state === "connecting") return;
1082
+ if (state === "reconnecting") {
1083
+ debug(
1084
+ "[dynamodb-reactive] Tab active during reconnect; reconnecting now"
1085
+ );
1086
+ renderedManaged.client.forceReconnect();
1087
+ } else {
1088
+ void renderedManaged.client.connect().catch(() => void 0);
1089
+ }
1090
+ };
1091
+ const handleVisibility = () => {
1092
+ if (document.visibilityState === "visible") attemptReconnect();
1093
+ };
1094
+ document.addEventListener("visibilitychange", handleVisibility);
1095
+ window.addEventListener("focus", attemptReconnect);
1096
+ return () => {
1097
+ document.removeEventListener("visibilitychange", handleVisibility);
1098
+ window.removeEventListener("focus", attemptReconnect);
1099
+ };
1100
+ }, [renderedManaged]);
1101
+ react.useEffect(() => {
1102
+ if (!config?.url && !externalClient) {
1103
+ warn(
1104
+ "[dynamodb-reactive] WebSocket URL not configured; real-time features disabled"
1105
+ );
1106
+ }
1107
+ }, [config?.url, externalClient]);
1108
+ return /* @__PURE__ */ jsxRuntime.jsx(ReactiveClientContext.Provider, { value: renderedManaged?.client ?? null, children });
1109
+ }
1110
+ function useReactiveClient() {
1111
+ return react.useContext(ReactiveClientContext);
1112
+ }
1113
+ function useConnectionState() {
1114
+ const client = useReactiveClient();
1115
+ return react.useSyncExternalStore(
1116
+ (listener) => client ? client.onConnectionStateChange(listener) : () => void 0,
1117
+ () => client ? client.getConnectionState() : "disabled",
1118
+ () => "disabled"
1119
+ );
1120
+ }
1121
+ function useSubscription(path, options = {}) {
1122
+ const client = useReactiveClient();
1123
+ const queryKey = getQueryKey(path, options.input);
1124
+ const [active, setActive] = react.useState(null);
1125
+ const onDataRef = react.useRef(options.onData);
1126
+ const onErrorRef = react.useRef(options.onError);
1127
+ onDataRef.current = options.onData;
1128
+ onErrorRef.current = options.onError;
1129
+ react.useEffect(() => {
1130
+ if (!client) {
1131
+ setActive(null);
1132
+ return;
1133
+ }
1134
+ const next = client.subscribe(path, {
1135
+ input: options.input,
1136
+ onData: (data) => onDataRef.current?.(data),
1137
+ onError: (failure) => onErrorRef.current?.(failure)
1138
+ });
1139
+ setActive({ client, subscription: next });
1140
+ return () => next.unsubscribe();
1141
+ }, [client, path, queryKey]);
1142
+ const subscription = active?.client === client && active.subscription.key === queryKey ? active.subscription : null;
1143
+ react.useSyncExternalStore(
1144
+ (listener) => client && subscription ? client.addSubscriptionListener(subscription.id, listener) : () => void 0,
1145
+ () => client && subscription ? client.getSubscriptionRevision(subscription.id) : -1,
1146
+ () => -1
1147
+ );
1148
+ const refetch = async () => {
1149
+ await subscription?.refetch();
1150
+ };
1151
+ const setData = (updater) => {
1152
+ if (client && subscription) {
1153
+ client.applyOptimisticUpdate(
1154
+ subscription.id,
1155
+ updater
1156
+ );
1157
+ }
1158
+ };
1159
+ return {
1160
+ data: subscription?.data,
1161
+ loading: client ? subscription?.loading ?? true : false,
1162
+ stale: client ? subscription?.stale ?? true : false,
1163
+ error: subscription?.error,
1164
+ disabled: !client,
1165
+ refetch,
1166
+ setData
1167
+ };
1168
+ }
1169
+ function useMutation(path, options) {
1170
+ const client = useReactiveClient();
1171
+ const [state, setState] = react.useState({ data: void 0, loading: false, error: void 0 });
1172
+ const pendingCountRef = react.useRef(0);
1173
+ const latestCallRef = react.useRef(0);
1174
+ const mutate = async (input) => {
1175
+ if (!client) {
1176
+ throw new Error("WebSocket not configured; mutations are disabled");
1177
+ }
1178
+ const callId = ++latestCallRef.current;
1179
+ pendingCountRef.current++;
1180
+ setState((current) => ({ ...current, loading: true, error: void 0 }));
1181
+ try {
1182
+ const result = await client.call(path, input, options);
1183
+ pendingCountRef.current--;
1184
+ setState(
1185
+ (current) => callId === latestCallRef.current ? {
1186
+ data: result,
1187
+ loading: pendingCountRef.current > 0,
1188
+ error: void 0
1189
+ } : { ...current, loading: pendingCountRef.current > 0 }
1190
+ );
1191
+ return result;
1192
+ } catch (cause) {
1193
+ const failure = cause instanceof Error ? cause : new Error(String(cause));
1194
+ pendingCountRef.current--;
1195
+ setState(
1196
+ (current) => callId === latestCallRef.current ? {
1197
+ ...current,
1198
+ loading: pendingCountRef.current > 0,
1199
+ error: failure
1200
+ } : { ...current, loading: pendingCountRef.current > 0 }
1201
+ );
1202
+ throw failure;
1203
+ }
1204
+ };
1205
+ const reset = () => {
1206
+ latestCallRef.current++;
1207
+ setState({
1208
+ data: void 0,
1209
+ loading: pendingCountRef.current > 0,
1210
+ error: void 0
1211
+ });
1212
+ };
1213
+ return { mutate, ...state, disabled: !client, reset };
1214
+ }
1215
+ function useClient() {
1216
+ function buildProxy(path) {
1217
+ return new Proxy(
1218
+ {},
1219
+ {
1220
+ get(_target, property) {
1221
+ if (typeof property !== "string") return void 0;
1222
+ if (property === "useQuery") {
1223
+ return function useQueryHook(input, queryOptions) {
1224
+ return useSubscription(path.join("."), {
1225
+ ...queryOptions,
1226
+ input
1227
+ });
1228
+ };
1229
+ }
1230
+ if (property === "useMutation") {
1231
+ return function useMutationHook(mutationOptions) {
1232
+ return useMutation(path.join("."), mutationOptions);
1233
+ };
1234
+ }
1235
+ return buildProxy([...path, property]);
1236
+ }
1237
+ }
1238
+ );
1239
+ }
1240
+ return buildProxy([]);
1241
+ }
1242
+
1243
+ exports.ReactiveClientProvider = ReactiveClientProvider;
1244
+ exports.useClient = useClient;
1245
+ exports.useConnectionState = useConnectionState;
1246
+ exports.useMutation = useMutation;
1247
+ exports.useReactiveClient = useReactiveClient;
1248
+ exports.useSubscription = useSubscription;
1249
+ //# sourceMappingURL=react.cjs.map
1250
+ //# sourceMappingURL=react.cjs.map