cross-tab-worker-databus 0.21.23 → 0.21.24
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 +24 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-UT4ZYUW4.js → chunk-IFSC44DS.js} +10 -3
- package/dist/chunk-IFSC44DS.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +9 -2
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +9 -2
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/api.md +2 -0
- package/docs/roadmap.md +9 -1
- package/docs/zh/api.md +2 -0
- package/docs/zh/roadmap.md +9 -1
- package/package.json +1 -1
- package/dist/chunk-UT4ZYUW4.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## [0.21.24] - 2026-09-25
|
|
2
|
+
|
|
3
|
+
Two lifecycle defects that the coverage ledger handed over as a by-product: a cluster runtime stopped during its own activation rebuilt everything its teardown had removed, and a `stop()` issued from one of its callbacks was dropped on the floor. A verdict now sits at every zero-count site the ledger had left bare.
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **A teardown arriving during activation abandons the rest of it.** `activate()` sets `started = true` and only then reaches consumer code — the environment adapter's `createChannel`, and `handlers.onControl` for a self-addressed SUBSCRIBE when storage is unavailable — before it arms its heartbeat. A `stop()` or pagehide landing in that span had already run `pause()`, so the remaining lines rebuilt exactly what it removed: the runtime kept a live channel with its listener attached, was written back into storage as a worker record no peer would ever TTL-prune, armed an interval no later `stop()` could clear (every leaked tick re-registered the departed tab), and accepted a `CONTROL/SUBSCRIBE` frame addressed to it after the stop. All four were measured on the unmodified tree, and each half of the rollback now dies to its own named assertion.
|
|
8
|
+
- **A `stop()` issued from `handlers.onResume` takes effect.** `handlePageShow()` clears `suspended` on the statement before it calls that callback, and `started` is set only by the `activate()` its own generation bump then skips, so both of `stop()`'s entry flags read false while consumer code was running: the teardown was dropped, the lifecycle listeners stayed attached, and the next hide/show pair re-fired `onResume` and put `coordinated` back to true. The guard's third term is `lifecycleListening`, which is what "something is still attached" actually means here.
|
|
9
|
+
|
|
10
|
+
### Tests
|
|
11
|
+
|
|
12
|
+
- **Three cases, one per symptom, and a census to tell the rest from unreachability.** Two new `cluster.test.ts` tests construct the activation window from the two seams that open it (a channel adapter that stops the runtime, a control handler that stops the runtime) and assert the abandoned activation leaves no registration, no heartbeat and no live listener; the third stops the runtime from inside `onResume` and asserts a later visibility toggle fires nothing. For the arms that stayed zero-count the method from `0.21.23` was reused: a flag-vector census over one full suite run (four distinct `pause()` vectors, one each for `activate()`'s entry and the listener removal, two for `reconcile()` and `writeRoute()` — every one of them reading the flag false *somewhere*, which is the control on the rows that never do), plus a `tsc --noEmit` measurement for each leg whose deadness is a type question rather than a reach question: one error apiece for `resolvePublishTarget`'s `?? this.workerId`, for `writeRoute`'s storage guard (reported on the very next statement), and for each of the four projected-load reads, and five for the handoff's `if (!owner) continue` — the count the comment at that site has been claiming, re-measured against this tree rather than inherited.
|
|
13
|
+
- **The ledger moved for the first time in six phases: 47 \u2192 46 zero-count branch arms**, denominator 1958 \u2192 1963, aggregate 99.02 / 97.59 \u2192 **97.65** / 99.27 / 99.69, tests 930 \u2192 933. One arm (`pause()`'s clear-the-interval false leg) became reachable-and-covered because of the fix, and every new branch the fix introduced is exercised on both sides.
|
|
14
|
+
- **Two terms of `stop()`'s guard are dominated by the new one, and stay.** Deleting `!started` alone or `!suspended` alone leaves the suite green at its full count, because each implies `lifecycleListening`. They are kept on an asymmetry: a redundant term in a disjunction costs one boolean test, while a missing one drops a teardown silently — which is the bug this release fixes.
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
- **Thirteen verdicts written at their sites, and one shipped enumeration corrected.** Every remaining zero-count arm in `cluster.ts` now carries what decides it: the caller enumeration for `writeRoute()`'s storage guard (with the contrast that makes its twin `writeSubscriber()` covered — one unconditional call site), `routeOwnerIsLive()`'s own conjunct for the publish-target fallback, the Map seeding for the two handoff projected-load reads (the only difference from their covered twins in `reconcileSubscriptions()`), and the three-flag invariant stated once, at `stop()`, with the other two sites pointing at it. One of those sites described a `?? this.currentRecord` fallback in the method whose comment it was — that election has no such fallback; the three are `subscribe()`'s and the two in `reconcileSubscriptions()` — and the list is now named by owning method.
|
|
19
|
+
- **A consumer-visible contract was added to `docs/api.md` and its Chinese mirror**: `stop()` is effective from inside any synchronous callback of `WorkerClusterRuntime`, and an abandoned activation leaves nothing registered. One repository rule went into `AGENTS.md`: a verdict of "no caller can bring the premise" must be re-checked against the consumer code the enclosing method itself calls, because in both defects found this way the caller list was correct and the re-entry was inside the audited method.
|
|
20
|
+
|
|
21
|
+
### Compatibility
|
|
22
|
+
|
|
23
|
+
No frame, storage key, default, export or option moved. The behavior change is only observable to a consumer that stops a cluster runtime from inside one of its own callbacks or its adapter's `createChannel`, and it changes that case from "the stop is dropped and the tab revives" to "the stop takes effect" — mixed-version peers see a well-behaved peer either way. `verify:compat`, `verify:types` and `verify:pack` pass against `v0.21.23`.
|
|
24
|
+
|
|
1
25
|
## [0.21.23] - 2026-09-24
|
|
2
26
|
|
|
3
27
|
A sixteen-mutant sweep of the last multi-term guards in the repository, one behavior that no test had ever named, and a comment that existed to justify an otherwise dead operand replaced by a lint gate that refuses the edit for real.
|
package/dist/centrifuge.js
CHANGED
|
@@ -742,7 +742,7 @@ var WorkerClusterRuntime = class {
|
|
|
742
742
|
*/
|
|
743
743
|
stop() {
|
|
744
744
|
this.lifecycleGeneration += 1;
|
|
745
|
-
if (!this.started && !this.suspended) return;
|
|
745
|
+
if (!this.started && !this.suspended && !this.lifecycleListening) return;
|
|
746
746
|
this.pause();
|
|
747
747
|
this.flushStorage();
|
|
748
748
|
this.removeLifecycleListeners();
|
|
@@ -770,6 +770,12 @@ var WorkerClusterRuntime = class {
|
|
|
770
770
|
this.channel = null;
|
|
771
771
|
}
|
|
772
772
|
if (!this.channel) this.storage = null;
|
|
773
|
+
if (!this.started) {
|
|
774
|
+
const channel = this.channel;
|
|
775
|
+
this.channel = null;
|
|
776
|
+
channel?.close();
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
773
779
|
this.channel?.addEventListener("message", this.handleMessage);
|
|
774
780
|
const now = this.environment.now();
|
|
775
781
|
this.currentRecord = {
|
|
@@ -785,6 +791,7 @@ var WorkerClusterRuntime = class {
|
|
|
785
791
|
if (!this.storage) this.sendControl(this.workerId, CONTROL_ACTION.SUBSCRIBE, topic, topicKey);
|
|
786
792
|
else this.writeSubscriber(topicKey);
|
|
787
793
|
}
|
|
794
|
+
if (!this.started) return;
|
|
788
795
|
this.reconcile();
|
|
789
796
|
this.heartbeatHandle = this.environment.setInterval(() => {
|
|
790
797
|
this.writeRecord(false);
|
|
@@ -2484,7 +2491,7 @@ var SDK_VERSION = (
|
|
|
2484
2491
|
// declaration's type is `string | undefined` and the export's is `string`
|
|
2485
2492
|
// (dropping the conditional is TS2322, measured). Recorded here so the leg reads
|
|
2486
2493
|
// as a shape the harness cannot reach rather than as a case nobody exercised.
|
|
2487
|
-
true ? "0.21.
|
|
2494
|
+
true ? "0.21.24" : ""
|
|
2488
2495
|
);
|
|
2489
2496
|
|
|
2490
2497
|
// src/core/data-bus.ts
|
|
@@ -3718,4 +3725,4 @@ export {
|
|
|
3718
3725
|
parseDataBusPublication,
|
|
3719
3726
|
selectWorkerBackend
|
|
3720
3727
|
};
|
|
3721
|
-
//# sourceMappingURL=chunk-
|
|
3728
|
+
//# sourceMappingURL=chunk-IFSC44DS.js.map
|