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