{"version":3,"sources":["../../../../src/core/experimental/frames/websocket-frame.ts"],"sourcesContent":["import { TypedEvent } from 'rettime'\nimport { type WebSocketConnectionData } from '@mswjs/interceptors/WebSocket'\nimport {\n  kConnect,\n  kAutoConnect,\n  type WebSocketHandler,\n} from '../../handlers/WebSocketHandler'\nimport {\n  NetworkFrame,\n  type NetworkFrameResolutionContext,\n} from './network-frame'\nimport {\n  executeUnhandledFrameHandle,\n  type UnhandledFrameHandle,\n} from '../on-unhandled-frame'\nimport { devUtils } from '../../utils/internal/devUtils'\nimport type { HandlersController } from '../handlers-controller'\nimport { type AnyHandler } from '../handlers-controller'\n\nexport interface WebSocketNetworkFrameOptions {\n  connection: WebSocketConnectionData\n}\n\nexport type WebSocketNetworkFrameEventMap = {\n  connection: WebSocketConnectionEvent\n  unhandledException: UnhandledWebSocketExceptionEvent\n}\n\nclass WebSocketConnectionEvent<\n  DataType extends {\n    url: URL\n    protocols: string | Array<string> | undefined\n  } = { url: URL; protocols: string | Array<string> | undefined },\n  ReturnType = void,\n  EventType extends string = string,\n> extends TypedEvent<DataType, ReturnType, EventType> {\n  public readonly url: URL\n  public readonly protocols: string | Array<string> | undefined\n\n  constructor(type: EventType, data: DataType) {\n    super(...([type, {}] as any))\n    this.url = data.url\n    this.protocols = data.protocols\n  }\n}\n\nclass UnhandledWebSocketExceptionEvent<\n  DataType extends {\n    url: URL\n    protocols: string | Array<string> | undefined\n    error: unknown\n  } = {\n    url: URL\n    protocols: string | Array<string> | undefined\n    error: unknown\n  },\n  ReturnType = void,\n  EventType extends string = string,\n> extends TypedEvent<DataType, ReturnType, EventType> {\n  public readonly url: URL\n  public readonly protocols: string | Array<string> | undefined\n  public readonly error: unknown\n\n  constructor(type: EventType, data: DataType) {\n    super(...([type, {}] as any))\n    this.url = data.url\n    this.protocols = data.protocols\n    this.error = data.error\n  }\n}\n\nexport abstract class WebSocketNetworkFrame extends NetworkFrame<\n  'ws',\n  {\n    connection: WebSocketConnectionData\n  },\n  WebSocketNetworkFrameEventMap\n> {\n  constructor(options: WebSocketNetworkFrameOptions) {\n    super('ws', {\n      connection: options.connection,\n    })\n  }\n\n  public getHandlers(controller: HandlersController): Array<AnyHandler> {\n    return controller.getHandlersByKind('websocket')\n  }\n\n  public async resolve(\n    handlers: Array<WebSocketHandler>,\n    onUnhandledFrame: UnhandledFrameHandle,\n    resolutionContext?: NetworkFrameResolutionContext,\n  ): Promise<boolean | null> {\n    const { connection } = this.data\n\n    this.events.emit(\n      new WebSocketConnectionEvent('connection', {\n        url: connection.client.url,\n        protocols: connection.info.protocols,\n      }),\n    )\n\n    // No WebSocket handlers defined.\n    if (handlers.length === 0) {\n      await executeUnhandledFrameHandle(this, onUnhandledFrame).then(\n        () => this.passthrough(),\n        (error) => this.errorWith(error),\n      )\n\n      return false\n    }\n\n    let hasMatchingHandlers = false\n\n    for (const handler of handlers) {\n      const handlerConnection = await handler.run(connection, {\n        baseUrl: resolutionContext?.baseUrl?.toString(),\n        /**\n         * @note Do not emit the \"connection\" event when running the handler.\n         * Use the run only to get the resolved connection object.\n         */\n        [kAutoConnect]: false,\n      })\n\n      if (!handlerConnection) {\n        continue\n      }\n\n      hasMatchingHandlers = true\n\n      /**\n       * @note Attach the WebSocket logger *before* emitting the \"connection\" event.\n       * Connection event listeners may perform actions that should be reflected in the logs\n       * (e.g. closing the connection immediately). If the logger is attached after the connection,\n       * those actions cannot be properly logged.\n       */\n      const removeLogger = !resolutionContext?.quiet\n        ? handler.log(connection)\n        : undefined\n\n      try {\n        if (!handler[kConnect](handlerConnection)) {\n          removeLogger?.()\n        }\n      } catch (error) {\n        if (\n          !this.events.emit(\n            new UnhandledWebSocketExceptionEvent('unhandledException', {\n              error,\n              url: connection.client.url,\n              protocols: connection.info.protocols,\n            }),\n          )\n        ) {\n          console.error(error)\n          devUtils.error(\n            'Encountered an unhandled exception during the handler lookup for \"%s\". Please see the original error above.',\n            connection.client.url,\n          )\n        }\n\n        /**\n         * @note Throw the caught error so it gets picked up by WebSocketInterceptor.\n         * It's the interceptor who translates handler errors to WebSocket closures.\n         */\n        throw error\n      }\n    }\n\n    // No matching WebSocket handlers found.\n    if (!hasMatchingHandlers) {\n      await executeUnhandledFrameHandle(this, onUnhandledFrame).then(\n        () => this.passthrough(),\n        (error) => this.errorWith(error),\n      )\n\n      return false\n    }\n\n    return true\n  }\n\n  public async getUnhandledMessage(): Promise<string> {\n    const { connection } = this.data\n    const details = `\\n\\n  \\u2022 ${connection.client.url}\\n\\n`\n\n    return `intercepted a WebSocket connection without a matching event handler:${details}If you still wish to intercept this unhandled connection, please create an event handler for it.\\nRead more: https://mswjs.io/docs/websocket`\n  }\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,eAA6C;AAC7C;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP,SAAS,gBAAgB;AAEzB,eAAgC;AAWhC,MAAM,iCAOI,WAA4C;AAAA,EACpC;AAAA,EACA;AAAA,EAEhB,YAAY,MAAiB,MAAgB;AAC3C,UAAM,GAAI,CAAC,MAAM,CAAC,CAAC,CAAS;AAC5B,SAAK,MAAM,KAAK;AAChB,SAAK,YAAY,KAAK;AAAA,EACxB;AACF;AAEA,MAAM,yCAYI,WAA4C;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EAEhB,YAAY,MAAiB,MAAgB;AAC3C,UAAM,GAAI,CAAC,MAAM,CAAC,CAAC,CAAS;AAC5B,SAAK,MAAM,KAAK;AAChB,SAAK,YAAY,KAAK;AACtB,SAAK,QAAQ,KAAK;AAAA,EACpB;AACF;AAEO,MAAe,8BAA8B,aAMlD;AAAA,EACA,YAAY,SAAuC;AACjD,UAAM,MAAM;AAAA,MACV,YAAY,QAAQ;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEO,YAAY,YAAmD;AACpE,WAAO,WAAW,kBAAkB,WAAW;AAAA,EACjD;AAAA,EAEA,MAAa,QACX,UACA,kBACA,mBACyB;AACzB,UAAM,EAAE,WAAW,IAAI,KAAK;AAE5B,SAAK,OAAO;AAAA,MACV,IAAI,yBAAyB,cAAc;AAAA,QACzC,KAAK,WAAW,OAAO;AAAA,QACvB,WAAW,WAAW,KAAK;AAAA,MAC7B,CAAC;AAAA,IACH;AAGA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,4BAA4B,MAAM,gBAAgB,EAAE;AAAA,QACxD,MAAM,KAAK,YAAY;AAAA,QACvB,CAAC,UAAU,KAAK,UAAU,KAAK;AAAA,MACjC;AAEA,aAAO;AAAA,IACT;AAEA,QAAI,sBAAsB;AAE1B,eAAW,WAAW,UAAU;AAC9B,YAAM,oBAAoB,MAAM,QAAQ,IAAI,YAAY;AAAA,QACtD,SAAS,mBAAmB,SAAS,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,QAK9C,CAAC,YAAY,GAAG;AAAA,MAClB,CAAC;AAED,UAAI,CAAC,mBAAmB;AACtB;AAAA,MACF;AAEA,4BAAsB;AAQtB,YAAM,eAAe,CAAC,mBAAmB,QACrC,QAAQ,IAAI,UAAU,IACtB;AAEJ,UAAI;AACF,YAAI,CAAC,QAAQ,QAAQ,EAAE,iBAAiB,GAAG;AACzC,yBAAe;AAAA,QACjB;AAAA,MACF,SAAS,OAAO;AACd,YACE,CAAC,KAAK,OAAO;AAAA,UACX,IAAI,iCAAiC,sBAAsB;AAAA,YACzD;AAAA,YACA,KAAK,WAAW,OAAO;AAAA,YACvB,WAAW,WAAW,KAAK;AAAA,UAC7B,CAAC;AAAA,QACH,GACA;AACA,kBAAQ,MAAM,KAAK;AACnB,mBAAS;AAAA,YACP;AAAA,YACA,WAAW,OAAO;AAAA,UACpB;AAAA,QACF;AAMA,cAAM;AAAA,MACR;AAAA,IACF;AAGA,QAAI,CAAC,qBAAqB;AACxB,YAAM,4BAA4B,MAAM,gBAAgB,EAAE;AAAA,QACxD,MAAM,KAAK,YAAY;AAAA,QACvB,CAAC,UAAU,KAAK,UAAU,KAAK;AAAA,MACjC;AAEA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,sBAAuC;AAClD,UAAM,EAAE,WAAW,IAAI,KAAK;AAC5B,UAAM,UAAU;AAAA;AAAA,WAAgB,WAAW,OAAO,GAAG;AAAA;AAAA;AAErD,WAAO,uEAAuE,OAAO;AAAA;AAAA,EACvF;AACF;","names":[]}