cross-tab-worker-databus 0.20.70 → 0.20.85
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 +209 -2
- package/README.md +8 -0
- package/README.zh.md +4 -0
- package/dist/centrifuge-protocol.d.ts +59 -26
- package/dist/centrifuge-protocol.d.ts.map +1 -1
- package/dist/centrifuge-session.d.ts +9 -0
- package/dist/centrifuge-session.d.ts.map +1 -1
- package/dist/centrifuge.d.ts +12 -2
- package/dist/centrifuge.d.ts.map +1 -1
- package/dist/centrifuge.js +168 -88
- package/dist/centrifuge.js.map +3 -3
- package/dist/centrifuge.shared.worker.js +146 -38
- package/dist/centrifuge.shared.worker.js.map +3 -3
- package/dist/centrifuge.worker.js +140 -33
- package/dist/centrifuge.worker.js.map +3 -3
- package/dist/{chunk-IKBJIREH.js → chunk-PW63EWIK.js} +1090 -476
- package/dist/chunk-PW63EWIK.js.map +7 -0
- package/dist/chunk-TZ7ZP7YD.js +175 -0
- package/dist/chunk-TZ7ZP7YD.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +1405 -621
- package/dist/cjs/centrifuge.cjs.map +4 -4
- package/dist/cjs/hooks.cjs +37 -2
- package/dist/cjs/hooks.cjs.map +3 -3
- package/dist/cjs/index.cjs +1469 -688
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/cjs/vue.cjs +45 -2
- package/dist/cjs/vue.cjs.map +3 -3
- package/dist/core/cluster.d.ts +40 -4
- package/dist/core/cluster.d.ts.map +1 -1
- package/dist/core/data-bus.d.ts +93 -66
- package/dist/core/data-bus.d.ts.map +1 -1
- package/dist/core/dedup-manager.d.ts +98 -0
- package/dist/core/dedup-manager.d.ts.map +1 -0
- package/dist/core/environment.d.ts +43 -3
- package/dist/core/environment.d.ts.map +1 -1
- package/dist/core/replay-manager.d.ts +128 -0
- package/dist/core/replay-manager.d.ts.map +1 -0
- package/dist/core/replay-persistence.d.ts +4 -1
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/routing.d.ts +22 -3
- package/dist/core/routing.d.ts.map +1 -1
- package/dist/core/storage-batch.d.ts +0 -4
- package/dist/core/storage-batch.d.ts.map +1 -1
- package/dist/core/trace.d.ts +55 -15
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/types.d.ts +58 -9
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/hooks.d.ts +12 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +28 -2
- package/dist/hooks.js.map +2 -2
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +231 -125
- package/dist/index.js.map +3 -3
- package/dist/utils/constants.d.ts +170 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/error-utils.d.ts +28 -0
- package/dist/utils/error-utils.d.ts.map +1 -0
- package/dist/utils/metadata.d.ts +16 -0
- package/dist/utils/metadata.d.ts.map +1 -0
- package/dist/utils/storage-utils.d.ts +24 -0
- package/dist/utils/storage-utils.d.ts.map +1 -0
- package/dist/utils/validation.d.ts +76 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/vue.d.ts +13 -1
- package/dist/vue.d.ts.map +1 -1
- package/dist/vue.js +36 -2
- package/dist/vue.js.map +2 -2
- package/dist/websocket.d.ts +6 -1
- package/dist/websocket.d.ts.map +1 -1
- package/dist/worker-mode.d.ts +6 -2
- package/dist/worker-mode.d.ts.map +1 -1
- package/docs/README.md +1 -0
- package/docs/api.md +122 -2
- package/docs/architecture.md +80 -0
- package/docs/benchmarks.md +24 -0
- package/docs/capabilities.md +24 -3
- package/docs/configuration.md +58 -0
- package/docs/getting-started.md +35 -0
- package/docs/release-checklist.md +28 -5
- package/docs/roadmap.md +78 -5
- package/docs/zh/README.md +2 -1
- package/docs/zh/api.md +121 -2
- package/docs/zh/architecture.md +49 -0
- package/docs/zh/benchmarks.md +24 -0
- package/docs/zh/capabilities.md +21 -3
- package/docs/zh/configuration.md +53 -0
- package/docs/zh/getting-started.md +35 -0
- package/docs/zh/release-checklist.md +29 -6
- package/docs/zh/roadmap.md +90 -5
- package/package.json +39 -16
- package/dist/chunk-IKBJIREH.js.map +0 -7
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 公共字符串常量 —— 生命周期状态、角色、控制动作、协议消息类型、枚举与
|
|
3
|
+
* 命名空间前缀。
|
|
4
|
+
*
|
|
5
|
+
* 所有运行时使用的字符串字面量集中在此一份值,类型定义(src/core/types.ts
|
|
6
|
+
* 等)通过 `(typeof X)[keyof typeof X]` 从这些常量派生,比较/switch 处引用
|
|
7
|
+
* 同一对象成员。这样既能消除散落在各文件里的重复字符串(避免大小写不统一
|
|
8
|
+
* 或笔误),又保证类型与值永不错位。
|
|
9
|
+
*
|
|
10
|
+
* ## 使用约定
|
|
11
|
+
*
|
|
12
|
+
* - **新增或修改字符串字面量时,先到这里查找/增补,不要在业务文件里直接写
|
|
13
|
+
* 魔法字符串。** 扫描残留字面量:`rg "'(['a-z]+)'" src/`。
|
|
14
|
+
* - **只用于类型位置的常量**(如 `PERSISTENCE_OPERATION`)在调用方用
|
|
15
|
+
* `import type`;**在运行时比较/构造中使用的**(如 `WORKER_STATUS.ERROR`、
|
|
16
|
+
* `TRACE_EVENT_TYPE.LIFECYCLE`)必须用值导入,否则会报 "cannot be used as
|
|
17
|
+
* a value because it was imported using 'import type'"。
|
|
18
|
+
* - **值派生类型**:`export type WorkerStatus = (typeof WORKER_STATUS)[keyof
|
|
19
|
+
* typeof WORKER_STATUS]`。这样新增枚举分支时类型自动收窄,编译器会指出
|
|
20
|
+
* 每个遗漏的 switch 分支。
|
|
21
|
+
* - 分组的顺序(状态 / 角色 / 后端 / 消息类型 / 枚举 / 前缀)保持稳定,方便
|
|
22
|
+
* 维护者一眼定位。
|
|
23
|
+
*/
|
|
24
|
+
export declare const WORKER_STATUS: {
|
|
25
|
+
readonly CONNECTING: "connecting";
|
|
26
|
+
readonly CONNECTED: "connected";
|
|
27
|
+
readonly DISCONNECTED: "disconnected";
|
|
28
|
+
readonly ERROR: "error";
|
|
29
|
+
};
|
|
30
|
+
export declare const WORKER_ROLE: {
|
|
31
|
+
readonly ACTIVE: "active";
|
|
32
|
+
readonly STANDBY: "standby";
|
|
33
|
+
};
|
|
34
|
+
export declare const TAB_VISIBILITY: {
|
|
35
|
+
readonly VISIBLE: "visible";
|
|
36
|
+
readonly HIDDEN: "hidden";
|
|
37
|
+
};
|
|
38
|
+
export declare const WORKER_MODE: {
|
|
39
|
+
readonly DEDICATED: "dedicated";
|
|
40
|
+
readonly SHARED: "shared";
|
|
41
|
+
readonly AUTO: "auto";
|
|
42
|
+
};
|
|
43
|
+
export declare const WORKER_BACKEND: {
|
|
44
|
+
readonly DEDICATED: "dedicated";
|
|
45
|
+
readonly SHARED: "shared";
|
|
46
|
+
readonly LOCAL: "local";
|
|
47
|
+
};
|
|
48
|
+
export declare const CHANNEL_FALLBACK: {
|
|
49
|
+
readonly NONE: "none";
|
|
50
|
+
readonly STORAGE_EVENT: "storage-event";
|
|
51
|
+
};
|
|
52
|
+
export declare const EVENT_TYPE: {
|
|
53
|
+
readonly MESSAGE: "message";
|
|
54
|
+
readonly MESSAGEERROR: "messageerror";
|
|
55
|
+
readonly ERROR: "error";
|
|
56
|
+
};
|
|
57
|
+
export declare const CONTROL_ACTION: {
|
|
58
|
+
readonly SUBSCRIBE: "SUBSCRIBE";
|
|
59
|
+
readonly UNSUBSCRIBE: "UNSUBSCRIBE";
|
|
60
|
+
readonly PUBLISH: "PUBLISH";
|
|
61
|
+
};
|
|
62
|
+
export declare const CLUSTER_MESSAGE_TYPE: {
|
|
63
|
+
readonly CONTROL: "CONTROL";
|
|
64
|
+
readonly EVENT: "EVENT";
|
|
65
|
+
readonly REGISTRY: "REGISTRY";
|
|
66
|
+
readonly ROUTE_RELEASED: "ROUTE_RELEASED";
|
|
67
|
+
};
|
|
68
|
+
export declare const DEFAULT_STORAGE_PREFIX = "cross-tab-worker-databus";
|
|
69
|
+
export declare const STORAGE_CHANNEL_PREFIX = "cross-tab-worker-databus:channel:";
|
|
70
|
+
export declare const TAB_ID_STORAGE_KEY = "cross-tab-worker-databus:tab-id";
|
|
71
|
+
export declare const PUBLICATION_EVENT = "DATABUS_PUBLICATION";
|
|
72
|
+
export declare const PUBLICATION_ENVELOPE_OP = "publication";
|
|
73
|
+
export declare const TRACE_EVENT_TYPE: {
|
|
74
|
+
readonly LIFECYCLE: "lifecycle";
|
|
75
|
+
readonly STATUS: "status";
|
|
76
|
+
readonly SUBSCRIPTION: "subscription";
|
|
77
|
+
readonly COORDINATION: "coordination";
|
|
78
|
+
readonly ERROR: "error";
|
|
79
|
+
readonly RELIABILITY: "reliability";
|
|
80
|
+
readonly MESSAGE_METRICS: "message_metrics";
|
|
81
|
+
};
|
|
82
|
+
export declare const TRACE_LIFECYCLE_ACTION: {
|
|
83
|
+
readonly START: "start";
|
|
84
|
+
readonly STOP: "stop";
|
|
85
|
+
readonly SUSPEND: "suspend";
|
|
86
|
+
readonly RESUME: "resume";
|
|
87
|
+
};
|
|
88
|
+
export declare const INVOKE_LABEL: {
|
|
89
|
+
readonly DISPATCH: "dispatch";
|
|
90
|
+
readonly STATUS: "status";
|
|
91
|
+
readonly ERROR_HANDLER: "error handler";
|
|
92
|
+
};
|
|
93
|
+
export declare const TRACE_MODE: {
|
|
94
|
+
readonly EVENTS: "events";
|
|
95
|
+
readonly METRICS: "metrics";
|
|
96
|
+
readonly ALL: "all";
|
|
97
|
+
};
|
|
98
|
+
export declare const TRACE_ERROR_SOURCE: {
|
|
99
|
+
readonly TRANSPORT: "transport";
|
|
100
|
+
readonly OPERATION: "operation";
|
|
101
|
+
};
|
|
102
|
+
export declare const RELIABILITY_OPERATION: {
|
|
103
|
+
readonly TRANSPORT_RECOVERY: "transport_recovery";
|
|
104
|
+
readonly ROUTE_ACK: "route_ack";
|
|
105
|
+
readonly ROUTE_MIGRATION: "route_migration";
|
|
106
|
+
readonly ROUTE_MIGRATION_RECOVERY: "route_migration_recovery";
|
|
107
|
+
readonly PERSISTENCE_CLEANUP: "persistence_cleanup";
|
|
108
|
+
readonly PERSISTENCE_RETRY: "persistence_retry";
|
|
109
|
+
readonly DEDUP_SUPPRESSED: "dedup_suppressed";
|
|
110
|
+
};
|
|
111
|
+
export declare const RECOVERY_OUTCOME: {
|
|
112
|
+
readonly SCHEDULED: "scheduled";
|
|
113
|
+
readonly SUCCEEDED: "succeeded";
|
|
114
|
+
readonly FAILED: "failed";
|
|
115
|
+
readonly EXHAUSTED: "exhausted";
|
|
116
|
+
};
|
|
117
|
+
export declare const SUBSCRIPTION_ACTION: {
|
|
118
|
+
readonly SUBSCRIBE: "subscribe";
|
|
119
|
+
readonly UNSUBSCRIBE: "unsubscribe";
|
|
120
|
+
};
|
|
121
|
+
export declare const PRUNE_STRATEGY: {
|
|
122
|
+
readonly COUNT: "count";
|
|
123
|
+
readonly AGE: "age";
|
|
124
|
+
readonly BOTH: "both";
|
|
125
|
+
};
|
|
126
|
+
export declare const PERSISTENCE_OPERATION: {
|
|
127
|
+
readonly LOAD: "load";
|
|
128
|
+
readonly APPEND: "append";
|
|
129
|
+
readonly CLEAR: "clear";
|
|
130
|
+
readonly CLEAR_TOPIC: "clearTopic";
|
|
131
|
+
readonly CLEAR_BEFORE: "clearBefore";
|
|
132
|
+
};
|
|
133
|
+
export declare const FAILURE_SOURCE: {
|
|
134
|
+
readonly TRANSPORT: "transport";
|
|
135
|
+
readonly PERSISTENCE: "persistence";
|
|
136
|
+
readonly DISPATCH: "dispatch";
|
|
137
|
+
};
|
|
138
|
+
export declare const HEALTH_STATE: {
|
|
139
|
+
readonly STOPPED: "stopped";
|
|
140
|
+
readonly STARTING: "starting";
|
|
141
|
+
readonly HEALTHY: "healthy";
|
|
142
|
+
readonly RECOVERING: "recovering";
|
|
143
|
+
readonly SUSPENDED: "suspended";
|
|
144
|
+
readonly DEGRADED: "degraded";
|
|
145
|
+
};
|
|
146
|
+
export declare const WS_OP: {
|
|
147
|
+
readonly SUBSCRIBE: "subscribe";
|
|
148
|
+
readonly UNSUBSCRIBE: "unsubscribe";
|
|
149
|
+
readonly PUBLISH: "publish";
|
|
150
|
+
readonly PUBLISH_BATCH: "publishBatch";
|
|
151
|
+
};
|
|
152
|
+
export declare const CENTRIFUGE_INPUT_TYPE: {
|
|
153
|
+
readonly INIT: "INIT";
|
|
154
|
+
readonly SUBSCRIBE: "SUBSCRIBE";
|
|
155
|
+
readonly UNSUBSCRIBE: "UNSUBSCRIBE";
|
|
156
|
+
readonly PUBLISH: "PUBLISH";
|
|
157
|
+
readonly PUBLISH_BIN: "PUBLISH_BIN";
|
|
158
|
+
readonly PING: "PING";
|
|
159
|
+
readonly STOP: "STOP";
|
|
160
|
+
readonly TOKEN_RESPONSE: "TOKEN_RESPONSE";
|
|
161
|
+
readonly TOKEN_ERROR: "TOKEN_ERROR";
|
|
162
|
+
};
|
|
163
|
+
export declare const CENTRIFUGE_OUTPUT_TYPE: {
|
|
164
|
+
readonly STATUS: "STATUS";
|
|
165
|
+
readonly MESSAGE: "MESSAGE";
|
|
166
|
+
readonly MESSAGE_BIN: "MESSAGE_BIN";
|
|
167
|
+
readonly ERROR: "ERROR";
|
|
168
|
+
readonly TOKEN_REQUEST: "TOKEN_REQUEST";
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,eAAO,MAAM,WAAW;;;CAGd,CAAC;AAEX,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AAGX,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAGX,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAKX,eAAO,MAAM,UAAU;;;;CAIb,CAAC;AAGX,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAGX,eAAO,MAAM,oBAAoB;;;;;CAKvB,CAAC;AAGX,eAAO,MAAM,sBAAsB,6BAA6B,CAAC;AACjE,eAAO,MAAM,sBAAsB,sCAAuC,CAAC;AAC3E,eAAO,MAAM,kBAAkB,oCAAqC,CAAC;AACrE,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAGvD,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAGrD,eAAO,MAAM,gBAAgB;;;;;;;;CAQnB,CAAC;AAGX,eAAO,MAAM,sBAAsB;;;;;CAKzB,CAAC;AAGX,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAGX,eAAO,MAAM,UAAU;;;;CAIb,CAAC;AAGX,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAGX,eAAO,MAAM,qBAAqB;;;;;;;;CAWxB,CAAC;AAGX,eAAO,MAAM,gBAAgB;;;;;CAKnB,CAAC;AAGX,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAGX,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAGX,eAAO,MAAM,qBAAqB;;;;;;CAMxB,CAAC;AAGX,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAGX,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAGX,eAAO,MAAM,KAAK;;;;;CAKR,CAAC;AAGX,eAAO,MAAM,qBAAqB;;;;;;;;;;CAUxB,CAAC;AAGX,eAAO,MAAM,sBAAsB;;;;;;CAMzB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 错误序列化工具 —— 在 Worker 边界往返传输 Error。
|
|
3
|
+
*
|
|
4
|
+
* Error 实例无法通过 postMessage 结构化克隆,所以 CentrifugeSession 侧用
|
|
5
|
+
* serializeError 把它压成 plain object,主线程侧用 deserializeWorkerError
|
|
6
|
+
* 还原成真 Error(带 name/stack)。此前两个函数分别内联在 centrifuge-session.ts
|
|
7
|
+
* 与 centrifuge.ts,此处统一定义并从两处引用。
|
|
8
|
+
*/
|
|
9
|
+
/** Error object serialized for cross-thread transfer. */
|
|
10
|
+
export interface SerializedWorkerError {
|
|
11
|
+
/** The Error's `name` (e.g. 'TypeError', 'CentrifugeError'). */
|
|
12
|
+
name: string;
|
|
13
|
+
/** The Error's `message`. */
|
|
14
|
+
message: string;
|
|
15
|
+
/** The Error's `stack` if available (for debugging). */
|
|
16
|
+
stack?: string;
|
|
17
|
+
/** Arbitrary context attached by the Worker (e.g. the failing operation). */
|
|
18
|
+
context?: unknown;
|
|
19
|
+
}
|
|
20
|
+
/** Convert an arbitrary error into a structured-cloneable form for postMessage.
|
|
21
|
+
* A non-Error value is attached as `context`, but only when it is itself
|
|
22
|
+
* structured-cloneable: a function, symbol, or an object/map holding one would
|
|
23
|
+
* otherwise make the serialised error unserializable, so `postMessage` would
|
|
24
|
+
* throw `DataCloneError` while reporting the original failure. */
|
|
25
|
+
export declare function serializeError(error: unknown): SerializedWorkerError;
|
|
26
|
+
/** Reconstruct an Error instance from its serialised form. */
|
|
27
|
+
export declare function deserializeWorkerError(error: SerializedWorkerError): Error;
|
|
28
|
+
//# sourceMappingURL=error-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-utils.d.ts","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,yDAAyD;AACzD,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;kEAIkE;AAClE,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAcpE;AAeD,8DAA8D;AAC9D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,GAAG,KAAK,CAM1E"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 发布元数据工具 —— 构造 { messageId?, timestamp? },仅在字段已定义时写入,
|
|
3
|
+
* 避免往 wire 帧/控制消息里塞空枚举字段。
|
|
4
|
+
*
|
|
5
|
+
* 此前 cluster.ts、data-bus.ts、centrifuge.ts、centrifuge-session.ts 各自内联
|
|
6
|
+
* 实现了一遍 `...(x === undefined ? {} : { x })` 展开,行为容易漂移;统一到此
|
|
7
|
+
* 一处后所有调用方共享同一语义。
|
|
8
|
+
*/
|
|
9
|
+
import type { DataBusPublicationMetadata } from '../core/types';
|
|
10
|
+
/**
|
|
11
|
+
* Copy defined publication metadata without adding empty enumerable fields.
|
|
12
|
+
* Returns `undefined` when neither field is set, so callers can preserve a
|
|
13
|
+
* legacy "no metadata" argument shape (e.g. `onControl(..., undefined)`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function publicationMetadata(messageId?: string, timestamp?: number): DataBusPublicationMetadata | undefined;
|
|
16
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/utils/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,0BAA0B,GAAG,SAAS,CAMxC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* localStorage 读写工具 —— 容错的 JSON 读、静默写、按前缀枚举。
|
|
3
|
+
*
|
|
4
|
+
* 从 WorkerClusterRuntime 拆出:cluster.ts 顶部原有的 readJson/writeJson/
|
|
5
|
+
* listKeys/readAllByPrefix 四处搬到此文件,协调层与快照逻辑共用同一套
|
|
6
|
+
* 容错语义(损坏 JSON 视为不存在、写失败不抛出)。
|
|
7
|
+
*/
|
|
8
|
+
import type { StorageLike } from '../core/environment';
|
|
9
|
+
/** Parse a JSON value from storage, returning null on malformed or missing data.
|
|
10
|
+
* Never throws — a corrupt record is treated as absent so the reconcile cycle
|
|
11
|
+
* can recreate it. */
|
|
12
|
+
export declare function readJson<T>(storage: StorageLike, key: string): T | null;
|
|
13
|
+
/** Write a JSON value to storage, swallowing storage errors (coordination is
|
|
14
|
+
* best-effort; a failed write does not break the local transport). The actual
|
|
15
|
+
* write may be coalesced by BatchingStorageWriter — this just calls setItem. */
|
|
16
|
+
export declare function writeJson(storage: StorageLike, key: string, value: unknown): void;
|
|
17
|
+
/** List all storage keys that start with `prefix`. */
|
|
18
|
+
export declare function listKeys(storage: StorageLike, prefix: string): string[];
|
|
19
|
+
/** Read and parse every JSON record whose key starts with `prefix`. */
|
|
20
|
+
export declare function readAllByPrefix<T>(storage: StorageLike, prefix: string): Array<{
|
|
21
|
+
key: string;
|
|
22
|
+
value: T;
|
|
23
|
+
}>;
|
|
24
|
+
//# sourceMappingURL=storage-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-utils.d.ts","sourceRoot":"","sources":["../../src/utils/storage-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;sBAEsB;AACtB,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOvE;AAED;;gFAEgF;AAChF,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAMjF;AAED,sDAAsD;AACtD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvE;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,CAIzG"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 参数校验工具 —— CrossTabDataBus 构造选项与持久化配置的入口校验。
|
|
3
|
+
*
|
|
4
|
+
* 所有 `throw new TypeError(...)` 校验集中在此,DataBus 构造器与
|
|
5
|
+
* IndexedDbReplayPersistence 共用同一组断言,错误消息与原有语义保持一致。
|
|
6
|
+
*
|
|
7
|
+
* 语义约定:可选字段只在**显式提供**时校验(undefined 由调用方落到默认值,
|
|
8
|
+
* 默认值始终合法);必填字段总是校验。
|
|
9
|
+
*/
|
|
10
|
+
import type { DataBusDedupOptions, DataBusPersistenceRetryOptions, DataBusReplayOptions } from '../core/data-bus';
|
|
11
|
+
import type { LoadWeightingOptions } from '../core/types';
|
|
12
|
+
/** Assert `value` is a positive safe integer. Throws a TypeError otherwise. */
|
|
13
|
+
export declare function assertPositiveSafeInteger(value: unknown, name: string): void;
|
|
14
|
+
/** Assert `value` is a positive finite number. Throws a TypeError otherwise. */
|
|
15
|
+
export declare function assertPositiveFiniteNumber(value: unknown, name: string): void;
|
|
16
|
+
/** Assert `value` is a non-negative finite number. Throws a TypeError otherwise. */
|
|
17
|
+
export declare function assertNonNegativeFiniteNumber(value: unknown, name: string): void;
|
|
18
|
+
/** Assert `value` is a valid replay prune strategy ('count' | 'age' | 'both'). */
|
|
19
|
+
export declare function assertPruneStrategy(value: unknown): asserts value is 'count' | 'age' | 'both';
|
|
20
|
+
/** Validate the replay options block. Optional fields are validated only when
|
|
21
|
+
* provided; omitted fields fall through to their defaults. */
|
|
22
|
+
export declare function assertReplayOptions(replay: DataBusReplayOptions | undefined): void;
|
|
23
|
+
/** Validate the replay persistence retry policy. */
|
|
24
|
+
export declare function assertPersistenceRetryOptions(retry: DataBusPersistenceRetryOptions): void;
|
|
25
|
+
/** Validate the dedup options block. Optional fields are validated only when
|
|
26
|
+
* provided; omitted fields fall through to their defaults. */
|
|
27
|
+
export declare function assertDedupOptions(dedup: DataBusDedupOptions | undefined): void;
|
|
28
|
+
/** Validate the transport recovery pacing options. Optional fields are validated
|
|
29
|
+
* only when provided; omitted fields fall through to their defaults. */
|
|
30
|
+
export declare function assertRecoveryOptions(recovery: {
|
|
31
|
+
cooldownMs?: number;
|
|
32
|
+
maxAttempts?: number;
|
|
33
|
+
} | undefined): void;
|
|
34
|
+
/** Validate the adaptive owner-weighting weights.
|
|
35
|
+
*
|
|
36
|
+
* Each weight is a non-negative finite number of "topic-equivalents" added per
|
|
37
|
+
* unit of the sampled signal; `0` (the default) disables that signal. A
|
|
38
|
+
* negative weight would invert the documented policy — biasing NEW routes
|
|
39
|
+
* toward the *busiest* Worker instead of the quietest — and a non-finite one
|
|
40
|
+
* would poison the score, so both are rejected here rather than silently
|
|
41
|
+
* steering traffic. */
|
|
42
|
+
export declare function assertLoadWeightingOptions(loadWeighting: LoadWeightingOptions | undefined): void;
|
|
43
|
+
/** Validate the cluster coordination options shared by `WorkerClusterRuntime`
|
|
44
|
+
* and `CrossTabDataBus`.
|
|
45
|
+
*
|
|
46
|
+
* These were previously unvalidated, so a `heartbeatIntervalMs` of `0` or `NaN`
|
|
47
|
+
* silently turned the heartbeat `setInterval` into a 0ms busy loop (the same
|
|
48
|
+
* failure the Centrifuge PING guard exists to prevent), a non-positive
|
|
49
|
+
* `workerTtlMs` pruned every peer on the first reconcile, and a non-positive
|
|
50
|
+
* `maxActiveWorkers`/`routeOwnerCacheMax` disabled ownership or caching
|
|
51
|
+
* outright. `Infinity` is rejected for the heartbeat here: unlike the Centrifuge
|
|
52
|
+
* PING it cannot mean "disable", because a Worker that never refreshes its
|
|
53
|
+
* heartbeat is pruned by its own TTL. */
|
|
54
|
+
export declare function assertClusterOptions(options: {
|
|
55
|
+
maxActiveWorkers?: number;
|
|
56
|
+
heartbeatIntervalMs?: number;
|
|
57
|
+
workerTtlMs?: number;
|
|
58
|
+
routeOwnerCacheMax?: number;
|
|
59
|
+
loadWeighting?: LoadWeightingOptions;
|
|
60
|
+
}): void;
|
|
61
|
+
/** Validate the SharedWorker PING heartbeat interval. A value of `0`, a negative
|
|
62
|
+
* number, or `NaN` would otherwise make `setInterval` degenerate into a 0ms busy
|
|
63
|
+
* loop, driving the reaper and the main-thread PING out of control. `Infinity`
|
|
64
|
+
* is allowed and disables heartbeats entirely (for environments where the
|
|
65
|
+
* SharedWorker reaper is not needed, e.g. a single-tab deployment).
|
|
66
|
+
* @throws {TypeError} when `value` is not a positive finite number or Infinity. */
|
|
67
|
+
export declare function assertHeartbeatInterval(value: number): void;
|
|
68
|
+
/** Validate that `value` is structured-cloneable. Throws early so config errors
|
|
69
|
+
* surface on the main thread rather than silently failing inside the Worker
|
|
70
|
+
* (where a DataCloneError would be reported as a generic Worker error with no
|
|
71
|
+
* actionable message). Skips validation when `structuredClone` is unavailable
|
|
72
|
+
* (older browsers without the API) — the Worker will still throw on its own.
|
|
73
|
+
* @throws {TypeError} when `value` contains non-cloneable members (functions,
|
|
74
|
+
* Symbols, DOM nodes, etc.). */
|
|
75
|
+
export declare function assertStructuredCloneable(value: unknown): void;
|
|
76
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EACV,mBAAmB,EACnB,8BAA8B,EAC9B,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D,+EAA+E;AAC/E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI5E;AAED,gFAAgF;AAChF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI7E;AAED,oFAAoF;AACpF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAIhF;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,GAAG,KAAK,GAAG,MAAM,CAK7F;AAED;8DAC8D;AAC9D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI,CASlF;AAED,oDAAoD;AACpD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,8BAA8B,GAAG,IAAI,CAOzF;AAED;8DAC8D;AAC9D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI,CAiB/E;AAED;wEACwE;AACxE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,SAAS,GAAG,IAAI,CAanB;AAED;;;;;;;uBAOuB;AACvB,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI,CAUhG;AAED;;;;;;;;;;yCAUyC;AACzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC,GAAG,IAAI,CAcP;AAED;;;;;mFAKmF;AACnF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM3D;AAED;;;;;;kCAMkC;AAClC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAU9D"}
|
package/dist/vue.d.ts
CHANGED
|
@@ -2,9 +2,21 @@
|
|
|
2
2
|
* Vue is an optional peer dependency; this module is a separate entry point.
|
|
3
3
|
*/
|
|
4
4
|
import { type Ref } from 'vue';
|
|
5
|
-
import type { CrossTabDataBus } from './core/data-bus';
|
|
5
|
+
import type { CrossTabDataBus, DataBusHealthSummary } from './core/data-bus';
|
|
6
6
|
import type { DataBusMessage, WorkerStatus } from './core/types';
|
|
7
7
|
export declare function useCrossTabDataBus<TConfig, TData>(create: () => CrossTabDataBus<TConfig, TData>, deps?: ReadonlyArray<Ref<unknown> | (() => unknown)>): Ref<CrossTabDataBus<TConfig, TData> | null>;
|
|
8
8
|
export declare function useCrossTabSubscription<TConfig, TData>(bus: Ref<CrossTabDataBus<TConfig, TData> | null>, topic: Ref<string> | string, handler: (message: DataBusMessage<TData>) => void): void;
|
|
9
9
|
export declare function useCrossTabStatus<TConfig, TData>(bus: Ref<CrossTabDataBus<TConfig, TData> | null>): Ref<WorkerStatus>;
|
|
10
|
+
/** Options for {@link useCrossTabHealth}. */
|
|
11
|
+
export interface UseCrossTabHealthOptions {
|
|
12
|
+
/** Polling cadence in ms for the health snapshot. Default 1000; `0` disables
|
|
13
|
+
* polling and relies on status/error events only. */
|
|
14
|
+
intervalMs?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Mirror the bus health summary into a Vue ref. `getHealthSummary()` is a
|
|
18
|
+
* snapshot, so the composable polls it on an interval (default 1000 ms) and
|
|
19
|
+
* refreshes on status changes and errors. Returns `null` until the bus exists.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useCrossTabHealth<TConfig, TData>(bus: Ref<CrossTabDataBus<TConfig, TData> | null>, options?: UseCrossTabHealthOptions): Ref<DataBusHealthSummary | null>;
|
|
10
22
|
//# sourceMappingURL=vue.d.ts.map
|
package/dist/vue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAsD,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAsD,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,KAAK,EAC/C,MAAM,EAAE,MAAM,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,EAC7C,IAAI,GAAE,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,CAAM,GACvD,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,CA2B7C;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,KAAK,EACpD,GAAG,EAAE,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAC7E,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,GAChD,IAAI,CAoBN;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAC9C,GAAG,EAAE,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,GAC/C,GAAG,CAAC,YAAY,CAAC,CAUnB;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC;yDACqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAC9C,GAAG,EAAE,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,EAChD,OAAO,CAAC,EAAE,wBAAwB,GACjC,GAAG,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAyBlC"}
|
package/dist/vue.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WORKER_STATUS
|
|
3
|
+
} from "./chunk-TZ7ZP7YD.js";
|
|
4
|
+
|
|
1
5
|
// src/vue.ts
|
|
2
6
|
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from "vue";
|
|
3
7
|
function useCrossTabDataBus(create, deps = []) {
|
|
@@ -23,6 +27,7 @@ function useCrossTabDataBus(create, deps = []) {
|
|
|
23
27
|
};
|
|
24
28
|
onMounted(start);
|
|
25
29
|
onBeforeUnmount(() => {
|
|
30
|
+
lifecycleGeneration += 1;
|
|
26
31
|
void stop();
|
|
27
32
|
});
|
|
28
33
|
if (deps.length > 0) watch(deps, start);
|
|
@@ -57,12 +62,12 @@ function useCrossTabSubscription(bus, topic, handler) {
|
|
|
57
62
|
onBeforeUnmount(stop);
|
|
58
63
|
}
|
|
59
64
|
function useCrossTabStatus(bus) {
|
|
60
|
-
const status = ref(
|
|
65
|
+
const status = ref(WORKER_STATUS.CONNECTING);
|
|
61
66
|
let cleanup;
|
|
62
67
|
watch(bus, (next) => {
|
|
63
68
|
cleanup?.();
|
|
64
69
|
cleanup = void 0;
|
|
65
|
-
status.value = next?.getStatus() ??
|
|
70
|
+
status.value = next?.getStatus() ?? WORKER_STATUS.CONNECTING;
|
|
66
71
|
if (next) cleanup = next.onStatus((value) => {
|
|
67
72
|
status.value = value;
|
|
68
73
|
});
|
|
@@ -70,8 +75,37 @@ function useCrossTabStatus(bus) {
|
|
|
70
75
|
onBeforeUnmount(() => cleanup?.());
|
|
71
76
|
return status;
|
|
72
77
|
}
|
|
78
|
+
function useCrossTabHealth(bus, options) {
|
|
79
|
+
const health = ref(null);
|
|
80
|
+
let timer = null;
|
|
81
|
+
let cleanups = [];
|
|
82
|
+
const teardown = () => {
|
|
83
|
+
if (timer) clearInterval(timer);
|
|
84
|
+
timer = null;
|
|
85
|
+
for (const cleanup of cleanups) cleanup();
|
|
86
|
+
cleanups = [];
|
|
87
|
+
};
|
|
88
|
+
watch(bus, (next) => {
|
|
89
|
+
teardown();
|
|
90
|
+
if (!next) {
|
|
91
|
+
health.value = null;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const refresh = () => {
|
|
95
|
+
health.value = next.getHealthSummary();
|
|
96
|
+
};
|
|
97
|
+
refresh();
|
|
98
|
+
cleanups.push(next.onStatus(refresh));
|
|
99
|
+
cleanups.push(next.onError(refresh));
|
|
100
|
+
const intervalMs = options?.intervalMs ?? 1e3;
|
|
101
|
+
if (intervalMs > 0) timer = setInterval(refresh, intervalMs);
|
|
102
|
+
}, { immediate: true });
|
|
103
|
+
onBeforeUnmount(teardown);
|
|
104
|
+
return health;
|
|
105
|
+
}
|
|
73
106
|
export {
|
|
74
107
|
useCrossTabDataBus,
|
|
108
|
+
useCrossTabHealth,
|
|
75
109
|
useCrossTabStatus,
|
|
76
110
|
useCrossTabSubscription
|
|
77
111
|
};
|
package/dist/vue.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/vue.ts"],
|
|
4
|
-
"sourcesContent": ["/** Vue 3 composables adapter for cross-tab-worker-databus.\n * Vue is an optional peer dependency; this module is a separate entry point.\n */\nimport { onBeforeUnmount, onMounted, ref, shallowRef, watch, type Ref } from 'vue';\nimport type { CrossTabDataBus } from './core/data-bus';\nimport type { DataBusMessage, WorkerStatus } from './core/types';\n\nexport function useCrossTabDataBus<TConfig, TData>(\n create: () => CrossTabDataBus<TConfig, TData>,\n deps: ReadonlyArray<Ref<unknown> | (() => unknown)> = []\n): Ref<CrossTabDataBus<TConfig, TData> | null> {\n const bus = shallowRef<CrossTabDataBus<TConfig, TData> | null>(null);\n let instance: CrossTabDataBus<TConfig, TData> | null = null;\n let lifecycleGeneration = 0;\n const stop = async () => { const current = instance; instance = null; bus.value = null; if (current) await current.stop(); };\n const start = () => {\n const generation = ++lifecycleGeneration;\n void stop().then(() => {\n if (generation !== lifecycleGeneration) return;\n const next = create();\n instance = next;\n bus.value = next;\n void next.ready().catch(() => {});\n });\n };\n onMounted(start);\n onBeforeUnmount(() => { void stop()
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/** Vue 3 composables adapter for cross-tab-worker-databus.\n * Vue is an optional peer dependency; this module is a separate entry point.\n */\nimport { onBeforeUnmount, onMounted, ref, shallowRef, watch, type Ref } from 'vue';\nimport type { CrossTabDataBus, DataBusHealthSummary } from './core/data-bus';\nimport type { DataBusMessage, WorkerStatus } from './core/types';\nimport { WORKER_STATUS } from './utils/constants';\n\nexport function useCrossTabDataBus<TConfig, TData>(\n create: () => CrossTabDataBus<TConfig, TData>,\n deps: ReadonlyArray<Ref<unknown> | (() => unknown)> = []\n): Ref<CrossTabDataBus<TConfig, TData> | null> {\n const bus = shallowRef<CrossTabDataBus<TConfig, TData> | null>(null);\n let instance: CrossTabDataBus<TConfig, TData> | null = null;\n let lifecycleGeneration = 0;\n const stop = async () => { const current = instance; instance = null; bus.value = null; if (current) await current.stop(); };\n const start = () => {\n const generation = ++lifecycleGeneration;\n void stop().then(() => {\n if (generation !== lifecycleGeneration) return;\n const next = create();\n instance = next;\n bus.value = next;\n void next.ready().catch(() => {});\n });\n };\n onMounted(start);\n onBeforeUnmount(() => {\n // Bump the generation so a start() still awaiting its stop() sees itself\n // superseded. Without this the pending continuation would run create()\n // after the component is gone, leaving a live bus with no owner to stop\n // it (the React adapter has no such window: its create() is synchronous\n // inside useEffect).\n lifecycleGeneration += 1;\n void stop();\n });\n if (deps.length > 0) watch(deps, start);\n return bus as Ref<CrossTabDataBus<TConfig, TData> | null>;\n}\n\nexport function useCrossTabSubscription<TConfig, TData>(\n bus: Ref<CrossTabDataBus<TConfig, TData> | null>, topic: Ref<string> | string,\n handler: (message: DataBusMessage<TData>) => void\n): void {\n let currentBus: CrossTabDataBus<TConfig, TData> | null = null;\n let currentTopic: string | null = null;\n let cleanup: (() => void) | undefined;\n let latestHandler = handler;\n const stop = () => { cleanup?.(); cleanup = undefined; currentBus = null; currentTopic = null; };\n const sync = () => {\n const nextBus = bus.value;\n const nextTopic = typeof topic === 'string' ? topic : topic.value;\n if (nextBus === currentBus && currentTopic === nextTopic && cleanup) return;\n stop();\n if (!nextBus) return;\n currentBus = nextBus;\n currentTopic = nextTopic;\n cleanup = nextBus.subscribe(nextTopic, message => latestHandler(message));\n };\n watch(bus, sync, { immediate: true });\n if (typeof topic !== 'string') watch(topic, sync);\n watch(() => handler, value => { latestHandler = value; });\n onBeforeUnmount(stop);\n}\n\nexport function useCrossTabStatus<TConfig, TData>(\n bus: Ref<CrossTabDataBus<TConfig, TData> | null>\n): Ref<WorkerStatus> {\n const status = ref<WorkerStatus>(WORKER_STATUS.CONNECTING);\n let cleanup: (() => void) | undefined;\n watch(bus, next => {\n cleanup?.(); cleanup = undefined;\n status.value = next?.getStatus() ?? WORKER_STATUS.CONNECTING;\n if (next) cleanup = next.onStatus(value => { status.value = value; });\n }, { immediate: true });\n onBeforeUnmount(() => cleanup?.());\n return status;\n}\n\n/** Options for {@link useCrossTabHealth}. */\nexport interface UseCrossTabHealthOptions {\n /** Polling cadence in ms for the health snapshot. Default 1000; `0` disables\n * polling and relies on status/error events only. */\n intervalMs?: number;\n}\n\n/**\n * Mirror the bus health summary into a Vue ref. `getHealthSummary()` is a\n * snapshot, so the composable polls it on an interval (default 1000 ms) and\n * refreshes on status changes and errors. Returns `null` until the bus exists.\n */\nexport function useCrossTabHealth<TConfig, TData>(\n bus: Ref<CrossTabDataBus<TConfig, TData> | null>,\n options?: UseCrossTabHealthOptions\n): Ref<DataBusHealthSummary | null> {\n const health = ref<DataBusHealthSummary | null>(null);\n let timer: ReturnType<typeof setInterval> | null = null;\n let cleanups: Array<() => void> = [];\n const teardown = () => {\n if (timer) clearInterval(timer);\n timer = null;\n for (const cleanup of cleanups) cleanup();\n cleanups = [];\n };\n watch(bus, next => {\n teardown();\n if (!next) {\n health.value = null;\n return;\n }\n const refresh = () => { health.value = next.getHealthSummary(); };\n refresh();\n cleanups.push(next.onStatus(refresh));\n cleanups.push(next.onError(refresh));\n const intervalMs = options?.intervalMs ?? 1_000;\n if (intervalMs > 0) timer = setInterval(refresh, intervalMs);\n }, { immediate: true });\n onBeforeUnmount(teardown);\n return health as Ref<DataBusHealthSummary | null>;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;AAGA,SAAS,iBAAiB,WAAW,KAAK,YAAY,aAAuB;AAKtE,SAAS,mBACd,QACA,OAAsD,CAAC,GACV;AAC7C,QAAM,MAAM,WAAmD,IAAI;AACnE,MAAI,WAAmD;AACvD,MAAI,sBAAsB;AAC1B,QAAM,OAAO,YAAY;AAAE,UAAM,UAAU;AAAU,eAAW;AAAM,QAAI,QAAQ;AAAM,QAAI,QAAS,OAAM,QAAQ,KAAK;AAAA,EAAG;AAC3H,QAAM,QAAQ,MAAM;AAClB,UAAM,aAAa,EAAE;AACrB,SAAK,KAAK,EAAE,KAAK,MAAM;AACrB,UAAI,eAAe,oBAAqB;AACxC,YAAM,OAAO,OAAO;AACpB,iBAAW;AACX,UAAI,QAAQ;AACZ,WAAK,KAAK,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAClC,CAAC;AAAA,EACH;AACA,YAAU,KAAK;AACf,kBAAgB,MAAM;AAMpB,2BAAuB;AACvB,SAAK,KAAK;AAAA,EACZ,CAAC;AACD,MAAI,KAAK,SAAS,EAAG,OAAM,MAAM,KAAK;AACtC,SAAO;AACT;AAEO,SAAS,wBACd,KAAkD,OAClD,SACM;AACN,MAAI,aAAqD;AACzD,MAAI,eAA8B;AAClC,MAAI;AACJ,MAAI,gBAAgB;AACpB,QAAM,OAAO,MAAM;AAAE,cAAU;AAAG,cAAU;AAAW,iBAAa;AAAM,mBAAe;AAAA,EAAM;AAC/F,QAAM,OAAO,MAAM;AACjB,UAAM,UAAU,IAAI;AACpB,UAAM,YAAY,OAAO,UAAU,WAAW,QAAQ,MAAM;AAC5D,QAAI,YAAY,cAAc,iBAAiB,aAAa,QAAS;AACrE,SAAK;AACL,QAAI,CAAC,QAAS;AACd,iBAAa;AACb,mBAAe;AACf,cAAU,QAAQ,UAAU,WAAW,aAAW,cAAc,OAAO,CAAC;AAAA,EAC1E;AACA,QAAM,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC;AACpC,MAAI,OAAO,UAAU,SAAU,OAAM,OAAO,IAAI;AAChD,QAAM,MAAM,SAAS,WAAS;AAAE,oBAAgB;AAAA,EAAO,CAAC;AACxD,kBAAgB,IAAI;AACtB;AAEO,SAAS,kBACd,KACmB;AACnB,QAAM,SAAS,IAAkB,cAAc,UAAU;AACzD,MAAI;AACJ,QAAM,KAAK,UAAQ;AACjB,cAAU;AAAG,cAAU;AACvB,WAAO,QAAQ,MAAM,UAAU,KAAK,cAAc;AAClD,QAAI,KAAM,WAAU,KAAK,SAAS,WAAS;AAAE,aAAO,QAAQ;AAAA,IAAO,CAAC;AAAA,EACtE,GAAG,EAAE,WAAW,KAAK,CAAC;AACtB,kBAAgB,MAAM,UAAU,CAAC;AACjC,SAAO;AACT;AAcO,SAAS,kBACd,KACA,SACkC;AAClC,QAAM,SAAS,IAAiC,IAAI;AACpD,MAAI,QAA+C;AACnD,MAAI,WAA8B,CAAC;AACnC,QAAM,WAAW,MAAM;AACrB,QAAI,MAAO,eAAc,KAAK;AAC9B,YAAQ;AACR,eAAW,WAAW,SAAU,SAAQ;AACxC,eAAW,CAAC;AAAA,EACd;AACA,QAAM,KAAK,UAAQ;AACjB,aAAS;AACT,QAAI,CAAC,MAAM;AACT,aAAO,QAAQ;AACf;AAAA,IACF;AACA,UAAM,UAAU,MAAM;AAAE,aAAO,QAAQ,KAAK,iBAAiB;AAAA,IAAG;AAChE,YAAQ;AACR,aAAS,KAAK,KAAK,SAAS,OAAO,CAAC;AACpC,aAAS,KAAK,KAAK,QAAQ,OAAO,CAAC;AACnC,UAAM,aAAa,SAAS,cAAc;AAC1C,QAAI,aAAa,EAAG,SAAQ,YAAY,SAAS,UAAU;AAAA,EAC7D,GAAG,EAAE,WAAW,KAAK,CAAC;AACtB,kBAAgB,QAAQ;AACxB,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/websocket.d.ts
CHANGED
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Wire protocol (JSON text frames):
|
|
10
10
|
* - client → server: `{"op":"subscribe"|"unsubscribe"|"publish","topic":...,"data":...}`
|
|
11
|
+
* - client → server (batched): `{"op":"publishBatch","topic":...,"items":[{data,...}]}`
|
|
11
12
|
* - server → client: `{"topic":...,"data":...}` for publications; anything
|
|
12
13
|
* without a string `topic` field is ignored (forward-compatible).
|
|
13
14
|
*/
|
|
14
15
|
import { CrossTabDataBus } from './core/data-bus';
|
|
15
16
|
import type { CrossTabDataBusOptions } from './core/data-bus';
|
|
16
|
-
import type { DataBusTransport, DataBusTransportHandlers, DataBusPublishOptions, MaybePromise, WorkerStatus } from './core/types';
|
|
17
|
+
import type { DataBusTransport, DataBusTransportHandlers, DataBusPublishOptions, DataBusPublicationItem, MaybePromise, WorkerStatus } from './core/types';
|
|
17
18
|
/** Minimal WebSocket surface used by the transport. Matches the browser
|
|
18
19
|
* `WebSocket` subset the transport touches; injectable for tests and runtimes. */
|
|
19
20
|
export interface WebSocketLike {
|
|
@@ -68,6 +69,10 @@ export declare class WebSocketTransport<TData = unknown> implements DataBusTrans
|
|
|
68
69
|
unsubscribe(topic: string): MaybePromise<void>;
|
|
69
70
|
/** Publish `data` to `topic` as a JSON frame. Requires an open socket. */
|
|
70
71
|
publish(topic: string, data: unknown, options?: DataBusPublishOptions): MaybePromise<void>;
|
|
72
|
+
/** Publish many items for one topic as a single wire frame. One-item
|
|
73
|
+
* batches delegate to `publish` so the legacy single-publication frame
|
|
74
|
+
* shape (including binary framing) is preserved. */
|
|
75
|
+
publishBatch(topic: string, items: ReadonlyArray<DataBusPublicationItem>): MaybePromise<void>;
|
|
71
76
|
/** Close the socket and drop all state. Safe to call multiple times. */
|
|
72
77
|
stop(): MaybePromise<void>;
|
|
73
78
|
/** Send one JSON frame. Frames are dropped with an `onError` report when
|
package/dist/websocket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"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"}
|
package/dist/worker-mode.d.ts
CHANGED
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
* Defines the WorkerMode preference flags and the selectWorkerBackend function
|
|
5
5
|
* that resolves the preference against browser capability to pick the actual
|
|
6
6
|
* backend (dedicated, shared, or local fallback).
|
|
7
|
+
*
|
|
8
|
+
* The literal values are derived from `utils/constants.ts` so the preference and
|
|
9
|
+
* resolved-backend strings referenced across the transport stay in one place.
|
|
7
10
|
*/
|
|
11
|
+
import { WORKER_BACKEND, WORKER_MODE } from './utils/constants';
|
|
8
12
|
/** Preferred Worker mode.
|
|
9
13
|
* - `dedicated` → try Dedicated Worker first (one WebSocket per tab).
|
|
10
14
|
* - `shared` → try SharedWorker first (one process, per-port connections).
|
|
11
15
|
* - `auto` → same as `shared` (alias for forward compatibility). */
|
|
12
|
-
export type WorkerMode =
|
|
16
|
+
export type WorkerMode = (typeof WORKER_MODE)[keyof typeof WORKER_MODE];
|
|
13
17
|
/** Resolved backend that was actually created. `local` means the session
|
|
14
18
|
* runs on the main thread (fallback when no Worker API is available). */
|
|
15
|
-
export type WorkerBackend =
|
|
19
|
+
export type WorkerBackend = (typeof WORKER_BACKEND)[keyof typeof WORKER_BACKEND];
|
|
16
20
|
/** Override Worker availability for testing or environments where feature
|
|
17
21
|
* detection is unreliable (e.g. sandboxed iframes). When a field is omitted,
|
|
18
22
|
* the global `typeof Worker` / `typeof SharedWorker` check is used. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-mode.d.ts","sourceRoot":"","sources":["../src/worker-mode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"worker-mode.d.ts","sourceRoot":"","sources":["../src/worker-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;oEAGoE;AACpE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAExE;yEACyE;AACzE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAEjF;;uEAEuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,YAAY,GAAE,kBAAuB,GACpC,aAAa,CAOf"}
|
package/docs/README.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
| [Architecture](./architecture.md) | Worker cluster, routing, storage, migration, and degradation design |
|
|
12
12
|
| [Capabilities Matrix](./capabilities.md) | Implemented, not implemented, and planned capabilities matrix |
|
|
13
13
|
| [Roadmap](./roadmap.md) | Release-oriented priorities and verification checklist |
|
|
14
|
+
| [Benchmark trend](./benchmarks.md) | Auto-generated browser benchmark history from `bench-results/` |
|
|
14
15
|
| [Release checklist](./release-checklist.md) | Local gates, tagging, publishing, and post-release verification |
|
|
15
16
|
| [../examples/demo](../examples/demo) | Runnable multi-tab browser demo |
|
|
16
17
|
| [../CHANGELOG.md](../CHANGELOG.md) | Version changelog |
|