msw
Version:
1 lines • 28 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/core/handlers/RequestHandler.ts"],"sourcesContent":["import { Headers as HeadersPolyfill } from 'headers-polyfill'\nimport { getCallFrame } from '../utils/internal/getCallFrame'\nimport {\n isIterable,\n type AsyncIterable,\n type Iterable,\n} from '../utils/internal/isIterable'\nimport type { ResponseResolutionContext } from '../utils/executeHandlers'\nimport type { MaybePromise } from '../typeUtils'\nimport type { HttpResponse } from '../HttpResponse'\nimport {\n type StrictRequest,\n type DefaultUnsafeFetchResponse,\n} from '../HttpResponse'\nimport type { GraphQLRequestBody } from './GraphQLHandler'\nimport { devUtils } from '../utils/internal/devUtils'\nimport { getRawSetCookie } from '../utils/HttpResponse/decorators'\nimport { observeResponseBodyStream } from '../utils/internal/observe-response-body-stream'\n\nexport type DefaultRequestMultipartBody = Record<\n string,\n string | File | Array<string | File>\n>\n\nexport type DefaultBodyType =\n | Record<string, any>\n | DefaultRequestMultipartBody\n | string\n | number\n | boolean\n | null\n | undefined\n\nexport type JsonBodyType =\n Record<string, any> | string | number | boolean | null | undefined\n\nexport interface RequestHandlerDefaultInfo {\n header: string\n}\n\nexport interface RequestHandlerInternalInfo {\n callFrame?: string\n}\n\nexport type ResponseResolverReturnType<\n ResponseBodyType extends DefaultBodyType = undefined,\n> =\n // If ResponseBodyType is a union and one of the types is `undefined`,\n // allow plain Response as the type.\n | ([ResponseBodyType] extends [undefined]\n ? Response\n : /**\n * Treat GraphQL response body type as a special case.\n * For esome reason, making the default HttpResponse<T> | DefaultUnsafeFetchResponse\n * union breaks the body type inference for HTTP requests.\n * @see https://github.com/mswjs/msw/issues/2130\n */\n ResponseBodyType extends GraphQLRequestBody<any>\n ? HttpResponse<ResponseBodyType> | DefaultUnsafeFetchResponse\n : HttpResponse<ResponseBodyType>)\n | undefined\n | void\n\nexport type MaybeAsyncResponseResolverReturnType<\n ResponseBodyType extends DefaultBodyType,\n> = MaybePromise<ResponseResolverReturnType<ResponseBodyType>>\n\nexport type AsyncResponseResolverReturnType<\n ResponseBodyType extends DefaultBodyType,\n> = MaybePromise<\n | ResponseResolverReturnType<ResponseBodyType>\n | Iterable<\n MaybeAsyncResponseResolverReturnType<ResponseBodyType>,\n MaybeAsyncResponseResolverReturnType<ResponseBodyType>,\n MaybeAsyncResponseResolverReturnType<ResponseBodyType>\n >\n | AsyncIterable<\n MaybeAsyncResponseResolverReturnType<ResponseBodyType>,\n MaybeAsyncResponseResolverReturnType<ResponseBodyType>,\n MaybeAsyncResponseResolverReturnType<ResponseBodyType>\n >\n>\n\nexport type ResponseResolverInfo<\n ResolverExtraInfo extends Record<string, unknown>,\n RequestBodyType extends DefaultBodyType = DefaultBodyType,\n> = {\n request: StrictRequest<RequestBodyType>\n requestId: string\n /**\n * Schedule a callback to run after this response resolver completes.\n * Handy for cleaning up the side effects introduced in the resolver.\n *\n * For responses with a `ReadableStream` body (including `sse()` handlers),\n * the callback runs once the response stream settles: it is read to\n * completion, errored, or canceled, or the request is aborted.\n * @example\n * sse('/', ({ client, finalize }) => {\n * const interval = setInterval(() => client.send({ data: 'ping' }))\n * finalize(() => clearInterval(interval))\n * })\n */\n finalize: ResponseResolverFinalizeFunction\n} & ResolverExtraInfo\n\ntype ResponseResolverFinalizeFunction = (\n callback: () => MaybePromise<void>,\n) => void\n\nexport type ResponseResolver<\n ResolverExtraInfo extends Record<string, unknown> = Record<string, unknown>,\n RequestBodyType extends DefaultBodyType = DefaultBodyType,\n ResponseBodyType extends DefaultBodyType = undefined,\n> = (\n info: ResponseResolverInfo<ResolverExtraInfo, RequestBodyType>,\n) => AsyncResponseResolverReturnType<ResponseBodyType>\n\nexport interface RequestHandlerArgs<\n HandlerInfo,\n HandlerOptions extends RequestHandlerOptions,\n> {\n info: HandlerInfo\n resolver: ResponseResolver<any>\n options?: HandlerOptions\n}\n\nexport interface RequestHandlerOptions {\n once?: boolean\n}\n\nexport interface RequestHandlerExecutionResult<\n ParsedResult extends object | undefined,\n> {\n handler: RequestHandler\n parsedResult?: ParsedResult\n request: Request\n requestId: string\n response?: Response\n}\n\nexport abstract class RequestHandler<\n HandlerInfo extends RequestHandlerDefaultInfo = RequestHandlerDefaultInfo,\n ParsedResult extends Record<string, any> | undefined = any,\n ResolverExtras extends Record<string, unknown> = any,\n HandlerOptions extends RequestHandlerOptions = RequestHandlerOptions,\n> {\n static cache = new WeakMap<\n StrictRequest<DefaultBodyType>,\n StrictRequest<DefaultBodyType>\n >()\n\n public readonly kind = 'request' as const\n\n protected resolver: ResponseResolver<ResolverExtras, any, any>\n private resolverIterator?:\n | Iterator<\n MaybeAsyncResponseResolverReturnType<any>,\n MaybeAsyncResponseResolverReturnType<any>,\n MaybeAsyncResponseResolverReturnType<any>\n >\n | AsyncIterator<\n MaybeAsyncResponseResolverReturnType<any>,\n MaybeAsyncResponseResolverReturnType<any>,\n MaybeAsyncResponseResolverReturnType<any>\n >\n private resolverIteratorResult?: Response | HttpResponse<any>\n private resolverIteratorCleanups?: Array<() => MaybePromise<void>>\n private options?: HandlerOptions\n private scheduledCleanups: Map<string, Array<() => MaybePromise<void>>>\n\n public info: HandlerInfo & RequestHandlerInternalInfo\n\n /**\n * Indicates whether this request handler has been used\n * (its resolver has successfully executed).\n */\n public isUsed: boolean\n\n constructor(args: RequestHandlerArgs<HandlerInfo, HandlerOptions>) {\n this.resolver = args.resolver\n this.options = args.options\n this.scheduledCleanups = new Map()\n\n const callFrame = getCallFrame(new Error())\n\n this.info = {\n ...args.info,\n callFrame,\n }\n\n this.isUsed = false\n }\n\n /**\n * Reset the runtime state accumulated during response resolution,\n * such as generator iterator progress. Called when this handler is\n * removed from the active handlers list so re-adding it later starts\n * from a clean state.\n */\n protected reset(): void {\n this.scheduledCleanups.clear()\n\n const iterator = this.resolverIterator\n this.resolverIterator = undefined\n this.resolverIteratorResult = undefined\n this.resolverIteratorCleanups = undefined\n\n if (typeof iterator?.return === 'function') {\n void Promise.resolve(iterator.return())\n }\n }\n\n /**\n * Restore this handler so it can match requests again after being\n * exhausted (e.g. via `{ once: true }`). Also clears any accumulated\n * resolution state.\n */\n protected restore(): void {\n if (this.options?.once) {\n this.reset()\n this.isUsed = false\n }\n }\n\n /**\n * Determine if the intercepted request should be mocked.\n */\n abstract predicate(args: {\n request: Request\n parsedResult: ParsedResult\n resolutionContext?: ResponseResolutionContext\n }): boolean | Promise<boolean>\n\n /**\n * Print out the successfully handled request.\n */\n abstract log(args: {\n request: Request\n response: Response\n parsedResult: ParsedResult\n }): void\n\n /**\n * Parse the intercepted request to extract additional information from it.\n * Parsed result is then exposed to other methods of this request handler.\n */\n async parse(_args: {\n request: Request\n resolutionContext?: ResponseResolutionContext\n }): Promise<ParsedResult> {\n return {} as ParsedResult\n }\n\n /**\n * Test if this handler matches the given request.\n *\n * This method is not used internally but is exposed\n * as a convenience method for consumers writing custom\n * handlers.\n */\n public async test(args: {\n request: Request\n resolutionContext?: ResponseResolutionContext\n }): Promise<boolean> {\n const parsedResult = await this.parse({\n request: args.request,\n resolutionContext: args.resolutionContext,\n })\n\n return this.predicate({\n request: args.request,\n parsedResult,\n resolutionContext: args.resolutionContext,\n })\n }\n\n protected extendResolverArgs(_args: {\n request: Request\n parsedResult: ParsedResult\n }): ResolverExtras {\n return {} as ResolverExtras\n }\n\n // Clone the request instance before it's passed to the handler phases\n // and the response resolver so we can always read it for logging.\n // We only clone it once per request to avoid unnecessary overhead.\n private cloneRequestOrGetFromCache(\n request: StrictRequest<DefaultBodyType>,\n ): StrictRequest<DefaultBodyType> {\n const existingClone = RequestHandler.cache.get(request)\n\n if (typeof existingClone !== 'undefined') {\n return existingClone\n }\n\n const clonedRequest = request.clone()\n RequestHandler.cache.set(request, clonedRequest)\n\n return clonedRequest\n }\n\n /**\n * Execute this request handler and produce a mocked response\n * using the given resolver function.\n */\n public async run(args: {\n request: StrictRequest<any>\n requestId: string\n resolutionContext?: ResponseResolutionContext\n }): Promise<RequestHandlerExecutionResult<ParsedResult> | null> {\n if (this.isUsed && this.options?.once) {\n return null\n }\n\n // Clone the request.\n // If this is the first time MSW handles this request, a fresh clone\n // will be created and cached. Upon further handling of the same request,\n // the request clone from the cache will be reused to prevent abundant\n // \"abort\" listeners and save up resources on cloning.\n const requestClone = this.cloneRequestOrGetFromCache(args.request)\n\n const parsedResult = await this.parse({\n request: args.request,\n resolutionContext: args.resolutionContext,\n })\n const shouldInterceptRequest = await this.predicate({\n request: args.request,\n parsedResult,\n resolutionContext: args.resolutionContext,\n })\n\n if (!shouldInterceptRequest) {\n return null\n }\n\n // Re-check isUsed, in case another request hit this handler while we were\n // asynchronously parsing the request.\n if (this.isUsed && this.options?.once) {\n return null\n }\n\n // Preemptively mark the handler as used.\n // Generators will undo this because only when the resolver reaches the\n // \"done\" state of the generator that it considers the handler used.\n this.isUsed = true\n\n // Create a response extraction wrapper around the resolver\n // since it can be both an async function and a generator.\n const executeResolver = this.wrapResolver(this.resolver)\n\n const resolverExtras = this.extendResolverArgs({\n request: args.request,\n parsedResult,\n })\n\n const listenerController = new AbortController()\n\n /**\n * @note Initialize the `finalize` machinery lazily, on the first\n * access of the `finalize` property by the resolver. If the resolver\n * never accesses it, the handler behaves as if `finalize` never\n * existed: no abort listeners, no scheduled cleanups, and no response\n * body stream observation (see `this.complete()`).\n */\n let finalizeFunction: ResponseResolverFinalizeFunction | undefined\n\n const getFinalize = (): ResponseResolverFinalizeFunction => {\n if (finalizeFunction == null) {\n // Run any scheduled cleanups if the request gets aborted\n // while the resolver is still executing.\n if (!args.request.signal.aborted) {\n args.request.signal.addEventListener(\n 'abort',\n () => this.runScheduledCleanups(args.requestId),\n {\n once: true,\n signal: listenerController.signal,\n },\n )\n }\n\n finalizeFunction = (callback) => {\n this.scheduleCleanup(args.requestId, callback)\n\n /**\n * @note Run the cleanup immediately if the request has already\n * been aborted. The \"abort\" listener above never fires for an\n * already-aborted signal (and fires at most once), while\n * long-lived resolvers (streams, generators) may never settle\n * to run the cleanups on completion.\n */\n if (args.request.signal.aborted) {\n void this.runScheduledCleanups(args.requestId)\n }\n }\n }\n\n return finalizeFunction\n }\n\n const mockedResponsePromise = (\n executeResolver({\n ...resolverExtras,\n get finalize(): ResponseResolverFinalizeFunction {\n return getFinalize()\n },\n requestId: args.requestId,\n request: args.request,\n }) as Promise<Response>\n )\n .catch((errorOrResponse) => {\n // Allow throwing a Response instance in a response resolver.\n if (errorOrResponse instanceof Response) {\n return errorOrResponse\n }\n\n // Otherwise, throw the error as-is.\n throw errorOrResponse\n })\n .finally(() => {\n listenerController.abort()\n })\n\n const mockedResponse = await mockedResponsePromise\n\n if (mockedResponse) {\n forwardResponseCookies(mockedResponse)\n }\n\n const executionResult = this.createExecutionResult({\n // Pass the cloned request to the result so that logging\n // and other consumers could read its body once more.\n request: requestClone,\n requestId: args.requestId,\n response: mockedResponse,\n parsedResult,\n })\n\n return executionResult\n }\n\n private wrapResolver(\n resolver: ResponseResolver<ResolverExtras>,\n ): ResponseResolver<ResolverExtras> {\n return async (info): Promise<ResponseResolverReturnType<any>> => {\n if (!this.resolverIterator) {\n let result\n\n try {\n result = await resolver(info)\n } catch (error) {\n // Ensure cleanup if the resolver throws, regardless of the returned type.\n await this.runScheduledCleanups(info.requestId)\n\n // Re-throw the error for the \"run()\" method to handle (e.g. thrown responses).\n throw error\n }\n\n if (!isIterable(result)) {\n // Otherwise, run the cleanups if it's a plain response resolver\n // (deferred until the response stream settles for streamed responses).\n return this.complete({\n request: info.request,\n requestId: info.requestId,\n response: result,\n })\n }\n\n /**\n * @note Carry over any previously registered cleanups onto the iterator cleanups.\n * This is only relevant if \"finalize()\" is called in a regular resolver that\n * returns an iterator.\n * @example\n * http.get('/', async ({ finalize }) => {\n * finalize(cleanup)\n * return (async function*() {})()\n * })\n */\n const existingCleanups = this.scheduledCleanups.get(info.requestId)\n if (existingCleanups != null && existingCleanups.length > 0) {\n this.resolverIteratorCleanups = existingCleanups\n this.scheduledCleanups.delete(info.requestId)\n }\n\n this.resolverIterator =\n Symbol.iterator in result\n ? result[Symbol.iterator]()\n : result[Symbol.asyncIterator]()\n }\n\n // Opt-out from marking this handler as used.\n this.isUsed = false\n\n const { done, value } = await this.resolverIterator.next()\n const nextResponse = await value\n\n if (nextResponse) {\n this.resolverIteratorResult = nextResponse.clone()\n }\n\n if (done) {\n // A one-time generator resolver stops affecting the network\n // only after it's been completely exhausted.\n this.isUsed = true\n\n // Clone the previously stored response so it can be read\n // when receiving it repeatedly from the \"done\" generator.\n return this.complete({\n request: info.request,\n requestId: info.requestId,\n response: this.resolverIteratorResult?.clone(),\n })\n }\n\n return nextResponse\n }\n }\n\n private createExecutionResult(args: {\n request: Request\n requestId: string\n parsedResult: ParsedResult\n response?: Response\n }): RequestHandlerExecutionResult<ParsedResult> {\n return {\n handler: this,\n request: args.request,\n requestId: args.requestId,\n response: args.response,\n parsedResult: args.parsedResult,\n }\n }\n\n private scheduleCleanup(\n requestId: string,\n callback: () => MaybePromise<void>,\n ): void {\n if (this.resolverIterator) {\n ;(this.resolverIteratorCleanups ||= []).unshift(callback)\n return\n }\n\n const cleanups = this.scheduledCleanups.get(requestId) || []\n cleanups.unshift(callback)\n this.scheduledCleanups.set(requestId, cleanups)\n }\n\n private async exhaustCleanups(\n cleanups: Array<() => MaybePromise<void>>,\n ): Promise<void> {\n const errors: Array<Error> = []\n\n for (const cleanup of cleanups) {\n try {\n await cleanup()\n } catch (error) {\n if (error instanceof Error) {\n errors.push(error)\n }\n }\n }\n\n if (errors.length > 0) {\n devUtils.error(\n 'Failed to execute cleanup for request handler \"%s\"',\n this.info.header,\n new AggregateError(\n errors,\n `Failed to execute cleanup for request handler \"${this.info.header}\"`,\n ),\n )\n }\n }\n\n /**\n * Remove and return the cleanups scheduled for the given request\n * (or the pending iterator cleanups for generator resolvers).\n */\n private takeScheduledCleanups(\n requestId: string,\n ): Array<() => MaybePromise<void>> | undefined {\n if (\n this.resolverIterator &&\n this.resolverIteratorCleanups != null &&\n this.resolverIteratorCleanups.length > 0\n ) {\n const cleanups = this.resolverIteratorCleanups\n this.resolverIteratorCleanups = undefined\n return cleanups\n }\n\n const cleanups = this.scheduledCleanups.get(requestId)\n\n if (!cleanups || cleanups.length === 0) {\n return undefined\n }\n\n this.scheduledCleanups.delete(requestId)\n return cleanups\n }\n\n private async runScheduledCleanups(requestId: string): Promise<void> {\n const cleanups = this.takeScheduledCleanups(requestId)\n\n if (cleanups) {\n await this.exhaustCleanups(cleanups)\n }\n }\n\n /**\n * Conclude the response resolution for the given request.\n * Runs the scheduled cleanups immediately for responses without a\n * `ReadableStream` body. For streamed responses, returns an observed\n * copy of the response and defers the cleanups until its body settles\n * (is read to completion, errored, or canceled) or the request is\n * aborted, whichever comes first.\n */\n private async complete(args: {\n request: StrictRequest<any>\n requestId: string\n response: Response | undefined | void\n }): Promise<Response | undefined | void> {\n const cleanups = this.takeScheduledCleanups(args.requestId)\n\n if (!cleanups) {\n return args.response\n }\n\n const observedResponse = args.response\n ? observeResponseBodyStream(args.response)\n : null\n\n if (!observedResponse) {\n await this.exhaustCleanups(cleanups)\n return args.response\n }\n\n const listenerController = new AbortController()\n\n const runCleanupsOnce = (): void => {\n if (listenerController.signal.aborted) {\n return\n }\n\n listenerController.abort()\n void this.exhaustCleanups(cleanups)\n }\n\n void observedResponse.settled.then(runCleanupsOnce)\n\n /**\n * @note Also run the cleanups when the request is aborted.\n * Stream cancellation does not always propagate to the observed\n * response (e.g. if an unconsumed clone of the response exists),\n * while the request abort reliably means the response is unused.\n */\n if (args.request.signal.aborted) {\n runCleanupsOnce()\n } else {\n args.request.signal.addEventListener('abort', runCleanupsOnce, {\n once: true,\n signal: listenerController.signal,\n })\n }\n\n return observedResponse.response\n }\n}\n\n/**\n * Forwards the cookies from the given response to `document.cookie`.\n */\nexport function forwardResponseCookies(response: Response): void {\n // Cookie forwarding is only relevant in the browser.\n if (typeof document === 'undefined') {\n return\n }\n\n const responseCookies = getRawSetCookie(response)\n\n if (!responseCookies) {\n return\n }\n\n // Write the mocked response cookies to the document.\n // Use `headers-polyfill` to get the Set-Cookie header value correctly.\n // This is an alternative until TypeScript 5.2\n // and Node.js v20 become the minimum supported versions\n // and \"Headers.prototype.getSetCookie\" can be used directly.\n const allResponseCookies = HeadersPolyfill.prototype.getSetCookie.call(\n new Headers([['set-cookie', responseCookies]]),\n )\n\n for (const cookieString of allResponseCookies) {\n document.cookie = cookieString\n }\n}\n"],"mappings":"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","names":["cleanups"]}