@stackline/sse 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1539 @@
1
+ /*! @stackline/sse v1.0.0 | MIT */
2
+
3
+ // src/errors.js
4
+ var SSEError = class extends Error {
5
+ constructor(message, code, details) {
6
+ super(message);
7
+ this.name = new.target.name;
8
+ this.code = code;
9
+ if (details && details.cause !== void 0) this.cause = details.cause;
10
+ }
11
+ };
12
+ var SSEParseError = class extends SSEError {
13
+ constructor(message, details = {}) {
14
+ super(message, details.code || "ERR_SSE_PARSE", details);
15
+ this.fatal = details.fatal === true;
16
+ if (details.field !== void 0) this.field = details.field;
17
+ if (details.line !== void 0) this.line = details.line;
18
+ if (details.limit !== void 0) this.limit = details.limit;
19
+ }
20
+ };
21
+ var SSEEncodeError = class extends SSEError {
22
+ constructor(message, field, cause) {
23
+ super(message, "ERR_SSE_ENCODE", { cause });
24
+ this.field = field;
25
+ }
26
+ };
27
+ var SSEHTTPError = class extends SSEError {
28
+ constructor(message, response, code = "ERR_SSE_HTTP") {
29
+ super(message, code);
30
+ this.response = response;
31
+ this.status = response && typeof response.status === "number" ? response.status : 0;
32
+ }
33
+ };
34
+ var SSETimeoutError = class extends SSEError {
35
+ constructor(phase, timeout, cause) {
36
+ super(`SSE ${phase} timeout after ${timeout}ms`, "ERR_SSE_TIMEOUT", { cause });
37
+ this.phase = phase;
38
+ this.timeout = timeout;
39
+ }
40
+ };
41
+ var SSERetryError = class extends SSEError {
42
+ constructor(attempts, cause) {
43
+ super(`SSE retry budget exhausted after ${attempts} attempt${attempts === 1 ? "" : "s"}`, "ERR_SSE_RETRY", { cause });
44
+ this.attempts = attempts;
45
+ }
46
+ };
47
+ var SSEReplayError = class extends SSEError {
48
+ constructor() {
49
+ super(
50
+ "The request body cannot be replayed. Provide bodyFactory or disable reconnection.",
51
+ "ERR_SSE_BODY_REPLAY"
52
+ );
53
+ }
54
+ };
55
+
56
+ // src/parser.js
57
+ var DEFAULT_MAX_EVENT_SIZE = 1024 * 1024;
58
+ var DEFAULT_MAX_LINE_LENGTH = 1024 * 1024;
59
+ var LF = 10;
60
+ var CR = 13;
61
+ var SPACE = 32;
62
+ var BOM = 65279;
63
+ var NUL = "\0";
64
+ var noop = () => {
65
+ };
66
+ function createParser(config = {}) {
67
+ if (typeof config === "function") config = { onEvent: config };
68
+ if (!config || typeof config !== "object") {
69
+ throw new TypeError("Parser configuration must be an object or event callback");
70
+ }
71
+ const onEvent = typeof config.onEvent === "function" ? config.onEvent : noop;
72
+ const onComment = typeof config.onComment === "function" ? config.onComment : noop;
73
+ const onRetry = typeof config.onRetry === "function" ? config.onRetry : noop;
74
+ const onError = typeof config.onError === "function" ? config.onError : noop;
75
+ const onId = typeof config.onId === "function" ? config.onId : noop;
76
+ const strict = config.strict === true;
77
+ const fatalUTF8 = config.fatalUTF8 === true;
78
+ const maxEventSize = readLimit(
79
+ config.maxEventSize === void 0 ? config.maxBufferSize : config.maxEventSize,
80
+ DEFAULT_MAX_EVENT_SIZE,
81
+ "maxEventSize"
82
+ );
83
+ const maxLineLength = readLimit(
84
+ config.maxLineLength,
85
+ DEFAULT_MAX_LINE_LENGTH,
86
+ "maxLineLength"
87
+ );
88
+ let decoder;
89
+ let inputMode;
90
+ let lineFragments = [];
91
+ let lineLength = 0;
92
+ let skipLeadingLF = false;
93
+ let atStart = true;
94
+ let dataLines = [];
95
+ let eventSize = 0;
96
+ let eventType;
97
+ let idBuffer = normalizeInitialId(config.lastEventId);
98
+ let committedId = idBuffer;
99
+ let hasPendingId = false;
100
+ let terminated = false;
101
+ let bytes = 0;
102
+ let characters = 0;
103
+ let events = 0;
104
+ let comments = 0;
105
+ let retries = 0;
106
+ function feed(chunk) {
107
+ if (terminated) {
108
+ throw new SSEParseError("Parser is terminated; call reset() before feeding more data", {
109
+ code: "ERR_SSE_TERMINATED",
110
+ fatal: true
111
+ });
112
+ }
113
+ if (typeof chunk === "string") {
114
+ if (inputMode === "bytes") {
115
+ throw fatal("Cannot mix string and byte chunks in one parser stream", "ERR_SSE_CHUNK_TYPE");
116
+ }
117
+ inputMode = "string";
118
+ characters += chunk.length;
119
+ processText(chunk);
120
+ return api2;
121
+ }
122
+ if (!(chunk instanceof Uint8Array)) {
123
+ throw new TypeError("SSE parser chunks must be strings or Uint8Array values");
124
+ }
125
+ if (inputMode === "string") {
126
+ throw fatal("Cannot mix string and byte chunks in one parser stream", "ERR_SSE_CHUNK_TYPE");
127
+ }
128
+ inputMode = "bytes";
129
+ bytes += chunk.byteLength;
130
+ let text;
131
+ try {
132
+ text = getDecoder().decode(chunk, { stream: true });
133
+ } catch (error) {
134
+ if (error instanceof SSEParseError) throw error;
135
+ throw fatal("Invalid UTF-8 in the SSE stream", "ERR_SSE_UTF8", { cause: error });
136
+ }
137
+ characters += text.length;
138
+ processText(text);
139
+ return api2;
140
+ }
141
+ function end() {
142
+ if (terminated) return api2;
143
+ if (decoder) {
144
+ try {
145
+ const tail = decoder.decode();
146
+ characters += tail.length;
147
+ processText(tail);
148
+ } catch (error) {
149
+ throw fatal("Invalid UTF-8 at the end of the SSE stream", "ERR_SSE_UTF8", { cause: error });
150
+ }
151
+ }
152
+ discardPendingEvent();
153
+ clearLine();
154
+ return api2;
155
+ }
156
+ function reset(options = {}) {
157
+ if (options && options.consume && lineLength > 0) processLine(joinLine());
158
+ const preserve = Boolean(options && options.preserveLastEventId);
159
+ const nextId = preserve ? committedId : normalizeInitialId(config.lastEventId);
160
+ decoder = void 0;
161
+ inputMode = void 0;
162
+ lineFragments = [];
163
+ lineLength = 0;
164
+ skipLeadingLF = false;
165
+ atStart = true;
166
+ dataLines = [];
167
+ eventSize = 0;
168
+ eventType = void 0;
169
+ idBuffer = nextId;
170
+ committedId = nextId;
171
+ hasPendingId = false;
172
+ terminated = false;
173
+ bytes = 0;
174
+ characters = 0;
175
+ events = 0;
176
+ comments = 0;
177
+ retries = 0;
178
+ return api2;
179
+ }
180
+ function processText(input) {
181
+ let text = input;
182
+ if (atStart && text.length > 0) {
183
+ atStart = false;
184
+ if (text.charCodeAt(0) === BOM) text = text.slice(1);
185
+ }
186
+ if (text.length === 0) return;
187
+ if (!skipLeadingLF && text.indexOf("\r") === -1) {
188
+ processLFText(text);
189
+ return;
190
+ }
191
+ let index = 0;
192
+ let segmentStart = 0;
193
+ if (skipLeadingLF) {
194
+ skipLeadingLF = false;
195
+ if (text.charCodeAt(0) === LF) {
196
+ index = 1;
197
+ segmentStart = 1;
198
+ }
199
+ }
200
+ for (; index < text.length; index++) {
201
+ const code = text.charCodeAt(index);
202
+ if (code !== LF && code !== CR) continue;
203
+ appendLine(text.slice(segmentStart, index));
204
+ processLine(joinLine());
205
+ clearLine();
206
+ if (code === CR) {
207
+ if (text.charCodeAt(index + 1) === LF) index++;
208
+ else if (index + 1 === text.length) skipLeadingLF = true;
209
+ }
210
+ segmentStart = index + 1;
211
+ }
212
+ appendLine(text.slice(segmentStart));
213
+ }
214
+ function processLFText(text) {
215
+ let start = 0;
216
+ let end2 = text.indexOf("\n");
217
+ while (end2 !== -1) {
218
+ if (lineLength === 0 && dataLines.length === 0 && text.charCodeAt(end2 + 1) === LF && isDataPrefix(text, start)) {
219
+ const valueStart = text.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5;
220
+ const value = text.slice(valueStart, end2);
221
+ if (end2 - start > maxLineLength) {
222
+ throw fatal(`SSE line exceeded ${maxLineLength} characters`, "ERR_SSE_LINE_LIMIT", {
223
+ limit: maxLineLength
224
+ });
225
+ }
226
+ addEventSize(value.length + 1);
227
+ dispatchSingleData(value);
228
+ start = end2 + 2;
229
+ end2 = text.indexOf("\n", start);
230
+ continue;
231
+ }
232
+ if (lineLength > 0) {
233
+ appendLine(text.slice(start, end2));
234
+ processLine(joinLine());
235
+ clearLine();
236
+ } else {
237
+ const length = end2 - start;
238
+ if (length > maxLineLength) {
239
+ throw fatal(`SSE line exceeded ${maxLineLength} characters`, "ERR_SSE_LINE_LIMIT", {
240
+ limit: maxLineLength
241
+ });
242
+ }
243
+ processLine(text.slice(start, end2));
244
+ }
245
+ start = end2 + 1;
246
+ end2 = text.indexOf("\n", start);
247
+ }
248
+ appendLine(text.slice(start));
249
+ }
250
+ function appendLine(fragment) {
251
+ if (!fragment) return;
252
+ lineFragments.push(fragment);
253
+ lineLength += fragment.length;
254
+ if (lineLength > maxLineLength) {
255
+ throw fatal(`SSE line exceeded ${maxLineLength} characters`, "ERR_SSE_LINE_LIMIT", {
256
+ limit: maxLineLength
257
+ });
258
+ }
259
+ }
260
+ function joinLine() {
261
+ if (lineFragments.length === 0) return "";
262
+ if (lineFragments.length === 1) return lineFragments[0];
263
+ return lineFragments.join("");
264
+ }
265
+ function clearLine() {
266
+ lineFragments = [];
267
+ lineLength = 0;
268
+ }
269
+ function processLine(line) {
270
+ if (line.length === 0) {
271
+ dispatchEvent();
272
+ return;
273
+ }
274
+ if (line.charCodeAt(0) === 58) {
275
+ comments++;
276
+ onComment(line.slice(line.charCodeAt(1) === SPACE ? 2 : 1));
277
+ return;
278
+ }
279
+ const first = line.charCodeAt(0);
280
+ if (first === 100 && line.charCodeAt(1) === 97 && line.charCodeAt(2) === 116 && line.charCodeAt(3) === 97 && line.charCodeAt(4) === 58) {
281
+ const start = line.charCodeAt(5) === SPACE ? 6 : 5;
282
+ const value2 = line.slice(start);
283
+ addEventSize(value2.length + 1);
284
+ dataLines.push(value2);
285
+ return;
286
+ }
287
+ if (first === 101 && line.charCodeAt(1) === 118 && line.charCodeAt(2) === 101 && line.charCodeAt(3) === 110 && line.charCodeAt(4) === 116 && line.charCodeAt(5) === 58) {
288
+ const start = line.charCodeAt(6) === SPACE ? 7 : 6;
289
+ const value2 = line.slice(start);
290
+ addEventSize(value2.length);
291
+ eventType = value2 || void 0;
292
+ return;
293
+ }
294
+ if (first === 105 && line.charCodeAt(1) === 100 && line.charCodeAt(2) === 58) {
295
+ const start = line.charCodeAt(3) === SPACE ? 4 : 3;
296
+ const value2 = line.slice(start);
297
+ if (value2.indexOf(NUL) === -1) {
298
+ addEventSize(value2.length);
299
+ idBuffer = value2;
300
+ hasPendingId = true;
301
+ }
302
+ return;
303
+ }
304
+ if (first === 114 && line.charCodeAt(1) === 101 && line.charCodeAt(2) === 116 && line.charCodeAt(3) === 114 && line.charCodeAt(4) === 121 && line.charCodeAt(5) === 58) {
305
+ const start = line.charCodeAt(6) === SPACE ? 7 : 6;
306
+ processRetry(line.slice(start), line);
307
+ return;
308
+ }
309
+ const separator = line.indexOf(":");
310
+ const field = separator === -1 ? line : line.slice(0, separator);
311
+ let value = separator === -1 ? "" : line.slice(separator + 1);
312
+ if (value.charCodeAt(0) === SPACE) value = value.slice(1);
313
+ switch (field) {
314
+ case "data":
315
+ addEventSize(value.length + 1);
316
+ dataLines.push(value);
317
+ break;
318
+ case "event":
319
+ addEventSize(value.length);
320
+ eventType = value || void 0;
321
+ break;
322
+ case "id":
323
+ if (value.indexOf(NUL) === -1) {
324
+ addEventSize(value.length);
325
+ idBuffer = value;
326
+ hasPendingId = true;
327
+ }
328
+ break;
329
+ case "retry":
330
+ processRetry(value, line);
331
+ break;
332
+ default:
333
+ if (strict) recoverable(`Unknown SSE field "${truncate(field)}"`, "ERR_SSE_UNKNOWN_FIELD", {
334
+ field,
335
+ line
336
+ });
337
+ }
338
+ }
339
+ function processRetry(value, line) {
340
+ if (!isAsciiDigits(value)) {
341
+ if (strict) recoverable(`Invalid SSE retry value "${truncate(value)}"`, "ERR_SSE_RETRY_VALUE", {
342
+ field: "retry",
343
+ line
344
+ });
345
+ return;
346
+ }
347
+ const number = Number(value);
348
+ if (!Number.isSafeInteger(number)) {
349
+ if (strict) recoverable("SSE retry value exceeds the safe integer range", "ERR_SSE_RETRY_VALUE", {
350
+ field: "retry",
351
+ line
352
+ });
353
+ return;
354
+ }
355
+ retries++;
356
+ onRetry(number);
357
+ }
358
+ function dispatchEvent() {
359
+ const eventId = hasPendingId ? idBuffer : void 0;
360
+ if (hasPendingId) {
361
+ committedId = idBuffer;
362
+ hasPendingId = false;
363
+ onId(committedId);
364
+ }
365
+ if (dataLines.length === 0) {
366
+ eventType = void 0;
367
+ eventSize = 0;
368
+ return;
369
+ }
370
+ const event = {
371
+ data: dataLines.join("\n"),
372
+ id: eventId,
373
+ event: eventType,
374
+ lastEventId: committedId
375
+ };
376
+ events++;
377
+ dataLines = [];
378
+ eventType = void 0;
379
+ eventSize = 0;
380
+ onEvent(event);
381
+ }
382
+ function dispatchSingleData(data) {
383
+ const eventId = hasPendingId ? idBuffer : void 0;
384
+ if (hasPendingId) {
385
+ committedId = idBuffer;
386
+ hasPendingId = false;
387
+ onId(committedId);
388
+ }
389
+ const event = {
390
+ data,
391
+ id: eventId,
392
+ event: eventType,
393
+ lastEventId: committedId
394
+ };
395
+ events++;
396
+ eventType = void 0;
397
+ eventSize = 0;
398
+ onEvent(event);
399
+ }
400
+ function addEventSize(amount) {
401
+ eventSize += amount;
402
+ if (eventSize > maxEventSize) {
403
+ throw fatal(`SSE event exceeded ${maxEventSize} characters`, "ERR_SSE_EVENT_LIMIT", {
404
+ limit: maxEventSize
405
+ });
406
+ }
407
+ }
408
+ function discardPendingEvent() {
409
+ dataLines = [];
410
+ eventType = void 0;
411
+ eventSize = 0;
412
+ hasPendingId = false;
413
+ idBuffer = committedId;
414
+ }
415
+ function getDecoder() {
416
+ if (!decoder) {
417
+ if (typeof TextDecoder !== "function") {
418
+ throw fatal("TextDecoder is required for byte chunks", "ERR_SSE_TEXT_DECODER");
419
+ }
420
+ decoder = new TextDecoder("utf-8", { fatal: fatalUTF8 });
421
+ }
422
+ return decoder;
423
+ }
424
+ function recoverable(message, code, details) {
425
+ const error = new SSEParseError(message, { ...details, code, fatal: false });
426
+ onError(error);
427
+ return error;
428
+ }
429
+ function fatal(message, code, details = {}) {
430
+ terminated = true;
431
+ lineFragments = [];
432
+ lineLength = 0;
433
+ dataLines = [];
434
+ eventType = void 0;
435
+ eventSize = 0;
436
+ const error = new SSEParseError(message, { ...details, code, fatal: true });
437
+ onError(error);
438
+ return error;
439
+ }
440
+ const api2 = {
441
+ feed,
442
+ end,
443
+ reset,
444
+ get state() {
445
+ return Object.freeze({
446
+ buffered: lineLength + eventSize,
447
+ bytes,
448
+ characters,
449
+ comments,
450
+ events,
451
+ lastEventId: committedId,
452
+ retries,
453
+ terminated
454
+ });
455
+ }
456
+ };
457
+ return api2;
458
+ }
459
+ function readLimit(value, fallback, name) {
460
+ const limit = value === void 0 ? fallback : value;
461
+ if (!Number.isSafeInteger(limit) || limit < 1) {
462
+ throw new RangeError(`${name} must be a positive safe integer`);
463
+ }
464
+ return limit;
465
+ }
466
+ function normalizeInitialId(value) {
467
+ if (value === void 0 || value === null) return "";
468
+ if (typeof value !== "string") throw new TypeError("lastEventId must be a string");
469
+ if (value.indexOf(NUL) !== -1 || value.indexOf("\r") !== -1 || value.indexOf("\n") !== -1) {
470
+ throw new TypeError("lastEventId cannot contain NUL, CR or LF");
471
+ }
472
+ return value;
473
+ }
474
+ function isAsciiDigits(value) {
475
+ if (value.length === 0) return false;
476
+ for (let index = 0; index < value.length; index++) {
477
+ const code = value.charCodeAt(index);
478
+ if (code < 48 || code > 57) return false;
479
+ }
480
+ return true;
481
+ }
482
+ function truncate(value) {
483
+ return value.length > 80 ? `${value.slice(0, 80)}...` : value;
484
+ }
485
+ function isDataPrefix(value, index) {
486
+ return value.charCodeAt(index) === 100 && value.charCodeAt(index + 1) === 97 && value.charCodeAt(index + 2) === 116 && value.charCodeAt(index + 3) === 97 && value.charCodeAt(index + 4) === 58;
487
+ }
488
+
489
+ // src/encoder.js
490
+ var EVENT_STREAM_CONTENT_TYPE = "text/event-stream";
491
+ var EventStreamContentType = EVENT_STREAM_CONTENT_TYPE;
492
+ function encodeSSE(message, options = {}) {
493
+ if (!message || typeof message !== "object") {
494
+ throw new TypeError("SSE message must be an object");
495
+ }
496
+ const newline = options.newline === void 0 ? "\n" : options.newline;
497
+ if (newline !== "\n" && newline !== "\r\n") {
498
+ throw new SSEEncodeError('newline must be "\\n" or "\\r\\n"', "newline");
499
+ }
500
+ const lines = [];
501
+ if (message.comment !== void 0) {
502
+ for (const line of splitLines(toText(message.comment, "comment"))) lines.push(`:${line ? ` ${line}` : ""}`);
503
+ }
504
+ if (message.event !== void 0 && message.event !== "") {
505
+ const value = toText(message.event, "event");
506
+ assertSingleLine(value, "event", false);
507
+ lines.push(`event: ${value}`);
508
+ }
509
+ if (message.id !== void 0) {
510
+ const value = toText(message.id, "id");
511
+ assertSingleLine(value, "id", true);
512
+ lines.push(`id: ${value}`);
513
+ }
514
+ if (message.retry !== void 0) {
515
+ if (!Number.isSafeInteger(message.retry) || message.retry < 0) {
516
+ throw new SSEEncodeError("retry must be a non-negative safe integer", "retry");
517
+ }
518
+ lines.push(`retry: ${message.retry}`);
519
+ }
520
+ if (message.data !== void 0) {
521
+ for (const line of splitLines(toText(message.data, "data"))) lines.push(`data: ${line}`);
522
+ }
523
+ if (lines.length === 0) {
524
+ throw new SSEEncodeError("SSE message must contain data, comment, event, id or retry", "message");
525
+ }
526
+ return `${lines.join(newline)}${newline}${newline}`;
527
+ }
528
+ var encode = encodeSSE;
529
+ function encodeJSON(data, fields = {}, options = {}) {
530
+ let encoded;
531
+ try {
532
+ encoded = JSON.stringify(data, options.replacer);
533
+ } catch (error) {
534
+ throw new SSEEncodeError("Unable to encode SSE JSON data", "data", error);
535
+ }
536
+ if (encoded === void 0) {
537
+ throw new SSEEncodeError("JSON data cannot be undefined, a function or a symbol", "data");
538
+ }
539
+ return encodeSSE({ ...fields, data: encoded }, options);
540
+ }
541
+ function encodeComment(comment, options = {}) {
542
+ return encodeSSE({ comment }, options);
543
+ }
544
+ function createEncoderStream(options = {}) {
545
+ const Transform = getTransformStream();
546
+ const bytes = options.bytes !== false;
547
+ const encoder = bytes ? getTextEncoder() : null;
548
+ return new Transform({
549
+ transform(message, controller) {
550
+ const output = options.json === true ? encodeJSON(message.data, message, options) : encodeSSE(message, options);
551
+ controller.enqueue(encoder ? encoder.encode(output) : output);
552
+ }
553
+ });
554
+ }
555
+ function splitLines(value) {
556
+ const lines = [];
557
+ let start = 0;
558
+ for (let index = 0; index < value.length; index++) {
559
+ const code = value.charCodeAt(index);
560
+ if (code !== 10 && code !== 13) continue;
561
+ lines.push(value.slice(start, index));
562
+ if (code === 13 && value.charCodeAt(index + 1) === 10) index++;
563
+ start = index + 1;
564
+ }
565
+ lines.push(value.slice(start));
566
+ return lines;
567
+ }
568
+ function toText(value, field) {
569
+ if (typeof value === "string") return value;
570
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
571
+ return String(value);
572
+ }
573
+ throw new SSEEncodeError(`${field} must be a string or primitive value`, field);
574
+ }
575
+ function assertSingleLine(value, field, rejectNul) {
576
+ if (value.indexOf("\r") !== -1 || value.indexOf("\n") !== -1 || rejectNul && value.indexOf("\0") !== -1) {
577
+ throw new SSEEncodeError(`${field} contains a forbidden control character`, field);
578
+ }
579
+ }
580
+ function getTransformStream() {
581
+ if (typeof TransformStream !== "function") {
582
+ throw new Error("TransformStream is not available in this runtime");
583
+ }
584
+ return TransformStream;
585
+ }
586
+ function getTextEncoder() {
587
+ if (typeof TextEncoder !== "function") throw new Error("TextEncoder is not available in this runtime");
588
+ return new TextEncoder();
589
+ }
590
+
591
+ // src/iterable.js
592
+ var DEFAULT_FEED_SIZE = 16 * 1024;
593
+ var DEFAULT_MAX_QUEUED_EVENTS = 4096;
594
+ async function* decodeSSE(source, options = {}) {
595
+ const signal = options.signal;
596
+ const feedSize = readPositive(options.feedSize, DEFAULT_FEED_SIZE, "feedSize");
597
+ const maxQueuedEvents = readPositive(
598
+ options.maxQueuedEvents,
599
+ DEFAULT_MAX_QUEUED_EVENTS,
600
+ "maxQueuedEvents"
601
+ );
602
+ const queue = [];
603
+ const parserOptions = options.parser || options;
604
+ const userOnEvent = parserOptions.onEvent;
605
+ const parser = createParser({
606
+ ...parserOptions,
607
+ onEvent(event) {
608
+ if (queue.length >= maxQueuedEvents) {
609
+ throw new SSEParseError(`Queued SSE events exceeded ${maxQueuedEvents}`, {
610
+ code: "ERR_SSE_QUEUE_LIMIT",
611
+ fatal: true,
612
+ limit: maxQueuedEvents
613
+ });
614
+ }
615
+ queue.push(event);
616
+ if (typeof userOnEvent === "function") userOnEvent(event);
617
+ }
618
+ });
619
+ const iterable = toAsyncIterable(source);
620
+ const iterator = iterable[Symbol.asyncIterator]();
621
+ try {
622
+ while (true) {
623
+ throwIfAborted(signal);
624
+ const item = await nextWithAbort(iterator, signal);
625
+ if (item.done) break;
626
+ const chunk = item.value;
627
+ if (typeof options.onChunk === "function") options.onChunk(chunk);
628
+ if (typeof chunk !== "string" && !(chunk instanceof Uint8Array)) {
629
+ throw new TypeError("SSE stream chunks must be strings or Uint8Array values");
630
+ }
631
+ for (let offset = 0; offset < chunk.length; offset += feedSize) {
632
+ parser.feed(chunk.slice(offset, offset + feedSize));
633
+ for (let index = 0; index < queue.length; index++) yield queue[index];
634
+ queue.length = 0;
635
+ throwIfAborted(signal);
636
+ }
637
+ }
638
+ parser.end();
639
+ for (let index = 0; index < queue.length; index++) yield queue[index];
640
+ } finally {
641
+ await closeIterator(iterator, signal);
642
+ }
643
+ }
644
+ async function* decodeJSON(source, options = {}) {
645
+ const doneSentinel = options.doneSentinel === void 0 ? "[DONE]" : options.doneSentinel;
646
+ for await (const event of decodeSSE(source, options)) {
647
+ if (doneSentinel !== false && event.data === doneSentinel) return;
648
+ try {
649
+ yield { ...event, data: JSON.parse(event.data, options.reviver) };
650
+ } catch (cause) {
651
+ if (options.ignoreInvalidJSON === true) continue;
652
+ throw new SSEParseError("SSE event data is not valid JSON", {
653
+ code: "ERR_SSE_JSON",
654
+ fatal: true,
655
+ cause
656
+ });
657
+ }
658
+ }
659
+ }
660
+ function createDecoderStream(options = {}) {
661
+ if (typeof TransformStream !== "function") {
662
+ throw new Error("TransformStream is not available in this runtime");
663
+ }
664
+ let controller;
665
+ const parser = createParser({
666
+ ...options.parser || options,
667
+ onEvent(event) {
668
+ controller.enqueue(event);
669
+ const callback = (options.parser || options).onEvent;
670
+ if (typeof callback === "function") callback(event);
671
+ }
672
+ });
673
+ return new TransformStream({
674
+ start(value) {
675
+ controller = value;
676
+ },
677
+ transform(chunk) {
678
+ parser.feed(chunk);
679
+ },
680
+ flush() {
681
+ parser.end();
682
+ }
683
+ });
684
+ }
685
+ function toAsyncIterable(source) {
686
+ const value = source && source.body ? source.body : source;
687
+ if (!value) throw new TypeError("An SSE stream source is required");
688
+ if (typeof value[Symbol.asyncIterator] === "function") return value;
689
+ if (typeof value.getReader === "function") return webStreamIterable(value);
690
+ if (typeof value[Symbol.iterator] === "function") return syncToAsync(value);
691
+ throw new TypeError("Source must be a Response, ReadableStream, AsyncIterable or Iterable");
692
+ }
693
+ async function* webStreamIterable(stream) {
694
+ const reader = stream.getReader();
695
+ try {
696
+ while (true) {
697
+ const item = await reader.read();
698
+ if (item.done) return;
699
+ yield item.value;
700
+ }
701
+ } finally {
702
+ try {
703
+ await reader.cancel();
704
+ } catch (e) {
705
+ }
706
+ if (typeof reader.releaseLock === "function") reader.releaseLock();
707
+ }
708
+ }
709
+ async function* syncToAsync(iterable) {
710
+ yield* iterable;
711
+ }
712
+ function throwIfAborted(signal) {
713
+ if (!signal || !signal.aborted) return;
714
+ if (signal.reason instanceof Error) throw signal.reason;
715
+ const error = new Error("The operation was aborted");
716
+ error.name = "AbortError";
717
+ throw error;
718
+ }
719
+ function nextWithAbort(iterator, signal) {
720
+ if (!signal) return iterator.next();
721
+ throwIfAborted(signal);
722
+ return new Promise((resolve, reject) => {
723
+ const cleanup = () => signal.removeEventListener("abort", abort);
724
+ const abort = () => {
725
+ cleanup();
726
+ try {
727
+ throwIfAborted(signal);
728
+ } catch (error) {
729
+ reject(error);
730
+ }
731
+ };
732
+ signal.addEventListener("abort", abort, { once: true });
733
+ if (signal.aborted) {
734
+ abort();
735
+ return;
736
+ }
737
+ Promise.resolve(iterator.next()).then(
738
+ (item) => {
739
+ cleanup();
740
+ resolve(item);
741
+ },
742
+ (error) => {
743
+ cleanup();
744
+ reject(error);
745
+ }
746
+ );
747
+ });
748
+ }
749
+ async function closeIterator(iterator, signal) {
750
+ if (typeof iterator.return !== "function") return;
751
+ try {
752
+ const result = iterator.return();
753
+ if (!signal || !signal.aborted) await result;
754
+ else Promise.resolve(result).catch(() => {
755
+ });
756
+ } catch (error) {
757
+ if (!signal || !signal.aborted) throw error;
758
+ }
759
+ }
760
+ function readPositive(value, fallback, name) {
761
+ const number = value === void 0 ? fallback : value;
762
+ if (!Number.isSafeInteger(number) || number < 1) {
763
+ throw new RangeError(`${name} must be a positive safe integer`);
764
+ }
765
+ return number;
766
+ }
767
+
768
+ // src/client.js
769
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
770
+ async function* fetchSSE(input, options = {}) {
771
+ const fetchImpl = options.fetch === void 0 ? globalThis.fetch : options.fetch;
772
+ if (typeof fetchImpl !== "function") {
773
+ throw new TypeError("No fetch implementation is available; pass options.fetch");
774
+ }
775
+ const retry = normalizeRetry(options.retry);
776
+ const connectTimeout = readOptionalDelay(options.connectTimeout, "connectTimeout");
777
+ const idleTimeout = readOptionalDelay(options.idleTimeout, "idleTimeout");
778
+ const totalTimeout = readOptionalDelay(options.totalTimeout, "totalTimeout");
779
+ const externalSignal = options.signal;
780
+ const requestInit = omitClientOptions(options);
781
+ const originalBody = requestInit.body;
782
+ const bodyReplayable = isReplayableBody(originalBody);
783
+ let lastEventId = validateLastEventId(
784
+ options.lastEventId === void 0 ? "" : options.lastEventId
785
+ );
786
+ let serverRetry;
787
+ let attempts = 0;
788
+ let reconnects = 0;
789
+ let lastError;
790
+ let stopped = false;
791
+ const startedAt = monotonicNow();
792
+ while (!stopped) {
793
+ attempts++;
794
+ throwIfAborted2(externalSignal);
795
+ if (attempts > 1 && !bodyReplayable && typeof options.bodyFactory !== "function") {
796
+ throw new SSEReplayError();
797
+ }
798
+ const attempt = createAttemptController(externalSignal);
799
+ let response;
800
+ let timeoutPhase;
801
+ let connectTimer;
802
+ let idleTimer;
803
+ let totalTimer;
804
+ let fetchStarted = false;
805
+ let retryDelayOverride;
806
+ try {
807
+ if (totalTimeout) {
808
+ const remaining = totalTimeout - (monotonicNow() - startedAt);
809
+ if (remaining <= 0) throw new SSETimeoutError("total", totalTimeout);
810
+ totalTimer = setTimer(() => {
811
+ timeoutPhase = "total";
812
+ attempt.abort();
813
+ }, remaining);
814
+ }
815
+ if (connectTimeout) {
816
+ connectTimer = setTimer(() => {
817
+ timeoutPhase = "connect";
818
+ attempt.abort();
819
+ }, connectTimeout);
820
+ }
821
+ let body = originalBody;
822
+ if (typeof options.bodyFactory === "function") {
823
+ try {
824
+ body = await waitForAbort(
825
+ options.bodyFactory({
826
+ attempt: attempts,
827
+ lastEventId,
828
+ signal: attempt.controller.signal
829
+ }),
830
+ attempt.controller.signal
831
+ );
832
+ } catch (cause) {
833
+ throw new SSEError("SSE bodyFactory failed", "ERR_SSE_BODY_FACTORY", { cause });
834
+ }
835
+ }
836
+ const sourceHeaders = requestInit.headers === void 0 && input && input.headers ? input.headers : requestInit.headers;
837
+ const init = {
838
+ ...requestInit,
839
+ body,
840
+ cache: requestInit.cache === void 0 ? "no-store" : requestInit.cache,
841
+ headers: mergeSSEHeaders(sourceHeaders, lastEventId),
842
+ signal: attempt.controller.signal
843
+ };
844
+ const attemptInput = cloneInput(input);
845
+ fetchStarted = true;
846
+ response = await waitForAbort(
847
+ fetchImpl(attemptInput, init),
848
+ attempt.controller.signal,
849
+ cancelResponseBody
850
+ );
851
+ clearTimer(connectTimer);
852
+ connectTimer = void 0;
853
+ if (!response || typeof response.status !== "number") {
854
+ throw new SSEError("fetch returned an invalid Response-like value", "ERR_SSE_RESPONSE");
855
+ }
856
+ if (response.status === 204) {
857
+ stopped = true;
858
+ if (typeof options.onClose === "function") {
859
+ await callHook(
860
+ options.onClose,
861
+ [{ attempts, lastEventId, reason: "server", reconnects, response }],
862
+ "onClose"
863
+ );
864
+ }
865
+ return;
866
+ }
867
+ validateResponse(response);
868
+ if (typeof options.onOpen === "function") {
869
+ const result = await callHook(
870
+ options.onOpen,
871
+ [response, { attempts, lastEventId, reconnects }],
872
+ "onOpen"
873
+ );
874
+ if (result === false) {
875
+ await cancelResponseBody(response);
876
+ return;
877
+ }
878
+ }
879
+ const resetIdle = () => {
880
+ if (!idleTimeout) return;
881
+ clearTimer(idleTimer);
882
+ idleTimer = setTimer(() => {
883
+ timeoutPhase = "idle";
884
+ attempt.abort();
885
+ }, idleTimeout);
886
+ };
887
+ resetIdle();
888
+ const parserOptions = options.parser || {};
889
+ const userOnId = parserOptions.onId;
890
+ const userOnRetry = parserOptions.onRetry;
891
+ for await (const event of decodeSSE(response.body, {
892
+ ...options.decode,
893
+ signal: attempt.controller.signal,
894
+ onChunk(chunk) {
895
+ resetIdle();
896
+ if (options.decode && typeof options.decode.onChunk === "function") {
897
+ options.decode.onChunk(chunk);
898
+ }
899
+ },
900
+ parser: {
901
+ ...parserOptions,
902
+ lastEventId,
903
+ onId(id) {
904
+ lastEventId = id;
905
+ if (typeof userOnId === "function") userOnId(id);
906
+ },
907
+ onRetry(delay2) {
908
+ serverRetry = delay2;
909
+ if (typeof userOnRetry === "function") userOnRetry(delay2);
910
+ }
911
+ }
912
+ })) {
913
+ lastEventId = event.lastEventId;
914
+ if (typeof options.onEvent === "function") {
915
+ await callHook(
916
+ options.onEvent,
917
+ [event, { attempts, lastEventId, reconnects, response }],
918
+ "onEvent"
919
+ );
920
+ }
921
+ yield event;
922
+ }
923
+ clearTimer(idleTimer);
924
+ lastError = void 0;
925
+ } catch (caught) {
926
+ clearTimer(connectTimer);
927
+ clearTimer(idleTimer);
928
+ clearTimer(totalTimer);
929
+ if (externalSignal && externalSignal.aborted) throw abortReason(externalSignal);
930
+ if (timeoutPhase) {
931
+ lastError = new SSETimeoutError(timeoutPhase, options[`${timeoutPhase}Timeout`], caught);
932
+ } else {
933
+ lastError = caught;
934
+ }
935
+ await cancelResponseBody(response);
936
+ if (typeof options.onError === "function") {
937
+ const decision = await callHook(
938
+ options.onError,
939
+ [{ attempt: attempts, error: lastError, lastEventId, reconnects, response }],
940
+ "onError"
941
+ );
942
+ if (decision === false) throw lastError;
943
+ if (typeof decision === "number") retryDelayOverride = decision;
944
+ }
945
+ if (!shouldRetryError(lastError, response, retry, fetchStarted)) throw lastError;
946
+ } finally {
947
+ clearTimer(totalTimer);
948
+ await cancelResponseBody(response);
949
+ attempt.dispose();
950
+ }
951
+ if (reconnects >= retry.retries) {
952
+ if (!lastError) {
953
+ if (typeof options.onClose === "function") {
954
+ await callHook(
955
+ options.onClose,
956
+ [{ attempts, lastEventId, reason: "eof", reconnects, response }],
957
+ "onClose"
958
+ );
959
+ }
960
+ return;
961
+ }
962
+ if (retry.retries === 0) throw lastError;
963
+ throw new SSERetryError(attempts, lastError);
964
+ }
965
+ reconnects++;
966
+ let delay = retryDelay(response, serverRetry, reconnects, retry);
967
+ if (retryDelayOverride !== void 0) delay = clampDelay(retryDelayOverride, retry.maxDelay);
968
+ const context = {
969
+ attempt: attempts,
970
+ delay,
971
+ error: lastError,
972
+ lastEventId,
973
+ reconnects,
974
+ response
975
+ };
976
+ if (typeof options.onRetry === "function") {
977
+ const decision = await callHook(options.onRetry, [context], "onRetry");
978
+ if (decision === false) return;
979
+ if (typeof decision === "number") delay = clampDelay(decision, retry.maxDelay);
980
+ }
981
+ await sleep(delay, externalSignal, totalTimeout, startedAt);
982
+ }
983
+ }
984
+ async function consumeSSE(input, options = {}) {
985
+ let count = 0;
986
+ let lastEventId = options.lastEventId || "";
987
+ for await (const event of fetchSSE(input, options)) {
988
+ count++;
989
+ lastEventId = event.lastEventId;
990
+ }
991
+ return { events: count, lastEventId };
992
+ }
993
+ async function fetchEventSource(input, options = {}) {
994
+ const mapped = {
995
+ ...options,
996
+ onOpen: options.onopen || options.onOpen,
997
+ onEvent: options.onmessage || options.onEvent,
998
+ onClose: options.onclose || options.onClose,
999
+ async onError(context) {
1000
+ if (typeof options.onerror !== "function") {
1001
+ return typeof options.onError === "function" ? options.onError(context) : void 0;
1002
+ }
1003
+ const decision = await options.onerror(context.error);
1004
+ return decision === void 0 ? true : decision;
1005
+ },
1006
+ async onRetry(context) {
1007
+ if (typeof options.onRetry === "function") return options.onRetry(context);
1008
+ return void 0;
1009
+ }
1010
+ };
1011
+ return consumeSSE(input, mapped);
1012
+ }
1013
+ function normalizeRetry(value) {
1014
+ const input = value === false ? { retries: 0 } : typeof value === "number" ? { retries: value } : value || {};
1015
+ const retries = input.retries === void 0 ? Infinity : input.retries;
1016
+ if (retries !== Infinity && (!Number.isSafeInteger(retries) || retries < 0)) {
1017
+ throw new RangeError("retry.retries must be a non-negative safe integer or Infinity");
1018
+ }
1019
+ const minDelay = readDelay(input.minDelay, 1e3, "retry.minDelay");
1020
+ const maxDelay = readDelay(input.maxDelay, 3e4, "retry.maxDelay");
1021
+ const factor = input.factor === void 0 ? 2 : input.factor;
1022
+ if (!Number.isFinite(factor) || factor < 1) throw new RangeError("retry.factor must be at least 1");
1023
+ return {
1024
+ retries,
1025
+ minDelay,
1026
+ maxDelay: Math.max(minDelay, maxDelay),
1027
+ factor,
1028
+ jitter: input.jitter === void 0 ? "full" : input.jitter,
1029
+ statusCodes: new Set(input.statusCodes || RETRYABLE_STATUS),
1030
+ shouldRetry: input.shouldRetry
1031
+ };
1032
+ }
1033
+ function validateResponse(response) {
1034
+ if (response.status !== 200) {
1035
+ throw new SSEHTTPError(`SSE endpoint responded with HTTP ${response.status}`, response);
1036
+ }
1037
+ const contentType = response.headers && typeof response.headers.get === "function" ? response.headers.get("content-type") : null;
1038
+ if (!contentType || contentType.split(";", 1)[0].trim().toLowerCase() !== EVENT_STREAM_CONTENT_TYPE) {
1039
+ throw new SSEHTTPError(
1040
+ `Expected Content-Type ${EVENT_STREAM_CONTENT_TYPE}; received ${contentType || "none"}`,
1041
+ response,
1042
+ "ERR_SSE_CONTENT_TYPE"
1043
+ );
1044
+ }
1045
+ if (!response.body) throw new SSEHTTPError("SSE response has no readable body", response);
1046
+ }
1047
+ function shouldRetryError(error, response, retry, fetchStarted) {
1048
+ if (typeof retry.shouldRetry === "function") return retry.shouldRetry({ error, response }) === true;
1049
+ if (error && error.code === "ERR_SSE_CONTENT_TYPE") return false;
1050
+ if (!error) return false;
1051
+ if (error.code === "ERR_SSE_TIMEOUT") return true;
1052
+ if (response && response.status !== 200) return retry.statusCodes.has(response.status);
1053
+ if (!fetchStarted) return false;
1054
+ return error.name === "TypeError" || NETWORK_ERROR_CODES.has(error.code);
1055
+ }
1056
+ function retryDelay(response, serverRetry, reconnects, options) {
1057
+ const header = response && response.headers && typeof response.headers.get === "function" ? response.headers.get("retry-after") : null;
1058
+ const fromHeader = parseRetryAfter(header);
1059
+ if (fromHeader !== void 0) return clampDelay(fromHeader, options.maxDelay);
1060
+ if (serverRetry !== void 0) return clampDelay(serverRetry, options.maxDelay);
1061
+ const exponential = Math.min(
1062
+ options.maxDelay,
1063
+ options.minDelay * Math.pow(options.factor, reconnects - 1)
1064
+ );
1065
+ if (options.jitter === false || options.jitter === "none") return exponential;
1066
+ if (typeof options.jitter === "function") return clampDelay(options.jitter(exponential), options.maxDelay);
1067
+ return Math.floor(Math.random() * (exponential + 1));
1068
+ }
1069
+ function parseRetryAfter(value) {
1070
+ if (!value) return void 0;
1071
+ const seconds = Number(value);
1072
+ if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1e3);
1073
+ const date = Date.parse(value);
1074
+ if (Number.isNaN(date)) return void 0;
1075
+ return Math.max(0, date - Date.now());
1076
+ }
1077
+ function mergeSSEHeaders(input, lastEventId) {
1078
+ const entries = [];
1079
+ if (input && typeof input[Symbol.iterator] === "function" && typeof input !== "string") {
1080
+ for (const pair of input) setHeader(entries, pair[0], pair[1]);
1081
+ } else if (input && typeof input.forEach === "function") {
1082
+ input.forEach((value, key) => setHeader(entries, key, value));
1083
+ } else if (input && typeof input === "object") {
1084
+ for (const key of Object.keys(input)) setHeader(entries, key, input[key]);
1085
+ }
1086
+ if (!hasHeader(entries, "accept")) setHeader(entries, "Accept", EVENT_STREAM_CONTENT_TYPE);
1087
+ if (lastEventId) setHeader(entries, "Last-Event-ID", lastEventId);
1088
+ else deleteHeader(entries, "last-event-id");
1089
+ return entries;
1090
+ }
1091
+ function setHeader(entries, key, value) {
1092
+ const name = String(key);
1093
+ deleteHeader(entries, name);
1094
+ entries.push([name, String(value)]);
1095
+ }
1096
+ function deleteHeader(entries, name) {
1097
+ const lower = String(name).toLowerCase();
1098
+ for (let index = entries.length - 1; index >= 0; index--) {
1099
+ if (entries[index][0].toLowerCase() === lower) entries.splice(index, 1);
1100
+ }
1101
+ }
1102
+ function hasHeader(entries, name) {
1103
+ const lower = name.toLowerCase();
1104
+ return entries.some((entry) => entry[0].toLowerCase() === lower);
1105
+ }
1106
+ function omitClientOptions(options) {
1107
+ const output = {};
1108
+ const privateKeys = /* @__PURE__ */ new Set([
1109
+ "bodyFactory",
1110
+ "connectTimeout",
1111
+ "decode",
1112
+ "fetch",
1113
+ "idleTimeout",
1114
+ "lastEventId",
1115
+ "onClose",
1116
+ "onError",
1117
+ "onEvent",
1118
+ "onOpen",
1119
+ "onRetry",
1120
+ "onclose",
1121
+ "onerror",
1122
+ "onmessage",
1123
+ "onopen",
1124
+ "openWhenHidden",
1125
+ "parser",
1126
+ "retry",
1127
+ "totalTimeout"
1128
+ ]);
1129
+ for (const key of Object.keys(options)) if (!privateKeys.has(key)) output[key] = options[key];
1130
+ return output;
1131
+ }
1132
+ function isReplayableBody(body) {
1133
+ if (body === void 0 || body === null) return true;
1134
+ if (typeof body === "string") return true;
1135
+ if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return true;
1136
+ if (typeof URLSearchParams === "function" && body instanceof URLSearchParams) return true;
1137
+ if (typeof Blob === "function" && body instanceof Blob) return true;
1138
+ if (typeof FormData === "function" && body instanceof FormData) return true;
1139
+ return !(typeof body.getReader === "function" || typeof body[Symbol.asyncIterator] === "function");
1140
+ }
1141
+ function cloneInput(input) {
1142
+ if (input && typeof input === "object" && typeof input.clone === "function") return input.clone();
1143
+ return input;
1144
+ }
1145
+ function validateLastEventId(value) {
1146
+ if (typeof value !== "string") throw new TypeError("lastEventId must be a string");
1147
+ if (value.indexOf("\0") !== -1 || value.indexOf("\r") !== -1 || value.indexOf("\n") !== -1) {
1148
+ throw new TypeError("lastEventId cannot contain NUL, CR or LF");
1149
+ }
1150
+ return value;
1151
+ }
1152
+ function createAttemptController(signal) {
1153
+ if (typeof AbortController !== "function") throw new Error("AbortController is not available");
1154
+ const controller = new AbortController();
1155
+ const forward = () => controller.abort();
1156
+ if (signal) signal.addEventListener("abort", forward, { once: true });
1157
+ if (signal && signal.aborted) controller.abort();
1158
+ return {
1159
+ controller,
1160
+ abort() {
1161
+ controller.abort();
1162
+ },
1163
+ dispose() {
1164
+ if (signal) signal.removeEventListener("abort", forward);
1165
+ }
1166
+ };
1167
+ }
1168
+ async function sleep(delay, signal, totalTimeout, startedAt) {
1169
+ if (delay <= 0) return;
1170
+ throwIfAborted2(signal);
1171
+ let actual = delay;
1172
+ if (totalTimeout) {
1173
+ const remaining = totalTimeout - (monotonicNow() - startedAt);
1174
+ if (remaining <= 0) throw new SSETimeoutError("total", totalTimeout);
1175
+ actual = Math.min(actual, remaining);
1176
+ }
1177
+ await new Promise((resolve, reject) => {
1178
+ const done = () => {
1179
+ if (signal) signal.removeEventListener("abort", abort);
1180
+ resolve();
1181
+ };
1182
+ const timer = setTimer(done, actual);
1183
+ const abort = () => {
1184
+ clearTimer(timer);
1185
+ if (signal) signal.removeEventListener("abort", abort);
1186
+ reject(abortReason(signal));
1187
+ };
1188
+ if (signal) {
1189
+ signal.addEventListener("abort", abort, { once: true });
1190
+ if (signal.aborted) abort();
1191
+ }
1192
+ });
1193
+ if (totalTimeout && monotonicNow() - startedAt >= totalTimeout) {
1194
+ throw new SSETimeoutError("total", totalTimeout);
1195
+ }
1196
+ }
1197
+ function waitForAbort(value, signal, onLateValue) {
1198
+ throwIfAborted2(signal);
1199
+ return new Promise((resolve, reject) => {
1200
+ let aborted = false;
1201
+ const cleanup = () => signal.removeEventListener("abort", abort);
1202
+ const abort = () => {
1203
+ aborted = true;
1204
+ cleanup();
1205
+ reject(abortReason(signal));
1206
+ };
1207
+ signal.addEventListener("abort", abort, { once: true });
1208
+ if (signal.aborted) {
1209
+ abort();
1210
+ return;
1211
+ }
1212
+ Promise.resolve(value).then(
1213
+ (result) => {
1214
+ cleanup();
1215
+ if (aborted) {
1216
+ if (onLateValue) onLateValue(result);
1217
+ return;
1218
+ }
1219
+ resolve(result);
1220
+ },
1221
+ (error) => {
1222
+ cleanup();
1223
+ reject(error);
1224
+ }
1225
+ );
1226
+ });
1227
+ }
1228
+ function setTimer(callback, delay) {
1229
+ return setTimeout(callback, delay);
1230
+ }
1231
+ function clearTimer(timer) {
1232
+ if (timer !== void 0) clearTimeout(timer);
1233
+ }
1234
+ function throwIfAborted2(signal) {
1235
+ if (signal && signal.aborted) throw abortReason(signal);
1236
+ }
1237
+ function abortReason(signal) {
1238
+ if (signal && signal.reason instanceof Error) return signal.reason;
1239
+ const error = new Error("The operation was aborted");
1240
+ error.name = "AbortError";
1241
+ return error;
1242
+ }
1243
+ function readDelay(value, fallback, name) {
1244
+ const number = value === void 0 ? fallback : value;
1245
+ if (!Number.isFinite(number) || number < 0) throw new RangeError(`${name} must be a non-negative number`);
1246
+ return Math.round(number);
1247
+ }
1248
+ function readOptionalDelay(value, name) {
1249
+ if (value === void 0 || value === null || value === 0) return 0;
1250
+ return readDelay(value, 0, name);
1251
+ }
1252
+ function clampDelay(value, max) {
1253
+ if (!Number.isFinite(value) || value < 0) throw new RangeError("Retry delay must be a non-negative number");
1254
+ return Math.min(Math.round(value), max);
1255
+ }
1256
+ function monotonicNow() {
1257
+ return typeof performance === "object" && performance && typeof performance.now === "function" ? performance.now() : Date.now();
1258
+ }
1259
+ function cancelResponseBody(response) {
1260
+ if (!response || !response.body || typeof response.body.cancel !== "function") return;
1261
+ try {
1262
+ const cancellation = response.body.cancel();
1263
+ if (cancellation && typeof cancellation.catch === "function") cancellation.catch(() => {
1264
+ });
1265
+ } catch (e) {
1266
+ }
1267
+ }
1268
+ async function callHook(hook, args, name) {
1269
+ try {
1270
+ return await hook(...args);
1271
+ } catch (cause) {
1272
+ throw new SSEError(`SSE ${name} callback failed`, "ERR_SSE_CALLBACK", { cause });
1273
+ }
1274
+ }
1275
+ var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
1276
+ "ECONNREFUSED",
1277
+ "ECONNRESET",
1278
+ "EHOSTUNREACH",
1279
+ "ENETDOWN",
1280
+ "ENETUNREACH",
1281
+ "ENOTFOUND",
1282
+ "ETIMEDOUT"
1283
+ ]);
1284
+
1285
+ // src/server.js
1286
+ function createSSEChannel(options = {}) {
1287
+ var _a;
1288
+ if (typeof ReadableStream !== "function") throw new Error("ReadableStream is not available in this runtime");
1289
+ if (typeof TextEncoder !== "function") throw new Error("TextEncoder is not available in this runtime");
1290
+ const encoder = new TextEncoder();
1291
+ const highWaterMark = readHighWaterMark(options.highWaterMark);
1292
+ const heartbeatInterval = readHeartbeatInterval(options.heartbeatInterval);
1293
+ const heartbeat = heartbeatInterval ? encoder.encode(encodeSSE({ comment: (_a = options.heartbeatComment) != null ? _a : "keep-alive" }, options)) : void 0;
1294
+ let controller;
1295
+ let open = true;
1296
+ let readyPromise = Promise.resolve();
1297
+ let resolveReady;
1298
+ let resolveClosed;
1299
+ let heartbeatTimer;
1300
+ let responseClaimed = false;
1301
+ const closed = new Promise((resolve) => {
1302
+ resolveClosed = resolve;
1303
+ });
1304
+ const stream = new ReadableStream({
1305
+ start(value) {
1306
+ controller = value;
1307
+ if (heartbeatInterval) {
1308
+ heartbeatTimer = setInterval(() => {
1309
+ if (open && controller.desiredSize > 0) controller.enqueue(heartbeat);
1310
+ }, heartbeatInterval);
1311
+ if (heartbeatTimer && typeof heartbeatTimer.unref === "function") heartbeatTimer.unref();
1312
+ }
1313
+ },
1314
+ pull() {
1315
+ if (resolveReady) {
1316
+ resolveReady();
1317
+ resolveReady = void 0;
1318
+ readyPromise = Promise.resolve();
1319
+ }
1320
+ },
1321
+ cancel(reason) {
1322
+ finish(reason);
1323
+ }
1324
+ }, {
1325
+ highWaterMark,
1326
+ size(chunk) {
1327
+ return chunk.byteLength;
1328
+ }
1329
+ });
1330
+ function send(message) {
1331
+ assertOpen();
1332
+ controller.enqueue(encoder.encode(encodeSSE(message, options)));
1333
+ updateReady();
1334
+ return controller.desiredSize > 0;
1335
+ }
1336
+ function sendJSON(data, fields = {}) {
1337
+ assertOpen();
1338
+ controller.enqueue(encoder.encode(encodeJSON(data, fields, options)));
1339
+ updateReady();
1340
+ return controller.desiredSize > 0;
1341
+ }
1342
+ function comment(value) {
1343
+ return send({ comment: value });
1344
+ }
1345
+ function close() {
1346
+ if (!open) return;
1347
+ open = false;
1348
+ clearInterval(heartbeatTimer);
1349
+ if (resolveReady) resolveReady();
1350
+ controller.close();
1351
+ resolveClosed();
1352
+ }
1353
+ function error(reason) {
1354
+ if (!open) return;
1355
+ open = false;
1356
+ clearInterval(heartbeatTimer);
1357
+ if (resolveReady) resolveReady();
1358
+ controller.error(reason);
1359
+ resolveClosed(reason);
1360
+ }
1361
+ function toResponse(init = {}) {
1362
+ if (typeof Response !== "function") throw new Error("Response is not available in this runtime");
1363
+ if (responseClaimed) throw new Error("SSE channel response has already been created");
1364
+ responseClaimed = true;
1365
+ return new Response(stream, {
1366
+ ...init,
1367
+ headers: responseHeaders(init.headers)
1368
+ });
1369
+ }
1370
+ function updateReady() {
1371
+ if (controller.desiredSize > 0 || resolveReady) return;
1372
+ readyPromise = new Promise((resolve) => {
1373
+ resolveReady = resolve;
1374
+ });
1375
+ }
1376
+ function finish(reason) {
1377
+ if (!open) return;
1378
+ open = false;
1379
+ clearInterval(heartbeatTimer);
1380
+ if (resolveReady) resolveReady();
1381
+ resolveClosed(reason);
1382
+ }
1383
+ function assertOpen() {
1384
+ if (!open) throw new Error("SSE channel is closed");
1385
+ }
1386
+ return {
1387
+ stream,
1388
+ send,
1389
+ sendJSON,
1390
+ comment,
1391
+ close,
1392
+ error,
1393
+ toResponse,
1394
+ closed,
1395
+ get open() {
1396
+ return open;
1397
+ },
1398
+ get ready() {
1399
+ return readyPromise;
1400
+ }
1401
+ };
1402
+ }
1403
+ function eventStreamResponse(source, options = {}) {
1404
+ if (typeof ReadableStream !== "function") throw new Error("ReadableStream is not available in this runtime");
1405
+ if (typeof Response !== "function") throw new Error("Response is not available in this runtime");
1406
+ if (typeof TextEncoder !== "function") throw new Error("TextEncoder is not available in this runtime");
1407
+ const encoder = new TextEncoder();
1408
+ const iterator = toIterator(source);
1409
+ const stream = new ReadableStream({
1410
+ async pull(controller) {
1411
+ try {
1412
+ const item = await iterator.next();
1413
+ if (item.done) {
1414
+ controller.close();
1415
+ return;
1416
+ }
1417
+ const output = options.json === true ? encodeJSON(item.value.data, item.value, options) : encodeSSE(item.value, options);
1418
+ controller.enqueue(encoder.encode(output));
1419
+ } catch (error) {
1420
+ if (typeof iterator.return === "function") {
1421
+ try {
1422
+ await iterator.return(error);
1423
+ } catch (e) {
1424
+ }
1425
+ }
1426
+ controller.error(error);
1427
+ }
1428
+ },
1429
+ async cancel(reason) {
1430
+ if (typeof iterator.return === "function") await iterator.return(reason);
1431
+ }
1432
+ }, {
1433
+ highWaterMark: readHighWaterMark(options.highWaterMark),
1434
+ size(chunk) {
1435
+ return chunk.byteLength;
1436
+ }
1437
+ });
1438
+ return new Response(stream, {
1439
+ status: options.status || 200,
1440
+ statusText: options.statusText,
1441
+ headers: responseHeaders(options.headers)
1442
+ });
1443
+ }
1444
+ function responseHeaders(input) {
1445
+ const headers = typeof Headers === "function" ? new Headers(input) : /* @__PURE__ */ new Map();
1446
+ const has = (name) => typeof headers.has === "function" && headers.has(name);
1447
+ const set = (name, value) => {
1448
+ if (typeof headers.set === "function") headers.set(name, value);
1449
+ };
1450
+ if (!has("Content-Type")) set("Content-Type", `${EVENT_STREAM_CONTENT_TYPE}; charset=utf-8`);
1451
+ if (!has("Cache-Control")) set("Cache-Control", "no-cache, no-transform");
1452
+ if (!has("X-Accel-Buffering")) set("X-Accel-Buffering", "no");
1453
+ return headers;
1454
+ }
1455
+ function toIterator(source) {
1456
+ if (!source) throw new TypeError("An event iterable is required");
1457
+ if (typeof source[Symbol.asyncIterator] === "function") return source[Symbol.asyncIterator]();
1458
+ if (typeof source[Symbol.iterator] === "function") {
1459
+ const iterator = source[Symbol.iterator]();
1460
+ return {
1461
+ next() {
1462
+ return Promise.resolve(iterator.next());
1463
+ },
1464
+ return(value) {
1465
+ return Promise.resolve(typeof iterator.return === "function" ? iterator.return(value) : { done: true, value });
1466
+ }
1467
+ };
1468
+ }
1469
+ throw new TypeError("Events must be an Iterable or AsyncIterable");
1470
+ }
1471
+ function readHighWaterMark(value) {
1472
+ const number = value === void 0 ? 64 * 1024 : value;
1473
+ if (!Number.isFinite(number) || number < 1) throw new RangeError("highWaterMark must be a positive number");
1474
+ return number;
1475
+ }
1476
+ function readHeartbeatInterval(value) {
1477
+ if (value === void 0 || value === null || value === 0) return 0;
1478
+ if (!Number.isFinite(value) || value < 0) {
1479
+ throw new RangeError("heartbeatInterval must be a non-negative number");
1480
+ }
1481
+ return Math.round(value);
1482
+ }
1483
+
1484
+ // src/index.js
1485
+ var api = Object.freeze({
1486
+ EVENT_STREAM_CONTENT_TYPE,
1487
+ EventStreamContentType,
1488
+ SSEEncodeError,
1489
+ SSEError,
1490
+ SSEHTTPError,
1491
+ SSEParseError,
1492
+ SSEReplayError,
1493
+ SSERetryError,
1494
+ SSETimeoutError,
1495
+ consumeSSE,
1496
+ createDecoderStream,
1497
+ createEncoderStream,
1498
+ createParser,
1499
+ createSSEChannel,
1500
+ decodeJSON,
1501
+ decodeSSE,
1502
+ encode,
1503
+ encodeComment,
1504
+ encodeJSON,
1505
+ encodeSSE,
1506
+ eventStreamResponse,
1507
+ fetchEventSource,
1508
+ fetchSSE,
1509
+ toAsyncIterable
1510
+ });
1511
+ var index_default = api;
1512
+ export {
1513
+ EVENT_STREAM_CONTENT_TYPE,
1514
+ EventStreamContentType,
1515
+ SSEEncodeError,
1516
+ SSEError,
1517
+ SSEHTTPError,
1518
+ SSEParseError,
1519
+ SSEReplayError,
1520
+ SSERetryError,
1521
+ SSETimeoutError,
1522
+ consumeSSE,
1523
+ createDecoderStream,
1524
+ createEncoderStream,
1525
+ createParser,
1526
+ createSSEChannel,
1527
+ decodeJSON,
1528
+ decodeSSE,
1529
+ index_default as default,
1530
+ encode,
1531
+ encodeComment,
1532
+ encodeJSON,
1533
+ encodeSSE,
1534
+ eventStreamResponse,
1535
+ fetchEventSource,
1536
+ fetchSSE,
1537
+ toAsyncIterable
1538
+ };
1539
+ //# sourceMappingURL=index.js.map