@depup/mswjs__interceptors 0.41.3-depup.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 (248) hide show
  1. package/ClientRequest/package.json +11 -0
  2. package/LICENSE.md +9 -0
  3. package/README.md +31 -0
  4. package/RemoteHttpInterceptor/package.json +11 -0
  5. package/WebSocket/package.json +12 -0
  6. package/XMLHttpRequest/package.json +12 -0
  7. package/changes.json +10 -0
  8. package/fetch/package.json +12 -0
  9. package/lib/browser/Interceptor-Deczogc8.d.cts +65 -0
  10. package/lib/browser/Interceptor-gqKgs-aF.d.mts +65 -0
  11. package/lib/browser/XMLHttpRequest-BACqefB-.cjs +761 -0
  12. package/lib/browser/XMLHttpRequest-BACqefB-.cjs.map +1 -0
  13. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs +756 -0
  14. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs.map +1 -0
  15. package/lib/browser/bufferUtils-BiiO6HZv.mjs +20 -0
  16. package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +1 -0
  17. package/lib/browser/bufferUtils-Uc0eRItL.cjs +38 -0
  18. package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +1 -0
  19. package/lib/browser/createRequestId-Cs4oXfa1.cjs +205 -0
  20. package/lib/browser/createRequestId-Cs4oXfa1.cjs.map +1 -0
  21. package/lib/browser/createRequestId-DQcIlohW.mjs +170 -0
  22. package/lib/browser/createRequestId-DQcIlohW.mjs.map +1 -0
  23. package/lib/browser/fetch-DdKEdDOR.mjs +248 -0
  24. package/lib/browser/fetch-DdKEdDOR.mjs.map +1 -0
  25. package/lib/browser/fetch-U3v3Y4ap.cjs +253 -0
  26. package/lib/browser/fetch-U3v3Y4ap.cjs.map +1 -0
  27. package/lib/browser/getRawRequest-BTaNLFr0.mjs +218 -0
  28. package/lib/browser/getRawRequest-BTaNLFr0.mjs.map +1 -0
  29. package/lib/browser/getRawRequest-zx8rUJL2.cjs +259 -0
  30. package/lib/browser/getRawRequest-zx8rUJL2.cjs.map +1 -0
  31. package/lib/browser/glossary-BdLS4k1H.d.cts +70 -0
  32. package/lib/browser/glossary-DYwOrogs.d.mts +70 -0
  33. package/lib/browser/handleRequest-CvX2G-Lz.cjs +189 -0
  34. package/lib/browser/handleRequest-CvX2G-Lz.cjs.map +1 -0
  35. package/lib/browser/handleRequest-D7kpTI5U.mjs +178 -0
  36. package/lib/browser/handleRequest-D7kpTI5U.mjs.map +1 -0
  37. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs +45 -0
  38. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs.map +1 -0
  39. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs +33 -0
  40. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs.map +1 -0
  41. package/lib/browser/index.cjs +70 -0
  42. package/lib/browser/index.cjs.map +1 -0
  43. package/lib/browser/index.d.cts +96 -0
  44. package/lib/browser/index.d.mts +96 -0
  45. package/lib/browser/index.mjs +56 -0
  46. package/lib/browser/index.mjs.map +1 -0
  47. package/lib/browser/interceptors/WebSocket/index.cjs +622 -0
  48. package/lib/browser/interceptors/WebSocket/index.cjs.map +1 -0
  49. package/lib/browser/interceptors/WebSocket/index.d.cts +277 -0
  50. package/lib/browser/interceptors/WebSocket/index.d.mts +277 -0
  51. package/lib/browser/interceptors/WebSocket/index.mjs +615 -0
  52. package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -0
  53. package/lib/browser/interceptors/XMLHttpRequest/index.cjs +7 -0
  54. package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +15 -0
  55. package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +15 -0
  56. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +7 -0
  57. package/lib/browser/interceptors/fetch/index.cjs +6 -0
  58. package/lib/browser/interceptors/fetch/index.d.cts +13 -0
  59. package/lib/browser/interceptors/fetch/index.d.mts +13 -0
  60. package/lib/browser/interceptors/fetch/index.mjs +6 -0
  61. package/lib/browser/presets/browser.cjs +17 -0
  62. package/lib/browser/presets/browser.cjs.map +1 -0
  63. package/lib/browser/presets/browser.d.cts +12 -0
  64. package/lib/browser/presets/browser.d.mts +14 -0
  65. package/lib/browser/presets/browser.mjs +17 -0
  66. package/lib/browser/presets/browser.mjs.map +1 -0
  67. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +31 -0
  68. package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +1 -0
  69. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs +25 -0
  70. package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +1 -0
  71. package/lib/node/BatchInterceptor-3LnAnLTx.cjs +49 -0
  72. package/lib/node/BatchInterceptor-3LnAnLTx.cjs.map +1 -0
  73. package/lib/node/BatchInterceptor-D7mXzHcQ.d.mts +26 -0
  74. package/lib/node/BatchInterceptor-DFaBPilf.mjs +44 -0
  75. package/lib/node/BatchInterceptor-DFaBPilf.mjs.map +1 -0
  76. package/lib/node/BatchInterceptor-D_YqR8qU.d.cts +26 -0
  77. package/lib/node/ClientRequest-2rDe54Ui.cjs +1043 -0
  78. package/lib/node/ClientRequest-2rDe54Ui.cjs.map +1 -0
  79. package/lib/node/ClientRequest-Ca8Qykuv.mjs +1034 -0
  80. package/lib/node/ClientRequest-Ca8Qykuv.mjs.map +1 -0
  81. package/lib/node/Interceptor-DEazpLJd.d.mts +133 -0
  82. package/lib/node/Interceptor-DJ2akVWI.d.cts +133 -0
  83. package/lib/node/RemoteHttpInterceptor.cjs +154 -0
  84. package/lib/node/RemoteHttpInterceptor.cjs.map +1 -0
  85. package/lib/node/RemoteHttpInterceptor.d.cts +39 -0
  86. package/lib/node/RemoteHttpInterceptor.d.mts +39 -0
  87. package/lib/node/RemoteHttpInterceptor.mjs +152 -0
  88. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -0
  89. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs +763 -0
  90. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs.map +1 -0
  91. package/lib/node/XMLHttpRequest-C8dIZpds.mjs +757 -0
  92. package/lib/node/XMLHttpRequest-C8dIZpds.mjs.map +1 -0
  93. package/lib/node/bufferUtils-DiCTqG-7.cjs +38 -0
  94. package/lib/node/bufferUtils-DiCTqG-7.cjs.map +1 -0
  95. package/lib/node/bufferUtils-_8XfKIfX.mjs +20 -0
  96. package/lib/node/bufferUtils-_8XfKIfX.mjs.map +1 -0
  97. package/lib/node/chunk-CbDLau6x.cjs +34 -0
  98. package/lib/node/fetch-BmXpK10r.cjs +272 -0
  99. package/lib/node/fetch-BmXpK10r.cjs.map +1 -0
  100. package/lib/node/fetch-G1DVwDKG.mjs +265 -0
  101. package/lib/node/fetch-G1DVwDKG.mjs.map +1 -0
  102. package/lib/node/fetchUtils-BaY5iWXw.cjs +419 -0
  103. package/lib/node/fetchUtils-BaY5iWXw.cjs.map +1 -0
  104. package/lib/node/fetchUtils-CoU35g3M.mjs +359 -0
  105. package/lib/node/fetchUtils-CoU35g3M.mjs.map +1 -0
  106. package/lib/node/getRawRequest-BavnMWh_.cjs +36 -0
  107. package/lib/node/getRawRequest-BavnMWh_.cjs.map +1 -0
  108. package/lib/node/getRawRequest-DnwmXyOW.mjs +24 -0
  109. package/lib/node/getRawRequest-DnwmXyOW.mjs.map +1 -0
  110. package/lib/node/glossary-BLKRyLBd.cjs +12 -0
  111. package/lib/node/glossary-BLKRyLBd.cjs.map +1 -0
  112. package/lib/node/glossary-glQBRnVD.mjs +6 -0
  113. package/lib/node/glossary-glQBRnVD.mjs.map +1 -0
  114. package/lib/node/handleRequest-Bb7Y-XLw.cjs +220 -0
  115. package/lib/node/handleRequest-Bb7Y-XLw.cjs.map +1 -0
  116. package/lib/node/handleRequest-Y97UwBbF.mjs +190 -0
  117. package/lib/node/handleRequest-Y97UwBbF.mjs.map +1 -0
  118. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs +26 -0
  119. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs.map +1 -0
  120. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs +20 -0
  121. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs.map +1 -0
  122. package/lib/node/index-BMbJ8FXL.d.cts +113 -0
  123. package/lib/node/index-C0YAQ36w.d.mts +113 -0
  124. package/lib/node/index.cjs +54 -0
  125. package/lib/node/index.cjs.map +1 -0
  126. package/lib/node/index.d.cts +75 -0
  127. package/lib/node/index.d.mts +75 -0
  128. package/lib/node/index.mjs +40 -0
  129. package/lib/node/index.mjs.map +1 -0
  130. package/lib/node/interceptors/ClientRequest/index.cjs +6 -0
  131. package/lib/node/interceptors/ClientRequest/index.d.cts +2 -0
  132. package/lib/node/interceptors/ClientRequest/index.d.mts +3 -0
  133. package/lib/node/interceptors/ClientRequest/index.mjs +6 -0
  134. package/lib/node/interceptors/XMLHttpRequest/index.cjs +6 -0
  135. package/lib/node/interceptors/XMLHttpRequest/index.d.cts +14 -0
  136. package/lib/node/interceptors/XMLHttpRequest/index.d.mts +14 -0
  137. package/lib/node/interceptors/XMLHttpRequest/index.mjs +6 -0
  138. package/lib/node/interceptors/fetch/index.cjs +5 -0
  139. package/lib/node/interceptors/fetch/index.d.cts +12 -0
  140. package/lib/node/interceptors/fetch/index.d.mts +12 -0
  141. package/lib/node/interceptors/fetch/index.mjs +5 -0
  142. package/lib/node/node-DwCc6iuP.mjs +27 -0
  143. package/lib/node/node-DwCc6iuP.mjs.map +1 -0
  144. package/lib/node/node-dKdAf3tC.cjs +39 -0
  145. package/lib/node/node-dKdAf3tC.cjs.map +1 -0
  146. package/lib/node/presets/node.cjs +22 -0
  147. package/lib/node/presets/node.cjs.map +1 -0
  148. package/lib/node/presets/node.d.cts +13 -0
  149. package/lib/node/presets/node.d.mts +15 -0
  150. package/lib/node/presets/node.mjs +22 -0
  151. package/lib/node/presets/node.mjs.map +1 -0
  152. package/lib/node/utils/node/index.cjs +4 -0
  153. package/lib/node/utils/node/index.d.cts +16 -0
  154. package/lib/node/utils/node/index.d.mts +16 -0
  155. package/lib/node/utils/node/index.mjs +3 -0
  156. package/package.json +204 -0
  157. package/presets/browser/package.json +5 -0
  158. package/presets/node/package.json +11 -0
  159. package/src/BatchInterceptor.test.ts +255 -0
  160. package/src/BatchInterceptor.ts +95 -0
  161. package/src/Interceptor.test.ts +205 -0
  162. package/src/Interceptor.ts +249 -0
  163. package/src/InterceptorError.ts +7 -0
  164. package/src/RemoteHttpInterceptor.ts +251 -0
  165. package/src/RequestController.test.ts +104 -0
  166. package/src/RequestController.ts +109 -0
  167. package/src/createRequestId.test.ts +7 -0
  168. package/src/createRequestId.ts +9 -0
  169. package/src/getRawRequest.ts +21 -0
  170. package/src/glossary.ts +37 -0
  171. package/src/index.ts +15 -0
  172. package/src/interceptors/ClientRequest/MockHttpSocket.ts +724 -0
  173. package/src/interceptors/ClientRequest/agents.ts +110 -0
  174. package/src/interceptors/ClientRequest/index.test.ts +75 -0
  175. package/src/interceptors/ClientRequest/index.ts +193 -0
  176. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +54 -0
  177. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +45 -0
  178. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +427 -0
  179. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +268 -0
  180. package/src/interceptors/ClientRequest/utils/parserUtils.ts +48 -0
  181. package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +258 -0
  182. package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +262 -0
  183. package/src/interceptors/Socket/MockSocket.test.ts +264 -0
  184. package/src/interceptors/Socket/MockSocket.ts +58 -0
  185. package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +26 -0
  186. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +52 -0
  187. package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +33 -0
  188. package/src/interceptors/WebSocket/WebSocketClassTransport.ts +116 -0
  189. package/src/interceptors/WebSocket/WebSocketClientConnection.ts +152 -0
  190. package/src/interceptors/WebSocket/WebSocketOverride.ts +252 -0
  191. package/src/interceptors/WebSocket/WebSocketServerConnection.ts +420 -0
  192. package/src/interceptors/WebSocket/WebSocketTransport.ts +39 -0
  193. package/src/interceptors/WebSocket/index.ts +191 -0
  194. package/src/interceptors/WebSocket/utils/bindEvent.test.ts +27 -0
  195. package/src/interceptors/WebSocket/utils/bindEvent.ts +21 -0
  196. package/src/interceptors/WebSocket/utils/events.test.ts +101 -0
  197. package/src/interceptors/WebSocket/utils/events.ts +94 -0
  198. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +746 -0
  199. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +121 -0
  200. package/src/interceptors/XMLHttpRequest/index.ts +61 -0
  201. package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
  202. package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
  203. package/src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts +12 -0
  204. package/src/interceptors/XMLHttpRequest/utils/concateArrayBuffer.test.ts +12 -0
  205. package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +26 -0
  206. package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
  207. package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +49 -0
  208. package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.test.ts +164 -0
  209. package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts +16 -0
  210. package/src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts +14 -0
  211. package/src/interceptors/fetch/index.ts +214 -0
  212. package/src/interceptors/fetch/utils/brotli-decompress.browser.ts +14 -0
  213. package/src/interceptors/fetch/utils/brotli-decompress.ts +31 -0
  214. package/src/interceptors/fetch/utils/createNetworkError.ts +5 -0
  215. package/src/interceptors/fetch/utils/decompression.ts +85 -0
  216. package/src/interceptors/fetch/utils/followRedirect.ts +114 -0
  217. package/src/presets/browser.ts +11 -0
  218. package/src/presets/node.ts +13 -0
  219. package/src/utils/bufferUtils.test.ts +21 -0
  220. package/src/utils/bufferUtils.ts +22 -0
  221. package/src/utils/canParseUrl.ts +13 -0
  222. package/src/utils/cloneObject.test.ts +94 -0
  223. package/src/utils/cloneObject.ts +36 -0
  224. package/src/utils/createProxy.test.ts +164 -0
  225. package/src/utils/createProxy.ts +104 -0
  226. package/src/utils/emitAsync.ts +25 -0
  227. package/src/utils/fetchUtils.ts +119 -0
  228. package/src/utils/findPropertySource.test.ts +27 -0
  229. package/src/utils/findPropertySource.ts +20 -0
  230. package/src/utils/getCleanUrl.test.ts +32 -0
  231. package/src/utils/getCleanUrl.ts +6 -0
  232. package/src/utils/getUrlByRequestOptions.test.ts +163 -0
  233. package/src/utils/getUrlByRequestOptions.ts +152 -0
  234. package/src/utils/getValueBySymbol.test.ts +14 -0
  235. package/src/utils/getValueBySymbol.ts +19 -0
  236. package/src/utils/handleRequest.ts +205 -0
  237. package/src/utils/hasConfigurableGlobal.test.ts +83 -0
  238. package/src/utils/hasConfigurableGlobal.ts +34 -0
  239. package/src/utils/isNodeLikeError.ts +13 -0
  240. package/src/utils/isObject.test.ts +21 -0
  241. package/src/utils/isObject.ts +8 -0
  242. package/src/utils/isPropertyAccessible.ts +19 -0
  243. package/src/utils/nextTick.ts +11 -0
  244. package/src/utils/node/index.ts +39 -0
  245. package/src/utils/parseJson.test.ts +10 -0
  246. package/src/utils/parseJson.ts +12 -0
  247. package/src/utils/resolveWebSocketUrl.ts +45 -0
  248. package/src/utils/responseUtils.ts +59 -0
