{"version":3,"sources":["../../src/client.ts"],"sourcesContent":["import {\n  Message,\n  MessageType,\n  parseMessage,\n  type MessageTypes,\n  type ParsedMessage,\n  type ParsedMessageType,\n} from \"@mocky-balboa/websocket-messages\";\nimport { v4 as uuid } from \"uuid\";\nimport { minimatch } from \"minimatch\";\nimport WebSocket from \"isomorphic-ws\";\nimport { waitForAck } from \"./utils.js\";\nimport { Route, type RouteResponse } from \"./route.js\";\nimport { DefaultWebSocketServerPort } from \"@mocky-balboa/shared-config\";\nimport { logger } from \"./logger.js\";\n\n/** Default timeout duration in milliseconds for establishing an identified connection with the WebSocket server */\nexport const DefaultWebSocketServerTimeout = 5000;\n\n/** Default timeout duration in milliseconds for waiting on a request to be sent */\nexport const DefaultWaitForRequestTimeout = 5000;\n\n/** Possible values for URL pattern matching */\nexport type UrlMatcher =\n  | string\n  | RegExp\n  | ((url: URL) => boolean | Promise<boolean>);\n\n/** Possible values for route type */\nexport type RouteType = \"server-only\" | \"client-only\" | \"both\";\nexport const RouteType = {\n  ServerOnly: \"server-only\",\n  ClientOnly: \"client-only\",\n  Both: \"both\",\n} as const;\n\n/** Options when configuring a route */\nexport interface RouteOptions {\n  /**\n   * Total number of times that a route handler will be run when the URL pattern matcher is a hit.\n   *\n   * @remarks\n   * When `undefined`, the route handler will be run indefinitely.\n   */\n  times?: number;\n  /**\n   * Defines the behaviour of the route handler.\n   *\n   * - `server-only` - The route handler will only be called if the request is executed on the server.\n   * - `client-only` - The route handler will only be called if the request is executed on the client.\n   * - `both` - The route handler will be called regardless of whether the request is executed on the server or client.\n   *\n   * @default \"both\"\n   */\n  type?: RouteType;\n}\n\ntype RouteMeta = RouteOptions & {\n  calls: number;\n};\n\nexport interface WaitForRequestOptions {\n  /**\n   * Timeout duration in milliseconds for waiting for a request to be received from the WebSocket server\n   *\n   * @default {@link DefaultWaitForRequestTimeout}\n   */\n  timeout?: number;\n  /**\n   * Determines where to expect the request to be executed from\n   *\n   * - `server-only` - The request will only be received if it originated from the server.\n   * - `client-only` - The request will only be received if it originated from the client.\n   * - `both` - The request will be received regardless of whether it originated from the server or client. The request returned is the first request received.\n   *\n   * @default \"both\"\n   */\n  type?: RouteType;\n}\n\n/**\n * Connection options for the WebSocket server client connection\n */\nexport interface ConnectOptions {\n  /**\n   * Port number to connect to the WebSocket server on\n   *\n   * @default {@link DefaultWebSocketServerPort}\n   */\n  port?: number;\n  /**\n   * Timeout duration in milliseconds for establishing an identified connection with the WebSocket server\n   *\n   * @default {@link DefaultWebSocketServerTimeout}\n   */\n  timeout?: number;\n}\n\n/**\n * Data related to the mocked response\n */\nexport interface ResponseData {\n  response?: Response;\n  /** @default false */\n  error?: boolean;\n  /**\n   * The file path if any to load the response body from\n   */\n  path?: string | undefined;\n}\n\n/**\n * External handlers should not handle explicit fallbacks as this is used\n * internally to continue to the next handler\n */\nexport type ExternalRouteHandlerRouteResponse = Exclude<\n  RouteResponse,\n  { type: \"fallback\" }\n>;\n\n/**\n * Client used to interface with the WebSocket server for mocking server-side network requests. And\n * optionally integrating for mocking client-side network requests.\n *\n * @hideconstructor\n */\nexport class Client {\n  /** The WebSocket connection */\n  private _ws?: WebSocket;\n  /** Unique identifier for the client, used for continuity across the WebSocket connection enabling parallel mocking */\n  public readonly clientIdentifier: string;\n  /** Message handlers when receiving messages from the WebSocket server */\n  private messageHandlers: Map<\n    MessageType,\n    Set<(message: ParsedMessage) => void | Promise<void>>\n  > = new Map();\n  /** Tracks whether an external client side route handler is attached */\n  private externalClientSideRouteHandlerAttached = false;\n  /** Callback handlers for waiting on client-side network requests */\n  private clientWaitForRequestHandlers: Set<\n    (request: Request) => Promise<void>\n  > = new Set();\n\n  /** Convenient access to route types */\n  public readonly RouteType = RouteType;\n\n  /** Registered route handlers to handle outgoing network requests on the server */\n  private routeHandlers: Map<\n    string,\n    [\n      UrlMatcher,\n      (route: Route) => RouteResponse | Promise<RouteResponse>,\n      RouteMeta,\n    ]\n  > = new Map();\n\n  constructor() {\n    this.clientIdentifier = uuid();\n    this.onMessage = this.onMessage.bind(this);\n    this.onRequest = this.onRequest.bind(this);\n\n    this.on(MessageType.REQUEST, this.onRequest);\n  }\n\n  /**\n   * Waits for the WebSocket connection to open before proceeding.\n   *\n   * @param ws - the WebSocket connection\n   * @param timeoutDuration - the duration in milliseconds to wait for the connection to open\n   * @returns a Promise that resolves when the connection is open, or rejects if the timeout is reached\n   */\n  private waitForConnection(ws: WebSocket, timeoutDuration: number) {\n    return new Promise<void>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        reject(new Error(\"Timed out connecting to server\"));\n      }, timeoutDuration);\n\n      ws.addEventListener(\"open\", () => {\n        clearTimeout(timeout);\n        resolve();\n      });\n    });\n  }\n\n  /**\n   * Checks if the request URL matches the given URL matcher.\n   *\n   * @remarks\n   * This method uses [minimatch](https://www.npmjs.com/package/minimatch) when urlMatcher is a string for glob pattern matching.\n   *\n   * @param urlMatcher - method of matching the request URL\n   * @param url - the request URL\n   * @returns Promise resolving to true if the URL matches, otherwise returns a promise resolving to false\n   */\n  private async doesUrlMatch(\n    urlMatcher: UrlMatcher,\n    url: URL,\n  ): Promise<boolean> {\n    if (typeof urlMatcher === \"string\") {\n      return minimatch(url.toString(), urlMatcher);\n    }\n\n    if (urlMatcher instanceof RegExp) {\n      return urlMatcher.test(url.toString());\n    }\n\n    return urlMatcher(url);\n  }\n\n  /**\n   * Sends a response message to the WebSocket server and waits for an acknowledgement.\n   *\n   * @param ws - the WebSocket connection\n   * @param requestId - the request ID originally sent from the WebSocket server\n   * @param response - optional response to send back to the WebSocket server\n   * @param error - optional error flag to simulate a network level error\n   */\n  private async respondWithResponse(\n    ws: WebSocket,\n    requestId: string,\n    responseData: ResponseData = {},\n  ) {\n    const { response, error, path } = responseData;\n    const message = new Message(MessageType.RESPONSE, {\n      id: requestId,\n      error,\n      response: response\n        ? {\n            status: response.status,\n            headers: Object.fromEntries(response.headers),\n            body: await response.text(),\n            path,\n          }\n        : undefined,\n    });\n\n    const ackPromise = waitForAck(ws, message.messageId, 2000);\n    ws.send(message.toString());\n    await ackPromise;\n  }\n\n  /**\n   * Builds a Request instance from a parsed request message.\n   */\n  private getRequestObjectFromRequestMessage(\n    message: ParsedMessageType<MessageTypes[\"REQUEST\"]>,\n  ) {\n    const { request } = message.payload;\n    let requestInit: RequestInit;\n    if (request.method === \"GET\" || request.method === \"HEAD\") {\n      requestInit = {\n        body: null,\n        headers: request.headers,\n        method: request.method,\n      };\n    } else {\n      requestInit = {\n        body: request.body ?? null,\n        headers: request.headers,\n        method: request.method,\n      };\n    }\n\n    return new Request(request.url, requestInit);\n  }\n\n  /**\n   * Waits for a request to be made that matches the given URL matcher.\n   *\n   * @param urlMatcher - The URL matcher to match against.\n   * @param options - Options for the wait operation.\n   * @returns Promise resolving to an instance of Request matching the original request dispatched by the server, rejects with an error if the request is not found within the specified timeout duration.\n   */\n  async waitForRequest(\n    urlMatcher: UrlMatcher,\n    options: WaitForRequestOptions = {},\n  ): Promise<Request> {\n    const {\n      timeout: timeoutDuration = DefaultWaitForRequestTimeout,\n      type = RouteType.Both,\n    } = options;\n    return new Promise<Request>((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        reject(new Error(\"Timed out waiting for request\"));\n      }, timeoutDuration);\n\n      const client = this;\n      async function onServerRequest(\n        message: ParsedMessageType<MessageTypes[\"REQUEST\"]>,\n      ) {\n        const urlMatch = await client.doesUrlMatch(\n          urlMatcher,\n          new URL(message.payload.request.url),\n        );\n        if (urlMatch) {\n          unregisterHandlers();\n          clearTimeout(timeout);\n          resolve(client.getRequestObjectFromRequestMessage(message));\n        }\n      }\n\n      async function onClientRequest(request: Request) {\n        const urlMatch = await client.doesUrlMatch(\n          urlMatcher,\n          new URL(request.url),\n        );\n        if (urlMatch) {\n          unregisterHandlers();\n          clearTimeout(timeout);\n          resolve(request);\n        }\n      }\n\n      function unregisterHandlers() {\n        client.off(MessageType.REQUEST, onServerRequest);\n        client.clientWaitForRequestHandlers.delete(onClientRequest);\n      }\n\n      if (this.shouldHandleRouteType(type, \"server\")) {\n        this.on(MessageType.REQUEST, onServerRequest);\n      }\n\n      if (this.shouldHandleRouteType(type, \"client\")) {\n        this.clientWaitForRequestHandlers.add(onClientRequest);\n      }\n    });\n  }\n\n  /**\n   * Increments the call count for a route handler. Function to be called\n   * whenever a route handler is executed, irregardless of the result.\n   */\n  private incrementRouteHandlerCallCount = (\n    routeHandlerId: string,\n    routeMeta: RouteMeta,\n  ) => {\n    routeMeta.calls++;\n    if (routeMeta.times === routeMeta.calls) {\n      this.unroute(routeHandlerId);\n    }\n  };\n\n  /**\n   * Deduce whether or not a handler should be executed based on the route metadata and handler type.\n   *\n   * @param routeMeta - The metadata & options of the route.\n   * @param handlerType - The type of the handler wanting to execute the route handler.\n   * @returns Whether or not the handler should be executed.\n   */\n  private shouldHandleRouteType(\n    routeType: RouteType | undefined,\n    handlerType: \"server\" | \"client\",\n  ) {\n    const type = routeType ?? RouteType.Both;\n    switch (handlerType) {\n      case \"server\":\n        return type === \"server-only\" || type === \"both\";\n      case \"client\":\n        return type === \"client-only\" || type === \"both\";\n      default:\n        throw new Error(`Invalid handler type: ${handlerType}`);\n    }\n  }\n\n  /**\n   * Callback handler when a request message is received from the WebSocket server. This method is responsible for finding the appropriate route handlers, executing them, and sending the response back to the WebSocket server.\n   *\n   * @param message - The received request message.\n   */\n  private async onRequest(message: ParsedMessageType<MessageTypes[\"REQUEST\"]>) {\n    if (!this._ws) {\n      throw new Error(\"WebSocket is not connected\");\n    }\n\n    const { request, id } = message.payload;\n    const url = new URL(request.url);\n\n    const requestObject = this.getRequestObjectFromRequestMessage(message);\n    const route = new Route(requestObject);\n\n    // Iterate over all the route handlers sequentially. This ensures the\n    // correct order of execution.\n    for (const [routeHandlerId, [urlMatcher, handler, routeMeta]] of this\n      .routeHandlers) {\n      // Skip the handler if the URL does not match\n      if (!(await this.doesUrlMatch(urlMatcher, url))) continue;\n      // Skip the handler if it should not be handled by the server\n      if (!this.shouldHandleRouteType(routeMeta.type, \"server\")) continue;\n\n      // Execute the handler\n      const routeResponse = await handler(route);\n\n      let responded = false;\n      // When the route handler responds with fallback behavior we loop to the next handler\n      switch (routeResponse.type) {\n        // Finite result. Terminates the route with a simulated network error.\n        case \"error\":\n          await this.respondWithResponse(this._ws, id, { error: true });\n          responded = true;\n          break;\n        // Finite result. Terminates the route telling the server to execute the request without mocking.\n        case \"passthrough\":\n          await this.respondWithResponse(this._ws, id);\n          responded = true;\n          break;\n        // Finite result. Terminates the route, passing the response to the server.\n        case \"fulfill\":\n          await this.respondWithResponse(this._ws, id, {\n            response: routeResponse.response,\n            path: routeResponse.path,\n          });\n          responded = true;\n          break;\n      }\n\n      this.incrementRouteHandlerCallCount(routeHandlerId, routeMeta);\n\n      // If we have responded then exit\n      if (responded) {\n        return;\n      }\n    }\n\n    // Fallback to passthrough behaviour\n    await this.respondWithResponse(this._ws, id);\n  }\n\n  /**\n   * Callback when a message is received from the WebSocket server. We find all the handlers for the message type and call them concurrently.\n   *\n   * @param data - the raw WebSocket message data\n   */\n  private async onMessage({ data }: WebSocket.MessageEvent) {\n    if (!this._ws) {\n      throw new Error(\"WebSocket is not connected\");\n    }\n\n    const parsedMessage = parseMessage(data.toString());\n    this._ws.send(\n      new Message(MessageType.ACK, {}, parsedMessage.messageId).toString(),\n    );\n\n    const handlers = this.messageHandlers.get(parsedMessage.type);\n    if (handlers) {\n      const results = await Promise.allSettled(\n        [...handlers].map((handler) => handler(parsedMessage)),\n      );\n      const errors = results\n        .filter((result) => result.status === \"rejected\")\n        .map((result) => result.reason);\n\n      errors.forEach((error) => {\n        logger.error(`Error handling message ${parsedMessage.type}`, error);\n      });\n\n      if (errors.length > 0) {\n        throw new Error(\n          `Failed to handle message \"${parsedMessage.type}\". Check console or stdout for more details.`,\n        );\n      }\n    }\n  }\n\n  /**\n   * Used to connect the client to the WebSocket server. This will also identify the client on the server to enable concurrency for mocking. You need to call this method before you can register any route handlers.\n   *\n   * @param options - Options for the connection.\n   */\n  async connect({\n    port = DefaultWebSocketServerPort,\n    timeout: timeoutDuration = DefaultWebSocketServerTimeout,\n  }: ConnectOptions) {\n    this._ws = new WebSocket(`ws://localhost:${port}`);\n    const startTime = Date.now();\n    await this.waitForConnection(this._ws, timeoutDuration);\n\n    // The timeout is the sum of the time it takes to connect to the server and the time it takes to identify the client.\n    const elapsedTime = Date.now() - startTime;\n\n    const identifyMessage = new Message(MessageType.IDENTIFY, {\n      id: this.clientIdentifier,\n    });\n\n    const identifyAckPromise = waitForAck(\n      this._ws,\n      identifyMessage.messageId,\n      timeoutDuration - elapsedTime,\n    );\n    this._ws.send(JSON.stringify(identifyMessage));\n\n    // Wait until the server has acknowledged the identification message.\n    await identifyAckPromise;\n\n    this._ws.addEventListener(\"message\", this.onMessage);\n  }\n\n  /**\n   * Registers a handler for a specific message type.\n   * @param messageType - The type of message to handle.\n   * @param handler - The handler function to call when a message of the specified type is received. The handler function receives the parsed message as an argument.\n   */\n  on<TMessageType extends MessageType>(\n    messageType: TMessageType,\n    handler: (message: ParsedMessageType<TMessageType>) => void | Promise<void>,\n  ) {\n    const handlers = this.messageHandlers.get(messageType) ?? new Set();\n    handlers.add(handler as (message: ParsedMessage) => void | Promise<void>);\n    this.messageHandlers.set(messageType, handlers);\n  }\n\n  /**\n   * Unregisters a handler for a specific message type.\n   * @param messageType - The type of message to handle.\n   * @param handler - The original handler function that was registered.\n   */\n  off<TMessageType extends MessageType>(\n    messageType: TMessageType,\n    handler: (message: ParsedMessageType<TMessageType>) => void | Promise<void>,\n  ) {\n    const handlers = this.messageHandlers.get(messageType);\n    if (!handlers) return;\n\n    handlers.delete(\n      handler as (message: ParsedMessage) => void | Promise<void>,\n    );\n  }\n\n  /**\n   * Disconnects the WebSocket connection. You need to run {@link Client.connect} again to reconnect.\n   *\n   * @remarks\n   * Route handlers and message handlers are not removed when the client disconnects.\n   */\n  disconnect() {\n    if (!this._ws) {\n      throw new Error(\"Client is not connected\");\n    }\n\n    this._ws.close();\n    delete this._ws;\n  }\n\n  /**\n   * Registers a route handler, when the client receives a request message.\n   * @param url - the URL pattern to match against the incoming request URL\n   * @param handler - the function to handle the request\n   * @param options - optional options for the route handler\n   * @returns a route handler ID that can be used to unregister the handler later if required\n   */\n  route(\n    url: UrlMatcher,\n    handler: (route: Route) => RouteResponse | Promise<RouteResponse>,\n    options: RouteOptions = {},\n  ) {\n    const routeHandlerId = uuid();\n    this.routeHandlers.set(routeHandlerId, [\n      url,\n      handler,\n      { ...options, calls: 0 },\n    ]);\n\n    return routeHandlerId;\n  }\n\n  /**\n   * Used to unregister a route handler.\n   * @param routeHandlerId - the route handler ID returned from {@link Client.route}\n   */\n  unroute(routeHandlerId: string) {\n    this.routeHandlers.delete(routeHandlerId);\n  }\n\n  /**\n   * Removes all route handlers\n   */\n  unrouteAll() {\n    this.routeHandlers.clear();\n  }\n\n  /**\n   * Allows an external handler to be injected into the route handling process for dealing with\n   * client-side request interception.\n   *\n   * @internal\n   *\n   * @param options - options for the external route handler\n   * @param options.extractRequest - a function that extracts a request from the arguments passed to the external handler\n   * @param options.handleResult - a function that transforms the internal result to the external handlers expected result\n   * @returns a function that acts as a handler for the external route handler\n   */\n  attachExternalClientSideRouteHandler<\n    TCallbackArgs extends unknown[],\n    TResult,\n  >({\n    extractRequest,\n    handleResult,\n  }: {\n    extractRequest: (...args: TCallbackArgs) => Request | Promise<Request>;\n    handleResult: (\n      response: ExternalRouteHandlerRouteResponse | undefined,\n      ...args: TCallbackArgs\n    ) => TResult | undefined | Promise<TResult | undefined>;\n  }): (...args: TCallbackArgs) => Promise<TResult | undefined> {\n    if (this.externalClientSideRouteHandlerAttached) {\n      logger.warn(\n        \"External client side route handler already attached. Are you sure you want to call attachExternalClientSideRouteHandler? attachExternalClientSideRouteHandler was only intended to be called once per client instance.\",\n      );\n    }\n\n    this.externalClientSideRouteHandlerAttached = true;\n    return async (...args: TCallbackArgs) => {\n      const request = await extractRequest(...args);\n      const url = new URL(request.url);\n      const route = new Route(request);\n\n      for (const awaitingRequestHandler of this.clientWaitForRequestHandlers) {\n        await awaitingRequestHandler(request);\n      }\n\n      for (const [routeHandlerId, [urlMatcher, handler, routeMeta]] of this\n        .routeHandlers) {\n        // Skip the handler if the URL does not match\n        if (!(await this.doesUrlMatch(urlMatcher, url))) continue;\n        // Skip the handler if it should not be handled by the client\n        if (!this.shouldHandleRouteType(routeMeta.type, \"client\")) continue;\n\n        // Execute the handler\n        const routeResponse = await handler(route);\n        let finalResponse: TResult | undefined;\n        let handledRoute = false;\n        if (routeResponse.type !== \"fallback\") {\n          handledRoute = true;\n          finalResponse = await handleResult(routeResponse, ...args);\n        }\n\n        this.incrementRouteHandlerCallCount(routeHandlerId, routeMeta);\n        if (handledRoute) return finalResponse;\n      }\n\n      await handleResult(undefined, ...args);\n    };\n  }\n\n  /**\n   * WebSocket connection.\n   *\n   * @throws an error if the client has not been connected yet with {@link Client.connect}\n   */\n  get ws(): WebSocket {\n    if (!this._ws) {\n      throw new Error(\"Call connect() before accessing webSocket\");\n    }\n\n    return this._ws;\n  }\n}\n\nexport { Route } from \"./route.js\";\nexport type {\n  FetchOptions,\n  FulfillOptions,\n  ModifyResponseOptions,\n  FallbackRouteResponse,\n  PassthroughRouteResponse,\n  ErrorRouteResponse,\n  FulfillRouteResponse,\n  RouteResponse,\n} from \"./route.js\";\nexport { ClientIdentityStorageHeader } from \"@mocky-balboa/shared-config\";\nexport {\n  MessageType,\n  type MessageTypes,\n  type ParsedMessage,\n  type ParsedMessageType,\n} from \"@mocky-balboa/websocket-messages\";\n"],"names":["Client","ClientIdentityStorageHeader","DefaultWaitForRequestTimeout","DefaultWebSocketServerTimeout","MessageType","Route","RouteType","ServerOnly","ClientOnly","Both","waitForConnection","ws","timeoutDuration","Promise","resolve","reject","timeout","setTimeout","Error","addEventListener","clearTimeout","doesUrlMatch","urlMatcher","url","minimatch","toString","RegExp","test","respondWithResponse","requestId","responseData","response","error","path","message","Message","RESPONSE","id","status","headers","Object","fromEntries","body","text","undefined","ackPromise","waitForAck","messageId","send","getRequestObjectFromRequestMessage","request","payload","requestInit","method","Request","waitForRequest","options","type","client","onServerRequest","urlMatch","URL","unregisterHandlers","onClientRequest","off","REQUEST","clientWaitForRequestHandlers","delete","shouldHandleRouteType","on","add","routeType","handlerType","onRequest","_ws","requestObject","route","routeHandlerId","handler","routeMeta","routeHandlers","routeResponse","responded","incrementRouteHandlerCallCount","onMessage","data","parsedMessage","parseMessage","ACK","handlers","messageHandlers","get","results","allSettled","map","errors","filter","result","reason","forEach","logger","length","connect","port","DefaultWebSocketServerPort","WebSocket","startTime","Date","now","elapsedTime","identifyMessage","IDENTIFY","clientIdentifier","identifyAckPromise","JSON","stringify","messageType","Set","set","disconnect","close","uuid","calls","unroute","unrouteAll","clear","attachExternalClientSideRouteHandler","extractRequest","handleResult","externalClientSideRouteHandlerAttached","warn","args","awaitingRequestHandler","finalResponse","handledRoute","Map","times","bind"],"mappings":"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"}