@lowerdeck/websocket-client 1.0.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/.turbo/turbo-build.log +12 -0
- package/.turbo/turbo-test.log +11 -0
- package/README.md +74 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.modern.js +2 -0
- package/dist/index.modern.js.map +1 -0
- package/dist/index.module.js +2 -0
- package/dist/index.module.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +33 -0
- package/src/index.ts +93 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
[0m[2m[35m$[0m [2m[1mmicrobundle[0m
|
|
3
|
+
No name was provided for external module '@lowerdeck/emitter' in output.globals – guessing 'emitter'
|
|
4
|
+
[34mBuild "@lowerdeck/websocket-client" to dist:[39m
|
|
5
|
+
[32m840 B[39m: [37mindex.cjs[39m.gz
|
|
6
|
+
[32m735 B[39m: [37mindex.cjs[39m.br
|
|
7
|
+
[32m518 B[39m: [37mindex.modern.js[39m.gz
|
|
8
|
+
[32m453 B[39m: [37mindex.modern.js[39m.br
|
|
9
|
+
[32m844 B[39m: [37mindex.module.js[39m.gz
|
|
10
|
+
[32m737 B[39m: [37mindex.module.js[39m.br
|
|
11
|
+
[32m913 B[39m: [37mindex.umd.js[39m.gz
|
|
12
|
+
[32m802 B[39m: [37mindex.umd.js[39m.br
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
[0m[2m[35m$[0m [2m[1mvitest run --passWithNoTests[0m
|
|
3
|
+
[?25l
|
|
4
|
+
[1m[46m RUN [49m[22m [36mv3.2.4 [39m[90m/Users/tobias/code/metorial/metorial-enterprise/oss/src/packages/shared/websocket[39m
|
|
5
|
+
|
|
6
|
+
No test files found, exiting with code 0
|
|
7
|
+
|
|
8
|
+
[2minclude: [22m[33m**/*.{test,spec}.?(c|m)[jt]s?(x)[39m
|
|
9
|
+
[2mexclude: [22m[33m**/node_modules/**[2m, [22m**/dist/**[2m, [22m**/cypress/**[2m, [22m**/.{idea,git,cache,output,temp}/**[2m, [22m**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*[39m
|
|
10
|
+
|
|
11
|
+
[?25h
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# `@lowerdeck/websocket-client`
|
|
2
|
+
|
|
3
|
+
Auto-reconnecting WebSocket client with exponential backoff. Handles connection failures and provides a familiar event-driven API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lowerdeck/websocket-client
|
|
9
|
+
yarn add @lowerdeck/websocket-client
|
|
10
|
+
bun add @lowerdeck/websocket-client
|
|
11
|
+
pnpm add @lowerdeck/websocket-client
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { ReconnectingWebSocketClient } from '@lowerdeck/websocket-client';
|
|
18
|
+
|
|
19
|
+
// Create client with automatic reconnection
|
|
20
|
+
const ws = new ReconnectingWebSocketClient('wss://example.com/socket', {
|
|
21
|
+
protocols: ['v1'],
|
|
22
|
+
maxAttempts: 5,
|
|
23
|
+
timeout: 5000,
|
|
24
|
+
onReconnect: (attempt) => {
|
|
25
|
+
console.log(`Reconnecting... Attempt ${attempt}`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Listen for events
|
|
30
|
+
ws.addEventListener('open', () => {
|
|
31
|
+
console.log('Connected');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
ws.addEventListener('message', (event) => {
|
|
35
|
+
console.log('Received:', event.data);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
ws.addEventListener('close', () => {
|
|
39
|
+
console.log('Disconnected');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
ws.addEventListener('error', (error) => {
|
|
43
|
+
console.error('Error:', error);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
ws.addEventListener('maximum', () => {
|
|
47
|
+
console.error('Max reconnection attempts reached');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Send messages
|
|
51
|
+
ws.send('Hello server');
|
|
52
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
53
|
+
|
|
54
|
+
// Close connection
|
|
55
|
+
ws.close();
|
|
56
|
+
|
|
57
|
+
// Check connection state
|
|
58
|
+
console.log(ws.readyState); // 0: CONNECTING, 1: OPEN, 2: CLOSING, 3: CLOSED
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Reconnection Behavior
|
|
62
|
+
|
|
63
|
+
- Automatically reconnects on connection loss
|
|
64
|
+
- Exponential backoff between attempts
|
|
65
|
+
- Maximum retry attempts configurable
|
|
66
|
+
- Manual close prevents reconnection
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
This project is licensed under the Apache License 2.0.
|
|
71
|
+
|
|
72
|
+
<div align="center">
|
|
73
|
+
<sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
|
|
74
|
+
</div>
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=require("@lowerdeck/emitter");function e(t){var e=function(t){if("object"!=typeof t||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var i=e.call(t,"string");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t);return"symbol"==typeof e?e:e+""}exports.ReconnectingWebSocketClient=/*#__PURE__*/function(){function i(e,i){var n;void 0===i&&(i={}),this.ws=void 0,this.attempts=0,this.timer=null,this.url=void 0,this.opts=void 0,this.maxAttempts=void 0,this.emitter=new t.Emitter,this.url=e,this.opts=i,this.maxAttempts=null!=(n=i.maxAttempts)?n:Infinity,this.open()}var n,r,o=i.prototype;return o.open=function(){var t,e=this;this.ws=new WebSocket(this.url,null!=(t=this.opts.protocols)?t:[]),this.ws.addEventListener("open",function(t){e.attempts=0,e.emitter.emit("open",t)}),this.ws.addEventListener("close",function(t){e.reconnect(t)}),this.ws.addEventListener("error",function(t){"ECONNREFUSED"===(null==t?void 0:t.code)?e.reconnect(t):e.emitter.emit("error",t)}),this.ws.addEventListener("message",function(t){e.emitter.emit("message",t)})},o.reconnect=function(t){var e,i=this;this.timer&&clearTimeout(this.timer),this.attempts++<this.maxAttempts?this.timer=setTimeout(function(){null==i.opts.onReconnect||i.opts.onReconnect(t),i.open()},null!=(e=this.opts.timeout)?e:1e3):(this.emitter.emit("maximum",t),this.emitter.emit("close",t),this.emitter.clear())},o.send=function(t){this.ws.send(t)},o.close=function(t,e){void 0===t&&(t=1e3),this.timer&&clearTimeout(this.timer),this.emitter.clear(),this.ws.close(t,e)},o.addEventListener=function(t,e){return this.emitter.on(t,e)},n=i,(r=[{key:"readyState",get:function(){return this.ws.readyState}}])&&function(t,i){for(var n=0;n<i.length;n++){var r=i[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,e(r.key),r)}}(n.prototype,r),Object.defineProperty(n,"prototype",{writable:!1}),n}();
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import { Emitter } from '@lowerdeck/emitter';\n\n// Highly inspired by https://github.com/lukeed/sockette/blob/master/src/index.js\n\ninterface ReconnectingWebSocketOptions {\n protocols?: string | string[];\n maxAttempts?: number;\n timeout?: number;\n\n onReconnect?: (event: Event) => void;\n}\n\nexport class ReconnectingWebSocketClient {\n private ws!: WebSocket;\n private attempts = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly url: string;\n private readonly opts: ReconnectingWebSocketOptions;\n private readonly maxAttempts: number;\n private emitter = new Emitter<any>();\n\n constructor(url: string, opts: ReconnectingWebSocketOptions = {}) {\n this.url = url;\n this.opts = opts;\n this.maxAttempts = opts.maxAttempts ?? Infinity;\n\n this.open();\n }\n\n private open(): void {\n this.ws = new WebSocket(this.url, this.opts.protocols ?? []);\n\n this.ws.addEventListener('open', event => {\n this.attempts = 0;\n this.emitter.emit('open', event);\n });\n\n this.ws.addEventListener('close', event => {\n // let shouldReconnect = ![1000, 1001, 1005].includes(event.code);\n // if (shouldReconnect) {\n this.reconnect(event);\n // } else {\n // this.emitter.emit('close', event);\n // }\n });\n\n this.ws.addEventListener('error', event => {\n let error = event as any;\n if (error?.code === 'ECONNREFUSED') {\n this.reconnect(event);\n } else {\n this.emitter.emit('error', event);\n }\n });\n\n this.ws.addEventListener('message', event => {\n this.emitter.emit('message', event);\n });\n }\n\n private reconnect(event: Event): void {\n if (this.timer) clearTimeout(this.timer);\n\n if (this.attempts++ < this.maxAttempts) {\n this.timer = setTimeout(() => {\n this.opts.onReconnect?.(event);\n this.open();\n }, this.opts.timeout ?? 1000);\n } else {\n this.emitter.emit('maximum', event);\n this.emitter.emit('close', event);\n this.emitter.clear();\n }\n }\n\n send(data: string | ArrayBufferLike): void {\n this.ws.send(data);\n }\n\n close(code: number = 1000, reason?: string): void {\n if (this.timer) clearTimeout(this.timer);\n this.emitter.clear();\n this.ws.close(code, reason);\n }\n\n addEventListener(event: string, callback: (data: any) => void) {\n return this.emitter.on(event, callback);\n }\n\n get readyState() {\n return this.ws.readyState;\n }\n}\n"],"names":["ReconnectingWebSocketClient","url","opts","_opts$maxAttempts","this","ws","attempts","timer","maxAttempts","emitter","Emitter","Infinity","open","_proto","prototype","_this$opts$protocols","_this","WebSocket","protocols","addEventListener","event","emit","reconnect","code","_this2","_this$opts$timeout","clearTimeout","setTimeout","onReconnect","timeout","clear","send","data","close","reason","callback","on","key","get","readyState"],"mappings":"oXAqBE,WAAA,SAAAA,EAAYC,EAAaC,GAAuCC,IAAAA,OAAvCD,IAAAA,IAAAA,EAAqC,CAAE,GAAAE,KARxDC,QAAE,EAAAD,KACFE,SAAW,EACXC,KAAAA,MAA8C,KACrCN,KAAAA,SACAC,EAAAA,KAAAA,UACAM,EAAAA,KAAAA,iBACTC,EAAAA,KAAAA,QAAU,IAAIC,EAAcA,QAGlCN,KAAKH,IAAMA,EACXG,KAAKF,KAAOA,EACZE,KAAKI,YAA8BL,OAAnBA,EAAGD,EAAKM,aAAWL,EAAIQ,SAEvCP,KAAKQ,MACP,CAAC,QAAAC,EAAAb,EAAAc,UA4DA,OA5DAD,EAEOD,KAAA,WAAIG,IAAAA,EAAAC,EACVZ,KAAAA,KAAKC,GAAK,IAAIY,UAAUb,KAAKH,IAAwBc,OAArBA,EAAEX,KAAKF,KAAKgB,WAASH,EAAI,IAEzDX,KAAKC,GAAGc,iBAAiB,OAAQ,SAAAC,GAC/BJ,EAAKV,SAAW,EAChBU,EAAKP,QAAQY,KAAK,OAAQD,EAC5B,GAEAhB,KAAKC,GAAGc,iBAAiB,QAAS,SAAAC,GAGhCJ,EAAKM,UAAUF,EAIjB,GAEAhB,KAAKC,GAAGc,iBAAiB,QAAS,SAAAC,GAEZ,kBAAX,MADGA,OACH,EADGA,EACDG,MACTP,EAAKM,UAAUF,GAEfJ,EAAKP,QAAQY,KAAK,QAASD,EAE/B,GAEAhB,KAAKC,GAAGc,iBAAiB,UAAW,SAAAC,GAClCJ,EAAKP,QAAQY,KAAK,UAAWD,EAC/B,EACF,EAACP,EAEOS,UAAA,SAAUF,GAAYI,IAGYC,EAHZD,EAC5BpB,KAAIA,KAAKG,OAAOmB,aAAatB,KAAKG,OAE9BH,KAAKE,WAAaF,KAAKI,YACzBJ,KAAKG,MAAQoB,WAAW,WACtBH,MAAAA,EAAKtB,KAAK0B,aAAVJ,EAAKtB,KAAK0B,YAAcR,GACxBI,EAAKZ,MACP,EAAoB,OAAnBa,EAAErB,KAAKF,KAAK2B,SAAOJ,EAAI,MAExBrB,KAAKK,QAAQY,KAAK,UAAWD,GAC7BhB,KAAKK,QAAQY,KAAK,QAASD,GAC3BhB,KAAKK,QAAQqB,QAEjB,EAACjB,EAEDkB,KAAA,SAAKC,GACH5B,KAAKC,GAAG0B,KAAKC,EACf,EAACnB,EAEDoB,MAAA,SAAMV,EAAqBW,QAArBX,IAAAA,IAAAA,EAAe,KACfnB,KAAKG,OAAOmB,aAAatB,KAAKG,OAClCH,KAAKK,QAAQqB,QACb1B,KAAKC,GAAG4B,MAAMV,EAAMW,EACtB,EAACrB,EAEDM,iBAAA,SAAiBC,EAAee,GAC9B,OAAO/B,KAAKK,QAAQ2B,GAAGhB,EAAOe,EAChC,IAACnC,KAAA,CAAA,CAAAqC,IAAA,aAAAC,IAED,WACE,OAAOlC,KAAKC,GAAGkC,UACjB,iPAAC,CAtED"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
interface ReconnectingWebSocketOptions {
|
|
2
|
+
protocols?: string | string[];
|
|
3
|
+
maxAttempts?: number;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
onReconnect?: (event: Event) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare class ReconnectingWebSocketClient {
|
|
8
|
+
private ws;
|
|
9
|
+
private attempts;
|
|
10
|
+
private timer;
|
|
11
|
+
private readonly url;
|
|
12
|
+
private readonly opts;
|
|
13
|
+
private readonly maxAttempts;
|
|
14
|
+
private emitter;
|
|
15
|
+
constructor(url: string, opts?: ReconnectingWebSocketOptions);
|
|
16
|
+
private open;
|
|
17
|
+
private reconnect;
|
|
18
|
+
send(data: string | ArrayBufferLike): void;
|
|
19
|
+
close(code?: number, reason?: string): void;
|
|
20
|
+
addEventListener(event: string, callback: (data: any) => void): () => void;
|
|
21
|
+
get readyState(): number;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,UAAU,4BAA4B;IACpC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACtC;AAED,qBAAa,2BAA2B;IACtC,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,OAAO,CAAsB;gBAEzB,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,4BAAiC;IAQhE,OAAO,CAAC,IAAI;IA+BZ,OAAO,CAAC,SAAS;IAejB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAI1C,KAAK,CAAC,IAAI,GAAE,MAAa,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAMjD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI;IAI7D,IAAI,UAAU,WAEb;CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Emitter as t}from"@lowerdeck/emitter";class e{constructor(e,s={}){var i;this.ws=void 0,this.attempts=0,this.timer=null,this.url=void 0,this.opts=void 0,this.maxAttempts=void 0,this.emitter=new t,this.url=e,this.opts=s,this.maxAttempts=null!=(i=s.maxAttempts)?i:Infinity,this.open()}open(){var t;this.ws=new WebSocket(this.url,null!=(t=this.opts.protocols)?t:[]),this.ws.addEventListener("open",t=>{this.attempts=0,this.emitter.emit("open",t)}),this.ws.addEventListener("close",t=>{this.reconnect(t)}),this.ws.addEventListener("error",t=>{"ECONNREFUSED"===(null==t?void 0:t.code)?this.reconnect(t):this.emitter.emit("error",t)}),this.ws.addEventListener("message",t=>{this.emitter.emit("message",t)})}reconnect(t){var e;this.timer&&clearTimeout(this.timer),this.attempts++<this.maxAttempts?this.timer=setTimeout(()=>{var e,s;null==(e=(s=this.opts).onReconnect)||e.call(s,t),this.open()},null!=(e=this.opts.timeout)?e:1e3):(this.emitter.emit("maximum",t),this.emitter.emit("close",t),this.emitter.clear())}send(t){this.ws.send(t)}close(t=1e3,e){this.timer&&clearTimeout(this.timer),this.emitter.clear(),this.ws.close(t,e)}addEventListener(t,e){return this.emitter.on(t,e)}get readyState(){return this.ws.readyState}}export{e as ReconnectingWebSocketClient};
|
|
2
|
+
//# sourceMappingURL=index.modern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.modern.js","sources":["../src/index.ts"],"sourcesContent":["import { Emitter } from '@lowerdeck/emitter';\n\n// Highly inspired by https://github.com/lukeed/sockette/blob/master/src/index.js\n\ninterface ReconnectingWebSocketOptions {\n protocols?: string | string[];\n maxAttempts?: number;\n timeout?: number;\n\n onReconnect?: (event: Event) => void;\n}\n\nexport class ReconnectingWebSocketClient {\n private ws!: WebSocket;\n private attempts = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly url: string;\n private readonly opts: ReconnectingWebSocketOptions;\n private readonly maxAttempts: number;\n private emitter = new Emitter<any>();\n\n constructor(url: string, opts: ReconnectingWebSocketOptions = {}) {\n this.url = url;\n this.opts = opts;\n this.maxAttempts = opts.maxAttempts ?? Infinity;\n\n this.open();\n }\n\n private open(): void {\n this.ws = new WebSocket(this.url, this.opts.protocols ?? []);\n\n this.ws.addEventListener('open', event => {\n this.attempts = 0;\n this.emitter.emit('open', event);\n });\n\n this.ws.addEventListener('close', event => {\n // let shouldReconnect = ![1000, 1001, 1005].includes(event.code);\n // if (shouldReconnect) {\n this.reconnect(event);\n // } else {\n // this.emitter.emit('close', event);\n // }\n });\n\n this.ws.addEventListener('error', event => {\n let error = event as any;\n if (error?.code === 'ECONNREFUSED') {\n this.reconnect(event);\n } else {\n this.emitter.emit('error', event);\n }\n });\n\n this.ws.addEventListener('message', event => {\n this.emitter.emit('message', event);\n });\n }\n\n private reconnect(event: Event): void {\n if (this.timer) clearTimeout(this.timer);\n\n if (this.attempts++ < this.maxAttempts) {\n this.timer = setTimeout(() => {\n this.opts.onReconnect?.(event);\n this.open();\n }, this.opts.timeout ?? 1000);\n } else {\n this.emitter.emit('maximum', event);\n this.emitter.emit('close', event);\n this.emitter.clear();\n }\n }\n\n send(data: string | ArrayBufferLike): void {\n this.ws.send(data);\n }\n\n close(code: number = 1000, reason?: string): void {\n if (this.timer) clearTimeout(this.timer);\n this.emitter.clear();\n this.ws.close(code, reason);\n }\n\n addEventListener(event: string, callback: (data: any) => void) {\n return this.emitter.on(event, callback);\n }\n\n get readyState() {\n return this.ws.readyState;\n }\n}\n"],"names":["ReconnectingWebSocketClient","constructor","url","opts","_opts$maxAttempts","this","ws","attempts","timer","maxAttempts","emitter","Emitter","Infinity","open","_this$opts$protocols","WebSocket","protocols","addEventListener","event","emit","reconnect","error","code","_this$opts$timeout","clearTimeout","setTimeout","_this$opts$onReconnec","_this$opts","onReconnect","call","timeout","clear","send","data","close","reason","callback","on","readyState"],"mappings":"6CAYa,MAAAA,EASXC,WAAAA,CAAYC,EAAaC,EAAqC,CAAE,OAAAC,EAAAC,KARxDC,QAAE,EAAAD,KACFE,SAAW,EACXC,KAAAA,MAA8C,KAAIH,KACzCH,SACAC,EAAAA,KAAAA,UACAM,EAAAA,KAAAA,iBACTC,EAAAA,KAAAA,QAAU,IAAIC,EAGpBN,KAAKH,IAAMA,EACXG,KAAKF,KAAOA,EACZE,KAAKI,YAA8B,OAAnBL,EAAGD,EAAKM,aAAWL,EAAIQ,SAEvCP,KAAKQ,MACP,CAEQA,IAAAA,GAAI,IAAAC,EACVT,KAAKC,GAAK,IAAIS,UAAUV,KAAKH,IAAwBY,OAArBA,EAAET,KAAKF,KAAKa,WAASF,EAAI,IAEzDT,KAAKC,GAAGW,iBAAiB,OAAQC,IAC/Bb,KAAKE,SAAW,EAChBF,KAAKK,QAAQS,KAAK,OAAQD,KAG5Bb,KAAKC,GAAGW,iBAAiB,QAASC,IAGhCb,KAAKe,UAAUF,KAMjBb,KAAKC,GAAGW,iBAAiB,QAASC,IAEZ,kBAAhBG,MADQH,OACRG,EADQH,EACDI,MACTjB,KAAKe,UAAUF,GAEfb,KAAKK,QAAQS,KAAK,QAASD,KAI/Bb,KAAKC,GAAGW,iBAAiB,UAAWC,IAClCb,KAAKK,QAAQS,KAAK,UAAWD,IAEjC,CAEQE,SAAAA,CAAUF,GAGwBK,IAAAA,EAFpClB,KAAKG,OAAOgB,aAAanB,KAAKG,OAE9BH,KAAKE,WAAaF,KAAKI,YACzBJ,KAAKG,MAAQiB,WAAW,KAAK,IAAAC,EAAAC,SAC3BD,GAAAC,EAAAtB,KAAKF,MAAKyB,cAAVF,EAAAG,KAAAF,EAAwBT,GACxBb,KAAKQ,QACa,OAAnBU,EAAElB,KAAKF,KAAK2B,SAAOP,EAAI,MAExBlB,KAAKK,QAAQS,KAAK,UAAWD,GAC7Bb,KAAKK,QAAQS,KAAK,QAASD,GAC3Bb,KAAKK,QAAQqB,QAEjB,CAEAC,IAAAA,CAAKC,GACH5B,KAAKC,GAAG0B,KAAKC,EACf,CAEAC,KAAAA,CAAMZ,EAAe,IAAMa,GACrB9B,KAAKG,OAAOgB,aAAanB,KAAKG,OAClCH,KAAKK,QAAQqB,QACb1B,KAAKC,GAAG4B,MAAMZ,EAAMa,EACtB,CAEAlB,gBAAAA,CAAiBC,EAAekB,GAC9B,OAAW/B,KAACK,QAAQ2B,GAAGnB,EAAOkB,EAChC,CAEA,cAAIE,GACF,YAAYhC,GAAGgC,UACjB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Emitter as t}from"@lowerdeck/emitter";function e(t){var e=function(t){if("object"!=typeof t||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var i=e.call(t,"string");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t);return"symbol"==typeof e?e:e+""}var i=/*#__PURE__*/function(){function i(e,i){var r;void 0===i&&(i={}),this.ws=void 0,this.attempts=0,this.timer=null,this.url=void 0,this.opts=void 0,this.maxAttempts=void 0,this.emitter=new t,this.url=e,this.opts=i,this.maxAttempts=null!=(r=i.maxAttempts)?r:Infinity,this.open()}var r,n,o=i.prototype;return o.open=function(){var t,e=this;this.ws=new WebSocket(this.url,null!=(t=this.opts.protocols)?t:[]),this.ws.addEventListener("open",function(t){e.attempts=0,e.emitter.emit("open",t)}),this.ws.addEventListener("close",function(t){e.reconnect(t)}),this.ws.addEventListener("error",function(t){"ECONNREFUSED"===(null==t?void 0:t.code)?e.reconnect(t):e.emitter.emit("error",t)}),this.ws.addEventListener("message",function(t){e.emitter.emit("message",t)})},o.reconnect=function(t){var e,i=this;this.timer&&clearTimeout(this.timer),this.attempts++<this.maxAttempts?this.timer=setTimeout(function(){null==i.opts.onReconnect||i.opts.onReconnect(t),i.open()},null!=(e=this.opts.timeout)?e:1e3):(this.emitter.emit("maximum",t),this.emitter.emit("close",t),this.emitter.clear())},o.send=function(t){this.ws.send(t)},o.close=function(t,e){void 0===t&&(t=1e3),this.timer&&clearTimeout(this.timer),this.emitter.clear(),this.ws.close(t,e)},o.addEventListener=function(t,e){return this.emitter.on(t,e)},r=i,(n=[{key:"readyState",get:function(){return this.ws.readyState}}])&&function(t,i){for(var r=0;r<i.length;r++){var n=i[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,e(n.key),n)}}(r.prototype,n),Object.defineProperty(r,"prototype",{writable:!1}),r}();export{i as ReconnectingWebSocketClient};
|
|
2
|
+
//# sourceMappingURL=index.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.module.js","sources":["../src/index.ts"],"sourcesContent":["import { Emitter } from '@lowerdeck/emitter';\n\n// Highly inspired by https://github.com/lukeed/sockette/blob/master/src/index.js\n\ninterface ReconnectingWebSocketOptions {\n protocols?: string | string[];\n maxAttempts?: number;\n timeout?: number;\n\n onReconnect?: (event: Event) => void;\n}\n\nexport class ReconnectingWebSocketClient {\n private ws!: WebSocket;\n private attempts = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly url: string;\n private readonly opts: ReconnectingWebSocketOptions;\n private readonly maxAttempts: number;\n private emitter = new Emitter<any>();\n\n constructor(url: string, opts: ReconnectingWebSocketOptions = {}) {\n this.url = url;\n this.opts = opts;\n this.maxAttempts = opts.maxAttempts ?? Infinity;\n\n this.open();\n }\n\n private open(): void {\n this.ws = new WebSocket(this.url, this.opts.protocols ?? []);\n\n this.ws.addEventListener('open', event => {\n this.attempts = 0;\n this.emitter.emit('open', event);\n });\n\n this.ws.addEventListener('close', event => {\n // let shouldReconnect = ![1000, 1001, 1005].includes(event.code);\n // if (shouldReconnect) {\n this.reconnect(event);\n // } else {\n // this.emitter.emit('close', event);\n // }\n });\n\n this.ws.addEventListener('error', event => {\n let error = event as any;\n if (error?.code === 'ECONNREFUSED') {\n this.reconnect(event);\n } else {\n this.emitter.emit('error', event);\n }\n });\n\n this.ws.addEventListener('message', event => {\n this.emitter.emit('message', event);\n });\n }\n\n private reconnect(event: Event): void {\n if (this.timer) clearTimeout(this.timer);\n\n if (this.attempts++ < this.maxAttempts) {\n this.timer = setTimeout(() => {\n this.opts.onReconnect?.(event);\n this.open();\n }, this.opts.timeout ?? 1000);\n } else {\n this.emitter.emit('maximum', event);\n this.emitter.emit('close', event);\n this.emitter.clear();\n }\n }\n\n send(data: string | ArrayBufferLike): void {\n this.ws.send(data);\n }\n\n close(code: number = 1000, reason?: string): void {\n if (this.timer) clearTimeout(this.timer);\n this.emitter.clear();\n this.ws.close(code, reason);\n }\n\n addEventListener(event: string, callback: (data: any) => void) {\n return this.emitter.on(event, callback);\n }\n\n get readyState() {\n return this.ws.readyState;\n }\n}\n"],"names":["ReconnectingWebSocketClient","url","opts","_opts$maxAttempts","this","ws","attempts","timer","maxAttempts","emitter","Emitter","Infinity","open","_proto","prototype","_this$opts$protocols","_this","WebSocket","protocols","addEventListener","event","emit","reconnect","code","_this2","_this$opts$timeout","clearTimeout","setTimeout","onReconnect","timeout","clear","send","data","close","reason","callback","on","key","get","readyState"],"mappings":"4UAYa,IAAAA,eASX,WAAA,SAAAA,EAAYC,EAAaC,GAAuCC,IAAAA,OAAvCD,IAAAA,IAAAA,EAAqC,CAAE,GAAAE,KARxDC,QAAE,EAAAD,KACFE,SAAW,EACXC,KAAAA,MAA8C,KACrCN,KAAAA,SACAC,EAAAA,KAAAA,UACAM,EAAAA,KAAAA,iBACTC,EAAAA,KAAAA,QAAU,IAAIC,EAGpBN,KAAKH,IAAMA,EACXG,KAAKF,KAAOA,EACZE,KAAKI,YAA8BL,OAAnBA,EAAGD,EAAKM,aAAWL,EAAIQ,SAEvCP,KAAKQ,MACP,CAAC,QAAAC,EAAAb,EAAAc,UA4DA,OA5DAD,EAEOD,KAAA,WAAIG,IAAAA,EAAAC,EACVZ,KAAAA,KAAKC,GAAK,IAAIY,UAAUb,KAAKH,IAAwBc,OAArBA,EAAEX,KAAKF,KAAKgB,WAASH,EAAI,IAEzDX,KAAKC,GAAGc,iBAAiB,OAAQ,SAAAC,GAC/BJ,EAAKV,SAAW,EAChBU,EAAKP,QAAQY,KAAK,OAAQD,EAC5B,GAEAhB,KAAKC,GAAGc,iBAAiB,QAAS,SAAAC,GAGhCJ,EAAKM,UAAUF,EAIjB,GAEAhB,KAAKC,GAAGc,iBAAiB,QAAS,SAAAC,GAEZ,kBAAX,MADGA,OACH,EADGA,EACDG,MACTP,EAAKM,UAAUF,GAEfJ,EAAKP,QAAQY,KAAK,QAASD,EAE/B,GAEAhB,KAAKC,GAAGc,iBAAiB,UAAW,SAAAC,GAClCJ,EAAKP,QAAQY,KAAK,UAAWD,EAC/B,EACF,EAACP,EAEOS,UAAA,SAAUF,GAAYI,IAGYC,EAHZD,EAC5BpB,KAAIA,KAAKG,OAAOmB,aAAatB,KAAKG,OAE9BH,KAAKE,WAAaF,KAAKI,YACzBJ,KAAKG,MAAQoB,WAAW,WACtBH,MAAAA,EAAKtB,KAAK0B,aAAVJ,EAAKtB,KAAK0B,YAAcR,GACxBI,EAAKZ,MACP,EAAoB,OAAnBa,EAAErB,KAAKF,KAAK2B,SAAOJ,EAAI,MAExBrB,KAAKK,QAAQY,KAAK,UAAWD,GAC7BhB,KAAKK,QAAQY,KAAK,QAASD,GAC3BhB,KAAKK,QAAQqB,QAEjB,EAACjB,EAEDkB,KAAA,SAAKC,GACH5B,KAAKC,GAAG0B,KAAKC,EACf,EAACnB,EAEDoB,MAAA,SAAMV,EAAqBW,QAArBX,IAAAA,IAAAA,EAAe,KACfnB,KAAKG,OAAOmB,aAAatB,KAAKG,OAClCH,KAAKK,QAAQqB,QACb1B,KAAKC,GAAG4B,MAAMV,EAAMW,EACtB,EAACrB,EAEDM,iBAAA,SAAiBC,EAAee,GAC9B,OAAO/B,KAAKK,QAAQ2B,GAAGhB,EAAOe,EAChC,IAACnC,KAAA,CAAA,CAAAqC,IAAA,aAAAC,IAED,WACE,OAAOlC,KAAKC,GAAGkC,UACjB,iPAAC,CAtED"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@lowerdeck/emitter")):"function"==typeof define&&define.amd?define(["exports","@lowerdeck/emitter"],e):e((t||self).websocketClient={},t.emitter)}(this,function(t,e){function i(t){var e=function(t){if("object"!=typeof t||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var i=e.call(t,"string");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t);return"symbol"==typeof e?e:e+""}t.ReconnectingWebSocketClient=/*#__PURE__*/function(){function t(t,i){var n;void 0===i&&(i={}),this.ws=void 0,this.attempts=0,this.timer=null,this.url=void 0,this.opts=void 0,this.maxAttempts=void 0,this.emitter=new e.Emitter,this.url=t,this.opts=i,this.maxAttempts=null!=(n=i.maxAttempts)?n:Infinity,this.open()}var n,o,r=t.prototype;return r.open=function(){var t,e=this;this.ws=new WebSocket(this.url,null!=(t=this.opts.protocols)?t:[]),this.ws.addEventListener("open",function(t){e.attempts=0,e.emitter.emit("open",t)}),this.ws.addEventListener("close",function(t){e.reconnect(t)}),this.ws.addEventListener("error",function(t){"ECONNREFUSED"===(null==t?void 0:t.code)?e.reconnect(t):e.emitter.emit("error",t)}),this.ws.addEventListener("message",function(t){e.emitter.emit("message",t)})},r.reconnect=function(t){var e,i=this;this.timer&&clearTimeout(this.timer),this.attempts++<this.maxAttempts?this.timer=setTimeout(function(){null==i.opts.onReconnect||i.opts.onReconnect(t),i.open()},null!=(e=this.opts.timeout)?e:1e3):(this.emitter.emit("maximum",t),this.emitter.emit("close",t),this.emitter.clear())},r.send=function(t){this.ws.send(t)},r.close=function(t,e){void 0===t&&(t=1e3),this.timer&&clearTimeout(this.timer),this.emitter.clear(),this.ws.close(t,e)},r.addEventListener=function(t,e){return this.emitter.on(t,e)},n=t,(o=[{key:"readyState",get:function(){return this.ws.readyState}}])&&function(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,i(o.key),o)}}(n.prototype,o),Object.defineProperty(n,"prototype",{writable:!1}),n}()});
|
|
2
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/index.ts"],"sourcesContent":["import { Emitter } from '@lowerdeck/emitter';\n\n// Highly inspired by https://github.com/lukeed/sockette/blob/master/src/index.js\n\ninterface ReconnectingWebSocketOptions {\n protocols?: string | string[];\n maxAttempts?: number;\n timeout?: number;\n\n onReconnect?: (event: Event) => void;\n}\n\nexport class ReconnectingWebSocketClient {\n private ws!: WebSocket;\n private attempts = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly url: string;\n private readonly opts: ReconnectingWebSocketOptions;\n private readonly maxAttempts: number;\n private emitter = new Emitter<any>();\n\n constructor(url: string, opts: ReconnectingWebSocketOptions = {}) {\n this.url = url;\n this.opts = opts;\n this.maxAttempts = opts.maxAttempts ?? Infinity;\n\n this.open();\n }\n\n private open(): void {\n this.ws = new WebSocket(this.url, this.opts.protocols ?? []);\n\n this.ws.addEventListener('open', event => {\n this.attempts = 0;\n this.emitter.emit('open', event);\n });\n\n this.ws.addEventListener('close', event => {\n // let shouldReconnect = ![1000, 1001, 1005].includes(event.code);\n // if (shouldReconnect) {\n this.reconnect(event);\n // } else {\n // this.emitter.emit('close', event);\n // }\n });\n\n this.ws.addEventListener('error', event => {\n let error = event as any;\n if (error?.code === 'ECONNREFUSED') {\n this.reconnect(event);\n } else {\n this.emitter.emit('error', event);\n }\n });\n\n this.ws.addEventListener('message', event => {\n this.emitter.emit('message', event);\n });\n }\n\n private reconnect(event: Event): void {\n if (this.timer) clearTimeout(this.timer);\n\n if (this.attempts++ < this.maxAttempts) {\n this.timer = setTimeout(() => {\n this.opts.onReconnect?.(event);\n this.open();\n }, this.opts.timeout ?? 1000);\n } else {\n this.emitter.emit('maximum', event);\n this.emitter.emit('close', event);\n this.emitter.clear();\n }\n }\n\n send(data: string | ArrayBufferLike): void {\n this.ws.send(data);\n }\n\n close(code: number = 1000, reason?: string): void {\n if (this.timer) clearTimeout(this.timer);\n this.emitter.clear();\n this.ws.close(code, reason);\n }\n\n addEventListener(event: string, callback: (data: any) => void) {\n return this.emitter.on(event, callback);\n }\n\n get readyState() {\n return this.ws.readyState;\n }\n}\n"],"names":["ReconnectingWebSocketClient","url","opts","_opts$maxAttempts","this","ws","attempts","timer","maxAttempts","emitter","Emitter","Infinity","open","_proto","prototype","_this$opts$protocols","_this","WebSocket","protocols","addEventListener","event","emit","reconnect","code","_this2","_this$opts$timeout","clearTimeout","setTimeout","onReconnect","timeout","clear","send","data","close","reason","callback","on","key","get","readyState"],"mappings":"knBAqBE,WAAA,SAAAA,EAAYC,EAAaC,GAAuCC,IAAAA,OAAvCD,IAAAA,IAAAA,EAAqC,CAAE,GAAAE,KARxDC,QAAE,EAAAD,KACFE,SAAW,EACXC,KAAAA,MAA8C,KACrCN,KAAAA,SACAC,EAAAA,KAAAA,UACAM,EAAAA,KAAAA,iBACTC,EAAAA,KAAAA,QAAU,IAAIC,EAAcA,QAGlCN,KAAKH,IAAMA,EACXG,KAAKF,KAAOA,EACZE,KAAKI,YAA8BL,OAAnBA,EAAGD,EAAKM,aAAWL,EAAIQ,SAEvCP,KAAKQ,MACP,CAAC,QAAAC,EAAAb,EAAAc,UA4DA,OA5DAD,EAEOD,KAAA,WAAIG,IAAAA,EAAAC,EACVZ,KAAAA,KAAKC,GAAK,IAAIY,UAAUb,KAAKH,IAAwBc,OAArBA,EAAEX,KAAKF,KAAKgB,WAASH,EAAI,IAEzDX,KAAKC,GAAGc,iBAAiB,OAAQ,SAAAC,GAC/BJ,EAAKV,SAAW,EAChBU,EAAKP,QAAQY,KAAK,OAAQD,EAC5B,GAEAhB,KAAKC,GAAGc,iBAAiB,QAAS,SAAAC,GAGhCJ,EAAKM,UAAUF,EAIjB,GAEAhB,KAAKC,GAAGc,iBAAiB,QAAS,SAAAC,GAEZ,kBAAX,MADGA,OACH,EADGA,EACDG,MACTP,EAAKM,UAAUF,GAEfJ,EAAKP,QAAQY,KAAK,QAASD,EAE/B,GAEAhB,KAAKC,GAAGc,iBAAiB,UAAW,SAAAC,GAClCJ,EAAKP,QAAQY,KAAK,UAAWD,EAC/B,EACF,EAACP,EAEOS,UAAA,SAAUF,GAAYI,IAGYC,EAHZD,EAC5BpB,KAAIA,KAAKG,OAAOmB,aAAatB,KAAKG,OAE9BH,KAAKE,WAAaF,KAAKI,YACzBJ,KAAKG,MAAQoB,WAAW,WACtBH,MAAAA,EAAKtB,KAAK0B,aAAVJ,EAAKtB,KAAK0B,YAAcR,GACxBI,EAAKZ,MACP,EAAoB,OAAnBa,EAAErB,KAAKF,KAAK2B,SAAOJ,EAAI,MAExBrB,KAAKK,QAAQY,KAAK,UAAWD,GAC7BhB,KAAKK,QAAQY,KAAK,QAASD,GAC3BhB,KAAKK,QAAQqB,QAEjB,EAACjB,EAEDkB,KAAA,SAAKC,GACH5B,KAAKC,GAAG0B,KAAKC,EACf,EAACnB,EAEDoB,MAAA,SAAMV,EAAqBW,QAArBX,IAAAA,IAAAA,EAAe,KACfnB,KAAKG,OAAOmB,aAAatB,KAAKG,OAClCH,KAAKK,QAAQqB,QACb1B,KAAKC,GAAG4B,MAAMV,EAAMW,EACtB,EAACrB,EAEDM,iBAAA,SAAiBC,EAAee,GAC9B,OAAO/B,KAAKK,QAAQ2B,GAAGhB,EAAOe,EAChC,IAACnC,KAAA,CAAA,CAAAqC,IAAA,aAAAC,IAED,WACE,OAAOlC,KAAKC,GAAGkC,UACjB,iPAAC,CAtED"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lowerdeck/websocket-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"author": "Tobias Herber",
|
|
8
|
+
"license": "Apache 2",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"source": "src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"default": "./dist/index.modern.js"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.module.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"unpkg": "./dist/index.umd.js",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run --passWithNoTests",
|
|
21
|
+
"lint": "prettier src/**/*.ts --check",
|
|
22
|
+
"build": "microbundle"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@lowerdeck/emitter": "^1.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"microbundle": "^0.15.1",
|
|
29
|
+
"@lowerdeck/tsconfig": "^1.0.0",
|
|
30
|
+
"typescript": "^5.8.3",
|
|
31
|
+
"vitest": "^3.1.2"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Emitter } from '@lowerdeck/emitter';
|
|
2
|
+
|
|
3
|
+
// Highly inspired by https://github.com/lukeed/sockette/blob/master/src/index.js
|
|
4
|
+
|
|
5
|
+
interface ReconnectingWebSocketOptions {
|
|
6
|
+
protocols?: string | string[];
|
|
7
|
+
maxAttempts?: number;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
|
|
10
|
+
onReconnect?: (event: Event) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ReconnectingWebSocketClient {
|
|
14
|
+
private ws!: WebSocket;
|
|
15
|
+
private attempts = 0;
|
|
16
|
+
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
17
|
+
private readonly url: string;
|
|
18
|
+
private readonly opts: ReconnectingWebSocketOptions;
|
|
19
|
+
private readonly maxAttempts: number;
|
|
20
|
+
private emitter = new Emitter<any>();
|
|
21
|
+
|
|
22
|
+
constructor(url: string, opts: ReconnectingWebSocketOptions = {}) {
|
|
23
|
+
this.url = url;
|
|
24
|
+
this.opts = opts;
|
|
25
|
+
this.maxAttempts = opts.maxAttempts ?? Infinity;
|
|
26
|
+
|
|
27
|
+
this.open();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private open(): void {
|
|
31
|
+
this.ws = new WebSocket(this.url, this.opts.protocols ?? []);
|
|
32
|
+
|
|
33
|
+
this.ws.addEventListener('open', event => {
|
|
34
|
+
this.attempts = 0;
|
|
35
|
+
this.emitter.emit('open', event);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.ws.addEventListener('close', event => {
|
|
39
|
+
// let shouldReconnect = ![1000, 1001, 1005].includes(event.code);
|
|
40
|
+
// if (shouldReconnect) {
|
|
41
|
+
this.reconnect(event);
|
|
42
|
+
// } else {
|
|
43
|
+
// this.emitter.emit('close', event);
|
|
44
|
+
// }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
this.ws.addEventListener('error', event => {
|
|
48
|
+
let error = event as any;
|
|
49
|
+
if (error?.code === 'ECONNREFUSED') {
|
|
50
|
+
this.reconnect(event);
|
|
51
|
+
} else {
|
|
52
|
+
this.emitter.emit('error', event);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.ws.addEventListener('message', event => {
|
|
57
|
+
this.emitter.emit('message', event);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private reconnect(event: Event): void {
|
|
62
|
+
if (this.timer) clearTimeout(this.timer);
|
|
63
|
+
|
|
64
|
+
if (this.attempts++ < this.maxAttempts) {
|
|
65
|
+
this.timer = setTimeout(() => {
|
|
66
|
+
this.opts.onReconnect?.(event);
|
|
67
|
+
this.open();
|
|
68
|
+
}, this.opts.timeout ?? 1000);
|
|
69
|
+
} else {
|
|
70
|
+
this.emitter.emit('maximum', event);
|
|
71
|
+
this.emitter.emit('close', event);
|
|
72
|
+
this.emitter.clear();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
send(data: string | ArrayBufferLike): void {
|
|
77
|
+
this.ws.send(data);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
close(code: number = 1000, reason?: string): void {
|
|
81
|
+
if (this.timer) clearTimeout(this.timer);
|
|
82
|
+
this.emitter.clear();
|
|
83
|
+
this.ws.close(code, reason);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
addEventListener(event: string, callback: (data: any) => void) {
|
|
87
|
+
return this.emitter.on(event, callback);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get readyState() {
|
|
91
|
+
return this.ws.readyState;
|
|
92
|
+
}
|
|
93
|
+
}
|