websocket-pro-client
Advanced tools
| /** | ||
| * topicPattern 匹配 | ||
| * - `*`:任意长度任意字符 | ||
| * - `?`:任意单个字符 | ||
| * - `{a,b}`:多备选 | ||
| */ | ||
| export declare function matchTopicPattern(pattern: string, topic: string): boolean; |
@@ -5,3 +5,6 @@ export declare enum WebSocketErrorCode { | ||
| AckMaxRetries = "ACK_MAX_RETRIES", | ||
| ClosedBeforeAck = "CLOSED_BEFORE_ACK" | ||
| ClosedBeforeAck = "CLOSED_BEFORE_ACK", | ||
| OfflineQueueOverflow = "OFFLINE_QUEUE_OVERFLOW", | ||
| OfflineQueueTTLExpired = "OFFLINE_QUEUE_TTL_EXPIRED", | ||
| ClosedBeforeSend = "CLOSED_BEFORE_SEND" | ||
| } | ||
@@ -8,0 +11,0 @@ export declare const WebSocketErrorMessage: Record<WebSocketErrorCode, string>; |
@@ -13,2 +13,3 @@ import { EventEmitter } from './EventEmitter'; | ||
| private isOverMaxReconnectAttempts; | ||
| private isClosingForReconnect; | ||
| private readonly messageQueue; | ||
@@ -37,3 +38,2 @@ private heartbeat?; | ||
| private dispatchSubscribedMessage; | ||
| private isTopicMatch; | ||
| private reSyncSubscriptions; | ||
@@ -48,2 +48,3 @@ /** | ||
| private flushMessageQueue; | ||
| private enqueueOfflineMessage; | ||
| private sendInternal; | ||
@@ -77,4 +78,6 @@ private handleAckTimeout; | ||
| subscribe(topic: string, listener: (data: any) => void): () => void; | ||
| subscribe(topics: string[], listener: (data: any) => void): () => void; | ||
| subscribeOnce(topic: string, listener: (data: any) => void): () => void; | ||
| unsubscribe(topic: string, listener?: (data: any) => void): void; | ||
| unsubscribe(topics: string[], listener?: (data: any) => void): void; | ||
| close(code?: number, reason?: string): void; | ||
@@ -81,0 +84,0 @@ reconnect(): void; |
+3
-2
@@ -1,2 +0,2 @@ | ||
| import { WebSocketClientState, WebSocketConfig, WebSocketEvent, WebSocketClientStats, IWebSocketManager, IWebSocketClient, SubscriptionStrategy, Serializer } from './types'; | ||
| import { OfflineQueueDropStrategy, WebSocketClientState, OfflineQueueConfig, ResetStatsOptions, WebSocketConfig, WebSocketEvent, WebSocketClientStats, IWebSocketManager, IWebSocketClient, SubscriptionStrategy, Serializer } from './types'; | ||
| /** | ||
@@ -37,3 +37,4 @@ * 创建 WebSocket 管理器实例 | ||
| export declare const MsgPackSerializer: Serializer; | ||
| export type { WebSocketConfig, WebSocketEvent, WebSocketClientStats, IWebSocketManager, IWebSocketClient, SubscriptionStrategy, Serializer, }; | ||
| export type { OfflineQueueConfig, ResetStatsOptions, WebSocketConfig, WebSocketEvent, WebSocketClientStats, IWebSocketManager, IWebSocketClient, SubscriptionStrategy, Serializer, }; | ||
| export { OfflineQueueDropStrategy }; | ||
| export { WebSocketClientState }; | ||
@@ -40,0 +41,0 @@ export { HeartbeatMessage, HeartbeatEvent } from './constants/heartbeat'; |
@@ -118,2 +118,32 @@ import { HeartbeatTimerMode } from '../constants/heartbeat'; | ||
| /** | ||
| * 离线消息队列策略 | ||
| */ | ||
| export declare enum OfflineQueueDropStrategy { | ||
| DropOldest = "dropOldest", | ||
| DropNewest = "dropNewest", | ||
| Reject = "reject" | ||
| } | ||
| export type OfflineQueueConfig = { | ||
| /** | ||
| * 是否启用离线队列(socket 非 OPEN 时入队) | ||
| * 默认 true | ||
| */ | ||
| enabled?: boolean; | ||
| /** | ||
| * 队列容量上限(默认 1000) | ||
| * - Infinity 表示不限制 | ||
| */ | ||
| maxQueueSize?: number; | ||
| /** | ||
| * 超出容量后的处理策略 | ||
| * 默认 OfflineQueueDropStrategy.DropOldest | ||
| */ | ||
| dropStrategy?: OfflineQueueDropStrategy; | ||
| /** | ||
| * 消息 TTL(毫秒),过期会从队列移除并 reject 对应 Promise | ||
| * 默认不启用 TTL | ||
| */ | ||
| messageTTL?: number; | ||
| }; | ||
| /** | ||
| * WebSocket 客户端运行状态枚举 | ||
@@ -187,2 +217,4 @@ */ | ||
| subscription?: SubscriptionStrategy; | ||
| /** 离线消息队列配置 */ | ||
| offlineQueue?: OfflineQueueConfig; | ||
| /** 是否需要心跳 (默认: true) */ | ||
@@ -213,2 +245,3 @@ isNeedHeartbeat?: boolean; | ||
| subscribe(topic: string, listener: (data: any) => void): () => void; | ||
| subscribe(topics: string[], listener: (data: any) => void): () => void; | ||
| /** | ||
@@ -219,2 +252,3 @@ * 订阅某个 topic 的消息(只触发一次),触发后会自动退订 | ||
| unsubscribe(topic: string, listener?: (data: any) => void): void; | ||
| unsubscribe(topics: string[], listener?: (data: any) => void): void; | ||
| getState(): WebSocketClientState; | ||
@@ -221,0 +255,0 @@ getStats(): WebSocketClientStats; |
| export declare const deepMerge: (target: any, source: any) => any; | ||
| export declare const isEqual: (target: any, source: any) => boolean; | ||
| export { matchTopicPattern } from './topicPattern'; |
+291
-167
@@ -1,3 +0,15 @@ | ||
| var C = /* @__PURE__ */ ((s) => (s.Ping = "PING", s.Pong = "PONG", s))(C || {}), T = /* @__PURE__ */ ((s) => (s.Timeout = "timeout", s.Pong = "pong", s))(T || {}), g = /* @__PURE__ */ ((s) => (s.Auto = "auto", s.Main = "main", s.Worker = "worker", s))(g || {}); | ||
| const P = { | ||
| var u = /* @__PURE__ */ ((s) => (s.Open = "open", s.Message = "message", s.Close = "close", s.Error = "error", s.Reconnect = "reconnect", s.Heartbeat = "heartbeat", s.Latency = "latency", s.OverMaxReconnectAttempts = "overMaxReconnectAttempts", s))(u || {}); | ||
| const I = [ | ||
| "open", | ||
| "message", | ||
| "close", | ||
| "error", | ||
| "reconnect", | ||
| "heartbeat", | ||
| "latency", | ||
| "overMaxReconnectAttempts" | ||
| /* OverMaxReconnectAttempts */ | ||
| ]; | ||
| var y = /* @__PURE__ */ ((s) => (s.DropOldest = "dropOldest", s.DropNewest = "dropNewest", s.Reject = "reject", s))(y || {}), p = /* @__PURE__ */ ((s) => (s.Connecting = "connecting", s.Open = "open", s.Reconnecting = "reconnecting", s.Closed = "closed", s.OverMaxReconnectAttempts = "overMaxReconnectAttempts", s))(p || {}), A = /* @__PURE__ */ ((s) => (s.Ping = "PING", s.Pong = "PONG", s))(A || {}), w = /* @__PURE__ */ ((s) => (s.Timeout = "timeout", s.Pong = "pong", s))(w || {}), b = /* @__PURE__ */ ((s) => (s.Auto = "auto", s.Main = "main", s.Worker = "worker", s))(b || {}); | ||
| const O = { | ||
| maxReconnectAttempts: 10, | ||
@@ -47,2 +59,8 @@ reconnectDelay: 1e3, | ||
| }, | ||
| offlineQueue: { | ||
| enabled: !0, | ||
| maxQueueSize: 1e3, | ||
| dropStrategy: y.DropOldest, | ||
| messageTTL: void 0 | ||
| }, | ||
| isNeedHeartbeat: !0, | ||
@@ -52,21 +70,8 @@ heartbeat: { | ||
| timeout: 45e3, | ||
| pingMessage: C.Ping, | ||
| pongMessage: C.Pong, | ||
| timerMode: g.Auto | ||
| pingMessage: A.Ping, | ||
| pongMessage: A.Pong, | ||
| timerMode: b.Auto | ||
| } | ||
| }; | ||
| var h = /* @__PURE__ */ ((s) => (s.Open = "open", s.Message = "message", s.Close = "close", s.Error = "error", s.Reconnect = "reconnect", s.Heartbeat = "heartbeat", s.Latency = "latency", s.OverMaxReconnectAttempts = "overMaxReconnectAttempts", s))(h || {}); | ||
| const O = [ | ||
| "open", | ||
| "message", | ||
| "close", | ||
| "error", | ||
| "reconnect", | ||
| "heartbeat", | ||
| "latency", | ||
| "overMaxReconnectAttempts" | ||
| /* OverMaxReconnectAttempts */ | ||
| ]; | ||
| var m = /* @__PURE__ */ ((s) => (s.Connecting = "connecting", s.Open = "open", s.Reconnecting = "reconnecting", s.Closed = "closed", s.OverMaxReconnectAttempts = "overMaxReconnectAttempts", s))(m || {}); | ||
| class L { | ||
| class N { | ||
| constructor(e = "[WebSocketPro]") { | ||
@@ -91,5 +96,5 @@ this.prefix = e; | ||
| } | ||
| let I = new L(); | ||
| const y = () => I; | ||
| class x { | ||
| let D = new N(); | ||
| const C = () => D; | ||
| class R { | ||
| constructor() { | ||
@@ -109,3 +114,3 @@ this.events = {}; | ||
| } catch (n) { | ||
| y().error(`Event "${e}" listener error:`, n); | ||
| C().error(`Event "${e}" listener error:`, n); | ||
| } | ||
@@ -124,3 +129,3 @@ }); | ||
| } | ||
| class k { | ||
| class _ { | ||
| setTimeout(e, t) { | ||
@@ -133,3 +138,3 @@ return globalThis.setTimeout(e, t); | ||
| } | ||
| class N { | ||
| class Q { | ||
| constructor() { | ||
@@ -206,4 +211,4 @@ this.callbackMap = /* @__PURE__ */ new Map(), this.nextId = 1, this.isAvailable = !1, this.worker = this.createWorker(), this.isAvailable = !!this.worker; | ||
| return; | ||
| const a = this.callbackMap.get(o.id); | ||
| a && (this.callbackMap.delete(o.id), a()); | ||
| const c = this.callbackMap.get(o.id); | ||
| c && (this.callbackMap.delete(o.id), c()); | ||
| }, n.onerror = () => { | ||
@@ -223,19 +228,19 @@ this.markUnavailable(); | ||
| } catch { | ||
| y().error("Heartbeat worker error"); | ||
| C().error("Heartbeat worker error"); | ||
| } | ||
| } | ||
| } | ||
| function v(s) { | ||
| const e = s.timerMode ?? g.Auto; | ||
| if (e === g.Main) | ||
| return new k(); | ||
| const t = new N(); | ||
| return e === g.Worker && !t.available ? (y().warn("Heartbeat worker timer unavailable, fallback to main"), t.destroy(), new k()) : e === g.Auto && !t.available ? (t.destroy(), new k()) : t; | ||
| function M(s) { | ||
| const e = s.timerMode ?? b.Auto; | ||
| if (e === b.Main) | ||
| return new _(); | ||
| const t = new Q(); | ||
| return e === b.Worker && !t.available ? (C().warn("Heartbeat worker timer unavailable, fallback to main"), t.destroy(), new _()) : e === b.Auto && !t.available ? (t.destroy(), new _()) : t; | ||
| } | ||
| class D extends x { | ||
| class q extends R { | ||
| constructor(e = {}, t) { | ||
| super(), this.config = e, this.sendPing = t, this.lastPongTime = 0, this.lastPingTime = 0, this.expectedNextPingAt = 0, this.isRunning = !1, this.timer = v(this.config); | ||
| super(), this.config = e, this.sendPing = t, this.lastPongTime = 0, this.lastPingTime = 0, this.expectedNextPingAt = 0, this.isRunning = !1, this.timer = M(this.config); | ||
| } | ||
| start() { | ||
| this.config || y().warn("Heartbeat config is empty"), this.stop(), this.isRunning = !0; | ||
| this.config || C().warn("Heartbeat config is empty"), this.stop(), this.isRunning = !0; | ||
| const e = Date.now(); | ||
@@ -248,7 +253,7 @@ this.lastPongTime = e, this.lastPingTime = 0, this.expectedNextPingAt = e + (this.config.interval ?? 0), this.schedulePongTimeoutCheck(), this.scheduleNextPing(); | ||
| handleDefaultTimeout(e) { | ||
| this.stop(), e && e(), this.emit(T.Timeout); | ||
| this.stop(), e && e(), this.emit(w.Timeout); | ||
| } | ||
| recordPong() { | ||
| const e = Date.now(), t = this.lastPingTime ? e - this.lastPingTime : 0; | ||
| this.lastPongTime = e, this.timer.clearTimeout(this.pongTimeoutTimer), this.emit(T.Pong, t), this.schedulePongTimeoutCheck(); | ||
| this.lastPongTime = e, this.timer.clearTimeout(this.pongTimeoutTimer), this.emit(w.Pong, t), this.schedulePongTimeoutCheck(); | ||
| } | ||
@@ -274,3 +279,3 @@ getLastPongTime() { | ||
| } | ||
| this.config = { ...this.config, ...e.heartbeat }, (r = (n = this.timer).destroy) == null || r.call(n), this.timer = v(this.config), this.stop(), this.start(); | ||
| this.config = { ...this.config, ...e.heartbeat }, (r = (n = this.timer).destroy) == null || r.call(n), this.timer = M(this.config), this.stop(), this.start(); | ||
| } | ||
@@ -311,3 +316,3 @@ scheduleNextPing() { | ||
| } | ||
| class q { | ||
| class j { | ||
| constructor(e, t) { | ||
@@ -322,7 +327,7 @@ this.maxConcurrent = e, this.onTaskError = t, this.queue = [], this.runningCount = 0; | ||
| await e(), i(); | ||
| } catch (a) { | ||
| (o = this.onTaskError) == null || o.call(this, a), n(a); | ||
| } catch (c) { | ||
| (o = this.onTaskError) == null || o.call(this, c), n(c); | ||
| } | ||
| }; | ||
| this.queue.push({ task: r, priority: t }), this.queue.sort((o, a) => a.priority - o.priority), this.run(); | ||
| this.queue.push({ task: r, priority: t }), this.queue.sort((o, c) => c.priority - o.priority), this.run(); | ||
| }); | ||
@@ -345,10 +350,70 @@ } | ||
| } | ||
| const w = (s, e) => { | ||
| const P = /* @__PURE__ */ new Map(); | ||
| function z(s, e) { | ||
| if (s === e) | ||
| return !0; | ||
| if (!(s.includes("*") || s.includes("?") || s.includes("{"))) | ||
| return !1; | ||
| const i = P.get(s); | ||
| if (i) | ||
| return i.test(e); | ||
| const n = U(s), r = new RegExp(n); | ||
| return P.set(s, r), r.test(e); | ||
| } | ||
| function U(s) { | ||
| const e = (i) => "\\^$+?.()|[\\]{}".includes(i) ? `\\${i}` : i, t = (i) => { | ||
| let n = ""; | ||
| for (let r = 0; r < i.length; r += 1) { | ||
| const o = i[r]; | ||
| if (o === "*") { | ||
| n += ".*"; | ||
| continue; | ||
| } | ||
| if (o === "?") { | ||
| n += "."; | ||
| continue; | ||
| } | ||
| if (o === "{") { | ||
| const { content: c, endIndex: a } = F(i, r), T = H(c).map((l) => t(l)).join("|"); | ||
| n += `(?:${T})`, r = a; | ||
| continue; | ||
| } | ||
| n += e(o); | ||
| } | ||
| return n; | ||
| }; | ||
| return `^${t(s)}$`; | ||
| } | ||
| function F(s, e) { | ||
| let t = 0; | ||
| for (let i = e; i < s.length; i += 1) { | ||
| const n = s[i]; | ||
| if (n === "{" && (t += 1), n === "}" && (t -= 1), t === 0) { | ||
| const r = i; | ||
| return { content: s.slice(e + 1, r), endIndex: r }; | ||
| } | ||
| } | ||
| return { content: s.slice(e + 1), endIndex: s.length - 1 }; | ||
| } | ||
| function H(s) { | ||
| const e = []; | ||
| let t = 0, i = ""; | ||
| for (let n = 0; n < s.length; n += 1) { | ||
| const r = s[n]; | ||
| if (r === "{" && (t += 1), r === "}" && (t -= 1), r === "," && t === 0) { | ||
| e.push(i), i = ""; | ||
| continue; | ||
| } | ||
| i += r; | ||
| } | ||
| return e.push(i), e; | ||
| } | ||
| const k = (s, e) => { | ||
| const t = { ...s }; | ||
| for (const i in e) { | ||
| const n = e[i]; | ||
| n && typeof n == "object" && !Array.isArray(n) ? t[i] = w(s[i] || {}, n) : t[i] = n; | ||
| n && typeof n == "object" && !Array.isArray(n) ? t[i] = k(s[i] || {}, n) : t[i] = n; | ||
| } | ||
| return t; | ||
| }, A = (s, e) => { | ||
| }, x = (s, e) => { | ||
| if (s === e) | ||
@@ -362,3 +427,3 @@ return !0; | ||
| for (let n = 0; n < s.length; n++) | ||
| if (!A(s[n], e[n])) | ||
| if (!x(s[n], e[n])) | ||
| return !1; | ||
@@ -373,23 +438,26 @@ return !0; | ||
| for (const n of t) | ||
| if (!e.hasOwnProperty(n) || !A(s[n], e[n])) | ||
| if (!e.hasOwnProperty(n) || !x(s[n], e[n])) | ||
| return !1; | ||
| return !0; | ||
| }; | ||
| var p = /* @__PURE__ */ ((s) => (s.MsgPackNotInstalled = "MSG_PACK_NOT_INSTALLED", s.AckTimeout = "ACK_TIMEOUT", s.AckMaxRetries = "ACK_MAX_RETRIES", s.ClosedBeforeAck = "CLOSED_BEFORE_ACK", s))(p || {}); | ||
| const z = { | ||
| var f = /* @__PURE__ */ ((s) => (s.MsgPackNotInstalled = "MSG_PACK_NOT_INSTALLED", s.AckTimeout = "ACK_TIMEOUT", s.AckMaxRetries = "ACK_MAX_RETRIES", s.ClosedBeforeAck = "CLOSED_BEFORE_ACK", s.OfflineQueueOverflow = "OFFLINE_QUEUE_OVERFLOW", s.OfflineQueueTTLExpired = "OFFLINE_QUEUE_TTL_EXPIRED", s.ClosedBeforeSend = "CLOSED_BEFORE_SEND", s))(f || {}); | ||
| const B = { | ||
| MSG_PACK_NOT_INSTALLED: "MsgPack serializer requires @msgpack/msgpack installation", | ||
| ACK_TIMEOUT: "ACK timeout", | ||
| ACK_MAX_RETRIES: "ACK timeout, maximum retry attempts reached", | ||
| CLOSED_BEFORE_ACK: "WebSocket connection closed before ACK was received" | ||
| CLOSED_BEFORE_ACK: "WebSocket connection closed before ACK was received", | ||
| OFFLINE_QUEUE_OVERFLOW: "Offline message queue overflow", | ||
| OFFLINE_QUEUE_TTL_EXPIRED: "Offline message queue message TTL expired", | ||
| CLOSED_BEFORE_SEND: "WebSocket connection closed before offline queued send" | ||
| }; | ||
| class b extends Error { | ||
| class d extends Error { | ||
| constructor(e) { | ||
| super(z[e]), this.code = e, this.name = "WebSocketClientError"; | ||
| super(B[e]), this.code = e, this.name = "WebSocketClientError"; | ||
| } | ||
| } | ||
| class j extends x { | ||
| class K extends R { | ||
| constructor(e, t, i) { | ||
| super(), this.url = e, this.protocols = t, this.config = i, this.socket = null, this.reconnectAttempts = 0, this.isManualClose = !1, this.isOverMaxReconnectAttempts = !1, this.messageQueue = [], this.topicListeners = /* @__PURE__ */ new Map(), this.sentCount = 0, this.receivedCount = 0, this.errorCount = 0, this.reconnectScheduledCount = 0, this.ackTimeoutCount = 0, this.pendingAcks = /* @__PURE__ */ new Map(), this.isUpdatingConfig = !1, this.configQueue = [], this.currentConfig = w(P, this.config), this.initHeartbeat(), this.scheduler = new q( | ||
| super(), this.url = e, this.protocols = t, this.config = i, this.socket = null, this.reconnectAttempts = 0, this.isManualClose = !1, this.isOverMaxReconnectAttempts = !1, this.isClosingForReconnect = !1, this.messageQueue = [], this.topicListeners = /* @__PURE__ */ new Map(), this.sentCount = 0, this.receivedCount = 0, this.errorCount = 0, this.reconnectScheduledCount = 0, this.ackTimeoutCount = 0, this.pendingAcks = /* @__PURE__ */ new Map(), this.isUpdatingConfig = !1, this.configQueue = [], this.currentConfig = k(O, this.config), this.initHeartbeat(), this.scheduler = new j( | ||
| this.currentConfig.maxConcurrent, | ||
| (n) => this.emit(h.Error, n) | ||
| (n) => this.emit(u.Error, n) | ||
| ), this.connect(); | ||
@@ -399,10 +467,10 @@ } | ||
| initHeartbeat() { | ||
| this.currentConfig.isNeedHeartbeat && (this.heartbeat = new D(this.currentConfig.heartbeat, () => { | ||
| const e = this.currentConfig.heartbeat, t = typeof (e == null ? void 0 : e.getPing) == "function" ? e.getPing() : (e == null ? void 0 : e.pingMessage) ?? C.Ping; | ||
| this.currentConfig.isNeedHeartbeat && (this.heartbeat = new q(this.currentConfig.heartbeat, () => { | ||
| const e = this.currentConfig.heartbeat, t = typeof (e == null ? void 0 : e.getPing) == "function" ? e.getPing() : (e == null ? void 0 : e.pingMessage) ?? A.Ping; | ||
| this.sendHeartbeat(t); | ||
| }), this.heartbeat.on(T.Timeout, () => { | ||
| }), this.heartbeat.on(w.Timeout, () => { | ||
| var e; | ||
| y().warn("Heartbeat timeout, triggering reconnect..."), ((e = this.socket) == null ? void 0 : e.readyState) === WebSocket.OPEN && this.close(1e3, "heartbeat timeout"), this.scheduleReconnect(); | ||
| }), this.heartbeat.on(T.Pong, (e) => { | ||
| this.emit(h.Heartbeat, e), this.emit(h.Latency, e), this.lastHeartbeatLatency = e; | ||
| C().warn("Heartbeat timeout, triggering reconnect..."), ((e = this.socket) == null ? void 0 : e.readyState) === WebSocket.OPEN && this.close(1e3, "heartbeat timeout"), this.scheduleReconnect(); | ||
| }), this.heartbeat.on(w.Pong, (e) => { | ||
| this.emit(u.Heartbeat, e), this.emit(u.Latency, e), this.lastHeartbeatLatency = e; | ||
| })); | ||
@@ -413,3 +481,3 @@ } | ||
| const t = this.reconnectAttempts > 0; | ||
| this.reconnectAttempts = 0, this.isOverMaxReconnectAttempts = !1, clearTimeout(this.reconnectTimer), this.reconnectTimer = void 0, this.heartbeat && this.heartbeat.start(), this.flushMessageQueue(), t && (this.reSyncSubscriptions(), this.emit(h.Reconnect)), this.emit(h.Open, e); | ||
| this.reconnectAttempts = 0, this.isOverMaxReconnectAttempts = !1, clearTimeout(this.reconnectTimer), this.reconnectTimer = void 0, this.heartbeat && this.heartbeat.start(), this.flushMessageQueue(), t && (this.reSyncSubscriptions(), this.emit(u.Reconnect)), this.emit(u.Open, e); | ||
| }, this.socket.onmessage = (e) => { | ||
@@ -431,7 +499,7 @@ this.receivedCount += 1; | ||
| if (o != null && o.enabled && typeof o.extractAckId == "function") { | ||
| const c = o.extractAckId(i); | ||
| if (c != null) { | ||
| const f = this.pendingAcks.get(c); | ||
| if (f) { | ||
| clearTimeout(f.timer), this.pendingAcks.delete(c), f.resolve(); | ||
| const a = o.extractAckId(i); | ||
| if (a != null) { | ||
| const g = this.pendingAcks.get(a); | ||
| if (g) { | ||
| clearTimeout(g.timer), this.pendingAcks.delete(a), g.resolve(); | ||
| return; | ||
@@ -441,12 +509,12 @@ } | ||
| } | ||
| const a = this.currentConfig.sequence; | ||
| if (a != null && a.enabled && typeof a.extractInboundSeq == "function") { | ||
| const c = a.extractInboundSeq(i); | ||
| c != null && (this.lastInboundSeq = c); | ||
| const c = this.currentConfig.sequence; | ||
| if (c != null && c.enabled && typeof c.extractInboundSeq == "function") { | ||
| const a = c.extractInboundSeq(i); | ||
| a != null && (this.lastInboundSeq = a); | ||
| } | ||
| this.emit(h.Message, i), this.dispatchSubscribedMessage(i); | ||
| this.emit(u.Message, i), this.dispatchSubscribedMessage(i); | ||
| }, this.socket.onclose = (e) => { | ||
| this.heartbeat && this.heartbeat.stop(), this.lastCloseCode = e.code, this.lastCloseReason = e.reason, this.lastCloseAt = Date.now(), this.emit(h.Close, e), this.isManualClose || this.scheduleReconnect(); | ||
| this.heartbeat && this.heartbeat.stop(), this.lastCloseCode = e.code, this.lastCloseReason = e.reason, this.lastCloseAt = Date.now(), this.emit(u.Close, e), this.isManualClose || this.scheduleReconnect(); | ||
| }, this.socket.onerror = (e) => { | ||
| this.heartbeat && this.heartbeat.stop(), this.emit(h.Error, e), this.errorCount += 1, this.lastErrorAt = Date.now(), this.scheduleReconnect(); | ||
| this.heartbeat && this.heartbeat.stop(), this.emit(u.Error, e), this.errorCount += 1, this.lastErrorAt = Date.now(), this.scheduleReconnect(); | ||
| }; | ||
@@ -461,7 +529,7 @@ } | ||
| i && this.topicListeners.size !== 0 && this.topicListeners.forEach((n, r) => { | ||
| this.isTopicMatch(r, i) && n.forEach((o) => { | ||
| z(r, i) && n.forEach((o) => { | ||
| try { | ||
| o(e); | ||
| } catch (a) { | ||
| this.emit(h.Error, a); | ||
| } catch (c) { | ||
| this.emit(u.Error, c); | ||
| } | ||
@@ -471,10 +539,2 @@ }); | ||
| } | ||
| isTopicMatch(e, t) { | ||
| if (e === t) | ||
| return !0; | ||
| if (!e.includes("*")) | ||
| return !1; | ||
| const n = "^" + e.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$"; | ||
| return new RegExp(n).test(t); | ||
| } | ||
| reSyncSubscriptions() { | ||
@@ -490,3 +550,3 @@ const e = this.currentConfig.subscription; | ||
| } catch (o) { | ||
| this.emit(h.Error, o); | ||
| this.emit(u.Error, o); | ||
| } | ||
@@ -507,3 +567,3 @@ }); | ||
| } catch (i) { | ||
| this.emit(h.Error, i); | ||
| this.emit(u.Error, i); | ||
| } | ||
@@ -515,3 +575,3 @@ } | ||
| if (this.reconnectAttempts >= this.currentConfig.maxReconnectAttempts) { | ||
| this.isOverMaxReconnectAttempts = !0, this.emit(h.OverMaxReconnectAttempts); | ||
| this.isOverMaxReconnectAttempts = !0, this.emit(u.OverMaxReconnectAttempts); | ||
| return; | ||
@@ -524,3 +584,3 @@ } | ||
| this.reconnectTimer = setTimeout(() => { | ||
| this.reconnectTimer = void 0, this.reconnectAttempts++, this.emit(h.Reconnect, { | ||
| this.reconnectTimer = void 0, this.reconnectAttempts++, this.emit(u.Reconnect, { | ||
| attempt: this.reconnectAttempts, | ||
@@ -532,7 +592,52 @@ delay: n | ||
| flushMessageQueue() { | ||
| var t; | ||
| const e = (t = this.currentConfig.offlineQueue) == null ? void 0 : t.messageTTL; | ||
| for (; this.messageQueue.length > 0; ) { | ||
| const { data: e, priority: t, needAck: i, resolve: n, reject: r } = this.messageQueue.shift(); | ||
| this.sendInternal(e, t, i).then(n).catch(r); | ||
| if (e !== void 0 && e > 0) { | ||
| const a = Date.now(), g = this.messageQueue[0]; | ||
| if (a - g.createdAt > e) { | ||
| this.messageQueue.shift().reject(new d(f.OfflineQueueTTLExpired)); | ||
| continue; | ||
| } | ||
| } | ||
| const { data: i, priority: n, needAck: r, resolve: o, reject: c } = this.messageQueue.shift(); | ||
| this.sendInternal(i, n, r).then(o).catch(c); | ||
| } | ||
| } | ||
| enqueueOfflineMessage(e) { | ||
| const t = this.currentConfig.offlineQueue; | ||
| if (!(t != null && t.enabled)) { | ||
| this.messageQueue.push(e); | ||
| return; | ||
| } | ||
| const i = Date.now(), n = t.messageTTL; | ||
| if (n !== void 0 && n > 0) | ||
| for (; this.messageQueue.length > 0; ) { | ||
| const c = this.messageQueue[0]; | ||
| if (i - c.createdAt <= n) | ||
| break; | ||
| this.messageQueue.shift().reject( | ||
| new d(f.OfflineQueueTTLExpired) | ||
| ); | ||
| } | ||
| const r = t.maxQueueSize ?? Number.POSITIVE_INFINITY; | ||
| if (r <= 0) { | ||
| e.reject(new d(f.OfflineQueueOverflow)); | ||
| return; | ||
| } | ||
| if (this.messageQueue.length < r) { | ||
| this.messageQueue.push(e); | ||
| return; | ||
| } | ||
| const o = t.dropStrategy ?? y.Reject; | ||
| if (o === y.Reject || o === y.DropNewest) { | ||
| e.reject(new d(f.OfflineQueueOverflow)); | ||
| return; | ||
| } | ||
| for (; this.messageQueue.length >= r; ) | ||
| this.messageQueue.shift().reject( | ||
| new d(f.OfflineQueueOverflow) | ||
| ); | ||
| this.messageQueue.push(e); | ||
| } | ||
| sendInternal(e, t, i) { | ||
@@ -542,16 +647,16 @@ var n; | ||
| const r = this.currentConfig.ack; | ||
| let o = null, a, c; | ||
| const f = i ? new Promise((l, u) => { | ||
| a = l, c = u; | ||
| }) : void 0, _ = this.scheduler.add(async () => { | ||
| var M, R; | ||
| let o = null, c, a; | ||
| const g = i ? new Promise((l, h) => { | ||
| c = l, a = h; | ||
| }) : void 0, T = this.scheduler.add(async () => { | ||
| var v, E; | ||
| let l = e; | ||
| const u = this.currentConfig.sequence; | ||
| if (u != null && u.enabled && typeof u.wrapOutbound == "function") { | ||
| const d = (M = u.generateSeq) == null ? void 0 : M.call(u); | ||
| d !== void 0 && (l = u.wrapOutbound(d, l)); | ||
| const h = this.currentConfig.sequence; | ||
| if (h != null && h.enabled && typeof h.wrapOutbound == "function") { | ||
| const m = (v = h.generateSeq) == null ? void 0 : v.call(h); | ||
| m !== void 0 && (l = h.wrapOutbound(m, l)); | ||
| } | ||
| if (i && (r != null && r.enabled)) { | ||
| const d = (R = r.generateId) == null ? void 0 : R.call(r); | ||
| d != null && typeof r.wrapOutbound == "function" && (o = d, l = r.wrapOutbound(d, l)); | ||
| const m = (E = r.generateId) == null ? void 0 : E.call(r); | ||
| m != null && typeof r.wrapOutbound == "function" && (o = m, l = r.wrapOutbound(m, l)); | ||
| } | ||
@@ -561,6 +666,6 @@ const S = this.currentConfig.serializer.serialize(l); | ||
| return; | ||
| const E = r.timeout ?? 5e3; | ||
| const L = r.timeout ?? 5e3; | ||
| this.pendingAcks.set(o, { | ||
| resolve: () => a == null ? void 0 : a(), | ||
| reject: (d) => c == null ? void 0 : c(d), | ||
| resolve: () => c == null ? void 0 : c(), | ||
| reject: (m) => a == null ? void 0 : a(m), | ||
| retries: 0, | ||
@@ -571,18 +676,26 @@ rawData: e, | ||
| this.handleAckTimeout(o); | ||
| }, E) | ||
| }, L) | ||
| }); | ||
| }, t); | ||
| return i ? _.catch((l) => { | ||
| return i ? T.catch((l) => { | ||
| if (o !== null) { | ||
| const u = this.pendingAcks.get(o); | ||
| u && (clearTimeout(u.timer), this.pendingAcks.delete(o)); | ||
| const h = this.pendingAcks.get(o); | ||
| h && (clearTimeout(h.timer), this.pendingAcks.delete(o)); | ||
| } | ||
| throw c == null || c(l), l; | ||
| throw a == null || a(l), l; | ||
| }).then(() => { | ||
| if (!(!(r != null && r.enabled) || o === null || !f)) | ||
| return f; | ||
| }) : _; | ||
| if (!(!(r != null && r.enabled) || o === null || !g)) | ||
| return g; | ||
| }) : T; | ||
| } | ||
| return new Promise((r, o) => { | ||
| this.messageQueue.push({ data: e, priority: t, needAck: i, resolve: r, reject: o }); | ||
| const c = Date.now(); | ||
| this.enqueueOfflineMessage({ | ||
| data: e, | ||
| priority: t, | ||
| needAck: i, | ||
| resolve: r, | ||
| reject: o, | ||
| createdAt: c | ||
| }); | ||
| }); | ||
@@ -593,3 +706,3 @@ } | ||
| if (!i || !(t != null && t.enabled)) { | ||
| i && (this.pendingAcks.delete(e), i.reject(new b(p.AckTimeout))); | ||
| i && (this.pendingAcks.delete(e), i.reject(new d(f.AckTimeout))); | ||
| return; | ||
@@ -602,3 +715,3 @@ } | ||
| this.handleAckTimeout(e); | ||
| }, n)) : (this.pendingAcks.delete(e), this.ackTimeoutCount += 1, i.reject(new b(p.AckMaxRetries))); | ||
| }, n)) : (this.pendingAcks.delete(e), this.ackTimeoutCount += 1, i.reject(new d(f.AckMaxRetries))); | ||
| } | ||
@@ -621,3 +734,3 @@ send(e, t = this.currentConfig.defaultPriority) { | ||
| const e = ((t = this.socket) == null ? void 0 : t.readyState) ?? null; | ||
| return this.isOverMaxReconnectAttempts ? m.OverMaxReconnectAttempts : this.reconnectTimer ? m.Reconnecting : e === WebSocket.OPEN ? m.Open : e === WebSocket.CONNECTING ? m.Connecting : m.Closed; | ||
| return this.isOverMaxReconnectAttempts ? p.OverMaxReconnectAttempts : this.reconnectTimer ? p.Reconnecting : e === WebSocket.OPEN ? p.Open : e === WebSocket.CONNECTING ? p.Connecting : p.Closed; | ||
| } | ||
@@ -650,22 +763,23 @@ getStats() { | ||
| resetStats(e = {}) { | ||
| const { | ||
| resetCounters: t = !0, | ||
| resetLastEvents: i = !0 | ||
| } = e; | ||
| const { resetCounters: t = !0, resetLastEvents: i = !0 } = e; | ||
| t && (this.sentCount = 0, this.receivedCount = 0, this.errorCount = 0, this.reconnectScheduledCount = 0, this.ackTimeoutCount = 0), i && (this.lastHeartbeatLatency = void 0, this.lastErrorAt = void 0, this.lastCloseCode = void 0, this.lastCloseReason = void 0, this.lastCloseAt = void 0); | ||
| } | ||
| // TODO: 支持批量订阅 | ||
| subscribe(e, t) { | ||
| var i, n; | ||
| if (!e) | ||
| var n, r; | ||
| if (Array.isArray(e)) { | ||
| const o = e.filter(Boolean).map((c) => this.subscribe(c, t)); | ||
| return () => o.forEach((c) => c()); | ||
| } | ||
| const i = e; | ||
| if (!i) | ||
| return () => { | ||
| }; | ||
| if (!this.topicListeners.has(e)) { | ||
| this.topicListeners.set(e, /* @__PURE__ */ new Set()); | ||
| const r = (n = (i = this.currentConfig.subscription) == null ? void 0 : i.buildSubscribeMessage) == null ? void 0 : n.call(i, e); | ||
| r !== void 0 && this.send(r).catch((o) => { | ||
| this.emit(h.Error, o); | ||
| if (!this.topicListeners.has(i)) { | ||
| this.topicListeners.set(i, /* @__PURE__ */ new Set()); | ||
| const o = (r = (n = this.currentConfig.subscription) == null ? void 0 : n.buildSubscribeMessage) == null ? void 0 : r.call(n, i); | ||
| o !== void 0 && this.send(o).catch((c) => { | ||
| this.emit(u.Error, c); | ||
| }); | ||
| } | ||
| return this.topicListeners.get(e).add(t), () => this.unsubscribe(e, t); | ||
| return this.topicListeners.get(i).add(t), () => this.unsubscribe(i, t); | ||
| } | ||
@@ -689,10 +803,16 @@ subscribeOnce(e, t) { | ||
| unsubscribe(e, t) { | ||
| var r, o; | ||
| const i = this.topicListeners.get(e); | ||
| if (!i || (t ? i.delete(t) : i.clear(), i.size > 0)) | ||
| var o, c; | ||
| if (Array.isArray(e)) { | ||
| e.filter(Boolean).forEach((a) => { | ||
| this.unsubscribe(a, t); | ||
| }); | ||
| return; | ||
| this.topicListeners.delete(e); | ||
| const n = (o = (r = this.currentConfig.subscription) == null ? void 0 : r.buildUnsubscribeMessage) == null ? void 0 : o.call(r, e); | ||
| n !== void 0 && this.send(n).catch((a) => { | ||
| this.emit(h.Error, a); | ||
| } | ||
| const i = e, n = this.topicListeners.get(i); | ||
| if (!n || (t ? n.delete(t) : n.clear(), n.size > 0)) | ||
| return; | ||
| this.topicListeners.delete(i); | ||
| const r = (c = (o = this.currentConfig.subscription) == null ? void 0 : o.buildUnsubscribeMessage) == null ? void 0 : c.call(o, i); | ||
| r !== void 0 && this.send(r).catch((a) => { | ||
| this.emit(u.Error, a); | ||
| }); | ||
@@ -702,8 +822,11 @@ } | ||
| var i; | ||
| this.isManualClose = !0, clearTimeout(this.reconnectTimer), this.reconnectTimer = void 0, this.heartbeat && this.heartbeat.stop(), this.pendingAcks.forEach((n, r) => { | ||
| clearTimeout(n.timer), n.reject(new b(p.ClosedBeforeAck)), this.pendingAcks.delete(r); | ||
| }), (i = this.socket) == null || i.close(e, t), this.socket = null; | ||
| if (this.isManualClose = !0, clearTimeout(this.reconnectTimer), this.reconnectTimer = void 0, this.heartbeat && this.heartbeat.stop(), this.pendingAcks.forEach((n, r) => { | ||
| clearTimeout(n.timer), n.reject(new d(f.ClosedBeforeAck)), this.pendingAcks.delete(r); | ||
| }), !this.isClosingForReconnect) | ||
| for (; this.messageQueue.length > 0; ) | ||
| this.messageQueue.shift().reject(new d(f.ClosedBeforeSend)); | ||
| (i = this.socket) == null || i.close(e, t), this.socket = null; | ||
| } | ||
| reconnect() { | ||
| clearTimeout(this.reconnectTimer), this.reconnectTimer = void 0, this.reconnectAttempts = 0, this.isOverMaxReconnectAttempts = !1, this.close(), this.isManualClose = !1, this.connect(); | ||
| clearTimeout(this.reconnectTimer), this.reconnectTimer = void 0, this.reconnectAttempts = 0, this.isOverMaxReconnectAttempts = !1, this.isClosingForReconnect = !0, this.close(), this.isManualClose = !1, this.isClosingForReconnect = !1, this.connect(); | ||
| } | ||
@@ -722,7 +845,7 @@ // 配置更新方法 | ||
| const t = { ...this.currentConfig }; | ||
| this.currentConfig = w(this.currentConfig, e), this.handleConfigChange(t, this.currentConfig), this.applyConfig(); | ||
| this.currentConfig = k(this.currentConfig, e), this.handleConfigChange(t, this.currentConfig), this.applyConfig(); | ||
| } | ||
| // 处理特定配置变更 | ||
| handleConfigChange(e, t) { | ||
| A(e.heartbeat, t.heartbeat) || this.reInitHeartbeat(), (e.maxReconnectAttempts !== t.maxReconnectAttempts || e.reconnectDelay !== t.reconnectDelay || e.reconnectExponent !== t.reconnectExponent || e.maxReconnectDelay !== t.maxReconnectDelay) && this.resetReconnectTimer(); | ||
| x(e.heartbeat, t.heartbeat) || this.reInitHeartbeat(), (e.maxReconnectAttempts !== t.maxReconnectAttempts || e.reconnectDelay !== t.reconnectDelay || e.reconnectExponent !== t.reconnectExponent || e.maxReconnectDelay !== t.maxReconnectDelay) && this.resetReconnectTimer(); | ||
| } | ||
@@ -746,3 +869,3 @@ // 应用新配置到各模块 | ||
| } | ||
| class H extends x { | ||
| class W extends R { | ||
| constructor(e) { | ||
@@ -755,8 +878,8 @@ super(), this.config = e, this.clients = /* @__PURE__ */ new Map(); | ||
| return this.clients.get(i); | ||
| const n = new j(e, t, this.config); | ||
| const n = new K(e, t, this.config); | ||
| this.clients.set(i, n); | ||
| const r = (o) => (a) => { | ||
| this.emit(o, { url: e, protocols: t, data: a }); | ||
| const r = (o) => (c) => { | ||
| this.emit(o, { url: e, protocols: t, data: c }); | ||
| }; | ||
| return O.forEach((o) => { | ||
| return I.forEach((o) => { | ||
| n.on(o, r(o)); | ||
@@ -773,27 +896,28 @@ }), n; | ||
| } | ||
| const U = (s = {}) => { | ||
| const e = w(P, s); | ||
| return new H(e); | ||
| }, K = { | ||
| const $ = (s = {}) => { | ||
| const e = k(O, s); | ||
| return new W(e); | ||
| }, G = { | ||
| serialize: JSON.stringify, | ||
| deserialize: JSON.parse | ||
| }, Q = { | ||
| }, J = { | ||
| serialize: (s) => { | ||
| throw new b(p.MsgPackNotInstalled); | ||
| throw new d(f.MsgPackNotInstalled); | ||
| }, | ||
| deserialize: (s) => { | ||
| throw new b(p.MsgPackNotInstalled); | ||
| throw new d(f.MsgPackNotInstalled); | ||
| } | ||
| }; | ||
| export { | ||
| x as EventEmitter, | ||
| T as HeartbeatEvent, | ||
| C as HeartbeatMessage, | ||
| g as HeartbeatTimerMode, | ||
| K as JsonSerializer, | ||
| Q as MsgPackSerializer, | ||
| j as WebSocketClient, | ||
| m as WebSocketClientState, | ||
| H as WebSocketManager, | ||
| U as createWebSocketManager | ||
| R as EventEmitter, | ||
| w as HeartbeatEvent, | ||
| A as HeartbeatMessage, | ||
| b as HeartbeatTimerMode, | ||
| G as JsonSerializer, | ||
| J as MsgPackSerializer, | ||
| y as OfflineQueueDropStrategy, | ||
| K as WebSocketClient, | ||
| p as WebSocketClientState, | ||
| W as WebSocketManager, | ||
| $ as createWebSocketManager | ||
| }; |
@@ -1,2 +0,2 @@ | ||
| (function(u,m){typeof exports=="object"&&typeof module<"u"?m(exports):typeof define=="function"&&define.amd?define(["exports"],m):(u=typeof globalThis<"u"?globalThis:u||self,m(u.WebSocketPro={}))})(this,function(u){"use strict";var m=typeof document<"u"?document.currentScript:null,C=(s=>(s.Ping="PING",s.Pong="PONG",s))(C||{}),b=(s=>(s.Timeout="timeout",s.Pong="pong",s))(b||{}),g=(s=>(s.Auto="auto",s.Main="main",s.Worker="worker",s))(g||{});const x={maxReconnectAttempts:10,reconnectDelay:1e3,reconnectExponent:1.5,maxReconnectDelay:3e4,connectionPoolSize:5,maxConcurrent:1,defaultPriority:1,enableCompression:!1,serializer:{serialize:JSON.stringify,deserialize:JSON.parse},ack:{enabled:!0,timeout:5e3,maxRetries:2,generateId:()=>{window.__ws_pro_client_ack_id__||(window.__ws_pro_client_ack_id__=1);const s=window.__ws_pro_client_ack_id__;return window.__ws_pro_client_ack_id__=s+1,s},wrapOutbound:(s,e)=>({id:s,payload:e}),extractAckId:s=>s&&typeof s=="object"&&"ackId"in s?s.ackId:null},sequence:{enabled:!0,generateSeq:()=>{window.__ws_pro_client_seq__||(window.__ws_pro_client_seq__=1);const s=window.__ws_pro_client_seq__;return window.__ws_pro_client_seq__=s+1,s},wrapOutbound:(s,e)=>({seq:s,payload:e}),extractInboundSeq:s=>s&&typeof s=="object"&&"seq"in s?s.seq:null},subscription:{extractTopic:s=>s&&typeof s=="object"&&"topic"in s&&typeof s.topic=="string"?s.topic:null,autoResubscribe:!0},isNeedHeartbeat:!0,heartbeat:{interval:25e3,timeout:45e3,pingMessage:C.Ping,pongMessage:C.Pong,timerMode:g.Auto}};var c=(s=>(s.Open="open",s.Message="message",s.Close="close",s.Error="error",s.Reconnect="reconnect",s.Heartbeat="heartbeat",s.Latency="latency",s.OverMaxReconnectAttempts="overMaxReconnectAttempts",s))(c||{});const I=["open","message","close","error","reconnect","heartbeat","latency","overMaxReconnectAttempts"];var p=(s=>(s.Connecting="connecting",s.Open="open",s.Reconnecting="reconnecting",s.Closed="closed",s.OverMaxReconnectAttempts="overMaxReconnectAttempts",s))(p||{});class N{constructor(e="[WebSocketPro]"){this.prefix=e}debug(...e){(typeof{url:typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:m&&m.tagName.toUpperCase()==="SCRIPT"&&m.src||new URL("websocket-pro.umd.js",document.baseURI).href}<"u"&&void 0||typeof process<"u"&&(process==null?void 0:process.env)&&process.env.NODE_ENV!=="production")&&console.debug(this.prefix,...e)}info(...e){console.info(this.prefix,...e)}warn(...e){console.warn(this.prefix,...e)}error(...e){console.error(this.prefix,...e)}}let D=new N;const k=()=>D;class A{constructor(){this.events={}}on(e,t){return this.events[e]||(this.events[e]=[]),this.events[e].push(t),()=>this.off(e,t)}off(e,t){this.events[e]&&(this.events[e]=this.events[e].filter(i=>i!==t))}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){k().error(`Event "${e}" listener error:`,n)}})}once(e,t){const i=(...n)=>{this.off(e,i),t(...n)};this.on(e,i)}removeAllListeners(e){e?delete this.events[e]:this.events={}}}class M{setTimeout(e,t){return globalThis.setTimeout(e,t)}clearTimeout(e){e!==void 0&&globalThis.clearTimeout(e)}}class q{constructor(){this.callbackMap=new Map,this.nextId=1,this.isAvailable=!1,this.worker=this.createWorker(),this.isAvailable=!!this.worker}get available(){return this.isAvailable}setTimeout(e,t){if(!this.worker||!this.isAvailable)return globalThis.setTimeout(e,t);const i=this.nextId++;this.callbackMap.set(i,e);try{this.worker.postMessage({type:"setTimeout",id:i,delay:t})}catch{return this.markUnavailable(),globalThis.setTimeout(e,t)}return i}clearTimeout(e){if(e!==void 0){if(!this.worker||!this.isAvailable){globalThis.clearTimeout(e);return}this.callbackMap.delete(e);try{this.worker.postMessage({type:"clearTimeout",id:e})}catch{this.markUnavailable()}}}destroy(){var e;this.callbackMap.clear();try{(e=this.worker)==null||e.terminate()}catch{}this.worker=void 0,this.isAvailable=!1}createWorker(){if(typeof Worker>"u"||typeof URL>"u"||typeof Blob>"u")return;const e=` | ||
| (function(u,b){typeof exports=="object"&&typeof module<"u"?b(exports):typeof define=="function"&&define.amd?define(["exports"],b):(u=typeof globalThis<"u"?globalThis:u||self,b(u.WebSocketPro={}))})(this,function(u){"use strict";var b=typeof document<"u"?document.currentScript:null,h=(s=>(s.Open="open",s.Message="message",s.Close="close",s.Error="error",s.Reconnect="reconnect",s.Heartbeat="heartbeat",s.Latency="latency",s.OverMaxReconnectAttempts="overMaxReconnectAttempts",s))(h||{});const D=["open","message","close","error","reconnect","heartbeat","latency","overMaxReconnectAttempts"];var w=(s=>(s.DropOldest="dropOldest",s.DropNewest="dropNewest",s.Reject="reject",s))(w||{}),T=(s=>(s.Connecting="connecting",s.Open="open",s.Reconnecting="reconnecting",s.Closed="closed",s.OverMaxReconnectAttempts="overMaxReconnectAttempts",s))(T||{}),k=(s=>(s.Ping="PING",s.Pong="PONG",s))(k||{}),C=(s=>(s.Timeout="timeout",s.Pong="pong",s))(C||{}),y=(s=>(s.Auto="auto",s.Main="main",s.Worker="worker",s))(y||{});const M={maxReconnectAttempts:10,reconnectDelay:1e3,reconnectExponent:1.5,maxReconnectDelay:3e4,connectionPoolSize:5,maxConcurrent:1,defaultPriority:1,enableCompression:!1,serializer:{serialize:JSON.stringify,deserialize:JSON.parse},ack:{enabled:!0,timeout:5e3,maxRetries:2,generateId:()=>{window.__ws_pro_client_ack_id__||(window.__ws_pro_client_ack_id__=1);const s=window.__ws_pro_client_ack_id__;return window.__ws_pro_client_ack_id__=s+1,s},wrapOutbound:(s,e)=>({id:s,payload:e}),extractAckId:s=>s&&typeof s=="object"&&"ackId"in s?s.ackId:null},sequence:{enabled:!0,generateSeq:()=>{window.__ws_pro_client_seq__||(window.__ws_pro_client_seq__=1);const s=window.__ws_pro_client_seq__;return window.__ws_pro_client_seq__=s+1,s},wrapOutbound:(s,e)=>({seq:s,payload:e}),extractInboundSeq:s=>s&&typeof s=="object"&&"seq"in s?s.seq:null},subscription:{extractTopic:s=>s&&typeof s=="object"&&"topic"in s&&typeof s.topic=="string"?s.topic:null,autoResubscribe:!0},offlineQueue:{enabled:!0,maxQueueSize:1e3,dropStrategy:w.DropOldest,messageTTL:void 0},isNeedHeartbeat:!0,heartbeat:{interval:25e3,timeout:45e3,pingMessage:k.Ping,pongMessage:k.Pong,timerMode:y.Auto}};class Q{constructor(e="[WebSocketPro]"){this.prefix=e}debug(...e){(typeof{url:typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:b&&b.tagName.toUpperCase()==="SCRIPT"&&b.src||new URL("websocket-pro.umd.js",document.baseURI).href}<"u"&&void 0||typeof process<"u"&&(process==null?void 0:process.env)&&process.env.NODE_ENV!=="production")&&console.debug(this.prefix,...e)}info(...e){console.info(this.prefix,...e)}warn(...e){console.warn(this.prefix,...e)}error(...e){console.error(this.prefix,...e)}}let j=new Q;const A=()=>j;class R{constructor(){this.events={}}on(e,t){return this.events[e]||(this.events[e]=[]),this.events[e].push(t),()=>this.off(e,t)}off(e,t){this.events[e]&&(this.events[e]=this.events[e].filter(i=>i!==t))}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){A().error(`Event "${e}" listener error:`,n)}})}once(e,t){const i=(...n)=>{this.off(e,i),t(...n)};this.on(e,i)}removeAllListeners(e){e?delete this.events[e]:this.events={}}}class v{setTimeout(e,t){return globalThis.setTimeout(e,t)}clearTimeout(e){e!==void 0&&globalThis.clearTimeout(e)}}class q{constructor(){this.callbackMap=new Map,this.nextId=1,this.isAvailable=!1,this.worker=this.createWorker(),this.isAvailable=!!this.worker}get available(){return this.isAvailable}setTimeout(e,t){if(!this.worker||!this.isAvailable)return globalThis.setTimeout(e,t);const i=this.nextId++;this.callbackMap.set(i,e);try{this.worker.postMessage({type:"setTimeout",id:i,delay:t})}catch{return this.markUnavailable(),globalThis.setTimeout(e,t)}return i}clearTimeout(e){if(e!==void 0){if(!this.worker||!this.isAvailable){globalThis.clearTimeout(e);return}this.callbackMap.delete(e);try{this.worker.postMessage({type:"clearTimeout",id:e})}catch{this.markUnavailable()}}}destroy(){var e;this.callbackMap.clear();try{(e=this.worker)==null||e.terminate()}catch{}this.worker=void 0,this.isAvailable=!1}createWorker(){if(typeof Worker>"u"||typeof URL>"u"||typeof Blob>"u")return;const e=` | ||
| const timers = new Map() | ||
@@ -21,2 +21,2 @@ self.onmessage = (e) => { | ||
| } | ||
| `;try{const t=new Blob([e],{type:"application/javascript"}),i=URL.createObjectURL(t),n=new Worker(i);return URL.revokeObjectURL(i),n.onmessage=r=>{const o=r.data||{};if(o.type!=="fire")return;const a=this.callbackMap.get(o.id);a&&(this.callbackMap.delete(o.id),a())},n.onerror=()=>{this.markUnavailable()},n}catch{return}}markUnavailable(){this.isAvailable=!1;const e=this.worker;this.worker=void 0;try{e==null||e.terminate()}catch{k().error("Heartbeat worker error")}}}function P(s){const e=s.timerMode??g.Auto;if(e===g.Main)return new M;const t=new q;return e===g.Worker&&!t.available?(k().warn("Heartbeat worker timer unavailable, fallback to main"),t.destroy(),new M):e===g.Auto&&!t.available?(t.destroy(),new M):t}class z extends A{constructor(e={},t){super(),this.config=e,this.sendPing=t,this.lastPongTime=0,this.lastPingTime=0,this.expectedNextPingAt=0,this.isRunning=!1,this.timer=P(this.config)}start(){this.config||k().warn("Heartbeat config is empty"),this.stop(),this.isRunning=!0;const e=Date.now();this.lastPongTime=e,this.lastPingTime=0,this.expectedNextPingAt=e+(this.config.interval??0),this.schedulePongTimeoutCheck(),this.scheduleNextPing()}stop(){this.isRunning=!1,this.timer.clearTimeout(this.pingTimer),this.timer.clearTimeout(this.pongTimeoutTimer)}handleDefaultTimeout(e){this.stop(),e&&e(),this.emit(b.Timeout)}recordPong(){const e=Date.now(),t=this.lastPingTime?e-this.lastPingTime:0;this.lastPongTime=e,this.timer.clearTimeout(this.pongTimeoutTimer),this.emit(b.Pong,t),this.schedulePongTimeoutCheck()}getLastPongTime(){return this.lastPongTime}checkTimeout(){if(!this.isRunning)return!1;const e=this.config.timeout??0;return e?Date.now()-this.lastPongTime>e:!1}updateConfig(e){var t,i,n,r;if(!(e!=null&&e.isNeedHeartbeat)){this.stop(),(i=(t=this.timer).destroy)==null||i.call(t);return}this.config={...this.config,...e.heartbeat},(r=(n=this.timer).destroy)==null||r.call(n),this.timer=P(this.config),this.stop(),this.start()}scheduleNextPing(){if(!this.isRunning)return;const e=this.config.interval??0,t=Date.now();(this.expectedNextPingAt<=0||t-this.expectedNextPingAt>e)&&(this.expectedNextPingAt=t+e);const i=Math.max(0,this.expectedNextPingAt-t);this.timer.clearTimeout(this.pingTimer),this.pingTimer=this.timer.setTimeout(()=>{this.isRunning&&(this.lastPingTime=Date.now(),this.sendPing(),this.expectedNextPingAt=this.expectedNextPingAt+e,this.scheduleNextPing())},i)}schedulePongTimeoutCheck(){if(!this.isRunning)return;const e=this.config.timeout??0;if(e<=0)return;this.timer.clearTimeout(this.pongTimeoutTimer);const t=this.lastPongTime+e,i=Math.max(0,t-Date.now());this.pongTimeoutTimer=this.timer.setTimeout(()=>{if(this.isRunning){if(this.checkTimeout()){this.handleDefaultTimeout(this.config.onTimeout);return}this.schedulePongTimeoutCheck()}},i)}}class j{constructor(e,t){this.maxConcurrent=e,this.onTaskError=t,this.queue=[],this.runningCount=0}add(e,t){return new Promise((i,n)=>{const r=async()=>{var o;try{await e(),i()}catch(a){(o=this.onTaskError)==null||o.call(this,a),n(a)}};this.queue.push({task:r,priority:t}),this.queue.sort((o,a)=>a.priority-o.priority),this.run()})}run(){for(;this.runningCount<this.maxConcurrent&&this.queue.length>0;){const{task:e}=this.queue.shift();this.runningCount++,e().finally(()=>{this.runningCount--,this.run()})}}clear(){this.queue=[]}updateThresholds(e){e!==void 0&&(this.maxConcurrent=e),this.run()}}const _=(s,e)=>{const t={...s};for(const i in e){const n=e[i];n&&typeof n=="object"&&!Array.isArray(n)?t[i]=_(s[i]||{},n):t[i]=n}return t},R=(s,e)=>{if(s===e)return!0;if(s==null||e==null||typeof s!="object"||typeof e!="object")return s===e;if(Array.isArray(s)&&Array.isArray(e)){if(s.length!==e.length)return!1;for(let n=0;n<s.length;n++)if(!R(s[n],e[n]))return!1;return!0}if(Array.isArray(s)||Array.isArray(e))return!1;const t=Object.keys(s),i=Object.keys(e);if(t.length!==i.length)return!1;for(const n of t)if(!e.hasOwnProperty(n)||!R(s[n],e[n]))return!1;return!0};var y=(s=>(s.MsgPackNotInstalled="MSG_PACK_NOT_INSTALLED",s.AckTimeout="ACK_TIMEOUT",s.AckMaxRetries="ACK_MAX_RETRIES",s.ClosedBeforeAck="CLOSED_BEFORE_ACK",s))(y||{});const H={MSG_PACK_NOT_INSTALLED:"MsgPack serializer requires @msgpack/msgpack installation",ACK_TIMEOUT:"ACK timeout",ACK_MAX_RETRIES:"ACK timeout, maximum retry attempts reached",CLOSED_BEFORE_ACK:"WebSocket connection closed before ACK was received"};class w extends Error{constructor(e){super(H[e]),this.code=e,this.name="WebSocketClientError"}}class v extends A{constructor(e,t,i){super(),this.url=e,this.protocols=t,this.config=i,this.socket=null,this.reconnectAttempts=0,this.isManualClose=!1,this.isOverMaxReconnectAttempts=!1,this.messageQueue=[],this.topicListeners=new Map,this.sentCount=0,this.receivedCount=0,this.errorCount=0,this.reconnectScheduledCount=0,this.ackTimeoutCount=0,this.pendingAcks=new Map,this.isUpdatingConfig=!1,this.configQueue=[],this.currentConfig=_(x,this.config),this.initHeartbeat(),this.scheduler=new j(this.currentConfig.maxConcurrent,n=>this.emit(c.Error,n)),this.connect()}initHeartbeat(){this.currentConfig.isNeedHeartbeat&&(this.heartbeat=new z(this.currentConfig.heartbeat,()=>{const e=this.currentConfig.heartbeat,t=typeof(e==null?void 0:e.getPing)=="function"?e.getPing():(e==null?void 0:e.pingMessage)??C.Ping;this.sendHeartbeat(t)}),this.heartbeat.on(b.Timeout,()=>{var e;k().warn("Heartbeat timeout, triggering reconnect..."),((e=this.socket)==null?void 0:e.readyState)===WebSocket.OPEN&&this.close(1e3,"heartbeat timeout"),this.scheduleReconnect()}),this.heartbeat.on(b.Pong,e=>{this.emit(c.Heartbeat,e),this.emit(c.Latency,e),this.lastHeartbeatLatency=e}))}connect(){this.isManualClose=!1,this.socket=new WebSocket(this.url,this.protocols),this.socket.binaryType="arraybuffer",this.socket.onopen=e=>{const t=this.reconnectAttempts>0;this.reconnectAttempts=0,this.isOverMaxReconnectAttempts=!1,clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0,this.heartbeat&&this.heartbeat.start(),this.flushMessageQueue(),t&&(this.reSyncSubscriptions(),this.emit(c.Reconnect)),this.emit(c.Open,e)},this.socket.onmessage=e=>{this.receivedCount+=1;const t=e.data;let i=t;try{i=this.currentConfig.serializer.deserialize(t)}catch{i=t}const n=this.currentConfig.heartbeat;if(typeof(n==null?void 0:n.isPong)=="function"?n.isPong(t,i):(n==null?void 0:n.pongMessage)!==void 0&&(t===n.pongMessage||i===n.pongMessage)){this.heartbeat&&this.heartbeat.recordPong();return}const o=this.currentConfig.ack;if(o!=null&&o.enabled&&typeof o.extractAckId=="function"){const h=o.extractAckId(i);if(h!=null){const T=this.pendingAcks.get(h);if(T){clearTimeout(T.timer),this.pendingAcks.delete(h),T.resolve();return}}}const a=this.currentConfig.sequence;if(a!=null&&a.enabled&&typeof a.extractInboundSeq=="function"){const h=a.extractInboundSeq(i);h!=null&&(this.lastInboundSeq=h)}this.emit(c.Message,i),this.dispatchSubscribedMessage(i)},this.socket.onclose=e=>{this.heartbeat&&this.heartbeat.stop(),this.lastCloseCode=e.code,this.lastCloseReason=e.reason,this.lastCloseAt=Date.now(),this.emit(c.Close,e),this.isManualClose||this.scheduleReconnect()},this.socket.onerror=e=>{this.heartbeat&&this.heartbeat.stop(),this.emit(c.Error,e),this.errorCount+=1,this.lastErrorAt=Date.now(),this.scheduleReconnect()}}sendRaw(e){var t;((t=this.socket)==null?void 0:t.readyState)===WebSocket.OPEN&&(this.socket.send(e),this.sentCount+=1)}dispatchSubscribedMessage(e){const t=this.currentConfig.subscription,i=typeof(t==null?void 0:t.extractTopic)=="function"?t.extractTopic(e):null;i&&this.topicListeners.size!==0&&this.topicListeners.forEach((n,r)=>{this.isTopicMatch(r,i)&&n.forEach(o=>{try{o(e)}catch(a){this.emit(c.Error,a)}})})}isTopicMatch(e,t){if(e===t)return!0;if(!e.includes("*"))return!1;const n="^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$";return new RegExp(n).test(t)}reSyncSubscriptions(){const e=this.currentConfig.subscription;e!=null&&e.autoResubscribe&&typeof e.buildSubscribeMessage=="function"&&this.topicListeners.forEach((t,i)=>{var r;const n=(r=e.buildSubscribeMessage)==null?void 0:r.call(e,i);if(n!==void 0)try{const o=this.currentConfig.serializer.serialize(n);this.sendRaw(o)}catch(o){this.emit(c.Error,o)}})}sendHeartbeat(e){var t;if(((t=this.socket)==null?void 0:t.readyState)===WebSocket.OPEN)try{const i=this.currentConfig.serializer.serialize(e);this.sendRaw(i)}catch(i){this.emit(c.Error,i)}}scheduleReconnect(){if(this.reconnectTimer)return;if(this.reconnectAttempts>=this.currentConfig.maxReconnectAttempts){this.isOverMaxReconnectAttempts=!0,this.emit(c.OverMaxReconnectAttempts);return}const e=Math.min(this.currentConfig.reconnectDelay*Math.pow(this.currentConfig.reconnectExponent,this.reconnectAttempts),this.currentConfig.maxReconnectDelay),i=e*.2*(Math.random()*2-1),n=Math.max(1e3,e+i);this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0,this.reconnectAttempts++,this.emit(c.Reconnect,{attempt:this.reconnectAttempts,delay:n}),this.connect()},n),this.reconnectScheduledCount+=1}flushMessageQueue(){for(;this.messageQueue.length>0;){const{data:e,priority:t,needAck:i,resolve:n,reject:r}=this.messageQueue.shift();this.sendInternal(e,t,i).then(n).catch(r)}}sendInternal(e,t,i){var n;if(((n=this.socket)==null?void 0:n.readyState)===WebSocket.OPEN){const r=this.currentConfig.ack;let o=null,a,h;const T=i?new Promise((d,l)=>{a=d,h=l}):void 0,E=this.scheduler.add(async()=>{var O,L;let d=e;const l=this.currentConfig.sequence;if(l!=null&&l.enabled&&typeof l.wrapOutbound=="function"){const f=(O=l.generateSeq)==null?void 0:O.call(l);f!==void 0&&(d=l.wrapOutbound(f,d))}if(i&&(r!=null&&r.enabled)){const f=(L=r.generateId)==null?void 0:L.call(r);f!=null&&typeof r.wrapOutbound=="function"&&(o=f,d=r.wrapOutbound(f,d))}const Q=this.currentConfig.serializer.serialize(d);if(this.sendRaw(Q),!i||!(r!=null&&r.enabled)||o===null)return;const B=r.timeout??5e3;this.pendingAcks.set(o,{resolve:()=>a==null?void 0:a(),reject:f=>h==null?void 0:h(f),retries:0,rawData:e,priority:t,timer:setTimeout(()=>{this.handleAckTimeout(o)},B)})},t);return i?E.catch(d=>{if(o!==null){const l=this.pendingAcks.get(o);l&&(clearTimeout(l.timer),this.pendingAcks.delete(o))}throw h==null||h(d),d}).then(()=>{if(!(!(r!=null&&r.enabled)||o===null||!T))return T}):E}return new Promise((r,o)=>{this.messageQueue.push({data:e,priority:t,needAck:i,resolve:r,reject:o})})}handleAckTimeout(e){const t=this.currentConfig.ack,i=this.pendingAcks.get(e);if(!i||!(t!=null&&t.enabled)){i&&(this.pendingAcks.delete(e),i.reject(new w(y.AckTimeout)));return}const n=t.timeout??5e3,r=t.maxRetries??0;i.retries<r?(i.retries+=1,this.sendInternal(i.rawData,i.priority,!1).catch(i.reject),i.timer=setTimeout(()=>{this.handleAckTimeout(e)},n)):(this.pendingAcks.delete(e),this.ackTimeoutCount+=1,i.reject(new w(y.AckMaxRetries)))}send(e,t=this.currentConfig.defaultPriority){return this.sendInternal(e,t,!1)}sendWithAck(e,t=this.currentConfig.defaultPriority){return this.sendInternal(e,t,!0)}getLastInboundSeq(){return this.lastInboundSeq}updateLastInboundSeq(e){this.lastInboundSeq=e}getState(){var t;const e=((t=this.socket)==null?void 0:t.readyState)??null;return this.isOverMaxReconnectAttempts?p.OverMaxReconnectAttempts:this.reconnectTimer?p.Reconnecting:e===WebSocket.OPEN?p.Open:e===WebSocket.CONNECTING?p.Connecting:p.Closed}getStats(){var t;let e=0;return this.topicListeners.forEach(i=>{e+=i.size}),{sentCount:this.sentCount,receivedCount:this.receivedCount,errorCount:this.errorCount,reconnectScheduledCount:this.reconnectScheduledCount,ackTimeoutCount:this.ackTimeoutCount,reconnectAttempts:this.reconnectAttempts,pendingAcksCount:this.pendingAcks.size,messageQueueLength:this.messageQueue.length,subscribedTopicCount:this.topicListeners.size,subscriptionListenerCount:e,lastInboundSeq:this.lastInboundSeq,socketReadyState:((t=this.socket)==null?void 0:t.readyState)??null,lastHeartbeatLatency:this.lastHeartbeatLatency,lastErrorAt:this.lastErrorAt,lastCloseCode:this.lastCloseCode,lastCloseReason:this.lastCloseReason,lastCloseAt:this.lastCloseAt}}resetStats(e={}){const{resetCounters:t=!0,resetLastEvents:i=!0}=e;t&&(this.sentCount=0,this.receivedCount=0,this.errorCount=0,this.reconnectScheduledCount=0,this.ackTimeoutCount=0),i&&(this.lastHeartbeatLatency=void 0,this.lastErrorAt=void 0,this.lastCloseCode=void 0,this.lastCloseReason=void 0,this.lastCloseAt=void 0)}subscribe(e,t){var i,n;if(!e)return()=>{};if(!this.topicListeners.has(e)){this.topicListeners.set(e,new Set);const r=(n=(i=this.currentConfig.subscription)==null?void 0:i.buildSubscribeMessage)==null?void 0:n.call(i,e);r!==void 0&&this.send(r).catch(o=>{this.emit(c.Error,o)})}return this.topicListeners.get(e).add(t),()=>this.unsubscribe(e,t)}subscribeOnce(e,t){let i=!1;const n=r=>{if(!i){i=!0;try{t(r)}finally{this.unsubscribe(e,n)}}};return this.subscribe(e,n),()=>{i||(i=!0,this.unsubscribe(e,n))}}unsubscribe(e,t){var r,o;const i=this.topicListeners.get(e);if(!i||(t?i.delete(t):i.clear(),i.size>0))return;this.topicListeners.delete(e);const n=(o=(r=this.currentConfig.subscription)==null?void 0:r.buildUnsubscribeMessage)==null?void 0:o.call(r,e);n!==void 0&&this.send(n).catch(a=>{this.emit(c.Error,a)})}close(e,t){var i;this.isManualClose=!0,clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0,this.heartbeat&&this.heartbeat.stop(),this.pendingAcks.forEach((n,r)=>{clearTimeout(n.timer),n.reject(new w(y.ClosedBeforeAck)),this.pendingAcks.delete(r)}),(i=this.socket)==null||i.close(e,t),this.socket=null}reconnect(){clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0,this.reconnectAttempts=0,this.isOverMaxReconnectAttempts=!1,this.close(),this.isManualClose=!1,this.connect()}async updateConfig(e){if(this.configQueue.push(e),!this.isUpdatingConfig){for(this.isUpdatingConfig=!0;this.configQueue.length>0;){const t=this.configQueue.shift();await this.applyConfigSafely(t)}this.isUpdatingConfig=!1}}applyConfigSafely(e){const t={...this.currentConfig};this.currentConfig=_(this.currentConfig,e),this.handleConfigChange(t,this.currentConfig),this.applyConfig()}handleConfigChange(e,t){R(e.heartbeat,t.heartbeat)||this.reInitHeartbeat(),(e.maxReconnectAttempts!==t.maxReconnectAttempts||e.reconnectDelay!==t.reconnectDelay||e.reconnectExponent!==t.reconnectExponent||e.maxReconnectDelay!==t.maxReconnectDelay)&&this.resetReconnectTimer()}applyConfig(){var e,t;(e=this.heartbeat)==null||e.updateConfig(this.currentConfig),(t=this.scheduler)==null||t.updateThresholds(this.currentConfig.maxConcurrent)}reInitHeartbeat(){var e;if(!this.currentConfig.isNeedHeartbeat){(e=this.heartbeat)==null||e.stop(),this.heartbeat=void 0;return}this.heartbeat?(this.heartbeat.stop(),this.heartbeat.start()):this.initHeartbeat()}resetReconnectTimer(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.scheduleReconnect())}}class S extends A{constructor(e){super(),this.config=e,this.clients=new Map}connect(e,t=[]){const i=`${e}|${t.join(",")}`;if(this.clients.has(i))return this.clients.get(i);const n=new v(e,t,this.config);this.clients.set(i,n);const r=o=>a=>{this.emit(o,{url:e,protocols:t,data:a})};return I.forEach(o=>{n.on(o,r(o))}),n}closeAll(e,t){this.clients.forEach(i=>i.close(e,t)),this.clients.clear()}getClient(e,t){const i=`${e}|${(t==null?void 0:t.join(","))||""}`;return this.clients.get(i)}}const U=(s={})=>{const e=_(x,s);return new S(e)},W={serialize:JSON.stringify,deserialize:JSON.parse},K={serialize:s=>{throw new w(y.MsgPackNotInstalled)},deserialize:s=>{throw new w(y.MsgPackNotInstalled)}};u.EventEmitter=A,u.HeartbeatEvent=b,u.HeartbeatMessage=C,u.HeartbeatTimerMode=g,u.JsonSerializer=W,u.MsgPackSerializer=K,u.WebSocketClient=v,u.WebSocketClientState=p,u.WebSocketManager=S,u.createWebSocketManager=U,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}); | ||
| `;try{const t=new Blob([e],{type:"application/javascript"}),i=URL.createObjectURL(t),n=new Worker(i);return URL.revokeObjectURL(i),n.onmessage=r=>{const o=r.data||{};if(o.type!=="fire")return;const c=this.callbackMap.get(o.id);c&&(this.callbackMap.delete(o.id),c())},n.onerror=()=>{this.markUnavailable()},n}catch{return}}markUnavailable(){this.isAvailable=!1;const e=this.worker;this.worker=void 0;try{e==null||e.terminate()}catch{A().error("Heartbeat worker error")}}}function S(s){const e=s.timerMode??y.Auto;if(e===y.Main)return new v;const t=new q;return e===y.Worker&&!t.available?(A().warn("Heartbeat worker timer unavailable, fallback to main"),t.destroy(),new v):e===y.Auto&&!t.available?(t.destroy(),new v):t}class z extends R{constructor(e={},t){super(),this.config=e,this.sendPing=t,this.lastPongTime=0,this.lastPingTime=0,this.expectedNextPingAt=0,this.isRunning=!1,this.timer=S(this.config)}start(){this.config||A().warn("Heartbeat config is empty"),this.stop(),this.isRunning=!0;const e=Date.now();this.lastPongTime=e,this.lastPingTime=0,this.expectedNextPingAt=e+(this.config.interval??0),this.schedulePongTimeoutCheck(),this.scheduleNextPing()}stop(){this.isRunning=!1,this.timer.clearTimeout(this.pingTimer),this.timer.clearTimeout(this.pongTimeoutTimer)}handleDefaultTimeout(e){this.stop(),e&&e(),this.emit(C.Timeout)}recordPong(){const e=Date.now(),t=this.lastPingTime?e-this.lastPingTime:0;this.lastPongTime=e,this.timer.clearTimeout(this.pongTimeoutTimer),this.emit(C.Pong,t),this.schedulePongTimeoutCheck()}getLastPongTime(){return this.lastPongTime}checkTimeout(){if(!this.isRunning)return!1;const e=this.config.timeout??0;return e?Date.now()-this.lastPongTime>e:!1}updateConfig(e){var t,i,n,r;if(!(e!=null&&e.isNeedHeartbeat)){this.stop(),(i=(t=this.timer).destroy)==null||i.call(t);return}this.config={...this.config,...e.heartbeat},(r=(n=this.timer).destroy)==null||r.call(n),this.timer=S(this.config),this.stop(),this.start()}scheduleNextPing(){if(!this.isRunning)return;const e=this.config.interval??0,t=Date.now();(this.expectedNextPingAt<=0||t-this.expectedNextPingAt>e)&&(this.expectedNextPingAt=t+e);const i=Math.max(0,this.expectedNextPingAt-t);this.timer.clearTimeout(this.pingTimer),this.pingTimer=this.timer.setTimeout(()=>{this.isRunning&&(this.lastPingTime=Date.now(),this.sendPing(),this.expectedNextPingAt=this.expectedNextPingAt+e,this.scheduleNextPing())},i)}schedulePongTimeoutCheck(){if(!this.isRunning)return;const e=this.config.timeout??0;if(e<=0)return;this.timer.clearTimeout(this.pongTimeoutTimer);const t=this.lastPongTime+e,i=Math.max(0,t-Date.now());this.pongTimeoutTimer=this.timer.setTimeout(()=>{if(this.isRunning){if(this.checkTimeout()){this.handleDefaultTimeout(this.config.onTimeout);return}this.schedulePongTimeoutCheck()}},i)}}class U{constructor(e,t){this.maxConcurrent=e,this.onTaskError=t,this.queue=[],this.runningCount=0}add(e,t){return new Promise((i,n)=>{const r=async()=>{var o;try{await e(),i()}catch(c){(o=this.onTaskError)==null||o.call(this,c),n(c)}};this.queue.push({task:r,priority:t}),this.queue.sort((o,c)=>c.priority-o.priority),this.run()})}run(){for(;this.runningCount<this.maxConcurrent&&this.queue.length>0;){const{task:e}=this.queue.shift();this.runningCount++,e().finally(()=>{this.runningCount--,this.run()})}}clear(){this.queue=[]}updateThresholds(e){e!==void 0&&(this.maxConcurrent=e),this.run()}}const P=new Map;function F(s,e){if(s===e)return!0;if(!(s.includes("*")||s.includes("?")||s.includes("{")))return!1;const i=P.get(s);if(i)return i.test(e);const n=H(s),r=new RegExp(n);return P.set(s,r),r.test(e)}function H(s){const e=i=>"\\^$+?.()|[\\]{}".includes(i)?`\\${i}`:i,t=i=>{let n="";for(let r=0;r<i.length;r+=1){const o=i[r];if(o==="*"){n+=".*";continue}if(o==="?"){n+=".";continue}if(o==="{"){const{content:c,endIndex:a}=B(i,r),_=W(c).map(d=>t(d)).join("|");n+=`(?:${_})`,r=a;continue}n+=e(o)}return n};return`^${t(s)}$`}function B(s,e){let t=0;for(let i=e;i<s.length;i+=1){const n=s[i];if(n==="{"&&(t+=1),n==="}"&&(t-=1),t===0){const r=i;return{content:s.slice(e+1,r),endIndex:r}}}return{content:s.slice(e+1),endIndex:s.length-1}}function W(s){const e=[];let t=0,i="";for(let n=0;n<s.length;n+=1){const r=s[n];if(r==="{"&&(t+=1),r==="}"&&(t-=1),r===","&&t===0){e.push(i),i="";continue}i+=r}return e.push(i),e}const x=(s,e)=>{const t={...s};for(const i in e){const n=e[i];n&&typeof n=="object"&&!Array.isArray(n)?t[i]=x(s[i]||{},n):t[i]=n}return t},E=(s,e)=>{if(s===e)return!0;if(s==null||e==null||typeof s!="object"||typeof e!="object")return s===e;if(Array.isArray(s)&&Array.isArray(e)){if(s.length!==e.length)return!1;for(let n=0;n<s.length;n++)if(!E(s[n],e[n]))return!1;return!0}if(Array.isArray(s)||Array.isArray(e))return!1;const t=Object.keys(s),i=Object.keys(e);if(t.length!==i.length)return!1;for(const n of t)if(!e.hasOwnProperty(n)||!E(s[n],e[n]))return!1;return!0};var f=(s=>(s.MsgPackNotInstalled="MSG_PACK_NOT_INSTALLED",s.AckTimeout="ACK_TIMEOUT",s.AckMaxRetries="ACK_MAX_RETRIES",s.ClosedBeforeAck="CLOSED_BEFORE_ACK",s.OfflineQueueOverflow="OFFLINE_QUEUE_OVERFLOW",s.OfflineQueueTTLExpired="OFFLINE_QUEUE_TTL_EXPIRED",s.ClosedBeforeSend="CLOSED_BEFORE_SEND",s))(f||{});const K={MSG_PACK_NOT_INSTALLED:"MsgPack serializer requires @msgpack/msgpack installation",ACK_TIMEOUT:"ACK timeout",ACK_MAX_RETRIES:"ACK timeout, maximum retry attempts reached",CLOSED_BEFORE_ACK:"WebSocket connection closed before ACK was received",OFFLINE_QUEUE_OVERFLOW:"Offline message queue overflow",OFFLINE_QUEUE_TTL_EXPIRED:"Offline message queue message TTL expired",CLOSED_BEFORE_SEND:"WebSocket connection closed before offline queued send"};class g extends Error{constructor(e){super(K[e]),this.code=e,this.name="WebSocketClientError"}}class O extends R{constructor(e,t,i){super(),this.url=e,this.protocols=t,this.config=i,this.socket=null,this.reconnectAttempts=0,this.isManualClose=!1,this.isOverMaxReconnectAttempts=!1,this.isClosingForReconnect=!1,this.messageQueue=[],this.topicListeners=new Map,this.sentCount=0,this.receivedCount=0,this.errorCount=0,this.reconnectScheduledCount=0,this.ackTimeoutCount=0,this.pendingAcks=new Map,this.isUpdatingConfig=!1,this.configQueue=[],this.currentConfig=x(M,this.config),this.initHeartbeat(),this.scheduler=new U(this.currentConfig.maxConcurrent,n=>this.emit(h.Error,n)),this.connect()}initHeartbeat(){this.currentConfig.isNeedHeartbeat&&(this.heartbeat=new z(this.currentConfig.heartbeat,()=>{const e=this.currentConfig.heartbeat,t=typeof(e==null?void 0:e.getPing)=="function"?e.getPing():(e==null?void 0:e.pingMessage)??k.Ping;this.sendHeartbeat(t)}),this.heartbeat.on(C.Timeout,()=>{var e;A().warn("Heartbeat timeout, triggering reconnect..."),((e=this.socket)==null?void 0:e.readyState)===WebSocket.OPEN&&this.close(1e3,"heartbeat timeout"),this.scheduleReconnect()}),this.heartbeat.on(C.Pong,e=>{this.emit(h.Heartbeat,e),this.emit(h.Latency,e),this.lastHeartbeatLatency=e}))}connect(){this.isManualClose=!1,this.socket=new WebSocket(this.url,this.protocols),this.socket.binaryType="arraybuffer",this.socket.onopen=e=>{const t=this.reconnectAttempts>0;this.reconnectAttempts=0,this.isOverMaxReconnectAttempts=!1,clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0,this.heartbeat&&this.heartbeat.start(),this.flushMessageQueue(),t&&(this.reSyncSubscriptions(),this.emit(h.Reconnect)),this.emit(h.Open,e)},this.socket.onmessage=e=>{this.receivedCount+=1;const t=e.data;let i=t;try{i=this.currentConfig.serializer.deserialize(t)}catch{i=t}const n=this.currentConfig.heartbeat;if(typeof(n==null?void 0:n.isPong)=="function"?n.isPong(t,i):(n==null?void 0:n.pongMessage)!==void 0&&(t===n.pongMessage||i===n.pongMessage)){this.heartbeat&&this.heartbeat.recordPong();return}const o=this.currentConfig.ack;if(o!=null&&o.enabled&&typeof o.extractAckId=="function"){const a=o.extractAckId(i);if(a!=null){const m=this.pendingAcks.get(a);if(m){clearTimeout(m.timer),this.pendingAcks.delete(a),m.resolve();return}}}const c=this.currentConfig.sequence;if(c!=null&&c.enabled&&typeof c.extractInboundSeq=="function"){const a=c.extractInboundSeq(i);a!=null&&(this.lastInboundSeq=a)}this.emit(h.Message,i),this.dispatchSubscribedMessage(i)},this.socket.onclose=e=>{this.heartbeat&&this.heartbeat.stop(),this.lastCloseCode=e.code,this.lastCloseReason=e.reason,this.lastCloseAt=Date.now(),this.emit(h.Close,e),this.isManualClose||this.scheduleReconnect()},this.socket.onerror=e=>{this.heartbeat&&this.heartbeat.stop(),this.emit(h.Error,e),this.errorCount+=1,this.lastErrorAt=Date.now(),this.scheduleReconnect()}}sendRaw(e){var t;((t=this.socket)==null?void 0:t.readyState)===WebSocket.OPEN&&(this.socket.send(e),this.sentCount+=1)}dispatchSubscribedMessage(e){const t=this.currentConfig.subscription,i=typeof(t==null?void 0:t.extractTopic)=="function"?t.extractTopic(e):null;i&&this.topicListeners.size!==0&&this.topicListeners.forEach((n,r)=>{F(r,i)&&n.forEach(o=>{try{o(e)}catch(c){this.emit(h.Error,c)}})})}reSyncSubscriptions(){const e=this.currentConfig.subscription;e!=null&&e.autoResubscribe&&typeof e.buildSubscribeMessage=="function"&&this.topicListeners.forEach((t,i)=>{var r;const n=(r=e.buildSubscribeMessage)==null?void 0:r.call(e,i);if(n!==void 0)try{const o=this.currentConfig.serializer.serialize(n);this.sendRaw(o)}catch(o){this.emit(h.Error,o)}})}sendHeartbeat(e){var t;if(((t=this.socket)==null?void 0:t.readyState)===WebSocket.OPEN)try{const i=this.currentConfig.serializer.serialize(e);this.sendRaw(i)}catch(i){this.emit(h.Error,i)}}scheduleReconnect(){if(this.reconnectTimer)return;if(this.reconnectAttempts>=this.currentConfig.maxReconnectAttempts){this.isOverMaxReconnectAttempts=!0,this.emit(h.OverMaxReconnectAttempts);return}const e=Math.min(this.currentConfig.reconnectDelay*Math.pow(this.currentConfig.reconnectExponent,this.reconnectAttempts),this.currentConfig.maxReconnectDelay),i=e*.2*(Math.random()*2-1),n=Math.max(1e3,e+i);this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0,this.reconnectAttempts++,this.emit(h.Reconnect,{attempt:this.reconnectAttempts,delay:n}),this.connect()},n),this.reconnectScheduledCount+=1}flushMessageQueue(){var t;const e=(t=this.currentConfig.offlineQueue)==null?void 0:t.messageTTL;for(;this.messageQueue.length>0;){if(e!==void 0&&e>0){const a=Date.now(),m=this.messageQueue[0];if(a-m.createdAt>e){this.messageQueue.shift().reject(new g(f.OfflineQueueTTLExpired));continue}}const{data:i,priority:n,needAck:r,resolve:o,reject:c}=this.messageQueue.shift();this.sendInternal(i,n,r).then(o).catch(c)}}enqueueOfflineMessage(e){const t=this.currentConfig.offlineQueue;if(!(t!=null&&t.enabled)){this.messageQueue.push(e);return}const i=Date.now(),n=t.messageTTL;if(n!==void 0&&n>0)for(;this.messageQueue.length>0;){const c=this.messageQueue[0];if(i-c.createdAt<=n)break;this.messageQueue.shift().reject(new g(f.OfflineQueueTTLExpired))}const r=t.maxQueueSize??Number.POSITIVE_INFINITY;if(r<=0){e.reject(new g(f.OfflineQueueOverflow));return}if(this.messageQueue.length<r){this.messageQueue.push(e);return}const o=t.dropStrategy??w.Reject;if(o===w.Reject||o===w.DropNewest){e.reject(new g(f.OfflineQueueOverflow));return}for(;this.messageQueue.length>=r;)this.messageQueue.shift().reject(new g(f.OfflineQueueOverflow));this.messageQueue.push(e)}sendInternal(e,t,i){var n;if(((n=this.socket)==null?void 0:n.readyState)===WebSocket.OPEN){const r=this.currentConfig.ack;let o=null,c,a;const m=i?new Promise((d,l)=>{c=d,a=l}):void 0,_=this.scheduler.add(async()=>{var I,N;let d=e;const l=this.currentConfig.sequence;if(l!=null&&l.enabled&&typeof l.wrapOutbound=="function"){const p=(I=l.generateSeq)==null?void 0:I.call(l);p!==void 0&&(d=l.wrapOutbound(p,d))}if(i&&(r!=null&&r.enabled)){const p=(N=r.generateId)==null?void 0:N.call(r);p!=null&&typeof r.wrapOutbound=="function"&&(o=p,d=r.wrapOutbound(p,d))}const V=this.currentConfig.serializer.serialize(d);if(this.sendRaw(V),!i||!(r!=null&&r.enabled)||o===null)return;const X=r.timeout??5e3;this.pendingAcks.set(o,{resolve:()=>c==null?void 0:c(),reject:p=>a==null?void 0:a(p),retries:0,rawData:e,priority:t,timer:setTimeout(()=>{this.handleAckTimeout(o)},X)})},t);return i?_.catch(d=>{if(o!==null){const l=this.pendingAcks.get(o);l&&(clearTimeout(l.timer),this.pendingAcks.delete(o))}throw a==null||a(d),d}).then(()=>{if(!(!(r!=null&&r.enabled)||o===null||!m))return m}):_}return new Promise((r,o)=>{const c=Date.now();this.enqueueOfflineMessage({data:e,priority:t,needAck:i,resolve:r,reject:o,createdAt:c})})}handleAckTimeout(e){const t=this.currentConfig.ack,i=this.pendingAcks.get(e);if(!i||!(t!=null&&t.enabled)){i&&(this.pendingAcks.delete(e),i.reject(new g(f.AckTimeout)));return}const n=t.timeout??5e3,r=t.maxRetries??0;i.retries<r?(i.retries+=1,this.sendInternal(i.rawData,i.priority,!1).catch(i.reject),i.timer=setTimeout(()=>{this.handleAckTimeout(e)},n)):(this.pendingAcks.delete(e),this.ackTimeoutCount+=1,i.reject(new g(f.AckMaxRetries)))}send(e,t=this.currentConfig.defaultPriority){return this.sendInternal(e,t,!1)}sendWithAck(e,t=this.currentConfig.defaultPriority){return this.sendInternal(e,t,!0)}getLastInboundSeq(){return this.lastInboundSeq}updateLastInboundSeq(e){this.lastInboundSeq=e}getState(){var t;const e=((t=this.socket)==null?void 0:t.readyState)??null;return this.isOverMaxReconnectAttempts?T.OverMaxReconnectAttempts:this.reconnectTimer?T.Reconnecting:e===WebSocket.OPEN?T.Open:e===WebSocket.CONNECTING?T.Connecting:T.Closed}getStats(){var t;let e=0;return this.topicListeners.forEach(i=>{e+=i.size}),{sentCount:this.sentCount,receivedCount:this.receivedCount,errorCount:this.errorCount,reconnectScheduledCount:this.reconnectScheduledCount,ackTimeoutCount:this.ackTimeoutCount,reconnectAttempts:this.reconnectAttempts,pendingAcksCount:this.pendingAcks.size,messageQueueLength:this.messageQueue.length,subscribedTopicCount:this.topicListeners.size,subscriptionListenerCount:e,lastInboundSeq:this.lastInboundSeq,socketReadyState:((t=this.socket)==null?void 0:t.readyState)??null,lastHeartbeatLatency:this.lastHeartbeatLatency,lastErrorAt:this.lastErrorAt,lastCloseCode:this.lastCloseCode,lastCloseReason:this.lastCloseReason,lastCloseAt:this.lastCloseAt}}resetStats(e={}){const{resetCounters:t=!0,resetLastEvents:i=!0}=e;t&&(this.sentCount=0,this.receivedCount=0,this.errorCount=0,this.reconnectScheduledCount=0,this.ackTimeoutCount=0),i&&(this.lastHeartbeatLatency=void 0,this.lastErrorAt=void 0,this.lastCloseCode=void 0,this.lastCloseReason=void 0,this.lastCloseAt=void 0)}subscribe(e,t){var n,r;if(Array.isArray(e)){const o=e.filter(Boolean).map(c=>this.subscribe(c,t));return()=>o.forEach(c=>c())}const i=e;if(!i)return()=>{};if(!this.topicListeners.has(i)){this.topicListeners.set(i,new Set);const o=(r=(n=this.currentConfig.subscription)==null?void 0:n.buildSubscribeMessage)==null?void 0:r.call(n,i);o!==void 0&&this.send(o).catch(c=>{this.emit(h.Error,c)})}return this.topicListeners.get(i).add(t),()=>this.unsubscribe(i,t)}subscribeOnce(e,t){let i=!1;const n=r=>{if(!i){i=!0;try{t(r)}finally{this.unsubscribe(e,n)}}};return this.subscribe(e,n),()=>{i||(i=!0,this.unsubscribe(e,n))}}unsubscribe(e,t){var o,c;if(Array.isArray(e)){e.filter(Boolean).forEach(a=>{this.unsubscribe(a,t)});return}const i=e,n=this.topicListeners.get(i);if(!n||(t?n.delete(t):n.clear(),n.size>0))return;this.topicListeners.delete(i);const r=(c=(o=this.currentConfig.subscription)==null?void 0:o.buildUnsubscribeMessage)==null?void 0:c.call(o,i);r!==void 0&&this.send(r).catch(a=>{this.emit(h.Error,a)})}close(e,t){var i;if(this.isManualClose=!0,clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0,this.heartbeat&&this.heartbeat.stop(),this.pendingAcks.forEach((n,r)=>{clearTimeout(n.timer),n.reject(new g(f.ClosedBeforeAck)),this.pendingAcks.delete(r)}),!this.isClosingForReconnect)for(;this.messageQueue.length>0;)this.messageQueue.shift().reject(new g(f.ClosedBeforeSend));(i=this.socket)==null||i.close(e,t),this.socket=null}reconnect(){clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0,this.reconnectAttempts=0,this.isOverMaxReconnectAttempts=!1,this.isClosingForReconnect=!0,this.close(),this.isManualClose=!1,this.isClosingForReconnect=!1,this.connect()}async updateConfig(e){if(this.configQueue.push(e),!this.isUpdatingConfig){for(this.isUpdatingConfig=!0;this.configQueue.length>0;){const t=this.configQueue.shift();await this.applyConfigSafely(t)}this.isUpdatingConfig=!1}}applyConfigSafely(e){const t={...this.currentConfig};this.currentConfig=x(this.currentConfig,e),this.handleConfigChange(t,this.currentConfig),this.applyConfig()}handleConfigChange(e,t){E(e.heartbeat,t.heartbeat)||this.reInitHeartbeat(),(e.maxReconnectAttempts!==t.maxReconnectAttempts||e.reconnectDelay!==t.reconnectDelay||e.reconnectExponent!==t.reconnectExponent||e.maxReconnectDelay!==t.maxReconnectDelay)&&this.resetReconnectTimer()}applyConfig(){var e,t;(e=this.heartbeat)==null||e.updateConfig(this.currentConfig),(t=this.scheduler)==null||t.updateThresholds(this.currentConfig.maxConcurrent)}reInitHeartbeat(){var e;if(!this.currentConfig.isNeedHeartbeat){(e=this.heartbeat)==null||e.stop(),this.heartbeat=void 0;return}this.heartbeat?(this.heartbeat.stop(),this.heartbeat.start()):this.initHeartbeat()}resetReconnectTimer(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.scheduleReconnect())}}class L extends R{constructor(e){super(),this.config=e,this.clients=new Map}connect(e,t=[]){const i=`${e}|${t.join(",")}`;if(this.clients.has(i))return this.clients.get(i);const n=new O(e,t,this.config);this.clients.set(i,n);const r=o=>c=>{this.emit(o,{url:e,protocols:t,data:c})};return D.forEach(o=>{n.on(o,r(o))}),n}closeAll(e,t){this.clients.forEach(i=>i.close(e,t)),this.clients.clear()}getClient(e,t){const i=`${e}|${(t==null?void 0:t.join(","))||""}`;return this.clients.get(i)}}const $=(s={})=>{const e=x(M,s);return new L(e)},G={serialize:JSON.stringify,deserialize:JSON.parse},J={serialize:s=>{throw new g(f.MsgPackNotInstalled)},deserialize:s=>{throw new g(f.MsgPackNotInstalled)}};u.EventEmitter=R,u.HeartbeatEvent=C,u.HeartbeatMessage=k,u.HeartbeatTimerMode=y,u.JsonSerializer=G,u.MsgPackSerializer=J,u.OfflineQueueDropStrategy=w,u.WebSocketClient=O,u.WebSocketClientState=T,u.WebSocketManager=L,u.createWebSocketManager=$,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}); |
+1
-1
| { | ||
| "name": "websocket-pro-client", | ||
| "version": "1.1.0", | ||
| "version": "1.2.0-beta.1", | ||
| "description": "High-performance WebSocket client with auto-reconnect, heartbeat and priority messaging", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+30
-1
@@ -6,3 +6,3 @@ ## WebSocket Pro Client | ||
| 高性能 WebSocket 客户端,专为现代 Web 应用设计,内置自动重连、心跳、消息优先级调度、连接池管理,以及**可配置的消息 ACK 与序列号机制**。 | ||
| 面向现代 Web 应用的高性能 WebSocket 客户端:开箱即用的自动重连与心跳保活,灵活可配的 ACK/序列号与主题订阅(支持通配符、自动重订阅),再加上离线队列策略与可观测状态统计,让你的实时通信在弱网、抖动和复杂业务场景下依然稳定、可控、易扩展。 | ||
@@ -19,2 +19,3 @@ ### 特性一览 | ||
| - 🧭 消息主题/类型订阅(支持通配符(`order.*`)+ 自动重订阅) | ||
| - 📴 离线消息队列 | ||
| - 📊 运行状态与统计 | ||
@@ -200,2 +201,5 @@ - 🔍 完整 TypeScript 类型定义 | ||
| - `order.*` 匹配 `order.created` / `order.updated` 等任意后缀 | ||
| - 也支持: | ||
| - `?`:匹配任意单个字符(如 `order.updat?d`) | ||
| - `{a,b}`:匹配多个备选(如 `order.{created,updated}`) | ||
| - 默认会通过 `subscription.extractTopic` 从入站消息提取 topic(默认读取 `message.topic`)并分发到对应 listener。 | ||
@@ -266,2 +270,5 @@ - 如果配置了 `subscription.buildSubscribeMessage`,会在首次订阅 topic 时发送订阅报文。 | ||
| subscription?: SubscriptionStrategy | ||
| // 离线消息队列 | ||
| offlineQueue?: OfflineQueueConfig | ||
| } | ||
@@ -423,2 +430,24 @@ ``` | ||
| ### 5. 离线消息队列配置 OfflineQueueConfig | ||
| ```ts | ||
| export type OfflineQueueConfig = { | ||
| enabled?: boolean | ||
| maxQueueSize?: number | ||
| dropStrategy?: OfflineQueueDropStrategy | ||
| messageTTL?: number | ||
| } | ||
| ``` | ||
| 当底层 `WebSocket` 还没进入 `OPEN` 状态时(断线/重连中/初始化阶段),`send/sendWithAck` 会把消息暂存到离线队列;当连接恢复后会按队列顺序发送。 | ||
| 队列行为: | ||
| - `maxQueueSize`:队列容量上限(默认 `1000`),超过后按 `dropStrategy` 处理 | ||
| - `dropStrategy`: | ||
| - `OfflineQueueDropStrategy.DropOldest`:丢弃队列最老消息,保留新消息 | ||
| - `OfflineQueueDropStrategy.DropNewest`:丢弃新消息 | ||
| - `OfflineQueueDropStrategy.Reject`:丢弃并立即 reject 新消息 Promise | ||
| - `messageTTL`(毫秒):过期消息会被移除并 reject(错误码 `OfflineQueueTTLExpired`) | ||
| --- | ||
@@ -425,0 +454,0 @@ |
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
92631
11.4%21
5%1528
13.94%545
5.62%3
200%