UNPKG

msw

Version:

Seamless REST/GraphQL API mocking library for browser and Node.js.

121 lines (103 loc) 3.9 kB
import { until } from '@open-draft/until' import { Emitter } from 'strict-event-emitter' import { LifeCycleEventsMap, SharedOptions } from '../sharedOptions' import { RequiredDeep } from '../typeUtils' import type { RequestHandler } from '../handlers/RequestHandler' import { HandlersExecutionResult, executeHandlers } from './executeHandlers' import { onUnhandledRequest } from './request/onUnhandledRequest' import { storeResponseCookies } from './request/storeResponseCookies' export interface HandleRequestOptions { /** * `resolutionContext` is not part of the general public api * but is exposed to aid in creating extensions like * `@mswjs/http-middleware`. */ resolutionContext?: { /** * A base url to use when resolving relative urls. * @note This is primarily used by the `@mswjs/http-middleware` * to resolve relative urls in the context of the running server */ baseUrl?: string } /** * Invoked whenever a request is performed as-is. */ onPassthroughResponse?(request: Request): void /** * Invoked when the mocked response is ready to be sent. */ onMockedResponse?( response: Response, handler: RequiredDeep<HandlersExecutionResult>, ): void } export async function handleRequest( request: Request, requestId: string, handlers: Array<RequestHandler>, options: RequiredDeep<SharedOptions>, emitter: Emitter<LifeCycleEventsMap>, handleRequestOptions?: HandleRequestOptions, ): Promise<Response | undefined> { emitter.emit('request:start', { request, requestId }) // Perform requests wrapped in "bypass()" as-is. if (request.headers.get('accept')?.includes('msw/passthrough')) { emitter.emit('request:end', { request, requestId }) handleRequestOptions?.onPassthroughResponse?.(request) return } // Resolve a mocked response from the list of request handlers. const lookupResult = await until(() => { return executeHandlers({ request, requestId, handlers, resolutionContext: handleRequestOptions?.resolutionContext, }) }) if (lookupResult.error) { // Allow developers to react to unhandled exceptions in request handlers. emitter.emit('unhandledException', { error: lookupResult.error, request, requestId, }) throw lookupResult.error } // If the handler lookup returned nothing, no request handler was found // matching this request. Report the request as unhandled. if (!lookupResult.data) { await onUnhandledRequest(request, options.onUnhandledRequest) emitter.emit('request:unhandled', { request, requestId }) emitter.emit('request:end', { request, requestId }) handleRequestOptions?.onPassthroughResponse?.(request) return } const { response } = lookupResult.data // When the handled request returned no mocked response, warn the developer, // as it may be an oversight on their part. Perform the request as-is. if (!response) { emitter.emit('request:end', { request, requestId }) handleRequestOptions?.onPassthroughResponse?.(request) return } // Perform the request as-is when the developer explicitly returned "req.passthrough()". // This produces no warning as the request was handled. if ( response.status === 302 && response.headers.get('x-msw-intention') === 'passthrough' ) { emitter.emit('request:end', { request, requestId }) handleRequestOptions?.onPassthroughResponse?.(request) return } // Store all the received response cookies in the cookie jar. storeResponseCookies(request, response) emitter.emit('request:match', { request, requestId }) const requiredLookupResult = lookupResult.data as RequiredDeep<HandlersExecutionResult> handleRequestOptions?.onMockedResponse?.(response, requiredLookupResult) emitter.emit('request:end', { request, requestId }) return response }