cross-tab-worker-databus 0.21.40 → 0.21.42
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-HWGWX4M4.js → chunk-VWVYUSIN.js} +3 -3
- package/dist/chunk-VWVYUSIN.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +2 -2
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +2 -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/configuration.md +2 -0
- package/docs/release-checklist.md +8 -2
- package/docs/roadmap.md +11 -1
- package/docs/zh/configuration.md +2 -0
- package/docs/zh/release-checklist.md +8 -2
- package/docs/zh/roadmap.md +11 -1
- package/package.json +1 -1
- package/dist/chunk-HWGWX4M4.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## [0.21.42] - 2026-09-26
|
|
2
|
+
|
|
3
|
+
The library's central privacy claim — that a topic's plaintext never reaches localStorage — was enforced on keys by accident and on values by nothing, and the guard that protects the coordination plane's key/topic pair was written to tolerate a missing field it never had to tolerate. Both halves of that sentence were found by re-auditing claims rather than code: the first by measuring which mutations anything noticed, the second by enumerating the readers of a pair. One behavioral change, no API, option, default, frame or storage-key change.
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **A forged `CONTROL` frame carrying no `topicKey` skipped the check that is supposed to stop a forged one.** `handleControlMessage` guarded the pairing comparison with `message.topicKey !== undefined && …`, so a frame with the field absent passed; `handleRouteReleasedMessage` — the other reader of the same pair, and the one that *completes* a handoff — compares unconditionally. `AGENTS.md` stated that both handlers enforce the invariant, so the claim held on one reader and not the other, the same shape as `0.21.5`, when the check existed only in `handleControlMessage` and the release note described it as if it covered the channel. The tolerance had no referent to protect: `topicKey` has been a *required* field of this message variant since the initial commit, so no released version ever omitted it, and the clause arrived in the same commit that added the check rather than being inherited from a legacy peer — unlike the optional `protocolVersion` beside it, which is a real legacy tolerance. Measured before the fix, by writing the case first: the keyless frame reached the control dispatch, put the attacker's plaintext into `assignedTopics` and `knownTopics` under the key `undefined`, and made `getSnapshot().assignedTopics` report a topic the tab never subscribed to until the next reconcile swept it. No durable ownership is minted and nothing is written to storage — there is no route under that key — so the cost is a lying public snapshot for one tick, which is why this is a patch. The frame a same-origin script can post is the same unauthenticated channel as every other coordination frame, so a forged frame naming a topic with no key is a shape the receiver had to reject rather than adopt; a missing field is a disagreement, since `createOpaqueKey` returns 32 hex characters and `undefined` is none of them. Pinned by a case whose forged frame is widened at the call site (a per-field cast over a required field would typecheck, and the test suite does not type check), asserting all three surfaces the tolerance reached. Re-adding the clause fails that case and nothing else in the suite.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **The storage-plaintext invariant is now enforced over keys *and* values, and every durable write in `src/` is enumerated.** The multi-tab coordination sweep plants a per-seed marker in each topic name and searches the whole registry for it after every step rather than only on the quiesced end state — a record the next reconcile deletes again would leave the converged state clean while the plaintext was readable in between. Measured on three mutations, the two halves were not equally covered: a route record growing a `topic` field beside its `topicKey` failed **1 test in 969**, that arm alone, while a plaintext-bearing key also tripped two pre-existing cases that are about something else (a `clusterKey` namespace-containment assertion and the pagehide leftovers assertion) — keys had incidental coverage, values had none. `tests/storage-writers.test.ts` closes the remaining gap by enumerating every durable write in `src/` and re-deriving which of them carry a topic, so the two documented opt-ins (`channelFallback: 'storage-event'`, `replay.persistence`) are a checked list rather than a claim. Its topic half is there because the obvious word test was measured *passing* a leak that travelled through a variable named `topicKey`; the term that carries the weight is `knownTopics`, the one map that turns a hash back into a name. `docs/configuration.md` and its Chinese mirror now say the two opt-ins are the complete list and that it is checked.
|
|
12
|
+
|
|
13
|
+
## [0.21.41] - 2026-09-25
|
|
14
|
+
|
|
15
|
+
The release that `0.21.39` was — a run that dies inside its own tag build, having published nothing — has no branch in the shipped release checklist, so the procedure that recovered it lived only in `AGENTS.md` and `docs/progress.md`, neither of which is in the tarball. No library behavior, export, option, default, frame or storage key changed; the coverage floors and the zero-count arm ledger are `0.21.40`'s.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Both `docs/release-checklist.md` languages now carry the dead-tag recovery procedure.** The section that handles a failed release run said, in three clauses, "rerun against the unchanged tag" for propagation failures, "ship a new patch version" for artifact defects, and "never move or reuse a published tag". All three are true and none applies when the run published nothing: the second leaves an unpublished version standing in `CHANGELOG.md`, which `scripts/verify-release-version.mjs` then refuses on every later tag — so one abandoned publication would block the whole series. The procedure is now written out where a maintainer will read it: the two reads that establish "nothing was published" and the way each can lie (a configured mirror answers *"not there"* about a version it has simply not learned, and a wrong repository slug answers `release not found`, which is the expected shape of the answer and therefore gets accepted); the delete-and-recreate, stated as the two ordinary pushes it is, with the immutability rule left intact because it constrains a *published* tag; the `creatordate` consequence that makes re-cutting on the same calendar day keep the release-date gate green; and the step that prevents the case altogether — create the annotated tag, re-run `pnpm check`, `pnpm lint` and the `verify:*` trio with the tag present, and only then push the ref. That last half is why `0.21.39` died where it did: the tag build is the one moment in the cycle when `package.json`'s version already carries its own tag, so a check keyed on "the newest tag" is invisible to PR CI *and* to `main`-push CI, and three green runs said nothing. Step 6 of "Before tagging" now points at that paragraph, because the reader who needs it is the one holding the tag.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **`scripts/verify-release-version.mjs` offered a remedy in a form that does nothing.** Its refusal read "Each needs either its artifact published or its section marked as a version that was never released", and the second half is a prose edit — so which prose edit is the contract, and it was unstated. Measured against the exported `findUnpublishedReleases` with a four-shape fixture and a standing-section control: `## [0.20.91] - never released` is reported exactly like the plain section it is meant to excuse, while `## 0.20.91 — never released` and `## ~~[0.20.91]~~ …` come back clean. The exemption belongs to the `## [x.y.z]` heading *shape* the scan matches, never to a keyword — which is the property worth keeping (no phrase a typo can trip), and the reason the message now names the shape instead of the word. Pinned by three legs that each die to their own mutant: a keyword exemption added to the predicate fails the still-reported leg, a regex widened to accept bare headings fails the exempted leg, and reverting the message fails the leg that reads the message. The first two legs pass under a keyword exemption whether or not the third is read, which is why the wording is asserted rather than trusted to be legible. `scripts/` and `tests/` are not in `package.json` `files`, so this half reaches a maintainer in a clone and not a consumer running `npm install` — the checklist half is the shipped part.
|
|
24
|
+
|
|
1
25
|
## [0.21.40] - 2026-09-25
|
|
2
26
|
|
|
3
27
|
`0.21.39` added a gate, then replaced that gate's rule in the test -- and the prose describing the gate, written a day earlier by the release-prep PR, was in a file the fix never opened. So the notes that shipped with `0.21.39` state the requirement its own fix deleted. No library behavior, export, option, default, frame or storage key changed; the coverage floors and the zero-count arm ledger are `0.21.39`'s.
|
package/dist/centrifuge.js
CHANGED
|
@@ -1158,7 +1158,7 @@ var WorkerClusterRuntime = class {
|
|
|
1158
1158
|
/** Handle a point-to-point CONTROL message (SUBSCRIBE / UNSUBSCRIBE / PUBLISH). */
|
|
1159
1159
|
handleControlMessage(message) {
|
|
1160
1160
|
if (message.targetWorkerId !== this.workerId) return;
|
|
1161
|
-
if (
|
|
1161
|
+
if (createOpaqueKey(message.topic) !== message.topicKey) return;
|
|
1162
1162
|
this.rememberTopic(message.topic);
|
|
1163
1163
|
switch (message.action) {
|
|
1164
1164
|
case CONTROL_ACTION.SUBSCRIBE: {
|
|
@@ -2475,7 +2475,7 @@ var SDK_VERSION = (
|
|
|
2475
2475
|
// declaration's type is `string | undefined` and the export's is `string`
|
|
2476
2476
|
// (dropping the conditional is TS2322, measured). Recorded here so the leg reads
|
|
2477
2477
|
// as a shape the harness cannot reach rather than as a case nobody exercised.
|
|
2478
|
-
true ? "0.21.
|
|
2478
|
+
true ? "0.21.42" : ""
|
|
2479
2479
|
);
|
|
2480
2480
|
|
|
2481
2481
|
// src/core/data-bus.ts
|
|
@@ -3715,4 +3715,4 @@ export {
|
|
|
3715
3715
|
parseDataBusPublication,
|
|
3716
3716
|
selectWorkerBackend
|
|
3717
3717
|
};
|
|
3718
|
-
//# sourceMappingURL=chunk-
|
|
3718
|
+
//# sourceMappingURL=chunk-VWVYUSIN.js.map
|