@mswjs/interceptors 0.32.2 → 0.33.1

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 (120) 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-2COJKQQB.js → chunk-42632LKH.js} +3 -3
  39. package/lib/node/chunk-5WWNCLB3.js +196 -0
  40. package/lib/node/chunk-5WWNCLB3.js.map +1 -0
  41. package/lib/node/{chunk-TGTPXCLF.mjs → chunk-BUCULLYM.mjs} +1 -1
  42. package/lib/node/{chunk-TGTPXCLF.mjs.map → chunk-BUCULLYM.mjs.map} +1 -1
  43. package/lib/node/{chunk-OJ6O4LSC.mjs → chunk-BZ3Y7YV5.mjs} +1 -1
  44. package/lib/node/chunk-BZ3Y7YV5.mjs.map +1 -0
  45. package/lib/node/{chunk-PNWPIDEL.mjs → chunk-HGQLG7KE.mjs} +2 -2
  46. package/lib/node/{chunk-EIBTX65O.js → chunk-IDEEMJ3F.js} +1 -1
  47. package/lib/node/chunk-IDEEMJ3F.js.map +1 -0
  48. package/lib/node/chunk-KY3RJ2M3.mjs +196 -0
  49. package/lib/node/chunk-KY3RJ2M3.mjs.map +1 -0
  50. package/lib/node/{chunk-CFRXZJO4.js → chunk-MCSMMYNC.js} +81 -89
  51. package/lib/node/chunk-MCSMMYNC.js.map +1 -0
  52. package/lib/node/{chunk-CMVICWQS.mjs → chunk-MII7WYYZ.mjs} +73 -81
  53. package/lib/node/chunk-MII7WYYZ.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 +1 -1
  84. package/src/interceptors/ClientRequest/index.test.ts +23 -34
  85. package/src/interceptors/ClientRequest/index.ts +21 -82
  86. package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +159 -0
  87. package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +80 -27
  88. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +1 -1
  89. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +27 -108
  90. package/src/interceptors/XMLHttpRequest/index.ts +0 -6
  91. package/src/interceptors/fetch/index.ts +52 -169
  92. package/src/utils/handleRequest.ts +213 -0
  93. package/src/utils/responseUtils.ts +4 -4
  94. package/lib/browser/chunk-2CRB3JAQ.js.map +0 -1
  95. package/lib/browser/chunk-732REFPX.mjs.map +0 -1
  96. package/lib/browser/chunk-MAEPOYB6.mjs +0 -213
  97. package/lib/browser/chunk-MAEPOYB6.mjs.map +0 -1
  98. package/lib/browser/chunk-MQJ3JOOK.js +0 -49
  99. package/lib/browser/chunk-MQJ3JOOK.js.map +0 -1
  100. package/lib/browser/chunk-OMISYKWR.mjs.map +0 -1
  101. package/lib/browser/chunk-OUWBQF3Z.mjs +0 -49
  102. package/lib/browser/chunk-OUWBQF3Z.mjs.map +0 -1
  103. package/lib/browser/chunk-PSX5J3RF.js.map +0 -1
  104. package/lib/browser/chunk-WBHIW62P.js +0 -213
  105. package/lib/browser/chunk-WBHIW62P.js.map +0 -1
  106. package/lib/browser/glossary-1c204f45.d.ts +0 -44
  107. package/lib/node/chunk-BFLYGQ6D.js.map +0 -1
  108. package/lib/node/chunk-CFRXZJO4.js.map +0 -1
  109. package/lib/node/chunk-CMVICWQS.mjs.map +0 -1
  110. package/lib/node/chunk-DV4PBH4D.mjs.map +0 -1
  111. package/lib/node/chunk-EIBTX65O.js.map +0 -1
  112. package/lib/node/chunk-KWV3JXSI.mjs +0 -49
  113. package/lib/node/chunk-KWV3JXSI.mjs.map +0 -1
  114. package/lib/node/chunk-OJ6O4LSC.mjs.map +0 -1
  115. package/lib/node/chunk-PYD4E2EJ.js.map +0 -1
  116. package/lib/node/chunk-UXCYRE4F.js +0 -49
  117. package/lib/node/chunk-UXCYRE4F.js.map +0 -1
  118. package/src/utils/toInteractiveRequest.ts +0 -23
  119. /package/lib/node/{chunk-2COJKQQB.js.map → chunk-42632LKH.js.map} +0 -0
  120. /package/lib/node/{chunk-PNWPIDEL.mjs.map → chunk-HGQLG7KE.mjs.map} +0 -0
@@ -0,0 +1,159 @@
1
+ // @vitest-environment node
2
+ import { it, expect, afterEach } from 'vitest'
3
+ import {
4
+ recordRawFetchHeaders,
5
+ restoreHeadersPrototype,
6
+ getRawFetchHeaders,
7
+ } from './recordRawHeaders'
8
+
9
+ const url = 'http://localhost'
10
+
11
+ afterEach(() => {
12
+ restoreHeadersPrototype()
13
+ })
14
+
15
+ it('returns an empty list if no headers were set', () => {
16
+ expect(getRawFetchHeaders(new Headers())).toEqual([])
17
+ expect(getRawFetchHeaders(new Headers(undefined))).toEqual([])
18
+ expect(getRawFetchHeaders(new Headers({}))).toEqual([])
19
+ expect(getRawFetchHeaders(new Headers([]))).toEqual([])
20
+ expect(getRawFetchHeaders(new Request(url).headers)).toEqual([])
21
+ expect(getRawFetchHeaders(new Response().headers)).toEqual([])
22
+ })
23
+
24
+ it('records raw headers (Headers / object as init)', () => {
25
+ recordRawFetchHeaders()
26
+ const headers = new Headers({
27
+ 'Content-Type': 'application/json',
28
+ 'X-My-Header': '1',
29
+ })
30
+
31
+ expect(getRawFetchHeaders(headers)).toEqual([
32
+ ['Content-Type', 'application/json'],
33
+ ['X-My-Header', '1'],
34
+ ])
35
+ expect(Object.fromEntries(headers)).toEqual({
36
+ 'content-type': 'application/json',
37
+ 'x-my-header': '1',
38
+ })
39
+ })
40
+
41
+ it('records raw headers (Headers / array as init)', () => {
42
+ recordRawFetchHeaders()
43
+ const headers = new Headers([['X-My-Header', '1']])
44
+
45
+ expect(getRawFetchHeaders(headers)).toEqual([['X-My-Header', '1']])
46
+ expect(Object.fromEntries(headers)).toEqual({
47
+ 'x-my-header': '1',
48
+ })
49
+ })
50
+
51
+ it('records raw headers (Headers / Headers as init', () => {
52
+ recordRawFetchHeaders()
53
+ const headers = new Headers([['X-My-Header', '1']])
54
+
55
+ expect(getRawFetchHeaders(new Headers(headers))).toEqual([
56
+ ['X-My-Header', '1'],
57
+ ])
58
+ })
59
+
60
+ it('records raw headers added via ".set()"', () => {
61
+ recordRawFetchHeaders()
62
+ const headers = new Headers([['X-My-Header', '1']])
63
+ headers.set('X-Another-Header', '2')
64
+
65
+ expect(getRawFetchHeaders(headers)).toEqual([
66
+ ['X-My-Header', '1'],
67
+ ['X-Another-Header', '2'],
68
+ ])
69
+ })
70
+
71
+ it('records raw headers added via ".append()"', () => {
72
+ recordRawFetchHeaders()
73
+ const headers = new Headers([['X-My-Header', '1']])
74
+ headers.append('X-My-Header', '2')
75
+
76
+ expect(getRawFetchHeaders(headers)).toEqual([
77
+ ['X-My-Header', '1'],
78
+ ['X-My-Header', '2'],
79
+ ])
80
+ })
81
+
82
+ it('deletes the header when called ".delete()"', () => {
83
+ const headers = new Headers([['X-My-Header', '1']])
84
+ headers.delete('X-My-Header')
85
+
86
+ expect(getRawFetchHeaders(headers)).toEqual([])
87
+ })
88
+
89
+ it('records raw headers (Request / object as init)', () => {
90
+ recordRawFetchHeaders()
91
+ const request = new Request(url, {
92
+ headers: {
93
+ 'Content-Type': 'application/json',
94
+ 'X-My-Header': '1',
95
+ },
96
+ })
97
+
98
+ expect(getRawFetchHeaders(request.headers)).toEqual([
99
+ ['Content-Type', 'application/json'],
100
+ ['X-My-Header', '1'],
101
+ ])
102
+ })
103
+
104
+ it('records raw headers (Request / array as init)', () => {
105
+ recordRawFetchHeaders()
106
+ const request = new Request(url, {
107
+ headers: [['X-My-Header', '1']],
108
+ })
109
+
110
+ expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
111
+ })
112
+
113
+ it('records raw headers (Request / Headers as init)', () => {
114
+ recordRawFetchHeaders()
115
+ const headers = new Headers([['X-My-Header', '1']])
116
+ const request = new Request(url, { headers })
117
+
118
+ expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
119
+ })
120
+
121
+ it('records raw headers (Response / object as init)', () => {
122
+ recordRawFetchHeaders()
123
+ const response = new Response(null, {
124
+ headers: {
125
+ 'Content-Type': 'application/json',
126
+ 'X-My-Header': '1',
127
+ },
128
+ })
129
+
130
+ expect(getRawFetchHeaders(response.headers)).toEqual([
131
+ ['Content-Type', 'application/json'],
132
+ ['X-My-Header', '1'],
133
+ ])
134
+ })
135
+
136
+ it('records raw headers (Response / array as init)', () => {
137
+ recordRawFetchHeaders()
138
+ const response = new Response(null, {
139
+ headers: [['X-My-Header', '1']],
140
+ })
141
+
142
+ expect(getRawFetchHeaders(response.headers)).toEqual([['X-My-Header', '1']])
143
+ })
144
+
145
+ it('records raw headers (Response / Headers as init)', () => {
146
+ recordRawFetchHeaders()
147
+ const headers = new Headers([['X-My-Header', '1']])
148
+ const response = new Response(null, { headers })
149
+
150
+ expect(getRawFetchHeaders(response.headers)).toEqual([['X-My-Header', '1']])
151
+ })
152
+
153
+ it('stops recording once the patches are restored', () => {
154
+ restoreHeadersPrototype()
155
+
156
+ const headers = new Headers({ 'X-My-Header': '1' })
157
+ // Must return the normalized headers (no access to raw headers).
158
+ expect(getRawFetchHeaders(headers)).toEqual([['x-my-header', '1']])
159
+ })
@@ -5,16 +5,24 @@ const kRawHeaders = Symbol('kRawHeaders')
5
5
  const kRestorePatches = Symbol('kRestorePatches')
