@mswjs/interceptors 0.41.4 → 0.41.5

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 (153) hide show
  1. package/lib/browser/{XMLHttpRequest-C2KXXdBy.cjs → XMLHttpRequest-DSAhl6_a.cjs} +9 -21
  2. package/lib/browser/XMLHttpRequest-DSAhl6_a.cjs.map +1 -0
  3. package/lib/browser/{XMLHttpRequest-Bbvn_AjX.mjs → XMLHttpRequest-DYyDAIJh.mjs} +9 -21
  4. package/lib/{node/XMLHttpRequest-BBbs-p9i.mjs.map → browser/XMLHttpRequest-DYyDAIJh.mjs.map} +1 -1
  5. package/lib/browser/{createRequestId-Cs4oXfa1.cjs → createRequestId-DOf8Ktjs.cjs} +1 -1
  6. package/lib/browser/{createRequestId-Cs4oXfa1.cjs.map → createRequestId-DOf8Ktjs.cjs.map} +1 -1
  7. package/lib/browser/{createRequestId-DQcIlohW.mjs → createRequestId-DYCsFHOi.mjs} +1 -1
  8. package/lib/browser/{createRequestId-DQcIlohW.mjs.map → createRequestId-DYCsFHOi.mjs.map} +1 -1
  9. package/lib/browser/{fetch-CJYWVliE.cjs → fetch-BMC2en2x.cjs} +10 -18
  10. package/lib/browser/fetch-BMC2en2x.cjs.map +1 -0
  11. package/lib/browser/{fetch-D4es-Apw.mjs → fetch-D5LGhx5i.mjs} +10 -18
  12. package/lib/browser/fetch-D5LGhx5i.mjs.map +1 -0
  13. package/lib/browser/{getRawRequest-DaxX6uMy.cjs → getRawRequest-B9kRTvjs.cjs} +1 -11
  14. package/lib/browser/getRawRequest-B9kRTvjs.cjs.map +1 -0
  15. package/lib/browser/{getRawRequest-Df640uw8.mjs → getRawRequest-BEw5hEOW.mjs} +2 -6
  16. package/lib/browser/getRawRequest-BEw5hEOW.mjs.map +1 -0
  17. package/lib/browser/{glossary-BdLS4k1H.d.cts → glossary-1lhZx6Aw.d.cts} +2 -3
  18. package/lib/browser/{glossary-DYwOrogs.d.mts → glossary-nkHWcwvY.d.mts} +2 -3
  19. package/lib/browser/{handleRequest--veikAod.cjs → handleRequest-BCtqvFSS.cjs} +3 -3
  20. package/lib/browser/{handleRequest--veikAod.cjs.map → handleRequest-BCtqvFSS.cjs.map} +1 -1
  21. package/lib/browser/{handleRequest-Dq1a55b4.mjs → handleRequest-DuUoGpfA.mjs} +3 -3
  22. package/lib/browser/{handleRequest-Dq1a55b4.mjs.map → handleRequest-DuUoGpfA.mjs.map} +1 -1
  23. package/lib/browser/hasConfigurableGlobal-B39W0thh.mjs +98 -0
  24. package/lib/browser/hasConfigurableGlobal-B39W0thh.mjs.map +1 -0
  25. package/lib/browser/hasConfigurableGlobal-B5o6PHq3.cjs +115 -0
  26. package/lib/browser/hasConfigurableGlobal-B5o6PHq3.cjs.map +1 -0
  27. package/lib/browser/index.cjs +2 -3
  28. package/lib/browser/index.d.cts +2 -2
  29. package/lib/browser/index.d.mts +2 -2
  30. package/lib/browser/index.mjs +3 -3
  31. package/lib/browser/interceptors/WebSocket/index.cjs +6 -10
  32. package/lib/browser/interceptors/WebSocket/index.cjs.map +1 -1
  33. package/lib/browser/interceptors/WebSocket/index.mjs +6 -10
  34. package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -1
  35. package/lib/browser/interceptors/XMLHttpRequest/index.cjs +5 -4
  36. package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +1 -1
  37. package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +1 -1
  38. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +5 -4
  39. package/lib/browser/interceptors/fetch/index.cjs +5 -4
  40. package/lib/browser/interceptors/fetch/index.d.cts +1 -1
  41. package/lib/browser/interceptors/fetch/index.d.mts +1 -1
  42. package/lib/browser/interceptors/fetch/index.mjs +5 -4
  43. package/lib/browser/presets/browser.cjs +6 -5
  44. package/lib/browser/presets/browser.cjs.map +1 -1
  45. package/lib/browser/presets/browser.mjs +6 -5
  46. package/lib/browser/presets/browser.mjs.map +1 -1
  47. package/lib/node/{BatchInterceptor-7u9fqQIe.cjs → BatchInterceptor-AZr7q8p_.cjs} +2 -2
  48. package/lib/node/{BatchInterceptor-7u9fqQIe.cjs.map → BatchInterceptor-AZr7q8p_.cjs.map} +1 -1
  49. package/lib/node/{BatchInterceptor-BxgAXyLw.mjs → BatchInterceptor-B2YAn5m9.mjs} +2 -2
  50. package/lib/node/{BatchInterceptor-BxgAXyLw.mjs.map → BatchInterceptor-B2YAn5m9.mjs.map} +1 -1
  51. package/lib/node/{BatchInterceptor-D7mXzHcQ.d.mts → BatchInterceptor-KKF8Q47I.d.cts} +2 -2
  52. package/lib/node/{BatchInterceptor-D_YqR8qU.d.cts → BatchInterceptor-rSr-NH_H.d.mts} +2 -2
  53. package/lib/node/{ClientRequest-Cl3Ky7Tw.mjs → ClientRequest-5WjHvs9Y.mjs} +5 -5
  54. package/lib/node/{ClientRequest-Cl3Ky7Tw.mjs.map → ClientRequest-5WjHvs9Y.mjs.map} +1 -1
  55. package/lib/node/{ClientRequest-CMSXChAM.cjs → ClientRequest-BgwQG-9G.cjs} +5 -5
  56. package/lib/node/{ClientRequest-CMSXChAM.cjs.map → ClientRequest-BgwQG-9G.cjs.map} +1 -1
  57. package/lib/node/{Interceptor-DJ2akVWI.d.cts → Interceptor-BsZ21ue0.d.cts} +2 -3
  58. package/lib/node/{Interceptor-DEazpLJd.d.mts → Interceptor-soD-WpQO.d.mts} +2 -3
  59. package/lib/node/RemoteHttpInterceptor.cjs +9 -8
  60. package/lib/node/RemoteHttpInterceptor.cjs.map +1 -1
  61. package/lib/node/RemoteHttpInterceptor.d.cts +3 -3
  62. package/lib/node/RemoteHttpInterceptor.d.mts +3 -3
  63. package/lib/node/RemoteHttpInterceptor.mjs +9 -8
  64. package/lib/node/RemoteHttpInterceptor.mjs.map +1 -1
  65. package/lib/node/{XMLHttpRequest-Dj3xJgke.cjs → XMLHttpRequest-B7sloBex.cjs} +10 -23
  66. package/lib/node/XMLHttpRequest-B7sloBex.cjs.map +1 -0
  67. package/lib/node/{XMLHttpRequest-BBbs-p9i.mjs → XMLHttpRequest-DmsdUGmj.mjs} +10 -23
  68. package/lib/{browser/XMLHttpRequest-Bbvn_AjX.mjs.map → node/XMLHttpRequest-DmsdUGmj.mjs.map} +1 -1
  69. package/lib/node/{bufferUtils-_8XfKIfX.mjs → bufferUtils-DxPxwff_.mjs} +1 -1
  70. package/lib/node/{bufferUtils-_8XfKIfX.mjs.map → bufferUtils-DxPxwff_.mjs.map} +1 -1
  71. package/lib/node/{bufferUtils-DiCTqG-7.cjs → bufferUtils-S5_-2eN4.cjs} +1 -1
  72. package/lib/node/{bufferUtils-DiCTqG-7.cjs.map → bufferUtils-S5_-2eN4.cjs.map} +1 -1
  73. package/lib/node/{fetch-DmiwAmpu.mjs → fetch-DDAGTtZi.mjs} +10 -19
  74. package/lib/node/fetch-DDAGTtZi.mjs.map +1 -0
  75. package/lib/node/{fetch-BoCFyaYx.cjs → fetch-uc7pNtPg.cjs} +10 -19
  76. package/lib/node/fetch-uc7pNtPg.cjs.map +1 -0
  77. package/lib/node/{fetchUtils-BTrdMHGp.mjs → fetchUtils-BJxD7wvl.mjs} +1 -1
  78. package/lib/node/{fetchUtils-BTrdMHGp.mjs.map → fetchUtils-BJxD7wvl.mjs.map} +1 -1
  79. package/lib/node/{fetchUtils-DE_q7tpr.cjs → fetchUtils-Dw6kQghF.cjs} +1 -1
  80. package/lib/node/{fetchUtils-DE_q7tpr.cjs.map → fetchUtils-Dw6kQghF.cjs.map} +1 -1
  81. package/lib/node/{getRawRequest-BavnMWh_.cjs → getRawRequest-B6znU6il.cjs} +1 -1
  82. package/lib/node/{getRawRequest-BavnMWh_.cjs.map → getRawRequest-B6znU6il.cjs.map} +1 -1
  83. package/lib/node/{getRawRequest-DnwmXyOW.mjs → getRawRequest-C2-1urzA.mjs} +1 -1
  84. package/lib/node/{getRawRequest-DnwmXyOW.mjs.map → getRawRequest-C2-1urzA.mjs.map} +1 -1
  85. package/lib/node/{handleRequest-CDabRyt1.mjs → handleRequest-DGfUg9bm.mjs} +2 -2
  86. package/lib/node/{handleRequest-CDabRyt1.mjs.map → handleRequest-DGfUg9bm.mjs.map} +1 -1
  87. package/lib/node/{handleRequest-pLIe3KKo.cjs → handleRequest-Dzg1d3e5.cjs} +2 -2
  88. package/lib/node/{handleRequest-pLIe3KKo.cjs.map → handleRequest-Dzg1d3e5.cjs.map} +1 -1
  89. package/lib/node/hasConfigurableGlobal-Befl_QJD.cjs +97 -0
  90. package/lib/node/hasConfigurableGlobal-Befl_QJD.cjs.map +1 -0
  91. package/lib/node/hasConfigurableGlobal-jN_-v3gp.mjs +85 -0
  92. package/lib/node/hasConfigurableGlobal-jN_-v3gp.mjs.map +1 -0
  93. package/lib/node/{index-BMbJ8FXL.d.cts → index-BJ5E5aW-.d.cts} +2 -2
  94. package/lib/node/{index-C0YAQ36w.d.mts → index-DfoF45ih.d.mts} +2 -2
  95. package/lib/node/index.cjs +4 -6
  96. package/lib/node/index.cjs.map +1 -1
  97. package/lib/node/index.d.cts +3 -3
  98. package/lib/node/index.d.mts +3 -3
  99. package/lib/node/index.mjs +5 -6
  100. package/lib/node/index.mjs.map +1 -1
  101. package/lib/node/interceptors/ClientRequest/index.cjs +4 -4
  102. package/lib/node/interceptors/ClientRequest/index.d.cts +1 -1
  103. package/lib/node/interceptors/ClientRequest/index.d.mts +2 -2
  104. package/lib/node/interceptors/ClientRequest/index.mjs +4 -4
  105. package/lib/node/interceptors/XMLHttpRequest/index.cjs +5 -4
  106. package/lib/node/interceptors/XMLHttpRequest/index.d.cts +1 -1
  107. package/lib/node/interceptors/XMLHttpRequest/index.d.mts +1 -1
  108. package/lib/node/interceptors/XMLHttpRequest/index.mjs +5 -4
  109. package/lib/node/interceptors/fetch/index.cjs +4 -3
  110. package/lib/node/interceptors/fetch/index.d.cts +1 -1
  111. package/lib/node/interceptors/fetch/index.d.mts +1 -1
  112. package/lib/node/interceptors/fetch/index.mjs +4 -3
  113. package/lib/node/{node-dKdAf3tC.cjs → node-DIKcnzhK.cjs} +2 -2
  114. package/lib/node/{node-dKdAf3tC.cjs.map → node-DIKcnzhK.cjs.map} +1 -1
  115. package/lib/node/{node-DwCc6iuP.mjs → node-lsdNwZEW.mjs} +2 -2
  116. package/lib/node/{node-DwCc6iuP.mjs.map → node-lsdNwZEW.mjs.map} +1 -1
  117. package/lib/node/presets/node.cjs +8 -7
  118. package/lib/node/presets/node.cjs.map +1 -1
  119. package/lib/node/presets/node.d.cts +1 -1
  120. package/lib/node/presets/node.d.mts +2 -2
  121. package/lib/node/presets/node.mjs +8 -7
  122. package/lib/node/presets/node.mjs.map +1 -1
  123. package/lib/node/utils/node/index.cjs +1 -1
  124. package/lib/node/utils/node/index.mjs +1 -1
  125. package/package.json +1 -1
  126. package/src/glossary.ts +3 -5
  127. package/src/interceptors/WebSocket/index.ts +8 -15
  128. package/src/interceptors/XMLHttpRequest/index.ts +12 -32
  129. package/src/interceptors/fetch/index.ts +8 -24
  130. package/src/utils/globalUtils.test.ts +144 -0
  131. package/src/utils/globalsRegistry.ts +107 -0
  132. package/src/utils/hasConfigurableGlobal.test.ts +11 -0
  133. package/src/utils/hasConfigurableGlobal.ts +9 -3
  134. package/lib/browser/XMLHttpRequest-C2KXXdBy.cjs.map +0 -1
  135. package/lib/browser/fetch-CJYWVliE.cjs.map +0 -1
  136. package/lib/browser/fetch-D4es-Apw.mjs.map +0 -1
  137. package/lib/browser/getRawRequest-DaxX6uMy.cjs.map +0 -1
  138. package/lib/browser/getRawRequest-Df640uw8.mjs.map +0 -1
  139. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs +0 -45
  140. package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs.map +0 -1
  141. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs +0 -33
  142. package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs.map +0 -1
  143. package/lib/node/XMLHttpRequest-Dj3xJgke.cjs.map +0 -1
  144. package/lib/node/fetch-BoCFyaYx.cjs.map +0 -1
  145. package/lib/node/fetch-DmiwAmpu.mjs.map +0 -1
  146. package/lib/node/glossary-BLKRyLBd.cjs +0 -12
  147. package/lib/node/glossary-BLKRyLBd.cjs.map +0 -1
  148. package/lib/node/glossary-glQBRnVD.mjs +0 -6
  149. package/lib/node/glossary-glQBRnVD.mjs.map +0 -1
  150. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs +0 -26
  151. package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs.map +0 -1
  152. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs +0 -20
  153. package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs.map +0 -1
