@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
@@ -3,12 +3,11 @@ import { DeferredPromise } from '@open-draft/deferred-promise'
3
3
  import { until } from '@open-draft/until'
4
4
  import type { HttpRequestEventMap } from '../glossary'
5
5
  import { emitAsync } from './emitAsync'
6
- import { kResponsePromise, RequestController } from '../RequestController'
6
+ import { RequestController } from '../RequestController'
7
7
  import {
8
8
  createServerErrorResponse,
9
9
  isResponseError,
10
10
  isResponseLike,
11
- ResponseError,
12
11
  } from './responseUtils'
13
12
  import { InterceptorError } from '../InterceptorError'
14
13
  import { isNodeLikeError } from './isNodeLikeError'
@@ -19,43 +18,22 @@ interface HandleRequestOptions {
19
18
  request: Request
20
19
  emitter: Emitter<HttpRequestEventMap>
21
20
  controller: RequestController
22
-
23
- /**
24
- * Called when the request has been handled
25
- * with the given `Response` instance.
26
- */
27
- onResponse: (response: Response) => void | Promise<void>
28
-
29
- /**
30
- * Called when the request has been handled
31
- * with the given `Response.error()` instance.
32
- */
33
- onRequestError: (response: ResponseError) => void
34
-
35
- /**
36
- * Called when an unhandled error happens during the
37
- * request handling. This is never a thrown error/response.
38
- */
39
- onError: (error: unknown) => void
40
21
  }
41
22
 
