cross-tab-worker-databus 0.1.1 → 0.2.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.
- package/CHANGELOG.md +59 -0
- package/README.md +11 -0
- package/README.zh.md +9 -0
- package/dist/centrifuge-protocol.d.ts +51 -15
- package/dist/centrifuge-protocol.d.ts.map +1 -1
- package/dist/centrifuge-session.d.ts +14 -4
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.d.ts +10 -2
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +76 -28
- package/dist/centrifuge.js.map +2 -2
- package/dist/centrifuge.shared.worker.js +84 -28
- package/dist/centrifuge.shared.worker.js.map +2 -2
- package/dist/centrifuge.worker.js +48 -14
- package/dist/centrifuge.worker.js.map +2 -2
- package/dist/{chunk-GABYBK7I.js → chunk-LBXREMZA.js} +287 -162
- package/dist/chunk-LBXREMZA.js.map +7 -0
- package/dist/core/cluster.d.ts +49 -4
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +19 -2
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/environment.d.ts +16 -2
- package/dist/core/environment.d.ts.map +1 -1
- package/dist/core/hash.d.ts.map +1 -1
- package/dist/core/routing.d.ts +5 -2
- package/dist/core/routing.d.ts.map +1 -1
- package/dist/core/storage-batch.d.ts +14 -0
- package/dist/core/storage-batch.d.ts.map +1 -1
- package/dist/core/trace.d.ts +16 -1
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/types.d.ts +76 -21
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/worker-mode.d.ts +12 -3
- package/dist/worker-mode.d.ts.map +1 -1
- package/dist/workers/port-reaper.d.ts +23 -6
- package/dist/workers/port-reaper.d.ts.map +1 -1
- package/docs/architecture.md +20 -8
- package/docs/configuration.md +2 -0
- package/docs/transports.md +144 -0
- package/docs/zh/architecture.md +20 -8
- package/docs/zh/configuration.md +2 -0
- package/docs/zh/transports.md +132 -0
- package/package.json +7 -2
- package/dist/chunk-GABYBK7I.js.map +0 -7
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Transport Backends
|
|
2
|
+
|
|
3
|
+
> [中文](./zh/transports.md) | English
|
|
4
|
+
|
|
5
|
+
The core package (`cross-tab-worker-databus`) is transport-agnostic. It defines a
|
|
6
|
+
`DataBusTransport` contract; the built-in Centrifuge backend is one implementation
|
|
7
|
+
of that contract, exposed as the optional `./centrifuge` subpath. This document
|
|
8
|
+
describes the contract and how to wire up a third-party backend (native WebSocket,
|
|
9
|
+
socket.io, SSE, etc.).
|
|
10
|
+
|
|
11
|
+
## The `DataBusTransport` contract
|
|
12
|
+
|
|
13
|
+
Every backend implements five methods. `subscribe` / `unsubscribe` MUST be
|
|
14
|
+
idempotent — the DataBus may call them repeatedly and replays them on reconnect.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
interface DataBusTransport<TConfig = unknown, TData = unknown> {
|
|
18
|
+
start(config: TConfig, handlers: DataBusTransportHandlers<TData>): MaybePromise<void>;
|
|
19
|
+
subscribe(topic: string): MaybePromise<void>;
|
|
20
|
+
unsubscribe(topic: string): MaybePromise<void>;
|
|
21
|
+
publish(topic: string, data: unknown): MaybePromise<void>;
|
|
22
|
+
stop(): MaybePromise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface DataBusTransportHandlers<TData = unknown> {
|
|
26
|
+
onMessage: (message: DataBusMessage<TData>) => void;
|
|
27
|
+
onStatus: (status: WorkerStatus) => void; // 'connecting' | 'connected' | 'disconnected' | 'error'
|
|
28
|
+
onError: (error: unknown) => void;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`start()` receives the user-supplied connection config (untyped `TConfig` — the
|
|
33
|
+
backend owns its shape) and the three callbacks. Call `onStatus` whenever the
|
|
34
|
+
connection state changes; call `onMessage` for each inbound publication; call
|
|
35
|
+
`onError` for non-fatal errors (the DataBus applies a recovery cooldown so a
|
|
36
|
+
flapping connection does not retry-loop).
|
|
37
|
+
|
|
38
|
+
## Architectural layers
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
CrossTabDataBus ──► DataBusTransport (your backend)
|
|
42
|
+
│
|
|
43
|
+
┌───────┴────────┐
|
|
44
|
+
│ Worker protocol │ (your backend's main-thread ↔ worker messages)
|
|
45
|
+
└───────┬────────┘
|
|
46
|
+
│
|
|
47
|
+
Session layer (the actual client: WebSocket / centrifuge / …)
|
|
48
|
+
│
|
|
49
|
+
Server
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The DataBus layer handles cross-tab coordination (BroadcastChannel control
|
|
53
|
+
plane, localStorage routes, owner selection, failover, page lifecycle). Your
|
|
54
|
+
transport only owns the I/O path: connect, subscribe, publish, disconnect.
|
|
55
|
+
|
|
56
|
+
## Implementing a backend
|
|
57
|
+
|
|
58
|
+
### 1. Define your Worker protocol
|
|
59
|
+
|
|
60
|
+
Mirror the Centrifuge backend's `centrifuge-protocol.ts`: a discriminated union
|
|
61
|
+
of messages the main thread sends to the Worker (`INIT` / `SUBSCRIBE` /
|
|
62
|
+
`UNSUBSCRIBE` / `PUBLISH` / `STOP`) and a union the Worker posts back
|
|
63
|
+
(`STATUS` / `MESSAGE` / `ERROR`). Keep it structured-cloneable (no functions,
|
|
64
|
+
no class instances — `Error` must be serialised).
|
|
65
|
+
|
|
66
|
+
### 2. Implement the session
|
|
67
|
+
|
|
68
|
+
A session class owns one connection and lives inside the Worker (or, as a
|
|
69
|
+
fallback, on the main thread). It receives protocol messages via a `handle()`
|
|
70
|
+
method and posts outputs back through a sink. See
|
|
71
|
+
[`centrifuge-session.ts`](../src/centrifuge-session.ts) for the reference shape:
|
|
72
|
+
|
|
73
|
+
- `handle(message)` dispatches by `message.type`.
|
|
74
|
+
- `subscribe(topic)` is idempotent — re-subscribing an existing topic is a no-op.
|
|
75
|
+
- `unsubscribe(topic)` removes listeners before disconnecting, to avoid a late
|
|
76
|
+
event resurrecting a re-subscribed topic.
|
|
77
|
+
- `stop()` disconnects, clears all subscriptions, and emits `disconnected`.
|
|
78
|
+
|
|
79
|
+
### 3. Implement the transport
|
|
80
|
+
|
|
81
|
+
The transport selects a backend (SharedWorker / Dedicated Worker / local),
|
|
82
|
+
posts protocol messages to it, and routes Worker outputs back to the
|
|
83
|
+
`DataBusTransportHandlers`. See [`centrifuge.ts`](../src/centrifuge.ts) for the
|
|
84
|
+
reference shape, including:
|
|
85
|
+
|
|
86
|
+
- **Backend selection**: reuse `selectWorkerBackend` from `worker-mode.ts` so
|
|
87
|
+
your backend degrades consistently with the rest of the SDK.
|
|
88
|
+
- **Generation guard**: bump a monotonic counter when a backend is created;
|
|
89
|
+
error handlers check it so late errors from a superseded Worker cannot
|
|
90
|
+
corrupt the fresh session.
|
|
91
|
+
- **SharedWorker heartbeat**: if you use a SharedWorker, send periodic PINGs
|
|
92
|
+
so a `PortReaper` can reclaim dead-tab sessions.
|
|
93
|
+
|
|
94
|
+
### 4. Expose as a subpath
|
|
95
|
+
|
|
96
|
+
Add `exports` entries in `package.json` (one per entry point — the main bundle,
|
|
97
|
+
the dedicated worker, the shared worker):
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"exports": {
|
|
102
|
+
"./your-backend": {
|
|
103
|
+
"types": "./dist/your-backend.d.ts",
|
|
104
|
+
"import": "./dist/your-backend.js"
|
|
105
|
+
},
|
|
106
|
+
"./your-backend.worker": "./dist/your-backend.worker.js",
|
|
107
|
+
"./your-backend.shared.worker": "./dist/your-backend.shared.worker.js"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This keeps the core package zero-dependency: users who do not import
|
|
113
|
+
`./your-backend` never pull your client library into their bundle.
|
|
114
|
+
|
|
115
|
+
### 5. Register the peer dependency
|
|
116
|
+
|
|
117
|
+
Declare your client library as an optional peer dependency so consumers
|
|
118
|
+
opt in:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"peerDependencies": { "your-client-lib": "^x.y.z" },
|
|
123
|
+
"peerDependenciesMeta": { "your-client-lib": { "optional": true } }
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Factory entry point
|
|
128
|
+
|
|
129
|
+
Provide a `create<Backend>DataBus(options)` factory that wires the transport
|
|
130
|
+
into a `CrossTabDataBus`, mirroring `createCentrifugeDataBus`. This is the
|
|
131
|
+
surface most consumers use; it should accept the connection config, cluster
|
|
132
|
+
key (defaulting to the connection URL), and forward trace / worker-mode
|
|
133
|
+
options to the DataBus.
|
|
134
|
+
|
|
135
|
+
## What the transport does NOT own
|
|
136
|
+
|
|
137
|
+
- **Cross-tab routing**: the `WorkerClusterRuntime` decides which tab owns a
|
|
138
|
+
topic. Your transport just subscribes when told.
|
|
139
|
+
- **Reconnect replay**: the DataBus replays the current owner's topics on
|
|
140
|
+
reconnect; your transport's `subscribe` must be safe to call again.
|
|
141
|
+
- **Publication fan-out**: the owner broadcasts publications over
|
|
142
|
+
BroadcastChannel; your transport only receives and reports them.
|
|
143
|
+
- **Page lifecycle**: the DataBus suspends/resumes the transport on
|
|
144
|
+
`pagehide` / `pageshow`; your transport's `stop()` must be clean.
|
package/docs/zh/architecture.md
CHANGED
|
@@ -10,23 +10,33 @@ graph TB
|
|
|
10
10
|
subgraph TabA["Tab A"]
|
|
11
11
|
AppA["业务模块"] --> BusA["CrossTabDataBus"]
|
|
12
12
|
BusA --> RuntimeA["WorkerClusterRuntime"]
|
|
13
|
-
BusA -->
|
|
13
|
+
BusA --> TransportA["CentrifugeWorkerTransport"]
|
|
14
|
+
TransportA --> WorkerA["Dedicated / Shared Worker A"]
|
|
14
15
|
end
|
|
15
16
|
subgraph TabB["Tab B"]
|
|
16
17
|
AppB["业务模块"] --> BusB["CrossTabDataBus"]
|
|
17
18
|
BusB --> RuntimeB["WorkerClusterRuntime"]
|
|
18
|
-
BusB -->
|
|
19
|
+
BusB --> TransportB["CentrifugeWorkerTransport"]
|
|
20
|
+
TransportB --> WorkerB["Dedicated / Shared Worker B"]
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
RuntimeA <--> Channel["BroadcastChannel 控制面"]
|
|
23
25
|
RuntimeB <--> Channel
|
|
24
|
-
RuntimeA
|
|
25
|
-
RuntimeB
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
RuntimeA --> BatchA["BatchingStorageWriter"]
|
|
27
|
+
RuntimeB --> BatchB["BatchingStorageWriter"]
|
|
28
|
+
BatchA <--> Registry["localStorage Worker 注册表"]
|
|
29
|
+
BatchB <--> Registry
|
|
30
|
+
BatchA <--> Routes["localStorage Topic 路由表"]
|
|
31
|
+
BatchB <--> Routes
|
|
32
|
+
WorkerA --> SessionA["CentrifugeSession"]
|
|
33
|
+
WorkerB --> SessionB["CentrifugeSession"]
|
|
34
|
+
SessionA --> Server["Centrifuge / 实时服务器"]
|
|
35
|
+
SessionB --> Server
|
|
36
|
+
subgraph SW["SharedWorker 进程(backend = shared 时)"]
|
|
37
|
+
Reaper["PortReaper"] -.-> SessionA
|
|
38
|
+
Reaper -.-> SessionB
|
|
39
|
+
end
|
|
30
40
|
```
|
|
31
41
|
|
|
32
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 只负责最终一致的协调元数据。
|
|
@@ -377,6 +387,8 @@ sequenceDiagram
|
|
|
377
387
|
4. owner 通过 BroadcastChannel 广播 `EVENT/DATABUS_PUBLICATION`;自身 Tab 也有本地订阅时直接分发一次。BroadcastChannel 从不把消息回传给发送者,因此不会重复分发。
|
|
378
388
|
5. 其余每个 Tab 收到 `EVENT` 后,仅当自己持有该 Topic 的 `subscriber:{topicKey}:{tabId}` 记录时才调用本地 handler;没有本地订阅的 Tab 直接丢弃。
|
|
379
389
|
|
|
390
|
+
在 transport 层,Centrifuge 客户端可能同时在 `client` 对象和对应 `Subscription` 对象上触发同一 publication。为避免把同一条服务器 publication 分发两次,CentrifugeSession 的 client 级 `publication` 监听只处理**没有客户端订阅**的 topic(即服务端订阅);已有活跃订阅的 topic 仅由 subscription 级监听派发。
|
|
391
|
+
|
|
380
392
|
```mermaid
|
|
381
393
|
sequenceDiagram
|
|
382
394
|
participant Pub as 发布方 Tab A
|
package/docs/zh/configuration.md
CHANGED
|
@@ -132,6 +132,8 @@ const bus = createCentrifugeDataBus({
|
|
|
132
132
|
- **会话超时**:`3 × heartbeatIntervalMs`(默认 `30000` ms)。超过超时未收到消息的端口会被回收:其会话停止,WebSocket 关闭。这与 Core 集群心跳(默认 `3000` ms,通过 localStorage 跟踪 worker 存活)相互独立——见下方说明。
|
|
133
133
|
- **自适应频率**:回收器以所有活动端口中最小的心跳间隔运行,使短心跳端口的会话能被及时回收。当最后一个端口断开时,回收器定时器清除,避免长时间存在的 SharedWorker 在连接爆发间隙运行永久的空循环。
|
|
134
134
|
- **先关闭端口再停止会话**:回收端口时,先关闭端口,再停止会话。关闭端口会丢弃会话的 `disconnected` 状态通知(使其不会到达可能仍在运行但缓慢的主线程),并保证已关闭的端口永远无法传递后续消息,从而在回收器追踪之外复活僵尸会话。
|
|
135
|
+
- **失败隔离**:回收与 `dispose()` 都用 try-catch 包裹 `target.close()`/`target.stop()`,单个异常端口不会中断本轮回收,也不会让后续死 Tab 无人回收。
|
|
136
|
+
- **关闭清理**:SharedWorker 关闭时,`PortReaper.dispose()` 停止定时器并关闭/停止**所有**仍被追踪的会话,确保没有任何 `CentrifugeSession` 或 WebSocket 比 reaper 活得更久。这补充了按端口回收——后者只覆盖 reaper 运行期间静默的端口。
|
|
135
137
|
|
|
136
138
|
这是从崩溃(未发送 `STOP`)的 Tab 中恢复会话的机制。降低 `heartbeatIntervalMs` 可更快回收死会话,代价是端口上更频繁的 PING 消息。
|
|
137
139
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Transport 后端
|
|
2
|
+
|
|
3
|
+
> 中文 | [English](../transports.md)
|
|
4
|
+
|
|
5
|
+
核心包(`cross-tab-worker-databus`)与 transport 无关。它定义了一个
|
|
6
|
+
`DataBusTransport` 契约;内置的 Centrifuge 后端是该契约的一个实现,作为可选的
|
|
7
|
+
`./centrifuge` subpath 暴露。本文档描述该契约以及如何接入第三方后端(原生
|
|
8
|
+
WebSocket、socket.io、SSE 等)。
|
|
9
|
+
|
|
10
|
+
## `DataBusTransport` 契约
|
|
11
|
+
|
|
12
|
+
每个后端实现 5 个方法。`subscribe` / `unsubscribe` 必须幂等——DataBus 可能重复
|
|
13
|
+
调用,并在重连时回放。
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
interface DataBusTransport<TConfig = unknown, TData = unknown> {
|
|
17
|
+
start(config: TConfig, handlers: DataBusTransportHandlers<TData>): MaybePromise<void>;
|
|
18
|
+
subscribe(topic: string): MaybePromise<void>;
|
|
19
|
+
unsubscribe(topic: string): MaybePromise<void>;
|
|
20
|
+
publish(topic: string, data: unknown): MaybePromise<void>;
|
|
21
|
+
stop(): MaybePromise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface DataBusTransportHandlers<TData = unknown> {
|
|
25
|
+
onMessage: (message: DataBusMessage<TData>) => void;
|
|
26
|
+
onStatus: (status: WorkerStatus) => void; // 'connecting' | 'connected' | 'disconnected' | 'error'
|
|
27
|
+
onError: (error: unknown) => void;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`start()` 接收用户提供的连接配置(无类型 `TConfig`——后端自行定义其形状)和
|
|
32
|
+
三个回调。连接状态变化时调 `onStatus`;收到 publication 时调 `onMessage`;
|
|
33
|
+
非致命错误调 `onError`(DataBus 有恢复冷却窗口,避免抖动连接死循环重试)。
|
|
34
|
+
|
|
35
|
+
## 架构分层
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
CrossTabDataBus ──► DataBusTransport(你的后端)
|
|
39
|
+
│
|
|
40
|
+
┌───────┴────────┐
|
|
41
|
+
│ Worker 协议 │ (你的后端的主线程 ↔ worker 消息)
|
|
42
|
+
└───────┬────────┘
|
|
43
|
+
│
|
|
44
|
+
Session 层 (真正的客户端:WebSocket / centrifuge / …)
|
|
45
|
+
│
|
|
46
|
+
服务端
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
DataBus 层负责跨 Tab 协调(BroadcastChannel 控制面、localStorage 路由、owner 选举、
|
|
50
|
+
故障转移、页面生命周期)。你的 transport 只负责 I/O 路径:连接、订阅、发布、断开。
|
|
51
|
+
|
|
52
|
+
## 实现一个后端
|
|
53
|
+
|
|
54
|
+
### 1. 定义你的 Worker 协议
|
|
55
|
+
|
|
56
|
+
参照 Centrifuge 后端的 `centrifuge-protocol.ts`:一个主线程发给 Worker 的
|
|
57
|
+
判别联合(`INIT` / `SUBSCRIBE` / `UNSUBSCRIBE` / `PUBLISH` / `STOP`)和一个
|
|
58
|
+
Worker 回传的联合(`STATUS` / `MESSAGE` / `ERROR`)。保持结构化克隆安全
|
|
59
|
+
(无函数、无类实例——`Error` 必须序列化)。
|
|
60
|
+
|
|
61
|
+
### 2. 实现 session
|
|
62
|
+
|
|
63
|
+
一个 session 类持有一个连接,运行在 Worker 内(或作为降级运行在主线程)。
|
|
64
|
+
它通过 `handle()` 方法接收协议消息,通过 sink 回传输出。参见
|
|
65
|
+
[`centrifuge-session.ts`](../../src/centrifuge-session.ts) 的参考形状:
|
|
66
|
+
|
|
67
|
+
- `handle(message)` 按 `message.type` 分派。
|
|
68
|
+
- `subscribe(topic)` 幂等——对已存在 topic 重复订阅是 no-op。
|
|
69
|
+
- `unsubscribe(topic)` 先移除监听器再断开,避免迟到事件复活已重订阅的 topic。
|
|
70
|
+
- `stop()` 断开、清理所有订阅、emit `disconnected`。
|
|
71
|
+
|
|
72
|
+
### 3. 实现 transport
|
|
73
|
+
|
|
74
|
+
transport 选择后端(SharedWorker / Dedicated Worker / 本地),向它发送协议
|
|
75
|
+
消息,并把 Worker 输出路由回 `DataBusTransportHandlers`。参见
|
|
76
|
+
[`centrifuge.ts`](../../src/centrifuge.ts) 的参考形状,包括:
|
|
77
|
+
|
|
78
|
+
- **后端选举**:复用 `worker-mode.ts` 的 `selectWorkerBackend`,使你的后端与
|
|
79
|
+
SDK 其余部分降级行为一致。
|
|
80
|
+
- **generation 守卫**:创建后端时递增单调计数器;错误处理检查它,使被取代的
|
|
81
|
+
Worker 的迟到错误不会污染新 session。
|
|
82
|
+
- **SharedWorker 心跳**:若用 SharedWorker,定期发 PING,让 `PortReaper` 能
|
|
83
|
+
回收死 tab 的 session。
|
|
84
|
+
|
|
85
|
+
### 4. 作为 subpath 暴露
|
|
86
|
+
|
|
87
|
+
在 `package.json` 加 `exports` 条目(每个入口一个——主 bundle、dedicated worker、
|
|
88
|
+
shared worker):
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"exports": {
|
|
93
|
+
"./your-backend": {
|
|
94
|
+
"types": "./dist/your-backend.d.ts",
|
|
95
|
+
"import": "./dist/your-backend.js"
|
|
96
|
+
},
|
|
97
|
+
"./your-backend.worker": "./dist/your-backend.worker.js",
|
|
98
|
+
"./your-backend.shared.worker": "./dist/your-backend.shared.worker.js"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
这保持核心包零依赖:不导入 `./your-backend` 的用户不会把你的客户端库打进 bundle。
|
|
104
|
+
|
|
105
|
+
### 5. 声明 peer 依赖
|
|
106
|
+
|
|
107
|
+
将你的客户端库声明为可选 peer 依赖,让消费者自行选择:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"peerDependencies": { "your-client-lib": "^x.y.z" },
|
|
112
|
+
"peerDependenciesMeta": { "your-client-lib": { "optional": true } }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 工厂入口
|
|
117
|
+
|
|
118
|
+
提供一个 `create<Backend>DataBus(options)` 工厂,把 transport 接入
|
|
119
|
+
`CrossTabDataBus`,与 `createCentrifugeDataBus` 对称。这是大多数消费者使用的
|
|
120
|
+
界面;它应接受连接配置、cluster key(默认为连接 URL),并把 trace /
|
|
121
|
+
worker-mode 选项转发给 DataBus。
|
|
122
|
+
|
|
123
|
+
## transport 不负责的事
|
|
124
|
+
|
|
125
|
+
- **跨 Tab 路由**:`WorkerClusterRuntime` 决定哪个 tab 拥有 topic。你的 transport
|
|
126
|
+
只在被通知时订阅。
|
|
127
|
+
- **重连回放**:DataBus 在重连时回放当前 owner 的 topic;你的 transport 的
|
|
128
|
+
`subscribe` 必须可安全重复调用。
|
|
129
|
+
- **publication 扇出**:owner 通过 BroadcastChannel 广播 publication;你的
|
|
130
|
+
transport 只接收并上报。
|
|
131
|
+
- **页面生命周期**:DataBus 在 `pagehide` / `pageshow` 时挂起/恢复 transport;
|
|
132
|
+
你的 transport 的 `stop()` 必须干净。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cross-tab-worker-databus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -54,9 +54,14 @@
|
|
|
54
54
|
"test:e2e": "pnpm build && playwright test",
|
|
55
55
|
"typecheck": "tsc --noEmit"
|
|
56
56
|
},
|
|
57
|
-
"
|
|
57
|
+
"peerDependencies": {
|
|
58
58
|
"centrifuge": "^5.5.3"
|
|
59
59
|
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"centrifuge": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
64
|
+
},
|
|
60
65
|
"devDependencies": {
|
|
61
66
|
"@playwright/test": "^1.62.1",
|
|
62
67
|
"@types/node": "^24.0.0",
|