cross-tab-worker-databus 0.7.0 → 0.10.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 +28 -1
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/dist/centrifuge-protocol.d.ts +8 -0
- package/dist/centrifuge-protocol.d.ts.map +1 -1
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.d.ts +2 -1
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +31 -10
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +38 -4
- package/dist/centrifuge.shared.worker.js.map +3 -3
- package/dist/centrifuge.worker.js +38 -4
- package/dist/centrifuge.worker.js.map +3 -3
- package/dist/{chunk-SI7N5KNJ.js → chunk-ZOPNTR4E.js} +128 -22
- package/dist/chunk-ZOPNTR4E.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +157 -30
- package/dist/cjs/centrifuge.cjs.map +4 -4
- package/dist/cjs/index.cjs +166 -28
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/core/cluster.d.ts +4 -3
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +20 -2
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/publication.d.ts +10 -0
- package/dist/core/publication.d.ts.map +1 -0
- package/dist/core/replay-persistence.d.ts +2 -0
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/trace.d.ts +2 -11
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/types.d.ts +25 -6
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -8
- package/dist/index.js.map +2 -2
- package/dist/websocket.d.ts +2 -2
- package/dist/websocket.d.ts.map +1 -1
- package/docs/api.md +35 -4
- package/docs/architecture.md +3 -1
- package/docs/transports.md +9 -5
- package/docs/zh/api.md +35 -4
- package/docs/zh/architecture.md +3 -1
- package/docs/zh/transports.md +8 -4
- package/package.json +1 -1
- package/dist/chunk-SI7N5KNJ.js.map +0 -7
|
@@ -298,6 +298,13 @@ function writeJson(storage, key, value) {
|
|
|
298
298
|
} catch {
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
+
function publicationMetadata(messageId, timestamp) {
|
|
302
|
+
if (messageId === void 0 && timestamp === void 0) return void 0;
|
|
303
|
+
return {
|
|
304
|
+
...messageId === void 0 ? {} : { messageId },
|
|
305
|
+
...timestamp === void 0 ? {} : { timestamp }
|
|
306
|
+
};
|
|
307
|
+
}
|
|
301
308
|
function listKeys(storage, prefix) {
|
|
302
309
|
try {
|
|
303
310
|
return Array.from({ length: storage.length }, (_, index) => storage.key(index)).filter(
|
|
@@ -527,26 +534,21 @@ var WorkerClusterRuntime = class {
|
|
|
527
534
|
this.sendRouteReleased(owner.workerId, topic, topicKey, generation);
|
|
528
535
|
}
|
|
529
536
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
* no owner is found). Returns false when the control message could not be
|
|
533
|
-
* posted to a remote owner, so the caller can surface the failure instead of
|
|
534
|
-
* silently dropping the publication.
|
|
535
|
-
*/
|
|
536
|
-
publish(topic, data) {
|
|
537
|
+
publish(topic, data, metadataOrMessageId) {
|
|
538
|
+
const metadata = typeof metadataOrMessageId === "string" ? { messageId: metadataOrMessageId } : metadataOrMessageId;
|
|
537
539
|
const topicKey = this.rememberTopic(topic);
|
|
538
540
|
if (this.assignedTopics.has(topicKey)) {
|
|
539
|
-
return this.sendControl(this.workerId, "PUBLISH", topic, topicKey, data);
|
|
541
|
+
return this.sendControl(this.workerId, "PUBLISH", topic, topicKey, data, metadata);
|
|
540
542
|
}
|
|
541
543
|
for (const pattern of this.assignedTopics.values()) {
|
|
542
544
|
if (pattern !== topic && topicMatchesPattern(pattern, topic)) {
|
|
543
|
-
return this.sendControl(this.workerId, "PUBLISH", topic, topicKey, data);
|
|
545
|
+
return this.sendControl(this.workerId, "PUBLISH", topic, topicKey, data, metadata);
|
|
544
546
|
}
|
|
545
547
|
}
|
|
546
548
|
const workers = this.readWorkers();
|
|
547
549
|
const route = this.readRoute(topicKey);
|
|
548
550
|
const target = this.routeOwnerIsLive(route, workers) ? route?.workerId ?? this.workerId : this.workerId;
|
|
549
|
-
return this.sendControl(target, "PUBLISH", topic, topicKey, data);
|
|
551
|
+
return this.sendControl(target, "PUBLISH", topic, topicKey, data, metadata);
|
|
550
552
|
}
|
|
551
553
|
/** True when `route` exists and its owner worker is among `workers`.
|
|
552
554
|
* Shared by subscribe (skip re-assignment) and publish (route to owner).
|
|
@@ -668,7 +670,15 @@ var WorkerClusterRuntime = class {
|
|
|
668
670
|
default:
|
|
669
671
|
break;
|
|
670
672
|
}
|
|
671
|
-
|
|
673
|
+
const metadata = publicationMetadata(message.messageId, message.timestamp);
|
|
674
|
+
if (metadata) this.handlers.onControl(
|
|
675
|
+
message.action,
|
|
676
|
+
message.topic,
|
|
677
|
+
message.data,
|
|
678
|
+
metadata.messageId,
|
|
679
|
+
metadata.timestamp
|
|
680
|
+
);
|
|
681
|
+
else this.handlers.onControl(message.action, message.topic, message.data);
|
|
672
682
|
if (message.action !== "PUBLISH") this.updateLoad();
|
|
673
683
|
}
|
|
674
684
|
/**
|
|
@@ -785,7 +795,7 @@ var WorkerClusterRuntime = class {
|
|
|
785
795
|
* Local execution updates the assignment map and route synchronously, bypassing
|
|
786
796
|
* the BroadcastChannel latency.
|
|
787
797
|
*/
|
|
788
|
-
sendControl(targetWorkerId, action, topic, topicKey, data) {
|
|
798
|
+
sendControl(targetWorkerId, action, topic, topicKey, data, metadata) {
|
|
789
799
|
if (targetWorkerId === this.workerId) {
|
|
790
800
|
switch (action) {
|
|
791
801
|
case "SUBSCRIBE":
|
|
@@ -799,7 +809,14 @@ var WorkerClusterRuntime = class {
|
|
|
799
809
|
default:
|
|
800
810
|
break;
|
|
801
811
|
}
|
|
802
|
-
this.handlers.onControl(
|
|
812
|
+
if (metadata) this.handlers.onControl(
|
|
813
|
+
action,
|
|
814
|
+
topic,
|
|
815
|
+
data,
|
|
816
|
+
metadata.messageId,
|
|
817
|
+
metadata.timestamp
|
|
818
|
+
);
|
|
819
|
+
else this.handlers.onControl(action, topic, data);
|
|
803
820
|
if (action !== "PUBLISH") this.updateLoad();
|
|
804
821
|
return true;
|
|
805
822
|
}
|
|
@@ -810,7 +827,9 @@ var WorkerClusterRuntime = class {
|
|
|
810
827
|
action,
|
|
811
828
|
topic,
|
|
812
829
|
topicKey,
|
|
813
|
-
...data === void 0 ? {} : { data }
|
|
830
|
+
...data === void 0 ? {} : { data },
|
|
831
|
+
...metadata?.messageId === void 0 ? {} : { messageId: metadata.messageId },
|
|
832
|
+
...metadata?.timestamp === void 0 ? {} : { timestamp: metadata.timestamp }
|
|
814
833
|
});
|
|
815
834
|
}
|
|
816
835
|
/** Post a message on the BroadcastChannel. Returns false on postMessage failure. */
|
|
@@ -1205,6 +1224,8 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1205
1224
|
dedupTtlMs;
|
|
1206
1225
|
dedupEnabled;
|
|
1207
1226
|
seenMessageIds = /* @__PURE__ */ new Map();
|
|
1227
|
+
dedupSuppressed = 0;
|
|
1228
|
+
dedupAccepted = 0;
|
|
1208
1229
|
activeConfig;
|
|
1209
1230
|
status = "disconnected";
|
|
1210
1231
|
started = false;
|
|
@@ -1262,7 +1283,7 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1262
1283
|
handlers: {
|
|
1263
1284
|
// The cluster calls `onControl` when it receives a SUBSCRIBE/UNSUBSCRIBE/PUBLISH
|
|
1264
1285
|
// control message — meaning the owning Worker has delegated the action to us.
|
|
1265
|
-
onControl: (action, topic, data) => {
|
|
1286
|
+
onControl: (action, topic, data, messageId, timestamp) => {
|
|
1266
1287
|
switch (action) {
|
|
1267
1288
|
case "SUBSCRIBE":
|
|
1268
1289
|
if (this.subscribeTransport(topic)) this.traceSubscription("subscribe", topic);
|
|
@@ -1271,7 +1292,14 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1271
1292
|
if (this.unsubscribeTransport(topic)) this.traceSubscription("unsubscribe", topic);
|
|
1272
1293
|
break;
|
|
1273
1294
|
case "PUBLISH":
|
|
1274
|
-
this.runTransport(() => this.transport.publish(
|
|
1295
|
+
this.runTransport(() => this.transport.publish(
|
|
1296
|
+
topic,
|
|
1297
|
+
data,
|
|
1298
|
+
messageId === void 0 && timestamp === void 0 ? void 0 : {
|
|
1299
|
+
...messageId === void 0 ? {} : { messageId },
|
|
1300
|
+
...timestamp === void 0 ? {} : { timestamp }
|
|
1301
|
+
}
|
|
1302
|
+
));
|
|
1275
1303
|
break;
|
|
1276
1304
|
default:
|
|
1277
1305
|
break;
|
|
@@ -1450,10 +1478,61 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1450
1478
|
}
|
|
1451
1479
|
this.cluster.unsubscribe(topic);
|
|
1452
1480
|
}
|
|
1481
|
+
/** Clear all in-memory replay buffers and, when supported, durable history. */
|
|
1482
|
+
async clearReplay() {
|
|
1483
|
+
this.replayBuffers?.clear();
|
|
1484
|
+
if (this.replayPersistence?.clear) {
|
|
1485
|
+
try {
|
|
1486
|
+
await this.replayPersistence.clear();
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
this.reportError(error);
|
|
1489
|
+
throw error;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
/** Clear replay history for one exact topic, including durable storage. */
|
|
1494
|
+
async clearReplayTopic(topic) {
|
|
1495
|
+
this.replayBuffers?.delete(topic);
|
|
1496
|
+
if (this.replayPersistence?.clearTopic) {
|
|
1497
|
+
try {
|
|
1498
|
+
await this.replayPersistence.clearTopic(topic);
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
this.reportError(error);
|
|
1501
|
+
throw error;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
/** Remove replay entries older than an epoch-millisecond cutoff. */
|
|
1506
|
+
async clearReplayBefore(timestamp) {
|
|
1507
|
+
if (!Number.isFinite(timestamp)) throw new TypeError("timestamp must be finite.");
|
|
1508
|
+
if (this.replayBuffers) {
|
|
1509
|
+
for (const [topic, messages] of this.replayBuffers) {
|
|
1510
|
+
const kept = messages.filter((message) => (message.timestamp ?? 0) >= timestamp);
|
|
1511
|
+
if (kept.length) this.replayBuffers.set(topic, kept);
|
|
1512
|
+
else this.replayBuffers.delete(topic);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (this.replayPersistence?.clearBefore) await this.replayPersistence.clearBefore(timestamp);
|
|
1516
|
+
}
|
|
1517
|
+
/** Return bounded deduplication counters for diagnostics and health checks. */
|
|
1518
|
+
getDedupStats() {
|
|
1519
|
+
return {
|
|
1520
|
+
enabled: this.dedupEnabled,
|
|
1521
|
+
tracked: this.seenMessageIds.size,
|
|
1522
|
+
suppressed: this.dedupSuppressed,
|
|
1523
|
+
accepted: this.dedupAccepted
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
/** Drop all remembered IDs and reset dedup counters. */
|
|
1527
|
+
resetDedup() {
|
|
1528
|
+
this.seenMessageIds.clear();
|
|
1529
|
+
this.dedupSuppressed = 0;
|
|
1530
|
+
this.dedupAccepted = 0;
|
|
1531
|
+
}
|
|
1453
1532
|
/** Publish a message to `topic`. The owning Worker delivers it to the transport. */
|
|
1454
|
-
publish(topic, data) {
|
|
1533
|
+
publish(topic, data, options) {
|
|
1455
1534
|
this.ensureStarted();
|
|
1456
|
-
if (!this.cluster.publish(topic, data)) {
|
|
1535
|
+
if (!this.cluster.publish(topic, data, options)) {
|
|
1457
1536
|
this.reportError(
|
|
1458
1537
|
new Error("Failed to send the publish control message to the owning worker.")
|
|
1459
1538
|
);
|
|
@@ -1540,8 +1619,13 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1540
1619
|
for (const [id, timestamp] of this.seenMessageIds) {
|
|
1541
1620
|
if (now - timestamp > this.dedupTtlMs) this.seenMessageIds.delete(id);
|
|
1542
1621
|
}
|
|
1543
|
-
if (this.seenMessageIds.has(message.messageId))
|
|
1622
|
+
if (this.seenMessageIds.has(message.messageId)) {
|
|
1623
|
+
this.trace.event({ type: "reliability", operation: "dedup_suppressed", topic: message.topic });
|
|
1624
|
+
this.dedupSuppressed += 1;
|
|
1625
|
+
return true;
|
|
1626
|
+
}
|
|
1544
1627
|
this.seenMessageIds.set(message.messageId, now);
|
|
1628
|
+
this.dedupAccepted += 1;
|
|
1545
1629
|
while (this.seenMessageIds.size > this.dedupMaxEntries) {
|
|
1546
1630
|
const oldest = this.seenMessageIds.keys().next().value;
|
|
1547
1631
|
if (oldest === void 0) break;
|
|
@@ -1571,10 +1655,11 @@ var CrossTabDataBus = class _CrossTabDataBus {
|
|
|
1571
1655
|
buffer = [];
|
|
1572
1656
|
this.replayBuffers.set(message.topic, buffer);
|
|
1573
1657
|
}
|
|
1574
|
-
|
|
1658
|
+
const storedMessage = message;
|
|
1659
|
+
buffer.push(storedMessage);
|
|
1575
1660
|
if (buffer.length > this.replayMaxPerTopic) buffer.shift();
|
|
1576
1661
|
if (this.replayPersistence) {
|
|
1577
|
-
void this.replayPersistence.append(
|
|
1662
|
+
void this.replayPersistence.append(storedMessage).catch((error) => this.reportError(error));
|
|
1578
1663
|
}
|
|
1579
1664
|
}
|
|
1580
1665
|
async hydrateReplay() {
|
|
@@ -1799,6 +1884,26 @@ function selectWorkerBackend(mode, availability = {}) {
|
|
|
1799
1884
|
return hasDedicated ? "dedicated" : hasShared ? "shared" : "local";
|
|
1800
1885
|
}
|
|
1801
1886
|
|
|
1887
|
+
// src/core/publication.ts
|
|
1888
|
+
function parseDataBusPublication(value, fallbackTopic) {
|
|
1889
|
+
if (!value || typeof value !== "object") {
|
|
1890
|
+
return fallbackTopic ? { topic: fallbackTopic, data: value } : null;
|
|
1891
|
+
}
|
|
1892
|
+
const frame = value;
|
|
1893
|
+
const nested = frame.publication && typeof frame.publication === "object" ? frame.publication : null;
|
|
1894
|
+
const publication = nested ?? frame;
|
|
1895
|
+
const topic = typeof publication.topic === "string" ? publication.topic : fallbackTopic;
|
|
1896
|
+
if (!topic) return null;
|
|
1897
|
+
const hasMetadataEnvelope = fallbackTopic !== void 0 && Object.prototype.hasOwnProperty.call(publication, "data") && (typeof publication.messageId === "string" || typeof publication.timestamp === "number");
|
|
1898
|
+
const data = nested || fallbackTopic === void 0 || hasMetadataEnvelope ? publication.data : value;
|
|
1899
|
+
return {
|
|
1900
|
+
topic,
|
|
1901
|
+
data,
|
|
1902
|
+
...typeof publication.messageId === "string" ? { messageId: publication.messageId } : {},
|
|
1903
|
+
...typeof publication.timestamp === "number" ? { timestamp: publication.timestamp } : {}
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1802
1907
|
export {
|
|
1803
1908
|
createBrowserEnvironment,
|
|
1804
1909
|
getOrCreateTabId,
|
|
@@ -1812,6 +1917,7 @@ export {
|
|
|
1812
1917
|
topicMatchesPattern,
|
|
1813
1918
|
WorkerClusterRuntime,
|
|
1814
1919
|
CrossTabDataBus,
|
|
1920
|
+
parseDataBusPublication,
|
|
1815
1921
|
selectWorkerBackend
|
|
1816
1922
|
};
|
|
1817
|
-
//# sourceMappingURL=chunk-
|
|
1923
|
+
//# sourceMappingURL=chunk-ZOPNTR4E.js.map
|