@mswjs/interceptors 0.42.3 → 0.42.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/lib/browser/interceptors/WebSocket/index.js +14 -1
  2. package/lib/browser/interceptors/WebSocket/index.js.map +1 -1
  3. package/lib/node/{batch-interceptor-ByEZVih3.js → batch-interceptor-DMOfd-9x.js} +2 -2
  4. package/lib/node/{batch-interceptor-ByEZVih3.js.map → batch-interceptor-DMOfd-9x.js.map} +1 -1
  5. package/lib/node/buffer-utils-B4FUq-l2.js +17 -0
  6. package/lib/node/buffer-utils-B4FUq-l2.js.map +1 -0
  7. package/lib/node/create-request-id-DHo-fRTV.js +14 -0
  8. package/lib/node/create-request-id-DHo-fRTV.js.map +1 -0
  9. package/lib/node/{fetch-utils-Dw1PsmtX.js → fetch-utils-Tm5LbwBe.js} +3 -14
  10. package/lib/node/{fetch-utils-Dw1PsmtX.js.map → fetch-utils-Tm5LbwBe.js.map} +1 -1
  11. package/lib/node/{has-configurable-global-BU1sT1u4.js → has-configurable-global-CT6-QYdg.js} +2 -2
  12. package/lib/node/{has-configurable-global-BU1sT1u4.js.map → has-configurable-global-CT6-QYdg.js.map} +1 -1
  13. package/lib/node/index.d.ts +2 -243
  14. package/lib/node/index.js +6 -25
  15. package/lib/node/index.js.map +1 -1
  16. package/lib/node/{buffer-utils-BvPY1Tc-.js → interceptor-C8qRPjxG.js} +2 -16
  17. package/lib/node/interceptor-C8qRPjxG.js.map +1 -0
  18. package/lib/node/interceptors/ClientRequest/index.js +3 -3
  19. package/lib/node/interceptors/WebSocket/index.d.ts +2 -0
  20. package/lib/node/interceptors/WebSocket/index.js +635 -0
  21. package/lib/node/interceptors/WebSocket/index.js.map +1 -0
  22. package/lib/node/interceptors/XMLHttpRequest/node.js +5 -5
  23. package/lib/node/interceptors/fetch/node.js +5 -5
  24. package/lib/node/interceptors/http/index.js +2 -2
  25. package/lib/node/interceptors/net/index.d.ts +9 -0
  26. package/lib/node/interceptors/net/index.js +1 -1
  27. package/lib/node/{net-Ca8p1rIe.js → net-DkiHxhQF.js} +43 -79
  28. package/lib/node/net-DkiHxhQF.js.map +1 -0
  29. package/lib/node/patches-registry-DxR5TEc-.js +76 -0
  30. package/lib/node/patches-registry-DxR5TEc-.js.map +1 -0
  31. package/lib/node/remote-http-interceptor.js +4 -4
  32. package/lib/node/resolve-web-socket-url-CSvNPLGi.js +25 -0
  33. package/lib/node/resolve-web-socket-url-CSvNPLGi.js.map +1 -0
  34. package/lib/node/{source-CIgPng7r.js → source-BFZ6wg4P.js} +95 -38
  35. package/lib/node/source-BFZ6wg4P.js.map +1 -0
  36. package/lib/node/websocket-CC0nB0md.d.ts +285 -0
  37. package/package.json +5 -2
  38. package/src/interceptors/WebSocket/index.ts +4 -1
  39. package/src/interceptors/http/http-parser/index.ts +9 -1
  40. package/src/interceptors/http/http-parser.ts +21 -4
  41. package/src/interceptors/http/source.ts +71 -3
  42. package/src/interceptors/net/index.ts +39 -12
  43. package/src/interceptors/net/socket-controller.ts +34 -9
  44. package/src/utils/internal-connection.ts +22 -0
  45. package/lib/node/buffer-utils-BvPY1Tc-.js.map +0 -1
  46. package/lib/node/net-Ca8p1rIe.js.map +0 -1
  47. package/lib/node/source-CIgPng7r.js.map +0 -1
