cross-tab-worker-databus 0.21.18 → 0.21.20

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 CHANGED
@@ -1,3 +1,43 @@
1
+ ## [0.21.20] - 2026-09-24
2
+
3
+ Six uncovered branch arms closed by calls no test had ever made, one shipped sentence corrected, and the coverage ledger given a third tier — the kind a compiler, not a test, owns.
4
+
5
+ ### Tests
6
+
7
+ - **The string overload of `publish()` had never been called.** `WorkerClusterRuntime.publish` declares `(topic, data, messageId: string)` alongside the metadata form, and the normalization at the top of the method is the only thing that turns it into the object every downstream reader expects. `CrossTabDataBus.publish` always hands over an object, so nothing reachable *through the bus* can take that arm — and no test had called the runtime directly with a string, even though `docs/api.md` points callers who coordinate straight at the cluster there.
8
+ - **A single-item `publishBatch` whose item carries partial or no metadata.** Every existing case supplied both `messageId` and `timestamp`, so the `… || …` test, its `: undefined` fallback, and both conditional spreads had each only ever been evaluated one way. The absent-metadata case is the one with a consequence: `sendControl` calls the handler with **three** arguments when the metadata is falsy and five otherwise, and that arity is what separates "arrived without an id" from "arrived with an id of `undefined`". The two partial shapes are now covered and have no assertion of their own — recorded as such, because the receiving side re-normalizes with `metadata?.x === undefined ? {} : …`, so an omitted key and an `undefined` value produce the same wire frame.
9
+ - **`clusterKey: ''` is a namespace alias, and it is now pinned on both sides.** The namespace is `createOpaqueKey(options.clusterKey || '__default__')`, so an empty key hashes the literal `'__default__'` rather than naming its own cluster. Two runtimes on one storage and channel, keyed `''` and `'__default__'`, now have to leave exactly one owner for a shared topic, and a third runtime on another key owns its own copy — which is the half that fails if the fallback ever widens past the empty string.
10
+
11
+ ### Documentation
12
+
13
+ - **`clusterKey`'s shipped JSDoc said different keys "operate in isolation" without naming that pair** — an absolute about behavior, reaching consumers through both the declarations and the bundles. The exception is now stated where the option is declared, and in `AGENTS.md`.
14
+ - **A third tier in the uncovered-arm 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; `exactOptionalPropertyTypes` produces the same tier from the other side, where the always-present form of a conditional spread is a `TS2769` while the whole suite stays green. Both were established by **deleting the fallback and running `tsc`** per arm, which is the procedure the rule prescribes: in the route-write block it separated five compiler-held legs from one (`(previous?.generation ?? 0)`) that compiles bare and is therefore dominated by a filter three lines above rather than by its own expression.
15
+
16
+ ### Compatibility
17
+
18
+ Mixed-version peers are unaffected: no frame, default, storage key, or export moved, and no behavior changed — the empty-`clusterKey` alias is documented as it already was, because moving `''` into its own namespace would silently relocate storage for anyone passing an empty key today. `verify:compat`, `verify:types` and `verify:pack` pass against `v0.21.19`.
19
+
20
+ ## [0.21.19] - 2026-09-24
21
+
22
+ 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.
23
+
24
+ ### Fixed
25
+
26
+ - **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.
27
+
28
+ ### Tests
29
+
30
+ - **`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.
31
+
32
+ ### Documentation
33
+
34
+ - **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()`.
35
+ - **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.
36
+
37
+ ### Compatibility
38
+
39
+ 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.
40
+
1
41
  ## [0.21.18] - 2026-09-24
2
42
 
3
43
  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.
@@ -7,7 +7,7 @@ import {
7
7
  publicationMetadata,
8
8
  selectWorkerBackend,
9
9
  serializeError
10
- } from "./chunk-KRSD6KHE.js";
10
+ } from "./chunk-VQZXIMXR.js";
11
11
  import {
12
12
  CENTRIFUGE_INPUT_TYPE,
13
13
  CENTRIFUGE_OUTPUT_TYPE,