@@ -1,5 +1,6 @@
1
- import "../../fetchUtils-BTrdMHGp.mjs";
2
- import "../../handleRequest-CDabRyt1.mjs";
3
- import { t as FetchInterceptor } from "../../fetch-DmiwAmpu.mjs";
1
+ import "../../fetchUtils-BJxD7wvl.mjs";
2
+ import "../../handleRequest-DGfUg9bm.mjs";
3
+ import "../../hasConfigurableGlobal-jN_-v3gp.mjs";
4
+ import { t as FetchInterceptor } from "../../fetch-DDAGTtZi.mjs";
4
5
 
5
6
  export { FetchInterceptor };
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require('./chunk-CbDLau6x.cjs');
2
- const require_getRawRequest = require('./getRawRequest-BavnMWh_.cjs');
2
+ const require_getRawRequest = require('./getRawRequest-B6znU6il.cjs');
3
3
  let outvariant = require("outvariant");
4
4
  let node_http = require("node:http");
5
5
  let node_stream = require("node:stream");
@@ -36,4 +36,4 @@ Object.defineProperty(exports, 'setRawRequestBodyStream', {
36
36
  return setRawRequestBodyStream;
37
37
  }
38
38
  });
39
- //# sourceMappingURL=node-dKdAf3tC.cjs.map
39
+ //# sourceMappingURL=node-DIKcnzhK.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-dKdAf3tC.cjs","names":["getRawRequest","ClientRequest","Readable"],"sources":["../../src/utils/node/index.ts"],"sourcesContent":["import { ClientRequest } from 'node:http'\nimport { Readable } from 'node:stream'\nimport { invariant } from 'outvariant'\nimport { getRawRequest } from '../../getRawRequest'\n\nconst kRawRequestBodyStream = Symbol('kRawRequestBodyStream')\n\n/**\n * Returns the request body stream of the given request.\n * @note This is only relevant in the context of `http.ClientRequest`.\n * This function will throw if the given `request` wasn't created based on\n * the `http.ClientRequest` instance.\n * You must rely on the web stream consumers for other request clients.\n */\nexport function getClientRequestBodyStream(request: Request): Readable {\n const rawRequest = getRawRequest(request)\n\n invariant(\n rawRequest instanceof ClientRequest,\n `Failed to retrieve raw request body stream: request is not an instance of \"http.ClientRequest\". Note that you can only use the \"getClientRequestBodyStream\" function with the requests issued by \"http.clientRequest\".`\n )\n\n const requestBodyStream = Reflect.get(request, kRawRequestBodyStream)\n\n invariant(\n requestBodyStream instanceof Readable,\n 'Failed to retrieve raw request body stream: corrupted stream (%s)',\n typeof requestBodyStream\n )\n\n return requestBodyStream\n}\n\nexport function setRawRequestBodyStream(\n request: Request,\n stream: Readable\n): void {\n Reflect.set(request, kRawRequestBodyStream, stream)\n}\n"],"mappings":";;;;;;;AAKA,MAAM,wBAAwB,OAAO,wBAAwB;;;;;;;;AAS7D,SAAgB,2BAA2B,SAA4B;AAGrE,2BAFmBA,oCAAc,QAAQ,YAGjBC,yBACtB,yNACD;CAED,MAAM,oBAAoB,QAAQ,IAAI,SAAS,sBAAsB;AAErE,2BACE,6BAA6BC,sBAC7B,qEACA,OAAO,kBACR;AAED,QAAO;;AAGT,SAAgB,wBACd,SACA,QACM;AACN,SAAQ,IAAI,SAAS,uBAAuB,OAAO"}
1
+ {"version":3,"file":"node-DIKcnzhK.cjs","names":["getRawRequest","ClientRequest","Readable"],"sources":["../../src/utils/node/index.ts"],"sourcesContent":["import { ClientRequest } from 'node:http'\nimport { Readable } from 'node:stream'\nimport { invariant } from 'outvariant'\nimport { getRawRequest } from '../../getRawRequest'\n\nconst kRawRequestBodyStream = Symbol('kRawRequestBodyStream')\n\n/**\n * Returns the request body stream of the given request.\n * @note This is only relevant in the context of `http.ClientRequest`.\n * This function will throw if the given `request` wasn't created based on\n * the `http.ClientRequest` instance.\n * You must rely on the web stream consumers for other request clients.\n */\nexport function getClientRequestBodyStream(request: Request): Readable {\n const rawRequest = getRawRequest(request)\n\n invariant(\n rawRequest instanceof ClientRequest,\n `Failed to retrieve raw request body stream: request is not an instance of \"http.ClientRequest\". Note that you can only use the \"getClientRequestBodyStream\" function with the requests issued by \"http.clientRequest\".`\n )\n\n const requestBodyStream = Reflect.get(request, kRawRequestBodyStream)\n\n invariant(\n requestBodyStream instanceof Readable,\n 'Failed to retrieve raw request body stream: corrupted stream (%s)',\n typeof requestBodyStream\n )\n\n return requestBodyStream\n}\n\nexport function setRawRequestBodyStream(\n request: Request,\n stream: Readable\n): void {\n Reflect.set(request, kRawRequestBodyStream, stream)\n}\n"],"mappings":";;;;;;;AAKA,MAAM,wBAAwB,OAAO,wBAAwB;;;;;;;;AAS7D,SAAgB,2BAA2B,SAA4B;AAGrE,2BAFmBA,oCAAc,QAAQ,YAGjBC,yBACtB,yNACD;CAED,MAAM,oBAAoB,QAAQ,IAAI,SAAS,sBAAsB;AAErE,2BACE,6BAA6BC,sBAC7B,qEACA,OAAO,kBACR;AAED,QAAO;;AAGT,SAAgB,wBACd,SACA,QACM;AACN,SAAQ,IAAI,SAAS,uBAAuB,OAAO"}
@@ -1,4 +1,4 @@
1
- import { t as getRawRequest } from "./getRawRequest-DnwmXyOW.mjs";
1
+ import { t as getRawRequest } from "./getRawRequest-C2-1urzA.mjs";
2
2
  import { invariant } from "outvariant";
