@mswjs/interceptors 0.39.7 → 0.40.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 (142) hide show
  1. package/lib/browser/{chunk-PFGO5BSM.js → chunk-2HUMWGRD.js} +15 -3
  2. package/lib/browser/chunk-2HUMWGRD.js.map +1 -0
  3. package/lib/browser/{chunk-ZXAL3FMU.js → chunk-2MCNQOY3.js} +56 -51
  4. package/lib/browser/chunk-2MCNQOY3.js.map +1 -0
  5. package/lib/browser/chunk-57RIRQUY.js +218 -0
  6. package/lib/browser/chunk-57RIRQUY.js.map +1 -0
  7. package/lib/browser/chunk-FW45TRCB.js +178 -0
  8. package/lib/browser/chunk-FW45TRCB.js.map +1 -0
  9. package/lib/browser/{chunk-TIPR373R.js → chunk-JQ2S7G56.js} +19 -3
  10. package/lib/browser/chunk-JQ2S7G56.js.map +1 -0
  11. package/lib/browser/{chunk-3RXCRGL2.mjs → chunk-LIKZF2VU.mjs} +102 -1
  12. package/lib/browser/chunk-LIKZF2VU.mjs.map +1 -0
  13. package/lib/browser/{chunk-F2NPA2FP.js → chunk-MNT2FUCH.js} +61 -56
  14. package/lib/browser/chunk-MNT2FUCH.js.map +1 -0
  15. package/lib/browser/chunk-VOUOVDAW.mjs +178 -0
  16. package/lib/browser/chunk-VOUOVDAW.mjs.map +1 -0
  17. package/lib/browser/{chunk-TX5GBTFY.mjs → chunk-VYSDLBSS.mjs} +13 -1
  18. package/lib/browser/chunk-VYSDLBSS.mjs.map +1 -0
  19. package/lib/browser/{chunk-ZDGZFWQH.mjs → chunk-WADP6VHN.mjs} +49 -44
  20. package/lib/browser/chunk-WADP6VHN.mjs.map +1 -0
  21. package/lib/browser/{chunk-MDMPOBGY.mjs → chunk-WOWPV4GR.mjs} +52 -47
  22. package/lib/browser/chunk-WOWPV4GR.mjs.map +1 -0
  23. package/lib/browser/{chunk-QED3Q6Z2.mjs → chunk-Z5TSB3T6.mjs} +17 -1
  24. package/lib/browser/{chunk-QED3Q6Z2.mjs.map → chunk-Z5TSB3T6.mjs.map} +1 -1
  25. package/lib/browser/{glossary-7152281e.d.ts → glossary-f7ee1c9d.d.ts} +22 -17
  26. package/lib/browser/index.d.ts +1 -2
  27. package/lib/browser/index.js +6 -4
  28. package/lib/browser/index.js.map +1 -1
  29. package/lib/browser/index.mjs +4 -2
  30. package/lib/browser/index.mjs.map +1 -1
  31. package/lib/browser/interceptors/WebSocket/index.js +9 -7
  32. package/lib/browser/interceptors/WebSocket/index.js.map +1 -1
  33. package/lib/browser/interceptors/WebSocket/index.mjs +6 -4
  34. package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -1
  35. package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +1 -2
  36. package/lib/browser/interceptors/XMLHttpRequest/index.js +6 -6
  37. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +5 -5
  38. package/lib/browser/interceptors/fetch/index.d.ts +1 -2
  39. package/lib/browser/interceptors/fetch/index.js +6 -6
  40. package/lib/browser/interceptors/fetch/index.mjs +5 -5
  41. package/lib/browser/presets/browser.d.ts +1 -2
  42. package/lib/browser/presets/browser.js +8 -8
  43. package/lib/browser/presets/browser.mjs +6 -6
  44. package/lib/node/{BatchInterceptor-5b72232f.d.ts → BatchInterceptor-cb9a2eee.d.ts} +1 -1
  45. package/lib/node/{Interceptor-bc5a9d8e.d.ts → Interceptor-dc0a39b5.d.ts} +22 -16
  46. package/lib/node/RemoteHttpInterceptor.d.ts +2 -3
  47. package/lib/node/RemoteHttpInterceptor.js +31 -27
  48. package/lib/node/RemoteHttpInterceptor.js.map +1 -1
  49. package/lib/node/RemoteHttpInterceptor.mjs +28 -24
  50. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -1
  51. package/lib/node/{chunk-EKNRB5ZS.mjs → chunk-5UGIB6OX.mjs} +40 -29
  52. package/lib/node/chunk-5UGIB6OX.mjs.map +1 -0
  53. package/lib/node/{chunk-4NEYTVWD.mjs → chunk-5V3SIIW2.mjs} +48 -43
  54. package/lib/node/chunk-5V3SIIW2.mjs.map +1 -0
  55. package/lib/node/{chunk-VV2LUF5K.js → chunk-6B3ZQOO2.js} +51 -46
  56. package/lib/node/chunk-6B3ZQOO2.js.map +1 -0
  57. package/lib/node/chunk-7Q53NNPV.js +189 -0
  58. package/lib/node/chunk-7Q53NNPV.js.map +1 -0
  59. package/lib/node/{chunk-A7U44ARP.js → chunk-DOWWQYXZ.js} +104 -3
  60. package/lib/node/chunk-DOWWQYXZ.js.map +1 -0
  61. package/lib/node/{chunk-Z5LWCBZS.js → chunk-FRZQJNBO.js} +56 -51
  62. package/lib/node/chunk-FRZQJNBO.js.map +1 -0
  63. package/lib/node/{chunk-TJDMZZXE.mjs → chunk-GKN5RBVR.mjs} +2 -2
  64. package/lib/node/{chunk-R6JVCM7X.js → chunk-J5MULIHT.js} +3 -3
  65. package/lib/node/{chunk-IHJSPMYM.mjs → chunk-JXGB54LE.mjs} +102 -1
  66. package/lib/node/chunk-JXGB54LE.mjs.map +1 -0
  67. package/lib/node/{chunk-3CNGDJFB.mjs → chunk-OFW5L5ET.mjs} +50 -45
  68. package/lib/node/chunk-OFW5L5ET.mjs.map +1 -0
  69. package/lib/node/{chunk-A7Q4RTDJ.mjs → chunk-R6T7CL5E.mjs} +55 -115
  70. package/lib/node/chunk-R6T7CL5E.mjs.map +1 -0
  71. package/lib/node/{chunk-RC2XPCC4.mjs → chunk-SQ6RHTJR.mjs} +2 -2
  72. package/lib/node/chunk-SRMAQGPM.js +30 -0
  73. package/lib/node/chunk-SRMAQGPM.js.map +1 -0
  74. package/lib/node/{chunk-4YBV77DG.js → chunk-T3TW4P64.js} +3 -3
  75. package/lib/node/{chunk-N4ZZFE24.js → chunk-VYO5XDY2.js} +56 -45
  76. package/lib/node/chunk-VYO5XDY2.js.map +1 -0
  77. package/lib/node/chunk-YWNGXXUQ.mjs +30 -0
  78. package/lib/node/{chunk-3GJB4JDF.mjs.map → chunk-YWNGXXUQ.mjs.map} +1 -1
  79. package/lib/node/index.d.ts +2 -3
  80. package/lib/node/index.js +6 -4
  81. package/lib/node/index.js.map +1 -1
  82. package/lib/node/index.mjs +5 -3
  83. package/lib/node/index.mjs.map +1 -1
  84. package/lib/node/interceptors/ClientRequest/index.d.ts +1 -2
  85. package/lib/node/interceptors/ClientRequest/index.js +6 -6
  86. package/lib/node/interceptors/ClientRequest/index.mjs +5 -5
  87. package/lib/node/interceptors/XMLHttpRequest/index.d.ts +1 -2
  88. package/lib/node/interceptors/XMLHttpRequest/index.js +5 -5
  89. package/lib/node/interceptors/XMLHttpRequest/index.mjs +4 -4
  90. package/lib/node/interceptors/fetch/index.d.ts +1 -2
  91. package/lib/node/interceptors/fetch/index.js +5 -5
  92. package/lib/node/interceptors/fetch/index.mjs +4 -4
  93. package/lib/node/presets/node.d.ts +1 -2
  94. package/lib/node/presets/node.js +10 -10
  95. package/lib/node/presets/node.mjs +7 -7
  96. package/lib/node/utils/node/index.js +3 -3
  97. package/lib/node/utils/node/index.mjs +2 -2
  98. package/package.json +2 -1
  99. package/src/RemoteHttpInterceptor.ts +18 -13
  100. package/src/RequestController.test.ts +78 -31
  101. package/src/RequestController.ts +63 -39
  102. package/src/index.ts +4 -0
  103. package/src/interceptors/ClientRequest/MockHttpSocket.ts +24 -3
  104. package/src/interceptors/ClientRequest/index.ts +14 -18
  105. package/src/interceptors/WebSocket/index.ts +6 -2
  106. package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +45 -35
  107. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +24 -21
  108. package/src/interceptors/fetch/index.ts +61 -50
  109. package/src/utils/handleRequest.ts +65 -95
  110. package/lib/browser/chunk-3RXCRGL2.mjs.map +0 -1
  111. package/lib/browser/chunk-F2NPA2FP.js.map +0 -1
  112. package/lib/browser/chunk-MDMPOBGY.mjs.map +0 -1
  113. package/lib/browser/chunk-MMKGBEJO.mjs +0 -246
  114. package/lib/browser/chunk-MMKGBEJO.mjs.map +0 -1
  115. package/lib/browser/chunk-PFGO5BSM.js.map +0 -1
  116. package/lib/browser/chunk-T7TBRNJZ.js +0 -117
  117. package/lib/browser/chunk-T7TBRNJZ.js.map +0 -1
  118. package/lib/browser/chunk-TIPR373R.js.map +0 -1
  119. package/lib/browser/chunk-TX5GBTFY.mjs.map +0 -1
  120. package/lib/browser/chunk-XX6WKANU.js +0 -246
  121. package/lib/browser/chunk-XX6WKANU.js.map +0 -1
  122. package/lib/browser/chunk-ZDGZFWQH.mjs.map +0 -1
  123. package/lib/browser/chunk-ZXAL3FMU.js.map +0 -1
  124. package/lib/node/chunk-3CNGDJFB.mjs.map +0 -1
  125. package/lib/node/chunk-3GJB4JDF.mjs +0 -14
  126. package/lib/node/chunk-4NEYTVWD.mjs.map +0 -1
  127. package/lib/node/chunk-72ZIHMEB.js +0 -249
  128. package/lib/node/chunk-72ZIHMEB.js.map +0 -1
  129. package/lib/node/chunk-A7Q4RTDJ.mjs.map +0 -1
  130. package/lib/node/chunk-A7U44ARP.js.map +0 -1
  131. package/lib/node/chunk-EKNRB5ZS.mjs.map +0 -1
  132. package/lib/node/chunk-IHJSPMYM.mjs.map +0 -1
  133. package/lib/node/chunk-N4ZZFE24.js.map +0 -1
  134. package/lib/node/chunk-SMXZPJEA.js +0 -14
  135. package/lib/node/chunk-SMXZPJEA.js.map +0 -1
  136. package/lib/node/chunk-VV2LUF5K.js.map +0 -1
  137. package/lib/node/chunk-Z5LWCBZS.js.map +0 -1
  138. package/src/utils/RequestController.ts +0 -21
  139. /package/lib/node/{chunk-TJDMZZXE.mjs.map → chunk-GKN5RBVR.mjs.map} +0 -0
  140. /package/lib/node/{chunk-R6JVCM7X.js.map → chunk-J5MULIHT.js.map} +0 -0
  141. /package/lib/node/{chunk-RC2XPCC4.mjs.map → chunk-SQ6RHTJR.mjs.map} +0 -0
  142. /package/lib/node/{chunk-4YBV77DG.js.map → chunk-T3TW4P64.js.map} +0 -0
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../../chunk-EKNRB5ZS.mjs";
4
- import "../../chunk-TJDMZZXE.mjs";
5
- import "../../chunk-A7Q4RTDJ.mjs";
6
- import "../../chunk-IHJSPMYM.mjs";
7
- import "../../chunk-3GJB4JDF.mjs";
3
+ } from "../../chunk-5UGIB6OX.mjs";
4
+ import "../../chunk-GKN5RBVR.mjs";
5
+ import "../../chunk-R6T7CL5E.mjs";
6
+ import "../../chunk-JXGB54LE.mjs";
7
+ import "../../chunk-YWNGXXUQ.mjs";
8
8
  export {
9
9
  ClientRequestInterceptor
10
10
  };
