cross-tab-worker-databus 0.20.63 → 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.
- package/CHANGELOG.md +12 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-ZHCGYGL3.js → chunk-KKSZ5GUC.js} +59 -4
- package/dist/{chunk-ZHCGYGL3.js.map → chunk-KKSZ5GUC.js.map} +2 -2
- package/dist/cjs/centrifuge.cjs +58 -3
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +58 -3
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/data-bus.d.ts +14 -0
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/trace.d.ts +6 -0
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/roadmap.md +9 -1
- package/docs/zh/roadmap.md +9 -1
- package/package.json +1 -1
package/dist/cjs/centrifuge.cjs
CHANGED
|
@@ -1174,6 +1174,9 @@ var DataBusTraceReporter = class {
|
|
|
1174
1174
|
metricsIntervalMs;
|
|
1175
1175
|
sink;
|
|
1176
1176
|
now;
|
|
1177
|
+
asyncSink;
|
|
1178
|
+
pendingEvents = [];
|
|
1179
|
+
sinkFlushScheduled = false;
|
|
1177
1180
|
intervalHandle = null;
|
|
1178
1181
|
intervalStartedAt = 0;
|
|
1179
1182
|
// A reporter may be flushed explicitly before start(), but once stop() is
|
|
@@ -1195,6 +1198,7 @@ var DataBusTraceReporter = class {
|
|
|
1195
1198
|
this.mode = options?.mode ?? "all";
|
|
1196
1199
|
this.metricsIntervalMs = normalizeInterval(options?.metricsIntervalMs);
|
|
1197
1200
|
this.sink = options?.sink ?? (() => void 0);
|
|
1201
|
+
this.asyncSink = options?.asyncSink ?? false;
|
|
1198
1202
|
this.now = now;
|
|
1199
1203
|
}
|
|
1200
1204
|
/** Start the periodic metrics flush interval. No-op when mode is 'events'
|
|
@@ -1321,6 +1325,22 @@ var DataBusTraceReporter = class {
|
|
|
1321
1325
|
this.dedupSuppressed = 0;
|
|
1322
1326
|
}
|
|
1323
1327
|
emit(event) {
|
|
1328
|
+
if (this.asyncSink) {
|
|
1329
|
+
this.pendingEvents.push(event);
|
|
1330
|
+
if (!this.sinkFlushScheduled) {
|
|
1331
|
+
this.sinkFlushScheduled = true;
|
|
1332
|
+
queueMicrotask(() => {
|
|
1333
|
+
this.sinkFlushScheduled = false;
|
|
1334
|
+
const events = this.pendingEvents;
|
|
1335
|
+
this.pendingEvents = [];
|
|
1336
|
+
for (const queued of events) this.emitSync(queued);
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
this.emitSync(event);
|
|
1342
|
+
}
|
|
1343
|
+
emitSync(event) {
|
|
1324
1344
|
try {
|
|
1325
1345
|
this.sink(event);
|
|
1326
1346
|
} catch (error) {
|
|
@@ -1376,6 +1396,7 @@ var CrossTabDataBus = class {
|
|
|
1376
1396
|
replayMaxPerTopic;
|
|
1377
1397
|
replayPersistence;
|
|
1378
1398
|
replayRetentionMs;
|
|
1399
|
+
replayPruneStrategy;
|
|
1379
1400
|
replayRetentionSweepMs;
|
|
1380
1401
|
persistenceRetryMaxAttempts;
|
|
1381
1402
|
persistenceRetryBackoffMs;
|
|
@@ -1391,6 +1412,10 @@ var CrossTabDataBus = class {
|
|
|
1391
1412
|
trace;
|
|
1392
1413
|
dedupMaxEntries;
|
|
1393
1414
|
dedupTtlMs;
|
|
1415
|
+
dedupAdaptiveBounds;
|
|
1416
|
+
dedupWindowStartedAt = 0;
|
|
1417
|
+
dedupWindowAccepted = 0;
|
|
1418
|
+
dedupAdaptiveWindowMs = 5e3;
|
|
1394
1419
|
dedupSweepMs;
|
|
1395
1420
|
dedupSweepTimer = null;
|
|
1396
1421
|
dedupEnabled;
|
|
@@ -1449,6 +1474,8 @@ var CrossTabDataBus = class {
|
|
|
1449
1474
|
this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
|
|
1450
1475
|
this.replayPersistence = replay?.persistence ?? null;
|
|
1451
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.");
|
|
1452
1479
|
if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
|
|
1453
1480
|
throw new TypeError("replay.retentionMs must be a positive finite number.");
|
|
1454
1481
|
}
|
|
@@ -1481,6 +1508,9 @@ var CrossTabDataBus = class {
|
|
|
1481
1508
|
this.trace = new DataBusTraceReporter(trace);
|
|
1482
1509
|
this.dedupMaxEntries = dedup?.maxEntries ?? 1e3;
|
|
1483
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();
|
|
1484
1514
|
this.dedupSweepMs = dedup?.sweepMs;
|
|
1485
1515
|
this.dedupEnabled = dedup !== void 0;
|
|
1486
1516
|
if (!Number.isSafeInteger(this.dedupMaxEntries) || this.dedupMaxEntries <= 0) {
|
|
@@ -1754,7 +1784,8 @@ var CrossTabDataBus = class {
|
|
|
1754
1784
|
enabled: this.dedupEnabled,
|
|
1755
1785
|
tracked: this.seenMessageIds.size,
|
|
1756
1786
|
suppressed: this.dedupSuppressed,
|
|
1757
|
-
accepted: this.dedupAccepted
|
|
1787
|
+
accepted: this.dedupAccepted,
|
|
1788
|
+
...this.dedupAdaptiveBounds ? { ttlMs: this.currentDedupTtl() } : {}
|
|
1758
1789
|
};
|
|
1759
1790
|
}
|
|
1760
1791
|
/** Drop all remembered IDs and reset dedup counters. */
|
|
@@ -1926,6 +1957,7 @@ var CrossTabDataBus = class {
|
|
|
1926
1957
|
}
|
|
1927
1958
|
this.seenMessageIds.set(message.messageId, now);
|
|
1928
1959
|
this.dedupAccepted += 1;
|
|
1960
|
+
this.dedupWindowAccepted += 1;
|
|
1929
1961
|
this.trace.recordDedupAccepted();
|
|
1930
1962
|
while (this.seenMessageIds.size > this.dedupMaxEntries) {
|
|
1931
1963
|
const oldest = this.seenMessageIds.keys().next().value;
|
|
@@ -1943,11 +1975,24 @@ var CrossTabDataBus = class {
|
|
|
1943
1975
|
this.dedupSweepTimer = null;
|
|
1944
1976
|
}
|
|
1945
1977
|
pruneExpiredDedup() {
|
|
1946
|
-
const cutoff = this.now() - this.
|
|
1978
|
+
const cutoff = this.now() - this.currentDedupTtl();
|
|
1947
1979
|
for (const [id, timestamp] of this.seenMessageIds) {
|
|
1948
1980
|
if (timestamp < cutoff) this.seenMessageIds.delete(id);
|
|
1949
1981
|
}
|
|
1950
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
|
+
}
|
|
1951
1996
|
/** Deliver a message to every local handler registered for its topic,
|
|
1952
1997
|
* plus every handler registered with a wildcard subscription that matches
|
|
1953
1998
|
* (e.g. a handler subscribed to "chat.*" receives "chat.room.1"). */
|
|
@@ -1972,7 +2017,17 @@ var CrossTabDataBus = class {
|
|
|
1972
2017
|
}
|
|
1973
2018
|
const storedMessage = message;
|
|
1974
2019
|
buffer.push(storedMessage);
|
|
1975
|
-
if (
|
|
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
|
+
}
|
|
1976
2031
|
if (this.replayPersistence) {
|
|
1977
2032
|
void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
|
|
1978
2033
|
if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
|