{"version":3,"file":"handleRequest-CvX2G-Lz.cjs","names":["InterceptorError","DeferredPromise","emitAsync","RequestController"],"sources":["../../src/utils/isObject.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts","../../src/utils/isNodeLikeError.ts","../../src/utils/handleRequest.ts"],"sourcesContent":["/**\n * Determines if a given value is an instance of object.\n */\nexport function isObject<T>(value: any, loose = false): value is T {\n  return loose\n    ? Object.prototype.toString.call(value).startsWith('[object ')\n    : Object.prototype.toString.call(value) === '[object Object]'\n}\n","/**\n * A function that validates if property access is possible on an object\n * without throwing. It returns `true` if the property access is possible\n * and `false` otherwise.\n *\n * Environments like miniflare will throw on property access on certain objects\n * like Request and Response, for unimplemented properties.\n */\nexport function isPropertyAccessible<Obj extends Record<string, any>>(\n  obj: Obj,\n  key: keyof Obj\n) {\n  try {\n    obj[key]\n    return true\n  } catch {\n    return false\n  }\n}\n","import { isObject } from './isObject'\nimport { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n  return new Response(\n    JSON.stringify(\n      body instanceof Error\n        ? {\n            name: body.name,\n            message: body.message,\n            stack: body.stack,\n          }\n        : body\n    ),\n    {\n      status: 500,\n      statusText: 'Unhandled Exception',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n    }\n  )\n}\n\nexport type ResponseError = Response & { type: 'error' }\n\n/**\n * Check if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(response: unknown): response is ResponseError {\n  return (\n    response != null &&\n    response instanceof Response &&\n    isPropertyAccessible(response, 'type') &&\n    response.type === 'error'\n  )\n}\n\n/**\n * Check if the given value is a `Response` or a Response-like object.\n * This is different from `value instanceof Response` because it supports\n * custom `Response` constructors, like the one when using Undici directly.\n */\nexport function isResponseLike(value: unknown): value is Response {\n  return (\n    isObject<Record<string, any>>(value, true) &&\n    isPropertyAccessible(value, 'status') &&\n    isPropertyAccessible(value, 'statusText') &&\n    isPropertyAccessible(value, 'bodyUsed')\n  )\n}\n","export function isNodeLikeError(\n  error: unknown\n): error is NodeJS.ErrnoException {\n  if (error == null) {\n    return false\n  }\n\n  if (!(error instanceof Error)) {\n    return false\n  }\n\n  return 'code' in error && 'errno' in error\n}\n","import type { Emitter } from 'strict-event-emitter'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport type { HttpRequestEventMap } from '../glossary'\nimport { emitAsync } from './emitAsync'\nimport { RequestController } from '../RequestController'\nimport {\n  createServerErrorResponse,\n  isResponseError,\n  isResponseLike,\n} from './responseUtils'\nimport { InterceptorError } from '../InterceptorError'\nimport { isNodeLikeError } from './isNodeLikeError'\nimport { isObject } from './isObject'\n\ninterface HandleRequestOptions {\n  requestId: string\n  request: Request\n  emitter: Emitter<HttpRequestEventMap>\n  controller: RequestController\n}\n\nexport async function handleRequest(\n  options: HandleRequestOptions\n): Promise<void> {\n  const handleResponse = async (\n    response: Response | Error | Record<string, any>\n  ) => {\n    if (response instanceof Error) {\n      await options.controller.errorWith(response)\n      return true\n    }\n\n    // Handle \"Response.error()\" instances.\n    if (isResponseError(response)) {\n      await options.controller.respondWith(response)\n      return true\n    }\n\n    /**\n     * Handle normal responses or response-like objects.\n     * @note This must come before the arbitrary object check\n     * since Response instances are, in fact, objects.\n     */\n    if (isResponseLike(response)) {\n      await options.controller.respondWith(response)\n      return true\n    }\n\n    // Handle arbitrary objects provided to `.errorWith(reason)`.\n    if (isObject(response)) {\n      await options.controller.errorWith(response)\n      return true\n    }\n\n    return false\n  }\n\n  const handleResponseError = async (error: unknown): Promise<boolean> => {\n    // Forward the special interceptor error instances\n    // to the developer. These must not be handled in any way.\n    if (error instanceof InterceptorError) {\n      throw result.error\n    }\n\n    // Support mocking Node.js-like errors.\n    if (isNodeLikeError(error)) {\n      await options.controller.errorWith(error)\n      return true\n    }\n\n    // Handle thrown responses.\n    if (error instanceof Response) {\n      return await handleResponse(error)\n    }\n\n    return false\n  }\n\n  // Add the last \"request\" listener to check if the request\n  // has been handled in any way. If it hasn't, resolve the\n  // response promise with undefined.\n  // options.emitter.once('request', async ({ requestId: pendingRequestId }) => {\n  //   if (\n  //     pendingRequestId === options.requestId &&\n  //     options.controller.readyState === RequestController.PENDING\n  //   ) {\n  //     await options.controller.passthrough()\n  //   }\n  // })\n\n  const requestAbortPromise = new DeferredPromise<void, unknown>()\n\n  /**\n   * @note `signal` is not always defined in React Native.\n   */\n  if (options.request.signal) {\n    if (options.request.signal.aborted) {\n      await options.controller.errorWith(options.request.signal.reason)\n      return\n    }\n\n    options.request.signal.addEventListener(\n      'abort',\n      () => {\n        requestAbortPromise.reject(options.request.signal.reason)\n      },\n      { once: true }\n    )\n  }\n\n  const result = await until(async () => {\n    // Emit the \"request\" event and wait until all the listeners\n    // for that event are finished (e.g. async listeners awaited).\n    // By the end of this promise, the developer cannot affect the\n    // request anymore.\n    const requestListenersPromise = emitAsync(options.emitter, 'request', {\n      requestId: options.requestId,\n      request: options.request,\n      controller: options.controller,\n    })\n\n    await Promise.race([\n      // Short-circuit the request handling promise if the request gets aborted.\n      requestAbortPromise,\n      requestListenersPromise,\n      options.controller.handled,\n    ])\n  })\n\n  // Handle the request being aborted while waiting for the request listeners.\n  if (requestAbortPromise.state === 'rejected') {\n    await options.controller.errorWith(requestAbortPromise.rejectionReason)\n    return\n  }\n\n  if (result.error) {\n    // Handle the error during the request listener execution.\n    // These can be thrown responses or request errors.\n    if (await handleResponseError(result.error)) {\n      return\n    }\n\n    // If the developer has added \"unhandledException\" listeners,\n    // allow them to handle the error. They can translate it to a\n    // mocked response, network error, or forward it as-is.\n    if (options.emitter.listenerCount('unhandledException') > 0) {\n      // Create a new request controller just for the unhandled exception case.\n      // This is needed because the original controller might have been already\n      // interacted with (e.g. \"respondWith\" or \"errorWith\" called on it).\n      const unhandledExceptionController = new RequestController(\n        options.request,\n        {\n          /**\n           * @note Intentionally empty passthrough handle.\n           * This controller is created within another controller and we only need\n           * to know if `unhandledException` listeners handled the request.\n           */\n          passthrough() {},\n          async respondWith(response) {\n            await handleResponse(response)\n          },\n          async errorWith(reason) {\n            /**\n             * @note Handle the result of the unhandled controller\n             * in the same way as the original request controller.\n             * The exception here is that thrown errors within the\n             * \"unhandledException\" event do NOT result in another\n             * emit of the same event. They are forwarded as-is.\n             */\n            await options.controller.errorWith(reason)\n          },\n        }\n      )\n\n      await emitAsync(options.emitter, 'unhandledException', {\n        error: result.error,\n        request: options.request,\n        requestId: options.requestId,\n        controller: unhandledExceptionController,\n      })\n\n      // If all the \"unhandledException\" listeners have finished\n      // but have not handled the request in any way, passthrough.\n      if (\n        unhandledExceptionController.readyState !== RequestController.PENDING\n      ) {\n        return\n      }\n    }\n\n    // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.\n    await options.controller.respondWith(\n      createServerErrorResponse(result.error)\n    )\n    return\n  }\n\n  // If the request hasn't been handled by this point, passthrough.\n  if (options.controller.readyState === RequestController.PENDING) {\n    return await options.controller.passthrough()\n  }\n\n  return options.controller.handled\n}\n"],"mappings":"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"}