@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,48 @@
1
+ import type { Socket } from 'node:net'
2
+ import { HTTPParser } from '_http_common'
3
+
4
+ /**
5
+ * @see https://github.com/nodejs/node/blob/f3adc11e37b8bfaaa026ea85c1cf22e3a0e29ae9/lib/_http_common.js#L180
6
+ */
7
+ export function freeParser(parser: HTTPParser<any>, socket?: Socket): void {
8
+ if (parser._consumed) {
9
+ parser.unconsume()
10
+ }
11
+
12
+ parser._headers = []
13
+ parser._url = ''
14
+ parser.socket = null
15
+ parser.incoming = null
16
+ parser.outgoing = null
17
+ parser.maxHeaderPairs = 2000
18
+ parser._consumed = false
19
+ parser.onIncoming = null
20
+
21
+ parser[HTTPParser.kOnHeaders] = null
22
+ parser[HTTPParser.kOnHeadersComplete] = null
23
+ parser[HTTPParser.kOnMessageBegin] = null
24
+ parser[HTTPParser.kOnMessageComplete] = null
25
+ parser[HTTPParser.kOnBody] = null
26
+ parser[HTTPParser.kOnExecute] = null
27
+ parser[HTTPParser.kOnTimeout] = null
28
+
29
+ parser.remove()
30
+ parser.free()
31
+
32
+ if (socket) {
33
+ /**
34
+ * @note Unassigning the socket's parser will fail this assertion
35
+ * if there's still some data being processed on the socket:
36
+ * @see https://github.com/nodejs/node/blob/4e1f39b678b37017ac9baa0971e3aeecd3b67b51/lib/_http_client.js#L613
37
+ */
38
+ if (socket.destroyed) {
39
+ // @ts-expect-error Node.js internals.
40
+ socket.parser = null
41
+ } else {
42
+ socket.once('end', () => {
43
+ // @ts-expect-error Node.js internals.
44
+ socket.parser = null
45
+ })
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,258 @@
1
+ // @vitest-environment node
2
+ import { it, expect, afterEach } from 'vitest'
3
+ import {
4
+ recordRawFetchHeaders,
5
+ restoreHeadersPrototype,
6
+ getRawFetchHeaders,
7
+ } from './recordRawHeaders'
8
+
9
+ const url = 'http://localhost'
10
+
11
+ afterEach(() => {
12
+ restoreHeadersPrototype()
13
+ })
14
+
15
+ it('returns an empty list if no headers were set', () => {
16
+ expect(getRawFetchHeaders(new Headers())).toEqual([])
17
+ expect(getRawFetchHeaders(new Headers(undefined))).toEqual([])
18
+ expect(getRawFetchHeaders(new Headers({}))).toEqual([])
19
+ expect(getRawFetchHeaders(new Headers([]))).toEqual([])
20
+ expect(getRawFetchHeaders(new Request(url).headers)).toEqual([])
21
+ expect(getRawFetchHeaders(new Response().headers)).toEqual([])
22
+ })
23
+
24
+ it('records raw headers (Headers / object as init)', () => {
25
+ recordRawFetchHeaders()
26
+ const headers = new Headers({
27
+ 'Content-Type': 'application/json',
28
+ 'X-My-Header': '1',
29
+ })
30
+
31
+ expect(getRawFetchHeaders(headers)).toEqual([
32
+ ['Content-Type', 'application/json'],
33
+ ['X-My-Header', '1'],
34
+ ])
35
+ expect(Object.fromEntries(headers)).toEqual({
36
+ 'content-type': 'application/json',
37
+ 'x-my-header': '1',
38
+ })
39
+ })
40
+
41
+ it('records raw headers (Headers / array as init)', () => {
42
+ recordRawFetchHeaders()
43
+ const headers = new Headers([['X-My-Header', '1']])
44
+
45
+ expect(getRawFetchHeaders(headers)).toEqual([['X-My-Header', '1']])
46
+ expect(Object.fromEntries(headers)).toEqual({
47
+ 'x-my-header': '1',
48
+ })
49
+ })
50
+
51
+ it('records raw headers (Headers / Headers as init', () => {
52
+ recordRawFetchHeaders()
53
+ const headers = new Headers([['X-My-Header', '1']])
54
+
55
+ expect(getRawFetchHeaders(new Headers(headers))).toEqual([
56
+ ['X-My-Header', '1'],
57
+ ])
58
+ })
59
+
60
+ it('records raw headers added via ".set()"', () => {
61
+ recordRawFetchHeaders()
62
+ const headers = new Headers([['X-My-Header', '1']])
63
+ headers.set('X-Another-Header', '2')
64
+
65
+ expect(getRawFetchHeaders(headers)).toEqual([
66
+ ['X-My-Header', '1'],
67
+ ['X-Another-Header', '2'],
68
+ ])
69
+ })
70
+
71
+ it('records raw headers added via ".append()"', () => {
72
+ recordRawFetchHeaders()
73
+ const headers = new Headers([['X-My-Header', '1']])
74
+ headers.append('X-My-Header', '2')
75
+
76
+ expect(getRawFetchHeaders(headers)).toEqual([
77
+ ['X-My-Header', '1'],
78
+ ['X-My-Header', '2'],
79
+ ])
80
+ })
81
+
82
+ it('deletes the header when called ".delete()"', () => {
83
+ const headers = new Headers([['X-My-Header', '1']])
84
+ headers.delete('X-My-Header')
85
+
86
+ expect(getRawFetchHeaders(headers)).toEqual([])
87
+ })
88
+
89
+ it('records raw headers (Request / object as init)', () => {
90
+ recordRawFetchHeaders()
91
+ const request = new Request(url, {
92
+ headers: {
93
+ 'Content-Type': 'application/json',
94
+ 'X-My-Header': '1',
95
+ },
96
+ })
97
+
98
+ expect(getRawFetchHeaders(request.headers)).toEqual([
99
+ ['Content-Type', 'application/json'],
100
+ ['X-My-Header', '1'],
101
+ ])
102
+ })
103
+
104
+ it('records raw headers (Request / array as init)', () => {
105
+ recordRawFetchHeaders()
106
+ const request = new Request(url, {
107
+ headers: [['X-My-Header', '1']],
108
+ })
109
+
110
+ expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
111
+ })
112
+
113
+ it('records raw headers (Request / Headers as init)', () => {
114
+ recordRawFetchHeaders()
115
+ const headers = new Headers([['X-My-Header', '1']])
116
+ const request = new Request(url, { headers })
117
+
118
+ expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
119
+ })
120
+
121
+ it('records raw headers (Request / Request as init)', () => {
122
+ recordRawFetchHeaders()
123
+ const init = new Request(url, { headers: [['X-My-Header', '1']] })
124
+ const request = new Request(init)
125
+
126
+ expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
127
+ })
128
+
129
+ it('preserves headers instanceof (Request / Request as init)', () => {
130
+ recordRawFetchHeaders()
131
+ const init = new Request(url, { headers: [['X-My-Header', '1']] })
132
+ new Request(init)
133
+ expect(init.headers).toBeInstanceOf(Headers)
134
+ })
135
+
136
+ it('preserves headers instanceof (Request / Request with Headers as init)', () => {
137
+ recordRawFetchHeaders()
138
+ const headers = new Headers([['X-My-Header', '1']])
139
+ const init = new Request(url, { headers })
140
+ new Request(init)
141
+ expect(init.headers).toBeInstanceOf(Headers)
142
+ })
143
+
144
+ it('preserves headers instanceof (Response / Response with Headers as init)', () => {
145
+ recordRawFetchHeaders()
146
+ const init = { headers: new Headers([['X-My-Header', '1']]) }
147
+ new Response(url, init)
148
+ expect(init.headers).toBeInstanceOf(Headers)
149
+ })
150
+
151
+ it('records raw headers (Request / Request+Headers as init)', () => {
152
+ recordRawFetchHeaders()
153
+ const init = new Request(url, { headers: [['X-My-Header', '1']] })
154
+ expect(getRawFetchHeaders(init.headers)).toEqual([['X-My-Header', '1']])
155
+
156
+ const request = new Request(init, {
157
+ headers: new Headers([['X-Another-Header', '2']]),
158
+ })
159
+
160
+ // Must merge the raw headers from the request init
161
+ // and the request instance itself.
162
+ expect(getRawFetchHeaders(request.headers)).toEqual([
163
+ ['X-My-Header', '1'],
164
+ ['X-Another-Header', '2'],
165
+ ])
166
+ })
167
+
168
+ it('records raw headers (Response / object as init)', () => {
169
+ recordRawFetchHeaders()
170
+ const response = new Response(null, {
171
+ headers: {
172
+ 'Content-Type': 'application/json',
173
+ 'X-My-Header': '1',
174
+ },
175
+ })
176
+
177
+ expect(getRawFetchHeaders(response.headers)).toEqual([
178
+ ['Content-Type', 'application/json'],
179
+ ['X-My-Header', '1'],
180
+ ])
181
+ })
182
+
183
+ it('records raw headers (Response / array as init)', () => {
184
+ recordRawFetchHeaders()
185
+ const response = new Response(null, {
186
+ headers: [['X-My-Header', '1']],
187
+ })
188
+
189
+ expect(getRawFetchHeaders(response.headers)).toEqual([['X-My-Header', '1']])
190
+ })
191
+
192
+ it('records raw headers (Response / Headers as init)', () => {
193
+ recordRawFetchHeaders()
194
+ const headers = new Headers([['X-My-Header', '1']])
195
+ const response = new Response(null, { headers })
196
+
197
+ expect(getRawFetchHeaders(response.headers)).toEqual([['X-My-Header', '1']])
198
+ })
199
+
200
+ it('stops recording once the patches are restored', () => {
201
+ restoreHeadersPrototype()
202
+
203
+ const headers = new Headers({ 'X-My-Header': '1' })
204
+ // Must return the normalized headers (no access to raw headers).
205
+ expect(getRawFetchHeaders(headers)).toEqual([['x-my-header', '1']])
206
+ })
207
+
208
+ it('overrides an existing header when calling ".set()"', () => {
209
+ recordRawFetchHeaders()
210
+ const headers = new Headers([['a', '1']])
211
+ expect(headers.get('a')).toBe('1')
212
+
213
+ headers.set('a', '2')
214
+ expect(headers.get('a')).toBe('2')
215
+
216
+ const headersClone = new Headers(headers)
217
+ expect(headersClone.get('a')).toBe('2')
218
+ })
219
+
220
+ it('overrides an existing multi-value header when calling ".set()"', () => {
221
+ recordRawFetchHeaders()
222
+ const headers = new Headers([
223
+ ['a', '1'],
224
+ ['a', '2'],
225
+ ])
226
+ expect(headers.get('a')).toBe('1, 2')
227
+
228
+ headers.set('a', '3')
229
+ expect(headers.get('a')).toBe('3')
230
+ })
231
+
232
+ it('does not throw on using Headers before recording', () => {
233
+ // If the consumer constructs a Headers instance before
234
+ // the interceptor is enabled, it will have no internal symbol set.
235
+ const headers = new Headers()
236
+ recordRawFetchHeaders()
237
+ const request = new Request(url, { headers })
238
+
239
+ expect(getRawFetchHeaders(request.headers)).toEqual([])
240
+
241
+ request.headers.set('X-My-Header', '1')
242
+ expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
243
+ })
244
+
245
+ /**
246
+ * @see https://github.com/mswjs/interceptors/issues/681
247
+ */
248
+ it('isolates headers between different headers instances', async () => {
249
+ recordRawFetchHeaders()
250
+ const original = new Headers()
251
+ const firstClone = new Headers(original)
252
+ firstClone.set('Content-Type', 'application/json')
253
+ const secondClone = new Headers(original)
254
+
255
+ expect(original.get('Content-Type')).toBeNull()
256
+ expect(firstClone.get('Content-Type')).toBe('application/json')
257
+ expect(secondClone.get('Content-Type')).toBeNull()
258
+ })
@@ -0,0 +1,262 @@
1
+ type HeaderTuple = [string, string]
2
+ type RawHeaders = Array<HeaderTuple>
3
+ type SetHeaderBehavior = 'set' | 'append'
4
+
5
+ const kRawHeaders = Symbol('kRawHeaders')
6
+ const kRestorePatches = Symbol('kRestorePatches')
7
+
8
+ function recordRawHeader(
9
+ headers: Headers,
10
+ args: HeaderTuple,
11
+ behavior: SetHeaderBehavior
12
+ ) {
13
+ ensureRawHeadersSymbol(headers, [])
14
+ const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders
15
+
16
+ if (behavior === 'set') {
17
+ // When recording a set header, ensure we remove any matching existing headers.
18
+ for (let index = rawHeaders.length - 1; index >= 0; index--) {
19
+ if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {
20
+ rawHeaders.splice(index, 1)
21
+ }
22
+ }
23
+ }
24
+
25
+ rawHeaders.push(args)
26
+ }
27
+
28
+ /**
29
+ * Define the raw headers symbol on the given `Headers` instance.
30
+ * If the symbol already exists, this function does nothing.
31
+ */
32
+ function ensureRawHeadersSymbol(
33
+ headers: Headers,
34
+ rawHeaders: RawHeaders
35
+ ): void {
36
+ if (Reflect.has(headers, kRawHeaders)) {
37
+ return
38
+ }
39
+
40
+ defineRawHeadersSymbol(headers, rawHeaders)
41
+ }
42
+
43
+ /**
44
+ * Define the raw headers symbol on the given `Headers` instance.
45
+ * If the symbol already exists, it gets overridden.
46
+ */
47
+ function defineRawHeadersSymbol(headers: Headers, rawHeaders: RawHeaders) {
48
+ Object.defineProperty(headers, kRawHeaders, {
49
+ value: rawHeaders,
50
+ enumerable: false,
51
+ // Mark the symbol as configurable so its value can be overridden.
52
+ // Overrides happen when merging raw headers from multiple sources.
53
+ // E.g. new Request(new Request(url, { headers }), { headers })
54
+ configurable: true,
55
+ })
56
+ }
57
+
58
+ /**
59
+ * Patch the global `Headers` class to store raw headers.
60
+ * This is for compatibility with `IncomingMessage.prototype.rawHeaders`.
61
+ *
62
+ * @note Node.js has their own raw headers symbol but it
63
+ * only records the first header name in case of multi-value headers.
64
+ * Any other headers are normalized before comparing. This makes it
65
+ * incompatible with the `rawHeaders` format.
66
+ *
67
+ * let h = new Headers()
68
+ * h.append('X-Custom', 'one')
69
+ * h.append('x-custom', 'two')
70
+ * h[Symbol('headers map')] // Map { 'X-Custom' => 'one, two' }
71
+ */
72
+ export function recordRawFetchHeaders() {
73
+ // Prevent patching the Headers prototype multiple times.
74
+ if (Reflect.get(Headers, kRestorePatches)) {
75
+ return Reflect.get(Headers, kRestorePatches)
76
+ }
77
+
78
+ const {
79
+ Headers: OriginalHeaders,
80
+ Request: OriginalRequest,
81
+ Response: OriginalResponse,
82
+ } = globalThis
83
+ const { set, append, delete: headersDeleteMethod } = Headers.prototype
84
+
85
+ Object.defineProperty(Headers, kRestorePatches, {
86
+ value: () => {
87
+ Headers.prototype.set = set
88
+ Headers.prototype.append = append
89
+ Headers.prototype.delete = headersDeleteMethod
90
+ globalThis.Headers = OriginalHeaders
91
+
92
+ globalThis.Request = OriginalRequest
93
+ globalThis.Response = OriginalResponse
94
+
95
+ Reflect.deleteProperty(Headers, kRestorePatches)
96
+ },
97
+ enumerable: false,
98
+ /**
99
+ * @note Mark this property as configurable
100
+ * so we can delete it using `Reflect.delete` during cleanup.
101
+ */
102
+ configurable: true,
103
+ })
104
+
105
+ Object.defineProperty(globalThis, 'Headers', {
106
+ enumerable: true,
107
+ writable: true,
108
+ value: new Proxy(Headers, {
109
+ construct(target, args, newTarget) {
110
+ const headersInit = args[0] || []
111
+
112
+ if (
113
+ headersInit instanceof Headers &&
114
+ Reflect.has(headersInit, kRawHeaders)
115
+ ) {
116
+ const headers = Reflect.construct(
117
+ target,
118
+ [Reflect.get(headersInit, kRawHeaders)],
119
+ newTarget
120
+ )
121
+ ensureRawHeadersSymbol(headers, [
122
+ /**
123
+ * @note Spread the retrieved headers to clone them.
124
+ * This prevents multiple Headers instances from pointing
125
+ * at the same internal "rawHeaders" array.
126
+ */
127
+ ...Reflect.get(headersInit, kRawHeaders),
128
+ ])
129
+ return headers
130
+ }
131
+
132
+ const headers = Reflect.construct(target, args, newTarget)
133
+
134
+ // Request/Response constructors will set the symbol
135
+ // upon creating a new instance, using the raw developer
136
+ // input as the raw headers. Skip the symbol altogether
137
+ // in those cases because the input to Headers will be normalized.
138
+ if (!Reflect.has(headers, kRawHeaders)) {
139
+ const rawHeadersInit = Array.isArray(headersInit)
140
+ ? headersInit
141
+ : Object.entries(headersInit)
142
+ ensureRawHeadersSymbol(headers, rawHeadersInit)
143
+ }
144
+
145
+ return headers
146
+ },
147
+ }),
148
+ })
149
+
150
+ Headers.prototype.set = new Proxy(Headers.prototype.set, {
151
+ apply(target, thisArg, args: HeaderTuple) {
152
+ recordRawHeader(thisArg, args, 'set')
153
+ return Reflect.apply(target, thisArg, args)
154
+ },
155
+ })
156
+
157
+ Headers.prototype.append = new Proxy(Headers.prototype.append, {
158
+ apply(target, thisArg, args: HeaderTuple) {
159
+ recordRawHeader(thisArg, args, 'append')
160
+ return Reflect.apply(target, thisArg, args)
161
+ },
162
+ })
163
+
164
+ Headers.prototype.delete = new Proxy(Headers.prototype.delete, {
165
+ apply(target, thisArg, args: [string]) {
166
+ const rawHeaders = Reflect.get(thisArg, kRawHeaders) as RawHeaders
167
+
168
+ if (rawHeaders) {
169
+ for (let index = rawHeaders.length - 1; index >= 0; index--) {
170
+ if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {
171
+ rawHeaders.splice(index, 1)
172
+ }
173
+ }
174
+ }
175
+
176
+ return Reflect.apply(target, thisArg, args)
177
+ },
178
+ })
179
+
180
+ Object.defineProperty(globalThis, 'Request', {
181
+ enumerable: true,
182
+ writable: true,
183
+ value: new Proxy(Request, {
184
+ construct(target, args, newTarget) {
185
+ const request = Reflect.construct(target, args, newTarget)
186
+ const inferredRawHeaders: RawHeaders = []
187
+
188
+ // Infer raw headers from a `Request` instance used as init.
189
+ if (typeof args[0] === 'object' && args[0].headers != null) {
190
+ inferredRawHeaders.push(...inferRawHeaders(args[0].headers))
191
+ }
192
+
193
+ // Infer raw headers from the "headers" init argument.
194
+ if (typeof args[1] === 'object' && args[1].headers != null) {
195
+ inferredRawHeaders.push(...inferRawHeaders(args[1].headers))
196
+ }
197
+
198
+ if (inferredRawHeaders.length > 0) {
199
+ ensureRawHeadersSymbol(request.headers, inferredRawHeaders)
200
+ }
201
+
202
+ return request
203
+ },
204
+ }),
205
+ })
206
+
207
+ Object.defineProperty(globalThis, 'Response', {
208
+ enumerable: true,
209
+ writable: true,
210
+ value: new Proxy(Response, {
211
+ construct(target, args, newTarget) {
212
+ const response = Reflect.construct(target, args, newTarget)
213
+
214
+ if (typeof args[1] === 'object' && args[1].headers != null) {
215
+ ensureRawHeadersSymbol(
216
+ response.headers,
217
+ inferRawHeaders(args[1].headers)
218
+ )
219
+ }
220
+
221
+ return response
222
+ },
223
+ }),
224
+ })
225
+ }
226
+
227
+ export function restoreHeadersPrototype() {
228
+ if (!Reflect.get(Headers, kRestorePatches)) {
229
+ return
230
+ }
231
+
232
+ Reflect.get(Headers, kRestorePatches)()
233
+ }
234
+
235
+ export function getRawFetchHeaders(headers: Headers): RawHeaders {
236
+ // If the raw headers recording failed for some reason,
237
+ // use the normalized header entries instead.
238
+ if (!Reflect.has(headers, kRawHeaders)) {
239
+ return Array.from(headers.entries())
240
+ }
241
+
242
+ const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders
243
+ return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries())
244
+ }
245
+
246
+ /**
247
+ * Infers the raw headers from the given `HeadersInit` provided
248
+ * to the Request/Response constructor.
249
+ *
250
+ * If the `init.headers` is a Headers instance, use it directly.
251
+ * That means the headers were created standalone and already have
252
+ * the raw headers stored.
253
+ * If the `init.headers` is a HeadersInit, create a new Headers
254
+ * instance out of it.
255
+ */
256
+ function inferRawHeaders(headers: HeadersInit): RawHeaders {
257
+ if (headers instanceof Headers) {
258
+ return Reflect.get(headers, kRawHeaders) || []
259
+ }
260
+
261
+ return Reflect.get(new Headers(headers), kRawHeaders)
262
+ }