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/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.clearConnectionTimers();
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;
@@ -304,6 +297,7 @@ var WebSocketManager = class {
304
297
  return;
305
298
  }
306
299
  if (message.type === "pong") {
300
+ this.clearPongTimer();
307
301
  if (!this._connectionId || this.state === "connected") return;
308
302
  this.clearHandshakeTimer();
309
303
  this.lastConnectionError = void 0;
@@ -332,6 +326,18 @@ var WebSocketManager = class {
332
326
  socket.close(4e3, "Handshake timeout");
333
327
  }, this.config.handshakeTimeout);
334
328
  }
329
+ handleClose() {
330
+ this.clearConnectionTimers();
331
+ this.ws = null;
332
+ this._connectionId = null;
333
+ this.setState("disconnected");
334
+ this.invokeLifecycleCallback("onDisconnect", this.config.onDisconnect);
335
+ if (this.config.autoReconnect && !this.intentionalDisconnect) {
336
+ this.scheduleReconnect();
337
+ } else {
338
+ this.rejectWaiters(new Error("WebSocket disconnected"));
339
+ }
340
+ }
335
341
  startHeartbeat(socket, generation) {
336
342
  this.clearHeartbeatTimer();
337
343
  this.heartbeatTimer = setInterval(() => {
@@ -339,7 +345,17 @@ var WebSocketManager = class {
339
345
  return;
340
346
  }
341
347
  socket.send(JSON.stringify({ type: "ping" }));
342
- }, HEARTBEAT_INTERVAL_MS);
348
+ this.pongTimer ??= setTimeout(() => {
349
+ this.pongTimer = null;
350
+ if (!this.isCurrent(socket, generation)) return;
351
+ const failure = new Error("WebSocket heartbeat timed out");
352
+ this.lastConnectionError = failure;
353
+ this.invokeLifecycleCallback("onError", this.config.onError, failure);
354
+ socket.onclose = null;
355
+ socket.close(4001, "Heartbeat timeout");
356
+ this.handleClose();
357
+ }, this.config.heartbeatTimeout);
358
+ }, this.config.heartbeatInterval);
343
359
  }
344
360
  isCurrent(socket, generation) {
345
361
  return this.ws === socket && this.socketGeneration === generation;
@@ -406,9 +422,15 @@ var WebSocketManager = class {
406
422
  clearInterval(this.heartbeatTimer);
407
423
  this.heartbeatTimer = null;
408
424
  }
425
+ clearPongTimer() {
426
+ if (!this.pongTimer) return;
427
+ clearTimeout(this.pongTimer);
428
+ this.pongTimer = null;
429
+ }
409
430
  clearConnectionTimers() {
410
431
  this.clearHandshakeTimer();
411
432
  this.clearHeartbeatTimer();
433
+ this.clearPongTimer();
412
434
  }
413
435
  clearTimers() {
414
436
  this.clearConnectionTimers();
@@ -508,10 +530,7 @@ var ReactiveClient = class {
508
530
  this.subscriptionsByKey.set(key, shared);
509
531
  this.subscriptionsById.set(shared.id, shared);
510
532
  if (this.connectionState === "connected") {
511
- void this.ensureRegistered(
512
- shared,
513
- this.wsManager.generation
514
- ).catch(() => void 0);
533
+ void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
515
534
  }
516
535
  }
517
536
  const leaseId = generateId();
@@ -520,9 +539,7 @@ var ReactiveClient = class {
520
539
  onError: options.onError
521
540
  });
522
541
  if (this.connectionState === "connected" && (!shared.hasRegistered || shared.stale) && !shared.registrationPromise) {
523
- void this.ensureRegistered(shared, this.wsManager.generation).catch(
524
- () => void 0
525
- );
542
+ void this.ensureRegistered(shared, this.wsManager.generation).catch(() => void 0);
526
543
  }
527
544
  const state = shared;
528
545
  let released = false;
@@ -611,9 +628,7 @@ var ReactiveClient = class {
611
628
  registerActiveSubscriptions(socketGeneration) {
612
629
  for (const state of this.subscriptionsByKey.values()) {
613
630
  if (state.active) {
614
- void this.ensureRegistered(state, socketGeneration).catch(
615
- () => void 0
616
- );
631
+ void this.ensureRegistered(state, socketGeneration).catch(() => void 0);
617
632
  }
618
633
  }
619
634
  }
@@ -666,9 +681,7 @@ var ReactiveClient = class {
666
681
  })
667
682
  });
