cross-tab-worker-databus 0.20.64 → 0.20.66

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.20.66] - 2026-09-05
4
+
5
+ ### Added
6
+ - Extended IndexedDB replay persistence with optional `pruneStrategy` (`count`, `age`, `both`) and `retentionMs`, applying the same trimming semantics as in-memory replay.
7
+
8
+ ## [0.20.65] - 2026-09-05
9
+
10
+ ### Added
11
+ - Added opt-in adaptive dedup TTL sampling with bounded `minMs`/`maxMs` controls and diagnostics exposure.
12
+ - Added replay `pruneStrategy` configuration (`count`, `age`, `both`) for memory history trimming while preserving legacy defaults.
13
+
3
14
  ## [0.20.64] - 2026-09-05
4
15
 
5
16
  ### Added
@@ -2,7 +2,7 @@ import {
2
2
  CrossTabDataBus,
3
3
  parseDataBusPublication,
4
4
  selectWorkerBackend
5
- } from "./chunk-5CP7GNU6.js";
5
+ } from "./chunk-KKSZ5GUC.js";
6
6
 
7
7
  // src/centrifuge-session.ts
8
8
  import { Centrifuge } from "centrifuge";
@@ -1380,6 +1380,7 @@ var CrossTabDataBus = class {
1380
1380
  replayMaxPerTopic;
1381
1381
  replayPersistence;
1382
1382
  replayRetentionMs;
1383
+ replayPruneStrategy;
1383
1384
  replayRetentionSweepMs;
1384
1385
  persistenceRetryMaxAttempts;
1385
1386
  persistenceRetryBackoffMs;
@@ -1395,6 +1396,10 @@ var CrossTabDataBus = class {
1395
1396
  trace;
1396
1397
  dedupMaxEntries;
1397
1398
  dedupTtlMs;
1399
+ dedupAdaptiveBounds;
1400
+ dedupWindowStartedAt = 0;
1401
+ dedupWindowAccepted = 0;
1402
+ dedupAdaptiveWindowMs = 5e3;
1398
1403
  dedupSweepMs;
1399
1404
  dedupSweepTimer = null;
1400
1405
  dedupEnabled;
@@ -1453,6 +1458,8 @@ var CrossTabDataBus = class {
1453
1458
  this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
1454
1459
  this.replayPersistence = replay?.persistence ?? null;
1455
1460
  this.replayRetentionMs = replay?.retentionMs;
1461
+ this.replayPruneStrategy = replay?.pruneStrategy ?? "count";
1462
+ if (!["count", "age", "both"].includes(this.replayPruneStrategy)) throw new TypeError("replay.pruneStrategy must be count, age, or both.");
1456
1463
  if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
1457
1464
  throw new TypeError("replay.retentionMs must be a positive finite number.");
1458
1465
  }
@@ -1485,6 +1492,9 @@ var CrossTabDataBus = class {
1485
1492
  this.trace = new DataBusTraceReporter(trace);
1486
1493
  this.dedupMaxEntries = dedup?.maxEntries ?? 1e3;
1487
1494
  this.dedupTtlMs = dedup?.ttlMs ?? 6e4;
1495
+ this.dedupAdaptiveBounds = dedup?.adaptiveTtl;
1496
+ if (this.dedupAdaptiveBounds && (this.dedupAdaptiveBounds.minMs <= 0 || this.dedupAdaptiveBounds.maxMs < this.dedupAdaptiveBounds.minMs)) throw new TypeError("dedup.adaptiveTtl bounds are invalid.");
1497
+ this.dedupWindowStartedAt = this.now();
1488
1498
  this.dedupSweepMs = dedup?.sweepMs;
1489
1499
  this.dedupEnabled = dedup !== void 0;
1490
1500
  if (!Number.isSafeInteger(this.dedupMaxEntries) || this.dedupMaxEntries <= 0) {
@@ -1758,7 +1768,8 @@ var CrossTabDataBus = class {
1758
1768
  enabled: this.dedupEnabled,
1759
1769
  tracked: this.seenMessageIds.size,
1760
1770
  suppressed: this.dedupSuppressed,
1761
- accepted: this.dedupAccepted
1771
+ accepted: this.dedupAccepted,
1772
+ ...this.dedupAdaptiveBounds ? { ttlMs: this.currentDedupTtl() } : {}
1762
1773
  };
1763
1774
  }
1764
1775
  /** Drop all remembered IDs and reset dedup counters. */
@@ -1930,6 +1941,7 @@ var CrossTabDataBus = class {
1930
1941
  }
1931
1942
  this.seenMessageIds.set(message.messageId, now);
1932
1943
  this.dedupAccepted += 1;
1944
+ this.dedupWindowAccepted += 1;
1933
1945
  this.trace.recordDedupAccepted();
1934
1946
  while (this.seenMessageIds.size > this.dedupMaxEntries) {
1935
1947
  const oldest = this.seenMessageIds.keys().next().value;
@@ -1947,11 +1959,24 @@ var CrossTabDataBus = class {
1947
1959
  this.dedupSweepTimer = null;
1948
1960
  }
1949
1961
  pruneExpiredDedup() {
1950
- const cutoff = this.now() - this.dedupTtlMs;
1962
+ const cutoff = this.now() - this.currentDedupTtl();
1951
1963
  for (const [id, timestamp] of this.seenMessageIds) {
1952
1964
  if (timestamp < cutoff) this.seenMessageIds.delete(id);
1953
1965
  }
1954
1966
  }
1967
+ currentDedupTtl() {
1968
+ if (!this.dedupAdaptiveBounds) return this.dedupTtlMs;
1969
+ const now = this.now();
1970
+ const elapsed = now - this.dedupWindowStartedAt;
1971
+ if (elapsed >= this.dedupAdaptiveWindowMs) {
1972
+ this.dedupWindowStartedAt = now;
1973
+ this.dedupWindowAccepted = 0;
1974
+ return this.dedupAdaptiveBounds.maxMs;
1975
+ }
1976
+ const rate = this.dedupWindowAccepted / Math.max(1, elapsed);
1977
+ const factor = Math.min(1, rate / 0.01);
1978
+ return this.dedupAdaptiveBounds.maxMs - (this.dedupAdaptiveBounds.maxMs - this.dedupAdaptiveBounds.minMs) * factor;
1979
+ }
1955
1980
  /** Deliver a message to every local handler registered for its topic,
1956
1981
  * plus every handler registered with a wildcard subscription that matches
1957
1982
  * (e.g. a handler subscribed to "chat.*" receives "chat.room.1"). */
@@ -1976,7 +2001,17 @@ var CrossTabDataBus = class {
1976
2001
  }
1977
2002
  const storedMessage = message;
1978
2003
  buffer.push(storedMessage);
1979
- if (buffer.length > this.replayMaxPerTopic) buffer.shift();
2004
+ if (this.replayPruneStrategy !== "age") {
2005
+ while (buffer.length > this.replayMaxPerTopic) buffer.shift();
2006
+ }
2007
+ if (this.replayPruneStrategy !== "count" && this.replayRetentionMs !== void 0) {
2008
+ const cutoff = this.now() - this.replayRetentionMs;
2009
+ while (buffer.length > 0) {
2010
+ const first = buffer[0];
2011
+ if (!first || first.timestamp === void 0 || first.timestamp >= cutoff) break;
2012
+ buffer.shift();
2013
+ }
2014
+ }
1980
2015
  if (this.replayPersistence) {
1981
2016
  void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
1982
2017
  if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
@@ -2329,4 +2364,4 @@ export {
2329
2364
  parseDataBusPublication,
2330
2365
  selectWorkerBackend
2331
2366
  };
2332
- //# sourceMappingURL=chunk-5CP7GNU6.js.map
2367
+ //# sourceMappingURL=chunk-KKSZ5GUC.js.map