import { AsyncLocalStorage } from 'node:async_hooks' import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest' import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/XMLHttpRequest' import { FetchInterceptor } from '@mswjs/interceptors/fetch' import { HandlersController } from '~/core/SetupApi' import type { RequestHandler } from '~/core/handlers/RequestHandler' import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler' import type { SetupServer } from './glossary' import { SetupServerCommonApi } from './SetupServerCommonApi' const store = new AsyncLocalStorage() type RequestHandlersContext = { initialHandlers: Array handlers: Array } /** * A handlers controller that utilizes `AsyncLocalStorage` in Node.js * to prevent the request handlers list from being a shared state * across mutliple tests. */ class AsyncHandlersController implements HandlersController { private rootContext: RequestHandlersContext constructor(initialHandlers: Array) { this.rootContext = { initialHandlers, handlers: [] } } get context(): RequestHandlersContext { return store.getStore() || this.rootContext } public prepend(runtimeHandlers: Array) { this.context.handlers.unshift(...runtimeHandlers) } public reset(nextHandlers: Array) { const context = this.context context.handlers = [] context.initialHandlers = nextHandlers.length > 0 ? nextHandlers : context.initialHandlers } public currentHandlers(): Array { const { initialHandlers, handlers } = this.context return handlers.concat(initialHandlers) } } export class SetupServerApi extends SetupServerCommonApi implements SetupServer { constructor(handlers: Array) { super( [ClientRequestInterceptor, XMLHttpRequestInterceptor, FetchInterceptor], handlers, ) this.handlersController = new AsyncHandlersController(handlers) } public boundary, R>( callback: (...args: Args) => R, ): (...args: Args) => R { return (...args: Args): R => { return store.run( { initialHandlers: this.handlersController.currentHandlers(), handlers: [], }, callback, ...args, ) } } public close(): void { super.close() store.disable() } }