@mswjs/interceptors 0.39.7 → 0.40.0

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 (142) hide show
  1. package/lib/browser/{chunk-PFGO5BSM.js → chunk-2HUMWGRD.js} +15 -3
  2. package/lib/browser/chunk-2HUMWGRD.js.map +1 -0
  3. package/lib/browser/{chunk-ZXAL3FMU.js → chunk-2MCNQOY3.js} +56 -51
  4. package/lib/browser/chunk-2MCNQOY3.js.map +1 -0
  5. package/lib/browser/chunk-57RIRQUY.js +218 -0
  6. package/lib/browser/chunk-57RIRQUY.js.map +1 -0
  7. package/lib/browser/chunk-FW45TRCB.js +178 -0
  8. package/lib/browser/chunk-FW45TRCB.js.map +1 -0
  9. package/lib/browser/{chunk-TIPR373R.js → chunk-JQ2S7G56.js} +19 -3
  10. package/lib/browser/chunk-JQ2S7G56.js.map +1 -0
  11. package/lib/browser/{chunk-3RXCRGL2.mjs → chunk-LIKZF2VU.mjs} +102 -1
  12. package/lib/browser/chunk-LIKZF2VU.mjs.map +1 -0
  13. package/lib/browser/{chunk-F2NPA2FP.js → chunk-MNT2FUCH.js} +61 -56
  14. package/lib/browser/chunk-MNT2FUCH.js.map +1 -0
  15. package/lib/browser/chunk-VOUOVDAW.mjs +178 -0
  16. package/lib/browser/chunk-VOUOVDAW.mjs.map +1 -0
  17. package/lib/browser/{chunk-TX5GBTFY.mjs → chunk-VYSDLBSS.mjs} +13 -1
  18. package/lib/browser/chunk-VYSDLBSS.mjs.map +1 -0
  19. package/lib/browser/{chunk-ZDGZFWQH.mjs → chunk-WADP6VHN.mjs} +49 -44
  20. package/lib/browser/chunk-WADP6VHN.mjs.map +1 -0
  21. package/lib/browser/{chunk-MDMPOBGY.mjs → chunk-WOWPV4GR.mjs} +52 -47
  22. package/lib/browser/chunk-WOWPV4GR.mjs.map +1 -0
  23. package/lib/browser/{chunk-QED3Q6Z2.mjs → chunk-Z5TSB3T6.mjs} +17 -1
  24. package/lib/browser/{chunk-QED3Q6Z2.mjs.map → chunk-Z5TSB3T6.mjs.map} +1 -1
  25. package/lib/browser/{glossary-7152281e.d.ts → glossary-f7ee1c9d.d.ts} +22 -17
  26. package/lib/browser/index.d.ts +1 -2
  27. package/lib/browser/index.js +6 -4
  28. package/lib/browser/index.js.map +1 -1
  29. package/lib/browser/index.mjs +4 -2
  30. package/lib/browser/index.mjs.map +1 -1
  31. package/lib/browser/interceptors/WebSocket/index.js +9 -7
  32. package/lib/browser/interceptors/WebSocket/index.js.map +1 -1
  33. package/lib/browser/interceptors/WebSocket/index.mjs +6 -4
  34. package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -1
  35. package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +1 -2
  36. package/lib/browser/interceptors/XMLHttpRequest/index.js +6 -6
  37. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +5 -5
  38. package/lib/browser/interceptors/fetch/index.d.ts +1 -2
  39. package/lib/browser/interceptors/fetch/index.js +6 -6
  40. package/lib/browser/interceptors/fetch/index.mjs +5 -5
  41. package/lib/browser/presets/browser.d.ts +1 -2
  42. package/lib/browser/presets/browser.js +8 -8
  43. package/lib/browser/presets/browser.mjs +6 -6
  44. package/lib/node/{BatchInterceptor-5b72232f.d.ts → BatchInterceptor-cb9a2eee.d.ts} +1 -1
  45. package/lib/node/{Interceptor-bc5a9d8e.d.ts → Interceptor-dc0a39b5.d.ts} +22 -16
  46. package/lib/node/RemoteHttpInterceptor.d.ts +2 -3
  47. package/lib/node/RemoteHttpInterceptor.js +31 -27
  48. package/lib/node/RemoteHttpInterceptor.js.map +1 -1
  49. package/lib/node/RemoteHttpInterceptor.mjs +28 -24
  50. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -1
  51. package/lib/node/{chunk-EKNRB5ZS.mjs → chunk-5UGIB6OX.mjs} +40 -29
  52. package/lib/node/chunk-5UGIB6OX.mjs.map +1 -0
  53. package/lib/node/{chunk-4NEYTVWD.mjs → chunk-5V3SIIW2.mjs} +48 -43
  54. package/lib/node/chunk-5V3SIIW2.mjs.map +1 -0
  55. package/lib/node/{chunk-VV2LUF5K.js → chunk-6B3ZQOO2.js} +51 -46
  56. package/lib/node/chunk-6B3ZQOO2.js.map +1 -0
  57. package/lib/node/chunk-7Q53NNPV.js +189 -0
  58. package/lib/node/chunk-7Q53NNPV.js.map +1 -0
  59. package/lib/node/{chunk-A7U44ARP.js → chunk-DOWWQYXZ.js} +104 -3
  60. package/lib/node/chunk-DOWWQYXZ.js.map +1 -0
  61. package/lib/node/{chunk-Z5LWCBZS.js → chunk-FRZQJNBO.js} +56 -51
  62. package/lib/node/chunk-FRZQJNBO.js.map +1 -0
  63. package/lib/node/{chunk-TJDMZZXE.mjs → chunk-GKN5RBVR.mjs} +2 -2
  64. package/lib/node/{chunk-R6JVCM7X.js → chunk-J5MULIHT.js} +3 -3
  65. package/lib/node/{chunk-IHJSPMYM.mjs → chunk-JXGB54LE.mjs} +102 -1
  66. package/lib/node/chunk-JXGB54LE.mjs.map +1 -0
  67. package/lib/node/{chunk-3CNGDJFB.mjs → chunk-OFW5L5ET.mjs} +50 -45
  68. package/lib/node/chunk-OFW5L5ET.mjs.map +1 -0
  69. package/lib/node/{chunk-A7Q4RTDJ.mjs → chunk-R6T7CL5E.mjs} +55 -115
  70. package/lib/node/chunk-R6T7CL5E.mjs.map +1 -0
  71. package/lib/node/{chunk-RC2XPCC4.mjs → chunk-SQ6RHTJR.mjs} +2 -2
  72. package/lib/node/chunk-SRMAQGPM.js +30 -0
  73. package/lib/node/chunk-SRMAQGPM.js.map +1 -0
  74. package/lib/node/{chunk-4YBV77DG.js → chunk-T3TW4P64.js} +3 -3
  75. package/lib/node/{chunk-N4ZZFE24.js → chunk-VYO5XDY2.js} +56 -45
  76. package/lib/node/chunk-VYO5XDY2.js.map +1 -0
  77. package/lib/node/chunk-YWNGXXUQ.mjs +30 -0
  78. package/lib/node/{chunk-3GJB4JDF.mjs.map → chunk-YWNGXXUQ.mjs.map} +1 -1
  79. package/lib/node/index.d.ts +2 -3
  80. package/lib/node/index.js +6 -4
  81. package/lib/node/index.js.map +1 -1
  82. package/lib/node/index.mjs +5 -3
  83. package/lib/node/index.mjs.map +1 -1
  84. package/lib/node/interceptors/ClientRequest/index.d.ts +1 -2
  85. package/lib/node/interceptors/ClientRequest/index.js +6 -6
  86. package/lib/node/interceptors/ClientRequest/index.mjs +5 -5
  87. package/lib/node/interceptors/XMLHttpRequest/index.d.ts +1 -2
  88. package/lib/node/interceptors/XMLHttpRequest/index.js +5 -5
  89. package/lib/node/interceptors/XMLHttpRequest/index.mjs +4 -4
  90. package/lib/node/interceptors/fetch/index.d.ts +1 -2
  91. package/lib/node/interceptors/fetch/index.js +5 -5
  92. package/lib/node/interceptors/fetch/index.mjs +4 -4
  93. package/lib/node/presets/node.d.ts +1 -2
  94. package/lib/node/presets/node.js +10 -10
  95. package/lib/node/presets/node.mjs +7 -7
  96. package/lib/node/utils/node/index.js +3 -3
  97. package/lib/node/utils/node/index.mjs +2 -2
  98. package/package.json +2 -1
  99. package/src/RemoteHttpInterceptor.ts +18 -13
  100. package/src/RequestController.test.ts +78 -31
  101. package/src/RequestController.ts +63 -39
  102. package/src/index.ts +4 -0
  103. package/src/interceptors/ClientRequest/MockHttpSocket.ts +24 -3
  104. package/src/interceptors/ClientRequest/index.ts +14 -18
  105. package/src/interceptors/WebSocket/index.ts +6 -2
  106. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +45 -35
  107. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +24 -21
  108. package/src/interceptors/fetch/index.ts +61 -50
  109. package/src/utils/handleRequest.ts +65 -95
  110. package/lib/browser/chunk-3RXCRGL2.mjs.map +0 -1
  111. package/lib/browser/chunk-F2NPA2FP.js.map +0 -1
  112. package/lib/browser/chunk-MDMPOBGY.mjs.map +0 -1
  113. package/lib/browser/chunk-MMKGBEJO.mjs +0 -246
  114. package/lib/browser/chunk-MMKGBEJO.mjs.map +0 -1
  115. package/lib/browser/chunk-PFGO5BSM.js.map +0 -1
  116. package/lib/browser/chunk-T7TBRNJZ.js +0 -117
  117. package/lib/browser/chunk-T7TBRNJZ.js.map +0 -1
  118. package/lib/browser/chunk-TIPR373R.js.map +0 -1
  119. package/lib/browser/chunk-TX5GBTFY.mjs.map +0 -1
  120. package/lib/browser/chunk-XX6WKANU.js +0 -246
  121. package/lib/browser/chunk-XX6WKANU.js.map +0 -1
  122. package/lib/browser/chunk-ZDGZFWQH.mjs.map +0 -1
  123. package/lib/browser/chunk-ZXAL3FMU.js.map +0 -1
  124. package/lib/node/chunk-3CNGDJFB.mjs.map +0 -1
  125. package/lib/node/chunk-3GJB4JDF.mjs +0 -14
  126. package/lib/node/chunk-4NEYTVWD.mjs.map +0 -1
  127. package/lib/node/chunk-72ZIHMEB.js +0 -249
  128. package/lib/node/chunk-72ZIHMEB.js.map +0 -1
  129. package/lib/node/chunk-A7Q4RTDJ.mjs.map +0 -1
  130. package/lib/node/chunk-A7U44ARP.js.map +0 -1
  131. package/lib/node/chunk-EKNRB5ZS.mjs.map +0 -1
  132. package/lib/node/chunk-IHJSPMYM.mjs.map +0 -1
  133. package/lib/node/chunk-N4ZZFE24.js.map +0 -1
  134. package/lib/node/chunk-SMXZPJEA.js +0 -14
  135. package/lib/node/chunk-SMXZPJEA.js.map +0 -1
  136. package/lib/node/chunk-VV2LUF5K.js.map +0 -1
  137. package/lib/node/chunk-Z5LWCBZS.js.map +0 -1
  138. package/src/utils/RequestController.ts +0 -21
  139. /package/lib/node/{chunk-TJDMZZXE.mjs.map → chunk-GKN5RBVR.mjs.map} +0 -0
  140. /package/lib/node/{chunk-R6JVCM7X.js.map → chunk-J5MULIHT.js.map} +0 -0
  141. /package/lib/node/{chunk-RC2XPCC4.mjs.map → chunk-SQ6RHTJR.mjs.map} +0 -0
  142. /package/lib/node/{chunk-4YBV77DG.js.map → chunk-T3TW4P64.js.map} +0 -0
@@ -153,30 +153,26 @@ export class ClientRequestInterceptor extends Interceptor<HttpRequestEventMap> {
153
153
  request,
154
154
  socket,
155
155
  }) => {
156
- const requestId = Reflect.get(request, kRequestId)
157
- const controller = new RequestController(request)
158
-
159
- const isRequestHandled = await handleRequest({
160
- request,
161
- requestId,
162
- controller,
163
- emitter: this.emitter,
164
- onResponse: (response) => {
165
- socket.respondWith(response)
156
+ const controller = new RequestController(request, {
157
+ passthrough() {
158
+ socket.passthrough()
166
159
  },
167
- onRequestError: (response) => {
168
- socket.respondWith(response)
160
+ async respondWith(response) {
161
+ await socket.respondWith(response)
169
162
  },
170
- onError: (error) => {
171
- if (error instanceof Error) {
172
- socket.errorWith(error)
163
+ errorWith(reason) {
164
+ if (reason instanceof Error) {
165
+ socket.errorWith(reason)
173
166
  }
174
167
  },
175
168
  })
176
169
 
177
- if (!isRequestHandled) {
178
- return socket.passthrough()
179
- }
170
+ await handleRequest({
171
+ request,
172
+ requestId: Reflect.get(request, kRequestId),
173
+ controller,
174
+ emitter: this.emitter,
175
+ })
180
176
  }
181
177
 
182
178
  public onResponse: MockHttpSocketResponseCallback = async ({
@@ -17,6 +17,7 @@ import {
17
17
  } from './WebSocketOverride'
18
18
  import { bindEvent } from './utils/bindEvent'
19
19
  import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
20
+ import { emitAsync } from '../../utils/emitAsync'
20
21
 
21
22
  export { type WebSocketData, WebSocketTransport } from './WebSocketTransport'
22
23
  export {
@@ -102,7 +103,7 @@ export class WebSocketInterceptor extends Interceptor<WebSocketEventMap> {
102
103
  // Emit the "connection" event to the interceptor on the next tick
103
104
  // so the client can modify WebSocket options, like "binaryType"
104
105
  // while the connection is already pending.
105
- queueMicrotask(() => {
106
+ queueMicrotask(async () => {
106
107
  try {
107
108
  const server = new WebSocketServerConnection(
108
109
  socket,
@@ -110,10 +111,13 @@ export class WebSocketInterceptor extends Interceptor<WebSocketEventMap> {
110
111
  createConnection
111
112
  )
112
113
 
114
+ const hasConnectionListeners =
115
+ this.emitter.listenerCount('connection') > 0
116
+
113
117
  // The "globalThis.WebSocket" class stands for
114
118
  // the client-side connection. Assume it's established
115
119
  // as soon as the WebSocket instance is constructed.
116
- const hasConnectionListeners = this.emitter.emit('connection', {
120
+ await emitAsync(this.emitter, 'connection', {
117
121
  client: new WebSocketClientConnection(socket, transport),
118
122
  server,
119
123
  info: {
@@ -57,7 +57,10 @@ export class XMLHttpRequestController {
57
57
  Array<Function>
58
58
  >
59
59
 
60
- constructor(readonly initialRequest: XMLHttpRequest, public logger: Logger) {
60
+ constructor(
61
+ readonly initialRequest: XMLHttpRequest,
62
+ public logger: Logger
63
+ ) {
61
64
  this[kIsRequestHandled] = false
62
65
 
63
66
  this.events = new Map()
@@ -111,7 +114,7 @@ export class XMLHttpRequestController {
111
114
  case 'addEventListener': {
112
115
  const [eventName, listener] = args as [
113
116
  keyof XMLHttpRequestEventTargetEventMap,
114
- Function
117
+ Function,
115
118
  ]
116
119
 
117
120
  this.registerEvent(eventName, listener)
@@ -131,7 +134,7 @@ export class XMLHttpRequestController {
131
134
 
132
135
  case 'send': {
133
136
  const [body] = args as [
134
- body?: XMLHttpRequestBodyInit | Document | null
137
+ body?: XMLHttpRequestBodyInit | Document | null,
135
138
  ]
136
139
 
137
140
  this.request.addEventListener('load', () => {
@@ -166,38 +169,44 @@ export class XMLHttpRequestController {
166
169
  const fetchRequest = this.toFetchApiRequest(requestBody)
167
170
  this[kFetchRequest] = fetchRequest.clone()
168
171
 
169
- const onceRequestSettled =
170
- this.onRequest?.call(this, {
171
- request: fetchRequest,
172
- requestId: this.requestId!,
173
- }) || Promise.resolve()
174
-
175
- onceRequestSettled.finally(() => {
176
- // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.
177
- if (!this[kIsRequestHandled]) {
178
- this.logger.info(
179
- 'request callback settled but request has not been handled (readystate %d), performing as-is...',
180
- this.request.readyState
181
- )
182
-
183
- /**
184
- * @note Set the intercepted request ID on the original request in Node.js
185
- * so that if it triggers any other interceptors, they don't attempt
186
- * to process it once again.
187
- *
188
- * For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
189
- * and we don't want for both XHR and ClientRequest interceptors to
190
- * handle the same request at the same time (e.g. emit the "response" event twice).
191
- */
192
- if (IS_NODE) {
193
- this.request.setRequestHeader(
194
- INTERNAL_REQUEST_ID_HEADER_NAME,
195
- this.requestId!
172
+ /**
173
+ * @note Start request handling on the next tick so that the user
174
+ * could add event listeners for "loadend" before the interceptor fires it.
175
+ */
176
+ queueMicrotask(() => {
177
+ const onceRequestSettled =
178
+ this.onRequest?.call(this, {
179
+ request: fetchRequest,
180
+ requestId: this.requestId!,
181
+ }) || Promise.resolve()
182
+
183
+ onceRequestSettled.finally(() => {
184
+ // If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.
185
+ if (!this[kIsRequestHandled]) {
186
+ this.logger.info(
187
+ 'request callback settled but request has not been handled (readystate %d), performing as-is...',
188
+ this.request.readyState
196
189
  )
197
- }
198
190
 
199
- return invoke()
200
- }
191
+ /**
192
+ * @note Set the intercepted request ID on the original request in Node.js
193
+ * so that if it triggers any other interceptors, they don't attempt
194
+ * to process it once again.
195
+ *
196
+ * For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
197
+ * and we don't want for both XHR and ClientRequest interceptors to
198
+ * handle the same request at the same time (e.g. emit the "response" event twice).
199
+ */
200
+ if (IS_NODE) {
201
+ this.request.setRequestHeader(
202
+ INTERNAL_REQUEST_ID_HEADER_NAME,
203
+ this.requestId!
204
+ )
205
+ }
206
+
207
+ return invoke()
208
+ }
209
+ })
201
210
  })
202
211
 
203
212
  break
@@ -241,7 +250,7 @@ export class XMLHttpRequestController {
241
250
  case 'addEventListener': {
242
251
  const [eventName, listener] = args as [
243
252
  keyof XMLHttpRequestEventTargetEventMap,
244
- Function
253
+ Function,
245
254
  ]
246
255
  this.registerUploadEvent(eventName, listener)
247
256
  this.logger.info('upload.addEventListener', eventName, listener)
@@ -312,6 +321,7 @@ export class XMLHttpRequestController {
312
321
  loaded: totalRequestBodyLength,
313
322
  total: totalRequestBodyLength,
314
323
  })
324
+
315
325
  this.trigger('loadend', this.request.upload, {
316
326
  loaded: totalRequestBodyLength,
317
327
  total: totalRequestBodyLength,
@@ -614,7 +624,7 @@ export class XMLHttpRequestController {
614
624
  private trigger<
615
625
  EventName extends keyof (XMLHttpRequestEventTargetEventMap & {
616
626
  readystatechange: ProgressEvent<XMLHttpRequestEventTarget>
617
- })
627
+ }),
618
628
  >(
619
629
  eventName: EventName,
620
630
  target: XMLHttpRequest | XMLHttpRequestUpload,
@@ -3,6 +3,7 @@ import { XMLHttpRequestEmitter } from '.'
3
3
  import { RequestController } from '../../RequestController'
4
4
  import { XMLHttpRequestController } from './XMLHttpRequestController'
5
5
  import { handleRequest } from '../../utils/handleRequest'
6
+ import { isResponseError } from '../../utils/responseUtils'
6
7
 
7
8
  export interface XMLHttpRequestProxyOptions {
8
9
  emitter: XMLHttpRequestEmitter
@@ -52,7 +53,28 @@ export function createXMLHttpRequestProxy({
52
53
  )
53
54
 
54
55
  xhrRequestController.onRequest = async function ({ request, requestId }) {
55
- const controller = new RequestController(request)
56
+ const controller = new RequestController(request, {
57
+ passthrough: () => {
58
+ this.logger.info(
59
+ 'no mocked response received, performing request as-is...'
60
+ )
61
+ },
62
+ respondWith: async (response) => {
63
+ if (isResponseError(response)) {
64
+ this.errorWith(new TypeError('Network error'))
65
+ return
66
+ }
67
+
68
+ await this.respondWith(response)
69
+ },
70
+ errorWith: (reason) => {
71
+ this.logger.info('request errored!', { error: reason })
72
+
73
+ if (reason instanceof Error) {
74
+ this.errorWith(reason)
75
+ }
76
+ },
77
+ })
56
78
 
57
79
  this.logger.info('awaiting mocked response...')
58
80
 
@@ -61,31 +83,12 @@ export function createXMLHttpRequestProxy({
61
83
  emitter.listenerCount('request')
62
84
  )
63
85
 
64
- const isRequestHandled = await handleRequest({
86
+ await handleRequest({
65
87
  request,
66
88
  requestId,
67
89
  controller,
68
90
  emitter,
69
- onResponse: async (response) => {
70
- await this.respondWith(response)
71
- },
72
- onRequestError: () => {
73
- this.errorWith(new TypeError('Network error'))
74
- },
75
- onError: (error) => {
76
- this.logger.info('request errored!', { error })
77
-
78
- if (error instanceof Error) {
79
- this.errorWith(error)
80
- }
81
- },
82
91
  })
83
-
84
- if (!isRequestHandled) {
85
- this.logger.info(
86
- 'no mocked response received, performing request as-is...'
87
- )
88
- }
89
92
  }
90
93
 
91
94
  xhrRequestController.onResponse = async function ({
@@ -1,4 +1,5 @@
1
1
  import { invariant } from 'outvariant'
2
+ import { until } from '@open-draft/until'
2
3
  import { DeferredPromise } from '@open-draft/deferred-promise'
3
4
  import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
4
5
  import { Interceptor } from '../../Interceptor'
@@ -13,6 +14,7 @@ import { decompressResponse } from './utils/decompression'
13
14
  import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
14
15
  import { FetchResponse } from '../../utils/fetchUtils'
15
16
  import { setRawRequest } from '../../getRawRequest'
17
+ import { isResponseError } from '../../utils/responseUtils'
16
18
 
17
19
  export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
18
20
  static symbol = Symbol('fetch')
@@ -59,22 +61,54 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
59
61
  }
60
62
 
61
63
  const responsePromise = new DeferredPromise<Response>()
62
- const controller = new RequestController(request)
63
64
 
64
- this.logger.info('[%s] %s', request.method, request.url)
65
- this.logger.info('awaiting for the mocked response...')
65
+ const controller = new RequestController(request, {
66
+ passthrough: async () => {
67
+ this.logger.info('request has not been handled, passthrough...')
66
68
 
67
- this.logger.info(
68
- 'emitting the "request" event for %s listener(s)...',
69
- this.emitter.listenerCount('request')
70
- )
69
+ /**
70
+ * @note Clone the request instance right before performing it.
71
+ * This preserves any modifications made to the intercepted request
72
+ * in the "request" listener. This also allows the user to read the
73
+ * request body in the "response" listener (otherwise "unusable").
74
+ */
75
+ const requestCloneForResponseEvent = request.clone()
76
+
77
+ // Perform the intercepted request as-is.
78
+ const { error: responseError, data: originalResponse } = await until(
79
+ () => pureFetch(request)
80
+ )
81
+
82
+ if (responseError) {
83
+ return responsePromise.reject(responseError)
84
+ }
85
+
86
+ this.logger.info('original fetch performed', originalResponse)
87
+
88
+ if (this.emitter.listenerCount('response') > 0) {
89
+ this.logger.info('emitting the "response" event...')
90
+
91
+ const responseClone = originalResponse.clone()
92
+ await emitAsync(this.emitter, 'response', {
93
+ response: responseClone,
94
+ isMockedResponse: false,
95
+ request: requestCloneForResponseEvent,
96
+ requestId,
97
+ })
98
+ }
99
+
100
+ // Resolve the response promise with the original response
101
+ // since the `fetch()` return this internal promise.
102
+ responsePromise.resolve(originalResponse)
103
+ },
104
+ respondWith: async (rawResponse) => {
105
+ // Handle mocked `Response.error()` (i.e. request errors).
106
+ if (isResponseError(rawResponse)) {
107
+ this.logger.info('request has errored!', { response: rawResponse })
108
+ responsePromise.reject(createNetworkError(rawResponse))
109
+ return
110
+ }
71
111
 
72
- const isRequestHandled = await handleRequest({
73
- request,
74
- requestId,
75
- emitter: this.emitter,
76
- controller,
77
- onResponse: async (rawResponse) => {
78
112
  this.logger.info('received mocked response!', {
79
113
  rawResponse,
80
114
  })
@@ -134,51 +168,28 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
134
168
 
135
169
  responsePromise.resolve(response)
136
170
  },
137
- onRequestError: (response) => {
138
- this.logger.info('request has errored!', { response })
139
- responsePromise.reject(createNetworkError(response))
140
- },
141
- onError: (error) => {
142
- this.logger.info('request has been aborted!', { error })
143
- responsePromise.reject(error)
171
+ errorWith: (reason) => {
172
+ this.logger.info('request has been aborted!', { reason })
173
+ responsePromise.reject(reason)
144
174
  },
145
175
  })
146
176
 
147
- if (isRequestHandled) {
148
- this.logger.info('request has been handled, returning mock promise...')
149
- return responsePromise
150
- }
177
+ this.logger.info('[%s] %s', request.method, request.url)
178
+ this.logger.info('awaiting for the mocked response...')
151
179
 
152
180
  this.logger.info(
153
- 'no mocked response received, performing request as-is...'
181
+ 'emitting the "request" event for %s listener(s)...',
182
+ this.emitter.listenerCount('request')
154
183
  )
155
184
 
156
- /**
157
- * @note Clone the request instance right before performing it.
158
- * This preserves any modifications made to the intercepted request
159
- * in the "request" listener. This also allows the user to read the
160
- * request body in the "response" listener (otherwise "unusable").
161
- */
162
- const requestCloneForResponseEvent = request.clone()
163
-
164
- return pureFetch(request).then(async (response) => {
165
- this.logger.info('original fetch performed', response)
166
-
167
- if (this.emitter.listenerCount('response') > 0) {
168
- this.logger.info('emitting the "response" event...')
169
-
170
- const responseClone = response.clone()
171
-
172
- await emitAsync(this.emitter, 'response', {
173
- response: responseClone,
174
- isMockedResponse: false,
175
- request: requestCloneForResponseEvent,
176
- requestId,
177
- })
178
- }
179
-
180
- return response
185
+ await handleRequest({
186
+ request,
187
+ requestId,
188
+ emitter: this.emitter,
189
+ controller,
181
190
  })
191
+
192
+ return responsePromise
182
193
  }
183
194
 
184
195
  Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {