@mswjs/interceptors 0.41.3 → 0.41.4

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 (88) hide show
  1. package/lib/browser/{XMLHttpRequest-BvxZV0WU.mjs → XMLHttpRequest-Bbvn_AjX.mjs} +4 -4
  2. package/lib/browser/XMLHttpRequest-Bbvn_AjX.mjs.map +1 -0
  3. package/lib/browser/{XMLHttpRequest-BACqefB-.cjs → XMLHttpRequest-C2KXXdBy.cjs} +4 -4
  4. package/lib/browser/XMLHttpRequest-C2KXXdBy.cjs.map +1 -0
  5. package/lib/browser/{fetch-U3v3Y4ap.cjs → fetch-CJYWVliE.cjs} +4 -11
  6. package/lib/browser/fetch-CJYWVliE.cjs.map +1 -0
  7. package/lib/browser/{fetch-DdKEdDOR.mjs → fetch-D4es-Apw.mjs} +4 -11
  8. package/lib/browser/fetch-D4es-Apw.mjs.map +1 -0
  9. package/lib/browser/{getRawRequest-zx8rUJL2.cjs → getRawRequest-DaxX6uMy.cjs} +83 -1
  10. package/lib/browser/getRawRequest-DaxX6uMy.cjs.map +1 -0
  11. package/lib/browser/{getRawRequest-BTaNLFr0.mjs → getRawRequest-Df640uw8.mjs} +78 -2
  12. package/lib/browser/getRawRequest-Df640uw8.mjs.map +1 -0
  13. package/lib/browser/{handleRequest-CvX2G-Lz.cjs → handleRequest--veikAod.cjs} +2 -2
  14. package/lib/browser/{handleRequest-CvX2G-Lz.cjs.map → handleRequest--veikAod.cjs.map} +1 -1
  15. package/lib/browser/{handleRequest-D7kpTI5U.mjs → handleRequest-Dq1a55b4.mjs} +2 -2
  16. package/lib/browser/{handleRequest-D7kpTI5U.mjs.map → handleRequest-Dq1a55b4.mjs.map} +1 -1
  17. package/lib/browser/index.cjs +2 -1
  18. package/lib/browser/index.d.cts +20 -1
  19. package/lib/browser/index.d.mts +20 -1
  20. package/lib/browser/index.mjs +2 -2
  21. package/lib/browser/interceptors/XMLHttpRequest/index.cjs +3 -3
  22. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +3 -3
  23. package/lib/browser/interceptors/fetch/index.cjs +3 -3
  24. package/lib/browser/interceptors/fetch/index.mjs +3 -3
  25. package/lib/browser/presets/browser.cjs +4 -4
  26. package/lib/browser/presets/browser.mjs +4 -4
  27. package/lib/node/{BatchInterceptor-3LnAnLTx.cjs → BatchInterceptor-7u9fqQIe.cjs} +2 -2
  28. package/lib/node/{BatchInterceptor-3LnAnLTx.cjs.map → BatchInterceptor-7u9fqQIe.cjs.map} +1 -1
  29. package/lib/node/{BatchInterceptor-DFaBPilf.mjs → BatchInterceptor-BxgAXyLw.mjs} +2 -2
  30. package/lib/node/{BatchInterceptor-DFaBPilf.mjs.map → BatchInterceptor-BxgAXyLw.mjs.map} +1 -1
  31. package/lib/node/{ClientRequest-2rDe54Ui.cjs → ClientRequest-CMSXChAM.cjs} +18 -4
  32. package/lib/node/ClientRequest-CMSXChAM.cjs.map +1 -0
  33. package/lib/node/{ClientRequest-Ca8Qykuv.mjs → ClientRequest-Cl3Ky7Tw.mjs} +18 -4
  34. package/lib/node/ClientRequest-Cl3Ky7Tw.mjs.map +1 -0
  35. package/lib/node/RemoteHttpInterceptor.cjs +7 -7
  36. package/lib/node/RemoteHttpInterceptor.cjs.map +1 -1
  37. package/lib/node/RemoteHttpInterceptor.mjs +7 -7
  38. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -1
  39. package/lib/node/{XMLHttpRequest-C8dIZpds.mjs → XMLHttpRequest-BBbs-p9i.mjs} +4 -4
  40. package/lib/node/XMLHttpRequest-BBbs-p9i.mjs.map +1 -0
  41. package/lib/node/{XMLHttpRequest-B7kJdYYI.cjs → XMLHttpRequest-Dj3xJgke.cjs} +4 -4
  42. package/lib/node/XMLHttpRequest-Dj3xJgke.cjs.map +1 -0
  43. package/lib/node/{fetch-BmXpK10r.cjs → fetch-BoCFyaYx.cjs} +4 -11
  44. package/lib/node/fetch-BoCFyaYx.cjs.map +1 -0
  45. package/lib/node/{fetch-G1DVwDKG.mjs → fetch-DmiwAmpu.mjs} +4 -11
  46. package/lib/node/fetch-DmiwAmpu.mjs.map +1 -0
  47. package/lib/node/{fetchUtils-CoU35g3M.mjs → fetchUtils-BTrdMHGp.mjs} +78 -2
  48. package/lib/node/{fetchUtils-BaY5iWXw.cjs.map → fetchUtils-BTrdMHGp.mjs.map} +1 -1
  49. package/lib/node/{fetchUtils-BaY5iWXw.cjs → fetchUtils-DE_q7tpr.cjs} +83 -1
  50. package/lib/node/fetchUtils-DE_q7tpr.cjs.map +1 -0
  51. package/lib/node/{handleRequest-Y97UwBbF.mjs → handleRequest-CDabRyt1.mjs} +2 -2
  52. package/lib/node/{handleRequest-Y97UwBbF.mjs.map → handleRequest-CDabRyt1.mjs.map} +1 -1
  53. package/lib/node/{handleRequest-Bb7Y-XLw.cjs → handleRequest-pLIe3KKo.cjs} +2 -2
  54. package/lib/node/{handleRequest-Bb7Y-XLw.cjs.map → handleRequest-pLIe3KKo.cjs.map} +1 -1
  55. package/lib/node/index.cjs +3 -2
  56. package/lib/node/index.d.cts +20 -1
  57. package/lib/node/index.d.mts +20 -1
  58. package/lib/node/index.mjs +3 -3
  59. package/lib/node/interceptors/ClientRequest/index.cjs +3 -3
  60. package/lib/node/interceptors/ClientRequest/index.mjs +3 -3
  61. package/lib/node/interceptors/XMLHttpRequest/index.cjs +3 -3
  62. package/lib/node/interceptors/XMLHttpRequest/index.mjs +3 -3
  63. package/lib/node/interceptors/fetch/index.cjs +3 -3
  64. package/lib/node/interceptors/fetch/index.mjs +3 -3
  65. package/lib/node/presets/node.cjs +5 -5
  66. package/lib/node/presets/node.mjs +5 -5
  67. package/package.json +1 -1
  68. package/src/RemoteHttpInterceptor.ts +2 -2
  69. package/src/index.ts +1 -1
  70. package/src/interceptors/ClientRequest/MockHttpSocket.ts +2 -2
  71. package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +18 -0
  72. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +2 -1
  73. package/src/interceptors/fetch/index.ts +2 -2
  74. package/src/utils/fetchUtils.test.ts +85 -0
  75. package/src/utils/fetchUtils.ts +139 -3
  76. package/lib/browser/XMLHttpRequest-BACqefB-.cjs.map +0 -1
  77. package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs.map +0 -1
  78. package/lib/browser/fetch-DdKEdDOR.mjs.map +0 -1
  79. package/lib/browser/fetch-U3v3Y4ap.cjs.map +0 -1
  80. package/lib/browser/getRawRequest-BTaNLFr0.mjs.map +0 -1
  81. package/lib/browser/getRawRequest-zx8rUJL2.cjs.map +0 -1
  82. package/lib/node/ClientRequest-2rDe54Ui.cjs.map +0 -1
  83. package/lib/node/ClientRequest-Ca8Qykuv.mjs.map +0 -1
  84. package/lib/node/XMLHttpRequest-B7kJdYYI.cjs.map +0 -1
  85. package/lib/node/XMLHttpRequest-C8dIZpds.mjs.map +0 -1
  86. package/lib/node/fetch-BmXpK10r.cjs.map +0 -1
  87. package/lib/node/fetch-G1DVwDKG.mjs.map +0 -1
  88. package/lib/node/fetchUtils-CoU35g3M.mjs.map +0 -1
@@ -22,6 +22,25 @@ declare function encodeBuffer(text: string): Uint8Array;
22
22
  declare function decodeBuffer(buffer: ArrayBuffer, encoding?: string): string;
23
23
  //#endregion
24
24
  //#region src/utils/fetchUtils.d.ts
25
+ interface FetchRequestInit extends Omit<RequestInit, 'mode'> {
26
+ mode?: RequestMode | 'websocket' | 'webtransport';
27
+ duplex?: 'half' | 'full';
28
+ }
29
+ declare class FetchRequest extends Request {
30
+ #private;
31
+ /**
32
+ * Check if the given request method is configurable.
33
+ * @see https://fetch.spec.whatwg.org/#methods
34
+ */
35
+ static isConfigurableMethod(method: string): boolean;
36
+ static isMethodWithBody(method: string): boolean;
37
+ /**
38
+ * Check if the given request `mode` is configurable.
39
+ * @see https://fetch.spec.whatwg.org/#concept-request-mode
40
+ */
41
+ static isConfigurableMode(mode: string): boolean;
42
+ constructor(input: URL | RequestInfo, init?: FetchRequestInit);
43
+ }
25
44
  interface FetchResponseInit extends ResponseInit {
26
45
  url?: string;
27
46
  }
@@ -71,5 +90,5 @@ declare function getRawRequest(request: Request): unknown | undefined;
71
90
  */
