dynamodb-reactive 0.4.0 → 0.5.0
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/README.md +76 -51
- package/dist/{client-Zb67giI5.d.ts → client-wZQ9XC9S.d.ts} +11 -0
- package/dist/client.cjs +53 -55
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +53 -55
- package/dist/client.js.map +1 -1
- package/dist/cloudflare.cjs +95 -13
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +37 -3
- package/dist/cloudflare.d.ts +37 -3
- package/dist/cloudflare.js +95 -13
- package/dist/cloudflare.js.map +1 -1
- package/dist/cluster.cjs +66 -66
- package/dist/cluster.cjs.map +1 -1
- package/dist/cluster.js +66 -66
- package/dist/cluster.js.map +1 -1
- package/dist/core.cjs +1 -3
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +1 -3
- package/dist/core.js.map +1 -1
- package/dist/hub.cjs +8 -5
- package/dist/hub.cjs.map +1 -1
- package/dist/hub.d.cts +3 -1
- package/dist/hub.d.ts +3 -1
- package/dist/hub.js +8 -5
- package/dist/hub.js.map +1 -1
- package/dist/index.cjs +1 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/infra.cjs +19 -33
- package/dist/infra.cjs.map +1 -1
- package/dist/infra.js +19 -33
- package/dist/infra.js.map +1 -1
- package/dist/react.cjs +56 -65
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +56 -65
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +46 -139
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +46 -139
- package/dist/server.js.map +1 -1
- package/package.json +22 -24
package/dist/react.js
CHANGED
|
@@ -27,9 +27,7 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
27
27
|
if (Array.isArray(value)) {
|
|
28
28
|
const items = [];
|
|
29
29
|
for (let index = 0; index < value.length; index++) {
|
|
30
|
-
items.push(
|
|
31
|
-
canonicalize(value[index], seen, String(index), true) ?? "null"
|
|
32
|
-
);
|
|
30
|
+
items.push(canonicalize(value[index], seen, String(index), true) ?? "null");
|
|
33
31
|
}
|
|
34
32
|
return `[${items.join(",")}]`;
|
|
35
33
|
}
|
|
@@ -46,6 +44,9 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
46
44
|
seen.delete(value);
|
|
47
45
|
}
|
|
48
46
|
}
|
|
47
|
+
case "bigint":
|
|
48
|
+
case "function":
|
|
49
|
+
case "symbol":
|
|
49
50
|
default:
|
|
50
51
|
throw new TypeError(`Unsupported query input value: ${typeof value}`);
|
|
51
52
|
}
|
|
@@ -53,7 +54,6 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
53
54
|
function getQueryKey(path, input) {
|
|
54
55
|
return `${JSON.stringify(path)}:${canonicalize(input, /* @__PURE__ */ new Set(), "") ?? "undefined"}`;
|
|
55
56
|
}
|
|
56
|
-
var { applyPatch } = jsonpatch;
|
|
57
57
|
function applyPatches(document2, patches) {
|
|
58
58
|
if (patches.length === 0) {
|
|
59
59
|
return document2;
|
|
@@ -72,7 +72,7 @@ function applyPatches(document2, patches) {
|
|
|
72
72
|
return op;
|
|
73
73
|
});
|
|
74
74
|
try {
|
|
75
|
-
const result = applyPatch(
|
|
75
|
+
const result = jsonpatch.applyPatch(
|
|
76
76
|
structuredClone(document2),
|
|
77
77
|
operations,
|
|
78
78
|
true,
|
|
@@ -100,12 +100,13 @@ function error(...values) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// ../client/src/websocket.ts
|
|
103
|
-
var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
104
103
|
function withDefaults(config) {
|
|
105
104
|
return {
|
|
106
105
|
autoReconnect: true,
|
|
107
106
|
connectionTimeout: 1e4,
|
|
108
107
|
handshakeTimeout: 1e4,
|
|
108
|
+
heartbeatInterval: 5 * 60 * 1e3,
|
|
109
|
+
heartbeatTimeout: 15e3,
|
|
109
110
|
maxReconnectAttempts: 10,
|
|
110
111
|
reconnectDelay: 1e3,
|
|
111
112
|
...config
|
|
@@ -119,6 +120,7 @@ var WebSocketManager = class {
|
|
|
119
120
|
reconnectTimer = null;
|
|
120
121
|
handshakeTimer = null;
|
|
121
122
|
heartbeatTimer = null;
|
|
123
|
+
pongTimer = null;
|
|
122
124
|
messageHandlers = /* @__PURE__ */ new Set();
|
|
123
125
|
stateHandlers = /* @__PURE__ */ new Set();
|
|
124
126
|
handshakeHandlers = /* @__PURE__ */ new Set();
|
|
@@ -218,16 +220,7 @@ var WebSocketManager = class {
|
|
|
218
220
|
};
|
|
219
221
|
nextSocket.onclose = () => {
|
|
220
222
|
if (!this.isCurrent(nextSocket, generation)) return;
|
|
221
|
-
this.
|
|
222
|
-
this.ws = null;
|
|
223
|
-
this._connectionId = null;
|
|
224
|
-
this.setState("disconnected");
|
|
225
|
-
this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
|
|
226
|
-
if (this.config.autoReconnect && !this.intentionalDisconnect) {
|
|
227
|
-
this.scheduleReconnect();
|
|
228
|
-
} else {
|
|
229
|
-
this.rejectWaiters(new Error("WebSocket disconnected"));
|
|
230
|
-
}
|
|
223
|
+
this.handleClose();
|
|
231
224
|
};
|
|
232
225
|
nextSocket.onerror = () => {
|
|
233
226
|
if (!this.isCurrent(nextSocket, generation)) return;
|
|
@@ -316,6 +309,7 @@ var WebSocketManager = class {
|
|
|
316
309
|
return;
|
|
317
310
|
}
|
|
318
311
|
if (message.type === "pong") {
|
|
312
|
+
this.clearPongTimer();
|
|
319
313
|
if (!this._connectionId || this.state === "connected") return;
|
|
320
314
|
this.clearHandshakeTimer();
|
|
321
315
|
this.lastConnectionError = void 0;
|
|
@@ -344,6 +338,18 @@ var WebSocketManager = class {
|
|
|
344
338
|
socket.close(4e3, "Handshake timeout");
|
|
345
339
|
}, this.config.handshakeTimeout);
|
|
346
340
|
}
|
|
341
|
+
handleClose() {
|
|
342
|
+
this.clearConnectionTimers();
|
|
343
|
+
this.ws = null;
|
|
344
|
+
this._connectionId = null;
|
|
345
|
+
this.setState("disconnected");
|
|
346
|
+
this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
|
|
347
|
+
if (this.config.autoReconnect && !this.intentionalDisconnect) {
|
|
348
|
+
this.scheduleReconnect();
|
|
349
|
+
} else {
|
|
350
|
+
this.rejectWaiters(new Error("WebSocket disconnected"));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
347
353
|
startHeartbeat(socket, generation) {
|
|
348
354
|
this.clearHeartbeatTimer();
|
|
349
355
|
this.heartbeatTimer = setInterval(() => {
|
|
@@ -351,7 +357,17 @@ var WebSocketManager = class {
|
|
|
351
357
|
return;
|
|
352
358
|
}
|
|
353
359
|
socket.send(JSON.stringify({ type: "ping" }));
|
|
354
|
-
|
|
360
|
+
this.pongTimer ??= setTimeout(() => {
|
|
361
|
+
this.pongTimer = null;
|
|
362
|
+
if (!this.isCurrent(socket, generation)) return;
|
|
363
|
+
const failure = new Error("WebSocket heartbeat timed out");
|
|
364
|
+
this.lastConnectionError = failure;
|
|
365
|
+
this.invokeLifecycleCallback("onError", this.config.onError, failure);
|
|
366
|
+
socket.onclose = null;
|
|
367
|
+
socket.close(4001, "Heartbeat timeout");
|
|
368
|
+
this.handleClose();
|
|
369
|
+
}, this.config.heartbeatTimeout);
|
|
370
|
+
}, this.config.heartbeatInterval);
|
|
355
371
|
}
|
|
356
372
|
isCurrent(socket, generation) {
|
|
357
373
|
return this.ws === socket && this.socketGeneration === generation;
|
|
@@ -418,9 +434,15 @@ var WebSocketManager = class {
|
|
|
418
434
|
clearInterval(this.heartbeatTimer);
|
|
419
435
|
this.heartbeatTimer = null;
|
|
420
436
|
}
|
|
437
|
+
clearPongTimer() {
|
|
438
|
+
if (!this.pongTimer) return;
|
|
439
|
+
clearTimeout(this.pongTimer);
|
|
440
|
+
this.pongTimer = null;
|
|
441
|
+
}
|
|
421
442
|
clearConnectionTimers() {
|
|
422
443
|
this.clearHandshakeTimer();
|
|
423
444
|
this.clearHeartbeatTimer();
|
|
445
|
+
this.clearPongTimer();
|
|
424
446
|
}
|
|
425
447
|
clearTimers() {
|
|
426
448
|
this.clearConnectionTimers();
|
|
@@ -520,10 +542,7 @@ var ReactiveClient = class {
|
|
|
520
542
|
this.subscriptionsByKey.set(key, shared);
|
|
521
543
|
this.subscriptionsById.set(shared.id, shared);
|
|
522
544
|
if (this.connectionState === "connected") {
|
|
523
|
-
void this.ensureRegistered(
|
|
524
|
-
shared,
|
|
525
|
-
this.wsManager.generation
|
|
526
|
-
).catch(() => void 0);
|
|
545
|
+
void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
|
|
527
546
|
}
|
|
528
547
|
}
|
|
529
548
|
const leaseId = generateId();
|
|
@@ -532,9 +551,7 @@ var ReactiveClient = class {
|
|
|
532
551
|
onError: options.onError
|
|
533
552
|
});
|
|
534
553
|
if (this.connectionState === "connected" && (!shared.hasRegistered || shared.stale) && !shared.registrationPromise) {
|
|
535
|
-
void this.ensureRegistered(shared, this.wsManager.generation).catch(
|
|
536
|
-
() => void 0
|
|
537
|
-
);
|
|
554
|
+
void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
|
|
538
555
|
}
|
|
539
556
|
const state = shared;
|
|
540
557
|
let released = false;
|
|
@@ -623,9 +640,7 @@ var ReactiveClient = class {
|
|
|
623
640
|
registerActiveSubscriptions(socketGeneration) {
|
|
624
641
|
for (const state of this.subscriptionsByKey.values()) {
|
|
625
642
|
if (state.active) {
|
|
626
|
-
void this.ensureRegistered(state, socketGeneration).catch(
|
|
627
|
-
() => void 0
|
|
628
|
-
);
|
|
643
|
+
void this.ensureRegistered(state, socketGeneration).catch(() => void 0);
|
|
629
644
|
}
|
|
630
645
|
}
|
|
631
646
|
}
|
|
@@ -678,9 +693,7 @@ var ReactiveClient = class {
|
|
|
678
693
|
})
|
|
679
694
|
});
|
|
680
695
|
if (!response.ok) {
|
|
681
|
-
throw new Error(
|
|
682
|
-
`Subscribe failed: ${response.status} ${response.statusText}`
|
|
683
|
-
);
|
|
696
|
+
throw new Error(`Subscribe failed: ${response.status} ${response.statusText}`);
|
|
684
697
|
}
|
|
685
698
|
const result = await response.json();
|
|
686
699
|
if (result.type === "error") {
|
|
@@ -699,12 +712,7 @@ var ReactiveClient = class {
|
|
|
699
712
|
throw cause;
|
|
700
713
|
} finally {
|
|
701
714
|
if ((!state.active || !isCurrent()) && requestIssued) {
|
|
702
|
-
void this.sendUnsubscribe(
|
|
703
|
-
registrationConnectionId,
|
|
704
|
-
state.id,
|
|
705
|
-
generation,
|
|
706
|
-
registrationId
|
|
707
|
-
);
|
|
715
|
+
void this.sendUnsubscribe(registrationConnectionId, state.id, generation, registrationId);
|
|
708
716
|
}
|
|
709
717
|
if (state.registrationPromise === request) {
|
|
710
718
|
state.registrationPromise = void 0;
|
|
@@ -809,15 +817,12 @@ var ReactiveClient = class {
|
|
|
809
817
|
return;
|
|
810
818
|
}
|
|
811
819
|
this.discardStalePatches(state);
|
|
812
|
-
|
|
813
|
-
const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion)
|
|
820
|
+
for (; ; ) {
|
|
821
|
+
const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion).at(0);
|
|
814
822
|
if (!next) break;
|
|
815
823
|
state.patchBuffer.delete(next.messageId);
|
|
816
824
|
try {
|
|
817
|
-
state.authoritativeData = applyPatches(
|
|
818
|
-
state.authoritativeData,
|
|
819
|
-
next.patches
|
|
820
|
-
);
|
|
825
|
+
state.authoritativeData = applyPatches(state.authoritativeData, next.patches);
|
|
821
826
|
state.version = next.toVersion;
|
|
822
827
|
state.appliedMessageIds.add(next.messageId);
|
|
823
828
|
const awaitingReconciliation = this.hasSettledLayers(state);
|
|
@@ -855,12 +860,7 @@ var ReactiveClient = class {
|
|
|
855
860
|
state.stale = true;
|
|
856
861
|
state.loading = true;
|
|
857
862
|
this.notifySubscription(state);
|
|
858
|
-
|
|
859
|
-
request = this.ensureRegistered(
|
|
860
|
-
state,
|
|
861
|
-
this.wsManager.generation,
|
|
862
|
-
true
|
|
863
|
-
).finally(() => {
|
|
863
|
+
const request = this.ensureRegistered(state, this.wsManager.generation, true).finally(() => {
|
|
864
864
|
if (state.resyncPromise === request) {
|
|
865
865
|
state.resyncPromise = void 0;
|
|
866
866
|
state.resyncGeneration = void 0;
|
|
@@ -928,7 +928,10 @@ var ReactiveClient = class {
|
|
|
928
928
|
if (options?.optimisticData === void 0) return void 0;
|
|
929
929
|
return (data) => {
|
|
930
930
|
if (!Array.isArray(data)) return data;
|
|
931
|
-
const optimistic = settled ?
|
|
931
|
+
const optimistic = settled ? (
|
|
932
|
+
// oxlint-disable-next-line typescript/prefer-nullish-coalescing -- null is a valid mutation result.
|
|
933
|
+
result === void 0 ? typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData : result
|
|
934
|
+
) : typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData;
|
|
932
935
|
if (settled && optimistic !== null && typeof optimistic === "object") {
|
|
933
936
|
const optimisticId = optimistic.id;
|
|
934
937
|
if (optimisticId !== void 0 && data.some(
|
|
@@ -943,12 +946,7 @@ var ReactiveClient = class {
|
|
|
943
946
|
settleMutationLayers(mutationId, input, result, options, states) {
|
|
944
947
|
for (const state of states) {
|
|
945
948
|
const layer = state.optimisticLayers.get(mutationId);
|
|
946
|
-
const updater = this.createOptimisticUpdater(
|
|
947
|
-
input,
|
|
948
|
-
options,
|
|
949
|
-
result,
|
|
950
|
-
true
|
|
951
|
-
);
|
|
949
|
+
const updater = this.createOptimisticUpdater(input, options, result, true);
|
|
952
950
|
if (!layer || !updater) continue;
|
|
953
951
|
layer.updater = updater;
|
|
954
952
|
layer.settledRevision = ++state.settlementRevision;
|
|
@@ -1094,9 +1092,7 @@ function ReactiveClientProvider({
|
|
|
1094
1092
|
const state = renderedManaged.client.getConnectionState();
|
|
1095
1093
|
if (state === "connected" || state === "connecting") return;
|
|
1096
1094
|
if (state === "reconnecting") {
|
|
1097
|
-
debug(
|
|
1098
|
-
"[dynamodb-reactive] Tab active during reconnect; reconnecting now"
|
|
1099
|
-
);
|
|
1095
|
+
debug("[dynamodb-reactive] Tab active during reconnect; reconnecting now");
|
|
1100
1096
|
renderedManaged.client.forceReconnect();
|
|
1101
1097
|
} else {
|
|
1102
1098
|
void renderedManaged.client.connect().catch(() => void 0);
|
|
@@ -1114,9 +1110,7 @@ function ReactiveClientProvider({
|
|
|
1114
1110
|
}, [renderedManaged]);
|
|
1115
1111
|
useEffect(() => {
|
|
1116
1112
|
if (!config?.url && !externalClient) {
|
|
1117
|
-
warn(
|
|
1118
|
-
"[dynamodb-reactive] WebSocket URL not configured; real-time features disabled"
|
|
1119
|
-
);
|
|
1113
|
+
warn("[dynamodb-reactive] WebSocket URL not configured; real-time features disabled");
|
|
1120
1114
|
}
|
|
1121
1115
|
}, [config?.url, externalClient]);
|
|
1122
1116
|
return /* @__PURE__ */ jsx(ReactiveClientContext.Provider, { value: renderedManaged?.client ?? null, children });
|
|
@@ -1164,10 +1158,7 @@ function useSubscription(path, options = {}) {
|
|
|
1164
1158
|
};
|
|
1165
1159
|
const setData = (updater) => {
|
|
1166
1160
|
if (client && subscription) {
|
|
1167
|
-
client.applyOptimisticUpdate(
|
|
1168
|
-
subscription.id,
|
|
1169
|
-
updater
|
|
1170
|
-
);
|
|
1161
|
+
client.applyOptimisticUpdate(subscription.id, updater);
|
|
1171
1162
|
}
|
|
1172
1163
|
};
|
|
1173
1164
|
return {
|