cross-tab-worker-databus 0.8.0 → 0.11.0

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +2 -0
  3. package/README.zh.md +2 -0
  4. package/dist/centrifuge-protocol.d.ts +4 -0
  5. package/dist/centrifuge-protocol.d.ts.map +1 -1
  6. package/dist/centrifuge-session.d.ts.map +1 -1
  7. package/dist/centrifuge.js +29 -11
  8. package/dist/centrifuge.js.map +2 -2
  9. package/dist/centrifuge.shared.worker.js +37 -6
  10. package/dist/centrifuge.shared.worker.js.map +3 -3
  11. package/dist/centrifuge.worker.js +37 -6
  12. package/dist/centrifuge.worker.js.map +3 -3
  13. package/dist/{chunk-WWUY2FV2.js → chunk-NX76TAV3.js} +137 -26
  14. package/dist/chunk-NX76TAV3.js.map +7 -0
  15. package/dist/cjs/centrifuge.cjs +164 -35
  16. package/dist/cjs/centrifuge.cjs.map +4 -4
  17. package/dist/cjs/index.cjs +172 -38
  18. package/dist/cjs/index.cjs.map +3 -3
  19. package/dist/core/cluster.d.ts +3 -2
  20. package/dist/core/cluster.d.ts.map +1 -1
  21. package/dist/core/data-bus.d.ts +19 -0
  22. package/dist/core/data-bus.d.ts.map +1 -1
  23. package/dist/core/publication.d.ts +10 -0
  24. package/dist/core/publication.d.ts.map +1 -0
  25. package/dist/core/replay-persistence.d.ts +2 -0
  26. package/dist/core/replay-persistence.d.ts.map +1 -1
  27. package/dist/core/trace.d.ts +7 -0
  28. package/dist/core/trace.d.ts.map +1 -1
  29. package/dist/core/types.d.ts +19 -7
  30. package/dist/core/types.d.ts.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +39 -14
  34. package/dist/index.js.map +2 -2
  35. package/dist/websocket.d.ts.map +1 -1
  36. package/docs/api.md +27 -6
  37. package/docs/architecture.md +7 -1
  38. package/docs/capabilities.md +2 -2
  39. package/docs/configuration.md +1 -1
  40. package/docs/roadmap.md +15 -1
  41. package/docs/transports.md +9 -5
  42. package/docs/zh/api.md +27 -6
  43. package/docs/zh/architecture.md +7 -1
  44. package/docs/zh/capabilities.md +2 -2
  45. package/docs/zh/configuration.md +1 -1
  46. package/docs/zh/transports.md +8 -4
  47. package/package.json +1 -1
  48. package/dist/chunk-WWUY2FV2.js.map +0 -7
@@ -22,7 +22,7 @@ Status Legend: `✅ Implemented` means the current version has code and test cov
22
22
  | Degradation | Runs locally when localStorage or BroadcastChannel is unavailable | ✅ Implemented | Preserves the current Tab's connection and subscription capabilities |
23
23
  | Centrifuge | Built-in Dedicated / Shared Worker transport | ✅ Implemented | Supports subscribe, unsubscribe, publish, connection status, and error reporting; `auto` degrades from SharedWorker → Dedicated Worker → main-thread WebSocket |
24
24
  | Security Boundary | localStorage uses opaque keys derived from connection and Topic; BroadcastChannel coordination messages carry plaintext topic names | ✅ Implemented | Does not persist URLs, raw Topic names, credentials, or publication payloads. BroadcastChannel coordination messages are in-memory only and carry plaintext topic names — they are not persisted. |
25
- | Diagnostics | Aggregates lifecycle events, throughput, delivery latency, recovery retries, route acknowledgments, and migrations | ✅ Implemented | Disabled by default; metrics are emitted every 5 seconds by default, with bounded reliability events for recovery and route coordination |
25
+ | Diagnostics | Aggregates lifecycle events, throughput, delivery latency, dedup outcomes, recovery retries, route acknowledgments, and migrations | ✅ Implemented | Disabled by default; metrics are emitted every 5 seconds by default, with bounded reliability events for recovery and route coordination |
26
26
  | Performance | Batched writes of coordination metadata with backoff retry | ✅ Implemented | Heartbeat, route, and subscriber writes are merged and flushed in a microtask; failures use exponential backoff; `pagehide` / `stop()` flush synchronously |
