pushstream-client 0.1.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.
@@ -0,0 +1,528 @@
1
+ /*!
2
+ * PushStream JavaScript Client v0.1.0
3
+ * A lightweight, zero-dependency SSE client library
4
+ * (c) 2026
5
+ * Released under the MIT License
6
+ */
7
+ /**
8
+ * Connection states for EventClient
9
+ * @readonly
10
+ * @enum {string}
11
+ */
12
+ const ConnectionState = Object.freeze({
13
+ /** Client is not connected */
14
+ DISCONNECTED: 'disconnected',
15
+ /** Client is attempting to connect */
16
+ CONNECTING: 'connecting',
17
+ /** Client is connected and receiving events */
18
+ CONNECTED: 'connected'
19
+ });
20
+
21
+ /**
22
+ * Built-in event names emitted by EventClient
23
+ * @readonly
24
+ * @enum {string}
25
+ */
26
+ const BuiltInEvents = Object.freeze({
27
+ /** Emitted when connection is established */
28
+ OPEN: 'stream.open',
29
+ /** Emitted when connection is closed */
30
+ CLOSE: 'stream.close',
31
+ /** Emitted when an error occurs */
32
+ ERROR: 'stream.error',
33
+ /** Emitted when connection state changes */
34
+ STATE_CHANGE: 'stream.statechange'
35
+ });
36
+
37
+ /**
38
+ * Default options for EventClient
39
+ * @readonly
40
+ */
41
+ const DefaultOptions = Object.freeze({
42
+ /** Whether to automatically reconnect on connection loss */
43
+ reconnect: true,
44
+ /** Base delay in milliseconds between reconnection attempts */
45
+ reconnectInterval: 1000,
46
+ /** Maximum number of reconnection attempts before giving up */
47
+ maxReconnectAttempts: 10,
48
+ /** Maximum delay cap for exponential backoff (30 seconds) */
49
+ maxReconnectDelay: 30000,
50
+ /** Whether to include credentials in cross-origin requests */
51
+ withCredentials: false
52
+ });
53
+
54
+ /**
55
+ * Manages event subscriptions with O(1) add/remove operations.
56
+ * Uses Map for event->callbacks storage and Set for callback deduplication.
57
+ */
58
+ class SubscriptionManager {
59
+ constructor() {
60
+ /** @type {Map<string, Set<Function>>} */
61
+ this._listeners = new Map();
62
+ }
63
+
64
+ /**
65
+ * Register a callback for a specific event.
66
+ * @param {string} event - The event name to subscribe to
67
+ * @param {Function} callback - The callback to invoke when the event occurs
68
+ * @returns {boolean} True if the callback was added, false if already exists
69
+ */
70
+ add(event, callback) {
71
+ if (typeof callback !== 'function') {
72
+ throw new TypeError('Callback must be a function');
73
+ }
74
+
75
+ if (!this._listeners.has(event)) {
76
+ this._listeners.set(event, new Set());
77
+ }
78
+
79
+ const callbacks = this._listeners.get(event);
80
+ const existed = callbacks.has(callback);
81
+ callbacks.add(callback);
82
+
83
+ return !existed;
84
+ }
85
+
86
+ /**
87
+ * Remove a specific callback for an event.
88
+ * @param {string} event - The event name
89
+ * @param {Function} callback - The callback to remove
90
+ * @returns {boolean} True if the callback was removed
91
+ */
92
+ remove(event, callback) {
93
+ const callbacks = this._listeners.get(event);
94
+ if (!callbacks) {
95
+ return false;
96
+ }
97
+
98
+ const removed = callbacks.delete(callback);
99
+
100
+ // Clean up empty sets
101
+ if (callbacks.size === 0) {
102
+ this._listeners.delete(event);
103
+ }
104
+
105
+ return removed;
106
+ }
107
+
108
+ /**
109
+ * Remove all callbacks for a specific event.
110
+ * @param {string} event - The event name
111
+ * @returns {boolean} True if any callbacks were removed
112
+ */
113
+ removeAll(event) {
114
+ return this._listeners.delete(event);
115
+ }
116
+
117
+ /**
118
+ * Emit an event to all registered callbacks.
119
+ * Creates a snapshot of callbacks to allow safe modification during iteration.
120
+ * @param {string} event - The event name
121
+ * @param {*} data - The data to pass to callbacks
122
+ */
123
+ emit(event, data) {
124
+ const callbacks = this._listeners.get(event);
125
+ if (!callbacks || callbacks.size === 0) {
126
+ return;
127
+ }
128
+
129
+ // Create snapshot to allow modifications during iteration
130
+ const snapshot = Array.from(callbacks);
131
+
132
+ for (const callback of snapshot) {
133
+ try {
134
+ callback(data);
135
+ } catch (error) {
136
+ // Log but don't throw to prevent one bad callback from breaking others
137
+ console.error(`Error in event callback for "${event}":`, error);
138
+ }
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Check if an event has any subscribers.
144
+ * @param {string} event - The event name
145
+ * @returns {boolean} True if the event has subscribers
146
+ */
147
+ has(event) {
148
+ const callbacks = this._listeners.get(event);
149
+ return callbacks !== undefined && callbacks.size > 0;
150
+ }
151
+
152
+ /**
153
+ * Get all registered event names.
154
+ * @returns {string[]} Array of event names
155
+ */
156
+ getEvents() {
157
+ return Array.from(this._listeners.keys());
158
+ }
159
+
160
+ /**
161
+ * Get the number of callbacks for a specific event.
162
+ * @param {string} event - The event name
163
+ * @returns {number} Number of callbacks
164
+ */
165
+ getCount(event) {
166
+ const callbacks = this._listeners.get(event);
167
+ return callbacks ? callbacks.size : 0;
168
+ }
169
+
170
+ /**
171
+ * Clear all subscriptions.
172
+ */
173
+ clear() {
174
+ this._listeners.clear();
175
+ }
176
+ }
177
+
178
+ /**
179
+ * EventClient provides a clean abstraction over EventSource for consuming SSE events.
180
+ *
181
+ * Features:
182
+ * - Automatic reconnection with exponential backoff and jitter
183
+ * - Event subscription management
184
+ * - Automatic JSON payload parsing
185
+ * - Connection state tracking
186
+ * - Built-in lifecycle events
187
+ *
188
+ * @example
189
+ * const client = new EventClient('/events');
190
+ * client.on('task.progress', (data) => console.log(data.percentage));
191
+ * client.connect();
192
+ */
193
+ class EventClient {
194
+ /**
195
+ * Create a new EventClient instance.
196
+ * @param {string} url - The SSE endpoint URL (relative or absolute)
197
+ * @param {Object} [options] - Configuration options
198
+ * @param {boolean} [options.reconnect=true] - Enable automatic reconnection
199
+ * @param {number} [options.reconnectInterval=1000] - Base reconnection delay in ms
200
+ * @param {number} [options.maxReconnectAttempts=10] - Maximum reconnection attempts
201
+ * @param {number} [options.maxReconnectDelay=30000] - Maximum delay cap in ms
202
+ * @param {boolean} [options.withCredentials=false] - Include credentials in CORS requests
203
+ */
204
+ constructor(url, options = {}) {
205
+ if (!url || typeof url !== 'string') {
206
+ throw new TypeError('URL must be a non-empty string');
207
+ }
208
+
209
+ this._url = url;
210
+ this._options = { ...DefaultOptions, ...options };
211
+ this._eventSource = null;
212
+ this._subscriptions = new SubscriptionManager();
213
+ this._state = ConnectionState.DISCONNECTED;
214
+ this._reconnectAttempts = 0;
215
+ this._reconnectTimer = null;
216
+ this._manualDisconnect = false;
217
+ this._registeredEventTypes = new Set();
218
+ }
219
+
220
+ /**
221
+ * Get the current connection state.
222
+ * @returns {string} One of: 'disconnected', 'connecting', 'connected'
223
+ */
224
+ get state() {
225
+ return this._state;
226
+ }
227
+
228
+ /**
229
+ * Get the endpoint URL.
230
+ * @returns {string} The SSE endpoint URL
231
+ */
232
+ get url() {
233
+ return this._url;
234
+ }
235
+
236
+ /**
237
+ * Establish an SSE connection to the server.
238
+ * This method is idempotent - calling it while already connected has no effect.
239
+ */
240
+ connect() {
241
+ // Idempotent: don't reconnect if already connecting or connected
242
+ if (this._state !== ConnectionState.DISCONNECTED) {
243
+ return;
244
+ }
245
+
246
+ this._manualDisconnect = false;
247
+ this._setState(ConnectionState.CONNECTING);
248
+
249
+ try {
250
+ this._eventSource = new EventSource(this._url, {
251
+ withCredentials: this._options.withCredentials
252
+ });
253
+
254
+ this._eventSource.onopen = () => this._handleOpen();
255
+ this._eventSource.onerror = (event) => this._handleError(event);
256
+
257
+ // Register event listeners for all currently subscribed event types
258
+ this._registerEventListeners();
259
+ } catch (error) {
260
+ this._handleConnectionError(error);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Close the SSE connection.
266
+ * This method is idempotent - calling it while already disconnected has no effect.
267
+ * After calling disconnect(), no automatic reconnection will be attempted.
268
+ */
269
+ disconnect() {
270
+ this._manualDisconnect = true;
271
+ this._cleanup();
272
+
273
+ if (this._state !== ConnectionState.DISCONNECTED) {
274
+ this._setState(ConnectionState.DISCONNECTED);
275
+ this._emit(BuiltInEvents.CLOSE, { manual: true });
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Subscribe to an event.
281
+ * Subscriptions can be registered before or after connecting.
282
+ * @param {string} event - The event name to subscribe to
283
+ * @param {Function} callback - The callback to invoke when the event occurs
284
+ * @returns {EventClient} This instance for chaining
285
+ */
286
+ on(event, callback) {
287
+ if (typeof event !== 'string' || !event) {
288
+ throw new TypeError('Event name must be a non-empty string');
289
+ }
290
+ if (typeof callback !== 'function') {
291
+ throw new TypeError('Callback must be a function');
292
+ }
293
+
294
+ this._subscriptions.add(event, callback);
295
+
296
+ // If already connected and this is a new event type, register it
297
+ if (this._eventSource && !this._isBuiltInEvent(event) && !this._registeredEventTypes.has(event)) {
298
+ this._registerSingleEventListener(event);
299
+ }
300
+
301
+ return this;
302
+ }
303
+
304
+ /**
305
+ * Unsubscribe from an event.
306
+ * @param {string} event - The event name
307
+ * @param {Function} [callback] - Specific callback to remove. If omitted, removes all callbacks for the event.
308
+ * @returns {EventClient} This instance for chaining
309
+ */
310
+ off(event, callback) {
311
+ if (typeof event !== 'string' || !event) {
312
+ throw new TypeError('Event name must be a non-empty string');
313
+ }
314
+
315
+ if (callback !== undefined) {
316
+ this._subscriptions.remove(event, callback);
317
+ } else {
318
+ this._subscriptions.removeAll(event);
319
+ }
320
+
321
+ return this;
322
+ }
323
+
324
+ // =====================
325
+ // Private Methods
326
+ // =====================
327
+
328
+ /**
329
+ * Update connection state and emit state change event.
330
+ * @private
331
+ */
332
+ _setState(newState) {
333
+ const oldState = this._state;
334
+ if (oldState === newState) {
335
+ return;
336
+ }
337
+
338
+ this._state = newState;
339
+ this._emit(BuiltInEvents.STATE_CHANGE, {
340
+ previousState: oldState,
341
+ currentState: newState
342
+ });
343
+ }
344
+
345
+ /**
346
+ * Emit an event to all subscribers.
347
+ * @private
348
+ */
349
+ _emit(event, data) {
350
+ this._subscriptions.emit(event, data);
351
+ }
352
+
353
+ /**
354
+ * Handle successful connection.
355
+ * @private
356
+ */
357
+ _handleOpen() {
358
+ this._reconnectAttempts = 0; // Reset on successful connection
359
+ this._setState(ConnectionState.CONNECTED);
360
+ this._emit(BuiltInEvents.OPEN, { url: this._url });
361
+ }
362
+
363
+ /**
364
+ * Handle connection error.
365
+ * @private
366
+ */
367
+ _handleError(event) {
368
+ // EventSource error event doesn't provide much detail
369
+ const errorInfo = {
370
+ message: 'Connection error',
371
+ readyState: this._eventSource?.readyState
372
+ };
373
+
374
+ this._emit(BuiltInEvents.ERROR, errorInfo);
375
+
376
+ // Check if connection was lost
377
+ if (this._eventSource?.readyState === EventSource.CLOSED) {
378
+ this._handleConnectionLoss();
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Handle initial connection failure.
384
+ * @private
385
+ */
386
+ _handleConnectionError(error) {
387
+ this._setState(ConnectionState.DISCONNECTED);
388
+ this._emit(BuiltInEvents.ERROR, {
389
+ message: error.message || 'Failed to create connection',
390
+ error
391
+ });
392
+ }
393
+
394
+ /**
395
+ * Handle connection loss and schedule reconnection.
396
+ * @private
397
+ */
398
+ _handleConnectionLoss() {
399
+ this._cleanup();
400
+ this._setState(ConnectionState.DISCONNECTED);
401
+ this._emit(BuiltInEvents.CLOSE, { manual: false });
402
+
403
+ // Schedule reconnection if enabled and not manually disconnected
404
+ if (this._options.reconnect && !this._manualDisconnect) {
405
+ this._scheduleReconnect();
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Schedule a reconnection attempt with exponential backoff and jitter.
411
+ * @private
412
+ */
413
+ _scheduleReconnect() {
414
+ if (this._manualDisconnect) {
415
+ return;
416
+ }
417
+
418
+ if (this._reconnectAttempts >= this._options.maxReconnectAttempts) {
419
+ this._emit(BuiltInEvents.ERROR, {
420
+ message: 'Max reconnection attempts reached',
421
+ attempts: this._reconnectAttempts
422
+ });
423
+ return;
424
+ }
425
+
426
+ // Exponential backoff: interval * 2^attempts
427
+ const exponentialDelay = this._options.reconnectInterval * Math.pow(2, this._reconnectAttempts);
428
+
429
+ // Cap at max delay
430
+ const cappedDelay = Math.min(exponentialDelay, this._options.maxReconnectDelay);
431
+
432
+ // Add jitter (0-1000ms random) to prevent thundering herd
433
+ const jitter = Math.random() * 1000;
434
+ const finalDelay = cappedDelay + jitter;
435
+
436
+ this._reconnectTimer = setTimeout(() => {
437
+ this._reconnectAttempts++;
438
+ this.connect();
439
+ }, finalDelay);
440
+ }
441
+
442
+ /**
443
+ * Register EventSource listeners for all subscribed event types.
444
+ * @private
445
+ */
446
+ _registerEventListeners() {
447
+ const events = this._subscriptions.getEvents();
448
+
449
+ for (const event of events) {
450
+ if (!this._isBuiltInEvent(event)) {
451
+ this._registerSingleEventListener(event);
452
+ }
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Register a single event listener on the EventSource.
458
+ * @private
459
+ */
460
+ _registerSingleEventListener(event) {
461
+ if (!this._eventSource || this._registeredEventTypes.has(event)) {
462
+ return;
463
+ }
464
+
465
+ this._registeredEventTypes.add(event);
466
+
467
+ this._eventSource.addEventListener(event, (sseEvent) => {
468
+ this._handleMessage(event, sseEvent);
469
+ });
470
+ }
471
+
472
+ /**
473
+ * Handle incoming SSE message.
474
+ * @private
475
+ */
476
+ _handleMessage(eventType, sseEvent) {
477
+ let data;
478
+
479
+ try {
480
+ // Attempt JSON parsing
481
+ data = JSON.parse(sseEvent.data);
482
+ } catch (parseError) {
483
+ // JSON parsing failed - emit error but don't disconnect
484
+ this._emit(BuiltInEvents.ERROR, {
485
+ message: 'Failed to parse JSON payload',
486
+ eventType,
487
+ originalData: sseEvent.data,
488
+ error: parseError.message
489
+ });
490
+ return;
491
+ }
492
+
493
+ // Emit the parsed event to subscribers
494
+ this._emit(eventType, data);
495
+ }
496
+
497
+ /**
498
+ * Check if an event name is a built-in event.
499
+ * @private
500
+ */
501
+ _isBuiltInEvent(event) {
502
+ return event.startsWith('stream.');
503
+ }
504
+
505
+ /**
506
+ * Clean up resources.
507
+ * @private
508
+ */
509
+ _cleanup() {
510
+ // Clear reconnection timer
511
+ if (this._reconnectTimer) {
512
+ clearTimeout(this._reconnectTimer);
513
+ this._reconnectTimer = null;
514
+ }
515
+
516
+ // Close EventSource
517
+ if (this._eventSource) {
518
+ this._eventSource.close();
519
+ this._eventSource = null;
520
+ }
521
+
522
+ // Clear registered event types (will be re-registered on reconnect)
523
+ this._registeredEventTypes.clear();
524
+ }
525
+ }
526
+
527
+ export { BuiltInEvents, ConnectionState, DefaultOptions, EventClient, SubscriptionManager };
528
+ //# sourceMappingURL=pushstream.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pushstream.esm.js","sources":["../src/constants.js","../src/SubscriptionManager.js","../src/EventClient.js"],"sourcesContent":["/**\r\n * Connection states for EventClient\r\n * @readonly\r\n * @enum {string}\r\n */\r\nexport const ConnectionState = Object.freeze({\r\n /** Client is not connected */\r\n DISCONNECTED: 'disconnected',\r\n /** Client is attempting to connect */\r\n CONNECTING: 'connecting',\r\n /** Client is connected and receiving events */\r\n CONNECTED: 'connected'\r\n});\r\n\r\n/**\r\n * Built-in event names emitted by EventClient\r\n * @readonly\r\n * @enum {string}\r\n */\r\nexport const BuiltInEvents = Object.freeze({\r\n /** Emitted when connection is established */\r\n OPEN: 'stream.open',\r\n /** Emitted when connection is closed */\r\n CLOSE: 'stream.close',\r\n /** Emitted when an error occurs */\r\n ERROR: 'stream.error',\r\n /** Emitted when connection state changes */\r\n STATE_CHANGE: 'stream.statechange'\r\n});\r\n\r\n/**\r\n * Default options for EventClient\r\n * @readonly\r\n */\r\nexport const DefaultOptions = Object.freeze({\r\n /** Whether to automatically reconnect on connection loss */\r\n reconnect: true,\r\n /** Base delay in milliseconds between reconnection attempts */\r\n reconnectInterval: 1000,\r\n /** Maximum number of reconnection attempts before giving up */\r\n maxReconnectAttempts: 10,\r\n /** Maximum delay cap for exponential backoff (30 seconds) */\r\n maxReconnectDelay: 30000,\r\n /** Whether to include credentials in cross-origin requests */\r\n withCredentials: false\r\n});\r\n\r\n","/**\r\n * Manages event subscriptions with O(1) add/remove operations.\r\n * Uses Map for event->callbacks storage and Set for callback deduplication.\r\n */\r\nexport class SubscriptionManager {\r\n constructor() {\r\n /** @type {Map<string, Set<Function>>} */\r\n this._listeners = new Map();\r\n }\r\n\r\n /**\r\n * Register a callback for a specific event.\r\n * @param {string} event - The event name to subscribe to\r\n * @param {Function} callback - The callback to invoke when the event occurs\r\n * @returns {boolean} True if the callback was added, false if already exists\r\n */\r\n add(event, callback) {\r\n if (typeof callback !== 'function') {\r\n throw new TypeError('Callback must be a function');\r\n }\r\n\r\n if (!this._listeners.has(event)) {\r\n this._listeners.set(event, new Set());\r\n }\r\n\r\n const callbacks = this._listeners.get(event);\r\n const existed = callbacks.has(callback);\r\n callbacks.add(callback);\r\n \r\n return !existed;\r\n }\r\n\r\n /**\r\n * Remove a specific callback for an event.\r\n * @param {string} event - The event name\r\n * @param {Function} callback - The callback to remove\r\n * @returns {boolean} True if the callback was removed\r\n */\r\n remove(event, callback) {\r\n const callbacks = this._listeners.get(event);\r\n if (!callbacks) {\r\n return false;\r\n }\r\n\r\n const removed = callbacks.delete(callback);\r\n \r\n // Clean up empty sets\r\n if (callbacks.size === 0) {\r\n this._listeners.delete(event);\r\n }\r\n\r\n return removed;\r\n }\r\n\r\n /**\r\n * Remove all callbacks for a specific event.\r\n * @param {string} event - The event name\r\n * @returns {boolean} True if any callbacks were removed\r\n */\r\n removeAll(event) {\r\n return this._listeners.delete(event);\r\n }\r\n\r\n /**\r\n * Emit an event to all registered callbacks.\r\n * Creates a snapshot of callbacks to allow safe modification during iteration.\r\n * @param {string} event - The event name\r\n * @param {*} data - The data to pass to callbacks\r\n */\r\n emit(event, data) {\r\n const callbacks = this._listeners.get(event);\r\n if (!callbacks || callbacks.size === 0) {\r\n return;\r\n }\r\n\r\n // Create snapshot to allow modifications during iteration\r\n const snapshot = Array.from(callbacks);\r\n \r\n for (const callback of snapshot) {\r\n try {\r\n callback(data);\r\n } catch (error) {\r\n // Log but don't throw to prevent one bad callback from breaking others\r\n console.error(`Error in event callback for \"${event}\":`, error);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Check if an event has any subscribers.\r\n * @param {string} event - The event name\r\n * @returns {boolean} True if the event has subscribers\r\n */\r\n has(event) {\r\n const callbacks = this._listeners.get(event);\r\n return callbacks !== undefined && callbacks.size > 0;\r\n }\r\n\r\n /**\r\n * Get all registered event names.\r\n * @returns {string[]} Array of event names\r\n */\r\n getEvents() {\r\n return Array.from(this._listeners.keys());\r\n }\r\n\r\n /**\r\n * Get the number of callbacks for a specific event.\r\n * @param {string} event - The event name\r\n * @returns {number} Number of callbacks\r\n */\r\n getCount(event) {\r\n const callbacks = this._listeners.get(event);\r\n return callbacks ? callbacks.size : 0;\r\n }\r\n\r\n /**\r\n * Clear all subscriptions.\r\n */\r\n clear() {\r\n this._listeners.clear();\r\n }\r\n}\r\n\r\n","import { ConnectionState, BuiltInEvents, DefaultOptions } from './constants.js';\r\nimport { SubscriptionManager } from './SubscriptionManager.js';\r\n\r\n/**\r\n * EventClient provides a clean abstraction over EventSource for consuming SSE events.\r\n * \r\n * Features:\r\n * - Automatic reconnection with exponential backoff and jitter\r\n * - Event subscription management\r\n * - Automatic JSON payload parsing\r\n * - Connection state tracking\r\n * - Built-in lifecycle events\r\n * \r\n * @example\r\n * const client = new EventClient('/events');\r\n * client.on('task.progress', (data) => console.log(data.percentage));\r\n * client.connect();\r\n */\r\nexport class EventClient {\r\n /**\r\n * Create a new EventClient instance.\r\n * @param {string} url - The SSE endpoint URL (relative or absolute)\r\n * @param {Object} [options] - Configuration options\r\n * @param {boolean} [options.reconnect=true] - Enable automatic reconnection\r\n * @param {number} [options.reconnectInterval=1000] - Base reconnection delay in ms\r\n * @param {number} [options.maxReconnectAttempts=10] - Maximum reconnection attempts\r\n * @param {number} [options.maxReconnectDelay=30000] - Maximum delay cap in ms\r\n * @param {boolean} [options.withCredentials=false] - Include credentials in CORS requests\r\n */\r\n constructor(url, options = {}) {\r\n if (!url || typeof url !== 'string') {\r\n throw new TypeError('URL must be a non-empty string');\r\n }\r\n\r\n this._url = url;\r\n this._options = { ...DefaultOptions, ...options };\r\n this._eventSource = null;\r\n this._subscriptions = new SubscriptionManager();\r\n this._state = ConnectionState.DISCONNECTED;\r\n this._reconnectAttempts = 0;\r\n this._reconnectTimer = null;\r\n this._manualDisconnect = false;\r\n this._registeredEventTypes = new Set();\r\n }\r\n\r\n /**\r\n * Get the current connection state.\r\n * @returns {string} One of: 'disconnected', 'connecting', 'connected'\r\n */\r\n get state() {\r\n return this._state;\r\n }\r\n\r\n /**\r\n * Get the endpoint URL.\r\n * @returns {string} The SSE endpoint URL\r\n */\r\n get url() {\r\n return this._url;\r\n }\r\n\r\n /**\r\n * Establish an SSE connection to the server.\r\n * This method is idempotent - calling it while already connected has no effect.\r\n */\r\n connect() {\r\n // Idempotent: don't reconnect if already connecting or connected\r\n if (this._state !== ConnectionState.DISCONNECTED) {\r\n return;\r\n }\r\n\r\n this._manualDisconnect = false;\r\n this._setState(ConnectionState.CONNECTING);\r\n\r\n try {\r\n this._eventSource = new EventSource(this._url, {\r\n withCredentials: this._options.withCredentials\r\n });\r\n\r\n this._eventSource.onopen = () => this._handleOpen();\r\n this._eventSource.onerror = (event) => this._handleError(event);\r\n\r\n // Register event listeners for all currently subscribed event types\r\n this._registerEventListeners();\r\n } catch (error) {\r\n this._handleConnectionError(error);\r\n }\r\n }\r\n\r\n /**\r\n * Close the SSE connection.\r\n * This method is idempotent - calling it while already disconnected has no effect.\r\n * After calling disconnect(), no automatic reconnection will be attempted.\r\n */\r\n disconnect() {\r\n this._manualDisconnect = true;\r\n this._cleanup();\r\n \r\n if (this._state !== ConnectionState.DISCONNECTED) {\r\n this._setState(ConnectionState.DISCONNECTED);\r\n this._emit(BuiltInEvents.CLOSE, { manual: true });\r\n }\r\n }\r\n\r\n /**\r\n * Subscribe to an event.\r\n * Subscriptions can be registered before or after connecting.\r\n * @param {string} event - The event name to subscribe to\r\n * @param {Function} callback - The callback to invoke when the event occurs\r\n * @returns {EventClient} This instance for chaining\r\n */\r\n on(event, callback) {\r\n if (typeof event !== 'string' || !event) {\r\n throw new TypeError('Event name must be a non-empty string');\r\n }\r\n if (typeof callback !== 'function') {\r\n throw new TypeError('Callback must be a function');\r\n }\r\n\r\n this._subscriptions.add(event, callback);\r\n\r\n // If already connected and this is a new event type, register it\r\n if (this._eventSource && !this._isBuiltInEvent(event) && !this._registeredEventTypes.has(event)) {\r\n this._registerSingleEventListener(event);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Unsubscribe from an event.\r\n * @param {string} event - The event name\r\n * @param {Function} [callback] - Specific callback to remove. If omitted, removes all callbacks for the event.\r\n * @returns {EventClient} This instance for chaining\r\n */\r\n off(event, callback) {\r\n if (typeof event !== 'string' || !event) {\r\n throw new TypeError('Event name must be a non-empty string');\r\n }\r\n\r\n if (callback !== undefined) {\r\n this._subscriptions.remove(event, callback);\r\n } else {\r\n this._subscriptions.removeAll(event);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n // =====================\r\n // Private Methods\r\n // =====================\r\n\r\n /**\r\n * Update connection state and emit state change event.\r\n * @private\r\n */\r\n _setState(newState) {\r\n const oldState = this._state;\r\n if (oldState === newState) {\r\n return;\r\n }\r\n\r\n this._state = newState;\r\n this._emit(BuiltInEvents.STATE_CHANGE, {\r\n previousState: oldState,\r\n currentState: newState\r\n });\r\n }\r\n\r\n /**\r\n * Emit an event to all subscribers.\r\n * @private\r\n */\r\n _emit(event, data) {\r\n this._subscriptions.emit(event, data);\r\n }\r\n\r\n /**\r\n * Handle successful connection.\r\n * @private\r\n */\r\n _handleOpen() {\r\n this._reconnectAttempts = 0; // Reset on successful connection\r\n this._setState(ConnectionState.CONNECTED);\r\n this._emit(BuiltInEvents.OPEN, { url: this._url });\r\n }\r\n\r\n /**\r\n * Handle connection error.\r\n * @private\r\n */\r\n _handleError(event) {\r\n // EventSource error event doesn't provide much detail\r\n const errorInfo = {\r\n message: 'Connection error',\r\n readyState: this._eventSource?.readyState\r\n };\r\n\r\n this._emit(BuiltInEvents.ERROR, errorInfo);\r\n\r\n // Check if connection was lost\r\n if (this._eventSource?.readyState === EventSource.CLOSED) {\r\n this._handleConnectionLoss();\r\n }\r\n }\r\n\r\n /**\r\n * Handle initial connection failure.\r\n * @private\r\n */\r\n _handleConnectionError(error) {\r\n this._setState(ConnectionState.DISCONNECTED);\r\n this._emit(BuiltInEvents.ERROR, {\r\n message: error.message || 'Failed to create connection',\r\n error\r\n });\r\n }\r\n\r\n /**\r\n * Handle connection loss and schedule reconnection.\r\n * @private\r\n */\r\n _handleConnectionLoss() {\r\n this._cleanup();\r\n this._setState(ConnectionState.DISCONNECTED);\r\n this._emit(BuiltInEvents.CLOSE, { manual: false });\r\n\r\n // Schedule reconnection if enabled and not manually disconnected\r\n if (this._options.reconnect && !this._manualDisconnect) {\r\n this._scheduleReconnect();\r\n }\r\n }\r\n\r\n /**\r\n * Schedule a reconnection attempt with exponential backoff and jitter.\r\n * @private\r\n */\r\n _scheduleReconnect() {\r\n if (this._manualDisconnect) {\r\n return;\r\n }\r\n\r\n if (this._reconnectAttempts >= this._options.maxReconnectAttempts) {\r\n this._emit(BuiltInEvents.ERROR, {\r\n message: 'Max reconnection attempts reached',\r\n attempts: this._reconnectAttempts\r\n });\r\n return;\r\n }\r\n\r\n // Exponential backoff: interval * 2^attempts\r\n const exponentialDelay = this._options.reconnectInterval * Math.pow(2, this._reconnectAttempts);\r\n \r\n // Cap at max delay\r\n const cappedDelay = Math.min(exponentialDelay, this._options.maxReconnectDelay);\r\n \r\n // Add jitter (0-1000ms random) to prevent thundering herd\r\n const jitter = Math.random() * 1000;\r\n const finalDelay = cappedDelay + jitter;\r\n\r\n this._reconnectTimer = setTimeout(() => {\r\n this._reconnectAttempts++;\r\n this.connect();\r\n }, finalDelay);\r\n }\r\n\r\n /**\r\n * Register EventSource listeners for all subscribed event types.\r\n * @private\r\n */\r\n _registerEventListeners() {\r\n const events = this._subscriptions.getEvents();\r\n \r\n for (const event of events) {\r\n if (!this._isBuiltInEvent(event)) {\r\n this._registerSingleEventListener(event);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Register a single event listener on the EventSource.\r\n * @private\r\n */\r\n _registerSingleEventListener(event) {\r\n if (!this._eventSource || this._registeredEventTypes.has(event)) {\r\n return;\r\n }\r\n\r\n this._registeredEventTypes.add(event);\r\n \r\n this._eventSource.addEventListener(event, (sseEvent) => {\r\n this._handleMessage(event, sseEvent);\r\n });\r\n }\r\n\r\n /**\r\n * Handle incoming SSE message.\r\n * @private\r\n */\r\n _handleMessage(eventType, sseEvent) {\r\n let data;\r\n \r\n try {\r\n // Attempt JSON parsing\r\n data = JSON.parse(sseEvent.data);\r\n } catch (parseError) {\r\n // JSON parsing failed - emit error but don't disconnect\r\n this._emit(BuiltInEvents.ERROR, {\r\n message: 'Failed to parse JSON payload',\r\n eventType,\r\n originalData: sseEvent.data,\r\n error: parseError.message\r\n });\r\n return;\r\n }\r\n\r\n // Emit the parsed event to subscribers\r\n this._emit(eventType, data);\r\n }\r\n\r\n /**\r\n * Check if an event name is a built-in event.\r\n * @private\r\n */\r\n _isBuiltInEvent(event) {\r\n return event.startsWith('stream.');\r\n }\r\n\r\n /**\r\n * Clean up resources.\r\n * @private\r\n */\r\n _cleanup() {\r\n // Clear reconnection timer\r\n if (this._reconnectTimer) {\r\n clearTimeout(this._reconnectTimer);\r\n this._reconnectTimer = null;\r\n }\r\n\r\n // Close EventSource\r\n if (this._eventSource) {\r\n this._eventSource.close();\r\n this._eventSource = null;\r\n }\r\n\r\n // Clear registered event types (will be re-registered on reconnect)\r\n this._registeredEventTypes.clear();\r\n }\r\n}\r\n\r\n"],"names":[],"mappings":"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"}