cross-tab-worker-databus 0.20.64 → 0.20.65

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.
@@ -1421,6 +1421,7 @@ var CrossTabDataBus = class {
1421
1421
  replayMaxPerTopic;
1422
1422
  replayPersistence;
1423
1423
  replayRetentionMs;
1424
+ replayPruneStrategy;
1424
1425
  replayRetentionSweepMs;
1425
1426
  persistenceRetryMaxAttempts;
1426
1427
  persistenceRetryBackoffMs;
@@ -1436,6 +1437,10 @@ var CrossTabDataBus = class {
1436
1437
  trace;
1437
1438
  dedupMaxEntries;
1438
1439
  dedupTtlMs;
1440
+ dedupAdaptiveBounds;
1441
+ dedupWindowStartedAt = 0;
1442
+ dedupWindowAccepted = 0;
1443
+ dedupAdaptiveWindowMs = 5e3;
1439
1444
  dedupSweepMs;
1440
1445
  dedupSweepTimer = null;
1441
1446
  dedupEnabled;
@@ -1494,6 +1499,8 @@ var CrossTabDataBus = class {
1494
1499
  this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
1495
1500
  this.replayPersistence = replay?.persistence ?? null;
1496
1501
  this.replayRetentionMs = replay?.retentionMs;
1502
+ this.replayPruneStrategy = replay?.pruneStrategy ?? "count";
1503
+ if (!["count", "age", "both"].includes(this.replayPruneStrategy)) throw new TypeError("replay.pruneStrategy must be count, age, or both.");
1497
1504
  if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
1498
1505
  throw new TypeError("replay.retentionMs must be a positive finite number.");
1499
1506
  }
@@ -1526,6 +1533,9 @@ var CrossTabDataBus = class {
1526
1533
  this.trace = new DataBusTraceReporter(trace);
1527
1534
  this.dedupMaxEntries = dedup?.maxEntries ?? 1e3;
1528
1535
  this.dedupTtlMs = dedup?.ttlMs ?? 6e4;
1536
+ this.dedupAdaptiveBounds = dedup?.adaptiveTtl;
1537
+ if (this.dedupAdaptiveBounds && (this.dedupAdaptiveBounds.minMs <= 0 || this.dedupAdaptiveBounds.maxMs < this.dedupAdaptiveBounds.minMs)) throw new TypeError("dedup.adaptiveTtl bounds are invalid.");
1538
+ this.dedupWindowStartedAt = this.now();
1529
1539
  this.dedupSweepMs = dedup?.sweepMs;
1530
1540
  this.dedupEnabled = dedup !== void 0;
1531
1541
  if (!Number.isSafeInteger(this.dedupMaxEntries) || this.dedupMaxEntries <= 0) {
@@ -1799,7 +1809,8 @@ var CrossTabDataBus = class {
1799
1809
  enabled: this.dedupEnabled,
1800
1810
  tracked: this.seenMessageIds.size,
1801
1811
  suppressed: this.dedupSuppressed,
1802
- accepted: this.dedupAccepted
1812
+ accepted: this.dedupAccepted,
1813
+ ...this.dedupAdaptiveBounds ? { ttlMs: this.currentDedupTtl() } : {}
1803
1814
  };
1804
1815
  }
1805
1816
  /** Drop all remembered IDs and reset dedup counters. */
@@ -1971,6 +1982,7 @@ var CrossTabDataBus = class {
1971
1982
  }
1972
1983
  this.seenMessageIds.set(message.messageId, now);
1973
1984
  this.dedupAccepted += 1;
1985
+ this.dedupWindowAccepted += 1;
1974
1986
  this.trace.recordDedupAccepted();
1975
1987
  while (this.seenMessageIds.size > this.dedupMaxEntries) {
1976
1988
  const oldest = this.seenMessageIds.keys().next().value;
@@ -1988,11 +2000,24 @@ var CrossTabDataBus = class {
1988
2000
  this.dedupSweepTimer = null;
1989
2001
  }
1990
2002
  pruneExpiredDedup() {
1991
- const cutoff = this.now() - this.dedupTtlMs;
2003
+ const cutoff = this.now() - this.currentDedupTtl();
1992
2004
  for (const [id, timestamp] of this.seenMessageIds) {
1993
2005
  if (timestamp < cutoff) this.seenMessageIds.delete(id);
1994
2006
  }
1995
2007
  }
2008
+ currentDedupTtl() {
2009
+ if (!this.dedupAdaptiveBounds) return this.dedupTtlMs;
2010
+ const now = this.now();
2011
+ const elapsed = now - this.dedupWindowStartedAt;
2012
+ if (elapsed >= this.dedupAdaptiveWindowMs) {
2013
+ this.dedupWindowStartedAt = now;
2014
+ this.dedupWindowAccepted = 0;
2015
+ return this.dedupAdaptiveBounds.maxMs;
2016
+ }
2017
+ const rate = this.dedupWindowAccepted / Math.max(1, elapsed);
2018
+ const factor = Math.min(1, rate / 0.01);
2019
+ return this.dedupAdaptiveBounds.maxMs - (this.dedupAdaptiveBounds.maxMs - this.dedupAdaptiveBounds.minMs) * factor;
2020
+ }
1996
2021
  /** Deliver a message to every local handler registered for its topic,
1997
2022
  * plus every handler registered with a wildcard subscription that matches
1998
2023
  * (e.g. a handler subscribed to "chat.*" receives "chat.room.1"). */
@@ -2017,7 +2042,17 @@ var CrossTabDataBus = class {
2017
2042
  }
2018
2043
  const storedMessage = message;
2019
2044
  buffer.push(storedMessage);
2020
- if (buffer.length > this.replayMaxPerTopic) buffer.shift();
2045
+ if (this.replayPruneStrategy !== "age") {
2046
+ while (buffer.length > this.replayMaxPerTopic) buffer.shift();
2047
+ }
2048
+ if (this.replayPruneStrategy !== "count" && this.replayRetentionMs !== void 0) {
2049
+ const cutoff = this.now() - this.replayRetentionMs;
2050
+ while (buffer.length > 0) {
2051
+ const first = buffer[0];
2052
+ if (!first || first.timestamp === void 0 || first.timestamp >= cutoff) break;
2053
+ buffer.shift();
2054
+ }
2055
+ }
2021
2056
  if (this.replayPersistence) {
2022
2057
  void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
2023
2058
  if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {