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.
@@ -1396,6 +1396,7 @@ var CrossTabDataBus = class {
1396
1396
  replayMaxPerTopic;
1397
1397
  replayPersistence;
1398
1398
  replayRetentionMs;
1399
+ replayPruneStrategy;
1399
1400
  replayRetentionSweepMs;
1400
1401
  persistenceRetryMaxAttempts;
1401
1402
  persistenceRetryBackoffMs;
@@ -1411,6 +1412,10 @@ var CrossTabDataBus = class {
1411
1412
  trace;
1412
1413
  dedupMaxEntries;
1413
1414
  dedupTtlMs;
1415
+ dedupAdaptiveBounds;
1416
+ dedupWindowStartedAt = 0;
1417
+ dedupWindowAccepted = 0;
1418
+ dedupAdaptiveWindowMs = 5e3;
1414
1419
  dedupSweepMs;
1415
1420
  dedupSweepTimer = null;
1416
1421
  dedupEnabled;
@@ -1469,6 +1474,8 @@ var CrossTabDataBus = class {
1469
1474
  this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
1470
1475
  this.replayPersistence = replay?.persistence ?? null;
1471
1476
  this.replayRetentionMs = replay?.retentionMs;
1477
+ this.replayPruneStrategy = replay?.pruneStrategy ?? "count";
1478
+ if (!["count", "age", "both"].includes(this.replayPruneStrategy)) throw new TypeError("replay.pruneStrategy must be count, age, or both.");
1472
1479
  if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
1473
1480
  throw new TypeError("replay.retentionMs must be a positive finite number.");
1474
1481
  }
@@ -1501,6 +1508,9 @@ var CrossTabDataBus = class {
1501
1508
  this.trace = new DataBusTraceReporter(trace);
1502
1509
  this.dedupMaxEntries = dedup?.maxEntries ?? 1e3;
1503
1510
  this.dedupTtlMs = dedup?.ttlMs ?? 6e4;
1511
+ this.dedupAdaptiveBounds = dedup?.adaptiveTtl;
1512
+ if (this.dedupAdaptiveBounds && (this.dedupAdaptiveBounds.minMs <= 0 || this.dedupAdaptiveBounds.maxMs < this.dedupAdaptiveBounds.minMs)) throw new TypeError("dedup.adaptiveTtl bounds are invalid.");
1513
+ this.dedupWindowStartedAt = this.now();
1504
1514
  this.dedupSweepMs = dedup?.sweepMs;
1505
1515
  this.dedupEnabled = dedup !== void 0;
1506
1516
  if (!Number.isSafeInteger(this.dedupMaxEntries) || this.dedupMaxEntries <= 0) {
@@ -1774,7 +1784,8 @@ var CrossTabDataBus = class {
1774
1784
  enabled: this.dedupEnabled,
1775
1785
  tracked: this.seenMessageIds.size,
1776
1786
  suppressed: this.dedupSuppressed,
1777
- accepted: this.dedupAccepted
1787
+ accepted: this.dedupAccepted,
1788
+ ...this.dedupAdaptiveBounds ? { ttlMs: this.currentDedupTtl() } : {}
1778
1789
  };
1779
1790
  }
1780
1791
  /** Drop all remembered IDs and reset dedup counters. */
@@ -1946,6 +1957,7 @@ var CrossTabDataBus = class {
1946
1957
  }
1947
1958
  this.seenMessageIds.set(message.messageId, now);
1948
1959
  this.dedupAccepted += 1;
1960
+ this.dedupWindowAccepted += 1;
1949
1961
  this.trace.recordDedupAccepted();
1950
1962
  while (this.seenMessageIds.size > this.dedupMaxEntries) {
1951
1963
  const oldest = this.seenMessageIds.keys().next().value;
@@ -1963,11 +1975,24 @@ var CrossTabDataBus = class {
1963
1975
  this.dedupSweepTimer = null;
1964
1976
  }
1965
1977
  pruneExpiredDedup() {
1966
- const cutoff = this.now() - this.dedupTtlMs;
1978
+ const cutoff = this.now() - this.currentDedupTtl();
1967
1979
  for (const [id, timestamp] of this.seenMessageIds) {
1968
1980
  if (timestamp < cutoff) this.seenMessageIds.delete(id);
1969
1981
  }
1970
1982
  }
1983
+ currentDedupTtl() {
1984
+ if (!this.dedupAdaptiveBounds) return this.dedupTtlMs;
1985
+ const now = this.now();
1986
+ const elapsed = now - this.dedupWindowStartedAt;
1987
+ if (elapsed >= this.dedupAdaptiveWindowMs) {
1988
+ this.dedupWindowStartedAt = now;
1989
+ this.dedupWindowAccepted = 0;
1990
+ return this.dedupAdaptiveBounds.maxMs;
1991
+ }
1992
+ const rate = this.dedupWindowAccepted / Math.max(1, elapsed);
1993
+ const factor = Math.min(1, rate / 0.01);
1994
+ return this.dedupAdaptiveBounds.maxMs - (this.dedupAdaptiveBounds.maxMs - this.dedupAdaptiveBounds.minMs) * factor;
1995
+ }
1971
1996
  /** Deliver a message to every local handler registered for its topic,
1972
1997
  * plus every handler registered with a wildcard subscription that matches
1973
1998
  * (e.g. a handler subscribed to "chat.*" receives "chat.room.1"). */
@@ -1992,7 +2017,17 @@ var CrossTabDataBus = class {
1992
2017
  }
1993
2018
  const storedMessage = message;
1994
2019
  buffer.push(storedMessage);
1995
- if (buffer.length > this.replayMaxPerTopic) buffer.shift();
2020
+ if (this.replayPruneStrategy !== "age") {
2021
+ while (buffer.length > this.replayMaxPerTopic) buffer.shift();
2022
+ }
2023
+ if (this.replayPruneStrategy !== "count" && this.replayRetentionMs !== void 0) {
2024
+ const cutoff = this.now() - this.replayRetentionMs;
2025
+ while (buffer.length > 0) {
2026
+ const first = buffer[0];
2027
+ if (!first || first.timestamp === void 0 || first.timestamp >= cutoff) break;
2028
+ buffer.shift();
2029
+ }
2030
+ }
1996
2031
  if (this.replayPersistence) {
1997
2032
  void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
1998
2033
  if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {