{"version":3,"sources":["../../../src/core/handlers/RequestHandler.ts"],"sourcesContent":["import { getCallFrame } from '../utils/internal/getCallFrame'\nimport {\n  AsyncIterable,\n  Iterable,\n  isIterable,\n} from '../utils/internal/isIterable'\nimport type { ResponseResolutionContext } from '../utils/executeHandlers'\nimport type { MaybePromise } from '../typeUtils'\nimport {\n  StrictRequest,\n  HttpResponse,\n  DefaultUnsafeFetchResponse,\n} from '../HttpResponse'\nimport type { HandlerKind } from './common'\nimport type { GraphQLRequestBody } from './GraphQLHandler'\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>\n  | string\n  | number\n  | boolean\n  | null\n  | 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} & ResolverExtraInfo\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  private readonly __kind: HandlerKind\n\n  public info: HandlerInfo & RequestHandlerInternalInfo\n  /**\n   * Indicates whether this request handler has been used\n   * (its resolver has successfully executed).\n   */\n  public isUsed: boolean\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 options?: HandlerOptions\n\n  constructor(args: RequestHandlerArgs<HandlerInfo, HandlerOptions>) {\n    this.resolver = args.resolver\n    this.options = args.options\n\n    const callFrame = getCallFrame(new Error())\n\n    this.info = {\n      ...args.info,\n      callFrame,\n    }\n\n    this.isUsed = false\n    this.__kind = 'RequestHandler'\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 mockedResponsePromise = (\n      executeResolver({\n        ...resolverExtras,\n        requestId: args.requestId,\n        request: args.request,\n      }) as Promise<Response>\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\n    const mockedResponse = await mockedResponsePromise\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        const result = await resolver(info)\n\n        if (!isIterable(result)) {\n          return result\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.resolverIteratorResult?.clone()\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"],"mappings":"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","names":[]}