72
91
  declare function resolveWebSocketUrl(url: string | URL): string;
73
92
  //#endregion
74
- export { BatchInterceptor, BatchInterceptorOptions, ExtractEventMapType, ExtractEventNames, FetchResponse, HttpRequestEventMap, INTERNAL_REQUEST_ID_HEADER_NAME, IS_PATCHED_MODULE, Interceptor, InterceptorEventMap, InterceptorReadyState, InterceptorSubscription, RequestController, type RequestControllerSource, RequestCredentials, createRequestId, decodeBuffer, deleteGlobalSymbol, encodeBuffer, getCleanUrl, getGlobalSymbol, getRawRequest, resolveWebSocketUrl };
93
+ export { BatchInterceptor, BatchInterceptorOptions, ExtractEventMapType, ExtractEventNames, FetchRequest, FetchResponse, HttpRequestEventMap, INTERNAL_REQUEST_ID_HEADER_NAME, IS_PATCHED_MODULE, Interceptor, InterceptorEventMap, InterceptorReadyState, InterceptorSubscription, RequestController, type RequestControllerSource, RequestCredentials, createRequestId, decodeBuffer, deleteGlobalSymbol, encodeBuffer, getCleanUrl, getGlobalSymbol, getRawRequest, resolveWebSocketUrl };
75
94
  //# sourceMappingURL=index.d.cts.map
@@ -22,6 +22,25 @@ declare function encodeBuffer(text: string): Uint8Array;
22
22
  declare function decodeBuffer(buffer: ArrayBuffer, encoding?: string): string;
23
23
  //#endregion
24
24
  //#region src/utils/fetchUtils.d.ts
25
+ interface FetchRequestInit extends Omit<RequestInit, 'mode'> {
26
+ mode?: RequestMode | 'websocket' | 'webtransport';
27
+ duplex?: 'half' | 'full';
28
+ }
29
+ declare class FetchRequest extends Request {
30
+ #private;
31
+ /**
32
+ * Check if the given request method is configurable.
33
+ * @see https://fetch.spec.whatwg.org/#methods
34
+ */
35
+ static isConfigurableMethod(method: string): boolean;
36
+ static isMethodWithBody(method: string): boolean;
37
+ /**
38
+ * Check if the given request `mode` is configurable.
39
+ * @see https://fetch.spec.whatwg.org/#concept-request-mode
40
+ */
41
+ static isConfigurableMode(mode: string): boolean;
42
+ constructor(input: URL | RequestInfo, init?: FetchRequestInit);
43
+ }
25
44
  interface FetchResponseInit extends ResponseInit {
26
45
  url?: string;
27
46
  }
@@ -71,5 +90,5 @@ declare function getRawRequest(request: Request): unknown | undefined;
71
90
  */
72
91
  declare function resolveWebSocketUrl(url: string | URL): string;
73
92
  //#endregion
74
- export { BatchInterceptor, BatchInterceptorOptions, ExtractEventMapType, ExtractEventNames, FetchResponse, HttpRequestEventMap, INTERNAL_REQUEST_ID_HEADER_NAME, IS_PATCHED_MODULE, Interceptor, InterceptorEventMap, InterceptorReadyState, InterceptorSubscription, RequestController, type RequestControllerSource, RequestCredentials, createRequestId, decodeBuffer, deleteGlobalSymbol, encodeBuffer, getCleanUrl, getGlobalSymbol, getRawRequest, resolveWebSocketUrl };
93
+ export { BatchInterceptor, BatchInterceptorOptions, ExtractEventMapType, ExtractEventNames, FetchRequest, FetchResponse, HttpRequestEventMap, INTERNAL_REQUEST_ID_HEADER_NAME, IS_PATCHED_MODULE, Interceptor, InterceptorEventMap, InterceptorReadyState, InterceptorSubscription, RequestController, type RequestControllerSource, RequestCredentials, createRequestId, decodeBuffer, deleteGlobalSymbol, encodeBuffer, getCleanUrl, getGlobalSymbol, getRawRequest, resolveWebSocketUrl };
75
94
  //# sourceMappingURL=index.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { t as IS_PATCHED_MODULE } from "./glossary-glQBRnVD.mjs";
2
- import { c as InterceptorReadyState, i as RequestController, l as deleteGlobalSymbol, o as INTERNAL_REQUEST_ID_HEADER_NAME, r as createRequestId, s as Interceptor, t as FetchResponse, u as getGlobalSymbol } from "./fetchUtils-CoU35g3M.mjs";
3
- import { t as BatchInterceptor } from "./BatchInterceptor-DFaBPilf.mjs";
2
+ import { a as RequestController, c as Interceptor, d as getGlobalSymbol, i as createRequestId, l as InterceptorReadyState, n as FetchResponse, s as INTERNAL_REQUEST_ID_HEADER_NAME, t as FetchRequest, u as deleteGlobalSymbol } from "./fetchUtils-BTrdMHGp.mjs";
3
+ import { t as BatchInterceptor } from "./BatchInterceptor-BxgAXyLw.mjs";
4
4
  import { n as encodeBuffer, t as decodeBuffer } from "./bufferUtils-_8XfKIfX.mjs";
