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/index.cjs
CHANGED
|
@@ -1199,6 +1199,9 @@ var DataBusTraceReporter = class {
|
|
|
1199
1199
|
metricsIntervalMs;
|
|
1200
1200
|
sink;
|
|
1201
1201
|
now;
|
|
1202
|
+
asyncSink;
|
|
1203
|
+
pendingEvents = [];
|
|
1204
|
+
sinkFlushScheduled = false;
|
|
1202
1205
|
intervalHandle = null;
|
|
1203
1206
|
intervalStartedAt = 0;
|
|
1204
1207
|
// A reporter may be flushed explicitly before start(), but once stop() is
|
|
@@ -1220,6 +1223,7 @@ var DataBusTraceReporter = class {
|
|
|
1220
1223
|
this.mode = options?.mode ?? "all";
|
|
1221
1224
|
this.metricsIntervalMs = normalizeInterval(options?.metricsIntervalMs);
|
|
1222
1225
|
this.sink = options?.sink ?? (() => void 0);
|
|
1226
|
+
this.asyncSink = options?.asyncSink ?? false;
|
|
1223
1227
|
this.now = now;
|
|
1224
1228
|
}
|
|
1225
1229
|
/** Start the periodic metrics flush interval. No-op when mode is 'events'
|
|
@@ -1346,6 +1350,22 @@ var DataBusTraceReporter = class {
|
|
|
1346
1350
|
this.dedupSuppressed = 0;
|
|
1347
1351
|
}
|
|
1348
1352
|
emit(event) {
|
|
1353
|
+
if (this.asyncSink) {
|
|
1354
|
+
this.pendingEvents.push(event);
|
|
1355
|
+
if (!this.sinkFlushScheduled) {
|
|
1356
|
+
this.sinkFlushScheduled = true;
|
|
1357
|
+
queueMicrotask(() => {
|
|
1358
|
+
this.sinkFlushScheduled = false;
|
|
1359
|
+
const events = this.pendingEvents;
|
|
1360
|
+
this.pendingEvents = [];
|
|
1361
|
+
for (const queued of events) this.emitSync(queued);
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
this.emitSync(event);
|
|
1367
|
+
}
|
|
1368
|
+
emitSync(event) {
|
|
1349
1369
|
try {
|
|
1350
1370
|
this.sink(event);
|
|
1351
1371
|
} catch (error) {
|
|
@@ -1401,6 +1421,7 @@ var CrossTabDataBus = class {
|
|
|
1401
1421
|
replayMaxPerTopic;
|
|
1402
1422
|
replayPersistence;
|
|
1403
1423
|
replayRetentionMs;
|
|
1424
|
+
replayPruneStrategy;
|
|
1404
1425
|
replayRetentionSweepMs;
|
|
1405
1426
|
persistenceRetryMaxAttempts;
|
|
1406
1427
|
persistenceRetryBackoffMs;
|
|
@@ -1416,6 +1437,10 @@ var CrossTabDataBus = class {
|
|
|
1416
1437
|
trace;
|
|
1417
1438
|
dedupMaxEntries;
|
|
1418
1439
|
dedupTtlMs;
|
|
1440
|
+
dedupAdaptiveBounds;
|
|
1441
|
+
dedupWindowStartedAt = 0;
|
|
1442
|
+
dedupWindowAccepted = 0;
|
|
1443
|
+
dedupAdaptiveWindowMs = 5e3;
|
|
1419
1444
|
dedupSweepMs;
|
|
1420
1445
|
dedupSweepTimer = null;
|
|
1421
1446
|
dedupEnabled;
|
|
@@ -1474,6 +1499,8 @@ var CrossTabDataBus = class {
|
|
|
1474
1499
|
this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
|
|
1475
1500
|
this.replayPersistence = replay?.persistence ?? null;
|
|
1476
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.");
|
|
1477
1504
|
if (this.replayRetentionMs !== void 0 && (!Number.isFinite(this.replayRetentionMs) || this.replayRetentionMs <= 0)) {
|
|
1478
1505
|
throw new TypeError("replay.retentionMs must be a positive finite number.");
|
|
1479
1506
|
}
|
|
@@ -1506,6 +1533,9 @@ var CrossTabDataBus = class {
|
|
|
1506
1533
|
this.trace = new DataBusTraceReporter(trace);
|
|
1507
1534
|
this.dedupMaxEntries = dedup?.maxEntries ?? 1e3;
|
|
1508
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();
|
|
1509
1539
|
this.dedupSweepMs = dedup?.sweepMs;
|
|
1510
1540
|
this.dedupEnabled = dedup !== void 0;
|
|
1511
1541
|
if (!Number.isSafeInteger(this.dedupMaxEntries) || this.dedupMaxEntries <= 0) {
|
|
@@ -1779,7 +1809,8 @@ var CrossTabDataBus = class {
|
|
|
1779
1809
|
enabled: this.dedupEnabled,
|
|
1780
1810
|
tracked: this.seenMessageIds.size,
|
|
1781
1811
|
suppressed: this.dedupSuppressed,
|
|
1782
|
-
accepted: this.dedupAccepted
|
|
1812
|
+
accepted: this.dedupAccepted,
|
|
1813
|
+
...this.dedupAdaptiveBounds ? { ttlMs: this.currentDedupTtl() } : {}
|
|
1783
1814
|
};
|
|
1784
1815
|
}
|
|
1785
1816
|
/** Drop all remembered IDs and reset dedup counters. */
|
|
@@ -1951,6 +1982,7 @@ var CrossTabDataBus = class {
|
|
|
1951
1982
|
}
|
|
1952
1983
|
this.seenMessageIds.set(message.messageId, now);
|
|
1953
1984
|
this.dedupAccepted += 1;
|
|
1985
|
+
this.dedupWindowAccepted += 1;
|
|
1954
1986
|
this.trace.recordDedupAccepted();
|
|
1955
1987
|
while (this.seenMessageIds.size > this.dedupMaxEntries) {
|
|
1956
1988
|
const oldest = this.seenMessageIds.keys().next().value;
|
|
@@ -1968,11 +2000,24 @@ var CrossTabDataBus = class {
|
|
|
1968
2000
|
this.dedupSweepTimer = null;
|
|
1969
2001
|
}
|
|
1970
2002
|
pruneExpiredDedup() {
|
|
1971
|
-
const cutoff = this.now() - this.
|
|
2003
|
+
const cutoff = this.now() - this.currentDedupTtl();
|
|
1972
2004
|
for (const [id, timestamp] of this.seenMessageIds) {
|
|
1973
2005
|
if (timestamp < cutoff) this.seenMessageIds.delete(id);
|
|
1974
2006
|
}
|
|
1975
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
|
+
}
|
|
1976
2021
|
/** Deliver a message to every local handler registered for its topic,
|
|
1977
2022
|
* plus every handler registered with a wildcard subscription that matches
|
|
1978
2023
|
* (e.g. a handler subscribed to "chat.*" receives "chat.room.1"). */
|
|
@@ -1997,7 +2042,17 @@ var CrossTabDataBus = class {
|
|
|
1997
2042
|
}
|
|
1998
2043
|
const storedMessage = message;
|
|
1999
2044
|
buffer.push(storedMessage);
|
|
2000
|
-
if (
|
|
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
|
+
}
|
|
2001
2056
|
if (this.replayPersistence) {
|
|
2002
2057
|
void this.withPersistenceRetry("append", () => this.replayPersistence.append(storedMessage)).catch((error) => this.reportPersistenceError(error));
|
|
2003
2058
|
if (this.replayRetentionMs !== void 0 && this.replayPersistence.clearBefore) {
|