cross-tab-worker-databus 0.21.12 → 0.21.13
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 +16 -0
- package/dist/centrifuge-protocol.d.ts.map +1 -1
- package/dist/centrifuge-session.d.ts +10 -3
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.d.ts +10 -4
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +23 -11
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +28 -11
- package/dist/centrifuge.shared.worker.js.map +2 -2
- package/dist/centrifuge.worker.js +10 -3
- package/dist/centrifuge.worker.js.map +2 -2
- package/dist/{chunk-TSTV3SDC.js → chunk-4HW6PYIR.js} +67 -33
- package/dist/chunk-4HW6PYIR.js.map +7 -0
- package/dist/{chunk-2TTJ7LGZ.js → chunk-NPFTD7YE.js} +1 -1
- package/dist/chunk-NPFTD7YE.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +86 -40
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/hooks.cjs.map +2 -2
- package/dist/cjs/index.cjs +80 -36
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/cjs/vue.cjs.map +2 -2
- package/dist/core/cluster.d.ts +33 -16
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +19 -5
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/dedup-manager.d.ts +6 -3
- package/dist/core/dedup-manager.d.ts.map +1 -1
- package/dist/core/environment.d.ts +14 -5
- package/dist/core/environment.d.ts.map +1 -1
- package/dist/core/hash.d.ts +10 -4
- package/dist/core/hash.d.ts.map +1 -1
- package/dist/core/replay-manager.d.ts +2 -1
- package/dist/core/replay-manager.d.ts.map +1 -1
- package/dist/core/routing.d.ts +18 -9
- package/dist/core/routing.d.ts.map +1 -1
- package/dist/core/storage-batch.d.ts +2 -2
- package/dist/core/storage-batch.d.ts.map +1 -1
- package/dist/core/trace.d.ts +21 -9
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/types.d.ts +13 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +1 -1
- package/dist/hooks.js.map +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +2 -2
- package/dist/utils/constants.d.ts +7 -4
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +12 -4
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/vue.js +1 -1
- package/dist/websocket.d.ts +24 -6
- package/dist/websocket.d.ts.map +1 -1
- package/dist/workers/port-reaper.d.ts +18 -8
- package/dist/workers/port-reaper.d.ts.map +1 -1
- package/docs/api.md +6 -5
- package/docs/benchmarks.md +9 -9
- package/docs/release-checklist.md +3 -3
- package/docs/roadmap.md +31 -1
- package/docs/transports.md +3 -2
- package/docs/zh/api.md +6 -5
- package/docs/zh/benchmarks.md +9 -9
- package/docs/zh/release-checklist.md +3 -3
- package/docs/zh/roadmap.md +22 -1
- package/docs/zh/transports.md +2 -1
- package/package.json +2 -1
- package/dist/chunk-2TTJ7LGZ.js.map +0 -7
- package/dist/chunk-TSTV3SDC.js.map +0 -7
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
WORKER_MODE,
|
|
24
24
|
WORKER_ROLE,
|
|
25
25
|
WORKER_STATUS
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-NPFTD7YE.js";
|
|
27
27
|
|
|
28
28
|
// src/core/environment.ts
|
|
29
29
|
function getStorage(name) {
|
|
@@ -479,8 +479,8 @@ var BatchingStorageWriter = class {
|
|
|
479
479
|
retryHandle = null;
|
|
480
480
|
retryDelayMs = INITIAL_RETRY_DELAY_MS;
|
|
481
481
|
/** Number of writes queued in memory but not yet flushed to storage.
|
|
482
|
-
*
|
|
483
|
-
* the
|
|
482
|
+
* Test-only as a reader: `flush()` tests `pending.size` directly rather than
|
|
483
|
+
* going through this getter, so it is not part of the drain decision. */
|
|
484
484
|
get pendingSize() {
|
|
485
485
|
return this.pending.size;
|
|
486
486
|
}
|
|
@@ -654,8 +654,9 @@ var WorkerClusterRuntime = class {
|
|
|
654
654
|
// Topics this tab has subscribed to (local interest, plaintext).
|
|
655
655
|
subscribedTopics = /* @__PURE__ */ new Set();
|
|
656
656
|
// Topics assigned to this Worker as owner (topicKey → topic). Authoritative:
|
|
657
|
-
// membership drives isAssigned() and load.
|
|
658
|
-
//
|
|
657
|
+
// membership drives isAssigned() and load. Grown by exactly three writers —
|
|
658
|
+
// CONTROL/SUBSCRIBE, ROUTE_RELEASED (completing a handoff this worker accepted),
|
|
659
|
+
// and local self-subscribe — and never by the reverse cache.
|
|
659
660
|
assignedTopics = /* @__PURE__ */ new Map();
|
|
660
661
|
routeOwnerCache = /* @__PURE__ */ new Map();
|
|
661
662
|
routeOwnerCacheMax;
|
|
@@ -1049,9 +1050,12 @@ var WorkerClusterRuntime = class {
|
|
|
1049
1050
|
windowMs,
|
|
1050
1051
|
messageCount: this.throughputWindow.messageCount,
|
|
1051
1052
|
byteCount: this.throughputWindow.byteCount,
|
|
1052
|
-
// How much later the
|
|
1053
|
-
// window is anchored at the previous writeRecord
|
|
1054
|
-
//
|
|
1053
|
+
// How much later the last record write landed relative to a nominal
|
|
1054
|
+
// heartbeat interval. The window is anchored at the previous writeRecord,
|
|
1055
|
+
// which the heartbeat tick causes but activation, a status change, a
|
|
1056
|
+
// visibility change and a load update also cause — so a worker writing on
|
|
1057
|
+
// any of those shorter paths yields a small window and a clamped zero.
|
|
1058
|
+
// A starved event loop stretches the window past the interval, and the
|
|
1055
1059
|
// positive excess is the scheduling-overrun signal.
|
|
1056
1060
|
overrunMs: Math.max(0, windowMs - this.heartbeatIntervalMs),
|
|
1057
1061
|
sampledAt: now
|
|
@@ -1216,11 +1220,13 @@ var WorkerClusterRuntime = class {
|
|
|
1216
1220
|
else this.handlers.onControl(message.action, message.topic, message.data);
|
|
1217
1221
|
if (message.action !== CONTROL_ACTION.PUBLISH) this.updateLoad();
|
|
1218
1222
|
}
|
|
1219
|
-
/**
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1223
|
-
* handoff
|
|
1223
|
+
/** Drop this worker's ownership of the topic, then — only when this worker is
|
|
1224
|
+
* the previous owner in a graceful handoff and the new owner is asking us to
|
|
1225
|
+
* unsubscribe — release the old transport subscription and ACK the handoff with
|
|
1226
|
+
* ROUTE_RELEASED. The ownership delete runs for *every* CONTROL/UNSUBSCRIBE,
|
|
1227
|
+
* before the handoff test; the handoff legs below do not gate it. Returns true
|
|
1228
|
+
* when the message was a handoff release (the generic CONTROL dispatch must not
|
|
1229
|
+
* run as well).
|
|
1224
1230
|
*/
|
|
1225
1231
|
releaseHandoffOnUnsubscribe(message) {
|
|
1226
1232
|
this.assignedTopics.delete(message.topicKey);
|
|
@@ -1442,7 +1448,10 @@ var WorkerClusterRuntime = class {
|
|
|
1442
1448
|
}
|
|
1443
1449
|
return Array.from(subscribers);
|
|
1444
1450
|
}
|
|
1445
|
-
/** Read the current route for `topicKey
|
|
1451
|
+
/** Read the current route for `topicKey`. With a storage layer this returns
|
|
1452
|
+
* the durable route, or null when none is recorded. Without one (degraded
|
|
1453
|
+
* mode) it returns the synthesized local route from `buildLocalRoute`, which
|
|
1454
|
+
* is itself null unless this worker actually holds the topic. */
|
|
1446
1455
|
readRoute(topicKey) {
|
|
1447
1456
|
if (!this.storage) return this.buildLocalRoute(topicKey);
|
|
1448
1457
|
return readJson(this.storage, this.routeStorageKey(topicKey));
|
|
@@ -1523,8 +1532,9 @@ var WorkerClusterRuntime = class {
|
|
|
1523
1532
|
* @param notify — when true, broadcast a REGISTRY nudge so peers reconcile
|
|
1524
1533
|
* immediately instead of waiting for the next heartbeat. False on the
|
|
1525
1534
|
* periodic heartbeat tick (peers will notice on their own heartbeat) to
|
|
1526
|
-
* avoid a REGISTRY storm every 3 s
|
|
1527
|
-
*
|
|
1535
|
+
* avoid a REGISTRY storm every 3 s. Which sites pass what: activation,
|
|
1536
|
+
* status, visibility and load changes pass true; the heartbeat tick and a
|
|
1537
|
+
* role change pass false. */
|
|
1528
1538
|
writeRecord(notify) {
|
|
1529
1539
|
const now = this.environment.now();
|
|
1530
1540
|
const sample = this.loadWeighting !== void 0 ? this.sampleThroughput(now) : void 0;
|
|
@@ -1560,10 +1570,15 @@ var WorkerClusterRuntime = class {
|
|
|
1560
1570
|
* Despite the name, this is NOT a cache lookup — it unconditionally writes
|
|
1561
1571
|
* the `topicKey → topic` pair. Hashing is cheap enough that a caller needing
|
|
1562
1572
|
* the key should always call this rather than check `knownTopics` first;
|
|
1563
|
-
* the cache's FIFO eviction below keeps it bounded.
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1573
|
+
* the cache's FIFO eviction below keeps it bounded. Two sites deliberately
|
|
1574
|
+
* bypass it, and for the same kind of reason — neither may write a plaintext
|
|
1575
|
+
* into the reverse cache before it is authorized: `isAssigned` re-derives the
|
|
1576
|
+
* key as a read-only query, and the `ROUTE_RELEASED` handler hashes the
|
|
1577
|
+
* frame's own `topic` only to compare it against `topicKey`, on a channel
|
|
1578
|
+
* where that field is attacker-controlled until the comparison passes. (The
|
|
1579
|
+
* `CONTROL` handler performs the same re-derivation but then *does* call this
|
|
1580
|
+
* method, so it is not a bypass.) If you add a new call site, prefer
|
|
1581
|
+
* `rememberTopic` unless you have one of those reasons.
|
|
1567
1582
|
*/
|
|
1568
1583
|
rememberTopic(topic) {
|
|
1569
1584
|
const topicKey = createOpaqueKey(topic);
|
|
@@ -1637,8 +1652,10 @@ var DataBusTraceReporter = class {
|
|
|
1637
1652
|
this.asyncSink = options?.asyncSink ?? false;
|
|
1638
1653
|
this.now = now;
|
|
1639
1654
|
}
|
|
1640
|
-
/** Start the periodic metrics flush interval.
|
|
1641
|
-
*
|
|
1655
|
+
/** Start the periodic metrics flush interval. Only the `!enabled` return is a
|
|
1656
|
+
* true no-op: in 'events' mode, or with an interval already armed, it returns
|
|
1657
|
+
* without arming anything but has already cleared `stopped` one statement
|
|
1658
|
+
* earlier — which is what re-opens the event stream after a `stop()`. */
|
|
1642
1659
|
start() {
|
|
1643
1660
|
if (!this.enabled) return;
|
|
1644
1661
|
this.stopped = false;
|
|
@@ -1745,9 +1762,11 @@ var DataBusTraceReporter = class {
|
|
|
1745
1762
|
recordDedupSuppressed() {
|
|
1746
1763
|
if (this.metricsActive) this.dedupSuppressed += 1;
|
|
1747
1764
|
}
|
|
1748
|
-
/** True when metrics recording is active: enabled and mode
|
|
1749
|
-
* Extracted so the
|
|
1750
|
-
*
|
|
1765
|
+
/** True when metrics recording is active: enabled, not stopped, and the mode
|
|
1766
|
+
* includes metrics. Extracted so the seven readers — `getMetrics`, the five
|
|
1767
|
+
* `record*` methods and `flush` — share one guard instead of repeating it.
|
|
1768
|
+
* The `!stopped` term is part of the contract, not a bonus: `event()` does not
|
|
1769
|
+
* go through this getter and gates on `stopped` itself. */
|
|
1751
1770
|
get metricsActive() {
|
|
1752
1771
|
return this.enabled && !this.stopped && this.mode !== TRACE_MODE.EVENTS;
|
|
1753
1772
|
}
|
|
@@ -2011,7 +2030,8 @@ var ReplayManager = class {
|
|
|
2011
2030
|
}
|
|
2012
2031
|
}
|
|
2013
2032
|
}
|
|
2014
|
-
/** Clear replay history for one exact topic,
|
|
2033
|
+
/** Clear replay history for one exact topic, and for its durable history when
|
|
2034
|
+
* the injected adapter provides the optional `clearTopic`. */
|
|
2015
2035
|
async clearTopic(topic) {
|
|
2016
2036
|
if (!this.buffers) return;
|
|
2017
2037
|
const clearing = this.persistence?.clearTopic ? this.withPersistenceRetry(PERSISTENCE_OPERATION.CLEAR_TOPIC, () => this.persistence.clearTopic(topic)) : null;
|
|
@@ -2390,7 +2410,7 @@ var DedupManager = class {
|
|
|
2390
2410
|
};
|
|
2391
2411
|
|
|
2392
2412
|
// src/core/version.ts
|
|
2393
|
-
var SDK_VERSION = true ? "0.21.
|
|
2413
|
+
var SDK_VERSION = true ? "0.21.13" : "";
|
|
2394
2414
|
|
|
2395
2415
|
// src/core/data-bus.ts
|
|
2396
2416
|
var DEFAULT_REPLAY_MAX_PER_TOPIC = 100;
|
|
@@ -2921,7 +2941,10 @@ var CrossTabDataBus = class {
|
|
|
2921
2941
|
async clearReplay() {
|
|
2922
2942
|
await this.replayManager.clearAll();
|
|
2923
2943
|
}
|
|
2924
|
-
/** Clear replay history for one exact topic,
|
|
2944
|
+
/** Clear replay history for one exact topic and, when the persistence adapter
|
|
2945
|
+
* implements `clearTopic`, its durable history too — an adapter without that
|
|
2946
|
+
* optional method leaves the persisted rows in place, exactly as `clearReplay`
|
|
2947
|
+
* does. */
|
|
2925
2948
|
async clearReplayTopic(topic) {
|
|
2926
2949
|
await this.replayManager.clearTopic(topic);
|
|
2927
2950
|
}
|
|
@@ -3316,9 +3339,14 @@ var CrossTabDataBus = class {
|
|
|
3316
3339
|
});
|
|
3317
3340
|
}
|
|
3318
3341
|
/** Emit the coordination trace snapshot from the current cluster state.
|
|
3319
|
-
* Called
|
|
3320
|
-
*
|
|
3321
|
-
*
|
|
3342
|
+
* Called once per successful `start()`: `reopenTransport()`'s settle arms do not
|
|
3343
|
+
* emit, so a recovery produces no second snapshot. It runs after the opening
|
|
3344
|
+
* resolves — and not because a pre-open read would see no routes, which is the
|
|
3345
|
+
* wrong mechanism and was the shipped claim until it was measured: a snapshot
|
|
3346
|
+
* taken with the opening still unresolved already listed the route, its
|
|
3347
|
+
* confirmation and both tabs' worker records, because
|
|
3348
|
+
* `BatchingStorageWriter` serves pending writes on read (`storage-batch.ts:79-82`
|
|
3349
|
+
* for `getItem`, `:143-155` for the `keys()` union that `readAllByPrefix` uses). */
|
|
3322
3350
|
emitCoordinationTrace() {
|
|
3323
3351
|
const snapshot = this.cluster.getSnapshot();
|
|
3324
3352
|
this.trace.event({
|
|
@@ -3512,7 +3540,13 @@ var CrossTabDataBus = class {
|
|
|
3512
3540
|
}
|
|
3513
3541
|
/**
|
|
3514
3542
|
* Ensure the DataBus is started, throwing if no initialConfig was provided.
|
|
3515
|
-
*
|
|
3543
|
+
* subscribe/publish/ready call this *unconditionally*: the gate is the
|
|
3544
|
+
* `hasInitialConfig` check below, not the `autoStart` option. `autoStart`
|
|
3545
|
+
* participates in exactly one place, the constructor's
|
|
3546
|
+
* `if (autoStart ?? this.hasInitialConfig)`, where it overrides the default rather
|
|
3547
|
+
* than enabling a later path — so `autoStart: true` with no `initialConfig` throws
|
|
3548
|
+
* from the constructor. `docs/configuration.md`'s "`true` when `initialConfig` is
|
|
3549
|
+
* provided" row describes that single site.
|
|
3516
3550
|
*/
|
|
3517
3551
|
ensureStarted() {
|
|
3518
3552
|
if (this.started) return;
|
|
@@ -3592,4 +3626,4 @@ export {
|
|
|
3592
3626
|
parseDataBusPublication,
|
|
3593
3627
|
selectWorkerBackend
|
|
3594
3628
|
};
|
|
3595
|
-
//# sourceMappingURL=chunk-
|
|
3629
|
+
//# sourceMappingURL=chunk-4HW6PYIR.js.map
|