cross-tab-worker-databus 0.20.91 → 0.20.93

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,6 +1,43 @@
1
1
  ## [Unreleased]
2
2
 
3
- No unreleased changes yet.
3
+ ## [0.20.93] - 2026-09-19
4
+
5
+ ### Fixed
6
+ - The compatibility gate now rejects disabled public exports, removed worker default conditions, conditional exports replaced with untyped strings or null type targets, incompatible package-level `types`/`typesVersions` metadata, unavailable unconditional targets, destructive fallback-array changes, and removed nested or custom export conditions.
7
+ - Storage capability detection now requires a complete write-read-delete round trip, so write-only or unreadable adapters enter the existing local-mode fallback instead of starting coordination against an unusable registry.
8
+ - Cluster startup now degrades safely when channel construction throws, and tab identity remains stable within the document when session storage is unavailable or unreadable.
9
+ - Storage writer cleanup now cancels pending retries and resets backoff before a failing clear and after final cluster teardown flushes, preventing background writes from surviving page hide or stop.
10
+
11
+ ### Changed
12
+ - Refreshed patch-level development dependencies used by the test and lint toolchain.
13
+
14
+ ## [0.20.92] - 2026-09-18
15
+
16
+ ### Fixed
17
+ - Release automation now rejects mismatched tag/package versions and missing, duplicate or empty release notes before creating a release or publishing. Release guidance uses feature-branch PRs and immutable version tags.
18
+ - A rejected transport `stop()` is now retained consistently in both failure ledgers. `stop()` still resolves after reporting through `onError`, but `getRecoveryStats()` no longer reports `hasError: false` while `getHealthSummary().lastFailure` still describes the same stop failure; an explicit `start()` remains the boundary that clears both.
19
+ - Operations parked behind the recovery gate now remain queued when an explicit `start()` supersedes the automatic recovery timer. If that manual open also fails, the operations stay behind the still-closed gate and flush after the next successful automatic or demand-driven reopen instead of being silently dropped with the superseded opening.
20
+ - A transport operation parked behind the recovery gate is now invalidated when `stop()` / page-hide supersedes the recovery cycle. An immediate explicit `start()` can re-establish subscriptions on the replacement transport, but the stale waiter no longer replays its operation afterward, preventing a duplicate subscription from racing the restarted cluster.
21
+ - A delayed or replayed `CONTROL/SUBSCRIBE` can no longer make a worker subscribe when the durable route does not currently name it, and it can no longer confirm a pending graceful handoff before the matching `ROUTE_RELEASED`. Ownership now follows the route record instead of control-frame arrival order, preventing transient double subscriptions when an earlier assignment round is overtaken by a newer one.
22
+ - A restart queued behind an asynchronous `stop()` now stays suspended when `pagehide` lands during the stop cleanup. `WorkerClusterRuntime.start()` installs its lifecycle listeners before checking document visibility, so a hidden document waits for `pageshow` instead of opening a fresh transport in the background.
23
+ - `ROUTE_RELEASED` handoff acknowledgements are now accepted only when their route generation exactly matches the stored route. A delayed or replayed ACK carrying a newer generation could previously confirm a different handoff and release the new owner's `SUBSCRIBE` before the matching release arrived.
24
+ - Durable replay history loaded asynchronously can no longer overtake or evict publications recorded while `load()` is still pending. Hydration now places the durable snapshot ahead of that live tail before applying the retention/count policy, so a bounded ring keeps the newest messages instead of treating them as older history.
25
+ - Replay hydration now belongs to a replaceable lifecycle instead of one immutable constructor promise. A `clearAll()`, `clearTopic()`, topic unsubscribe, or `clearBefore()` issued while durable `load()` is pending is applied to the loaded snapshot, so cleared history cannot reappear. A load superseded by suspend/restart is cancelled without clobbering the replacement, and an explicit stop/start now performs a fresh hydration instead of leaving the restarted rings empty.
26
+ - A `start()` issued synchronously from the `DISCONNECTED` status callback during `pagehide` can no longer be undone by the stale hide continuation. `suspendTransport()` now abandons its stop chain when the status callback has already installed a newer lifecycle, so the replacement transport stays open and the health snapshot remains truthful instead of reporting `healthy` while the transport has been stopped again.
27
+ - Operations issued while a replacement transport is opening can no longer reach the closed connection. `reopenTransport()` now clears `transportReady` before its synchronous `CONNECTING` notification, so a second `publish()` / `subscribe()` in the same tick parks behind the opening gate instead of bypassing it. This also preserves operation order: the replacement receives the earlier parked operation before the later one.
28
+ - Replay retention cleanup queued after a suspend/resume cycle is no longer dropped when an older cleanup transaction is still unwinding. The old pass now hands a non-null queued cutoff to a fresh cleanup before releasing the in-flight slot, so the newest cutoff runs instead of waiting for an unrelated future publication.
29
+ - A transport operation that was already parked behind the recovery gate when an automatic recovery attempt failed is no longer stranded until an unrelated later operation arrives. `runTransport()` now counts the operations parked on the gate, and a failed automatic attempt immediately starts one on-demand reopen when any waiter is present, so an already-issued `publish()` / `subscribe()` itself drives recovery instead of waiting for a future operation that may never come. A failed on-demand reopen still re-arms the demand flag for a later operation rather than looping on its own failure, and every waiter is released in order once a reopen succeeds.
30
+ - A re-entrant `stop()` from the synchronous RESUME lifecycle trace event now cancels the rest of both an explicit `start()` resume and a native `pageshow` resume. Resume timers are only restarted when the callback still owns the lifecycle, and `WorkerClusterRuntime` now uses a lifecycle generation so its outer `pageshow` handler cannot reactivate a cluster already stopped or paused by `onResume`. Previously the callback's stop could settle first while the outer resume continued and left the bus reported as stopped with an active cluster.
31
+ - A re-entrant `stop()` from the synchronous CONNECTING status callback emitted during `reopenTransport()` now cancels that reopen instead of allowing it to install and start a new transport after teardown. The recovery lifecycle epoch and start promise are now installed before the status callback runs, so the stop gate observes the opening, its epoch guard abandons it, and health remains consistently stopped. Previously the callback's stop settled first while `reopenTransport()` continued and left `started: false` with a live connected transport.
32
+ - A re-entrant `stop()` from the synchronous START lifecycle trace event now supersedes the outer `start()` before it can open the transport. Lifecycle ownership and the shared start promise are installed before the trace event, and `start()` aborts the remaining timer/cluster setup if a callback stops or suspends the new lifecycle. Previously the stop settled while the outer start continued into `transport.start()`, leaving health reported as `stopped` with a live `connected` transport.
33
+ - `stop()` is now re-entrancy safe against a synchronous trace sink. The shared in-flight stop gate is installed before the teardown prelude runs, so a `stop()` re-entered from the synchronous STOP lifecycle trace event shares the single teardown instead of starting a second one. Previously the nested call ran before `stopPromise` was assigned and invoked `transport.stop()` a second time. The teardown still takes effect in the same tick, concurrent and repeated `stop()` calls keep returning the same promise, and a rejecting transport stop still resolves `stop()` through `onError`.
34
+
35
+ ### Tests
36
+ - A parked-recovery cancellation regression covers page-hide followed by an immediate explicit `start()`: the replacement connection receives exactly one subscription for each assigned topic, and the stale recovery waiter cannot replay after that restart.
37
+ - A route-generation guard regression proves a `ROUTE_RELEASED` carrying a newer generation than the current handoff cannot enter `assignedTopics`, trigger `SUBSCRIBE`, or stamp `confirmedAt`.
38
+ - A delayed durable `load()` now has a direct replay ordering regression: a publication recorded while hydration is pending must remain newer than the loaded snapshot and survive a one-entry count cap.
39
+ - Hydration lifecycle regressions now cover `clearAll()`, `clearTopic()`, topic unsubscribe, and `clearBefore()` racing a pending load, plus explicit stop/start and immediate suspend/start re-entry. The replacement load must win, stale results must stay out of the rings, and durable history must be restored after an explicit restart.
40
+ - The documented storage and BroadcastChannel isolation boundary between different `clusterKey` values is now pinned by a two-runtime regression. It proves each tenant can independently own the same topic, publications do not cross, and persisted keys are namespaced under distinct opaque hashes without exposing the plaintext cluster identifier.
4
41
 
5
42
  ## [0.20.91] - 2026-09-16
6
43
 
@@ -5,7 +5,7 @@ import {
5
5
  parseDataBusPublication,
6
6
  publicationMetadata,
7
7
  selectWorkerBackend
8
- } from "./chunk-DMM5KBNG.js";
8
+ } from "./chunk-OXEPZGXR.js";
9
9
  import {
10
10
  CENTRIFUGE_INPUT_TYPE,
11
11
  CENTRIFUGE_OUTPUT_TYPE,