dynamodb-reactive 0.3.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 +121 -43
- 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 +461 -0
- package/dist/cloudflare.cjs.map +1 -0
- package/dist/cloudflare.d.cts +129 -0
- package/dist/cloudflare.d.ts +129 -0
- package/dist/cloudflare.js +456 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/cluster.cjs +66 -66
- package/dist/cluster.cjs.map +1 -1
- package/dist/cluster.d.cts +2 -78
- package/dist/cluster.d.ts +2 -78
- 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 +164 -0
- package/dist/hub.cjs.map +1 -0
- package/dist/hub.d.cts +91 -0
- package/dist/hub.d.ts +91 -0
- package/dist/hub.js +157 -0
- package/dist/hub.js.map +1 -0
- 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/protocol-C4Syt_Bl.d.ts +78 -0
- 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 +42 -24
package/dist/react.cjs
CHANGED
|
@@ -33,9 +33,7 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
33
33
|
if (Array.isArray(value)) {
|
|
34
34
|
const items = [];
|
|
35
35
|
for (let index = 0; index < value.length; index++) {
|
|
36
|
-
items.push(
|
|
37
|
-
canonicalize(value[index], seen, String(index), true) ?? "null"
|
|
38
|
-
);
|
|
36
|
+
items.push(canonicalize(value[index], seen, String(index), true) ?? "null");
|
|
39
37
|
}
|
|
40
38
|
return `[${items.join(",")}]`;
|
|
41
39
|
}
|
|
@@ -52,6 +50,9 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
52
50
|
seen.delete(value);
|
|
53
51
|
}
|
|
54
52
|
}
|
|
53
|
+
case "bigint":
|
|
54
|
+
case "function":
|
|
55
|
+
case "symbol":
|
|
55
56
|
default:
|
|
56
57
|
throw new TypeError(`Unsupported query input value: ${typeof value}`);
|
|
57
58
|
}
|
|
@@ -59,7 +60,6 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
59
60
|
function getQueryKey(path, input) {
|
|
60
61
|
return `${JSON.stringify(path)}:${canonicalize(input, /* @__PURE__ */ new Set(), "") ?? "undefined"}`;
|
|
61
62
|
}
|
|
62
|
-
var { applyPatch } = jsonpatch__default.default;
|
|
63
63
|
function applyPatches(document2, patches) {
|
|
64
64
|
if (patches.length === 0) {
|
|
65
65
|
return document2;
|
|
@@ -78,7 +78,7 @@ function applyPatches(document2, patches) {
|
|
|
78
78
|
return op;
|
|
79
79
|
});
|
|
80
80
|
try {
|
|
81
|
-
const result = applyPatch(
|
|
81
|
+
const result = jsonpatch__default.default.applyPatch(
|
|
82
82
|
structuredClone(document2),
|
|
83
83
|
operations,
|
|
84
84
|
true,
|
|
@@ -106,12 +106,13 @@ function error(...values) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// ../client/src/websocket.ts
|
|
109
|
-
var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
110
109
|
function withDefaults(config) {
|
|
111
110
|
return {
|
|
112
111
|
autoReconnect: true,
|
|
113
112
|
connectionTimeout: 1e4,
|
|
114
113
|
handshakeTimeout: 1e4,
|
|
114
|
+
heartbeatInterval: 5 * 60 * 1e3,
|
|
115
|
+
heartbeatTimeout: 15e3,
|
|
115
116
|
maxReconnectAttempts: 10,
|
|
116
117
|
reconnectDelay: 1e3,
|
|
117
118
|
...config
|
|
@@ -125,6 +126,7 @@ var WebSocketManager = class {
|
|
|
125
126
|
reconnectTimer = null;
|
|
126
127
|
handshakeTimer = null;
|
|
127
128
|
heartbeatTimer = null;
|
|
129
|
+
pongTimer = null;
|
|
128
130
|
messageHandlers = /* @__PURE__ */ new Set();
|
|
129
131
|
stateHandlers = /* @__PURE__ */ new Set();
|
|
130
132
|
handshakeHandlers = /* @__PURE__ */ new Set();
|
|
@@ -224,16 +226,7 @@ var WebSocketManager = class {
|
|
|
224
226
|
};
|
|
225
227
|
nextSocket.onclose = () => {
|
|
226
228
|
if (!this.isCurrent(nextSocket, generation)) return;
|
|
227
|
-
this.
|
|
228
|
-
this.ws = null;
|
|
229
|
-
this._connectionId = null;
|
|
230
|
-
this.setState("disconnected");
|
|
231
|
-
this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
|
|
232
|
-
if (this.config.autoReconnect && !this.intentionalDisconnect) {
|
|
233
|
-
this.scheduleReconnect();
|
|
234
|
-
} else {
|
|
235
|
-
this.rejectWaiters(new Error("WebSocket disconnected"));
|
|
236
|
-
}
|
|
229
|
+
this.handleClose();
|
|
237
230
|
};
|
|
238
231
|
nextSocket.onerror = () => {
|
|
239
232
|
if (!this.isCurrent(nextSocket, generation)) return;
|
|
@@ -322,6 +315,7 @@ var WebSocketManager = class {
|
|
|
322
315
|
return;
|
|
323
316
|
}
|
|
324
317
|
if (message.type === "pong") {
|
|
318
|
+
this.clearPongTimer();
|
|
325
319
|
if (!this._connectionId || this.state === "connected") return;
|
|
326
320
|
this.clearHandshakeTimer();
|
|
327
321
|
this.lastConnectionError = void 0;
|
|
@@ -350,6 +344,18 @@ var WebSocketManager = class {
|
|
|
350
344
|
socket.close(4e3, "Handshake timeout");
|
|
351
345
|
}, this.config.handshakeTimeout);
|
|
352
346
|
}
|
|
347
|
+
handleClose() {
|
|
348
|
+
this.clearConnectionTimers();
|
|
349
|
+
this.ws = null;
|
|
350
|
+
this._connectionId = null;
|
|
351
|
+
this.setState("disconnected");
|
|
352
|
+
this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
|
|
353
|
+
if (this.config.autoReconnect && !this.intentionalDisconnect) {
|
|
354
|
+
this.scheduleReconnect();
|
|
355
|
+
} else {
|
|
356
|
+
this.rejectWaiters(new Error("WebSocket disconnected"));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
353
359
|
startHeartbeat(socket, generation) {
|
|
354
360
|
this.clearHeartbeatTimer();
|
|
355
361
|
this.heartbeatTimer = setInterval(() => {
|
|
@@ -357,7 +363,17 @@ var WebSocketManager = class {
|
|
|
357
363
|
return;
|
|
358
364
|
}
|
|
359
365
|
socket.send(JSON.stringify({ type: "ping" }));
|
|
360
|
-
|
|
366
|
+
this.pongTimer ??= setTimeout(() => {
|
|
367
|
+
this.pongTimer = null;
|
|
368
|
+
if (!this.isCurrent(socket, generation)) return;
|
|
369
|
+
const failure = new Error("WebSocket heartbeat timed out");
|
|
370
|
+
this.lastConnectionError = failure;
|
|
371
|
+
this.invokeLifecycleCallback("onError", this.config.onError, failure);
|
|
372
|
+
socket.onclose = null;
|
|
373
|
+
socket.close(4001, "Heartbeat timeout");
|
|
374
|
+
this.handleClose();
|
|
375
|
+
}, this.config.heartbeatTimeout);
|
|
376
|
+
}, this.config.heartbeatInterval);
|
|
361
377
|
}
|
|
362
378
|
isCurrent(socket, generation) {
|
|
363
379
|
return this.ws === socket && this.socketGeneration === generation;
|
|
@@ -424,9 +440,15 @@ var WebSocketManager = class {
|
|
|
424
440
|
clearInterval(this.heartbeatTimer);
|
|
425
441
|
this.heartbeatTimer = null;
|
|
426
442
|
}
|
|
443
|
+
clearPongTimer() {
|
|
444
|
+
if (!this.pongTimer) return;
|
|
445
|
+
clearTimeout(this.pongTimer);
|
|
446
|
+
this.pongTimer = null;
|
|
447
|
+
}
|
|
427
448
|
clearConnectionTimers() {
|
|
428
449
|
this.clearHandshakeTimer();
|
|
429
450
|
this.clearHeartbeatTimer();
|
|
451
|
+
this.clearPongTimer();
|
|
430
452
|
}
|
|
431
453
|
clearTimers() {
|
|
432
454
|
this.clearConnectionTimers();
|
|
@@ -526,10 +548,7 @@ var ReactiveClient = class {
|
|
|
526
548
|
this.subscriptionsByKey.set(key, shared);
|
|
527
549
|
this.subscriptionsById.set(shared.id, shared);
|
|
528
550
|
if (this.connectionState === "connected") {
|
|
529
|
-
void this.ensureRegistered(
|
|
530
|
-
shared,
|
|
531
|
-
this.wsManager.generation
|
|
532
|
-
).catch(() => void 0);
|
|
551
|
+
void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
|
|
533
552
|
}
|
|
534
553
|
}
|
|
535
554
|
const leaseId = generateId();
|
|
@@ -538,9 +557,7 @@ var ReactiveClient = class {
|
|
|
538
557
|
onError: options.onError
|
|
539
558
|
});
|
|
540
559
|
if (this.connectionState === "connected" && (!shared.hasRegistered || shared.stale) && !shared.registrationPromise) {
|
|
541
|
-
void this.ensureRegistered(shared, this.wsManager.generation).catch(
|
|
542
|
-
() => void 0
|
|
543
|
-
);
|
|
560
|
+
void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
|
|
544
561
|
}
|
|
545
562
|
const state = shared;
|
|
546
563
|
let released = false;
|
|
@@ -629,9 +646,7 @@ var ReactiveClient = class {
|
|
|
629
646
|
registerActiveSubscriptions(socketGeneration) {
|
|
630
647
|
for (const state of this.subscriptionsByKey.values()) {
|
|
631
648
|
if (state.active) {
|
|
632
|
-
void this.ensureRegistered(state, socketGeneration).catch(
|
|
633
|
-
() => void 0
|
|
634
|
-
);
|
|
649
|
+
void this.ensureRegistered(state, socketGeneration).catch(() => void 0);
|
|
635
650
|
}
|
|
636
651
|
}
|
|
637
652
|
}
|
|
@@ -684,9 +699,7 @@ var ReactiveClient = class {
|
|
|
684
699
|
})
|
|
685
700
|
});
|
|
686
701
|
if (!response.ok) {
|
|
687
|
-
throw new Error(
|
|
688
|
-
`Subscribe failed: ${response.status} ${response.statusText}`
|
|
689
|
-
);
|
|
702
|
+
throw new Error(`Subscribe failed: ${response.status} ${response.statusText}`);
|
|
690
703
|
}
|
|
691
704
|
const result = await response.json();
|
|
692
705
|
if (result.type === "error") {
|
|
@@ -705,12 +718,7 @@ var ReactiveClient = class {
|
|
|
705
718
|
throw cause;
|
|
706
719
|
} finally {
|
|
707
720
|
if ((!state.active || !isCurrent()) && requestIssued) {
|
|
708
|
-
void this.sendUnsubscribe(
|
|
709
|
-
registrationConnectionId,
|
|
710
|
-
state.id,
|
|
711
|
-
generation,
|
|
712
|
-
registrationId
|
|
713
|
-
);
|
|
721
|
+
void this.sendUnsubscribe(registrationConnectionId, state.id, generation, registrationId);
|
|
714
722
|
}
|
|
715
723
|
if (state.registrationPromise === request) {
|
|
716
724
|
state.registrationPromise = void 0;
|
|
@@ -815,15 +823,12 @@ var ReactiveClient = class {
|
|
|
815
823
|
return;
|
|
816
824
|
}
|
|
817
825
|
this.discardStalePatches(state);
|
|
818
|
-
|
|
819
|
-
const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion)
|
|
826
|
+
for (; ; ) {
|
|
827
|
+
const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion).at(0);
|
|
820
828
|
if (!next) break;
|
|
821
829
|
state.patchBuffer.delete(next.messageId);
|
|
822
830
|
try {
|
|
823
|
-
state.authoritativeData = applyPatches(
|
|
824
|
-
state.authoritativeData,
|
|
825
|
-
next.patches
|
|
826
|
-
);
|
|
831
|
+
state.authoritativeData = applyPatches(state.authoritativeData, next.patches);
|
|
827
832
|
state.version = next.toVersion;
|
|
828
833
|
state.appliedMessageIds.add(next.messageId);
|
|
829
834
|
const awaitingReconciliation = this.hasSettledLayers(state);
|
|
@@ -861,12 +866,7 @@ var ReactiveClient = class {
|
|
|
861
866
|
state.stale = true;
|
|
862
867
|
state.loading = true;
|
|
863
868
|
this.notifySubscription(state);
|
|
864
|
-
|
|
865
|
-
request = this.ensureRegistered(
|
|
866
|
-
state,
|
|
867
|
-
this.wsManager.generation,
|
|
868
|
-
true
|
|
869
|
-
).finally(() => {
|
|
869
|
+
const request = this.ensureRegistered(state, this.wsManager.generation, true).finally(() => {
|
|
870
870
|
if (state.resyncPromise === request) {
|
|
871
871
|
state.resyncPromise = void 0;
|
|
872
872
|
state.resyncGeneration = void 0;
|
|
@@ -934,7 +934,10 @@ var ReactiveClient = class {
|
|
|
934
934
|
if (options?.optimisticData === void 0) return void 0;
|
|
935
935
|
return (data) => {
|
|
936
936
|
if (!Array.isArray(data)) return data;
|
|
937
|
-
const optimistic = settled ?
|
|
937
|
+
const optimistic = settled ? (
|
|
938
|
+
// oxlint-disable-next-line typescript/prefer-nullish-coalescing -- null is a valid mutation result.
|
|
939
|
+
result === void 0 ? typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData : result
|
|
940
|
+
) : typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData;
|
|
938
941
|
if (settled && optimistic !== null && typeof optimistic === "object") {
|
|
939
942
|
const optimisticId = optimistic.id;
|
|
940
943
|
if (optimisticId !== void 0 && data.some(
|
|
@@ -949,12 +952,7 @@ var ReactiveClient = class {
|
|
|
949
952
|
settleMutationLayers(mutationId, input, result, options, states) {
|
|
950
953
|
for (const state of states) {
|
|
951
954
|
const layer = state.optimisticLayers.get(mutationId);
|
|
952
|
-
const updater = this.createOptimisticUpdater(
|
|
953
|
-
input,
|
|
954
|
-
options,
|
|
955
|
-
result,
|
|
956
|
-
true
|
|
957
|
-
);
|
|
955
|
+
const updater = this.createOptimisticUpdater(input, options, result, true);
|
|
958
956
|
if (!layer || !updater) continue;
|
|
959
957
|
layer.updater = updater;
|
|
960
958
|
layer.settledRevision = ++state.settlementRevision;
|
|
@@ -1100,9 +1098,7 @@ function ReactiveClientProvider({
|
|
|
1100
1098
|
const state = renderedManaged.client.getConnectionState();
|
|
1101
1099
|
if (state === "connected" || state === "connecting") return;
|
|
1102
1100
|
if (state === "reconnecting") {
|
|
1103
|
-
debug(
|
|
1104
|
-
"[dynamodb-reactive] Tab active during reconnect; reconnecting now"
|
|
1105
|
-
);
|
|
1101
|
+
debug("[dynamodb-reactive] Tab active during reconnect; reconnecting now");
|
|
1106
1102
|
renderedManaged.client.forceReconnect();
|
|
1107
1103
|
} else {
|
|
1108
1104
|
void renderedManaged.client.connect().catch(() => void 0);
|
|
@@ -1120,9 +1116,7 @@ function ReactiveClientProvider({
|
|
|
1120
1116
|
}, [renderedManaged]);
|
|
1121
1117
|
react.useEffect(() => {
|
|
1122
1118
|
if (!config?.url && !externalClient) {
|
|
1123
|
-
warn(
|
|
1124
|
-
"[dynamodb-reactive] WebSocket URL not configured; real-time features disabled"
|
|
1125
|
-
);
|
|
1119
|
+
warn("[dynamodb-reactive] WebSocket URL not configured; real-time features disabled");
|
|
1126
1120
|
}
|
|
1127
1121
|
}, [config?.url, externalClient]);
|
|
1128
1122
|
return /* @__PURE__ */ jsxRuntime.jsx(ReactiveClientContext.Provider, { value: renderedManaged?.client ?? null, children });
|
|
@@ -1170,10 +1164,7 @@ function useSubscription(path, options = {}) {
|
|
|
1170
1164
|
};
|
|
1171
1165
|
const setData = (updater) => {
|
|
1172
1166
|
if (client && subscription) {
|
|
1173
|
-
client.applyOptimisticUpdate(
|
|
1174
|
-
subscription.id,
|
|
1175
|
-
updater
|
|
1176
|
-
);
|
|
1167
|
+
client.applyOptimisticUpdate(subscription.id, updater);
|
|
1177
1168
|
}
|
|
1178
1169
|
};
|
|
1179
1170
|
return {
|