@camera.ui/transport 0.0.30 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,10 @@
1
+ import { t as classifyClose } from "../closeCodes-CltSRjdJ.js";
1
2
  import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-dt1m8U-o.js";
2
- import { t as classifyClose } from "../closeCodes-ClWJVOpq.js";
3
- import { Manager, io } from "socket.io-client";
3
+ import { Manager } from "socket.io-client";
4
4
  //#region src/transports/socketio.ts
5
5
  var RESOLVE_RETRY_MIN_MS = 3e3;
6
+ var PROBE_TIMEOUT_MS = 3e3;
7
+ var RELEASE_GRACE_MS = 1e4;
6
8
  var SOCKETIO_SPEC = {
7
9
  id: "socketio",
8
10
  kind: "persistent",
@@ -22,17 +24,23 @@ function createSocketioTransport(options = {}) {
22
24
  const logger = options.logger;
23
25
  const resolveQuery = options.resolveQuery;
24
26
  const resolveRefreshMs = options.resolveRefreshMs ?? 0;
27
+ const releaseGraceMs = options.releaseGraceMs ?? RELEASE_GRACE_MS;
25
28
  const emitter = new TransportEmitter();
26
29
  const sockets = /* @__PURE__ */ new Map();
30
+ const reopen = /* @__PURE__ */ new Set();
31
+ const rebuildListeners = /* @__PURE__ */ new Set();
32
+ const holds = /* @__PURE__ */ new Map();
33
+ const unheld = /* @__PURE__ */ new Set();
34
+ const releaseTimers = /* @__PURE__ */ new Map();
27
35
  let manager = null;
28
36
  let currentTarget = null;
29
- let currentQuery = {};
30
37
  let refreshTimer = null;
31
38
  let queryRefreshAt = 0;
32
39
  let queryRefreshing = false;
33
40
  let status = { up: false };
34
41
  let disposed = false;
35
42
  let applyEpoch = 0;
43
+ let ensureInflight = null;
36
44
  const staleAfterMs = options.staleAfterMs ?? 6e4;
37
45
  let lastActivityAt = 0;
38
46
  function touchActivity() {
@@ -41,18 +49,16 @@ function createSocketioTransport(options = {}) {
41
49
  function isStaleNow() {
42
50
  return lastActivityAt > 0 && Date.now() - lastActivityAt > staleAfterMs;
43
51
  }
44
- function buildAuth(target) {
45
- const auth = { token: `Bearer ${target.tokens.access}` };
46
- if (target.tokens.proxySession) auth.session = target.tokens.proxySession;
47
- return auth;
52
+ function isDeadNow() {
53
+ const deadline = (manager?.engine)?._pingTimeoutTime;
54
+ if (typeof deadline === "number") return Date.now() > deadline;
55
+ return isStaleNow();
48
56
  }
49
- function buildQuery(target) {
50
- const q = {};
51
- if (target.tokens.proxySession) q.session = target.tokens.proxySession;
52
- return q;
57
+ function buildAuth(target) {
58
+ return { token: `Bearer ${target.tokens.access}` };
53
59
  }
54
60
  async function queryFor(target) {
55
- const base = buildQuery(target);
61
+ const base = {};
56
62
  if (!resolveQuery) return base;
57
63
  return resolveQuery({
58
64
  target,
@@ -68,8 +74,7 @@ function createSocketioTransport(options = {}) {
68
74
  try {
69
75
  const query = await queryFor(target);
70
76
  if (currentTarget !== target || disposed) return;
71
- currentQuery = query;
72
- for (const sock of sockets.values()) sock.io.opts.query = query;
77
+ if (manager) manager.opts.query = query;
73
78
  } catch (err) {
74
79
  logger?.warn(`query refresh failed: ${err instanceof Error ? err.message : String(err)}`);
75
80
  } finally {
@@ -77,6 +82,14 @@ function createSocketioTransport(options = {}) {
77
82
  queryRefreshAt = Date.now();
78
83
  }
79
84
  }
85
+ async function refreshQueryWithin(ms) {
86
+ let timer;
87
+ try {
88
+ await Promise.race([refreshQuery(), new Promise((resolve) => timer = setTimeout(resolve, ms))]);
89
+ } finally {
90
+ clearTimeout(timer);
91
+ }
92
+ }
80
93
  function stopRefresh() {
81
94
  if (refreshTimer) {
82
95
  clearInterval(refreshTimer);
@@ -103,12 +116,20 @@ function createSocketioTransport(options = {}) {
103
116
  logger?.debug(`markDown (${reason})`);
104
117
  emitter.emit("down", { reason });
105
118
  }
106
- function isAuthError(msg) {
107
- return classifyClose(msg) === "auth-expired";
119
+ function isAuthError(err) {
120
+ return classifyClose({
121
+ message: err?.message,
122
+ data: err?.data
123
+ }) === "auth-expired";
124
+ }
125
+ function dialedAccess(socket) {
126
+ const token = socket.auth?.token;
127
+ return typeof token === "string" && token.startsWith("Bearer ") ? token.slice(7) : void 0;
108
128
  }
109
129
  function bindMainSocketEvents(socket) {
110
130
  socket.on("connect", () => markUp());
111
131
  socket.on("disconnect", (reason) => {
132
+ if (sockets.get(mainNs) !== socket) return;
112
133
  markDown(reason);
113
134
  if (resolveQuery) refreshQuery();
114
135
  });
@@ -116,8 +137,11 @@ function createSocketioTransport(options = {}) {
116
137
  const msg = err?.message ?? "connect_error";
117
138
  logger?.debug(`connect_error (main): ${msg}`);
118
139
  if (resolveQuery) refreshQuery();
119
- if (isAuthError(msg)) {
120
- emitter.emit("auth-error", { message: msg });
140
+ if (isAuthError(err)) {
141
+ emitter.emit("auth-error", {
142
+ message: msg,
143
+ access: dialedAccess(socket)
144
+ });
121
145
  return;
122
146
  }
123
147
  markDown(msg);
@@ -125,8 +149,10 @@ function createSocketioTransport(options = {}) {
125
149
  }
126
150
  function bindSecondarySocketEvents(socket) {
127
151
  socket.on("connect_error", (err) => {
128
- const msg = err?.message ?? "connect_error";
129
- if (isAuthError(msg)) emitter.emit("auth-error", { message: msg });
152
+ if (isAuthError(err)) emitter.emit("auth-error", {
153
+ message: err?.message ?? "connect_error",
154
+ access: dialedAccess(socket)
155
+ });
130
156
  });
131
157
  }
132
158
  function socketOrigin(target) {
@@ -135,24 +161,17 @@ function createSocketioTransport(options = {}) {
135
161
  function socketPath(target) {
136
162
  return `${new URL(target.endpoint.url).pathname.replace(/\/$/, "")}${path}`;
137
163
  }
138
- function openSocket(namespace, target) {
139
- const url = `${socketOrigin(target)}${namespace}`;
140
- const sock = io(url, {
141
- path: socketPath(target),
142
- auth: buildAuth(target),
143
- query: currentQuery,
144
- reconnection,
145
- reconnectionDelay,
146
- reconnectionDelayMax,
147
- timeout,
148
- rejectUnauthorized: false,
149
- transports: ["websocket"]
150
- });
164
+ function openSocket(namespace, mgr, target) {
165
+ const auth = buildAuth(target);
166
+ const sock = mgr.socket(namespace, { auth });
167
+ sock.auth = auth;
151
168
  sockets.set(namespace, sock);
169
+ if (namespace === mainNs) bindMainSocketEvents(sock);
170
+ else bindSecondarySocketEvents(sock);
171
+ sock.connect();
152
172
  return sock;
153
173
  }
154
- async function rebuildManager(target) {
155
- stopRefresh();
174
+ async function rebuildManager(target, epoch) {
156
175
  let query;
157
176
  try {
158
177
  query = await queryFor(target);
@@ -160,11 +179,10 @@ function createSocketioTransport(options = {}) {
160
179
  markDown(err instanceof Error ? err.message : String(err));
161
180
  throw err;
162
181
  }
163
- if (currentTarget !== target || disposed) return;
164
- closeAllSockets();
165
- currentQuery = query;
166
- manager = new Manager(socketOrigin(target), {
182
+ if (epoch !== applyEpoch || disposed) return;
183
+ const mgr = new Manager(socketOrigin(target), {
167
184
  path: socketPath(target),
185
+ query,
168
186
  autoConnect: false,
169
187
  reconnection,
170
188
  reconnectionDelay,
@@ -173,24 +191,31 @@ function createSocketioTransport(options = {}) {
173
191
  rejectUnauthorized: false,
174
192
  transports: ["websocket"]
175
193
  });
176
- const main = openSocket(mainNs, target);
177
- bindMainSocketEvents(main);
178
- startRefresh();
194
+ manager = mgr;
179
195
  lastActivityAt = Date.now();
180
- main.io.on("open", touchActivity);
181
- main.io.on("ping", touchActivity);
182
- main.io.on("packet", touchActivity);
196
+ mgr.on("open", touchActivity);
197
+ mgr.on("ping", touchActivity);
198
+ mgr.on("packet", touchActivity);
199
+ openSocket(mainNs, mgr, target);
200
+ for (const ns of reopen) if (!sockets.has(ns)) openSocket(ns, mgr, target);
201
+ reopen.clear();
202
+ startRefresh();
203
+ for (const handler of [...rebuildListeners]) try {
204
+ handler();
205
+ } catch (err) {
206
+ logger?.warn(`rebuild listener failed: ${err instanceof Error ? err.message : String(err)}`);
207
+ }
183
208
  }
184
209
  function closeAllSockets() {
185
- for (const sock of sockets.values()) {
186
- sock.removeAllListeners();
187
- sock.disconnect();
188
- }
210
+ const closing = [...sockets.values()];
211
+ const mgr = manager;
189
212
  sockets.clear();
190
- if (manager) {
191
- manager._close();
192
- manager = null;
213
+ manager = null;
214
+ for (const sock of closing) {
215
+ sock.disconnect();
216
+ sock.removeAllListeners();
193
217
  }
218
+ mgr?._close();
194
219
  }
195
220
  function rebindAuth(target) {
196
221
  const auth = buildAuth(target);
@@ -205,7 +230,9 @@ function createSocketioTransport(options = {}) {
205
230
  const endpointChanged = isEndpointChange(currentTarget, target);
206
231
  currentTarget = target;
207
232
  if (!target) {
233
+ applyEpoch++;
208
234
  closeAllSockets();
235
+ reopen.clear();
209
236
  stopRefresh();
210
237
  lastActivityAt = 0;
211
238
  markDown("detached");
@@ -213,8 +240,11 @@ function createSocketioTransport(options = {}) {
213
240
  }
214
241
  if (endpointChanged || !manager) {
215
242
  const epoch = ++applyEpoch;
243
+ for (const ns of sockets.keys()) if (ns !== mainNs) reopen.add(ns);
244
+ closeAllSockets();
245
+ stopRefresh();
216
246
  try {
217
- await rebuildManager(target);
247
+ await rebuildManager(target, epoch);
218
248
  } catch (err) {
219
249
  if (epoch === applyEpoch && currentTarget === target) currentTarget = null;
220
250
  throw err;
@@ -224,7 +254,7 @@ function createSocketioTransport(options = {}) {
224
254
  rebindAuth(target);
225
255
  }
226
256
  function health() {
227
- if (status.up && isStaleNow()) return {
257
+ if (status.up && isDeadNow()) return {
228
258
  up: false,
229
259
  lastError: "staleConnection"
230
260
  };
@@ -235,7 +265,14 @@ function createSocketioTransport(options = {}) {
235
265
  }
236
266
  async function dispose() {
237
267
  disposed = true;
268
+ applyEpoch++;
238
269
  closeAllSockets();
270
+ reopen.clear();
271
+ rebuildListeners.clear();
272
+ for (const timer of releaseTimers.values()) clearTimeout(timer);
273
+ releaseTimers.clear();
274
+ holds.clear();
275
+ unheld.clear();
239
276
  stopRefresh();
240
277
  currentTarget = null;
241
278
  status = { up: false };
@@ -245,20 +282,54 @@ function createSocketioTransport(options = {}) {
245
282
  return sockets.get(namespace) ?? null;
246
283
  }
247
284
  function ensureSocket(namespace) {
248
- if (!currentTarget || !manager) return null;
249
- let sock = sockets.get(namespace);
250
- if (!sock) {
251
- sock = openSocket(namespace, currentTarget);
252
- bindSecondarySocketEvents(sock);
285
+ if (!currentTarget) return null;
286
+ if (!holds.has(namespace)) unheld.add(namespace);
287
+ if (!manager) {
288
+ reopen.add(namespace);
289
+ return null;
253
290
  }
254
- return sock;
291
+ return sockets.get(namespace) ?? openSocket(namespace, manager, currentTarget);
292
+ }
293
+ function acquire(namespace) {
294
+ holds.set(namespace, (holds.get(namespace) ?? 0) + 1);
295
+ clearTimeout(releaseTimers.get(namespace));
296
+ releaseTimers.delete(namespace);
297
+ let held = true;
298
+ return () => {
299
+ if (!held || disposed) return;
300
+ held = false;
301
+ const left = (holds.get(namespace) ?? 1) - 1;
302
+ if (left > 0) {
303
+ holds.set(namespace, left);
304
+ return;
305
+ }
306
+ holds.delete(namespace);
307
+ if (namespace === mainNs) return;
308
+ releaseTimers.set(namespace, setTimeout(() => closeReleased(namespace), releaseGraceMs));
309
+ };
310
+ }
311
+ function closeReleased(namespace) {
312
+ releaseTimers.delete(namespace);
313
+ if (holds.has(namespace) || unheld.has(namespace)) return;
314
+ reopen.delete(namespace);
315
+ const sock = sockets.get(namespace);
316
+ if (!sock) return;
317
+ sockets.delete(namespace);
318
+ sock.disconnect();
319
+ sock.removeAllListeners();
320
+ }
321
+ function onRebuild(handler) {
322
+ rebuildListeners.add(handler);
323
+ return () => {
324
+ rebuildListeners.delete(handler);
325
+ };
255
326
  }
256
327
  function reviveDeadSockets() {
257
328
  if (!currentTarget) return;
258
- if (status.up && isStaleNow()) {
329
+ if (status.up && isDeadNow()) {
259
330
  logger?.debug("reviveDeadSockets: engine stale — forcing reconnect");
260
331
  lastActivityAt = Date.now();
261
- sockets.get(mainNs)?.io.engine?.close();
332
+ manager?.engine?.close();
262
333
  return;
263
334
  }
264
335
  const auth = buildAuth(currentTarget);
@@ -270,11 +341,39 @@ function createSocketioTransport(options = {}) {
270
341
  }
271
342
  logger?.debug(`reviveDeadSockets: ${revived}/${sockets.size} reconnecting`);
272
343
  }
273
- async function ensureAlive() {
274
- if (disposed || !currentTarget) return health();
275
- if (resolveQuery) await refreshQuery();
276
- reviveDeadSockets();
277
- return health();
344
+ function reconnectInactive(target) {
345
+ const auth = buildAuth(target);
346
+ for (const sock of sockets.values()) {
347
+ if (sock.active) continue;
348
+ sock.auth = auth;
349
+ sock.connect();
350
+ }
351
+ }
352
+ function ensureAlive() {
353
+ if (ensureInflight) return ensureInflight;
354
+ const run = async () => {
355
+ if (disposed || !currentTarget) return health();
356
+ const main = sockets.get(mainNs);
357
+ if (main?.connected && !isDeadNow()) {
358
+ if (resolveQuery) refreshQuery();
359
+ const engine = main.io.engine;
360
+ const answered = await new Promise((resolve) => {
361
+ main.timeout(PROBE_TIMEOUT_MS).emit("get-status", null, (err) => resolve(!err));
362
+ });
363
+ if (!answered && main.connected && main.io.engine === engine) {
364
+ logger?.debug("ensureAlive: probe unanswered, closing engine");
365
+ engine.close();
366
+ } else if (answered && currentTarget) reconnectInactive(currentTarget);
367
+ return health();
368
+ }
369
+ if (resolveQuery) await refreshQueryWithin(PROBE_TIMEOUT_MS);
370
+ reviveDeadSockets();
371
+ return health();
372
+ };
373
+ ensureInflight = run().finally(() => {
374
+ ensureInflight = null;
375
+ });
376
+ return ensureInflight;
278
377
  }
279
378
  return {
280
379
  get spec() {
@@ -289,6 +388,8 @@ function createSocketioTransport(options = {}) {
289
388
  dispose,
290
389
  socket,
291
390
  ensureSocket,
391
+ acquire,
392
+ onRebuild,
292
393
  reviveDeadSockets,
293
394
  ensureAlive
294
395
  };
@@ -33,7 +33,6 @@ export interface WsTransportOptions {
33
33
  readonly spec?: Partial<TransportSpec>;
34
34
  readonly webSocketCtor?: typeof WebSocket;
35
35
  readonly tokenParam?: string;
36
- readonly sessionParam?: string;
37
36
  readonly logger?: Logger;
38
37
  readonly resolveUrl?: WsUrlResolver;
39
38
  }
@@ -1,5 +1,5 @@
1
+ import { t as classifyClose } from "../closeCodes-CltSRjdJ.js";
1
2
  import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-dt1m8U-o.js";
2
- import { t as classifyClose } from "../closeCodes-ClWJVOpq.js";
3
3
  //#region src/transports/ws.ts
4
4
  var WS_SPEC = {
5
5
  id: "ws",
@@ -10,13 +10,13 @@ var WS_CLOSE_TARGET_CHANGED = 4e3;
10
10
  var WS_CLOSE_DETACHED = 4001;
11
11
  var WS_CLOSE_DISPOSED = 4002;
12
12
  var WS_CLOSE_RESOLVE_FAILED = 4003;
13
+ var RESOLVE_TIMEOUT_MS = 1e4;
13
14
  function createWsTransport(options = {}) {
14
15
  const spec = {
15
16
  ...WS_SPEC,
16
17
  ...options.spec
17
18
  };
18
19
  const tokenParam = options.tokenParam ?? "token";
19
- const sessionParam = options.sessionParam ?? "session";
20
20
  const WsCtor = options.webSocketCtor ?? (typeof WebSocket !== "undefined" ? WebSocket : void 0);
21
21
  const resolveUrl = options.resolveUrl;
22
22
  const logger = options.logger;
@@ -38,7 +38,6 @@ function createWsTransport(options = {}) {
38
38
  const params = new URLSearchParams();
39
39
  for (const [k, v] of Object.entries(handleSpec.query ?? {})) if (v !== void 0 && v !== null && v !== "") params.set(k, v);
40
40
  params.set(tokenParam, target.tokens.access);
41
- if (target.tokens.proxySession) params.set(sessionParam, target.tokens.proxySession);
42
41
  return `${wsProtocol}//${base.hostname}:${port}${prefix}${handleSpec.path}?${params.toString()}`;
43
42
  }
44
43
  function isAuthCloseEvent(event) {
@@ -67,7 +66,10 @@ function createWsTransport(options = {}) {
67
66
  closeDelivered.add(ws);
68
67
  if (!isCurrent) return;
69
68
  logger?.debug(`close ${handle.spec.path} code=${event.code} clean=${event.wasClean}${event.reason ? ` reason=${event.reason}` : ""}`);
70
- if (isAuthCloseEvent(event)) emitter.emit("auth-error", { message: event.reason || `ws close code ${event.code}` });
69
+ if (isAuthCloseEvent(event)) emitter.emit("auth-error", {
70
+ message: event.reason || `ws close code ${event.code}`,
71
+ access: handle.access
72
+ });
71
73
  const info = {
72
74
  code: event.code,
73
75
  reason: event.reason,
@@ -86,6 +88,7 @@ function createWsTransport(options = {}) {
86
88
  }
87
89
  function closeWs(handle, code, reason) {
88
90
  handle.openSeq++;
91
+ clearResolveTimer(handle);
89
92
  const ws = handle.ws;
90
93
  if (!ws) return;
91
94
  const Ctor = ensureCtor();
@@ -108,11 +111,12 @@ function createWsTransport(options = {}) {
108
111
  };
109
112
  for (const fn of [...handle.listeners.close]) fn(info);
110
113
  }
111
- function connectWs(handle, url) {
114
+ function connectWs(handle, url, access) {
112
115
  const ws = new (ensureCtor())(url, handle.spec.protocols);
113
116
  if (handle.spec.binaryType) ws.binaryType = handle.spec.binaryType;
114
117
  handle.ws = ws;
115
118
  handle.url = url;
119
+ handle.access = access;
116
120
  bindWs(handle, ws);
117
121
  }
118
122
  function openWs(handle) {
@@ -121,29 +125,51 @@ function createWsTransport(options = {}) {
121
125
  const target = currentTarget;
122
126
  const defaultUrl = buildUrl(target, handle.spec);
123
127
  if (!resolveUrl) {
124
- connectWs(handle, defaultUrl);
128
+ connectWs(handle, defaultUrl, target.tokens.access);
125
129
  return;
126
130
  }
127
131
  const seq = ++handle.openSeq;
132
+ clearResolveTimer(handle);
133
+ handle.resolveTimer = setTimeout(() => {
134
+ handle.resolveTimer = void 0;
135
+ if (handle.disposed || seq !== handle.openSeq) return;
136
+ handle.openSeq++;
137
+ logger?.warn(`resolveUrl timed out for ${handle.spec.path} after ${RESOLVE_TIMEOUT_MS}ms`);
138
+ failResolve(handle, "resolve-timeout");
139
+ }, RESOLVE_TIMEOUT_MS);
128
140
  resolveUrl({
129
141
  target,
130
142
  spec: handle.spec,
131
143
  defaultUrl
132
144
  }).then((url) => {
133
- if (handle.disposed || seq !== handle.openSeq || currentTarget !== target) return;
145
+ if (handle.disposed || seq !== handle.openSeq) return;
146
+ clearResolveTimer(handle);
147
+ if (currentTarget !== target) {
148
+ openWs(handle);
149
+ return;
150
+ }
134
151
  connectWs(handle, url);
135
152
  }, (err) => {
136
153
  if (handle.disposed || seq !== handle.openSeq) return;
154
+ clearResolveTimer(handle);
137
155
  const reason = err instanceof Error ? err.message : String(err);
138
156
  logger?.warn(`resolveUrl failed for ${handle.spec.path}: ${reason}`);
139
- const info = {
140
- code: WS_CLOSE_RESOLVE_FAILED,
141
- reason: "resolve-failed",
142
- wasClean: false
143
- };
144
- for (const fn of [...handle.listeners.close]) fn(info);
157
+ failResolve(handle, "resolve-failed");
145
158
  });
146
159
  }
160
+ function failResolve(handle, reason) {
161
+ const info = {
162
+ code: WS_CLOSE_RESOLVE_FAILED,
163
+ reason,
164
+ wasClean: false
165
+ };
166
+ for (const fn of [...handle.listeners.close]) fn(info);
167
+ }
168
+ function clearResolveTimer(handle) {
169
+ if (handle.resolveTimer === void 0) return;
170
+ clearTimeout(handle.resolveTimer);
171
+ handle.resolveTimer = void 0;
172
+ }
147
173
  function recycleAll(code, reason) {
148
174
  const snapshot = [...handles];
149
175
  logger?.debug(`recycleAll (${reason}) handles=${snapshot.length}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camera.ui/transport",
3
- "version": "0.0.30",
3
+ "version": "0.1.1",
4
4
  "description": "camera.ui transport layer — framework-agnostic connection kernel, reducer state, pluggable transports (HTTP/WS/Socket.IO/NATS), lifecycle effects and worker bridge",
5
5
  "author": "seydx (https://github.com/cameraui/clients)",
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "import": "./dist/worker.js"
40
40
  },
41
41
  "./testing": {
42
- "types": "./dist/testing.d.ts",
42
+ "types": "./dist/testing/index.d.ts",
43
43
  "import": "./dist/testing.js"
44
44
  }
45
45
  },
@@ -56,26 +56,27 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "@camera.ui/logger": ">=0.0.3",
59
- "@camera.ui/rpc": ">=1.0.12",
59
+ "@camera.ui/rpc": ">=1.1.1",
60
60
  "axios": ">=1.20.0",
61
- "socket.io-client": ">=4.8.3"
61
+ "socket.io-client": ">=4.8.4"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@eslint/js": "9.39.4",
65
65
  "@stylistic/eslint-plugin": "^5.10.0",
66
- "@typescript-eslint/parser": "^8.70.0",
67
- "@vue/language-core": "^3.3.11",
66
+ "@typescript-eslint/parser": "^8.72.0",
67
+ "@vue/language-core": "^3.3.13",
68
68
  "eslint": "9.39.2",
69
- "globals": "^17.12.0",
69
+ "globals": "^17.13.0",
70
70
  "jiti": "^2.7.0",
71
- "prettier": "^3.9.6",
71
+ "prettier": "^3.9.10",
72
72
  "rimraf": "^6.1.3",
73
+ "socket.io": "^4.8.4",
73
74
  "typescript": "5.9.3",
74
- "typescript-eslint": "^8.70.0",
75
- "unplugin-dts": "^1.1.0",
76
- "updates": "^18.1.0",
77
- "vite": "^8.3.0",
78
- "vitest": "^5.0.1"
75
+ "typescript-eslint": "^8.72.0",
76
+ "unplugin-dts": "^1.1.2",
77
+ "updates": "^19.3.0",
78
+ "vite": "^8.3.5",
79
+ "vitest": "^5.0.5"
79
80
  },
80
81
  "overrides": {
81
82
  "@vue/language-core": "$@vue/language-core"
@@ -1,14 +0,0 @@
1
- //#region src/transports/closeCodes.ts
2
- function classifyClose(input) {
3
- const obj = typeof input === "string" ? void 0 : input;
4
- const code = obj?.wsCloseCode ?? obj?.code;
5
- if (code === 4401 || code === 1008) return "auth-expired";
6
- if (code === 4403 || code === 4400) return "forbidden";
7
- const text = (typeof input === "string" ? input : obj?.reason ?? obj?.message ?? "").toLowerCase();
8
- if (!text) return "other";
9
- if (text.includes("forbidden") || text.includes("403")) return "forbidden";
10
- if (text.includes("unauthorized") || text.includes("401") || text.includes("auth")) return "auth-expired";
11
- return "other";
12
- }
13
- //#endregion
14
- export { classifyClose as t };