dynamodb-reactive 0.4.0 → 0.6.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 +93 -51
- package/dist/{client-Zb67giI5.d.ts → client-BPAct-Dz.d.ts} +19 -0
- package/dist/client.cjs +68 -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 +68 -55
- package/dist/client.js.map +1 -1
- package/dist/cloudflare.cjs +223 -36
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +78 -6
- package/dist/cloudflare.d.ts +78 -6
- package/dist/cloudflare.js +223 -36
- package/dist/cloudflare.js.map +1 -1
- package/dist/cluster.cjs +81 -66
- package/dist/cluster.cjs.map +1 -1
- package/dist/cluster.js +81 -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 +35 -10
- package/dist/hub.cjs.map +1 -1
- package/dist/hub.d.cts +18 -3
- package/dist/hub.d.ts +18 -3
- package/dist/hub.js +35 -10
- 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 +71 -65
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +71 -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/client.js
CHANGED
|
@@ -23,9 +23,7 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
23
23
|
if (Array.isArray(value)) {
|
|
24
24
|
const items = [];
|
|
25
25
|
for (let index = 0; index < value.length; index++) {
|
|
26
|
-
items.push(
|
|
27
|
-
canonicalize(value[index], seen, String(index), true) ?? "null"
|
|
28
|
-
);
|
|
26
|
+
items.push(canonicalize(value[index], seen, String(index), true) ?? "null");
|
|
29
27
|
}
|
|
30
28
|
return `[${items.join(",")}]`;
|
|
31
29
|
}
|
|
@@ -42,6 +40,9 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
42
40
|
seen.delete(value);
|
|
43
41
|
}
|
|
44
42
|
}
|
|
43
|
+
case "bigint":
|
|
44
|
+
case "function":
|
|
45
|
+
case "symbol":
|
|
45
46
|
default:
|
|
46
47
|
throw new TypeError(`Unsupported query input value: ${typeof value}`);
|
|
47
48
|
}
|
|
@@ -49,7 +50,6 @@ function canonicalize(value, seen, key, arrayItem = false, applyToJSON = true) {
|
|
|
49
50
|
function getQueryKey(path, input) {
|
|
50
51
|
return `${JSON.stringify(path)}:${canonicalize(input, /* @__PURE__ */ new Set(), "") ?? "undefined"}`;
|
|
51
52
|
}
|
|
52
|
-
var { applyPatch } = jsonpatch;
|
|
53
53
|
function applyPatches(document, patches) {
|
|
54
54
|
if (patches.length === 0) {
|
|
55
55
|
return document;
|
|
@@ -68,7 +68,7 @@ function applyPatches(document, patches) {
|
|
|
68
68
|
return op;
|
|
69
69
|
});
|
|
70
70
|
try {
|
|
71
|
-
const result = applyPatch(
|
|
71
|
+
const result = jsonpatch.applyPatch(
|
|
72
72
|
structuredClone(document),
|
|
73
73
|
operations,
|
|
74
74
|
true,
|
|
@@ -88,12 +88,13 @@ function error(...values) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// ../client/src/websocket.ts
|
|
91
|
-
var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
92
91
|
function withDefaults(config) {
|
|
93
92
|
return {
|
|
94
93
|
autoReconnect: true,
|
|
95
94
|
connectionTimeout: 1e4,
|
|
96
95
|
handshakeTimeout: 1e4,
|
|
96
|
+
heartbeatInterval: 5 * 60 * 1e3,
|
|
97
|
+
heartbeatTimeout: 15e3,
|
|
97
98
|
maxReconnectAttempts: 10,
|
|
98
99
|
reconnectDelay: 1e3,
|
|
99
100
|
...config
|
|
@@ -107,6 +108,7 @@ var WebSocketManager = class {
|
|
|
107
108
|
reconnectTimer = null;
|
|
108
109
|
handshakeTimer = null;
|
|
109
110
|
heartbeatTimer = null;
|
|
111
|
+
pongTimer = null;
|
|
110
112
|
messageHandlers = /* @__PURE__ */ new Set();
|
|
111
113
|
stateHandlers = /* @__PURE__ */ new Set();
|
|
112
114
|
handshakeHandlers = /* @__PURE__ */ new Set();
|
|
@@ -206,16 +208,7 @@ var WebSocketManager = class {
|
|
|
206
208
|
};
|
|
207
209
|
nextSocket.onclose = () => {
|
|
208
210
|
if (!this.isCurrent(nextSocket, generation)) return;
|
|
209
|
-
this.
|
|
210
|
-
this.ws = null;
|
|
211
|
-
this._connectionId = null;
|
|
212
|
-
this.setState("disconnected");
|
|
213
|
-
this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
|
|
214
|
-
if (this.config.autoReconnect && !this.intentionalDisconnect) {
|
|
215
|
-
this.scheduleReconnect();
|
|
216
|
-
} else {
|
|
217
|
-
this.rejectWaiters(new Error("WebSocket disconnected"));
|
|
218
|
-
}
|
|
211
|
+
this.handleClose();
|
|
219
212
|
};
|
|
220
213
|
nextSocket.onerror = () => {
|
|
221
214
|
if (!this.isCurrent(nextSocket, generation)) return;
|
|
@@ -274,6 +267,13 @@ var WebSocketManager = class {
|
|
|
274
267
|
this.setState("disconnected");
|
|
275
268
|
void this.connect().catch(() => void 0);
|
|
276
269
|
}
|
|
270
|
+
/** Sends an application frame on the open socket; false when not connected. */
|
|
271
|
+
send(frame) {
|
|
272
|
+
const socket = this.ws;
|
|
273
|
+
if (!socket || this.state !== "connected" || socket.readyState !== socket.OPEN) return false;
|
|
274
|
+
socket.send(JSON.stringify(frame));
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
277
|
onMessage(handler) {
|
|
278
278
|
this.messageHandlers.add(handler);
|
|
279
279
|
return () => this.messageHandlers.delete(handler);
|
|
@@ -304,6 +304,7 @@ var WebSocketManager = class {
|
|
|
304
304
|
return;
|
|
305
305
|
}
|
|
306
306
|
if (message.type === "pong") {
|
|
307
|
+
this.clearPongTimer();
|
|
307
308
|
if (!this._connectionId || this.state === "connected") return;
|
|
308
309
|
this.clearHandshakeTimer();
|
|
309
310
|
this.lastConnectionError = void 0;
|
|
@@ -332,6 +333,18 @@ var WebSocketManager = class {
|
|
|
332
333
|
socket.close(4e3, "Handshake timeout");
|
|
333
334
|
}, this.config.handshakeTimeout);
|
|
334
335
|
}
|
|
336
|
+
handleClose() {
|
|
337
|
+
this.clearConnectionTimers();
|
|
338
|
+
this.ws = null;
|
|
339
|
+
this._connectionId = null;
|
|
340
|
+
this.setState("disconnected");
|
|
341
|
+
this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
|
|
342
|
+
if (this.config.autoReconnect && !this.intentionalDisconnect) {
|
|
343
|
+
this.scheduleReconnect();
|
|
344
|
+
} else {
|
|
345
|
+
this.rejectWaiters(new Error("WebSocket disconnected"));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
335
348
|
startHeartbeat(socket, generation) {
|
|
336
349
|
this.clearHeartbeatTimer();
|
|
337
350
|
this.heartbeatTimer = setInterval(() => {
|
|
@@ -339,7 +352,17 @@ var WebSocketManager = class {
|
|
|
339
352
|
return;
|
|
340
353
|
}
|
|
341
354
|
socket.send(JSON.stringify({ type: "ping" }));
|
|
342
|
-
|
|
355
|
+
this.pongTimer ??= setTimeout(() => {
|
|
356
|
+
this.pongTimer = null;
|
|
357
|
+
if (!this.isCurrent(socket, generation)) return;
|
|
358
|
+
const failure = new Error("WebSocket heartbeat timed out");
|
|
359
|
+
this.lastConnectionError = failure;
|
|
360
|
+
this.invokeLifecycleCallback("onError", this.config.onError, failure);
|
|
361
|
+
socket.onclose = null;
|
|
362
|
+
socket.close(4001, "Heartbeat timeout");
|
|
363
|
+
this.handleClose();
|
|
364
|
+
}, this.config.heartbeatTimeout);
|
|
365
|
+
}, this.config.heartbeatInterval);
|
|
343
366
|
}
|
|
344
367
|
isCurrent(socket, generation) {
|
|
345
368
|
return this.ws === socket && this.socketGeneration === generation;
|
|
@@ -406,9 +429,15 @@ var WebSocketManager = class {
|
|
|
406
429
|
clearInterval(this.heartbeatTimer);
|
|
407
430
|
this.heartbeatTimer = null;
|
|
408
431
|
}
|
|
432
|
+
clearPongTimer() {
|
|
433
|
+
if (!this.pongTimer) return;
|
|
434
|
+
clearTimeout(this.pongTimer);
|
|
435
|
+
this.pongTimer = null;
|
|
436
|
+
}
|
|
409
437
|
clearConnectionTimers() {
|
|
410
438
|
this.clearHandshakeTimer();
|
|
411
439
|
this.clearHeartbeatTimer();
|
|
440
|
+
this.clearPongTimer();
|
|
412
441
|
}
|
|
413
442
|
clearTimers() {
|
|
414
443
|
this.clearConnectionTimers();
|
|
@@ -468,6 +497,14 @@ var ReactiveClient = class {
|
|
|
468
497
|
getConnectionState() {
|
|
469
498
|
return this.connectionState;
|
|
470
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* Sends an application frame over the socket, such as a presence update a
|
|
502
|
+
* hub's `onMessage` handles. Returns false while disconnected; resend from
|
|
503
|
+
* `onConnectionStateChange` once connected again.
|
|
504
|
+
*/
|
|
505
|
+
send(frame) {
|
|
506
|
+
return this.wsManager.send(frame);
|
|
507
|
+
}
|
|
471
508
|
onConnectionStateChange(listener) {
|
|
472
509
|
this.stateListeners.add(listener);
|
|
473
510
|
return () => this.stateListeners.delete(listener);
|
|
@@ -508,10 +545,7 @@ var ReactiveClient = class {
|
|
|
508
545
|
this.subscriptionsByKey.set(key, shared);
|
|
509
546
|
this.subscriptionsById.set(shared.id, shared);
|
|
510
547
|
if (this.connectionState === "connected") {
|
|
511
|
-
void this.ensureRegistered(
|
|
512
|
-
shared,
|
|
513
|
-
this.wsManager.generation
|
|
514
|
-
).catch(() => void 0);
|
|
548
|
+
void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
|
|
515
549
|
}
|
|
516
550
|
}
|
|
517
551
|
const leaseId = generateId();
|
|
@@ -520,9 +554,7 @@ var ReactiveClient = class {
|
|
|
520
554
|
onError: options.onError
|
|
521
555
|
});
|
|
522
556
|
if (this.connectionState === "connected" && (!shared.hasRegistered || shared.stale) && !shared.registrationPromise) {
|
|
523
|
-
void this.ensureRegistered(shared, this.wsManager.generation).catch(
|
|
524
|
-
() => void 0
|
|
525
|
-
);
|
|
557
|
+
void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
|
|
526
558
|
}
|
|
527
559
|
const state = shared;
|
|
528
560
|
let released = false;
|
|
@@ -611,9 +643,7 @@ var ReactiveClient = class {
|
|
|
611
643
|
registerActiveSubscriptions(socketGeneration) {
|
|
612
644
|
for (const state of this.subscriptionsByKey.values()) {
|
|
613
645
|
if (state.active) {
|
|
614
|
-
void this.ensureRegistered(state, socketGeneration).catch(
|
|
615
|
-
() => void 0
|
|
616
|
-
);
|
|
646
|
+
void this.ensureRegistered(state, socketGeneration).catch(() => void 0);
|
|
617
647
|
}
|
|
618
648
|
}
|
|
619
649
|
}
|
|
@@ -666,9 +696,7 @@ var ReactiveClient = class {
|
|
|
666
696
|
})
|
|
667
697
|
});
|
|
668
698
|
if (!response.ok) {
|
|
669
|
-
throw new Error(
|
|
670
|
-
`Subscribe failed: ${response.status} ${response.statusText}`
|
|
671
|
-
);
|
|
699
|
+
throw new Error(`Subscribe failed: ${response.status} ${response.statusText}`);
|
|
672
700
|
}
|
|
673
701
|
const result = await response.json();
|
|
674
702
|
if (result.type === "error") {
|
|
@@ -687,12 +715,7 @@ var ReactiveClient = class {
|
|
|
687
715
|
throw cause;
|
|
688
716
|
} finally {
|
|
689
717
|
if ((!state.active || !isCurrent()) && requestIssued) {
|
|
690
|
-
void this.sendUnsubscribe(
|
|
691
|
-
registrationConnectionId,
|
|
692
|
-
state.id,
|
|
693
|
-
generation,
|
|
694
|
-
registrationId
|
|
695
|
-
);
|
|
718
|
+
void this.sendUnsubscribe(registrationConnectionId, state.id, generation, registrationId);
|
|
696
719
|
}
|
|
697
720
|
if (state.registrationPromise === request) {
|
|
698
721
|
state.registrationPromise = void 0;
|
|
@@ -797,15 +820,12 @@ var ReactiveClient = class {
|
|
|
797
820
|
return;
|
|
798
821
|
}
|
|
799
822
|
this.discardStalePatches(state);
|
|
800
|
-
|
|
801
|
-
const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion)
|
|
823
|
+
for (; ; ) {
|
|
824
|
+
const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion).at(0);
|
|
802
825
|
if (!next) break;
|
|
803
826
|
state.patchBuffer.delete(next.messageId);
|
|
804
827
|
try {
|
|
805
|
-
state.authoritativeData = applyPatches(
|
|
806
|
-
state.authoritativeData,
|
|
807
|
-
next.patches
|
|
808
|
-
);
|
|
828
|
+
state.authoritativeData = applyPatches(state.authoritativeData, next.patches);
|
|
809
829
|
state.version = next.toVersion;
|
|
810
830
|
state.appliedMessageIds.add(next.messageId);
|
|
811
831
|
const awaitingReconciliation = this.hasSettledLayers(state);
|
|
@@ -843,12 +863,7 @@ var ReactiveClient = class {
|
|
|
843
863
|
state.stale = true;
|
|
844
864
|
state.loading = true;
|
|
845
865
|
this.notifySubscription(state);
|
|
846
|
-
|
|
847
|
-
request = this.ensureRegistered(
|
|
848
|
-
state,
|
|
849
|
-
this.wsManager.generation,
|
|
850
|
-
true
|
|
851
|
-
).finally(() => {
|
|
866
|
+
const request = this.ensureRegistered(state, this.wsManager.generation, true).finally(() => {
|
|
852
867
|
if (state.resyncPromise === request) {
|
|
853
868
|
state.resyncPromise = void 0;
|
|
854
869
|
state.resyncGeneration = void 0;
|
|
@@ -916,7 +931,10 @@ var ReactiveClient = class {
|
|
|
916
931
|
if (options?.optimisticData === void 0) return void 0;
|
|
917
932
|
return (data) => {
|
|
918
933
|
if (!Array.isArray(data)) return data;
|
|
919
|
-
const optimistic = settled ?
|
|
934
|
+
const optimistic = settled ? (
|
|
935
|
+
// oxlint-disable-next-line typescript/prefer-nullish-coalescing -- null is a valid mutation result.
|
|
936
|
+
result === void 0 ? typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData : result
|
|
937
|
+
) : typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData;
|
|
920
938
|
if (settled && optimistic !== null && typeof optimistic === "object") {
|
|
921
939
|
const optimisticId = optimistic.id;
|
|
922
940
|
if (optimisticId !== void 0 && data.some(
|
|
@@ -931,12 +949,7 @@ var ReactiveClient = class {
|
|
|
931
949
|
settleMutationLayers(mutationId, input, result, options, states) {
|
|
932
950
|
for (const state of states) {
|
|
933
951
|
const layer = state.optimisticLayers.get(mutationId);
|
|
934
|
-
const updater = this.createOptimisticUpdater(
|
|
935
|
-
input,
|
|
936
|
-
options,
|
|
937
|
-
result,
|
|
938
|
-
true
|
|
939
|
-
);
|
|
952
|
+
const updater = this.createOptimisticUpdater(input, options, result, true);
|
|
940
953
|
if (!layer || !updater) continue;
|
|
941
954
|
layer.updater = updater;
|
|
942
955
|
layer.settledRevision = ++state.settlementRevision;
|