{"version":3,"sources":["../../../src/core/experimental/define-network.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport { Emitter, type DefaultEventMap } from 'rettime'\nimport {\n  NetworkSource,\n  type ExtractSourceEvents,\n} from './sources/network-source'\nimport { type NetworkFrameResolutionContext } from './frames/network-frame'\nimport { type UnhandledFrameHandle } from './on-unhandled-frame'\nimport {\n  HandlersController,\n  InMemoryHandlersController,\n  type AnyHandler,\n} from './handlers-controller'\nimport { toReadonlyArray } from '../utils/internal/toReadonlyArray'\n\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n  k: infer I,\n) => void\n  ? I\n  : never\n\ntype MergeEventMaps<Sources extends Array<NetworkSource<any>>> =\n  UnionToIntersection<ExtractSourceEvents<Sources[number]>> extends infer R\n    ? R extends Record<string, any>\n      ? R\n      : DefaultEventMap\n    : DefaultEventMap\n\ntype MaybePromise<T> =\n  Extract<T, Promise<unknown>> extends never ? void : Promise<void>\n\nexport interface DefineNetworkOptions<\n  Sources extends Array<NetworkSource<any>>,\n> {\n  /**\n   * List of the network sources.\n   * Every network source emits frames, and every frame describes how\n   * to handle the various network scenarios, like mocking a response,\n   * erroring the request, or performing it as-is.\n   */\n  sources: Sources\n  /**\n   * List of handlers to describe the network.\n   */\n  handlers?: Array<AnyHandler> | HandlersController\n  context?: NetworkFrameResolutionContext\n  onUnhandledFrame?: UnhandledFrameHandle\n}\n\nexport interface NetworkApi<\n  Sources extends Array<NetworkSource<any>>,\n> extends NetworkHandlersApi {\n  readyState: NetworkReadyState\n  /**\n   * Enable the network interception and handling.\n   */\n  enable: () => MaybePromise<ReturnType<Sources[number]['enable']>>\n  /**\n   * Disable the network interception and handling.\n   */\n  disable: () => MaybePromise<ReturnType<Sources[number]['disable']>>\n  /**\n   * Configure the network instance with additional options.\n   * The options provided in the `.configure()` call will override the same\n   * options in the `defineNetwork()` call.\n   */\n  configure: (options: Partial<DefineNetworkOptions<Sources>>) => void\n  events: Emitter<MergeEventMaps<Sources>>\n}\n\nexport interface NetworkHandlersApi {\n  use: (...handlers: Array<AnyHandler>) => void\n  resetHandlers: (...handlers: Array<AnyHandler>) => void\n  restoreHandlers: () => void\n  listHandlers: () => ReadonlyArray<AnyHandler>\n}\n\nfunction colorlessPromiseAll<T>(values: Array<T>): MaybePromise<T>\nfunction colorlessPromiseAll(values: Array<unknown>): Promise<void> | void {\n  const promises: Array<Promise<void>> = []\n\n  for (const value of values) {\n    if (value instanceof Promise) {\n      promises.push(value)\n    }\n  }\n\n  if (promises.length > 0) {\n    return Promise.all(promises).then(() => {})\n  }\n}\n\nexport enum NetworkReadyState {\n  DISABLED,\n  ENABLED,\n}\n\n/**\n * Define a network instance with the given configuration.\n * @example\n * import { InterceptorSource } from 'msw/experimental'\n * import { handlers } from './handlers'\n *\n * const network = defineNetwork({\n *   sources: [new InterceptorSource({ interceptors })],\n *   handlers,\n * })\n * await network.enable()\n */\nexport function defineNetwork<Sources extends Array<NetworkSource<any>>>(\n  options: DefineNetworkOptions<Sources>,\n): NetworkApi<Sources> {\n  let readyState: NetworkReadyState = NetworkReadyState.DISABLED\n  const events = new Emitter<MergeEventMaps<Sources>>()\n\n  const deriveHandlersController = (\n    handlers: DefineNetworkOptions<Sources>['handlers'],\n  ) => {\n    return handlers instanceof HandlersController\n      ? handlers\n      : new InMemoryHandlersController(handlers || [])\n  }\n\n  let resolvedOptions: DefineNetworkOptions<Sources> = {\n    ...options,\n  }\n\n  /**\n   * @note Create the handlers controller immediately because\n   * certain setup APIs, like `setupServer`, don't await `.enable` (`.listen`).\n   */\n  let handlersController = deriveHandlersController(resolvedOptions.handlers)\n  let listenersController: AbortController\n\n  return {\n    get readyState() {\n      return readyState\n    },\n    events,\n    configure(options) {\n      invariant(readyState === NetworkReadyState.DISABLED, '')\n\n      if (\n        options.handlers &&\n        !Object.is(options.handlers, resolvedOptions.handlers)\n      ) {\n        handlersController = deriveHandlersController(options.handlers)\n      }\n\n      resolvedOptions = {\n        ...resolvedOptions,\n        ...options,\n      }\n    },\n    enable() {\n      invariant(\n        readyState === NetworkReadyState.DISABLED,\n        'Failed to call \"enable\" on the network: already enabled',\n      )\n\n      listenersController = new AbortController()\n      readyState = NetworkReadyState.ENABLED\n\n      const result = resolvedOptions.sources.map((source) => {\n        /**\n         * @note Preemptively disable the network source before enabling.\n         * This intentionally calls only the prototype method that clears the\n         * event listeners and nothing else. This prevents the \"frame\" listeners\n         * from accumulating across enable/disable in case the source is a singleton.\n         */\n        NetworkSource.prototype.disable.call(source)\n\n        source.on('frame', async ({ frame }) => {\n          frame.events.on('*', (event) => events.emit(event), {\n            signal: listenersController.signal,\n          })\n\n          const handlers = frame.getHandlers(handlersController)\n\n          await frame.resolve(\n            handlers,\n            resolvedOptions.onUnhandledFrame || 'warn',\n            resolvedOptions.context,\n          )\n        })\n\n        return source.enable()\n      })\n\n      return colorlessPromiseAll(result) as MaybePromise<\n        ReturnType<Sources[number]['enable']>\n      >\n    },\n    disable() {\n      invariant(\n        readyState === NetworkReadyState.ENABLED,\n        'Failed to call \"disable\" on the network: already disabled',\n      )\n\n      listenersController.abort()\n      readyState = NetworkReadyState.DISABLED\n\n      return colorlessPromiseAll(\n        resolvedOptions.sources.map((source) => source.disable()),\n      ) as MaybePromise<ReturnType<Sources[number]['disable']>>\n    },\n    use(...handlers) {\n      handlersController.use(handlers)\n    },\n    resetHandlers(...handlers) {\n      handlersController.reset(handlers)\n    },\n    restoreHandlers() {\n      handlersController.restore()\n    },\n    listHandlers() {\n      return toReadonlyArray(handlersController.currentHandlers())\n    },\n  }\n}\n"],"mappings":"AAAA,SAAS,iBAAiB;AAC1B,SAAS,eAAqC;AAC9C;AAAA,EACE;AAAA,OAEK;AACP,eAAmD;AACnD,eAA0C;AAC1C;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,uBAAuB;AAiEhC,SAAS,oBAAoB,QAA8C;AACzE,QAAM,WAAiC,CAAC;AAExC,aAAW,SAAS,QAAQ;AAC1B,QAAI,iBAAiB,SAAS;AAC5B,eAAS,KAAK,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO,QAAQ,IAAI,QAAQ,EAAE,KAAK,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5C;AACF;AAEO,IAAK,oBAAL,kBAAKA,uBAAL;AACL,EAAAA,sCAAA;AACA,EAAAA,sCAAA;AAFU,SAAAA;AAAA,GAAA;AAiBL,SAAS,cACd,SACqB;AACrB,MAAI,aAAgC;AACpC,QAAM,SAAS,IAAI,QAAiC;AAEpD,QAAM,2BAA2B,CAC/B,aACG;AACH,WAAO,oBAAoB,qBACvB,WACA,IAAI,2BAA2B,YAAY,CAAC,CAAC;AAAA,EACnD;AAEA,MAAI,kBAAiD;AAAA,IACnD,GAAG;AAAA,EACL;AAMA,MAAI,qBAAqB,yBAAyB,gBAAgB,QAAQ;AAC1E,MAAI;AAEJ,SAAO;AAAA,IACL,IAAI,aAAa;AACf,aAAO;AAAA,IACT;AAAA,IACA;AAAA,IACA,UAAUC,UAAS;AACjB,gBAAU,eAAe,kBAA4B,EAAE;AAEvD,UACEA,SAAQ,YACR,CAAC,OAAO,GAAGA,SAAQ,UAAU,gBAAgB,QAAQ,GACrD;AACA,6BAAqB,yBAAyBA,SAAQ,QAAQ;AAAA,MAChE;AAEA,wBAAkB;AAAA,QAChB,GAAG;AAAA,QACH,GAAGA;AAAA,MACL;AAAA,IACF;AAAA,IACA,SAAS;AACP;AAAA,QACE,eAAe;AAAA,QACf;AAAA,MACF;AAEA,4BAAsB,IAAI,gBAAgB;AAC1C,mBAAa;AAEb,YAAM,SAAS,gBAAgB,QAAQ,IAAI,CAAC,WAAW;AAOrD,sBAAc,UAAU,QAAQ,KAAK,MAAM;AAE3C,eAAO,GAAG,SAAS,OAAO,EAAE,MAAM,MAAM;AACtC,gBAAM,OAAO,GAAG,KAAK,CAAC,UAAU,OAAO,KAAK,KAAK,GAAG;AAAA,YAClD,QAAQ,oBAAoB;AAAA,UAC9B,CAAC;AAED,gBAAM,WAAW,MAAM,YAAY,kBAAkB;AAErD,gBAAM,MAAM;AAAA,YACV;AAAA,YACA,gBAAgB,oBAAoB;AAAA,YACpC,gBAAgB;AAAA,UAClB;AAAA,QACF,CAAC;AAED,eAAO,OAAO,OAAO;AAAA,MACvB,CAAC;AAED,aAAO,oBAAoB,MAAM;AAAA,IAGnC;AAAA,IACA,UAAU;AACR;AAAA,QACE,eAAe;AAAA,QACf;AAAA,MACF;AAEA,0BAAoB,MAAM;AAC1B,mBAAa;AAEb,aAAO;AAAA,QACL,gBAAgB,QAAQ,IAAI,CAAC,WAAW,OAAO,QAAQ,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,IACA,OAAO,UAAU;AACf,yBAAmB,IAAI,QAAQ;AAAA,IACjC;AAAA,IACA,iBAAiB,UAAU;AACzB,yBAAmB,MAAM,QAAQ;AAAA,IACnC;AAAA,IACA,kBAAkB;AAChB,yBAAmB,QAAQ;AAAA,IAC7B;AAAA,IACA,eAAe;AACb,aAAO,gBAAgB,mBAAmB,gBAAgB,CAAC;AAAA,IAC7D;AAAA,EACF;AACF;","names":["NetworkReadyState","options"]}