27
27
  | Performance | Optional ArrayBuffer Transferable transport | ✅ Implemented | With `transferable: true`, binary publish / receive bypasses structured clone copying; the object message API is unchanged |
28
28
  | Message Semantics | exactly-once delivery | Not Implemented | Graceful handoff avoids overlap, but crash recovery and transport/server behavior still do not provide an exactly-once guarantee |
@@ -31,7 +31,7 @@ Status Legend: `✅ Implemented` means the current version has code and test cov
31
31
  | Load Policy | Adaptive weighting by message rate, byte count, or CPU | Planned | Load is currently computed only from the number of owner Topics |
32
32
  | Observability | Metrics/events for owner acknowledgments, migrations, and recovery attempts | ✅ Implemented | `DataBusReliabilityTraceEvent` reports bounded route ack/migration and transport recovery events; exact server-side ack remains transport-specific |
33
33
  | Runtime Model | SharedWorker / Dedicated Worker transport | ✅ Implemented | `workerMode` supports `dedicated`, `shared`, and `auto`, defaulting to `dedicated` |
34
- | Runtime Model | Service Worker transport | Not Implemented | Service Worker hosting of real-time connections is not currently provided |
34
+ | Runtime Model | Service Worker transport | Not Implemented | Deliberately deferred; lifetime and long-lived connection constraints are documented in `docs/architecture.md` |
35
35
  | Durable Messages | Persisting publications or publish commands across page close | Not Implemented | The SDK does not persist business payloads, nor does it replay publish commands after restoration |
36
36
 
37
37
  ## Acceptance Criteria