3
3
  import { ClientRequest } from "node:http";
4
4
  import { Readable } from "node:stream";
@@ -24,4 +24,4 @@ function setRawRequestBodyStream(request, stream) {
24
24
 
25
25
  //#endregion
26
26
  export { setRawRequestBodyStream as n, getClientRequestBodyStream as t };
27
- //# sourceMappingURL=node-DwCc6iuP.mjs.map
27
+ //# sourceMappingURL=node-lsdNwZEW.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-DwCc6iuP.mjs","names":[],"sources":["../../src/utils/node/index.ts"],"sourcesContent":["import { ClientRequest } from 'node:http'\nimport { Readable } from 'node:stream'\nimport { invariant } from 'outvariant'\nimport { getRawRequest } from '../../getRawRequest'\n\nconst kRawRequestBodyStream = Symbol('kRawRequestBodyStream')\n\n/**\n * Returns the request body stream of the given request.\n * @note This is only relevant in the context of `http.ClientRequest`.\n * This function will throw if the given `request` wasn't created based on\n * the `http.ClientRequest` instance.\n * You must rely on the web stream consumers for other request clients.\n */\nexport function getClientRequestBodyStream(request: Request): Readable {\n const rawRequest = getRawRequest(request)\n\n invariant(\n rawRequest instanceof ClientRequest,\n `Failed to retrieve raw request body stream: request is not an instance of \"http.ClientRequest\". Note that you can only use the \"getClientRequestBodyStream\" function with the requests issued by \"http.clientRequest\".`\n )\n\n const requestBodyStream = Reflect.get(request, kRawRequestBodyStream)\n\n invariant(\n requestBodyStream instanceof Readable,\n 'Failed to retrieve raw request body stream: corrupted stream (%s)',\n typeof requestBodyStream\n )\n\n return requestBodyStream\n}\n\nexport function setRawRequestBodyStream(\n request: Request,\n stream: Readable\n): void {\n Reflect.set(request, kRawRequestBodyStream, stream)\n}\n"],"mappings":";;;;;;AAKA,MAAM,wBAAwB,OAAO,wBAAwB;;;;;;;;AAS7D,SAAgB,2BAA2B,SAA4B;AAGrE,WAFmB,cAAc,QAAQ,YAGjB,eACtB,yNACD;CAED,MAAM,oBAAoB,QAAQ,IAAI,SAAS,sBAAsB;AAErE,WACE,6BAA6B,UAC7B,qEACA,OAAO,kBACR;AAED,QAAO;;AAGT,SAAgB,wBACd,SACA,QACM;AACN,SAAQ,IAAI,SAAS,uBAAuB,OAAO"}
1
+ {"version":3,"file":"node-lsdNwZEW.mjs","names":[],"sources":["../../src/utils/node/index.ts"],"sourcesContent":["import { ClientRequest } from 'node:http'\nimport { Readable } from 'node:stream'\nimport { invariant } from 'outvariant'\nimport { getRawRequest } from '../../getRawRequest'\n\nconst kRawRequestBodyStream = Symbol('kRawRequestBodyStream')\n\n/**\n * Returns the request body stream of the given request.\n * @note This is only relevant in the context of `http.ClientRequest`.\n * This function will throw if the given `request` wasn't created based on\n * the `http.ClientRequest` instance.\n * You must rely on the web stream consumers for other request clients.\n */\nexport function getClientRequestBodyStream(request: Request): Readable {\n const rawRequest = getRawRequest(request)\n\n invariant(\n rawRequest instanceof ClientRequest,\n `Failed to retrieve raw request body stream: request is not an instance of \"http.ClientRequest\". Note that you can only use the \"getClientRequestBodyStream\" function with the requests issued by \"http.clientRequest\".`\n )\n\n const requestBodyStream = Reflect.get(request, kRawRequestBodyStream)\n\n invariant(\n requestBodyStream instanceof Readable,\n 'Failed to retrieve raw request body stream: corrupted stream (%s)',\n typeof requestBodyStream\n )\n\n return requestBodyStream\n}\n\nexport function setRawRequestBodyStream(\n request: Request,\n stream: Readable\n): void {\n Reflect.set(request, kRawRequestBodyStream, stream)\n}\n"],"mappings":";;;;;;AAKA,MAAM,wBAAwB,OAAO,wBAAwB;;;;;;;;AAS7D,SAAgB,2BAA2B,SAA4B;AAGrE,WAFmB,cAAc,QAAQ,YAGjB,eACtB,yNACD;CAED,MAAM,oBAAoB,QAAQ,IAAI,SAAS,sBAAsB;AAErE,WACE,6BAA6B,UAC7B,qEACA,OAAO,kBACR;AAED,QAAO;;AAGT,SAAgB,wBACd,SACA,QACM;AACN,SAAQ,IAAI,SAAS,uBAAuB,OAAO"}
@@ -1,10 +1,11 @@
1
- require('../fetchUtils-DE_q7tpr.cjs');
2
- require('../bufferUtils-DiCTqG-7.cjs');
3
- const require_ClientRequest = require('../ClientRequest-CMSXChAM.cjs');
4
- require('../handleRequest-pLIe3KKo.cjs');
5
- require('../node-dKdAf3tC.cjs');
6
- const require_XMLHttpRequest = require('../XMLHttpRequest-Dj3xJgke.cjs');
7
- const require_fetch = require('../fetch-BoCFyaYx.cjs');
1
+ require('../fetchUtils-Dw6kQghF.cjs');
2
+ require('../bufferUtils-S5_-2eN4.cjs');
3
+ const require_ClientRequest = require('../ClientRequest-BgwQG-9G.cjs');
4
+ require('../handleRequest-Dzg1d3e5.cjs');
5
+ require('../node-DIKcnzhK.cjs');
6
+ const require_XMLHttpRequest = require('../XMLHttpRequest-B7sloBex.cjs');
7
+ require('../hasConfigurableGlobal-Befl_QJD.cjs');
8
+ const require_fetch = require('../fetch-uc7pNtPg.cjs');
8
9
 
9
10
  //#region src/presets/node.ts
10
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"node.cjs","names":["ClientRequestInterceptor","XMLHttpRequestInterceptor","FetchInterceptor"],"sources":["../../../src/presets/node.ts"],"sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\nimport { FetchInterceptor } from '../interceptors/fetch'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n new FetchInterceptor(),\n] as const\n"],"mappings":";;;;;;;;;;;;;AAQA,mBAAe;CACb,IAAIA,gDAA0B;CAC9B,IAAIC,kDAA2B;CAC/B,IAAIC,gCAAkB;CACvB"}
1
+ {"version":3,"file":"node.cjs","names":["ClientRequestInterceptor","XMLHttpRequestInterceptor","FetchInterceptor"],"sources":["../../../src/presets/node.ts"],"sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\nimport { FetchInterceptor } from '../interceptors/fetch'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n new FetchInterceptor(),\n] as const\n"],"mappings":";;;;;;;;;;;;;;AAQA,mBAAe;CACb,IAAIA,gDAA0B;CAC9B,IAAIC,kDAA2B;CAC/B,IAAIC,gCAAkB;CACvB"}
@@ -1,4 +1,4 @@
1
- import { t as ClientRequestInterceptor } from "../index-BMbJ8FXL.cjs";
1
+ import { t as ClientRequestInterceptor } from "../index-BJ5E5aW-.cjs";
2
2
  import { XMLHttpRequestInterceptor } from "../interceptors/XMLHttpRequest/index.cjs";
