cross-tab-worker-databus 0.20.59 → 0.20.61
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 +21 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-RAUX7RZC.js → chunk-VB4P6DS6.js} +123 -12
- package/dist/chunk-VB4P6DS6.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +122 -11
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +122 -11
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/cluster.d.ts +26 -4
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +11 -0
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/types.d.ts +22 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/roadmap.md +12 -1
- package/docs/zh/roadmap.md +12 -1
- package/package.json +1 -1
- package/dist/chunk-RAUX7RZC.js.map +0 -7
package/dist/cjs/index.cjs
CHANGED
|
@@ -613,6 +613,67 @@ var WorkerClusterRuntime = class {
|
|
|
613
613
|
}
|
|
614
614
|
this.wildcardPublishCache.set(topic, null);
|
|
615
615
|
}
|
|
616
|
+
return this.sendControl(this.resolvePublishTarget(topic, topicKey), "PUBLISH", topic, topicKey, data, metadata);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Burst-friendly variant of `publish()`: packs up to N items into a single
|
|
620
|
+
* BroadcastChannel postMessage so the receiving owner dispatches them all in
|
|
621
|
+
* one tick. Per-item dedup / replay / dispatch ordering is preserved; items
|
|
622
|
+
* may carry their own messageId/timestamp. Empty batch is a no-op,
|
|
623
|
+
* single-item batch delegates to `publish()`.
|
|
624
|
+
*/
|
|
625
|
+
publishBatch(topic, items) {
|
|
626
|
+
if (items.length === 0) return true;
|
|
627
|
+
if (items.length === 1) {
|
|
628
|
+
const single = items[0];
|
|
629
|
+
const metadata = single.messageId !== void 0 || single.timestamp !== void 0 ? {
|
|
630
|
+
...single.messageId !== void 0 ? { messageId: single.messageId } : {},
|
|
631
|
+
...single.timestamp !== void 0 ? { timestamp: single.timestamp } : {}
|
|
632
|
+
} : void 0;
|
|
633
|
+
return this.publish(topic, single.data, metadata);
|
|
634
|
+
}
|
|
635
|
+
const topicKey = this.rememberTopic(topic);
|
|
636
|
+
if (this.assignedTopics.has(topicKey)) {
|
|
637
|
+
for (const item of items) this.dispatchLocalPublish(topic, topicKey, item.data, item.messageId, item.timestamp);
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
const cachedPattern = this.wildcardPublishCache.get(topic);
|
|
641
|
+
if (cachedPattern !== void 0 && cachedPattern !== null && this.assignedTopics.has(cachedPattern)) {
|
|
642
|
+
for (const item of items) this.dispatchLocalPublish(topic, topicKey, item.data, item.messageId, item.timestamp);
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
if (cachedPattern === void 0) {
|
|
646
|
+
for (const pattern of this.assignedTopics.values()) {
|
|
647
|
+
if (pattern !== topic && topicMatchesPattern(pattern, topic)) {
|
|
648
|
+
this.wildcardPublishCache.set(topic, pattern);
|
|
649
|
+
for (const item of items) this.dispatchLocalPublish(topic, topicKey, item.data, item.messageId, item.timestamp);
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
this.wildcardPublishCache.set(topic, null);
|
|
654
|
+
}
|
|
655
|
+
const target = this.resolvePublishTarget(topic, topicKey);
|
|
656
|
+
if (target === this.workerId) {
|
|
657
|
+
for (const item of items) this.dispatchLocalPublish(topic, topicKey, item.data, item.messageId, item.timestamp);
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
return this.send({
|
|
661
|
+
type: "CONTROL",
|
|
662
|
+
sourceWorkerId: this.workerId,
|
|
663
|
+
targetWorkerId: target,
|
|
664
|
+
action: "PUBLISH",
|
|
665
|
+
topic,
|
|
666
|
+
topicKey,
|
|
667
|
+
items: items.map((item) => ({
|
|
668
|
+
data: item.data,
|
|
669
|
+
...item.messageId !== void 0 ? { messageId: item.messageId } : {},
|
|
670
|
+
...item.timestamp !== void 0 ? { timestamp: item.timestamp } : {}
|
|
671
|
+
}))
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
/** Resolve which worker should receive a PUBLISH for `topic`. Centralises the
|
|
675
|
+
* route-owner cache lookup so `publish()` and `publishBatch()` share one path. */
|
|
676
|
+
resolvePublishTarget(topic, topicKey) {
|
|
616
677
|
const workers = this.readWorkers();
|
|
617
678
|
const route = this.readRoute(topicKey);
|
|
618
679
|
const cached = this.routeOwnerCache.get(topicKey);
|
|
@@ -620,9 +681,19 @@ var WorkerClusterRuntime = class {
|
|
|
620
681
|
if (cachedLive) this.routeOwnerCacheHits += 1;
|
|
621
682
|
else this.routeOwnerCacheMisses += 1;
|
|
622
683
|
const target = cachedLive ? cached.workerId : this.routeOwnerIsLive(route, workers) ? route?.workerId ?? this.workerId : this.workerId;
|
|
623
|
-
if (route && target === route.workerId)
|
|
624
|
-
|
|
625
|
-
|
|
684
|
+
if (route && target === route.workerId) {
|
|
685
|
+
this.touchRouteOwnerCache(topicKey, { workerId: route.workerId, generation: route.generation });
|
|
686
|
+
} else {
|
|
687
|
+
this.routeOwnerCache.delete(topicKey);
|
|
688
|
+
}
|
|
689
|
+
return target;
|
|
690
|
+
}
|
|
691
|
+
/** Fan out a single batched item to the local onControl path. */
|
|
692
|
+
dispatchLocalPublish(topic, topicKey, data, messageId, timestamp) {
|
|
693
|
+
void topicKey;
|
|
694
|
+
const meta = publicationMetadata(messageId, timestamp);
|
|
695
|
+
if (meta) this.handlers.onControl("PUBLISH", topic, data, meta.messageId, meta.timestamp);
|
|
696
|
+
else this.handlers.onControl("PUBLISH", topic, data);
|
|
626
697
|
}
|
|
627
698
|
/** True when `route` exists and its owner worker is among `workers`.
|
|
628
699
|
* Shared by subscribe (skip re-assignment) and publish (route to owner).
|
|
@@ -631,9 +702,12 @@ var WorkerClusterRuntime = class {
|
|
|
631
702
|
routeOwnerIsLive(route, workers) {
|
|
632
703
|
return Boolean(route && workers.some((worker) => worker.workerId === route.workerId));
|
|
633
704
|
}
|
|
634
|
-
/** Broadcast an event to every tab — used to fan out transport publications.
|
|
635
|
-
|
|
636
|
-
|
|
705
|
+
/** Broadcast an event to every tab — used to fan out transport publications.
|
|
706
|
+
* `originTabId` (when set) is propagated across the BroadcastChannel hop so
|
|
707
|
+
* listeners can attribute the event to its source tab even after fan-out. */
|
|
708
|
+
broadcastEvent(eventType, payload, originTabId) {
|
|
709
|
+
const effectiveOriginTabId = originTabId ?? this.tabId;
|
|
710
|
+
this.send({ type: "EVENT", sourceWorkerId: this.workerId, eventType, payload, originTabId: effectiveOriginTabId });
|
|
637
711
|
}
|
|
638
712
|
isAssigned(topic) {
|
|
639
713
|
const topicKey = createOpaqueKey(topic);
|
|
@@ -721,7 +795,7 @@ var WorkerClusterRuntime = class {
|
|
|
721
795
|
case "ROUTE_RELEASED":
|
|
722
796
|
return this.handleRouteReleasedMessage(message);
|
|
723
797
|
case "EVENT":
|
|
724
|
-
this.handlers.onEvent(message.eventType, message.payload, message.sourceWorkerId);
|
|
798
|
+
this.handlers.onEvent(message.eventType, message.payload, message.sourceWorkerId, message.originTabId);
|
|
725
799
|
return;
|
|
726
800
|
case "REGISTRY":
|
|
727
801
|
default:
|
|
@@ -742,6 +816,15 @@ var WorkerClusterRuntime = class {
|
|
|
742
816
|
if (this.releaseHandoffOnUnsubscribe(message)) return;
|
|
743
817
|
break;
|
|
744
818
|
case "PUBLISH":
|
|
819
|
+
if (message.items && message.items.length > 0) {
|
|
820
|
+
for (const item of message.items) {
|
|
821
|
+
const itemMeta = publicationMetadata(item.messageId, item.timestamp);
|
|
822
|
+
if (itemMeta) this.handlers.onControl("PUBLISH", message.topic, item.data, itemMeta.messageId, itemMeta.timestamp);
|
|
823
|
+
else this.handlers.onControl("PUBLISH", message.topic, item.data);
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
break;
|
|
745
828
|
default:
|
|
746
829
|
break;
|
|
747
830
|
}
|
|
@@ -1464,9 +1547,10 @@ var CrossTabDataBus = class {
|
|
|
1464
1547
|
// typed `unknown` at the cluster boundary (the cluster is transport-
|
|
1465
1548
|
// agnostic); here we narrow it to DataBusMessage — the sender is our
|
|
1466
1549
|
// own broadcastEvent call, which always posts a DataBusMessage.
|
|
1467
|
-
onEvent: (eventType, payload) => {
|
|
1550
|
+
onEvent: (eventType, payload, _sourceWorkerId, originTabId) => {
|
|
1468
1551
|
if (eventType !== PUBLICATION_EVENT) return;
|
|
1469
|
-
const
|
|
1552
|
+
const incoming = payload;
|
|
1553
|
+
const message = incoming.originTabId !== void 0 ? incoming : originTabId !== void 0 ? { ...incoming, originTabId } : incoming;
|
|
1470
1554
|
if (this.cluster.hasLocalSubscriber(message.topic)) this.dispatch(message);
|
|
1471
1555
|
},
|
|
1472
1556
|
onSuspend: () => {
|
|
@@ -1711,6 +1795,32 @@ var CrossTabDataBus = class {
|
|
|
1711
1795
|
);
|
|
1712
1796
|
}
|
|
1713
1797
|
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Burst-friendly variant of `publish()`: delivers `items` in a single
|
|
1800
|
+
* BroadcastChannel postMessage so the receiving owner can dispatch them all
|
|
1801
|
+
* in one tick. Per-item dedup / replay / ordering is preserved; each item
|
|
1802
|
+
* may carry its own `messageId` / `timestamp` via `options`. Empty array is
|
|
1803
|
+
* a no-op; single-item array delegates to `publish()`.
|
|
1804
|
+
*/
|
|
1805
|
+
publishBatch(topic, items) {
|
|
1806
|
+
this.ensureStarted();
|
|
1807
|
+
if (items.length === 0) return;
|
|
1808
|
+
if (items.length === 1) {
|
|
1809
|
+
const first = items[0];
|
|
1810
|
+
this.publish(topic, first.data, first.options);
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
const mapped = items.map((item) => ({
|
|
1814
|
+
data: item.data,
|
|
1815
|
+
...item.options?.messageId !== void 0 ? { messageId: item.options.messageId } : {},
|
|
1816
|
+
...item.options?.timestamp !== void 0 ? { timestamp: item.options.timestamp } : {}
|
|
1817
|
+
}));
|
|
1818
|
+
if (!this.cluster.publishBatch(topic, mapped)) {
|
|
1819
|
+
this.reportError(
|
|
1820
|
+
new Error("Failed to send the batched publish control message to the owning worker.")
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1714
1824
|
/** Register a handler that fires on every transport status change. Immediately invoked with the current status. */
|
|
1715
1825
|
onStatus(handler) {
|
|
1716
1826
|
this.statusHandlers.add(handler);
|
|
@@ -1803,9 +1913,10 @@ var CrossTabDataBus = class {
|
|
|
1803
1913
|
this.trace.recordDiscarded(message.topic);
|
|
1804
1914
|
return;
|
|
1805
1915
|
}
|
|
1806
|
-
this.cluster.
|
|
1916
|
+
const stamped = message.originTabId === void 0 ? { ...message, originTabId: this.cluster.tabId } : message;
|
|
1917
|
+
this.cluster.broadcastEvent(PUBLICATION_EVENT, stamped, stamped.originTabId);
|
|
1807
1918
|
if (this.cluster.hasLocalSubscriber(message.topic)) {
|
|
1808
|
-
this.dispatch(
|
|
1919
|
+
this.dispatch(stamped);
|
|
1809
1920
|
return;
|
|
1810
1921
|
}
|
|
1811
1922
|
this.trace.recordDiscarded(message.topic);
|