msw
Version:
1 lines • 35.3 kB
Source Map (JSON)
{"version":3,"sources":["../../src/core/sse.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport { Emitter } from 'strict-event-emitter'\nimport type { ResponseResolver } from './handlers/RequestHandler'\nimport {\n HttpHandler,\n type HttpRequestResolverExtras,\n type HttpRequestParsedResult,\n} from './handlers/HttpHandler'\nimport type { Path, PathParams } from './utils/matching/matchRequestUrl'\nimport { delay } from './delay'\nimport { getTimestamp } from './utils/logging/getTimestamp'\nimport { devUtils } from './utils/internal/devUtils'\nimport { colors } from './ws/utils/attachWebSocketLogger'\nimport { toPublicUrl } from './utils/request/toPublicUrl'\n\ntype EventMapConstraint = {\n message?: unknown\n [key: string]: unknown\n [key: symbol | number]: never\n}\n\nexport type ServerSentEventResolverExtras<\n EventMap extends EventMapConstraint,\n Params extends PathParams,\n> = HttpRequestResolverExtras<Params> & {\n client: ServerSentEventClient<EventMap>\n server: ServerSentEventServer\n}\n\nexport type ServerSentEventResolver<\n EventMap extends EventMapConstraint,\n Params extends PathParams,\n> = ResponseResolver<ServerSentEventResolverExtras<EventMap, Params>, any, any>\n\nexport type ServerSentEventRequestHandler = <\n EventMap extends EventMapConstraint = { message: unknown },\n Params extends PathParams<keyof Params> = PathParams,\n RequestPath extends Path = Path,\n>(\n path: RequestPath,\n resolver: ServerSentEventResolver<EventMap, Params>,\n) => HttpHandler\n\nexport type ServerSentEventMessage<\n EventMap extends EventMapConstraint = { message: unknown },\n> =\n | ToEventDiscriminatedUnion<EventMap & { message: unknown }>\n | {\n id?: never\n event?: never\n data?: never\n retry: number\n }\n\n/**\n * Intercept Server-Sent Events (SSE).\n *\n * @example\n * sse('http://localhost:4321', ({ client }) => {\n * client.send({ data: 'hello world' })\n * })\n *\n * @see {@link https://mswjs.io/docs/sse/ Mocking Server-Sent Events}\n * @see {@link https://mswjs.io/docs/api/sse `sse()` API reference}\n */\nexport const sse: ServerSentEventRequestHandler = (path, resolver) => {\n return new ServerSentEventHandler(path, resolver)\n}\n\nclass ServerSentEventHandler<\n EventMap extends EventMapConstraint,\n> extends HttpHandler {\n constructor(path: Path, resolver: ServerSentEventResolver<EventMap, any>) {\n invariant(\n typeof EventSource !== 'undefined',\n 'Failed to construct a Server-Sent Event handler for path \"%s\": the EventSource API is not supported in this environment',\n path,\n )\n\n const clientEmitter = new Emitter<ServerSentEventClientEventMap>()\n\n super('GET', path, async (info) => {\n const responseInit: ResponseInit = {\n headers: {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n },\n }\n\n /**\n * @note Log the intercepted request early.\n * Normally, the `this.log()` method is called when the handler returns a response.\n * For SSE, call that method earlier so the logs are in correct order.\n */\n await super.log({\n request: info.request,\n /**\n * @note Construct a placeholder response since SSE response\n * is being streamed and cannot be cloned/consumed for logging.\n */\n response: new Response('[streaming]', responseInit),\n })\n this.#attachClientLogger(info.request, clientEmitter)\n\n const stream = new ReadableStream({\n async start(controller) {\n const client = new ServerSentEventClient<EventMap>({\n controller,\n emitter: clientEmitter,\n })\n const server = new ServerSentEventServer({\n request: info.request,\n client,\n })\n\n await resolver({\n ...info,\n client,\n server,\n })\n },\n })\n\n return new Response(stream, responseInit)\n })\n }\n\n async predicate(args: {\n request: Request\n parsedResult: HttpRequestParsedResult\n }) {\n if (args.request.headers.get('accept') !== 'text/event-stream') {\n return false\n }\n\n return super.predicate(args)\n }\n\n async log(_args: { request: Request; response: Response }): Promise<void> {\n /**\n * @note Skip the default `this.log()` logic so that when this handler is logged\n * upon handling the request, nothing is printed (we log SSE requests early).\n */\n return\n }\n\n #attachClientLogger(\n request: Request,\n emitter: Emitter<ServerSentEventClientEventMap>,\n ): void {\n const publicUrl = toPublicUrl(request.url)\n\n /* eslint-disable no-console */\n emitter.on('message', (payload) => {\n console.groupCollapsed(\n devUtils.formatMessage(\n `${getTimestamp()} SSE %s %c⇣%c ${payload.event}`,\n ),\n publicUrl,\n `color:${colors.mocked}`,\n 'color:inherit',\n )\n console.log(payload.frames)\n console.groupEnd()\n })\n\n emitter.on('error', () => {\n console.groupCollapsed(\n devUtils.formatMessage(`${getTimestamp()} SSE %s %c\\u00D7%c error`),\n publicUrl,\n `color: ${colors.system}`,\n 'color:inherit',\n )\n console.log('Handler:', this)\n console.groupEnd()\n })\n\n emitter.on('close', () => {\n console.groupCollapsed(\n devUtils.formatMessage(`${getTimestamp()} SSE %s %c■%c close`),\n publicUrl,\n `colors:${colors.system}`,\n 'color:inherit',\n )\n console.log('Handler:', this)\n console.groupEnd()\n })\n /* eslint-enable no-console */\n }\n}\n\ntype Values<T> = T[keyof T]\ntype Identity<T> = { [K in keyof T]: T[K] } & unknown\ntype ToEventDiscriminatedUnion<T> = Values<{\n [K in keyof T]: Identity<\n (K extends 'message'\n ? {\n id?: string\n event?: K\n data?: T[K]\n retry?: never\n }\n : {\n id?: string\n event: K\n data?: T[K]\n retry?: never\n }) &\n // Make the `data` field conditionally required through an intersection.\n (undefined extends T[K] ? unknown : { data: unknown })\n >\n}>\n\ntype ServerSentEventClientEventMap = {\n message: [\n payload: {\n id?: string\n event: string\n data?: unknown\n frames: Array<string>\n },\n ]\n error: []\n close: []\n}\n\nclass ServerSentEventClient<\n EventMap extends EventMapConstraint = { message: unknown },\n> {\n #encoder: TextEncoder\n #controller: ReadableStreamDefaultController\n #emitter: Emitter<ServerSentEventClientEventMap>\n\n constructor(args: {\n controller: ReadableStreamDefaultController\n emitter: Emitter<ServerSentEventClientEventMap>\n }) {\n this.#encoder = new TextEncoder()\n this.#controller = args.controller\n this.#emitter = args.emitter\n }\n\n /**\n * Sends the given payload to the intercepted `EventSource`.\n */\n public send(payload: ServerSentEventMessage<EventMap>): void {\n if ('retry' in payload && payload.retry != null) {\n this.#sendRetry(payload.retry)\n return\n }\n\n this.#sendMessage({\n id: payload.id,\n event: payload.event,\n data:\n typeof payload.data === 'object'\n ? JSON.stringify(payload.data)\n : payload.data,\n })\n }\n\n /**\n * Dispatches the given event on the intercepted `EventSource`.\n */\n public dispatchEvent(event: Event) {\n if (event instanceof MessageEvent) {\n /**\n * @note Use the internal send mechanism to skip normalization\n * of the message data (already normalized by the server).\n */\n this.#sendMessage({\n id: event.lastEventId || undefined,\n event: event.type === 'message' ? undefined : event.type,\n data: event.data,\n })\n return\n }\n\n if (event.type === 'error') {\n this.error()\n return\n }\n\n if (event.type === 'close') {\n this.close()\n return\n }\n }\n\n /**\n * Errors the underlying `EventSource`, closing the connection with an error.\n * This is equivalent to aborting the connection and will produce a `TypeError: Failed to fetch`\n * error.\n */\n public error(): void {\n this.#controller.error()\n this.#emitter.emit('error')\n }\n\n /**\n * Closes the underlying `EventSource`, closing the connection.\n */\n public close(): void {\n this.#controller.close()\n this.#emitter.emit('close')\n }\n\n #sendRetry(retry: number): void {\n if (typeof retry === 'number') {\n this.#controller.enqueue(this.#encoder.encode(`retry:${retry}\\n\\n`))\n }\n }\n\n #sendMessage(message: {\n id?: string\n event?: unknown\n data: unknown | undefined\n }): void {\n const frames: Array<string> = []\n\n if (message.id) {\n frames.push(`id:${message.id}`)\n }\n\n if (message.event) {\n frames.push(`event:${message.event?.toString()}`)\n }\n\n frames.push(`data:${message.data}`)\n frames.push('', '')\n\n this.#controller.enqueue(this.#encoder.encode(frames.join('\\n')))\n\n this.#emitter.emit('message', {\n id: message.id,\n event: message.event?.toString() || 'message',\n data: message.data,\n frames,\n })\n }\n}\n\nclass ServerSentEventServer {\n #request: Request\n #client: ServerSentEventClient<ServerSentEventClientEventMap>\n\n constructor(args: { request: Request; client: ServerSentEventClient<any> }) {\n this.#request = args.request\n this.#client = args.client\n }\n\n /**\n * Establishes the actual connection for this SSE request\n * and returns the `EventSource` instance.\n */\n public connect(): EventSource {\n const source = new ObservableEventSource(this.#request.url, {\n withCredentials: this.#request.credentials === 'include',\n headers: {\n /**\n * @note Mark this request as passthrough so it doesn't trigger\n * an infinite loop matching against the existing request handler.\n */\n accept: 'msw/passthrough',\n },\n })\n\n source[kOnAnyMessage] = (event) => {\n Object.defineProperties(event, {\n target: {\n value: this,\n enumerable: true,\n writable: true,\n configurable: true,\n },\n })\n\n // Schedule the server-to-client forwarding for the next tick\n // so the user can prevent the message event.\n queueMicrotask(() => {\n if (!event.defaultPrevented) {\n this.#client.dispatchEvent(event)\n }\n })\n }\n\n // Forward stream errors from the actual server to the client.\n source.addEventListener('error', (event) => {\n Object.defineProperties(event, {\n target: {\n value: this,\n enumerable: true,\n writable: true,\n configurable: true,\n },\n })\n\n queueMicrotask(() => {\n // Allow the user to opt-out from this forwarding.\n if (!event.defaultPrevented) {\n this.#client.dispatchEvent(event)\n }\n })\n })\n\n return source\n }\n}\n\ninterface ObservableEventSourceInit extends EventSourceInit {\n headers?: HeadersInit\n}\n\ntype EventHandler<EventType extends Event> = (\n this: EventSource,\n event: EventType,\n) => any\n\nconst kRequest = Symbol('kRequest')\nconst kReconnectionTime = Symbol('kReconnectionTime')\nconst kLastEventId = Symbol('kLastEventId')\nconst kAbortController = Symbol('kAbortController')\nconst kOnOpen = Symbol('kOnOpen')\nconst kOnMessage = Symbol('kOnMessage')\nconst kOnAnyMessage = Symbol('kOnAnyMessage')\nconst kOnError = Symbol('kOnError')\n\nclass ObservableEventSource extends EventTarget implements EventSource {\n static readonly CONNECTING = 0\n static readonly OPEN = 1\n static readonly CLOSED = 2\n\n public readonly CONNECTING = ObservableEventSource.CONNECTING\n public readonly OPEN = ObservableEventSource.OPEN\n public readonly CLOSED = ObservableEventSource.CLOSED\n\n public readyState: number\n public url: string\n public withCredentials: boolean\n\n private [kRequest]: Request\n private [kReconnectionTime]: number\n private [kLastEventId]: string\n private [kAbortController]: AbortController\n private [kOnOpen]: EventHandler<Event> | null = null\n private [kOnMessage]: EventHandler<MessageEvent> | null = null\n private [kOnAnyMessage]: EventHandler<MessageEvent> | null = null\n private [kOnError]: EventHandler<Event> | null = null\n\n constructor(url: string | URL, init?: ObservableEventSourceInit) {\n super()\n\n this.url = new URL(url).href\n this.withCredentials = init?.withCredentials ?? false\n\n this.readyState = this.CONNECTING\n\n // Support custom request init.\n const headers = new Headers(init?.headers || {})\n headers.append('accept', 'text/event-stream')\n\n this[kAbortController] = new AbortController()\n this[kReconnectionTime] = 2000\n this[kLastEventId] = ''\n this[kRequest] = new Request(this.url, {\n method: 'GET',\n headers,\n credentials: this.withCredentials ? 'include' : 'omit',\n signal: this[kAbortController].signal,\n })\n\n this.connect()\n }\n\n get onopen(): EventHandler<Event> | null {\n return this[kOnOpen]\n }\n\n set onopen(handler: EventHandler<Event>) {\n if (this[kOnOpen]) {\n this.removeEventListener('open', this[kOnOpen])\n }\n this[kOnOpen] = handler.bind(this)\n this.addEventListener('open', this[kOnOpen])\n }\n\n get onmessage(): EventHandler<MessageEvent> | null {\n return this[kOnMessage]\n }\n set onmessage(handler: EventHandler<MessageEvent>) {\n if (this[kOnMessage]) {\n this.removeEventListener('message', { handleEvent: this[kOnMessage] })\n }\n this[kOnMessage] = handler.bind(this)\n this.addEventListener('message', { handleEvent: this[kOnMessage] })\n }\n\n get onerror(): EventHandler<Event> | null {\n return this[kOnError]\n }\n set oneerror(handler: EventHandler<Event>) {\n if (this[kOnError]) {\n this.removeEventListener('error', { handleEvent: this[kOnError] })\n }\n this[kOnError] = handler.bind(this)\n this.addEventListener('error', { handleEvent: this[kOnError] })\n }\n\n public addEventListener<K extends keyof EventSourceEventMap>(\n type: K,\n listener: EventHandler<EventSourceEventMap[K]>,\n options?: boolean | AddEventListenerOptions,\n ): void\n public addEventListener(\n type: string,\n listener: EventHandler<MessageEvent>,\n options?: boolean | AddEventListenerOptions,\n ): void\n public addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void\n\n public addEventListener(\n type: string,\n listener: EventHandler<MessageEvent> | EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void {\n super.addEventListener(\n type,\n listener as EventListenerOrEventListenerObject,\n options,\n )\n }\n\n public removeEventListener<K extends keyof EventSourceEventMap>(\n type: K,\n listener: (this: EventSource, ev: EventSourceEventMap[K]) => any,\n options?: boolean | EventListenerOptions,\n ): void\n public removeEventListener(\n type: string,\n listener: (this: EventSource, event: MessageEvent) => any,\n options?: boolean | EventListenerOptions,\n ): void\n public removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void\n\n public removeEventListener(\n type: string,\n listener: EventHandler<MessageEvent> | EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void {\n super.removeEventListener(\n type,\n listener as EventListenerOrEventListenerObject,\n options,\n )\n }\n\n public dispatchEvent(event: Event): boolean {\n return super.dispatchEvent(event)\n }\n\n public close(): void {\n this[kAbortController].abort()\n this.readyState = this.CLOSED\n }\n\n private async connect() {\n await fetch(this[kRequest])\n .then((response) => {\n this.processResponse(response)\n })\n .catch(() => {\n // Fail the connection on request errors instead of\n // throwing a generic \"Failed to fetch\" error.\n this.failConnection()\n })\n }\n\n private processResponse(response: Response): void {\n if (!response.body) {\n this.failConnection()\n return\n }\n\n if (isNetworkError(response)) {\n this.reestablishConnection()\n return\n }\n\n if (\n response.status !== 200 ||\n response.headers.get('content-type') !== 'text/event-stream'\n ) {\n this.failConnection()\n return\n }\n\n this.announceConnection()\n this.interpretResponseBody(response)\n }\n\n private announceConnection(): void {\n queueMicrotask(() => {\n if (this.readyState !== this.CLOSED) {\n this.readyState = this.OPEN\n this.dispatchEvent(new Event('open'))\n }\n })\n }\n\n private interpretResponseBody(response: Response): void {\n const parsingStream = new EventSourceParsingStream({\n message: (message) => {\n if (message.id) {\n this[kLastEventId] = message.id\n }\n\n if (message.retry) {\n this[kReconnectionTime] = message.retry\n }\n\n const messageEvent = new MessageEvent(\n message.event ? message.event : 'message',\n {\n data: message.data,\n origin: this[kRequest].url,\n lastEventId: this[kLastEventId],\n cancelable: true,\n },\n )\n\n this[kOnAnyMessage]?.(messageEvent)\n this.dispatchEvent(messageEvent)\n },\n abort: () => {\n throw new Error('Stream abort is not implemented')\n },\n close: () => {\n this.failConnection()\n },\n })\n\n response\n .body!.pipeTo(parsingStream)\n .then(() => {\n this.processResponseEndOfBody(response)\n })\n .catch(() => {\n this.failConnection()\n })\n }\n\n private processResponseEndOfBody(response: Response): void {\n if (!isNetworkError(response)) {\n this.reestablishConnection()\n }\n }\n\n private async reestablishConnection(): Promise<void> {\n queueMicrotask(() => {\n if (this.readyState === this.CLOSED) {\n return\n }\n\n this.readyState = this.CONNECTING\n this.dispatchEvent(new Event('error'))\n })\n\n await delay(this[kReconnectionTime])\n\n queueMicrotask(async () => {\n if (this.readyState !== this.CONNECTING) {\n return\n }\n\n if (this[kLastEventId] !== '') {\n this[kRequest].headers.set('last-event-id', this[kLastEventId])\n }\n\n await this.connect()\n })\n }\n\n private failConnection(): void {\n queueMicrotask(() => {\n if (this.readyState !== this.CLOSED) {\n this.readyState = this.CLOSED\n this.dispatchEvent(new Event('error'))\n }\n })\n }\n}\n\n/**\n * Checks if the given `Response` instance is a network error.\n * @see https://fetch.spec.whatwg.org/#concept-network-error\n */\nfunction isNetworkError(response: Response): boolean {\n return (\n response.type === 'error' &&\n response.status === 0 &&\n response.statusText === '' &&\n Array.from(response.headers.entries()).length === 0 &&\n response.body === null\n )\n}\n\nconst enum ControlCharacters {\n NewLine = 10,\n CarriageReturn = 13,\n Space = 32,\n Colon = 58,\n}\n\ninterface EventSourceMessage {\n id?: string\n event?: string\n data?: string\n retry?: number\n}\n\nclass EventSourceParsingStream extends WritableStream {\n private decoder: TextDecoder\n\n private buffer?: Uint8Array\n private position: number\n private fieldLength?: number\n private discardTrailingNewline = false\n\n private message: EventSourceMessage = {\n id: undefined,\n event: undefined,\n data: undefined,\n retry: undefined,\n }\n\n constructor(\n private underlyingSink: {\n message: (message: EventSourceMessage) => void\n abort?: (reason: any) => void\n close?: () => void\n },\n ) {\n super({\n write: (chunk) => {\n this.processResponseBodyChunk(chunk)\n },\n abort: (reason) => {\n this.underlyingSink.abort?.(reason)\n },\n close: () => {\n this.underlyingSink.close?.()\n },\n })\n\n this.decoder = new TextDecoder()\n this.position = 0\n }\n\n private resetMessage(): void {\n this.message = {\n id: undefined,\n event: undefined,\n data: undefined,\n retry: undefined,\n }\n }\n\n private processResponseBodyChunk(chunk: Uint8Array): void {\n if (this.buffer == null) {\n this.buffer = chunk\n this.position = 0\n this.fieldLength = -1\n } else {\n const nextBuffer = new Uint8Array(this.buffer.length + chunk.length)\n nextBuffer.set(this.buffer)\n nextBuffer.set(chunk, this.buffer.length)\n this.buffer = nextBuffer\n }\n\n const bufferLength = this.buffer.length\n let lineStart = 0\n\n while (this.position < bufferLength) {\n if (this.discardTrailingNewline) {\n if (this.buffer[this.position] === ControlCharacters.NewLine) {\n lineStart = ++this.position\n }\n\n this.discardTrailingNewline = false\n }\n\n let lineEnd = -1\n\n for (; this.position < bufferLength && lineEnd === -1; ++this.position) {\n switch (this.buffer[this.position]) {\n case ControlCharacters.Colon: {\n if (this.fieldLength === -1) {\n this.fieldLength = this.position - lineStart\n }\n break\n }\n\n case ControlCharacters.CarriageReturn: {\n this.discardTrailingNewline = true\n break\n }\n\n case ControlCharacters.NewLine: {\n lineEnd = this.position\n break\n }\n }\n }\n\n if (lineEnd === -1) {\n break\n }\n\n this.processLine(\n this.buffer.subarray(lineStart, lineEnd),\n this.fieldLength!,\n )\n\n lineStart = this.position\n this.fieldLength = -1\n }\n\n if (lineStart === bufferLength) {\n this.buffer = undefined\n } else if (lineStart !== 0) {\n this.buffer = this.buffer.subarray(lineStart)\n this.position -= lineStart\n }\n }\n\n private processLine(line: Uint8Array, fieldLength: number): void {\n // New line indicates the end of the message. Dispatch it.\n if (line.length === 0) {\n // Prevent dispatching the message if the data is an empty string.\n // That is a no-op per spec.\n if (this.message.data === undefined) {\n this.message.event = undefined\n return\n }\n\n this.underlyingSink.message(this.message)\n this.resetMessage()\n return\n }\n\n // Otherwise, keep accumulating message fields until the new line.\n if (fieldLength > 0) {\n const field = this.decoder.decode(line.subarray(0, fieldLength))\n const valueOffset =\n fieldLength +\n (line[fieldLength + 1] === ControlCharacters.Space ? 2 : 1)\n const value = this.decoder.decode(line.subarray(valueOffset))\n\n switch (field) {\n case 'data': {\n this.message.data = this.message.data\n ? this.message.data + '\\n' + value\n : value\n break\n }\n\n case 'event': {\n this.message.event = value\n break\n }\n\n case 'id': {\n this.message.id = value\n break\n }\n\n case 'retry': {\n const retry = parseInt(value, 10)\n\n if (!isNaN(retry)) {\n this.message.retry = retry\n }\n break\n }\n }\n }\n }\n}\n"],"mappings":"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","names":["ControlCharacters"]}