extra-native-websocket 0.4.0 → 0.4.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.
- package/README.md +8 -3
- package/lib/extra-native-websocket.d.ts +1 -1
- package/lib/extra-native-websocket.js +38 -10
- package/lib/extra-native-websocket.js.map +1 -1
- package/lib/utils/auto-reconnect-with-exponential-back-off.d.ts +2 -1
- package/lib/utils/auto-reconnect-with-exponential-back-off.js +5 -2
- package/lib/utils/auto-reconnect-with-exponential-back-off.js.map +1 -1
- package/lib/utils/auto-reconnect.d.ts +1 -1
- package/lib/utils/auto-reconnect.js +8 -4
- package/lib/utils/auto-reconnect.js.map +1 -1
- package/package.json +7 -9
- package/src/extra-native-websocket.ts +44 -10
- package/src/utils/auto-reconnect-with-exponential-back-off.ts +8 -1
- package/src/utils/auto-reconnect.ts +15 -4
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,7 +54,11 @@ class ExtraNativeWebSocket extends Emitter<{
|
|
|
54
54
|
|
|
55
55
|
### autoReconnect
|
|
56
56
|
```ts
|
|
57
|
-
function autoReconnect(
|
|
57
|
+
function autoReconnect(
|
|
58
|
+
ws: ExtraNativeWebSocket
|
|
59
|
+
, reconnectTimeout?: number = 0
|
|
60
|
+
, connectTimeout?: number
|
|
61
|
+
): () => void
|
|
58
62
|
```
|
|
59
63
|
|
|
60
64
|
### autoReconnectWithExponentialBackOff
|
|
@@ -63,9 +67,10 @@ function autoReonnectWithExponentialBackOff(
|
|
|
63
67
|
ws: ExtraWebSocket
|
|
64
68
|
, options: {
|
|
65
69
|
baseTimeout: number
|
|
66
|
-
maxTimeout?: number
|
|
70
|
+
maxTimeout?: number = Infinity
|
|
67
71
|
factor?: number = 2
|
|
68
72
|
jitter?: boolean = true
|
|
73
|
+
connectTimeout?: number
|
|
69
74
|
}
|
|
70
75
|
): () => void
|
|
71
76
|
```
|
|
@@ -24,7 +24,7 @@ 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
|
+
connect(signal?: AbortSignal): Promise<void>;
|
|
28
28
|
close(code?: number, reason?: string): Promise<void>;
|
|
29
29
|
send(data: Data): void;
|
|
30
30
|
}
|
|
@@ -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,54 @@ export class ExtraNativeWebSocket extends Emitter {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
connect() {
|
|
62
|
+
connect(signal) {
|
|
62
63
|
return new Promise((resolve, reject) => {
|
|
64
|
+
signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
|
|
63
65
|
assert(this.getState() === State.Closed, 'WebSocket is not closed');
|
|
64
66
|
const self = this;
|
|
65
|
-
const ws = this.
|
|
67
|
+
const ws = this.createWebSocket();
|
|
68
|
+
this.instance = ws;
|
|
69
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', abortListener, { once: true });
|
|
70
|
+
const destructor = new SyncDestructor();
|
|
66
71
|
ws.addEventListener('error', errorListener, { once: true });
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
destructor.defer(() => ws.removeEventListener('error', errorListener));
|
|
73
|
+
{
|
|
74
|
+
const listener = (event) => this.emit('open', event);
|
|
75
|
+
ws.addEventListener('open', listener);
|
|
76
|
+
destructor.defer(() => ws.removeEventListener('open', listener));
|
|
77
|
+
}
|
|
78
|
+
{
|
|
79
|
+
const listener = (event) => this.emit('message', event);
|
|
80
|
+
ws.addEventListener('message', listener);
|
|
81
|
+
destructor.defer(() => ws.removeEventListener('message', listener));
|
|
82
|
+
}
|
|
83
|
+
{
|
|
84
|
+
const listener = (event) => this.emit('error', event);
|
|
85
|
+
ws.addEventListener('error', listener);
|
|
86
|
+
destructor.defer(() => ws.removeEventListener('error', listener));
|
|
87
|
+
}
|
|
88
|
+
{
|
|
89
|
+
const listener = (event) => this.emit('close', event);
|
|
90
|
+
ws.addEventListener('close', listener);
|
|
91
|
+
destructor.defer(() => ws.removeEventListener('close', listener));
|
|
92
|
+
}
|
|
71
93
|
this.setBinaryType(this.binaryType);
|
|
72
94
|
ws.addEventListener('open', openListener, { once: true });
|
|
95
|
+
function abortListener() {
|
|
96
|
+
assert(signal);
|
|
97
|
+
destructor.execute();
|
|
98
|
+
ws.close();
|
|
99
|
+
reject(signal.reason);
|
|
100
|
+
}
|
|
73
101
|
function errorListener(event) {
|
|
74
102
|
ws.addEventListener('close', closeListener, { once: true });
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
103
|
+
function closeListener(event) {
|
|
104
|
+
destructor.execute();
|
|
105
|
+
reject(new WebSocketError(event.code, event.reason));
|
|
106
|
+
}
|
|
78
107
|
}
|
|
79
108
|
function openListener(event) {
|
|
80
109
|
ws.removeEventListener('error', errorListener);
|
|
81
|
-
ws.removeEventListener('close', closeListener);
|
|
82
110
|
for (let size = self.unsentMessages.size; size--;) {
|
|
83
111
|
self.send(self.unsentMessages.dequeue());
|
|
84
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extra-native-websocket.js","sourceRoot":"","sources":["../src/extra-native-websocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"extra-native-websocket.js","sourceRoot":"","sources":["../src/extra-native-websocket.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ExtraNativeWebSocket } from "../extra-native-websocket.js";
|
|
2
|
-
export declare function autoReconnectWithExponentialBackOff(ws: ExtraNativeWebSocket, { baseTimeout, maxTimeout, factor, jitter }: {
|
|
2
|
+
export declare function autoReconnectWithExponentialBackOff(ws: ExtraNativeWebSocket, { baseTimeout, maxTimeout, factor, jitter, connectTimeout }: {
|
|
3
3
|
baseTimeout: number;
|
|
4
4
|
maxTimeout?: number;
|
|
5
5
|
factor?: number;
|
|
6
6
|
jitter?: boolean;
|
|
7
|
+
connectTimeout?: number;
|
|
7
8
|
}): () => void;
|
|
@@ -3,7 +3,8 @@ 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
|
-
|
|
6
|
+
import { AbortController, timeoutSignal } from 'extra-abort';
|
|
7
|
+
export function autoReconnectWithExponentialBackOff(ws, { baseTimeout, maxTimeout = Infinity, factor = 2, jitter = true, connectTimeout }) {
|
|
7
8
|
const controller = new AbortController();
|
|
8
9
|
const removeErrorListener = ws.on('error', pass);
|
|
9
10
|
let removeCloseListener = ws.once('close', closeListener);
|
|
@@ -28,7 +29,9 @@ export function autoReconnectWithExponentialBackOff(ws, { baseTimeout, maxTimeou
|
|
|
28
29
|
return;
|
|
29
30
|
try {
|
|
30
31
|
await waitForFunction(() => ws.getState() === State.Closed);
|
|
31
|
-
await ws.connect(
|
|
32
|
+
await ws.connect(connectTimeout
|
|
33
|
+
? timeoutSignal(connectTimeout)
|
|
34
|
+
: undefined);
|
|
32
35
|
if (controller.signal.aborted)
|
|
33
36
|
return;
|
|
34
37
|
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;
|
|
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,WAAW,EACX,UAAU,GAAG,QAAQ,EACrB,MAAM,GAAG,CAAC,EACV,MAAM,GAAG,IAAI,EACb,cAAc,EAOf;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,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,
|
|
2
|
+
export declare function autoReconnect(ws: ExtraNativeWebSocket, reconnectTimeout?: number, connectTimeout?: number): () => void;
|
|
@@ -1,25 +1,29 @@
|
|
|
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,
|
|
6
|
+
export function autoReconnect(ws, reconnectTimeout = 0, connectTimeout) {
|
|
7
7
|
const controller = new AbortController();
|
|
8
|
+
const removeErrorListener = ws.on('error', pass);
|
|
8
9
|
let removeCloseListener = ws.once('close', closeListener);
|
|
9
10
|
return () => {
|
|
10
11
|
controller.abort();
|
|
11
12
|
removeCloseListener();
|
|
13
|
+
removeErrorListener();
|
|
12
14
|
};
|
|
13
15
|
async function closeListener() {
|
|
14
16
|
while (true) {
|
|
15
17
|
if (controller.signal.aborted)
|
|
16
18
|
return;
|
|
17
|
-
await delay(
|
|
19
|
+
await delay(reconnectTimeout);
|
|
18
20
|
if (controller.signal.aborted)
|
|
19
21
|
return;
|
|
20
22
|
try {
|
|
21
23
|
await waitForFunction(() => ws.getState() === State.Closed);
|
|
22
|
-
await ws.connect(
|
|
24
|
+
await ws.connect(connectTimeout
|
|
25
|
+
? timeoutSignal(connectTimeout)
|
|
26
|
+
: undefined);
|
|
23
27
|
if (controller.signal.aborted)
|
|
24
28
|
return;
|
|
25
29
|
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;
|
|
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,mBAA2B,CAAC,EAC5B,cAAuB;IAEvB,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,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAM;YAErC,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAA;YAC7B,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.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"files": [
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"prepare": "ts-patch install -s",
|
|
22
22
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
|
|
23
|
-
"test": "
|
|
24
|
-
"test:debug": "cross-env NODE_OPTIONS=--experimental-vm-modules node --inspect-brk node_modules/.bin/jest --runInBand --config jest.config.cjs",
|
|
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,28 +33,26 @@
|
|
|
34
33
|
}
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
|
-
"@blackglory/jest-resolver": "^0.3.1",
|
|
38
36
|
"@commitlint/cli": "^18.4.3",
|
|
39
37
|
"@commitlint/config-conventional": "^18.4.3",
|
|
40
|
-
"@types/jest": "^29.5.11",
|
|
41
38
|
"@types/ws": "^8.5.10",
|
|
42
39
|
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
|
43
40
|
"@typescript-eslint/parser": "^6.13.2",
|
|
44
41
|
"cross-env": "^7.0.3",
|
|
45
42
|
"eslint": "^8.55.0",
|
|
46
43
|
"husky": "4",
|
|
47
|
-
"
|
|
48
|
-
"jest-environment-jsdom": "^29.7.0",
|
|
49
|
-
"jest-resolve": "^29.7.0",
|
|
44
|
+
"jsdom": "^23.0.1",
|
|
50
45
|
"npm-run-all": "^4.1.5",
|
|
51
46
|
"return-style": "^3.0.1",
|
|
52
47
|
"rimraf": "^5.0.5",
|
|
53
48
|
"standard-version": "^9.5.0",
|
|
54
|
-
"ts-jest": "^29.1.1",
|
|
55
49
|
"ts-patch": "^3.1.1",
|
|
56
50
|
"tslib": "^2.6.2",
|
|
57
51
|
"typescript": "^5.3.3",
|
|
58
52
|
"typescript-transform-paths": "^3.4.6",
|
|
53
|
+
"vite": "^5.0.7",
|
|
54
|
+
"vite-tsconfig-paths": "^4.2.2",
|
|
55
|
+
"vitest": "^1.0.4",
|
|
59
56
|
"ws": "^8.14.2"
|
|
60
57
|
},
|
|
61
58
|
"dependencies": {
|
|
@@ -64,6 +61,7 @@
|
|
|
64
61
|
"@blackglory/structures": "^0.13.4",
|
|
65
62
|
"@blackglory/wait-for": "^0.7.4",
|
|
66
63
|
"extra-abort": "^0.3.7",
|
|
64
|
+
"extra-defer": "^0.3.0",
|
|
67
65
|
"extra-promise": "^6.0.8"
|
|
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.
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
139
|
+
|
|
106
140
|
for (let size = self.unsentMessages.size; size--;) {
|
|
107
141
|
self.send(self.unsentMessages.dequeue()!)
|
|
108
142
|
}
|
|
@@ -3,6 +3,7 @@ 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
|
|
@@ -11,11 +12,13 @@ export function autoReconnectWithExponentialBackOff(
|
|
|
11
12
|
, maxTimeout = Infinity
|
|
12
13
|
, factor = 2
|
|
13
14
|
, jitter = true
|
|
15
|
+
, connectTimeout
|
|
14
16
|
}: {
|
|
15
17
|
baseTimeout: number
|
|
16
18
|
maxTimeout?: number
|
|
17
19
|
factor?: number
|
|
18
20
|
jitter?: boolean
|
|
21
|
+
connectTimeout?: number
|
|
19
22
|
}
|
|
20
23
|
): () => void {
|
|
21
24
|
const controller = new AbortController()
|
|
@@ -46,7 +49,11 @@ export function autoReconnectWithExponentialBackOff(
|
|
|
46
49
|
|
|
47
50
|
try {
|
|
48
51
|
await waitForFunction(() => ws.getState() === State.Closed)
|
|
49
|
-
await ws.connect(
|
|
52
|
+
await ws.connect(
|
|
53
|
+
connectTimeout
|
|
54
|
+
? timeoutSignal(connectTimeout)
|
|
55
|
+
: undefined
|
|
56
|
+
)
|
|
50
57
|
if (controller.signal.aborted) return
|
|
51
58
|
|
|
52
59
|
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(
|
|
7
|
+
export function autoReconnect(
|
|
8
|
+
ws: ExtraNativeWebSocket
|
|
9
|
+
, reconnectTimeout: 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(
|
|
28
|
+
await delay(reconnectTimeout)
|
|
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)
|