5
5
  import { t as getRawRequest } from "./getRawRequest-DnwmXyOW.mjs";
6
6
 
@@ -36,5 +36,5 @@ function resolveWebSocketUrl(url) {
36
36
  }
37
37
 
38
38
  //#endregion
39
- export { BatchInterceptor, FetchResponse, INTERNAL_REQUEST_ID_HEADER_NAME, IS_PATCHED_MODULE, Interceptor, InterceptorReadyState, RequestController, createRequestId, decodeBuffer, deleteGlobalSymbol, encodeBuffer, getCleanUrl, getGlobalSymbol, getRawRequest, resolveWebSocketUrl };
39
+ export { BatchInterceptor, FetchRequest, FetchResponse, INTERNAL_REQUEST_ID_HEADER_NAME, IS_PATCHED_MODULE, Interceptor, InterceptorReadyState, RequestController, createRequestId, decodeBuffer, deleteGlobalSymbol, encodeBuffer, getCleanUrl, getGlobalSymbol, getRawRequest, resolveWebSocketUrl };
40
40
  //# sourceMappingURL=index.mjs.map
@@ -1,6 +1,6 @@
1
- require('../../fetchUtils-BaY5iWXw.cjs');
2
- const require_ClientRequest = require('../../ClientRequest-2rDe54Ui.cjs');
3
- require('../../handleRequest-Bb7Y-XLw.cjs');
1
+ require('../../fetchUtils-DE_q7tpr.cjs');
2
+ const require_ClientRequest = require('../../ClientRequest-CMSXChAM.cjs');
3
+ require('../../handleRequest-pLIe3KKo.cjs');
4
4
  require('../../node-dKdAf3tC.cjs');
5
5
 
6
6
  exports.ClientRequestInterceptor = require_ClientRequest.ClientRequestInterceptor;
@@ -1,6 +1,6 @@
1
- import "../../fetchUtils-CoU35g3M.mjs";
2
- import { t as ClientRequestInterceptor } from "../../ClientRequest-Ca8Qykuv.mjs";
3
- import "../../handleRequest-Y97UwBbF.mjs";
1
+ import "../../fetchUtils-BTrdMHGp.mjs";
2
+ import { t as ClientRequestInterceptor } from "../../ClientRequest-Cl3Ky7Tw.mjs";
3
+ import "../../handleRequest-CDabRyt1.mjs";
4
4
  import "../../node-DwCc6iuP.mjs";
5
5
 
6
6
  export { ClientRequestInterceptor };
@@ -1,6 +1,6 @@
1
- require('../../fetchUtils-BaY5iWXw.cjs');
1
+ require('../../fetchUtils-DE_q7tpr.cjs');
2
2
  require('../../bufferUtils-DiCTqG-7.cjs');
3
- require('../../handleRequest-Bb7Y-XLw.cjs');
4
- const require_XMLHttpRequest = require('../../XMLHttpRequest-B7kJdYYI.cjs');
3
+ require('../../handleRequest-pLIe3KKo.cjs');
4
+ const require_XMLHttpRequest = require('../../XMLHttpRequest-Dj3xJgke.cjs');
5
5
 
6
6
  exports.XMLHttpRequestInterceptor = require_XMLHttpRequest.XMLHttpRequestInterceptor;
@@ -1,6 +1,6 @@
1
- import "../../fetchUtils-CoU35g3M.mjs";
1
+ import "../../fetchUtils-BTrdMHGp.mjs";
2
2
  import "../../bufferUtils-_8XfKIfX.mjs";
3
- import "../../handleRequest-Y97UwBbF.mjs";
4
- import { t as XMLHttpRequestInterceptor } from "../../XMLHttpRequest-C8dIZpds.mjs";
3
+ import "../../handleRequest-CDabRyt1.mjs";
4
+ import { t as XMLHttpRequestInterceptor } from "../../XMLHttpRequest-BBbs-p9i.mjs";
5
5
 
6
6
  export { XMLHttpRequestInterceptor };
@@ -1,5 +1,5 @@
1
- require('../../fetchUtils-BaY5iWXw.cjs');
2
- require('../../handleRequest-Bb7Y-XLw.cjs');
3
- const require_fetch = require('../../fetch-BmXpK10r.cjs');
1
+ require('../../fetchUtils-DE_q7tpr.cjs');
2
+ require('../../handleRequest-pLIe3KKo.cjs');
3
+ const require_fetch = require('../../fetch-BoCFyaYx.cjs');
4
4
 
5
5
  exports.FetchInterceptor = require_fetch.FetchInterceptor;
@@ -1,5 +1,5 @@
1
- import "../../fetchUtils-CoU35g3M.mjs";
2
- import "../../handleRequest-Y97UwBbF.mjs";
3
- import { t as FetchInterceptor } from "../../fetch-G1DVwDKG.mjs";
1
+ import "../../fetchUtils-BTrdMHGp.mjs";
2
+ import "../../handleRequest-CDabRyt1.mjs";
3
+ import { t as FetchInterceptor } from "../../fetch-DmiwAmpu.mjs";
4
4
 
5
5
  export { FetchInterceptor };
@@ -1,10 +1,10 @@
1
- require('../fetchUtils-BaY5iWXw.cjs');
1
+ require('../fetchUtils-DE_q7tpr.cjs');
2
2
  require('../bufferUtils-DiCTqG-7.cjs');
3
- const require_ClientRequest = require('../ClientRequest-2rDe54Ui.cjs');
4
- require('../handleRequest-Bb7Y-XLw.cjs');
3
+ const require_ClientRequest = require('../ClientRequest-CMSXChAM.cjs');
4
+ require('../handleRequest-pLIe3KKo.cjs');
5
5
  require('../node-dKdAf3tC.cjs');
6
- const require_XMLHttpRequest = require('../XMLHttpRequest-B7kJdYYI.cjs');
7
- const require_fetch = require('../fetch-BmXpK10r.cjs');
6
+ const require_XMLHttpRequest = require('../XMLHttpRequest-Dj3xJgke.cjs');
7
+ const require_fetch = require('../fetch-BoCFyaYx.cjs');
8
8
 
9
9
  //#region src/presets/node.ts
10
10
  /**
@@ -1,10 +1,10 @@
1
- import "../fetchUtils-CoU35g3M.mjs";
1
+ import "../fetchUtils-BTrdMHGp.mjs";
2
2
  import "../bufferUtils-_8XfKIfX.mjs";
3
- import { t as ClientRequestInterceptor } from "../ClientRequest-Ca8Qykuv.mjs";
4
- import "../handleRequest-Y97UwBbF.mjs";
3
+ import { t as ClientRequestInterceptor } from "../ClientRequest-Cl3Ky7Tw.mjs";
4
+ import "../handleRequest-CDabRyt1.mjs";
5
5
  import "../node-DwCc6iuP.mjs";
6
- import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-C8dIZpds.mjs";
7
- import { t as FetchInterceptor } from "../fetch-G1DVwDKG.mjs";
6
+ import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-BBbs-p9i.mjs";
7
+ import { t as FetchInterceptor } from "../fetch-DmiwAmpu.mjs";
8
8
 
9
9
  //#region src/presets/node.ts
10
10
  /**
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "commonjs",
3
3
  "name": "@mswjs/interceptors",
4
4
  "description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
5
- "version": "0.41.3",
5
+ "version": "0.41.4",
6
6
  "main": "./lib/node/index.cjs",
7
7
  "module": "./lib/node/index.mjs",
8
8
  "types": "./lib/node/index.d.cts",
@@ -7,7 +7,7 @@ import { XMLHttpRequestInterceptor } from './interceptors/XMLHttpRequest'
7
7
  import { FetchInterceptor } from './interceptors/fetch'
8
8
  import { handleRequest } from './utils/handleRequest'
9
9
  import { RequestController } from './RequestController'
10
- import { FetchResponse } from './utils/fetchUtils'
10
+ import { FetchRequest, FetchResponse } from './utils/fetchUtils'
11
11
  import { isResponseError } from './utils/responseUtils'
12
12
 
13
13
  export interface SerializedRequest {
@@ -172,7 +172,7 @@ export class RemoteHttpResolver extends Interceptor<HttpRequestEventMap> {
172
172
 
173
173
  logger.info('parsed intercepted request', requestJson)
174
174
 
175
- const request = new Request(requestJson.url, {
175
+ const request = new FetchRequest(requestJson.url, {
176
176
  method: requestJson.method,
177
177
  headers: new Headers(requestJson.headers),
178
178
  credentials: requestJson.credentials,
package/src/index.ts CHANGED
@@ -10,6 +10,6 @@ export {
10
10
  export { createRequestId } from './createRequestId'
11
11
  export { getCleanUrl } from './utils/getCleanUrl'
12
12
  export { encodeBuffer, decodeBuffer } from './utils/bufferUtils'
13
- export { FetchResponse } from './utils/fetchUtils'
13
+ export { FetchRequest, FetchResponse } from './utils/fetchUtils'
14
14
  export { getRawRequest } from './getRawRequest'
15
15
  export { resolveWebSocketUrl } from './utils/resolveWebSocketUrl'
@@ -15,7 +15,7 @@ import { isPropertyAccessible } from '../../utils/isPropertyAccessible'
15
15
  import { baseUrlFromConnectionOptions } from '../Socket/utils/baseUrlFromConnectionOptions'
16
16
  import { createRequestId } from '../../createRequestId'
17
17
  import { getRawFetchHeaders } from './utils/recordRawHeaders'
18
- import { FetchResponse } from '../../utils/fetchUtils'
18
+ import { FetchRequest, FetchResponse } from '../../utils/fetchUtils'
19
19
  import { setRawRequest } from '../../getRawRequest'
20
20
  import { setRawRequestBodyStream } from '../../utils/node'
21
21
  import { freeParser } from './utils/parserUtils'
@@ -576,7 +576,7 @@ export class MockHttpSocket extends MockSocket {
576
576
  })
577
577
 
578
578
  const requestId = createRequestId()
579
- this.request = new Request(url, {
579
+ this.request = new FetchRequest(url, {
580
580
  method,
581
581
  headers,
582
582
  credentials: 'same-origin',
@@ -1,3 +1,5 @@
1
+ import { FetchRequest, FetchResponse } from '../../../utils/fetchUtils'
2
+
1
3
  type HeaderTuple = [string, string]
2
4
  type RawHeaders = Array<HeaderTuple>
3
5
  type SetHeaderBehavior = 'set' | 'append'
@@ -92,6 +94,11 @@ export function recordRawFetchHeaders() {
92
94
  globalThis.Request = OriginalRequest
93
95
  globalThis.Response = OriginalResponse
94
96
 
97
+ Object.setPrototypeOf(FetchRequest, OriginalRequest)
98
+ Object.setPrototypeOf(FetchRequest.prototype, OriginalRequest.prototype)
99
+ Object.setPrototypeOf(FetchResponse, OriginalResponse)
100
+ Object.setPrototypeOf(FetchResponse.prototype, OriginalResponse.prototype)
101
+
95
102
  Reflect.deleteProperty(Headers, kRestorePatches)
96
103
  },
97
104
  enumerable: false,
@@ -222,6 +229,17 @@ export function recordRawFetchHeaders() {
222
229
  },
223
230
  }),
224
231
  })
232
+
233
+ /**
234
+ * Re-parent FetchRequest/FetchResponse so their `super()` calls go
235
+ * through the proxied globalThis.Request/Response above. Without this,
236
+ * FetchRequest extends the statically-captured (original) Request,
237
+ * bypassing the construct proxy that records raw headers.
238
+ */
239
+ Object.setPrototypeOf(FetchRequest, globalThis.Request)
240
+ Object.setPrototypeOf(FetchRequest.prototype, globalThis.Request.prototype)
241
+ Object.setPrototypeOf(FetchResponse, globalThis.Response)
242
+ Object.setPrototypeOf(FetchResponse.prototype, globalThis.Response.prototype)
225
243
  }
