extra-native-websocket 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,7 +46,7 @@ class ExtraNativeWebSocket extends Emitter<{
46
46
  /**
47
47
  * @throws {WebSocketError}
48
48
  */
49
- connect(): Promise<void>
49
+ connect(signal?: AbortSignal): Promise<void>
50
50
  close(code?: number, reason?: string): Promise<void>
51
51
  send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void
52
52
  }
@@ -54,18 +54,23 @@ class ExtraNativeWebSocket extends Emitter<{
54
54
 
55
55
  ### autoReconnect
56
56
  ```ts
57
- function autoReconnect(ws: ExtraNativeWebSocket, timeout?: number): () => void
57
+ function autoReconnect(
58
+ ws: ExtraNativeWebSocket
59
+ , reconnectInterval?: number = 0
60
+ , connectTimeout?: number
61
+ ): () => void
58
62
  ```
59
63
 
60
64
  ### autoReconnectWithExponentialBackOff
61
65
  ```ts
62
66
  function autoReonnectWithExponentialBackOff(
63
67
  ws: ExtraWebSocket
64
- , options: {
65
- baseTimeout: number
66
- maxTimeout?: number
68
+ , reconnectInterval: {
69
+ baseInterval: number
70
+ maxInterval?: number = Infinity
67
71
  factor?: number = 2
68
72
  jitter?: boolean = true
69
73
  }
74
+ , connectTimeout?: number
70
75
  ): () => void
71
76
  ```
@@ -24,7 +24,10 @@ export declare class ExtraNativeWebSocket extends Emitter<{
24
24
  getState(): State;
25
25
  getBinaryType(): BinaryType;
26
26
  setBinaryType(val: BinaryType): void;
27
- connect(): Promise<void>;
27
+ /**
28
+ * @throws {WebSocketError}
29
+ */
30
+ connect(signal?: AbortSignal): Promise<void>;
28
31
  close(code?: number, reason?: string): Promise<void>;
29
32
  send(data: Data): void;
30
33
  }
@@ -1,6 +1,7 @@
1
1
  import { assert } from '@blackglory/prelude';
2
2
  import { WebSocketError } from './websocket-error.js';
3
3
  import { Queue, Emitter } from '@blackglory/structures';
4
+ import { SyncDestructor } from 'extra-defer';
4
5
  export var BinaryType;
5
6
  (function (BinaryType) {
6
7
  BinaryType[BinaryType["Blob"] = 0] = "Blob";
@@ -58,27 +59,57 @@ export class ExtraNativeWebSocket extends Emitter {
58
59
  }
59
60
  }
60
61
  }
61
- connect() {
62
+ /**
63
+ * @throws {WebSocketError}
64
+ */
65
+ connect(signal) {
62
66
  return new Promise((resolve, reject) => {
67
+ signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
63
68
  assert(this.getState() === State.Closed, 'WebSocket is not closed');
64
69
  const self = this;
65
- const ws = this.instance = this.createWebSocket();
70
+ const ws = this.createWebSocket();
71
+ this.instance = ws;
72
+ signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', abortListener, { once: true });
73
+ const destructor = new SyncDestructor();
66
74
  ws.addEventListener('error', errorListener, { once: true });
67
- ws.addEventListener('open', event => this.emit('open', event));
68
- ws.addEventListener('message', event => this.emit('message', event));
69
- ws.addEventListener('error', event => this.emit('error', event));
70
- ws.addEventListener('close', event => this.emit('close', event));
75
+ destructor.defer(() => ws.removeEventListener('error', errorListener));
76
+ {
77
+ const listener = (event) => this.emit('open', event);
78
+ ws.addEventListener('open', listener);
79
+ destructor.defer(() => ws.removeEventListener('open', listener));
80
+ }
81
+ {
82
+ const listener = (event) => this.emit('message', event);
83
+ ws.addEventListener('message', listener);
84
+ destructor.defer(() => ws.removeEventListener('message', listener));
85
+ }
86
+ {
87
+ const listener = (event) => this.emit('error', event);
88
+ ws.addEventListener('error', listener);
89
+ destructor.defer(() => ws.removeEventListener('error', listener));
90
+ }
91
+ {
92
+ const listener = (event) => this.emit('close', event);
93
+ ws.addEventListener('close', listener);
94
+ destructor.defer(() => ws.removeEventListener('close', listener));
95
+ }
71
96
  this.setBinaryType(this.binaryType);
72
97
  ws.addEventListener('open', openListener, { once: true });
98
+ function abortListener() {
99
+ assert(signal);
100
+ destructor.execute();
101
+ ws.close();
102
+ reject(signal.reason);
103
+ }
73
104
  function errorListener(event) {
74
105
  ws.addEventListener('close', closeListener, { once: true });
75
- }
76
- function closeListener(event) {
77
- reject(new WebSocketError(event.code, event.reason));
106
+ function closeListener(event) {
107
+ destructor.execute();
108
+ reject(new WebSocketError(event.code, event.reason));
109
+ }
78
110
  }
79
111
  function openListener(event) {
80
112
  ws.removeEventListener('error', errorListener);
81
- ws.removeEventListener('close', closeListener);
82
113
  for (let size = self.unsentMessages.size; size--;) {
83
114
  self.send(self.unsentMessages.dequeue());
84
115
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extra-native-websocket.js","sourceRoot":"","sources":["../src/extra-native-websocket.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extra-native-websocket.js","sourceRoot":"","sources":["../src/extra-native-websocket.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { ExtraNativeWebSocket } from "../extra-native-websocket.js";
2
- export declare function autoReconnectWithExponentialBackOff(ws: ExtraNativeWebSocket, { baseTimeout, maxTimeout, factor, jitter }: {
3
- baseTimeout: number;
4
- maxTimeout?: number;
2
+ export declare function autoReconnectWithExponentialBackOff(ws: ExtraNativeWebSocket, { baseInterval, maxInterval, factor, jitter }: {
3
+ baseInterval: number;
4
+ maxInterval?: number;
5
5
  factor?: number;
6
6
  jitter?: boolean;
7
- }): () => void;
7
+ }, connectTimeout?: number): () => void;
@@ -3,8 +3,10 @@ import { calculateExponentialBackoffTimeout } from 'extra-timers';
3
3
  import { pass } from '@blackglory/prelude';
4
4
  import { delay } from 'extra-promise';
5
5
  import { waitForFunction } from '@blackglory/wait-for';
6
- export function autoReconnectWithExponentialBackOff(ws, { baseTimeout, maxTimeout = Infinity, factor = 2, jitter = true }) {
6
+ import { AbortController, timeoutSignal } from 'extra-abort';
7
+ export function autoReconnectWithExponentialBackOff(ws, { baseInterval, maxInterval = Infinity, factor = 2, jitter = true }, connectTimeout) {
7
8
  const controller = new AbortController();
9
+ // Make sure the error listener is added, prevent crashes due to uncaught errors.
8
10
  const removeErrorListener = ws.on('error', pass);
9
11
  let removeCloseListener = ws.once('close', closeListener);
10
12
  return () => {
@@ -19,8 +21,8 @@ export function autoReconnectWithExponentialBackOff(ws, { baseTimeout, maxTimeou
19
21
  return;
20
22
  await delay(calculateExponentialBackoffTimeout({
21
23
  retries,
22
- baseTimeout,
23
- maxTimeout,
24
+ baseTimeout: baseInterval,
25
+ maxTimeout: maxInterval,
24
26
  factor,
25
27
  jitter
26
28
  }));
@@ -28,7 +30,9 @@ export function autoReconnectWithExponentialBackOff(ws, { baseTimeout, maxTimeou
28
30
  return;
29
31
  try {
30
32
  await waitForFunction(() => ws.getState() === State.Closed);
31
- await ws.connect();
33
+ await ws.connect(connectTimeout
34
+ ? timeoutSignal(connectTimeout)
35
+ : undefined);
32
36
  if (controller.signal.aborted)
33
37
  return;
34
38
  removeCloseListener = ws.once('close', closeListener);
@@ -1 +1 @@
1
- {"version":3,"file":"auto-reconnect-with-exponential-back-off.js","sourceRoot":"","sources":["../../src/utils/auto-reconnect-with-exponential-back-off.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,EAAE,qCAAsC;AAC5E,OAAO,EAAE,kCAAkC,EAAE,MAAM,cAAc,CAAA;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,UAAU,mCAAmC,CACjD,EAAwB,EACxB,EACE,WAAW,EACX,UAAU,GAAG,QAAQ,EACrB,MAAM,GAAG,CAAC,EACV,MAAM,GAAG,IAAI,EAMd;IAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IAGxC,MAAM,mBAAmB,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAChD,IAAI,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAEzD,OAAO,GAAG,EAAE;QACV,UAAU,CAAC,KAAK,EAAE,CAAA;QAClB,mBAAmB,EAAE,CAAA;QACrB,mBAAmB,EAAE,CAAA;IACvB,CAAC,CAAA;IAED,KAAK,UAAU,aAAa;QAC1B,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,MAAM,KAAK,CAAC,kCAAkC,CAAC;gBAC7C,OAAO;gBACP,WAAW;gBACX,UAAU;gBACV,MAAM;gBACN,MAAM;aACP,CAAC,CAAC,CAAA;YACH,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAA;gBAC3D,MAAM,EAAE,CAAC,OAAO,EAAE,CAAA;gBAClB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAM;gBAErC,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;gBACrD,MAAK;YACP,CAAC;YAAC,WAAM,CAAC;gBACP,OAAO,EAAE,CAAA;gBACT,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"auto-reconnect-with-exponential-back-off.js","sourceRoot":"","sources":["../../src/utils/auto-reconnect-with-exponential-back-off.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,EAAE,qCAAsC;AAC5E,OAAO,EAAE,kCAAkC,EAAE,MAAM,cAAc,CAAA;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE5D,MAAM,UAAU,mCAAmC,CACjD,EAAwB,EACxB,EACE,YAAY,EACZ,WAAW,GAAG,QAAQ,EACtB,MAAM,GAAG,CAAC,EACV,MAAM,GAAG,IAAI,EAMd,EACD,cAAuB;IAEvB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IAExC,iFAAiF;IACjF,MAAM,mBAAmB,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAChD,IAAI,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAEzD,OAAO,GAAG,EAAE;QACV,UAAU,CAAC,KAAK,EAAE,CAAA;QAClB,mBAAmB,EAAE,CAAA;QACrB,mBAAmB,EAAE,CAAA;IACvB,CAAC,CAAA;IAED,KAAK,UAAU,aAAa;QAC1B,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,MAAM,KAAK,CAAC,kCAAkC,CAAC;gBAC7C,OAAO;gBACP,WAAW,EAAE,YAAY;gBACzB,UAAU,EAAE,WAAW;gBACvB,MAAM;gBACN,MAAM;aACP,CAAC,CAAC,CAAA;YACH,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAA;gBAC3D,MAAM,EAAE,CAAC,OAAO,CACd,cAAc;oBAChB,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC;oBAC/B,CAAC,CAAC,SAAS,CACV,CAAA;gBACD,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAM;gBAErC,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;gBACrD,MAAK;YACP,CAAC;YAAC,WAAM,CAAC;gBACP,OAAO,EAAE,CAAA;gBACT,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -1,2 +1,2 @@
1
1
  import { ExtraNativeWebSocket } from "../extra-native-websocket.js";
2
- export declare function autoReconnect(ws: ExtraNativeWebSocket, timeout?: number): () => void;
2
+ export declare function autoReconnect(ws: ExtraNativeWebSocket, reconnectInterval?: number, connectTimeout?: number): () => void;
@@ -1,25 +1,30 @@
1
1
  import { State } from "../extra-native-websocket.js";
2
2
  import { delay } from 'extra-promise';
3
- import { AbortController } from 'extra-abort';
3
+ import { AbortController, timeoutSignal } from 'extra-abort';
4
4
  import { pass } from '@blackglory/prelude';
5
5
  import { waitForFunction } from '@blackglory/wait-for';
6
- export function autoReconnect(ws, timeout = 0) {
6
+ export function autoReconnect(ws, reconnectInterval = 0, connectTimeout) {
7
7
  const controller = new AbortController();
8
+ // Make sure the error listener is added, prevent crashes due to uncaught errors.
9
+ const removeErrorListener = ws.on('error', pass);
8
10
  let removeCloseListener = ws.once('close', closeListener);
9
11
  return () => {
10
12
  controller.abort();
11
13
  removeCloseListener();
14
+ removeErrorListener();
12
15
  };
13
16
  async function closeListener() {
14
17
  while (true) {
15
18
  if (controller.signal.aborted)
16
19
  return;
17
- await delay(timeout);
20
+ await delay(reconnectInterval);
18
21
  if (controller.signal.aborted)
19
22
  return;
20
23
  try {
21
24
  await waitForFunction(() => ws.getState() === State.Closed);
22
- await ws.connect();
25
+ await ws.connect(connectTimeout
26
+ ? timeoutSignal(connectTimeout)
27
+ : undefined);
23
28
  if (controller.signal.aborted)
24
29
  return;
25
30
  removeCloseListener = ws.once('close', closeListener);
@@ -1 +1 @@
1
- {"version":3,"file":"auto-reconnect.js","sourceRoot":"","sources":["../../src/utils/auto-reconnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,EAAE,qCAAsC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,UAAU,aAAa,CAAC,EAAwB,EAAE,UAAkB,CAAC;IACzE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IAExC,IAAI,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAEzD,OAAO,GAAG,EAAE;QACV,UAAU,CAAC,KAAK,EAAE,CAAA;QAClB,mBAAmB,EAAE,CAAA;IACvB,CAAC,CAAA;IAED,KAAK,UAAU,aAAa;QAC1B,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,MAAM,KAAK,CAAC,OAAO,CAAC,CAAA;YACpB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAA;gBAC3D,MAAM,EAAE,CAAC,OAAO,EAAE,CAAA;gBAClB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAM;gBAErC,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;gBACrD,MAAK;YACP,CAAC;YAAC,WAAM,CAAC;gBACP,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"auto-reconnect.js","sourceRoot":"","sources":["../../src/utils/auto-reconnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,EAAE,qCAAsC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,UAAU,aAAa,CAC3B,EAAwB,EACxB,oBAA4B,CAAC,EAC7B,cAAuB;IAEvB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IAExC,iFAAiF;IACjF,MAAM,mBAAmB,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAChD,IAAI,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;IAEzD,OAAO,GAAG,EAAE;QACV,UAAU,CAAC,KAAK,EAAE,CAAA;QAClB,mBAAmB,EAAE,CAAA;QACrB,mBAAmB,EAAE,CAAA;IACvB,CAAC,CAAA;IAED,KAAK,UAAU,aAAa;QAC1B,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,MAAM,KAAK,CAAC,iBAAiB,CAAC,CAAA;YAC9B,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAA;gBAC3D,MAAM,EAAE,CAAC,OAAO,CACd,cAAc;oBAChB,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC;oBAC/B,CAAC,CAAC,SAAS,CACV,CAAA;gBACD,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAM;gBAErC,mBAAmB,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;gBACrD,MAAK;YACP,CAAC;YAAC,WAAM,CAAC;gBACP,IAAI,EAAE,CAAA;YACR,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "extra-native-websocket",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "files": [
@@ -15,13 +15,12 @@
15
15
  "license": "MIT",
16
16
  "sideEffects": false,
17
17
  "engines": {
18
- "node": ">=18.17.0"
18
+ "node": ">=22"
19
19
  },
20
20
  "scripts": {
21
21
  "prepare": "ts-patch install -s",
22
- "lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
23
- "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --runInBand --config jest.config.cjs",
24
- "test:debug": "cross-env NODE_OPTIONS=--experimental-vm-modules node --inspect-brk node_modules/.bin/jest --runInBand --config jest.config.cjs",
22
+ "lint": "eslint --quiet src __tests__",
23
+ "test": "vitest --run",
25
24
  "prepublishOnly": "run-s prepare clean build",
26
25
  "clean": "rimraf lib",
27
26
  "build": "tsc --project tsconfig.build.json --outDir lib",
@@ -34,36 +33,35 @@
34
33
  }
35
34
  },
36
35
  "devDependencies": {
37
- "@blackglory/jest-resolver": "^0.3.1",
38
- "@commitlint/cli": "^18.4.3",
39
- "@commitlint/config-conventional": "^18.4.3",
40
- "@types/jest": "^29.5.11",
41
- "@types/ws": "^8.5.10",
42
- "@typescript-eslint/eslint-plugin": "^6.13.2",
43
- "@typescript-eslint/parser": "^6.13.2",
36
+ "@commitlint/cli": "^19.8.1",
37
+ "@commitlint/config-conventional": "^19.8.1",
38
+ "@eslint/js": "^9.31.0",
39
+ "@types/ws": "^8.18.1",
44
40
  "cross-env": "^7.0.3",
45
- "eslint": "^8.55.0",
41
+ "eslint": "^9.31.0",
46
42
  "husky": "4",
47
- "jest": "^29.7.0",
48
- "jest-environment-jsdom": "^29.7.0",
49
- "jest-resolve": "^29.7.0",
43
+ "jsdom": "^26.1.0",
50
44
  "npm-run-all": "^4.1.5",
51
45
  "return-style": "^3.0.1",
52
- "rimraf": "^5.0.5",
46
+ "rimraf": "^6.0.1",
53
47
  "standard-version": "^9.5.0",
54
- "ts-jest": "^29.1.1",
55
- "ts-patch": "^3.1.1",
56
- "tslib": "^2.6.2",
57
- "typescript": "^5.3.3",
58
- "typescript-transform-paths": "^3.4.6",
59
- "ws": "^8.14.2"
48
+ "ts-patch": "^3.3.0",
49
+ "tslib": "^2.8.1",
50
+ "typescript": "^5.8.3",
51
+ "typescript-eslint": "^8.37.0",
52
+ "typescript-transform-paths": "^3.5.5",
53
+ "vite": "^7.0.5",
54
+ "vite-tsconfig-paths": "^5.1.4",
55
+ "vitest": "^3.2.4",
56
+ "ws": "^8.18.3"
60
57
  },
61
58
  "dependencies": {
62
59
  "@blackglory/errors": "^3.0.3",
63
- "@blackglory/prelude": "^0.3.4",
64
- "@blackglory/structures": "^0.13.4",
65
- "@blackglory/wait-for": "^0.7.4",
66
- "extra-abort": "^0.3.7",
67
- "extra-promise": "^6.0.8"
60
+ "@blackglory/prelude": "^0.4.0",
61
+ "@blackglory/structures": "^0.14.10",
62
+ "@blackglory/wait-for": "^0.8.1",
63
+ "extra-abort": "^0.4.0",
64
+ "extra-defer": "^0.3.1",
65
+ "extra-promise": "^7.0.0"
68
66
  }
69
67
  }
@@ -1,6 +1,7 @@
1
1
  import { assert } from '@blackglory/prelude'
2
2
  import { WebSocketError } from './websocket-error.js'
3
3
  import { Queue, Emitter } from '@blackglory/structures'
4
+ import { SyncDestructor } from 'extra-defer'
4
5
 
5
6
  export enum BinaryType {
6
7
  Blob
@@ -74,35 +75,68 @@ export class ExtraNativeWebSocket extends Emitter<{
74
75
  /**
75
76
  * @throws {WebSocketError}
76
77
  */
77
- connect(): Promise<void> {
78
+ connect(signal?: AbortSignal): Promise<void> {
78
79
  return new Promise((resolve, reject) => {
80
+ signal?.throwIfAborted()
79
81
  assert(this.getState() === State.Closed, 'WebSocket is not closed')
80
82
 
81
83
  const self = this
82
- const ws = this.instance = this.createWebSocket()
84
+ const ws = this.createWebSocket()
85
+ this.instance = ws
83
86
 
87
+ signal?.addEventListener('abort', abortListener, { once: true })
88
+
89
+ const destructor = new SyncDestructor()
84
90
  ws.addEventListener('error', errorListener, { once: true })
91
+ destructor.defer(() => ws.removeEventListener('error', errorListener))
85
92
 
86
- ws.addEventListener('open', event => this.emit('open', event))
87
- ws.addEventListener('message', event => this.emit('message', event))
88
- ws.addEventListener('error', event => this.emit('error', event))
89
- ws.addEventListener('close', event => this.emit('close', event))
93
+ {
94
+ const listener = (event: Event) => this.emit('open', event)
95
+ ws.addEventListener('open', listener)
96
+ destructor.defer(() => ws.removeEventListener('open', listener))
97
+ }
98
+ {
99
+ const listener = (event: MessageEvent) => this.emit('message', event)
100
+ ws.addEventListener('message', listener)
101
+ destructor.defer(() => ws.removeEventListener('message', listener))
102
+ }
103
+ {
104
+ const listener = (event: Event) => this.emit('error', event)
105
+ ws.addEventListener('error', listener)
106
+ destructor.defer(() => ws.removeEventListener('error', listener))
107
+ }
108
+ {
109
+ const listener = (event: CloseEvent) => this.emit('close', event)
110
+ ws.addEventListener('close', listener)
111
+ destructor.defer(() => ws.removeEventListener('close', listener))
112
+ }
90
113
 
91
114
  this.setBinaryType(this.binaryType)
92
115
 
93
116
  ws.addEventListener('open', openListener, { once: true })
94
117
 
118
+ function abortListener(): void {
119
+ assert(signal)
120
+
121
+ destructor.execute()
122
+ ws.close()
123
+
124
+ reject(signal.reason)
125
+ }
126
+
95
127
  function errorListener(event: Event): void {
96
128
  ws.addEventListener('close', closeListener, { once: true })
97
- }
98
129
 
99
- function closeListener(event: CloseEvent): void {
100
- reject(new WebSocketError(event.code, event.reason))
130
+ function closeListener(event: CloseEvent): void {
131
+ destructor.execute()
132
+
133
+ reject(new WebSocketError(event.code, event.reason))
134
+ }
101
135
  }
102
136
 
103
137
  function openListener(event: Event): void {
104
138
  ws.removeEventListener('error', errorListener)
105
- ws.removeEventListener('close', closeListener)
139
+
106
140
  for (let size = self.unsentMessages.size; size--;) {
107
141
  self.send(self.unsentMessages.dequeue()!)
108
142
  }
@@ -3,20 +3,22 @@ import { calculateExponentialBackoffTimeout } from 'extra-timers'
3
3
  import { pass } from '@blackglory/prelude'
4
4
  import { delay } from 'extra-promise'
5
5
  import { waitForFunction } from '@blackglory/wait-for'
6
+ import { AbortController, timeoutSignal } from 'extra-abort'
6
7
 
7
8
  export function autoReconnectWithExponentialBackOff(
8
9
  ws: ExtraNativeWebSocket
9
10
  , {
10
- baseTimeout
11
- , maxTimeout = Infinity
11
+ baseInterval
12
+ , maxInterval = Infinity
12
13
  , factor = 2
13
14
  , jitter = true
14
15
  }: {
15
- baseTimeout: number
16
- maxTimeout?: number
16
+ baseInterval: number
17
+ maxInterval?: number
17
18
  factor?: number
18
19
  jitter?: boolean
19
20
  }
21
+ , connectTimeout?: number
20
22
  ): () => void {
21
23
  const controller = new AbortController()
22
24
 
@@ -37,8 +39,8 @@ export function autoReconnectWithExponentialBackOff(
37
39
 
38
40
  await delay(calculateExponentialBackoffTimeout({
39
41
  retries
40
- , baseTimeout
41
- , maxTimeout
42
+ , baseTimeout: baseInterval
43
+ , maxTimeout: maxInterval
42
44
  , factor
43
45
  , jitter
44
46
  }))
@@ -46,7 +48,11 @@ export function autoReconnectWithExponentialBackOff(
46
48
 
47
49
  try {
48
50
  await waitForFunction(() => ws.getState() === State.Closed)
49
- await ws.connect()
51
+ await ws.connect(
52
+ connectTimeout
53
+ ? timeoutSignal(connectTimeout)
54
+ : undefined
55
+ )
50
56
  if (controller.signal.aborted) return
51
57
 
52
58
  removeCloseListener = ws.once('close', closeListener)
@@ -1,29 +1,40 @@
1
1
  import { ExtraNativeWebSocket, State } from '@src/extra-native-websocket.js'
2
2
  import { delay } from 'extra-promise'
3
- import { AbortController } from 'extra-abort'
3
+ import { AbortController, timeoutSignal } from 'extra-abort'
4
4
  import { pass } from '@blackglory/prelude'
5
5
  import { waitForFunction } from '@blackglory/wait-for'
6
6
 
7
- export function autoReconnect(ws: ExtraNativeWebSocket, timeout: number = 0): () => void {
7
+ export function autoReconnect(
8
+ ws: ExtraNativeWebSocket
9
+ , reconnectInterval: number = 0
10
+ , connectTimeout?: number
11
+ ): () => void {
8
12
  const controller = new AbortController()
9
13
 
14
+ // Make sure the error listener is added, prevent crashes due to uncaught errors.
15
+ const removeErrorListener = ws.on('error', pass)
10
16
  let removeCloseListener = ws.once('close', closeListener)
11
17
 
12
18
  return () => {
13
19
  controller.abort()
14
20
  removeCloseListener()
21
+ removeErrorListener()
15
22
  }
16
23
 
17
24
  async function closeListener(): Promise<void> {
18
25
  while (true) {
19
26
  if (controller.signal.aborted) return
20
27
 
21
- await delay(timeout)
28
+ await delay(reconnectInterval)
22
29
  if (controller.signal.aborted) return
23
30
 
24
31
  try {
25
32
  await waitForFunction(() => ws.getState() === State.Closed)
26
- await ws.connect()
33
+ await ws.connect(
34
+ connectTimeout
35
+ ? timeoutSignal(connectTimeout)
36
+ : undefined
37
+ )
27
38
  if (controller.signal.aborted) return
28
39
 
29
40
  removeCloseListener = ws.once('close', closeListener)