3
3
  import { FetchInterceptor } from "../interceptors/fetch/index.cjs";
4
4
 
@@ -1,5 +1,5 @@
1
- import "../Interceptor-DEazpLJd.mjs";
2
- import { t as ClientRequestInterceptor } from "../index-C0YAQ36w.mjs";
1
+ import "../Interceptor-soD-WpQO.mjs";
2
+ import { t as ClientRequestInterceptor } from "../index-DfoF45ih.mjs";
3
3
  import { XMLHttpRequestInterceptor } from "../interceptors/XMLHttpRequest/index.mjs";
4
4
  import { FetchInterceptor } from "../interceptors/fetch/index.mjs";
5
5
 
@@ -1,10 +1,11 @@
1
- import "../fetchUtils-BTrdMHGp.mjs";
2
- import "../bufferUtils-_8XfKIfX.mjs";
3
- import { t as ClientRequestInterceptor } from "../ClientRequest-Cl3Ky7Tw.mjs";
4
- import "../handleRequest-CDabRyt1.mjs";
5
- import "../node-DwCc6iuP.mjs";
6
- import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-BBbs-p9i.mjs";
7
- import { t as FetchInterceptor } from "../fetch-DmiwAmpu.mjs";
1
+ import "../fetchUtils-BJxD7wvl.mjs";
2
+ import "../bufferUtils-DxPxwff_.mjs";
3
+ import { t as ClientRequestInterceptor } from "../ClientRequest-5WjHvs9Y.mjs";
4
+ import "../handleRequest-DGfUg9bm.mjs";
5
+ import "../node-lsdNwZEW.mjs";
6
+ import { t as XMLHttpRequestInterceptor } from "../XMLHttpRequest-DmsdUGmj.mjs";
7
+ import "../hasConfigurableGlobal-jN_-v3gp.mjs";
8
+ import { t as FetchInterceptor } from "../fetch-DDAGTtZi.mjs";
8
9
 
9
10
  //#region src/presets/node.ts
10
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"node.mjs","names":[],"sources":["../../../src/presets/node.ts"],"sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\nimport { FetchInterceptor } from '../interceptors/fetch'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n new FetchInterceptor(),\n] as const\n"],"mappings":";;;;;;;;;;;;;AAQA,mBAAe;CACb,IAAI,0BAA0B;CAC9B,IAAI,2BAA2B;CAC/B,IAAI,kBAAkB;CACvB"}
1
+ {"version":3,"file":"node.mjs","names":[],"sources":["../../../src/presets/node.ts"],"sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\nimport { FetchInterceptor } from '../interceptors/fetch'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n new FetchInterceptor(),\n] as const\n"],"mappings":";;;;;;;;;;;;;;AAQA,mBAAe;CACb,IAAI,0BAA0B;CAC9B,IAAI,2BAA2B;CAC/B,IAAI,kBAAkB;CACvB"}
@@ -1,4 +1,4 @@
1
- const require_node = require('../../node-dKdAf3tC.cjs');
1
+ const require_node = require('../../node-DIKcnzhK.cjs');
2
2
 
3
3
  exports.getClientRequestBodyStream = require_node.getClientRequestBodyStream;
4
4
  exports.setRawRequestBodyStream = require_node.setRawRequestBodyStream;
@@ -1,3 +1,3 @@
1
- import { n as setRawRequestBodyStream, t as getClientRequestBodyStream } from "../../node-DwCc6iuP.mjs";
1
+ import { n as setRawRequestBodyStream, t as getClientRequestBodyStream } from "../../node-lsdNwZEW.mjs";
2
2
 
3
3
  export { getClientRequestBodyStream, setRawRequestBodyStream };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "commonjs",
3
3
  "name": "@mswjs/interceptors",
4
4
  "description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
5
- "version": "0.41.4",
5
+ "version": "0.41.5",
6
6
  "main": "./lib/node/index.cjs",
7
7
  "module": "./lib/node/index.mjs",
8
8
  "types": "./lib/node/index.d.cts",
package/src/glossary.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import type { RequestController } from './RequestController'
2
2
 
3
- export const IS_PATCHED_MODULE: unique symbol = Symbol('isPatchedModule')
4
-
5
3
  /**
6
4
  * @note Export `RequestController` as a type only.
7
5
  * It's never meant to be created in the userland.
@@ -16,7 +14,7 @@ export type HttpRequestEventMap = {
16
14
  request: Request
17
15
  requestId: string
18
16
  controller: RequestController
19
- }
17
+ },
20
18
  ]
21
19
  response: [
22
20
  args: {
@@ -24,7 +22,7 @@ export type HttpRequestEventMap = {
24
22
  isMockedResponse: boolean
25
23
  request: Request
26
24
  requestId: string
27
- }
25
+ },
28
26
  ]
29
27
  unhandledException: [
30
28
  args: {
@@ -32,6 +30,6 @@ export type HttpRequestEventMap = {
32
30
  request: Request
33
31
  requestId: string
34
32
  controller: RequestController
35
- }
33
+ },
36
34
  ]
37
35
  }
@@ -18,6 +18,7 @@ import {
18
18
  import { bindEvent } from './utils/bindEvent'
19
19
  import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
20
20
  import { emitAsync } from '../../utils/emitAsync'
21
+ import { globalsRegistry } from '../../utils/globalsRegistry'
21
22
 
22
23
  export {
23
24
  type WebSocketData,
@@ -80,10 +81,7 @@ export class WebSocketInterceptor extends Interceptor<WebSocketEventMap> {
80
81
  }
81
82
 
82
83
  protected setup(): void {
83
- const originalWebSocketDescriptor = Object.getOwnPropertyDescriptor(
84
- globalThis,
85
- 'WebSocket'
86
- )
84
+ const logger = this.logger.extend('setup')
87
85
 
88
86
  const WebSocketProxy = new Proxy(globalThis.WebSocket, {
89
87
  construct: (
@@ -175,17 +173,12 @@ export class WebSocketInterceptor extends Interceptor<WebSocketEventMap> {
175
173
  },
176
174
  })
177
175
 
178
- Object.defineProperty(globalThis, 'WebSocket', {
179
- value: WebSocketProxy,
180
- configurable: true,
181
- })
176
+ logger.info('patching global WebSocket...')
182
177
 
183
- this.subscriptions.push(() => {
184
- Object.defineProperty(
185
- globalThis,
186
- 'WebSocket',
187
- originalWebSocketDescriptor!
188
- )
189
- })
178
+ this.subscriptions.push(
179
+ globalsRegistry.replaceGlobal('WebSocket', WebSocketProxy)
180
+ )
181
+
182
+ logger.info('global WebSocket patched!', globalThis.WebSocket.name)
190
183
  }
191
184
  }
@@ -1,9 +1,9 @@
1
- import { invariant } from 'outvariant'
2
1
  import { Emitter } from 'strict-event-emitter'
3
- import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
2
+ import { HttpRequestEventMap } from '../../glossary'
4
3
  import { Interceptor } from '../../Interceptor'
5
4
  import { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'
6
5
  import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
6
+ import { globalsRegistry } from '../../utils/globalsRegistry'
7
7
 
8
8
  export type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>
9
9
 
@@ -21,41 +21,21 @@ export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap>
21
21
  protected setup() {
22
22
  const logger = this.logger.extend('setup')
23
23
 
24
- logger.info('patching "XMLHttpRequest" module...')
24
+ logger.info('patching global XMLHttpRequest...')
25
25
 
26
- const PureXMLHttpRequest = globalThis.XMLHttpRequest
27
-
28
- invariant(
29
- !(PureXMLHttpRequest as any)[IS_PATCHED_MODULE],
30
- 'Failed to patch the "XMLHttpRequest" module: already patched.'
26
+ this.subscriptions.push(
27
+ globalsRegistry.replaceGlobal(
28
+ 'XMLHttpRequest',
29
+ createXMLHttpRequestProxy({
30
+ emitter: this.emitter,
31
+ logger: this.logger,
32
+ })
33
+ )
31
34
  )
32
35
 
33
- globalThis.XMLHttpRequest = createXMLHttpRequestProxy({
34
- emitter: this.emitter,
35
- logger: this.logger,
36
- })
37
-
38
36
  logger.info(
39
- 'native "XMLHttpRequest" module patched!',
37
+ 'global XMLHttpRequest patched!',
40
38
  globalThis.XMLHttpRequest.name
41
39
  )
42
-
43
- Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
44
- enumerable: true,
45
- configurable: true,
46
- value: true,
47
- })
48
-
49
- this.subscriptions.push(() => {
50
- Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
51
- value: undefined,
52
- })
53
-
54
- globalThis.XMLHttpRequest = PureXMLHttpRequest
55
- logger.info(
56
- 'native "XMLHttpRequest" module restored!',
57
- globalThis.XMLHttpRequest.name
58
- )
59
- })
60
40
  }
61
41
  }
@@ -1,7 +1,6 @@
1
- import { invariant } from 'outvariant'
2
1
  import { until } from '@open-draft/until'
3
2
  import { DeferredPromise } from '@open-draft/deferred-promise'
4
- import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
3
+ import { HttpRequestEventMap } from '../../glossary'
5
4
  import { Interceptor } from '../../Interceptor'
6
5
  import { RequestController } from '../../RequestController'
7
6
  import { emitAsync } from '../../utils/emitAsync'
@@ -15,6 +14,7 @@ import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
15
14
  import { FetchRequest, FetchResponse } from '../../utils/fetchUtils'
16
15
  import { setRawRequest } from '../../getRawRequest'
17
16
  import { isResponseError } from '../../utils/responseUtils'
17
+ import { globalsRegistry } from '../../utils/globalsRegistry'
18
18
 
19
19
  export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
20
20
  static symbol = Symbol('fetch')
@@ -28,14 +28,11 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
28
28
  }
29
29
 
30
30
  protected async setup() {
31
- const pureFetch = globalThis.fetch
31
+ const logger = this.logger.extend('setup')
32
32
 
33
- invariant(
34
- !(pureFetch as any)[IS_PATCHED_MODULE],
35
- 'Failed to patch the "fetch" module: already patched.'
36
- )
33
+ const pureFetch = globalThis.fetch
37
34
 
38
- globalThis.fetch = async (input, init) => {
35
+ const fetchProxy: typeof fetch = async (input, init) => {
39
36
  const requestId = createRequestId()
40
37
 
41
38
  /**
@@ -192,23 +189,10 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
192
189
  return responsePromise
193
190
  }
194
191
 
195
- Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
196
- enumerable: true,
197
- configurable: true,
198
- value: true,
199
- })
200
-
201
- this.subscriptions.push(() => {
202
- Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
203
- value: undefined,
204
- })
192
+ logger.info('patching global fetch...')
205
193
 
206
- globalThis.fetch = pureFetch
194
+ this.subscriptions.push(globalsRegistry.replaceGlobal('fetch', fetchProxy))
207
195
 
208
- this.logger.info(
209
- 'restored native "globalThis.fetch"!',
210
- globalThis.fetch.name
211
- )
212
- })
196
+ logger.info('global fetch patched!', globalThis.fetch.name)
213
197
  }
214
198
  }
@@ -0,0 +1,144 @@
1
+ import { it, beforeEach, afterEach, expect, vi } from 'vitest'
2
+ import { globalsRegistry } from './globalsRegistry'
3
+
4
+ declare global {
5
+ var foo: { original: boolean }
6
+ }
7
+
8
+ const realGlobalPrototype = Object.getPrototypeOf(global)
9
+
10
+ beforeEach(() => {
11
+ global.foo = { original: true }
12
+ })
13
+
14
+ afterEach(() => {
15
+ Object.setPrototypeOf(global, realGlobalPrototype)
16
+ globalsRegistry.restoreAllGlobals()
17
+ })
18
+
19
+ it('replaces the global', () => {
20
+ globalsRegistry.replaceGlobal('foo', { original: false })
21
+ expect(global.foo).toEqual({ original: false })
22
+ })
23
+
24
+ it('replaces the global set on the prototype', () => {
25
+ function FakeGlobalScope() {}
26
+ FakeGlobalScope.prototype.foo = { prototype: true }
27
+ Object.setPrototypeOf(global, FakeGlobalScope.prototype)
28
+ Reflect.deleteProperty(global, 'foo')
29
+
30
+ expect(global.foo).toEqual({ prototype: true })
31
+ expect(FakeGlobalScope.prototype.foo).toEqual({ prototype: true })
32
+
33
+ globalsRegistry.replaceGlobal('foo', { original: false })
34
+
35
+ expect(global.foo).toEqual({ original: false })
36
+ expect(FakeGlobalScope.prototype.foo, 'Preserves prototype value').toEqual({
37
+ prototype: true,
38
+ })
39
+ })
40
+
41
+ it('replaces the global after it was restored', () => {
42
+ const restoreGlobal = globalsRegistry.replaceGlobal('foo', {
43
+ original: false,
44
+ })
45
+ expect(global.foo).toEqual({ original: false })
46
+
47
+ restoreGlobal()
48
+ expect(global.foo).toEqual({ original: true })
49
+
50
+ globalsRegistry.replaceGlobal('foo', { original: false })
51
+ expect(global.foo).toEqual({ original: false })
52
+ })
53
+
54
+ it('warns on replacing a non-existing global', () => {
55
+ vi.spyOn(console, 'warn').mockImplementation(() => {})
56
+
57
+ globalsRegistry.replaceGlobal(
58
+ // @ts-expect-error Intentionally invalid value.
59
+ 'NON-EXISTING',
60
+ { original: false }
61
+ )
62
+ expect(console.warn).toHaveBeenCalledExactlyOnceWith(
63
+ 'Failed to replace a global value at "NON-EXISTING": not a global value.'
64
+ )
65
+ })
66
+
67
+ it('throws if replacing an already replaced global', () => {
68
+ globalsRegistry.replaceGlobal('foo', { original: false })
69
+ expect(global.foo).toEqual({ original: false })
70
+
71
+ expect(() =>
72
+ globalsRegistry.replaceGlobal('foo', { original: false })
73
+ ).toThrow('Failed to replace a global value at "foo": already replaced.')
74
+ })
75
+
76
+ it('does nothing if restoring an already restored global', () => {
77
+ const restoreGlobal = globalsRegistry.replaceGlobal('foo', {
78
+ original: false,
79
+ })
80
+
81
+ expect(global.foo).toEqual({ original: false })
82
+
83
+ restoreGlobal()
84
+ expect(global.foo).toEqual({ original: true })
85
+
86
+ restoreGlobal()
87
+ expect(global.foo).toEqual({ original: true })
88
+ })
89
+
90
+ it('restores the global', () => {
91
+ const restoreGlobal = globalsRegistry.replaceGlobal('foo', {
92
+ original: false,
93
+ })
94
+ expect(global.foo).toEqual({ original: false })
95
+
96
+ restoreGlobal()
97
+ expect(global.foo).toEqual({ original: true })
98
+ })
99
+
100
+ it('restores the global set on the prototype', () => {
101
+ function FakeGlobalScope() {}
102
+ FakeGlobalScope.prototype.foo = { prototype: true }
103
+ Object.setPrototypeOf(global, FakeGlobalScope.prototype)
104
+ Reflect.deleteProperty(global, 'foo')
105
+
106
+ expect(global.foo).toEqual({ prototype: true })
107
+
108
+ const restoreGlobal = globalsRegistry.replaceGlobal('foo', {
109
+ original: false,
110
+ })
111
+
112
+ expect(global.foo).toEqual({ original: false })
113
+
114
+ restoreGlobal()
115
+ expect(global.foo).toEqual({ prototype: true })
116
+ })
117
+
118
+ it('restores global to the original property descriptor', () => {
119
+ const descriptor: PropertyDescriptor = {
120
+ value: { original: true },
121
+ enumerable: false,
122
+ configurable: true,
123
+ writable: false,
124
+ }
125
+ Object.defineProperty(global, 'foo', descriptor)
126
+ expect(Object.getOwnPropertyDescriptor(global, 'foo')).toEqual(descriptor)
127
+
128
+ const restoreGlobal = globalsRegistry.replaceGlobal('foo', {
129
+ original: false,
130
+ })
131
+
132
+ expect(global.foo).toEqual({ original: false })
133
+ expect(Object.getOwnPropertyDescriptor(global, 'foo')).toEqual({
134
+ value: { original: false },
135
+ enumerable: true,
136
+ configurable: true,
137
+ writable: false,
138
+ })
139
+
140
+ restoreGlobal()
141
+
142
+ expect(global.foo).toEqual({ original: true })
143
+ expect(Object.getOwnPropertyDescriptor(global, 'foo')).toEqual(descriptor)
144
+ })
@@ -0,0 +1,107 @@
1
+ import { invariant } from 'outvariant'
2
+
3
+ class GlobalsRegistry {
4
+ #globals = new Map<keyof typeof globalThis, () => void>()
5
+
6
+ public replaceGlobal<K extends keyof typeof globalThis>(
7
+ key: K,
8
+ nextValue: (typeof globalThis)[K]
9
+ ): () => void {
10
+ invariant(
11
+ !this.#globals.has(key),
12
+ `Failed to replace a global value at "${key}": already replaced.`
13
+ )
14
+
15
+ const match = getDeepPropertyDescriptor(globalThis, key)
16
+
17
+ if (typeof match === 'undefined') {
18
+ console.warn(
19
+ `Failed to replace a global value at "${key}": not a global value.`
20
+ )
21
+ return () => {}
22
+ }
23
+
24
+ Object.defineProperty(globalThis, key, {
25
+ value: nextValue,
26
+ enumerable: true,
27
+ configurable: true,
28
+ })
29
+
30
+ const restoreGlobal = () => {
31
+ if (!this.#globals.has(key)) {
32
+ return
33
+ }
34
+
35
+ if (match.owner === globalThis) {
36
+ Object.defineProperty(match.owner, key, match.descriptor)
37
+ } else {
38
+ /**
39
+ * @todo Delete the proxy property set by the registry.
40
+ * If the owner isn't `globalThis`, the property is likely nested in the prototype.
41
+ * The registry does not meddle with those, they are left intact.
42
+ */
43
+ Reflect.deleteProperty(globalThis, key)
44
+ }
45
+
46
+ this.#globals.delete(key)
47
+ }
48
+
49
+ this.#globals.set(key, restoreGlobal)
50
+
51
+ return restoreGlobal
52
+ }
53
+
54
+ public restoreAllGlobals(): void {
55
+ const errors: Array<Error> = []
56
+
57
+ for (const [, restoreGlobal] of this.#globals) {
58
+ try {
59
+ restoreGlobal()
60
+ } catch (error) {
61
+ if (error instanceof Error) {
62
+ errors.push(error)
63
+ } else {
64
+ throw error
65
+ }
66
+ }
67
+ }
68
+
69
+ if (errors.length > 0) {
70
+ throw new AggregateError(errors, 'FOO!')
71
+ }
72
+ }
73
+ }
74
+
75
+ export const globalsRegistry = new GlobalsRegistry()
76
+
77
+ interface DeepDescriptorMatch {
78
+ owner: object
79
+ descriptor: PropertyDescriptor
80
+ }
81
+
82
+ /**
83
+ * Returns a property descriptor for the given property on the owner.
84
+ * Walks down the prototype chain if the property does not exist on the owner.
85
+ * Handy for getting a global property descriptor where `globalThis` is
86
+ * replaced with a controlled class (e.g. ServiceWorkerGlobalScope).
87
+ */
88
+ export function getDeepPropertyDescriptor<Owner extends object>(
89
+ owner: Owner,
90
+ key: keyof Owner
91
+ ): DeepDescriptorMatch | undefined {
92
+ let currentOwner: Owner | null = owner
93
+ let descriptor: PropertyDescriptor | undefined
94
+
95
+ while (currentOwner) {
96
+ descriptor = Object.getOwnPropertyDescriptor(currentOwner, key)
97
+
98
+ if (descriptor) {
99
+ return {
100
+ owner: currentOwner,
101
+ descriptor,
102
+ }
103
+ }
104
+
105
+ currentOwner = Object.getPrototypeOf(currentOwner)
106
+ }
107
+ }
@@ -1,12 +1,15 @@
1
1
  import { vi, beforeAll, afterEach, afterAll, it, expect } from 'vitest'
2
2
  import { hasConfigurableGlobal } from './hasConfigurableGlobal'
3
3
 
4
+ let originalGlobalPrototype = Object.getPrototypeOf(globalThis)
5
+
4
6
  beforeAll(() => {
5
7
  vi.spyOn(console, 'error').mockImplementation(() => {})
6
8
  })
7
9
 
8
10
  afterEach(() => {
9
11
  vi.clearAllMocks()
12
+ Object.setPrototypeOf(globalThis, originalGlobalPrototype)
10
13
  })
11
14
 
12
15
  afterAll(() => {
@@ -81,3 +84,11 @@ it('returns false and prints an error for global property that only has a getter
81
84
  '[MSW] Failed to apply interceptor: the global `_onlyGetter` property is non-configurable. This is likely an issue with your environment. If you are using a framework, please open an issue about this in their repository.'
82
85
  )
83
86
  })
87
+
88
+ it('returns true for a property on a global prototype', () => {
89
+ function FakeGlobalScope() {}
90
+ FakeGlobalScope.prototype._prototypeGlobal = 123
91
+ Object.setPrototypeOf(globalThis, FakeGlobalScope.prototype)
92
+
93
+ expect(hasConfigurableGlobal('_prototypeGlobal')).toBe(true)
94
+ })
@@ -1,15 +1,21 @@
1
+ import { getDeepPropertyDescriptor } from './globalsRegistry'
2
+
1
3
  /**
2
4
  * Returns a boolean indicating whether the given global property
3
5
  * is defined and is configurable.
4
6
  */
5
- export function hasConfigurableGlobal(propertyName: string): boolean {
6
- const descriptor = Object.getOwnPropertyDescriptor(globalThis, propertyName)
7
+ export function hasConfigurableGlobal(
8
+ propertyName: keyof typeof globalThis
9
+ ): boolean {
10
+ const match = getDeepPropertyDescriptor(globalThis, propertyName)
7
11
 
8
12
  // The property is not set at all.
9
- if (typeof descriptor === 'undefined') {
13
+ if (typeof match === 'undefined') {
10
14
  return false
11
15
  }
12
16
 
17
+ const { descriptor } = match
18
+
13
19
  // The property is set to a getter that returns undefined.
14
20
  if (
15
21
  typeof descriptor.get === 'function' &&