cross-tab-worker-databus 0.21.17 → 0.21.19
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 +44 -0
- package/dist/centrifuge.js +1 -1
- package/dist/centrifuge.shared.worker.js.map +2 -2
- package/dist/{chunk-EZYCSJRS.js → chunk-Q2INCAFO.js} +20 -6
- package/dist/{chunk-EZYCSJRS.js.map → chunk-Q2INCAFO.js.map} +2 -2
- package/dist/cjs/centrifuge.cjs +19 -5
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +19 -5
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/trace.d.ts +2 -1
- 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/websocket.d.ts.map +1 -1
- package/dist/workers/port-reaper.d.ts.map +1 -1
- package/docs/roadmap.md +17 -1
- package/docs/zh/roadmap.md +17 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
## [0.21.19] - 2026-09-24
|
|
2
|
+
|
|
3
|
+
One behavior fix in the trace reporter, two test pins that each close a leg no test named, and the last unclassified uncovered-branch legs outside the two large modules written up at their sites.
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **A non-finite reading of the public `trace.now()` clock no longer blanks a metrics window.** `recordDispatched()` counted every dispatch that had a matching receive as a latency sample, and the `Math.max(0, …)` it used was added for a *backwards* clock — which `Math.max` clamps — while `NaN` passes straight through it, because `Math.max` returns `NaN` for any `NaN` argument. So the sample count rose while the bucket write landed on the non-index property `"NaN"`, which no percentile walk can reach (it iterates `0 … buckets.length - 1`). Measured on the unmodified reporter with one 50 ms sample and one NaN-delay sample: `dispatchSamples: 2`, `dispatchAvgMs: NaN` — which `JSON.stringify` renders as `null`, so the field disappears rather than reading wrong — and `dispatchP95Ms`/`dispatchMaxMs` pinned at the histogram ceiling because the rank exceeds the sum of every reachable bucket, while `dispatchP50Ms` still printed a plausible number. A dispatch whose delay cannot be computed now records **no** latency sample, on the same terms as a dispatch with no matching receive; `received`, `dispatched` and `topics` still count it, and a negative delay is still clamped to 0 because that pair is a real measurement in the wrong order. Both non-finite kinds (`NaN` and `±Infinity`) drop the sample.
|
|
8
|
+
|
|
9
|
+
### Tests
|
|
10
|
+
|
|
11
|
+
- **`ReplayManager.clearAll()` and `clearBefore()` on a persistence adapter that does not implement the method.** Both operations put their durable call behind an optional-method check, and `DataBusReplayPersistence` declares both methods optional, so a `load`/`append`-only adapter is legal — while every existing case either injected a full adapter or issued one `clearBefore` per lifecycle, so neither "no durable operation" leg had ever executed. The `clearAll` case asserts the local clear succeeds *without* reporting a persistence error (degrading the check to `this.persistence` turns an operation that had nothing durable to do into a `TypeError` after the retry budget — a caller-visible failure of a success). The `clearBefore` case asserts the **newest** cutoff wins over an earlier one when nothing prunes durably, which is where the filter over `load()`'s result is the only enforcement of a cutoff the caller already asked for.
|
|
12
|
+
|
|
13
|
+
### Documentation
|
|
14
|
+
|
|
15
|
+
- **Six uncovered-branch legs now say at the site why they read 0**, so a coverage pass stops re-hunting them: `websocket.ts`'s `connectPromise ?? undefined` (excluded by the field's whole write set — both assignments sit in the same frame as the premise fields the reuse gate reads), `replay-persistence.ts`'s `dbPromise === pending` fall-through (no writer fits between a rejection and the first reaction registered on that promise), `port-reaper.ts`'s null-handle arm (unreachable, not merely unreached: every `schedule()` caller is gated on a tracked port, and the only statement that nulls the handle without emptying the maps is that branch itself), and three `invalidate`+`reject` latches in `clear`/`clearTopic`/`clearBefore` that now point at the enumeration which already covered all four of them but sat only in `load()`.
|
|
16
|
+
- **A third tier in the coverage ledger, in `AGENTS.md`.** With `noUncheckedIndexedAccess` on, an index read is `T | undefined` whether or not a hole is reachable, so a `?? fallback` that satisfies an assignment can neither run nor be deleted. Measured on four legs: `latencyBuckets[i] += 1` and `seen += buckets[index] ?? 0` in `src/core/trace.ts` are `TS2532`, `return this.connectPromise` in `src/websocket.ts` and `export const SDK_VERSION: string = __SDK_VERSION__` in `src/core/version.ts` are `TS2322`. Before hunting an arm, write the bare field and run `pnpm typecheck` — a compiler error means the counter is reporting a type artifact. `src/core/trace.ts` and `src/core/version.ts` carry that statement at the sites.
|
|
17
|
+
|
|
18
|
+
### Compatibility
|
|
19
|
+
|
|
20
|
+
Mixed-version peers are unaffected: no frame, default, storage key, or export moved. `verify:compat`, `verify:types` and `verify:pack` pass against `v0.21.18`. The only consumer-visible difference is the fixed one above — a trace window whose clock misbehaves now reports finite numbers instead of a missing average.
|
|
21
|
+
|
|
22
|
+
## [0.21.18] - 2026-09-24
|
|
23
|
+
|
|
24
|
+
No behavior, public API, protocol, or storage change. This release carries five test pins that each close a leg no test named, one rewritten source comment that ships through the bundles, and two repository workflow rules.
|
|
25
|
+
|
|
26
|
+
### Tests
|
|
27
|
+
|
|
28
|
+
- **A non-string frame `type` is normalized to `null` before it is reported.** `getDiagnostics().protocol.lastUnknownMessageType` is declared `string | null` and is filled from a BroadcastChannel any same-origin script can post into. The `null` side of that normalization had never executed, and `getUnknownMessageStats()` had no reader at all — so the `unknownMessages` count published beside it had none either. Four forged frames now arrive through a channel joined *by name* rather than by calling the handler. The prototype-less-object frame is posted **first** on purpose: the failure it exists to catch is a throw (`String(Object.create(null))` raises `TypeError: Cannot convert object to primitive value`), and in any other order an earlier value mismatch hides it.
|
|
29
|
+
- **A `replay.persistenceRetry` block that names only one field applies the documented default to the other.** Each field is validated only when present and each default is supplied independently, so `{ maxAttempts: 2 }` must still wait the 50 ms backoff and `{ backoffMs: 5 }` must still make exactly one attempt. Previously every partial object the suite built threw at the field it named, so the validator's accept path for a partial policy had never completed. This gap was invisible from the defaulting sites' own counters — both `??` legs read hot, because a bus with no policy takes the right one and a bus with both takes the left; the untested shape was between them.
|
|
30
|
+
- **A failed handshake leaves no connect timer armed.** `failConnect()` cancels the attempt's connect timer before it rejects, and that cancel — not the callback's staleness guard — is what makes the guard unreachable. It had no test. Without it, the dead attempt's own timeout later reports `onStatus('error')` plus an `onError('WebSocket did not open within Nms.')` for a connection the application was already told had failed.
|
|
31
|
+
- **A `channelToken` credential request that names no channel is answered with an empty one.** `channel` is optional on the `TOKEN_REQUEST` wire shape and the session omits it when it is `undefined`, which is reachable from an older worker on the port or from a dependency handing the callback a non-string. The `?? ''` is the only thing keeping `undefined` from crossing into an application `getChannelToken(channel: string)`.
|
|
32
|
+
- **A `Blob` frame whose conversion fails after its connection was replaced is not reported to the replacement.** That catch reports through `this.handlers` — whoever holds the connection at that moment — so the guard comparing the captured socket/handler pair is the only thing between a dead socket's conversion error and the live connection's `onError`, which an application would read as the new connection being broken.
|
|
33
|
+
|
|
34
|
+
Each case was mutation-checked, and the attribution is recorded in `docs/progress.md` (Phases 155–159): which single assertion fails, which mutant dies at which leg, and the three legs found to have **no mutant of their own**. Four zero-count branch slots closed; `src/` now reads 56 of 1960, and `validation.ts` reports 100% branches for the first time.
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
|
|
38
|
+
- `src/websocket.ts` — the connect-timeout callback's guard comment now names the enumeration that makes **all three** of its terms unreachable (`socket` and the handler set are reassigned only in `start()` and `stop()`; reaching either needs `socketActive` already false, and every path that lowers it has already cancelled the armed timer), says which test holds which leg, and **drops** its "all 880 tests green" count instead of refreshing a number that rots in place. This is shipped text: it reaches consumers through both `dist/` bundles and the declarations.
|
|
39
|
+
- `AGENTS.md` — two rules taken from measurements in this batch. (1) Vitest transpiles `tests/**` with no type checking, so a test that constructs a deliberately illegal value is red only under `pnpm check`; the widen belongs on the post helper, not on each frame, because `Object.create(null)` is `any`. (2) Do not rebase a branch whose PR is already open: the progress log has a single append anchor, so two open PRs touching it leave the older unmergeable, and the repair cannot be pushed without a force-push, which this repository forbids.
|
|
40
|
+
|
|
41
|
+
### Compatibility
|
|
42
|
+
|
|
43
|
+
Mixed-version peers are unaffected: no frame, default, storage key, or export moved. `verify:compat`, `verify:types` and `verify:pack` pass against `v0.21.17`, and the published-consumer gate re-runs on the tag.
|
|
44
|
+
|
|
1
45
|
## [0.21.17] - 2026-09-24
|
|
2
46
|
|
|
3
47
|
### Fixed
|