@@ -1,6 +1,5 @@
1
1
  import { Emitter } from 'strict-event-emitter';
2
- import { H as HttpRequestEventMap, h as Interceptor } from '../../Interceptor-bc5a9d8e.js';
3
- import '@open-draft/deferred-promise';
2
+ import { H as HttpRequestEventMap, i as Interceptor } from '../../Interceptor-dc0a39b5.js';
4
3
  import '@open-draft/logger';
5
4
 
6
5
  type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>;
@@ -1,13 +1,13 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkVV2LUF5Kjs = require('../../chunk-VV2LUF5K.js');
3
+ var _chunk6B3ZQOO2js = require('../../chunk-6B3ZQOO2.js');
4
4
  require('../../chunk-LK6DILFK.js');
5
5
  require('../../chunk-PFGO5BSM.js');
6
6
  require('../../chunk-73NOP3T5.js');
7
- require('../../chunk-72ZIHMEB.js');
8
- require('../../chunk-A7U44ARP.js');
9
- require('../../chunk-SMXZPJEA.js');
7
+ require('../../chunk-7Q53NNPV.js');
8
+ require('../../chunk-DOWWQYXZ.js');
9
+ require('../../chunk-SRMAQGPM.js');
10
10
 
11
11
 
12
- exports.XMLHttpRequestInterceptor = _chunkVV2LUF5Kjs.XMLHttpRequestInterceptor;
12
+ exports.XMLHttpRequestInterceptor = _chunk6B3ZQOO2js.XMLHttpRequestInterceptor;
13
13
  //# sourceMappingURL=index.js.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  XMLHttpRequestInterceptor
3
- } from "../../chunk-4NEYTVWD.mjs";
3
+ } from "../../chunk-5V3SIIW2.mjs";
4
4
  import "../../chunk-6HYIRFX2.mjs";
5
5
  import "../../chunk-TX5GBTFY.mjs";
6
6
  import "../../chunk-6YM4PLBI.mjs";
7
- import "../../chunk-A7Q4RTDJ.mjs";
8
- import "../../chunk-IHJSPMYM.mjs";
9
- import "../../chunk-3GJB4JDF.mjs";
7
+ import "../../chunk-R6T7CL5E.mjs";
8
+ import "../../chunk-JXGB54LE.mjs";
9
+ import "../../chunk-YWNGXXUQ.mjs";
10
10
  export {
11
11
  XMLHttpRequestInterceptor
12
12
  };
@@ -1,5 +1,4 @@
1
- import { h as Interceptor, H as HttpRequestEventMap } from '../../Interceptor-bc5a9d8e.js';
2
- import '@open-draft/deferred-promise';
1
+ import { i as Interceptor, H as HttpRequestEventMap } from '../../Interceptor-dc0a39b5.js';
3
2
  import '@open-draft/logger';
4
3
  import 'strict-event-emitter';
5
4
 
@@ -1,12 +1,12 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkZ5LWCBZSjs = require('../../chunk-Z5LWCBZS.js');
3
+ var _chunkFRZQJNBOjs = require('../../chunk-FRZQJNBO.js');
4
4
  require('../../chunk-PFGO5BSM.js');
5
5
  require('../../chunk-73NOP3T5.js');
6
- require('../../chunk-72ZIHMEB.js');
7
- require('../../chunk-A7U44ARP.js');
8
- require('../../chunk-SMXZPJEA.js');
6
+ require('../../chunk-7Q53NNPV.js');
7
+ require('../../chunk-DOWWQYXZ.js');
8
+ require('../../chunk-SRMAQGPM.js');
9
9
 
10
10
 
11
- exports.FetchInterceptor = _chunkZ5LWCBZSjs.FetchInterceptor;
11
+ exports.FetchInterceptor = _chunkFRZQJNBOjs.FetchInterceptor;
12
12
  //# sourceMappingURL=index.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  FetchInterceptor
3
- } from "../../chunk-3CNGDJFB.mjs";
3
+ } from "../../chunk-OFW5L5ET.mjs";
4
4
  import "../../chunk-TX5GBTFY.mjs";
5
5
  import "../../chunk-6YM4PLBI.mjs";
6
- import "../../chunk-A7Q4RTDJ.mjs";
7
- import "../../chunk-IHJSPMYM.mjs";
8
- import "../../chunk-3GJB4JDF.mjs";
6
+ import "../../chunk-R6T7CL5E.mjs";
7
+ import "../../chunk-JXGB54LE.mjs";
8
+ import "../../chunk-YWNGXXUQ.mjs";
9
9
  export {
10
10
  FetchInterceptor
11
11
  };
@@ -1,8 +1,7 @@
1
1
  import { ClientRequestInterceptor } from '../interceptors/ClientRequest/index.js';
2
2
  import { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest/index.js';
3
3
  import { FetchInterceptor } from '../interceptors/fetch/index.js';
4
- import '../Interceptor-bc5a9d8e.js';
5
- import '@open-draft/deferred-promise';
4
+ import '../Interceptor-dc0a39b5.js';
6
5
  import '@open-draft/logger';
7
6
  import 'strict-event-emitter';
8
7
  import 'node:net';
@@ -1,25 +1,25 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkN4ZZFE24js = require('../chunk-N4ZZFE24.js');
4
- require('../chunk-4YBV77DG.js');
3
+ var _chunkVYO5XDY2js = require('../chunk-VYO5XDY2.js');
4
+ require('../chunk-T3TW4P64.js');
5
5
 
6
6
 
7
- var _chunkVV2LUF5Kjs = require('../chunk-VV2LUF5K.js');
7
+ var _chunk6B3ZQOO2js = require('../chunk-6B3ZQOO2.js');
8
8
  require('../chunk-LK6DILFK.js');
9
9
 
10
10
 
11
- var _chunkZ5LWCBZSjs = require('../chunk-Z5LWCBZS.js');
11
+ var _chunkFRZQJNBOjs = require('../chunk-FRZQJNBO.js');
12
12
  require('../chunk-PFGO5BSM.js');
13
13
  require('../chunk-73NOP3T5.js');
14
- require('../chunk-72ZIHMEB.js');
15
- require('../chunk-A7U44ARP.js');
16
- require('../chunk-SMXZPJEA.js');
14
+ require('../chunk-7Q53NNPV.js');
15
+ require('../chunk-DOWWQYXZ.js');
16
+ require('../chunk-SRMAQGPM.js');
17
17
 
18
18
  // src/presets/node.ts
19
19
  var node_default = [
20
- new (0, _chunkN4ZZFE24js.ClientRequestInterceptor)(),
21
- new (0, _chunkVV2LUF5Kjs.XMLHttpRequestInterceptor)(),
22
- new (0, _chunkZ5LWCBZSjs.FetchInterceptor)()
20
+ new (0, _chunkVYO5XDY2js.ClientRequestInterceptor)(),
21
+ new (0, _chunk6B3ZQOO2js.XMLHttpRequestInterceptor)(),
22
+ new (0, _chunkFRZQJNBOjs.FetchInterceptor)()
23
23
  ];
24
24
 
25
25
 
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../chunk-EKNRB5ZS.mjs";
4
- import "../chunk-TJDMZZXE.mjs";
3
+ } from "../chunk-5UGIB6OX.mjs";
4
+ import "../chunk-GKN5RBVR.mjs";
5
5
  import {
6
6
  XMLHttpRequestInterceptor
7
- } from "../chunk-4NEYTVWD.mjs";
7
+ } from "../chunk-5V3SIIW2.mjs";
8
8
  import "../chunk-6HYIRFX2.mjs";
9
9
  import {
10
10
  FetchInterceptor
11
- } from "../chunk-3CNGDJFB.mjs";
11
+ } from "../chunk-OFW5L5ET.mjs";
12
12
  import "../chunk-TX5GBTFY.mjs";
13
13
  import "../chunk-6YM4PLBI.mjs";
14
- import "../chunk-A7Q4RTDJ.mjs";
15
- import "../chunk-IHJSPMYM.mjs";
16
- import "../chunk-3GJB4JDF.mjs";
14
+ import "../chunk-R6T7CL5E.mjs";
15
+ import "../chunk-JXGB54LE.mjs";
16
+ import "../chunk-YWNGXXUQ.mjs";
17
17
 
18
18
  // src/presets/node.ts
19
19
  var node_default = [
@@ -1,10 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
3
 
4
- var _chunk4YBV77DGjs = require('../../chunk-4YBV77DG.js');
5
- require('../../chunk-SMXZPJEA.js');
4
+ var _chunkT3TW4P64js = require('../../chunk-T3TW4P64.js');
5
+ require('../../chunk-SRMAQGPM.js');
6
6
 
7
7
 
8
8
 
9
- exports.getClientRequestBodyStream = _chunk4YBV77DGjs.getClientRequestBodyStream; exports.setRawRequestBodyStream = _chunk4YBV77DGjs.setRawRequestBodyStream;
9
+ exports.getClientRequestBodyStream = _chunkT3TW4P64js.getClientRequestBodyStream; exports.setRawRequestBodyStream = _chunkT3TW4P64js.setRawRequestBodyStream;
10
10
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  getClientRequestBodyStream,
3
3
  setRawRequestBodyStream
4
- } from "../../chunk-TJDMZZXE.mjs";
5
- import "../../chunk-3GJB4JDF.mjs";
4
+ } from "../../chunk-GKN5RBVR.mjs";
5
+ import "../../chunk-YWNGXXUQ.mjs";
6
6
  export {
7
7
  getClientRequestBodyStream,
8
8
  setRawRequestBodyStream
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mswjs/interceptors",
3
3
  "description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
4
- "version": "0.39.7",
4
+ "version": "0.40.0",
5
5
  "main": "./lib/node/index.js",
6
6
  "module": "./lib/node/index.mjs",
7
7
  "types": "./lib/node/index.d.ts",
@@ -204,6 +204,7 @@
204
204
  "test:integration": "pnpm test:node && pnpm test:browser",
205
205
  "test:node": "vitest -c test/vitest.config.js",
206
206
  "test:browser": "pnpm playwright test -c test/playwright.config.ts",
207
+ "test:nock": "./test/third-party/nock.sh",
207
208
  "clean": "rimraf lib",
208
209
  "build": "pnpm clean && cross-env NODE_ENV=production tsup --splitting",
209
210
  "release": "release publish"
@@ -8,6 +8,7 @@ import { FetchInterceptor } from './interceptors/fetch'
8
8
  import { handleRequest } from './utils/handleRequest'
9
9
  import { RequestController } from './RequestController'
10
10
  import { FetchResponse } from './utils/fetchUtils'
11
+ import { isResponseError } from './utils/responseUtils'
11
12
 
12
13
  export interface SerializedRequest {
13
14
  id: string
@@ -178,13 +179,14 @@ export class RemoteHttpResolver extends Interceptor<HttpRequestEventMap> {
178
179
  body: requestJson.body,
179
180
  })
180
181
 
181
- const controller = new RequestController(request)
182
- await handleRequest({
183
- request,
184
- requestId: requestJson.id,
185
- controller,
186
- emitter: this.emitter,
187
- onResponse: async (response) => {
182
+ const controller = new RequestController(request, {
183
+ passthrough: () => {},
184
+ respondWith: async (response) => {
185
+ if (isResponseError(response)) {
186
+ this.logger.info('received a network error!', { response })
187
+ throw new Error('Not implemented')
188
+ }
189
+
188
190
  this.logger.info('received mocked response!', { response })
189
191
 
190
192
  const responseClone = response.clone()
@@ -221,15 +223,18 @@ export class RemoteHttpResolver extends Interceptor<HttpRequestEventMap> {
221
223
  serializedResponse
222
224
  )
223
225
  },
224
- onRequestError: (response) => {
225
- this.logger.info('received a network error!', { response })
226
- throw new Error('Not implemented')
227
- },
228
- onError: (error) => {
229
- this.logger.info('request has errored!', { error })
226
+ errorWith: (reason) => {
227
+ this.logger.info('request has errored!', { error: reason })
230
228
  throw new Error('Not implemented')
231
229
  },
232
230
  })
231
+
232
+ await handleRequest({
233
+ request,
234
+ requestId: requestJson.id,
235
+ controller,
236
+ emitter: this.emitter,
237
+ })
233
238
  }
234
239
 
235
240
  this.subscriptions.push(() => {
@@ -1,57 +1,104 @@
1
- import { it, expect } from 'vitest'
2
- import { kResponsePromise, RequestController } from './RequestController'
1
+ import { vi, it, expect } from 'vitest'
2
+ import {
3
+ RequestController,
4
+ type RequestControllerSource,
5
+ } from './RequestController'
6
+ import { InterceptorError } from './InterceptorError'
3
7
 
4
- it('creates a pending response promise on construction', () => {
5
- const controller = new RequestController(new Request('http://localhost'))
6
- expect(controller[kResponsePromise]).toBeInstanceOf(Promise)
7
- expect(controller[kResponsePromise].state).toBe('pending')
8
+ const defaultSource = {
9
+ passthrough() {},
10
+ respondWith() {},
11
+ errorWith() {},
12
+ } satisfies RequestControllerSource
13
+
14
+ it('has a pending state upon construction', () => {
15
+ const controller = new RequestController(
16
+ new Request('http://localhost'),
17
+ defaultSource
18
+ )
19
+
20
+ expect(controller.handled).toBeInstanceOf(Promise)
21
+ expect(controller.readyState).toBe(RequestController.PENDING)
8
22
  })
9
23
 
10
- it('resolves the response promise with the response provided to "respondWith"', async () => {
11
- const controller = new RequestController(new Request('http://localhost'))
12
- controller.respondWith(new Response('hello world'))
24
+ it('handles a request when calling ".respondWith()" with a mocked response', async () => {
25
+ const respondWith = vi.fn<RequestControllerSource['respondWith']>()
26
+ const controller = new RequestController(new Request('http://localhost'), {
27
+ ...defaultSource,
28
+ respondWith,
29
+ })
30
+
31
+ await controller.respondWith(new Response('hello world'))
32
+
33
+ expect(controller.readyState).toBe(RequestController.RESPONSE)
34
+ await expect(controller.handled).resolves.toBeUndefined()
13
35
 
14
- const response = (await controller[kResponsePromise]) as Response
36
+ expect(respondWith).toHaveBeenCalledOnce()
37
+ const [response] = respondWith.mock.calls[0]
15
38
 
16
39
  expect(response).toBeInstanceOf(Response)
17
40
  expect(response.status).toBe(200)
18
- expect(await response.text()).toBe('hello world')
41
+ await expect(response.text()).resolves.toBe('hello world')
19
42
  })
20
43
 
21
- it('resolves the response promise with the error provided to "errorWith"', async () => {
22
- const controller = new RequestController(new Request('http://localhost'))
44
+ it('handles the request when calling ".errorWith()" with an error', async () => {
45
+ const errorWith = vi.fn<RequestControllerSource['errorWith']>()
46
+ const controller = new RequestController(new Request('http://localhost'), {
47
+ ...defaultSource,
48
+ errorWith,
49
+ })
50
+
23
51
  const error = new Error('Oops!')
24
- controller.errorWith(error)
52
+ await controller.errorWith(error)
53
+
54
+ expect(controller.readyState).toBe(RequestController.ERROR)
55
+ await expect(controller.handled).resolves.toBeUndefined()
25
56
 
26
- await expect(controller[kResponsePromise]).resolves.toEqual(error)
57
+ expect(errorWith).toHaveBeenCalledOnce()
58
+ expect(errorWith).toHaveBeenCalledWith(error)
27
59
  })
28
60
 
29
- it('resolves the response promise with an arbitrary object provided to "errorWith"', async () => {
30
- const controller = new RequestController(new Request('http://localhost'))
61
+ it('handles the request when calling ".errorWith()" with an arbitrary object', async () => {
62
+ const errorWith = vi.fn<RequestControllerSource['errorWith']>()
63
+ const controller = new RequestController(new Request('http://localhost'), {
64
+ ...defaultSource,
65
+ errorWith,
66
+ })
67
+
31
68
  const error = { message: 'Oops!' }
32
- controller.errorWith(error)
69
+ await controller.errorWith(error)
33
70
 
34
- await expect(controller[kResponsePromise]).resolves.toEqual(error)
71
+ expect(controller.readyState).toBe(RequestController.ERROR)
72
+ await expect(controller.handled).resolves.toBeUndefined()
73
+
74
+ expect(errorWith).toHaveBeenCalledOnce()
75
+ expect(errorWith).toHaveBeenCalledWith(error)
35
76
  })
36
77
 
37
- it('throws when calling "respondWith" multiple times', () => {
38
- const controller = new RequestController(new Request('http://localhost'))
78
+ it('throws when calling "respondWith" multiple times', async () => {
79
+ const controller = new RequestController(
80
+ new Request('http://localhost'),
81
+ defaultSource
82
+ )
39
83
  controller.respondWith(new Response('hello world'))
40
84
 
41
- expect(() => {
42
- controller.respondWith(new Response('second response'))
43
- }).toThrow(
44
- 'Failed to respond to the "GET http://localhost/" request: the "request" event has already been handled.'
85
+ expect(() => controller.respondWith(new Response('second response'))).toThrow(
86
+ new InterceptorError(
87
+ 'Failed to respond to the "GET http://localhost/" request with "200 OK": the request has already been handled (2)'
88
+ )
45
89
  )
46
90
  })
47
91
 
48
- it('throws when calling "errorWith" multiple times', () => {
49
- const controller = new RequestController(new Request('http://localhost'))
92
+ it('throws when calling "errorWith" multiple times', async () => {
93
+ const controller = new RequestController(
94
+ new Request('http://localhost'),
95
+ defaultSource
96
+ )
50
97
  controller.errorWith(new Error('Oops!'))
51
98
 
52
- expect(() => {
53
- controller.errorWith(new Error('second error'))
54
- }).toThrow(
55
- 'Failed to error the "GET http://localhost/" request: the "request" event has already been handled.'
99
+ expect(() => controller.errorWith(new Error('second error'))).toThrow(
100
+ new InterceptorError(
101
+ 'Failed to error the "GET http://localhost/" request with "Error: second error": the request has already been handled (3)'
102
+ )
56
103
  )
57
104
  })
@@ -1,35 +1,59 @@
1
- import { invariant } from 'outvariant'
2
1
  import { DeferredPromise } from '@open-draft/deferred-promise'
2
+ import { invariant } from 'outvariant'
3
3
  import { InterceptorError } from './InterceptorError'
4
4
 
5
- const kRequestHandled = Symbol('kRequestHandled')
6
- export const kResponsePromise = Symbol('kResponsePromise')
5
+ export interface RequestControllerSource {
6
+ passthrough(): void
7
+ respondWith(response: Response): void
8
+ errorWith(reason?: unknown): void
9
+ }
7
10
 
8
11
  export class RequestController {
12
+ static PENDING = 0 as const
13
+ static PASSTHROUGH = 1 as const
14
+ static RESPONSE = 2 as const
15
+ static ERROR = 3 as const
16
+
17
+ public readyState: number
18
+
9
19
  /**
10
- * Internal response promise.
11
- * Available only for the library internals to grab the
12
- * response instance provided by the developer.
13
- * @note This promise cannot be rejected. It's either infinitely
14
- * pending or resolved with whichever Response was passed to `respondWith()`.
20
+ * A Promise that resolves when this controller handles a request.
21
+ * See `controller.readyState` for more information on the handling result.
15
22
  */
16
- [kResponsePromise]: DeferredPromise<
17
- Response | Record<string, any> | undefined
18
- >;
23
+ public handled: Promise<void>
24
+
25
+ constructor(
26
+ protected readonly request: Request,
27
+ protected readonly source: RequestControllerSource
28
+ ) {
29
+ this.readyState = RequestController.PENDING
30
+ this.handled = new DeferredPromise<void>()
31
+ }
32
+
33
+ get #handled() {
34
+ return this.handled as DeferredPromise<void>
35
+ }
19
36
 
20
37
  /**
21
- * Internal flag indicating if this request has been handled.
22
- * @note The response promise becomes "fulfilled" on the next tick.
38
+ * Perform this request as-is.
23
39
  */
24
- [kRequestHandled]: boolean
40
+ public async passthrough(): Promise<void> {
41
+ invariant.as(
42
+ InterceptorError,
43
+ this.readyState === RequestController.PENDING,
44
+ 'Failed to passthrough the "%s %s" request: the request has already been handled',
45
+ this.request.method,
46
+ this.request.url
47
+ )
25
48
 
26
- constructor(private request: Request) {
27
- this[kRequestHandled] = false
28
- this[kResponsePromise] = new DeferredPromise()
49
+ this.readyState = RequestController.PASSTHROUGH
50
+ await this.source.passthrough()
51
+ this.#handled.resolve()
29
52
  }
30
53
 
31
54
  /**
32
55
  * Respond to this request with the given `Response` instance.
56
+ *
33
57
  * @example
34
58
  * controller.respondWith(new Response())
35
59
  * controller.respondWith(Response.json({ id }))
@@ -38,22 +62,25 @@ export class RequestController {
38
62
  public respondWith(response: Response): void {
39
63
  invariant.as(
40
64
  InterceptorError,
41
- !this[kRequestHandled],
42
- 'Failed to respond to the "%s %s" request: the "request" event has already been handled.',
65
+ this.readyState === RequestController.PENDING,
66
+ 'Failed to respond to the "%s %s" request with "%d %s": the request has already been handled (%d)',
43
67
  this.request.method,
44
- this.request.url
68
+ this.request.url,
69
+ response.status,
70
+ response.statusText || 'OK',
71
+ this.readyState
45
72
  )
46
73
 
47
- this[kRequestHandled] = true
48
- this[kResponsePromise].resolve(response)
74
+ this.readyState = RequestController.RESPONSE
75
+ this.#handled.resolve()
49
76
 
50
77
  /**
51
- * @note The request controller doesn't do anything
52
- * apart from letting the interceptor await the response
53
- * provided by the developer through the response promise.
54
- * Each interceptor implements the actual respondWith/errorWith
55
- * logic based on that interceptor's needs.
78
+ * @note Although `source.respondWith()` is potentially asynchronous,
79
+ * do NOT await it for backward-compatibility. Awaiting it will short-circuit
80
+ * the request listener invocation as soon as a listener responds to a request.
81
+ * Ideally, that's what we want, but that's not what we promise the user.
56
82
  */
83
+ this.source.respondWith(response)
57
84
  }
58
85
 
59
86
  /**
@@ -64,22 +91,19 @@ export class RequestController {
64
91
  * controller.errorWith(new Error('Oops!'))
65
92
  * controller.errorWith({ message: 'Oops!'})
66
93
  */
67
- public errorWith(reason?: Error | Record<string, any>): void {
94
+ public errorWith(reason?: unknown): void {
68
95
  invariant.as(
69
96
  InterceptorError,
70
- !this[kRequestHandled],
71
- 'Failed to error the "%s %s" request: the "request" event has already been handled.',
97
+ this.readyState === RequestController.PENDING,
98
+ 'Failed to error the "%s %s" request with "%s": the request has already been handled (%d)',
72
99
  this.request.method,
73
- this.request.url
100
+ this.request.url,
101
+ reason?.toString(),
102
+ this.readyState
74
103
  )
75
104
 
76
- this[kRequestHandled] = true
77
-
78
- /**
79
- * @note Resolve the response promise, not reject.
80
- * This helps us differentiate between unhandled exceptions
81
- * and intended errors ("errorWith") while waiting for the response.
82
- */
83
- this[kResponsePromise].resolve(reason)
105
+ this.readyState = RequestController.ERROR
106
+ this.source.errorWith(reason)
107
+ this.#handled.resolve()
84
108
  }
85
109
  }
package/src/index.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export * from './glossary'
2
2
  export * from './Interceptor'
3
3
  export * from './BatchInterceptor'
4
+ export {
5
+ RequestController,
6
+ type RequestControllerSource,
7
+ } from './RequestController'
4
8
 
5
9
  /* Utils */
6
10
  export { createRequestId } from './createRequestId'
@@ -146,6 +146,7 @@ export class MockHttpSocket extends MockSocket {
146
146
  Reflect.set(this, 'getProtocol', () => 'TLSv1.3')
147
147
  Reflect.set(this, 'getSession', () => undefined)
148
148
  Reflect.set(this, 'isSessionReused', () => false)
149
+ Reflect.set(this, 'getCipher', () => ({ name: 'AES256-SHA', standardName: 'TLS_RSA_WITH_AES_256_CBC_SHA', version: 'TLSv1.3' }))
149
150
  }
150
151
  }
151
152
 
@@ -258,6 +259,7 @@ export class MockHttpSocket extends MockSocket {
258
259
  'getProtocol',
259
260
  'getSession',
260
261
  'isSessionReused',
262
+ 'getCipher'
261
263
  ]
262
264
 
263
265
  tlsProperties.forEach((propertyName) => {
@@ -311,6 +313,16 @@ export class MockHttpSocket extends MockSocket {
311
313
  return
312
314
  }
313
315
 
316
+ // Prevent recursive calls.
317
+ invariant(
318
+ this.socketState !== 'mock',
319
+ '[MockHttpSocket] Failed to respond to the "%s %s" request with "%s %s": the request has already been handled',
320
+ this.request?.method,
321
+ this.request?.url,
322
+ response.status,
323
+ response.statusText
324
+ )
325
+
314
326
  // Handle "type: error" responses.
315
327
  if (isPropertyAccessible(response, 'type') && response.type === 'error') {
316
328
  this.errorWith(new TypeError('Network error'))
@@ -393,9 +405,18 @@ export class MockHttpSocket extends MockSocket {
393
405
  serverResponse.write(value)
394
406
  }
395
407
  } catch (error) {
396
- // Coerce response stream errors to 500 responses.
397
- this.respondWith(createServerErrorResponse(error))
398
- return
408
+ if (error instanceof Error) {
409
+ serverResponse.destroy()
410
+ /**
411
+ * @note Destroy the request socket gracefully.
412
+ * Response stream errors do NOT produce request errors.
413
+ */
414
+ this.destroy()
415
+ return
416
+ }
417
+
418
+ serverResponse.destroy()
419
+ throw error
399
420
  }
400
421
  } else {
401
422
  serverResponse.end()