cross-tab-worker-databus 0.21.7 → 0.21.9
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-protocol.d.ts +7 -0
- package/dist/centrifuge-protocol.d.ts.map +1 -1
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +5 -2
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +8 -1
- package/dist/centrifuge.shared.worker.js.map +2 -2
- package/dist/centrifuge.worker.js +2 -1
- package/dist/centrifuge.worker.js.map +2 -2
- package/dist/{chunk-TZ7ZP7YD.js → chunk-2TTJ7LGZ.js} +3 -2
- package/dist/{chunk-TZ7ZP7YD.js.map → chunk-2TTJ7LGZ.js.map} +2 -2
- package/dist/{chunk-JQEKC5WW.js → chunk-75ZDU7VG.js} +3 -3
- package/dist/chunk-75ZDU7VG.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +6 -2
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/hooks.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/cjs/vue.cjs.map +1 -1
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/hooks.js +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +2 -2
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/vue.js +1 -1
- package/dist/workers/port-reaper.d.ts +4 -1
- package/dist/workers/port-reaper.d.ts.map +1 -1
- package/docs/api.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/benchmarks.md +7 -7
- package/docs/configuration.md +2 -1
- package/docs/release-checklist.md +1 -1
- package/docs/roadmap.md +27 -1
- package/docs/transports.md +6 -1
- package/docs/zh/api.md +1 -1
- package/docs/zh/architecture.md +1 -1
- package/docs/zh/benchmarks.md +7 -7
- package/docs/zh/configuration.md +2 -1
- package/docs/zh/release-checklist.md +1 -1
- package/docs/zh/roadmap.md +21 -1
- package/docs/zh/transports.md +4 -1
- package/package.json +1 -1
- package/dist/chunk-JQEKC5WW.js.map +0 -7
package/docs/transports.md
CHANGED
|
@@ -96,7 +96,12 @@ reference shape, including:
|
|
|
96
96
|
error handlers check it so late errors from a superseded Worker cannot
|
|
97
97
|
corrupt the fresh session.
|
|
98
98
|
- **SharedWorker heartbeat**: if you use a SharedWorker, send periodic PINGs
|
|
99
|
-
so a `PortReaper` can reclaim dead-tab sessions
|
|
99
|
+
so a `PortReaper` can reclaim dead-tab sessions, and handle the
|
|
100
|
+
`SESSION_REAPED` message the reaper posts before it closes a port. That
|
|
101
|
+
message is the only signal a starved-but-still-alive tab can receive (a
|
|
102
|
+
`MessagePort` has no close event, and posting into a closed one delivers
|
|
103
|
+
nothing), and this transport turns it into a reported backend failure so the
|
|
104
|
+
recovery path rebuilds the session.
|
|
100
105
|
|
|
101
106
|
### 4. Expose as a subpath
|
|
102
107
|
|
package/docs/zh/api.md
CHANGED
|
@@ -296,7 +296,7 @@ trace: {
|
|
|
296
296
|
}
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
-
低频事件类型包括 `lifecycle`、`status`、`subscription`、`coordination`、`reliability` 和 `error`;高频数据按窗口输出 `message_metrics`,包含接收/分发计数、活跃 Topic 数量和分发延迟聚合(`dispatchSamples`、`dispatchAvgMs`、`dispatchP50Ms`、`dispatchP95Ms`、`dispatchMaxMs`),以及去重结果(`dedupAccepted`、`dedupSuppressed`)。路由归属变化通过 `reliability` 事件呈现:优雅交接上报 `operation: 'route_migration'`,而恢复悬挂未确认交接的重选上报 `operation: 'route_migration_recovery'`(前任 owner 已消失且 ACK 始终未到达),便于 trace 消费者区分恢复与常规交接。`coordination` 事件在每次 transport 打开后发出,携带 `coordinated`、`activeWorkers`、`workers`(格式化后的 worker 记录)与 `routes`(`topicKey@workerId|confirmed
|
|
299
|
+
低频事件类型包括 `lifecycle`、`status`、`subscription`、`coordination`、`reliability` 和 `error`;高频数据按窗口输出 `message_metrics`,包含接收/分发计数、活跃 Topic 数量和分发延迟聚合(`dispatchSamples`、`dispatchAvgMs`、`dispatchP50Ms`、`dispatchP95Ms`、`dispatchMaxMs`),以及去重结果(`dedupAccepted`、`dedupSuppressed`)。路由归属变化通过 `reliability` 事件呈现:优雅交接上报 `operation: 'route_migration'`,而恢复悬挂未确认交接的重选上报 `operation: 'route_migration_recovery'`(前任 owner 已消失且 ACK 始终未到达),便于 trace 消费者区分恢复与常规交接。`coordination` 事件在每次 transport 打开后发出,携带 `coordinated`、`activeWorkers`、`workers`(格式化后的 worker 记录)与 `routes`(`topicKey@workerId|confirmed=…`,反映已收敛的路由列表)。所有公开事件都使用固定结构,其中不会携带消息 payload、连接地址或错误正文。Topic 明文只出现在两种事件里:`subscription` 记录订阅变更,`reliability` 则为涉及具体路由的操作标注该路由(`route_ack`、`route_migration`、`route_migration_recovery`,其余 reliability 操作不带 Topic);其它事件不含 Topic,`coordination` 使用不可逆的 `topicKey` 而非名称来标识路由。既然这两种事件确实携带 Topic 名称,就应把 trace sink 视为诊断出口:在把事件转发给外部监控前,按业务 Topic 约定脱敏,详见 `configuration.md`。sink 抛错会被隔离,不会中断消息分发,但会向 `console.warn` 输出错误,便于定位诊断配置问题。sink 应尽量避免抛出异常——预期中的错误条件应通过事件数据表达,而不是通过异常上报。
|
|
300
300
|
|
|
301
301
|
**`asyncSink: true` 的投递语义。** 默认(`false`)下,每条事件同步调用 sink。开启 `asyncSink: true` 后,事件先入内存队列,在**一个微任务批次**中统一投递:任务的第一条事件调度 `queueMicrotask`,在该微任务运行前产生的所有事件(含周期性 `message_metrics` 快照)按 FIFO 顺序一次性送往 sink。这样热路径永远不会因 sink 工作而阻塞。错误隔离与同步模式一致——sink 抛错被捕获并记入 `console.warn`,既不会中断分发,也不会中断批次内其余事件。顺序保证边界:**批次内**顺序是确定的,但投递被推迟到下一个微任务,因此事件不再保证在你的下一行语句执行前可见;批次 flush 之后新产生的事件会落入后续批次。当需要"每条事件在下一行代码前可见"时,请使用默认的同步 sink(或自行合并计数)。
|
|
302
302
|
|
package/docs/zh/architecture.md
CHANGED
|
@@ -41,7 +41,7 @@ graph TB
|
|
|
41
41
|
|
|
42
42
|
默认 `workerMode: 'dedicated'` 时,每个 Tab 使用独立的 transport Worker。配置为 `shared` 或 `auto` 且浏览器支持 SharedWorker 时,同源 Tab 复用同一个 SharedWorker;SharedWorker 内每个连接 port 各自维护独立的 `CentrifugeSession`,一个 Tab 刷新或停止不会影响其他 Tab。`auto` 模式按 **SharedWorker → Dedicated Worker → 主线程 WebSocket** 降级,`dedicated` 模式按 **Dedicated Worker → SharedWorker → 主线程 WebSocket** 降级。`BroadcastChannel` 只负责控制消息和实时 publication 转发;localStorage 只负责最终一致的协调元数据。
|
|
43
43
|
|
|
44
|
-
由于 `MessagePort` 没有 `close` 事件,Tab 崩溃且未发送 `STOP` 时会遗留 session 和 WebSocket。主线程因此每 10 秒发送一次 `PING`,SharedWorker 对超过 30 秒无消息的 port 执行回收,释放对应 session
|
|
44
|
+
由于 `MessagePort` 没有 `close` 事件,Tab 崩溃且未发送 `STOP` 时会遗留 session 和 WebSocket。主线程因此每 10 秒发送一次 `PING`,SharedWorker 对超过 30 秒无消息的 port 执行回收,释放对应 session 及其订阅。但“沉默”并不等于“不存在”,因此被回收的端口会在关闭前先收到一条通知:心跳只是被饿死(长同步任务、后台 Tab 受到的定时器节流)的 Tab 由此得知 backend 已失效,会上报该失败并在新端口上重建会话。
|
|
45
45
|
|
|
46
46
|
## 分层
|
|
47
47
|
|
package/docs/zh/benchmarks.md
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
# 浏览器基准趋势
|
|
4
4
|
|
|
5
|
-
> 数据截至 2026-09-22,基于
|
|
5
|
+
> 数据截至 2026-09-22,基于 69 份归档的 `bench-results/browser-*.json` 报告(运行 `pnpm bench:browser` 追加一份;用 `node scripts/bench-trend.mjs` 重新生成本文档)。
|
|
6
6
|
|
|
7
7
|
发布门禁的对比基线是 `pnpm bench:compare --fail-above-pct 50`:将最新报告与其之前若干报告中同一指标的中位数比较(50% 上限用于吸收共享 runner 的噪声;此前只看"上一份报告"并不稳定,因为页内热路径指标在同一份代码上会在快/慢两档之间来回跳)。本文记录长期趋势:数值为逐指标延迟,越低越好;最后一列是最近 5 份报告内的最优值,而不是历史纪录——页内基准矩阵在 2026 年 9 月初变更过测量语义,更早的报告不可比。
|
|
8
8
|
|
|
9
9
|
<!-- BENCH-TREND:BEGIN (machine-generated table) -->
|
|
10
10
|
| 指标 | 上次 (ms) | 本次 (ms) | Δ | 近 5 次最优 (ms) |
|
|
11
11
|
|---|---|---|---|---|
|
|
12
|
-
| publish per-message (ms, lower is better) — dedicated |
|
|
13
|
-
| publish per-message (ms, lower is better) — shared |
|
|
14
|
-
| wildcard dispatch ×1000 (ms, lower is better) |
|
|
15
|
-
| publishBatch ×1000 (ms, lower is better) |
|
|
16
|
-
| dedup ×1000 (ms, lower is better) |
|
|
17
|
-
| trace + publish ×1000 (ms, lower is better) |
|
|
12
|
+
| publish per-message (ms, lower is better) — dedicated | 80.1971 | 63.1886 | -17.01 | 62.3503 |
|
|
13
|
+
| publish per-message (ms, lower is better) — shared | 49.8516 | 54.1494 | +4.30 | 39.6914 |
|
|
14
|
+
| wildcard dispatch ×1000 (ms, lower is better) | 7.9 | 6.6 | -1.30 | 6.5 |
|
|
15
|
+
| publishBatch ×1000 (ms, lower is better) | 4.4 | 5 | +0.60 | 4.4 |
|
|
16
|
+
| dedup ×1000 (ms, lower is better) | 14 | 15.6 | +1.60 | 14 |
|
|
17
|
+
| trace + publish ×1000 (ms, lower is better) | 5.3 | 5.9 | +0.60 | 5.2 |
|
|
18
18
|
| first-packet cold dispatch (ms, lower is better) | 0 | 0 | +0.00 | 0 |
|
|
19
19
|
<!-- BENCH-TREND:END -->
|
|
20
20
|
|
package/docs/zh/configuration.md
CHANGED
|
@@ -206,11 +206,12 @@ storage 事件只在其他 document 中派发。因此同一 document 内共享
|
|
|
206
206
|
- **心跳间隔**:`heartbeatIntervalMs`(默认 `10000` ms)。主线程按此间隔发送 `PING`。传 `Infinity` 完全禁用心跳——仅在确保 SharedWorker 会随 Tab 一起销毁时使用。
|
|
207
207
|
- **会话超时**:`3 × heartbeatIntervalMs`(默认 `30000` ms)。超过超时未收到消息的端口会被回收:其会话停止,WebSocket 关闭。这与 Core 集群心跳(默认 `3000` ms,通过 localStorage 跟踪 worker 存活)相互独立——见下方说明。
|
|
208
208
|
- **自适应频率**:回收器以所有活动端口中最小的心跳间隔运行,使短心跳端口的会话能被及时回收。当最后一个端口断开时,回收器定时器清除,避免长时间存在的 SharedWorker 在连接爆发间隙运行永久的空循环。
|
|
209
|
+
- **关闭端口前先通知 Tab**:被回收的端口会先收到最后一条 `SESSION_REAPED` 消息,然后才被关闭、其会话才被停止。顺序正是关键所在——`MessagePort` 没有 close 事件,而向一个对端已关闭的端口发送消息会“成功”却什么都不送达,这是被饿死的 Tab 唯一能得知真相的途径。此前,一个只是被长时间阻塞、并没有消失的 Tab(一个很长的同步任务,或后台 Tab 受到的定时器节流)会保留自己的集群角色与 topic 路由,而它发出的每条发布都消失在已关闭的端口里,`getHealthSummary()` 仍在报告 `healthy` / `connected`。收到该消息后,transport 会丢弃这个 backend,并通过 `onError` 与 `error` 状态报告失败,正常恢复路径随后在新端口上重建会话。真实浏览器实测:34 秒阻塞之后,Tab 自行重建了订阅——无需用户操作、无需生命周期事件——观察时这条替代连接建立尚不到一秒。
|
|
209
210
|
- **先关闭端口再停止会话**:回收端口时,先关闭端口,再停止会话。关闭端口会丢弃会话的 `disconnected` 状态通知(使其不会到达可能仍在运行但缓慢的主线程),并保证已关闭的端口永远无法传递后续消息,从而在回收器追踪之外复活僵尸会话。
|
|
210
211
|
- **失败隔离**:回收与 `dispose()` 都用 try-catch 包裹 `target.close()`/`target.stop()`,单个异常端口不会中断本轮回收,也不会让后续死 Tab 无人回收。
|
|
211
212
|
- **关闭清理**:SharedWorker 关闭时,`PortReaper.dispose()` 停止定时器并关闭/停止**所有**仍被追踪的会话,确保没有任何 `CentrifugeSession` 或 WebSocket 比 reaper 活得更久。这补充了按端口回收——后者只覆盖 reaper 运行期间静默的端口。
|
|
212
213
|
|
|
213
|
-
这是从崩溃(未发送 `STOP`)的 Tab
|
|
214
|
+
这是从崩溃(未发送 `STOP`)的 Tab 中恢复会话的机制;而它发出的那条通知,正是没有崩溃、只是被饿死的 Tab 能够察觉并重连的原因。降低 `heartbeatIntervalMs` 可更快回收死会话,代价是端口上更频繁的 PING 消息——另外请注意:如果它的值低于页面最长任务耗时的约三分之一,活着的 Tab 也会被开始回收,而每一次回收都要靠重建会话来恢复。
|
|
214
215
|
|
|
215
216
|
`heartbeatIntervalMs` 必须为正数或 `Infinity`;`0`、负数或 `NaN` 会导致 transport 构造函数立即抛出 `TypeError`(否则 `setInterval` 会降级为 0ms 忙循环)。
|
|
216
217
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 发布检查清单
|
|
2
2
|
|
|
3
|
-
每个 1.0.0
|
|
3
|
+
每个 1.0.0 之前的版本都按此清单执行。推送版本标签即为发布:`Release` 工作流会在标签上运行,由于本仓库已配置 `NPM_TOKEN`,它会发布到 npm,随后运行阻塞式的 `verify:published` 校验。本文末尾的「手动 `npm publish`」只适用于不存在该 secret 的仓库状态。
|
|
4
4
|
|
|
5
5
|
## 公共 API 稳定性与弃用策略(1.0 之前)
|
|
6
6
|
|
package/docs/zh/roadmap.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# 路线图
|
|
2
2
|
|
|
3
|
-
0.21.
|
|
3
|
+
0.21.9 已于 2026 年 9 月 23 日发布。项目会先持续完成可靠性发布,再进入 1.0.0 稳定性冻结。
|
|
4
|
+
|
|
5
|
+
## 0.21.9 已完成范围
|
|
6
|
+
|
|
7
|
+
- 被饿死的 SharedWorker Tab 曾经会静默丢失 transport 并且永不恢复:一个长同步任务,或后台
|
|
8
|
+
Tab 受到的定时器节流,都会让共享线程里的回收器收回该端口;而 `MessagePort` 没有 close
|
|
9
|
+
事件、向已关闭端口发送消息会“成功”却什么都不送达,于是这个 Tab 仍在持有自己的路由、仍在
|
|
10
|
+
报告 `healthy` / `connected`,而它发出的发布全部消失。现在回收器会在关闭端口前先在该端口上发出这次回收的通知,transport 把它当作一次普通的 backend 失效,于是既有的恢复流程会重建会话。
|
|
11
|
+
真实浏览器两个方向都验证过:有通知时 36.7 秒自动重建,去掉通知后 90 秒内始终不恢复。
|
|
12
|
+
- 共享模式的所有权迁移此前完全没有浏览器级覆盖。现在三个共享 Tab 会固定住:关闭 owner 时,
|
|
13
|
+
那条唯一的服务端订阅必须落到一条本来就早已打开的连接上,并且交接之后两个方向都仍能投递。
|
|
14
|
+
- 没有改动任何线格式、存储布局或集群协议版本:新增的消息只在页面与它自己的 Worker 之间流动,
|
|
15
|
+
不认识该类型的主线程会直接忽略。
|
|
16
|
+
|
|
17
|
+
## 0.21.8 已完成范围
|
|
18
|
+
|
|
19
|
+
- 第二个随包分发的句子同样是错的,性质与 0.21.7 那条一致。`docs/api.md` 在中英文里都承诺任何公开 trace 事件都不包含原始 Topic,而 `docs/configuration.md` 在两种语言里写的恰恰相反,还进一步提醒接入方在把 sink 转发给遥测前对 Topic 名称脱敏。事实是:subscription 事件携带它所报告的 Topic,涉及具体路由的 `reliability` 操作也一样。修正后的文字只陈述测量到的东西:任何事件都不携带消息 payload、连接地址或错误正文;Topic 明文恰好只出现在这两种事件里;`coordination` 使用不可逆的 key 来标识路由。现在有一个测试把 payload 哨兵与错误正文哨兵穿过真实的 bus,并断言 sink 两者都看不到——这是这两条声明第一次拥有见证。
|
|
20
|
+
- 套件里那个长期被当作共享 runner 抖动的 E2E 失败并不是负载。shared 模式的下线用例断言的是 demo server 的全局连接数,而 specs 是并行跑的,其它存活 tab 都会被算进去;即便通过也是空过:一个 topic 只有 owner 的 transport 会订阅它的频道,所以「2」可能是一个持有频道的 socket 加上一个什么都没订阅的 socket。现在 socket 按各自持有的频道来寻址,用例也同时断言两半:关闭的 tab 的 socket 必须从服务器列表里消失,存活 tab 的那个必须仍在列表里。把该用例重复三次(旧断言下三次全败)现在能通过。
|
|
21
|
+
- 两个带种子的不变量 harness 分别跑到出厂深度的 80 倍与 133 倍(400,000 与 200,000 个种子,753.7 秒与 262.6 秒),没有发现任何破坏,因此剩下的零计数腿是分类工作,而不是隐藏的协同缺陷。它们的文件头现在记录了可用深度,以及能结束一次深跑的三道彼此独立的限制。
|
|
22
|
+
- 一个一直被读反的覆盖零值现在按它真正计量的东西标注:当 `??` 的兜底是一个箭头函数时,分支区间落在该闭包体内,所以这一臂统计的是被调用,而不是有多少调用方省略了参数。全仓库扫描下来,这是唯一一条这种形状的腿,规则已写进 `AGENTS.md`。
|
|
23
|
+
- 本次发布没有行为、线格式或存储布局的变更。
|
|
4
24
|
|
|
5
25
|
## 0.21.7 已完成范围
|
|
6
26
|
|
package/docs/zh/transports.md
CHANGED
|
@@ -85,7 +85,10 @@ transport 选择后端(SharedWorker / Dedicated Worker / 本地),向它发
|
|
|
85
85
|
- **generation 守卫**:创建后端时递增单调计数器;错误处理检查它,使被取代的
|
|
86
86
|
Worker 的迟到错误不会污染新 session。
|
|
87
87
|
- **SharedWorker 心跳**:若用 SharedWorker,定期发 PING,让 `PortReaper` 能
|
|
88
|
-
回收死 tab 的 session
|
|
88
|
+
回收死 tab 的 session,并处理回收器在关闭端口前发出的 `SESSION_REAPED`
|
|
89
|
+
消息。对被饿死但仍存活的 tab 来说,那是唯一能收到的信号(`MessagePort` 没有
|
|
90
|
+
close 事件,向已关闭的端口发送消息什么都不会送达),本 transport 会把它转成
|
|
91
|
+
一次上报的 backend 失败,让恢复流程得以重建 session。
|
|
89
92
|
|
|
90
93
|
### 4. 作为 subpath 暴露
|
|
91
94
|
|
package/package.json
CHANGED