cross-tab-worker-databus 0.21.21 → 0.21.23
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 +47 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-37NTHK22.js → chunk-UT4ZYUW4.js} +30 -16
- package/dist/chunk-UT4ZYUW4.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +29 -15
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +29 -15
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/environment.d.ts.map +1 -1
- package/dist/core/trace.d.ts +19 -5
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/version.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/utils/validation.d.ts +6 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/websocket.d.ts.map +1 -1
- package/docs/benchmarks.md +8 -8
- package/docs/roadmap.md +17 -1
- package/docs/zh/benchmarks.md +8 -8
- package/docs/zh/roadmap.md +17 -1
- package/package.json +1 -1
- package/dist/chunk-37NTHK22.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
## [0.21.23] - 2026-09-24
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
### Tests
|
|
6
|
+
|
|
7
|
+
- **One live gap in sixteen operands.** Every four-term state chain in `data-bus.ts` was measured one deletion at a time against the whole suite: `stop()`'s "nothing to tear down" early return, the recovery-timer callback gate, `runTransport()`'s ready shortcut, and the demand reopen under it. Four of the sixteen were already pinned by a named test, eleven are closed by construction, and exactly one — `!this.stopping` on the ready shortcut — was live and unnamed. `sends no unsubscribe to a transport while the bus is stopping` pins it now, and dies to that mutant alone.
|
|
8
|
+
- **How the guard was found is the reusable part.** For a state guard there is no input vector to differential, so the operands were classified with a *flag-vector census*: instrument the guard, record the operand fields at every evaluation, run the suite once, and compare the observed rows against the single assignment each surviving operand needs to be the decider (7589 recorded `stop()` calls over seven vectors; 49 timer-callback entries over two). Where a premise row did occur but its deletion still passed, printing a short stack on first sight named the *production path* reaching the guard — `unsubscribeTransport()` ← a control frame ← `WorkerClusterRuntime.stop()`'s handoff — which is what turned a surviving mutant into a test. The construction needed a second tab: with no remaining subscriber the handoff removes the route instead of posting a frame, and the single-tab version of the test passed against its own mutation.
|
|
9
|
+
- **Two closure mechanisms that had not met this repository before.** A leg can be dominated by the *function it calls* rather than by a neighbouring operand: three of the demand reopen's four legs restate guards `reopenTransport()` performs first, so deleting one assigns the caller the same promise it would have received. And a leg can have a premise that genuinely exists for the length of every `start()` and still be unobservable, because no caller boundary falls inside it — `stop()`'s `!this.started` is that case, and the same invariant is already written down at `reopenTransport()`, in the one copy of two that had a comment.
|
|
10
|
+
- **Two tests were written, measured, and deleted.** A draft aimed at the `status !== ERROR` leg and an early single-tab version of the unsubscribe test both passed against the mutation they were written for: the first parked its operation behind the recovery gate, which pre-empts the guard a few lines earlier; the second never reached the guard at all. Neither is in the suite; the verdicts that replaced them say "no route found, held against this branch being relaxed" instead of claiming unreachability.
|
|
11
|
+
|
|
12
|
+
### Quality gates
|
|
13
|
+
|
|
14
|
+
- **`no-restricted-globals` now refuses `isFinite` and `isNaN`.** The three `typeof value !== 'number'` operands in `src/utils/validation.ts` cannot decide anything for any input, and the only reason recorded for keeping one was that it catches a specific edit — writing the coercing global `isFinite(value)` where `Number.isFinite(value)` was — which nothing refused. A leg justified by a forbidden edit should have a gate refusing the edit, so the rule was added, and then *proved to have teeth* by introducing the swap: `pnpm lint` reports it at the call. The clean tree has zero findings, because no bare `isFinite(`/`isNaN(` call exists anywhere in `src/`, `tests/` or `scripts/`.
|
|
15
|
+
- **Which meant the claim had to be hunted down.** Two live copies said the rule does not exist — the validator's own paragraph and the `AGENTS.md` bullet citing this measurement — and both now state that the operand is defended twice, and which removal each defence covers. Five historical `docs/progress.md` mentions were left as written, with the new entry as the pointer.
|
|
16
|
+
|
|
17
|
+
### Documentation
|
|
18
|
+
|
|
19
|
+
- **Three repository rules.** Measure a *state* guard with a flag-vector census plus the assignment enumeration, and use a stack on first sight of a premise row to find the path that reaches it; a cannot-decide leg whose justification is a forbidden edit should end up with a gate in front of the edit, not only a comment; and name which of "no test reaches it" and "it cannot be reached" a verdict actually establishes, because the first is a coverage statement and the second is a claim about the state machine.
|
|
20
|
+
- **Ledger:** **47** zero-count branch arms of **1958** at both ends of all three phases, aggregate **99.02 / 97.59 / 99.27 / 99.69**, tests 929 → 930. Every operand probed is evaluated on every call, so no coverage total could have pointed at the gap this release closed — the sixth consecutive phase to make that argument by measurement rather than by assertion.
|
|
21
|
+
|
|
22
|
+
### Compatibility
|
|
23
|
+
|
|
24
|
+
Mixed-version peers are unaffected: no frame, default, storage key, export or behavior moved. The pinned behavior is one the code already had and no test named; the lint rule is repository-local and reaches consumers only as comment text in the declarations and the bundles. `verify:compat`, `verify:types` and `verify:pack` pass against `v0.21.22`.
|
|
25
|
+
|
|
26
|
+
## [0.21.22] - 2026-09-24
|
|
27
|
+
|
|
28
|
+
Six phases of one method, applied to every multi-term guard the previous scan ranked: delete a single operand, run the whole suite, and ask which of three answers that mutant produced. Nine behaviors that no test named are now named, five operands are closed as incapable of deciding anything, and the coverage ledger did not move once — which is the finding, not a shortfall.
|
|
29
|
+
|
|
30
|
+
### Tests
|
|
31
|
+
|
|
32
|
+
- **Nine newly pinned operands.** Measured one deletion at a time across six guards: three staleness terms in `websocket.ts`'s socket listeners (each now dies to its own new case), two of `trace.ts`'s four-term activity check (`received > 0 || dispatched > 0 || dedupAccepted > 0 || dedupSuppressed > 0`, where the one existing test raised *both* dedup counters in one window, so whichever operand survived still emitted), two operands of the `storage-event` frame envelope (three labelled writes now exercise an absent `seq`, a `seq` that is the numeric string `"1"`, and an absent `message` separately), and two option legs of `DedupManager.start()`'s sweep gate, whose own doc had long stated "no-op when disabled or no `sweepMs` was configured" without either leg being verified. Tests 923 → 929.
|
|
33
|
+
- **Why none of that showed up as coverage.** Every operand probed is evaluated on every call, so no line count, branch arm or function total distinguishes "runs" from "decides". `src/` stays at **47** zero-count branch arms through all six phases — 99.02 / 97.59 / 99.27 / 99.69 — and the only movement in the ledger is the denominator, 1962 → 1958, from the two dominated operands deleted below. The arm ledger cannot find this class; a whole-suite mutant can.
|
|
34
|
+
- **Five operands closed as incapable of deciding, by differential rather than by argument.** Two in the storage envelope (`typeof parsed !== 'object'` is never first, and `!parsed`'s rejection is absorbed by the same `catch` that would report it) and all three `typeof value !== 'number'` tests in `validation.ts` (`Number.isSafeInteger` and `Number.isFinite` reject a non-number without coercing, so any value reaching the first operand reaches the second with the same message). Each was proven with a fixed input vector — 23 stored payloads, and 23 values × 3 validators × 4 variants — that produced a byte-identical accept/reject-and-message result for the guard as written and for each mutant.
|
|
35
|
+
- **A kept leg's justification was then tested in both directions.** The `typeof` operand stays because of one specific easy edit, so that edit was constructed: write the coercing global `isFinite(value)` where `Number.isFinite(value)` was, which no lint rule forbids. With the operand in place the swap kills nothing; with the operand also deleted, `heartbeatIntervalMs: '3000'` becomes an accepted option and `rejects a non-positive or non-finite heartbeatIntervalMs` reddens.
|
|
36
|
+
- **One clean bill, and one thin pin named as such.** `ReplayManager.start()`'s four-term gate turned out to have every operand already pinned by exactly the test `DedupManager` lacked — parity between two managers of the same construct, checked rather than assumed. And of the six `validation.ts` deletions that die, five die to several cases while `assertNonNegativeFiniteNumber`'s predicate leg dies to exactly one; that one feeds `NaN` and `Infinity`, so it is thin in count and correct in kind, and the asymmetry is recorded at the site instead of being smoothed into "six of nine".
|
|
37
|
+
|
|
38
|
+
### Documentation
|
|
39
|
+
|
|
40
|
+
- **Two dominated operands deleted, and the guard's own comment rewritten against the counter-evidence.** `getMetrics()` and `flush()` each appended `|| this.stopped` to a condition whose first operand had just evaluated a term containing `!this.stopped`; such a read cannot be the decider, and it was silently *absorbing* the mutation that would have proved the getter's contract. Deleted, with both kill sets recorded: with the repeats in place, removing the getter's term left all 37 test files green; with them gone, the same deletion fails two.
|
|
41
|
+
- **Enumerations and numbers re-read against what they count.** The `getMetrics` case whose name promises all three inactive causes asserted two of them, and the getter's JSDoc enumeration was rewritten to name each (disabled, stopped, events-only); the async-hub cost of raising fuzz delivery to browser-realistic timing was restated in `AGENTS.md` at "~16 tests" where the measurement is 18 (16 post-and-assert cases plus 2 of the hub's own delivery pins); and `version.ts`'s header claimed source-level consumers "get the same value via the vitest `define`" while its own next clause admitted `tsc` sees no value at all — `pnpm typecheck` is `tsc --noEmit`, which evaluates no code, so the drift it feared has an owner (`scripts/verify-release-version.mjs` refuses a tag that is not `v` plus `package.json`'s field) rather than a compiler. Every claim now names the artifact that would show it, and the three shipped comment rules added this release are about the shapes that went wrong: a redundant re-read is load-bearing in the wrong direction, measure a disjunction operand by operand, and a surviving mutant means something different depending on whether a fixed input vector can distinguish it.
|
|
42
|
+
- **The comment-only claim was proven, not asserted.** Each prose change was emitted through esbuild and compared against the previous revision at both ends of the position rule this release also documented: identical with comment lines stripped, and identical raw where — as in `validation.ts` — every added block is a declaration header, the position esbuild drops. The first version of that proof reported a false difference, because a comment in *expression* position does survive into the bundle.
|
|
43
|
+
|
|
44
|
+
### Compatibility
|
|
45
|
+
|
|
46
|
+
Mixed-version peers are unaffected: no frame, default, storage key, export or behavior moved. The two deleted `trace.ts` operands are provably consequence-free — the same expression reads the field synchronously on their left, so no input can distinguish them, and the two tests above are the evidence. Consumer-visible differences are comment text, which reaches them through both the declarations and the bundles. `verify:compat`, `verify:types` and `verify:pack` pass against `v0.21.21`.
|
|
47
|
+
|
|
1
48
|
## [0.21.21] - 2026-09-24
|
|
2
49
|
|
|
3
50
|
One branch arm closed by a call that had never been made, three shipped comment claims retracted against the code they describe, and a repository rule about the difference between a guard nobody can pin and a guard nobody has pinned.
|
package/dist/centrifuge.js
CHANGED
|
@@ -1735,10 +1735,11 @@ var DataBusTraceReporter = class {
|
|
|
1735
1735
|
}
|
|
1736
1736
|
/** Synchronous snapshot of the current metrics window without resetting it.
|
|
1737
1737
|
* Returns the same derived counters as a periodic `message_metrics` event,
|
|
1738
|
-
* or null when metrics recording is inactive
|
|
1739
|
-
* The window keeps accumulating until the next
|
|
1738
|
+
* or null when metrics recording is inactive, which covers each of disabled,
|
|
1739
|
+
* stopped and events-only mode. The window keeps accumulating until the next
|
|
1740
|
+
* interval flush. */
|
|
1740
1741
|
getMetrics() {
|
|
1741
|
-
if (!this.metricsActive
|
|
1742
|
+
if (!this.metricsActive) return null;
|
|
1742
1743
|
const timestamp = this.now();
|
|
1743
1744
|
const samples = this.latencySamples;
|
|
1744
1745
|
return {
|
|
@@ -1814,15 +1815,28 @@ var DataBusTraceReporter = class {
|
|
|
1814
1815
|
}
|
|
1815
1816
|
/** True when metrics recording is active: enabled, not stopped, and the mode
|
|
1816
1817
|
* includes metrics. Extracted so the seven readers — `getMetrics`, the five
|
|
1817
|
-
* `record*` methods and `flush` — share one guard instead of repeating it
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1818
|
+
* `record*` methods and `flush` — share one guard instead of repeating it, which
|
|
1819
|
+
* leaves `stopped` read in exactly two places: this term, and `event()`, which
|
|
1820
|
+
* skips metrics entirely and so gates on the field itself.
|
|
1821
|
+
*
|
|
1822
|
+
* Do not add a `|| this.stopped` back at a call site. `getMetrics` and `flush`
|
|
1823
|
+
* each carried one, and the repetition is what made this term look untestable:
|
|
1824
|
+
* deleting the term left all 37 test files green, because a read of a field the
|
|
1825
|
+
* guard consulted one expression earlier can never be the decider. With both
|
|
1826
|
+
* repeats gone the same deletion dies twice in `tests/trace.test.ts` — to
|
|
1827
|
+
* "starts a fresh metrics window after pause and resume, and stop prevents later
|
|
1828
|
+
* flushes", and to "getMetrics returns null while metrics recording is inactive",
|
|
1829
|
+
* whose name promises all three inactive cases and which tested two of them until
|
|
1830
|
+
* this file stopped absorbing the third. What the term holds: `stop()` resets the
|
|
1831
|
+
* counters through `pause()` before any later call can reach them, so without it a
|
|
1832
|
+
* `record*` during a stopped window accumulates unreset and `start()` — which
|
|
1833
|
+
* clears `stopped` without resetting — hands it to the new session's window. */
|
|
1820
1834
|
get metricsActive() {
|
|
1821
1835
|
return this.enabled && !this.stopped && this.mode !== TRACE_MODE.EVENTS;
|
|
1822
1836
|
}
|
|
1823
1837
|
/** Emit the accumulated metrics snapshot if the interval is active. */
|
|
1824
1838
|
flush() {
|
|
1825
|
-
if (!this.metricsActive
|
|
1839
|
+
if (!this.metricsActive) return;
|
|
1826
1840
|
this.flushNow();
|
|
1827
1841
|
}
|
|
1828
1842
|
flushNow() {
|
|
@@ -2463,14 +2477,14 @@ var DedupManager = class {
|
|
|
2463
2477
|
var SDK_VERSION = (
|
|
2464
2478
|
// The `''` arm has zero counts in every coverage run and cannot be lit from a
|
|
2465
2479
|
// test: `vitest.config.ts` defines the global for the whole suite, and so does
|
|
2466
|
-
// each bundle `scripts/build.mjs` emits, so reaching this side needs a
|
|
2467
|
-
//
|
|
2468
|
-
//
|
|
2469
|
-
//
|
|
2470
|
-
// `string`
|
|
2471
|
-
//
|
|
2472
|
-
// exercised.
|
|
2473
|
-
true ? "0.21.
|
|
2480
|
+
// each bundle `scripts/build.mjs` emits, so reaching this side needs a tool that
|
|
2481
|
+
// consumes `src/` without passing the define — and this repository has none. The
|
|
2482
|
+
// two source-level readers are named in the header above, and only one of them
|
|
2483
|
+
// evaluates this expression. It cannot be deleted either, since the
|
|
2484
|
+
// declaration's type is `string | undefined` and the export's is `string`
|
|
2485
|
+
// (dropping the conditional is TS2322, measured). Recorded here so the leg reads
|
|
2486
|
+
// as a shape the harness cannot reach rather than as a case nobody exercised.
|
|
2487
|
+
true ? "0.21.23" : ""
|
|
2474
2488
|
);
|
|
2475
2489
|
|
|
2476
2490
|
// src/core/data-bus.ts
|
|
@@ -3704,4 +3718,4 @@ export {
|
|
|
3704
3718
|
parseDataBusPublication,
|
|
3705
3719
|
selectWorkerBackend
|
|
3706
3720
|
};
|
|
3707
|
-
//# sourceMappingURL=chunk-
|
|
3721
|
+
//# sourceMappingURL=chunk-UT4ZYUW4.js.map
|