@@ -4,6 +4,7 @@ import { FetchRequest, FetchResponse } from '../../utils/fetch-utils'
4
4
  import { HttpParser } from './http-parser/index'
5
5
 
6
6
  interface HttpRequestParserOptions {
7
+ onError: (error: Error) => void
7
8
  connectionOptions: {
8
9
  method?: string
9
10
  url: URL
@@ -18,6 +19,7 @@ export class HttpRequestParser extends HttpParser<1> {
18
19
 
19
20
  constructor(options: HttpRequestParserOptions) {
20
21
  super(1, {
22
+ onError: options.onError,
21
23
  onHeadersComplete: ({ rawHeaders, method, url: path, upgrade }) => {
22
24
  this.#upgrade = upgrade
23
25
  /**
@@ -82,6 +84,7 @@ export class HttpRequestParser extends HttpParser<1> {
82
84
  },
83
85
  onMessageComplete: () => {
84
86
  this.#requestBodyStream?.push(null)
87
+ this.#requestBodyStream = undefined
85
88
 
86
89
  /**
87
90
  * @note An upgraded exchange (e.g. "CONNECT", WebSocket) has
@@ -95,23 +98,30 @@ export class HttpRequestParser extends HttpParser<1> {
95
98
  })
96
99
  }
97
100
 
98
- public free(): void {
101
+ public free(error?: Error): void {
99
102
  this.destroy()
100
- this.#requestBodyStream?.destroy()
103
+ this.#requestBodyStream?.destroy(error)
101
104
  this.#requestBodyStream = undefined
102
105
  }
103
106
  }
104
107
 
105
108
  export class HttpResponseParser extends HttpParser<2> {
106
109
  #responseBodyStream?: Readable | null
110
+ #status = 0
107
111
 
108
- constructor(options: { onResponse: (response: Response) => void }) {
112
+ constructor(options: {
113
+ onResponse: (response: Response) => void
114
+ onError: (error: Error) => void
115
+ onMessageComplete?: (status: number) => void
116
+ }) {
109
117
  super(2, {
118
+ onError: options.onError,
110
119
  onHeadersComplete: ({
111
120
  rawHeaders,
112
121
  statusCode: status,
113
122
  statusMessage: statusText,
114
123
  }) => {
124
+ this.#status = status
115
125
  const headers = FetchResponse.parseRawHeaders([...rawHeaders])
116
126
 
117
127
  const response = new FetchResponse(
@@ -139,12 +149,19 @@ export class HttpResponseParser extends HttpParser<2> {
139
149
  },
140
150
  onMessageComplete: () => {
141
151
  this.#responseBodyStream?.push(null)
152
+ this.#responseBodyStream = null
153
+ options.onMessageComplete?.(this.#status)
142
154
  },
143
155
  })
144
156
  }
145
157
 
146
- public free(): void {
158
+ public free(error?: Error): void {
147
159
  this.destroy()
160
+
161
+ if (error) {
162
+ this.#responseBodyStream?.destroy(error)
163
+ }
164
+
148
165
  this.#responseBodyStream = null
149
166
  }
150
167
  }
@@ -67,6 +67,24 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
67
67
  let requestParser: HttpRequestParser | undefined
68
68
  let tunnelUrl: URL | undefined
69
69
  let abortPendingRequest: (() => void) | undefined
70
+ let pendingRequestController: RequestController | undefined
71
+
72
+ // A malformed request loses the boundary for subsequent requests.
73
+ // Preserve the original bytes for the client and server to handle.
74
+ const stopParsingRequests = (error: Error) => {
75
+ httpLogger.verbose('stopping HTTP request parsing: %o', error)
76
+ isHttpConnection = false
77
+
78
+ if (
79
+ pendingRequestController?.readyState === RequestController.PENDING
80
+ ) {
81
+ void pendingRequestController.passthrough()
82
+ } else {
83
+ socketController.decline()
84
+ }
85
+
86
+ requestParser?.free(error)
87
+ }
70
88
 
71
89
  /**
72
90
  * @note Capture the request context of the connection itself.
@@ -99,6 +117,7 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
99
117
  */
100
118
  socket.on('data', (chunk) => {
101
119
  if (isHttpConnection === false) {
120
+ socketController.decline()
102
121
  return
103
122
  }
104
123
 
@@ -164,6 +183,7 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
164
183
  const initiator = requestContextValue?.initiator || socket
165
184
 
166
185
  requestParser = new HttpRequestParser({
186
+ onError: stopParsingRequests,
167
187
  connectionOptions: {
168
188
  method: httpMethod,
169
189
  url: baseUrl,
@@ -287,9 +307,15 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
287
307
  },
288
308
  passthrough: () => {
289
309
  const realSocket = socketController.passthrough(
290
- this.#modifyHttpHeaders(context.request)
310
+ isHttpConnection === false
311
+ ? undefined
312
+ : this.#modifyHttpHeaders(context.request)
291
313
  )
292
314
 
315
+ if (isHttpConnection === false) {
316
+ return
317
+ }
318
+
293
319
  if (this.emitter.listenerCount('response') > 0) {
294
320
  httpLogger.verbose(
295
321
  'found "response" listener, corking socket reads'
@@ -304,8 +330,20 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
304
330
  */
305
331
  socketController.corkReads()
306
332
 
333
+ let responseParserDisposed = false
334
+ let responseComplete = false
335
+ let hasFinalResponse = false
307
336
  const responseParser = new HttpResponseParser({
337
+ onError: (error) => {
338
+ disposeResponseParser(error)
339
+ socketController.uncorkReads()
340
+ },
341
+ onMessageComplete: (status) => {
342
+ responseComplete = status >= 200 || status === 101
343
+ },
308
344
  onResponse: async (response) => {
345
+ hasFinalResponse =
346
+ response.status >= 200 || response.status === 101
309
347
  httpLogger.verbose(
310
348
  'HTTP response parser parsed: %d %s',
311
349
  response.status,
@@ -345,6 +383,7 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
345
383
  * final response on the "response" event listeners.
346
384
  */
347
385
  if (
386
+ !responseParserDisposed &&
348
387
  response.status < 200 &&
349
388
  response.status !== 101
350
389
  ) {
@@ -354,9 +393,35 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
354
393
  },
355
394
  })
356
395
 
396
+ const onResponseData = (chunk: Buffer) => {
397
+ responseParser.execute(chunk)
398
+
399
+ // Free only after llhttp returns from its callbacks.
400
+ if (responseComplete) {
401
+ disposeResponseParser()
402
+ }
403
+ }
404
+
405
+ const onResponseClose = () => {
406
+ disposeResponseParser()
407
+
408
+ // Without a response, no response listener will release
409
+ // the buffered EOF/close that rejects the client request.
410
+ if (!hasFinalResponse) {
411
+ socketController.uncorkReads()
412
+ }
413
+ }
414
+
415
+ const disposeResponseParser = (error?: Error) => {
416
+ responseParserDisposed = true
417
+ realSocket.removeListener('data', onResponseData)
418
+ realSocket.removeListener('close', onResponseClose)
419
+ responseParser.free(error)
420
+ }
421
+
357
422
  realSocket
358
- .on('data', (chunk) => responseParser.execute(chunk))
359
- .on('close', () => responseParser.free())
423
+ .on('data', onResponseData)
424
+ .once('close', onResponseClose)
360
425
  }
361
426
  },
362
427
  },
@@ -403,9 +468,12 @@ export class NodeHttpRequestSource extends Interceptor<HttpRequestEventMap> {
403
468
  }
404
469
  }
405
470
 
471
+ pendingRequestController = requestController
472
+
406
473
  try {
407
474
  await handleRequest(context)
408
475
  } finally {
476
+ pendingRequestController = undefined
409
477
  abortPendingRequest = undefined
410
478
  }
411
479
  },
@@ -1,6 +1,7 @@
1
1
  import net from 'node:net'
2
2
  import tls from 'node:tls'
3
3
  import http from 'node:http'
4
+ import { AsyncLocalStorage } from 'node:async_hooks'
4
5
  import { TypedEvent } from 'rettime'
5
6
  import {
6
7
  type NetworkConnectionOptions,
@@ -16,6 +17,33 @@ import { getTlsConnectOptions } from './utils/get-tls-connect-options'
16
17
  import { createLogger } from '../../utils/logger'
17
18
  import { patchesRegistry } from '../../utils/patches-registry'
18
19
  import { Interceptor } from '#/src/interceptor'
20
+ import '../../utils/internal-connection'
21
+
22
+ /**
23
+ * @note Initialize once across entry points and package copies. The runner
24
+ * is inert without the socket patch, so it needs no disposal lifecycle.
25
+ */
26
+ globalThis.__MSW_INTERNAL_CONNECTION_CONTEXT ??= (() => {
27
+ const context = new AsyncLocalStorage<{ consumed: boolean }>()
28
+
29
+ return {
30
+ run<T>(callback: () => T): T {
31
+ return context.run({ consumed: false }, callback)
32
+ },
33
+ consume(): boolean {
34
+ const connection = context.getStore()
35
+
36
+ if (!connection || connection.consumed) {
37
+ return false
38
+ }
39
+
40
+ // Socket events inherit this context. Consume it before connecting
41
+ // so requests from user event listeners remain intercepted.
42
+ connection.consumed = true
43
+ return true
44
+ },
45
+ }
46
+ })()
19
47
 
20
48
  declare module 'node:http' {
21
49
  interface Agent {
@@ -23,10 +51,7 @@ declare module 'node:http' {
23
51
  * @note An undocumented method backing every agent-driven request
24
52
  * (see "#stopReusingUnpatchedSockets").
25
53
  */
26
- addRequest?: (
27
- request: http.ClientRequest,
28
- ...args: Array<unknown>
29
- ) => void
54
+ addRequest?: (request: http.ClientRequest, ...args: Array<unknown>) => void
30
55
  }
31
56
  }
32
57
 
@@ -143,6 +168,13 @@ export class SocketInterceptor extends Interceptor<SocketEventMap> {
143
168
  return realSocketConnect.apply(socket, args)
144
169
  }
145
170
 
171
+ if (globalThis.__MSW_INTERNAL_CONNECTION_CONTEXT?.consume()) {
172
+ // Internal connections must bypass every socket consumer,
173
+ // including HTTP upgrade interception. Mark reconnects too.
174
+ socket[kPatched] = true
175
+ return realSocketConnect.apply(socket, args)
176
+ }
177
+
146
178
  logger.verbose('socket.connect() %o', args)
147
179
 
148
180
  /**
@@ -188,7 +220,7 @@ export class SocketInterceptor extends Interceptor<SocketEventMap> {
188
220
  () => {
189
221
  /**
190
222
  * @note Create the passthrough connection via the original
191
- * "tls.connect()" with the original connection options
223
+ * "tls.connect()" with the effective connection options
192
224
  * (the real DNS lookup and the caller's certificate
193
225
  * validation included). The latch exempts the transport
194
226
  * connect of that connection from interception.
@@ -196,10 +228,7 @@ export class SocketInterceptor extends Interceptor<SocketEventMap> {
196
228
  isCreatingPassthroughConnection = true
197
229
 
198
230
  try {
199
- return tls.connect(
200
- (realTlsConnectionOptions ??
201
- tlsConnectionOptions) as tls.ConnectionOptions
202
- )
231
+ return tls.connect(tlsConnectionOptions)
203
232
  } finally {
204
233
  isCreatingPassthroughConnection = false
205
234
  }
@@ -253,9 +282,7 @@ export class SocketInterceptor extends Interceptor<SocketEventMap> {
253
282
  * listeners to claim the connection (or once every listener
254
283
  * declines it), the controller passes it through as-is.
255
284
  */
256
- controller.awaitVerdicts(
257
- interceptor.listenerCount('connection')
258
- )
285
+ controller.awaitVerdicts(interceptor.listenerCount('connection'))
259
286
 
260
287
  interceptor.emitter.emit(
261
288
  new SocketConnectionEvent({
@@ -187,7 +187,10 @@ function toServerSocket<T extends net.Socket>(socket: T): T {
187
187
 
188
188
  // Deliver the final chunk passed to "end(chunk)", if any.
189
189
  if (finalEnd.chunk != null) {
190
- socket.push(toBuffer(finalEnd.chunk, finalEnd.encoding), finalEnd.encoding)
190
+ socket.push(
191
+ toBuffer(finalEnd.chunk, finalEnd.encoding),
192
+ finalEnd.encoding
193
+ )
191
194
  }
192
195
 
193
196
  socket.push(null)
@@ -538,7 +541,11 @@ export class TcpSocketController extends SocketController {
538
541
  typeof args[0] === 'object' &&
539
542
  (args[0].localAddress != null || args[0].localPort != null)
540
543
  ) {
541
- args[0] = { ...args[0], localAddress: undefined, localPort: undefined }
544
+ args[0] = {
545
+ ...args[0],
546
+ localAddress: undefined,
547
+ localPort: undefined,
548
+ }
542
549
  }
543
550
 
544
551
  return Reflect.apply(target, thisArg, args)
@@ -868,9 +875,7 @@ export class TcpSocketController extends SocketController {
868
875
  }
869
876
  }
870
877
 
871
- #removeBufferedWrite(
872
- args: Parameters<net.Socket['_writeGeneric']>
873
- ): boolean {
878
+ #removeBufferedWrite(args: Parameters<net.Socket['_writeGeneric']>): boolean {
874
879
  const index = this.#bufferedWrites.indexOf(args)
875
880
 
876
881
  if (index === -1) {
@@ -1107,6 +1112,10 @@ export class TcpSocketController extends SocketController {
1107
1112
  * the consumer reads the buffered data.
1108
1113
  */
1109
1114
  #emitClientClose(hadError: boolean): void {
1115
+ if (this.#clientCloseEmitted) {
1116
+ return
1117
+ }
1118
+
1110
1119
  if (
1111
1120
  this.#clientEndPushed &&
1112
1121
  !this.socket.readableEnded &&
@@ -1120,9 +1129,7 @@ export class TcpSocketController extends SocketController {
1120
1129
  closeDelivered = true
1121
1130
 
1122
1131
  process.nextTick(() => {
1123
- if (!this.#clientCloseEmitted) {
1124
- this.socket.emit('close', hadErrorOverride ?? hadError)
1125
- }
1132
+ this.#emitClientClose(hadErrorOverride ?? hadError)
1126
1133
  })
1127
1134
  }
1128
1135
 
@@ -1144,7 +1151,13 @@ export class TcpSocketController extends SocketController {
1144
1151
  return
1145
1152
  }
1146
1153
 
1147
- this.socket.emit('close', hadError)
1154
+ // Agent pools inspect the socket state inside their close listeners.
1155
+ // Match Node.js: a closed socket must already be destroyed and unwritable.
1156
+ this.socket.destroy()
1157
+
1158
+ if (this.#realHandleSwapped) {
1159
+ this.socket.emit('close', hadError)
1160
+ }
1148
1161
  }
1149
1162
 
1150
1163
  #onMockSocketDrain = () => {
@@ -1462,6 +1475,18 @@ export class TlsSocketController extends TcpSocketController {
1462
1475
  }
1463
1476
 
1464
1477
  protected emulateConnect(): void {
1478
+ /**
1479
+ * @note The client chooses its wire protocol when we emulate the
1480
+ * handshake. A later passthrough handshake must preserve that choice
1481
+ * instead of negotiating a different protocol for buffered writes.
1482
+ */
1483
+ if (this.#tlsConnectionOptions) {
1484
+ const alpnProtocol = this.socket._handle.getALPNNegotiatedProtocol()
1485
+ this.#tlsConnectionOptions.ALPNProtocols = alpnProtocol
1486
+ ? [alpnProtocol]
1487
+ : []
1488
+ }
1489
+
1465
1490
  super.emulateConnect()
1466
1491
 
1467
1492
  // For TLS sockets, also invoke the "secureConnect" callbacks since some consumers,
@@ -0,0 +1,22 @@
1
+ interface InternalConnectionContext {
2
+ run<T>(callback: () => T): T
3
+ consume(): boolean
4
+ }
5
+
6
+ declare global {
7
+ var __MSW_INTERNAL_CONNECTION_CONTEXT: InternalConnectionContext | undefined
8
+ }
9
+
10
+ /**
11
+ * Share the Node-provided async context with the browser-built WebSocket
12
+ * entry point without importing Node modules into the browser bundle.
13
+ */
14
+ export function runAsInternalConnection<T>(callback: () => T): T {
15
+ const context = globalThis.__MSW_INTERNAL_CONNECTION_CONTEXT
16
+
17
+ if (context) {
18
+ return context.run(callback)
19
+ }
20
+
21
+ return callback()
22
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"buffer-utils-BvPY1Tc-.js","names":["#owners","#getLoggerNamespace"],"sources":["../../src/disposable.ts","../../src/utils/logger.ts","../../src/interceptor.ts","../../src/utils/buffer-utils.ts"],"sourcesContent":["export type DisposableSubscription = () => void\n\nexport class Disposable {\n protected subscriptions: Array<DisposableSubscription> = []\n\n public dispose() {\n let subscription: DisposableSubscription | undefined\n\n while ((subscription = this.subscriptions.pop())) {\n subscription()\n }\n }\n}\n","import debug from 'debug'\n\nexport type LogLevel = 'default' | 'verbose'\n\nexport interface Logger {\n info(message: string, ...positionals: Array<unknown>): void\n verbose(message: string, ...positionals: Array<unknown>): void\n isEnabled(level: LogLevel): boolean\n}\n\nconst LOG_TIMESTAMP_REGEXP = /\\d{2}:\\d{2}:\\d{2}\\.\\d{3}/\n\nfunction normalizeNamespace(namespace: string): string {\n return namespace\n .split(':')\n .map((segment) => {\n return segment\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase()\n })\n .filter(Boolean)\n .join(':')\n}\n\nfunction getTimestamp(): string {\n return new Date().toISOString().slice(11, 23)\n}\n\nasync function readBody(message: Request | Response): Promise<string | null> {\n if (message.body == null) {\n return null\n }\n\n try {\n return await message.clone().text()\n } catch {\n return null\n }\n}\n\nfunction formatHeaders(headers: Headers): Array<string> {\n return Array.from(headers.entries()).map(([name, value]) => {\n return `${name}: ${value}`\n })\n}\n\nasync function formatHttpMessage(\n startLine: string,\n message: Request | Response\n): Promise<string> {\n const lines = [startLine, ...formatHeaders(message.headers)]\n const body = await readBody(message)\n\n lines.push('', body ?? '')\n\n return lines.join('\\n')\n}\n\nexport async function formatRequest(request: Request): Promise<string> {\n return formatHttpMessage(`${request.method} ${request.url}`, request)\n}\n\nexport async function formatResponse(response: Response): Promise<string> {\n const statusText = response.statusText ? ` ${response.statusText}` : ''\n return formatHttpMessage(\n `HTTP ${response.status}${statusText}`,\n response\n )\n}\n\nfunction formatLogArguments(arguments_: Array<unknown>): void {\n const message = arguments_[0]\n\n if (typeof message === 'string') {\n const messageWithoutDebugTimestamp = message.replace(\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z /,\n ''\n )\n const timestampMatch = messageWithoutDebugTimestamp.match(\n LOG_TIMESTAMP_REGEXP\n )\n\n if (!timestampMatch || timestampMatch.index === undefined) {\n arguments_[0] = messageWithoutDebugTimestamp\n return\n }\n\n const messagePrefix = messageWithoutDebugTimestamp\n .slice(0, timestampMatch.index)\n .trim()\n const messageBody = messageWithoutDebugTimestamp\n .slice(timestampMatch.index + timestampMatch[0].length)\n .trimStart()\n\n arguments_[0] = `${timestampMatch[0]} ${messagePrefix} ${messageBody}`\n }\n}\n\nfunction useConciseTimestamp(logger: debug.Debugger): void {\n logger.log = (...arguments_) => {\n formatLogArguments(arguments_)\n debug.log(...arguments_)\n }\n}\n\nfunction isVerboseLoggingEnabled(): boolean {\n if (typeof process !== 'undefined' && process.env.DEBUG_LEVEL === 'verbose') {\n return true\n }\n\n /**\n * @note Consult the localStorage only in browser-like environments.\n * In Node.js 26+, reading \"globalThis.localStorage\" without the\n * \"--localstorage-file\" flag set emits an experimental warning\n * (a try/catch cannot suppress it). Node.js consumers control the\n * log level via the \"DEBUG_LEVEL\" environment variable above.\n */\n if (typeof document === 'undefined') {\n return false\n }\n\n try {\n return globalThis.localStorage?.getItem('debugLevel') === 'verbose'\n } catch {\n return false\n }\n}\n\nexport function createLogger(namespace: string): Logger {\n const normalizedNamespace = normalizeNamespace(namespace)\n const logger = debug(`interceptors:${normalizedNamespace}`)\n Reflect.set(logger, 'useColors', true)\n useConciseTimestamp(logger)\n\n return {\n info(message, ...positionals) {\n logger(`${getTimestamp()} ${message}`, ...positionals)\n },\n verbose(message, ...positionals) {\n if (!isVerboseLoggingEnabled()) {\n return\n }\n\n logger(`${getTimestamp()} ${message}`, ...positionals)\n },\n isEnabled(level) {\n return (\n logger.enabled && (level === 'default' || isVerboseLoggingEnabled())\n )\n },\n }\n}\n","import { Emitter, type DefaultEventMap } from 'rettime'\nimport { Disposable } from './disposable'\nimport { createLogger, type Logger } from './utils/logger'\n\nexport enum InterceptorReadyState {\n INACTIVE = 'INACTIVE',\n ACTIVE = 'ACTIVE',\n DISPOSED = 'DISPOSED',\n}\n\ndeclare global {\n var __MSW_INTERCEPTORS_REGISTRY: Map<symbol, Interceptor<any>> | undefined\n}\n\nconst interceptorsRegistry = (globalThis.__MSW_INTERCEPTORS_REGISTRY ??=\n new Map<symbol, Interceptor<any>>())\n\nexport abstract class Interceptor<\n Events extends DefaultEventMap,\n> extends Disposable {\n declare ['constructor']: typeof Interceptor\n\n protected emitter: Emitter<Events>\n protected readonly logger: Logger\n\n public readyState: InterceptorReadyState\n\n static readonly symbol: symbol\n\n #owners: Set<object>\n\n static singleton<T extends Interceptor<any>>(\n InterceptorClass: (new () => T) & { symbol: symbol }\n ): T {\n const symbol = InterceptorClass.symbol\n const existing = interceptorsRegistry.get(symbol)\n\n if (existing instanceof InterceptorClass) {\n return existing\n }\n\n const newInstance = new InterceptorClass()\n interceptorsRegistry.set(symbol, newInstance)\n return newInstance\n }\n\n constructor() {\n super()\n\n this.#owners = new Set()\n this.readyState = InterceptorReadyState.INACTIVE\n this.emitter = new Emitter()\n this.logger = createLogger(this.#getLoggerNamespace())\n }\n\n protected abstract predicate(): boolean\n protected abstract setup(): void\n\n public apply(owner: object = this): void {\n if (this.#owners.has(owner)) {\n return\n }\n\n if (\n this.readyState !== InterceptorReadyState.ACTIVE &&\n !this.predicate()\n ) {\n return\n }\n\n this.#owners.add(owner)\n\n if (this.readyState === InterceptorReadyState.ACTIVE) {\n return\n }\n\n try {\n this.setup()\n this.readyState = InterceptorReadyState.ACTIVE\n this.logger.info('apply')\n } catch (error) {\n this.dispose(owner)\n throw error\n }\n }\n\n public dispose(owner: object = this): void {\n if (!this.#owners.delete(owner)) {\n return\n }\n\n if (this.#owners.size > 0) {\n return\n }\n\n super.dispose()\n this.emitter.removeAllListeners()\n this.readyState = InterceptorReadyState.DISPOSED\n this.logger.info('disable')\n }\n\n public on: Emitter<Events>['on'] = (type, listener, options) => {\n return this.emitter.on(type, listener, options)\n }\n\n public once: Emitter<Events>['once'] = (type, listener, options) => {\n return this.emitter.once(type, listener, options)\n }\n\n public listeners: Emitter<Events>['listeners'] = (type) => {\n return this.emitter.listeners(type)\n }\n\n public listenerCount: Emitter<Events>['listenerCount'] = (type) => {\n return this.emitter.listenerCount(type)\n }\n\n public removeListener: Emitter<Events>['removeListener'] = (\n type,\n listener\n ) => {\n return this.emitter.removeListener(type, listener)\n }\n\n public removeAllListeners: Emitter<Events>['removeAllListeners'] = (type) => {\n this.logger.info('removeAllListeners %o', { eventType: type ?? '*' })\n return this.emitter.removeAllListeners(type)\n }\n\n #getLoggerNamespace(): string {\n const symbolDescription = this.constructor.symbol?.description\n\n if (symbolDescription) {\n return symbolDescription.replace(/-interceptor$/, '')\n }\n\n return this.constructor.name.replace(/Interceptor$/, '')\n }\n}\n","const encoder = new TextEncoder()\n\nexport function encodeBuffer(text: string) {\n return encoder.encode(text)\n}\n\nexport function decodeBuffer(buffer: AllowSharedBufferSource, encoding?: string): string {\n const decoder = new TextDecoder(encoding)\n return decoder.decode(buffer)\n}\n\n/**\n * Create an `ArrayBuffer` from the given `Uint8Array`.\n * Takes the byte offset into account to produce the right buffer\n * in the case when the buffer is bigger than the data view.\n */\nexport function toArrayBuffer(array: Uint8Array<ArrayBuffer>): ArrayBuffer {\n return array.buffer.slice(\n array.byteOffset,\n array.byteOffset + array.byteLength\n )\n}\n\nexport function toBuffer(\n data: string | Buffer | Uint8Array<ArrayBufferLike>,\n encoding?: BufferEncoding\n): Buffer {\n if (Buffer.isBuffer(data)) {\n return data\n }\n\n if (data instanceof Uint8Array) {\n return Buffer.from(data.buffer)\n }\n\n return Buffer.from(data, encoding)\n}\n"],"mappings":"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"}