UNPKG

@mswjs/interceptors

Version:

Low-level HTTP/HTTPS/XHR/fetch request interception library.

1 lines 48.1 kB
{"version":3,"file":"index.mjs","names":["kEmitter","kBoundListener","socket: WebSocket","transport: WebSocketTransport","client: WebSocketOverride","transport: WebSocketClassTransport","createConnection: () => WebSocket","socket: WebSocketOverride"],"sources":["../../../../src/interceptors/WebSocket/utils/bindEvent.ts","../../../../src/interceptors/WebSocket/utils/events.ts","../../../../src/interceptors/WebSocket/WebSocketClientConnection.ts","../../../../src/interceptors/WebSocket/WebSocketOverride.ts","../../../../src/interceptors/WebSocket/WebSocketServerConnection.ts","../../../../src/interceptors/WebSocket/WebSocketClassTransport.ts","../../../../src/interceptors/WebSocket/index.ts"],"sourcesContent":["type EventWithTarget<E extends Event, T> = E & { target: T }\n\nexport function bindEvent<E extends Event, T>(\n target: T,\n event: E\n): EventWithTarget<E, T> {\n Object.defineProperties(event, {\n target: {\n value: target,\n enumerable: true,\n writable: true,\n },\n currentTarget: {\n value: target,\n enumerable: true,\n writable: true,\n },\n })\n\n return event as EventWithTarget<E, T>\n}\n","const kCancelable = Symbol('kCancelable')\nconst kDefaultPrevented = Symbol('kDefaultPrevented')\n\n/**\n * A `MessageEvent` superset that supports event cancellation\n * in Node.js. It's rather non-intrusive so it can be safely\n * used in the browser as well.\n *\n * @see https://github.com/nodejs/node/issues/51767\n */\nexport class CancelableMessageEvent<T = any> extends MessageEvent<T> {\n [kCancelable]: boolean;\n [kDefaultPrevented]: boolean\n\n constructor(type: string, init: MessageEventInit<T>) {\n super(type, init)\n this[kCancelable] = !!init.cancelable\n this[kDefaultPrevented] = false\n }\n\n get cancelable() {\n return this[kCancelable]\n }\n\n set cancelable(nextCancelable) {\n this[kCancelable] = nextCancelable\n }\n\n get defaultPrevented() {\n return this[kDefaultPrevented]\n }\n\n set defaultPrevented(nextDefaultPrevented) {\n this[kDefaultPrevented] = nextDefaultPrevented\n }\n\n public preventDefault(): void {\n if (this.cancelable && !this[kDefaultPrevented]) {\n this[kDefaultPrevented] = true\n }\n }\n}\n\ninterface CloseEventInit extends EventInit {\n code?: number\n reason?: string\n wasClean?: boolean\n}\n\nexport class CloseEvent extends Event {\n public code: number\n public reason: string\n public wasClean: boolean\n\n constructor(type: string, init: CloseEventInit = {}) {\n super(type, init)\n this.code = init.code === undefined ? 0 : init.code\n this.reason = init.reason === undefined ? '' : init.reason\n this.wasClean = init.wasClean === undefined ? false : init.wasClean\n }\n}\n\nexport class CancelableCloseEvent extends CloseEvent {\n [kCancelable]: boolean;\n [kDefaultPrevented]: boolean\n\n constructor(type: string, init: CloseEventInit = {}) {\n super(type, init)\n this[kCancelable] = !!init.cancelable\n this[kDefaultPrevented] = false\n }\n\n get cancelable() {\n return this[kCancelable]\n }\n\n set cancelable(nextCancelable) {\n this[kCancelable] = nextCancelable\n }\n\n get defaultPrevented() {\n return this[kDefaultPrevented]\n }\n\n set defaultPrevented(nextDefaultPrevented) {\n this[kDefaultPrevented] = nextDefaultPrevented\n }\n\n public preventDefault(): void {\n if (this.cancelable && !this[kDefaultPrevented]) {\n this[kDefaultPrevented] = true\n }\n }\n}\n","import type { WebSocketData, WebSocketTransport } from './WebSocketTransport'\nimport type { WebSocketEventListener } from './WebSocketOverride'\nimport { bindEvent } from './utils/bindEvent'\nimport { CancelableMessageEvent, CloseEvent } from './utils/events'\nimport { createRequestId } from '../../createRequestId'\n\nconst kEmitter = Symbol('kEmitter')\nconst kBoundListener = Symbol('kBoundListener')\n\nexport interface WebSocketClientEventMap {\n message: MessageEvent<WebSocketData>\n close: CloseEvent\n}\n\nexport abstract class WebSocketClientConnectionProtocol {\n abstract id: string\n abstract url: URL\n public abstract send(data: WebSocketData): void\n public abstract close(code?: number, reason?: string): void\n\n public abstract addEventListener<\n EventType extends keyof WebSocketClientEventMap,\n >(\n type: EventType,\n listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,\n options?: AddEventListenerOptions | boolean\n ): void\n\n public abstract removeEventListener<\n EventType extends keyof WebSocketClientEventMap,\n >(\n event: EventType,\n listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,\n options?: EventListenerOptions | boolean\n ): void\n}\n\n/**\n * The WebSocket client instance represents an incoming\n * client connection. The user can control the connection,\n * send and receive events.\n */\nexport class WebSocketClientConnection implements WebSocketClientConnectionProtocol {\n public readonly id: string\n public readonly url: URL\n\n private [kEmitter]: EventTarget\n\n constructor(\n public readonly socket: WebSocket,\n private readonly transport: WebSocketTransport\n ) {\n this.id = createRequestId()\n this.url = new URL(socket.url)\n this[kEmitter] = new EventTarget()\n\n // Emit outgoing client data (\"ws.send()\") as \"message\"\n // events on the \"client\" connection.\n this.transport.addEventListener('outgoing', (event) => {\n const message = bindEvent(\n this.socket,\n new CancelableMessageEvent('message', {\n data: event.data,\n origin: event.origin,\n cancelable: true,\n })\n )\n\n this[kEmitter].dispatchEvent(message)\n\n // This is a bit silly but forward the cancellation state\n // of the \"client\" message event to the \"outgoing\" transport event.\n // This way, other agens (like \"server\" connection) can know\n // whether the client listener has pervented the default.\n if (message.defaultPrevented) {\n event.preventDefault()\n }\n })\n\n /**\n * Emit the \"close\" event on the \"client\" connection\n * whenever the underlying transport is closed.\n * @note \"client.close()\" does NOT dispatch the \"close\"\n * event on the WebSocket because it uses non-configurable\n * close status code. Thus, we listen to the transport\n * instead of the WebSocket's \"close\" event.\n */\n this.transport.addEventListener('close', (event) => {\n this[kEmitter].dispatchEvent(\n bindEvent(this.socket, new CloseEvent('close', event))\n )\n })\n }\n\n /**\n * Listen for the outgoing events from the connected WebSocket client.\n */\n public addEventListener<EventType extends keyof WebSocketClientEventMap>(\n type: EventType,\n listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,\n options?: AddEventListenerOptions | boolean\n ): void {\n if (!Reflect.has(listener, kBoundListener)) {\n const boundListener = listener.bind(this.socket)\n\n // Store the bound listener on the original listener\n // so the exact bound function can be accessed in \"removeEventListener()\".\n Object.defineProperty(listener, kBoundListener, {\n value: boundListener,\n enumerable: false,\n configurable: false,\n })\n }\n\n this[kEmitter].addEventListener(\n type,\n Reflect.get(listener, kBoundListener) as EventListener,\n options\n )\n }\n\n /**\n * Removes the listener for the given event.\n */\n public removeEventListener<EventType extends keyof WebSocketClientEventMap>(\n event: EventType,\n listener: WebSocketEventListener<WebSocketClientEventMap[EventType]>,\n options?: EventListenerOptions | boolean\n ): void {\n this[kEmitter].removeEventListener(\n event,\n Reflect.get(listener, kBoundListener) as EventListener,\n options\n )\n }\n\n /**\n * Send data to the connected client.\n */\n public send(data: WebSocketData): void {\n this.transport.send(data)\n }\n\n /**\n * Close the WebSocket connection.\n * @param {number} code A status code (see https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1).\n * @param {string} reason A custom connection close reason.\n */\n public close(code?: number, reason?: string): void {\n this.transport.close(code, reason)\n }\n}\n","import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport type { WebSocketData } from './WebSocketTransport'\nimport { bindEvent } from './utils/bindEvent'\nimport { CloseEvent } from './utils/events'\nimport { resolveWebSocketUrl } from '../../utils/resolveWebSocketUrl'\n\nexport type WebSocketEventListener<\n EventType extends WebSocketEventMap[keyof WebSocketEventMap] = Event,\n> = (this: WebSocket, event: EventType) => void\n\nconst WEBSOCKET_CLOSE_CODE_RANGE_ERROR =\n 'InvalidAccessError: close code out of user configurable range'\n\nexport const kPassthroughPromise = Symbol('kPassthroughPromise')\nexport const kOnSend = Symbol('kOnSend')\nexport const kClose = Symbol('kClose')\n\nexport class WebSocketOverride extends EventTarget implements WebSocket {\n static readonly CONNECTING = 0\n static readonly OPEN = 1\n static readonly CLOSING = 2\n static readonly CLOSED = 3\n readonly CONNECTING = 0\n readonly OPEN = 1\n readonly CLOSING = 2\n readonly CLOSED = 3\n\n public url: string\n public protocol: string\n public extensions: string\n public binaryType: BinaryType\n public readyState: WebSocket['readyState']\n public bufferedAmount: number\n\n private _onopen: WebSocketEventListener | null = null\n private _onmessage: WebSocketEventListener<\n MessageEvent<WebSocketData>\n > | null = null\n private _onerror: WebSocketEventListener | null = null\n private _onclose: WebSocketEventListener<CloseEvent> | null = null\n\n private [kPassthroughPromise]: DeferredPromise<boolean>\n private [kOnSend]?: (data: WebSocketData) => void\n\n constructor(url: string | URL, protocols?: string | Array<string>) {\n super()\n this.url = resolveWebSocketUrl(url)\n this.protocol = ''\n this.extensions = ''\n this.binaryType = 'blob'\n this.readyState = this.CONNECTING\n this.bufferedAmount = 0\n\n this[kPassthroughPromise] = new DeferredPromise<boolean>()\n\n queueMicrotask(async () => {\n if (await this[kPassthroughPromise]) {\n return\n }\n\n this.protocol =\n typeof protocols === 'string'\n ? protocols\n : Array.isArray(protocols) && protocols.length > 0\n ? protocols[0]\n : ''\n\n /**\n * @note Check that nothing has prevented this connection\n * (e.g. called `client.close()` in the connection listener).\n * If the connection has been prevented, never dispatch the open event,.\n */\n if (this.readyState === this.CONNECTING) {\n this.readyState = this.OPEN\n this.dispatchEvent(bindEvent(this, new Event('open')))\n }\n })\n }\n\n set onopen(listener: WebSocketEventListener | null) {\n this.removeEventListener('open', this._onopen)\n this._onopen = listener\n if (listener !== null) {\n this.addEventListener('open', listener)\n }\n }\n get onopen(): WebSocketEventListener | null {\n return this._onopen\n }\n\n set onmessage(\n listener: WebSocketEventListener<MessageEvent<WebSocketData>> | null\n ) {\n this.removeEventListener(\n 'message',\n this._onmessage as WebSocketEventListener\n )\n this._onmessage = listener\n if (listener !== null) {\n this.addEventListener('message', listener)\n }\n }\n get onmessage(): WebSocketEventListener<MessageEvent<WebSocketData>> | null {\n return this._onmessage\n }\n\n set onerror(listener: WebSocketEventListener | null) {\n this.removeEventListener('error', this._onerror)\n this._onerror = listener\n if (listener !== null) {\n this.addEventListener('error', listener)\n }\n }\n get onerror(): WebSocketEventListener | null {\n return this._onerror\n }\n\n set onclose(listener: WebSocketEventListener<CloseEvent> | null) {\n this.removeEventListener('close', this._onclose as WebSocketEventListener)\n this._onclose = listener\n if (listener !== null) {\n this.addEventListener('close', listener)\n }\n }\n get onclose(): WebSocketEventListener<CloseEvent> | null {\n return this._onclose\n }\n\n /**\n * @see https://websockets.spec.whatwg.org/#ref-for-dom-websocket-send%E2%91%A0\n */\n public send(data: WebSocketData): void {\n if (this.readyState === this.CONNECTING) {\n this.close()\n throw new DOMException('InvalidStateError')\n }\n\n // Sending when the socket is about to close\n // discards the sent data.\n if (this.readyState === this.CLOSING || this.readyState === this.CLOSED) {\n return\n }\n\n // Buffer the data to send in this even loop\n // but send it in the next.\n this.bufferedAmount += getDataSize(data)\n\n queueMicrotask(() => {\n // This is a bit optimistic but since no actual data transfer\n // is involved, all the data will be \"sent\" on the next tick.\n this.bufferedAmount = 0\n\n /**\n * @note Notify the parent about outgoing data.\n * This notifies the transport and the connection\n * listens to the outgoing data to emit the \"message\" event.\n */\n this[kOnSend]?.(data)\n })\n }\n\n public close(code: number = 1000, reason?: string): void {\n invariant(code, WEBSOCKET_CLOSE_CODE_RANGE_ERROR)\n invariant(\n code === 1000 || (code >= 3000 && code <= 4999),\n WEBSOCKET_CLOSE_CODE_RANGE_ERROR\n )\n\n this[kClose](code, reason)\n }\n\n private [kClose](\n code: number = 1000,\n reason?: string,\n wasClean = true\n ): void {\n /**\n * @note Move this check here so that even internal closures,\n * like those triggered by the `server` connection, are not\n * performed twice.\n */\n if (this.readyState === this.CLOSING || this.readyState === this.CLOSED) {\n return\n }\n\n this.readyState = this.CLOSING\n\n queueMicrotask(() => {\n this.readyState = this.CLOSED\n\n this.dispatchEvent(\n bindEvent(\n this,\n new CloseEvent('close', {\n code,\n reason,\n wasClean,\n })\n )\n )\n\n // Remove all event listeners once the socket is closed.\n this._onopen = null\n this._onmessage = null\n this._onerror = null\n this._onclose = null\n })\n }\n\n public addEventListener<K extends keyof WebSocketEventMap>(\n type: K,\n listener: (this: WebSocket, event: WebSocketEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions\n ): void\n public addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void\n public addEventListener(\n type: unknown,\n listener: unknown,\n options?: unknown\n ): void {\n return super.addEventListener(\n type as string,\n listener as EventListener,\n options as AddEventListenerOptions\n )\n }\n\n removeEventListener<K extends keyof WebSocketEventMap>(\n type: K,\n callback: EventListenerOrEventListenerObject | null,\n options?: boolean | EventListenerOptions\n ): void {\n return super.removeEventListener(type, callback, options)\n }\n}\n\nfunction getDataSize(data: WebSocketData): number {\n if (typeof data === 'string') {\n return data.length\n }\n\n if (data instanceof Blob) {\n return data.size\n }\n\n return data.byteLength\n}\n","import { invariant } from 'outvariant'\nimport {\n kClose,\n WebSocketEventListener,\n WebSocketOverride,\n} from './WebSocketOverride'\nimport type { WebSocketData } from './WebSocketTransport'\nimport type { WebSocketClassTransport } from './WebSocketClassTransport'\nimport { bindEvent } from './utils/bindEvent'\nimport {\n CancelableMessageEvent,\n CancelableCloseEvent,\n CloseEvent,\n} from './utils/events'\n\nconst kEmitter = Symbol('kEmitter')\nconst kBoundListener = Symbol('kBoundListener')\nconst kSend = Symbol('kSend')\n\nexport interface WebSocketServerEventMap {\n open: Event\n message: MessageEvent<WebSocketData>\n error: Event\n close: CloseEvent\n}\n\nexport abstract class WebSocketServerConnectionProtocol {\n public abstract connect(): void\n public abstract send(data: WebSocketData): void\n public abstract close(): void\n\n public abstract addEventListener<\n EventType extends keyof WebSocketServerEventMap,\n >(\n event: EventType,\n listener: WebSocketEventListener<WebSocketServerEventMap[EventType]>,\n options?: AddEventListenerOptions | boolean\n ): void\n\n public abstract removeEventListener<\n EventType extends keyof WebSocketServerEventMap,\n >(\n event: EventType,\n listener: WebSocketEventListener<WebSocketServerEventMap[EventType]>,\n options?: EventListenerOptions | boolean\n ): void\n}\n\n/**\n * The WebSocket server instance represents the actual production\n * WebSocket server connection. It's idle by default but you can\n * establish it by calling `server.connect()`.\n */\nexport class WebSocketServerConnection implements WebSocketServerConnectionProtocol {\n /**\n * A WebSocket instance connected to the original server.\n */\n private realWebSocket?: WebSocket\n private mockCloseController: AbortController\n private realCloseController: AbortController\n private [kEmitter]: EventTarget\n\n constructor(\n private readonly client: WebSocketOverride,\n private readonly transport: WebSocketClassTransport,\n private readonly createConnection: () => WebSocket\n ) {\n this[kEmitter] = new EventTarget()\n this.mockCloseController = new AbortController()\n this.realCloseController = new AbortController()\n\n // Automatically forward outgoing client events\n // to the actual server unless the outgoing message event\n // has been prevented. The \"outgoing\" transport event it\n // dispatched by the \"client\" connection.\n this.transport.addEventListener('outgoing', (event) => {\n // Ignore client messages if the server connection\n // hasn't been established yet. Nowhere to forward.\n if (typeof this.realWebSocket === 'undefined') {\n return\n }\n\n // Every outgoing client message can prevent this forwarding\n // by preventing the default of the outgoing message event.\n // This listener will be added before user-defined listeners,\n // so execute the logic on the next tick.\n queueMicrotask(() => {\n if (!event.defaultPrevented) {\n /**\n * @note Use the internal send mechanism so consumers can tell\n * apart direct user calls to `server.send()` and internal calls.\n * E.g. MSW has to ignore this internal call to log out messages correctly.\n */\n this[kSend](event.data)\n }\n })\n })\n\n this.transport.addEventListener(\n 'incoming',\n this.handleIncomingMessage.bind(this)\n )\n }\n\n /**\n * The `WebSocket` instance connected to the original server.\n * Accessing this before calling `server.connect()` will throw.\n */\n public get socket(): WebSocket {\n invariant(\n this.realWebSocket,\n 'Cannot access \"socket\" on the original WebSocket server object: the connection is not open. Did you forget to call `server.connect()`?'\n )\n\n return this.realWebSocket\n }\n\n /**\n * Open connection to the original WebSocket server.\n */\n public connect(): void {\n invariant(\n !this.realWebSocket || this.realWebSocket.readyState !== WebSocket.OPEN,\n 'Failed to call \"connect()\" on the original WebSocket instance: the connection already open'\n )\n\n const realWebSocket = this.createConnection()\n\n // Inherit the binary type from the mock WebSocket client.\n realWebSocket.binaryType = this.client.binaryType\n\n // Allow the interceptor to listen to when the server connection\n // has been established. This isn't necessary to operate with the connection\n // but may be beneficial in some cases (like conditionally adding logging).\n realWebSocket.addEventListener(\n 'open',\n (event) => {\n this[kEmitter].dispatchEvent(\n bindEvent(this.realWebSocket!, new Event('open', event))\n )\n },\n { once: true }\n )\n\n realWebSocket.addEventListener('message', (event) => {\n // Dispatch the \"incoming\" transport event instead of\n // invoking the internal handler directly. This way,\n // anyone can listen to the \"incoming\" event but this\n // class is the one resulting in it.\n this.transport.dispatchEvent(\n bindEvent(\n this.realWebSocket!,\n new MessageEvent('incoming', {\n data: event.data,\n origin: event.origin,\n })\n )\n )\n })\n\n // Close the original connection when the mock client closes.\n // E.g. \"client.close()\" was called. This is never forwarded anywhere.\n this.client.addEventListener(\n 'close',\n (event) => {\n this.handleMockClose(event)\n },\n {\n signal: this.mockCloseController.signal,\n }\n )\n\n // Forward the \"close\" event to let the interceptor handle\n // closures initiated by the original server.\n realWebSocket.addEventListener(\n 'close',\n (event) => {\n this.handleRealClose(event)\n },\n {\n signal: this.realCloseController.signal,\n }\n )\n\n realWebSocket.addEventListener('error', () => {\n const errorEvent = bindEvent(\n realWebSocket,\n new Event('error', { cancelable: true })\n )\n\n // Emit the \"error\" event on the `server` connection\n // to let the interceptor react to original server errors.\n this[kEmitter].dispatchEvent(errorEvent)\n\n // If the error event from the original server hasn't been prevented,\n // forward it to the underlying client.\n if (!errorEvent.defaultPrevented) {\n this.client.dispatchEvent(bindEvent(this.client, new Event('error')))\n }\n })\n\n this.realWebSocket = realWebSocket\n }\n\n /**\n * Listen for the incoming events from the original WebSocket server.\n */\n public addEventListener<EventType extends keyof WebSocketServerEventMap>(\n event: EventType,\n listener: WebSocketEventListener<WebSocketServerEventMap[EventType]>,\n options?: AddEventListenerOptions | boolean\n ): void {\n if (!Reflect.has(listener, kBoundListener)) {\n const boundListener = listener.bind(this.client)\n\n // Store the bound listener on the original listener\n // so the exact bound function can be accessed in \"removeEventListener()\".\n Object.defineProperty(listener, kBoundListener, {\n value: boundListener,\n enumerable: false,\n })\n }\n\n this[kEmitter].addEventListener(\n event,\n Reflect.get(listener, kBoundListener) as EventListener,\n options\n )\n }\n\n /**\n * Remove the listener for the given event.\n */\n public removeEventListener<EventType extends keyof WebSocketServerEventMap>(\n event: EventType,\n listener: WebSocketEventListener<WebSocketServerEventMap[EventType]>,\n options?: EventListenerOptions | boolean\n ): void {\n this[kEmitter].removeEventListener(\n event,\n Reflect.get(listener, kBoundListener) as EventListener,\n options\n )\n }\n\n /**\n * Send data to the original WebSocket server.\n * @example\n * server.send('hello')\n * server.send(new Blob(['hello']))\n * server.send(new TextEncoder().encode('hello'))\n */\n public send(data: WebSocketData): void {\n this[kSend](data)\n }\n\n private [kSend](data: WebSocketData): void {\n const { realWebSocket } = this\n\n invariant(\n realWebSocket,\n 'Failed to call \"server.send()\" for \"%s\": the connection is not open. Did you forget to call \"server.connect()\"?',\n this.client.url\n )\n\n // Silently ignore writes on the closed original WebSocket.\n if (\n realWebSocket.readyState === WebSocket.CLOSING ||\n realWebSocket.readyState === WebSocket.CLOSED\n ) {\n return\n }\n\n // Delegate the send to when the original connection is open.\n // Unlike the mock, connecting to the original server may take time\n // so we cannot call this on the next tick.\n if (realWebSocket.readyState === WebSocket.CONNECTING) {\n realWebSocket.addEventListener(\n 'open',\n () => {\n realWebSocket.send(data)\n },\n { once: true }\n )\n return\n }\n\n // Send the data to the original WebSocket server.\n realWebSocket.send(data)\n }\n\n /**\n * Close the actual server connection.\n */\n public close(): void {\n const { realWebSocket } = this\n\n invariant(\n realWebSocket,\n 'Failed to close server connection for \"%s\": the connection is not open. Did you forget to call \"server.connect()\"?',\n this.client.url\n )\n\n // Remove the \"close\" event listener from the server\n // so it doesn't close the underlying WebSocket client\n // when you call \"server.close()\". This also prevents the\n // `close` event on the `server` connection from being dispatched twice.\n this.realCloseController.abort()\n\n if (\n realWebSocket.readyState === WebSocket.CLOSING ||\n realWebSocket.readyState === WebSocket.CLOSED\n ) {\n return\n }\n\n // Close the actual client connection.\n realWebSocket.close()\n\n // Dispatch the \"close\" event on the `server` connection.\n queueMicrotask(() => {\n this[kEmitter].dispatchEvent(\n bindEvent(\n this.realWebSocket,\n new CancelableCloseEvent('close', {\n /**\n * @note `server.close()` in the interceptor\n * always results in clean closures.\n */\n code: 1000,\n cancelable: true,\n })\n )\n )\n })\n }\n\n private handleIncomingMessage(event: MessageEvent<WebSocketData>): void {\n // Clone the event to dispatch it on this class\n // once again and prevent the \"already being dispatched\"\n // exception. Clone it here so we can observe this event\n // being prevented in the \"server.on()\" listeners.\n const messageEvent = bindEvent(\n event.target,\n new CancelableMessageEvent('message', {\n data: event.data,\n origin: event.origin,\n cancelable: true,\n })\n )\n\n /**\n * @note Emit \"message\" event on the server connection\n * instance to let the interceptor know about these\n * incoming events from the original server. In that listener,\n * the interceptor can modify or skip the event forwarding\n * to the mock WebSocket instance.\n */\n this[kEmitter].dispatchEvent(messageEvent)\n\n /**\n * @note Forward the incoming server events to the client.\n * Preventing the default on the message event stops this.\n */\n if (!messageEvent.defaultPrevented) {\n this.client.dispatchEvent(\n bindEvent(\n /**\n * @note Bind the forwarded original server events\n * to the mock WebSocket instance so it would\n * dispatch them straight away.\n */\n this.client,\n // Clone the message event again to prevent\n // the \"already being dispatched\" exception.\n new MessageEvent('message', {\n data: event.data,\n origin: event.origin,\n })\n )\n )\n }\n }\n\n private handleMockClose(_event: Event): void {\n // Close the original connection if the mock client closes.\n if (this.realWebSocket) {\n this.realWebSocket.close()\n }\n }\n\n private handleRealClose(event: CloseEvent): void {\n // For closures originating from the original server,\n // remove the \"close\" listener from the mock client.\n // original close -> (?) client[kClose]() --X--> \"close\" (again).\n this.mockCloseController.abort()\n\n const closeEvent = bindEvent(\n this.realWebSocket,\n new CancelableCloseEvent('close', {\n code: event.code,\n reason: event.reason,\n wasClean: event.wasClean,\n cancelable: true,\n })\n )\n\n this[kEmitter].dispatchEvent(closeEvent)\n\n // If the close event from the server hasn't been prevented,\n // forward the closure to the mock client.\n if (!closeEvent.defaultPrevented) {\n // Close the intercepted client forcefully to\n // allow non-configurable status codes from the server.\n // If the socket has been closed by now, no harm calling\n // this again—it will have no effect.\n this.client[kClose](event.code, event.reason)\n }\n }\n}\n","import { bindEvent } from './utils/bindEvent'\nimport {\n StrictEventListenerOrEventListenerObject,\n WebSocketData,\n WebSocketTransport,\n WebSocketTransportEventMap,\n} from './WebSocketTransport'\nimport { kOnSend, kClose, WebSocketOverride } from './WebSocketOverride'\nimport { CancelableMessageEvent, CloseEvent } from './utils/events'\n\n/**\n * Abstraction over the given mock `WebSocket` instance that allows\n * for controlling that instance (e.g. sending and receiving messages).\n */\nexport class WebSocketClassTransport\n extends EventTarget\n implements WebSocketTransport\n{\n constructor(protected readonly socket: WebSocketOverride) {\n super()\n\n // Emit the \"close\" event on the transport if the close\n // originates from the WebSocket client. E.g. the application\n // calls \"ws.close()\", not the interceptor.\n this.socket.addEventListener('close', (event) => {\n this.dispatchEvent(bindEvent(this.socket, new CloseEvent('close', event)))\n })\n\n /**\n * Emit the \"outgoing\" event on the transport\n * whenever the WebSocket client sends data (\"ws.send()\").\n */\n this.socket[kOnSend] = (data) => {\n this.dispatchEvent(\n bindEvent(\n this.socket,\n // Dispatch this as cancelable because \"client\" connection\n // re-creates this message event (cannot dispatch the same event).\n new CancelableMessageEvent('outgoing', {\n data,\n origin: this.socket.url,\n cancelable: true,\n })\n )\n )\n }\n }\n\n public addEventListener<EventType extends keyof WebSocketTransportEventMap>(\n type: EventType,\n callback: StrictEventListenerOrEventListenerObject<\n WebSocketTransportEventMap[EventType]\n > | null,\n options?: boolean | AddEventListenerOptions\n ): void {\n return super.addEventListener(type, callback as EventListener, options)\n }\n\n public dispatchEvent<EventType extends keyof WebSocketTransportEventMap>(\n event: WebSocketTransportEventMap[EventType]\n ): boolean {\n return super.dispatchEvent(event)\n }\n\n public send(data: WebSocketData): void {\n queueMicrotask(() => {\n if (\n this.socket.readyState === this.socket.CLOSING ||\n this.socket.readyState === this.socket.CLOSED\n ) {\n return\n }\n\n const dispatchEvent = () => {\n this.socket.dispatchEvent(\n bindEvent(\n /**\n * @note Setting this event's \"target\" to the\n * WebSocket override instance is important.\n * This way it can tell apart original incoming events\n * (must be forwarded to the transport) from the\n * mocked message events like the one below\n * (must be dispatched on the client instance).\n */\n this.socket,\n new MessageEvent('message', {\n data,\n origin: this.socket.url,\n })\n )\n )\n }\n\n if (this.socket.readyState === this.socket.CONNECTING) {\n this.socket.addEventListener(\n 'open',\n () => {\n dispatchEvent()\n },\n { once: true }\n )\n } else {\n dispatchEvent()\n }\n })\n }\n\n public close(code: number, reason?: string): void {\n /**\n * @note Call the internal close method directly\n * to allow closing the connection with the status codes\n * that are non-configurable by the user (> 1000 <= 1015).\n */\n this.socket[kClose](code, reason)\n }\n}\n","import { Interceptor } from '../../Interceptor'\nimport {\n WebSocketClientConnectionProtocol,\n WebSocketClientConnection,\n type WebSocketClientEventMap,\n} from './WebSocketClientConnection'\nimport {\n WebSocketServerConnectionProtocol,\n WebSocketServerConnection,\n type WebSocketServerEventMap,\n} from './WebSocketServerConnection'\nimport { WebSocketClassTransport } from './WebSocketClassTransport'\nimport {\n kClose,\n kPassthroughPromise,\n WebSocketOverride,\n} from './WebSocketOverride'\nimport { bindEvent } from './utils/bindEvent'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { emitAsync } from '../../utils/emitAsync'\n\nexport {\n type WebSocketData,\n type WebSocketTransport,\n} from './WebSocketTransport'\nexport {\n WebSocketClientEventMap,\n WebSocketClientConnectionProtocol,\n WebSocketClientConnection,\n WebSocketServerEventMap,\n WebSocketServerConnectionProtocol,\n WebSocketServerConnection,\n}\n\nexport {\n CloseEvent,\n CancelableCloseEvent,\n CancelableMessageEvent,\n} from './utils/events'\n\nexport type WebSocketEventMap = {\n connection: [args: WebSocketConnectionData]\n}\n\nexport type WebSocketConnectionData = {\n /**\n * The incoming WebSocket client connection.\n */\n client: WebSocketClientConnection\n\n /**\n * The original WebSocket server connection.\n */\n server: WebSocketServerConnection\n\n /**\n * The connection information.\n */\n info: {\n /**\n * The protocols supported by the WebSocket client.\n */\n protocols: string | Array<string> | undefined\n }\n}\n\n/**\n * Intercept the outgoing WebSocket connections created using\n * the global `WebSocket` class.\n */\nexport class WebSocketInterceptor extends Interceptor<WebSocketEventMap> {\n static symbol = Symbol('websocket')\n\n constructor() {\n super(WebSocketInterceptor.symbol)\n }\n\n protected checkEnvironment(): boolean {\n return hasConfigurableGlobal('WebSocket')\n }\n\n protected setup(): void {\n const originalWebSocketDescriptor = Object.getOwnPropertyDescriptor(\n globalThis,\n 'WebSocket'\n )\n\n const WebSocketProxy = new Proxy(globalThis.WebSocket, {\n construct: (\n target,\n args: ConstructorParameters<typeof globalThis.WebSocket>,\n newTarget\n ) => {\n const [url, protocols] = args\n\n const createConnection = (): WebSocket => {\n return Reflect.construct(target, args, newTarget)\n }\n\n // All WebSocket instances are mocked and don't forward\n // any events to the original server (no connection established).\n // To forward the events, the user must use the \"server.send()\" API.\n const socket = new WebSocketOverride(url, protocols)\n const transport = new WebSocketClassTransport(socket)\n\n // Emit the \"connection\" event to the interceptor on the next tick\n // so the client can modify WebSocket options, like \"binaryType\"\n // while the connection is already pending.\n queueMicrotask(async () => {\n try {\n const server = new WebSocketServerConnection(\n socket,\n transport,\n createConnection\n )\n\n const hasConnectionListeners =\n this.emitter.listenerCount('connection') > 0\n\n // The \"globalThis.WebSocket\" class stands for\n // the client-side connection. Assume it's established\n // as soon as the WebSocket instance is constructed.\n await emitAsync(this.emitter, 'connection', {\n client: new WebSocketClientConnection(socket, transport),\n server,\n info: {\n protocols,\n },\n })\n\n if (hasConnectionListeners) {\n socket[kPassthroughPromise].resolve(false)\n } else {\n socket[kPassthroughPromise].resolve(true)\n\n server.connect()\n\n // Forward the \"open\" event from the original server\n // to the mock WebSocket client in the case of a passthrough connection.\n server.addEventListener('open', () => {\n socket.dispatchEvent(bindEvent(socket, new Event('open')))\n\n // Forward the original connection protocol to the\n // mock WebSocket client.\n if (server['realWebSocket']) {\n socket.protocol = server['realWebSocket'].protocol\n }\n })\n }\n } catch (error) {\n /**\n * @note Translate unhandled exceptions during the connection\n * handling (i.e. interceptor exceptions) as WebSocket connection\n * closures with error. This prevents from the exceptions occurring\n * in `queueMicrotask` from being process-wide and uncatchable.\n */\n if (error instanceof Error) {\n socket.dispatchEvent(new Event('error'))\n\n // No need to close the connection if it's already being closed.\n // E.g. the interceptor called `client.close()` and then threw an error.\n if (\n socket.readyState !== WebSocket.CLOSING &&\n socket.readyState !== WebSocket.CLOSED\n ) {\n socket[kClose](1011, error.message, false)\n }\n\n console.error(error)\n }\n }\n })\n\n return socket\n },\n })\n\n Object.defineProperty(globalThis, 'WebSocket', {\n value: WebSocketProxy,\n configurable: true,\n })\n\n this.subscriptions.push(() => {\n Object.defineProperty(\n globalThis,\n 'WebSocket',\n originalWebSocketDescriptor!\n )\n })\n }\n}\n"],"mappings":"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"}