eventsource-client
Advanced tools
@@ -89,3 +89,3 @@ import { createParser } from "eventsource-parser"; | ||
| const { done, value } = await reader.read(); | ||
| value && parser.feed(decoder.decode(value, { stream: !done })), done && (open = !1, request = null, parser.reset(), onDisconnect(), scheduleReconnect()); | ||
| value && parser.feed(decoder.decode(value, { stream: !done })), done && (open = !1, request = null, parser.reset(), scheduleReconnect(), onDisconnect()); | ||
| } while (open); | ||
@@ -92,0 +92,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"default.esm.js","sources":["../src/constants.ts","../src/client.ts","../src/default.ts"],"sourcesContent":["// ReadyStates, mirrors WhatWG spec, but uses strings instead of numbers.\n// Why make it harder to read than it needs to be?\n\n/**\n * ReadyState representing a connection that is connecting or has been scheduled to reconnect.\n * @public\n */\nexport const CONNECTING = 'connecting'\n\n/**\n * ReadyState representing a connection that is open, eg connected.\n * @public\n */\nexport const OPEN = 'open'\n\n/**\n * ReadyState representing a connection that has been closed (manually, or due to an error).\n * @public\n */\nexport const CLOSED = 'closed'\n","import {createParser, type ParseEvent} from 'eventsource-parser'\n\nimport type {EnvAbstractions, EventSourceAsyncValueResolver} from './abstractions.js'\nimport {CLOSED, CONNECTING, OPEN} from './constants.js'\nimport type {\n EventSourceClient,\n EventSourceMessage,\n EventSourceOptions,\n FetchLike,\n FetchLikeInit,\n FetchLikeResponse,\n ReadyState,\n} from './types.js'\n\n/**\n * Intentional noop function for eased control flow\n */\nconst noop = () => {\n /* intentional noop */\n}\n\n/**\n * Creates a new EventSource client. Used internally by the environment-specific entry points,\n * and should not be used directly by consumers.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @param abstractions - Abstractions for the environments.\n * @returns A new EventSource client instance\n * @internal\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | string | URL,\n {getStream}: EnvAbstractions,\n): EventSourceClient {\n const options =\n typeof optionsOrUrl === 'string' || optionsOrUrl instanceof URL\n ? {url: optionsOrUrl}\n : optionsOrUrl\n const {onMessage, onConnect = noop, onDisconnect = noop, onScheduleReconnect = noop} = options\n const {fetch, url, initialLastEventId} = validate(options)\n const requestHeaders = {...options.headers} // Prevent post-creation mutations to headers\n\n const onCloseSubscribers: (() => void)[] = []\n const subscribers: ((event: EventSourceMessage) => void)[] = onMessage ? [onMessage] : []\n const emit = (event: EventSourceMessage) => subscribers.forEach((fn) => fn(event))\n const parser = createParser(onParsedMessage)\n\n // Client state\n let request: Promise<unknown> | null\n let currentUrl = url.toString()\n let controller = new AbortController()\n let lastEventId = initialLastEventId\n let reconnectMs = 2000\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let readyState: ReadyState = CLOSED\n\n // Let's go!\n connect()\n\n return {\n close,\n connect,\n [Symbol.iterator]: () => {\n throw new Error(\n 'EventSource does not support synchronous iteration. Use `for await` instead.',\n )\n },\n [Symbol.asyncIterator]: getEventIterator,\n get lastEventId() {\n return lastEventId\n },\n get url() {\n return currentUrl\n },\n get readyState() {\n return readyState\n },\n }\n\n function connect() {\n if (request) {\n return\n }\n\n readyState = CONNECTING\n controller = new AbortController()\n request = fetch(url, getRequestOptions())\n .then(onFetchResponse)\n .catch((err: Error & {type: string}) => {\n request = null\n\n // We expect abort errors when the user manually calls `close()` - ignore those\n if (err.name === 'AbortError' || err.type === 'aborted') {\n return\n }\n\n scheduleReconnect()\n })\n }\n\n function close() {\n readyState = CLOSED\n controller.abort()\n parser.reset()\n clearTimeout(reconnectTimer)\n onCloseSubscribers.forEach((fn) => fn())\n }\n\n function getEventIterator(): AsyncGenerator<EventSourceMessage, void> {\n const pullQueue: EventSourceAsyncValueResolver[] = []\n const pushQueue: EventSourceMessage[] = []\n\n function pullValue() {\n return new Promise<IteratorResult<EventSourceMessage, void>>((resolve) => {\n const value = pushQueue.shift()\n if (value) {\n resolve({value, done: false})\n } else {\n pullQueue.push(resolve)\n }\n })\n }\n\n const pushValue = function (value: EventSourceMessage) {\n const resolve = pullQueue.shift()\n if (resolve) {\n resolve({value, done: false})\n } else {\n pushQueue.push(value)\n }\n }\n\n function unsubscribe() {\n subscribers.splice(subscribers.indexOf(pushValue), 1)\n while (pullQueue.shift()) {}\n while (pushQueue.shift()) {}\n }\n\n function onClose() {\n const resolve = pullQueue.shift()\n if (!resolve) {\n return\n }\n\n resolve({done: true, value: undefined})\n unsubscribe()\n }\n\n onCloseSubscribers.push(onClose)\n subscribers.push(pushValue)\n\n return {\n next() {\n return readyState === CLOSED ? this.return() : pullValue()\n },\n return() {\n unsubscribe()\n return Promise.resolve({done: true, value: undefined})\n },\n throw(error) {\n unsubscribe()\n return Promise.reject(error)\n },\n [Symbol.asyncIterator]() {\n return this\n },\n }\n }\n\n function scheduleReconnect() {\n onScheduleReconnect({delay: reconnectMs})\n readyState = CONNECTING\n reconnectTimer = setTimeout(connect, reconnectMs)\n }\n\n async function onFetchResponse(response: FetchLikeResponse) {\n onConnect()\n parser.reset()\n\n const {body, redirected, status} = response\n\n // HTTP 204 means \"close the connection, no more data will be sent\"\n if (status === 204) {\n onDisconnect()\n close()\n return\n }\n\n if (!body) {\n throw new Error('Missing response body')\n }\n\n if (redirected) {\n currentUrl = response.url\n }\n\n // Ensure that the response stream is a web stream\n // @todo Figure out a way to make this work without casting\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = getStream(body as any)\n const decoder = new TextDecoder()\n\n const reader = stream.getReader()\n let open = true\n\n readyState = OPEN\n\n do {\n const {done, value} = await reader.read()\n if (value) {\n parser.feed(decoder.decode(value, {stream: !done}))\n }\n\n if (!done) {\n continue\n }\n\n open = false\n request = null\n parser.reset()\n\n onDisconnect()\n\n // EventSources never close unless explicitly handled with `.close()`:\n // Implementors should send an `done`/`complete`/`disconnect` event and\n // explicitly handle it in client code, or send an HTTP 204.\n scheduleReconnect()\n } while (open)\n }\n\n function onParsedMessage(msg: ParseEvent): void {\n if (msg.type === 'reconnect-interval') {\n reconnectMs = msg.value\n return\n }\n\n if (typeof msg.id === 'string') {\n lastEventId = msg.id\n }\n\n emit({\n id: msg.id,\n data: msg.data,\n event: msg.event,\n })\n }\n\n function getRequestOptions(): FetchLikeInit {\n // @todo allow interception of options, but don't allow overriding signal\n const {mode, credentials, body, method, redirect, referrer, referrerPolicy} = options\n const lastEvent = lastEventId ? {'Last-Event-ID': lastEventId} : undefined\n const headers = {Accept: 'text/event-stream', ...requestHeaders, ...lastEvent}\n return {\n mode,\n credentials,\n body,\n method,\n redirect,\n referrer,\n referrerPolicy,\n headers,\n cache: 'no-store',\n signal: controller.signal,\n }\n }\n}\n\nfunction validate(options: EventSourceOptions): {\n fetch: FetchLike\n url: string | URL\n initialLastEventId: string | undefined\n} {\n const fetch = options.fetch || globalThis.fetch\n if (!isFetchLike(fetch)) {\n throw new Error('No fetch implementation provided, and one was not found on the global object.')\n }\n\n if (typeof AbortController !== 'function') {\n throw new Error('Missing AbortController implementation')\n }\n\n const {url, initialLastEventId} = options\n\n if (typeof url !== 'string' && !(url instanceof URL)) {\n throw new Error('Invalid URL provided - must be string or URL instance')\n }\n\n if (typeof initialLastEventId !== 'string' && initialLastEventId !== undefined) {\n throw new Error('Invalid initialLastEventId provided - must be string or undefined')\n }\n\n return {fetch, url, initialLastEventId}\n}\n\n// This is obviously naive, but hard to probe for full compatibility\nfunction isFetchLike(fetch: FetchLike | typeof globalThis.fetch): fetch is FetchLike {\n return typeof fetch === 'function'\n}\n","import type {EnvAbstractions} from './abstractions.js'\nimport {createEventSource as createSource} from './client.js'\nimport type {EventSourceClient, EventSourceOptions} from './types.js'\n\nexport * from './constants.js'\nexport * from './types.js'\n\n/**\n * Default \"abstractions\", eg when all the APIs are globally available\n */\nconst defaultAbstractions: EnvAbstractions = {\n getStream,\n}\n\n/**\n * Creates a new EventSource client.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @returns A new EventSource client instance\n * @public\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | URL | string,\n): EventSourceClient {\n return createSource(optionsOrUrl, defaultAbstractions)\n}\n\n/**\n * Returns a ReadableStream (Web Stream) from either an existing ReadableStream.\n * Only defined because of environment abstractions - is actually a 1:1 (passthrough).\n *\n * @param body - The body to convert\n * @returns A ReadableStream\n * @private\n */\nfunction getStream(\n body: NodeJS.ReadableStream | ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n if (!(body instanceof ReadableStream)) {\n throw new Error('Invalid stream, expected a web ReadableStream')\n }\n\n return body\n}\n"],"names":["createEventSource","getStream","createSource"],"mappings":";AAOO,MAAM,aAAa,cAMb,OAAO,QAMP,SAAS,UCFhB,OAAO,MAAM;AAEnB;AAWO,SAASA,oBACd,cACA,EAAC,WAAAC,cACkB;AACb,QAAA,UACJ,OAAO,gBAAiB,YAAY,wBAAwB,MACxD,EAAC,KAAK,iBACN,cACA,EAAC,WAAW,YAAY,MAAM,eAAe,MAAM,sBAAsB,KAAQ,IAAA,SACjF,EAAC,OAAO,KAAK,mBAAA,IAAsB,SAAS,OAAO,GACnD,iBAAiB,EAAC,GAAG,QAAQ,WAE7B,qBAAqC,CAAC,GACtC,cAAuD,YAAY,CAAC,SAAS,IAAI,CACjF,GAAA,OAAO,CAAC,UAA8B,YAAY,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC,GAC3E,SAAS,aAAa,eAAe;AAG3C,MAAI,SACA,aAAa,IAAI,YACjB,aAAa,IAAI,gBAAgB,GACjC,cAAc,oBACd,cAAc,KACd,gBACA,aAAyB;AAG7B,SAAA,QAAA,GAEO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC,OAAO,QAAQ,GAAG,MAAM;AACvB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,IACA,CAAC,OAAO,aAAa,GAAG;AAAA,IACxB,IAAI,cAAc;AACT,aAAA;AAAA,IACT;AAAA,IACA,IAAI,MAAM;AACD,aAAA;AAAA,IACT;AAAA,IACA,IAAI,aAAa;AACR,aAAA;AAAA,IACT;AAAA,EAAA;AAGF,WAAS,UAAU;AACb,gBAIJ,aAAa,YACb,aAAa,IAAI,mBACjB,UAAU,MAAM,KAAK,kBAAmB,CAAA,EACrC,KAAK,eAAe,EACpB,MAAM,CAAC,QAAgC;AACtC,gBAAU,MAGN,EAAI,IAAA,SAAS,gBAAgB,IAAI,SAAS,cAI9C;IACD,CAAA;AAAA,EACL;AAEA,WAAS,QAAQ;AACf,iBAAa,QACb,WAAW,MAAM,GACjB,OAAO,MAAM,GACb,aAAa,cAAc,GAC3B,mBAAmB,QAAQ,CAAC,OAAO,IAAI;AAAA,EACzC;AAEA,WAAS,mBAA6D;AACpE,UAAM,YAA6C,CAAA,GAC7C,YAAkC;AAExC,aAAS,YAAY;AACZ,aAAA,IAAI,QAAkD,CAAC,YAAY;AAClE,cAAA,QAAQ,UAAU;AACpB,gBACF,QAAQ,EAAC,OAAO,MAAM,GAAM,CAAA,IAE5B,UAAU,KAAK,OAAO;AAAA,MAAA,CAEzB;AAAA,IACH;AAEM,UAAA,YAAY,SAAU,OAA2B;AAC/C,YAAA,UAAU,UAAU;AACtB,gBACF,QAAQ,EAAC,OAAO,MAAM,GAAM,CAAA,IAE5B,UAAU,KAAK,KAAK;AAAA,IAAA;AAIxB,aAAS,cAAc;AACrB,WAAA,YAAY,OAAO,YAAY,QAAQ,SAAS,GAAG,CAAC,GAC7C,UAAU,MAAM;AAAG;AAC1B,aAAO,UAAU,MAAM;AAAG;AAAA,IAC5B;AAEA,aAAS,UAAU;AACX,YAAA,UAAU,UAAU;AACrB,kBAIL,QAAQ,EAAC,MAAM,IAAM,OAAO,OAAU,CAAA,GACtC,YAAY;AAAA,IACd;AAEA,WAAA,mBAAmB,KAAK,OAAO,GAC/B,YAAY,KAAK,SAAS,GAEnB;AAAA,MACL,OAAO;AACL,eAAO,eAAe,SAAS,KAAK,WAAW,UAAU;AAAA,MAC3D;AAAA,MACA,SAAS;AACK,eAAA,YAAA,GACL,QAAQ,QAAQ,EAAC,MAAM,IAAM,OAAO,QAAU;AAAA,MACvD;AAAA,MACA,MAAM,OAAO;AACC,eAAA,YAAA,GACL,QAAQ,OAAO,KAAK;AAAA,MAC7B;AAAA,MACA,CAAC,OAAO,aAAa,IAAI;AAChB,eAAA;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAEA,WAAS,oBAAoB;AACP,wBAAA,EAAC,OAAO,YAAA,CAAY,GACxC,aAAa,YACb,iBAAiB,WAAW,SAAS,WAAW;AAAA,EAClD;AAEA,iBAAe,gBAAgB,UAA6B;AAChD,cAAA,GACV,OAAO;AAEP,UAAM,EAAC,MAAM,YAAY,OAAA,IAAU;AAGnC,QAAI,WAAW,KAAK;AAClB,mBAAA,GACA;AACA;AAAA,IACF;AAEA,QAAI,CAAC;AACG,YAAA,IAAI,MAAM,uBAAuB;AAGrC,mBACF,aAAa,SAAS;AAMlB,UAAA,SAASA,WAAU,IAAW,GAC9B,UAAU,IAAI,YAAY,GAE1B,SAAS,OAAO;AACtB,QAAI,OAAO;AAEE,iBAAA;AAEV,OAAA;AACD,YAAM,EAAC,MAAM,MAAA,IAAS,MAAM,OAAO,KAAK;AACpC,eACF,OAAO,KAAK,QAAQ,OAAO,OAAO,EAAC,QAAQ,CAAC,KAAI,CAAC,CAAC,GAG/C,SAIL,OAAO,IACP,UAAU,MACV,OAAO,MAAM,GAEb,aAAa,GAKb,kBAAkB;AAAA,IACX,SAAA;AAAA,EACX;AAEA,WAAS,gBAAgB,KAAuB;AAC1C,QAAA,IAAI,SAAS,sBAAsB;AACrC,oBAAc,IAAI;AAClB;AAAA,IACF;AAEI,WAAO,IAAI,MAAO,aACpB,cAAc,IAAI,KAGpB,KAAK;AAAA,MACH,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,IAAA,CACZ;AAAA,EACH;AAEA,WAAS,oBAAmC;AAEpC,UAAA,EAAC,MAAM,aAAa,MAAM,QAAQ,UAAU,UAAU,eAAc,IAAI,SAExE,UAAU,EAAC,QAAQ,qBAAqB,GAAG,gBAAgB,GAD/C,cAAc,EAAC,iBAAiB,YAAW,IAAI;AAE1D,WAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,QAAQ,WAAW;AAAA,IAAA;AAAA,EAEvB;AACF;AAEA,SAAS,SAAS,SAIhB;AACM,QAAA,QAAQ,QAAQ,SAAS,WAAW;AACtC,MAAA,CAAC,YAAY,KAAK;AACd,UAAA,IAAI,MAAM,+EAA+E;AAGjG,MAAI,OAAO,mBAAoB;AACvB,UAAA,IAAI,MAAM,wCAAwC;AAGpD,QAAA,EAAC,KAAK,mBAAsB,IAAA;AAElC,MAAI,OAAO,OAAQ,YAAY,EAAE,eAAe;AACxC,UAAA,IAAI,MAAM,uDAAuD;AAGrE,MAAA,OAAO,sBAAuB,YAAY,uBAAuB;AAC7D,UAAA,IAAI,MAAM,mEAAmE;AAG9E,SAAA,EAAC,OAAO,KAAK;AACtB;AAGA,SAAS,YAAY,OAAgE;AACnF,SAAO,OAAO,SAAU;AAC1B;AC/RA,MAAM,sBAAuC;AAAA,EAC3C;AACF;AASO,SAAS,kBACd,cACmB;AACZ,SAAAC,oBAAa,cAAc,mBAAmB;AACvD;AAUA,SAAS,UACP,MAC4B;AAC5B,MAAI,EAAE,gBAAgB;AACd,UAAA,IAAI,MAAM,+CAA+C;AAG1D,SAAA;AACT;"} | ||
| {"version":3,"file":"default.esm.js","sources":["../src/constants.ts","../src/client.ts","../src/default.ts"],"sourcesContent":["// ReadyStates, mirrors WhatWG spec, but uses strings instead of numbers.\n// Why make it harder to read than it needs to be?\n\n/**\n * ReadyState representing a connection that is connecting or has been scheduled to reconnect.\n * @public\n */\nexport const CONNECTING = 'connecting'\n\n/**\n * ReadyState representing a connection that is open, eg connected.\n * @public\n */\nexport const OPEN = 'open'\n\n/**\n * ReadyState representing a connection that has been closed (manually, or due to an error).\n * @public\n */\nexport const CLOSED = 'closed'\n","import {createParser, type ParseEvent} from 'eventsource-parser'\n\nimport type {EnvAbstractions, EventSourceAsyncValueResolver} from './abstractions.js'\nimport {CLOSED, CONNECTING, OPEN} from './constants.js'\nimport type {\n EventSourceClient,\n EventSourceMessage,\n EventSourceOptions,\n FetchLike,\n FetchLikeInit,\n FetchLikeResponse,\n ReadyState,\n} from './types.js'\n\n/**\n * Intentional noop function for eased control flow\n */\nconst noop = () => {\n /* intentional noop */\n}\n\n/**\n * Creates a new EventSource client. Used internally by the environment-specific entry points,\n * and should not be used directly by consumers.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @param abstractions - Abstractions for the environments.\n * @returns A new EventSource client instance\n * @internal\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | string | URL,\n {getStream}: EnvAbstractions,\n): EventSourceClient {\n const options =\n typeof optionsOrUrl === 'string' || optionsOrUrl instanceof URL\n ? {url: optionsOrUrl}\n : optionsOrUrl\n const {onMessage, onConnect = noop, onDisconnect = noop, onScheduleReconnect = noop} = options\n const {fetch, url, initialLastEventId} = validate(options)\n const requestHeaders = {...options.headers} // Prevent post-creation mutations to headers\n\n const onCloseSubscribers: (() => void)[] = []\n const subscribers: ((event: EventSourceMessage) => void)[] = onMessage ? [onMessage] : []\n const emit = (event: EventSourceMessage) => subscribers.forEach((fn) => fn(event))\n const parser = createParser(onParsedMessage)\n\n // Client state\n let request: Promise<unknown> | null\n let currentUrl = url.toString()\n let controller = new AbortController()\n let lastEventId = initialLastEventId\n let reconnectMs = 2000\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let readyState: ReadyState = CLOSED\n\n // Let's go!\n connect()\n\n return {\n close,\n connect,\n [Symbol.iterator]: () => {\n throw new Error(\n 'EventSource does not support synchronous iteration. Use `for await` instead.',\n )\n },\n [Symbol.asyncIterator]: getEventIterator,\n get lastEventId() {\n return lastEventId\n },\n get url() {\n return currentUrl\n },\n get readyState() {\n return readyState\n },\n }\n\n function connect() {\n if (request) {\n return\n }\n\n readyState = CONNECTING\n controller = new AbortController()\n request = fetch(url, getRequestOptions())\n .then(onFetchResponse)\n .catch((err: Error & {type: string}) => {\n request = null\n\n // We expect abort errors when the user manually calls `close()` - ignore those\n if (err.name === 'AbortError' || err.type === 'aborted') {\n return\n }\n\n scheduleReconnect()\n })\n }\n\n function close() {\n readyState = CLOSED\n controller.abort()\n parser.reset()\n clearTimeout(reconnectTimer)\n onCloseSubscribers.forEach((fn) => fn())\n }\n\n function getEventIterator(): AsyncGenerator<EventSourceMessage, void> {\n const pullQueue: EventSourceAsyncValueResolver[] = []\n const pushQueue: EventSourceMessage[] = []\n\n function pullValue() {\n return new Promise<IteratorResult<EventSourceMessage, void>>((resolve) => {\n const value = pushQueue.shift()\n if (value) {\n resolve({value, done: false})\n } else {\n pullQueue.push(resolve)\n }\n })\n }\n\n const pushValue = function (value: EventSourceMessage) {\n const resolve = pullQueue.shift()\n if (resolve) {\n resolve({value, done: false})\n } else {\n pushQueue.push(value)\n }\n }\n\n function unsubscribe() {\n subscribers.splice(subscribers.indexOf(pushValue), 1)\n while (pullQueue.shift()) {}\n while (pushQueue.shift()) {}\n }\n\n function onClose() {\n const resolve = pullQueue.shift()\n if (!resolve) {\n return\n }\n\n resolve({done: true, value: undefined})\n unsubscribe()\n }\n\n onCloseSubscribers.push(onClose)\n subscribers.push(pushValue)\n\n return {\n next() {\n return readyState === CLOSED ? this.return() : pullValue()\n },\n return() {\n unsubscribe()\n return Promise.resolve({done: true, value: undefined})\n },\n throw(error) {\n unsubscribe()\n return Promise.reject(error)\n },\n [Symbol.asyncIterator]() {\n return this\n },\n }\n }\n\n function scheduleReconnect() {\n onScheduleReconnect({delay: reconnectMs})\n readyState = CONNECTING\n reconnectTimer = setTimeout(connect, reconnectMs)\n }\n\n async function onFetchResponse(response: FetchLikeResponse) {\n onConnect()\n parser.reset()\n\n const {body, redirected, status} = response\n\n // HTTP 204 means \"close the connection, no more data will be sent\"\n if (status === 204) {\n onDisconnect()\n close()\n return\n }\n\n if (!body) {\n throw new Error('Missing response body')\n }\n\n if (redirected) {\n currentUrl = response.url\n }\n\n // Ensure that the response stream is a web stream\n // @todo Figure out a way to make this work without casting\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = getStream(body as any)\n const decoder = new TextDecoder()\n\n const reader = stream.getReader()\n let open = true\n\n readyState = OPEN\n\n do {\n const {done, value} = await reader.read()\n if (value) {\n parser.feed(decoder.decode(value, {stream: !done}))\n }\n\n if (!done) {\n continue\n }\n\n open = false\n request = null\n parser.reset()\n\n // EventSources never close unless explicitly handled with `.close()`:\n // Implementors should send an `done`/`complete`/`disconnect` event and\n // explicitly handle it in client code, or send an HTTP 204.\n scheduleReconnect()\n\n // Calling scheduleReconnect() prior to onDisconnect() allows consumers to\n // explicitly call .close() before the reconnection is performed.\n onDisconnect()\n } while (open)\n }\n\n function onParsedMessage(msg: ParseEvent): void {\n if (msg.type === 'reconnect-interval') {\n reconnectMs = msg.value\n return\n }\n\n if (typeof msg.id === 'string') {\n lastEventId = msg.id\n }\n\n emit({\n id: msg.id,\n data: msg.data,\n event: msg.event,\n })\n }\n\n function getRequestOptions(): FetchLikeInit {\n // @todo allow interception of options, but don't allow overriding signal\n const {mode, credentials, body, method, redirect, referrer, referrerPolicy} = options\n const lastEvent = lastEventId ? {'Last-Event-ID': lastEventId} : undefined\n const headers = {Accept: 'text/event-stream', ...requestHeaders, ...lastEvent}\n return {\n mode,\n credentials,\n body,\n method,\n redirect,\n referrer,\n referrerPolicy,\n headers,\n cache: 'no-store',\n signal: controller.signal,\n }\n }\n}\n\nfunction validate(options: EventSourceOptions): {\n fetch: FetchLike\n url: string | URL\n initialLastEventId: string | undefined\n} {\n const fetch = options.fetch || globalThis.fetch\n if (!isFetchLike(fetch)) {\n throw new Error('No fetch implementation provided, and one was not found on the global object.')\n }\n\n if (typeof AbortController !== 'function') {\n throw new Error('Missing AbortController implementation')\n }\n\n const {url, initialLastEventId} = options\n\n if (typeof url !== 'string' && !(url instanceof URL)) {\n throw new Error('Invalid URL provided - must be string or URL instance')\n }\n\n if (typeof initialLastEventId !== 'string' && initialLastEventId !== undefined) {\n throw new Error('Invalid initialLastEventId provided - must be string or undefined')\n }\n\n return {fetch, url, initialLastEventId}\n}\n\n// This is obviously naive, but hard to probe for full compatibility\nfunction isFetchLike(fetch: FetchLike | typeof globalThis.fetch): fetch is FetchLike {\n return typeof fetch === 'function'\n}\n","import type {EnvAbstractions} from './abstractions.js'\nimport {createEventSource as createSource} from './client.js'\nimport type {EventSourceClient, EventSourceOptions} from './types.js'\n\nexport * from './constants.js'\nexport * from './types.js'\n\n/**\n * Default \"abstractions\", eg when all the APIs are globally available\n */\nconst defaultAbstractions: EnvAbstractions = {\n getStream,\n}\n\n/**\n * Creates a new EventSource client.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @returns A new EventSource client instance\n * @public\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | URL | string,\n): EventSourceClient {\n return createSource(optionsOrUrl, defaultAbstractions)\n}\n\n/**\n * Returns a ReadableStream (Web Stream) from either an existing ReadableStream.\n * Only defined because of environment abstractions - is actually a 1:1 (passthrough).\n *\n * @param body - The body to convert\n * @returns A ReadableStream\n * @private\n */\nfunction getStream(\n body: NodeJS.ReadableStream | ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n if (!(body instanceof ReadableStream)) {\n throw new Error('Invalid stream, expected a web ReadableStream')\n }\n\n return body\n}\n"],"names":["createEventSource","getStream","createSource"],"mappings":"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"} |
+1
-1
@@ -91,3 +91,3 @@ "use strict"; | ||
| const { done, value } = await reader.read(); | ||
| value && parser.feed(decoder.decode(value, { stream: !done })), done && (open = !1, request = null, parser.reset(), onDisconnect(), scheduleReconnect()); | ||
| value && parser.feed(decoder.decode(value, { stream: !done })), done && (open = !1, request = null, parser.reset(), scheduleReconnect(), onDisconnect()); | ||
| } while (open); | ||
@@ -94,0 +94,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"default.js","sources":["../src/constants.ts","../src/client.ts","../src/default.ts"],"sourcesContent":["// ReadyStates, mirrors WhatWG spec, but uses strings instead of numbers.\n// Why make it harder to read than it needs to be?\n\n/**\n * ReadyState representing a connection that is connecting or has been scheduled to reconnect.\n * @public\n */\nexport const CONNECTING = 'connecting'\n\n/**\n * ReadyState representing a connection that is open, eg connected.\n * @public\n */\nexport const OPEN = 'open'\n\n/**\n * ReadyState representing a connection that has been closed (manually, or due to an error).\n * @public\n */\nexport const CLOSED = 'closed'\n","import {createParser, type ParseEvent} from 'eventsource-parser'\n\nimport type {EnvAbstractions, EventSourceAsyncValueResolver} from './abstractions.js'\nimport {CLOSED, CONNECTING, OPEN} from './constants.js'\nimport type {\n EventSourceClient,\n EventSourceMessage,\n EventSourceOptions,\n FetchLike,\n FetchLikeInit,\n FetchLikeResponse,\n ReadyState,\n} from './types.js'\n\n/**\n * Intentional noop function for eased control flow\n */\nconst noop = () => {\n /* intentional noop */\n}\n\n/**\n * Creates a new EventSource client. Used internally by the environment-specific entry points,\n * and should not be used directly by consumers.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @param abstractions - Abstractions for the environments.\n * @returns A new EventSource client instance\n * @internal\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | string | URL,\n {getStream}: EnvAbstractions,\n): EventSourceClient {\n const options =\n typeof optionsOrUrl === 'string' || optionsOrUrl instanceof URL\n ? {url: optionsOrUrl}\n : optionsOrUrl\n const {onMessage, onConnect = noop, onDisconnect = noop, onScheduleReconnect = noop} = options\n const {fetch, url, initialLastEventId} = validate(options)\n const requestHeaders = {...options.headers} // Prevent post-creation mutations to headers\n\n const onCloseSubscribers: (() => void)[] = []\n const subscribers: ((event: EventSourceMessage) => void)[] = onMessage ? [onMessage] : []\n const emit = (event: EventSourceMessage) => subscribers.forEach((fn) => fn(event))\n const parser = createParser(onParsedMessage)\n\n // Client state\n let request: Promise<unknown> | null\n let currentUrl = url.toString()\n let controller = new AbortController()\n let lastEventId = initialLastEventId\n let reconnectMs = 2000\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let readyState: ReadyState = CLOSED\n\n // Let's go!\n connect()\n\n return {\n close,\n connect,\n [Symbol.iterator]: () => {\n throw new Error(\n 'EventSource does not support synchronous iteration. Use `for await` instead.',\n )\n },\n [Symbol.asyncIterator]: getEventIterator,\n get lastEventId() {\n return lastEventId\n },\n get url() {\n return currentUrl\n },\n get readyState() {\n return readyState\n },\n }\n\n function connect() {\n if (request) {\n return\n }\n\n readyState = CONNECTING\n controller = new AbortController()\n request = fetch(url, getRequestOptions())\n .then(onFetchResponse)\n .catch((err: Error & {type: string}) => {\n request = null\n\n // We expect abort errors when the user manually calls `close()` - ignore those\n if (err.name === 'AbortError' || err.type === 'aborted') {\n return\n }\n\n scheduleReconnect()\n })\n }\n\n function close() {\n readyState = CLOSED\n controller.abort()\n parser.reset()\n clearTimeout(reconnectTimer)\n onCloseSubscribers.forEach((fn) => fn())\n }\n\n function getEventIterator(): AsyncGenerator<EventSourceMessage, void> {\n const pullQueue: EventSourceAsyncValueResolver[] = []\n const pushQueue: EventSourceMessage[] = []\n\n function pullValue() {\n return new Promise<IteratorResult<EventSourceMessage, void>>((resolve) => {\n const value = pushQueue.shift()\n if (value) {\n resolve({value, done: false})\n } else {\n pullQueue.push(resolve)\n }\n })\n }\n\n const pushValue = function (value: EventSourceMessage) {\n const resolve = pullQueue.shift()\n if (resolve) {\n resolve({value, done: false})\n } else {\n pushQueue.push(value)\n }\n }\n\n function unsubscribe() {\n subscribers.splice(subscribers.indexOf(pushValue), 1)\n while (pullQueue.shift()) {}\n while (pushQueue.shift()) {}\n }\n\n function onClose() {\n const resolve = pullQueue.shift()\n if (!resolve) {\n return\n }\n\n resolve({done: true, value: undefined})\n unsubscribe()\n }\n\n onCloseSubscribers.push(onClose)\n subscribers.push(pushValue)\n\n return {\n next() {\n return readyState === CLOSED ? this.return() : pullValue()\n },\n return() {\n unsubscribe()\n return Promise.resolve({done: true, value: undefined})\n },\n throw(error) {\n unsubscribe()\n return Promise.reject(error)\n },\n [Symbol.asyncIterator]() {\n return this\n },\n }\n }\n\n function scheduleReconnect() {\n onScheduleReconnect({delay: reconnectMs})\n readyState = CONNECTING\n reconnectTimer = setTimeout(connect, reconnectMs)\n }\n\n async function onFetchResponse(response: FetchLikeResponse) {\n onConnect()\n parser.reset()\n\n const {body, redirected, status} = response\n\n // HTTP 204 means \"close the connection, no more data will be sent\"\n if (status === 204) {\n onDisconnect()\n close()\n return\n }\n\n if (!body) {\n throw new Error('Missing response body')\n }\n\n if (redirected) {\n currentUrl = response.url\n }\n\n // Ensure that the response stream is a web stream\n // @todo Figure out a way to make this work without casting\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = getStream(body as any)\n const decoder = new TextDecoder()\n\n const reader = stream.getReader()\n let open = true\n\n readyState = OPEN\n\n do {\n const {done, value} = await reader.read()\n if (value) {\n parser.feed(decoder.decode(value, {stream: !done}))\n }\n\n if (!done) {\n continue\n }\n\n open = false\n request = null\n parser.reset()\n\n onDisconnect()\n\n // EventSources never close unless explicitly handled with `.close()`:\n // Implementors should send an `done`/`complete`/`disconnect` event and\n // explicitly handle it in client code, or send an HTTP 204.\n scheduleReconnect()\n } while (open)\n }\n\n function onParsedMessage(msg: ParseEvent): void {\n if (msg.type === 'reconnect-interval') {\n reconnectMs = msg.value\n return\n }\n\n if (typeof msg.id === 'string') {\n lastEventId = msg.id\n }\n\n emit({\n id: msg.id,\n data: msg.data,\n event: msg.event,\n })\n }\n\n function getRequestOptions(): FetchLikeInit {\n // @todo allow interception of options, but don't allow overriding signal\n const {mode, credentials, body, method, redirect, referrer, referrerPolicy} = options\n const lastEvent = lastEventId ? {'Last-Event-ID': lastEventId} : undefined\n const headers = {Accept: 'text/event-stream', ...requestHeaders, ...lastEvent}\n return {\n mode,\n credentials,\n body,\n method,\n redirect,\n referrer,\n referrerPolicy,\n headers,\n cache: 'no-store',\n signal: controller.signal,\n }\n }\n}\n\nfunction validate(options: EventSourceOptions): {\n fetch: FetchLike\n url: string | URL\n initialLastEventId: string | undefined\n} {\n const fetch = options.fetch || globalThis.fetch\n if (!isFetchLike(fetch)) {\n throw new Error('No fetch implementation provided, and one was not found on the global object.')\n }\n\n if (typeof AbortController !== 'function') {\n throw new Error('Missing AbortController implementation')\n }\n\n const {url, initialLastEventId} = options\n\n if (typeof url !== 'string' && !(url instanceof URL)) {\n throw new Error('Invalid URL provided - must be string or URL instance')\n }\n\n if (typeof initialLastEventId !== 'string' && initialLastEventId !== undefined) {\n throw new Error('Invalid initialLastEventId provided - must be string or undefined')\n }\n\n return {fetch, url, initialLastEventId}\n}\n\n// This is obviously naive, but hard to probe for full compatibility\nfunction isFetchLike(fetch: FetchLike | typeof globalThis.fetch): fetch is FetchLike {\n return typeof fetch === 'function'\n}\n","import type {EnvAbstractions} from './abstractions.js'\nimport {createEventSource as createSource} from './client.js'\nimport type {EventSourceClient, EventSourceOptions} from './types.js'\n\nexport * from './constants.js'\nexport * from './types.js'\n\n/**\n * Default \"abstractions\", eg when all the APIs are globally available\n */\nconst defaultAbstractions: EnvAbstractions = {\n getStream,\n}\n\n/**\n * Creates a new EventSource client.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @returns A new EventSource client instance\n * @public\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | URL | string,\n): EventSourceClient {\n return createSource(optionsOrUrl, defaultAbstractions)\n}\n\n/**\n * Returns a ReadableStream (Web Stream) from either an existing ReadableStream.\n * Only defined because of environment abstractions - is actually a 1:1 (passthrough).\n *\n * @param body - The body to convert\n * @returns A ReadableStream\n * @private\n */\nfunction getStream(\n body: NodeJS.ReadableStream | ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n if (!(body instanceof ReadableStream)) {\n throw new Error('Invalid stream, expected a web ReadableStream')\n }\n\n return body\n}\n"],"names":["createEventSource","getStream","createParser","createSource"],"mappings":"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"} | ||
| {"version":3,"file":"default.js","sources":["../src/constants.ts","../src/client.ts","../src/default.ts"],"sourcesContent":["// ReadyStates, mirrors WhatWG spec, but uses strings instead of numbers.\n// Why make it harder to read than it needs to be?\n\n/**\n * ReadyState representing a connection that is connecting or has been scheduled to reconnect.\n * @public\n */\nexport const CONNECTING = 'connecting'\n\n/**\n * ReadyState representing a connection that is open, eg connected.\n * @public\n */\nexport const OPEN = 'open'\n\n/**\n * ReadyState representing a connection that has been closed (manually, or due to an error).\n * @public\n */\nexport const CLOSED = 'closed'\n","import {createParser, type ParseEvent} from 'eventsource-parser'\n\nimport type {EnvAbstractions, EventSourceAsyncValueResolver} from './abstractions.js'\nimport {CLOSED, CONNECTING, OPEN} from './constants.js'\nimport type {\n EventSourceClient,\n EventSourceMessage,\n EventSourceOptions,\n FetchLike,\n FetchLikeInit,\n FetchLikeResponse,\n ReadyState,\n} from './types.js'\n\n/**\n * Intentional noop function for eased control flow\n */\nconst noop = () => {\n /* intentional noop */\n}\n\n/**\n * Creates a new EventSource client. Used internally by the environment-specific entry points,\n * and should not be used directly by consumers.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @param abstractions - Abstractions for the environments.\n * @returns A new EventSource client instance\n * @internal\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | string | URL,\n {getStream}: EnvAbstractions,\n): EventSourceClient {\n const options =\n typeof optionsOrUrl === 'string' || optionsOrUrl instanceof URL\n ? {url: optionsOrUrl}\n : optionsOrUrl\n const {onMessage, onConnect = noop, onDisconnect = noop, onScheduleReconnect = noop} = options\n const {fetch, url, initialLastEventId} = validate(options)\n const requestHeaders = {...options.headers} // Prevent post-creation mutations to headers\n\n const onCloseSubscribers: (() => void)[] = []\n const subscribers: ((event: EventSourceMessage) => void)[] = onMessage ? [onMessage] : []\n const emit = (event: EventSourceMessage) => subscribers.forEach((fn) => fn(event))\n const parser = createParser(onParsedMessage)\n\n // Client state\n let request: Promise<unknown> | null\n let currentUrl = url.toString()\n let controller = new AbortController()\n let lastEventId = initialLastEventId\n let reconnectMs = 2000\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let readyState: ReadyState = CLOSED\n\n // Let's go!\n connect()\n\n return {\n close,\n connect,\n [Symbol.iterator]: () => {\n throw new Error(\n 'EventSource does not support synchronous iteration. Use `for await` instead.',\n )\n },\n [Symbol.asyncIterator]: getEventIterator,\n get lastEventId() {\n return lastEventId\n },\n get url() {\n return currentUrl\n },\n get readyState() {\n return readyState\n },\n }\n\n function connect() {\n if (request) {\n return\n }\n\n readyState = CONNECTING\n controller = new AbortController()\n request = fetch(url, getRequestOptions())\n .then(onFetchResponse)\n .catch((err: Error & {type: string}) => {\n request = null\n\n // We expect abort errors when the user manually calls `close()` - ignore those\n if (err.name === 'AbortError' || err.type === 'aborted') {\n return\n }\n\n scheduleReconnect()\n })\n }\n\n function close() {\n readyState = CLOSED\n controller.abort()\n parser.reset()\n clearTimeout(reconnectTimer)\n onCloseSubscribers.forEach((fn) => fn())\n }\n\n function getEventIterator(): AsyncGenerator<EventSourceMessage, void> {\n const pullQueue: EventSourceAsyncValueResolver[] = []\n const pushQueue: EventSourceMessage[] = []\n\n function pullValue() {\n return new Promise<IteratorResult<EventSourceMessage, void>>((resolve) => {\n const value = pushQueue.shift()\n if (value) {\n resolve({value, done: false})\n } else {\n pullQueue.push(resolve)\n }\n })\n }\n\n const pushValue = function (value: EventSourceMessage) {\n const resolve = pullQueue.shift()\n if (resolve) {\n resolve({value, done: false})\n } else {\n pushQueue.push(value)\n }\n }\n\n function unsubscribe() {\n subscribers.splice(subscribers.indexOf(pushValue), 1)\n while (pullQueue.shift()) {}\n while (pushQueue.shift()) {}\n }\n\n function onClose() {\n const resolve = pullQueue.shift()\n if (!resolve) {\n return\n }\n\n resolve({done: true, value: undefined})\n unsubscribe()\n }\n\n onCloseSubscribers.push(onClose)\n subscribers.push(pushValue)\n\n return {\n next() {\n return readyState === CLOSED ? this.return() : pullValue()\n },\n return() {\n unsubscribe()\n return Promise.resolve({done: true, value: undefined})\n },\n throw(error) {\n unsubscribe()\n return Promise.reject(error)\n },\n [Symbol.asyncIterator]() {\n return this\n },\n }\n }\n\n function scheduleReconnect() {\n onScheduleReconnect({delay: reconnectMs})\n readyState = CONNECTING\n reconnectTimer = setTimeout(connect, reconnectMs)\n }\n\n async function onFetchResponse(response: FetchLikeResponse) {\n onConnect()\n parser.reset()\n\n const {body, redirected, status} = response\n\n // HTTP 204 means \"close the connection, no more data will be sent\"\n if (status === 204) {\n onDisconnect()\n close()\n return\n }\n\n if (!body) {\n throw new Error('Missing response body')\n }\n\n if (redirected) {\n currentUrl = response.url\n }\n\n // Ensure that the response stream is a web stream\n // @todo Figure out a way to make this work without casting\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = getStream(body as any)\n const decoder = new TextDecoder()\n\n const reader = stream.getReader()\n let open = true\n\n readyState = OPEN\n\n do {\n const {done, value} = await reader.read()\n if (value) {\n parser.feed(decoder.decode(value, {stream: !done}))\n }\n\n if (!done) {\n continue\n }\n\n open = false\n request = null\n parser.reset()\n\n // EventSources never close unless explicitly handled with `.close()`:\n // Implementors should send an `done`/`complete`/`disconnect` event and\n // explicitly handle it in client code, or send an HTTP 204.\n scheduleReconnect()\n\n // Calling scheduleReconnect() prior to onDisconnect() allows consumers to\n // explicitly call .close() before the reconnection is performed.\n onDisconnect()\n } while (open)\n }\n\n function onParsedMessage(msg: ParseEvent): void {\n if (msg.type === 'reconnect-interval') {\n reconnectMs = msg.value\n return\n }\n\n if (typeof msg.id === 'string') {\n lastEventId = msg.id\n }\n\n emit({\n id: msg.id,\n data: msg.data,\n event: msg.event,\n })\n }\n\n function getRequestOptions(): FetchLikeInit {\n // @todo allow interception of options, but don't allow overriding signal\n const {mode, credentials, body, method, redirect, referrer, referrerPolicy} = options\n const lastEvent = lastEventId ? {'Last-Event-ID': lastEventId} : undefined\n const headers = {Accept: 'text/event-stream', ...requestHeaders, ...lastEvent}\n return {\n mode,\n credentials,\n body,\n method,\n redirect,\n referrer,\n referrerPolicy,\n headers,\n cache: 'no-store',\n signal: controller.signal,\n }\n }\n}\n\nfunction validate(options: EventSourceOptions): {\n fetch: FetchLike\n url: string | URL\n initialLastEventId: string | undefined\n} {\n const fetch = options.fetch || globalThis.fetch\n if (!isFetchLike(fetch)) {\n throw new Error('No fetch implementation provided, and one was not found on the global object.')\n }\n\n if (typeof AbortController !== 'function') {\n throw new Error('Missing AbortController implementation')\n }\n\n const {url, initialLastEventId} = options\n\n if (typeof url !== 'string' && !(url instanceof URL)) {\n throw new Error('Invalid URL provided - must be string or URL instance')\n }\n\n if (typeof initialLastEventId !== 'string' && initialLastEventId !== undefined) {\n throw new Error('Invalid initialLastEventId provided - must be string or undefined')\n }\n\n return {fetch, url, initialLastEventId}\n}\n\n// This is obviously naive, but hard to probe for full compatibility\nfunction isFetchLike(fetch: FetchLike | typeof globalThis.fetch): fetch is FetchLike {\n return typeof fetch === 'function'\n}\n","import type {EnvAbstractions} from './abstractions.js'\nimport {createEventSource as createSource} from './client.js'\nimport type {EventSourceClient, EventSourceOptions} from './types.js'\n\nexport * from './constants.js'\nexport * from './types.js'\n\n/**\n * Default \"abstractions\", eg when all the APIs are globally available\n */\nconst defaultAbstractions: EnvAbstractions = {\n getStream,\n}\n\n/**\n * Creates a new EventSource client.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @returns A new EventSource client instance\n * @public\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | URL | string,\n): EventSourceClient {\n return createSource(optionsOrUrl, defaultAbstractions)\n}\n\n/**\n * Returns a ReadableStream (Web Stream) from either an existing ReadableStream.\n * Only defined because of environment abstractions - is actually a 1:1 (passthrough).\n *\n * @param body - The body to convert\n * @returns A ReadableStream\n * @private\n */\nfunction getStream(\n body: NodeJS.ReadableStream | ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n if (!(body instanceof ReadableStream)) {\n throw new Error('Invalid stream, expected a web ReadableStream')\n }\n\n return body\n}\n"],"names":["createEventSource","getStream","createParser","createSource"],"mappings":";;;AAOO,MAAM,aAAa,cAMb,OAAO,QAMP,SAAS,UCFhB,OAAO,MAAM;AAEnB;AAWO,SAASA,oBACd,cACA,EAAC,WAAAC,cACkB;AACb,QAAA,UACJ,OAAO,gBAAiB,YAAY,wBAAwB,MACxD,EAAC,KAAK,iBACN,cACA,EAAC,WAAW,YAAY,MAAM,eAAe,MAAM,sBAAsB,KAAQ,IAAA,SACjF,EAAC,OAAO,KAAK,mBAAA,IAAsB,SAAS,OAAO,GACnD,iBAAiB,EAAC,GAAG,QAAQ,WAE7B,qBAAqC,CAAC,GACtC,cAAuD,YAAY,CAAC,SAAS,IAAI,CACjF,GAAA,OAAO,CAAC,UAA8B,YAAY,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC,GAC3E,SAASC,kBAAA,aAAa,eAAe;AAG3C,MAAI,SACA,aAAa,IAAI,YACjB,aAAa,IAAI,gBAAgB,GACjC,cAAc,oBACd,cAAc,KACd,gBACA,aAAyB;AAG7B,SAAA,QAAA,GAEO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC,OAAO,QAAQ,GAAG,MAAM;AACvB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,IACA,CAAC,OAAO,aAAa,GAAG;AAAA,IACxB,IAAI,cAAc;AACT,aAAA;AAAA,IACT;AAAA,IACA,IAAI,MAAM;AACD,aAAA;AAAA,IACT;AAAA,IACA,IAAI,aAAa;AACR,aAAA;AAAA,IACT;AAAA,EAAA;AAGF,WAAS,UAAU;AACb,gBAIJ,aAAa,YACb,aAAa,IAAI,mBACjB,UAAU,MAAM,KAAK,kBAAmB,CAAA,EACrC,KAAK,eAAe,EACpB,MAAM,CAAC,QAAgC;AACtC,gBAAU,MAGN,EAAI,IAAA,SAAS,gBAAgB,IAAI,SAAS,cAI9C;IACD,CAAA;AAAA,EACL;AAEA,WAAS,QAAQ;AACf,iBAAa,QACb,WAAW,MAAM,GACjB,OAAO,MAAM,GACb,aAAa,cAAc,GAC3B,mBAAmB,QAAQ,CAAC,OAAO,IAAI;AAAA,EACzC;AAEA,WAAS,mBAA6D;AACpE,UAAM,YAA6C,CAAA,GAC7C,YAAkC;AAExC,aAAS,YAAY;AACZ,aAAA,IAAI,QAAkD,CAAC,YAAY;AAClE,cAAA,QAAQ,UAAU;AACpB,gBACF,QAAQ,EAAC,OAAO,MAAM,GAAM,CAAA,IAE5B,UAAU,KAAK,OAAO;AAAA,MAAA,CAEzB;AAAA,IACH;AAEM,UAAA,YAAY,SAAU,OAA2B;AAC/C,YAAA,UAAU,UAAU;AACtB,gBACF,QAAQ,EAAC,OAAO,MAAM,GAAM,CAAA,IAE5B,UAAU,KAAK,KAAK;AAAA,IAAA;AAIxB,aAAS,cAAc;AACrB,WAAA,YAAY,OAAO,YAAY,QAAQ,SAAS,GAAG,CAAC,GAC7C,UAAU,MAAM;AAAG;AAC1B,aAAO,UAAU,MAAM;AAAG;AAAA,IAC5B;AAEA,aAAS,UAAU;AACX,YAAA,UAAU,UAAU;AACrB,kBAIL,QAAQ,EAAC,MAAM,IAAM,OAAO,OAAU,CAAA,GACtC,YAAY;AAAA,IACd;AAEA,WAAA,mBAAmB,KAAK,OAAO,GAC/B,YAAY,KAAK,SAAS,GAEnB;AAAA,MACL,OAAO;AACL,eAAO,eAAe,SAAS,KAAK,WAAW,UAAU;AAAA,MAC3D;AAAA,MACA,SAAS;AACK,eAAA,YAAA,GACL,QAAQ,QAAQ,EAAC,MAAM,IAAM,OAAO,QAAU;AAAA,MACvD;AAAA,MACA,MAAM,OAAO;AACC,eAAA,YAAA,GACL,QAAQ,OAAO,KAAK;AAAA,MAC7B;AAAA,MACA,CAAC,OAAO,aAAa,IAAI;AAChB,eAAA;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AAEA,WAAS,oBAAoB;AACP,wBAAA,EAAC,OAAO,YAAA,CAAY,GACxC,aAAa,YACb,iBAAiB,WAAW,SAAS,WAAW;AAAA,EAClD;AAEA,iBAAe,gBAAgB,UAA6B;AAChD,cAAA,GACV,OAAO;AAEP,UAAM,EAAC,MAAM,YAAY,OAAA,IAAU;AAGnC,QAAI,WAAW,KAAK;AAClB,mBAAA,GACA;AACA;AAAA,IACF;AAEA,QAAI,CAAC;AACG,YAAA,IAAI,MAAM,uBAAuB;AAGrC,mBACF,aAAa,SAAS;AAMlB,UAAA,SAASD,WAAU,IAAW,GAC9B,UAAU,IAAI,YAAY,GAE1B,SAAS,OAAO;AACtB,QAAI,OAAO;AAEE,iBAAA;AAEV,OAAA;AACD,YAAM,EAAC,MAAM,MAAA,IAAS,MAAM,OAAO,KAAK;AACpC,eACF,OAAO,KAAK,QAAQ,OAAO,OAAO,EAAC,QAAQ,CAAC,KAAI,CAAC,CAAC,GAG/C,SAIL,OAAO,IACP,UAAU,MACV,OAAO,MAAM,GAKb,kBAAkB,GAIlB,aAAa;AAAA,IACN,SAAA;AAAA,EACX;AAEA,WAAS,gBAAgB,KAAuB;AAC1C,QAAA,IAAI,SAAS,sBAAsB;AACrC,oBAAc,IAAI;AAClB;AAAA,IACF;AAEI,WAAO,IAAI,MAAO,aACpB,cAAc,IAAI,KAGpB,KAAK;AAAA,MACH,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,IAAA,CACZ;AAAA,EACH;AAEA,WAAS,oBAAmC;AAEpC,UAAA,EAAC,MAAM,aAAa,MAAM,QAAQ,UAAU,UAAU,eAAc,IAAI,SAExE,UAAU,EAAC,QAAQ,qBAAqB,GAAG,gBAAgB,GAD/C,cAAc,EAAC,iBAAiB,YAAW,IAAI;AAE1D,WAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,QAAQ,WAAW;AAAA,IAAA;AAAA,EAEvB;AACF;AAEA,SAAS,SAAS,SAIhB;AACM,QAAA,QAAQ,QAAQ,SAAS,WAAW;AACtC,MAAA,CAAC,YAAY,KAAK;AACd,UAAA,IAAI,MAAM,+EAA+E;AAGjG,MAAI,OAAO,mBAAoB;AACvB,UAAA,IAAI,MAAM,wCAAwC;AAGpD,QAAA,EAAC,KAAK,mBAAsB,IAAA;AAElC,MAAI,OAAO,OAAQ,YAAY,EAAE,eAAe;AACxC,UAAA,IAAI,MAAM,uDAAuD;AAGrE,MAAA,OAAO,sBAAuB,YAAY,uBAAuB;AAC7D,UAAA,IAAI,MAAM,mEAAmE;AAG9E,SAAA,EAAC,OAAO,KAAK;AACtB;AAGA,SAAS,YAAY,OAAgE;AACnF,SAAO,OAAO,SAAU;AAC1B;ACjSA,MAAM,sBAAuC;AAAA,EAC3C;AACF;AASO,SAAS,kBACd,cACmB;AACZ,SAAAE,oBAAa,cAAc,mBAAmB;AACvD;AAUA,SAAS,UACP,MAC4B;AAC5B,MAAI,EAAE,gBAAgB;AACd,UAAA,IAAI,MAAM,+CAA+C;AAG1D,SAAA;AACT;;;;;"} |
+1
-1
@@ -91,3 +91,3 @@ "use strict"; | ||
| const { done, value } = await reader.read(); | ||
| value && parser.feed(decoder.decode(value, { stream: !done })), done && (open = !1, request = null, parser.reset(), onDisconnect(), scheduleReconnect()); | ||
| value && parser.feed(decoder.decode(value, { stream: !done })), done && (open = !1, request = null, parser.reset(), scheduleReconnect(), onDisconnect()); | ||
| } while (open); | ||
@@ -94,0 +94,0 @@ } |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"node.js","sources":["../src/constants.ts","../src/client.ts","../src/node.ts"],"sourcesContent":["// ReadyStates, mirrors WhatWG spec, but uses strings instead of numbers.\n// Why make it harder to read than it needs to be?\n\n/**\n * ReadyState representing a connection that is connecting or has been scheduled to reconnect.\n * @public\n */\nexport const CONNECTING = 'connecting'\n\n/**\n * ReadyState representing a connection that is open, eg connected.\n * @public\n */\nexport const OPEN = 'open'\n\n/**\n * ReadyState representing a connection that has been closed (manually, or due to an error).\n * @public\n */\nexport const CLOSED = 'closed'\n","import {createParser, type ParseEvent} from 'eventsource-parser'\n\nimport type {EnvAbstractions, EventSourceAsyncValueResolver} from './abstractions.js'\nimport {CLOSED, CONNECTING, OPEN} from './constants.js'\nimport type {\n EventSourceClient,\n EventSourceMessage,\n EventSourceOptions,\n FetchLike,\n FetchLikeInit,\n FetchLikeResponse,\n ReadyState,\n} from './types.js'\n\n/**\n * Intentional noop function for eased control flow\n */\nconst noop = () => {\n /* intentional noop */\n}\n\n/**\n * Creates a new EventSource client. Used internally by the environment-specific entry points,\n * and should not be used directly by consumers.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @param abstractions - Abstractions for the environments.\n * @returns A new EventSource client instance\n * @internal\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | string | URL,\n {getStream}: EnvAbstractions,\n): EventSourceClient {\n const options =\n typeof optionsOrUrl === 'string' || optionsOrUrl instanceof URL\n ? {url: optionsOrUrl}\n : optionsOrUrl\n const {onMessage, onConnect = noop, onDisconnect = noop, onScheduleReconnect = noop} = options\n const {fetch, url, initialLastEventId} = validate(options)\n const requestHeaders = {...options.headers} // Prevent post-creation mutations to headers\n\n const onCloseSubscribers: (() => void)[] = []\n const subscribers: ((event: EventSourceMessage) => void)[] = onMessage ? [onMessage] : []\n const emit = (event: EventSourceMessage) => subscribers.forEach((fn) => fn(event))\n const parser = createParser(onParsedMessage)\n\n // Client state\n let request: Promise<unknown> | null\n let currentUrl = url.toString()\n let controller = new AbortController()\n let lastEventId = initialLastEventId\n let reconnectMs = 2000\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let readyState: ReadyState = CLOSED\n\n // Let's go!\n connect()\n\n return {\n close,\n connect,\n [Symbol.iterator]: () => {\n throw new Error(\n 'EventSource does not support synchronous iteration. Use `for await` instead.',\n )\n },\n [Symbol.asyncIterator]: getEventIterator,\n get lastEventId() {\n return lastEventId\n },\n get url() {\n return currentUrl\n },\n get readyState() {\n return readyState\n },\n }\n\n function connect() {\n if (request) {\n return\n }\n\n readyState = CONNECTING\n controller = new AbortController()\n request = fetch(url, getRequestOptions())\n .then(onFetchResponse)\n .catch((err: Error & {type: string}) => {\n request = null\n\n // We expect abort errors when the user manually calls `close()` - ignore those\n if (err.name === 'AbortError' || err.type === 'aborted') {\n return\n }\n\n scheduleReconnect()\n })\n }\n\n function close() {\n readyState = CLOSED\n controller.abort()\n parser.reset()\n clearTimeout(reconnectTimer)\n onCloseSubscribers.forEach((fn) => fn())\n }\n\n function getEventIterator(): AsyncGenerator<EventSourceMessage, void> {\n const pullQueue: EventSourceAsyncValueResolver[] = []\n const pushQueue: EventSourceMessage[] = []\n\n function pullValue() {\n return new Promise<IteratorResult<EventSourceMessage, void>>((resolve) => {\n const value = pushQueue.shift()\n if (value) {\n resolve({value, done: false})\n } else {\n pullQueue.push(resolve)\n }\n })\n }\n\n const pushValue = function (value: EventSourceMessage) {\n const resolve = pullQueue.shift()\n if (resolve) {\n resolve({value, done: false})\n } else {\n pushQueue.push(value)\n }\n }\n\n function unsubscribe() {\n subscribers.splice(subscribers.indexOf(pushValue), 1)\n while (pullQueue.shift()) {}\n while (pushQueue.shift()) {}\n }\n\n function onClose() {\n const resolve = pullQueue.shift()\n if (!resolve) {\n return\n }\n\n resolve({done: true, value: undefined})\n unsubscribe()\n }\n\n onCloseSubscribers.push(onClose)\n subscribers.push(pushValue)\n\n return {\n next() {\n return readyState === CLOSED ? this.return() : pullValue()\n },\n return() {\n unsubscribe()\n return Promise.resolve({done: true, value: undefined})\n },\n throw(error) {\n unsubscribe()\n return Promise.reject(error)\n },\n [Symbol.asyncIterator]() {\n return this\n },\n }\n }\n\n function scheduleReconnect() {\n onScheduleReconnect({delay: reconnectMs})\n readyState = CONNECTING\n reconnectTimer = setTimeout(connect, reconnectMs)\n }\n\n async function onFetchResponse(response: FetchLikeResponse) {\n onConnect()\n parser.reset()\n\n const {body, redirected, status} = response\n\n // HTTP 204 means \"close the connection, no more data will be sent\"\n if (status === 204) {\n onDisconnect()\n close()\n return\n }\n\n if (!body) {\n throw new Error('Missing response body')\n }\n\n if (redirected) {\n currentUrl = response.url\n }\n\n // Ensure that the response stream is a web stream\n // @todo Figure out a way to make this work without casting\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = getStream(body as any)\n const decoder = new TextDecoder()\n\n const reader = stream.getReader()\n let open = true\n\n readyState = OPEN\n\n do {\n const {done, value} = await reader.read()\n if (value) {\n parser.feed(decoder.decode(value, {stream: !done}))\n }\n\n if (!done) {\n continue\n }\n\n open = false\n request = null\n parser.reset()\n\n onDisconnect()\n\n // EventSources never close unless explicitly handled with `.close()`:\n // Implementors should send an `done`/`complete`/`disconnect` event and\n // explicitly handle it in client code, or send an HTTP 204.\n scheduleReconnect()\n } while (open)\n }\n\n function onParsedMessage(msg: ParseEvent): void {\n if (msg.type === 'reconnect-interval') {\n reconnectMs = msg.value\n return\n }\n\n if (typeof msg.id === 'string') {\n lastEventId = msg.id\n }\n\n emit({\n id: msg.id,\n data: msg.data,\n event: msg.event,\n })\n }\n\n function getRequestOptions(): FetchLikeInit {\n // @todo allow interception of options, but don't allow overriding signal\n const {mode, credentials, body, method, redirect, referrer, referrerPolicy} = options\n const lastEvent = lastEventId ? {'Last-Event-ID': lastEventId} : undefined\n const headers = {Accept: 'text/event-stream', ...requestHeaders, ...lastEvent}\n return {\n mode,\n credentials,\n body,\n method,\n redirect,\n referrer,\n referrerPolicy,\n headers,\n cache: 'no-store',\n signal: controller.signal,\n }\n }\n}\n\nfunction validate(options: EventSourceOptions): {\n fetch: FetchLike\n url: string | URL\n initialLastEventId: string | undefined\n} {\n const fetch = options.fetch || globalThis.fetch\n if (!isFetchLike(fetch)) {\n throw new Error('No fetch implementation provided, and one was not found on the global object.')\n }\n\n if (typeof AbortController !== 'function') {\n throw new Error('Missing AbortController implementation')\n }\n\n const {url, initialLastEventId} = options\n\n if (typeof url !== 'string' && !(url instanceof URL)) {\n throw new Error('Invalid URL provided - must be string or URL instance')\n }\n\n if (typeof initialLastEventId !== 'string' && initialLastEventId !== undefined) {\n throw new Error('Invalid initialLastEventId provided - must be string or undefined')\n }\n\n return {fetch, url, initialLastEventId}\n}\n\n// This is obviously naive, but hard to probe for full compatibility\nfunction isFetchLike(fetch: FetchLike | typeof globalThis.fetch): fetch is FetchLike {\n return typeof fetch === 'function'\n}\n","import {Readable} from 'node:stream'\n\nimport type {EnvAbstractions} from './abstractions.js'\nimport {createEventSource as createSource} from './client.js'\nimport type {EventSourceClient, EventSourceOptions} from './types.js'\n\nexport * from './constants.js'\nexport * from './types.js'\n\nconst nodeAbstractions: EnvAbstractions = {\n getStream,\n}\n\n/**\n * Creates a new EventSource client.\n *\n * @param options - Options for the client, or an URL/URL string.\n * @returns A new EventSource client instance\n * @public\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | URL | string,\n): EventSourceClient {\n return createSource(optionsOrUrl, nodeAbstractions)\n}\n\n/**\n * Returns a ReadableStream (Web Stream) from either an existing ReadableStream,\n * or a node.js Readable stream. Ensures that it works with more `fetch()` polyfills.\n *\n * @param body - The body to convert\n * @returns A ReadableStream\n * @private\n */\nfunction getStream(\n body: NodeJS.ReadableStream | ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n if ('getReader' in body) {\n // Already a web stream\n return body\n }\n\n if (typeof body.pipe !== 'function' || typeof body.on !== 'function') {\n throw new Error('Invalid response body, expected a web or node.js stream')\n }\n\n // Available as of Node 17, and module requires Node 18\n if (typeof Readable.toWeb !== 'function') {\n throw new Error('Node.js 18 or higher required (`Readable.toWeb()` not defined)')\n }\n\n // @todo Figure out if we can prevent casting\n return Readable.toWeb(Readable.from(body)) as ReadableStream<Uint8Array>\n}\n"],"names":["createEventSource","getStream","createParser","createSource","Readable"],"mappings":"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"} | ||
| {"version":3,"file":"node.js","sources":["../src/constants.ts","../src/client.ts","../src/node.ts"],"sourcesContent":["// ReadyStates, mirrors WhatWG spec, but uses strings instead of numbers.\n// Why make it harder to read than it needs to be?\n\n/**\n * ReadyState representing a connection that is connecting or has been scheduled to reconnect.\n * @public\n */\nexport const CONNECTING = 'connecting'\n\n/**\n * ReadyState representing a connection that is open, eg connected.\n * @public\n */\nexport const OPEN = 'open'\n\n/**\n * ReadyState representing a connection that has been closed (manually, or due to an error).\n * @public\n */\nexport const CLOSED = 'closed'\n","import {createParser, type ParseEvent} from 'eventsource-parser'\n\nimport type {EnvAbstractions, EventSourceAsyncValueResolver} from './abstractions.js'\nimport {CLOSED, CONNECTING, OPEN} from './constants.js'\nimport type {\n EventSourceClient,\n EventSourceMessage,\n EventSourceOptions,\n FetchLike,\n FetchLikeInit,\n FetchLikeResponse,\n ReadyState,\n} from './types.js'\n\n/**\n * Intentional noop function for eased control flow\n */\nconst noop = () => {\n /* intentional noop */\n}\n\n/**\n * Creates a new EventSource client. Used internally by the environment-specific entry points,\n * and should not be used directly by consumers.\n *\n * @param optionsOrUrl - Options for the client, or an URL/URL string.\n * @param abstractions - Abstractions for the environments.\n * @returns A new EventSource client instance\n * @internal\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | string | URL,\n {getStream}: EnvAbstractions,\n): EventSourceClient {\n const options =\n typeof optionsOrUrl === 'string' || optionsOrUrl instanceof URL\n ? {url: optionsOrUrl}\n : optionsOrUrl\n const {onMessage, onConnect = noop, onDisconnect = noop, onScheduleReconnect = noop} = options\n const {fetch, url, initialLastEventId} = validate(options)\n const requestHeaders = {...options.headers} // Prevent post-creation mutations to headers\n\n const onCloseSubscribers: (() => void)[] = []\n const subscribers: ((event: EventSourceMessage) => void)[] = onMessage ? [onMessage] : []\n const emit = (event: EventSourceMessage) => subscribers.forEach((fn) => fn(event))\n const parser = createParser(onParsedMessage)\n\n // Client state\n let request: Promise<unknown> | null\n let currentUrl = url.toString()\n let controller = new AbortController()\n let lastEventId = initialLastEventId\n let reconnectMs = 2000\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let readyState: ReadyState = CLOSED\n\n // Let's go!\n connect()\n\n return {\n close,\n connect,\n [Symbol.iterator]: () => {\n throw new Error(\n 'EventSource does not support synchronous iteration. Use `for await` instead.',\n )\n },\n [Symbol.asyncIterator]: getEventIterator,\n get lastEventId() {\n return lastEventId\n },\n get url() {\n return currentUrl\n },\n get readyState() {\n return readyState\n },\n }\n\n function connect() {\n if (request) {\n return\n }\n\n readyState = CONNECTING\n controller = new AbortController()\n request = fetch(url, getRequestOptions())\n .then(onFetchResponse)\n .catch((err: Error & {type: string}) => {\n request = null\n\n // We expect abort errors when the user manually calls `close()` - ignore those\n if (err.name === 'AbortError' || err.type === 'aborted') {\n return\n }\n\n scheduleReconnect()\n })\n }\n\n function close() {\n readyState = CLOSED\n controller.abort()\n parser.reset()\n clearTimeout(reconnectTimer)\n onCloseSubscribers.forEach((fn) => fn())\n }\n\n function getEventIterator(): AsyncGenerator<EventSourceMessage, void> {\n const pullQueue: EventSourceAsyncValueResolver[] = []\n const pushQueue: EventSourceMessage[] = []\n\n function pullValue() {\n return new Promise<IteratorResult<EventSourceMessage, void>>((resolve) => {\n const value = pushQueue.shift()\n if (value) {\n resolve({value, done: false})\n } else {\n pullQueue.push(resolve)\n }\n })\n }\n\n const pushValue = function (value: EventSourceMessage) {\n const resolve = pullQueue.shift()\n if (resolve) {\n resolve({value, done: false})\n } else {\n pushQueue.push(value)\n }\n }\n\n function unsubscribe() {\n subscribers.splice(subscribers.indexOf(pushValue), 1)\n while (pullQueue.shift()) {}\n while (pushQueue.shift()) {}\n }\n\n function onClose() {\n const resolve = pullQueue.shift()\n if (!resolve) {\n return\n }\n\n resolve({done: true, value: undefined})\n unsubscribe()\n }\n\n onCloseSubscribers.push(onClose)\n subscribers.push(pushValue)\n\n return {\n next() {\n return readyState === CLOSED ? this.return() : pullValue()\n },\n return() {\n unsubscribe()\n return Promise.resolve({done: true, value: undefined})\n },\n throw(error) {\n unsubscribe()\n return Promise.reject(error)\n },\n [Symbol.asyncIterator]() {\n return this\n },\n }\n }\n\n function scheduleReconnect() {\n onScheduleReconnect({delay: reconnectMs})\n readyState = CONNECTING\n reconnectTimer = setTimeout(connect, reconnectMs)\n }\n\n async function onFetchResponse(response: FetchLikeResponse) {\n onConnect()\n parser.reset()\n\n const {body, redirected, status} = response\n\n // HTTP 204 means \"close the connection, no more data will be sent\"\n if (status === 204) {\n onDisconnect()\n close()\n return\n }\n\n if (!body) {\n throw new Error('Missing response body')\n }\n\n if (redirected) {\n currentUrl = response.url\n }\n\n // Ensure that the response stream is a web stream\n // @todo Figure out a way to make this work without casting\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stream = getStream(body as any)\n const decoder = new TextDecoder()\n\n const reader = stream.getReader()\n let open = true\n\n readyState = OPEN\n\n do {\n const {done, value} = await reader.read()\n if (value) {\n parser.feed(decoder.decode(value, {stream: !done}))\n }\n\n if (!done) {\n continue\n }\n\n open = false\n request = null\n parser.reset()\n\n // EventSources never close unless explicitly handled with `.close()`:\n // Implementors should send an `done`/`complete`/`disconnect` event and\n // explicitly handle it in client code, or send an HTTP 204.\n scheduleReconnect()\n\n // Calling scheduleReconnect() prior to onDisconnect() allows consumers to\n // explicitly call .close() before the reconnection is performed.\n onDisconnect()\n } while (open)\n }\n\n function onParsedMessage(msg: ParseEvent): void {\n if (msg.type === 'reconnect-interval') {\n reconnectMs = msg.value\n return\n }\n\n if (typeof msg.id === 'string') {\n lastEventId = msg.id\n }\n\n emit({\n id: msg.id,\n data: msg.data,\n event: msg.event,\n })\n }\n\n function getRequestOptions(): FetchLikeInit {\n // @todo allow interception of options, but don't allow overriding signal\n const {mode, credentials, body, method, redirect, referrer, referrerPolicy} = options\n const lastEvent = lastEventId ? {'Last-Event-ID': lastEventId} : undefined\n const headers = {Accept: 'text/event-stream', ...requestHeaders, ...lastEvent}\n return {\n mode,\n credentials,\n body,\n method,\n redirect,\n referrer,\n referrerPolicy,\n headers,\n cache: 'no-store',\n signal: controller.signal,\n }\n }\n}\n\nfunction validate(options: EventSourceOptions): {\n fetch: FetchLike\n url: string | URL\n initialLastEventId: string | undefined\n} {\n const fetch = options.fetch || globalThis.fetch\n if (!isFetchLike(fetch)) {\n throw new Error('No fetch implementation provided, and one was not found on the global object.')\n }\n\n if (typeof AbortController !== 'function') {\n throw new Error('Missing AbortController implementation')\n }\n\n const {url, initialLastEventId} = options\n\n if (typeof url !== 'string' && !(url instanceof URL)) {\n throw new Error('Invalid URL provided - must be string or URL instance')\n }\n\n if (typeof initialLastEventId !== 'string' && initialLastEventId !== undefined) {\n throw new Error('Invalid initialLastEventId provided - must be string or undefined')\n }\n\n return {fetch, url, initialLastEventId}\n}\n\n// This is obviously naive, but hard to probe for full compatibility\nfunction isFetchLike(fetch: FetchLike | typeof globalThis.fetch): fetch is FetchLike {\n return typeof fetch === 'function'\n}\n","import {Readable} from 'node:stream'\n\nimport type {EnvAbstractions} from './abstractions.js'\nimport {createEventSource as createSource} from './client.js'\nimport type {EventSourceClient, EventSourceOptions} from './types.js'\n\nexport * from './constants.js'\nexport * from './types.js'\n\nconst nodeAbstractions: EnvAbstractions = {\n getStream,\n}\n\n/**\n * Creates a new EventSource client.\n *\n * @param options - Options for the client, or an URL/URL string.\n * @returns A new EventSource client instance\n * @public\n */\nexport function createEventSource(\n optionsOrUrl: EventSourceOptions | URL | string,\n): EventSourceClient {\n return createSource(optionsOrUrl, nodeAbstractions)\n}\n\n/**\n * Returns a ReadableStream (Web Stream) from either an existing ReadableStream,\n * or a node.js Readable stream. Ensures that it works with more `fetch()` polyfills.\n *\n * @param body - The body to convert\n * @returns A ReadableStream\n * @private\n */\nfunction getStream(\n body: NodeJS.ReadableStream | ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n if ('getReader' in body) {\n // Already a web stream\n return body\n }\n\n if (typeof body.pipe !== 'function' || typeof body.on !== 'function') {\n throw new Error('Invalid response body, expected a web or node.js stream')\n }\n\n // Available as of Node 17, and module requires Node 18\n if (typeof Readable.toWeb !== 'function') {\n throw new Error('Node.js 18 or higher required (`Readable.toWeb()` not defined)')\n }\n\n // @todo Figure out if we can prevent casting\n return Readable.toWeb(Readable.from(body)) as ReadableStream<Uint8Array>\n}\n"],"names":["createEventSource","getStream","createParser","createSource","Readable"],"mappings":"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"} |
+2
-1
| { | ||
| "name": "eventsource-client", | ||
| "version": "1.1.1", | ||
| "version": "1.1.2", | ||
| "description": "Modern EventSource client for browsers and Node.js", | ||
@@ -28,2 +28,3 @@ "sideEffects": false, | ||
| "build": "pkg-utils build && pkg-utils --strict", | ||
| "build:watch": "pkg-utils watch", | ||
| "clean": "rimraf dist coverage", | ||
@@ -30,0 +31,0 @@ "lint": "eslint . && tsc --noEmit", |
+4
-2
@@ -222,4 +222,2 @@ import {createParser, type ParseEvent} from 'eventsource-parser' | ||
| onDisconnect() | ||
| // EventSources never close unless explicitly handled with `.close()`: | ||
@@ -229,2 +227,6 @@ // Implementors should send an `done`/`complete`/`disconnect` event and | ||
| scheduleReconnect() | ||
| // Calling scheduleReconnect() prior to onDisconnect() allows consumers to | ||
| // explicitly call .close() before the reconnection is performed. | ||
| onDisconnect() | ||
| } while (open) | ||
@@ -231,0 +233,0 @@ } |
Sorry, the diff of this file is too big to display
250320
0.26%1132
0.18%