honeydrop 1.0.1 → 1.0.3
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 +166 -230
- package/dist/honeydrop.cjs.js +10 -1
- package/dist/honeydrop.cjs.js.map +1 -1
- package/dist/honeydrop.esm.js +10 -1
- package/dist/honeydrop.esm.js.map +1 -1
- package/dist/honeydrop.umd.js +10 -1
- package/dist/honeydrop.umd.js.map +1 -1
- package/dist/index.d.ts +26 -2
- package/package.json +18 -9
package/README.md
CHANGED
|
@@ -1,298 +1,248 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# Honeydrop 🍯
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/honeydrop)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
|
|
12
|
+
**Simplify your real-time applications.**<br>
|
|
13
|
+
Effortless connection management, automatic reconnection, and powerful utilities for Socket.IO.
|
|
14
|
+
|
|
15
|
+
[Installation](#-installation) • [Quick Start](#-quick-start) • [Features](#-features) • [API Reference](#-api-reference) • [Contributing](#-contributing)
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
6
20
|
|
|
7
|
-
|
|
21
|
+
## 💡 Motivation
|
|
8
22
|
|
|
9
|
-
|
|
23
|
+
Building robust Socket.IO applications often involves repetitive boilerplate: managing room joins, handling reconnections gracefully, queuing events when the network drops, and implementing retry logic.
|
|
10
24
|
|
|
11
|
-
|
|
25
|
+
**Honeydrop** handles this complexity for you. It wraps the standard `socket.io-client` with a powerful, developer-friendly API that makes your real-time code cleaner, more reliable, and easier to maintain.
|
|
12
26
|
|
|
13
27
|
## ✨ Features
|
|
14
28
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- 🎨 **TypeScript Support** - Full type definitions included
|
|
29
|
+
- **🔌 effortless Connection Management**: Simple API to connect, disconnect, and monitor health.
|
|
30
|
+
- **🎯 Smart Event Handling**: Auto-cleanup of listeners on disconnect.
|
|
31
|
+
- **🔄 Robust Reconnection**: Configurable strategies (linear/exponential backoff) with hooks.
|
|
32
|
+
- **📛 Namespacing Made Easy**: Organize your events into logical channels (`chat:message`, `game:score`).
|
|
33
|
+
- **⚡ Powerful Utilities**: Multi-emit, multi-listen, throttle, debounce, and specific event waiting.
|
|
34
|
+
- **📦 Offline Queue**: Automatically queue events when disconnected and flush them on reconnect.
|
|
35
|
+
- **🐛 Dev-Friendly**: Built-in debug logging and full TypeScript support.
|
|
23
36
|
|
|
24
37
|
## 📦 Installation
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```
|
|
39
|
+
> [!IMPORTANT]
|
|
40
|
+
> Run the following command to install the package:
|
|
41
|
+
> ```bash
|
|
42
|
+
> npm install honeydrop
|
|
43
|
+
> ```
|
|
44
|
+
> *Note: `socket.io-client` is a peer dependency and will be installed if not present.*
|
|
29
45
|
|
|
30
46
|
## 🚀 Quick Start
|
|
31
47
|
|
|
32
|
-
|
|
48
|
+
Here's how easy it is to get started:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
33
51
|
import Honeydrop from 'honeydrop';
|
|
34
52
|
|
|
35
|
-
//
|
|
53
|
+
// 1. Initialize the client
|
|
36
54
|
const client = new Honeydrop('http://localhost:3000', {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
enabled: true,
|
|
40
|
-
maxAttempts: 5,
|
|
41
|
-
strategy: 'exponential'
|
|
42
|
-
}
|
|
55
|
+
autoConnect: true,
|
|
56
|
+
debug: true
|
|
43
57
|
});
|
|
44
58
|
|
|
45
|
-
// Listen for events
|
|
59
|
+
// 2. Listen for events
|
|
46
60
|
client.on('message', (data) => {
|
|
47
61
|
console.log('Received:', data);
|
|
48
62
|
});
|
|
49
63
|
|
|
50
|
-
// Emit events
|
|
64
|
+
// 3. Emit events (even if currently disconnected!)
|
|
51
65
|
client.emit('chat:message', { text: 'Hello, World!' });
|
|
52
66
|
|
|
53
|
-
//
|
|
54
|
-
client.disconnect();
|
|
67
|
+
// 4. Cleanup when done
|
|
68
|
+
// client.disconnect();
|
|
55
69
|
```
|
|
56
70
|
|
|
57
71
|
## 📖 API Reference
|
|
58
72
|
|
|
59
|
-
###
|
|
60
|
-
|
|
61
|
-
```javascript
|
|
62
|
-
new Honeydrop(url, options?)
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
| Option | Type | Default | Description |
|
|
66
|
-
|--------|------|---------|-------------|
|
|
67
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
68
|
-
| `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` | `'info'` | Minimum log level |
|
|
69
|
-
| `autoConnect` | `boolean` | `true` | Auto-connect on instantiation |
|
|
70
|
-
| `namespaceDelimiter` | `':' \| '/' \| '.' \| '_'` | `':'` | Delimiter for namespaced events |
|
|
71
|
-
| `reconnection` | `ReconnectionOptions` | — | Reconnection configuration |
|
|
72
|
-
| `offlineQueue` | `OfflineQueueOptions` | `{ enabled: true }` | Offline message queue configuration |
|
|
73
|
-
| `connectionMonitor` | `ConnectionMonitorOptions` | `{ enabled: true }` | Connection health monitoring |
|
|
74
|
-
| `roomManager` | `RoomManagerOptions` | — | Room join/leave event names |
|
|
75
|
-
| `socketOptions` | `object` | — | Socket.IO client options |
|
|
76
|
-
|
|
77
|
-
#### Reconnection Options
|
|
73
|
+
### Client Configuration
|
|
78
74
|
|
|
79
75
|
```javascript
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
new Honeydrop(url, {
|
|
77
|
+
debug: false, // Enable debug logs
|
|
78
|
+
autoConnect: true, // Connect immediately
|
|
79
|
+
reconnection: { // Reconnection strategy
|
|
80
|
+
enabled: true,
|
|
81
|
+
maxAttempts: 10,
|
|
82
|
+
strategy: 'exponential' // 'linear' | 'exponential'
|
|
83
|
+
},
|
|
84
|
+
offlineQueue: { // Offline behavior
|
|
85
|
+
enabled: true,
|
|
86
|
+
maxSize: 100
|
|
87
|
+
}
|
|
88
|
+
})
|
|
90
89
|
```
|
|
91
90
|
|
|
92
|
-
|
|
91
|
+
### Core Methods
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
onFlushed: (count) => {}, // Called when queue is flushed on reconnect
|
|
101
|
-
onDropped: (event) => {} // Called when event is dropped (queue full)
|
|
102
|
-
}
|
|
103
|
-
```
|
|
93
|
+
| Method | Description |
|
|
94
|
+
|--------|-------------|
|
|
95
|
+
| `connect()` | Manually connect to the server. |
|
|
96
|
+
| `disconnect()` | Disconnect and clean up all listeners. |
|
|
97
|
+
| `reconnect()` | Force a manual reconnection attempt. |
|
|
98
|
+
| `setDebug(bool)` | Toggle debug logging at runtime. |
|
|
104
99
|
|
|
100
|
+
### Event Handling
|
|
105
101
|
|
|
106
|
-
|
|
102
|
+
Honeydrop provides a rich API for event management:
|
|
107
103
|
|
|
108
|
-
```
|
|
109
|
-
//
|
|
110
|
-
client.
|
|
104
|
+
```typescript
|
|
105
|
+
// Standard listener
|
|
106
|
+
client.on('user:login', (user) => console.log(user));
|
|
111
107
|
|
|
112
|
-
//
|
|
113
|
-
client.
|
|
108
|
+
// One-time listener
|
|
109
|
+
client.once('init', () => console.log('Initialized'));
|
|
114
110
|
|
|
115
|
-
//
|
|
116
|
-
client.
|
|
117
|
-
client.id; // socket ID
|
|
111
|
+
// Remove listeners
|
|
112
|
+
client.off('user:login');
|
|
118
113
|
|
|
119
|
-
//
|
|
120
|
-
client.
|
|
121
|
-
// { connected: boolean, id: string | null, transport: string | null }
|
|
114
|
+
// Listen to MULTIPLE events with one handler
|
|
115
|
+
client.onMultiple(['connect', 'reconnect'], () => updateStatus('online'));
|
|
122
116
|
|
|
123
|
-
//
|
|
124
|
-
client.
|
|
117
|
+
// Wait for a specific event (Promise-based)
|
|
118
|
+
const data = await client.waitFor('ready', 5000);
|
|
125
119
|
```
|
|
126
120
|
|
|
127
|
-
###
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
// Register event listener
|
|
131
|
-
client.on('event', (data) => { /* ... */ });
|
|
132
|
-
|
|
133
|
-
// One-time listener
|
|
134
|
-
client.once('event', (data) => { /* ... */ });
|
|
121
|
+
### Emitting Events
|
|
135
122
|
|
|
136
|
-
|
|
137
|
-
client.off('event', handler);
|
|
138
|
-
client.off('event'); // Remove all listeners for event
|
|
123
|
+
Send data with confidence using advanced emit patterns:
|
|
139
124
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Fire once on any of the events
|
|
146
|
-
client.onceAny(['success', 'error'], (event, data) => {
|
|
147
|
-
console.log(`Got ${event}:`, data);
|
|
148
|
-
});
|
|
149
|
-
```
|
|
125
|
+
```typescript
|
|
126
|
+
// Standard emit
|
|
127
|
+
client.emit('update', data);
|
|
150
128
|
|
|
151
|
-
|
|
129
|
+
// Emit with Acknowledgment (Async/Await)
|
|
130
|
+
try {
|
|
131
|
+
const response = await client.emitWithAck('createUser', userData, 5000);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error('Server did not acknowledge in time');
|
|
134
|
+
}
|
|
152
135
|
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
client.
|
|
156
|
-
|
|
157
|
-
// Emit multiple events
|
|
158
|
-
client.emitMultiple([
|
|
159
|
-
{ event: 'init', data: { userId: 1 } },
|
|
160
|
-
{ event: 'status', data: { online: true } }
|
|
161
|
-
]);
|
|
162
|
-
|
|
163
|
-
// Emit with acknowledgment
|
|
164
|
-
const response = await client.emitWithAck('request', data, 5000);
|
|
165
|
-
|
|
166
|
-
// Emit multiple with acknowledgment
|
|
167
|
-
const responses = await client.emitMultipleWithAck([
|
|
168
|
-
{ event: 'validate', data: input1 },
|
|
169
|
-
{ event: 'validate', data: input2, timeout: 3000 }
|
|
170
|
-
]);
|
|
171
|
-
|
|
172
|
-
// Request/Response (RPC pattern)
|
|
173
|
-
const user = await client.request('getUser', { id: 123 });
|
|
174
|
-
// Server should emit 'getUser:response' with the result
|
|
175
|
-
|
|
176
|
-
// Emit with automatic retry
|
|
177
|
-
const result = await client.emitWithRetry('criticalAction', data, {
|
|
136
|
+
// Emit with Automatic Retry
|
|
137
|
+
// Great for critical actions that must reach the server
|
|
138
|
+
await client.emitWithRetry('saveData', payload, {
|
|
178
139
|
maxRetries: 3,
|
|
179
|
-
retryDelay: 1000
|
|
180
|
-
onRetry: (attempt, error) => console.log(`Retry ${attempt}`)
|
|
140
|
+
retryDelay: 1000
|
|
181
141
|
});
|
|
182
|
-
```
|
|
183
142
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
client.join('room-1');
|
|
143
|
+
// Throttled Emit (e.g., for mouse movement or window resize)
|
|
144
|
+
const updatePosition = client.throttle('cursor:move', 100);
|
|
145
|
+
updatePosition({ x: 10, y: 20 });
|
|
146
|
+
```
|
|
189
147
|
|
|
190
|
-
|
|
191
|
-
client.leave('room-1');
|
|
148
|
+
### Namespaces
|
|
192
149
|
|
|
193
|
-
|
|
194
|
-
client.toRoom('room-1').emit('message', { text: 'Hello room!' });
|
|
150
|
+
Organize your events into logical groups without creating multiple socket connections.
|
|
195
151
|
|
|
196
|
-
|
|
197
|
-
|
|
152
|
+
```typescript
|
|
153
|
+
const chat = client.namespace('chat'); // prefixes events with 'chat:'
|
|
198
154
|
|
|
199
|
-
//
|
|
200
|
-
|
|
155
|
+
chat.emit('msg', 'hello'); // Emits 'chat:msg'
|
|
156
|
+
chat.on('typing', showTyping); // Listens for 'chat:typing'
|
|
201
157
|
```
|
|
202
158
|
|
|
203
|
-
###
|
|
159
|
+
### Room Management
|
|
204
160
|
|
|
205
|
-
|
|
206
|
-
// Wait for a specific event
|
|
207
|
-
const data = await client.waitFor('ready', 5000);
|
|
161
|
+
Helper methods for room-based logic (requires server-side support for room events).
|
|
208
162
|
|
|
209
|
-
|
|
210
|
-
|
|
163
|
+
```typescript
|
|
164
|
+
client.join('room-123');
|
|
165
|
+
client.toRoom('room-123').emit('announcement', 'Welcome!');
|
|
166
|
+
const inRoom = client.isInRoom('room-123'); // true
|
|
211
167
|
```
|
|
212
168
|
|
|
213
|
-
|
|
169
|
+
## ⚛️ Using with React
|
|
214
170
|
|
|
215
|
-
|
|
216
|
-
// Create a namespace
|
|
217
|
-
const chat = client.namespace('chat');
|
|
171
|
+
Honeydrop provides first-class support for React with powerful hooks that handle lifecycle and cleanup for you.
|
|
218
172
|
|
|
219
|
-
|
|
220
|
-
chat.on('message', handler); // Listens to 'chat:message'
|
|
221
|
-
chat.emit('message', data); // Emits 'chat:message'
|
|
173
|
+
### Setup Provider
|
|
222
174
|
|
|
223
|
-
|
|
224
|
-
const room = chat.sub('room1');
|
|
225
|
-
room.emit('join'); // Emits 'chat:room1:join'
|
|
175
|
+
Wrap your app in the `HoneydropProvider` to make the client available throughout your component tree.
|
|
226
176
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
api.emit('users', query); // Emits 'api/users'
|
|
230
|
-
```
|
|
177
|
+
```tsx
|
|
178
|
+
import { Honeydrop, HoneydropProvider } from 'honeydrop';
|
|
231
179
|
|
|
232
|
-
|
|
180
|
+
const client = new Honeydrop('http://localhost:3000');
|
|
233
181
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const debouncedSearch = client.debounce('search', 300);
|
|
242
|
-
// Only emits after 300ms of no calls
|
|
243
|
-
debouncedSearch({ query: 'hello' });
|
|
182
|
+
function App() {
|
|
183
|
+
return (
|
|
184
|
+
<HoneydropProvider client={client}>
|
|
185
|
+
<Dashboard />
|
|
186
|
+
</HoneydropProvider>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
244
189
|
```
|
|
245
190
|
|
|
246
|
-
###
|
|
191
|
+
### Hooks API
|
|
247
192
|
|
|
248
|
-
|
|
193
|
+
#### useSocketEvent
|
|
194
|
+
Listens for an event and automatically removes the listener when the component unmounts. No more `useEffect` boilerplate!
|
|
249
195
|
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
client.emit('message', { text: 'Hello' }); // Queued if offline
|
|
253
|
-
|
|
254
|
-
// Check queue status
|
|
255
|
-
console.log(client.getQueueLength()); // Number of queued events
|
|
256
|
-
console.log(client.getQueuedEvents()); // Array of queued events
|
|
196
|
+
```tsx
|
|
197
|
+
import { useSocketEvent } from 'honeydrop';
|
|
257
198
|
|
|
258
|
-
|
|
259
|
-
|
|
199
|
+
function LiveCounter() {
|
|
200
|
+
// Listen for 'count:update' event
|
|
201
|
+
useSocketEvent('count:update', (count) => {
|
|
202
|
+
console.log('New count:', count);
|
|
203
|
+
});
|
|
260
204
|
|
|
261
|
-
|
|
262
|
-
|
|
205
|
+
return <div>Check console for updates</div>;
|
|
206
|
+
}
|
|
263
207
|
```
|
|
264
208
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
Monitor connection latency and quality:
|
|
268
|
-
|
|
269
|
-
```javascript
|
|
270
|
-
// Perform a ping and get latency
|
|
271
|
-
const latency = await client.ping(); // Returns latency in ms
|
|
209
|
+
#### useSocketStatus
|
|
210
|
+
Easily track your connection status to show loading spinners or offline badges.
|
|
272
211
|
|
|
273
|
-
|
|
274
|
-
|
|
212
|
+
```tsx
|
|
213
|
+
import { useSocketStatus } from 'honeydrop';
|
|
275
214
|
|
|
276
|
-
|
|
277
|
-
|
|
215
|
+
function ConnectionBadge() {
|
|
216
|
+
const status = useSocketStatus(); // 'connected' | 'disconnected' | 'connecting'
|
|
278
217
|
|
|
279
|
-
|
|
280
|
-
|
|
218
|
+
if (status === 'disconnected') {
|
|
219
|
+
return <span style={{ color: 'red' }}>Offline</span>;
|
|
220
|
+
}
|
|
221
|
+
return <span style={{ color: 'green' }}>Online</span>;
|
|
222
|
+
}
|
|
281
223
|
```
|
|
282
224
|
|
|
283
|
-
|
|
225
|
+
## 🌐 Browser Support
|
|
284
226
|
|
|
227
|
+
Honeydrop works seamlessly in both Node.js and the Browser.
|
|
228
|
+
|
|
229
|
+
### Using with Bundlers (Vite, Webpack, etc.)
|
|
285
230
|
```javascript
|
|
286
|
-
|
|
287
|
-
|
|
231
|
+
import Honeydrop from 'honeydrop';
|
|
232
|
+
```
|
|
288
233
|
|
|
289
|
-
|
|
290
|
-
|
|
234
|
+
### Using via CDN
|
|
235
|
+
```html
|
|
236
|
+
<script src="https://cdn.socket.io/4.7.4/socket.io.min.js"></script>
|
|
237
|
+
<script src="https://unpkg.com/honeydrop/dist/honeydrop.umd.js"></script>
|
|
238
|
+
<script>
|
|
239
|
+
const client = new Honeydrop.Honeydrop('http://localhost:3000');
|
|
240
|
+
</script>
|
|
291
241
|
```
|
|
292
242
|
|
|
293
|
-
##
|
|
243
|
+
## 🤝 Contributing
|
|
294
244
|
|
|
295
|
-
|
|
245
|
+
We welcome contributions! Please feel free to verify the correctness of your changes by running the demo app:
|
|
296
246
|
|
|
297
247
|
```bash
|
|
298
248
|
cd demo
|
|
@@ -300,26 +250,12 @@ npm install
|
|
|
300
250
|
npm start
|
|
301
251
|
```
|
|
302
252
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
## 🌐 Browser Usage
|
|
306
|
-
|
|
307
|
-
### With Bundler (Webpack, Vite, etc.)
|
|
308
|
-
|
|
309
|
-
```javascript
|
|
310
|
-
import Honeydrop from 'honeydrop';
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### Via Script Tag
|
|
253
|
+
## 📄 License
|
|
314
254
|
|
|
315
|
-
|
|
316
|
-
<script src="https://cdn.socket.io/4.7.4/socket.io.min.js"></script>
|
|
317
|
-
<script src="path/to/honeydrop.umd.js"></script>
|
|
318
|
-
<script>
|
|
319
|
-
const client = new Honeydrop.Honeydrop('http://localhost:3000');
|
|
320
|
-
</script>
|
|
321
|
-
```
|
|
255
|
+
MIT License © 2024 Neeraj
|
|
322
256
|
|
|
323
|
-
|
|
257
|
+
<br>
|
|
324
258
|
|
|
325
|
-
|
|
259
|
+
<p align="center">
|
|
260
|
+
Made with ❤️ by <a href="https://github.com/neeer4j">neeer4j</a>
|
|
261
|
+
</p>
|
package/dist/honeydrop.cjs.js
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("socket.io-client");const t={debug:0,info:1,warn:2,error:3,none:4},n={debug:"#9E9E9E",info:"#2196F3",warn:"#FF9800",error:"#F44336",none:""};class o{constructor(e={}){this.level=e.level??"info",this.prefix=e.prefix??"[Honeydrop]",this.enabled=e.enabled??!1,this.isBrowser="undefined"!=typeof window}setLevel(e){this.level=e}setEnabled(e){this.enabled=e}shouldLog(e){return!!this.enabled&&t[e]>=t[this.level]}formatMessage(e,t){const o=(new Date).toISOString().split("T")[1].slice(0,-1);return this.isBrowser?[`%c${this.prefix} %c${e.toUpperCase()} %c[${o}] %c${t}`,"color: #FFC107; font-weight: bold",`color: ${n[e]}; font-weight: bold`,"color: #9E9E9E","color: inherit"]:[`${this.prefix} ${e.toUpperCase()} [${o}] ${t}`]}debug(e,...t){if(this.shouldLog("debug")){const n=this.formatMessage("debug",e);console.debug(...n,...t)}}info(e,...t){if(this.shouldLog("info")){const n=this.formatMessage("info",e);console.info(...n,...t)}}warn(e,...t){if(this.shouldLog("warn")){const n=this.formatMessage("warn",e);console.warn(...n,...t)}}error(e,...t){if(this.shouldLog("error")){const n=this.formatMessage("error",e);console.error(...n,...t)}}connection(e,t){this.info({connected:"🟢 Connected to server",disconnected:"🔴 Disconnected from server",reconnecting:"🟡 Attempting to reconnect...",reconnected:"🟢 Reconnected to server"}[e],t??"")}emit(e,t){this.debug(`📤 Emit: ${e}`,void 0!==t?t:"")}receive(e,t){this.debug(`📥 Received: ${e}`,void 0!==t?t:"")}}new o;class i{constructor(e){this.listeners=new Map,this.socket=null,this.logger=e}setSocket(e){this.socket=e}on(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const n=(...n)=>{this.logger.receive(e,1===n.length?n[0]:n),t(...n)};this.socket.on(e,n),this.addListener(e,n,!1)}once(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const n=(...o)=>{this.logger.receive(e,1===o.length?o[0]:o),this.removeListener(e,n),t(...o)};this.socket.once(e,n),this.addListener(e,n,!0)}off(e,t){if(this.socket)if(t)this.socket.off(e,t),this.removeListener(e,t);else{const t=this.listeners.get(e);t&&(t.forEach(t=>{this.socket?.off(e,t.handler)}),this.listeners.delete(e))}}onMultiple(e,t){e.forEach(e=>{this.on(e,(...n)=>t(e,...n))})}onceAny(e,t){let n=!1;const o=()=>{n||(n=!0,e.forEach(e=>this.off(e)))};e.forEach(e=>{this.on(e,(...i)=>{n||(o(),t(e,...i))})})}listenerCount(e){return this.listeners.get(e)?.length??0}eventNames(){return Array.from(this.listeners.keys())}removeAllListeners(){this.socket&&(this.listeners.forEach((e,t)=>{e.forEach(e=>{this.socket?.off(t,e.handler)})}),this.listeners.clear(),this.logger.debug("All event listeners removed"))}addListener(e,t,n){const o=this.listeners.get(e)??[];o.push({event:e,handler:t,once:n}),this.listeners.set(e,o)}removeListener(e,t){const n=this.listeners.get(e);if(n){const o=n.findIndex(e=>e.handler===t);-1!==o&&(n.splice(o,1),0===n.length&&this.listeners.delete(e))}}}const s={enabled:!0,maxAttempts:10,delay:1e3,maxDelay:3e4,strategy:"exponential"};class r{constructor(e,t){this.socket=null,this.attempts=0,this.timer=null,this.isReconnecting=!1,this.options={...s,...e},this.callbacks={onReconnecting:e.onReconnecting,onReconnected:e.onReconnected,onFailed:e.onFailed},this.logger=t}setSocket(e){this.socket=e,this.setupListeners()}calculateDelay(){let e;return e="exponential"===this.options.strategy?this.options.delay*Math.pow(2,this.attempts-1):this.options.delay,Math.min(e,this.options.maxDelay)}setupListeners(){this.socket&&(this.socket.on("disconnect",e=>{this.logger.connection("disconnected",{reason:e}),"io client disconnect"!==e&&this.options.enabled&&this.startReconnection()}),this.socket.on("connect",()=>{this.isReconnecting?(this.logger.connection("reconnected"),this.callbacks.onReconnected?.(),this.reset()):this.logger.connection("connected")}),this.socket.on("connect_error",()=>{this.isReconnecting&&this.attemptReconnection()}))}startReconnection(){this.isReconnecting||(this.isReconnecting=!0,this.attempts=0,this.attemptReconnection())}attemptReconnection(){if(!this.socket||!this.isReconnecting)return;if(this.attempts++,this.attempts>this.options.maxAttempts)return this.logger.error(`Reconnection failed after ${this.options.maxAttempts} attempts`),this.callbacks.onFailed?.(),void this.reset();const e=this.calculateDelay();this.logger.connection("reconnecting",{attempt:this.attempts,delay:e}),this.callbacks.onReconnecting?.(this.attempts),this.timer=setTimeout(()=>{this.socket&&this.isReconnecting&&this.socket.connect()},e)}reconnect(){if(!this.socket)throw new Error("Socket not initialized");this.reset(),this.socket.connect()}reset(){this.isReconnecting=!1,this.attempts=0,this.timer&&(clearTimeout(this.timer),this.timer=null)}stop(){this.reset(),this.logger.debug("Reconnection handler stopped")}getStatus(){return{isReconnecting:this.isReconnecting,attempts:this.attempts}}updateOptions(e){Object.assign(this.options,e),void 0!==e.onReconnecting&&(this.callbacks.onReconnecting=e.onReconnecting),void 0!==e.onReconnected&&(this.callbacks.onReconnected=e.onReconnected),void 0!==e.onFailed&&(this.callbacks.onFailed=e.onFailed)}}class c{constructor(e,t,n={}){this.name=e,this.emitter=t,this.delimiter=n.delimiter??":"}getEventName(e){return`${this.name}${this.delimiter}${e}`}on(e,t){this.emitter.on(this.getEventName(e),t)}once(e,t){this.emitter.once(this.getEventName(e),t)}off(e,t){this.emitter.off(this.getEventName(e),t)}emit(e,...t){this.emitter.emit(this.getEventName(e),...t)}getName(){return this.name}getDelimiter(){return this.delimiter}sub(e){return new c(`${this.name}${this.delimiter}${e}`,this.emitter,{delimiter:this.delimiter})}}const h={enabled:!0,maxSize:100,maxAge:0};class l{constructor(e={},t){this.queue=[],this.socket=null,this.options={...h,...e},this.logger=t}setSocket(e){this.socket=e,e.on("connect",()=>{this.flush()})}enqueue(e,...t){if(!this.options.enabled)return!1;const n={event:e,args:t,timestamp:Date.now()};return this.options.maxSize&&this.options.maxSize>0&&this.queue.length>=this.options.maxSize?(this.logger.warn(`Offline queue full. Dropping event: ${e}`),this.options.onDropped?.(n),!1):(this.queue.push(n),this.logger.debug(`Event queued offline: ${e} (queue size: ${this.queue.length})`),this.options.onQueued?.(n),!0)}flush(){if(!this.socket?.connected||0===this.queue.length)return;if(this.options.maxAge&&this.options.maxAge>0){const e=Date.now(),t=this.queue.length;this.queue=this.queue.filter(t=>e-t.timestamp<this.options.maxAge);const n=t-this.queue.length;n>0&&this.logger.debug(`Discarded ${n} expired events from queue`)}const e=this.queue.length;if(0!==e){for(this.logger.info(`Flushing ${e} queued events...`);this.queue.length>0;){const e=this.queue.shift();this.socket.emit(e.event,...e.args),this.logger.debug(`Flushed: ${e.event}`)}this.options.onFlushed?.(e),this.logger.info(`Successfully flushed ${e} events`)}}get length(){return this.queue.length}getQueue(){return[...this.queue]}clear(){const e=this.queue.length;this.queue=[],this.logger.debug(`Cleared ${e} events from offline queue`)}get enabled(){return this.options.enabled??!0}setEnabled(e){this.options.enabled=e}}const a={enabled:!0,pingInterval:5e3,pingTimeout:3e3,sampleSize:5,thresholds:{excellent:50,good:100,fair:300}};class u{constructor(e={},t){this.socket=null,this.intervalId=null,this.latencySamples=[],this.currentQuality="disconnected",this.lastPingTime=0,this.options={...a,...e,thresholds:{...a.thresholds,...e.thresholds}},this.logger=t}setSocket(e){this.socket=e,e.on("connect",()=>{this.options.enabled&&this.start()}),e.on("disconnect",()=>{this.stop(),this.updateQuality("disconnected",0)}),e.on("pong",()=>{const e=Date.now()-this.lastPingTime;this.recordLatency(e)}),e.connected&&this.options.enabled&&this.start()}start(){this.intervalId||(this.logger.debug("Connection monitoring started"),this.intervalId=setInterval(()=>{this.ping().catch(()=>{})},this.options.pingInterval??a.pingInterval),this.ping().catch(()=>{}))}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null,this.logger.debug("Connection monitoring stopped"))}async ping(){if(!this.socket?.connected)throw new Error("Socket not connected");return new Promise((e,t)=>{const n=this.options.pingTimeout??a.pingTimeout,o=setTimeout(()=>{t(new Error("Ping timeout")),this.recordLatency(n)},n);this.lastPingTime=Date.now(),this.socket.volatile.emit("ping",()=>{clearTimeout(o);const t=Date.now()-this.lastPingTime;this.recordLatency(t),e(t)})})}recordLatency(e){const t=this.options.sampleSize??a.sampleSize;this.latencySamples.push(e),this.latencySamples.length>t&&this.latencySamples.shift();const n=this.getAverageLatency(),o=this.calculateQuality(n);o!==this.currentQuality&&this.updateQuality(o,n)}updateQuality(e,t){const n=this.currentQuality;this.currentQuality=e,this.logger.debug(`Connection quality: ${n} -> ${e} (${t}ms)`),this.options.onQualityChange?.(e,t)}calculateQuality(e){const t={...a.thresholds,...this.options.thresholds};return e<=t.excellent?"excellent":e<=t.good?"good":e<=t.fair?"fair":"poor"}getAverageLatency(){if(0===this.latencySamples.length)return 0;const e=this.latencySamples.reduce((e,t)=>e+t,0);return Math.round(e/this.latencySamples.length)}getLatency(){return this.latencySamples[this.latencySamples.length-1]??0}getQuality(){return this.currentQuality}getLatencySamples(){return[...this.latencySamples]}get isMonitoring(){return null!==this.intervalId}setEnabled(e){this.options.enabled=e,e&&this.socket?.connected?this.start():e||this.stop()}}const g={joinEvent:"join",leaveEvent:"leave"};class d{constructor(e={},t){this.socket=null,this.joinedRooms=new Set,this.options={...g,...e},this.logger=t}setSocket(e){this.socket=e,e.on("disconnect",()=>{this.joinedRooms.clear(),this.logger.debug("Cleared room list on disconnect")})}join(e,t){if(!this.socket?.connected)throw new Error("Socket not connected. Cannot join room.");const n=this.options.joinEvent??g.joinEvent;void 0!==t?this.socket.emit(n,e,t):this.socket.emit(n,e),this.joinedRooms.add(e),this.logger.debug(`Joined room: ${e}`)}leave(e,t){if(!this.socket?.connected)throw new Error("Socket not connected. Cannot leave room.");const n=this.options.leaveEvent??g.leaveEvent;void 0!==t?this.socket.emit(n,e,t):this.socket.emit(n,e),this.joinedRooms.delete(e),this.logger.debug(`Left room: ${e}`)}toRoom(e){return{emit:(t,...n)=>{if(!this.socket?.connected)throw new Error("Socket not connected. Cannot emit to room.");this.socket.emit(t,e,...n),this.logger.debug(`Emitted to room ${e}: ${t}`)}}}getRooms(){return Array.from(this.joinedRooms)}isInRoom(e){return this.joinedRooms.has(e)}leaveAll(){for(const e of this.joinedRooms)this.leave(e)}}function m(e,t){t.forEach(({event:t,data:n})=>{void 0!==n?e.emit(t,n):e.emit(t)})}async function p(e,t){const n=t.map(async({event:t,data:n,timeout:o=5e3})=>new Promise((i,s)=>{const r=setTimeout(()=>{s(new Error(`Timeout waiting for ack: ${t}`))},o),c=void 0!==n?[n]:[];e.emit(t,...c,e=>{clearTimeout(r),i(e)})}));return Promise.all(n)}function f(e,t,n=5e3){return new Promise((o,i)=>{const s=setTimeout(()=>{e.off(t,r),i(new Error(`Timeout waiting for event: ${t}`))},n),r=e=>{clearTimeout(s),o(e)};e.once(t,r)})}function k(e,t,n=5e3){return new Promise((o,i)=>{const s=new Map,r=setTimeout(()=>{c(),i(new Error(`Timeout waiting for events: ${t.join(", ")}`))},n),c=()=>{s.forEach((t,n)=>{e.off(n,t)})};t.forEach(t=>{const n=e=>{clearTimeout(r),c(),o({event:t,data:e})};s.set(t,n),e.once(t,n)})})}function v(e,t,n,o=5e3){return new Promise((i,s)=>{const r=setTimeout(()=>{s(new Error(`Timeout waiting for ack: ${t}`))},o),c=void 0!==n?[n]:[];e.emit(t,...c,e=>{clearTimeout(r),i(e)})})}function w(e){return e?.connected??!1}function y(e){return e?{connected:e.connected,id:e.id??null,transport:e.io?.engine?.transport?.name??null}:{connected:!1,id:null,transport:null}}function E(e,t,n){let o,i=0,s=null;return r=>{const c=Date.now(),h=c-i;h>=n?(i=c,void 0!==r?e.emit(t,r):e.emit(t)):(o=r,s||(s=setTimeout(()=>{i=Date.now(),void 0!==o?e.emit(t,o):e.emit(t),s=null},n-h)))}}function b(e,t,n){let o=null;return i=>{o&&clearTimeout(o),o=setTimeout(()=>{void 0!==i?e.emit(t,i):e.emit(t),o=null},n)}}const R={debug:!1,logLevel:"info",namespaceDelimiter:":",autoConnect:!0};class S{constructor(e,t={}){this.socket=null,this.reconnectionHandler=null,this.namespaces=new Map,this.url=e,this.options={...R,...t},this.logger=new o({enabled:this.options.debug,level:this.options.logLevel}),this.eventManager=new i(this.logger),this.offlineQueue=new l(this.options.offlineQueue??{},this.logger),this.connectionMonitor=new u(this.options.connectionMonitor??{},this.logger),this.roomManager=new d(this.options.roomManager??{},this.logger),!1!==this.options.reconnection?.enabled&&(this.reconnectionHandler=new r(this.options.reconnection??{},this.logger)),this.options.autoConnect&&this.connect()}connect(){if(this.socket?.connected)return this.logger.warn("Already connected"),this.socket;const t={...this.options.socketOptions,reconnection:!1};return this.socket=e.io(this.url,t),this.eventManager.setSocket(this.socket),this.offlineQueue.setSocket(this.socket),this.connectionMonitor.setSocket(this.socket),this.roomManager.setSocket(this.socket),this.reconnectionHandler&&this.reconnectionHandler.setSocket(this.socket),this.socket}disconnect(){this.socket&&(this.eventManager.removeAllListeners(),this.reconnectionHandler?.stop(),this.connectionMonitor.stop(),this.socket.disconnect(),this.logger.connection("disconnected"))}on(e,t){return this.eventManager.on(e,t),this}once(e,t){return this.eventManager.once(e,t),this}off(e,t){return this.eventManager.off(e,t),this}emit(e,...t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return!this.socket.connected&&this.offlineQueue.enabled?(this.offlineQueue.enqueue(e,...t),this):(this.logger.emit(e,1===t.length?t[0]:t),this.socket.emit(e,...t),this)}emitMultiple(e){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return m(this.socket,e),this}async emitWithAck(e,t,n){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return v(this.socket,e,t,n)}async request(e,t,n={}){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const o=n.timeout??5e3,i=n.responseEvent??`${e}:response`;return new Promise((n,s)=>{const r=setTimeout(()=>{this.socket?.off(i,c),s(new Error(`Request timeout: ${e} (${o}ms)`))},o),c=e=>{clearTimeout(r),n(e)};this.socket.once(i,c),this.logger.emit(e,t),void 0!==t?this.socket.emit(e,t):this.socket.emit(e)})}async emitWithRetry(e,t,n={}){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const o=n.maxRetries??3,i=n.retryDelay??1e3,s=n.timeout??5e3;let r=new Error("Unknown error");for(let c=1;c<=o;c++)try{return await v(this.socket,e,t,s)}catch(t){r=t instanceof Error?t:new Error(String(t)),this.logger.warn(`Emit failed (attempt ${c}/${o}): ${e}`),n.onRetry?.(c,r),c<o&&await new Promise(e=>setTimeout(e,i))}throw new Error(`Emit failed after ${o} attempts: ${e}. Last error: ${r.message}`)}async emitMultipleWithAck(e){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return p(this.socket,e)}onMultiple(e,t){return this.eventManager.onMultiple(e,t),this}onceAny(e,t){return this.eventManager.onceAny(e,t),this}async waitFor(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return f(this.socket,e,t)}async waitForAny(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return k(this.socket,e,t)}namespace(e,t){const n=e;if(!this.namespaces.has(n)){const o={delimiter:t?.delimiter??this.options.namespaceDelimiter};this.namespaces.set(n,new c(e,this,o))}return this.namespaces.get(n)}throttle(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return E(this.socket,e,t)}debounce(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return b(this.socket,e,t)}getSocket(){return this.socket}get connected(){return w(this.socket)}get id(){return this.socket?.id}getConnectionInfo(){return y(this.socket)}reconnect(){if(!this.reconnectionHandler)throw new Error("Reconnection is not enabled");this.reconnectionHandler.reconnect()}getReconnectionStatus(){return this.reconnectionHandler?.getStatus()??null}listenerCount(e){return this.eventManager.listenerCount(e)}eventNames(){return this.eventManager.eventNames()}setDebug(e){return this.logger.setEnabled(e),this}setLogLevel(e){return this.logger.setLevel(e),this}getQueueLength(){return this.offlineQueue.length}getQueuedEvents(){return this.offlineQueue.getQueue()}clearQueue(){return this.offlineQueue.clear(),this}setOfflineQueue(e){return this.offlineQueue.setEnabled(e),this}async ping(){return this.connectionMonitor.ping()}getLatency(){return this.connectionMonitor.getAverageLatency()}getConnectionQuality(){return this.connectionMonitor.getQuality()}setConnectionMonitoring(e){return this.connectionMonitor.setEnabled(e),this}join(e,t){return this.roomManager.join(e,t),this}leave(e,t){return this.roomManager.leave(e,t),this}toRoom(e){return this.roomManager.toRoom(e)}getRooms(){return this.roomManager.getRooms()}isInRoom(e){return this.roomManager.isInRoom(e)}}exports.ConnectionMonitor=u,exports.EventManager=i,exports.Honeydrop=S,exports.Logger=o,exports.NamespacedEvents=c,exports.OfflineQueue=l,exports.ReconnectionHandler=r,exports.RoomManager=d,exports.createDebouncedEmit=b,exports.createThrottledEmit=E,exports.default=S,exports.emitMultiple=m,exports.emitMultipleWithAck=p,exports.emitWithAck=v,exports.getConnectionInfo=y,exports.isConnected=w,exports.waitForAnyEvent=k,exports.waitForEvent=f;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("socket.io-client"),t=require("react");const n={debug:0,info:1,warn:2,error:3,none:4},o={debug:"#9E9E9E",info:"#2196F3",warn:"#FF9800",error:"#F44336",none:""};class i{constructor(e={}){this.level=e.level??"info",this.prefix=e.prefix??"[Honeydrop]",this.enabled=e.enabled??!1,this.isBrowser="undefined"!=typeof window}setLevel(e){this.level=e}setEnabled(e){this.enabled=e}shouldLog(e){return!!this.enabled&&n[e]>=n[this.level]}formatMessage(e,t){const n=(new Date).toISOString().split("T")[1].slice(0,-1);return this.isBrowser?[`%c${this.prefix} %c${e.toUpperCase()} %c[${n}] %c${t}`,"color: #FFC107; font-weight: bold",`color: ${o[e]}; font-weight: bold`,"color: #9E9E9E","color: inherit"]:[`${this.prefix} ${e.toUpperCase()} [${n}] ${t}`]}debug(e,...t){if(this.shouldLog("debug")){const n=this.formatMessage("debug",e);console.debug(...n,...t)}}info(e,...t){if(this.shouldLog("info")){const n=this.formatMessage("info",e);console.info(...n,...t)}}warn(e,...t){if(this.shouldLog("warn")){const n=this.formatMessage("warn",e);console.warn(...n,...t)}}error(e,...t){if(this.shouldLog("error")){const n=this.formatMessage("error",e);console.error(...n,...t)}}connection(e,t){this.info({connected:"🟢 Connected to server",disconnected:"🔴 Disconnected from server",reconnecting:"🟡 Attempting to reconnect...",reconnected:"🟢 Reconnected to server"}[e],t??"")}emit(e,t){this.debug(`📤 Emit: ${e}`,void 0!==t?t:"")}receive(e,t){this.debug(`📥 Received: ${e}`,void 0!==t?t:"")}}new i;class s{constructor(e){this.listeners=new Map,this.socket=null,this.logger=e}setSocket(e){this.socket=e}on(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const n=(...n)=>{this.logger.receive(e,1===n.length?n[0]:n),t(...n)};this.socket.on(e,n),this.addListener(e,n,!1)}once(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const n=(...o)=>{this.logger.receive(e,1===o.length?o[0]:o),this.removeListener(e,n),t(...o)};this.socket.once(e,n),this.addListener(e,n,!0)}off(e,t){if(this.socket)if(t)this.socket.off(e,t),this.removeListener(e,t);else{const t=this.listeners.get(e);t&&(t.forEach(t=>{this.socket?.off(e,t.handler)}),this.listeners.delete(e))}}onMultiple(e,t){e.forEach(e=>{this.on(e,(...n)=>t(e,...n))})}onceAny(e,t){let n=!1;const o=()=>{n||(n=!0,e.forEach(e=>this.off(e)))};e.forEach(e=>{this.on(e,(...i)=>{n||(o(),t(e,...i))})})}listenerCount(e){return this.listeners.get(e)?.length??0}eventNames(){return Array.from(this.listeners.keys())}removeAllListeners(){this.socket&&(this.listeners.forEach((e,t)=>{e.forEach(e=>{this.socket?.off(t,e.handler)})}),this.listeners.clear(),this.logger.debug("All event listeners removed"))}addListener(e,t,n){const o=this.listeners.get(e)??[];o.push({event:e,handler:t,once:n}),this.listeners.set(e,o)}removeListener(e,t){const n=this.listeners.get(e);if(n){const o=n.findIndex(e=>e.handler===t);-1!==o&&(n.splice(o,1),0===n.length&&this.listeners.delete(e))}}}const r={enabled:!0,maxAttempts:10,delay:1e3,maxDelay:3e4,strategy:"exponential"};class c{constructor(e,t){this.socket=null,this.attempts=0,this.timer=null,this.isReconnecting=!1,this.options={...r,...e},this.callbacks={onReconnecting:e.onReconnecting,onReconnected:e.onReconnected,onFailed:e.onFailed},this.logger=t}setSocket(e){this.socket=e,this.setupListeners()}calculateDelay(){let e;return e="exponential"===this.options.strategy?this.options.delay*Math.pow(2,this.attempts-1):this.options.delay,Math.min(e,this.options.maxDelay)}setupListeners(){this.socket&&(this.socket.on("disconnect",e=>{this.logger.connection("disconnected",{reason:e}),"io client disconnect"!==e&&this.options.enabled&&this.startReconnection()}),this.socket.on("connect",()=>{this.isReconnecting?(this.logger.connection("reconnected"),this.callbacks.onReconnected?.(),this.reset()):this.logger.connection("connected")}),this.socket.on("connect_error",()=>{this.isReconnecting&&this.attemptReconnection()}))}startReconnection(){this.isReconnecting||(this.isReconnecting=!0,this.attempts=0,this.attemptReconnection())}attemptReconnection(){if(!this.socket||!this.isReconnecting)return;if(this.attempts++,this.attempts>this.options.maxAttempts)return this.logger.error(`Reconnection failed after ${this.options.maxAttempts} attempts`),this.callbacks.onFailed?.(),void this.reset();const e=this.calculateDelay();this.logger.connection("reconnecting",{attempt:this.attempts,delay:e}),this.callbacks.onReconnecting?.(this.attempts),this.timer=setTimeout(()=>{this.socket&&this.isReconnecting&&this.socket.connect()},e)}reconnect(){if(!this.socket)throw new Error("Socket not initialized");this.reset(),this.socket.connect()}reset(){this.isReconnecting=!1,this.attempts=0,this.timer&&(clearTimeout(this.timer),this.timer=null)}stop(){this.reset(),this.logger.debug("Reconnection handler stopped")}getStatus(){return{isReconnecting:this.isReconnecting,attempts:this.attempts}}updateOptions(e){Object.assign(this.options,e),void 0!==e.onReconnecting&&(this.callbacks.onReconnecting=e.onReconnecting),void 0!==e.onReconnected&&(this.callbacks.onReconnected=e.onReconnected),void 0!==e.onFailed&&(this.callbacks.onFailed=e.onFailed)}}class a{constructor(e,t,n={}){this.name=e,this.emitter=t,this.delimiter=n.delimiter??":"}getEventName(e){return`${this.name}${this.delimiter}${e}`}on(e,t){this.emitter.on(this.getEventName(e),t)}once(e,t){this.emitter.once(this.getEventName(e),t)}off(e,t){this.emitter.off(this.getEventName(e),t)}emit(e,...t){this.emitter.emit(this.getEventName(e),...t)}getName(){return this.name}getDelimiter(){return this.delimiter}sub(e){return new a(`${this.name}${this.delimiter}${e}`,this.emitter,{delimiter:this.delimiter})}}const l={enabled:!0,maxSize:100,maxAge:0};class h{constructor(e={},t){this.queue=[],this.socket=null,this.options={...l,...e},this.logger=t}setSocket(e){this.socket=e,e.on("connect",()=>{this.flush()})}enqueue(e,...t){if(!this.options.enabled)return!1;const n={event:e,args:t,timestamp:Date.now()};return this.options.maxSize&&this.options.maxSize>0&&this.queue.length>=this.options.maxSize?(this.logger.warn(`Offline queue full. Dropping event: ${e}`),this.options.onDropped?.(n),!1):(this.queue.push(n),this.logger.debug(`Event queued offline: ${e} (queue size: ${this.queue.length})`),this.options.onQueued?.(n),!0)}flush(){if(!this.socket?.connected||0===this.queue.length)return;if(this.options.maxAge&&this.options.maxAge>0){const e=Date.now(),t=this.queue.length;this.queue=this.queue.filter(t=>e-t.timestamp<this.options.maxAge);const n=t-this.queue.length;n>0&&this.logger.debug(`Discarded ${n} expired events from queue`)}const e=this.queue.length;if(0!==e){for(this.logger.info(`Flushing ${e} queued events...`);this.queue.length>0;){const e=this.queue.shift();this.socket.emit(e.event,...e.args),this.logger.debug(`Flushed: ${e.event}`)}this.options.onFlushed?.(e),this.logger.info(`Successfully flushed ${e} events`)}}get length(){return this.queue.length}getQueue(){return[...this.queue]}clear(){const e=this.queue.length;this.queue=[],this.logger.debug(`Cleared ${e} events from offline queue`)}get enabled(){return this.options.enabled??!0}setEnabled(e){this.options.enabled=e}}const u={enabled:!0,pingInterval:5e3,pingTimeout:3e3,sampleSize:5,thresholds:{excellent:50,good:100,fair:300}};class d{constructor(e={},t){this.socket=null,this.intervalId=null,this.latencySamples=[],this.currentQuality="disconnected",this.lastPingTime=0,this.options={...u,...e,thresholds:{...u.thresholds,...e.thresholds}},this.logger=t}setSocket(e){this.socket=e,e.on("connect",()=>{this.options.enabled&&this.start()}),e.on("disconnect",()=>{this.stop(),this.updateQuality("disconnected",0)}),e.on("pong",()=>{const e=Date.now()-this.lastPingTime;this.recordLatency(e)}),e.connected&&this.options.enabled&&this.start()}start(){this.intervalId||(this.logger.debug("Connection monitoring started"),this.intervalId=setInterval(()=>{this.ping().catch(()=>{})},this.options.pingInterval??u.pingInterval),this.ping().catch(()=>{}))}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null,this.logger.debug("Connection monitoring stopped"))}async ping(){if(!this.socket?.connected)throw new Error("Socket not connected");return new Promise((e,t)=>{const n=this.options.pingTimeout??u.pingTimeout,o=setTimeout(()=>{t(new Error("Ping timeout")),this.recordLatency(n)},n);this.lastPingTime=Date.now(),this.socket.volatile.emit("ping",()=>{clearTimeout(o);const t=Date.now()-this.lastPingTime;this.recordLatency(t),e(t)})})}recordLatency(e){const t=this.options.sampleSize??u.sampleSize;this.latencySamples.push(e),this.latencySamples.length>t&&this.latencySamples.shift();const n=this.getAverageLatency(),o=this.calculateQuality(n);o!==this.currentQuality&&this.updateQuality(o,n)}updateQuality(e,t){const n=this.currentQuality;this.currentQuality=e,this.logger.debug(`Connection quality: ${n} -> ${e} (${t}ms)`),this.options.onQualityChange?.(e,t)}calculateQuality(e){const t={...u.thresholds,...this.options.thresholds};return e<=t.excellent?"excellent":e<=t.good?"good":e<=t.fair?"fair":"poor"}getAverageLatency(){if(0===this.latencySamples.length)return 0;const e=this.latencySamples.reduce((e,t)=>e+t,0);return Math.round(e/this.latencySamples.length)}getLatency(){return this.latencySamples[this.latencySamples.length-1]??0}getQuality(){return this.currentQuality}getLatencySamples(){return[...this.latencySamples]}get isMonitoring(){return null!==this.intervalId}setEnabled(e){this.options.enabled=e,e&&this.socket?.connected?this.start():e||this.stop()}}const f={joinEvent:"join",leaveEvent:"leave"};class m{constructor(e={},t){this.socket=null,this.joinedRooms=new Set,this.options={...f,...e},this.logger=t}setSocket(e){this.socket=e,e.on("disconnect",()=>{this.joinedRooms.clear(),this.logger.debug("Cleared room list on disconnect")})}join(e,t){if(!this.socket?.connected)throw new Error("Socket not connected. Cannot join room.");const n=this.options.joinEvent??f.joinEvent;void 0!==t?this.socket.emit(n,e,t):this.socket.emit(n,e),this.joinedRooms.add(e),this.logger.debug(`Joined room: ${e}`)}leave(e,t){if(!this.socket?.connected)throw new Error("Socket not connected. Cannot leave room.");const n=this.options.leaveEvent??f.leaveEvent;void 0!==t?this.socket.emit(n,e,t):this.socket.emit(n,e),this.joinedRooms.delete(e),this.logger.debug(`Left room: ${e}`)}toRoom(e){return{emit:(t,...n)=>{if(!this.socket?.connected)throw new Error("Socket not connected. Cannot emit to room.");this.socket.emit(t,e,...n),this.logger.debug(`Emitted to room ${e}: ${t}`)}}}getRooms(){return Array.from(this.joinedRooms)}isInRoom(e){return this.joinedRooms.has(e)}leaveAll(){for(const e of this.joinedRooms)this.leave(e)}}function g(e,t){t.forEach(({event:t,data:n})=>{void 0!==n?e.emit(t,n):e.emit(t)})}async function p(e,t){const n=t.map(async({event:t,data:n,timeout:o=5e3})=>new Promise((i,s)=>{const r=setTimeout(()=>{s(new Error(`Timeout waiting for ack: ${t}`))},o),c=void 0!==n?[n]:[];e.emit(t,...c,e=>{clearTimeout(r),i(e)})}));return Promise.all(n)}function y(e,t,n=5e3){return new Promise((o,i)=>{const s=setTimeout(()=>{e.off(t,r),i(new Error(`Timeout waiting for event: ${t}`))},n),r=e=>{clearTimeout(s),o(e)};e.once(t,r)})}function k(e,t,n=5e3){return new Promise((o,i)=>{const s=new Map,r=setTimeout(()=>{c(),i(new Error(`Timeout waiting for events: ${t.join(", ")}`))},n),c=()=>{s.forEach((t,n)=>{e.off(n,t)})};t.forEach(t=>{const n=e=>{clearTimeout(r),c(),o({event:t,data:e})};s.set(t,n),e.once(t,n)})})}function v(e,t,n,o=5e3){return new Promise((i,s)=>{const r=setTimeout(()=>{s(new Error(`Timeout waiting for ack: ${t}`))},o),c=void 0!==n?[n]:[];e.emit(t,...c,e=>{clearTimeout(r),i(e)})})}function b(e){return e?.connected??!1}function w(e){return e?{connected:e.connected,id:e.id??null,transport:e.io?.engine?.transport?.name??null}:{connected:!1,id:null,transport:null}}function E(e,t,n){let o,i=0,s=null;return r=>{const c=Date.now(),a=c-i;a>=n?(i=c,void 0!==r?e.emit(t,r):e.emit(t)):(o=r,s||(s=setTimeout(()=>{i=Date.now(),void 0!==o?e.emit(t,o):e.emit(t),s=null},n-a)))}}function S(e,t,n){let o=null;return i=>{o&&clearTimeout(o),o=setTimeout(()=>{void 0!==i?e.emit(t,i):e.emit(t),o=null},n)}}const R={debug:!1,logLevel:"info",namespaceDelimiter:":",autoConnect:!0};class x{constructor(e,t={}){this.socket=null,this.reconnectionHandler=null,this.namespaces=new Map,this.url=e,this.options={...R,...t},this.logger=new i({enabled:this.options.debug,level:this.options.logLevel}),this.eventManager=new s(this.logger),this.offlineQueue=new h(this.options.offlineQueue??{},this.logger),this.connectionMonitor=new d(this.options.connectionMonitor??{},this.logger),this.roomManager=new m(this.options.roomManager??{},this.logger),!1!==this.options.reconnection?.enabled&&(this.reconnectionHandler=new c(this.options.reconnection??{},this.logger)),this.options.autoConnect&&this.connect()}connect(){if(this.socket?.connected)return this.logger.warn("Already connected"),this.socket;const t={...this.options.socketOptions,reconnection:!1};return this.socket=e.io(this.url,t),this.eventManager.setSocket(this.socket),this.offlineQueue.setSocket(this.socket),this.connectionMonitor.setSocket(this.socket),this.roomManager.setSocket(this.socket),this.reconnectionHandler&&this.reconnectionHandler.setSocket(this.socket),this.socket}disconnect(){this.socket&&(this.eventManager.removeAllListeners(),this.reconnectionHandler?.stop(),this.connectionMonitor.stop(),this.socket.disconnect(),this.logger.connection("disconnected"))}on(e,t){return this.eventManager.on(e,t),this}once(e,t){return this.eventManager.once(e,t),this}off(e,t){return this.eventManager.off(e,t),this}emit(e,...t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return!this.socket.connected&&this.offlineQueue.enabled?(this.offlineQueue.enqueue(e,...t),this):(this.logger.emit(e,1===t.length?t[0]:t),this.socket.emit(e,...t),this)}emitMultiple(e){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return g(this.socket,e),this}async emitWithAck(e,t,n){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return v(this.socket,e,t,n)}async request(e,t,n={}){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const o=n.timeout??5e3,i=n.responseEvent??`${e}:response`;return new Promise((n,s)=>{const r=setTimeout(()=>{this.socket?.off(i,c),s(new Error(`Request timeout: ${e} (${o}ms)`))},o),c=e=>{clearTimeout(r),n(e)};this.socket.once(i,c),this.logger.emit(e,t),void 0!==t?this.socket.emit(e,t):this.socket.emit(e)})}async emitWithRetry(e,t,n={}){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");const o=n.maxRetries??3,i=n.retryDelay??1e3,s=n.timeout??5e3;let r=new Error("Unknown error");for(let c=1;c<=o;c++)try{return await v(this.socket,e,t,s)}catch(t){r=t instanceof Error?t:new Error(String(t)),this.logger.warn(`Emit failed (attempt ${c}/${o}): ${e}`),n.onRetry?.(c,r),c<o&&await new Promise(e=>setTimeout(e,i))}throw new Error(`Emit failed after ${o} attempts: ${e}. Last error: ${r.message}`)}async emitMultipleWithAck(e){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return p(this.socket,e)}onMultiple(e,t){return this.eventManager.onMultiple(e,t),this}onceAny(e,t){return this.eventManager.onceAny(e,t),this}async waitFor(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return y(this.socket,e,t)}async waitForAny(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return k(this.socket,e,t)}namespace(e,t){const n=e;if(!this.namespaces.has(n)){const o={delimiter:t?.delimiter??this.options.namespaceDelimiter};this.namespaces.set(n,new a(e,this,o))}return this.namespaces.get(n)}throttle(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return E(this.socket,e,t)}debounce(e,t){if(!this.socket)throw new Error("Socket not initialized. Call connect() first.");return S(this.socket,e,t)}getSocket(){return this.socket}get connected(){return b(this.socket)}get id(){return this.socket?.id}getConnectionInfo(){return w(this.socket)}reconnect(){if(!this.reconnectionHandler)throw new Error("Reconnection is not enabled");this.reconnectionHandler.reconnect()}getReconnectionStatus(){return this.reconnectionHandler?.getStatus()??null}listenerCount(e){return this.eventManager.listenerCount(e)}eventNames(){return this.eventManager.eventNames()}setDebug(e){return this.logger.setEnabled(e),this}setLogLevel(e){return this.logger.setLevel(e),this}getQueueLength(){return this.offlineQueue.length}getQueuedEvents(){return this.offlineQueue.getQueue()}clearQueue(){return this.offlineQueue.clear(),this}setOfflineQueue(e){return this.offlineQueue.setEnabled(e),this}async ping(){return this.connectionMonitor.ping()}getLatency(){return this.connectionMonitor.getAverageLatency()}getConnectionQuality(){return this.connectionMonitor.getQuality()}setConnectionMonitoring(e){return this.connectionMonitor.setEnabled(e),this}join(e,t){return this.roomManager.join(e,t),this}leave(e,t){return this.roomManager.leave(e,t),this}toRoom(e){return this.roomManager.toRoom(e)}getRooms(){return this.roomManager.getRooms()}isInRoom(e){return this.roomManager.isInRoom(e)}}var $,M={exports:{}},j={};var _,C={};
|
|
2
|
+
/**
|
|
3
|
+
* @license React
|
|
4
|
+
* react-jsx-runtime.development.js
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the MIT license found in the
|
|
9
|
+
* LICENSE file in the root directory of this source tree.
|
|
10
|
+
*/"production"===process.env.NODE_ENV?M.exports=function(){if($)return j;$=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function n(t,n,o){var i=null;if(void 0!==o&&(i=""+o),void 0!==n.key&&(i=""+n.key),"key"in n)for(var s in o={},n)"key"!==s&&(o[s]=n[s]);else o=n;return n=o.ref,{$$typeof:e,type:t,key:i,ref:void 0!==n?n:null,props:o}}return j.Fragment=t,j.jsx=n,j.jsxs=n,j}():M.exports=(_||(_=1,"production"!==process.env.NODE_ENV&&function(){function e(t){if(null==t)return null;if("function"==typeof t)return t.$$typeof===x?null:t.displayName||t.name||null;if("string"==typeof t)return t;switch(t){case m:return"Fragment";case p:return"Profiler";case g:return"StrictMode";case b:return"Suspense";case w:return"SuspenseList";case R:return"Activity"}if("object"==typeof t)switch("number"==typeof t.tag&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),t.$$typeof){case f:return"Portal";case k:return t.displayName||"Context";case y:return(t._context.displayName||"Context")+".Consumer";case v:var n=t.render;return(t=t.displayName)||(t=""!==(t=n.displayName||n.name||"")?"ForwardRef("+t+")":"ForwardRef"),t;case E:return null!==(n=t.displayName||null)?n:e(t.type)||"Memo";case S:n=t._payload,t=t._init;try{return e(t(n))}catch(e){}}return null}function n(e){return""+e}function o(e){try{n(e);var t=!1}catch(e){t=!0}if(t){var o=(t=console).error,i="function"==typeof Symbol&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return o.call(t,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",i),n(e)}}function i(t){if(t===m)return"<>";if("object"==typeof t&&null!==t&&t.$$typeof===S)return"<...>";try{var n=e(t);return n?"<"+n+">":"<...>"}catch(e){return"<...>"}}function s(){return Error("react-stack-top-frame")}function r(){var t=e(this.type);return T[t]||(T[t]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),void 0!==(t=this.props.ref)?t:null}function c(t,n,i,s,c,l){var u,f=n.children;if(void 0!==f)if(s)if(j(f)){for(s=0;s<f.length;s++)a(f[s]);Object.freeze&&Object.freeze(f)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else a(f);if(M.call(n,"key")){f=e(t);var m=Object.keys(n).filter(function(e){return"key"!==e});s=0<m.length?"{key: someKey, "+m.join(": ..., ")+": ...}":"{key: someKey}",O[f+s]||(m=0<m.length?"{"+m.join(": ..., ")+": ...}":"{}",console.error('A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />',s,f,m,f),O[f+s]=!0)}if(f=null,void 0!==i&&(o(i),f=""+i),function(e){if(M.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return void 0!==e.key}(n)&&(o(n.key),f=""+n.key),"key"in n)for(var g in i={},n)"key"!==g&&(i[g]=n[g]);else i=n;return f&&function(e,t){function n(){h||(h=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}n.isReactWarning=!0,Object.defineProperty(e,"key",{get:n,configurable:!0})}(i,"function"==typeof t?t.displayName||t.name||"Unknown":t),function(e,t,n,o,i,s){var c=n.ref;return e={$$typeof:d,type:e,key:t,props:n,_owner:o},null!==(void 0!==c?c:null)?Object.defineProperty(e,"ref",{enumerable:!1,get:r}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:i}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:s}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}(t,f,i,null===(u=$.A)?null:u.getOwner(),c,l)}function a(e){l(e)?e._store&&(e._store.validated=1):"object"==typeof e&&null!==e&&e.$$typeof===S&&("fulfilled"===e._payload.status?l(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function l(e){return"object"==typeof e&&null!==e&&e.$$typeof===d}var h,u=t,d=Symbol.for("react.transitional.element"),f=Symbol.for("react.portal"),m=Symbol.for("react.fragment"),g=Symbol.for("react.strict_mode"),p=Symbol.for("react.profiler"),y=Symbol.for("react.consumer"),k=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),b=Symbol.for("react.suspense"),w=Symbol.for("react.suspense_list"),E=Symbol.for("react.memo"),S=Symbol.for("react.lazy"),R=Symbol.for("react.activity"),x=Symbol.for("react.client.reference"),$=u.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,M=Object.prototype.hasOwnProperty,j=Array.isArray,_=console.createTask?console.createTask:function(){return null},T={},L=(u={react_stack_bottom_frame:function(e){return e()}}).react_stack_bottom_frame.bind(u,s)(),A=_(i(s)),O={};C.Fragment=m,C.jsx=function(e,t,n){var o=1e4>$.recentlyCreatedOwnerStacks++;return c(e,t,n,!1,o?Error("react-stack-top-frame"):L,o?_(i(e)):A)},C.jsxs=function(e,t,n){var o=1e4>$.recentlyCreatedOwnerStacks++;return c(e,t,n,!0,o?Error("react-stack-top-frame"):L,o?_(i(e)):A)}}()),C);var T=M.exports;const L=t.createContext(null),A=()=>{const e=t.useContext(L);if(!e)throw new Error("useHoneydrop must be used within a HoneydropProvider");return e};exports.ConnectionMonitor=d,exports.EventManager=s,exports.Honeydrop=x,exports.HoneydropProvider=({client:e,children:t})=>T.jsx(L.Provider,{value:e,children:t}),exports.Logger=i,exports.NamespacedEvents=a,exports.OfflineQueue=h,exports.ReconnectionHandler=c,exports.RoomManager=m,exports.createDebouncedEmit=S,exports.createThrottledEmit=E,exports.default=x,exports.emitMultiple=g,exports.emitMultipleWithAck=p,exports.emitWithAck=v,exports.getConnectionInfo=w,exports.isConnected=b,exports.useHoneydrop=A,exports.useSocketEvent=function(e,n,o){const i=A(),s=t.useRef(n);t.useEffect(()=>{s.current=n},[n]),t.useEffect(()=>{const t=o?i.namespace(o):i,n=(...e)=>{s.current&&s.current(e[0])};return t.on(e,n),()=>{t.off(e,n)}},[e,o,i])},exports.useSocketStatus=function(){const e=A(),[n,o]=t.useState(e.connected?"connected":"disconnected");return t.useEffect(()=>{const t=()=>o("connected"),n=()=>o("disconnected"),i=()=>o("connecting");return e.on("connect",t),e.on("disconnect",n),e.on("reconnect_attempt",i),()=>{e.off("connect",t),e.off("disconnect",n),e.off("reconnect_attempt",i)}},[e]),n},exports.waitForAnyEvent=k,exports.waitForEvent=y;
|
|
2
11
|
//# sourceMappingURL=honeydrop.cjs.js.map
|