cross-tab-worker-databus 0.20.71 → 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.
Files changed (95) hide show
  1. package/CHANGELOG.md +204 -2
  2. package/README.md +8 -0
  3. package/README.zh.md +4 -0
  4. package/dist/centrifuge-protocol.d.ts +59 -26
  5. package/dist/centrifuge-protocol.d.ts.map +1 -1
  6. package/dist/centrifuge-session.d.ts +9 -0
  7. package/dist/centrifuge-session.d.ts.map +1 -1
  8. package/dist/centrifuge.d.ts +12 -2
  9. package/dist/centrifuge.d.ts.map +1 -1
  10. package/dist/centrifuge.js +168 -88
  11. package/dist/centrifuge.js.map +3 -3
  12. package/dist/centrifuge.shared.worker.js +146 -38
  13. package/dist/centrifuge.shared.worker.js.map +3 -3
  14. package/dist/centrifuge.worker.js +140 -33
  15. package/dist/centrifuge.worker.js.map +3 -3
  16. package/dist/{chunk-D2SIT473.js → chunk-PW63EWIK.js} +1090 -494
  17. package/dist/chunk-PW63EWIK.js.map +7 -0
  18. package/dist/chunk-TZ7ZP7YD.js +175 -0
  19. package/dist/chunk-TZ7ZP7YD.js.map +7 -0
  20. package/dist/cjs/centrifuge.cjs +1403 -637
  21. package/dist/cjs/centrifuge.cjs.map +4 -4
  22. package/dist/cjs/hooks.cjs +37 -2
  23. package/dist/cjs/hooks.cjs.map +3 -3
  24. package/dist/cjs/index.cjs +1453 -730
  25. package/dist/cjs/index.cjs.map +4 -4
  26. package/dist/cjs/vue.cjs +45 -2
  27. package/dist/cjs/vue.cjs.map +3 -3
  28. package/dist/core/cluster.d.ts +40 -4
  29. package/dist/core/cluster.d.ts.map +1 -1
  30. package/dist/core/data-bus.d.ts +93 -69
  31. package/dist/core/data-bus.d.ts.map +1 -1
  32. package/dist/core/dedup-manager.d.ts +98 -0
  33. package/dist/core/dedup-manager.d.ts.map +1 -0
  34. package/dist/core/environment.d.ts +43 -3
  35. package/dist/core/environment.d.ts.map +1 -1
  36. package/dist/core/replay-manager.d.ts +128 -0
  37. package/dist/core/replay-manager.d.ts.map +1 -0
  38. package/dist/core/replay-persistence.d.ts +2 -1
  39. package/dist/core/replay-persistence.d.ts.map +1 -1
  40. package/dist/core/routing.d.ts +22 -3
  41. package/dist/core/routing.d.ts.map +1 -1
  42. package/dist/core/storage-batch.d.ts +0 -4
  43. package/dist/core/storage-batch.d.ts.map +1 -1
  44. package/dist/core/trace.d.ts +55 -15
  45. package/dist/core/trace.d.ts.map +1 -1
  46. package/dist/core/types.d.ts +58 -9
  47. package/dist/core/types.d.ts.map +1 -1
  48. package/dist/core/version.d.ts +2 -0
  49. package/dist/core/version.d.ts.map +1 -0
  50. package/dist/hooks.d.ts +12 -1
  51. package/dist/hooks.d.ts.map +1 -1
  52. package/dist/hooks.js +28 -2
  53. package/dist/hooks.js.map +2 -2
  54. package/dist/index.d.ts +6 -6
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +228 -162
  57. package/dist/index.js.map +3 -3
  58. package/dist/utils/constants.d.ts +170 -0
  59. package/dist/utils/constants.d.ts.map +1 -0
  60. package/dist/utils/error-utils.d.ts +28 -0
  61. package/dist/utils/error-utils.d.ts.map +1 -0
  62. package/dist/utils/metadata.d.ts +16 -0
  63. package/dist/utils/metadata.d.ts.map +1 -0
  64. package/dist/utils/storage-utils.d.ts +24 -0
  65. package/dist/utils/storage-utils.d.ts.map +1 -0
  66. package/dist/utils/validation.d.ts +76 -0
  67. package/dist/utils/validation.d.ts.map +1 -0
  68. package/dist/vue.d.ts +13 -1
  69. package/dist/vue.d.ts.map +1 -1
  70. package/dist/vue.js +36 -2
  71. package/dist/vue.js.map +2 -2
  72. package/dist/websocket.d.ts +6 -1
  73. package/dist/websocket.d.ts.map +1 -1
  74. package/dist/worker-mode.d.ts +6 -2
  75. package/dist/worker-mode.d.ts.map +1 -1
  76. package/docs/README.md +1 -0
  77. package/docs/api.md +122 -2
  78. package/docs/architecture.md +80 -0
  79. package/docs/benchmarks.md +24 -0
  80. package/docs/capabilities.md +24 -3
  81. package/docs/configuration.md +58 -0
  82. package/docs/getting-started.md +35 -0
  83. package/docs/release-checklist.md +28 -5
  84. package/docs/roadmap.md +74 -5
  85. package/docs/zh/README.md +2 -1
  86. package/docs/zh/api.md +121 -2
  87. package/docs/zh/architecture.md +49 -0
  88. package/docs/zh/benchmarks.md +24 -0
  89. package/docs/zh/capabilities.md +21 -3
  90. package/docs/zh/configuration.md +53 -0
  91. package/docs/zh/getting-started.md +35 -0
  92. package/docs/zh/release-checklist.md +29 -6
  93. package/docs/zh/roadmap.md +86 -5
  94. package/package.json +39 -16
  95. package/dist/chunk-D2SIT473.js.map +0 -7
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/core/replay-persistence.ts", "../src/websocket.ts"],
4
- "sourcesContent": ["import type { DataBusMessage } from './types';\n\n/** Optional persistence backend for replay history. */\nexport interface DataBusReplayPersistence<TData = unknown> {\n load(): Promise<ReadonlyArray<DataBusMessage<TData>>>;\n append(message: DataBusMessage<TData>): Promise<void>;\n /** Optional bulk append used to amortize IndexedDB transaction overhead. */\n appendBatch?(messages: ReadonlyArray<DataBusMessage<TData>>): Promise<void>;\n /** Remove all persisted replay history. */\n clear?(): Promise<void>;\n /** Remove persisted replay history for one exact topic. */\n clearTopic?(topic: string): Promise<void>;\n /** Remove persisted messages older than the given epoch-millisecond cutoff. */\n clearBefore?(timestamp: number): Promise<void>;\n}\n\nexport interface IndexedDbReplayPersistenceOptions {\n dbName?: string;\n maxPerTopic: number;\n pruneStrategy?: 'count' | 'age' | 'both';\n retentionMs?: number;\n}\n\n/** Create a browser IndexedDB-backed replay store. */\nexport function createIndexedDbReplayPersistence<TData = unknown>(\n options: IndexedDbReplayPersistenceOptions\n): DataBusReplayPersistence<TData> {\n const indexedDb = globalThis.indexedDB;\n if (!indexedDb) throw new Error('IndexedDB is unavailable in this environment.');\n const dbName = options.dbName ?? 'cross-tab-worker-databus';\n const storeName = 'replay';\n const maxPerTopic = options.maxPerTopic;\n const pruneStrategy = options.pruneStrategy ?? 'count';\n const retentionMs = options.retentionMs;\n if (!['count', 'age', 'both'].includes(pruneStrategy)) throw new TypeError('pruneStrategy must be count, age, or both.');\n if (retentionMs !== undefined && (!Number.isFinite(retentionMs) || retentionMs <= 0)) throw new TypeError('retentionMs must be a positive finite number.');\n if (!Number.isSafeInteger(maxPerTopic) || maxPerTopic <= 0) {\n throw new TypeError(`maxPerTopic must be a positive safe integer, got ${String(maxPerTopic)}.`);\n }\n let dbPromise: Promise<IDBDatabase> | null = null;\n const invalidate = (db: IDBDatabase): void => {\n if (dbPromise) {\n void dbPromise.then(current => {\n if (current === db) {\n current.close();\n dbPromise = null;\n }\n }, () => undefined);\n }\n };\n // IndexedDB transactions are atomic, but a read-modify-write append can\n // still lose updates when callers start several appends concurrently.\n // Serialize all mutations per adapter instance while keeping reads free.\n let mutationQueue: Promise<void> = Promise.resolve();\n const serializeMutation = (mutation: () => Promise<void>): Promise<void> => {\n const next = mutationQueue.then(mutation, mutation);\n mutationQueue = next.catch(() => undefined);\n return next;\n };\n const open = (): Promise<IDBDatabase> => {\n if (dbPromise) return dbPromise;\n const pending = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDb.open(dbName, 1);\n request.onupgradeneeded = () => request.result.createObjectStore(storeName, { keyPath: 'topic' });\n request.onsuccess = () => {\n const db = request.result;\n // A schema upgrade in another tab invalidates this connection. Close\n // it and clear the cached promise so the next operation reopens a\n // usable connection instead of repeatedly targeting a dead database.\n db.onversionchange = () => {\n db.close();\n if (dbPromise) dbPromise = null;\n };\n resolve(db);\n };\n request.onerror = () => reject(request.error ?? new Error('Failed to open replay database.'));\n });\n dbPromise = pending;\n // Do not permanently cache a rejected open promise. IndexedDB can fail\n // transiently (quota, private-mode initialization, a closing connection,\n // or a browser shutdown); the next operation must be able to retry.\n void pending.catch(() => {\n if (dbPromise === pending) dbPromise = null;\n });\n return pending;\n };\n return {\n async load() {\n const db = await open();\n return new Promise((resolve, reject) => {\n let request: IDBRequest;\n try {\n request = db.transaction(storeName, 'readonly').objectStore(storeName).getAll();\n } catch (error) {\n invalidate(db);\n reject(error);\n return;\n }\n request.onsuccess = () => resolve((request.result as Array<{ messages: DataBusMessage<TData>[] }>).flatMap(record => record.messages));\n request.onerror = () => {\n invalidate(db);\n reject(request.error ?? new Error('Failed to load replay history.'));\n };\n });\n },\n append(message) {\n return serializeMutation(async () => {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n const store = transaction.objectStore(storeName);\n const request = store.get(message.topic);\n request.onsuccess = () => {\n let messages = ((request.result?.messages ?? []) as DataBusMessage<TData>[]).concat(message);\n if (pruneStrategy !== 'count' && retentionMs !== undefined) {\n const cutoff = Date.now() - retentionMs;\n messages = messages.filter(item => item.timestamp === undefined || item.timestamp >= cutoff);\n }\n if (pruneStrategy !== 'age') messages = messages.slice(-maxPerTopic);\n store.put({ topic: message.topic, messages });\n };\n request.onerror = () => { invalidate(db); reject(request.error ?? new Error('Failed to read replay history.')); };\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to persist replay history.')); };\n });\n });\n },\n appendBatch(messages) {\n if (messages.length === 0) return Promise.resolve();\n return serializeMutation(async () => {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n const store = transaction.objectStore(storeName);\n const grouped = new Map<string, DataBusMessage<TData>[]>();\n for (const message of messages) grouped.set(message.topic, [...(grouped.get(message.topic) ?? []), message]);\n for (const [topic, topicMessages] of grouped) {\n const request = store.get(topic);\n request.onsuccess = () => {\n let history = ((request.result?.messages ?? []) as DataBusMessage<TData>[]).concat(topicMessages);\n if (pruneStrategy !== 'count' && retentionMs !== undefined) {\n const cutoff = Date.now() - retentionMs;\n history = history.filter(item => item.timestamp === undefined || item.timestamp >= cutoff);\n }\n if (pruneStrategy !== 'age') history = history.slice(-maxPerTopic);\n store.put({ topic, messages: history });\n };\n request.onerror = () => { invalidate(db); reject(request.error ?? new Error('Failed to read replay history.')); };\n }\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to persist replay history batch.')); };\n });\n });\n },\n clear() {\n return serializeMutation(async () => {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n transaction.objectStore(storeName).clear();\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to clear replay history.')); };\n });\n });\n },\n clearTopic(topic) {\n return serializeMutation(async () => {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n transaction.objectStore(storeName).delete(topic);\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to clear topic replay history.')); };\n });\n });\n },\n clearBefore(timestamp) {\n return serializeMutation(async () => {\n const db = await open();\n await new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n const store = transaction.objectStore(storeName);\n const request = store.getAll();\n request.onsuccess = () => {\n for (const record of request.result as Array<{ topic: string; messages: DataBusMessage<TData>[] }>) {\n const messages = record.messages.filter(message => message.timestamp === undefined || message.timestamp >= timestamp);\n if (messages.length === 0) store.delete(record.topic);\n else if (messages.length !== record.messages.length) store.put({ topic: record.topic, messages });\n }\n };\n request.onerror = () => { invalidate(db); reject(request.error ?? new Error('Failed to read replay history.')); };\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to prune replay history.')); };\n });\n });\n }\n };\n}\n", "/**\n * WebSocketTransport \u2014 a dependency-free transport over a plain WebSocket.\n *\n * Validates the `DataBusTransport` abstraction with a second, minimal backend:\n * any WebSocket server that speaks the tiny JSON protocol below can back the\n * same cross-tab clustering stack (owner dedup, sticky routes, EVENT fan-out)\n * that the Centrifuge backend uses.\n *\n * Wire protocol (JSON text frames):\n * - client \u2192 server: `{\"op\":\"subscribe\"|\"unsubscribe\"|\"publish\",\"topic\":...,\"data\":...}`\n * - server \u2192 client: `{\"topic\":...,\"data\":...}` for publications; anything\n * without a string `topic` field is ignored (forward-compatible).\n */\nimport { CrossTabDataBus } from './core/data-bus';\nimport { parseDataBusPublication } from './core/publication';\nimport type { CrossTabDataBusOptions } from './core/data-bus';\nimport type {\n DataBusTransport,\n DataBusTransportHandlers,\n DataBusPublishOptions,\n DataBusMessage,\n MaybePromise,\n WorkerStatus\n} from './core/types';\n\n/** Minimal WebSocket surface used by the transport. Matches the browser\n * `WebSocket` subset the transport touches; injectable for tests and runtimes. */\nexport interface WebSocketLike {\n /** Current connection state; 1 (OPEN) means frames may be sent. */\n readonly readyState?: number;\n send(data: string | ArrayBuffer): void;\n close(code?: number, reason?: string): void;\n onopen: (() => void) | null;\n onclose: (() => void) | null;\n onerror: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/** Connection configuration for {@link WebSocketTransport}. */\nexport interface WebSocketDataBusConfig {\n /** WebSocket endpoint, e.g. `wss://example.test/ws`. */\n url: string;\n /** Subprotocol(s) passed to the WebSocket handshake. */\n protocols?: string | string[];\n /** Custom socket factory. Defaults to the global `WebSocket`; injectable\n * for tests and non-browser runtimes. */\n webSocketFactory?: (url: string, protocols?: string | string[]) => WebSocketLike;\n}\n\n/** Options for creating a fully-configured CrossTabDataBus with a WebSocket transport. */\nexport interface CreateWebSocketDataBusOptions<TData = unknown>\n extends Omit<\n CrossTabDataBusOptions<WebSocketDataBusConfig, TData>,\n 'autoStart' | 'clusterKey' | 'initialConfig' | 'transport'\n > {\n /** WebSocket connection configuration. */\n connection: WebSocketDataBusConfig;\n /** Cluster key for cross-tab coordination. Defaults to the connection URL. */\n clusterKey?: string;\n}\n\nconst WS_OPEN = 1;\n\n/** Transport that talks a minimal JSON protocol over a plain WebSocket.\n * Connection lifecycle maps directly to the DataBus status vocabulary:\n * open \u2192 `connected`, close \u2192 `disconnected`, error \u2192 `error` (which the\n * DataBus treats as its auto-recovery trigger). The transport holds no\n * reconnection logic of its own \u2014 reopening is the DataBus's job. */\nexport class WebSocketTransport<TData = unknown>\n implements DataBusTransport<WebSocketDataBusConfig, TData>\n{\n readonly diagnosticsName = 'websocket';\n readonly diagnosticsBackend = 'native-websocket';\n private socket: WebSocketLike | null = null;\n private handlers: DataBusTransportHandlers<TData> | null = null;\n private readonly subscribedTopics = new Set<string>();\n\n constructor(private readonly connection: WebSocketDataBusConfig) {}\n\n /** Open the WebSocket and wire lifecycle listeners. A factory failure is\n * reported through `onStatus('error')` so the DataBus can recover. */\n start(config: WebSocketDataBusConfig, handlers: DataBusTransportHandlers<TData>): MaybePromise<void> {\n if (this.socket) return;\n this.handlers = handlers;\n // The factory may live on the constructor connection (createWebSocketDataBus\n // path) or on the runtime config (direct transport use) \u2014 accept both.\n const factory = config.webSocketFactory ?? this.connection.webSocketFactory ?? defaultWebSocketFactory;\n const protocols = config.protocols ?? this.connection.protocols;\n let socket: WebSocketLike;\n try {\n socket = factory(config.url, protocols);\n } catch (error) {\n handlers.onStatus('error');\n handlers.onError(error);\n return;\n }\n socket.onopen = () => {\n if (this.socket !== socket || this.handlers !== handlers) return;\n // Re-assert every topic so a reopened socket (recovery path) restores\n // the server-side subscriptions without DataBus involvement.\n for (const topic of this.subscribedTopics) {\n this.sendFrame({ op: 'subscribe', topic });\n }\n handlers.onStatus('connected');\n };\n socket.onclose = () => {\n if (this.socket === socket && this.handlers === handlers) handlers.onStatus('disconnected');\n };\n socket.onerror = () => {\n if (this.socket === socket && this.handlers === handlers) handlers.onStatus('error');\n };\n socket.onmessage = event => {\n if (this.socket === socket && this.handlers === handlers) void this.handleMessage(event.data);\n };\n this.socket = socket;\n }\n\n /** Idempotent: re-subscribing an active topic re-sends the frame but does\n * not duplicate the local tracking entry. */\n subscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.add(topic);\n this.sendFrame({ op: 'subscribe', topic });\n }\n\n /** Idempotent: unsubscribing an unknown topic is a no-op. */\n unsubscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.delete(topic);\n this.sendFrame({ op: 'unsubscribe', topic });\n }\n\n /** Publish `data` to `topic` as a JSON frame. Requires an open socket. */\n publish(topic: string, data: unknown, options?: DataBusPublishOptions): MaybePromise<void> {\n if (data instanceof ArrayBuffer) {\n this.sendBinaryFrame(topic, data, options?.messageId, options?.timestamp);\n return;\n }\n this.sendFrame({\n op: 'publish',\n topic,\n data,\n ...(options?.messageId === undefined ? {} : { messageId: options.messageId }),\n ...(options?.timestamp === undefined ? {} : { timestamp: options.timestamp })\n });\n }\n\n /** Close the socket and drop all state. Safe to call multiple times. */\n stop(): MaybePromise<void> {\n const socket = this.socket;\n this.socket = null;\n this.handlers = null;\n this.subscribedTopics.clear();\n socket?.close();\n }\n\n /** Send one JSON frame. Frames are dropped with an `onError` report when\n * the socket is not open \u2014 subscribe frames are re-sent on open, so the\n * only real loss is a publish during a disconnect window. */\n private sendFrame(payload: { op: string; topic: string; data?: unknown; messageId?: string; timestamp?: number }): void {\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error(`WebSocket is not open; dropped \"${payload.op}\" frame.`));\n return;\n }\n this.socket.send(JSON.stringify(payload));\n }\n\n private sendBinaryFrame(topic: string, data: ArrayBuffer, messageId?: string, timestamp?: number): void {\n if (messageId !== undefined || timestamp !== undefined) {\n // Binary frames retain their compact legacy shape; metadata is sent as a\n // JSON envelope so IDs are never silently lost.\n this.sendFrame({\n op: 'publish',\n topic,\n data: Array.from(new Uint8Array(data)),\n ...(messageId === undefined ? {} : { messageId }),\n ...(timestamp === undefined ? {} : { timestamp })\n });\n return;\n }\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error('WebSocket is not open; dropped \"publish\" frame.'));\n return;\n }\n const topicBytes = new TextEncoder().encode(topic);\n if (topicBytes.length > 0xffff) {\n this.handlers?.onError(new Error('WebSocket topic is too long for a binary frame.'));\n return;\n }\n const frame = new Uint8Array(3 + topicBytes.length + data.byteLength);\n frame[0] = 0xc7;\n new DataView(frame.buffer).setUint16(1, topicBytes.length);\n frame.set(topicBytes, 3);\n frame.set(new Uint8Array(data), 3 + topicBytes.length);\n this.socket.send(frame.buffer);\n }\n\n /** Parse a server frame. Only objects carrying a string `topic` are\n * publications; malformed JSON and unknown shapes are ignored so a chatty\n * server cannot crash the message path. */\n private async handleMessage(raw: unknown): Promise<void> {\n // Browser WebSockets may deliver binary frames as Blob unless\n // `binaryType = 'arraybuffer'` is explicitly configured by the host.\n // Normalize Blob asynchronously and reuse the exact ArrayBuffer parser.\n if (typeof Blob !== 'undefined' && raw instanceof Blob) {\n try {\n await this.handleMessage(await raw.arrayBuffer());\n } catch (error) {\n this.handlers?.onError(error);\n }\n return;\n }\n let parsed: unknown;\n if (raw instanceof ArrayBuffer) {\n const bytes = new Uint8Array(raw);\n if (bytes[0] !== 0xc7 || bytes.length < 3) return;\n const topicLength = new DataView(raw).getUint16(1);\n if (bytes.length < 3 + topicLength) return;\n const topic = new TextDecoder().decode(bytes.subarray(3, 3 + topicLength));\n const data = bytes.slice(3 + topicLength).buffer;\n this.handlers?.onMessage({ topic, data: data as TData });\n return;\n }\n if (typeof raw !== 'string') return;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.handlers?.onError(new Error('WebSocket server sent a non-JSON frame.'));\n return;\n }\n if (!parsed || typeof parsed !== 'object') return;\n const publication = parseDataBusPublication<TData>(parsed);\n if (publication) this.handlers?.onMessage(publication as DataBusMessage<TData>);\n }\n}\n\n/** Resolve the platform WebSocket, or null in runtimes without one (SSR/Node). */\nfunction defaultWebSocketFactory(url: string, protocols?: string | string[]): WebSocketLike {\n if (typeof WebSocket === 'undefined') {\n throw new Error('WebSocketTransport requires a WebSocket implementation.');\n }\n return new WebSocket(url, protocols) as unknown as WebSocketLike;\n}\n\n/** Create a CrossTabDataBus backed by a plain WebSocket transport.\n * Cross-tab clustering (owner dedup, sticky routes, failover) works identically\n * to the Centrifuge backend \u2014 only the transport I/O differs. */\nexport function createWebSocketDataBus<TData = unknown>(\n options: CreateWebSocketDataBusOptions<TData>\n): CrossTabDataBus<WebSocketDataBusConfig, TData> {\n const { clusterKey, connection, ...dataBusOptions } = options;\n return new CrossTabDataBus({\n ...dataBusOptions,\n autoStart: true,\n clusterKey: clusterKey ?? connection.url,\n initialConfig: connection,\n transport: new WebSocketTransport<TData>(connection)\n });\n}\n\n/** Re-export for convenience: the status type used by the transport. */\nexport type { WorkerStatus };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAwBO,SAAS,iCACd,SACiC;AACjC,QAAM,YAAY,WAAW;AAC7B,MAAI,CAAC,UAAW,OAAM,IAAI,MAAM,+CAA+C;AAC/E,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,YAAY;AAClB,QAAM,cAAc,QAAQ;AAC5B,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,cAAc,QAAQ;AAC5B,MAAI,CAAC,CAAC,SAAS,OAAO,MAAM,EAAE,SAAS,aAAa,EAAG,OAAM,IAAI,UAAU,4CAA4C;AACvH,MAAI,gBAAgB,WAAc,CAAC,OAAO,SAAS,WAAW,KAAK,eAAe,GAAI,OAAM,IAAI,UAAU,+CAA+C;AACzJ,MAAI,CAAC,OAAO,cAAc,WAAW,KAAK,eAAe,GAAG;AAC1D,UAAM,IAAI,UAAU,oDAAoD,OAAO,WAAW,CAAC,GAAG;AAAA,EAChG;AACA,MAAI,YAAyC;AAC7C,QAAM,aAAa,CAAC,OAA0B;AAC5C,QAAI,WAAW;AACb,WAAK,UAAU,KAAK,aAAW;AAC7B,YAAI,YAAY,IAAI;AAClB,kBAAQ,MAAM;AACd,sBAAY;AAAA,QACd;AAAA,MACF,GAAG,MAAM,MAAS;AAAA,IACpB;AAAA,EACF;AAIA,MAAI,gBAA+B,QAAQ,QAAQ;AACnD,QAAM,oBAAoB,CAAC,aAAiD;AAC1E,UAAM,OAAO,cAAc,KAAK,UAAU,QAAQ;AAClD,oBAAgB,KAAK,MAAM,MAAM,MAAS;AAC1C,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAA4B;AACvC,QAAI,UAAW,QAAO;AACtB,UAAM,UAAU,IAAI,QAAqB,CAAC,SAAS,WAAW;AAC5D,YAAM,UAAU,UAAU,KAAK,QAAQ,CAAC;AACxC,cAAQ,kBAAkB,MAAM,QAAQ,OAAO,kBAAkB,WAAW,EAAE,SAAS,QAAQ,CAAC;AAChG,cAAQ,YAAY,MAAM;AACxB,cAAM,KAAK,QAAQ;AAInB,WAAG,kBAAkB,MAAM;AACzB,aAAG,MAAM;AACT,cAAI,UAAW,aAAY;AAAA,QAC7B;AACA,gBAAQ,EAAE;AAAA,MACZ;AACA,cAAQ,UAAU,MAAM,OAAO,QAAQ,SAAS,IAAI,MAAM,iCAAiC,CAAC;AAAA,IAC9F,CAAC;AACD,gBAAY;AAIZ,SAAK,QAAQ,MAAM,MAAM;AACvB,UAAI,cAAc,QAAS,aAAY;AAAA,IACzC,CAAC;AACD,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,MAAM,OAAO;AACX,YAAM,KAAK,MAAM,KAAK;AACtB,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAI;AACJ,YAAI;AACF,oBAAU,GAAG,YAAY,WAAW,UAAU,EAAE,YAAY,SAAS,EAAE,OAAO;AAAA,QAChF,SAAS,OAAO;AACd,qBAAW,EAAE;AACb,iBAAO,KAAK;AACZ;AAAA,QACF;AACA,gBAAQ,YAAY,MAAM,QAAS,QAAQ,OAAwD,QAAQ,YAAU,OAAO,QAAQ,CAAC;AACrI,gBAAQ,UAAU,MAAM;AACtB,qBAAW,EAAE;AACb,iBAAO,QAAQ,SAAS,IAAI,MAAM,gCAAgC,CAAC;AAAA,QACrE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,OAAO,SAAS;AACd,aAAO,kBAAkB,YAAY;AACnC,cAAM,KAAK,MAAM,KAAK;AACtB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC7C,cAAI;AACJ,cAAI;AAAE,0BAAc,GAAG,YAAY,WAAW,WAAW;AAAA,UAAG,SACrD,OAAO;AAAE,uBAAW,EAAE;AAAG,mBAAO,KAAK;AAAG;AAAA,UAAQ;AACvD,gBAAM,QAAQ,YAAY,YAAY,SAAS;AAC/C,gBAAM,UAAU,MAAM,IAAI,QAAQ,KAAK;AACvC,kBAAQ,YAAY,MAAM;AACxB,gBAAI,YAAa,QAAQ,QAAQ,YAAY,CAAC,GAA+B,OAAO,OAAO;AAC3F,gBAAI,kBAAkB,WAAW,gBAAgB,QAAW;AAC1D,oBAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,yBAAW,SAAS,OAAO,UAAQ,KAAK,cAAc,UAAa,KAAK,aAAa,MAAM;AAAA,YAC7F;AACA,gBAAI,kBAAkB,MAAO,YAAW,SAAS,MAAM,CAAC,WAAW;AACnE,kBAAM,IAAI,EAAE,OAAO,QAAQ,OAAO,SAAS,CAAC;AAAA,UAC9C;AACA,kBAAQ,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,QAAQ,SAAS,IAAI,MAAM,gCAAgC,CAAC;AAAA,UAAG;AAChH,sBAAY,aAAa,MAAM,QAAQ;AACvC,sBAAY,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,YAAY,SAAS,IAAI,MAAM,mCAAmC,CAAC;AAAA,UAAG;AAAA,QAC3H,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IACA,YAAY,UAAU;AACpB,UAAI,SAAS,WAAW,EAAG,QAAO,QAAQ,QAAQ;AAClD,aAAO,kBAAkB,YAAY;AACnC,cAAM,KAAK,MAAM,KAAK;AACtB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,cAAI;AACJ,cAAI;AAAE,0BAAc,GAAG,YAAY,WAAW,WAAW;AAAA,UAAG,SACrD,OAAO;AAAE,uBAAW,EAAE;AAAG,mBAAO,KAAK;AAAG;AAAA,UAAQ;AACvD,gBAAM,QAAQ,YAAY,YAAY,SAAS;AAC/C,gBAAM,UAAU,oBAAI,IAAqC;AACzD,qBAAW,WAAW,SAAU,SAAQ,IAAI,QAAQ,OAAO,CAAC,GAAI,QAAQ,IAAI,QAAQ,KAAK,KAAK,CAAC,GAAI,OAAO,CAAC;AAC3G,qBAAW,CAAC,OAAO,aAAa,KAAK,SAAS;AAC5C,kBAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,oBAAQ,YAAY,MAAM;AACxB,kBAAI,WAAY,QAAQ,QAAQ,YAAY,CAAC,GAA+B,OAAO,aAAa;AAChG,kBAAI,kBAAkB,WAAW,gBAAgB,QAAW;AAC1D,sBAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,0BAAU,QAAQ,OAAO,UAAQ,KAAK,cAAc,UAAa,KAAK,aAAa,MAAM;AAAA,cAC3F;AACA,kBAAI,kBAAkB,MAAO,WAAU,QAAQ,MAAM,CAAC,WAAW;AACjE,oBAAM,IAAI,EAAE,OAAO,UAAU,QAAQ,CAAC;AAAA,YACxC;AACA,oBAAQ,UAAU,MAAM;AAAE,yBAAW,EAAE;AAAG,qBAAO,QAAQ,SAAS,IAAI,MAAM,gCAAgC,CAAC;AAAA,YAAG;AAAA,UAClH;AACA,sBAAY,aAAa,MAAM,QAAQ;AACvC,sBAAY,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,YAAY,SAAS,IAAI,MAAM,yCAAyC,CAAC;AAAA,UAAG;AAAA,QACnI,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IACA,QAAQ;AACN,aAAO,kBAAkB,YAAY;AACnC,cAAM,KAAK,MAAM,KAAK;AACtB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC7C,cAAI;AACJ,cAAI;AAAE,0BAAc,GAAG,YAAY,WAAW,WAAW;AAAA,UAAG,SACrD,OAAO;AAAE,uBAAW,EAAE;AAAG,mBAAO,KAAK;AAAG;AAAA,UAAQ;AACvD,sBAAY,YAAY,SAAS,EAAE,MAAM;AACzC,sBAAY,aAAa,MAAM,QAAQ;AACvC,sBAAY,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,YAAY,SAAS,IAAI,MAAM,iCAAiC,CAAC;AAAA,UAAG;AAAA,QACzH,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IACA,WAAW,OAAO;AAChB,aAAO,kBAAkB,YAAY;AACnC,cAAM,KAAK,MAAM,KAAK;AACtB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC7C,cAAI;AACJ,cAAI;AAAE,0BAAc,GAAG,YAAY,WAAW,WAAW;AAAA,UAAG,SACrD,OAAO;AAAE,uBAAW,EAAE;AAAG,mBAAO,KAAK;AAAG;AAAA,UAAQ;AACvD,sBAAY,YAAY,SAAS,EAAE,OAAO,KAAK;AAC/C,sBAAY,aAAa,MAAM,QAAQ;AACvC,sBAAY,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,YAAY,SAAS,IAAI,MAAM,uCAAuC,CAAC;AAAA,UAAG;AAAA,QAC/H,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,IACA,YAAY,WAAW;AACrB,aAAO,kBAAkB,YAAY;AACnC,cAAM,KAAK,MAAM,KAAK;AACtB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC7C,cAAI;AACJ,cAAI;AAAE,0BAAc,GAAG,YAAY,WAAW,WAAW;AAAA,UAAG,SACrD,OAAO;AAAE,uBAAW,EAAE;AAAG,mBAAO,KAAK;AAAG;AAAA,UAAQ;AACvD,gBAAM,QAAQ,YAAY,YAAY,SAAS;AAC/C,gBAAM,UAAU,MAAM,OAAO;AAC7B,kBAAQ,YAAY,MAAM;AACxB,uBAAW,UAAU,QAAQ,QAAuE;AAClG,oBAAM,WAAW,OAAO,SAAS,OAAO,aAAW,QAAQ,cAAc,UAAa,QAAQ,aAAa,SAAS;AACpH,kBAAI,SAAS,WAAW,EAAG,OAAM,OAAO,OAAO,KAAK;AAAA,uBAC3C,SAAS,WAAW,OAAO,SAAS,OAAQ,OAAM,IAAI,EAAE,OAAO,OAAO,OAAO,SAAS,CAAC;AAAA,YAClG;AAAA,UACF;AACA,kBAAQ,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,QAAQ,SAAS,IAAI,MAAM,gCAAgC,CAAC;AAAA,UAAG;AAChH,sBAAY,aAAa,MAAM,QAAQ;AACvC,sBAAY,UAAU,MAAM;AAAE,uBAAW,EAAE;AAAG,mBAAO,YAAY,SAAS,IAAI,MAAM,iCAAiC,CAAC;AAAA,UAAG;AAAA,QACzH,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AClJA,IAAM,UAAU;AAOT,IAAM,qBAAN,MAEP;AAAA,EAOE,YAA6B,YAAoC;AAApC;AAAA,EAAqC;AAAA,EANzD,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACtB,SAA+B;AAAA,EAC/B,WAAmD;AAAA,EAC1C,mBAAmB,oBAAI,IAAY;AAAA;AAAA;AAAA,EAMpD,MAAM,QAAgC,UAA+D;AACnG,QAAI,KAAK,OAAQ;AACjB,SAAK,WAAW;AAGhB,UAAM,UAAU,OAAO,oBAAoB,KAAK,WAAW,oBAAoB;AAC/E,UAAM,YAAY,OAAO,aAAa,KAAK,WAAW;AACtD,QAAI;AACJ,QAAI;AACF,eAAS,QAAQ,OAAO,KAAK,SAAS;AAAA,IACxC,SAAS,OAAO;AACd,eAAS,SAAS,OAAO;AACzB,eAAS,QAAQ,KAAK;AACtB;AAAA,IACF;AACA,WAAO,SAAS,MAAM;AACpB,UAAI,KAAK,WAAW,UAAU,KAAK,aAAa,SAAU;AAG1D,iBAAW,SAAS,KAAK,kBAAkB;AACzC,aAAK,UAAU,EAAE,IAAI,aAAa,MAAM,CAAC;AAAA,MAC3C;AACA,eAAS,SAAS,WAAW;AAAA,IAC/B;AACA,WAAO,UAAU,MAAM;AACrB,UAAI,KAAK,WAAW,UAAU,KAAK,aAAa,SAAU,UAAS,SAAS,cAAc;AAAA,IAC5F;AACA,WAAO,UAAU,MAAM;AACrB,UAAI,KAAK,WAAW,UAAU,KAAK,aAAa,SAAU,UAAS,SAAS,OAAO;AAAA,IACrF;AACA,WAAO,YAAY,WAAS;AAC1B,UAAI,KAAK,WAAW,UAAU,KAAK,aAAa,SAAU,MAAK,KAAK,cAAc,MAAM,IAAI;AAAA,IAC9F;AACA,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA,EAIA,UAAU,OAAmC;AAC3C,SAAK,iBAAiB,IAAI,KAAK;AAC/B,SAAK,UAAU,EAAE,IAAI,aAAa,MAAM,CAAC;AAAA,EAC3C;AAAA;AAAA,EAGA,YAAY,OAAmC;AAC7C,SAAK,iBAAiB,OAAO,KAAK;AAClC,SAAK,UAAU,EAAE,IAAI,eAAe,MAAM,CAAC;AAAA,EAC7C;AAAA;AAAA,EAGA,QAAQ,OAAe,MAAe,SAAqD;AACzF,QAAI,gBAAgB,aAAa;AAC/B,WAAK,gBAAgB,OAAO,MAAM,SAAS,WAAW,SAAS,SAAS;AACxE;AAAA,IACF;AACA,SAAK,UAAU;AAAA,MACb,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA,GAAI,SAAS,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;AAAA,MAC3E,GAAI,SAAS,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;AAAA,IAC7E,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,OAA2B;AACzB,UAAM,SAAS,KAAK;AACpB,SAAK,SAAS;AACd,SAAK,WAAW;AAChB,SAAK,iBAAiB,MAAM;AAC5B,YAAQ,MAAM;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAU,SAAsG;AACtH,QAAI,KAAK,QAAQ,eAAe,SAAS;AACvC,WAAK,UAAU,QAAQ,IAAI,MAAM,mCAAmC,QAAQ,EAAE,UAAU,CAAC;AACzF;AAAA,IACF;AACA,SAAK,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC;AAAA,EAC1C;AAAA,EAEQ,gBAAgB,OAAe,MAAmB,WAAoB,WAA0B;AACtG,QAAI,cAAc,UAAa,cAAc,QAAW;AAGtD,WAAK,UAAU;AAAA,QACb,IAAI;AAAA,QACJ;AAAA,QACA,MAAM,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC;AAAA,QACrC,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AAAA,QAC/C,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,UAAU;AAAA,MACjD,CAAC;AACD;AAAA,IACF;AACA,QAAI,KAAK,QAAQ,eAAe,SAAS;AACvC,WAAK,UAAU,QAAQ,IAAI,MAAM,iDAAiD,CAAC;AACnF;AAAA,IACF;AACA,UAAM,aAAa,IAAI,YAAY,EAAE,OAAO,KAAK;AACjD,QAAI,WAAW,SAAS,OAAQ;AAC9B,WAAK,UAAU,QAAQ,IAAI,MAAM,iDAAiD,CAAC;AACnF;AAAA,IACF;AACA,UAAM,QAAQ,IAAI,WAAW,IAAI,WAAW,SAAS,KAAK,UAAU;AACpE,UAAM,CAAC,IAAI;AACX,QAAI,SAAS,MAAM,MAAM,EAAE,UAAU,GAAG,WAAW,MAAM;AACzD,UAAM,IAAI,YAAY,CAAC;AACvB,UAAM,IAAI,IAAI,WAAW,IAAI,GAAG,IAAI,WAAW,MAAM;AACrD,SAAK,OAAO,KAAK,MAAM,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,KAA6B;AAIvD,QAAI,OAAO,SAAS,eAAe,eAAe,MAAM;AACtD,UAAI;AACF,cAAM,KAAK,cAAc,MAAM,IAAI,YAAY,CAAC;AAAA,MAClD,SAAS,OAAO;AACd,aAAK,UAAU,QAAQ,KAAK;AAAA,MAC9B;AACA;AAAA,IACF;AACA,QAAI;AACJ,QAAI,eAAe,aAAa;AAC9B,YAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,UAAI,MAAM,CAAC,MAAM,OAAQ,MAAM,SAAS,EAAG;AAC3C,YAAM,cAAc,IAAI,SAAS,GAAG,EAAE,UAAU,CAAC;AACjD,UAAI,MAAM,SAAS,IAAI,YAAa;AACpC,YAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC;AACzE,YAAM,OAAO,MAAM,MAAM,IAAI,WAAW,EAAE;AAC1C,WAAK,UAAU,UAAU,EAAE,OAAO,KAAoB,CAAC;AACvD;AAAA,IACF;AACA,QAAI,OAAO,QAAQ,SAAU;AAC7B,QAAI;AACF,eAAS,KAAK,MAAM,GAAG;AAAA,IACzB,QAAQ;AACN,WAAK,UAAU,QAAQ,IAAI,MAAM,yCAAyC,CAAC;AAC3E;AAAA,IACF;AACA,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAC3C,UAAM,cAAc,wBAA+B,MAAM;AACzD,QAAI,YAAa,MAAK,UAAU,UAAU,WAAoC;AAAA,EAChF;AACF;AAGA,SAAS,wBAAwB,KAAa,WAA8C;AAC1F,MAAI,OAAO,cAAc,aAAa;AACpC,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO,IAAI,UAAU,KAAK,SAAS;AACrC;AAKO,SAAS,uBACd,SACgD;AAChD,QAAM,EAAE,YAAY,YAAY,GAAG,eAAe,IAAI;AACtD,SAAO,IAAI,gBAAgB;AAAA,IACzB,GAAG;AAAA,IACH,WAAW;AAAA,IACX,YAAY,cAAc,WAAW;AAAA,IACrC,eAAe;AAAA,IACf,WAAW,IAAI,mBAA0B,UAAU;AAAA,EACrD,CAAC;AACH;",
6
- "names": []
4
+ "sourcesContent": ["import type { DataBusMessage } from './types';\nimport { DEFAULT_STORAGE_PREFIX, PRUNE_STRATEGY } from '../utils/constants';\nimport { assertPositiveFiniteNumber, assertPositiveSafeInteger, assertPruneStrategy } from '../utils/validation';\n\n/** Optional persistence backend for replay history. */\nexport interface DataBusReplayPersistence<TData = unknown> {\n load(): Promise<ReadonlyArray<DataBusMessage<TData>>>;\n append(message: DataBusMessage<TData>): Promise<void>;\n /** Optional bulk append used to amortize IndexedDB transaction overhead. */\n appendBatch?(messages: ReadonlyArray<DataBusMessage<TData>>): Promise<void>;\n /** Remove all persisted replay history. */\n clear?(): Promise<void>;\n /** Remove persisted replay history for one exact topic. */\n clearTopic?(topic: string): Promise<void>;\n /** Remove persisted messages older than the given epoch-millisecond cutoff. */\n clearBefore?(timestamp: number): Promise<void>;\n}\n\nexport interface IndexedDbReplayPersistenceOptions {\n dbName?: string;\n maxPerTopic: number;\n pruneStrategy?: (typeof PRUNE_STRATEGY)[keyof typeof PRUNE_STRATEGY];\n retentionMs?: number;\n}\n\n/** Create a browser IndexedDB-backed replay store. */\nexport function createIndexedDbReplayPersistence<TData = unknown>(\n options: IndexedDbReplayPersistenceOptions\n): DataBusReplayPersistence<TData> {\n const indexedDb = globalThis.indexedDB;\n if (!indexedDb) throw new Error('IndexedDB is unavailable in this environment.');\n const dbName = options.dbName ?? DEFAULT_STORAGE_PREFIX;\n const storeName = 'replay';\n const maxPerTopic = options.maxPerTopic;\n const pruneStrategy = options.pruneStrategy ?? PRUNE_STRATEGY.COUNT;\n const retentionMs = options.retentionMs;\n assertPruneStrategy(pruneStrategy);\n if (retentionMs !== undefined) assertPositiveFiniteNumber(retentionMs, 'retentionMs');\n assertPositiveSafeInteger(maxPerTopic, 'maxPerTopic');\n let dbPromise: Promise<IDBDatabase> | null = null;\n const invalidate = (db: IDBDatabase): void => {\n if (dbPromise) {\n void dbPromise.then(current => {\n if (current === db) {\n current.close();\n dbPromise = null;\n }\n }, () => undefined);\n }\n };\n // IndexedDB transactions are atomic, but a read-modify-write append can\n // still lose updates when callers start several appends concurrently.\n // Serialize all mutations per adapter instance while keeping reads free.\n // Consecutive append/appendBatch entries are coalesced into a single\n // transaction at the head of the queue, so a burst spanning many microtask\n // flushes issues one transaction instead of one per flush. Ordering against\n // clears is preserved: coalescing only merges adjacent batch entries and\n // never reorders them relative to a clear.\n type QueuedMutation =\n | { kind: 'batch'; messages: ReadonlyArray<DataBusMessage<TData>> }\n | { kind: 'run'; run: () => Promise<void> };\n const pending: Array<{\n mutation: QueuedMutation;\n resolve: () => void;\n reject: (error: unknown) => void;\n }> = [];\n let draining = false;\n\n const drain = async (): Promise<void> => {\n if (draining) return;\n draining = true;\n try {\n // Let a burst of synchronous enqueues accumulate into `pending` before\n // the first coalescing pass, so a single flush cycle's batches merge\n // into one transaction instead of two.\n await Promise.resolve();\n while (pending.length > 0) {\n const head = pending[0]!.mutation;\n if (head.kind === 'batch') {\n // Merge every adjacent batch entry into one transaction.\n const merged: DataBusMessage<TData>[] = [];\n const entries: Array<{ resolve: () => void; reject: (error: unknown) => void }> = [];\n while (pending.length > 0) {\n const next = pending[0]!.mutation;\n if (next.kind !== 'batch') break;\n merged.push(...next.messages);\n entries.push({ resolve: pending[0]!.resolve, reject: pending[0]!.reject });\n pending.shift();\n }\n try {\n await appendTransaction(merged);\n for (const entry of entries) entry.resolve();\n } catch (error) {\n for (const entry of entries) entry.reject(error);\n }\n } else {\n const entry = pending.shift()!;\n try {\n await head.run();\n entry.resolve();\n } catch (error) {\n entry.reject(error);\n }\n }\n }\n } finally {\n draining = false;\n }\n };\n\n const enqueue = (mutation: QueuedMutation): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n pending.push({ mutation, resolve, reject });\n void drain();\n });\n\n /** Read-modify-write one topic-batched append inside a single transaction. */\n const appendTransaction = (messages: ReadonlyArray<DataBusMessage<TData>>): Promise<void> =>\n (async () => {\n const db = await open();\n return new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try {\n transaction = db.transaction(storeName, 'readwrite');\n } catch (error) {\n invalidate(db);\n reject(error);\n return;\n }\n const store = transaction.objectStore(storeName);\n const grouped = new Map<string, DataBusMessage<TData>[]>();\n for (const message of messages) {\n grouped.set(message.topic, [...(grouped.get(message.topic) ?? []), message]);\n }\n let hasError = false;\n for (const [topic, topicMessages] of grouped) {\n const request = store.get(topic);\n request.onsuccess = () => {\n if (hasError) return;\n let history = ((request.result?.messages ?? []) as DataBusMessage<TData>[]).concat(topicMessages);\n // Mirrors ReplayManager: an AGE strategy with no retention window\n // has nothing to prune by, so the count cap still applies (else the\n // stored topic record would grow without bound).\n const ageBounded = pruneStrategy !== PRUNE_STRATEGY.COUNT && retentionMs !== undefined;\n if (ageBounded) {\n const cutoff = Date.now() - retentionMs;\n history = history.filter(item => item.timestamp === undefined || item.timestamp >= cutoff);\n }\n if (pruneStrategy !== PRUNE_STRATEGY.AGE || !ageBounded) history = history.slice(-maxPerTopic);\n store.put({ topic, messages: history });\n };\n request.onerror = () => {\n if (hasError) return;\n hasError = true;\n invalidate(db);\n reject(request.error ?? new Error('Failed to read replay history.'));\n };\n }\n transaction.oncomplete = () => {\n if (!hasError) resolve();\n };\n transaction.onerror = () => {\n if (hasError) return;\n hasError = true;\n invalidate(db);\n reject(transaction.error ?? new Error('Failed to persist replay history.'));\n };\n });\n })();\n const open = (): Promise<IDBDatabase> => {\n if (dbPromise) return dbPromise;\n const pending = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDb.open(dbName, 1);\n request.onupgradeneeded = () => request.result.createObjectStore(storeName, { keyPath: 'topic' });\n request.onsuccess = () => {\n const db = request.result;\n // A schema upgrade in another tab invalidates this connection. Close\n // it and clear the cached promise so the next operation reopens a\n // usable connection instead of repeatedly targeting a dead database.\n db.onversionchange = () => {\n db.close();\n if (dbPromise) dbPromise = null;\n };\n resolve(db);\n };\n request.onerror = () => reject(request.error ?? new Error('Failed to open replay database.'));\n });\n dbPromise = pending;\n // Do not permanently cache a rejected open promise. IndexedDB can fail\n // transiently (quota, private-mode initialization, a closing connection,\n // or a browser shutdown); the next operation must be able to retry.\n void pending.catch(() => {\n if (dbPromise === pending) dbPromise = null;\n });\n return pending;\n };\n return {\n async load() {\n const db = await open();\n return new Promise((resolve, reject) => {\n let request: IDBRequest;\n try {\n request = db.transaction(storeName, 'readonly').objectStore(storeName).getAll();\n } catch (error) {\n invalidate(db);\n reject(error);\n return;\n }\n request.onsuccess = () => resolve((request.result as Array<{ messages: DataBusMessage<TData>[] }>).flatMap(record => record.messages));\n request.onerror = () => {\n invalidate(db);\n reject(request.error ?? new Error('Failed to load replay history.'));\n };\n });\n },\n append(message) {\n return enqueue({ kind: 'batch', messages: [message] });\n },\n appendBatch(messages) {\n if (messages.length === 0) return Promise.resolve();\n return enqueue({ kind: 'batch', messages });\n },\n clear() {\n return enqueue({\n kind: 'run',\n run: () => (async () => {\n const db = await open();\n return new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n transaction.objectStore(storeName).clear();\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to clear replay history.')); };\n });\n })()\n });\n },\n clearTopic(topic) {\n return enqueue({\n kind: 'run',\n run: () => (async () => {\n const db = await open();\n return new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n transaction.objectStore(storeName).delete(topic);\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to clear topic replay history.')); };\n });\n })()\n });\n },\n clearBefore(timestamp) {\n return enqueue({\n kind: 'run',\n run: () => (async () => {\n const db = await open();\n return new Promise<void>((resolve, reject) => {\n let transaction: IDBTransaction;\n try { transaction = db.transaction(storeName, 'readwrite'); }\n catch (error) { invalidate(db); reject(error); return; }\n const store = transaction.objectStore(storeName);\n const request = store.getAll();\n request.onsuccess = () => {\n for (const record of request.result as Array<{ topic: string; messages: DataBusMessage<TData>[] }>) {\n const messages = record.messages.filter(message => message.timestamp === undefined || message.timestamp >= timestamp);\n if (messages.length === 0) store.delete(record.topic);\n else if (messages.length !== record.messages.length) store.put({ topic: record.topic, messages });\n }\n };\n request.onerror = () => { invalidate(db); reject(request.error ?? new Error('Failed to read replay history.')); };\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => { invalidate(db); reject(transaction.error ?? new Error('Failed to prune replay history.')); };\n });\n })()\n });\n }\n };\n}\n", "/**\n * WebSocketTransport \u2014 a dependency-free transport over a plain WebSocket.\n *\n * Validates the `DataBusTransport` abstraction with a second, minimal backend:\n * any WebSocket server that speaks the tiny JSON protocol below can back the\n * same cross-tab clustering stack (owner dedup, sticky routes, EVENT fan-out)\n * that the Centrifuge backend uses.\n *\n * Wire protocol (JSON text frames):\n * - client \u2192 server: `{\"op\":\"subscribe\"|\"unsubscribe\"|\"publish\",\"topic\":...,\"data\":...}`\n * - client \u2192 server (batched): `{\"op\":\"publishBatch\",\"topic\":...,\"items\":[{data,...}]}`\n * - server \u2192 client: `{\"topic\":...,\"data\":...}` for publications; anything\n * without a string `topic` field is ignored (forward-compatible).\n */\nimport { CrossTabDataBus } from './core/data-bus';\nimport { parseDataBusPublication } from './core/publication';\nimport type { CrossTabDataBusOptions } from './core/data-bus';\nimport { WS_OP, WORKER_STATUS } from './utils/constants';\nimport type {\n DataBusTransport,\n DataBusTransportHandlers,\n DataBusPublishOptions,\n DataBusPublicationItem,\n DataBusMessage,\n MaybePromise,\n WorkerStatus\n} from './core/types';\n\n/** Minimal WebSocket surface used by the transport. Matches the browser\n * `WebSocket` subset the transport touches; injectable for tests and runtimes. */\nexport interface WebSocketLike {\n /** Current connection state; 1 (OPEN) means frames may be sent. */\n readonly readyState?: number;\n send(data: string | ArrayBuffer): void;\n close(code?: number, reason?: string): void;\n onopen: (() => void) | null;\n onclose: (() => void) | null;\n onerror: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/** Connection configuration for {@link WebSocketTransport}. */\nexport interface WebSocketDataBusConfig {\n /** WebSocket endpoint, e.g. `wss://example.test/ws`. */\n url: string;\n /** Subprotocol(s) passed to the WebSocket handshake. */\n protocols?: string | string[];\n /** Custom socket factory. Defaults to the global `WebSocket`; injectable\n * for tests and non-browser runtimes. */\n webSocketFactory?: (url: string, protocols?: string | string[]) => WebSocketLike;\n}\n\n/** Options for creating a fully-configured CrossTabDataBus with a WebSocket transport. */\nexport interface CreateWebSocketDataBusOptions<TData = unknown>\n extends Omit<\n CrossTabDataBusOptions<WebSocketDataBusConfig, TData>,\n 'autoStart' | 'clusterKey' | 'initialConfig' | 'transport'\n > {\n /** WebSocket connection configuration. */\n connection: WebSocketDataBusConfig;\n /** Cluster key for cross-tab coordination. Defaults to the connection URL. */\n clusterKey?: string;\n}\n\nconst WS_OPEN = 1;\n\n/** Transport that talks a minimal JSON protocol over a plain WebSocket.\n * Connection lifecycle maps directly to the DataBus status vocabulary:\n * open \u2192 `connected`, close \u2192 `disconnected`, error \u2192 `error` (which the\n * DataBus treats as its auto-recovery trigger). The transport holds no\n * reconnection logic of its own \u2014 reopening is the DataBus's job. */\nexport class WebSocketTransport<TData = unknown>\n implements DataBusTransport<WebSocketDataBusConfig, TData>\n{\n readonly diagnosticsName = 'websocket';\n readonly diagnosticsBackend = 'native-websocket';\n private socket: WebSocketLike | null = null;\n private handlers: DataBusTransportHandlers<TData> | null = null;\n private readonly subscribedTopics = new Set<string>();\n\n constructor(private readonly connection: WebSocketDataBusConfig) {}\n\n /** Open the WebSocket and wire lifecycle listeners. A factory failure is\n * reported through `onStatus('error')` so the DataBus can recover. */\n start(config: WebSocketDataBusConfig, handlers: DataBusTransportHandlers<TData>): MaybePromise<void> {\n if (this.socket) return;\n this.handlers = handlers;\n // The factory may live on the constructor connection (createWebSocketDataBus\n // path) or on the runtime config (direct transport use) \u2014 accept both.\n const factory = config.webSocketFactory ?? this.connection.webSocketFactory ?? defaultWebSocketFactory;\n const protocols = config.protocols ?? this.connection.protocols;\n let socket: WebSocketLike;\n try {\n socket = factory(config.url, protocols);\n } catch (error) {\n handlers.onStatus(WORKER_STATUS.ERROR);\n handlers.onError(error);\n return;\n }\n socket.onopen = () => {\n if (this.socket !== socket || this.handlers !== handlers) return;\n // Re-assert every topic so a reopened socket (recovery path) restores\n // the server-side subscriptions without DataBus involvement.\n for (const topic of this.subscribedTopics) {\n this.sendFrame({ op: WS_OP.SUBSCRIBE, topic });\n }\n handlers.onStatus(WORKER_STATUS.CONNECTED);\n };\n socket.onclose = () => {\n if (this.socket === socket && this.handlers === handlers) handlers.onStatus(WORKER_STATUS.DISCONNECTED);\n };\n socket.onerror = () => {\n if (this.socket === socket && this.handlers === handlers) handlers.onStatus(WORKER_STATUS.ERROR);\n };\n socket.onmessage = event => {\n if (this.socket === socket && this.handlers === handlers) void this.handleMessage(event.data);\n };\n this.socket = socket;\n }\n\n /** Idempotent: re-subscribing an active topic re-sends the frame but does\n * not duplicate the local tracking entry. */\n subscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.add(topic);\n this.sendFrame({ op: WS_OP.SUBSCRIBE, topic });\n }\n\n /** Idempotent: unsubscribing an unknown topic is a no-op. */\n unsubscribe(topic: string): MaybePromise<void> {\n this.subscribedTopics.delete(topic);\n this.sendFrame({ op: WS_OP.UNSUBSCRIBE, topic });\n }\n\n /** Publish `data` to `topic` as a JSON frame. Requires an open socket. */\n publish(topic: string, data: unknown, options?: DataBusPublishOptions): MaybePromise<void> {\n if (data instanceof ArrayBuffer) {\n this.sendBinaryFrame(topic, data, options?.messageId, options?.timestamp);\n return;\n }\n this.sendFrame({\n op: WS_OP.PUBLISH,\n topic,\n data,\n ...(options?.messageId === undefined ? {} : { messageId: options.messageId }),\n ...(options?.timestamp === undefined ? {} : { timestamp: options.timestamp })\n });\n }\n\n /** Publish many items for one topic as a single wire frame. One-item\n * batches delegate to `publish` so the legacy single-publication frame\n * shape (including binary framing) is preserved. */\n publishBatch(topic: string, items: ReadonlyArray<DataBusPublicationItem>): MaybePromise<void> {\n if (items.length === 0) return;\n if (items.length === 1) {\n const single = items[0]!;\n return this.publish(topic, single.data, {\n ...(single.messageId === undefined ? {} : { messageId: single.messageId }),\n ...(single.timestamp === undefined ? {} : { timestamp: single.timestamp })\n });\n }\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error('WebSocket is not open; dropped \"publishBatch\" frame.'));\n return;\n }\n // Binary payloads are embedded as byte arrays so the whole batch stays in\n // one JSON frame; the server re-fans them out as individual publications.\n this.socket.send(JSON.stringify({\n op: WS_OP.PUBLISH_BATCH,\n topic,\n items: items.map(item => ({\n data: item.data instanceof ArrayBuffer ? Array.from(new Uint8Array(item.data)) : item.data,\n ...(item.messageId === undefined ? {} : { messageId: item.messageId }),\n ...(item.timestamp === undefined ? {} : { timestamp: item.timestamp })\n }))\n }));\n }\n\n /** Close the socket and drop all state. Safe to call multiple times. */\n stop(): MaybePromise<void> {\n const socket = this.socket;\n this.socket = null;\n this.handlers = null;\n this.subscribedTopics.clear();\n socket?.close();\n }\n\n /** Send one JSON frame. Frames are dropped with an `onError` report when\n * the socket is not open \u2014 subscribe frames are re-sent on open, so the\n * only real loss is a publish during a disconnect window. */\n private sendFrame(payload: { op: string; topic: string; data?: unknown; messageId?: string; timestamp?: number }): void {\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error(`WebSocket is not open; dropped \"${payload.op}\" frame.`));\n return;\n }\n this.socket.send(JSON.stringify(payload));\n }\n\n private sendBinaryFrame(topic: string, data: ArrayBuffer, messageId?: string, timestamp?: number): void {\n if (messageId !== undefined || timestamp !== undefined) {\n // Binary frames retain their compact legacy shape; metadata is sent as a\n // JSON envelope so IDs are never silently lost.\n this.sendFrame({\n op: WS_OP.PUBLISH,\n topic,\n data: Array.from(new Uint8Array(data)),\n ...(messageId === undefined ? {} : { messageId }),\n ...(timestamp === undefined ? {} : { timestamp })\n });\n return;\n }\n if (this.socket?.readyState !== WS_OPEN) {\n this.handlers?.onError(new Error('WebSocket is not open; dropped \"publish\" frame.'));\n return;\n }\n const topicBytes = new TextEncoder().encode(topic);\n if (topicBytes.length > 0xffff) {\n this.handlers?.onError(new Error('WebSocket topic is too long for a binary frame.'));\n return;\n }\n const frame = new Uint8Array(3 + topicBytes.length + data.byteLength);\n frame[0] = 0xc7;\n new DataView(frame.buffer).setUint16(1, topicBytes.length);\n frame.set(topicBytes, 3);\n frame.set(new Uint8Array(data), 3 + topicBytes.length);\n this.socket.send(frame.buffer);\n }\n\n /** Parse a server frame. Only objects carrying a string `topic` are\n * publications; malformed JSON and unknown shapes are ignored so a chatty\n * server cannot crash the message path. */\n private async handleMessage(raw: unknown): Promise<void> {\n // Browser WebSockets may deliver binary frames as Blob unless\n // `binaryType = 'arraybuffer'` is explicitly configured by the host.\n // Normalize Blob asynchronously and reuse the exact ArrayBuffer parser.\n if (typeof Blob !== 'undefined' && raw instanceof Blob) {\n try {\n await this.handleMessage(await raw.arrayBuffer());\n } catch (error) {\n this.handlers?.onError(error);\n }\n return;\n }\n let parsed: unknown;\n if (raw instanceof ArrayBuffer) {\n const bytes = new Uint8Array(raw);\n if (bytes[0] !== 0xc7 || bytes.length < 3) return;\n const topicLength = new DataView(raw).getUint16(1);\n if (bytes.length < 3 + topicLength) return;\n const topic = new TextDecoder().decode(bytes.subarray(3, 3 + topicLength));\n const data = bytes.slice(3 + topicLength).buffer;\n this.handlers?.onMessage({ topic, data: data as TData });\n return;\n }\n if (typeof raw !== 'string') return;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.handlers?.onError(new Error('WebSocket server sent a non-JSON frame.'));\n return;\n }\n if (!parsed || typeof parsed !== 'object') return;\n const publication = parseDataBusPublication<TData>(parsed);\n if (publication) this.handlers?.onMessage(publication as DataBusMessage<TData>);\n }\n}\n\n/** Resolve the platform WebSocket, or null in runtimes without one (SSR/Node). */\nfunction defaultWebSocketFactory(url: string, protocols?: string | string[]): WebSocketLike {\n if (typeof WebSocket === 'undefined') {\n throw new Error('WebSocketTransport requires a WebSocket implementation.');\n }\n return new WebSocket(url, protocols) as unknown as WebSocketLike;\n}\n\n/** Create a CrossTabDataBus backed by a plain WebSocket transport.\n * Cross-tab clustering (owner dedup, sticky routes, failover) works identically\n * to the Centrifuge backend \u2014 only the transport I/O differs. */\nexport function createWebSocketDataBus<TData = unknown>(\n options: CreateWebSocketDataBusOptions<TData>\n): CrossTabDataBus<WebSocketDataBusConfig, TData> {\n const { clusterKey, connection, ...dataBusOptions } = options;\n return new CrossTabDataBus({\n ...dataBusOptions,\n autoStart: true,\n clusterKey: clusterKey ?? connection.url,\n initialConfig: connection,\n transport: new WebSocketTransport<TData>(connection)\n });\n}\n\n/** Re-export for convenience: the status type used by the transport. */\nexport type { WorkerStatus };\n"],
5
+ "mappings": "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",
6
+ "names": ["pending"]
7
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;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjE,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,CAmB7C;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"}
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("connecting");
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() ?? "connecting";
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
  };