@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,152 @@
1
+ import { Agent } from 'http'
2
+ import { RequestOptions, Agent as HttpsAgent } from 'https'
3
+ import { Logger } from '@open-draft/logger'
4
+
5
+ const logger = new Logger('utils getUrlByRequestOptions')
6
+
7
+ // Request instance constructed by the "request" library
8
+ // has a "self" property that has a "uri" field. This is
9
+ // reproducible by performing a "XMLHttpRequest" request in JSDOM.
10
+ export interface RequestSelf {
11
+ uri?: URL
12
+ }
13
+
14
+ export type ResolvedRequestOptions = RequestOptions & RequestSelf
15
+
16
+ export const DEFAULT_PATH = '/'
17
+ const DEFAULT_PROTOCOL = 'http:'
18
+ const DEFAULT_HOSTNAME = 'localhost'
19
+ const SSL_PORT = 443
20
+
21
+ function getAgent(
22
+ options: ResolvedRequestOptions
23
+ ): Agent | HttpsAgent | undefined {
24
+ return options.agent instanceof Agent ? options.agent : undefined
25
+ }
26
+
27
+ function getProtocolByRequestOptions(options: ResolvedRequestOptions): string {
28
+ if (options.protocol) {
29
+ return options.protocol
30
+ }
31
+
32
+ const agent = getAgent(options)
33
+ const agentProtocol = (agent as RequestOptions)?.protocol
34
+
35
+ if (agentProtocol) {
36
+ return agentProtocol
37
+ }
38
+
39
+ const port = getPortByRequestOptions(options)
40
+ const isSecureRequest = options.cert || port === SSL_PORT
41
+
42
+ return isSecureRequest ? 'https:' : options.uri?.protocol || DEFAULT_PROTOCOL
43
+ }
44
+
45
+ function getPortByRequestOptions(
46
+ options: ResolvedRequestOptions
47
+ ): number | undefined {
48
+ // Use the explicitly provided port.
49
+ if (options.port) {
50
+ return Number(options.port)
51
+ }
52
+
53
+ // Otherwise, try to resolve port from the agent.
54
+ const agent = getAgent(options)
55
+
56
+ if ((agent as HttpsAgent)?.options.port) {
57
+ return Number((agent as HttpsAgent).options.port)
58
+ }
59
+
60
+ if ((agent as RequestOptions)?.defaultPort) {
61
+ return Number((agent as RequestOptions).defaultPort)
62
+ }
63
+
64
+ // Lastly, return undefined indicating that the port
65
+ // must inferred from the protocol. Do not infer it here.
66
+ return undefined
67
+ }
68
+
69
+ interface RequestAuth {
70
+ username: string
71
+ password: string
72
+ }
73
+
74
+ function getAuthByRequestOptions(
75
+ options: ResolvedRequestOptions
76
+ ): RequestAuth | undefined {
77
+ if (options.auth) {
78
+ const [username, password] = options.auth.split(':')
79
+ return { username, password }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Returns true if host looks like an IPv6 address without surrounding brackets
85
+ * It assumes any host containing `:` is definitely not IPv4 and probably IPv6,
86
+ * but note that this could include invalid IPv6 addresses as well.
87
+ */
88
+ function isRawIPv6Address(host: string): boolean {
89
+ return host.includes(':') && !host.startsWith('[') && !host.endsWith(']')
90
+ }
91
+
92
+ function getHostname(options: ResolvedRequestOptions): string | undefined {
93
+ let host = options.hostname || options.host
94
+
95
+ if (host) {
96
+ if (isRawIPv6Address(host)) {
97
+ host = `[${host}]`
98
+ }
99
+
100
+ // Check the presence of the port, and if it's present,
101
+ // remove it from the host, returning a hostname.
102
+ return new URL(`http://${host}`).hostname
103
+ }
104
+
105
+ return DEFAULT_HOSTNAME
106
+ }
107
+
108
+ /**
109
+ * Creates a `URL` instance from a given `RequestOptions` object.
110
+ */
111
+ export function getUrlByRequestOptions(options: ResolvedRequestOptions): URL {
112
+ logger.info('request options', options)
113
+
114
+ if (options.uri) {
115
+ logger.info(
116
+ 'constructing url from explicitly provided "options.uri": %s',
117
+ options.uri
118
+ )
119
+ return new URL(options.uri.href)
120
+ }
121
+
122
+ logger.info('figuring out url from request options...')
123
+
124
+ const protocol = getProtocolByRequestOptions(options)
125
+ logger.info('protocol', protocol)
126
+
127
+ const port = getPortByRequestOptions(options)
128
+ logger.info('port', port)
129
+
130
+ const hostname = getHostname(options)
131
+ logger.info('hostname', hostname)
132
+
133
+ const path = options.path || DEFAULT_PATH
134
+ logger.info('path', path)
135
+
136
+ const credentials = getAuthByRequestOptions(options)
137
+ logger.info('credentials', credentials)
138
+
139
+ const authString = credentials
140
+ ? `${credentials.username}:${credentials.password}@`
141
+ : ''
142
+ logger.info('auth string:', authString)
143
+
144
+ const portString = typeof port !== 'undefined' ? `:${port}` : ''
145
+ const url = new URL(`${protocol}//${hostname}${portString}${path}`)
146
+ url.username = credentials?.username || ''
147
+ url.password = credentials?.password || ''
148
+
149
+ logger.info('created url:', url)
150
+
151
+ return url
152
+ }
@@ -0,0 +1,14 @@
1
+ import { it, expect } from 'vitest'
2
+ import { getValueBySymbol } from './getValueBySymbol'
3
+
4
+ it('returns undefined given a non-existing symbol', () => {
5
+ expect(getValueBySymbol('non-existing', {})).toBeUndefined()
6
+ })
7
+
8
+ it('returns value behind the given symbol', () => {
9
+ const symbol = Symbol('kInternal')
10
+
11
+ expect(getValueBySymbol('kInternal', { [symbol]: null })).toBe(null)
12
+ expect(getValueBySymbol('kInternal', { [symbol]: true })).toBe(true)
13
+ expect(getValueBySymbol('kInternal', { [symbol]: 'value' })).toBe('value')
14
+ })
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Returns the value behind the symbol with the given name.
3
+ */
4
+ export function getValueBySymbol<T>(
5
+ symbolName: string,
6
+ source: object
7
+ ): T | undefined {
8
+ const ownSymbols = Object.getOwnPropertySymbols(source)
9
+
10
+ const symbol = ownSymbols.find((symbol) => {
11
+ return symbol.description === symbolName
12
+ })
13
+
14
+ if (symbol) {
15
+ return Reflect.get(source, symbol)
16
+ }
17
+
18
+ return
19
+ }
@@ -0,0 +1,205 @@
1
+ import type { Emitter } from 'strict-event-emitter'
2
+ import { DeferredPromise } from '@open-draft/deferred-promise'
3
+ import { until } from '@open-draft/until'
4
+ import type { HttpRequestEventMap } from '../glossary'
5
+ import { emitAsync } from './emitAsync'
6
+ import { RequestController } from '../RequestController'
7
+ import {
8
+ createServerErrorResponse,
9
+ isResponseError,
10
+ isResponseLike,
11
+ } from './responseUtils'
12
+ import { InterceptorError } from '../InterceptorError'
13
+ import { isNodeLikeError } from './isNodeLikeError'
14
+ import { isObject } from './isObject'
15
+
16
+ interface HandleRequestOptions {
17
+ requestId: string
18
+ request: Request
19
+ emitter: Emitter<HttpRequestEventMap>
20
+ controller: RequestController
21
+ }
22
+
23
+ export async function handleRequest(
24
+ options: HandleRequestOptions
25
+ ): Promise<void> {
26
+ const handleResponse = async (
27
+ response: Response | Error | Record<string, any>
28
+ ) => {
29
+ if (response instanceof Error) {
30
+ await options.controller.errorWith(response)
31
+ return true
32
+ }
33
+
34
+ // Handle "Response.error()" instances.
35
+ if (isResponseError(response)) {
36
+ await options.controller.respondWith(response)
37
+ return true
38
+ }
39
+
40
+ /**
41
+ * Handle normal responses or response-like objects.
42
+ * @note This must come before the arbitrary object check
43
+ * since Response instances are, in fact, objects.
44
+ */
45
+ if (isResponseLike(response)) {
46
+ await options.controller.respondWith(response)
47
+ return true
48
+ }
49
+
50
+ // Handle arbitrary objects provided to `.errorWith(reason)`.
51
+ if (isObject(response)) {
52
+ await options.controller.errorWith(response)
53
+ return true
54
+ }
55
+
56
+ return false
57
+ }
58
+
59
+ const handleResponseError = async (error: unknown): Promise<boolean> => {
60
+ // Forward the special interceptor error instances
61
+ // to the developer. These must not be handled in any way.
62
+ if (error instanceof InterceptorError) {
63
+ throw result.error
64
+ }
65
+
66
+ // Support mocking Node.js-like errors.
67
+ if (isNodeLikeError(error)) {
68
+ await options.controller.errorWith(error)
69
+ return true
70
+ }
71
+
72
+ // Handle thrown responses.
73
+ if (error instanceof Response) {
74
+ return await handleResponse(error)
75
+ }
76
+
77
+ return false
78
+ }
79
+
80
+ // Add the last "request" listener to check if the request
81
+ // has been handled in any way. If it hasn't, resolve the
82
+ // response promise with undefined.
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
+ // })
91
+
92
+ const requestAbortPromise = new DeferredPromise<void, unknown>()
93
+
94
+ /**
95
+ * @note `signal` is not always defined in React Native.
96
+ */
97
+ if (options.request.signal) {
98
+ if (options.request.signal.aborted) {
99
+ await options.controller.errorWith(options.request.signal.reason)
100
+ return
101
+ }
102
+
103
+ options.request.signal.addEventListener(
104
+ 'abort',
105
+ () => {
106
+ requestAbortPromise.reject(options.request.signal.reason)
107
+ },
108
+ { once: true }
109
+ )
110
+ }
111
+
112
+ const result = await until(async () => {
113
+ // Emit the "request" event and wait until all the listeners
114
+ // for that event are finished (e.g. async listeners awaited).
115
+ // By the end of this promise, the developer cannot affect the
116
+ // request anymore.
117
+ const requestListenersPromise = emitAsync(options.emitter, 'request', {
118
+ requestId: options.requestId,
119
+ request: options.request,
120
+ controller: options.controller,
121
+ })
122
+
123
+ await Promise.race([
124
+ // Short-circuit the request handling promise if the request gets aborted.
125
+ requestAbortPromise,
126
+ requestListenersPromise,
127
+ options.controller.handled,
128
+ ])
129
+ })
130
+
131
+ // Handle the request being aborted while waiting for the request listeners.
132
+ if (requestAbortPromise.state === 'rejected') {
133
+ await options.controller.errorWith(requestAbortPromise.rejectionReason)
134
+ return
135
+ }
136
+
137
+ if (result.error) {
138
+ // Handle the error during the request listener execution.
139
+ // These can be thrown responses or request errors.
140
+ if (await handleResponseError(result.error)) {
141
+ return
142
+ }
143
+
144
+ // If the developer has added "unhandledException" listeners,
145
+ // allow them to handle the error. They can translate it to a
146
+ // mocked response, network error, or forward it as-is.
147
+ if (options.emitter.listenerCount('unhandledException') > 0) {
148
+ // Create a new request controller just for the unhandled exception case.
149
+ // This is needed because the original controller might have been already
150
+ // interacted with (e.g. "respondWith" or "errorWith" called on it).
151
+ const unhandledExceptionController = new RequestController(
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
+ }
174
+ )
175
+
176
+ await emitAsync(options.emitter, 'unhandledException', {
177
+ error: result.error,
178
+ request: options.request,
179
+ requestId: options.requestId,
180
+ controller: unhandledExceptionController,
181
+ })
182
+
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
189
+ }
190
+ }
191
+
192
+ // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.
193
+ await options.controller.respondWith(
194
+ createServerErrorResponse(result.error)
195
+ )
196
+ return
197
+ }
198
+
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()
202
+ }
203
+
204
+ return options.controller.handled
205
+ }
@@ -0,0 +1,83 @@
1
+ import { vi, beforeAll, afterEach, afterAll, it, expect } from 'vitest'
2
+ import { hasConfigurableGlobal } from './hasConfigurableGlobal'
3
+
4
+ beforeAll(() => {
5
+ vi.spyOn(console, 'error').mockImplementation(() => {})
6
+ })
7
+
8
+ afterEach(() => {
9
+ vi.clearAllMocks()
10
+ })
11
+
12
+ afterAll(() => {
13
+ vi.restoreAllMocks()
14
+ })
15
+
16
+ it('returns true if the global property exists and is configurable', () => {
17
+ Object.defineProperty(global, '_existsAndConfigurable', {
18
+ value: 'something',
19
+ configurable: true,
20
+ })
21
+
22
+ expect(hasConfigurableGlobal('_existsAndConfigurable')).toBe(true)
23
+ })
24
+
25
+ it('returns false if the global property does not exist', () => {
26
+ expect(hasConfigurableGlobal('_non-existing')).toBe(false)
27
+ })
28
+
29
+ it('returns false for existing global with undefined as a value', () => {
30
+ Object.defineProperty(global, '_existsAndUndefined', {
31
+ value: undefined,
32
+ configurable: true,
33
+ })
34
+ expect(hasConfigurableGlobal('_existsAndUndefined')).toBe(false)
35
+ })
36
+
37
+ it('returns false for existing global with null as a value', () => {
38
+ Object.defineProperty(global, '_existsAndNull', {
39
+ value: null,
40
+ configurable: true,
41
+ })
42
+ expect(hasConfigurableGlobal('_existsAndNull')).toBe(false)
43
+ })
44
+
45
+ it('returns false for existing global with a getter that returns undefined', () => {
46
+ Object.defineProperty(global, '_existsGetterUndefined', {
47
+ get: () => undefined,
48
+ configurable: true,
49
+ })
50
+ expect(hasConfigurableGlobal('_existsGetterUndefined')).toBe(false)
51
+ })
52
+
53
+ it('returns false and prints an error for implicitly non-configurable global property', () => {
54
+ Object.defineProperty(global, '_implicitlyNonConfigurable', {
55
+ value: 'something',
56
+ })
57
+
58
+ expect(hasConfigurableGlobal('_implicitlyNonConfigurable')).toBe(false)
59
+ expect(console.error).toHaveBeenCalledWith(
60
+ '[MSW] Failed to apply interceptor: the global `_implicitlyNonConfigurable` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.'
61
+ )
62
+ })
63
+
64
+ it('returns false and prints an error for explicitly non-configurable global property', () => {
65
+ Object.defineProperty(global, '_explicitlyNonConfigurable', {
66
+ value: 'something',
67
+ configurable: false,
68
+ })
69
+
70
+ expect(hasConfigurableGlobal('_explicitlyNonConfigurable')).toBe(false)
71
+ expect(console.error).toHaveBeenCalledWith(
72
+ '[MSW] Failed to apply interceptor: the global `_explicitlyNonConfigurable` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.'
73
+ )
74
+ })
75
+
76
+ it('returns false and prints an error for global property that only has a getter', () => {
77
+ Object.defineProperty(global, '_onlyGetter', { get: () => 'something' })
78
+
79
+ expect(hasConfigurableGlobal('_onlyGetter')).toBe(false)
80
+ expect(console.error).toHaveBeenCalledWith(
81
+ '[MSW] Failed to apply interceptor: the global `_onlyGetter` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.'
82
+ )
83
+ })
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Returns a boolean indicating whether the given global property
3
+ * is defined and is configurable.
4
+ */
5
+ export function hasConfigurableGlobal(propertyName: string): boolean {
6
+ const descriptor = Object.getOwnPropertyDescriptor(globalThis, propertyName)
7
+
8
+ // The property is not set at all.
9
+ if (typeof descriptor === 'undefined') {
10
+ return false
11
+ }
12
+
13
+ // The property is set to a getter that returns undefined.
14
+ if (
15
+ typeof descriptor.get === 'function' &&
16
+ typeof descriptor.get() === 'undefined'
17
+ ) {
18
+ return false
19
+ }
20
+
21
+ // The property is set to a value equal to undefined.
22
+ if (typeof descriptor.get === 'undefined' && descriptor.value == null) {
23
+ return false
24
+ }
25
+
26
+ if (typeof descriptor.set === 'undefined' && !descriptor.configurable) {
27
+ console.error(
28
+ `[MSW] Failed to apply interceptor: the global \`${propertyName}\` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.`
29
+ )
30
+ return false
31
+ }
32
+
33
+ return true
34
+ }
@@ -0,0 +1,13 @@
1
+ export function isNodeLikeError(
2
+ error: unknown
3
+ ): error is NodeJS.ErrnoException {
4
+ if (error == null) {
5
+ return false
6
+ }
7
+
8
+ if (!(error instanceof Error)) {
9
+ return false
10
+ }
11
+
12
+ return 'code' in error && 'errno' in error
13
+ }
@@ -0,0 +1,21 @@
1
+ import { it, expect } from 'vitest'
2
+ import { isObject } from './isObject'
3
+
4
+ it('returns true given an object', () => {
5
+ expect(isObject({})).toBe(true)
6
+ expect(isObject({ a: 1 })).toBe(true)
7
+ })
8
+
9
+ it('returns false given an object-like instance', () => {
10
+ expect(isObject([1])).toBe(false)
11
+ expect(isObject(function () {})).toBe(false)
12
+ expect(isObject(new Response())).toBe(false)
13
+ })
14
+
15
+ it('returns false given a non-object instance', () => {
16
+ expect(isObject(null)).toBe(false)
17
+ expect(isObject(undefined)).toBe(false)
18
+ expect(isObject(false)).toBe(false)
19
+ expect(isObject(123)).toBe(false)
20
+ expect(isObject(Symbol('object Object'))).toBe(false)
21
+ })
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Determines if a given value is an instance of object.
3
+ */
4
+ export function isObject<T>(value: any, loose = false): value is T {
5
+ return loose
6
+ ? Object.prototype.toString.call(value).startsWith('[object ')
7
+ : Object.prototype.toString.call(value) === '[object Object]'
8
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A function that validates if property access is possible on an object
3
+ * without throwing. It returns `true` if the property access is possible
4
+ * and `false` otherwise.
5
+ *
6
+ * Environments like miniflare will throw on property access on certain objects
7
+ * like Request and Response, for unimplemented properties.
8
+ */
9
+ export function isPropertyAccessible<Obj extends Record<string, any>>(
10
+ obj: Obj,
11
+ key: keyof Obj
12
+ ) {
13
+ try {
14
+ obj[key]
15
+ return true
16
+ } catch {
17
+ return false
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ export function nextTick(callback: () => void) {
2
+ setTimeout(callback, 0)
3
+ }
4
+
5
+ export function nextTickAsync(callback: () => void) {
6
+ return new Promise((resolve) => {
7
+ setTimeout(() => {
8
+ resolve(callback())
9
+ }, 0)
10
+ })
11
+ }
@@ -0,0 +1,39 @@
1
+ import { ClientRequest } from 'node:http'
2
+ import { Readable } from 'node:stream'
3
+ import { invariant } from 'outvariant'
4
+ import { getRawRequest } from '../../getRawRequest'
5
+
6
+ const kRawRequestBodyStream = Symbol('kRawRequestBodyStream')
7
+
8
+ /**
9
+ * Returns the request body stream of the given request.
10
+ * @note This is only relevant in the context of `http.ClientRequest`.
11
+ * This function will throw if the given `request` wasn't created based on
12
+ * the `http.ClientRequest` instance.
13
+ * You must rely on the web stream consumers for other request clients.
14
+ */
15
+ export function getClientRequestBodyStream(request: Request): Readable {
16
+ const rawRequest = getRawRequest(request)
17
+
18
+ invariant(
19
+ rawRequest instanceof ClientRequest,
20
+ `Failed to retrieve raw request body stream: request is not an instance of "http.ClientRequest". Note that you can only use the "getClientRequestBodyStream" function with the requests issued by "http.clientRequest".`
21
+ )
22
+
23
+ const requestBodyStream = Reflect.get(request, kRawRequestBodyStream)
24
+
25
+ invariant(
26
+ requestBodyStream instanceof Readable,
27
+ 'Failed to retrieve raw request body stream: corrupted stream (%s)',
28
+ typeof requestBodyStream
29
+ )
30
+
31
+ return requestBodyStream
32
+ }
33
+
34
+ export function setRawRequestBodyStream(
35
+ request: Request,
36
+ stream: Readable
37
+ ): void {
38
+ Reflect.set(request, kRawRequestBodyStream, stream)
39
+ }
@@ -0,0 +1,10 @@
1
+ import { it, expect } from 'vitest'
2
+ import { parseJson } from './parseJson'
3
+
4
+ it('parses a given string into JSON', () => {
5
+ expect(parseJson('{"id":1}')).toEqual({ id: 1 })
6
+ })
7
+
8
+ it('returns null given invalid JSON string', () => {
9
+ expect(parseJson('{"o:2\'')).toBeNull()
10
+ })
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Parses a given string into JSON.
3
+ * Gracefully handles invalid JSON by returning `null`.
4
+ */
5
+ export function parseJson(data: string): Record<string, unknown> | null {
6
+ try {
7
+ const json = JSON.parse(data)
8
+ return json
9
+ } catch (_) {
10
+ return null
11
+ }
12
+ }