cross-tab-worker-databus 0.3.0 → 0.4.0
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 +17 -0
- package/dist/centrifuge.js +19 -3
- package/dist/centrifuge.js.map +2 -2
- package/dist/{chunk-PNYWDVTH.js → chunk-ZGQRELIV.js} +61 -2
- package/dist/{chunk-PNYWDVTH.js.map → chunk-ZGQRELIV.js.map} +3 -3
- package/dist/cjs/centrifuge.cjs +81 -3
- package/dist/cjs/centrifuge.cjs.map +3 -3
- package/dist/cjs/index.cjs +60 -1
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/core/data-bus.d.ts +25 -1
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/types.d.ts +3 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/api.md +1 -0
- package/docs/getting-started.md +2 -0
- package/docs/zh/api.md +1 -0
- package/docs/zh/getting-started.md +2 -0
- package/package.json +3 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -1210,6 +1210,7 @@ function roundMs(value) {
|
|
|
1210
1210
|
|
|
1211
1211
|
// src/core/data-bus.ts
|
|
1212
1212
|
var PUBLICATION_EVENT = "DATABUS_PUBLICATION";
|
|
1213
|
+
var DEFAULT_REPLAY_MAX_PER_TOPIC = 100;
|
|
1213
1214
|
var CrossTabDataBus = class _CrossTabDataBus {
|
|
1214
1215
|
transport;
|
|
1215
1216
|
cluster;
|
|
@@ -1220,6 +1221,10 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1220
1221
|
transportSubscribedTopics = /* @__PURE__ */ new Set();
|
|
1221
1222
|
statusHandlers = /* @__PURE__ */ new Set();
|
|
1222
1223
|
errorHandlers = /* @__PURE__ */ new Set();
|
|
1224
|
+
// Bounded per-topic ring of recent dispatched publications. Null unless
|
|
1225
|
+
// replay is enabled — buffering is opt-in and must cost nothing otherwise.
|
|
1226
|
+
replayBuffers;
|
|
1227
|
+
replayMaxPerTopic;
|
|
1223
1228
|
initialConfig;
|
|
1224
1229
|
hasInitialConfig;
|
|
1225
1230
|
trace;
|
|
@@ -1248,6 +1253,17 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1248
1253
|
// Minimum interval in ms between automatic recovery attempts.
|
|
1249
1254
|
static RECOVERY_COOLDOWN_MS = 1e3;
|
|
1250
1255
|
constructor(options) {
|
|
1256
|
+
const replay = options.replay;
|
|
1257
|
+
if (replay) {
|
|
1258
|
+
const maxPerTopic = replay.maxPerTopic ?? DEFAULT_REPLAY_MAX_PER_TOPIC;
|
|
1259
|
+
if (!Number.isSafeInteger(maxPerTopic) || maxPerTopic <= 0) {
|
|
1260
|
+
throw new TypeError(
|
|
1261
|
+
`replay.maxPerTopic must be a positive safe integer, got ${String(maxPerTopic)}.`
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
this.replayMaxPerTopic = replay?.maxPerTopic ?? DEFAULT_REPLAY_MAX_PER_TOPIC;
|
|
1266
|
+
this.replayBuffers = replay ? /* @__PURE__ */ new Map() : null;
|
|
1251
1267
|
const { autoStart, initialConfig, trace, transport, ...clusterOptions } = options;
|
|
1252
1268
|
this.transport = transport;
|
|
1253
1269
|
this.initialConfig = initialConfig;
|
|
@@ -1404,13 +1420,20 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1404
1420
|
* delivered to this tab, regardless of which tab published it. Returns an
|
|
1405
1421
|
* unsubscribe function for convenience.
|
|
1406
1422
|
*/
|
|
1407
|
-
subscribe(topic, handler) {
|
|
1423
|
+
subscribe(topic, handler, options) {
|
|
1408
1424
|
this.ensureStarted();
|
|
1409
1425
|
const handlers = this.topicHandlers.get(topic) ?? /* @__PURE__ */ new Set();
|
|
1410
1426
|
const wasUnused = handlers.size === 0;
|
|
1411
1427
|
handlers.add(handler);
|
|
1412
1428
|
this.topicHandlers.set(topic, handlers);
|
|
1413
1429
|
if (wasUnused) this.cluster.subscribe(topic);
|
|
1430
|
+
if (options?.replay) {
|
|
1431
|
+
const limit = Math.min(
|
|
1432
|
+
typeof options.replay === "number" ? Math.floor(options.replay) : this.replayMaxPerTopic,
|
|
1433
|
+
this.replayMaxPerTopic
|
|
1434
|
+
);
|
|
1435
|
+
this.deliverReplay(topic, limit, handler);
|
|
1436
|
+
}
|
|
1414
1437
|
return () => this.unsubscribe(topic, handler);
|
|
1415
1438
|
}
|
|
1416
1439
|
/** Remove a specific handler, or all handlers for `topic`.
|
|
@@ -1424,6 +1447,7 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1424
1447
|
else handlers.clear();
|
|
1425
1448
|
if (handlers.size > 0) return;
|
|
1426
1449
|
this.topicHandlers.delete(topic);
|
|
1450
|
+
this.replayBuffers?.delete(topic);
|
|
1427
1451
|
this.cluster.unsubscribe(topic);
|
|
1428
1452
|
}
|
|
1429
1453
|
/** Publish a message to `topic`. The owning Worker delivers it to the transport. */
|
|
@@ -1470,6 +1494,7 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1470
1494
|
this.trace.event({ type: "lifecycle", action: "stop" });
|
|
1471
1495
|
this.trace.stop();
|
|
1472
1496
|
this.topicHandlers.clear();
|
|
1497
|
+
this.replayBuffers?.clear();
|
|
1473
1498
|
this.cluster.stop();
|
|
1474
1499
|
try {
|
|
1475
1500
|
await this.startPromise?.catch(() => void 0);
|
|
@@ -1518,6 +1543,40 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1518
1543
|
this.invokeHandlers(handlers, (handler) => handler(message));
|
|
1519
1544
|
}
|
|
1520
1545
|
}
|
|
1546
|
+
this.recordReplay(message);
|
|
1547
|
+
}
|
|
1548
|
+
/** Append a dispatched publication to the topic's replay ring buffer.
|
|
1549
|
+
* No-op when replay is disabled. */
|
|
1550
|
+
recordReplay(message) {
|
|
1551
|
+
if (!this.replayBuffers) return;
|
|
1552
|
+
let buffer = this.replayBuffers.get(message.topic);
|
|
1553
|
+
if (!buffer) {
|
|
1554
|
+
buffer = [];
|
|
1555
|
+
this.replayBuffers.set(message.topic, buffer);
|
|
1556
|
+
}
|
|
1557
|
+
buffer.push(message);
|
|
1558
|
+
if (buffer.length > this.replayMaxPerTopic) buffer.shift();
|
|
1559
|
+
}
|
|
1560
|
+
/** Deliver buffered history to a newly-registered handler. For an exact
|
|
1561
|
+
* topic this is that topic's ring; for a wildcard subscription every
|
|
1562
|
+
* buffered topic matching the pattern contributes (in buffer insertion
|
|
1563
|
+
* order). Replay deliveries are marked `replayed: true` and are not
|
|
1564
|
+
* counted into trace metrics. */
|
|
1565
|
+
deliverReplay(topic, limit, handler) {
|
|
1566
|
+
if (!this.replayBuffers || limit <= 0) return;
|
|
1567
|
+
const deliver = (buffer2) => {
|
|
1568
|
+
for (const message of buffer2.slice(-limit)) {
|
|
1569
|
+
this.invokeHandlers([handler], (h) => h({ ...message, replayed: true }));
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
if (isWildcardTopic(topic)) {
|
|
1573
|
+
for (const [bufferedTopic, buffer2] of this.replayBuffers) {
|
|
1574
|
+
if (topicMatchesPattern(topic, bufferedTopic)) deliver(buffer2);
|
|
1575
|
+
}
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
const buffer = this.replayBuffers.get(topic);
|
|
1579
|
+
if (buffer) deliver(buffer);
|
|
1521
1580
|
}
|
|
1522
1581
|
/**
|
|
1523
1582
|
* Propagate a status change to the cluster, trace, and all registered
|