226
244
 
227
245
  export function restoreHeadersPrototype() {
@@ -16,6 +16,7 @@ import { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'
16
16
  import { createRequestId } from '../../createRequestId'
17
17
  import { getBodyByteLength } from './utils/getBodyByteLength'
18
18
  import { setRawRequest } from '../../getRawRequest'
19
+ import { FetchRequest } from '../../utils/fetchUtils'
19
20
 
20
21
  const kIsRequestHandled = Symbol('kIsRequestHandled')
21
22
  const IS_NODE = isNodeProcess()
@@ -671,7 +672,7 @@ export class XMLHttpRequestController {
671
672
  const resolvedBody =
672
673
  body instanceof Document ? body.documentElement.innerText : body
673
674
 
674
- const fetchRequest = new Request(this.url.href, {
675
+ const fetchRequest = new FetchRequest(this.url.href, {
675
676
  method: this.method,
676
677
  headers: this.requestHeaders,
677
678
  /**
@@ -12,7 +12,7 @@ import { createNetworkError } from './utils/createNetworkError'
12
12
  import { followFetchRedirect } from './utils/followRedirect'
13
13
  import { decompressResponse } from './utils/decompression'
14
14
  import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
15
- import { FetchResponse } from '../../utils/fetchUtils'
15
+ import { FetchRequest, FetchResponse } from '../../utils/fetchUtils'
16
16
  import { setRawRequest } from '../../getRawRequest'
17
17
  import { isResponseError } from '../../utils/responseUtils'
18
18
 
@@ -51,7 +51,7 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
51
51
  ? new URL(input, location.href)
52
52
  : input
53
53
 
54
- const request = new Request(resolvedInput, init)
54
+ const request = new FetchRequest(resolvedInput, init)
55
55
 
56
56
  /**
57
57
  * @note Set the raw request only if a Request instance was provided to fetch.
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { FetchRequest } from './fetchUtils'
3
+
4
+ describe('FetchRequest', () => {
5
+ const URL = 'https://example.com/'
6
+
7
+ it('passes instanceof check with the regular Request', () => {
8
+ expect(new FetchRequest(URL)).toBeInstanceOf(Request)
9
+ })
10
+
11
+ it('creates a request with a non-configurable method', () => {
12
+ expect
13
+ .soft(new FetchRequest(URL, { method: 'CONNECT' }))
14
+ .toHaveProperty('method', 'CONNECT')
15
+
16
+ expect
17
+ .soft(new FetchRequest(URL, { method: 'TRACE' }))
18
+ .toHaveProperty('method', 'TRACE')
19
+ expect
20
+ .soft(new FetchRequest(new FetchRequest(URL, { method: 'TRACE' })))
21
+ .toHaveProperty('method', 'TRACE')
22
+
23
+ expect
24
+ .soft(new FetchRequest(URL, { method: 'TRACK' }))
25
+ .toHaveProperty('method', 'TRACK')
26
+ expect
27
+ .soft(new FetchRequest(new FetchRequest(URL, { method: 'TRACK' })))
28
+ .toHaveProperty('method', 'TRACK')
29
+ })
30
+
31
+ it('creates a request with a non-configurable mode', () => {
32
+ expect
33
+ .soft(new FetchRequest(URL, { mode: 'navigate' }))
34
+ .toHaveProperty('mode', 'navigate')
35
+ expect
36
+ .soft(new FetchRequest(new FetchRequest(URL, { mode: 'navigate' })))
37
+ .toHaveProperty('mode', 'navigate')
38
+
39
+ expect
40
+ .soft(new FetchRequest(URL, { mode: 'websocket' }))
41
+ .toHaveProperty('mode', 'websocket')
42
+ expect
43
+ .soft(new FetchRequest(new FetchRequest(URL, { mode: 'websocket' })))
44
+ .toHaveProperty('mode', 'websocket')
45
+
46
+ expect
47
+ .soft(new FetchRequest(URL, { mode: 'webtransport' }))
48
+ .toHaveProperty('mode', 'webtransport')
49
+ expect
50
+ .soft(new FetchRequest(new FetchRequest(URL, { mode: 'webtransport' })))
51
+ .toHaveProperty('mode', 'webtransport')
52
+ })
53
+
54
+ it('ignores body for the requests without a body', () => {
55
+ expect
56
+ .soft(new FetchRequest(URL, { body: null }))
57
+ .toHaveProperty('body', null)
58
+ expect
59
+ .soft(new FetchRequest(URL, { body: undefined }))
60
+ .toHaveProperty('body', null)
61
+ expect
62
+ .soft(new FetchRequest(URL, { body: 'hello' }))
63
+ .toHaveProperty('body', null)
64
+
65
+ expect
66
+ .soft(new FetchRequest(URL, { method: 'GET', body: 'hello' }))
67
+ .toHaveProperty('body', null)
68
+ expect
69
+ .soft(new FetchRequest(URL, { method: 'HEAD', body: 'hello' }))
70
+ .toHaveProperty('body', null)
71
+ expect
72
+ .soft(new FetchRequest(URL, { method: 'CONNECT', body: 'hello' }))
73
+ .toHaveProperty('body', null)
74
+ expect
75
+ .soft(new FetchRequest(URL, { method: 'TRACE', body: 'hello' }))
76
+ .toHaveProperty('body', null)
77
+ expect
78
+ .soft(new FetchRequest(URL, { method: 'TRACK', body: 'hello' }))
79
+ .toHaveProperty('body', null)
80
+
81
+ expect
82
+ .soft(new FetchRequest(URL, { method: 'POST', body: 'hello' }))
83
+ .toHaveProperty('body', expect.any(ReadableStream))
84
+ })
85
+ })
@@ -1,11 +1,147 @@
1
1
  import { canParseUrl } from './canParseUrl'
2
2
  import { getValueBySymbol } from './getValueBySymbol'
3
3
 
4
+ interface UndiciRequestState extends RequestInit {}
5
+
6
+ interface FetchRequestInit extends Omit<RequestInit, 'mode'> {
7
+ mode?: RequestMode | 'websocket' | 'webtransport'
8
+ duplex?: 'half' | 'full'
9
+ }
10
+
11
+ export class FetchRequest extends Request {
12
+ static #resolveProperty<T extends keyof FetchRequestInit & keyof Request>(
13
+ input: RequestInfo | URL,
14
+ init: FetchRequestInit = {},
15
+ key: T
16
+ ): FetchRequestInit[T] {
17
+ return init[key] ?? (input instanceof Request ? input[key] : undefined)
18
+ }
19
+
20
+ /**
21
+ * Check if the given request method is configurable.
22
+ * @see https://fetch.spec.whatwg.org/#methods
23
+ */
24
+ static isConfigurableMethod(method: string): boolean {
25
+ return method !== 'CONNECT' && method !== 'TRACE' && method !== 'TRACK'
26
+ }
27
+
28
+ static isMethodWithBody(method: string): boolean {
29
+ return (
30
+ method !== 'HEAD' &&
31
+ method !== 'GET' &&
32
+ FetchRequest.isConfigurableMethod(method)
33
+ )
34
+ }
35
+
36
+ /**
37
+ * Check if the given request `mode` is configurable.
38
+ * @see https://fetch.spec.whatwg.org/#concept-request-mode
39
+ */
40
+ static isConfigurableMode(mode: string): boolean {
41
+ return (
42
+ mode !== 'navigate' && mode !== 'websocket' && mode !== 'webtransport'
43
+ )
44
+ }
45
+
46
+ constructor(input: URL | RequestInfo, init?: FetchRequestInit) {
47
+ const method = FetchRequest.#resolveProperty(input, init, 'method') || 'GET'
48
+ const safeMethod = FetchRequest.isConfigurableMethod(method)
49
+ ? method
50
+ : 'GET'
51
+
52
+ const hasExplicitBody = init != null && 'body' in init
53
+
54
+ /**
55
+ * Only include `body` in the super init when it needs to be overridden.
56
+ * When `input` is a Request and no explicit body is in `init`, let the
57
+ * Request constructor handle body transfer naturally so it properly
58
+ * marks the original request's body as consumed (bodyUsed = true).
59
+ */
60
+ const bodyInit: { body?: BodyInit | null } = !FetchRequest.isMethodWithBody(
61
+ method
62
+ )
63
+ ? { body: undefined }
64
+ : hasExplicitBody
65
+ ? { body: init.body }
66
+ : {}
67
+
68
+ const mode =
69
+ (FetchRequest.#resolveProperty(input, init, 'mode') as RequestMode) ??
70
+ undefined
71
+ const safeMode = FetchRequest.isConfigurableMode(mode) ? mode : undefined
72
+
73
+ super(input, {
74
+ ...(init || {}),
75
+ method: safeMethod,
76
+ mode: safeMode,
77
+ // @ts-expect-error Untyped Node.js property.
78
+ duplex:
79
+ init?.duplex ??
80
+ (FetchRequest.isMethodWithBody(method) ? 'half' : undefined),
81
+ ...bodyInit,
82
+ })
83
+
84
+ if (method !== safeMethod) {
85
+ this.#setInternalProperty('method', method)
86
+ }
87
+
88
+ if (method === 'CONNECT') {
89
+ const url = new URL(input instanceof Request ? input.url : input)
90
+
91
+ let authority: string
92
+
93
+ /**
94
+ * @note Node.js has a bug parsing raw CONNECT requests URLs like
95
+ * "http://127.0.0.1:1337/localhost:80". It would treat "localhost:" as a protocol.
96
+ */
97
+ if (url.protocol === 'localhost:') {
98
+ authority = url.href
99
+ } else {
100
+ authority = url.pathname.replace(/^\/+/, '')
101
+ }
102
+
103
+ /**
104
+ * @note Define "url" as a getter because Undici uses their own
105
+ * logic to resolve the "request.url" property. Simply reassigning
106
+ * its value doesn't do anything. This is a destructive action
107
+ * but it's safe because "CONNECT" requests are forbidden per fetch.
108
+ */
109
+ Object.defineProperty(this, 'url', {
110
+ get: () => authority,
111
+ enumerable: true,
112
+ configurable: true,
113
+ })
114
+ }
115
+
116
+ if (mode != null && mode !== safeMode) {
117
+ this.#setInternalProperty('mode', mode)
118
+ }
119
+ }
120
+
121
+ #setInternalProperty<T extends keyof Request>(
122
+ key: T,
123
+ value: Request[T]
124
+ ): void {
125
+ const internalState = getValueBySymbol<UndiciRequestState>('state', this)
126
+
127
+ if (internalState) {
128
+ Reflect.set(internalState, key, value)
129
+ } else {
130
+ Object.defineProperty(this, key, {
131
+ value,
132
+ enumerable: true,
133
+ configurable: true,
134
+ writable: false,
135
+ })
136
+ }
137
+ }
138
+ }
139
+
4
140
  export interface FetchResponseInit extends ResponseInit {
5
141
  url?: string
6
142
  }
7
143
 
8
- interface UndiciFetchInternalState {
144
+ interface UndiciResponseState {
9
145
  aborted: boolean
10
146
  rangeRequested: boolean
11
147
  timingAllowPassed: boolean
@@ -54,7 +190,7 @@ export class FetchResponse extends Response {
54
190
  return
55
191
  }
56
192
 
57
- const state = getValueBySymbol<UndiciFetchInternalState>('state', response)
193
+ const state = getValueBySymbol<UndiciResponseState>('state', response)
58
194
 
59
195
  if (state) {
60
196
  // In Undici, push the URL to the internal list of URLs.
@@ -100,7 +236,7 @@ export class FetchResponse extends Response {
100
236
  * @note Undici keeps an internal "Symbol(state)" that holds
101
237
  * the actual value of response status. Update that in Node.js.
102
238
  */
103
- const state = getValueBySymbol<UndiciFetchInternalState>('state', this)
239
+ const state = getValueBySymbol<UndiciResponseState>('state', this)
104
240
 
105
241
  if (state) {
106
242
  state.status = status