@mswjs/interceptors 0.32.1 → 0.33.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 (121) hide show
  1. package/README.md +35 -7
  2. package/lib/browser/{chunk-732REFPX.mjs → chunk-5ETVT6GU.mjs} +28 -79
  3. package/lib/browser/chunk-5ETVT6GU.mjs.map +1 -0
  4. package/lib/browser/chunk-6MBJUL74.js +142 -0
  5. package/lib/browser/chunk-6MBJUL74.js.map +1 -0
  6. package/lib/browser/chunk-7A4UJNSW.mjs +196 -0
  7. package/lib/browser/chunk-7A4UJNSW.mjs.map +1 -0
  8. package/lib/browser/{chunk-PSX5J3RF.js → chunk-7GVJEW45.js} +30 -81
  9. package/lib/browser/chunk-7GVJEW45.js.map +1 -0
  10. package/lib/browser/{chunk-2CRB3JAQ.js → chunk-FXSPMSSQ.js} +1 -1
  11. package/lib/browser/chunk-FXSPMSSQ.js.map +1 -0
  12. package/lib/browser/{chunk-OMISYKWR.mjs → chunk-GGUENBDN.mjs} +1 -1
  13. package/lib/browser/chunk-GGUENBDN.mjs.map +1 -0
  14. package/lib/browser/chunk-NU2MPFD6.mjs +142 -0
  15. package/lib/browser/chunk-NU2MPFD6.mjs.map +1 -0
  16. package/lib/browser/chunk-VRKVKT62.js +196 -0
  17. package/lib/browser/chunk-VRKVKT62.js.map +1 -0
  18. package/lib/browser/glossary-7d7adb4b.d.ts +66 -0
  19. package/lib/browser/index.d.ts +1 -1
  20. package/lib/browser/index.js +2 -2
  21. package/lib/browser/index.mjs +1 -1
  22. package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +2 -6
  23. package/lib/browser/interceptors/XMLHttpRequest/index.js +4 -4
  24. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +3 -3
  25. package/lib/browser/interceptors/fetch/index.d.ts +1 -1
  26. package/lib/browser/interceptors/fetch/index.js +4 -4
  27. package/lib/browser/interceptors/fetch/index.mjs +3 -3
  28. package/lib/browser/presets/browser.d.ts +1 -1
  29. package/lib/browser/presets/browser.js +6 -6
  30. package/lib/browser/presets/browser.mjs +4 -4
  31. package/lib/node/{BatchInterceptor-2badedde.d.ts → BatchInterceptor-13d40c95.d.ts} +1 -1
  32. package/lib/node/{Interceptor-88ee47c0.d.ts → Interceptor-a31b1217.d.ts} +35 -13
  33. package/lib/node/RemoteHttpInterceptor.d.ts +2 -2
  34. package/lib/node/RemoteHttpInterceptor.js +55 -52
  35. package/lib/node/RemoteHttpInterceptor.js.map +1 -1
  36. package/lib/node/RemoteHttpInterceptor.mjs +53 -50
  37. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -1
  38. package/lib/node/{chunk-5JMJ55U7.js → chunk-2MWIWEWV.js} +144 -113
  39. package/lib/node/chunk-2MWIWEWV.js.map +1 -0
  40. package/lib/node/{chunk-2COJKQQB.js → chunk-42632LKH.js} +3 -3
  41. package/lib/node/chunk-5WWNCLB3.js +196 -0
  42. package/lib/node/chunk-5WWNCLB3.js.map +1 -0
  43. package/lib/node/{chunk-TGTPXCLF.mjs → chunk-BUCULLYM.mjs} +1 -1
  44. package/lib/node/{chunk-TGTPXCLF.mjs.map → chunk-BUCULLYM.mjs.map} +1 -1
  45. package/lib/node/{chunk-OJ6O4LSC.mjs → chunk-BZ3Y7YV5.mjs} +1 -1
  46. package/lib/node/chunk-BZ3Y7YV5.mjs.map +1 -0
  47. package/lib/node/{chunk-3OJLYEWA.mjs → chunk-CU3YXMM4.mjs} +138 -107
  48. package/lib/node/chunk-CU3YXMM4.mjs.map +1 -0
  49. package/lib/node/{chunk-PNWPIDEL.mjs → chunk-HGQLG7KE.mjs} +2 -2
  50. package/lib/node/{chunk-EIBTX65O.js → chunk-IDEEMJ3F.js} +1 -1
  51. package/lib/node/chunk-IDEEMJ3F.js.map +1 -0
  52. package/lib/node/chunk-KY3RJ2M3.mjs +196 -0
  53. package/lib/node/chunk-KY3RJ2M3.mjs.map +1 -0
  54. package/lib/node/{chunk-PYD4E2EJ.js → chunk-P6QG76R3.js} +34 -85
  55. package/lib/node/chunk-P6QG76R3.js.map +1 -0
  56. package/lib/node/{chunk-DV4PBH4D.mjs → chunk-TOV4TYIX.mjs} +29 -80
  57. package/lib/node/chunk-TOV4TYIX.mjs.map +1 -0
  58. package/lib/node/{chunk-BFLYGQ6D.js → chunk-YGM3BCJU.js} +1 -1
  59. package/lib/node/chunk-YGM3BCJU.js.map +1 -0
  60. package/lib/node/index.d.ts +2 -2
  61. package/lib/node/index.js +4 -4
  62. package/lib/node/index.mjs +3 -3
  63. package/lib/node/interceptors/ClientRequest/index.d.ts +2 -2
  64. package/lib/node/interceptors/ClientRequest/index.js +4 -4
  65. package/lib/node/interceptors/ClientRequest/index.mjs +3 -3
  66. package/lib/node/interceptors/XMLHttpRequest/index.d.ts +2 -6
  67. package/lib/node/interceptors/XMLHttpRequest/index.js +5 -5
  68. package/lib/node/interceptors/XMLHttpRequest/index.mjs +4 -4
  69. package/lib/node/interceptors/fetch/index.d.ts +1 -1
  70. package/lib/node/interceptors/fetch/index.js +52 -123
  71. package/lib/node/interceptors/fetch/index.js.map +1 -1
  72. package/lib/node/interceptors/fetch/index.mjs +50 -121
  73. package/lib/node/interceptors/fetch/index.mjs.map +1 -1
  74. package/lib/node/presets/node.d.ts +1 -1
  75. package/lib/node/presets/node.js +7 -7
  76. package/lib/node/presets/node.mjs +5 -5
  77. package/package.json +2 -2
  78. package/src/InterceptorError.ts +7 -0
  79. package/src/RemoteHttpInterceptor.ts +62 -57
  80. package/src/RequestController.test.ts +49 -0
  81. package/src/RequestController.ts +81 -0
  82. package/src/glossary.ts +4 -6
  83. package/src/interceptors/ClientRequest/MockHttpSocket.ts +22 -16
  84. package/src/interceptors/ClientRequest/index.test.ts +2 -33
  85. package/src/interceptors/ClientRequest/index.ts +32 -82
  86. package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +170 -0
  87. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +1 -1
  88. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +27 -108
  89. package/src/interceptors/XMLHttpRequest/index.ts +0 -6
  90. package/src/interceptors/fetch/index.ts +52 -169
  91. package/src/utils/handleRequest.ts +213 -0
  92. package/src/utils/responseUtils.ts +4 -4
  93. package/lib/browser/chunk-2CRB3JAQ.js.map +0 -1
  94. package/lib/browser/chunk-732REFPX.mjs.map +0 -1
  95. package/lib/browser/chunk-MAEPOYB6.mjs +0 -213
  96. package/lib/browser/chunk-MAEPOYB6.mjs.map +0 -1
  97. package/lib/browser/chunk-MQJ3JOOK.js +0 -49
  98. package/lib/browser/chunk-MQJ3JOOK.js.map +0 -1
  99. package/lib/browser/chunk-OMISYKWR.mjs.map +0 -1
  100. package/lib/browser/chunk-OUWBQF3Z.mjs +0 -49
  101. package/lib/browser/chunk-OUWBQF3Z.mjs.map +0 -1
  102. package/lib/browser/chunk-PSX5J3RF.js.map +0 -1
  103. package/lib/browser/chunk-WBHIW62P.js +0 -213
  104. package/lib/browser/chunk-WBHIW62P.js.map +0 -1
  105. package/lib/browser/glossary-1c204f45.d.ts +0 -44
  106. package/lib/node/chunk-3OJLYEWA.mjs.map +0 -1
  107. package/lib/node/chunk-5JMJ55U7.js.map +0 -1
  108. package/lib/node/chunk-BFLYGQ6D.js.map +0 -1
  109. package/lib/node/chunk-DV4PBH4D.mjs.map +0 -1
  110. package/lib/node/chunk-EIBTX65O.js.map +0 -1
  111. package/lib/node/chunk-KWV3JXSI.mjs +0 -49
  112. package/lib/node/chunk-KWV3JXSI.mjs.map +0 -1
  113. package/lib/node/chunk-OJ6O4LSC.mjs.map +0 -1
  114. package/lib/node/chunk-PYD4E2EJ.js.map +0 -1
  115. package/lib/node/chunk-UXCYRE4F.js +0 -49
  116. package/lib/node/chunk-UXCYRE4F.js.map +0 -1
  117. package/src/utils/getRawFetchHeaders.test.ts +0 -50
  118. package/src/utils/getRawFetchHeaders.ts +0 -56
  119. package/src/utils/toInteractiveRequest.ts +0 -23
  120. /package/lib/node/{chunk-2COJKQQB.js.map → chunk-42632LKH.js.map} +0 -0
  121. /package/lib/node/{chunk-PNWPIDEL.mjs.map → chunk-HGQLG7KE.mjs.map} +0 -0
@@ -0,0 +1,196 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+ var _chunkYGM3BCJUjs = require('./chunk-YGM3BCJU.js');
5
+
6
+ // src/RequestController.ts
7
+ var _outvariant = require('outvariant');
8
+ var _deferredpromise = require('@open-draft/deferred-promise');
9
+
10
+ // src/InterceptorError.ts
11
+ var InterceptorError = class extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "InterceptorError";
15
+ Object.setPrototypeOf(this, InterceptorError.prototype);
16
+ }
17
+ };
18
+
19
+ // src/RequestController.ts
20
+ var kRequestHandled = Symbol("kRequestHandled");
21
+ var kResponsePromise = Symbol("kResponsePromise");
22
+ var RequestController = class {
23
+ constructor(request) {
24
+ this.request = request;
25
+ this[kRequestHandled] = false;
26
+ this[kResponsePromise] = new (0, _deferredpromise.DeferredPromise)();
27
+ }
28
+ /**
29
+ * Respond to this request with the given `Response` instance.
30
+ * @example
31
+ * controller.respondWith(new Response())
32
+ * controller.respondWith(Response.json({ id }))
33
+ * controller.respondWith(Response.error())
34
+ */
35
+ respondWith(response) {
36
+ _outvariant.invariant.as(
37
+ InterceptorError,
38
+ !this[kRequestHandled],
39
+ 'Failed to respond to the "%s %s" request: the "request" event has already been handled.',
40
+ this.request.method,
41
+ this.request.url
42
+ );
43
+ this[kRequestHandled] = true;
44
+ this[kResponsePromise].resolve(response);
45
+ }
46
+ /**
47
+ * Error this request with the given error.
48
+ * @example
49
+ * controller.errorWith()
50
+ * controller.errorWith(new Error('Oops!'))
51
+ */
52
+ errorWith(error) {
53
+ _outvariant.invariant.as(
54
+ InterceptorError,
55
+ !this[kRequestHandled],
56
+ 'Failed to error the "%s %s" request: the "request" event has already been handled.',
57
+ this.request.method,
58
+ this.request.url
59
+ );
60
+ this[kRequestHandled] = true;
61
+ this[kResponsePromise].resolve(error);
62
+ }
63
+ };
64
+ kResponsePromise, kRequestHandled;
65
+
66
+ // src/utils/emitAsync.ts
67
+ async function emitAsync(emitter, eventName, ...data) {
68
+ const listners = emitter.listeners(eventName);
69
+ if (listners.length === 0) {
70
+ return;
71
+ }
72
+ for (const listener of listners) {
73
+ await listener.apply(emitter, data);
74
+ }
75
+ }
76
+
77
+ // src/utils/handleRequest.ts
78
+
79
+ var _until = require('@open-draft/until');
80
+
81
+ // src/utils/isNodeLikeError.ts
82
+ function isNodeLikeError(error) {
83
+ if (error == null) {
84
+ return false;
85
+ }
86
+ if (!(error instanceof Error)) {
87
+ return false;
88
+ }
89
+ return "code" in error && "errno" in error;
90
+ }
91
+
92
+ // src/utils/handleRequest.ts
93
+ async function handleRequest(options) {
94
+ const handleResponse = (response) => {
95
+ if (response instanceof Error) {
96
+ options.onError(response);
97
+ } else if (_chunkYGM3BCJUjs.isResponseError.call(void 0, response)) {
98
+ options.onRequestError(response);
99
+ } else {
100
+ options.onResponse(response);
101
+ }
102
+ return true;
103
+ };
104
+ const handleResponseError = (error) => {
105
+ if (error instanceof InterceptorError) {
106
+ throw result.error;
107
+ }
108
+ if (isNodeLikeError(error)) {
109
+ options.onError(error);
110
+ return true;
111
+ }
112
+ if (error instanceof Response) {
113
+ return handleResponse(error);
114
+ }
115
+ return false;
116
+ };
117
+ options.emitter.once("request", ({ requestId: pendingRequestId }) => {
118
+ if (pendingRequestId !== options.requestId) {
119
+ return;
120
+ }
121
+ if (options.controller[kResponsePromise].state === "pending") {
122
+ options.controller[kResponsePromise].resolve(void 0);
123
+ }
124
+ });
125
+ const requestAbortPromise = new (0, _deferredpromise.DeferredPromise)();
126
+ if (options.request.signal) {
127
+ options.request.signal.addEventListener(
128
+ "abort",
129
+ () => {
130
+ requestAbortPromise.reject(options.request.signal.reason);
131
+ },
132
+ { once: true }
133
+ );
134
+ }
135
+ const result = await _until.until.call(void 0, async () => {
136
+ const requestListtenersPromise = emitAsync(options.emitter, "request", {
137
+ requestId: options.requestId,
138
+ request: options.request,
139
+ controller: options.controller
140
+ });
141
+ await Promise.race([
142
+ // Short-circuit the request handling promise if the request gets aborted.
143
+ requestAbortPromise,
144
+ requestListtenersPromise,
145
+ options.controller[kResponsePromise]
146
+ ]);
147
+ const mockedResponse = await options.controller[kResponsePromise];
148
+ return mockedResponse;
149
+ });
150
+ if (requestAbortPromise.state === "rejected") {
151
+ options.onError(requestAbortPromise.rejectionReason);
152
+ return true;
153
+ }
154
+ if (result.error) {
155
+ if (handleResponseError(result.error)) {
156
+ return true;
157
+ }
158
+ if (options.emitter.listenerCount("unhandledException") > 0) {
159
+ const unhandledExceptionController = new RequestController(
160
+ options.request
161
+ );
162
+ await emitAsync(options.emitter, "unhandledException", {
163
+ error: result.error,
164
+ request: options.request,
165
+ requestId: options.requestId,
166
+ controller: unhandledExceptionController
167
+ }).then(() => {
168
+ if (unhandledExceptionController[kResponsePromise].state === "pending") {
169
+ unhandledExceptionController[kResponsePromise].resolve(void 0);
170
+ }
171
+ });
172
+ const nextResult = await _until.until.call(void 0,
173
+ () => unhandledExceptionController[kResponsePromise]
174
+ );
175
+ if (nextResult.error) {
176
+ return handleResponseError(nextResult.error);
177
+ }
178
+ if (nextResult.data) {
179
+ return handleResponse(nextResult.data);
180
+ }
181
+ }
182
+ options.onResponse(_chunkYGM3BCJUjs.createServerErrorResponse.call(void 0, result.error));
183
+ return true;
184
+ }
185
+ if (result.data) {
186
+ return handleResponse(result.data);
187
+ }
188
+ return false;
189
+ }
190
+
191
+
192
+
193
+
194
+
195
+ exports.RequestController = RequestController; exports.emitAsync = emitAsync; exports.handleRequest = handleRequest;
196
+ //# sourceMappingURL=chunk-5WWNCLB3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/RequestController.ts","../../src/InterceptorError.ts","../../src/utils/emitAsync.ts","../../src/utils/handleRequest.ts","../../src/utils/isNodeLikeError.ts"],"names":["DeferredPromise"],"mappings":"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","sourcesContent":["import { invariant } from 'outvariant'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { InterceptorError } from './InterceptorError'\n\nconst kRequestHandled = Symbol('kRequestHandled')\nexport const kResponsePromise = Symbol('kResponsePromise')\n\nexport class RequestController {\n /**\n * Internal response promise.\n * Available only for the library internals to grab the\n * response instance provided by the developer.\n * @note This promise cannot be rejected. It's either infinitely\n * pending or resolved with whichever Response was passed to `respondWith()`.\n */\n [kResponsePromise]: DeferredPromise<Response | Error | undefined>;\n\n /**\n * Internal flag indicating if this request has been handled.\n * @note The response promise becomes \"fulfilled\" on the next tick.\n */\n [kRequestHandled]: boolean\n\n constructor(private request: Request) {\n this[kRequestHandled] = false\n this[kResponsePromise] = new DeferredPromise()\n }\n\n /**\n * Respond to this request with the given `Response` instance.\n * @example\n * controller.respondWith(new Response())\n * controller.respondWith(Response.json({ id }))\n * controller.respondWith(Response.error())\n */\n public respondWith(response: Response): void {\n invariant.as(\n InterceptorError,\n !this[kRequestHandled],\n 'Failed to respond to the \"%s %s\" request: the \"request\" event has already been handled.',\n this.request.method,\n this.request.url\n )\n\n this[kRequestHandled] = true\n this[kResponsePromise].resolve(response)\n\n /**\n * @note The request conrtoller doesn't do anything\n * apart from letting the interceptor await the response\n * provided by the developer through the response promise.\n * Each interceptor implements the actual respondWith/errorWith\n * logic based on that interceptor's needs.\n */\n }\n\n /**\n * Error this request with the given error.\n * @example\n * controller.errorWith()\n * controller.errorWith(new Error('Oops!'))\n */\n public errorWith(error?: Error): void {\n invariant.as(\n InterceptorError,\n !this[kRequestHandled],\n 'Failed to error the \"%s %s\" request: the \"request\" event has already been handled.',\n this.request.method,\n this.request.url\n )\n\n this[kRequestHandled] = true\n\n /**\n * @note Resolve the response promise, not reject.\n * This helps us differentiate between unhandled exceptions\n * and intended errors (\"errorWith\") while waiting for the response.\n */\n this[kResponsePromise].resolve(error)\n }\n}\n","export class InterceptorError extends Error {\n constructor(message?: string) {\n super(message)\n this.name = 'InterceptorError'\n Object.setPrototypeOf(this, InterceptorError.prototype)\n }\n}\n","import { Emitter, EventMap } from 'strict-event-emitter'\n\n/**\n * Emits an event on the given emitter but executes\n * the listeners sequentially. This accounts for asynchronous\n * listeners (e.g. those having \"sleep\" and handling the request).\n */\nexport async function emitAsync<\n Events extends EventMap,\n EventName extends keyof Events\n>(\n emitter: Emitter<Events>,\n eventName: EventName,\n ...data: Events[EventName]\n): Promise<void> {\n const listners = emitter.listeners(eventName)\n\n if (listners.length === 0) {\n return\n }\n\n for (const listener of listners) {\n await listener.apply(emitter, data)\n }\n}\n","import type { Emitter } from 'strict-event-emitter'\nimport { DeferredPromise } from '@open-draft/deferred-promise'\nimport { until } from '@open-draft/until'\nimport type { HttpRequestEventMap } from '../glossary'\nimport { emitAsync } from './emitAsync'\nimport { kResponsePromise, RequestController } from '../RequestController'\nimport {\n createServerErrorResponse,\n isResponseError,\n ResponseError,\n} from './responseUtils'\nimport { InterceptorError } from '../InterceptorError'\nimport { isNodeLikeError } from './isNodeLikeError'\n\ninterface HandleRequestOptions {\n requestId: string\n request: Request\n emitter: Emitter<HttpRequestEventMap>\n controller: RequestController\n\n /**\n * Called when the request has been handled\n * with the given `Response` instance.\n */\n onResponse: (response: Response) => void\n\n /**\n * Called when the request has been handled\n * with the given `Response.error()` instance.\n */\n onRequestError: (response: ResponseError) => void\n\n /**\n * Called when an unhandled error happens during the\n * request handling. This is never a thrown error/response.\n */\n onError: (error: unknown) => void\n}\n\n/**\n * @returns {Promise<boolean>} Indicates whether the request has been handled.\n */\nexport async function handleRequest(\n options: HandleRequestOptions\n): Promise<boolean> {\n const handleResponse = (response: Response | Error): true => {\n if (response instanceof Error) {\n options.onError(response)\n }\n\n // Handle \"Response.error()\" instances.\n else if (isResponseError(response)) {\n options.onRequestError(response)\n } else {\n options.onResponse(response)\n }\n\n return true\n }\n\n const handleResponseError = (error: unknown): boolean => {\n // Forward the special interceptor error instances\n // to the developer. These must not be handled in any way.\n if (error instanceof InterceptorError) {\n throw result.error\n }\n\n // Support mocking Node.js-like errors.\n if (isNodeLikeError(error)) {\n options.onError(error)\n return true\n }\n\n // Handle thrown responses.\n if (error instanceof Response) {\n return handleResponse(error)\n }\n\n return false\n }\n\n // Add the last \"request\" listener to check if the request\n // has been handled in any way. If it hasn't, resolve the\n // response promise with undefined.\n options.emitter.once('request', ({ requestId: pendingRequestId }) => {\n if (pendingRequestId !== options.requestId) {\n return\n }\n\n if (options.controller[kResponsePromise].state === 'pending') {\n options.controller[kResponsePromise].resolve(undefined)\n }\n })\n\n const requestAbortPromise = new DeferredPromise<void, unknown>()\n\n /**\n * @note `signal` is not always defined in React Native.\n */\n if (options.request.signal) {\n options.request.signal.addEventListener(\n 'abort',\n () => {\n requestAbortPromise.reject(options.request.signal.reason)\n },\n { once: true }\n )\n }\n\n const result = await until(async () => {\n // Emit the \"request\" event and wait until all the listeners\n // for that event are finished (e.g. async listeners awaited).\n // By the end of this promise, the developer cannot affect the\n // request anymore.\n const requestListtenersPromise = emitAsync(options.emitter, 'request', {\n requestId: options.requestId,\n request: options.request,\n controller: options.controller,\n })\n\n await Promise.race([\n // Short-circuit the request handling promise if the request gets aborted.\n requestAbortPromise,\n requestListtenersPromise,\n options.controller[kResponsePromise],\n ])\n\n // The response promise will settle immediately once\n // the developer calls either \"respondWith\" or \"errorWith\".\n const mockedResponse = await options.controller[kResponsePromise]\n return mockedResponse\n })\n\n // Handle the request being aborted while waiting for the request listeners.\n if (requestAbortPromise.state === 'rejected') {\n options.onError(requestAbortPromise.rejectionReason)\n return true\n }\n\n if (result.error) {\n // Handle the error during the request listener execution.\n // These can be thrown responses or request errors.\n if (handleResponseError(result.error)) {\n return true\n }\n\n // If the developer has added \"unhandledException\" listeners,\n // allow them to handle the error. They can translate it to a\n // mocked response, network error, or forward it as-is.\n if (options.emitter.listenerCount('unhandledException') > 0) {\n // Create a new request controller just for the unhandled exception case.\n // This is needed because the original controller might have been already\n // interacted with (e.g. \"respondWith\" or \"errorWith\" called on it).\n const unhandledExceptionController = new RequestController(\n options.request\n )\n\n await emitAsync(options.emitter, 'unhandledException', {\n error: result.error,\n request: options.request,\n requestId: options.requestId,\n controller: unhandledExceptionController,\n }).then(() => {\n // If all the \"unhandledException\" listeners have finished\n // but have not handled the response in any way, preemptively\n // resolve the pending response promise from the new controller.\n // This prevents it from hanging forever.\n if (\n unhandledExceptionController[kResponsePromise].state === 'pending'\n ) {\n unhandledExceptionController[kResponsePromise].resolve(undefined)\n }\n })\n\n const nextResult = await until(\n () => unhandledExceptionController[kResponsePromise]\n )\n\n /**\n * @note Handle the result of the unhandled controller\n * in the same way as the original request controller.\n * The exception here is that thrown errors within the\n * \"unhandledException\" event do NOT result in another\n * emit of the same event. They are forwarded as-is.\n */\n if (nextResult.error) {\n return handleResponseError(nextResult.error)\n }\n\n if (nextResult.data) {\n return handleResponse(nextResult.data)\n }\n }\n\n // Otherwise, coerce unhandled exceptions to a 500 Internal Server Error response.\n options.onResponse(createServerErrorResponse(result.error))\n return true\n }\n\n /**\n * Handle a mocked Response instance.\n * @note That this can also be an Error in case\n * the developer called \"errorWith\". This differentiates\n * unhandled exceptions from intended errors.\n */\n if (result.data) {\n return handleResponse(result.data)\n }\n\n // In all other cases, consider the request unhandled.\n // The interceptor must perform it as-is.\n return false\n}\n","export function isNodeLikeError(\n error: unknown\n): error is NodeJS.ErrnoException {\n if (error == null) {\n return false\n }\n\n if (!(error instanceof Error)) {\n return false\n }\n\n return 'code' in error && 'errno' in error\n}\n"]}
@@ -214,4 +214,4 @@ export {
214
214
  createServerErrorResponse,
215
215
  isResponseError
216
216
  };
217
- //# sourceMappingURL=chunk-TGTPXCLF.mjs.map
217
+ //# sourceMappingURL=chunk-BUCULLYM.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Interceptor.ts","../../src/createRequestId.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts"],"sourcesContent":["import { Logger } from '@open-draft/logger'\nimport { Emitter, Listener } from 'strict-event-emitter'\n\nexport type InterceptorEventMap = Record<string, any>\nexport type InterceptorSubscription = () => void\n\n/**\n * Request header name to detect when a single request\n * is being handled by nested interceptors (XHR -> ClientRequest).\n * Obscure by design to prevent collisions with user-defined headers.\n * Ideally, come up with the Interceptor-level mechanism for this.\n * @see https://github.com/mswjs/interceptors/issues/378\n */\nexport const INTERNAL_REQUEST_ID_HEADER_NAME =\n 'x-interceptors-internal-request-id'\n\nexport function getGlobalSymbol<V>(symbol: Symbol): V | undefined {\n return (\n // @ts-ignore https://github.com/Microsoft/TypeScript/issues/24587\n globalThis[symbol] || undefined\n )\n}\n\nfunction setGlobalSymbol(symbol: Symbol, value: any): void {\n // @ts-ignore\n globalThis[symbol] = value\n}\n\nexport function deleteGlobalSymbol(symbol: Symbol): void {\n // @ts-ignore\n delete globalThis[symbol]\n}\n\nexport enum InterceptorReadyState {\n INACTIVE = 'INACTIVE',\n APPLYING = 'APPLYING',\n APPLIED = 'APPLIED',\n DISPOSING = 'DISPOSING',\n DISPOSED = 'DISPOSED',\n}\n\nexport type ExtractEventNames<Events extends Record<string, any>> =\n Events extends Record<infer EventName, any> ? EventName : never\n\nexport class Interceptor<Events extends InterceptorEventMap> {\n protected emitter: Emitter<Events>\n protected subscriptions: Array<InterceptorSubscription>\n protected logger: Logger\n\n public readyState: InterceptorReadyState\n\n constructor(private readonly symbol: symbol) {\n this.readyState = InterceptorReadyState.INACTIVE\n\n this.emitter = new Emitter()\n this.subscriptions = []\n this.logger = new Logger(symbol.description!)\n\n // Do not limit the maximum number of listeners\n // so not to limit the maximum amount of parallel events emitted.\n this.emitter.setMaxListeners(0)\n\n this.logger.info('constructing the interceptor...')\n }\n\n /**\n * Determine if this interceptor can be applied\n * in the current environment.\n */\n protected checkEnvironment(): boolean {\n return true\n }\n\n /**\n * Apply this interceptor to the current process.\n * Returns an already running interceptor instance if it's present.\n */\n public apply(): void {\n const logger = this.logger.extend('apply')\n logger.info('applying the interceptor...')\n\n if (this.readyState === InterceptorReadyState.APPLIED) {\n logger.info('intercepted already applied!')\n return\n }\n\n const shouldApply = this.checkEnvironment()\n\n if (!shouldApply) {\n logger.info('the interceptor cannot be applied in this environment!')\n return\n }\n\n this.readyState = InterceptorReadyState.APPLYING\n\n // Whenever applying a new interceptor, check if it hasn't been applied already.\n // This enables to apply the same interceptor multiple times, for example from a different\n // interceptor, only proxying events but keeping the stubs in a single place.\n const runningInstance = this.getInstance()\n\n if (runningInstance) {\n logger.info('found a running instance, reusing...')\n\n // Proxy any listeners you set on this instance to the running instance.\n this.on = (event, listener) => {\n logger.info('proxying the \"%s\" listener', event)\n\n // Add listeners to the running instance so they appear\n // at the top of the event listeners list and are executed first.\n runningInstance.emitter.addListener(event, listener)\n\n // Ensure that once this interceptor instance is disposed,\n // it removes all listeners it has appended to the running interceptor instance.\n this.subscriptions.push(() => {\n runningInstance.emitter.removeListener(event, listener)\n logger.info('removed proxied \"%s\" listener!', event)\n })\n\n return this\n }\n\n this.readyState = InterceptorReadyState.APPLIED\n\n return\n }\n\n logger.info('no running instance found, setting up a new instance...')\n\n // Setup the interceptor.\n this.setup()\n\n // Store the newly applied interceptor instance globally.\n this.setInstance()\n\n this.readyState = InterceptorReadyState.APPLIED\n }\n\n /**\n * Setup the module augments and stubs necessary for this interceptor.\n * This method is not run if there's a running interceptor instance\n * to prevent instantiating an interceptor multiple times.\n */\n protected setup(): void {}\n\n /**\n * Listen to the interceptor's public events.\n */\n public on<EventName extends ExtractEventNames<Events>>(\n event: EventName,\n listener: Listener<Events[EventName]>\n ): this {\n const logger = this.logger.extend('on')\n\n if (\n this.readyState === InterceptorReadyState.DISPOSING ||\n this.readyState === InterceptorReadyState.DISPOSED\n ) {\n logger.info('cannot listen to events, already disposed!')\n return this\n }\n\n logger.info('adding \"%s\" event listener:', event, listener)\n\n this.emitter.on(event, listener)\n return this\n }\n\n public once<EventName extends ExtractEventNames<Events>>(\n event: EventName,\n listener: Listener<Events[EventName]>\n ): this {\n this.emitter.once(event, listener)\n return this\n }\n\n public off<EventName extends ExtractEventNames<Events>>(\n event: EventName,\n listener: Listener<Events[EventName]>\n ): this {\n this.emitter.off(event, listener)\n return this\n }\n\n public removeAllListeners<EventName extends ExtractEventNames<Events>>(\n event?: EventName\n ): this {\n this.emitter.removeAllListeners(event)\n return this\n }\n\n /**\n * Disposes of any side-effects this interceptor has introduced.\n */\n public dispose(): void {\n const logger = this.logger.extend('dispose')\n\n if (this.readyState === InterceptorReadyState.DISPOSED) {\n logger.info('cannot dispose, already disposed!')\n return\n }\n\n logger.info('disposing the interceptor...')\n this.readyState = InterceptorReadyState.DISPOSING\n\n if (!this.getInstance()) {\n logger.info('no interceptors running, skipping dispose...')\n return\n }\n\n // Delete the global symbol as soon as possible,\n // indicating that the interceptor is no longer running.\n this.clearInstance()\n\n logger.info('global symbol deleted:', getGlobalSymbol(this.symbol))\n\n if (this.subscriptions.length > 0) {\n logger.info('disposing of %d subscriptions...', this.subscriptions.length)\n\n for (const dispose of this.subscriptions) {\n dispose()\n }\n\n this.subscriptions = []\n\n logger.info('disposed of all subscriptions!', this.subscriptions.length)\n }\n\n this.emitter.removeAllListeners()\n logger.info('destroyed the listener!')\n\n this.readyState = InterceptorReadyState.DISPOSED\n }\n\n private getInstance(): this | undefined {\n const instance = getGlobalSymbol<this>(this.symbol)\n this.logger.info('retrieved global instance:', instance?.constructor?.name)\n return instance\n }\n\n private setInstance(): void {\n setGlobalSymbol(this.symbol, this)\n this.logger.info('set global instance!', this.symbol.description)\n }\n\n private clearInstance(): void {\n deleteGlobalSymbol(this.symbol)\n this.logger.info('cleared global instance!', this.symbol.description)\n }\n}\n","/**\n * Generate a random ID string to represent a request.\n * @example\n * createRequestId()\n * // \"f774b6c9c600f\"\n */\nexport function createRequestId(): string {\n return Math.random().toString(16).slice(2)\n}\n","/**\n * A function that validates if property access is possible on an object\n * without throwing. It returns `true` if the property access is possible\n * and `false` otherwise.\n *\n * Environments like miniflare will throw on property access on certain objects\n * like Request and Response, for unimplemented properties.\n */\nexport function isPropertyAccessible<Obj extends Record<string, any>>(\n obj: Obj,\n key: keyof Obj\n) {\n try {\n obj[key]\n return true\n } catch {\n return false\n }\n}\n","import { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Response status codes for responses that cannot have body.\n * @see https://fetch.spec.whatwg.org/#statuses\n */\nexport const RESPONSE_STATUS_CODES_WITHOUT_BODY = new Set([\n 101, 103, 204, 205, 304,\n])\n\n/**\n * Returns a boolean indicating whether the given response status\n * code represents a response that cannot have a body.\n */\nexport function isResponseWithoutBody(status: number): boolean {\n return RESPONSE_STATUS_CODES_WITHOUT_BODY.has(status)\n}\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n return new Response(\n JSON.stringify(\n body instanceof Error\n ? {\n name: body.name,\n message: body.message,\n stack: body.stack,\n }\n : body\n ),\n {\n status: 500,\n statusText: 'Unhandled Exception',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n}\n\n/**\n * Checks if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(\n response: Response\n): response is Response & { type: 'error' } {\n return isPropertyAccessible(response, 'type') && response.type === 'error'\n}\n"],"mappings":"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","names":["InterceptorReadyState"]}
1
+ {"version":3,"sources":["../../src/Interceptor.ts","../../src/createRequestId.ts","../../src/utils/isPropertyAccessible.ts","../../src/utils/responseUtils.ts"],"sourcesContent":["import { Logger } from '@open-draft/logger'\nimport { Emitter, Listener } from 'strict-event-emitter'\n\nexport type InterceptorEventMap = Record<string, any>\nexport type InterceptorSubscription = () => void\n\n/**\n * Request header name to detect when a single request\n * is being handled by nested interceptors (XHR -> ClientRequest).\n * Obscure by design to prevent collisions with user-defined headers.\n * Ideally, come up with the Interceptor-level mechanism for this.\n * @see https://github.com/mswjs/interceptors/issues/378\n */\nexport const INTERNAL_REQUEST_ID_HEADER_NAME =\n 'x-interceptors-internal-request-id'\n\nexport function getGlobalSymbol<V>(symbol: Symbol): V | undefined {\n return (\n // @ts-ignore https://github.com/Microsoft/TypeScript/issues/24587\n globalThis[symbol] || undefined\n )\n}\n\nfunction setGlobalSymbol(symbol: Symbol, value: any): void {\n // @ts-ignore\n globalThis[symbol] = value\n}\n\nexport function deleteGlobalSymbol(symbol: Symbol): void {\n // @ts-ignore\n delete globalThis[symbol]\n}\n\nexport enum InterceptorReadyState {\n INACTIVE = 'INACTIVE',\n APPLYING = 'APPLYING',\n APPLIED = 'APPLIED',\n DISPOSING = 'DISPOSING',\n DISPOSED = 'DISPOSED',\n}\n\nexport type ExtractEventNames<Events extends Record<string, any>> =\n Events extends Record<infer EventName, any> ? EventName : never\n\nexport class Interceptor<Events extends InterceptorEventMap> {\n protected emitter: Emitter<Events>\n protected subscriptions: Array<InterceptorSubscription>\n protected logger: Logger\n\n public readyState: InterceptorReadyState\n\n constructor(private readonly symbol: symbol) {\n this.readyState = InterceptorReadyState.INACTIVE\n\n this.emitter = new Emitter()\n this.subscriptions = []\n this.logger = new Logger(symbol.description!)\n\n // Do not limit the maximum number of listeners\n // so not to limit the maximum amount of parallel events emitted.\n this.emitter.setMaxListeners(0)\n\n this.logger.info('constructing the interceptor...')\n }\n\n /**\n * Determine if this interceptor can be applied\n * in the current environment.\n */\n protected checkEnvironment(): boolean {\n return true\n }\n\n /**\n * Apply this interceptor to the current process.\n * Returns an already running interceptor instance if it's present.\n */\n public apply(): void {\n const logger = this.logger.extend('apply')\n logger.info('applying the interceptor...')\n\n if (this.readyState === InterceptorReadyState.APPLIED) {\n logger.info('intercepted already applied!')\n return\n }\n\n const shouldApply = this.checkEnvironment()\n\n if (!shouldApply) {\n logger.info('the interceptor cannot be applied in this environment!')\n return\n }\n\n this.readyState = InterceptorReadyState.APPLYING\n\n // Whenever applying a new interceptor, check if it hasn't been applied already.\n // This enables to apply the same interceptor multiple times, for example from a different\n // interceptor, only proxying events but keeping the stubs in a single place.\n const runningInstance = this.getInstance()\n\n if (runningInstance) {\n logger.info('found a running instance, reusing...')\n\n // Proxy any listeners you set on this instance to the running instance.\n this.on = (event, listener) => {\n logger.info('proxying the \"%s\" listener', event)\n\n // Add listeners to the running instance so they appear\n // at the top of the event listeners list and are executed first.\n runningInstance.emitter.addListener(event, listener)\n\n // Ensure that once this interceptor instance is disposed,\n // it removes all listeners it has appended to the running interceptor instance.\n this.subscriptions.push(() => {\n runningInstance.emitter.removeListener(event, listener)\n logger.info('removed proxied \"%s\" listener!', event)\n })\n\n return this\n }\n\n this.readyState = InterceptorReadyState.APPLIED\n\n return\n }\n\n logger.info('no running instance found, setting up a new instance...')\n\n // Setup the interceptor.\n this.setup()\n\n // Store the newly applied interceptor instance globally.\n this.setInstance()\n\n this.readyState = InterceptorReadyState.APPLIED\n }\n\n /**\n * Setup the module augments and stubs necessary for this interceptor.\n * This method is not run if there's a running interceptor instance\n * to prevent instantiating an interceptor multiple times.\n */\n protected setup(): void {}\n\n /**\n * Listen to the interceptor's public events.\n */\n public on<EventName extends ExtractEventNames<Events>>(\n event: EventName,\n listener: Listener<Events[EventName]>\n ): this {\n const logger = this.logger.extend('on')\n\n if (\n this.readyState === InterceptorReadyState.DISPOSING ||\n this.readyState === InterceptorReadyState.DISPOSED\n ) {\n logger.info('cannot listen to events, already disposed!')\n return this\n }\n\n logger.info('adding \"%s\" event listener:', event, listener)\n\n this.emitter.on(event, listener)\n return this\n }\n\n public once<EventName extends ExtractEventNames<Events>>(\n event: EventName,\n listener: Listener<Events[EventName]>\n ): this {\n this.emitter.once(event, listener)\n return this\n }\n\n public off<EventName extends ExtractEventNames<Events>>(\n event: EventName,\n listener: Listener<Events[EventName]>\n ): this {\n this.emitter.off(event, listener)\n return this\n }\n\n public removeAllListeners<EventName extends ExtractEventNames<Events>>(\n event?: EventName\n ): this {\n this.emitter.removeAllListeners(event)\n return this\n }\n\n /**\n * Disposes of any side-effects this interceptor has introduced.\n */\n public dispose(): void {\n const logger = this.logger.extend('dispose')\n\n if (this.readyState === InterceptorReadyState.DISPOSED) {\n logger.info('cannot dispose, already disposed!')\n return\n }\n\n logger.info('disposing the interceptor...')\n this.readyState = InterceptorReadyState.DISPOSING\n\n if (!this.getInstance()) {\n logger.info('no interceptors running, skipping dispose...')\n return\n }\n\n // Delete the global symbol as soon as possible,\n // indicating that the interceptor is no longer running.\n this.clearInstance()\n\n logger.info('global symbol deleted:', getGlobalSymbol(this.symbol))\n\n if (this.subscriptions.length > 0) {\n logger.info('disposing of %d subscriptions...', this.subscriptions.length)\n\n for (const dispose of this.subscriptions) {\n dispose()\n }\n\n this.subscriptions = []\n\n logger.info('disposed of all subscriptions!', this.subscriptions.length)\n }\n\n this.emitter.removeAllListeners()\n logger.info('destroyed the listener!')\n\n this.readyState = InterceptorReadyState.DISPOSED\n }\n\n private getInstance(): this | undefined {\n const instance = getGlobalSymbol<this>(this.symbol)\n this.logger.info('retrieved global instance:', instance?.constructor?.name)\n return instance\n }\n\n private setInstance(): void {\n setGlobalSymbol(this.symbol, this)\n this.logger.info('set global instance!', this.symbol.description)\n }\n\n private clearInstance(): void {\n deleteGlobalSymbol(this.symbol)\n this.logger.info('cleared global instance!', this.symbol.description)\n }\n}\n","/**\n * Generate a random ID string to represent a request.\n * @example\n * createRequestId()\n * // \"f774b6c9c600f\"\n */\nexport function createRequestId(): string {\n return Math.random().toString(16).slice(2)\n}\n","/**\n * A function that validates if property access is possible on an object\n * without throwing. It returns `true` if the property access is possible\n * and `false` otherwise.\n *\n * Environments like miniflare will throw on property access on certain objects\n * like Request and Response, for unimplemented properties.\n */\nexport function isPropertyAccessible<Obj extends Record<string, any>>(\n obj: Obj,\n key: keyof Obj\n) {\n try {\n obj[key]\n return true\n } catch {\n return false\n }\n}\n","import { isPropertyAccessible } from './isPropertyAccessible'\n\n/**\n * Response status codes for responses that cannot have body.\n * @see https://fetch.spec.whatwg.org/#statuses\n */\nexport const RESPONSE_STATUS_CODES_WITHOUT_BODY = new Set([\n 101, 103, 204, 205, 304,\n])\n\n/**\n * Returns a boolean indicating whether the given response status\n * code represents a response that cannot have a body.\n */\nexport function isResponseWithoutBody(status: number): boolean {\n return RESPONSE_STATUS_CODES_WITHOUT_BODY.has(status)\n}\n\n/**\n * Creates a generic 500 Unhandled Exception response.\n */\nexport function createServerErrorResponse(body: unknown): Response {\n return new Response(\n JSON.stringify(\n body instanceof Error\n ? {\n name: body.name,\n message: body.message,\n stack: body.stack,\n }\n : body\n ),\n {\n status: 500,\n statusText: 'Unhandled Exception',\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n )\n}\n\nexport type ResponseError = Response & { type: 'error' }\n\n/**\n * Check if the given response is a `Response.error()`.\n *\n * @note Some environments, like Miniflare (Cloudflare) do not\n * implement the \"Response.type\" property and throw on its access.\n * Safely check if we can access \"type\" on \"Response\" before continuing.\n * @see https://github.com/mswjs/msw/issues/1834\n */\nexport function isResponseError(response: Response): response is ResponseError {\n return isPropertyAccessible(response, 'type') && response.type === 'error'\n}\n"],"mappings":"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","names":["InterceptorReadyState"]}
@@ -4,4 +4,4 @@ var IS_PATCHED_MODULE = Symbol("isPatchedModule");
4
4
  export {
5
5
  IS_PATCHED_MODULE
6
6
  };
7
- //# sourceMappingURL=chunk-OJ6O4LSC.mjs.map
7
+ //# sourceMappingURL=chunk-BZ3Y7YV5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/glossary.ts"],"sourcesContent":["import type { RequestController } from './RequestController'\n\nexport const IS_PATCHED_MODULE: unique symbol = Symbol('isPatchedModule')\n\nexport type RequestCredentials = 'omit' | 'include' | 'same-origin'\n\nexport type HttpRequestEventMap = {\n request: [\n args: {\n request: Request\n requestId: string\n controller: RequestController\n }\n ]\n response: [\n args: {\n response: Response\n isMockedResponse: boolean\n request: Request\n requestId: string\n }\n ]\n unhandledException: [\n args: {\n error: unknown\n request: Request\n requestId: string\n controller: RequestController\n }\n ]\n}\n"],"mappings":";AAEO,IAAM,oBAAmC,OAAO,iBAAiB;","names":[]}
@@ -1,7 +1,8 @@
1
1
  import {
2
+ RequestController,
2
3
  emitAsync,
3
- toInteractiveRequest
4
- } from "./chunk-KWV3JXSI.mjs";
4
+ handleRequest
5
+ } from "./chunk-KY3RJ2M3.mjs";
5
6
  import {
6
7
  INTERNAL_REQUEST_ID_HEADER_NAME,
7
8
  Interceptor,
@@ -9,19 +10,18 @@ import {
9
10
  createRequestId,
10
11
  createServerErrorResponse,
11
12
  isPropertyAccessible
12
- } from "./chunk-TGTPXCLF.mjs";
13
+ } from "./chunk-BUCULLYM.mjs";
13
14
 