42
- /**
43
- * @returns {Promise<boolean>} Indicates whether the request has been handled.
44
- */
45
23
  export async function handleRequest(
46
24
  options: HandleRequestOptions
47
- ): Promise<boolean> {
25
+ ): Promise<void> {
48
26
  const handleResponse = async (
49
27
  response: Response | Error | Record<string, any>
50
28
  ) => {
51
29
  if (response instanceof Error) {
52
- options.onError(response)
30
+ await options.controller.errorWith(response)
53
31
  return true
54
32
  }
55
33
 
56
34
  // Handle "Response.error()" instances.
57
35
  if (isResponseError(response)) {
58
- options.onRequestError(response)
36
+ await options.controller.respondWith(response)
59
37
  return true
60
38
  }
61
39
 
@@ -65,13 +43,13 @@ export async function handleRequest(
65
43
  * since Response instances are, in fact, objects.
66
44
  */
67
45
  if (isResponseLike(response)) {
68
- await options.onResponse(response)
46
+ await options.controller.respondWith(response)
69
47
  return true
70
48
  }
71
49
 
72
50
  // Handle arbitrary objects provided to `.errorWith(reason)`.
73
51
  if (isObject(response)) {
74
- options.onError(response)
52
+ await options.controller.errorWith(response)
75
53
  return true
76
54
  }
77
55
 
@@ -87,7 +65,7 @@ export async function handleRequest(
87
65
 
88
66
  // Support mocking Node.js-like errors.
89
67
  if (isNodeLikeError(error)) {
90
- options.onError(error)
68
+ await options.controller.errorWith(error)
91
69
  return true
92
70
  }
93
71
 
@@ -102,15 +80,14 @@ export async function handleRequest(
102
80
  // Add the last "request" listener to check if the request
103
81
  // has been handled in any way. If it hasn't, resolve the
104
82
  // response promise with undefined.
105
- options.emitter.once('request', ({ requestId: pendingRequestId }) => {
106
- if (pendingRequestId !== options.requestId) {
107
- return
108
- }
109
-
110
- if (options.controller[kResponsePromise].state === 'pending') {
111
- options.controller[kResponsePromise].resolve(undefined)
112
- }
113
- })
83
+ // options.emitter.once('request', async ({ requestId: pendingRequestId }) => {
84
+ // if (
85
+ // pendingRequestId === options.requestId &&
86
+ // options.controller.readyState === RequestController.PENDING
87
+ // ) {
88
+ // await options.controller.passthrough()
89
+ // }
90
+ // })
114
91
 
115
92
  const requestAbortPromise = new DeferredPromise<void, unknown>()
116
93
 
@@ -119,16 +96,17 @@ export async function handleRequest(
119
96
  */
120
97
  if (options.request.signal) {
121
98
  if (options.request.signal.aborted) {
122
- requestAbortPromise.reject(options.request.signal.reason)
123
- } else {
124
- options.request.signal.addEventListener(
125
- 'abort',
126
- () => {
127
- requestAbortPromise.reject(options.request.signal.reason)
128
- },
129
- { once: true }
130
- )
99
+ await options.controller.errorWith(options.request.signal.reason)
100
+ return
131
101
  }
102
+
103
+ options.request.signal.addEventListener(
104
+ 'abort',
105
+ () => {
106
+ requestAbortPromise.reject(options.request.signal.reason)
107
+ },
108
+ { once: true }
109
+ )
132
110
  }
133
111
 
134
112
  const result = await until(async () => {
@@ -146,25 +124,21 @@ export async function handleRequest(
146
124
  // Short-circuit the request handling promise if the request gets aborted.
147
125
  requestAbortPromise,
148
126
  requestListenersPromise,
149
- options.controller[kResponsePromise],
127
+ options.controller.handled,
150
128
  ])
151
-
152
- // The response promise will settle immediately once
153
- // the developer calls either "respondWith" or "errorWith".
154
- return await options.controller[kResponsePromise]
155
129
  })
156
130
 
157
131
  // Handle the request being aborted while waiting for the request listeners.
158
132
  if (requestAbortPromise.state === 'rejected') {
159
- options.onError(requestAbortPromise.rejectionReason)
160
- return true
133
+ await options.controller.errorWith(requestAbortPromise.rejectionReason)
134
+ return
161
135
  }
162
136
 
163
137
  if (result.error) {
164
138
  // Handle the error during the request listener execution.
165
139
  // These can be thrown responses or request errors.
166
140
  if (await handleResponseError(result.error)) {
167
- return true
141
+ return
168
142
  }
169
143
 
170
144
  // If the developer has added "unhandledException" listeners,
@@ -175,7 +149,28 @@ export async function handleRequest(
175
149
  // This is needed because the original controller might have been already
176
150
  // interacted with (e.g. "respondWith" or "errorWith" called on it).
177
151
  const unhandledExceptionController = new RequestController(
178
- options.request
152
+ options.request,
153
+ {
154
+ /**
155
+ * @note Intentionally empty passthrough handle.
156
+ * This controller is created within another controller and we only need
157
+ * to know if `unhandledException` listeners handled the request.
158
+ */
159
+ passthrough() {},
160
+ async respondWith(response) {
161
+ await handleResponse(response)
162
+ },
163
+ async errorWith(reason) {
164
+ /**
165
+ * @note Handle the result of the unhandled controller
166
+ * in the same way as the original request controller.
167
+ * The exception here is that thrown errors within the
168
+ * "unhandledException" event do NOT result in another
169
+ * emit of the same event. They are forwarded as-is.
170
+ */
171
+ await options.controller.errorWith(reason)
172
+ },
173
+ }
179
174
  )
180
175
 
181
176
  await emitAsync(options.emitter, 'unhandledException', {
@@ -183,53 +178,28 @@ export async function handleRequest(
183
178
  request: options.request,
184
179
  requestId: options.requestId,
185
180
  controller: unhandledExceptionController,
186
- }).then(() => {
187
- // If all the "unhandledException" listeners have finished
188
- // but have not handled the response in any way, preemptively
189
- // resolve the pending response promise from the new controller.
190
- // This prevents it from hanging forever.
191
- if (
192
- unhandledExceptionController[kResponsePromise].state === 'pending'
193
- ) {
194
- unhandledExceptionController[kResponsePromise].resolve(undefined)
195
- }
196
181
  })
197
182
 
198
- const nextResult = await until(
199
- () => unhandledExceptionController[kResponsePromise]
200
- )
201
-
202
- /**
203
- * @note Handle the result of the unhandled controller
204
- * in the same way as the original request controller.
205
- * The exception here is that thrown errors within the
206
- * "unhandledException" event do NOT result in another
207
- * emit of the same event. They are forwarded as-is.
208
- */
209
- if (nextResult.error) {
210
- return handleResponseError(nextResult.error)
211
- }
212
-
213
- if (nextResult.data) {
214
- return handleResponse(nextResult.data)
183
+ // If all the "unhandledException" listeners have finished
184
+ // but have not handled the request in any way, passthrough.
185
+ if (
186
+ unhandledExceptionController.readyState !== RequestController.PENDING
187
+ ) {
188
+ return
215
189
  }
216
190
  }
217
191
 
218
192
  // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.
219
- options.onResponse(createServerErrorResponse(result.error))
220
- return true
193
+ await options.controller.respondWith(
194
+ createServerErrorResponse(result.error)
195
+ )
196
+ return
221
197
  }
222
198
 
223
- /**
224
- * Handle a mocked Response instance.
225
- * @note That this can also be an Error in case
226
- * the developer called "errorWith". This differentiates
227
- * unhandled exceptions from intended errors.
228
- */
229
- if (result.data) {
230
- return handleResponse(result.data)
199
+ // If the request hasn't been handled by this point, passthrough.
200
+ if (options.controller.readyState === RequestController.PENDING) {
201
+ return await options.controller.passthrough()
231
202
  }
232
203
 
233
- // In all other cases, consider the request unhandled.
234
- return false
204
+ return options.controller.handled
235
205
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/glossary.ts","../../src/utils/canParseUrl.ts","../../src/utils/getValueBySymbol.ts","../../src/utils/fetchUtils.ts","../../src/getRawRequest.ts"],"sourcesContent":["import type { RequestController } from './RequestController'\n\nexport const IS_PATCHED_MODULE: unique symbol = Symbol('isPatchedModule')\n\n/**\n * @note Export `RequestController` as a type only.\n * It's never meant to be created in the userland.\n */\nexport type { RequestController }\n\nexport type RequestCredentials = 'omit' | 'include' | 'same-origin'\n\nexport type HttpRequestEventMap = {\n request: [\n args: {\n request: Request\n requestId: string\n controller: RequestController\n }\n ]\n response: [\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ]\n unhandledException: [\n args: {\n error: unknown\n request: Request\n requestId: string\n controller: RequestController\n }\n ]\n}\n","/**\n * Returns a boolean indicating whether the given URL string\n * can be parsed into a `URL` instance.\n * A substitute for `URL.canParse()` for Node.js 18.\n */\nexport function canParseUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch (_error) {\n return false\n }\n}\n","/**\n * Returns the value behind the symbol with the given name.\n */\nexport function getValueBySymbol<T>(\n symbolName: string,\n source: object\n): T | undefined {\n const ownSymbols = Object.getOwnPropertySymbols(source)\n\n const symbol = ownSymbols.find((symbol) => {\n return symbol.description === symbolName\n })\n\n if (symbol) {\n return Reflect.get(source, symbol)\n }\n\n return\n}\n","import { canParseUrl } from './canParseUrl'\nimport { getValueBySymbol } from './getValueBySymbol'\n\nexport interface FetchResponseInit extends ResponseInit {\n url?: string\n}\n\ninterface UndiciFetchInternalState {\n aborted: boolean\n rangeRequested: boolean\n timingAllowPassed: boolean\n requestIncludesCredentials: boolean\n type: ResponseType\n status: number\n statusText: string\n timingInfo: unknown\n cacheState: unknown\n headersList: Record<symbol, Map<string, unknown>>\n urlList: Array<URL>\n body?: {\n stream: ReadableStream\n source: unknown\n length: number\n }\n}\n\nexport class FetchResponse extends Response {\n /**\n * Response status codes for responses that cannot have body.\n * @see https://fetch.spec.whatwg.org/#statuses\n */\n static readonly STATUS_CODES_WITHOUT_BODY = [101, 103, 204, 205, 304]\n\n static readonly STATUS_CODES_WITH_REDIRECT = [301, 302, 303, 307, 308]\n\n static isConfigurableStatusCode(status: number): boolean {\n return status >= 200 && status <= 599\n }\n\n static isRedirectResponse(status: number): boolean {\n return FetchResponse.STATUS_CODES_WITH_REDIRECT.includes(status)\n }\n\n /**\n * Returns a boolean indicating whether the given response status\n * code represents a response that can have a body.\n */\n static isResponseWithBody(status: number): boolean {\n return !FetchResponse.STATUS_CODES_WITHOUT_BODY.includes(status)\n }\n\n static setUrl(url: string | undefined, response: Response): void {\n if (!url || url === 'about:' || !canParseUrl(url)) {\n return\n }\n\n const state = getValueBySymbol<UndiciFetchInternalState>('state', response)\n\n if (state) {\n // In Undici, push the URL to the internal list of URLs.\n // This will respect the `response.url` getter logic correctly.\n state.urlList.push(new URL(url))\n } else {\n // In other libraries, redefine the `url` property directly.\n Object.defineProperty(response, 'url', {\n value: url,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n }\n\n /**\n * Parses the given raw HTTP headers into a Fetch API `Headers` instance.\n */\n static parseRawHeaders(rawHeaders: Array<string>): Headers {\n const headers = new Headers()\n for (let line = 0; line < rawHeaders.length; line += 2) {\n headers.append(rawHeaders[line], rawHeaders[line + 1])\n }\n return headers\n }\n\n constructor(body?: BodyInit | null, init: FetchResponseInit = {}) {\n const status = init.status ?? 200\n const safeStatus = FetchResponse.isConfigurableStatusCode(status)\n ? status\n : 200\n const finalBody = FetchResponse.isResponseWithBody(status) ? body : null\n\n super(finalBody, {\n status: safeStatus,\n statusText: init.statusText,\n headers: init.headers,\n })\n\n if (status !== safeStatus) {\n /**\n * @note Undici keeps an internal \"Symbol(state)\" that holds\n * the actual value of response status. Update that in Node.js.\n */\n const state = getValueBySymbol<UndiciFetchInternalState>('state', this)\n\n if (state) {\n state.status = status\n } else {\n Object.defineProperty(this, 'status', {\n value: status,\n enumerable: true,\n configurable: true,\n writable: false,\n })\n }\n }\n\n FetchResponse.setUrl(init.url, this)\n }\n}\n","const kRawRequest = Symbol('kRawRequest')\n\n/**\n * Returns a raw request instance associated with this request.\n *\n * @example\n * interceptor.on('request', ({ request }) => {\n * const rawRequest = getRawRequest(request)\n *\n * if (rawRequest instanceof http.ClientRequest) {\n * console.log(rawRequest.rawHeaders)\n * }\n * })\n */\nexport function getRawRequest(request: Request): unknown | undefined {\n return Reflect.get(request, kRawRequest)\n}\n\nexport function setRawRequest(request: Request, rawRequest: unknown): void {\n Reflect.set(request, kRawRequest, rawRequest)\n}\n"],"mappings":";AAEO,IAAM,oBAAmC,OAAO,iBAAiB;;;ACGjE,SAAS,YAAY,KAAsB;AAChD,MAAI;AACF,QAAI,IAAI,GAAG;AACX,WAAO;AAAA,EACT,SAAS,QAAP;AACA,WAAO;AAAA,EACT;AACF;;;ACTO,SAAS,iBACd,YACA,QACe;AACf,QAAM,aAAa,OAAO,sBAAsB,MAAM;AAEtD,QAAM,SAAS,WAAW,KAAK,CAACA,YAAW;AACzC,WAAOA,QAAO,gBAAgB;AAAA,EAChC,CAAC;AAED,MAAI,QAAQ;AACV,WAAO,QAAQ,IAAI,QAAQ,MAAM;AAAA,EACnC;AAEA;AACF;;;ACQO,IAAM,iBAAN,cAA4B,SAAS;AAAA,EAS1C,OAAO,yBAAyB,QAAyB;AACvD,WAAO,UAAU,OAAO,UAAU;AAAA,EACpC;AAAA,EAEA,OAAO,mBAAmB,QAAyB;AACjD,WAAO,eAAc,2BAA2B,SAAS,MAAM;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,mBAAmB,QAAyB;AACjD,WAAO,CAAC,eAAc,0BAA0B,SAAS,MAAM;AAAA,EACjE;AAAA,EAEA,OAAO,OAAO,KAAyB,UAA0B;AAC/D,QAAI,CAAC,OAAO,QAAQ,YAAY,CAAC,YAAY,GAAG,GAAG;AACjD;AAAA,IACF;AAEA,UAAM,QAAQ,iBAA2C,SAAS,QAAQ;AAE1E,QAAI,OAAO;AAGT,YAAM,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC;AAAA,IACjC,OAAO;AAEL,aAAO,eAAe,UAAU,OAAO;AAAA,QACrC,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,gBAAgB,YAAoC;AACzD,UAAM,UAAU,IAAI,QAAQ;AAC5B,aAAS,OAAO,GAAG,OAAO,WAAW,QAAQ,QAAQ,GAAG;AACtD,cAAQ,OAAO,WAAW,IAAI,GAAG,WAAW,OAAO,CAAC,CAAC;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,MAAwB,OAA0B,CAAC,GAAG;AApFpE;AAqFI,UAAM,UAAS,UAAK,WAAL,YAAe;AAC9B,UAAM,aAAa,eAAc,yBAAyB,MAAM,IAC5D,SACA;AACJ,UAAM,YAAY,eAAc,mBAAmB,MAAM,IAAI,OAAO;AAEpE,UAAM,WAAW;AAAA,MACf,QAAQ;AAAA,MACR,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,IAChB,CAAC;AAED,QAAI,WAAW,YAAY;AAKzB,YAAM,QAAQ,iBAA2C,SAAS,IAAI;AAEtE,UAAI,OAAO;AACT,cAAM,SAAS;AAAA,MACjB,OAAO;AACL,eAAO,eAAe,MAAM,UAAU;AAAA,UACpC,OAAO;AAAA,UACP,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAEA,mBAAc,OAAO,KAAK,KAAK,IAAI;AAAA,EACrC;AACF;AA5FO,IAAM,gBAAN;AAAA;AAAA;AAAA;AAAA;AAAM,cAKK,4BAA4B,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AALzD,cAOK,6BAA6B,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;;;ACjCvE,IAAM,cAAc,OAAO,aAAa;AAcjC,SAAS,cAAc,SAAuC;AACnE,SAAO,QAAQ,IAAI,SAAS,WAAW;AACzC;AAEO,SAAS,cAAc,SAAkB,YAA2B;AACzE,UAAQ,IAAI,SAAS,aAAa,UAAU;AAC9C;","names":["symbol"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/interceptors/fetch/index.ts","../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts"],"names":["readable","transformers","response"],"mappings":"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","sourcesContent":["import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\n\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol('fetch')\n\n constructor() {\n super(FetchInterceptor.symbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const pureFetch = globalThis.fetch\n\n invariant(\n !(pureFetch as any)[IS_PATCHED_MODULE],\n 'Failed to patch the \"fetch\" module: already patched.'\n )\n\n globalThis.fetch = async (input, init) => {\n const requestId = createRequestId()\n\n /**\n * @note Resolve potentially relative request URL\n * against the present `location`. This is mainly\n * for native `fetch` in JSDOM.\n * @see https://github.com/mswjs/msw/issues/1625\n */\n const resolvedInput =\n typeof input === 'string' &&\n typeof location !== 'undefined' &&\n !canParseUrl(input)\n ? new URL(input, location.href)\n : input\n\n const request = new Request(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n const controller = new RequestController(request)\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n const isRequestHandled = await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n onResponse: async (rawResponse) => {\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response =\n decompressedStream === null\n ? rawResponse\n : new FetchResponse(decompressedStream, rawResponse)\n\n FetchResponse.setUrl(request.url, response)\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\n // Clone the mocked response for the \"response\" event listener.\n // This way, the listener can read the response and not lock its body\n // for the actual fetch consumer.\n response: response.clone(),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n onRequestError: (response) => {\n this.logger.info('request has errored!', { response })\n responsePromise.reject(createNetworkError(response))\n },\n onError: (error) => {\n this.logger.info('request has been aborted!', { error })\n responsePromise.reject(error)\n },\n })\n\n if (isRequestHandled) {\n this.logger.info('request has been handled, returning mock promise...')\n return responsePromise\n }\n\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n return pureFetch(request).then(async (response) => {\n this.logger.info('original fetch performed', response)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = response.clone()\n\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n return response\n })\n }\n\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n enumerable: true,\n configurable: true,\n value: true,\n })\n\n this.subscriptions.push(() => {\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n value: undefined,\n })\n\n globalThis.fetch = pureFetch\n\n this.logger.info(\n 'restored native \"globalThis.fetch\"!',\n globalThis.fetch.name\n )\n })\n }\n}\n","export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","export class BrotliDecompressionStream extends TransformStream {\n constructor() {\n console.warn(\n '[Interceptors]: Brotli decompression of response streams is not supported in the browser'\n )\n\n super({\n transform(chunk, controller) {\n // Keep the stream as passthrough, it does nothing.\n controller.enqueue(chunk)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/interceptors/fetch/index.ts","../../src/interceptors/fetch/utils/createNetworkError.ts","../../src/interceptors/fetch/utils/followRedirect.ts","../../src/interceptors/fetch/utils/brotli-decompress.browser.ts","../../src/interceptors/fetch/utils/decompression.ts"],"sourcesContent":["import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'\nimport { Interceptor } from '../../Interceptor'\nimport { RequestController } from '../../RequestController'\nimport { emitAsync } from '../../utils/emitAsync'\nimport { handleRequest } from '../../utils/handleRequest'\nimport { canParseUrl } from '../../utils/canParseUrl'\nimport { createRequestId } from '../../createRequestId'\nimport { createNetworkError } from './utils/createNetworkError'\nimport { followFetchRedirect } from './utils/followRedirect'\nimport { decompressResponse } from './utils/decompression'\nimport { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'\nimport { FetchResponse } from '../../utils/fetchUtils'\nimport { setRawRequest } from '../../getRawRequest'\n\nexport class FetchInterceptor extends Interceptor<HttpRequestEventMap> {\n static symbol = Symbol('fetch')\n\n constructor() {\n super(FetchInterceptor.symbol)\n }\n\n protected checkEnvironment() {\n return hasConfigurableGlobal('fetch')\n }\n\n protected async setup() {\n const pureFetch = globalThis.fetch\n\n invariant(\n !(pureFetch as any)[IS_PATCHED_MODULE],\n 'Failed to patch the \"fetch\" module: already patched.'\n )\n\n globalThis.fetch = async (input, init) => {\n const requestId = createRequestId()\n\n /**\n * @note Resolve potentially relative request URL\n * against the present `location`. This is mainly\n * for native `fetch` in JSDOM.\n * @see https://github.com/mswjs/msw/issues/1625\n */\n const resolvedInput =\n typeof input === 'string' &&\n typeof location !== 'undefined' &&\n !canParseUrl(input)\n ? new URL(input, location.href)\n : input\n\n const request = new Request(resolvedInput, init)\n\n /**\n * @note Set the raw request only if a Request instance was provided to fetch.\n */\n if (input instanceof Request) {\n setRawRequest(request, input)\n }\n\n const responsePromise = new DeferredPromise<Response>()\n const controller = new RequestController(request)\n\n this.logger.info('[%s] %s', request.method, request.url)\n this.logger.info('awaiting for the mocked response...')\n\n this.logger.info(\n 'emitting the \"request\" event for %s listener(s)...',\n this.emitter.listenerCount('request')\n )\n\n const isRequestHandled = await handleRequest({\n request,\n requestId,\n emitter: this.emitter,\n controller,\n onResponse: async (rawResponse) => {\n this.logger.info('received mocked response!', {\n rawResponse,\n })\n\n // Decompress the mocked response body, if applicable.\n const decompressedStream = decompressResponse(rawResponse)\n const response =\n decompressedStream === null\n ? rawResponse\n : new FetchResponse(decompressedStream, rawResponse)\n\n FetchResponse.setUrl(request.url, response)\n\n /**\n * Undici's handling of following redirect responses.\n * Treat the \"manual\" redirect mode as a regular mocked response.\n * This way, the client can manually follow the redirect it receives.\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1173\n */\n if (FetchResponse.isRedirectResponse(response.status)) {\n // Reject the request promise if its `redirect` is set to `error`\n // and it receives a mocked redirect response.\n if (request.redirect === 'error') {\n responsePromise.reject(createNetworkError('unexpected redirect'))\n return\n }\n\n if (request.redirect === 'follow') {\n followFetchRedirect(request, response).then(\n (response) => {\n responsePromise.resolve(response)\n },\n (reason) => {\n responsePromise.reject(reason)\n }\n )\n return\n }\n }\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n // Await the response listeners to finish before resolving\n // the response promise. This ensures all your logic finishes\n // before the interceptor resolves the pending response.\n await emitAsync(this.emitter, 'response', {\n // Clone the mocked response for the \"response\" event listener.\n // This way, the listener can read the response and not lock its body\n // for the actual fetch consumer.\n response: response.clone(),\n isMockedResponse: true,\n request,\n requestId,\n })\n }\n\n responsePromise.resolve(response)\n },\n onRequestError: (response) => {\n this.logger.info('request has errored!', { response })\n responsePromise.reject(createNetworkError(response))\n },\n onError: (error) => {\n this.logger.info('request has been aborted!', { error })\n responsePromise.reject(error)\n },\n })\n\n if (isRequestHandled) {\n this.logger.info('request has been handled, returning mock promise...')\n return responsePromise\n }\n\n this.logger.info(\n 'no mocked response received, performing request as-is...'\n )\n\n /**\n * @note Clone the request instance right before performing it.\n * This preserves any modifications made to the intercepted request\n * in the \"request\" listener. This also allows the user to read the\n * request body in the \"response\" listener (otherwise \"unusable\").\n */\n const requestCloneForResponseEvent = request.clone()\n\n return pureFetch(request).then(async (response) => {\n this.logger.info('original fetch performed', response)\n\n if (this.emitter.listenerCount('response') > 0) {\n this.logger.info('emitting the \"response\" event...')\n\n const responseClone = response.clone()\n\n await emitAsync(this.emitter, 'response', {\n response: responseClone,\n isMockedResponse: false,\n request: requestCloneForResponseEvent,\n requestId,\n })\n }\n\n return response\n })\n }\n\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n enumerable: true,\n configurable: true,\n value: true,\n })\n\n this.subscriptions.push(() => {\n Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {\n value: undefined,\n })\n\n globalThis.fetch = pureFetch\n\n this.logger.info(\n 'restored native \"globalThis.fetch\"!',\n globalThis.fetch.name\n )\n })\n }\n}\n","export function createNetworkError(cause?: unknown) {\n return Object.assign(new TypeError('Failed to fetch'), {\n cause,\n })\n}\n","import { createNetworkError } from './createNetworkError'\n\nconst REQUEST_BODY_HEADERS = [\n 'content-encoding',\n 'content-language',\n 'content-location',\n 'content-type',\n 'content-length',\n]\n\nconst kRedirectCount = Symbol('kRedirectCount')\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/index.js#L1210\n */\nexport async function followFetchRedirect(\n request: Request,\n response: Response\n): Promise<Response> {\n if (response.status !== 303 && request.body != null) {\n return Promise.reject(createNetworkError())\n }\n\n const requestUrl = new URL(request.url)\n\n let locationUrl: URL\n try {\n // If the location is a relative URL, use the request URL as the base URL.\n locationUrl = new URL(response.headers.get('location')!, request.url) \n } catch (error) {\n return Promise.reject(createNetworkError(error))\n }\n\n if (\n !(locationUrl.protocol === 'http:' || locationUrl.protocol === 'https:')\n ) {\n return Promise.reject(\n createNetworkError('URL scheme must be a HTTP(S) scheme')\n )\n }\n\n if (Reflect.get(request, kRedirectCount) > 20) {\n return Promise.reject(createNetworkError('redirect count exceeded'))\n }\n\n Object.defineProperty(request, kRedirectCount, {\n value: (Reflect.get(request, kRedirectCount) || 0) + 1,\n })\n\n if (\n request.mode === 'cors' &&\n (locationUrl.username || locationUrl.password) &&\n !sameOrigin(requestUrl, locationUrl)\n ) {\n return Promise.reject(\n createNetworkError('cross origin not allowed for request mode \"cors\"')\n )\n }\n\n const requestInit: RequestInit = {}\n\n if (\n ([301, 302].includes(response.status) && request.method === 'POST') ||\n (response.status === 303 && !['HEAD', 'GET'].includes(request.method))\n ) {\n requestInit.method = 'GET'\n requestInit.body = null\n\n REQUEST_BODY_HEADERS.forEach((headerName) => {\n request.headers.delete(headerName)\n })\n }\n\n if (!sameOrigin(requestUrl, locationUrl)) {\n request.headers.delete('authorization')\n request.headers.delete('proxy-authorization')\n request.headers.delete('cookie')\n request.headers.delete('host')\n }\n\n /**\n * @note Undici \"safely\" extracts the request body.\n * I suspect we cannot dispatch this request again\n * since its body has been read and the stream is locked.\n */\n\n requestInit.headers = request.headers\n const finalResponse = await fetch(new Request(locationUrl, requestInit))\n Object.defineProperty(finalResponse, 'redirected', {\n value: true,\n configurable: true,\n })\n\n return finalResponse\n}\n\n/**\n * @see https://github.com/nodejs/undici/blob/a6dac3149c505b58d2e6d068b97f4dc993da55f0/lib/web/fetch/util.js#L761\n */\nfunction sameOrigin(left: URL, right: URL): boolean {\n if (left.origin === right.origin && left.origin === 'null') {\n return true\n }\n\n if (\n left.protocol === right.protocol &&\n left.hostname === right.hostname &&\n left.port === right.port\n ) {\n return true\n }\n\n return false\n}\n","export class BrotliDecompressionStream extends TransformStream {\n constructor() {\n console.warn(\n '[Interceptors]: Brotli decompression of response streams is not supported in the browser'\n )\n\n super({\n transform(chunk, controller) {\n // Keep the stream as passthrough, it does nothing.\n controller.enqueue(chunk)\n },\n })\n }\n}\n","// Import from an internal alias that resolves to different modules\n// depending on the environment. This way, we can keep the fetch interceptor\n// intact while using different strategies for Brotli decompression.\nimport { BrotliDecompressionStream } from 'internal:brotli-decompress'\n\nclass PipelineStream extends TransformStream {\n constructor(\n transformStreams: Array<TransformStream>,\n ...strategies: Array<QueuingStrategy>\n ) {\n super({}, ...strategies)\n\n const readable = [super.readable as any, ...transformStreams].reduce(\n (readable, transform) => readable.pipeThrough(transform)\n )\n\n Object.defineProperty(this, 'readable', {\n get() {\n return readable\n },\n })\n }\n}\n\nexport function parseContentEncoding(contentEncoding: string): Array<string> {\n return contentEncoding\n .toLowerCase()\n .split(',')\n .map((coding) => coding.trim())\n}\n\nfunction createDecompressionStream(\n contentEncoding: string\n): TransformStream | null {\n if (contentEncoding === '') {\n return null\n }\n\n const codings = parseContentEncoding(contentEncoding)\n\n if (codings.length === 0) {\n return null\n }\n\n const transformers = codings.reduceRight<Array<TransformStream>>(\n (transformers, coding) => {\n if (coding === 'gzip' || coding === 'x-gzip') {\n return transformers.concat(new DecompressionStream('gzip'))\n } else if (coding === 'deflate') {\n return transformers.concat(new DecompressionStream('deflate'))\n } else if (coding === 'br') {\n return transformers.concat(new BrotliDecompressionStream())\n } else {\n transformers.length = 0\n }\n\n return transformers\n },\n []\n )\n\n return new PipelineStream(transformers)\n}\n\nexport function decompressResponse(\n response: Response\n): ReadableStream<any> | null {\n if (response.body === null) {\n return null\n }\n\n const decompressionStream = createDecompressionStream(\n response.headers.get('content-encoding') || ''\n )\n\n if (!decompressionStream) {\n return null\n }\n\n // Use `pipeTo` and return the decompression stream's readable\n // instead of `pipeThrough` because that will lock the original\n // response stream, making it unusable as the input to Response.\n response.body.pipeTo(decompressionStream.writable)\n return decompressionStream.readable\n}\n"],"mappings":"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","names":["readable","transformers","response"]}
@@ -1,246 +0,0 @@
1
- // src/RequestController.ts
2
- import { invariant } from "outvariant";
3
- import { DeferredPromise } from "@open-draft/deferred-promise";
4
-
5
- // src/InterceptorError.ts
6
- var InterceptorError = class extends Error {
7
- constructor(message) {
8
- super(message);
9
- this.name = "InterceptorError";
10
- Object.setPrototypeOf(this, InterceptorError.prototype);
11
- }
12
- };
13
-
14
- // src/RequestController.ts
15
- var kRequestHandled = Symbol("kRequestHandled");
16
- var kResponsePromise = Symbol("kResponsePromise");
17
- var RequestController = class {
18
- constructor(request) {
19
- this.request = request;
20
- this[kRequestHandled] = false;
21
- this[kResponsePromise] = new DeferredPromise();
22
- }
23
- /**
24
- * Respond to this request with the given `Response` instance.
25
- * @example
26
- * controller.respondWith(new Response())
27
- * controller.respondWith(Response.json({ id }))
28
- * controller.respondWith(Response.error())
29
- */
30
- respondWith(response) {
31
- invariant.as(
32
- InterceptorError,
33
- !this[kRequestHandled],
34
- 'Failed to respond to the "%s %s" request: the "request" event has already been handled.',
35
- this.request.method,
36
- this.request.url
37
- );
38
- this[kRequestHandled] = true;
39
- this[kResponsePromise].resolve(response);
40
- }
41
- /**
42
- * Error this request with the given reason.
43
- *
44
- * @example
45
- * controller.errorWith()
46
- * controller.errorWith(new Error('Oops!'))
47
- * controller.errorWith({ message: 'Oops!'})
48
- */
49
- errorWith(reason) {
50
- invariant.as(
51
- InterceptorError,
52
- !this[kRequestHandled],
53
- 'Failed to error the "%s %s" request: the "request" event has already been handled.',
54
- this.request.method,
55
- this.request.url
56
- );
57
- this[kRequestHandled] = true;
58
- this[kResponsePromise].resolve(reason);
59
- }
60
- };
61
- kResponsePromise, kRequestHandled;
62
-
63
- // src/utils/emitAsync.ts
64
- async function emitAsync(emitter, eventName, ...data) {
65
- const listeners = emitter.listeners(eventName);
66
- if (listeners.length === 0) {
67
- return;
68
- }
69
- for (const listener of listeners) {
70
- await listener.apply(emitter, data);
71
- }
72
- }
73
-
74
- // src/utils/handleRequest.ts
75
- import { DeferredPromise as DeferredPromise2 } from "@open-draft/deferred-promise";
76
- import { until } from "@open-draft/until";
77
-
78
- // src/utils/isObject.ts
79
- function isObject(value, loose = false) {
80
- return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
81
- }
82
-
83
- // src/utils/isPropertyAccessible.ts
84
- function isPropertyAccessible(obj, key) {
85
- try {
86
- obj[key];
87
- return true;
88
- } catch (e) {
89
- return false;
90
- }
91
- }
92
-
93
- // src/utils/responseUtils.ts
94
- function createServerErrorResponse(body) {
95
- return new Response(
96
- JSON.stringify(
97
- body instanceof Error ? {
98
- name: body.name,
99
- message: body.message,
100
- stack: body.stack
101
- } : body
102
- ),
103
- {
104
- status: 500,
105
- statusText: "Unhandled Exception",
106
- headers: {
107
- "Content-Type": "application/json"
108
- }
109
- }
110
- );
111
- }
112
- function isResponseError(response) {
113
- return response != null && response instanceof Response && isPropertyAccessible(response, "type") && response.type === "error";
114
- }
115
- function isResponseLike(value) {
116
- return isObject(value, true) && isPropertyAccessible(value, "status") && isPropertyAccessible(value, "statusText") && isPropertyAccessible(value, "bodyUsed");
117
- }
118
-
119
- // src/utils/isNodeLikeError.ts
120
- function isNodeLikeError(error) {
121
- if (error == null) {
122
- return false;
123
- }
124
- if (!(error instanceof Error)) {
125
- return false;
126
- }
127
- return "code" in error && "errno" in error;
128
- }
129
-
130
- // src/utils/handleRequest.ts
131
- async function handleRequest(options) {
132
- const handleResponse = async (response) => {
133
- if (response instanceof Error) {
134
- options.onError(response);
135
- return true;
136
- }
137
- if (isResponseError(response)) {
138
- options.onRequestError(response);
139
- return true;
140
- }
141
- if (isResponseLike(response)) {
142
- await options.onResponse(response);
143
- return true;
144
- }
145
- if (isObject(response)) {
146
- options.onError(response);
147
- return true;
148
- }
149
- return false;
150
- };
151
- const handleResponseError = async (error) => {
152
- if (error instanceof InterceptorError) {
153
- throw result.error;
154
- }
155
- if (isNodeLikeError(error)) {
156
- options.onError(error);
157
- return true;
158
- }
159
- if (error instanceof Response) {
160
- return await handleResponse(error);
161
- }
162
- return false;
163
- };
164
- options.emitter.once("request", ({ requestId: pendingRequestId }) => {
165
- if (pendingRequestId !== options.requestId) {
166
- return;
167
- }
168
- if (options.controller[kResponsePromise].state === "pending") {
169
- options.controller[kResponsePromise].resolve(void 0);
170
- }
171
- });
172
- const requestAbortPromise = new DeferredPromise2();
173
- if (options.request.signal) {
174
- if (options.request.signal.aborted) {
175
- requestAbortPromise.reject(options.request.signal.reason);
176
- } else {
177
- options.request.signal.addEventListener(
178
- "abort",
179
- () => {
180
- requestAbortPromise.reject(options.request.signal.reason);
181
- },
182
- { once: true }
183
- );
184
- }
185
- }
186
- const result = await until(async () => {
187
- const requestListenersPromise = emitAsync(options.emitter, "request", {
188
- requestId: options.requestId,
189
- request: options.request,
190
- controller: options.controller
191
- });
192
- await Promise.race([
193
- // Short-circuit the request handling promise if the request gets aborted.
194
- requestAbortPromise,
195
- requestListenersPromise,
196
- options.controller[kResponsePromise]
197
- ]);
198
- return await options.controller[kResponsePromise];
199
- });
200
- if (requestAbortPromise.state === "rejected") {
201
- options.onError(requestAbortPromise.rejectionReason);
202
- return true;
203
- }
204
- if (result.error) {
205
- if (await handleResponseError(result.error)) {
206
- return true;
207
- }
208
- if (options.emitter.listenerCount("unhandledException") > 0) {
209
- const unhandledExceptionController = new RequestController(
210
- options.request
211
- );
212
- await emitAsync(options.emitter, "unhandledException", {
213
- error: result.error,
214
- request: options.request,
215
- requestId: options.requestId,
216
- controller: unhandledExceptionController
217
- }).then(() => {
218
- if (unhandledExceptionController[kResponsePromise].state === "pending") {
219
- unhandledExceptionController[kResponsePromise].resolve(void 0);
220
- }
221
- });
222
- const nextResult = await until(
223
- () => unhandledExceptionController[kResponsePromise]
224
- );
225
- if (nextResult.error) {
226
- return handleResponseError(nextResult.error);
227
- }
228
- if (nextResult.data) {
229
- return handleResponse(nextResult.data);
230
- }
231
- }
232
- options.onResponse(createServerErrorResponse(result.error));
233
- return true;
234
- }
235
- if (result.data) {
236
- return handleResponse(result.data);
237
- }
238
- return false;
239
- }
240
-
241
- export {
242
- RequestController,
243
- emitAsync,
244
- handleRequest
245
- };
246
- //# sourceMappingURL=chunk-MMKGBEJO.mjs.map