@mswjs/interceptors 0.22.16 → 0.24.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 (69) hide show
  1. package/README.md +29 -11
  2. package/lib/browser/{Interceptor-c8fc448a.d.ts → Interceptor-0a020bc4.d.ts} +14 -2
  3. package/lib/browser/{chunk-OQKDMHBL.js → chunk-2TENISKM.js} +21 -18
  4. package/lib/{node/chunk-IHW3ERPT.js → browser/chunk-3LFH2WCF.js} +1 -9
  5. package/lib/{node/chunk-MPFSBY4S.js → browser/chunk-4CFMDU7Z.js} +36 -9
  6. package/lib/{node/chunk-LTX5IGCQ.mjs → browser/chunk-7II4SWKS.mjs} +0 -8
  7. package/lib/browser/{chunk-CCRODCJY.mjs → chunk-B74BGPYH.mjs} +89 -24
  8. package/lib/browser/{chunk-MW6NCDWE.mjs → chunk-GXJLJMOT.mjs} +33 -7
  9. package/lib/browser/{chunk-G2NATYCY.mjs → chunk-LHYX2GOM.mjs} +17 -14
  10. package/lib/browser/{chunk-RODLGD6G.js → chunk-UGP4JOAM.js} +96 -31
  11. package/lib/browser/index.d.ts +3 -3
  12. package/lib/browser/index.js +4 -4
  13. package/lib/browser/index.mjs +2 -2
  14. package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +5 -2
  15. package/lib/browser/interceptors/XMLHttpRequest/index.js +4 -4
  16. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +3 -3
  17. package/lib/browser/interceptors/fetch/index.d.ts +1 -1
  18. package/lib/browser/interceptors/fetch/index.js +3 -3
  19. package/lib/browser/interceptors/fetch/index.mjs +2 -2
  20. package/lib/browser/presets/browser.d.ts +1 -1
  21. package/lib/browser/presets/browser.js +6 -6
  22. package/lib/browser/presets/browser.mjs +4 -4
  23. package/lib/node/{BatchInterceptor-fe69020d.d.ts → BatchInterceptor-c841b068.d.ts} +1 -1
  24. package/lib/node/{Interceptor-f9dfe016.d.ts → Interceptor-738f79c5.d.ts} +14 -2
  25. package/lib/node/RemoteHttpInterceptor.d.ts +4 -5
  26. package/lib/node/RemoteHttpInterceptor.js +27 -25
  27. package/lib/node/RemoteHttpInterceptor.mjs +21 -19
  28. package/lib/node/chunk-3LFH2WCF.js +21 -0
  29. package/lib/node/chunk-7II4SWKS.mjs +21 -0
  30. package/lib/node/{chunk-VJDB3MIV.js → chunk-MVPEJK4V.js} +2 -2
  31. package/lib/node/{chunk-QRCYLMPE.js → chunk-OOSIWXHX.js} +57 -16
  32. package/lib/node/{chunk-R6MTHW6S.mjs → chunk-PSIO3L7D.mjs} +55 -14
  33. package/lib/node/{chunk-Y5QA6OEZ.mjs → chunk-RGYCLCLK.mjs} +33 -10
  34. package/lib/node/{chunk-IAIFDHPP.mjs → chunk-UWSK5F3S.mjs} +89 -24
  35. package/lib/node/{chunk-NUSH7ACE.mjs → chunk-VS3GJPUE.mjs} +1 -1
  36. package/lib/{browser/chunk-7VJMJSIJ.js → node/chunk-XYZRP5S2.js} +35 -13
  37. package/lib/node/{chunk-YFXBOTHW.js → chunk-YCEMBJEM.js} +93 -28
  38. package/lib/node/index.d.ts +2 -2
  39. package/lib/node/index.js +4 -4
  40. package/lib/node/index.mjs +3 -3
  41. package/lib/node/interceptors/ClientRequest/index.d.ts +1 -1
  42. package/lib/node/interceptors/ClientRequest/index.js +3 -3
  43. package/lib/node/interceptors/ClientRequest/index.mjs +2 -2
  44. package/lib/node/interceptors/XMLHttpRequest/index.d.ts +5 -2
  45. package/lib/node/interceptors/XMLHttpRequest/index.js +4 -4
  46. package/lib/node/interceptors/XMLHttpRequest/index.mjs +3 -3
  47. package/lib/node/interceptors/fetch/index.d.ts +1 -1
  48. package/lib/node/interceptors/fetch/index.js +18 -15
  49. package/lib/node/interceptors/fetch/index.mjs +17 -14
  50. package/lib/node/presets/node.d.ts +1 -1
  51. package/lib/node/presets/node.js +6 -6
  52. package/lib/node/presets/node.mjs +4 -4
  53. package/package.json +15 -18
  54. package/src/RemoteHttpInterceptor.ts +19 -16
  55. package/src/glossary.ts +14 -2
  56. package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +5 -5
  57. package/src/interceptors/ClientRequest/NodeClientRequest.ts +17 -9
  58. package/src/interceptors/ClientRequest/index.test.ts +2 -2
  59. package/src/interceptors/ClientRequest/utils/createRequest.ts +0 -1
  60. package/src/interceptors/ClientRequest/utils/createResponse.ts +34 -3
  61. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +53 -23
  62. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +16 -7
  63. package/src/interceptors/XMLHttpRequest/index.ts +3 -3
  64. package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +27 -5
  65. package/src/interceptors/fetch/index.ts +18 -15
  66. package/src/utils/bufferUtils.ts +0 -2
  67. package/lib/browser/chunk-MQA5WAD4.mjs +0 -2139
  68. package/lib/browser/chunk-QAZ3SPQZ.js +0 -2139
  69. package/src/shims/webEncoding.ts +0 -9
