@zimic/ws 0.1.0-canary.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/LICENSE.md ADDED
@@ -0,0 +1,16 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-Present Zimic Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ <p align="center">
2
+ <img src="../../docs/zimic.png" align="center" width="100px" height="100px">
3
+ </p>
4
+
5
+ <h1 align="center">
6
+ @zimic/ws
7
+ </h1>
8
+
9
+ <p align="center">
10
+ Next-gen TypeScript-first WebSocket client and server
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/@zimic/ws">npm</a>
15
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
16
+ <a href="https://zimic.dev">Docs</a>
17
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
18
+ <a href="https://zimic.dev/docs/examples">Examples</a>
19
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
20
+ <a href="https://github.com/zimicjs/zimic/issues">Issues</a>
21
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
22
+ <a href="https://github.com/orgs/zimicjs/projects/1/views/4">Roadmap</a>
23
+ </p>
24
+
25
+ <div align="center">
26
+
27
+ [![CI](https://github.com/zimicjs/zimic/actions/workflows/ci.yaml/badge.svg?branch=canary)](https://github.com/zimicjs/zimic/actions/workflows/ci.yaml)&nbsp;
28
+ [![Coverage](https://img.shields.io/badge/Coverage-100%25-31C654?labelColor=353C43)](https://github.com/zimicjs/zimic/actions)&nbsp;
29
+ [![License](https://img.shields.io/github/license/zimicjs/zimic?color=0E69BE&label=License&labelColor=353C43)](https://github.com/zimicjs/zimic/blob/canary/LICENSE.md)&nbsp;
30
+ [![Stars](https://img.shields.io/github/stars/zimicjs/zimic)](https://github.com/zimicjs/zimic)
31
+
32
+ [![NPM Downloads - @zimic/ws](https://img.shields.io/npm/dm/@zimic/ws?style=flat&logo=npm&color=0E69BE&label=%20%40zimic%2Fws&labelColor=353C43)](https://www.npmjs.com/package/@zimic/ws)&nbsp;
33
+ [![Bundle size - @zimic/ws](https://badgen.net/bundlephobia/minzip/@zimic/ws?color=0E69BE&labelColor=353C43&label=@zimic/ws%20min%20gzip)](https://bundlephobia.com/package/@zimic/ws)<br />
34
+
35
+ </div>
36
+
37
+ ---
38
+
39
+ `@zimic/ws` is a minimal and type-safe WebSocket client and server.
40
+
41
+ > [!NOTE]
42
+ >
43
+ > Status: 🚧 Alpha
44
+
45
+ ## Highlights
46
+
47
+ 🚧 TODO
48
+
49
+ **Learn more**:
50
+
51
+ 🚧 TODO
@@ -0,0 +1,149 @@
1
+ declare class WebSocketTimeoutError extends Error {
2
+ constructor(message: string);
3
+ }
4
+
5
+ declare class WebSocketCloseTimeoutError extends WebSocketTimeoutError {
6
+ constructor(reachedTimeout: number);
7
+ }
8
+
9
+ declare class WebSocketMessageAbortError extends WebSocketTimeoutError {
10
+ constructor();
11
+ }
12
+
13
+ declare class WebSocketOpenTimeoutError extends WebSocketTimeoutError {
14
+ constructor(reachedTimeout: number);
15
+ }
16
+
17
+ declare const brand: unique symbol;
18
+ /**
19
+ * A utility type to create a branded type. This is useful for creating types that are distinct from each other even if
20
+ * they have the same underlying structure. It also helps the TypeScript compiler to reference the type in the generated
21
+ * declaration files, rather than inlining it.
22
+ */
23
+ type Branded<Type, Brand extends string> = Type & {
24
+ [brand]?: Brand;
25
+ };
26
+
27
+ type JSON = {
28
+ [key: string]: JSON;
29
+ } | JSON[] | string | number | boolean | null | undefined;
30
+ declare namespace JSON {
31
+ type Loose = Record<string, any> | Loose[] | string | number | boolean | null | undefined;
32
+ }
33
+ /**
34
+ * Represents or validates a type that is compatible with JSON.
35
+ *
36
+ * **IMPORTANT**: the input of `JSONValue` and all of its internal types must be declared inline or as a type aliases
37
+ * (`type`). They cannot be interfaces.
38
+ *
39
+ * @example
40
+ * import { type JSONValue } from '@zimic/http';
41
+ *
42
+ * // Can be used as a standalone type:
43
+ * const value: JSONValue = {
44
+ * name: 'example',
45
+ * tags: ['one', 'two'],
46
+ * };
47
+ *
48
+ * @example
49
+ * import { type JSONValue } from '@zimic/http';
50
+ *
51
+ * // Can be used with a type argument to validate a JSON value:
52
+ * type ValidJSON = JSONValue<{
53
+ * id: string;
54
+ * email: string;
55
+ * createdAt: string;
56
+ * }>;
57
+ *
58
+ * // This results in a type error:
59
+ * type InvalidJSON = JSONValue<{
60
+ * id: string;
61
+ * email: string;
62
+ * createdAt: Date; // `Date` is not a valid JSON value.
63
+ * save: () => Promise<void>; // Functions are not valid JSON values.
64
+ * }>;
65
+ */
66
+ type JSONValue<Type extends JSON = JSON> = Type;
67
+ declare namespace JSONValue {
68
+ /** A loose version of the JSON value type. JSON objects are not strictly typed. */
69
+ type Loose<Type extends JSON.Loose = JSON.Loose> = Type;
70
+ }
71
+ declare global {
72
+ interface JSON {
73
+ readonly value: unique symbol;
74
+ stringify<Value>(value: Value, replacer?: ((this: any, key: string, value: Value) => any) | (number | string)[] | null, space?: string | number): JSONStringified$1<Value>;
75
+ parse<Value>(text: JSONStringified$1<Value>, reviver?: (this: any, key: string, value: any) => any): Value;
76
+ }
77
+ }
78
+ type JSONStringified$1<Value> = string & {
79
+ [JSON.value]: Value;
80
+ };
81
+
82
+ /**
83
+ * Represents a value stringified by `JSON.stringify`, maintaining a type reference to the original type.
84
+ *
85
+ * This type is used to validate that the expected stringified body is passed in WebSocket messages.
86
+ */
87
+ type JSONStringified<Value> = JSONStringified$1<Value>;
88
+
89
+ type BaseWebSocketSchema = JSONValue | string | Blob | ArrayBufferLike | ArrayBufferView;
90
+ type WebSocketSchema<Schema extends BaseWebSocketSchema = BaseWebSocketSchema> = Branded<Schema, 'WebSocketSchema'>;
91
+ type WebSocketMessageData<Schema extends WebSocketSchema> = Schema extends Blob ? Schema : Schema extends ArrayBufferLike ? Schema : Schema extends ArrayBufferView ? Schema : Schema extends string ? Schema : JSONStringified<Schema>;
92
+ interface WebSocketEventMap<Schema extends WebSocketSchema> {
93
+ open: Event;
94
+ message: MessageEvent<WebSocketMessageData<Schema>>;
95
+ close: CloseEvent;
96
+ error: Event;
97
+ }
98
+ type WebSocketEventType<Schema extends WebSocketSchema = WebSocketSchema> = keyof WebSocketEventMap<Schema>;
99
+ type WebSocketEvent<Schema extends WebSocketSchema = WebSocketSchema, Type extends WebSocketEventType = WebSocketEventType> = WebSocketEventMap<Schema>[Type];
100
+
101
+ type WebSocketClientEventListener<Schema extends WebSocketSchema, Type extends WebSocketEventType<Schema>> = (this: WebSocketClient<Schema>, event: WebSocketEvent<Schema, Type>) => unknown;
102
+ declare class WebSocketClient<Schema extends WebSocketSchema> implements Omit<WebSocket, `${string}EventListener` | `on${string}`> {
103
+ private _url;
104
+ private protocols?;
105
+ private socket?;
106
+ private _binaryType;
107
+ private _onopen;
108
+ private _onmessage;
109
+ private _onclose;
110
+ private _onerror;
111
+ private listeners;
112
+ constructor(_url: string, protocols?: string | string[] | undefined);
113
+ static CONNECTING: 0;
114
+ get CONNECTING(): 0;
115
+ static OPEN: 1;
116
+ get OPEN(): 1;
117
+ static CLOSING: 2;
118
+ get CLOSING(): 2;
119
+ static CLOSED: 3;
120
+ get CLOSED(): 3;
121
+ get binaryType(): "blob" | "arraybuffer";
122
+ set binaryType(value: 'blob' | 'arraybuffer');
123
+ get url(): string;
124
+ get protocol(): string;
125
+ get extensions(): string;
126
+ get readyState(): number;
127
+ get bufferedAmount(): number;
128
+ open(options?: {
129
+ timeout?: number;
130
+ }): Promise<void>;
131
+ close(code?: number, reason?: string, options?: {
132
+ timeout?: number;
133
+ }): Promise<void>;
134
+ send(data: WebSocketMessageData<Schema>): void;
135
+ addEventListener<Type extends WebSocketEventType<Schema>>(type: Type, listener: (this: WebSocketClient<Schema>, event: WebSocketEvent<Schema, Type>) => unknown, options?: boolean | AddEventListenerOptions): void;
136
+ removeEventListener<Type extends WebSocketEventType<Schema>>(type: Type, listener: (this: WebSocketClient<Schema>, event: WebSocketEvent<Schema, Type>) => unknown, options?: boolean | EventListenerOptions): void;
137
+ get onopen(): WebSocketClientEventListener<Schema, "open"> | null;
138
+ set onopen(listener: WebSocketClientEventListener<Schema, 'open'> | null);
139
+ get onmessage(): WebSocketClientEventListener<Schema, "message"> | null;
140
+ set onmessage(listener: WebSocketClientEventListener<Schema, 'message'> | null);
141
+ get onclose(): WebSocketClientEventListener<Schema, "close"> | null;
142
+ set onclose(listener: WebSocketClientEventListener<Schema, 'close'> | null);
143
+ get onerror(): WebSocketClientEventListener<Schema, "error"> | null;
144
+ set onerror(listener: WebSocketClientEventListener<Schema, 'error'> | null);
145
+ private setUnitaryEventListener;
146
+ dispatchEvent<Type extends WebSocketEventType<Schema>>(event: WebSocketEvent<Schema, Type>): boolean;
147
+ }
148
+
149
+ export { WebSocketClient, WebSocketCloseTimeoutError, WebSocketMessageAbortError, WebSocketOpenTimeoutError, WebSocketTimeoutError };
package/dist/index.js ADDED
@@ -0,0 +1,259 @@
1
+ 'use strict';
2
+
3
+ var NodeWebSocket = require('ws');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var NodeWebSocket__default = /*#__PURE__*/_interopDefault(NodeWebSocket);
8
+
9
+ // src/errors/WebSocketTimeoutError.ts
10
+ var WebSocketTimeoutError = class extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "WebSocketTimeoutError";
14
+ }
15
+ };
16
+
17
+ // src/errors/WebSocketCloseTimeoutError.ts
18
+ var WebSocketCloseTimeoutError = class extends WebSocketTimeoutError {
19
+ constructor(reachedTimeout) {
20
+ super(`Web socket close timed out after ${reachedTimeout}ms.`);
21
+ this.name = "WebSocketCloseTimeoutError";
22
+ }
23
+ };
24
+
25
+ // src/errors/WebSocketMessageAbortError.ts
26
+ var WebSocketMessageAbortError = class extends WebSocketTimeoutError {
27
+ constructor() {
28
+ super("Web socket message was aborted.");
29
+ this.name = "WebSocketMessageAbortError";
30
+ }
31
+ };
32
+
33
+ // src/errors/WebSocketOpenTimeoutError.ts
34
+ var WebSocketOpenTimeoutError = class extends WebSocketTimeoutError {
35
+ constructor(reachedTimeout) {
36
+ super(`Web socket open timed out after ${reachedTimeout}ms.`);
37
+ this.name = "WebSocketOpenTimeoutError";
38
+ }
39
+ };
40
+ var isNativeWebSocketAvailable = typeof WebSocket !== "undefined";
41
+ var ClientSocket = isNativeWebSocketAvailable ? WebSocket : NodeWebSocket__default.default;
42
+
43
+ // src/client/utils/lifecycle.ts
44
+ async function openClientSocket(socket, options = {}) {
45
+ const { timeout: timeoutDuration } = options;
46
+ const isAlreadyOpen = socket.readyState === socket.OPEN;
47
+ if (isAlreadyOpen) {
48
+ return;
49
+ }
50
+ await new Promise((resolve, reject) => {
51
+ function removeAllSocketListeners() {
52
+ socket.removeEventListener("open", handleOpenSuccess);
53
+ socket.removeEventListener("error", handleError);
54
+ socket.removeEventListener("close", handleError);
55
+ }
56
+ function handleError(error) {
57
+ removeAllSocketListeners();
58
+ reject(error);
59
+ }
60
+ const openTimeout = timeoutDuration === void 0 ? void 0 : setTimeout(() => {
61
+ const timeoutError = new WebSocketOpenTimeoutError(timeoutDuration);
62
+ handleError(timeoutError);
63
+ }, timeoutDuration);
64
+ function handleOpenSuccess() {
65
+ removeAllSocketListeners();
66
+ clearTimeout(openTimeout);
67
+ resolve();
68
+ }
69
+ socket.addEventListener("open", handleOpenSuccess);
70
+ socket.addEventListener("error", handleError);
71
+ socket.addEventListener("close", handleError);
72
+ });
73
+ }
74
+ async function closeClientSocket(socket, options = {}) {
75
+ const { timeout: timeoutDuration } = options;
76
+ const isAlreadyClosed = socket.readyState === socket.CLOSED;
77
+ if (isAlreadyClosed) {
78
+ return;
79
+ }
80
+ await new Promise((resolve, reject) => {
81
+ function removeAllSocketListeners() {
82
+ socket.removeEventListener("error", handleError);
83
+ socket.removeEventListener("close", handleClose);
84
+ }
85
+ function handleError(error) {
86
+ removeAllSocketListeners();
87
+ reject(error);
88
+ }
89
+ const closeTimeout = timeoutDuration === void 0 ? void 0 : setTimeout(() => {
90
+ const timeoutError = new WebSocketCloseTimeoutError(timeoutDuration);
91
+ handleError(timeoutError);
92
+ }, timeoutDuration);
93
+ function handleClose() {
94
+ removeAllSocketListeners();
95
+ clearTimeout(closeTimeout);
96
+ resolve();
97
+ }
98
+ socket.addEventListener("error", handleError);
99
+ socket.addEventListener("close", handleClose);
100
+ socket.close(options.code, options.reason);
101
+ });
102
+ }
103
+
104
+ // src/client/WebSocketClient.ts
105
+ var WebSocketClient = class _WebSocketClient {
106
+ constructor(_url, protocols) {
107
+ this._url = _url;
108
+ this.protocols = protocols;
109
+ }
110
+ socket;
111
+ _binaryType = "blob";
112
+ _onopen = null;
113
+ _onmessage = null;
114
+ _onclose = null;
115
+ _onerror = null;
116
+ listeners = {
117
+ open: /* @__PURE__ */ new Map(),
118
+ message: /* @__PURE__ */ new Map(),
119
+ close: /* @__PURE__ */ new Map(),
120
+ error: /* @__PURE__ */ new Map()
121
+ };
122
+ static CONNECTING = ClientSocket.CONNECTING;
123
+ get CONNECTING() {
124
+ return _WebSocketClient.CONNECTING;
125
+ }
126
+ static OPEN = ClientSocket.OPEN;
127
+ get OPEN() {
128
+ return _WebSocketClient.OPEN;
129
+ }
130
+ static CLOSING = ClientSocket.CLOSING;
131
+ get CLOSING() {
132
+ return _WebSocketClient.CLOSING;
133
+ }
134
+ static CLOSED = ClientSocket.CLOSED;
135
+ get CLOSED() {
136
+ return _WebSocketClient.CLOSED;
137
+ }
138
+ get binaryType() {
139
+ return this.socket?.binaryType ?? this._binaryType;
140
+ }
141
+ set binaryType(value) {
142
+ this._binaryType = value;
143
+ if (this.socket) {
144
+ this.socket.binaryType = value;
145
+ }
146
+ }
147
+ get url() {
148
+ return this.socket?.url ?? this._url;
149
+ }
150
+ get protocol() {
151
+ return this.socket?.protocol ?? "";
152
+ }
153
+ get extensions() {
154
+ return this.socket?.extensions ?? "";
155
+ }
156
+ get readyState() {
157
+ return this.socket?.readyState ?? ClientSocket.CLOSED;
158
+ }
159
+ get bufferedAmount() {
160
+ return this.socket?.bufferedAmount ?? 0;
161
+ }
162
+ async open(options = {}) {
163
+ this.socket = new ClientSocket(this._url, this.protocols);
164
+ if (this.socket.binaryType !== this._binaryType) {
165
+ this.socket.binaryType = this._binaryType;
166
+ }
167
+ await openClientSocket(this.socket, options);
168
+ }
169
+ async close(code, reason, options = {}) {
170
+ if (!this.socket) {
171
+ return;
172
+ }
173
+ try {
174
+ await closeClientSocket(this.socket, { ...options, code, reason });
175
+ } finally {
176
+ this.onopen = null;
177
+ this.onmessage = null;
178
+ this.onclose = null;
179
+ this.onerror = null;
180
+ for (const [type, listenerToRawListener] of Object.entries(this.listeners)) {
181
+ for (const rawListener of listenerToRawListener.values()) {
182
+ this.socket.removeEventListener(type, rawListener);
183
+ }
184
+ listenerToRawListener.clear();
185
+ }
186
+ this.socket = void 0;
187
+ }
188
+ }
189
+ send(data) {
190
+ this.socket?.send(data);
191
+ }
192
+ addEventListener(type, listener, options) {
193
+ const rawListener = listener.bind(this);
194
+ this.socket?.addEventListener(type, rawListener, options);
195
+ this.listeners[type].set(listener, rawListener);
196
+ }
197
+ removeEventListener(type, listener, options) {
198
+ const rawListener = this.listeners[type].get(listener);
199
+ if (rawListener) {
200
+ this.socket?.removeEventListener(type, rawListener, options);
201
+ this.listeners[type].delete(listener);
202
+ }
203
+ }
204
+ get onopen() {
205
+ return this._onopen;
206
+ }
207
+ set onopen(listener) {
208
+ this.setUnitaryEventListener("open", listener);
209
+ }
210
+ get onmessage() {
211
+ return this._onmessage;
212
+ }
213
+ set onmessage(listener) {
214
+ this.setUnitaryEventListener("message", listener);
215
+ }
216
+ get onclose() {
217
+ return this._onclose;
218
+ }
219
+ set onclose(listener) {
220
+ this.setUnitaryEventListener("close", listener);
221
+ }
222
+ get onerror() {
223
+ return this._onerror;
224
+ }
225
+ set onerror(listener) {
226
+ this.setUnitaryEventListener("error", listener);
227
+ }
228
+ setUnitaryEventListener(type, listener) {
229
+ if (listener) {
230
+ const rawListener = listener.bind(this);
231
+ this.listeners[type].set(listener, rawListener);
232
+ if (this.socket) {
233
+ this.socket[`on${type}`] = rawListener;
234
+ }
235
+ this[`_on${type}`] = listener;
236
+ } else {
237
+ const currentListener = this[`_on${type}`];
238
+ if (currentListener) {
239
+ this.listeners[type].delete(currentListener);
240
+ }
241
+ if (this.socket) {
242
+ this.socket[`on${type}`] = null;
243
+ }
244
+ this[`_on${type}`] = null;
245
+ }
246
+ }
247
+ dispatchEvent(event) {
248
+ return this.socket?.dispatchEvent(event) ?? false;
249
+ }
250
+ };
251
+ var WebSocketClient_default = WebSocketClient;
252
+
253
+ exports.WebSocketClient = WebSocketClient_default;
254
+ exports.WebSocketCloseTimeoutError = WebSocketCloseTimeoutError;
255
+ exports.WebSocketMessageAbortError = WebSocketMessageAbortError;
256
+ exports.WebSocketOpenTimeoutError = WebSocketOpenTimeoutError;
257
+ exports.WebSocketTimeoutError = WebSocketTimeoutError;
258
+ //# sourceMappingURL=index.js.map
259
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/WebSocketTimeoutError.ts","../src/errors/WebSocketCloseTimeoutError.ts","../src/errors/WebSocketMessageAbortError.ts","../src/errors/WebSocketOpenTimeoutError.ts","../src/client/ClientSocket.ts","../src/client/utils/lifecycle.ts","../src/client/WebSocketClient.ts"],"names":["NodeWebSocket"],"mappings":"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","file":"index.js","sourcesContent":["export class WebSocketTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'WebSocketTimeoutError';\n }\n}\n","import { WebSocketTimeoutError } from './WebSocketTimeoutError';\n\nexport class WebSocketCloseTimeoutError extends WebSocketTimeoutError {\n constructor(reachedTimeout: number) {\n super(`Web socket close timed out after ${reachedTimeout}ms.`);\n this.name = 'WebSocketCloseTimeoutError';\n }\n}\n","import { WebSocketTimeoutError } from './WebSocketTimeoutError';\n\nexport class WebSocketMessageAbortError extends WebSocketTimeoutError {\n constructor() {\n super('Web socket message was aborted.');\n this.name = 'WebSocketMessageAbortError';\n }\n}\n","import { WebSocketTimeoutError } from './WebSocketTimeoutError';\n\nexport class WebSocketOpenTimeoutError extends WebSocketTimeoutError {\n constructor(reachedTimeout: number) {\n super(`Web socket open timed out after ${reachedTimeout}ms.`);\n this.name = 'WebSocketOpenTimeoutError';\n }\n}\n","import NodeWebSocket from 'ws';\n\n// WebSocket is available natively in browsers and Node.js >=22.\nconst isNativeWebSocketAvailable = typeof WebSocket !== 'undefined';\n\nexport const ClientSocket = (isNativeWebSocketAvailable ? WebSocket : NodeWebSocket) as typeof WebSocket;\nexport type ClientSocket = InstanceType<typeof ClientSocket>;\n","import { WebSocketCloseTimeoutError } from '@/errors/WebSocketCloseTimeoutError';\nimport { WebSocketOpenTimeoutError } from '@/errors/WebSocketOpenTimeoutError';\n\nimport { ClientSocket } from '../ClientSocket';\n\nexport async function openClientSocket(socket: ClientSocket, options: { timeout?: number } = {}) {\n const { timeout: timeoutDuration } = options;\n\n const isAlreadyOpen = socket.readyState === socket.OPEN;\n\n if (isAlreadyOpen) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n function removeAllSocketListeners() {\n socket.removeEventListener('open', handleOpenSuccess); // eslint-disable-line @typescript-eslint/no-use-before-define\n socket.removeEventListener('error', handleError); // eslint-disable-line @typescript-eslint/no-use-before-define\n socket.removeEventListener('close', handleError); // eslint-disable-line @typescript-eslint/no-use-before-define\n }\n\n function handleError(error: unknown) {\n removeAllSocketListeners();\n reject(error);\n }\n\n const openTimeout =\n timeoutDuration === undefined\n ? undefined\n : setTimeout(() => {\n const timeoutError = new WebSocketOpenTimeoutError(timeoutDuration);\n handleError(timeoutError);\n }, timeoutDuration);\n\n function handleOpenSuccess() {\n removeAllSocketListeners();\n clearTimeout(openTimeout);\n resolve();\n }\n\n socket.addEventListener('open', handleOpenSuccess);\n socket.addEventListener('error', handleError);\n socket.addEventListener('close', handleError);\n });\n}\n\nexport async function closeClientSocket(\n socket: ClientSocket,\n options: { code?: number; reason?: string; timeout?: number } = {},\n) {\n const { timeout: timeoutDuration } = options;\n\n const isAlreadyClosed = socket.readyState === socket.CLOSED;\n\n if (isAlreadyClosed) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n function removeAllSocketListeners() {\n socket.removeEventListener('error', handleError); // eslint-disable-line @typescript-eslint/no-use-before-define\n socket.removeEventListener('close', handleClose); // eslint-disable-line @typescript-eslint/no-use-before-define\n }\n\n function handleError(error: unknown) {\n removeAllSocketListeners();\n reject(error);\n }\n\n const closeTimeout =\n timeoutDuration === undefined\n ? undefined\n : setTimeout(() => {\n const timeoutError = new WebSocketCloseTimeoutError(timeoutDuration);\n handleError(timeoutError);\n }, timeoutDuration);\n\n function handleClose() {\n removeAllSocketListeners();\n clearTimeout(closeTimeout);\n resolve();\n }\n\n socket.addEventListener('error', handleError);\n socket.addEventListener('close', handleClose);\n\n socket.close(options.code, options.reason);\n });\n}\n","import { WebSocketEvent, WebSocketEventType, WebSocketMessageData, WebSocketSchema } from '@/types/schema';\n\nimport { ClientSocket } from './ClientSocket';\nimport { closeClientSocket, openClientSocket } from './utils/lifecycle';\n\nexport type WebSocketClientEventListener<Schema extends WebSocketSchema, Type extends WebSocketEventType<Schema>> = (\n this: WebSocketClient<Schema>,\n event: WebSocketEvent<Schema, Type>,\n) => unknown;\n\ntype WebSocketClientRawEventListener = (this: ClientSocket, event: Event) => unknown;\n\nclass WebSocketClient<Schema extends WebSocketSchema> implements Omit<\n WebSocket,\n `${string}EventListener` | `on${string}`\n> {\n private socket?: ClientSocket;\n private _binaryType: BinaryType = 'blob';\n\n private _onopen: WebSocketClientEventListener<Schema, 'open'> | null = null;\n private _onmessage: WebSocketClientEventListener<Schema, 'message'> | null = null;\n private _onclose: WebSocketClientEventListener<Schema, 'close'> | null = null;\n private _onerror: WebSocketClientEventListener<Schema, 'error'> | null = null;\n\n private listeners: {\n [Type in WebSocketEventType<Schema>]: Map<\n WebSocketClientEventListener<Schema, Type>,\n WebSocketClientRawEventListener\n >;\n } = {\n open: new Map(),\n message: new Map(),\n close: new Map(),\n error: new Map(),\n };\n\n constructor(\n private _url: string,\n private protocols?: string | string[],\n ) {}\n\n static CONNECTING = ClientSocket.CONNECTING;\n\n get CONNECTING() {\n return WebSocketClient.CONNECTING;\n }\n\n static OPEN = ClientSocket.OPEN;\n\n get OPEN() {\n return WebSocketClient.OPEN;\n }\n\n static CLOSING = ClientSocket.CLOSING;\n\n get CLOSING() {\n return WebSocketClient.CLOSING;\n }\n\n static CLOSED = ClientSocket.CLOSED;\n\n get CLOSED() {\n return WebSocketClient.CLOSED;\n }\n\n get binaryType() {\n return this.socket?.binaryType ?? this._binaryType;\n }\n\n set binaryType(value: 'blob' | 'arraybuffer') {\n this._binaryType = value;\n\n if (this.socket) {\n this.socket.binaryType = value;\n }\n }\n\n get url() {\n return this.socket?.url ?? this._url;\n }\n\n get protocol() {\n return this.socket?.protocol ?? '';\n }\n\n get extensions() {\n return this.socket?.extensions ?? '';\n }\n\n get readyState() {\n return this.socket?.readyState ?? ClientSocket.CLOSED;\n }\n\n get bufferedAmount() {\n return this.socket?.bufferedAmount ?? 0;\n }\n\n async open(options: { timeout?: number } = {}) {\n this.socket = new ClientSocket(this._url, this.protocols);\n\n if (this.socket.binaryType !== this._binaryType) {\n this.socket.binaryType = this._binaryType;\n }\n\n await openClientSocket(this.socket, options);\n }\n\n async close(code?: number, reason?: string, options: { timeout?: number } = {}) {\n if (!this.socket) {\n return;\n }\n\n try {\n await closeClientSocket(this.socket, { ...options, code, reason });\n } finally {\n this.onopen = null;\n this.onmessage = null;\n this.onclose = null;\n this.onerror = null;\n\n for (const [type, listenerToRawListener] of Object.entries(this.listeners)) {\n for (const rawListener of listenerToRawListener.values()) {\n this.socket.removeEventListener(type, rawListener);\n }\n\n listenerToRawListener.clear();\n }\n\n this.socket = undefined;\n }\n }\n\n send(data: WebSocketMessageData<Schema>) {\n this.socket?.send(data);\n }\n\n addEventListener<Type extends WebSocketEventType<Schema>>(\n type: Type,\n listener: (this: WebSocketClient<Schema>, event: WebSocketEvent<Schema, Type>) => unknown,\n options?: boolean | AddEventListenerOptions,\n ) {\n const rawListener = listener.bind(this) as WebSocketClientRawEventListener;\n\n this.socket?.addEventListener(type, rawListener, options);\n this.listeners[type].set(listener, rawListener);\n }\n\n removeEventListener<Type extends WebSocketEventType<Schema>>(\n type: Type,\n listener: (this: WebSocketClient<Schema>, event: WebSocketEvent<Schema, Type>) => unknown,\n options?: boolean | EventListenerOptions,\n ) {\n const rawListener = this.listeners[type].get(listener);\n\n if (rawListener) {\n this.socket?.removeEventListener(type, rawListener, options);\n this.listeners[type].delete(listener);\n }\n }\n\n get onopen() {\n return this._onopen;\n }\n\n set onopen(listener: WebSocketClientEventListener<Schema, 'open'> | null) {\n this.setUnitaryEventListener('open', listener);\n }\n\n get onmessage() {\n return this._onmessage;\n }\n\n set onmessage(listener: WebSocketClientEventListener<Schema, 'message'> | null) {\n this.setUnitaryEventListener('message', listener);\n }\n\n get onclose() {\n return this._onclose;\n }\n\n set onclose(listener: WebSocketClientEventListener<Schema, 'close'> | null) {\n this.setUnitaryEventListener('close', listener);\n }\n\n get onerror() {\n return this._onerror;\n }\n\n set onerror(listener: WebSocketClientEventListener<Schema, 'error'> | null) {\n this.setUnitaryEventListener('error', listener);\n }\n\n private setUnitaryEventListener<Type extends WebSocketEventType<Schema>>(\n type: Type,\n listener: WebSocketClientEventListener<Schema, Type> | null,\n ) {\n type PrivateUnitaryListener = typeof listener;\n\n if (listener) {\n const rawListener = listener.bind(this) as WebSocketClientRawEventListener;\n this.listeners[type].set(listener, rawListener);\n\n if (this.socket) {\n this.socket[`on${type}`] = rawListener;\n }\n\n (this[`_on${type}`] as PrivateUnitaryListener) = listener;\n } else {\n const currentListener = this[`_on${type}`] as PrivateUnitaryListener;\n\n if (currentListener) {\n this.listeners[type].delete(currentListener);\n }\n\n if (this.socket) {\n this.socket[`on${type}`] = null;\n }\n\n (this[`_on${type}`] as PrivateUnitaryListener) = null;\n }\n }\n\n dispatchEvent<Type extends WebSocketEventType<Schema>>(event: WebSocketEvent<Schema, Type>) {\n return this.socket?.dispatchEvent(event) ?? false;\n }\n}\n\nexport default WebSocketClient;\n"]}