@iam4x/reconnecting-websocket 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/README.md +280 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# @iam4x/reconnecting-websocket
|
|
2
|
+
|
|
3
|
+
A robust, TypeScript-first WebSocket client with automatic reconnection, exponential backoff, and comprehensive event handling.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Automatic Reconnection** - Automatically reconnects on connection loss with exponential backoff
|
|
8
|
+
- ✅ **Connection Timeout** - Configurable timeout to detect stalled connections
|
|
9
|
+
- ✅ **Event-Driven API** - Familiar event listener pattern matching WebSocket API
|
|
10
|
+
- ✅ **TypeScript Support** - Full TypeScript definitions included
|
|
11
|
+
- ✅ **Customizable** - Configurable retry delays, backoff factors, and WebSocket implementations
|
|
12
|
+
- ✅ **Reconnect Events** - Separate `reconnect` event for tracking reconnection attempts
|
|
13
|
+
- ✅ **Memory Safe** - Proper cleanup of timers and event listeners
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add @iam4x/reconnecting-websocket
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
or
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @iam4x/reconnecting-websocket
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { ReconnectingWebSocket } from "@iam4x/reconnecting-websocket";
|
|
31
|
+
|
|
32
|
+
const ws = new ReconnectingWebSocket("wss://echo.websocket.org");
|
|
33
|
+
|
|
34
|
+
ws.addEventListener("open", () => {
|
|
35
|
+
console.log("Connected!");
|
|
36
|
+
ws.send("Hello, Server!");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
ws.addEventListener("message", (event: MessageEvent) => {
|
|
40
|
+
console.log("Received:", event.data);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
ws.addEventListener("close", (event) => {
|
|
44
|
+
console.log("Connection closed:", event.code, event.reason);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
ws.addEventListener("reconnect", () => {
|
|
48
|
+
console.log("Reconnected successfully!");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
ws.addEventListener("error", (event: Event) => {
|
|
52
|
+
console.error("WebSocket error:", event);
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
### Constructor
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
new ReconnectingWebSocket(url: string, options?: ReconnectOptions)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Creates a new `ReconnectingWebSocket` instance and immediately attempts to connect.
|
|
65
|
+
|
|
66
|
+
#### Parameters
|
|
67
|
+
|
|
68
|
+
- `url` (string): The WebSocket server URL (e.g., `"wss://example.com"`)
|
|
69
|
+
- `options` (ReconnectOptions, optional): Configuration options (see below)
|
|
70
|
+
|
|
71
|
+
### Options
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
interface ReconnectOptions {
|
|
75
|
+
retryDelay?: number; // Initial retry delay in ms (default: 1000)
|
|
76
|
+
maxRetryDelay?: number; // Maximum retry delay in ms (default: 30000)
|
|
77
|
+
connectionTimeout?: number; // Connection timeout in ms (default: 10000)
|
|
78
|
+
backoffFactor?: number; // Exponential backoff multiplier (default: 2)
|
|
79
|
+
WebSocketConstructor?: typeof WebSocket; // Custom WebSocket implementation
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
#### Option Details
|
|
84
|
+
|
|
85
|
+
- **retryDelay**: The initial delay before the first reconnection attempt (in milliseconds)
|
|
86
|
+
- **maxRetryDelay**: The maximum delay between reconnection attempts. The delay will grow exponentially but won't exceed this value
|
|
87
|
+
- **connectionTimeout**: If a connection doesn't establish within this time, it will be aborted and retried
|
|
88
|
+
- **backoffFactor**: The multiplier for exponential backoff. Each retry delay is multiplied by this factor
|
|
89
|
+
- **WebSocketConstructor**: Allows you to provide a custom WebSocket implementation (useful for Node.js environments using libraries like `ws`)
|
|
90
|
+
|
|
91
|
+
### Methods
|
|
92
|
+
|
|
93
|
+
#### `addEventListener(event, listener)`
|
|
94
|
+
|
|
95
|
+
Adds an event listener to the socket.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
ws.addEventListener("open", (event: Event) => {
|
|
99
|
+
// Handle open event
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Events:**
|
|
104
|
+
- `"open"` - Emitted when connection is established
|
|
105
|
+
- `"message"` - Emitted when a message is received (payload: `MessageEvent`)
|
|
106
|
+
- `"close"` - Emitted when connection closes (payload: `{ code: number, reason: string }`)
|
|
107
|
+
- `"reconnect"` - Emitted when successfully reconnected after a disconnection
|
|
108
|
+
- `"error"` - Emitted when an error occurs (payload: `Event`)
|
|
109
|
+
|
|
110
|
+
#### `removeEventListener(event, listener)`
|
|
111
|
+
|
|
112
|
+
Removes an event listener from the socket.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const handler = (event: Event) => console.log("Connected");
|
|
116
|
+
ws.addEventListener("open", handler);
|
|
117
|
+
ws.removeEventListener("open", handler);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `send(data)`
|
|
121
|
+
|
|
122
|
+
Sends data through the WebSocket connection.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
ws.send("Hello, Server!");
|
|
126
|
+
ws.send(JSON.stringify({ type: "ping" }));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Note:** This method will silently fail if the socket is not connected. Check `readyState` before sending if needed.
|
|
130
|
+
|
|
131
|
+
#### `close(code?, reason?)`
|
|
132
|
+
|
|
133
|
+
Closes the WebSocket connection and prevents automatic reconnection.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
ws.close(); // Close with default code
|
|
137
|
+
ws.close(1000, "Normal closure"); // Close with code and reason
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
After calling `close()`, the socket will not automatically reconnect. Create a new instance to reconnect.
|
|
141
|
+
|
|
142
|
+
### Properties
|
|
143
|
+
|
|
144
|
+
#### `readyState`
|
|
145
|
+
|
|
146
|
+
Returns the current ready state of the WebSocket connection.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
150
|
+
ws.send("Data");
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Values:**
|
|
155
|
+
- `WebSocket.CONNECTING` (0) - Connection is being established
|
|
156
|
+
- `WebSocket.OPEN` (1) - Connection is open and ready
|
|
157
|
+
- `WebSocket.CLOSED` (3) - Connection is closed
|
|
158
|
+
|
|
159
|
+
#### `bufferedAmount`
|
|
160
|
+
|
|
161
|
+
Returns the number of bytes of data that have been queued using `send()` but not yet transmitted.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
if (ws.bufferedAmount === 0) {
|
|
165
|
+
ws.send("Large message");
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Note:** Returns `0` if the socket is not connected.
|
|
170
|
+
|
|
171
|
+
## Examples
|
|
172
|
+
|
|
173
|
+
### Custom Retry Configuration
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const ws = new ReconnectingWebSocket("wss://api.example.com", {
|
|
177
|
+
retryDelay: 500, // Start with 500ms delay
|
|
178
|
+
maxRetryDelay: 60000, // Cap at 60 seconds
|
|
179
|
+
backoffFactor: 1.5, // Gentle backoff
|
|
180
|
+
connectionTimeout: 5000 // 5 second timeout
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Using with Node.js
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import WebSocket from "ws";
|
|
188
|
+
import { ReconnectingWebSocket } from "@iam4x/reconnecting-websocket";
|
|
189
|
+
|
|
190
|
+
const ws = new ReconnectingWebSocket("wss://api.example.com", {
|
|
191
|
+
WebSocketConstructor: WebSocket as any,
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Handling Reconnections
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
let messageQueue: string[] = [];
|
|
199
|
+
|
|
200
|
+
ws.addEventListener("open", () => {
|
|
201
|
+
// Flush queued messages when reconnected
|
|
202
|
+
while (messageQueue.length > 0) {
|
|
203
|
+
ws.send(messageQueue.shift()!);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
ws.addEventListener("reconnect", () => {
|
|
208
|
+
console.log("Reconnected! Resuming operations...");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Queue messages when disconnected
|
|
212
|
+
function sendMessage(data: string) {
|
|
213
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
214
|
+
ws.send(data);
|
|
215
|
+
} else {
|
|
216
|
+
messageQueue.push(data);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Error Handling
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
ws.addEventListener("error", (event: Event) => {
|
|
225
|
+
console.error("WebSocket error occurred:", event);
|
|
226
|
+
// Error events typically precede close events
|
|
227
|
+
// The socket will automatically attempt to reconnect
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
ws.addEventListener("close", (event) => {
|
|
231
|
+
if (event.code !== 1000) {
|
|
232
|
+
console.warn("Connection closed unexpectedly:", event.code, event.reason);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Manual Connection Management
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const ws = new ReconnectingWebSocket("wss://api.example.com");
|
|
241
|
+
|
|
242
|
+
// Later, close the connection
|
|
243
|
+
ws.close();
|
|
244
|
+
|
|
245
|
+
// To reconnect, create a new instance
|
|
246
|
+
const ws2 = new ReconnectingWebSocket("wss://api.example.com");
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Reconnection Behavior
|
|
250
|
+
|
|
251
|
+
The library uses exponential backoff for reconnection attempts:
|
|
252
|
+
|
|
253
|
+
1. **First retry**: After `retryDelay` milliseconds
|
|
254
|
+
2. **Second retry**: After `retryDelay * backoffFactor` milliseconds
|
|
255
|
+
3. **Third retry**: After `retryDelay * backoffFactor²` milliseconds
|
|
256
|
+
4. **And so on...** up to `maxRetryDelay`
|
|
257
|
+
|
|
258
|
+
Example with defaults (`retryDelay: 1000`, `backoffFactor: 2`, `maxRetryDelay: 30000`):
|
|
259
|
+
- Attempt 1: Wait 1 second
|
|
260
|
+
- Attempt 2: Wait 2 seconds
|
|
261
|
+
- Attempt 3: Wait 4 seconds
|
|
262
|
+
- Attempt 4: Wait 8 seconds
|
|
263
|
+
- Attempt 5: Wait 16 seconds
|
|
264
|
+
- Attempt 6+: Wait 30 seconds (max)
|
|
265
|
+
|
|
266
|
+
## TypeScript Support
|
|
267
|
+
|
|
268
|
+
Full TypeScript definitions are included. The library is written in TypeScript and exports all necessary types.
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import type { ReconnectingWebSocket } from "@iam4x/reconnecting-websocket";
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
MIT
|
|
277
|
+
|
|
278
|
+
## Contributing
|
|
279
|
+
|
|
280
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iam4x/reconnecting-websocket",
|
|
3
|
+
"module": "src/index.ts",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"private": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
16
|
+
"test": "bun test",
|
|
17
|
+
"lint": "bun run lint:eslint && bun run lint:tsc",
|
|
18
|
+
"lint:eslint": "eslint src",
|
|
19
|
+
"lint:tsc": "tsc --noEmit",
|
|
20
|
+
"prepare": "husky"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@eslint/js": "^9.38.0",
|
|
24
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
25
|
+
"eslint": "^9.38.0",
|
|
26
|
+
"eslint-config-prettier": "10.1.1",
|
|
27
|
+
"eslint-import-resolver-typescript": "4.3.1",
|
|
28
|
+
"eslint-plugin-import": "^2.32.0",
|
|
29
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
30
|
+
"globals": "^16.4.0",
|
|
31
|
+
"husky": "^9.1.7",
|
|
32
|
+
"prettier": "^3.6.2",
|
|
33
|
+
"typescript-eslint": "^8.46.2"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5.8.3"
|
|
37
|
+
}
|
|
38
|
+
}
|