@@ -46,7 +46,7 @@ const bus = new CrossTabDataBus({
46
46
  | `metricsIntervalMs` | `number` | `5000` | Aggregation window; must be a finite value greater than 0 |
47
47
  | `sink` | `(event) => void` | Required | Determined by the integrator: console, monitoring SDK, or other output |
48
48
 
49
- `message_metrics` contains window duration, received count, dispatched count, active Topic count, and receive-to-dispatch latency sample count, average, P50, P95, and maximum. Latency is aggregated in 50ms buckets and never contains an individual message payload. Subscription events include their Topic so an integrator can correlate ownership changes. They are emitted only when the owner transport subscription set changes; idempotent `CONTROL` retries do not produce duplicate subscription events. Treat trace sinks as diagnostic surfaces: redact sensitive Topic conventions before writing to a console or external telemetry. Trace events do not include URLs, credentials, payloads, or error bodies. Errors thrown in the sink are isolated and will not interrupt message distribution, but will output to `console.warn` to help integrators discover diagnostic configuration issues.
49
+ `message_metrics` contains window duration, received count, dispatched count, active Topic count, and receive-to-dispatch latency sample count, average, P50, P95, and maximum, plus `dedupAccepted` and `dedupSuppressed` counters for the same window. Latency is aggregated in 50ms buckets and never contains an individual message payload. Subscription events include their Topic so an integrator can correlate ownership changes. They are emitted only when the owner transport subscription set changes; idempotent `CONTROL` retries do not produce duplicate subscription events. Treat trace sinks as diagnostic surfaces: redact sensitive Topic conventions before writing to a console or external telemetry. Trace events do not include URLs, credentials, payloads, or error bodies. Errors thrown in the sink are isolated and will not interrupt message distribution, but will output to `console.warn` to help integrators discover diagnostic configuration issues.
50
50
 
51
51
  On `pagehide`, the aggregation timer stops and discards the incomplete window; on `pageshow`, it resumes with a new window. A permanent `stop()` clears the timer. Only diagnostics output is throttled; actual message reception and distribution are never rate-limited.
52
52
 
package/docs/roadmap.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # Roadmap
2
2
 
3
- 0.6.0 is released. The next work is organized as 0.7.0 candidates, with reliability and diagnostics landing first so the new behavior is observable before expanding the protocol surface.
3
+ 0.11.0 is the current development line. The next work is organized as 1.0.0 candidates, with protocol stability and operational guarantees taking priority over adding another browser runtime.
4
+
5
+ ## 0.11.0 delivered scope
6
+
7
+ - Automatic durable replay retention through `replay.retentionMs` when the persistence adapter supports `clearBefore`.
8
+ - Deduplication accepted/suppressed counters in periodic trace metrics.
9
+ - Publication metadata compatibility coverage across WebSocket, Centrifuge, Worker boundaries, and browser E2E.
10
+ - Service Worker transport decision: remain deliberately unimplemented until a stable connection-lifetime contract exists across target browsers.
11
+
12
+ ## 1.0.0 candidates
13
+
14
+ 1. Freeze the public export surface and transport-neutral publication envelope.
15
+ 2. Document at-least-once delivery and deduplication guarantees precisely.
16
+ 3. Add long-running browser soak coverage for replay retention, reconnect, BFCache, and owner migration.
17
+ 4. Publish a migration guide and deprecation policy for any pre-1.0 protocol aliases.
4
18
 
5
19
  ## 0.7.0 candidates
6
20
 
@@ -141,14 +141,18 @@ const bus = createWebSocketDataBus({
141
141
 
142
142
  Wire protocol (JSON text frames):
143
143
 
144
- - client → server: `{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...}`
145
- - server → client: publications are `{"topic":"...","data":...}`. Frames
146
- without a string `topic` are ignored; malformed JSON is reported through
147
- `handlers.onError` without throwing.
144
+ - client → server: `{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...,"messageId"?:"...","timestamp"?:123}`
145
+ - server → client: the canonical publication is `{"op":"publication","publication":{"topic":"...","data":...,"messageId"?:"...","timestamp"?:123}}`. The legacy flat `{"topic":"...","data":...}` frame remains accepted. Frames without a string topic are ignored; malformed JSON is reported through `handlers.onError` without throwing.
148
146
 
149
147
  When `data` is an `ArrayBuffer`, publish uses a binary frame with a small
150
148
  header (`0xc7`, UTF-8 topic length, topic, payload). Servers may echo the same
151
- frame unchanged; JSON remains the compatibility path for all other payloads.
149
+ frame unchanged. A binary publication carrying metadata uses the JSON-compatible
150
+ byte-array envelope so `messageId` and `timestamp` are not lost.
151
+
152
+ The Centrifuge session applies the same transport-neutral contract. Payloads
153
+ without metadata keep their original shape. Metadata-bearing publishes use
154
+ `{ data, messageId?, timestamp? }`, while inbound publications additionally
155
+ accept the canonical nested `DataBusPublicationEnvelope`.
152
156
 
153
157
  Lifecycle mapping: `open` → `connected`, `close` → `disconnected`,
154
158
  `error` → `error` (DataBus auto-recovery). Subscribe frames are re-sent when
package/docs/zh/api.md CHANGED
@@ -86,7 +86,7 @@ subscribe(
86
86
  - 最后一个 handler 释放后,当前 Tab 才退出该 Topic。
87
87
  - transport 尚未 ready 时订阅自动排队。
88
88
  - 通配符订阅:以 `.*` 结尾的 Topic(如 `chat.*`)匹配任意后缀,`*` 匹配全部。pattern 以字面量参与路由、归属与传输订阅;携带匹配的具体 topic(或 pattern 本身)的发布都会投递给通配 handler。匹配规则见下方 `topicMatchesPattern`。
89
- - 重放(可选):构造 bus 时传 `replay: { maxPerTopic }` 开启缓冲,`maxPerTopic` 必须是正安全整数;`subscribe()` 第三个参数传 `{ replay: true | n }` 后,新 handler 会立即收到缓冲历史(最多 `n` 条,受 `maxPerTopic` 上限约束,默认 100),消息带 `message.replayed: true` 标记——晚加入的 handler 不会错过更早的发布。只有被分发过的消息才入缓冲(无本地订阅者的 topic 会被 owner 丢弃);缓冲仅存内存,该 topic 最后一个 handler 退订时清空。通配订阅会对所有匹配 pattern 的已缓冲 topic 做回放。需要跨 reload/BFCache 持久化时,可传入 `createIndexedDbReplayPersistence({ maxPerTopic })` 创建的 `persistence`;持久化为异步操作,失败会通过 `onError` 报告,不影响实时投递。
89
+ - 重放(可选):构造 bus 时传 `replay: { maxPerTopic }` 开启缓冲,`maxPerTopic` 必须是正安全整数;`subscribe()` 第三个参数传 `{ replay: true | n }` 后,新 handler 会立即收到缓冲历史(最多 `n` 条,受 `maxPerTopic` 上限约束,默认 100),消息带 `message.replayed: true` 标记——晚加入的 handler 不会错过更早的发布。只有被分发过的消息才入缓冲(无本地订阅者的 topic 会被 owner 丢弃);缓冲仅存内存,该 topic 最后一个 handler 退订时清空。通配订阅会对所有匹配 pattern 的已缓冲 topic 做回放。需要跨 reload/BFCache 持久化时,可传入 `createIndexedDbReplayPersistence({ maxPerTopic })` 创建的 `persistence`;持久化为异步操作,失败会通过 `onError` 报告,不影响实时投递。设置 `retentionMs` 后,如果 adapter 支持 `clearBefore`,会在 hydrate 和追加后自动清理过期历史。
90
90
  - 持久化 replay store 还可实现 `clearTopic(topic)`;bus 会在最后一个 handler 退订时调用。应用可自行保留 `clear()` 做全量留存清理;`stop()` 会保留 durable history,以支持 reload/BFCache 恢复。
91
91
 
92
92
  ### `unsubscribe(topic, handler?)`
@@ -102,7 +102,11 @@ unsubscribe(topic: string, handler?: DataBusMessageHandler<TData>): void
102
102
  ### `publish(topic, data, options?)`
103
103
 
104
104
  ```ts
105
- publish(topic: string, data: unknown, options?: { messageId?: string }): void
105
+ publish(
106
+ topic: string,
107
+ data: unknown,
108
+ options?: { messageId?: string; timestamp?: number }
109
+ ): void
106
110
  ```
107
111
 
108
112
  将发布操作路由到当前 Topic owner;没有有效路由时使用当前 Worker。
@@ -111,7 +115,17 @@ publish(topic: string, data: unknown, options?: { messageId?: string }): void
111
115
 
112
116
  当 owner 是远端 Tab、且发布控制消息无法投递时(例如 BroadcastChannel 无法克隆 payload),`publish()` 会通过 `onError` 上报失败,而不是静默丢弃。
113
117
 
114
- 传入 `options.messageId` 后,ID 会经过跨 Tab 路由并由支持的 transport 透传。它只是元数据;服务端需要回显或保留该 ID,入站 dedup 才能生效。
118
+ 传入 `options.messageId` `options.timestamp` 后,元数据会穿过跨 Tab 路由、Worker 边界和支持的 transport。服务端必须回显或以其他方式保留它们,入站去重和 replay retention 才能使用。
119
+
120
+ `DataBusMessage` 与 `DataBusPublication` 暴露相同的可选元数据。
121
+ `DataBusPublicationEnvelope<TData>` 是标准 JSON envelope 类型:
122
+
123
+ ```ts
124
+ {
125
+ op: 'publication',
126
+ publication: { topic, data, messageId?, timestamp? }
127
+ }
128
+ ```
115
129
 
116
130
  ### `clearReplay()`
117
131
 
@@ -121,6 +135,10 @@ clearReplay(): Promise<void>
121
135
 
122
136
  清空内存 replay 缓冲,并调用持久化适配器可选的 `clear()`。适合留存策略、退出登录或租户切换;普通 `stop()` 仍会保留 durable history。
123
137
 
138
+ `clearReplayTopic(topic)` 只清理一个精确 topic。`getDedupStats()` 返回 `enabled`、`tracked`、`accepted`、`suppressed` 四项有界统计;`resetDedup()` 清除已记忆 ID 和计数,不改变 dedup 配置。
139
+
140
+ `clearReplayBefore(timestamp)` 按毫秒时间戳清理旧记录;实现可选 `clearBefore()` 的持久化适配器会同步执行该清理。
141
+
124
142
  ### `onStatus(handler)`
125
143
 
126
144
  ```ts
@@ -181,7 +199,7 @@ trace: {
181
199
  }
182
200
  ```
183
201
 
184
- 低频事件类型包括 `lifecycle`、`status`、`subscription`、`coordination` 和 `error`;高频数据按窗口输出 `message_metrics`,包含接收/分发计数、活跃 Topic 数量和分发延迟聚合(`dispatchSamples`、`dispatchAvgMs`、`dispatchP50Ms`、`dispatchP95Ms`、`dispatchMaxMs`)。所有公开事件都使用固定结构,不包含原始 Topic、消息 payload、连接地址或错误正文。sink 抛错会被隔离,不会中断消息分发,但会向 `console.warn` 输出错误,便于定位诊断配置问题。sink 应尽量避免抛出异常——预期中的错误条件应通过事件数据表达,而不是通过异常上报。
202
+ 低频事件类型包括 `lifecycle`、`status`、`subscription`、`coordination` 和 `error`;高频数据按窗口输出 `message_metrics`,包含接收/分发计数、活跃 Topic 数量和分发延迟聚合(`dispatchSamples`、`dispatchAvgMs`、`dispatchP50Ms`、`dispatchP95Ms`、`dispatchMaxMs`),以及去重结果(`dedupAccepted`、`dedupSuppressed`)。所有公开事件都使用固定结构,不包含原始 Topic、消息 payload、连接地址或错误正文。sink 抛错会被隔离,不会中断消息分发,但会向 `console.warn` 输出错误,便于定位诊断配置问题。sink 应尽量避免抛出异常——预期中的错误条件应通过事件数据表达,而不是通过异常上报。
185
203
 
186
204
  ### `stop()`
187
205
 
@@ -283,8 +301,11 @@ new WebSocketTransport<TData>(connection: WebSocketDataBusConfig)
283
301
 
284
302
  JSON 文本帧:
285
303
 
286
- - client → server:`{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...}`
287
- - server → client:发布为 `{"topic":"...","data":...}`。没有字符串 `topic` 字段的帧被忽略;非法 JSON 通过 `handlers.onError` 上报而不会抛出。
304
+ - client → server:`{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...,"messageId"?:...,"timestamp"?:...}`
305
+ - server → client(标准):`{"op":"publication","publication":{"topic":"...","data":...,"messageId"?:...,"timestamp"?:...}}`
306
+ - server → client(旧格式,继续兼容):`{"topic":"...","data":...}`
307
+
308
+ 没有字符串 publication `topic` 的帧会被忽略;非法 JSON 通过 `handlers.onError` 上报而不会抛出。
288
309
 
289
310
  支持 pattern 的服务器建议以具体 topic 标注发布;以 pattern 本身标注的发布走精确匹配路径投递。
290
311
 
@@ -387,6 +387,8 @@ sequenceDiagram
387
387
  4. owner 通过 BroadcastChannel 广播 `EVENT/DATABUS_PUBLICATION`;自身 Tab 也有本地订阅时直接分发一次。BroadcastChannel 从不把消息回传给发送者,因此不会重复分发。
388
388
  5. 其余每个 Tab 收到 `EVENT` 后,仅当自己持有该 Topic 的 `subscriber:{topicKey}:{tabId}` 记录时才调用本地 handler;没有本地订阅的 Tab 直接丢弃。
389
389
 
390
+ 可选的 publication 元数据(`messageId` 与 `timestamp`)与 payload 走同一条路径:`CONTROL/PUBLISH` → transport/服务器 → `DataBusMessage` → `EVENT` 扇出。它不会写入协调存储。各 transport 会在三处分发门之前,把旧 payload 格式与标准 `DataBusPublicationEnvelope` 统一规范化。
391
+
390
392
  在 transport 层,Centrifuge 客户端可能同时在 `client` 对象和对应 `Subscription` 对象上触发同一 publication。为避免把同一条服务器 publication 分发两次,CentrifugeSession 的 client 级 `publication` 监听只处理**没有客户端订阅**的 topic(即服务端订阅);已有活跃订阅的 topic 仅由 subscription 级监听派发。
391
393
 
392
394
  ```mermaid
@@ -410,7 +412,11 @@ sequenceDiagram
410
412
 
411
413
  BroadcastChannel 不会把消息回传给发送者,因此 owner 收不到自己广播的 `EVENT`——本地分发是唯一一次本地投递。
412
414
 
413
- publication 不写入 localStorage。消息数据只存在于 BroadcastChannel 内存事件和 transport 内;批量写入只覆盖协调元数据。
415
+ publication 不写入 localStorage。消息数据与 publication 元数据只存在于 BroadcastChannel 内存事件和 transport 内;批量写入只覆盖协调元数据。
416
+
417
+ ### Service Worker 边界
418
+
419
+ SDK 当前刻意不在 Service Worker 中承载实时 transport。Service Worker 可能在事件之间被浏览器终止,不能提供持久的前台连接生命周期,而且各浏览器对长连接 WebSocket 的限制并不一致。未来若实现 adapter,必须先定义明确的连接 owner、客户端唤醒协议、重连策略和持久化交接语义;在这些条件标准化并有真实浏览器测试前,Dedicated/Shared Worker 仍是受支持的运行模型。
414
420
 
415
421
  ### 分发流程:三道关卡
416
422
 
@@ -22,7 +22,7 @@
22
22
  | 降级 | localStorage 或 BroadcastChannel 不可用时本地运行 | ✅ 已实现 | 保留当前 Tab 的连接和订阅能力 |
23
23
  | Centrifuge | 内置 Dedicated / Shared Worker transport | ✅ 已实现 | 支持 subscribe、unsubscribe、publish、连接状态和错误上报;`auto` 从 SharedWorker → Dedicated Worker → 主线程 WebSocket 降级 |
24
24
  | 安全边界 | localStorage 使用连接和 Topic 派生不透明 key;BroadcastChannel 协调消息以明文传输 Topic 名称 | ✅ 已实现 | 不持久化 URL、原始 Topic 名称、凭证或 publication payload。BroadcastChannel 协调消息仅存在于内存中,以明文传输 Topic 名称——不会被持久化。 |
25
- | 诊断 | 聚合生命周期、吞吐量、分发延迟、恢复重试、路由确认和迁移 | ✅ 已实现 | 默认关闭;默认每 5 秒输出指标,并以有界 reliability 事件记录恢复和路由协调 |
25
+ | 诊断 | 聚合生命周期、吞吐量、分发延迟、去重结果、恢复重试、路由确认和迁移 | ✅ 已实现 | 默认关闭;默认每 5 秒输出指标,并以有界 reliability 事件记录恢复和路由协调 |
26
26
  | 性能 | 协调元数据批量写入 + 退避重试 | ✅ 已实现 | 心跳、路由和 subscriber 写入合并后在微任务中 flush;失败时指数退避;`pagehide` / `stop()` 同步 flush |
27
27
  | 性能 | 可选 ArrayBuffer Transferable 传输 | ✅ 已实现 | 开启 `transferable: true` 后,二进制 publish/receive 跳过 structured clone 复制;对象消息 API 不变 |
28
28
  | 消息语义 | exactly-once 投递 | 未实现 | 正常交接会避免重叠,但异常恢复和 transport/服务端行为仍不提供 exactly-once 保证 |
@@ -31,7 +31,7 @@
31
31
  | 负载策略 | 按消息速率、字节数或 CPU 自适应加权 | 规划中 | 当前负载仅按 owner Topic 数量计算 |
32
32
  | 可观测性 | owner 确认、迁移和恢复尝试的指标/事件 | ✅ 已实现 | `DataBusReliabilityTraceEvent` 记录有界的 route ack/migration 与 transport recovery;服务端最终确认仍由 transport 决定 |
33
33
  | 运行时模型 | SharedWorker / Dedicated Worker transport | ✅ 已实现 | `workerMode` 支持 `dedicated`、`shared` 和 `auto`,默认 `dedicated` |
34
- | 运行时模型 | Service Worker transport | 未实现 | 当前不提供使用 Service Worker 承载实时连接 |
34
+ | 运行时模型 | Service Worker transport | 未实现 | 刻意延后;生命周期与长连接约束见 `docs/zh/architecture.md` |
35
35
  | 持久消息 | 跨页面关闭持久化 publication 或发布命令 | 未实现 | SDK 不持久化业务 payload,也不在恢复后重放发布命令 |
36
36
 
37
37
  ## 验收标准
@@ -46,7 +46,7 @@ const bus = new CrossTabDataBus({
46
46
  | `metricsIntervalMs` | `number` | `5000` | 聚合窗口,必须是大于 0 的有限数值 |
47
47
  | `sink` | `(event) => void` | 必填 | 由接入方决定 console、监控 SDK 或其他出口 |
48
48
 
49
- `message_metrics` 包含窗口时长、接收数、分发数、活跃 Topic 数量,以及接收 → 分发延迟的样本数、平均值、P50、P95 和最大值。延迟按 50ms 桶聚合,不包含单条消息 payload。订阅事件会带 Topic,便于接入方关联 owner 变化;仅 owner transport 的订阅集合实际变化时才输出,幂等 `CONTROL` 重试不会产生重复订阅事件。将 trace 写入 console 或外部监控前,应按业务 Topic 约定进行脱敏。Trace 不包含 URL、凭证、payload 或错误正文。sink 抛错会被隔离,不会中断消息分发,但会向 `console.warn` 输出错误,方便接入方发现诊断配置问题。
49
+ `message_metrics` 包含窗口时长、接收数、分发数、活跃 Topic 数量,以及接收 → 分发延迟的样本数、平均值、P50、P95 和最大值,并包含同一窗口的 `dedupAccepted` 与 `dedupSuppressed` 计数。延迟按 50ms 桶聚合,不包含单条消息 payload。订阅事件会带 Topic,便于接入方关联 owner 变化;仅 owner transport 的订阅集合实际变化时才输出,幂等 `CONTROL` 重试不会产生重复订阅事件。将 trace 写入 console 或外部监控前,应按业务 Topic 约定进行脱敏。Trace 不包含 URL、凭证、payload 或错误正文。sink 抛错会被隔离,不会中断消息分发,但会向 `console.warn` 输出错误,方便接入方发现诊断配置问题。
50
50
 
51
51
  `pagehide` 时聚合定时器会停止并丢弃未完成窗口,`pageshow` 后以新窗口恢复;永久 `stop()` 会清理定时器。这里节流的只是诊断输出,真实消息接收与分发不会被限速。
52
52
 
@@ -129,13 +129,17 @@ const bus = createWebSocketDataBus({
129
129
 
130
130
  线协议(JSON 文本帧):
131
131
 
132
- - client → server:`{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...}`
133
- - server → client:发布为 `{"topic":"...","data":...}`。没有字符串 `topic` 的帧
134
- 会被忽略;非法 JSON 通过 `handlers.onError` 上报而不会抛出。
132
+ - client → server:`{"op":"subscribe"|"unsubscribe"|"publish","topic":"...","data":...,"messageId"?:"...","timestamp"?:123}`
133
+ - server → client:标准 publication 为 `{"op":"publication","publication":{"topic":"...","data":...,"messageId"?:"...","timestamp"?:123}}`;旧的扁平 `{"topic":"...","data":...}` 帧仍然兼容。没有字符串 topic 的帧会被忽略;非法 JSON 通过 `handlers.onError` 上报而不会抛出。
135
134
 
136
135
  当 `data` 是 `ArrayBuffer` 时,publish 使用二进制帧:帧头为 `0xc7`,随后是
137
136
  UTF-8 topic 长度、topic 和 payload。服务器可以原样回显该帧;其他 payload 仍走
138
- JSON 兼容路径。
137
+ JSON 兼容路径。带 metadata 的二进制 publication 使用 JSON 兼容的字节数组
138
+ envelope,避免丢失 `messageId` 与 `timestamp`。
139
+
140
+ Centrifuge session 遵循同一个传输无关契约:不带 metadata 的 payload 保持原始
141
+ 形状;带 metadata 的 publish 使用 `{ data, messageId?, timestamp? }`,入站还接受
142
+ 标准嵌套 `DataBusPublicationEnvelope`。
139
143
 
140
144
  生命周期映射:`open` → `connected`,`close` → `disconnected`,`error` → `error`
141
145
  (触发 DataBus 自动恢复)。socket 原地重连时自动重发订阅帧。支持 pattern 的
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cross-tab-worker-databus",
3
- "version": "0.8.0",
3
+ "version": "0.11.0",
4
4
  "description": "Framework-agnostic cross-tab data bus with Dedicated/Shared Worker clustering and Centrifuge support.",
5
5
  "type": "module",
6
6
  "license": "MIT",