668
683
  if (!response.ok) {
669
- throw new Error(
670
- `Subscribe failed: ${response.status} ${response.statusText}`
671
- );
684
+ throw new Error(`Subscribe failed: ${response.status} ${response.statusText}`);
672
685
  }
673
686
  const result = await response.json();
674
687
  if (result.type === "error") {
@@ -687,12 +700,7 @@ var ReactiveClient = class {
687
700
  throw cause;
688
701
  } finally {
689
702
  if ((!state.active || !isCurrent()) && requestIssued) {
690
- void this.sendUnsubscribe(
691
- registrationConnectionId,
692
- state.id,
693
- generation,
694
- registrationId
695
- );
703
+ void this.sendUnsubscribe(registrationConnectionId, state.id, generation, registrationId);
696
704
  }
697
705
  if (state.registrationPromise === request) {
698
706
  state.registrationPromise = void 0;
@@ -797,15 +805,12 @@ var ReactiveClient = class {
797
805
  return;
798
806
  }
799
807
  this.discardStalePatches(state);
800
- while (true) {
801
- const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion)[0];
808
+ for (; ; ) {
809
+ const next = [...state.patchBuffer.values()].filter((patch) => patch.fromVersion === state.version).sort((left, right) => left.toVersion - right.toVersion).at(0);
802
810
  if (!next) break;
803
811
  state.patchBuffer.delete(next.messageId);
804
812
  try {
805
- state.authoritativeData = applyPatches(
806
- state.authoritativeData,
807
- next.patches
808
- );
813
+ state.authoritativeData = applyPatches(state.authoritativeData, next.patches);
809
814
  state.version = next.toVersion;
810
815
  state.appliedMessageIds.add(next.messageId);
811
816
  const awaitingReconciliation = this.hasSettledLayers(state);
@@ -843,12 +848,7 @@ var ReactiveClient = class {
843
848
  state.stale = true;
844
849
  state.loading = true;
845
850
  this.notifySubscription(state);
846
- let request;
847
- request = this.ensureRegistered(
848
- state,
849
- this.wsManager.generation,
850
- true
851
- ).finally(() => {
851
+ const request = this.ensureRegistered(state, this.wsManager.generation, true).finally(() => {
852
852
  if (state.resyncPromise === request) {
853
853
  state.resyncPromise = void 0;
854
854
  state.resyncGeneration = void 0;
@@ -916,7 +916,10 @@ var ReactiveClient = class {
916
916
  if (options?.optimisticData === void 0) return void 0;
917
917
  return (data) => {
918
918
  if (!Array.isArray(data)) return data;
919
- 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;
919
+ const optimistic = settled ? (
920
+ // oxlint-disable-next-line typescript/prefer-nullish-coalescing -- null is a valid mutation result.
921
+ result === void 0 ? typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData : result
922
+ ) : typeof options.optimisticData === "function" ? options.optimisticData(input, data) : options.optimisticData;
920
923
  if (settled && optimistic !== null && typeof optimistic === "object") {
921
924
  const optimisticId = optimistic.id;
922
925
  if (optimisticId !== void 0 && data.some(
@@ -931,12 +934,7 @@ var ReactiveClient = class {
931
934
  settleMutationLayers(mutationId, input, result, options, states) {
932
935
  for (const state of states) {
933
936
  const layer = state.optimisticLayers.get(mutationId);
934
- const updater = this.createOptimisticUpdater(
935
- input,
936
- options,
937
- result,
938
- true
939
- );
937
+ const updater = this.createOptimisticUpdater(input, options, result, true);
940
938
  if (!layer || !updater) continue;
941
939
  layer.updater = updater;
942
940
  layer.settledRevision = ++state.settlementRevision;