🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

websocket-pro-client

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

websocket-pro-client - npm Package Compare versions

Comparing version
1.1.0
to
1.2.0-beta.1
+7
dist/utils/topicPattern.d.ts
/**
* topicPattern 匹配
* - `*`:任意长度任意字符
* - `?`:任意单个字符
* - `{a,b}`:多备选
*/
export declare function matchTopicPattern(pattern: string, topic: string): boolean;
+4
-1

@@ -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;

@@ -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;

+1
-0
export declare const deepMerge: (target: any, source: any) => any;
export declare const isEqual: (target: any, source: any) => boolean;
export { matchTopicPattern } from './topicPattern';

@@ -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"})});
{
"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": [

@@ -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 @@