6
6
 
7
7
  function recordRawHeader(headers: Headers, args: HeaderTuple) {
8
- if (Reflect.get(headers, kRawHeaders) == null) {
9
- Object.defineProperty(headers, kRawHeaders, {
10
- value: [],
11
- enumerable: false,
12
- })
8
+ if (!Reflect.has(headers, kRawHeaders)) {
9
+ defineRawHeaders(headers, [])
13
10
  }
14
11
  const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders
15
12
  rawHeaders.push(args)
16
13
  }
17
14
 
15
+ function defineRawHeaders(headers: Headers, rawHeaders: RawHeaders): void {
16
+ if (Reflect.has(headers, kRawHeaders)) {
17
+ return
18
+ }
19
+
20
+ Object.defineProperty(headers, kRawHeaders, {
21
+ value: rawHeaders,
22
+ enumerable: false,
23
+ })
24
+ }
25
+
18
26
  /**
19
27
  * Patch the global `Headers` class to store raw headers.
20
28
  * This is for compatibility with `IncomingMessage.prototype.rawHeaders`.
@@ -35,7 +43,11 @@ export function recordRawFetchHeaders() {
35
43
  return Reflect.get(Headers, kRestorePatches)
36
44
  }
37
45
 
38
- const { Request: OriginalRequest, Response: OriginalResponse } = globalThis
46
+ const {
47
+ Headers: OriginalHeaders,
48
+ Request: OriginalRequest,
49
+ Response: OriginalResponse,
50
+ } = globalThis
39
51
  const { set, append, delete: headersDeleteMethod } = Headers.prototype
40
52
 
41
53
  Object.defineProperty(Headers, kRestorePatches, {
@@ -43,30 +55,49 @@ export function recordRawFetchHeaders() {
43
55
  Headers.prototype.set = set
44
56
  Headers.prototype.append = append
45
57
  Headers.prototype.delete = headersDeleteMethod
58
+ globalThis.Headers = OriginalHeaders
46
59
 
47
60
  globalThis.Request = OriginalRequest
48
61
  globalThis.Response = OriginalResponse
62
+
63
+ Reflect.deleteProperty(Headers, kRestorePatches)
49
64
  },
50
65
  enumerable: false,
66
+ /**
67
+ * @note Mark this property as configurable
68
+ * so we can delete it using `Reflect.delete` during cleanup.
69
+ */
70
+ configurable: true,
51
71
  })
52
72
 
53
73
  Headers = new Proxy(Headers, {
54
74
  construct(target, args, newTarget) {
75
+ const headersInit = args[0] || []
76
+
77
+ if (
78
+ headersInit instanceof Headers &&
79
+ Reflect.has(headersInit, kRawHeaders)
80
+ ) {
81
+ const headers = Reflect.construct(
82
+ target,
83
+ [Reflect.get(headersInit, kRawHeaders)],
84
+ newTarget
85
+ )
86
+ defineRawHeaders(headers, Reflect.get(headersInit, kRawHeaders))
87
+ return headers
88
+ }
89
+
55
90
  const headers = Reflect.construct(target, args, newTarget)
56
- const initialHeaders = args[0] || []
57
- const initialRawHeaders = Array.isArray(initialHeaders)
58
- ? initialHeaders
59
- : Object.entries(initialHeaders)
60
91
 
61
92
  // Request/Response constructors will set the symbol
62
93
  // upon creating a new instance, using the raw developer
63
94
  // input as the raw headers. Skip the symbol altogether
64
95
  // in those cases because the input to Headers will be normalized.
65
96
  if (!Reflect.has(headers, kRawHeaders)) {
66
- Object.defineProperty(headers, kRawHeaders, {
67
- value: initialRawHeaders,
68
- enumerable: false,
69
- })
97
+ const rawHeadersInit = Array.isArray(headersInit)
98
+ ? headersInit
99
+ : Object.entries(headersInit)
100
+ defineRawHeaders(headers, rawHeadersInit)
70
101
  }
71
102
 
72
103
  return headers
@@ -105,14 +136,27 @@ export function recordRawFetchHeaders() {
105
136
 
106
137
  Request = new Proxy(Request, {
107
138
  construct(target, args, newTarget) {
108
- const request = Reflect.construct(target, args, newTarget)
109
-
139
+ /**
140
+ * @note If the headers init argument of Request
141
+ * is existing Headers instance, use its raw headers
142
+ * as the headers init instead.
143
+ * This is needed because the Headers constructor copies
144
+ * all normalized headers from the given Headers instance
145
+ * and uses ".append()" to add it to the new instance.
146
+ */
110
147
  if (
111
148
  typeof args[1] === 'object' &&
112
149
  args[1].headers != null &&
113
- !request.headers[kRawHeaders]
150
+ args[1].headers instanceof Headers &&
151
+ Reflect.has(args[1].headers, kRawHeaders)
114
152
  ) {
115
- request.headers[kRawHeaders] = inferRawHeaders(args[1].headers)
153
+ args[1].headers = args[1].headers[kRawHeaders]
154
+ }
155
+
156
+ const request = Reflect.construct(target, args, newTarget)
157
+
158
+ if (typeof args[1] === 'object' && args[1].headers != null) {
159
+ defineRawHeaders(request.headers, inferRawHeaders(args[1].headers))
116
160
  }
117
161
 
118
162
  return request
@@ -121,15 +165,19 @@ export function recordRawFetchHeaders() {
121
165
 
122
166
  Response = new Proxy(Response, {
123
167
  construct(target, args, newTarget) {
168
+ if (
169
+ typeof args[1] === 'object' &&
170
+ args[1].headers != null &&
171
+ args[1].headers instanceof Headers &&
172
+ Reflect.has(args[1].headers, kRawHeaders)
173
+ ) {
174
+ args[1].headers = args[1].headers[kRawHeaders]
175
+ }
176
+
124
177
  const response = Reflect.construct(target, args, newTarget)
125
178
 
126
179
  if (typeof args[1] === 'object' && args[1].headers != null) {
127
- /**
128
- * @note Pass the init argument directly because it gets
129
- * transformed into a normalized Headers instance once it
130
- * passes the Response constructor.
131
- */
132
- response.headers[kRawHeaders] = inferRawHeaders(args[1].headers)
180
+ defineRawHeaders(response.headers, inferRawHeaders(args[1].headers))
133
181
  }
134
182
 
135
183
  return response
@@ -146,9 +194,14 @@ export function restoreHeadersPrototype() {
146
194
  }
147
195
 
148
196
  export function getRawFetchHeaders(headers: Headers): RawHeaders {
149
- // Return the raw headers, if recorded (i.e. `.set()` or `.append()` was called).
150
- // If no raw headers were recorded, return all the headers.
151
- return Reflect.get(headers, kRawHeaders) || Array.from(headers.entries())
197
+ // If the raw headers recording failed for some reason,
198
+ // use the normalized header entries instead.
199
+ if (!Reflect.has(headers, kRawHeaders)) {
200
+ return Array.from(headers.entries())
201
+ }
202
+
203
+ const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders
204
+ return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries())
152
205
  }
153
206
 
154
207
  /**
@@ -481,7 +481,7 @@ export class XMLHttpRequestController {
481
481
  return null
482
482
  }
483
483
 
484
- public errorWith(error: Error): void {
484
+ public errorWith(error?: Error): void {
485
485
  this.logger.info('responding with an error')
486
486
 
487
487
  this.setReadyState(this.request.DONE)
@@ -1,13 +1,8 @@
1
- import { until } from '@open-draft/until'
2
1
  import type { Logger } from '@open-draft/logger'
3
2
  import { XMLHttpRequestEmitter } from '.'
4
- import { toInteractiveRequest } from '../../utils/toInteractiveRequest'
5
- import { emitAsync } from '../../utils/emitAsync'
3
+ import { RequestController } from '../../RequestController'
6
4
  import { XMLHttpRequestController } from './XMLHttpRequestController'
7
- import {
8
- createServerErrorResponse,
9
- isResponseError,
10
- } from '../../utils/responseUtils'
5
+ import { handleRequest } from '../../utils/handleRequest'
11
6
 
12
7
  export interface XMLHttpRequestProxyOptions {
13
8
  emitter: XMLHttpRequestEmitter
@@ -57,116 +52,40 @@ export function createXMLHttpRequestProxy({
57
52
  )
58
53
 
59
54
  xhrRequestController.onRequest = async function ({ request, requestId }) {
60
- const { interactiveRequest, requestController } =
61
- toInteractiveRequest(request)
55
+ const controller = new RequestController(request)
62
56
 
63
57
  this.logger.info('awaiting mocked response...')
64
58
 
65
- emitter.once('request', ({ requestId: pendingRequestId }) => {
66
- if (pendingRequestId !== requestId) {
67
- return
68
- }
69
-
70
- if (requestController.responsePromise.state === 'pending') {
71
- requestController.respondWith(undefined)
72
- }
73
- })
74
-
75
- const resolverResult = await until(async () => {
76
- this.logger.info(
77
- 'emitting the "request" event for %s listener(s)...',
78
- emitter.listenerCount('request')
79
- )
80
-
81
- await emitAsync(emitter, 'request', {
82
- request: interactiveRequest,
83
- requestId,
84
- })
85
-
86
- this.logger.info('all "request" listeners settled!')
87
-
88
- const mockedResponse = await requestController.responsePromise
89
-
90
- this.logger.info('event.respondWith called with:', mockedResponse)
91
-
92
- return mockedResponse
93
- })
94
-
95
- if (resolverResult.error) {
96
- this.logger.info(
97
- 'request listener threw an exception, aborting request...',
98
- resolverResult.error
99
- )
100
-
101
- // Treat thrown Responses as mocked responses.
102
- if (resolverResult.error instanceof Response) {
103
- if (isResponseError(resolverResult.error)) {
104
- xhrRequestController.errorWith(new TypeError('Network error'))
105
- } else {
106
- this.respondWith(resolverResult.error)
107
- }
108
-
109
- return
110
- }
111
-
112
- if (emitter.listenerCount('unhandledException') > 0) {
113
- // Emit the "unhandledException" event so the client can opt-out
114
- // from the default exception handling (producing 500 error responses).
115
- await emitAsync(emitter, 'unhandledException', {
116
- error: resolverResult.error,
117
- request,
118
- requestId,
119
- controller: {
120
- respondWith:
121
- xhrRequestController.respondWith.bind(xhrRequestController),
122
- errorWith:
123
- xhrRequestController.errorWith.bind(xhrRequestController),
124
- },
125
- })
59
+ this.logger.info(
60
+ 'emitting the "request" event for %s listener(s)...',
61
+ emitter.listenerCount('request')
62
+ )
126
63
 
127
- // If any of the "unhandledException" listeners handled the request,
128
- // do nothing. Note that mocked responses will dispatch
129
- // HEADERS_RECEIVED (2), then LOADING (3), and DONE (4) can take
130
- // time as the mocked response body finishes streaming.
131
- if (originalRequest.readyState > XMLHttpRequest.OPENED) {
132
- return
64
+ const isRequestHandled = await handleRequest({
65
+ request,
66
+ requestId,
67
+ controller,
68
+ emitter,
69
+ onResponse: (response) => {
70
+ this.respondWith(response)
71
+ },
72
+ onRequestError: () => {
73
+ this.errorWith(new TypeError('Network error'))
74
+ },
75
+ onError: (error) => {
76
+ this.logger.info('request errored!', { error })
77
+
78
+ if (error instanceof Error) {
79
+ this.errorWith(error)
133
80
  }
134
- }
135
-
136
- // Unhandled exceptions in the request listeners are
137
- // synonymous to unhandled exceptions on the server.
138
- // Those are represented as 500 error responses.
139
- xhrRequestController.respondWith(
140
- createServerErrorResponse(resolverResult.error)
141
- )
142
-
143
- return
144
- }
145
-
146
- const mockedResponse = resolverResult.data
81
+ },
82
+ })
147
83
 
148
- if (typeof mockedResponse !== 'undefined') {
84
+ if (!isRequestHandled) {
149
85
  this.logger.info(
150
- 'received mocked response: %d %s',
151
- mockedResponse.status,
152
- mockedResponse.statusText
86
+ 'no mocked response received, performing request as-is...'
153
87
  )
154
-
155
- if (isResponseError(mockedResponse)) {
156
- this.logger.info(
157
- 'received a network error response, rejecting the request promise...'
158
- )
159
-
160
- xhrRequestController.errorWith(new TypeError('Network error'))
161
- return
162
- }
163
-
164
- return xhrRequestController.respondWith(mockedResponse)
165
88
  }
166
-
167
- this.logger.info(
168
- 'no mocked response received, performing request as-is...'
169
- )
170
89
  }
171
90
 
172
91
  xhrRequestController.onResponse = async function ({
@@ -1,15 +1,9 @@
1
1
  import { invariant } from 'outvariant'
2
2
  import { Emitter } from 'strict-event-emitter'
3
3
  import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
4
- import { InteractiveRequest } from '../../utils/toInteractiveRequest'
5
4
  import { Interceptor } from '../../Interceptor'
6
5
  import { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'
7
6
 
8
- export type XMLHttpRequestEventListener = (args: {
9
- request: InteractiveRequest
10
- requestId: string
11
- }) => Promise<void> | void
12
-
13
7
  export type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>
14
8
 
15
9
  export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {