cross-tab-worker-databus 0.20.71 → 0.20.86
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 +216 -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-D2SIT473.js → chunk-SDOV3UHG.js} +1304 -515
- package/dist/chunk-SDOV3UHG.js.map +7 -0
- package/dist/chunk-TZ7ZP7YD.js +175 -0
- package/dist/chunk-TZ7ZP7YD.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +1608 -650
- 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 +1692 -747
- 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 +134 -77
- 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 +129 -0
- package/dist/core/replay-manager.d.ts.map +1 -0
- package/dist/core/replay-persistence.d.ts +2 -1
- package/dist/core/replay-persistence.d.ts.map +1 -1
- package/dist/core/replay-pruning.d.ts +21 -0
- package/dist/core/replay-pruning.d.ts.map +1 -0
- 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 +263 -166
- 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 +135 -4
- package/docs/architecture.md +92 -1
- 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 +81 -5
- package/docs/zh/README.md +2 -1
- package/docs/zh/api.md +134 -4
- package/docs/zh/architecture.md +61 -1
- 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 +93 -5
- package/package.json +39 -16
- package/dist/chunk-D2SIT473.js.map +0 -7
package/dist/cjs/vue.cjs
CHANGED
|
@@ -21,11 +21,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var vue_exports = {};
|
|
22
22
|
__export(vue_exports, {
|
|
23
23
|
useCrossTabDataBus: () => useCrossTabDataBus,
|
|
24
|
+
useCrossTabHealth: () => useCrossTabHealth,
|
|
24
25
|
useCrossTabStatus: () => useCrossTabStatus,
|
|
25
26
|
useCrossTabSubscription: () => useCrossTabSubscription
|
|
26
27
|
});
|
|
27
28
|
module.exports = __toCommonJS(vue_exports);
|
|
28
29
|
var import_vue = require("vue");
|
|
30
|
+
|
|
31
|
+
// src/utils/constants.ts
|
|
32
|
+
var WORKER_STATUS = {
|
|
33
|
+
CONNECTING: "connecting",
|
|
34
|
+
CONNECTED: "connected",
|
|
35
|
+
DISCONNECTED: "disconnected",
|
|
36
|
+
ERROR: "error"
|
|
37
|
+
};
|
|
38
|
+
var DEFAULT_STORAGE_PREFIX = "cross-tab-worker-databus";
|
|
39
|
+
var STORAGE_CHANNEL_PREFIX = `${DEFAULT_STORAGE_PREFIX}:channel:`;
|
|
40
|
+
var TAB_ID_STORAGE_KEY = `${DEFAULT_STORAGE_PREFIX}:tab-id`;
|
|
41
|
+
|
|
42
|
+
// src/vue.ts
|
|
29
43
|
function useCrossTabDataBus(create, deps = []) {
|
|
30
44
|
const bus = (0, import_vue.shallowRef)(null);
|
|
31
45
|
let instance = null;
|
|
@@ -49,6 +63,7 @@ function useCrossTabDataBus(create, deps = []) {
|
|
|
49
63
|
};
|
|
50
64
|
(0, import_vue.onMounted)(start);
|
|
51
65
|
(0, import_vue.onBeforeUnmount)(() => {
|
|
66
|
+
lifecycleGeneration += 1;
|
|
52
67
|
void stop();
|
|
53
68
|
});
|
|
54
69
|
if (deps.length > 0) (0, import_vue.watch)(deps, start);
|
|
@@ -83,12 +98,12 @@ function useCrossTabSubscription(bus, topic, handler) {
|
|
|
83
98
|
(0, import_vue.onBeforeUnmount)(stop);
|
|
84
99
|
}
|
|
85
100
|
function useCrossTabStatus(bus) {
|
|
86
|
-
const status = (0, import_vue.ref)(
|
|
101
|
+
const status = (0, import_vue.ref)(WORKER_STATUS.CONNECTING);
|
|
87
102
|
let cleanup;
|
|
88
103
|
(0, import_vue.watch)(bus, (next) => {
|
|
89
104
|
cleanup?.();
|
|
90
105
|
cleanup = void 0;
|
|
91
|
-
status.value = next?.getStatus() ??
|
|
106
|
+
status.value = next?.getStatus() ?? WORKER_STATUS.CONNECTING;
|
|
92
107
|
if (next) cleanup = next.onStatus((value) => {
|
|
93
108
|
status.value = value;
|
|
94
109
|
});
|
|
@@ -96,4 +111,32 @@ function useCrossTabStatus(bus) {
|
|
|
96
111
|
(0, import_vue.onBeforeUnmount)(() => cleanup?.());
|
|
97
112
|
return status;
|
|
98
113
|
}
|
|
114
|
+
function useCrossTabHealth(bus, options) {
|
|
115
|
+
const health = (0, import_vue.ref)(null);
|
|
116
|
+
let timer = null;
|
|
117
|
+
let cleanups = [];
|
|
118
|
+
const teardown = () => {
|
|
119
|
+
if (timer) clearInterval(timer);
|
|
120
|
+
timer = null;
|
|
121
|
+
for (const cleanup of cleanups) cleanup();
|
|
122
|
+
cleanups = [];
|
|
123
|
+
};
|
|
124
|
+
(0, import_vue.watch)(bus, (next) => {
|
|
125
|
+
teardown();
|
|
126
|
+
if (!next) {
|
|
127
|
+
health.value = null;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const refresh = () => {
|
|
131
|
+
health.value = next.getHealthSummary();
|
|
132
|
+
};
|
|
133
|
+
refresh();
|
|
134
|
+
cleanups.push(next.onStatus(refresh));
|
|
135
|
+
cleanups.push(next.onError(refresh));
|
|
136
|
+
const intervalMs = options?.intervalMs ?? 1e3;
|
|
137
|
+
if (intervalMs > 0) timer = setInterval(refresh, intervalMs);
|
|
138
|
+
}, { immediate: true });
|
|
139
|
+
(0, import_vue.onBeforeUnmount)(teardown);
|
|
140
|
+
return health;
|
|
141
|
+
}
|
|
99
142
|
//# sourceMappingURL=vue.cjs.map
|
package/dist/cjs/vue.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 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(); });\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>('connecting');\n let cleanup: (() => void) | undefined;\n watch(bus, next => {\n cleanup?.(); cleanup = undefined;\n status.value = next?.getStatus() ?? 'connecting';\n if (next) cleanup = next.onStatus(value => { status.value = value; });\n }, { immediate: true });\n onBeforeUnmount(() => cleanup?.());\n return status;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,iBAA6E;
|
|
3
|
+
"sources": ["../../src/vue.ts", "../../src/utils/constants.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, 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", "/**\n * \u516C\u5171\u5B57\u7B26\u4E32\u5E38\u91CF \u2014\u2014 \u751F\u547D\u5468\u671F\u72B6\u6001\u3001\u89D2\u8272\u3001\u63A7\u5236\u52A8\u4F5C\u3001\u534F\u8BAE\u6D88\u606F\u7C7B\u578B\u3001\u679A\u4E3E\u4E0E\n * \u547D\u540D\u7A7A\u95F4\u524D\u7F00\u3002\n *\n * \u6240\u6709\u8FD0\u884C\u65F6\u4F7F\u7528\u7684\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u96C6\u4E2D\u5728\u6B64\u4E00\u4EFD\u503C\uFF0C\u7C7B\u578B\u5B9A\u4E49\uFF08src/core/types.ts\n * \u7B49\uFF09\u901A\u8FC7 `(typeof X)[keyof typeof X]` \u4ECE\u8FD9\u4E9B\u5E38\u91CF\u6D3E\u751F\uFF0C\u6BD4\u8F83/switch \u5904\u5F15\u7528\n * \u540C\u4E00\u5BF9\u8C61\u6210\u5458\u3002\u8FD9\u6837\u65E2\u80FD\u6D88\u9664\u6563\u843D\u5728\u5404\u6587\u4EF6\u91CC\u7684\u91CD\u590D\u5B57\u7B26\u4E32\uFF08\u907F\u514D\u5927\u5C0F\u5199\u4E0D\u7EDF\u4E00\n * \u6216\u7B14\u8BEF\uFF09\uFF0C\u53C8\u4FDD\u8BC1\u7C7B\u578B\u4E0E\u503C\u6C38\u4E0D\u9519\u4F4D\u3002\n *\n * ## \u4F7F\u7528\u7EA6\u5B9A\n *\n * - **\u65B0\u589E\u6216\u4FEE\u6539\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u65F6\uFF0C\u5148\u5230\u8FD9\u91CC\u67E5\u627E/\u589E\u8865\uFF0C\u4E0D\u8981\u5728\u4E1A\u52A1\u6587\u4EF6\u91CC\u76F4\u63A5\u5199\n * \u9B54\u6CD5\u5B57\u7B26\u4E32\u3002** \u626B\u63CF\u6B8B\u7559\u5B57\u9762\u91CF\uFF1A`rg \"'(['a-z]+)'\" src/`\u3002\n * - **\u53EA\u7528\u4E8E\u7C7B\u578B\u4F4D\u7F6E\u7684\u5E38\u91CF**\uFF08\u5982 `PERSISTENCE_OPERATION`\uFF09\u5728\u8C03\u7528\u65B9\u7528\n * `import type`\uFF1B**\u5728\u8FD0\u884C\u65F6\u6BD4\u8F83/\u6784\u9020\u4E2D\u4F7F\u7528\u7684**\uFF08\u5982 `WORKER_STATUS.ERROR`\u3001\n * `TRACE_EVENT_TYPE.LIFECYCLE`\uFF09\u5FC5\u987B\u7528\u503C\u5BFC\u5165\uFF0C\u5426\u5219\u4F1A\u62A5 \"cannot be used as\n * a value because it was imported using 'import type'\"\u3002\n * - **\u503C\u6D3E\u751F\u7C7B\u578B**\uFF1A`export type WorkerStatus = (typeof WORKER_STATUS)[keyof\n * typeof WORKER_STATUS]`\u3002\u8FD9\u6837\u65B0\u589E\u679A\u4E3E\u5206\u652F\u65F6\u7C7B\u578B\u81EA\u52A8\u6536\u7A84\uFF0C\u7F16\u8BD1\u5668\u4F1A\u6307\u51FA\n * \u6BCF\u4E2A\u9057\u6F0F\u7684 switch \u5206\u652F\u3002\n * - \u5206\u7EC4\u7684\u987A\u5E8F\uFF08\u72B6\u6001 / \u89D2\u8272 / \u540E\u7AEF / \u6D88\u606F\u7C7B\u578B / \u679A\u4E3E / \u524D\u7F00\uFF09\u4FDD\u6301\u7A33\u5B9A\uFF0C\u65B9\u4FBF\n * \u7EF4\u62A4\u8005\u4E00\u773C\u5B9A\u4F4D\u3002\n */\n\n// ---- \u751F\u547D\u5468\u671F\u72B6\u6001 / \u89D2\u8272 / \u53EF\u89C1\u6027 ----\nexport const WORKER_STATUS = {\n CONNECTING: 'connecting',\n CONNECTED: 'connected',\n DISCONNECTED: 'disconnected',\n ERROR: 'error',\n} as const;\n\nexport const WORKER_ROLE = {\n ACTIVE: 'active',\n STANDBY: 'standby',\n} as const;\n\nexport const TAB_VISIBILITY = {\n VISIBLE: 'visible',\n HIDDEN: 'hidden',\n} as const;\n\n// ---- Worker \u540E\u7AEF\u6A21\u5F0F\u4E0E\u89E3\u6790\u7ED3\u679C\uFF08WorkerMode / WorkerBackend\uFF09----\nexport const WORKER_MODE = {\n DEDICATED: 'dedicated',\n SHARED: 'shared',\n AUTO: 'auto',\n} as const;\n\nexport const WORKER_BACKEND = {\n DEDICATED: 'dedicated',\n SHARED: 'shared',\n LOCAL: 'local',\n} as const;\n\n// ---- \u5B58\u50A8\u4E8B\u4EF6\u901A\u9053\u56DE\u9000 ----\nexport const CHANNEL_FALLBACK = {\n NONE: 'none',\n STORAGE_EVENT: 'storage-event',\n} as const;\n\n// ---- Worker / MessagePort / BroadcastChannel \u7684 EventTarget \u4E8B\u4EF6\u540D\u5224\u522B ----\n// MessageEvent.type\uFF08'message' / 'messageerror'\uFF09\u4E0E Worker \u4E0A\u7684 'error' \u4E8B\u4EF6\u540D\uFF0C\n// \u7528\u4E8E\u73AF\u5883\u9002\u914D\u5668\u63A5\u53E3\u7B7E\u540D\u4E0E\u6D4B\u8BD5\u66FF\u8EAB\u7684 addEventListener \u771F\u5047\u5206\u652F\u3002\nexport const EVENT_TYPE = {\n MESSAGE: 'message',\n MESSAGEERROR: 'messageerror',\n ERROR: 'error',\n} as const;\n\n// ---- \u63A7\u5236\u5E73\u9762\u52A8\u4F5C\uFF08WorkerControlAction\uFF09----\nexport const CONTROL_ACTION = {\n SUBSCRIBE: 'SUBSCRIBE',\n UNSUBSCRIBE: 'UNSUBSCRIBE',\n PUBLISH: 'PUBLISH',\n} as const;\n\n// ---- BroadcastChannel \u96C6\u7FA4\u6D88\u606F\u7C7B\u578B ----\nexport const CLUSTER_MESSAGE_TYPE = {\n CONTROL: 'CONTROL',\n EVENT: 'EVENT',\n REGISTRY: 'REGISTRY',\n ROUTE_RELEASED: 'ROUTE_RELEASED',\n} as const;\n\n// ---- \u547D\u540D\u7A7A\u95F4\u524D\u7F00\u4E0E\u4E8B\u4EF6\u540D ----\nexport const DEFAULT_STORAGE_PREFIX = 'cross-tab-worker-databus';\nexport const STORAGE_CHANNEL_PREFIX = `${DEFAULT_STORAGE_PREFIX}:channel:`;\nexport const TAB_ID_STORAGE_KEY = `${DEFAULT_STORAGE_PREFIX}:tab-id`;\nexport const PUBLICATION_EVENT = 'DATABUS_PUBLICATION';\n\n// ---- \u89C4\u8303 JSON \u4FE1\u5C01\u64CD\u4F5C\u7801 ----\nexport const PUBLICATION_ENVELOPE_OP = 'publication';\n\n// ---- trace \u4E8B\u4EF6\u7C7B\u578B ----\nexport const TRACE_EVENT_TYPE = {\n LIFECYCLE: 'lifecycle',\n STATUS: 'status',\n SUBSCRIPTION: 'subscription',\n COORDINATION: 'coordination',\n ERROR: 'error',\n RELIABILITY: 'reliability',\n MESSAGE_METRICS: 'message_metrics',\n} as const;\n\n// ---- trace \u751F\u547D\u5468\u671F\u52A8\u4F5C ----\nexport const TRACE_LIFECYCLE_ACTION = {\n START: 'start',\n STOP: 'stop',\n SUSPEND: 'suspend',\n RESUME: 'resume',\n} as const;\n\n// ---- \u5185\u90E8 handler \u8C03\u7528\u6807\u7B7E\uFF08\u9694\u79BB\u5F02\u5E38\u65F6\u7684\u5206\u7C7B\u6765\u6E90\uFF09----\nexport const INVOKE_LABEL = {\n DISPATCH: 'dispatch',\n STATUS: 'status',\n ERROR_HANDLER: 'error handler',\n} as const;\n\n// ---- trace \u6A21\u5F0F\uFF08DataBusTraceMode\uFF09----\nexport const TRACE_MODE = {\n EVENTS: 'events',\n METRICS: 'metrics',\n ALL: 'all',\n} as const;\n\n// ---- trace \u9519\u8BEF\u6765\u6E90 ----\nexport const TRACE_ERROR_SOURCE = {\n TRANSPORT: 'transport',\n OPERATION: 'operation',\n} as const;\n\n// ---- reliability \u8BCA\u65AD\u64CD\u4F5C ----\nexport const RELIABILITY_OPERATION = {\n TRANSPORT_RECOVERY: 'transport_recovery',\n ROUTE_ACK: 'route_ack',\n ROUTE_MIGRATION: 'route_migration',\n // A re-election that recovered a stranded unconfirmed handoff (previous\n // owner gone, ACK never arrived). Distinct from ROUTE_MIGRATION so trace\n // consumers can tell recoveries apart from routine graceful handoffs.\n ROUTE_MIGRATION_RECOVERY: 'route_migration_recovery',\n PERSISTENCE_CLEANUP: 'persistence_cleanup',\n PERSISTENCE_RETRY: 'persistence_retry',\n DEDUP_SUPPRESSED: 'dedup_suppressed',\n} as const;\n\n// ---- \u81EA\u52A8\u6062\u590D\u5C1D\u8BD5\u7ED3\u679C ----\nexport const RECOVERY_OUTCOME = {\n SCHEDULED: 'scheduled',\n SUCCEEDED: 'succeeded',\n FAILED: 'failed',\n EXHAUSTED: 'exhausted',\n} as const;\n\n// ---- \u8BA2\u9605\u52A8\u4F5C ----\nexport const SUBSCRIPTION_ACTION = {\n SUBSCRIBE: 'subscribe',\n UNSUBSCRIBE: 'unsubscribe',\n} as const;\n\n// ---- \u56DE\u653E\u4FEE\u526A\u7B56\u7565 ----\nexport const PRUNE_STRATEGY = {\n COUNT: 'count',\n AGE: 'age',\n BOTH: 'both',\n} as const;\n\n// ---- \u6301\u4E45\u5316\u64CD\u4F5C ----\nexport const PERSISTENCE_OPERATION = {\n LOAD: 'load',\n APPEND: 'append',\n CLEAR: 'clear',\n CLEAR_TOPIC: 'clearTopic',\n CLEAR_BEFORE: 'clearBefore',\n} as const;\n\n// ---- \u6545\u969C\u6765\u6E90\uFF08DataBusFailureSource\uFF09----\nexport const FAILURE_SOURCE = {\n TRANSPORT: 'transport',\n PERSISTENCE: 'persistence',\n DISPATCH: 'dispatch',\n} as const;\n\n// ---- \u5065\u5EB7\u5224\u5B9A\u72B6\u6001\uFF08DataBusHealthSummary['state']\uFF09----\nexport const HEALTH_STATE = {\n STOPPED: 'stopped',\n STARTING: 'starting',\n HEALTHY: 'healthy',\n RECOVERING: 'recovering',\n SUSPENDED: 'suspended',\n DEGRADED: 'degraded',\n} as const;\n\n// ---- WebSocket wire \u534F\u8BAE\u64CD\u4F5C\u7801 ----\nexport const WS_OP = {\n SUBSCRIBE: 'subscribe',\n UNSUBSCRIBE: 'unsubscribe',\n PUBLISH: 'publish',\n PUBLISH_BATCH: 'publishBatch',\n} as const;\n\n// ---- Centrifuge Worker \u8F93\u5165\u6D88\u606F\u7C7B\u578B ----\nexport const CENTRIFUGE_INPUT_TYPE = {\n INIT: 'INIT',\n SUBSCRIBE: 'SUBSCRIBE',\n UNSUBSCRIBE: 'UNSUBSCRIBE',\n PUBLISH: 'PUBLISH',\n PUBLISH_BIN: 'PUBLISH_BIN',\n PING: 'PING',\n STOP: 'STOP',\n TOKEN_RESPONSE: 'TOKEN_RESPONSE',\n TOKEN_ERROR: 'TOKEN_ERROR',\n} as const;\n\n// ---- Centrifuge Worker \u8F93\u51FA\u6D88\u606F\u7C7B\u578B ----\nexport const CENTRIFUGE_OUTPUT_TYPE = {\n STATUS: 'STATUS',\n MESSAGE: 'MESSAGE',\n MESSAGE_BIN: 'MESSAGE_BIN',\n ERROR: 'ERROR',\n TOKEN_REQUEST: 'TOKEN_REQUEST',\n} as const;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/core/cluster.d.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { ClusterEnvironment } from './environment';
|
|
2
|
-
import type { DataBusPublicationMetadata, WorkerControlAction, WorkerRecord, WorkerRoute, WorkerStatus } from './types';
|
|
2
|
+
import type { DataBusPublicationMetadata, LoadWeightingOptions, WorkerControlAction, WorkerRecord, WorkerRoute, WorkerStatus } from './types';
|
|
3
|
+
import { RELIABILITY_OPERATION } from '../utils/constants';
|
|
3
4
|
/** Callbacks the cluster invokes to drive the transport and lifecycle. */
|
|
4
5
|
export interface WorkerClusterHandlers {
|
|
5
6
|
/** A SUBSCRIBE/UNSUBSCRIBE/PUBLISH control action was received for this worker. */
|
|
6
7
|
onControl: (action: WorkerControlAction, topic: string, data?: unknown, messageId?: string, timestamp?: number) => void;
|
|
8
|
+
/** Optional batched variant of the PUBLISH action: invoked once when a
|
|
9
|
+
* CONTROL frame carries multiple publication items. When absent, the
|
|
10
|
+
* cluster falls back to per-item `onControl('PUBLISH', …)` calls. */
|
|
11
|
+
onPublishBatch?: (topic: string, items: ReadonlyArray<{
|
|
12
|
+
data: unknown;
|
|
13
|
+
messageId?: string;
|
|
14
|
+
timestamp?: number;
|
|
15
|
+
}>) => void;
|
|
7
16
|
/** A fan-out publication event was received from another Worker.
|
|
8
17
|
* `originTabId` is the tab that produced the original publication when the
|
|
9
18
|
* cluster forwards one; it survives the BroadcastChannel hop so listeners
|
|
@@ -15,9 +24,9 @@ export interface WorkerClusterHandlers {
|
|
|
15
24
|
onSuspend?: () => void;
|
|
16
25
|
/** The cluster resumed (tab visible / pageshow). */
|
|
17
26
|
onResume?: () => void;
|
|
18
|
-
/** Bounded diagnostics for route confirmation and
|
|
27
|
+
/** Bounded diagnostics for route confirmation, graceful migration, and stranded-handoff recovery. */
|
|
19
28
|
onDiagnostic?: (event: {
|
|
20
|
-
operation:
|
|
29
|
+
operation: (typeof RELIABILITY_OPERATION.ROUTE_ACK | typeof RELIABILITY_OPERATION.ROUTE_MIGRATION | typeof RELIABILITY_OPERATION.ROUTE_MIGRATION_RECOVERY);
|
|
21
30
|
topic: string;
|
|
22
31
|
}) => void;
|
|
23
32
|
}
|
|
@@ -44,6 +53,11 @@ export interface WorkerClusterOptions {
|
|
|
44
53
|
/** Maximum entries kept in the publish route-owner cache (default 256).
|
|
45
54
|
* When the cap is reached, the oldest (FIFO) entry is evicted. */
|
|
46
55
|
routeOwnerCacheMax?: number;
|
|
56
|
+
/** Optional adaptive owner weighting. When set, this worker samples its own
|
|
57
|
+
* fan-out traffic and publishes it with every heartbeat so peers can steer
|
|
58
|
+
* NEW routes toward quieter workers; the weights are forwarded to the
|
|
59
|
+
* least-loaded selection. Absent (default) keeps pure topic-count routing. */
|
|
60
|
+
loadWeighting?: LoadWeightingOptions;
|
|
47
61
|
}
|
|
48
62
|
/** Read-only snapshot of the cluster state for diagnostics and tracing. */
|
|
49
63
|
export interface WorkerClusterSnapshot {
|
|
@@ -98,6 +112,10 @@ export declare class WorkerClusterRuntime {
|
|
|
98
112
|
private readonly routePrefix;
|
|
99
113
|
private readonly subscriberPrefix;
|
|
100
114
|
private readonly channelName;
|
|
115
|
+
/** Adaptive load weighting options; undefined keeps legacy topic-count routing. */
|
|
116
|
+
private readonly loadWeighting;
|
|
117
|
+
/** Rolling traffic accumulator folded into the worker record on writeRecord. */
|
|
118
|
+
private throughputWindow;
|
|
101
119
|
private readonly subscribedTopics;
|
|
102
120
|
private readonly assignedTopics;
|
|
103
121
|
private readonly routeOwnerCache;
|
|
@@ -180,6 +198,9 @@ export declare class WorkerClusterRuntime {
|
|
|
180
198
|
private resolvePublishTarget;
|
|
181
199
|
/** Fan out a single batched item to the local onControl path. */
|
|
182
200
|
private dispatchLocalPublish;
|
|
201
|
+
/** Fan out a publication batch to the local onControl path: one
|
|
202
|
+
* onPublishBatch call when the owner supports it, per-item otherwise. */
|
|
203
|
+
private dispatchLocalPublishBatch;
|
|
183
204
|
/** True when `route` exists and its owner worker is among `workers`.
|
|
184
205
|
* Shared by subscribe (skip re-assignment) and publish (route to owner).
|
|
185
206
|
* Intentionally returns a plain boolean (not a type guard) so the caller
|
|
@@ -189,6 +210,13 @@ export declare class WorkerClusterRuntime {
|
|
|
189
210
|
* `originTabId` (when set) is propagated across the BroadcastChannel hop so
|
|
190
211
|
* listeners can attribute the event to its source tab even after fan-out. */
|
|
191
212
|
broadcastEvent(eventType: string, payload: unknown, originTabId?: string): void;
|
|
213
|
+
/** Count one fan-out unit toward the adaptive load sample. No-op unless
|
|
214
|
+
* adaptive weighting is configured. */
|
|
215
|
+
private recordTraffic;
|
|
216
|
+
/** Convert the accumulated window into a publishable throughput sample and
|
|
217
|
+
* reset the accumulator. Returns undefined until a full window has elapsed
|
|
218
|
+
* so the first write does not emit a zero-width sample. */
|
|
219
|
+
private sampleThroughput;
|
|
192
220
|
isAssigned(topic: string): boolean;
|
|
193
221
|
/** True if this worker is among the active set (eligible to own topics). */
|
|
194
222
|
isActiveWorker(): boolean;
|
|
@@ -232,8 +260,16 @@ export declare class WorkerClusterRuntime {
|
|
|
232
260
|
private handleRouteReleasedMessage;
|
|
233
261
|
/** A ROUTE_RELEASED is stale (and must be dropped) unless the route still
|
|
234
262
|
* points to us, the release comes from the recorded previous owner, and
|
|
235
|
-
* the release generation is at least as new as ours.
|
|
263
|
+
* the release generation is at least as new as ours. A replayed ACK from an
|
|
264
|
+
* earlier handoff round (e.g. an a↔b ping-pong) carries an older generation
|
|
265
|
+
* and must not confirm the current round. */
|
|
236
266
|
private isStaleRouteRelease;
|
|
267
|
+
/** True when an unconfirmed handoff route has been stuck longer than a
|
|
268
|
+
* worker TTL. The ACK for a live handoff is posted synchronously with the
|
|
269
|
+
* route write, so anything older than the TTL with a dead previous owner
|
|
270
|
+
* will never complete — while a fresh unconfirmed route may simply be
|
|
271
|
+
* waiting out its confirmation flush and must be left alone. */
|
|
272
|
+
private isStaleHandoff;
|
|
237
273
|
/** Full reconciliation cycle: workers, subscriptions, and assigned topics. */
|
|
238
274
|
private reconcile;
|
|
239
275
|
/** Prune stale workers/subscribers/routes and refresh role. Returns the live worker list.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/core/cluster.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,kBAAkB,EAAe,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/core/cluster.ts"],"names":[],"mappings":"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"}
|
package/dist/core/data-bus.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { WorkerClusterOptions, WorkerClusterSnapshot } from './cluster';
|
|
2
2
|
import type { DataBusErrorHandler, DataBusMessageHandler, DataBusPublishOptions, DataBusStatusHandler, DataBusTransport, WorkerStatus } from './types';
|
|
3
|
-
import type { DataBusTraceOptions } from './trace';
|
|
3
|
+
import type { DataBusMetricsSnapshot, DataBusTraceOptions } from './trace';
|
|
4
4
|
import type { DataBusReplayPersistence } from './replay-persistence';
|
|
5
|
+
import type { DataBusDedupOptions, DataBusDedupStats } from './dedup-manager';
|
|
6
|
+
import { FAILURE_SOURCE, HEALTH_STATE, PRUNE_STRATEGY } from '../utils/constants';
|
|
5
7
|
/** Constructor options for {@link CrossTabDataBus}. Extends WorkerClusterOptions
|
|
6
8
|
* (cluster coordination config) with the transport, initial connection config,
|
|
7
9
|
* and trace options. */
|
|
@@ -11,15 +13,21 @@ import type { DataBusReplayPersistence } from './replay-persistence';
|
|
|
11
13
|
* Buffers live in memory by default; an optional persistence backend can make
|
|
12
14
|
* them durable. */
|
|
13
15
|
export interface DataBusReplayOptions<TData = unknown> {
|
|
14
|
-
/** Maximum buffered publications per topic
|
|
15
|
-
*
|
|
16
|
+
/** Maximum buffered publications per topic under 'count'/'both'. With 'age',
|
|
17
|
+
* timestamped history is bounded by `retentionMs` and timestamp-less legacy
|
|
18
|
+
* entries are capped by this value. Oldest entries are evicted first.
|
|
19
|
+
* Default 100. */
|
|
16
20
|
maxPerTopic?: number;
|
|
17
21
|
/** Optional durable history backend. Defaults to in-memory only. */
|
|
18
22
|
persistence?: DataBusReplayPersistence<TData>;
|
|
19
23
|
/** Optional producer-timestamp retention window in milliseconds. */
|
|
20
24
|
retentionMs?: number;
|
|
21
|
-
/** History trimming policy
|
|
22
|
-
|
|
25
|
+
/** History trimming policy: 'count' (default) caps each topic at
|
|
26
|
+
* `maxPerTopic`, 'age' prunes by `retentionMs`, and 'both' applies both.
|
|
27
|
+
* With 'age', timestamped entries are bounded by the retention window and
|
|
28
|
+
* timestamp-less legacy entries are capped by `maxPerTopic`. An 'age'
|
|
29
|
+
* strategy without `retentionMs` falls back to the count cap. */
|
|
30
|
+
pruneStrategy?: (typeof PRUNE_STRATEGY)[keyof typeof PRUNE_STRATEGY];
|
|
23
31
|
/** Optional periodic sweep interval for durable retention cleanup. */
|
|
24
32
|
retentionSweepMs?: number;
|
|
25
33
|
/** Optional bounded retry policy for transient persistence failures. */
|
|
@@ -31,27 +39,7 @@ export interface DataBusPersistenceRetryOptions {
|
|
|
31
39
|
/** Initial delay between attempts. Default 50ms. */
|
|
32
40
|
backoffMs?: number;
|
|
33
41
|
}
|
|
34
|
-
|
|
35
|
-
export interface DataBusDedupOptions {
|
|
36
|
-
maxEntries?: number;
|
|
37
|
-
ttlMs?: number;
|
|
38
|
-
/** Optional periodic sweep interval for quiet-topic expiry. */
|
|
39
|
-
sweepMs?: number;
|
|
40
|
-
/** Injectable epoch clock for deterministic tests and non-wall-clock hosts. */
|
|
41
|
-
now?: () => number;
|
|
42
|
-
/** Optional adaptive TTL bounds; enabled only when both are provided. */
|
|
43
|
-
adaptiveTtl?: {
|
|
44
|
-
minMs: number;
|
|
45
|
-
maxMs: number;
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
export interface DataBusDedupStats {
|
|
49
|
-
enabled: boolean;
|
|
50
|
-
tracked: number;
|
|
51
|
-
suppressed: number;
|
|
52
|
-
accepted: number;
|
|
53
|
-
ttlMs?: number;
|
|
54
|
-
}
|
|
42
|
+
export type { DataBusDedupOptions, DataBusDedupStats };
|
|
55
43
|
export interface DataBusDiagnostics {
|
|
56
44
|
sdkVersion: string;
|
|
57
45
|
status: WorkerStatus;
|
|
@@ -72,7 +60,9 @@ export interface DataBusDiagnostics {
|
|
|
72
60
|
enabled: boolean;
|
|
73
61
|
topics: number;
|
|
74
62
|
messages: number;
|
|
63
|
+
bytes: number;
|
|
75
64
|
};
|
|
65
|
+
persistence: DataBusPersistenceHealth;
|
|
76
66
|
protocol: {
|
|
77
67
|
version: number;
|
|
78
68
|
unknownMessages: number;
|
|
@@ -82,8 +72,64 @@ export interface DataBusDiagnostics {
|
|
|
82
72
|
transport: {
|
|
83
73
|
name: string;
|
|
84
74
|
backend: string | null;
|
|
75
|
+
status: WorkerStatus;
|
|
76
|
+
suspended: boolean;
|
|
85
77
|
};
|
|
86
78
|
cluster: WorkerClusterSnapshot;
|
|
79
|
+
/** Current trace metrics window counters, or null when metrics are inactive. */
|
|
80
|
+
metrics: DataBusMetricsSnapshot | null;
|
|
81
|
+
/** Trace sink delivery mode and queued-event depth (asyncSink back-pressure). */
|
|
82
|
+
trace: {
|
|
83
|
+
asyncSink: boolean;
|
|
84
|
+
pendingEvents: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Where a retained failure originated, as surfaced by {@link DataBusHealthSummary}. */
|
|
88
|
+
export type DataBusFailureSource = (typeof FAILURE_SOURCE)[keyof typeof FAILURE_SOURCE];
|
|
89
|
+
export interface DataBusLastFailure {
|
|
90
|
+
source: DataBusFailureSource;
|
|
91
|
+
message: string;
|
|
92
|
+
at: number;
|
|
93
|
+
}
|
|
94
|
+
/** Bounded failure counters for the optional replay persistence backend. */
|
|
95
|
+
export interface DataBusPersistenceHealth {
|
|
96
|
+
/** Total persistence failures reported since the last explicit start(). */
|
|
97
|
+
failures: number;
|
|
98
|
+
lastFailureAt: number | null;
|
|
99
|
+
lastErrorMessage: string | null;
|
|
100
|
+
}
|
|
101
|
+
/** Compact single-object health verdict for dashboards and readiness probes.
|
|
102
|
+
* Unlike {@link DataBusDiagnostics} this answers one question first — is the
|
|
103
|
+
* bus usable right now — then attaches the failure and recovery context that
|
|
104
|
+
* explains the verdict. */
|
|
105
|
+
export interface DataBusHealthSummary {
|
|
106
|
+
/** True only while the bus is started, not suspended, and the transport is ready. */
|
|
107
|
+
healthy: boolean;
|
|
108
|
+
/** Lifecycle-derived verdict: 'stopped' | 'starting' | 'healthy' | 'recovering' | 'suspended' | 'degraded'.
|
|
109
|
+
* 'degraded' means automatic recovery is exhausted and the transport is still down — a manual
|
|
110
|
+
* start() (or resume) is required. */
|
|
111
|
+
state: (typeof HEALTH_STATE)[keyof typeof HEALTH_STATE];
|
|
112
|
+
status: WorkerStatus;
|
|
113
|
+
sdkVersion: string;
|
|
114
|
+
started: boolean;
|
|
115
|
+
suspended: boolean;
|
|
116
|
+
transport: {
|
|
117
|
+
name: string;
|
|
118
|
+
backend: string | null;
|
|
119
|
+
ready: boolean;
|
|
120
|
+
status: WorkerStatus;
|
|
121
|
+
};
|
|
122
|
+
recovery: ReturnType<CrossTabDataBus['getRecoveryStats']>;
|
|
123
|
+
/** Most recent failure of any source since the last explicit start(). */
|
|
124
|
+
lastFailure: DataBusLastFailure | null;
|
|
125
|
+
persistence: DataBusPersistenceHealth;
|
|
126
|
+
/** Current trace metrics window, or null when trace metrics are inactive. */
|
|
127
|
+
metrics: DataBusMetricsSnapshot | null;
|
|
128
|
+
/** Trace sink delivery mode and queued-event depth (asyncSink back-pressure). */
|
|
129
|
+
trace: {
|
|
130
|
+
asyncSink: boolean;
|
|
131
|
+
pendingEvents: number;
|
|
132
|
+
};
|
|
87
133
|
}
|
|
88
134
|
export interface CrossTabDataBusOptions<TConfig, TData> extends Omit<WorkerClusterOptions, 'handlers'> {
|
|
89
135
|
transport: DataBusTransport<TConfig, TData>;
|
|
@@ -115,37 +161,12 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
115
161
|
private readonly transportSubscribedTopics;
|
|
116
162
|
private readonly statusHandlers;
|
|
117
163
|
private readonly errorHandlers;
|
|
118
|
-
private readonly
|
|
119
|
-
private readonly replayMaxPerTopic;
|
|
120
|
-
private readonly replayPersistence;
|
|
121
|
-
private readonly replayRetentionMs;
|
|
122
|
-
private readonly replayPruneStrategy;
|
|
123
|
-
private readonly replayRetentionSweepMs;
|
|
124
|
-
private readonly persistenceRetryMaxAttempts;
|
|
125
|
-
private readonly persistenceRetryBackoffMs;
|
|
126
|
-
private persistenceRetryGeneration;
|
|
127
|
-
private pendingReplayPersistence;
|
|
128
|
-
private replayPersistenceFlushScheduled;
|
|
129
|
-
private readonly replayHydration;
|
|
130
|
-
private replayRetentionCleanup;
|
|
131
|
-
private replayRetentionCutoff;
|
|
132
|
-
private replayRetentionTimer;
|
|
164
|
+
private readonly replayManager;
|
|
133
165
|
private readonly initialConfig;
|
|
134
166
|
private readonly hasInitialConfig;
|
|
135
167
|
private readonly trace;
|
|
136
|
-
private readonly
|
|
137
|
-
private readonly dedupTtlMs;
|
|
138
|
-
private readonly dedupAdaptiveBounds;
|
|
139
|
-
private dedupWindowStartedAt;
|
|
140
|
-
private dedupWindowAccepted;
|
|
141
|
-
private readonly dedupAdaptiveWindowMs;
|
|
142
|
-
private readonly dedupSweepMs;
|
|
143
|
-
private dedupSweepTimer;
|
|
144
|
-
private readonly dedupEnabled;
|
|
168
|
+
private readonly dedupManager;
|
|
145
169
|
private readonly now;
|
|
146
|
-
private readonly seenMessageIds;
|
|
147
|
-
private dedupSuppressed;
|
|
148
|
-
private dedupAccepted;
|
|
149
170
|
private activeConfig;
|
|
150
171
|
private status;
|
|
151
172
|
private started;
|
|
@@ -153,7 +174,17 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
153
174
|
private transportReady;
|
|
154
175
|
private lastError;
|
|
155
176
|
private lastErrorAt;
|
|
177
|
+
private lastFailure;
|
|
178
|
+
private persistenceFailureCount;
|
|
179
|
+
private persistenceLastFailureAt;
|
|
180
|
+
private persistenceLastErrorMessage;
|
|
156
181
|
private startPromise;
|
|
182
|
+
private stopPromise;
|
|
183
|
+
private queuedStart;
|
|
184
|
+
private queuedStartReady;
|
|
185
|
+
private queuedStartReadyToken;
|
|
186
|
+
private queuedStartToken;
|
|
187
|
+
private canceledQueuedStartToken;
|
|
157
188
|
private lastRecoveryAt;
|
|
158
189
|
private recoveryAttempt;
|
|
159
190
|
private recoveryExhausted;
|
|
@@ -166,6 +197,7 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
166
197
|
private lastSuccessAt;
|
|
167
198
|
private suspended;
|
|
168
199
|
private pendingStop;
|
|
200
|
+
private lifecycleEpoch;
|
|
169
201
|
private readonly recoveryCooldownMs;
|
|
170
202
|
private readonly recoveryMaxAttempts;
|
|
171
203
|
constructor(options: CrossTabDataBusOptions<TConfig, TData>);
|
|
@@ -173,11 +205,20 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
173
205
|
* Start the DataBus with the given transport config.
|
|
174
206
|
*
|
|
175
207
|
* The first call starts the cluster and opens the transport. Concurrent calls
|
|
176
|
-
* during an in-flight
|
|
177
|
-
*
|
|
178
|
-
*
|
|
208
|
+
* during an in-flight open return the same promise. A call received while an
|
|
209
|
+
* explicit stop() is settling queues one fresh start after cleanup; a later
|
|
210
|
+
* stop() before that queued start runs cancels it, so the latest lifecycle
|
|
211
|
+
* intent wins. Once an operation settles (success or failure) its promise
|
|
212
|
+
* gate is cleared so a subsequent start() or resumeTransport() can open a
|
|
213
|
+
* fresh lifecycle.
|
|
179
214
|
*/
|
|
180
215
|
start(config: TConfig): Promise<void>;
|
|
216
|
+
/** Return a cancellation-aware readiness view of the current queued start. */
|
|
217
|
+
private getQueuedStartReady;
|
|
218
|
+
/** Queue exactly one fresh start after an in-flight explicit stop settles. */
|
|
219
|
+
private queueStartAfterStop;
|
|
220
|
+
/** Reset failure and recovery diagnostics for a new explicit start session. */
|
|
221
|
+
private resetFailureState;
|
|
181
222
|
/**
|
|
182
223
|
* Open the transport, chained after `before` to ensure lifecycle ordering.
|
|
183
224
|
* When `stopClusterOnFailure` is true (initial start), a transport failure
|
|
@@ -187,13 +228,17 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
187
228
|
/**
|
|
188
229
|
* Await the DataBus to be fully started (lazy init when using initialConfig).
|
|
189
230
|
* Returns a rejected promise when the transport has failed and no start is in
|
|
190
|
-
* flight — the caller can retry by calling start() or ready() again.
|
|
231
|
+
* flight — the caller can retry by calling start() or ready() again. While an
|
|
232
|
+
* explicit stop() is settling, this rejects unless a restart is queued behind
|
|
233
|
+
* it; false readiness during teardown is never reported.
|
|
191
234
|
*/
|
|
192
235
|
ready(): Promise<void>;
|
|
193
236
|
/**
|
|
194
237
|
* Register a handler for `topic`. The handler fires on every publication
|
|
195
238
|
* delivered to this tab, regardless of which tab published it. Returns an
|
|
196
|
-
* unsubscribe function for convenience.
|
|
239
|
+
* unsubscribe function for convenience. During an explicit stop() the
|
|
240
|
+
* registration is rejected through onError and a no-op cleanup is returned,
|
|
241
|
+
* so a late subscriber cannot leak into a future restart.
|
|
197
242
|
*/
|
|
198
243
|
subscribe(topic: string, handler: DataBusMessageHandler<TData>, options?: {
|
|
199
244
|
replay?: boolean | number;
|
|
@@ -247,55 +292,60 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
247
292
|
generation: number;
|
|
248
293
|
lastSuccessAt: number | null;
|
|
249
294
|
};
|
|
295
|
+
/** Bounded failure counters for the replay persistence backend. */
|
|
296
|
+
getPersistenceStats(): DataBusPersistenceHealth;
|
|
297
|
+
/** Compact health verdict for dashboards, readiness probes, and support
|
|
298
|
+
* bundles. Answers "is the bus usable right now" first, then attaches the
|
|
299
|
+
* unified failure ledger and recovery context that explains the verdict. */
|
|
300
|
+
getHealthSummary(): DataBusHealthSummary;
|
|
250
301
|
/** Snapshot of the cluster state (workers, routes, assignments).
|
|
251
302
|
* For diagnostics only — the returned object is a shallow copy but
|
|
252
303
|
* nested arrays are snapshots at call time. */
|
|
253
304
|
getClusterSnapshot(): WorkerClusterSnapshot;
|
|
254
305
|
/** Return a single health snapshot combining lifecycle, recovery, dedup, replay, and cluster state. */
|
|
255
306
|
getDiagnostics(): DataBusDiagnostics;
|
|
307
|
+
/** Synchronous snapshot of the current trace metrics window (throughput,
|
|
308
|
+
* dispatch latency, dedup outcomes), without flushing or resetting it.
|
|
309
|
+
* Returns null when trace metrics are inactive (disabled or events-only). */
|
|
310
|
+
getMetrics(): DataBusMetricsSnapshot | null;
|
|
256
311
|
/**
|
|
257
312
|
* Gracefully stop the DataBus: unsubscribe all topics, stop the cluster,
|
|
258
|
-
* and close the transport.
|
|
313
|
+
* and close the transport. Concurrent and repeated calls share the in-flight
|
|
314
|
+
* stop promise. A start() received while stopping runs after this completes,
|
|
315
|
+
* unless another stop() arrives first and cancels that queued restart.
|
|
259
316
|
*/
|
|
260
317
|
stop(): Promise<void>;
|
|
318
|
+
private performStop;
|
|
261
319
|
/**
|
|
262
320
|
* Incoming message from the transport.
|
|
263
321
|
* Records metrics, checks ownership via the cluster, broadcasts to other tabs,
|
|
264
322
|
* and dispatches locally.
|
|
265
323
|
*/
|
|
266
324
|
private handleTransportMessage;
|
|
267
|
-
|
|
325
|
+
/** Start enqueuing the dedup expiry sweep (delegated to {@link DedupManager}). */
|
|
268
326
|
private startDedupSweep;
|
|
327
|
+
/** Stop enqueuing the dedup expiry sweep. */
|
|
269
328
|
private stopDedupSweep;
|
|
270
|
-
private pruneExpiredDedup;
|
|
271
|
-
private currentDedupTtl;
|
|
272
329
|
/** Deliver a message to every local handler registered for its topic,
|
|
273
330
|
* plus every handler registered with a wildcard subscription that matches
|
|
274
331
|
* (e.g. a handler subscribed to "chat.*" receives "chat.room.1"). */
|
|
275
332
|
private dispatch;
|
|
276
|
-
/** Append a dispatched publication to the topic's replay ring buffer.
|
|
277
|
-
* No-op when replay is disabled. */
|
|
278
|
-
private recordReplay;
|
|
279
|
-
private scheduleReplayPersistenceFlush;
|
|
280
|
-
private hydrateReplay;
|
|
281
|
-
private scheduleReplayRetentionCleanup;
|
|
282
|
-
private startReplayRetentionSweep;
|
|
283
|
-
private stopReplayRetentionSweep;
|
|
284
|
-
private withPersistenceRetry;
|
|
285
|
-
/** Deliver buffered history to a newly-registered handler. For an exact
|
|
286
|
-
* topic this is that topic's ring; for a wildcard subscription every
|
|
287
|
-
* buffered topic matching the pattern contributes (in buffer insertion
|
|
288
|
-
* order). Replay deliveries are marked `replayed: true` and are not
|
|
289
|
-
* counted into trace metrics. */
|
|
290
|
-
private deliverReplay;
|
|
291
333
|
/**
|
|
292
334
|
* Propagate a status change to the cluster, trace, and all registered
|
|
293
335
|
* status handlers. On reconnect, re-subscribe any topics assigned to us.
|
|
294
336
|
*/
|
|
295
337
|
private updateStatus;
|
|
296
338
|
private reportError;
|
|
339
|
+
/** Report a persistence failure to the trace and the unified failure ledger,
|
|
340
|
+
* unless it is a {@link PersistenceRetryCancelledError} cancellation from a
|
|
341
|
+
* lifecycle transition (teardown should stay quiet). */
|
|
297
342
|
private reportPersistenceError;
|
|
298
343
|
private traceSubscription;
|
|
344
|
+
/** Emit the coordination trace snapshot from the current cluster state.
|
|
345
|
+
* Called after a transport opens (start and recovery), when the role and
|
|
346
|
+
* route picture has settled — the synchronous pre-open snapshot would see no
|
|
347
|
+
* routes because their writes are still coalesced in the batch writer. */
|
|
348
|
+
private emitCoordinationTrace;
|
|
299
349
|
/** Ask the transport to subscribe to a topic (idempotent). */
|
|
300
350
|
private subscribeTransport;
|
|
301
351
|
private unsubscribeTransport;
|
|
@@ -333,6 +383,13 @@ export declare class CrossTabDataBus<TConfig = unknown, TData = unknown> {
|
|
|
333
383
|
* during startup are not lost.
|
|
334
384
|
*/
|
|
335
385
|
private runTransport;
|
|
386
|
+
/**
|
|
387
|
+
* Publications started after teardown begins cannot reach any transport.
|
|
388
|
+
* Surface that as a normal asynchronous API failure instead of letting
|
|
389
|
+
* runTransport() return silently. Empty publishBatch() calls remain a no-op
|
|
390
|
+
* and are filtered by the caller before this check.
|
|
391
|
+
*/
|
|
392
|
+
private rejectPublishDuringStop;
|
|
336
393
|
/**
|
|
337
394
|
* Ensure the DataBus is started, throwing if no initialConfig was provided.
|
|
338
395
|
* Called automatically by subscribe/publish/ready when autoStart is true.
|