@@ -0,0 +1,121 @@
1
+ import type { Logger } from '@open-draft/logger'
2
+ import { XMLHttpRequestEmitter } from '.'
3
+ import { RequestController } from '../../RequestController'
4
+ import { XMLHttpRequestController } from './XMLHttpRequestController'
5
+ import { handleRequest } from '../../utils/handleRequest'
6
+ import { isResponseError } from '../../utils/responseUtils'
7
+
8
+ export interface XMLHttpRequestProxyOptions {
9
+ emitter: XMLHttpRequestEmitter
10
+ logger: Logger
11
+ }
12
+
13
+ /**
14
+ * Create a proxied `XMLHttpRequest` class.
15
+ * The proxied class establishes spies on certain methods,
16
+ * allowing us to intercept requests and respond to them.
17
+ */
18
+ export function createXMLHttpRequestProxy({
19
+ emitter,
20
+ logger,
21
+ }: XMLHttpRequestProxyOptions) {
22
+ const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {
23
+ construct(target, args, newTarget) {
24
+ logger.info('constructed new XMLHttpRequest')
25
+
26
+ const originalRequest = Reflect.construct(
27
+ target,
28
+ args,
29
+ newTarget
30
+ ) as XMLHttpRequest
31
+
32
+ /**
33
+ * @note Forward prototype descriptors onto the proxied object.
34
+ * XMLHttpRequest is implemented in JSDOM in a way that assigns
35
+ * a bunch of descriptors, like "set responseType()" on the prototype.
36
+ * With this propagation, we make sure that those descriptors trigger
37
+ * when the user operates with the proxied request instance.
38
+ */
39
+ const prototypeDescriptors = Object.getOwnPropertyDescriptors(
40
+ target.prototype
41
+ )
42
+ for (const propertyName in prototypeDescriptors) {
43
+ Reflect.defineProperty(
44
+ originalRequest,
45
+ propertyName,
46
+ prototypeDescriptors[propertyName]
47
+ )
48
+ }
49
+
50
+ const xhrRequestController = new XMLHttpRequestController(
51
+ originalRequest,
52
+ logger
53
+ )
54
+
55
+ xhrRequestController.onRequest = async function ({ request, requestId }) {
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
+ })
78
+
79
+ this.logger.info('awaiting mocked response...')
80
+
81
+ this.logger.info(
82
+ 'emitting the "request" event for %s listener(s)...',
83
+ emitter.listenerCount('request')
84
+ )
85
+
86
+ await handleRequest({
87
+ request,
88
+ requestId,
89
+ controller,
90
+ emitter,
91
+ })
92
+ }
93
+
94
+ xhrRequestController.onResponse = async function ({
95
+ response,
96
+ isMockedResponse,
97
+ request,
98
+ requestId,
99
+ }) {
100
+ this.logger.info(
101
+ 'emitting the "response" event for %s listener(s)...',
102
+ emitter.listenerCount('response')
103
+ )
104
+
105
+ emitter.emit('response', {
106
+ response,
107
+ isMockedResponse,
108
+ request,
109
+ requestId,
110
+ })
111
+ }
112
+
113
+ // Return the proxied request from the controller
114
+ // so that the controller can react to the consumer's interactions
115
+ // with this request (opening/sending/etc).
116
+ return xhrRequestController.request
117
+ },
118
+ })
119
+
120
+ return XMLHttpRequestProxy
121
+ }
@@ -0,0 +1,61 @@
1
+ import { invariant } from 'outvariant'
2
+ import { Emitter } from 'strict-event-emitter'
3
+ import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
4
+ import { Interceptor } from '../../Interceptor'
5
+ import { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'
6
+ import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
7
+
8
+ export type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>
9
+
10
+ export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
11
+ static interceptorSymbol = Symbol('xhr')
12
+
13
+ constructor() {
14
+ super(XMLHttpRequestInterceptor.interceptorSymbol)
15
+ }
16
+
17
+ protected checkEnvironment() {
18
+ return hasConfigurableGlobal('XMLHttpRequest')
19
+ }
20
+
21
+ protected setup() {
22
+ const logger = this.logger.extend('setup')
23
+
24
+ logger.info('patching "XMLHttpRequest" module...')
25
+
26
+ const PureXMLHttpRequest = globalThis.XMLHttpRequest
27
+
28
+ invariant(
29
+ !(PureXMLHttpRequest as any)[IS_PATCHED_MODULE],
30
+ 'Failed to patch the "XMLHttpRequest" module: already patched.'
31
+ )
32
+
33
+ globalThis.XMLHttpRequest = createXMLHttpRequestProxy({
34
+ emitter: this.emitter,
35
+ logger: this.logger,
36
+ })
37
+
38
+ logger.info(
39
+ 'native "XMLHttpRequest" module patched!',
40
+ globalThis.XMLHttpRequest.name
41
+ )
42
+
43
+ Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
44
+ enumerable: true,
45
+ configurable: true,
46
+ value: true,
47
+ })
48
+
49
+ this.subscriptions.push(() => {
50
+ Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
51
+ value: undefined,
52
+ })
53
+
54
+ globalThis.XMLHttpRequest = PureXMLHttpRequest
55
+ logger.info(
56
+ 'native "XMLHttpRequest" module restored!',
57
+ globalThis.XMLHttpRequest.name
58
+ )
59
+ })
60
+ }
61
+ }
@@ -0,0 +1,51 @@
1
+ export class EventPolyfill implements Event {
2
+ readonly NONE = 0
3
+ readonly CAPTURING_PHASE = 1
4
+ readonly AT_TARGET = 2
5
+ readonly BUBBLING_PHASE = 3
6
+
7
+ public type: string = ''
8
+ public srcElement: EventTarget | null = null
9
+ public target: EventTarget | null
10
+ public currentTarget: EventTarget | null = null
11
+ public eventPhase: number = 0
12
+ public timeStamp: number
13
+ public isTrusted: boolean = true
14
+ public composed: boolean = false
15
+ public cancelable: boolean = true
16
+ public defaultPrevented: boolean = false
17
+ public bubbles: boolean = true
18
+ public lengthComputable: boolean = true
19
+ public loaded: number = 0
20
+ public total: number = 0
21
+
22
+ cancelBubble: boolean = false
23
+ returnValue: boolean = true
24
+
25
+ constructor(
26
+ type: string,
27
+ options?: { target: EventTarget; currentTarget: EventTarget }
28
+ ) {
29
+ this.type = type
30
+ this.target = options?.target || null
31
+ this.currentTarget = options?.currentTarget || null
32
+ this.timeStamp = Date.now()
33
+ }
34
+
35
+ public composedPath(): EventTarget[] {
36
+ return []
37
+ }
38
+
39
+ public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {
40
+ this.type = type
41
+ this.bubbles = !!bubbles
42
+ this.cancelable = !!cancelable
43
+ }
44
+
45
+ public preventDefault() {
46
+ this.defaultPrevented = true
47
+ }
48
+
49
+ public stopPropagation() {}
50
+ public stopImmediatePropagation() {}
51
+ }
@@ -0,0 +1,17 @@
1
+ import { EventPolyfill } from './EventPolyfill'
2
+
3
+ export class ProgressEventPolyfill extends EventPolyfill {
4
+ readonly lengthComputable: boolean
5
+ readonly composed: boolean
6
+ readonly loaded: number
7
+ readonly total: number
8
+
9
+ constructor(type: string, init?: ProgressEventInit) {
10
+ super(type)
11
+
12
+ this.lengthComputable = init?.lengthComputable || false
13
+ this.composed = init?.composed || false
14
+ this.loaded = init?.loaded || 0
15
+ this.total = init?.total || 0
16
+ }
17
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Concatenate two `Uint8Array` buffers.
3
+ */
4
+ export function concatArrayBuffer(
5
+ left: Uint8Array,
6
+ right: Uint8Array
7
+ ): Uint8Array {
8
+ const result = new Uint8Array(left.byteLength + right.byteLength)
9
+ result.set(left, 0)
10
+ result.set(right, left.byteLength)
11
+ return result
12
+ }
@@ -0,0 +1,12 @@
1
+ import { it, expect } from 'vitest'
2
+ import { concatArrayBuffer } from './concatArrayBuffer'
3
+
4
+ const encoder = new TextEncoder()
5
+
6
+ it('concatenates two Uint8Array buffers', () => {
7
+ const result = concatArrayBuffer(
8
+ encoder.encode('hello'),
9
+ encoder.encode('world')
10
+ )
11
+ expect(result).toEqual(encoder.encode('helloworld'))
12
+ })
@@ -0,0 +1,26 @@
1
+ // @vitest-environment jsdom
2
+ import { it, expect } from 'vitest'
3
+ import { createEvent } from './createEvent'
4
+ import { EventPolyfill } from '../polyfills/EventPolyfill'
5
+
6
+ const request = new XMLHttpRequest()
7
+ request.open('POST', '/user')
8
+
9
+ it('returns an EventPolyfill instance with the given target set', () => {
10
+ const event = createEvent(request, 'my-event')
11
+ const target = event.target as XMLHttpRequest
12
+
13
+ expect(event).toBeInstanceOf(EventPolyfill)
14
+ expect(target).toBeInstanceOf(XMLHttpRequest)
15
+ })
16
+
17
+ it('returns the ProgressEvent instance', () => {
18
+ const event = createEvent(request, 'load', {
19
+ loaded: 100,
20
+ total: 500,
21
+ })
22
+
23
+ expect(event).toBeInstanceOf(ProgressEvent)
24
+ expect(event.loaded).toBe(100)
25
+ expect(event.total).toBe(500)
26
+ })
@@ -0,0 +1,41 @@
1
+ import { EventPolyfill } from '../polyfills/EventPolyfill'
2
+ import { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'
3
+
4
+ const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'
5
+
6
+ export function createEvent(
7
+ target: XMLHttpRequest | XMLHttpRequestUpload,
8
+ type: string,
9
+ init?: ProgressEventInit
10
+ ): EventPolyfill | ProgressEvent {
11
+ const progressEvents = [
12
+ 'error',
13
+ 'progress',
14
+ 'loadstart',
15
+ 'loadend',
16
+ 'load',
17
+ 'timeout',
18
+ 'abort',
19
+ ]
20
+
21
+ /**
22
+ * `ProgressEvent` is not supported in React Native.
23
+ * @see https://github.com/mswjs/interceptors/issues/40
24
+ */
25
+ const ProgressEventClass = SUPPORTS_PROGRESS_EVENT
26
+ ? ProgressEvent
27
+ : ProgressEventPolyfill
28
+
29
+ const event = progressEvents.includes(type)
30
+ ? new ProgressEventClass(type, {
31
+ lengthComputable: true,
32
+ loaded: init?.loaded || 0,
33
+ total: init?.total || 0,
34
+ })
35
+ : new EventPolyfill(type, {
36
+ target,
37
+ currentTarget: target,
38
+ })
39
+
40
+ return event
41
+ }
@@ -0,0 +1,49 @@
1
+ import { FetchResponse } from '../../../utils/fetchUtils'
2
+
3
+ /**
4
+ * Creates a Fetch API `Response` instance from the given
5
+ * `XMLHttpRequest` instance and a response body.
6
+ */
7
+ export function createResponse(
8
+ request: XMLHttpRequest,
9
+ body: BodyInit | null
10
+ ): Response {
11
+ /**
12
+ * Handle XMLHttpRequest responses that must have null as the
13
+ * response body when represented using Fetch API Response.
14
+ * XMLHttpRequest response will always have an empty string
15
+ * as the "request.response" in those cases, resulting in an error
16
+ * when constructing a Response instance.
17
+ * @see https://github.com/mswjs/interceptors/issues/379
18
+ */
19
+ const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)
20
+ ? body
21
+ : null
22
+
23
+ return new FetchResponse(responseBodyOrNull, {
24
+ url: request.responseURL,
25
+ status: request.status,
26
+ statusText: request.statusText,
27
+ headers: createHeadersFromXMLHttpRequestHeaders(
28
+ request.getAllResponseHeaders()
29
+ ),
30
+ })
31
+ }
32
+
33
+ function createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {
34
+ const headers = new Headers()
35
+
36
+ const lines = headersString.split(/[\r\n]+/)
37
+ for (const line of lines) {
38
+ if (line.trim() === '') {
39
+ continue
40
+ }
41
+
42
+ const [name, ...parts] = line.split(': ')
43
+ const value = parts.join(': ')
44
+
45
+ headers.append(name, value)
46
+ }
47
+
48
+ return headers
49
+ }
@@ -0,0 +1,164 @@
1
+ // @vitest-environment node
2
+ import { it, expect } from 'vitest'
3
+ import { getBodyByteLength } from './getBodyByteLength'
4
+
5
+ const url = 'http://localhost'
6
+
7
+ it('returns explicit body length set in the "Content-Length" header', async () => {
8
+ await expect(
9
+ getBodyByteLength(new Request(url, { headers: { 'Content-Length': '10' } }))
10
+ ).resolves.toBe(10)
11
+
12
+ await expect(
13
+ getBodyByteLength(
14
+ new Response('hello', { headers: { 'Content-Length': '5' } })
15
+ )
16
+ ).resolves.toBe(5)
17
+ })
18
+
19
+ /**
20
+ * Request.
21
+ */
22
+
23
+ it('returns 0 for a request with an empty body', async () => {
24
+ await expect(getBodyByteLength(new Request(url))).resolves.toBe(0)
25
+ await expect(
26
+ getBodyByteLength(new Request(url, { method: 'POST', body: null }))
27
+ ).resolves.toBe(0)
28
+ await expect(
29
+ getBodyByteLength(new Request(url, { method: 'POST', body: undefined }))
30
+ ).resolves.toBe(0)
31
+ await expect(
32
+ getBodyByteLength(new Request(url, { method: 'POST', body: '' }))
33
+ ).resolves.toBe(0)
34
+ })
35
+
36
+ it('calculates body length from the text request body', async () => {
37
+ await expect(
38
+ getBodyByteLength(
39
+ new Request(url, {
40
+ method: 'POST',
41
+ body: 'hello world',
42
+ })
43
+ )
44
+ ).resolves.toBe(11)
45
+ })
46
+
47
+ it('calculates body length from the URLSearchParams request body', async () => {
48
+ await expect(
49
+ getBodyByteLength(
50
+ new Request(url, {
51
+ method: 'POST',
52
+ body: new URLSearchParams([['hello', 'world']]),
53
+ })
54
+ )
55
+ ).resolves.toBe(11)
56
+ })
57
+
58
+ it('calculates body length from the Blob request body', async () => {
59
+ await expect(
60
+ getBodyByteLength(
61
+ new Request(url, {
62
+ method: 'POST',
63
+ body: new Blob(['hello world']),
64
+ })
65
+ )
66
+ ).resolves.toBe(11)
67
+ })
68
+
69
+ it('calculates body length from the ArrayBuffer request body', async () => {
70
+ await expect(
71
+ getBodyByteLength(
72
+ new Request(url, {
73
+ method: 'POST',
74
+ body: await new Blob(['hello world']).arrayBuffer(),
75
+ })
76
+ )
77
+ ).resolves.toBe(11)
78
+ })
79
+
80
+ it('calculates body length from the FormData request body', async () => {
81
+ const formData = new FormData()
82
+ formData.append('hello', 'world')
83
+
84
+ await expect(
85
+ getBodyByteLength(
86
+ new Request(url, {
87
+ method: 'POST',
88
+ body: formData,
89
+ })
90
+ )
91
+ ).resolves.toBe(129)
92
+ })
93
+
94
+ it('calculates body length from the ReadableStream request body', async () => {
95
+ const stream = new ReadableStream({
96
+ start(controller) {
97
+ controller.enqueue(new TextEncoder().encode('hello world'))
98
+ controller.close()
99
+ },
100
+ })
101
+
102
+ await expect(
103
+ getBodyByteLength(
104
+ new Request(url, {
105
+ method: 'POST',
106
+ body: stream,
107
+ // @ts-expect-error Undocumented required Undici property.
108
+ duplex: 'half',
109
+ })
110
+ )
111
+ ).resolves.toBe(11)
112
+ })
113
+
114
+ /**
115
+ * Response.
116
+ */
117
+ it('returns 0 for a response with an empty body', async () => {
118
+ await expect(getBodyByteLength(new Response())).resolves.toBe(0)
119
+ await expect(getBodyByteLength(new Response(null))).resolves.toBe(0)
120
+ await expect(getBodyByteLength(new Response(undefined))).resolves.toBe(0)
121
+ await expect(getBodyByteLength(new Response(''))).resolves.toBe(0)
122
+ })
123
+
124
+ it('calculates body length from the text response body', async () => {
125
+ await expect(getBodyByteLength(new Response('hello world'))).resolves.toBe(11)
126
+ })
127
+
128
+ it('calculates body length from the URLSearchParams response body', async () => {
129
+ await expect(
130
+ getBodyByteLength(new Response(new URLSearchParams([['hello', 'world']])))
131
+ ).resolves.toBe(11)
132
+ })
133
+
134
+ it('calculates body length from the Blob response body', async () => {
135
+ await expect(
136
+ getBodyByteLength(new Response(new Blob(['hello world'])))
137
+ ).resolves.toBe(11)
138
+ })
139
+
140
+ it('calculates body length from the ArrayBuffer response body', async () => {
141
+ await expect(
142
+ getBodyByteLength(
143
+ new Response(await new Blob(['hello world']).arrayBuffer())
144
+ )
145
+ ).resolves.toBe(11)
146
+ })
147
+
148
+ it('calculates body length from the FormData response body', async () => {
149
+ const formData = new FormData()
150
+ formData.append('hello', 'world')
151
+
152
+ await expect(getBodyByteLength(new Response(formData))).resolves.toBe(129)
153
+ })
154
+
155
+ it('calculates body length from the ReadableStream response body', async () => {
156
+ const stream = new ReadableStream({
157
+ start(controller) {
158
+ controller.enqueue(new TextEncoder().encode('hello world'))
159
+ controller.close()
160
+ },
161
+ })
162
+
163
+ await expect(getBodyByteLength(new Response(stream))).resolves.toBe(11)
164
+ })
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Return a total byte length of the given request/response body.
3
+ * If the `Content-Length` header is present, it will be used as the byte length.
4
+ */
5
+ export async function getBodyByteLength(
6
+ input: Request | Response
7
+ ): Promise<number> {
8
+ const explicitContentLength = input.headers.get('content-length')
9
+
10
+ if (explicitContentLength != null && explicitContentLength !== '') {
11
+ return Number(explicitContentLength)
12
+ }
13
+
14
+ const buffer = await input.arrayBuffer()
15
+ return buffer.byteLength
16
+ }
@@ -0,0 +1,14 @@
1
+ export function isDomParserSupportedType(
2
+ type: string
3
+ ): type is DOMParserSupportedType {
4
+ const supportedTypes: Array<DOMParserSupportedType> = [
5
+ 'application/xhtml+xml',
6
+ 'application/xml',
7
+ 'image/svg+xml',
8
+ 'text/html',
9
+ 'text/xml',
10
+ ]
11
+ return supportedTypes.some((supportedType) => {
12
+ return type.startsWith(supportedType)
13
+ })
14
+ }