cross-tab-worker-databus 0.21.31 → 0.21.33
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 +48 -1
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-M4RO3MVX.js → chunk-FEWEXHZA.js} +2 -38
- package/dist/chunk-FEWEXHZA.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +1 -37
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +1 -37
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/cluster.d.ts +0 -1
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/architecture.md +1 -1
- package/docs/release-checklist.md +1 -1
- package/docs/roadmap.md +20 -3
- package/docs/zh/architecture.md +1 -1
- package/docs/zh/release-checklist.md +1 -1
- package/docs/zh/roadmap.md +20 -3
- package/package.json +1 -1
- package/dist/chunk-M4RO3MVX.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,57 @@
|
|
|
1
|
+
## [0.21.33] - 2026-09-25
|
|
2
|
+
|
|
3
|
+
A sentence in the release checklist was wrong in a way only a new measurement could show, and the release it described was the one that produced it: `0.21.32`'s published-consumer step took 304 s while its registry record existed 76.1 s in. No library code changed.
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Documentation tooling: the counter-observation against a purely-local npm cache account is retracted, because it joined two machines.** The prose cited 0.21.30 as evidence *against* that account on the grounds that "the retry resolved before a fresh 21:44 fetch's 300 s lifetime had run out". Read against the run records, the two halves come from different hosts: the 21:44:35Z "fresh read" was a `npm pack` on a laptop, while the 1 s green at 21:47:10Z was a **new runner job**, and a new job starts with an empty npm cache — which is what the local account *predicts*, not what refutes it. What such an account still cannot explain is exactly one reading, and the text now names that one instead: the runner's own `ETARGET` at 21:45:07Z, 364 s after the poll loop opened at 21:39:03Z and so ~60 s after a copy taken then must have expired. The verdict stays *not established*, now resting on an anomaly rather than on a comparison that never was.
|
|
8
|
+
|
|
9
|
+
### Documentation
|
|
10
|
+
|
|
11
|
+
- **`0.21.32` is the cache-lifetime model's cleanest sample, and it is recorded as one.** Ack 23:34:46Z, registry `time['0.21.32']` 23:36:02.091Z (**76.1 s** — the second-smallest of the six now listed), a `no-cache` packument read from a laptop reporting the new `latest` 151 s in, and the runner's blocking verify finishing at **304 s** — one 300 s lifetime plus a few seconds of command time, measured from that runner's own first fetch. The verify-duration band is now **seven green samples at 304–308 s** (307, 304, 305, 304, 304 under the old ceiling; 308 and 304 under the raised one). Both sample lists — the ack→record gaps and the per-run durations — were extended in `docs/release-checklist.md` (en + zh), the workflow comment and `tests/workflows.test.ts`, and the `AGENTS.md` bullet that restates the lesson.
|
|
12
|
+
- **`maxMeasuredAckToRecordMs` deliberately did not move.** 76.1 s does not beat the 310 s that sizes the 720 s ceiling, so the floor's constant stays put and `tests/workflows.test.ts` still passes unchanged at 6/6 — which is the check that the new sample widened the list without invalidating the derivation.
|
|
13
|
+
- **The retired claim's earlier copies carry pointers rather than silent rewrites.** `CHANGELOG.md`'s `0.21.31` entry and both language `docs/roadmap.md` blocks keep the sentence they got wrong and add what superseded it, naming `0.21.33`, so the history reads as the sequence it was rather than as a claim that never existed.
|
|
14
|
+
- **Three numbers in that prose named the wrong quantity, and all three were caught before the tag.** (1) "the second-smallest of the **seven** now listed" counted the ack→record list with the verify-duration list's size: the gap list carries six values — the seven is `307 / 304 / 305 / 304 / 304 / 308 / 304` s of *step duration* — and the sentence's own parenthesis enumerated six. Fixed in this file and in `docs/progress.md`'s Phase 199 entry. (2) Three copies said 0.21.30 "burned its whole **364 s budget**"; that run's ceiling was **360 s** (48 × 7.5 s), and 364 s is the observed span from the step's first poll (21:39:03Z) to its last `ETARGET` (21:45:07Z) — a budget and a reading written as one number. `docs/release-checklist.md` (en + zh) and the workflow comment now say the 360 s ceiling and keep 364 s for the span. (3) `docs/progress.md` still carried the first draft of the anomaly sentence — "about 5 min 40 s after the fetch that should have gone stale by 21:44:05Z" — which matches neither the 364 s span nor the ~60 s past-expiry figure the corrected copies use; it now states those. While checking (1) the list's stated range, the two releases below it were sampled for the first time the same way: **157.8 s** for 0.21.25 (ack 17:56:53Z, record 17:59:30.781Z) and **96.1 s** for 0.21.26 (ack 18:41:40Z, record 18:43:16.084Z), whose step durations come back at 307 s and 304 s, exactly the two the duration list already records — so the run↔version attribution is confirmed by a number already on file. Eight gaps now exist and neither beats 310.0 s, so `maxMeasuredAckToRecordMs`, the 720 s ceiling and the shipped lists' stated range (0.21.27–0.21.32) are all unchanged; the two new readings live here rather than in every restatement, because a fourth copy of a list is a fourth thing to keep in sync.
|
|
15
|
+
|
|
16
|
+
### Compatibility
|
|
17
|
+
|
|
18
|
+
No library code, export, option, default, frame or storage key changed; `verify:compat`, `verify:types` and `verify:pack` run against `v0.21.32`. The edits are confined to `docs/`, `.github/workflows/release.yml`'s comment, `tests/workflows.test.ts`'s comment and `AGENTS.md`, none of which changes runtime behaviour; the workflow's `PUBLISHED_VERIFY_ATTEMPTS` stays at 96.
|
|
19
|
+
|
|
20
|
+
## [0.21.32] - 2026-09-25
|
|
21
|
+
|
|
22
|
+
A delivery bug, found while looking for something else. A topic's **first** publication from a tab that held a matching wildcard was dispatched locally and never forwarded, even while a live durable route named a different owner for it — and delivery is at-most-once, so that publication was gone for good. The fix is a deletion: the memo that made the decision decided nothing.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **Routing: a topic's first `publish()` / `publishBatch()` now reaches that topic's concrete owner.** Both methods carried a textually identical fast path: while `wildcardPublishCache` held no entry for a topic, it scanned the local wildcard patterns and, on a match, addressed the control frame to *this* worker without consulting the durable route — while every later publication for the same topic did consult it. Measured with one runtime holding `chat.*` and a peer concretely owning `chat.room.1` (both `isAssigned` true, the route record naming the peer): three publications from the wildcard holder landed `1,0,0` on the sender and `0,1,1` on the owner. The first never left the tab. The same shape reproduced for a two-item batch, whose frame the receiver unpacks per item.
|
|
27
|
+
- **This is `0.20.58` from the other side, which is why an eviction rule would not have fixed it.** There, a cached `null` short-circuited the owner lookup and the fix was to keep consulting the route; here a cache *miss* did the same thing. The stored value decided nothing either way — `pattern` and `null` were both read only as "have I scanned this topic" — so the memo is removed rather than bounded, and every non-owned publication now resolves its target through `resolvePublishTarget()`. Wildcard ownership is still honoured: with no live concrete route that call answers with this worker, and the receiving ownership gate matches the pattern. Removing the map also retires the unbounded growth documented in `0.21.31`; that growth was a side effect of this fast path, not a separate defect, so the eviction rule it was framed as needing is no longer needed at all.
|
|
28
|
+
|
|
29
|
+
- **Repository tooling: a scratch directory inside the linted tree made `pnpm lint` red without naming any product code.** A mutation-probe harness was written to `.gate-logs/` — in-tree on purpose, since `/tmp` had evicted a failing log mid-session and `pnpm test:coverage` deletes `coverage/`, where logs had also been written — and `eslint .` reported 9 errors from it (`no-undef` on `console`/`process`, `no-control-regex`) as a lint failure of the release tree. `.gate-logs/` is now ignored by both git and eslint, with the reason recorded beside the entry in `.gitignore`. `tests/regression.test.ts`'s "keeps scripts/ covered by ESLint" still passes, so the release tooling stays linted and only scratch is excluded.
|
|
30
|
+
|
|
31
|
+
### Tests
|
|
32
|
+
|
|
33
|
+
- **Two regressions, one per entry point, each asserted one publication at a time** so a regression names *which* publication was lost instead of reporting a count short by one: `forwards a topic's FIRST publish to its concrete remote owner, not to a matching local wildcard` and `forwards a batch's FIRST publish to the concrete remote owner as well`. Measured both ways — without the fix they fail at `first publish must reach the concrete owner: expected +0 to be 1` and `expected [] to deeply equal [1, 2]`; with it the file is green. Three pre-existing cases that named the removed cache in their titles were retitled to what they actually assert, and the single-runtime pair among them is now labelled as the characterization it is: a lone runtime always wins its own election, so it cannot distinguish the two paths.
|
|
34
|
+
- **`trace.ts`'s flush-activity guard was re-measured on the tree that shipped this fix**, not carried forward: deleting `received` alone reddens 3 tests, `dispatched` 2, `dedupAccepted` 1, `dedupSuppressed` 1 — each operand's own kill set, all four restorations byte-identical.
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
|
|
38
|
+
- **`trace.ts`: each kill now names the file it comes from.** The note recorded which test dies to each operand deletion but not where it lives, and the two `dispatched` kills sit in two different files (`tests/data-bus.test.ts` and `tests/trace.test.ts`).
|
|
39
|
+
- **The publication-lag samples were extended, and one of them had been undercounted.** `0.21.31`'s tag run is the first to exercise the raised 720 s published-consumer ceiling: green on attempt 1 at 308 s, with its registry record 127.2 s after the publish ack and the remaining ~181 s spent against the cache lifetime — so the 310 s maximum the ceiling is sized from still stands, and the ceiling does not need raising again. The other half of that sentence was wrong in the cheap direction: the checklist and `AGENTS.md` credited the 304–305 s regularity to "three consecutive green tag runs", where the five runs from 0.21.25 to 0.21.29 took 307, 304, 305, 304 and 304 s, six samples in a 5-second band once 0.21.31's 308 s is counted. The lag list, the run-by-run durations and the `gh api` lookup that re-derives both are now written out in `docs/release-checklist.md` (en + zh), the workflow's own comment, and `tests/workflows.test.ts`.
|
|
40
|
+
- **`docs/architecture.md` (en + zh): the wildcard publish memo is removed, not capped.** Both copies had described it as the per-topic map the documented `knownTopics` cap does not reach, with an open question about which of its two value shapes could be forgotten; neither half survives, so the paragraph now says what replaced it and records the 1,200-versus-500 measurement beside the delivery counts that made the mechanism visible.
|
|
41
|
+
- **`publish()`'s surviving comment described the deleted mechanism.** Above the `assignedTopics.has(topicKey)` early return it still said wildcard holders "use the same fast path" — which that lookup never did: `assignedTopics` is keyed by the plaintext it was handed, so a holder of `chat.*` carries the *pattern's* key and a concrete topic misses the `has()`, exactly the fall-through the fix relies on. The sentence now names which lookup answers for which assignment, checked against `isAssigned()`'s own `topicMatchesPattern` loop and against `publishBatch()`, which carried no such claim.
|
|
42
|
+
- **The upstream-blocked toolchain item was re-checked, and both channels still exclude TypeScript 7.** `typescript-eslint`'s latest (8.70.1) and its canary (8.70.2-alpha.7) both declare `typescript >=4.8.4 <6.1.0`, so TS 7.0.2 would break the lint gate before touching our own types. The roadmap entry now says how to look that up without being fooled: read `dist-tags.canary`, because sorting version strings puts `8.9.1-alpha.9` last — `9` sorts after `70` — while the line actually worth watching is 8.70.x. The English entry's date had also drifted from the Chinese one (2026-09-23 against 2026-09-22) for the same check; both now carry one date and the same two versions.
|
|
43
|
+
|
|
44
|
+
### Compatibility
|
|
45
|
+
|
|
46
|
+
One behavioural change: a first publication for a wildcard-covered topic that has a live concrete owner is now forwarded to that owner instead of being dispatched locally. No export, option, default, frame or storage key changed; `wildcardPublishCache` was a private field and is absent from the emitted `dist/core/cluster.d.ts`. `verify:compat`, `verify:types` and `verify:pack` run against `v0.21.31`.
|
|
47
|
+
|
|
1
48
|
## [0.21.31] - 2026-09-25
|
|
2
49
|
|
|
3
50
|
The `0.21.30` tag run failed its blocking published-consumer gate after a *successful* publish. Diagnosing that gave the retry budget a measured size, retired a floor in the gate whose job was to keep that failure out, and retracted a note this repository wrote about the same failure less than an hour before it was corrected. No library code changed.
|
|
4
51
|
|
|
5
52
|
### Fixed
|
|
6
53
|
|
|
7
|
-
- **Release tooling: the published-consumer retry ceiling sat below the propagation lag it exists to absorb.** `PUBLISHED_VERIFY_ATTEMPTS=48` × 7500 ms is 360 s, and the 0.21.30 tag run consumed all of it on `ETARGET` while that version's tarball was already fetchable; the re-run against the unchanged tag then passed in 1 s. Two measured terms now size the number. (1) `npm publish`'s success ack precedes the registry's own `time[<version>]` record — by 74.8 s on 0.21.29, 96.8 s on 0.21.28, 248.7 s on 0.21.27 and 310.0 s on 0.21.30. (2) The packument that `npm pack` resolves against is served `cache-control: public, max-age=300`, so a copy of it anywhere in the path may be a full cache lifetime stale by design; that term is also what the three preceding green tag runs were reporting when they consumed 305 s, 304 s and 304 s of the ceiling in turn. The budget is now 96 × 7500 ms = 720 s, clearing the 310 + 300 = 610 s the two add to. Which copy served this run's stale views is **not** established: a purely npm-local-cache account predicts the retry should have waited for the same 300 s object and it did not, so the comment records the open question instead of naming a cache.
|
|
54
|
+
- **Release tooling: the published-consumer retry ceiling sat below the propagation lag it exists to absorb.** `PUBLISHED_VERIFY_ATTEMPTS=48` × 7500 ms is 360 s, and the 0.21.30 tag run consumed all of it on `ETARGET` while that version's tarball was already fetchable; the re-run against the unchanged tag then passed in 1 s. Two measured terms now size the number. (1) `npm publish`'s success ack precedes the registry's own `time[<version>]` record — by 74.8 s on 0.21.29, 96.8 s on 0.21.28, 248.7 s on 0.21.27 and 310.0 s on 0.21.30. (2) The packument that `npm pack` resolves against is served `cache-control: public, max-age=300`, so a copy of it anywhere in the path may be a full cache lifetime stale by design; that term is also what the three preceding green tag runs were reporting when they consumed 305 s, 304 s and 304 s of the ceiling in turn. The budget is now 96 × 7500 ms = 720 s, clearing the 310 + 300 = 610 s the two add to. Which copy served this run's stale views is **not** established: a purely npm-local-cache account predicts the retry should have waited for the same 300 s object and it did not, so the comment records the open question instead of naming a cache. *(That last inference was wrong, and `0.21.33` retracts it: the two readings it joins come from different machines — a laptop's `npm pack` and a new runner job whose cache starts empty — which is what the local account predicts rather than refutes. Its corrected form, plus `0.21.32`'s supporting sample, is in that release's notes.)*
|
|
8
55
|
- **`tests/workflows.test.ts` pinned a floor that the value which just failed satisfied.** The assertion was `totalMs >= 5 * 60 * 1000`, so the 360 s budget that had just turned a good release red passed the gate whose entire purpose is to stop that happening, and nothing would have objected to lowering it again. The floor is now the sum above, written as two named constants beside the rule that re-derives each. Measured against it: setting the workflow's attempts from 96 back to 48 fails exactly that test, and both the 360 s budget that failed here and the 120 s one 0.20.89 exhausted are rejected by it.
|
|
9
56
|
|
|
10
57
|
### Documentation
|
package/dist/centrifuge.js
CHANGED
|
@@ -672,19 +672,6 @@ var WorkerClusterRuntime = class {
|
|
|
672
672
|
this.routeOwnerCache.delete(oldest);
|
|
673
673
|
}
|
|
674
674
|
}
|
|
675
|
-
// Concrete topic → the local wildcard pattern that matched it, or `null` for
|
|
676
|
-
// "scanned, nothing matched". UNLIKE the reverse cache below, this map has no cap:
|
|
677
|
-
// it grows one entry per distinct topic this worker publishes, and is cleared only
|
|
678
|
-
// on the two lifecycle teardowns whose `routeOwnerCache.clear()` sits beside each
|
|
679
|
-
// `wildcardPublishCache.clear()`. Measured: 1,200 `publish()` calls on distinct
|
|
680
|
-
// topic names leave `knownTopics` at its 500-entry cap and this map holding 1,200.
|
|
681
|
-
// A cap is not free here, because both halves decide something. Evicting a pattern
|
|
682
|
-
// re-runs the first local dispatch the entry exists to prevent. Evicting a `null`
|
|
683
|
-
// re-scans, and if a wildcard was assigned in the meantime that publication
|
|
684
|
-
// dispatches locally instead of routing by owner — the fan-out a first publication
|
|
685
|
-
// gets. So bounding this map needs a rule about which of the two may be forgotten,
|
|
686
|
-
// not a number; until one is chosen the growth is the honest cost of the memo.
|
|
687
|
-
wildcardPublishCache = /* @__PURE__ */ new Map();
|
|
688
675
|
// Reverse mapping: opaque topicKey → plaintext topic. A bounded cache with
|
|
689
676
|
// FIFO eviction — NOT authoritative. It can hold a topicKey that is also in
|
|
690
677
|
// assignedTopics (the owned guard prevents evicting those), because it is
|
|
@@ -761,7 +748,6 @@ var WorkerClusterRuntime = class {
|
|
|
761
748
|
this.subscribedTopics.clear();
|
|
762
749
|
this.assignedTopics.clear();
|
|
763
750
|
this.routeOwnerCache.clear();
|
|
764
|
-
this.wildcardPublishCache.clear();
|
|
765
751
|
this.knownTopics.clear();
|
|
766
752
|
this.suspended = false;
|
|
767
753
|
}
|
|
@@ -829,7 +815,6 @@ var WorkerClusterRuntime = class {
|
|
|
829
815
|
this.handoffAssignedTopics();
|
|
830
816
|
this.assignedTopics.clear();
|
|
831
817
|
this.routeOwnerCache.clear();
|
|
832
|
-
this.wildcardPublishCache.clear();
|
|
833
818
|
this.removeStorage(this.workerStorageKey(this.workerId));
|
|
834
819
|
this.flushStorage();
|
|
835
820
|
this.notifyRegistry();
|
|
@@ -936,16 +921,6 @@ var WorkerClusterRuntime = class {
|
|
|
936
921
|
if (this.assignedTopics.has(topicKey)) {
|
|
937
922
|
return this.sendControl(this.workerId, CONTROL_ACTION.PUBLISH, topic, topicKey, data, metadata);
|
|
938
923
|
}
|
|
939
|
-
const cachedPattern = this.wildcardPublishCache.get(topic);
|
|
940
|
-
if (cachedPattern === void 0) {
|
|
941
|
-
for (const pattern of this.assignedTopics.values()) {
|
|
942
|
-
if (pattern !== topic && topicMatchesPattern(pattern, topic)) {
|
|
943
|
-
this.wildcardPublishCache.set(topic, pattern);
|
|
944
|
-
return this.sendControl(this.workerId, CONTROL_ACTION.PUBLISH, topic, topicKey, data, metadata);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
this.wildcardPublishCache.set(topic, null);
|
|
948
|
-
}
|
|
949
924
|
return this.sendControl(this.resolvePublishTarget(topic, topicKey), CONTROL_ACTION.PUBLISH, topic, topicKey, data, metadata);
|
|
950
925
|
}
|
|
951
926
|
/**
|
|
@@ -970,17 +945,6 @@ var WorkerClusterRuntime = class {
|
|
|
970
945
|
this.dispatchLocalPublishBatch(topic, topicKey, items);
|
|
971
946
|
return true;
|
|
972
947
|
}
|
|
973
|
-
const cachedPattern = this.wildcardPublishCache.get(topic);
|
|
974
|
-
if (cachedPattern === void 0) {
|
|
975
|
-
for (const pattern of this.assignedTopics.values()) {
|
|
976
|
-
if (pattern !== topic && topicMatchesPattern(pattern, topic)) {
|
|
977
|
-
this.wildcardPublishCache.set(topic, pattern);
|
|
978
|
-
this.dispatchLocalPublishBatch(topic, topicKey, items);
|
|
979
|
-
return true;
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
this.wildcardPublishCache.set(topic, null);
|
|
983
|
-
}
|
|
984
948
|
const target = this.resolvePublishTarget(topic, topicKey);
|
|
985
949
|
if (target === this.workerId) {
|
|
986
950
|
this.dispatchLocalPublishBatch(topic, topicKey, items);
|
|
@@ -2511,7 +2475,7 @@ var SDK_VERSION = (
|
|
|
2511
2475
|
// declaration's type is `string | undefined` and the export's is `string`
|
|
2512
2476
|
// (dropping the conditional is TS2322, measured). Recorded here so the leg reads
|
|
2513
2477
|
// as a shape the harness cannot reach rather than as a case nobody exercised.
|
|
2514
|
-
true ? "0.21.
|
|
2478
|
+
true ? "0.21.33" : ""
|
|
2515
2479
|
);
|
|
2516
2480
|
|
|
2517
2481
|
// src/core/data-bus.ts
|
|
@@ -3751,4 +3715,4 @@ export {
|
|
|
3751
3715
|
parseDataBusPublication,
|
|
3752
3716
|
selectWorkerBackend
|
|
3753
3717
|
};
|
|
3754
|
-
//# sourceMappingURL=chunk-
|
|
3718
|
+
//# sourceMappingURL=chunk-FEWEXHZA.js.map
|