14
15
  // src/interceptors/ClientRequest/index.ts
15
16
  import http2 from "http";
16
17
  import https2 from "https";
17
- import { until } from "@open-draft/until";
18
18
 
19
19
  // src/interceptors/ClientRequest/MockHttpSocket.ts
20
20
  import net2 from "net";
21
21
  import {
22
22
  HTTPParser
23
23
  } from "_http_common";
24
- import { IncomingMessage, ServerResponse } from "http";
24
+ import { STATUS_CODES, IncomingMessage, ServerResponse } from "http";
25
25
  import { Readable } from "stream";
26
26
  import { invariant } from "outvariant";
27
27
 
@@ -108,45 +108,107 @@ function parseRawHeaders(rawHeaders) {
108
108
  return headers;
109
109
  }
110
110
 
111
- // src/utils/getValueBySymbol.ts
112
- function getValueBySymbol(symbolName, source) {
113
- const ownSymbols = Object.getOwnPropertySymbols(source);
114
- const symbol = ownSymbols.find((symbol2) => {
115
- return symbol2.description === symbolName;
116
- });
117
- if (symbol) {
118
- return Reflect.get(source, symbol);
111
+ // src/interceptors/ClientRequest/utils/recordRawHeaders.ts
112
+ var kRawHeaders = Symbol("kRawHeaders");
113
+ var kRestorePatches = Symbol("kRestorePatches");
114
+ function recordRawHeader(headers, args) {
115
+ if (Reflect.get(headers, kRawHeaders) == null) {
116
+ Object.defineProperty(headers, kRawHeaders, {
117
+ value: [],
118
+ enumerable: false
119
+ });
119
120
  }
120
- return;
121
+ const rawHeaders = Reflect.get(headers, kRawHeaders);
122
+ rawHeaders.push(args);
121
123
  }
122
-
123
- // src/utils/isObject.ts
124
- function isObject(value, loose = false) {
125
- return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
124
+ function recordRawFetchHeaders() {
125
+ if (Reflect.get(Headers, kRestorePatches)) {
126
+ return Reflect.get(Headers, kRestorePatches);
127
+ }
128
+ const { Request: OriginalRequest, Response: OriginalResponse } = globalThis;
129
+ const { set, append, delete: headersDeleteMethod } = Headers.prototype;
130
+ Object.defineProperty(Headers, kRestorePatches, {
131
+ value: () => {
132
+ Headers.prototype.set = set;
133
+ Headers.prototype.append = append;
134
+ Headers.prototype.delete = headersDeleteMethod;
135
+ globalThis.Request = OriginalRequest;
136
+ globalThis.Response = OriginalResponse;
137
+ },
138
+ enumerable: false
139
+ });
140
+ Headers = new Proxy(Headers, {
141
+ construct(target, args, newTarget) {
142
+ const headers = Reflect.construct(target, args, newTarget);
143
+ const initialHeaders = args[0] || [];
144
+ const initialRawHeaders = Array.isArray(initialHeaders) ? initialHeaders : Object.entries(initialHeaders);
145
+ if (!Reflect.has(headers, kRawHeaders)) {
146
+ Object.defineProperty(headers, kRawHeaders, {
147
+ value: initialRawHeaders,
148
+ enumerable: false
149
+ });
150
+ }
151
+ return headers;
152
+ }
153
+ });
154
+ Headers.prototype.set = new Proxy(Headers.prototype.set, {
155
+ apply(target, thisArg, args) {
156
+ recordRawHeader(thisArg, args);
157
+ return Reflect.apply(target, thisArg, args);
158
+ }
159
+ });
160
+ Headers.prototype.append = new Proxy(Headers.prototype.append, {
161
+ apply(target, thisArg, args) {
162
+ recordRawHeader(thisArg, args);
163
+ return Reflect.apply(target, thisArg, args);
164
+ }
165
+ });
166
+ Headers.prototype.delete = new Proxy(Headers.prototype.delete, {
167
+ apply(target, thisArg, args) {
168
+ const rawHeaders = Reflect.get(thisArg, kRawHeaders);
169
+ if (rawHeaders) {
170
+ for (let index = rawHeaders.length - 1; index >= 0; index--) {
171
+ if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) {
172
+ rawHeaders.splice(index, 1);
173
+ }
174
+ }
175
+ }
176
+ return Reflect.apply(target, thisArg, args);
177
+ }
178
+ });
179
+ Request = new Proxy(Request, {
180
+ construct(target, args, newTarget) {
181
+ const request = Reflect.construct(target, args, newTarget);
182
+ if (typeof args[1] === "object" && args[1].headers != null && !request.headers[kRawHeaders]) {
183
+ request.headers[kRawHeaders] = inferRawHeaders(args[1].headers);
184
+ }
185
+ return request;
186
+ }
187
+ });
188
+ Response = new Proxy(Response, {
189
+ construct(target, args, newTarget) {
190
+ const response = Reflect.construct(target, args, newTarget);
191
+ if (typeof args[1] === "object" && args[1].headers != null) {
192
+ response.headers[kRawHeaders] = inferRawHeaders(args[1].headers);
193
+ }
194
+ return response;
195
+ }
196
+ });
126
197
  }
127
-
128
- // src/utils/getRawFetchHeaders.ts
129
- function getRawFetchHeaders(headers) {
130
- const headersList = getValueBySymbol("headers list", headers);
131
- if (!headersList) {
198
+ function restoreHeadersPrototype() {
199
+ if (!Reflect.get(Headers, kRestorePatches)) {
132
200
  return;
133
201
  }
134
- const headersMap = getValueBySymbol("headers map", headersList);
135
- if (!headersMap || !isHeadersMapWithRawHeaderNames(headersMap)) {
136
- return;
137
- }
138
- const rawHeaders = /* @__PURE__ */ new Map();
139
- headersMap.forEach(({ name, value }) => {
140
- rawHeaders.set(name, value);
141
- });
142
- return rawHeaders;
202
+ Reflect.get(Headers, kRestorePatches)();
143
203
  }
144
- function isHeadersMapWithRawHeaderNames(headersMap) {
145
- return Array.from(
146
- headersMap.values()
147
- ).every((value) => {
148
- return isObject(value) && "name" in value;
149
- });
204
+ function getRawFetchHeaders(headers) {
205
+ return Reflect.get(headers, kRawHeaders) || Array.from(headers.entries());
206
+ }
207
+ function inferRawHeaders(headers) {
208
+ if (headers instanceof Headers) {
209
+ return Reflect.get(headers, kRawHeaders);
210
+ }
211
+ return Reflect.get(new Headers(headers), kRawHeaders);
150
212
  }
151
213
 
152
214
  // src/interceptors/ClientRequest/MockHttpSocket.ts
@@ -221,7 +283,8 @@ var MockHttpSocket = class extends MockSocket {
221
283
  const headers = parseRawHeaders(rawHeaders);
222
284
  const canHaveBody = !RESPONSE_STATUS_CODES_WITHOUT_BODY.has(status);
223
285
  if (canHaveBody) {
224
- this.responseStream = new Readable();
286
+ this.responseStream = new Readable({ read() {
287
+ } });
225
288
  }
226
289
  const response = new Response(
227
290
  /**
@@ -319,8 +382,10 @@ var MockHttpSocket = class extends MockSocket {
319
382
  const chunkAfterRequestHeaders = chunkString.slice(
320
383
  chunk.indexOf("\r\n\r\n")
321
384
  );
322
- const requestHeaders = getRawFetchHeaders(this.request.headers) || this.request.headers;
323
- const requestHeadersString = Array.from(requestHeaders.entries()).filter(([name]) => name !== INTERNAL_REQUEST_ID_HEADER_NAME).map(([name, value]) => `${name}: ${value}`).join("\r\n");
385
+ const rawRequestHeaders = getRawFetchHeaders(this.request.headers);
386
+ const requestHeadersString = rawRequestHeaders.filter(([name]) => {
387
+ return name.toLowerCase() !== INTERNAL_REQUEST_ID_HEADER_NAME;
388
+ }).map(([name, value]) => `${name}: ${value}`).join("\r\n");
324
389
  const headersChunk = `${chunkBeforeRequestHeaders}${requestHeadersString}${chunkAfterRequestHeaders}`;
325
390
  socket.write(headersChunk, encoding, callback);
326
391
  headersWritten = true;
@@ -384,17 +449,17 @@ var MockHttpSocket = class extends MockSocket {
384
449
  }
385
450
  })
386
451
  );
387
- serverResponse.statusCode = response.status;
388
- serverResponse.statusMessage = response.statusText;
389
452
  serverResponse.removeHeader("connection");
390
453
  serverResponse.removeHeader("date");
454
+ const rawResponseHeaders = getRawFetchHeaders(response.headers);
455
+ serverResponse.writeHead(
456
+ response.status,
457
+ response.statusText || STATUS_CODES[response.status],
458
+ rawResponseHeaders
459
+ );
391
460
  this.once("error", () => {
392
461
  serverResponse.destroy();
393
462
  });
394
- const headers = getRawFetchHeaders(response.headers) || response.headers;
395
- for (const [name, value] of headers) {
396
- serverResponse.setHeader(name, value);
397
- }
398
463
  if (response.body) {
399
464
  try {
400
465
  const reader = response.body.getReader();
@@ -672,6 +737,11 @@ function cloneObject(obj) {
672
737
  return isPlainObject(obj) ? enumerableProperties : Object.assign(Object.getPrototypeOf(obj), enumerableProperties);
673
738
  }
674
739
 
740
+ // src/utils/isObject.ts
741
+ function isObject(value, loose = false) {
742
+ return loose ? Object.prototype.toString.call(value).startsWith("[object ") : Object.prototype.toString.call(value) === "[object Object]";
743
+ }
744
+
675
745
  // src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts
676
746
  var logger3 = new Logger3("http normalizeClientRequestArgs");
677
747
  function resolveRequestOptions(args, url) {
@@ -795,17 +865,6 @@ function normalizeClientRequestArgs(defaultProtocol, args) {
795
865
  return [url, options, callback];
796
866
  }
797
867
 
798
- // src/utils/isNodeLikeError.ts
799
- function isNodeLikeError(error) {
800
- if (error == null) {
801
- return false;
802
- }
803
- if (!(error instanceof Error)) {
804
- return false;
805
- }
806
- return "code" in error && "errno" in error;
807
- }
808
-
809
868
  // src/interceptors/ClientRequest/index.ts
810
869
  var _ClientRequestInterceptor = class extends Interceptor {
811
870
  constructor() {
@@ -815,57 +874,27 @@ var _ClientRequestInterceptor = class extends Interceptor {
815
874
  socket
816
875
  }) => {
817
876
  const requestId = Reflect.get(request, kRequestId);
818
- const { interactiveRequest, requestController } = toInteractiveRequest(request);
819
- this.emitter.once("request", ({ requestId: pendingRequestId }) => {
820
- if (pendingRequestId !== requestId) {
821
- return;
822
- }
823
- if (requestController.responsePromise.state === "pending") {
824
- this.logger.info(
825
- "request has not been handled in listeners, executing fail-safe listener..."
826
- );
827
- requestController.responsePromise.resolve(void 0);
828
- }
829
- });
830
- const listenerResult = await until(async () => {
831
- await emitAsync(this.emitter, "request", {
832
- requestId,
833
- request: interactiveRequest
834
- });
835
- return await requestController.responsePromise;
836
- });
837
- if (listenerResult.error) {
838
- if (listenerResult.error instanceof Response) {
839
- socket.respondWith(listenerResult.error);
840
- return;
841
- }
842
- if (isNodeLikeError(listenerResult.error)) {
843
- socket.errorWith(listenerResult.error);
844
- return;
845
- }
846
- if (this.emitter.listenerCount("unhandledException") > 0) {
847
- await emitAsync(this.emitter, "unhandledException", {
848
- error: listenerResult.error,
849
- request,
850
- requestId,
851
- controller: {
852
- respondWith: socket.respondWith.bind(socket),
853
- errorWith: socket.errorWith.bind(socket)
854
- }
855
- });
856
- if (!socket.connecting || socket.destroyed) {
857
- return;
877
+ const controller = new RequestController(request);
878
+ const isRequestHandled = await handleRequest({
879
+ request,
880
+ requestId,
881
+ controller,
882
+ emitter: this.emitter,
883
+ onResponse: (response) => {
884
+ socket.respondWith(response);
885
+ },
886
+ onRequestError: (response) => {
887
+ socket.respondWith(response);
888
+ },
889
+ onError: (error) => {
890
+ if (error instanceof Error) {
891
+ socket.errorWith(error);
858
892
  }
859
893
  }
860
- socket.respondWith(createServerErrorResponse(listenerResult.error));
861
- return;
862
- }
863
- const mockedResponse = listenerResult.data;
864
- if (mockedResponse) {
865
- socket.respondWith(mockedResponse);
866
- return;
894
+ });
895
+ if (!isRequestHandled) {
896
+ return socket.passthrough();
867
897
  }
868
- socket.passthrough();
869
898
  };
870
899
  this.onResponse = async ({
871
900
  requestId,
@@ -946,11 +975,13 @@ var _ClientRequestInterceptor = class extends Interceptor {
946
975
  return Reflect.apply(target, thisArg, [url, options, callback]);
947
976
  }
948
977
  });
978
+ recordRawFetchHeaders();
949
979
  this.subscriptions.push(() => {
950
980
  http2.get = originalGet;
951
981
  http2.request = originalRequest;
952
982
  https2.get = originalHttpsGet;
953
983
  https2.request = originalHttpsRequest;
984
+ restoreHeadersPrototype();
954
985
  });
955
986
  }
956
987
  };
@@ -960,4 +991,4 @@ ClientRequestInterceptor.symbol = Symbol("client-request-interceptor");
960
991
  export {
961
992
  ClientRequestInterceptor
962
993
  };
963
- //# sourceMappingURL=chunk-3OJLYEWA.mjs.map
994
+ //# sourceMappingURL=chunk-CU3YXMM4.mjs.map