{"version":3,"sources":["../../src/core/ws.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport type {\n  WebSocketData,\n  WebSocketClientConnectionProtocol,\n} from '@mswjs/interceptors/WebSocket'\nimport {\n  WebSocketHandler,\n  kEmitter,\n  type WebSocketHandlerEventMap,\n} from './handlers/WebSocketHandler'\nimport { hasRefCounted } from './utils/internal/hasRefCounted'\nimport { Path, isPath } from './utils/matching/matchRequestUrl'\nimport { WebSocketClientManager } from './ws/WebSocketClientManager'\n\nconst webSocketChannel = new BroadcastChannel('msw:websocket-client-manager')\n\nif (hasRefCounted(webSocketChannel)) {\n  // Allows the Node.js thread to exit if it is the only active handle in the event system.\n  // https://nodejs.org/api/worker_threads.html#broadcastchannelunref\n  webSocketChannel.unref()\n}\n\nexport type WebSocketEventListener<\n  EventType extends keyof WebSocketHandlerEventMap,\n> = (...args: WebSocketHandlerEventMap[EventType]) => void\n\nexport type WebSocketLink = {\n  /**\n   * A set of all WebSocket clients connected\n   * to this link.\n   *\n   * @see {@link https://mswjs.io/docs/api/ws#clients `clients` API reference}\n   */\n  clients: Set<WebSocketClientConnectionProtocol>\n\n  /**\n   * Adds an event listener to this WebSocket link.\n   *\n   * @example\n   * const chat = ws.link('wss://chat.example.com')\n   * chat.addEventListener('connection', listener)\n   *\n   * @see {@link https://mswjs.io/docs/api/ws#onevent-listener `on()` API reference}\n   */\n  addEventListener<EventType extends keyof WebSocketHandlerEventMap>(\n    event: EventType,\n    listener: WebSocketEventListener<EventType>,\n  ): WebSocketHandler\n\n  /**\n   * Broadcasts the given data to all WebSocket clients.\n   *\n   * @example\n   * const service = ws.link('wss://example.com')\n   * service.addEventListener('connection', () => {\n   *   service.broadcast('hello, everyone!')\n   * })\n   *\n   * @see {@link https://mswjs.io/docs/api/ws#broadcastdata `broadcast()` API reference}\n   */\n  broadcast(data: WebSocketData): void\n\n  /**\n   * Broadcasts the given data to all WebSocket clients\n   * except the ones provided in the `clients` argument.\n   *\n   * @example\n   * const service = ws.link('wss://example.com')\n   * service.addEventListener('connection', ({ client }) => {\n   *   service.broadcastExcept(client, 'hi, the rest of you!')\n   * })\n   *\n   * @see {@link https://mswjs.io/docs/api/ws#broadcastexceptclients-data `broadcast()` API reference}\n   */\n  broadcastExcept(\n    clients:\n      | WebSocketClientConnectionProtocol\n      | Array<WebSocketClientConnectionProtocol>,\n    data: WebSocketData,\n  ): void\n}\n\n/**\n * Intercepts outgoing WebSocket connections to the given URL.\n *\n * @example\n * const chat = ws.link('wss://chat.example.com')\n * chat.addEventListener('connection', ({ client }) => {\n *   client.send('hello from server!')\n * })\n */\nfunction createWebSocketLinkHandler(url: Path): WebSocketLink {\n  invariant(url, 'Expected a WebSocket server URL but got undefined')\n\n  invariant(\n    isPath(url),\n    'Expected a WebSocket server URL to be a valid path but got %s',\n    typeof url,\n  )\n\n  const clientManager = new WebSocketClientManager(webSocketChannel)\n\n  return {\n    get clients() {\n      return clientManager.clients\n    },\n    addEventListener(event, listener) {\n      const handler = new WebSocketHandler(url)\n\n      // Add the connection event listener for when the\n      // handler matches and emits a connection event.\n      // When that happens, store that connection in the\n      // set of all connections for reference.\n      handler[kEmitter].on('connection', async ({ client }) => {\n        await clientManager.addConnection(client)\n      })\n\n      // The \"handleWebSocketEvent\" function will invoke\n      // the \"run()\" method on the WebSocketHandler.\n      // If the handler matches, it will emit the \"connection\"\n      // event. Attach the user-defined listener to that event.\n      handler[kEmitter].on(event, listener)\n\n      return handler\n    },\n\n    broadcast(data) {\n      // This will invoke \"send()\" on the immediate clients\n      // in this runtime and post a message to the broadcast channel\n      // to trigger send for the clients in other runtimes.\n      this.broadcastExcept([], data)\n    },\n\n    broadcastExcept(clients, data) {\n      const ignoreClients = Array.prototype\n        .concat(clients)\n        .map((client) => client.id)\n\n      clientManager.clients.forEach((otherClient) => {\n        if (!ignoreClients.includes(otherClient.id)) {\n          otherClient.send(data)\n        }\n      })\n    },\n  }\n}\n\n/**\n * A namespace to intercept and mock WebSocket connections.\n *\n * @example\n * const chat = ws.link('wss://chat.example.com')\n *\n * @see {@link https://mswjs.io/docs/api/ws `ws` API reference}\n * @see {@link https://mswjs.io/docs/basics/handling-websocket-events Handling WebSocket events}\n */\nexport const ws = {\n  link: createWebSocketLinkHandler,\n}\n\nexport { WebSocketData }\n"],"mappings":"AAAA,SAAS,iBAAiB;AAK1B;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,qBAAqB;AAC9B,SAAe,cAAc;AAC7B,SAAS,8BAA8B;AAEvC,MAAM,mBAAmB,IAAI,iBAAiB,8BAA8B;AAE5E,IAAI,cAAc,gBAAgB,GAAG;AAGnC,mBAAiB,MAAM;AACzB;AAuEA,SAAS,2BAA2B,KAA0B;AAC5D,YAAU,KAAK,mDAAmD;AAElE;AAAA,IACE,OAAO,GAAG;AAAA,IACV;AAAA,IACA,OAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,IAAI,uBAAuB,gBAAgB;AAEjE,SAAO;AAAA,IACL,IAAI,UAAU;AACZ,aAAO,cAAc;AAAA,IACvB;AAAA,IACA,iBAAiB,OAAO,UAAU;AAChC,YAAM,UAAU,IAAI,iBAAiB,GAAG;AAMxC,cAAQ,QAAQ,EAAE,GAAG,cAAc,OAAO,EAAE,OAAO,MAAM;AACvD,cAAM,cAAc,cAAc,MAAM;AAAA,MAC1C,CAAC;AAMD,cAAQ,QAAQ,EAAE,GAAG,OAAO,QAAQ;AAEpC,aAAO;AAAA,IACT;AAAA,IAEA,UAAU,MAAM;AAId,WAAK,gBAAgB,CAAC,GAAG,IAAI;AAAA,IAC/B;AAAA,IAEA,gBAAgB,SAAS,MAAM;AAC7B,YAAM,gBAAgB,MAAM,UACzB,OAAO,OAAO,EACd,IAAI,CAAC,WAAW,OAAO,EAAE;AAE5B,oBAAc,QAAQ,QAAQ,CAAC,gBAAgB;AAC7C,YAAI,CAAC,cAAc,SAAS,YAAY,EAAE,GAAG;AAC3C,sBAAY,KAAK,IAAI;AAAA,QACvB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAWO,MAAM,KAAK;AAAA,EAChB,MAAM;AACR;","names":[]}