package/README.md CHANGED
@@ -116,15 +116,18 @@ interceptor.apply()
116
116
 
117
117
  // Listen to any "http.ClientRequest" being dispatched,
118
118
  // and log its method and full URL.
119
- interceptor.on('request', (request, requestId) => {
119
+ interceptor.on('request', ({ request, requestId }) => {
120
120
  console.log(request.method, request.url)
121
121
  })
122
122
 
123
123
  // Listen to any responses sent to "http.ClientRequest".
124
124
  // Note that this listener is read-only and cannot affect responses.
125
- interceptor.on('response', (response, request) => {
126
- console.log('response to %s %s was:', request.method, request.url, response)
127
- })
125
+ interceptor.on(
126
+ 'response',
127
+ ({ response, isMockedResponse, request, requestId }) => {
128
+ console.log('response to %s %s was:', request.method, request.url, response)
129
+ }
130
+ )
128
131
  ```
129
132
 
130
133
  All HTTP request interceptors implement the same events:
@@ -203,7 +206,7 @@ All HTTP request interceptors emit a "request" event. In the listener to this ev
203
206
  > There are many ways to describe a request in Node.js but this library coerces different request definitions to a single specification-compliant `Request` instance to make the handling consistent.
204
207
 
205
208
  ```js
206
- interceptor.on('reqest', (request, requestId) => {
209
+ interceptor.on('request', ({ request, requestId }) => {
207
210
  console.log(request.method, request.url)
208
211
  })
209
212
  ```
@@ -211,7 +214,7 @@ interceptor.on('reqest', (request, requestId) => {
211
214
  Since the exposed `request` instance implements the Fetch API specification, you can operate with it just as you do with the regular browser request. For example, this is how you would read the request body as JSON:
212
215
 
213
216
  ```js
214
- interceptor.on('request', async (request, requestId) => {
217
+ interceptor.on('request', async ({ request, requestId }) => {
215
218
  const json = await request.clone().json()
216
219
  })
217
220
  ```
@@ -223,7 +226,7 @@ interceptor.on('request', async (request, requestId) => {
223
226
  Request representations are readonly. You can, however, mutate the intercepted request's headers in the "request" listener:
224
227
 
225
228
  ```js
226
- interceptor.on('request', (request) => {
229
+ interceptor.on('request', ({ request }) => {
227
230
  request.headers.set('X-My-Header', 'true')
228
231
  })
229
232
  ```
@@ -237,7 +240,7 @@ Although this library can be used purely for request introspection purposes, you
237
240
  Use the `request.respondWith()` method to respond to a request with a mocked response:
238
241
 
239
242
  ```js
240
- interceptor.on('request', (request, requestId) => {
243
+ interceptor.on('request', ({ request, requestId }) => {
241
244
  request.respondWith(
242
245
  new Response(
243
246
  JSON.stringify({
@@ -267,12 +270,27 @@ Requests must be responded to within the same tick as the request listener. This
267
270
  ```js
268
271
  // Respond to all requests with a 500 response
269
272
  // delayed by 500ms.
270
- interceptor.on('request', async (request, requestId) => {
273
+ interceptor.on('request', async ({ request, requestId }) => {
271
274
  await sleep(500)
272
275
  request.respondWith(new Response(null, { status: 500 }))
273
276
  })
274
277
  ```
275
278
 
279
+ ## Observing responses
280
+
281
+ You can use the "response" event to transparently observe any incoming responses in your Node.js process.
282
+
283
+ ```js
284
+ interceptor.on(
285
+ 'response',
286
+ ({ response, isMockedResponse, request, requestId }) => {
287
+ // react to the incoming response...
288
+ }
289
+ )
290
+ ```
291
+
292
+ > Note that the `isMockedResponse` property will only be set to `true` if you resolved this request in the "request" event listener using the `request.respondWith()` method and providing a mocked `Response` instance.
293
+
276
294
  ## API
277
295
 
278
296
  ### `Interceptor`
@@ -312,7 +330,7 @@ const interceptor = new BatchInterceptor({
312
330
 
313
331
  interceptor.apply()
314
332
 
315
- interceptor.on('request', (request, requestId) => {
333
+ interceptor.on('request', ({ request, requestId }) => {
316
334
  // Inspect the intercepted "request".
317
335
  // Optionally, return a mocked response.
318
336
  })
@@ -360,7 +378,7 @@ const resolver = new RemoteHttpResolver({
360
378
  process: appProcess,
361
379
  })
362
380
 
363
- resolver.on('request', (request, requestId) => {
381
+ resolver.on('request', ({ request, requestId }) => {
364
382
  // Optionally, return a mocked response
365
383
  // for a request that occurred in the "appProcess".
366
384
  })
@@ -16,8 +16,20 @@ type InteractiveRequest = globalThis.Request & {
16
16
  declare const IS_PATCHED_MODULE: unique symbol;
17
17
  type RequestCredentials = 'omit' | 'include' | 'same-origin';
18
18
  type HttpRequestEventMap = {
19
- request: [request: InteractiveRequest, requestId: string];
20
- response: [response: Response, request: Request, requestId: string];
19
+ request: [
20
+ args: {
21
+ request: InteractiveRequest;
22
+ requestId: string;
23
+ }
24
+ ];
25
+ response: [
26
+ args: {
27
+ response: Response;
28
+ isMockedResponse: boolean;
29
+ request: Request;
30
+ requestId: string;
31
+ }
32
+ ];
21
33
  };
22
34
 
23
35
  interface QueueItem<Args extends Array<unknown>> {
@@ -5,12 +5,12 @@ var _chunkPCFJD76Xjs = require('./chunk-PCFJD76X.js');
5
5
 
6
6
 
7
7
 
8
- var _chunk7VJMJSIJjs = require('./chunk-7VJMJSIJ.js');
8
+ var _chunk4CFMDU7Zjs = require('./chunk-4CFMDU7Z.js');
9
9
 
10
10
  // src/interceptors/fetch/index.ts
11
11
  var _outvariant = require('outvariant');
12
12
  var _until = require('@open-draft/until');
13
- var _FetchInterceptor = class extends _chunk7VJMJSIJjs.Interceptor {
13
+ var _FetchInterceptor = class extends _chunk4CFMDU7Zjs.Interceptor {
14
14
  constructor() {
15
15
  super(_FetchInterceptor.symbol);
16
16
  }
@@ -20,7 +20,7 @@ var _FetchInterceptor = class extends _chunk7VJMJSIJjs.Interceptor {
20
20
  setup() {
21
21
  const pureFetch = globalThis.fetch;
22
22
  _outvariant.invariant.call(void 0,
23
- !pureFetch[_chunk7VJMJSIJjs.IS_PATCHED_MODULE],
23
+ !pureFetch[_chunk4CFMDU7Zjs.IS_PATCHED_MODULE],
24
24
  'Failed to patch the "fetch" module: already patched.'
25
25
  );
26
26
  globalThis.fetch = async (input, init) => {
@@ -33,12 +33,15 @@ var _FetchInterceptor = class extends _chunk7VJMJSIJjs.Interceptor {
33
33
  'emitting the "request" event for %d listener(s)...',
34
34
  this.emitter.listenerCount("request")
35
35
  );
36
- this.emitter.emit("request", interactiveRequest, requestId);
36
+ this.emitter.emit("request", {
37
+ request: interactiveRequest,
38
+ requestId
39
+ });
37
40
  this.logger.info("awaiting for the mocked response...");
38
41
  const resolverResult = await _until.until.call(void 0, async () => {
39
42
  await this.emitter.untilIdle(
40
43
  "request",
41
- ({ args: [, pendingRequestId] }) => {
44
+ ({ args: [{ requestId: pendingRequestId }] }) => {
42
45
  return pendingRequestId === requestId;
43
46
  }
44
47
  );
@@ -56,13 +59,13 @@ var _FetchInterceptor = class extends _chunk7VJMJSIJjs.Interceptor {
56
59
  const mockedResponse = resolverResult.data;
57
60
  if (mockedResponse && !((_a = request.signal) == null ? void 0 : _a.aborted)) {
58
61
  this.logger.info("received mocked response:", mockedResponse);
59
- const responseCloine = mockedResponse.clone();
60
- this.emitter.emit(
61
- "response",
62
- responseCloine,
63
- interactiveRequest,
62
+ const responseClone = mockedResponse.clone();
63
+ this.emitter.emit("response", {
64
+ response: responseClone,
65
+ isMockedResponse: true,
66
+ request: interactiveRequest,
64
67
  requestId
65
- );
68
+ });
66
69
  const response = new Response(mockedResponse.body, mockedResponse);
67
70
  Object.defineProperty(response, "url", {
68
71
  writable: false,
@@ -76,22 +79,22 @@ var _FetchInterceptor = class extends _chunk7VJMJSIJjs.Interceptor {
76
79
  return pureFetch(request).then((response) => {
77
80
  const responseClone = response.clone();
78
81
  this.logger.info("original fetch performed", responseClone);
79
- this.emitter.emit(
80
- "response",
81
- responseClone,
82
- interactiveRequest,
82
+ this.emitter.emit("response", {
83
+ response: responseClone,
84
+ isMockedResponse: false,
85
+ request: interactiveRequest,
83
86
  requestId
84
- );
87
+ });
85
88
  return response;
86
89
  });
87
90
  };
88
- Object.defineProperty(globalThis.fetch, _chunk7VJMJSIJjs.IS_PATCHED_MODULE, {
91
+ Object.defineProperty(globalThis.fetch, _chunk4CFMDU7Zjs.IS_PATCHED_MODULE, {
89
92
  enumerable: true,
90
93
  configurable: true,
91
94
  value: true
92
95
  });
93
96
  this.subscriptions.push(() => {
94
- Object.defineProperty(globalThis.fetch, _chunk7VJMJSIJjs.IS_PATCHED_MODULE, {
97
+ Object.defineProperty(globalThis.fetch, _chunk4CFMDU7Zjs.IS_PATCHED_MODULE, {
95
98
  value: void 0
96
99
  });
97
100
  globalThis.fetch = pureFetch;
@@ -1,12 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
-
3
- var _chunkMPFSBY4Sjs = require('./chunk-MPFSBY4S.js');
4
-
5
- // src/shims/webEncoding.ts
6
- var TextEncoder = typeof globalThis.TextEncoder === "undefined" ? _chunkMPFSBY4Sjs.__require.call(void 0, "util").TextEncoder : globalThis.TextEncoder;
7
- var TextDecoder = typeof globalThis.TextDecoder === "undefined" ? _chunkMPFSBY4Sjs.__require.call(void 0, "util").TextDecoder : globalThis.TextDecoder;
8
-
9
- // src/utils/bufferUtils.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/utils/bufferUtils.ts
10
2
  var encoder = new TextEncoder();
11
3
  function encodeBuffer(text) {
12
4
  return encoder.encode(text);
@@ -1,10 +1,5 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined")
5
- return require.apply(this, arguments);
6
- throw new Error('Dynamic require of "' + x + '" is not supported');
7
- });
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/glossary.ts
2
+ var IS_PATCHED_MODULE = Symbol("isPatchedModule");
8
3
 
9
4
  // src/Interceptor.ts
10
5
  var _logger = require('@open-draft/logger');
@@ -71,6 +66,11 @@ var AsyncEventEmitter = class extends _stricteventemitter.Emitter {
71
66
  });
72
67
  return super.emit(eventName, ...data);
73
68
  }
69
+ /**
70
+ * Returns a promise that resolves when all the listeners for the given event
71
+ * has been called. Awaits asynchronous listeners.
72
+ * If the event has no listeners, resolves immediately.
73
+ */
74
74
  async untilIdle(eventName, filter = () => true) {
75
75
  const listenersQueue = this.queue.get(eventName) || [];
76
76
  await Promise.all(
@@ -112,6 +112,11 @@ var AsyncEventEmitter = class extends _stricteventemitter.Emitter {
112
112
  this.readyState = "ACTIVE" /* ACTIVE */;
113
113
  logger.info("set state to:", this.readyState);
114
114
  }
115
+ /**
116
+ * Deactivate this event emitter.
117
+ * Deactivated emitter can no longer emit and listen to events
118
+ * and needs to be activated again in order to do so.
119
+ */
115
120
  deactivate() {
116
121
  const logger = this.logger.extend("deactivate");
117
122
  logger.info("removing all listeners...");
@@ -126,7 +131,10 @@ var AsyncEventEmitter = class extends _stricteventemitter.Emitter {
126
131
 
127
132
  // src/Interceptor.ts
128
133
  function getGlobalSymbol(symbol) {
129
- return globalThis[symbol] || void 0;
134
+ return (
135
+ // @ts-ignore https://github.com/Microsoft/TypeScript/issues/24587
136
+ globalThis[symbol] || void 0
137
+ );
130
138
  }
131
139
  function setGlobalSymbol(symbol, value) {
132
140
  globalThis[symbol] = value;
@@ -152,9 +160,17 @@ var Interceptor = class {
152
160
  this.emitter.setMaxListeners(0);
153
161
  this.logger.info("constructing the interceptor...");
154
162
  }
163
+ /**
164
+ * Determine if this interceptor can be applied
165
+ * in the current environment.
166
+ */
155
167
  checkEnvironment() {
156
168
  return true;
157
169
  }
170
+ /**
171
+ * Apply this interceptor to the current process.
172
+ * Returns an already running interceptor instance if it's present.
173
+ */
158
174
  apply() {
159
175
  const logger = this.logger.extend("apply");
160
176
  logger.info("applying the interceptor...");
@@ -189,8 +205,16 @@ var Interceptor = class {
189
205
  this.setInstance();
190
206
  this.readyState = "APPLIED" /* APPLIED */;
191
207
  }
208
+ /**
209
+ * Setup the module augments and stubs necessary for this interceptor.
210
+ * This method is not run if there's a running interceptor instance
211
+ * to prevent instantiating an interceptor multiple times.
212
+ */
192
213
  setup() {
193
214
  }
215
+ /**
216
+ * Listen to the interceptor's public events.
217
+ */
194
218
  on(eventName, listener) {
195
219
  const logger = this.logger.extend("on");
196
220
  if (this.readyState === "DISPOSING" /* DISPOSING */ || this.readyState === "DISPOSED" /* DISPOSED */) {
@@ -200,6 +224,9 @@ var Interceptor = class {
200
224
  logger.info('adding "%s" event listener:', eventName, listener.name);
201
225
  this.emitter.on(eventName, listener);
202
226
  }
227
+ /**
228
+ * Disposes of any side-effects this interceptor has introduced.
229
+ */
203
230
  dispose() {
204
231
  const logger = this.logger.extend("dispose");
205
232
  if (this.readyState === "DISPOSED" /* DISPOSED */) {
@@ -248,4 +275,4 @@ var Interceptor = class {
248
275
 
249
276
 
250
277
 
251
- exports.__require = __require; exports.getGlobalSymbol = getGlobalSymbol; exports.deleteGlobalSymbol = deleteGlobalSymbol; exports.InterceptorReadyState = InterceptorReadyState; exports.Interceptor = Interceptor;
278
+ exports.IS_PATCHED_MODULE = IS_PATCHED_MODULE; exports.getGlobalSymbol = getGlobalSymbol; exports.deleteGlobalSymbol = deleteGlobalSymbol; exports.InterceptorReadyState = InterceptorReadyState; exports.Interceptor = Interceptor;
@@ -1,11 +1,3 @@
1
- import {
2
- __require
3
- } from "./chunk-Y5QA6OEZ.mjs";
4
-
5
- // src/shims/webEncoding.ts
6
- var TextEncoder = typeof globalThis.TextEncoder === "undefined" ? __require("util").TextEncoder : globalThis.TextEncoder;
7
- var TextDecoder = typeof globalThis.TextDecoder === "undefined" ? __require("util").TextDecoder : globalThis.TextDecoder;
8
-
9
1
  // src/utils/bufferUtils.ts
10
2
  var encoder = new TextEncoder();
11
3
  function encodeBuffer(text) {
@@ -2,7 +2,7 @@ import {
2
2
  decodeBuffer,
3
3
  encodeBuffer,
4
4
  toArrayBuffer
5
- } from "./chunk-MQA5WAD4.mjs";
5
+ } from "./chunk-7II4SWKS.mjs";
6
6
  import {
7
7
  toInteractiveRequest,
8
8
  uuidv4
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  IS_PATCHED_MODULE,
12
12
  Interceptor
13
- } from "./chunk-MW6NCDWE.mjs";
13
+ } from "./chunk-GXJLJMOT.mjs";
14
14
 
15
15
  // src/interceptors/XMLHttpRequest/index.ts
16
16
  import { invariant as invariant2 } from "outvariant";
@@ -19,7 +19,8 @@ import { invariant as invariant2 } from "outvariant";
19
19
  import { until } from "@open-draft/until";
20
20
 
21
21
  // src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
22
- import { headersToString } from "headers-polyfill";
22
+ import { invariant } from "outvariant";
23
+ import { isNodeProcess } from "is-node-process";
23
24
 
24
25
  // src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts
25
26
  function concatArrayBuffer(left, right) {
@@ -201,17 +202,32 @@ function parseJson(data) {
201
202
  }
202
203
 
203
204
  // src/interceptors/XMLHttpRequest/utils/createResponse.ts
204
- import { stringToHeaders } from "headers-polyfill";
205
- function createResponse(request, responseBody) {
206
- return new Response(responseBody, {
205
+ function createResponse(request, body) {
206
+ return new Response(body, {
207
207
  status: request.status,
208
208
  statusText: request.statusText,
209
- headers: stringToHeaders(request.getAllResponseHeaders())
209
+ headers: createHeadersFromXMLHttpReqestHeaders(
210
+ request.getAllResponseHeaders()
211
+ )
210
212
  });
211
213
  }
214
+ function createHeadersFromXMLHttpReqestHeaders(headersString) {
215
+ const headers = new Headers();
216
+ const lines = headersString.split(/[\r\n]+/);
217
+ for (const line of lines) {
218
+ if (line.trim() === "") {
219
+ continue;
220
+ }
221
+ const [name, ...parts] = line.split(": ");
222
+ const value = parts.join(": ");
223
+ headers.append(name, value);
224
+ }
225
+ return headers;
226
+ }
212
227
 
213
228
  // src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
214
- import { invariant } from "outvariant";
229
+ var IS_MOCKED_RESPONSE = Symbol("isMockedResponse");
230
+ var IS_NODE = isNodeProcess();
215
231
  var XMLHttpRequestController = class {
216
232
  constructor(initialRequest, logger) {
217
233
  this.initialRequest = initialRequest;
@@ -219,6 +235,7 @@ var XMLHttpRequestController = class {
219
235
  this.method = "GET";
220
236
  this.url = null;
221
237
  this.events = /* @__PURE__ */ new Map();
238
+ this.requestId = uuidv4();
222
239
  this.requestHeaders = new Headers();
223
240
  this.responseBuffer = new Uint8Array();
224
241
  this.request = createProxy(initialRequest, {
@@ -241,7 +258,6 @@ var XMLHttpRequestController = class {
241
258
  switch (methodName) {
242
259
  case "open": {
243
260
  const [method, url] = args;
244
- this.requestId = uuidv4();
245
261
  if (typeof url === "undefined") {
246
262
  this.method = "GET";
247
263
  this.url = toAbsoluteUrl(method);
@@ -274,25 +290,35 @@ var XMLHttpRequestController = class {
274
290
  if (typeof this.onResponse !== "undefined") {
275
291
  const fetchResponse = createResponse(
276
292
  this.request,
293
+ /**
294
+ * The `response` property is the right way to read
295
+ * the ambiguous response body, as the request's "responseType" may differ.
296
+ * @see https://xhr.spec.whatwg.org/#the-response-attribute
297
+ */
277
298
  this.request.response
278
299
  );
279
- this.onResponse.call(
280
- this,
281
- fetchResponse,
282
- fetchRequest,
283
- this.requestId
284
- );
300
+ this.onResponse.call(this, {
301
+ response: fetchResponse,
302
+ isMockedResponse: IS_MOCKED_RESPONSE in this.request,
303
+ request: fetchRequest,
304
+ requestId: this.requestId
305
+ });
285
306
  }
286
307
  });
287
308
  const fetchRequest = this.toFetchApiRequest();
288
- const onceRequestSettled = ((_a = this.onRequest) == null ? void 0 : _a.call(this, fetchRequest, this.requestId)) || Promise.resolve();
309
+ const onceRequestSettled = ((_a = this.onRequest) == null ? void 0 : _a.call(this, {
310
+ request: fetchRequest,
311
+ requestId: this.requestId
312
+ })) || Promise.resolve();
289
313
  onceRequestSettled.finally(() => {
290
314
  if (this.request.readyState < this.request.LOADING) {
291
315
  this.logger.info(
292
316
  "request callback settled but request has not been handled (readystate %d), performing as-is...",
293
317
  this.request.readyState
294
318
  );
295
- this.request.setRequestHeader("X-Request-Id", this.requestId);
319
+ if (IS_NODE) {
320
+ this.request.setRequestHeader("X-Request-Id", this.requestId);
321
+ }
296
322
  return invoke();
297
323
  }
298
324
  });
@@ -311,12 +337,17 @@ var XMLHttpRequestController = class {
311
337
  this.events.set(eventName, nextEvents);
312
338
  this.logger.info('registered event "%s"', eventName, listener.name);
313
339
  }
340
+ /**
341
+ * Responds to the current request with the given
342
+ * Fetch API `Response` instance.
343
+ */
314
344
  respondWith(response) {
315
345
  this.logger.info(
316
346
  "responding with a mocked response: %d %s",
317
347
  response.status,
318
348
  response.statusText
319
349
  );
350
+ define(this.request, IS_MOCKED_RESPONSE, true);
320
351
  define(this.request, "status", response.status);
321
352
  define(this.request, "statusText", response.statusText);
322
353
  define(this.request, "responseURL", this.url.href);
@@ -345,7 +376,10 @@ var XMLHttpRequestController = class {
345
376
  this.logger.info("headers not received yet, returning empty string");
346
377
  return "";
347
378
  }
348
- const allHeaders = headersToString(response.headers);
379
+ const headersList = Array.from(response.headers.entries());
380
+ const allHeaders = headersList.map(([headerName, headerValue]) => {
381
+ return `${headerName}: ${headerValue}`;
382
+ }).join("\r\n");
349
383
  this.logger.info("resolved all response headers to", allHeaders);
350
384
  return allHeaders;
351
385
  }
@@ -368,7 +402,12 @@ var XMLHttpRequestController = class {
368
402
  get: () => this.responseXML
369
403
  }
370
404
  });
371
- const totalResponseBodyLength = response.headers.has("Content-Length") ? Number(response.headers.get("Content-Length")) : void 0;
405
+ const totalResponseBodyLength = response.headers.has("Content-Length") ? Number(response.headers.get("Content-Length")) : (
406
+ /**
407
+ * @todo Infer the response body length from the response body.
408
+ */
409
+ void 0
410
+ );
372
411
  this.logger.info("calculated response body length", totalResponseBodyLength);
373
412
  this.trigger("loadstart", {
374
413
  loaded: 0,
@@ -499,6 +538,9 @@ var XMLHttpRequestController = class {
499
538
  this.trigger("error");
500
539
  this.trigger("loadend");
501
540
  }
541
+ /**
542
+ * Transitions this request's `readyState` to the given one.
543
+ */
502
544
  setReadyState(nextReadyState) {
503
545
  this.logger.info(
504
546
  "setReadyState: %d -> %d",
@@ -516,6 +558,9 @@ var XMLHttpRequestController = class {
516
558
  this.trigger("readystatechange");
517
559
  }
518
560
  }
561
+ /**
562
+ * Triggers given event on the `XMLHttpRequest` instance.
563
+ */
519
564
  trigger(eventName, options) {
520
565
  const callback = this.request[`on${eventName}`];
521
566
  const event = createEvent(this.request, eventName, options);
@@ -535,11 +580,17 @@ var XMLHttpRequestController = class {
535
580
  }
536
581
  }
537
582
  }
583
+ /**
584
+ * Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
585
+ */
538
586
  toFetchApiRequest() {
539
587
  this.logger.info("converting request to a Fetch API Request...");
540
588
  const fetchRequest = new Request(this.url.href, {
541
589
  method: this.method,
542
590
  headers: this.requestHeaders,
591
+ /**
592
+ * @see https://xhr.spec.whatwg.org/#cross-origin-credentials
593
+ */
543
594
  credentials: this.request.withCredentials ? "include" : "same-origin",
544
595
  body: ["GET", "HEAD"].includes(this.method) ? null : this.requestBody
545
596
  });
@@ -573,6 +624,7 @@ function toAbsoluteUrl(url) {
573
624
  }
574
625
  function define(target, property, value) {
575
626
  Reflect.defineProperty(target, property, {
627
+ // Ensure writable properties to allow redefining readonly properties.
576
628
  writable: true,
577
629
  enumerable: true,
578
630
  value
@@ -602,18 +654,21 @@ function createXMLHttpRequestProxy({
602
654
  originalRequest,
603
655
  logger
604
656
  );
605
- requestController.onRequest = async function(request, requestId) {
657
+ requestController.onRequest = async function({ request, requestId }) {
606
658
  const interactiveRequest = toInteractiveRequest(request);
607
659
  this.logger.info(
608
660
  'emitting the "request" event for %s listener(s)...',
609
661
  emitter.listenerCount("request")
610
662
  );
611
- emitter.emit("request", interactiveRequest, requestId);
663
+ emitter.emit("request", {
664
+ request: interactiveRequest,
665
+ requestId
666
+ });
612
667
  this.logger.info("awaiting mocked response...");
613
668
  const resolverResult = await until(async () => {
614
669
  await emitter.untilIdle(
615
670
  "request",
616
- ({ args: [, pendingRequestId] }) => {
671
+ ({ args: [{ requestId: pendingRequestId }] }) => {
617
672
  return pendingRequestId === requestId;
618
673
  }
619
674
  );
@@ -643,12 +698,22 @@ function createXMLHttpRequestProxy({
643
698
  "no mocked response received, performing request as-is..."
644
699
  );
645
700
  };
646
- requestController.onResponse = async function(response, request, requestId) {
701
+ requestController.onResponse = async function({
702
+ response,
703
+ isMockedResponse,
704
+ request,
705
+ requestId
706
+ }) {
647
707
  this.logger.info(
648
708
  'emitting the "response" event for %s listener(s)...',
649
709
  emitter.listenerCount("response")
650
710
  );
651
- emitter.emit("response", response, request, requestId);
711
+ emitter.emit("response", {
712
+ response,
713
+ isMockedResponse,
714
+ request,
715
+ requestId
716
+ });
652
717
  };
653
718
  return requestController.request;
654
719
  }