@buoy-gg/network 7.0.41 → 7.0.43

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 (272) hide show
  1. package/README.md +11 -85
  2. package/lib/commonjs/index.js +1 -332
  3. package/lib/commonjs/index.web.js +1 -420
  4. package/lib/commonjs/network/components/DataViewer.web.js +1 -879
  5. package/lib/commonjs/network/components/NetworkCopyScreen.web.js +1 -48
  6. package/lib/commonjs/network/components/NetworkCopySettingsView.js +27 -755
  7. package/lib/commonjs/network/components/NetworkCopySettingsView.web.js +14 -1242
  8. package/lib/commonjs/network/components/NetworkDetailScreen.web.js +1 -101
  9. package/lib/commonjs/network/components/NetworkDetailStepper.js +1 -108
  10. package/lib/commonjs/network/components/NetworkDetailStepper.web.js +1 -298
  11. package/lib/commonjs/network/components/NetworkEventDetailView.js +15 -1130
  12. package/lib/commonjs/network/components/NetworkEventDetailView.web.js +15 -1484
  13. package/lib/commonjs/network/components/NetworkEventItemCompact.js +2 -401
  14. package/lib/commonjs/network/components/NetworkEventItemCompact.web.js +2 -820
  15. package/lib/commonjs/network/components/NetworkFilterScreen.web.js +1 -55
  16. package/lib/commonjs/network/components/NetworkFilterViewV3.js +1 -442
  17. package/lib/commonjs/network/components/NetworkFilterViewV3.web.js +1 -1310
  18. package/lib/commonjs/network/components/NetworkHeaderLive.js +1 -375
  19. package/lib/commonjs/network/components/NetworkHeaderLive.web.js +1 -623
  20. package/lib/commonjs/network/components/NetworkHeaderMenu.js +1 -190
  21. package/lib/commonjs/network/components/NetworkListScreen.js +1 -403
  22. package/lib/commonjs/network/components/NetworkListScreen.web.js +1 -624
  23. package/lib/commonjs/network/components/NetworkModal.js +1 -786
  24. package/lib/commonjs/network/components/NetworkNumberPrompt.js +1 -178
  25. package/lib/commonjs/network/components/NetworkNumberPrompt.web.js +1 -232
  26. package/lib/commonjs/network/components/NetworkOverrideEditor.js +1 -814
  27. package/lib/commonjs/network/components/NetworkOverrideEditor.web.js +1 -942
  28. package/lib/commonjs/network/components/NetworkOverrideHeaderButton.js +1 -107
  29. package/lib/commonjs/network/components/NetworkOverrideHeaderButton.web.js +1 -133
  30. package/lib/commonjs/network/components/NetworkOverridesButton.js +1 -90
  31. package/lib/commonjs/network/components/NetworkOverridesButton.web.js +1 -143
  32. package/lib/commonjs/network/components/NetworkOverridesScreen.js +1 -543
  33. package/lib/commonjs/network/components/NetworkOverridesScreen.web.js +1 -830
  34. package/lib/commonjs/network/components/NetworkSavedScreen.js +1 -226
  35. package/lib/commonjs/network/components/NetworkSavedScreen.web.js +1 -463
  36. package/lib/commonjs/network/components/NetworkUpgradeBanner.js +1 -76
  37. package/lib/commonjs/network/components/response-editor/Explorer.js +1 -973
  38. package/lib/commonjs/network/components/response-editor/Explorer.web.js +1 -1136
  39. package/lib/commonjs/network/components/response-editor/deleteNestedDataByPath.js +1 -56
  40. package/lib/commonjs/network/components/response-editor/editorContext.js +1 -33
  41. package/lib/commonjs/network/components/response-editor/updateNestedDataByPath.js +1 -61
  42. package/lib/commonjs/network/conditions/NetworkConditionsControl.js +1 -235
  43. package/lib/commonjs/network/conditions/NetworkConditionsControl.test.js +1 -136
  44. package/lib/commonjs/network/conditions/NetworkConditionsIcon.js +1 -33
  45. package/lib/commonjs/network/conditions/NetworkFloatingTool.js +1 -310
  46. package/lib/commonjs/network/conditions/NetworkFloatingTool.test.js +1 -219
  47. package/lib/commonjs/network/conditions/store.js +1 -75
  48. package/lib/commonjs/network/conditions/wait.js +1 -43
  49. package/lib/commonjs/network/conditions/xhr.js +1 -135
  50. package/lib/commonjs/network/hooks/useLiveNetworkEvent.js +1 -62
  51. package/lib/commonjs/network/hooks/useNetworkBadgeCounts.js +1 -83
  52. package/lib/commonjs/network/hooks/useNetworkEventList.js +1 -108
  53. package/lib/commonjs/network/hooks/useNetworkEvents.js +1 -318
  54. package/lib/commonjs/network/hooks/useNetworkHasMatches.js +1 -56
  55. package/lib/commonjs/network/hooks/useNetworkSaved.js +1 -38
  56. package/lib/commonjs/network/hooks/useNetworkSelection.js +1 -53
  57. package/lib/commonjs/network/hooks/useOverrideRules.js +1 -36
  58. package/lib/commonjs/network/hooks/useTickEveryMinute.js +1 -21
  59. package/lib/commonjs/network/overrides/applyToFetch.js +1 -80
  60. package/lib/commonjs/network/overrides/applyToXhr.js +2 -510
  61. package/lib/commonjs/network/overrides/engine.js +1 -93
  62. package/lib/commonjs/network/overrides/matchRule.js +1 -87
  63. package/lib/commonjs/network/overrides/overriddenRows.js +1 -37
  64. package/lib/commonjs/network/overrides/overrideRulesStore.js +1 -550
  65. package/lib/commonjs/network/overrides/presets.js +1 -70
  66. package/lib/commonjs/network/overrides/resolveOverride.js +1 -158
  67. package/lib/commonjs/network/overrides/types.js +1 -90
  68. package/lib/commonjs/network/sync/networkBodyResolver.js +1 -39
  69. package/lib/commonjs/network/sync/networkSyncAdapter.js +1 -882
  70. package/lib/commonjs/network/sync/overrideHonesty.release.test.js +1 -36
  71. package/lib/commonjs/network/types/index.js +1 -5
  72. package/lib/commonjs/network/utils/extractOperationName.js +1 -80
  73. package/lib/commonjs/network/utils/filterNetworkEvents.js +1 -96
  74. package/lib/commonjs/network/utils/formatGraphQLVariables.js +1 -219
  75. package/lib/commonjs/network/utils/formatting.js +1 -30
  76. package/lib/commonjs/network/utils/generateCurl.js +2 -71
  77. package/lib/commonjs/network/utils/generateNetworkCopyText.js +37 -166
  78. package/lib/commonjs/network/utils/httpCompletion.js +1 -37
  79. package/lib/commonjs/network/utils/networkEventStore.js +1 -411
  80. package/lib/commonjs/network/utils/networkListener.js +4 -1352
  81. package/lib/commonjs/network/utils/networkSavedStore.js +3 -577
  82. package/lib/commonjs/network/utils/networkSelectionStore.js +1 -193
  83. package/lib/commonjs/network/utils/realNow.js +1 -0
  84. package/lib/commonjs/network/utils/requestId.js +1 -16
  85. package/lib/commonjs/network/utils/resolveRequestUrl.js +1 -15
  86. package/lib/commonjs/network/utils/selectSavedEvents.js +1 -39
  87. package/lib/commonjs/network/utils/startNetworkCapture.js +1 -24
  88. package/lib/commonjs/network/utils/withPinnedFirst.js +1 -32
  89. package/lib/commonjs/preset.js +1 -93
  90. package/lib/module/index.js +1 -104
  91. package/lib/module/index.web.js +1 -82
  92. package/lib/module/network/components/DataViewer.web.js +1 -875
  93. package/lib/module/network/components/NetworkCopyScreen.web.js +1 -44
  94. package/lib/module/network/components/NetworkCopySettingsView.js +27 -750
  95. package/lib/module/network/components/NetworkCopySettingsView.web.js +14 -1237
  96. package/lib/module/network/components/NetworkDetailScreen.web.js +1 -97
  97. package/lib/module/network/components/NetworkDetailStepper.js +1 -104
  98. package/lib/module/network/components/NetworkDetailStepper.web.js +1 -296
  99. package/lib/module/network/components/NetworkEventDetailView.js +15 -1129
  100. package/lib/module/network/components/NetworkEventDetailView.web.js +15 -1482
  101. package/lib/module/network/components/NetworkEventItemCompact.js +2 -398
  102. package/lib/module/network/components/NetworkEventItemCompact.web.js +2 -818
  103. package/lib/module/network/components/NetworkFilterScreen.web.js +1 -51
  104. package/lib/module/network/components/NetworkFilterViewV3.js +1 -438
  105. package/lib/module/network/components/NetworkFilterViewV3.web.js +1 -1306
  106. package/lib/module/network/components/NetworkHeaderLive.js +1 -366
  107. package/lib/module/network/components/NetworkHeaderLive.web.js +1 -617
  108. package/lib/module/network/components/NetworkHeaderMenu.js +1 -184
  109. package/lib/module/network/components/NetworkListScreen.js +1 -399
  110. package/lib/module/network/components/NetworkListScreen.web.js +1 -622
  111. package/lib/module/network/components/NetworkModal.js +1 -781
  112. package/lib/module/network/components/NetworkNumberPrompt.js +1 -174
  113. package/lib/module/network/components/NetworkNumberPrompt.web.js +1 -230
  114. package/lib/module/network/components/NetworkOverrideEditor.js +1 -811
  115. package/lib/module/network/components/NetworkOverrideEditor.web.js +1 -939
  116. package/lib/module/network/components/NetworkOverrideHeaderButton.js +1 -104
  117. package/lib/module/network/components/NetworkOverrideHeaderButton.web.js +1 -131
  118. package/lib/module/network/components/NetworkOverridesButton.js +1 -87
  119. package/lib/module/network/components/NetworkOverridesButton.web.js +1 -141
  120. package/lib/module/network/components/NetworkOverridesScreen.js +1 -539
  121. package/lib/module/network/components/NetworkOverridesScreen.web.js +1 -826
  122. package/lib/module/network/components/NetworkSavedScreen.js +1 -222
  123. package/lib/module/network/components/NetworkSavedScreen.web.js +1 -461
  124. package/lib/module/network/components/NetworkUpgradeBanner.js +1 -72
  125. package/lib/module/network/components/response-editor/Explorer.js +1 -972
  126. package/lib/module/network/components/response-editor/Explorer.web.js +1 -1133
  127. package/lib/module/network/components/response-editor/deleteNestedDataByPath.js +1 -51
  128. package/lib/module/network/components/response-editor/editorContext.js +1 -29
  129. package/lib/module/network/components/response-editor/updateNestedDataByPath.js +1 -56
  130. package/lib/module/network/conditions/NetworkConditionsControl.js +1 -231
  131. package/lib/module/network/conditions/NetworkConditionsControl.test.js +1 -135
  132. package/lib/module/network/conditions/NetworkConditionsIcon.js +1 -29
  133. package/lib/module/network/conditions/NetworkFloatingTool.js +1 -304
  134. package/lib/module/network/conditions/NetworkFloatingTool.test.js +1 -218
  135. package/lib/module/network/conditions/store.js +1 -65
  136. package/lib/module/network/conditions/wait.js +1 -38
  137. package/lib/module/network/conditions/xhr.js +1 -131
  138. package/lib/module/network/hooks/useLiveNetworkEvent.js +1 -58
  139. package/lib/module/network/hooks/useNetworkBadgeCounts.js +1 -79
  140. package/lib/module/network/hooks/useNetworkEventList.js +1 -104
  141. package/lib/module/network/hooks/useNetworkEvents.js +1 -314
  142. package/lib/module/network/hooks/useNetworkHasMatches.js +1 -52
  143. package/lib/module/network/hooks/useNetworkSaved.js +1 -34
  144. package/lib/module/network/hooks/useNetworkSelection.js +1 -46
  145. package/lib/module/network/hooks/useOverrideRules.js +1 -32
  146. package/lib/module/network/hooks/useTickEveryMinute.js +1 -11
  147. package/lib/module/network/overrides/applyToFetch.js +1 -72
  148. package/lib/module/network/overrides/applyToXhr.js +2 -502
  149. package/lib/module/network/overrides/engine.js +1 -85
  150. package/lib/module/network/overrides/matchRule.js +1 -81
  151. package/lib/module/network/overrides/overriddenRows.js +1 -33
  152. package/lib/module/network/overrides/overrideRulesStore.js +1 -547
  153. package/lib/module/network/overrides/presets.js +1 -65
  154. package/lib/module/network/overrides/resolveOverride.js +1 -152
  155. package/lib/module/network/overrides/types.js +1 -86
  156. package/lib/module/network/sync/networkBodyResolver.js +1 -34
  157. package/lib/module/network/sync/networkSyncAdapter.js +1 -880
  158. package/lib/module/network/sync/overrideHonesty.release.test.js +1 -35
  159. package/lib/module/network/types/index.js +1 -3
  160. package/lib/module/network/utils/extractOperationName.js +1 -76
  161. package/lib/module/network/utils/filterNetworkEvents.js +1 -92
  162. package/lib/module/network/utils/formatGraphQLVariables.js +1 -213
  163. package/lib/module/network/utils/formatting.js +1 -9
  164. package/lib/module/network/utils/generateCurl.js +2 -67
  165. package/lib/module/network/utils/generateNetworkCopyText.js +37 -162
  166. package/lib/module/network/utils/httpCompletion.js +1 -32
  167. package/lib/module/network/utils/networkEventStore.js +1 -409
  168. package/lib/module/network/utils/networkListener.js +4 -1341
  169. package/lib/module/network/utils/networkSavedStore.js +3 -572
  170. package/lib/module/network/utils/networkSelectionStore.js +1 -188
  171. package/lib/module/network/utils/realNow.js +1 -0
  172. package/lib/module/network/utils/requestId.js +1 -12
  173. package/lib/module/network/utils/resolveRequestUrl.js +1 -11
  174. package/lib/module/network/utils/selectSavedEvents.js +1 -35
  175. package/lib/module/network/utils/startNetworkCapture.js +1 -21
  176. package/lib/module/network/utils/withPinnedFirst.js +1 -28
  177. package/lib/module/preset.js +1 -91
  178. package/lib/typescript/network/overrides/engine.d.ts +1 -1
  179. package/lib/typescript/network/overrides/matchRule.d.ts +1 -1
  180. package/lib/typescript/network/overrides/resolveOverride.d.ts +2 -2
  181. package/lib/typescript/network/overrides/types.d.ts +9 -0
  182. package/lib/typescript/network/utils/networkListener.d.ts +11 -0
  183. package/lib/typescript/network/utils/realNow.d.ts +18 -0
  184. package/lib/web/index.mjs +73 -74
  185. package/package.json +3 -6
  186. package/lib/typescript/index.d.ts.map +0 -1
  187. package/lib/typescript/index.web.d.ts.map +0 -1
  188. package/lib/typescript/network/components/DataViewer.web.d.ts.map +0 -1
  189. package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts.map +0 -1
  190. package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +0 -1
  191. package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts.map +0 -1
  192. package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts.map +0 -1
  193. package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +0 -1
  194. package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts.map +0 -1
  195. package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +0 -1
  196. package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts.map +0 -1
  197. package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +0 -1
  198. package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts.map +0 -1
  199. package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts.map +0 -1
  200. package/lib/typescript/network/components/NetworkFilterViewV3.d.ts.map +0 -1
  201. package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts.map +0 -1
  202. package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +0 -1
  203. package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts.map +0 -1
  204. package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +0 -1
  205. package/lib/typescript/network/components/NetworkListScreen.d.ts.map +0 -1
  206. package/lib/typescript/network/components/NetworkListScreen.web.d.ts.map +0 -1
  207. package/lib/typescript/network/components/NetworkModal.d.ts.map +0 -1
  208. package/lib/typescript/network/components/NetworkNumberPrompt.d.ts.map +0 -1
  209. package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts.map +0 -1
  210. package/lib/typescript/network/components/NetworkOverrideEditor.d.ts.map +0 -1
  211. package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts.map +0 -1
  212. package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts.map +0 -1
  213. package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts.map +0 -1
  214. package/lib/typescript/network/components/NetworkOverridesButton.d.ts.map +0 -1
  215. package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts.map +0 -1
  216. package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +0 -1
  217. package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts.map +0 -1
  218. package/lib/typescript/network/components/NetworkSavedScreen.d.ts.map +0 -1
  219. package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts.map +0 -1
  220. package/lib/typescript/network/components/NetworkUpgradeBanner.d.ts.map +0 -1
  221. package/lib/typescript/network/components/response-editor/Explorer.d.ts.map +0 -1
  222. package/lib/typescript/network/components/response-editor/Explorer.web.d.ts.map +0 -1
  223. package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts.map +0 -1
  224. package/lib/typescript/network/components/response-editor/editorContext.d.ts.map +0 -1
  225. package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts.map +0 -1
  226. package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts.map +0 -1
  227. package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts.map +0 -1
  228. package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts.map +0 -1
  229. package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts.map +0 -1
  230. package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts.map +0 -1
  231. package/lib/typescript/network/conditions/store.d.ts.map +0 -1
  232. package/lib/typescript/network/conditions/wait.d.ts.map +0 -1
  233. package/lib/typescript/network/conditions/xhr.d.ts.map +0 -1
  234. package/lib/typescript/network/hooks/useLiveNetworkEvent.d.ts.map +0 -1
  235. package/lib/typescript/network/hooks/useNetworkBadgeCounts.d.ts.map +0 -1
  236. package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +0 -1
  237. package/lib/typescript/network/hooks/useNetworkEvents.d.ts.map +0 -1
  238. package/lib/typescript/network/hooks/useNetworkHasMatches.d.ts.map +0 -1
  239. package/lib/typescript/network/hooks/useNetworkSaved.d.ts.map +0 -1
  240. package/lib/typescript/network/hooks/useNetworkSelection.d.ts.map +0 -1
  241. package/lib/typescript/network/hooks/useOverrideRules.d.ts.map +0 -1
  242. package/lib/typescript/network/hooks/useTickEveryMinute.d.ts.map +0 -1
  243. package/lib/typescript/network/overrides/applyToFetch.d.ts.map +0 -1
  244. package/lib/typescript/network/overrides/applyToXhr.d.ts.map +0 -1
  245. package/lib/typescript/network/overrides/engine.d.ts.map +0 -1
  246. package/lib/typescript/network/overrides/matchRule.d.ts.map +0 -1
  247. package/lib/typescript/network/overrides/overriddenRows.d.ts.map +0 -1
  248. package/lib/typescript/network/overrides/overrideRulesStore.d.ts.map +0 -1
  249. package/lib/typescript/network/overrides/presets.d.ts.map +0 -1
  250. package/lib/typescript/network/overrides/resolveOverride.d.ts.map +0 -1
  251. package/lib/typescript/network/overrides/types.d.ts.map +0 -1
  252. package/lib/typescript/network/sync/networkBodyResolver.d.ts.map +0 -1
  253. package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +0 -1
  254. package/lib/typescript/network/sync/overrideHonesty.release.test.d.ts.map +0 -1
  255. package/lib/typescript/network/types/index.d.ts.map +0 -1
  256. package/lib/typescript/network/utils/extractOperationName.d.ts.map +0 -1
  257. package/lib/typescript/network/utils/filterNetworkEvents.d.ts.map +0 -1
  258. package/lib/typescript/network/utils/formatGraphQLVariables.d.ts.map +0 -1
  259. package/lib/typescript/network/utils/formatting.d.ts.map +0 -1
  260. package/lib/typescript/network/utils/generateCurl.d.ts.map +0 -1
  261. package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +0 -1
  262. package/lib/typescript/network/utils/httpCompletion.d.ts.map +0 -1
  263. package/lib/typescript/network/utils/networkEventStore.d.ts.map +0 -1
  264. package/lib/typescript/network/utils/networkListener.d.ts.map +0 -1
  265. package/lib/typescript/network/utils/networkSavedStore.d.ts.map +0 -1
  266. package/lib/typescript/network/utils/networkSelectionStore.d.ts.map +0 -1
  267. package/lib/typescript/network/utils/requestId.d.ts.map +0 -1
  268. package/lib/typescript/network/utils/resolveRequestUrl.d.ts.map +0 -1
  269. package/lib/typescript/network/utils/selectSavedEvents.d.ts.map +0 -1
  270. package/lib/typescript/network/utils/startNetworkCapture.d.ts.map +0 -1
  271. package/lib/typescript/network/utils/withPinnedFirst.d.ts.map +0 -1
  272. package/lib/typescript/preset.d.ts.map +0 -1
@@ -1,1355 +1,7 @@
1
- "use strict";
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.stopNetworkListener=exports.startNetworkListener=exports.removeAllNetworkListeners=exports.networkListener=exports.isNetworkListening=exports.isNetworkInterceptionLive=exports.getNetworkListenerCount=exports.addNetworkListener=exports.BUOY_XHR_INTERCEPTOR_FLAG=exports.BUOY_FETCH_INTERCEPTOR_FLAG=void 0;var _resolveRequestUrl=require("./resolveRequestUrl");var _requestId=require("./requestId");var _xhr=require("../conditions/xhr");var _store=require("../conditions/store");var _wait=require("../conditions/wait");var _engine=require("../overrides/engine");var _applyToXhr=require("../overrides/applyToXhr");var _applyToFetch=require("../overrides/applyToFetch");var _types=require("../overrides/types");var _httpCompletion=require("./httpCompletion");var _realNow=require("./realNow");let devOrigin;function injectedHeaders(w){const e=globalThis.__buoyHeaderInjectors;if(!e||e.length===0)return void 0;let t;for(const s of e){try{const c=s(w);if(c)t={...t,...c}}catch{}}return t}function devServerOrigin(){if(devOrigin!==void 0)return devOrigin;devOrigin=null;try{if(typeof __DEV__!=="undefined"&&!__DEV__)return devOrigin;const w=require("react-native");const e=w?.NativeModules?.SourceCode;const t=e?.getConstants?.().scriptURL??e?.scriptURL;if(typeof t==="string"&&/^https?:\/\//.test(t)){const s=new URL(t);devOrigin=`${s.protocol}//${s.host}`}}catch{devOrigin=null}return devOrigin}let insideFetchHandoff=false;function headersToRecord(w){const e={};if(!w)return e;if(typeof Headers!=="undefined"&&w instanceof Headers)w.forEach((t,s)=>{e[s]=t});else if(Array.isArray(w))for(const[t,s]of w)e[t]=s;else Object.assign(e,w);return e}const BUOY_FETCH_INTERCEPTOR_FLAG=exports.BUOY_FETCH_INTERCEPTOR_FLAG="__buoyNetworkFetchIntercepted";const BUOY_XHR_INTERCEPTOR_FLAG=exports.BUOY_XHR_INTERCEPTOR_FLAG="__buoyNetworkXhrIntercepted";const STREAMED_CONTENT_TYPES=["text/event-stream","application/x-ndjson","application/stream+json"];const STREAMING_PLACEHOLDER="[streaming\u2026]";function isStreamedContentType(w){if(!w)return false;const e=w.toLowerCase();return STREAMED_CONTENT_TYPES.some(t=>e.includes(t))}const INTERNAL_HOSTS_KEY="__BUOY_INTERNAL_HOSTS__";function isInternalBuoyHost(w){const e=globalThis[INTERNAL_HOSTS_KEY];if(!(e instanceof Set)||e.size===0)return false;let t;try{t=new URL(w).host.toLowerCase()}catch{return false}for(const s of e){if(typeof s==="string"&&s&&t===s.toLowerCase())return true}return false}class NetworkListener{listeners=[];isListening=false;patchedFetch=null;patchedXHROpen=null;patchedXHRSend=null;patchedXHRSetRequestHeader=null;fetchGeneration=0;xhrGeneration=0;constructor(){this.originalFetch=globalThis.fetch;this.originalXHROpen=XMLHttpRequest.prototype.open;this.originalXHRSend=XMLHttpRequest.prototype.send;this.originalXHRSetRequestHeader=XMLHttpRequest.prototype.setRequestHeader}shouldIgnoreUrl(e){try{const t=new URL(e);if(t.hostname==="keygen.sh"||t.hostname.endsWith(".keygen.sh"))return true;if((t.hostname==="buoy.gg"||t.hostname==="www.buoy.gg")&&t.pathname.startsWith("/api/license"))return true;const s=t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="10.0.2.2";if(s&&["8081","8802"].includes(t.port))return true;const c=devServerOrigin();if(c&&`${t.protocol}//${t.host}`===c)return true}catch{return false}return isInternalBuoyHost(e)}emit(e){this.listeners.forEach(t=>{try{t(e)}catch(s){}})}parseUrl(e){let t=null;const s=e.indexOf("?");if(s>-1){t={};e.substring(s+1).split("&").forEach(c=>{const[a,n]=c.split("=");if(a&&n!==void 0){t[a]=decodeURIComponent(n.replace(/\+/g," "))}})}return{url:s>-1?e.substring(0,s):e,params:t}}async readStreamedBody(e,t=256*1024){const s=e.body;if(!s||typeof s.getReader!=="function"){const f=await e.text();return f.length>t?{body:`${f.slice(0,t)}
2
2
 
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.stopNetworkListener = exports.startNetworkListener = exports.removeAllNetworkListeners = exports.networkListener = exports.isNetworkListening = exports.isNetworkInterceptionLive = exports.getNetworkListenerCount = exports.addNetworkListener = exports.BUOY_XHR_INTERCEPTOR_FLAG = exports.BUOY_FETCH_INTERCEPTOR_FLAG = void 0;
7
- var _resolveRequestUrl = require("./resolveRequestUrl");
8
- var _requestId = require("./requestId");
9
- var _xhr = require("../conditions/xhr");
10
- var _store = require("../conditions/store");
11
- var _wait = require("../conditions/wait");
12
- var _engine = require("../overrides/engine");
13
- var _applyToXhr = require("../overrides/applyToXhr");
14
- var _applyToFetch = require("../overrides/applyToFetch");
15
- var _types = require("../overrides/types");
16
- var _httpCompletion = require("./httpCompletion");
17
- /**
18
- * Network listener using Reactotron-style event pattern
19
- * Simple and reliable network interception for React Native
20
- */
3
+ ... [truncated]`,size:f.length}:{body:f,size:f.length}}const c=s.getReader();const a=new TextDecoder;let n="";let l=0;let R=false;try{for(;;){const{done:f,value:i}=await c.read();if(f)break;l+=i?.byteLength??0;if(!R){n+=a.decode(i,{stream:true});if(l>=t){n=`${n.slice(0,t)}
21
4
 
22
- let devOrigin;
5
+ ... [truncated]`;R=true;break}}}if(!R)n+=a.decode()}finally{try{void Promise.resolve(c.cancel()).catch(()=>{})}catch{}try{c.releaseLock()}catch{}}return{body:n,size:l}}async processResponseBody(e,t=1024*1024){try{const s=e.headers.get("content-length");if(s){const n=parseInt(s,10);if(!isNaN(n)&&n>t){return{body:`[Response too large: ${this.formatBytes(n)}]`,size:n,truncated:true}}}const c=await e.text();const a=c.length;if(a>t){const n=c.substring(0,t);const l=a-t;return{body:`${n}
23
6
 
24
- /**
25
- * Headers another Buoy tool adds to a request inside our wrapper.
26
- *
27
- * Impersonate patches fetch/XHR at boot, so this listener usually wraps it
28
- * and records the request before the impersonation header is added. The
29
- * request on the wire carried the header and the Network row did not. Tools
30
- * that add headers register a function here, keyed by URL, so the row shows
31
- * what was actually sent.
32
- */
33
- function injectedHeaders(url) {
34
- const injectors = globalThis.__buoyHeaderInjectors;
35
- if (!injectors || injectors.length === 0) return undefined;
36
- let out;
37
- for (const inject of injectors) {
38
- try {
39
- const h = inject(url);
40
- if (h) out = {
41
- ...out,
42
- ...h
43
- };
44
- } catch {
45
- /* a broken injector must not break capture */
46
- }
47
- }
48
- return out;
49
- }
50
-
51
- /**
52
- * Origin of the Metro dev server this JS bundle was loaded from, read once from
53
- * the bundle URL React Native reports. `null` in release builds (the bundle is
54
- * a file) and anywhere react-native isn't present.
55
- */
56
- function devServerOrigin() {
57
- if (devOrigin !== undefined) return devOrigin;
58
- devOrigin = null;
59
- try {
60
- if (typeof __DEV__ !== "undefined" && !__DEV__) return devOrigin;
61
- // eslint-disable-next-line @typescript-eslint/no-require-imports
62
- const rn = require("react-native");
63
- const sc = rn?.NativeModules?.SourceCode;
64
- const url = sc?.getConstants?.().scriptURL ?? sc?.scriptURL;
65
- if (typeof url === "string" && /^https?:\/\//.test(url)) {
66
- const u = new URL(url);
67
- devOrigin = `${u.protocol}//${u.host}`;
68
- }
69
- } catch {
70
- devOrigin = null;
71
- }
72
- return devOrigin;
73
- }
74
-
75
- /**
76
- * True only while `originalFetch` is being CALLED (not awaited).
77
- *
78
- * Overrides hook both `fetch` and `XMLHttpRequest`, because Expo's native
79
- * `expo/fetch` bypasses XHR while bare RN's whatwg-fetch polyfill runs straight
80
- * through it. On bare RN that means one `fetch()` would otherwise be overridden
81
- * twice — a doubled delay and a doubled hit count.
82
- *
83
- * whatwg-fetch creates and sends its XHR synchronously inside the Promise
84
- * executor, so a flag raised across the call expression alone is enough to
85
- * cover the inner `send()` without leaking across the await.
86
- */
87
- let insideFetchHandoff = false;
88
-
89
- // Extended XMLHttpRequest interface for monkey-patching
90
-
91
- // Event types for network operations
92
-
93
- /**
94
- * Markers stamped on the installed interceptor functions so their presence in
95
- * the live chain stays detectable even after another tool wraps them.
96
- * Cooperating patchers (@buoy-gg/impersonate) copy every `__buoy*` marker from
97
- * the function they wrap onto their own wrapper; a global fetch/XHR WITHOUT
98
- * the marker therefore means our interceptor was clobbered — some runtime
99
- * re-assigned `globalThis.fetch` over it (Expo's winter runtime, React Native
100
- * DevTools' network panel, a second dev server's tooling, hot-reload churn all
101
- * do this in the wild) — and capture must re-install. See startListening.
102
- */
103
- const BUOY_FETCH_INTERCEPTOR_FLAG = exports.BUOY_FETCH_INTERCEPTOR_FLAG = "__buoyNetworkFetchIntercepted";
104
- const BUOY_XHR_INTERCEPTOR_FLAG = exports.BUOY_XHR_INTERCEPTOR_FLAG = "__buoyNetworkXhrIntercepted";
105
- /**
106
- * Network traffic interceptor for React Native applications
107
- *
108
- * This class intercepts both fetch and XMLHttpRequest operations to provide
109
- * comprehensive network monitoring capabilities. It uses method swizzling to
110
- * wrap native networking APIs while preserving their original functionality.
111
- *
112
- * @example
113
- * ```typescript
114
- * // Start monitoring network traffic
115
- * startNetworkListener();
116
- *
117
- * // Add a listener for network events
118
- * const unsubscribe = addNetworkListener((event) => {
119
- * if (event.type === 'response') {
120
- * console.log(`${event.request.method} ${event.request.url}: ${event.response?.status}`);
121
- * }
122
- * });
123
- *
124
- * // Stop monitoring and cleanup
125
- * unsubscribe();
126
- * stopNetworkListener();
127
- * ```
128
- *
129
- * @performance Uses lazy singleton pattern to minimize memory footprint
130
- * @performance Includes URL filtering to ignore development traffic
131
- */
132
- /**
133
- * Hosts another Buoy package has claimed as its own traffic.
134
- *
135
- * A GLOBAL, not an import: `@buoy-gg/network` cannot depend on `agent-core`
136
- * (Ask Buoy is optional and this package is older), and `agent-core` ships
137
- * with no dependencies at all. `registerSelfEndpoint` in agent-core writes the
138
- * set; this reads it. Absent, undefined or the wrong shape all mean "nothing
139
- * to exclude", so nothing breaks in an app without Ask Buoy.
140
- *
141
- * WHY IT IS NOT ENOUGH TO FILTER LATER. The interceptor reads the response
142
- * body — `await processResponseBody(response.clone())` — BEFORE handing the
143
- * response back. On a streamed reply that resolves only at EOF, so an
144
- * intercepted model endpoint delivers its whole answer at once and Ask Buoy
145
- * cannot stream a word. Ignoring the URL is what keeps the socket the
146
- * caller's. It also keeps override rules off model traffic, which a broad
147
- * user-authored rule would otherwise rewrite.
148
- */
149
- /**
150
- * Content types whose body arrives over time rather than all at once.
151
- *
152
- * These are the ones where reading the clone before returning the response
153
- * changes the CALLER's behaviour instead of merely observing it.
154
- */
155
- const STREAMED_CONTENT_TYPES = ["text/event-stream", "application/x-ndjson", "application/stream+json"];
156
- const STREAMING_PLACEHOLDER = "[streaming\u2026]";
157
- function isStreamedContentType(contentType) {
158
- if (!contentType) return false;
159
- const lower = contentType.toLowerCase();
160
- return STREAMED_CONTENT_TYPES.some(t => lower.includes(t));
161
- }
162
- const INTERNAL_HOSTS_KEY = "__BUOY_INTERNAL_HOSTS__";
163
- function isInternalBuoyHost(url) {
164
- const hosts = globalThis[INTERNAL_HOSTS_KEY];
165
- if (!(hosts instanceof Set) || hosts.size === 0) return false;
166
- let authority;
167
- try {
168
- authority = new URL(url).host.toLowerCase();
169
- } catch {
170
- return false;
171
- }
172
- for (const host of hosts) {
173
- if (typeof host === "string" && host && authority === host.toLowerCase()) return true;
174
- }
175
- return false;
176
- }
177
- class NetworkListener {
178
- listeners = [];
179
- isListening = false;
180
-
181
- // Store original methods
182
-
183
- /**
184
- * The wrappers currently installed by THIS instance, for identity checks:
185
- * `stopListening` restores a global only when it is still literally ours —
186
- * assigning our saved original over someone who patched after us would wipe
187
- * their interceptor exactly the way this class gets wiped (see below).
188
- */
189
- patchedFetch = null;
190
- patchedXHROpen = null;
191
- patchedXHRSend = null;
192
- patchedXHRSetRequestHeader = null;
193
-
194
- /**
195
- * Bumped on every (re)install and on stop. Each wrapper closes over the
196
- * generation it was born with and goes silent (pure pass-through — no emit,
197
- * no counter bump) once a newer generation exists, so a wrapper stranded
198
- * inside another tool's chain can never double-report a request.
199
- */
200
- fetchGeneration = 0;
201
- xhrGeneration = 0;
202
- constructor() {
203
- // Store original methods
204
- this.originalFetch = globalThis.fetch;
205
- this.originalXHROpen = XMLHttpRequest.prototype.open;
206
- this.originalXHRSend = XMLHttpRequest.prototype.send;
207
- this.originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
208
- }
209
-
210
- /**
211
- * Check if URL should be ignored from network monitoring
212
- *
213
- * Filters out development-related URLs like Metro bundler, debugger proxy,
214
- * and symbolication requests to reduce noise in the network logs.
215
- *
216
- * @param url - The URL to check
217
- * @returns True if the URL should be ignored
218
- */
219
- shouldIgnoreUrl(url) {
220
- try {
221
- const parsed = new URL(url);
222
- if (parsed.hostname === "keygen.sh" || parsed.hostname.endsWith(".keygen.sh")) return true;
223
- // Buoy's own license endpoint (7.0.39+): the same traffic, new address.
224
- if ((parsed.hostname === "buoy.gg" || parsed.hostname === "www.buoy.gg") && parsed.pathname.startsWith("/api/license")) return true;
225
- const local = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "10.0.2.2";
226
- if (local && ["8081", "8802"].includes(parsed.port)) return true;
227
- // The dev server this bundle actually came from, whatever its host and
228
- // port (a LAN address, --port 8082…): Metro's own traffic — symbolicate,
229
- // logs, HMR, the bundle — is not the app's.
230
- const dev = devServerOrigin();
231
- if (dev && `${parsed.protocol}//${parsed.host}` === dev) return true;
232
- } catch {
233
- return false;
234
- }
235
- return isInternalBuoyHost(url);
236
- }
237
-
238
- // Emit event to all listeners
239
- emit(event) {
240
- this.listeners.forEach(listener => {
241
- try {
242
- listener(event);
243
- } catch (error) {
244
- // Error in event listener - continuing with others
245
- }
246
- });
247
- }
248
-
249
- /**
250
- * Parse URL to extract query parameters and clean URL
251
- *
252
- * @param url - The URL to parse
253
- * @returns Object containing cleaned URL and parsed query parameters
254
- *
255
- * @performance Uses manual parsing instead of URL constructor for better performance
256
- */
257
- parseUrl(url) {
258
- let params = null;
259
- const queryParamIdx = url.indexOf("?");
260
- if (queryParamIdx > -1) {
261
- params = {};
262
- url.substring(queryParamIdx + 1).split("&").forEach(pair => {
263
- const [key, value] = pair.split("=");
264
- if (key && value !== undefined) {
265
- params[key] = decodeURIComponent(value.replace(/\+/g, " "));
266
- }
267
- });
268
- }
269
- return {
270
- url: queryParamIdx > -1 ? url.substring(0, queryParamIdx) : url,
271
- params
272
- };
273
- }
274
-
275
- /**
276
- * Process response body with size limits to prevent memory issues
277
- * @param response - The Response object to process
278
- * @param maxSize - Maximum body size in bytes (default: 1MB)
279
- * @returns Object containing body, size, and truncation status
280
- */
281
- /**
282
- * Read a streamed body in the background, with a hard ceiling.
283
- *
284
- * Deliberately not `processResponseBody`: that one calls `.text()`, which
285
- * accumulates the WHOLE body before it checks the size, so a long-lived SSE
286
- * stream would grow unbounded in memory with nothing to stop it. This stops
287
- * at the cap and cancels the reader, and a runtime with no `body` (React
288
- * Native's own fetch) falls back to `.text()` — that response was never
289
- * streaming there in the first place.
290
- */
291
- async readStreamedBody(response, maxSize = 256 * 1024) {
292
- const body = response.body;
293
- if (!body || typeof body.getReader !== "function") {
294
- const text = await response.text();
295
- return text.length > maxSize ? {
296
- body: `${text.slice(0, maxSize)}\n\n... [truncated]`,
297
- size: text.length
298
- } : {
299
- body: text,
300
- size: text.length
301
- };
302
- }
303
- const reader = body.getReader();
304
- const decoder = new TextDecoder();
305
- let out = "";
306
- let size = 0;
307
- let capped = false;
308
- try {
309
- for (;;) {
310
- const {
311
- done,
312
- value
313
- } = await reader.read();
314
- if (done) break;
315
- size += value?.byteLength ?? 0;
316
- if (!capped) {
317
- out += decoder.decode(value, {
318
- stream: true
319
- });
320
- if (size >= maxSize) {
321
- out = `${out.slice(0, maxSize)}\n\n... [truncated]`;
322
- capped = true;
323
- break;
324
- }
325
- }
326
- }
327
- if (!capped) out += decoder.decode();
328
- } finally {
329
- try {
330
- void Promise.resolve(reader.cancel()).catch(() => {});
331
- } catch {
332
- /* already gone */
333
- }
334
- try {
335
- reader.releaseLock();
336
- } catch {
337
- /* already released */
338
- }
339
- }
340
- return {
341
- body: out,
342
- size
343
- };
344
- }
345
- async processResponseBody(response, maxSize = 1024 * 1024 // 1MB default
346
- ) {
347
- try {
348
- // Check Content-Length header first
349
- const contentLength = response.headers.get("content-length");
350
- if (contentLength) {
351
- const size = parseInt(contentLength, 10);
352
- if (!isNaN(size) && size > maxSize) {
353
- return {
354
- body: `[Response too large: ${this.formatBytes(size)}]`,
355
- size,
356
- truncated: true
357
- };
358
- }
359
- }
360
-
361
- // Read the response text
362
- const text = await response.text();
363
- const size = text.length;
364
-
365
- // Check if text exceeds max size
366
- if (size > maxSize) {
367
- const preview = text.substring(0, maxSize);
368
- const omitted = size - maxSize;
369
- return {
370
- body: `${preview}\n\n... [truncated, ${this.formatBytes(omitted)} omitted]`,
371
- size,
372
- truncated: true
373
- };
374
- }
375
-
376
- // Try to parse as JSON
377
- try {
378
- return {
379
- body: JSON.parse(text),
380
- size,
381
- truncated: false
382
- };
383
- } catch {
384
- // Return as text if not JSON
385
- return {
386
- body: text,
387
- size,
388
- truncated: false
389
- };
390
- }
391
- } catch (error) {
392
- return {
393
- body: "~~~ unable to read body ~~~",
394
- size: 0,
395
- truncated: false
396
- };
397
- }
398
- }
399
-
400
- /**
401
- * Format bytes into human-readable format
402
- * @param bytes - Number of bytes
403
- * @returns Formatted string (e.g., "1.5 MB")
404
- */
405
- formatBytes(bytes) {
406
- if (bytes === 0) return "0 B";
407
- const k = 1024;
408
- const sizes = ["B", "KB", "MB", "GB"];
409
- const i = Math.floor(Math.log(bytes) / Math.log(k));
410
- return `${Math.round(bytes / Math.pow(k, i) * 100) / 100} ${sizes[i]}`;
411
- }
412
-
413
- /**
414
- * Handle XMLHttpRequest response processing
415
- * This method processes the response and emits appropriate events
416
- */
417
- handleXHRResponse(xhr, requestId, cleanUrl, method, requestHeaders, requestData, params, startTime, isError = false, clientType = "xhr", override, condition) {
418
- const duration = startTime ? Date.now() - startTime : 0;
419
-
420
- // XHR's duration was ALREADY total (measured at readyState 4 / load), so
421
- // only the split is new. `_buoyTtfb` is stamped at HEADERS_RECEIVED; it is
422
- // absent for a request that failed before any headers arrived, and the
423
- // breakdown is then correctly omitted rather than faked as 0.
424
- const ttfb = xhr._buoyTtfb;
425
- const timings = ttfb === undefined ? undefined : {
426
- ttfb,
427
- download: Math.max(0, duration - ttfb)
428
- };
429
- if (isError || xhr.status === 0) {
430
- // Network error or aborted request
431
- this.emit({
432
- type: "error",
433
- timestamp: new Date(),
434
- duration,
435
- request: {
436
- id: requestId || "unknown",
437
- url: cleanUrl,
438
- method,
439
- headers: requestHeaders,
440
- data: requestData,
441
- params: params || undefined,
442
- client: clientType
443
- },
444
- error: {
445
- message: isError ? "Request failed" : "Network error or request aborted"
446
- },
447
- override,
448
- condition
449
- });
450
- return;
451
- }
452
-
453
- // Parse response
454
- let body;
455
- let responseSize = 0;
456
- try {
457
- const response = xhr.response;
458
-
459
- // Debug logging to help diagnose responseType issues
460
-
461
- // Try different ways to get response based on responseType
462
- if (xhr.responseType === "json" && response) {
463
- // Response is already parsed as JSON
464
- body = response;
465
- responseSize = JSON.stringify(response).length;
466
- } else if (xhr.responseType === "" || xhr.responseType === "text") {
467
- // Only access responseText when responseType allows it
468
- if (xhr.responseText) {
469
- responseSize = xhr.responseText.length;
470
- try {
471
- body = JSON.parse(xhr.responseText);
472
- } catch {
473
- body = xhr.responseText;
474
- }
475
- }
476
- } else if (xhr.responseType === "arraybuffer") {
477
- // For arraybuffer, try to decode as text and parse as JSON
478
- // This is common for Axios requests that return JSON
479
- if (response) {
480
- try {
481
- const text = new TextDecoder("utf-8").decode(response);
482
- responseSize = text.length;
483
- try {
484
- body = JSON.parse(text);
485
- } catch {
486
- // Not JSON, return as text
487
- body = text;
488
- }
489
- } catch (decodeError) {
490
- // Failed to decode, show placeholder
491
- body = `[arraybuffer response - ${response.byteLength || 0} bytes]`;
492
- responseSize = response.byteLength || 0;
493
- }
494
- } else {
495
- body = `[arraybuffer response - no data]`;
496
- responseSize = 0;
497
- }
498
- } else if (xhr.responseType === "blob") {
499
- // For blob responses, we can't synchronously read the content
500
- // Note: In React Native, most JSON responses shouldn't be blobs
501
- // but if they are, we show metadata
502
- if (response instanceof Blob) {
503
- body = `[blob response - ${response.size} bytes, type: ${response.type || "unknown"}]`;
504
- responseSize = response.size;
505
- } else if (response) {
506
- // Sometimes response might not be a Blob object but still have data
507
- // Try to handle it as an object
508
- try {
509
- body = typeof response === "string" ? JSON.parse(response) : response;
510
- responseSize = JSON.stringify(body).length;
511
- } catch {
512
- body = `[blob response - unable to parse]`;
513
- responseSize = 0;
514
- }
515
- } else {
516
- body = `[blob response - no data]`;
517
- responseSize = 0;
518
- }
519
- } else if (response) {
520
- // Fallback: try to handle the response regardless of responseType
521
- // This catches cases where Axios sets an unexpected responseType
522
- if (typeof response === "string") {
523
- body = response;
524
- responseSize = response.length;
525
- // Try to parse as JSON if it's a string
526
- try {
527
- body = JSON.parse(response);
528
- } catch {
529
- // Not JSON, keep as string
530
- }
531
- } else if (typeof response === "object") {
532
- // Already an object, use it directly
533
- body = response;
534
- responseSize = JSON.stringify(response).length;
535
- } else {
536
- body = String(response);
537
- responseSize = String(response).length;
538
- }
539
- }
540
- } catch (error) {
541
- // Failed to parse response
542
- body = "~~~ unable to read body ~~~";
543
- }
544
-
545
- // Parse response headers
546
- const responseHeaders = {};
547
- try {
548
- const headerString = xhr.getAllResponseHeaders();
549
- if (headerString) {
550
- headerString.split("\r\n").forEach(line => {
551
- if (line) {
552
- const colonIndex = line.indexOf(": ");
553
- if (colonIndex > 0) {
554
- const key = line.substring(0, colonIndex).toLowerCase();
555
- const value = line.substring(colonIndex + 2);
556
- responseHeaders[key] = value;
557
- }
558
- }
559
- });
560
- }
561
- } catch {
562
- // Ignore header parsing errors
563
- }
564
-
565
- // Emit response or error based on status. A 4xx/5xx is a completed HTTP
566
- // response, not a transport error — RN XHR leaves statusText empty, which
567
- // used to become `event.error = "HTTP 500: "` and MCP `ERR HTTP 500: no error`.
568
- if ((0, _httpCompletion.xhrCompletionKind)(xhr.status) === "response") {
569
- this.emit({
570
- type: "response",
571
- timestamp: new Date(),
572
- duration,
573
- timings,
574
- request: {
575
- id: requestId || "unknown",
576
- url: cleanUrl,
577
- method,
578
- headers: requestHeaders,
579
- data: requestData,
580
- params: params || undefined,
581
- client: clientType
582
- },
583
- response: {
584
- status: xhr.status,
585
- statusText: (0, _httpCompletion.httpStatusText)(xhr.status, xhr.statusText),
586
- headers: responseHeaders,
587
- body,
588
- size: responseSize
589
- },
590
- override,
591
- condition
592
- });
593
- } else {
594
- this.emit({
595
- type: "error",
596
- timestamp: new Date(),
597
- duration,
598
- request: {
599
- id: requestId || "unknown",
600
- url: cleanUrl,
601
- method,
602
- headers: requestHeaders,
603
- data: requestData,
604
- params: params || undefined,
605
- client: clientType
606
- },
607
- response: {
608
- status: xhr.status,
609
- statusText: xhr.statusText,
610
- headers: responseHeaders,
611
- body,
612
- size: responseSize
613
- },
614
- error: {
615
- message: "Network Error"
616
- },
617
- override,
618
- condition
619
- });
620
- }
621
- }
622
-
623
- /** Is our fetch interceptor still reachable through the global chain? */
624
- isFetchInterceptionLive() {
625
- return globalThis.fetch[BUOY_FETCH_INTERCEPTOR_FLAG] === true;
626
- }
627
-
628
- /** Is our XHR interceptor still reachable through the prototype chain? */
629
- isXhrInterceptionLive() {
630
- return XMLHttpRequest.prototype.send[BUOY_XHR_INTERCEPTOR_FLAG] === true;
631
- }
632
-
633
- /**
634
- * Both halves of the interceptor are reachable — requests actually flow
635
- * through capture. `isListening` alone cannot promise that (see
636
- * startListening); diagnostics should report THIS.
637
- */
638
- isInterceptionLive() {
639
- return this.isFetchInterceptionLive() && this.isXhrInterceptionLive();
640
- }
641
-
642
- /**
643
- * Start (or repair) interception by swizzling fetch and XMLHttpRequest.
644
- *
645
- * NOT guarded by a plain `isListening` flag: `globalThis.fetch` gets
646
- * re-assigned in the wild — Expo's winter runtime, React Native DevTools'
647
- * network panel, a second dev server's tooling, hot-reload churn. When that
648
- * happens the flag still says "listening" while every request bypasses
649
- * capture, and a flag-only guard makes the outage PERMANENT (observed as
650
- * "No network events" forever with `interceptorInstalled: true`). So this
651
- * verifies each interceptor is actually reachable (marker check) and
652
- * re-installs whichever half was clobbered, wrapping the CURRENT global so
653
- * whatever replaced us keeps working underneath. When both halves are live
654
- * this costs two property reads.
655
- *
656
- * @performance Uses method swizzling for minimal runtime overhead
657
- * @performance Includes request deduplication through ignored URL patterns
658
- */
659
- startListening() {
660
- const fetchLive = this.isListening && this.isFetchInterceptionLive();
661
- const xhrLive = this.isListening && this.isXhrInterceptionLive();
662
- if (fetchLive && xhrLive) {
663
- return;
664
- }
665
- if (!fetchLive) {
666
- this.installFetchInterceptor();
667
- }
668
- if (!xhrLive) {
669
- this.installXhrInterceptor();
670
- }
671
- this.isListening = true;
672
- if (__DEV__) {
673
- // Network listener has started monitoring fetch and XMLHttpRequest operations
674
- }
675
- }
676
-
677
- /** Swizzle `globalThis.fetch`, wrapping whatever is installed right now. */
678
- installFetchInterceptor() {
679
- const self = this;
680
- // The pass-through is the CURRENT global (not the construction-time one):
681
- // re-installing after a clobber must call through the replacement, and a
682
- // stale generation must replay the chain exactly as it stood beneath it.
683
- const passthroughFetch = globalThis.fetch.bind(globalThis);
684
- this.originalFetch = globalThis.fetch;
685
- const generation = ++this.fetchGeneration;
686
- const wrappedFetch = async (input, init) => {
687
- const url = (0, _resolveRequestUrl.resolveRequestUrl)(typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
688
-
689
- // A newer install supersedes this wrapper. If it is still stranded in
690
- // another tool's chain it must pass through silently, or one request
691
- // would be reported once per stranded generation.
692
- if (generation !== self.fetchGeneration) {
693
- return passthroughFetch(input, init);
694
- }
695
-
696
- // Skip ignored URLs
697
- if (self.shouldIgnoreUrl(url)) {
698
- return passthroughFetch(input, init);
699
- }
700
- const startTime = Date.now();
701
- const requestId = (0, _requestId.nextRequestId)("fetch");
702
- const inputRequest = typeof input === "object" && "method" in input ? input : undefined;
703
- const method = init?.method ?? inputRequest?.method ?? "GET";
704
- const signal = init?.signal !== undefined ? init.signal : inputRequest?.signal;
705
- (0, _wait.throwIfAborted)(signal);
706
- const condition = (0, _store.snapshotNetworkCondition)(url);
707
- const {
708
- url: cleanUrl,
709
- params
710
- } = self.parseUrl(url);
711
-
712
- // Parse request headers
713
- let requestHeaders = {};
714
- const headers = init?.headers ?? inputRequest?.headers;
715
- if (headers) {
716
- if (headers instanceof Headers) {
717
- headers.forEach((value, key) => {
718
- requestHeaders[key] = value;
719
- });
720
- } else if (Array.isArray(headers)) {
721
- headers.forEach(([key, value]) => {
722
- requestHeaders[key] = value;
723
- });
724
- } else {
725
- requestHeaders = {
726
- ...headers
727
- };
728
- }
729
- }
730
- Object.assign(requestHeaders, injectedHeaders(url));
731
-
732
- // Parse request body
733
- // Explicitly typed: the late emit for a streamed reply reads this from
734
- // inside a callback, which defeats the inference it used to rely on.
735
- let requestData;
736
- if (init?.body) {
737
- if (typeof init.body === "string") {
738
- try {
739
- requestData = JSON.parse(init.body);
740
- } catch {
741
- requestData = init.body;
742
- }
743
- } else {
744
- requestData = init.body;
745
- }
746
- }
747
-
748
- // Determine client type from X-Request-Client header or default to fetch
749
- const clientType = requestHeaders["X-Request-Client"] || requestHeaders["x-request-client"] || "fetch";
750
-
751
- /**
752
- * Response overrides, fetch side.
753
- *
754
- * Needed even though `send` below also resolves them: Expo installs a
755
- * NATIVE `expo/fetch` as `globalThis.fetch`, which never constructs an
756
- * XMLHttpRequest, so the XHR hook alone would never see these requests.
757
- * Skipped when `canSynthesizeResponse()` is false so a runtime without a
758
- * `Response` global degrades to a real request rather than a broken one.
759
- */
760
- const outcome = condition?.profile !== "offline" && (0, _applyToFetch.canSynthesizeResponse)() ? (0, _engine.overrideForRequest)(url, method) : null;
761
- const override = outcome ? (0, _types.markForOutcome)(outcome) : undefined;
762
-
763
- // Emit request event
764
- self.emit({
765
- type: "request",
766
- timestamp: new Date(),
767
- request: {
768
- id: requestId || "unknown",
769
- url: cleanUrl,
770
- method,
771
- headers: requestHeaders,
772
- data: requestData,
773
- params: params || undefined,
774
- client: clientType
775
- },
776
- override,
777
- condition
778
- });
779
-
780
- // Clone before dispatch; inspect the body without delaying or consuming the app's upload.
781
- if (init?.body === undefined && inputRequest?.body && !inputRequest.bodyUsed) {
782
- try {
783
- const bodyCopy = inputRequest.clone();
784
- void self.readStreamedBody(bodyCopy).then(({
785
- body
786
- }) => {
787
- try {
788
- requestData = JSON.parse(body);
789
- } catch {
790
- requestData = body;
791
- }
792
- self.emit({
793
- type: "request-body",
794
- timestamp: new Date(),
795
- request: {
796
- id: requestId || "unknown",
797
- url: cleanUrl,
798
- method,
799
- data: requestData,
800
- client: clientType
801
- }
802
- });
803
- }).catch(() => {});
804
- } catch {
805
- // An unreadable body must not prevent the app's request.
806
- }
807
- }
808
- try {
809
- if (condition || outcome) await (0, _wait.waitBeforeDispatch)((condition?.delayMs ?? 0) + (outcome?.delayMs ?? 0), signal);
810
- (0, _wait.throwIfAborted)(signal);
811
- if (condition?.profile === "offline") throw new TypeError("Network request failed (Buoy Offline)");
812
- if (outcome?.kind === "fail") throw (0, _applyToFetch.synthesizeFetchError)(outcome);
813
- let response;
814
- if (outcome?.kind === "respond") {
815
- response = (0, _applyToFetch.synthesizeResponse)(url, outcome);
816
- } else {
817
- // The flag spans the CALL only — see `insideFetchHandoff`. It has
818
- // to be cleared before the await, or it would still be raised for
819
- // every unrelated request that starts while this one is in flight.
820
- const previousFetchHandoff = insideFetchHandoff;
821
- insideFetchHandoff = true;
822
- let pending;
823
- try {
824
- pending = passthroughFetch(input, init);
825
- } finally {
826
- insideFetchHandoff = previousFetchHandoff;
827
- }
828
- response = await pending;
829
- }
830
- // A fetch promise resolves when the HEADERS arrive, not the body — so
831
- // this is time-to-first-byte, and it is where `duration` used to stop.
832
- // That silently excluded download time, making an identical request
833
- // look faster through fetch than through XHR.
834
- const ttfb = Date.now() - startTime;
835
-
836
- /**
837
- * A STREAMED reply is never awaited here.
838
- *
839
- * `processResponseBody` reads the clone to the end, and for
840
- * `text/event-stream` the end is when the server finishes — seconds
841
- * later, for an LLM. Awaiting it made the interceptor hold the
842
- * response back until then, so every consumer of a streaming endpoint
843
- * inside an app with capture on received the whole thing at once and
844
- * could not stream a word of it. The row is emitted now with what is
845
- * known, and the body is filled in when the stream closes: the store
846
- * merges a second `response` event onto the same request id.
847
- */
848
- const streamed = typeof response.body?.getReader === "function" || isStreamedContentType(response.headers?.get?.("content-type"));
849
-
850
- // Clone response to read body with size limits
851
- const responseClone = response.clone();
852
- const {
853
- body,
854
- size: responseSize
855
- } = streamed ? {
856
- body: STREAMING_PLACEHOLDER,
857
- size: 0
858
- } : await self.processResponseBody(responseClone);
859
-
860
- // Now the body is in — this is the comparable, total number.
861
- const duration = Date.now() - startTime;
862
-
863
- /**
864
- * The split is only real on platforms whose fetch resolves at HEADERS.
865
- *
866
- * React Native's does NOT: `fetch` there is the whatwg-fetch polyfill
867
- * over XMLHttpRequest, and it resolves inside `xhr.onload` — readyState
868
- * 4, i.e. after the entire body. On device this `ttfb` is therefore the
869
- * total and the download phase collapses to ~0, so claiming a "0ms
870
- * download" would be asserting a measurement we never made.
871
- *
872
- * Omitting is the right answer rather than a problem: RN's fetch runs
873
- * through XMLHttpRequest, so the XHR interceptor below sees readyState 2
874
- * and supplies a genuine TTFB for those requests anyway. Where fetch is
875
- * native (web, Node), the gap is real and we keep it.
876
- *
877
- * Verified both ways against a server that flushes headers then holds
878
- * the body 250ms: native fetch → 16ms / 251ms; resolve-at-end → the
879
- * download phase vanishes, which is what this guard catches.
880
- */
881
- const download = Math.max(0, duration - ttfb);
882
- const timings = download > 0 ? {
883
- ttfb,
884
- download
885
- } : undefined;
886
-
887
- // Parse response headers
888
- const responseHeaders = {};
889
- response.headers.forEach((value, key) => {
890
- responseHeaders[key.toLowerCase()] = value;
891
- });
892
-
893
- // Emit response event
894
- self.emit({
895
- type: "response",
896
- timestamp: new Date(),
897
- duration,
898
- timings,
899
- request: {
900
- id: requestId || "unknown",
901
- url: cleanUrl,
902
- method,
903
- headers: requestHeaders,
904
- data: requestData,
905
- params: params || undefined,
906
- client: clientType
907
- },
908
- response: {
909
- status: response.status,
910
- statusText: response.statusText,
911
- headers: responseHeaders,
912
- body,
913
- size: responseSize
914
- },
915
- override,
916
- condition
917
- });
918
- if (streamed) {
919
- // Bounded, cancelled at the cap, and never awaited by the caller.
920
- void self.readStreamedBody(responseClone).then(late => {
921
- let capturedBody = late.body;
922
- if (response.headers?.get?.("content-type")?.includes("json")) {
923
- try {
924
- capturedBody = JSON.parse(late.body);
925
- } catch {/* Partial or malformed JSON stays text. */}
926
- }
927
- self.emit({
928
- type: "response",
929
- timestamp: new Date(),
930
- duration: Date.now() - startTime,
931
- timings,
932
- request: {
933
- id: requestId || "unknown",
934
- url: cleanUrl,
935
- method,
936
- headers: requestHeaders,
937
- data: requestData,
938
- params: params || undefined,
939
- client: clientType
940
- },
941
- response: {
942
- status: response.status,
943
- statusText: response.statusText,
944
- headers: responseHeaders,
945
- body: capturedBody,
946
- size: late.size
947
- },
948
- override,
949
- condition
950
- });
951
- }).catch(() => {
952
- /* the caller's response is unaffected either way */
953
- });
954
- }
955
- return response;
956
- } catch (error) {
957
- const duration = Date.now() - startTime;
958
-
959
- // Emit error event
960
- self.emit({
961
- type: "error",
962
- timestamp: new Date(),
963
- duration,
964
- request: {
965
- id: requestId || "unknown",
966
- url: cleanUrl,
967
- method,
968
- headers: requestHeaders,
969
- data: requestData,
970
- params: params || undefined,
971
- client: clientType
972
- },
973
- error: {
974
- message: error instanceof Error ? error.message : "Network error",
975
- stack: error instanceof Error ? error.stack : undefined
976
- },
977
- override,
978
- condition
979
- });
980
- throw error;
981
- }
982
- };
983
- wrappedFetch[BUOY_FETCH_INTERCEPTOR_FLAG] = true;
984
- this.patchedFetch = wrappedFetch;
985
- globalThis.fetch = this.patchedFetch;
986
- }
987
-
988
- /** Swizzle the XHR prototype methods, wrapping whatever is installed now. */
989
- installXhrInterceptor() {
990
- const self = this;
991
- // Same contract as the fetch half: pass through to the CURRENT methods.
992
- const passthroughOpen = XMLHttpRequest.prototype.open;
993
- const passthroughSend = XMLHttpRequest.prototype.send;
994
- const passthroughSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
995
- this.originalXHROpen = passthroughOpen;
996
- this.originalXHRSend = passthroughSend;
997
- this.originalXHRSetRequestHeader = passthroughSetRequestHeader;
998
- const generation = ++this.xhrGeneration;
999
- const pendingCapture = new WeakMap();
1000
- const patchedOpen = function (method, url, async, user, password) {
1001
- // Native validation runs before replacing metadata or capture listeners.
1002
- const result = passthroughOpen.call(this, method, url, async ?? true, user, password);
1003
- if (generation === self.xhrGeneration) {
1004
- pendingCapture.get(this)?.();
1005
- const xhr = this;
1006
- xhr._buoyRequestId = (0, _requestId.nextRequestId)("xhr");
1007
- xhr._method = method;
1008
- xhr._url = (0, _resolveRequestUrl.resolveRequestUrl)(url);
1009
- xhr._buoyStartTime = Date.now();
1010
- xhr._requestHeaders = {};
1011
- }
1012
- return result;
1013
- };
1014
-
1015
- // Track request headers
1016
- const patchedSetRequestHeader = function (header, value) {
1017
- const xhr = this;
1018
- if (generation === self.xhrGeneration && xhr._requestHeaders) {
1019
- xhr._requestHeaders[header] = value;
1020
- }
1021
- return passthroughSetRequestHeader.call(this, header, value);
1022
- };
1023
- const patchedSend = function (
1024
- // @ts-ignore - this does exist on native
1025
- data) {
1026
- // Superseded generation: silent pass-through (see the fetch wrapper).
1027
- if (generation !== self.xhrGeneration || insideFetchHandoff) {
1028
- return passthroughSend.call(this, data);
1029
- }
1030
- if ((0, _applyToXhr.hasPendingStandardRequest)(this)) throw new DOMException("The request has already been sent.", "InvalidStateError");
1031
- if (this.readyState !== 1 || this._sent) {
1032
- return passthroughSend.call(this, data);
1033
- }
1034
- const xhr = this;
1035
- const requestId = xhr._buoyRequestId;
1036
- const method = xhr._method || "GET";
1037
- const url = xhr._url || "";
1038
- const startTime = Date.now();
1039
- xhr._buoyStartTime = startTime;
1040
- xhr._buoyTtfb = undefined;
1041
- const requestHeaders = xhr._requestHeaders || {};
1042
- Object.assign(requestHeaders, injectedHeaders(url));
1043
-
1044
- // Skip ignored URLs
1045
- if (self.shouldIgnoreUrl(url)) {
1046
- return passthroughSend.call(this, data);
1047
- }
1048
- const {
1049
- url: cleanUrl,
1050
- params
1051
- } = self.parseUrl(url);
1052
-
1053
- // Parse request data
1054
- let requestData;
1055
- if (data) {
1056
- if (typeof data === "string") {
1057
- try {
1058
- requestData = JSON.parse(data);
1059
- } catch {
1060
- requestData = data;
1061
- }
1062
- } else {
1063
- requestData = data;
1064
- }
1065
- }
1066
-
1067
- // Determine client type. Honor an explicit X-Request-Client header first.
1068
- // Otherwise sniff axios via its default Accept header and fall back to
1069
- // "xhr" — defaulting to "axios" misclassified every raw XMLHttpRequest.
1070
- const explicitClient = requestHeaders["X-Request-Client"] || requestHeaders["x-request-client"];
1071
- const acceptHeader = requestHeaders["Accept"] || requestHeaders["accept"] || "";
1072
- const looksLikeAxios = acceptHeader.includes("application/json") && acceptHeader.includes("text/plain") && acceptHeader.includes("*/*");
1073
- const clientType = explicitClient || (looksLikeAxios ? "axios" : "xhr");
1074
-
1075
- /**
1076
- * Response overrides.
1077
- *
1078
- * This is the layer that covers `axios` and raw XMLHttpRequest. It also
1079
- * covers `fetch` on BARE React Native, where the whatwg-fetch polyfill
1080
- * runs through XHR — which is why the fetch wrapper raises
1081
- * `insideFetchHandoff` around its pass-through, so one request can't
1082
- * be overridden twice.
1083
- *
1084
- * Matching uses the raw `url` (query string included), which is what the
1085
- * developer sees in the list and writes into a rule. No-ops in production
1086
- * builds; see `overrideForRequest`.
1087
- */
1088
- const condition = (0, _store.snapshotNetworkCondition)(url);
1089
- const outcome = condition?.profile === "offline" ? null : (0, _engine.overrideForRequest)(url, method);
1090
- const override = outcome ? (0, _types.markForOutcome)(outcome) : undefined;
1091
-
1092
- // Emit request event
1093
- self.emit({
1094
- type: "request",
1095
- timestamp: new Date(),
1096
- request: {
1097
- id: requestId || "unknown",
1098
- url: cleanUrl,
1099
- method,
1100
- headers: requestHeaders,
1101
- data: requestData,
1102
- params: params || undefined,
1103
- client: clientType
1104
- },
1105
- override,
1106
- condition
1107
- });
1108
-
1109
- // Track if we've already processed this request to avoid duplicate events
1110
- let processed = false;
1111
- const processResponse = (isError = false) => {
1112
- if (processed) return;
1113
- processed = true;
1114
- self.handleXHRResponse(this, requestId || "unknown", cleanUrl, method, requestHeaders, requestData, params, startTime || 0, isError, clientType, override, condition);
1115
- // Clean up event listeners after processing to prevent memory leaks
1116
- cleanup();
1117
- };
1118
-
1119
- // Cleanup function to remove event listeners
1120
- const cleanup = () => {
1121
- pendingCapture.delete(this);
1122
- this.removeEventListener("load", loadListener);
1123
- this.removeEventListener("error", errorListener);
1124
- this.removeEventListener("abort", abortListener);
1125
- this.removeEventListener("readystatechange", readyStateListener);
1126
- };
1127
- pendingCapture.set(this, cleanup);
1128
-
1129
- // Use addEventListener to listen to events WITHOUT replacing user handlers
1130
- // This is critical because React Native's XMLHttpRequest uses EventTarget
1131
- // with getters/setters that shouldn't be overridden
1132
-
1133
- const loadListener = () => {
1134
- processResponse(false);
1135
- };
1136
- const errorListener = () => {
1137
- processResponse(true);
1138
- };
1139
- const abortListener = () => {
1140
- processResponse(true);
1141
- };
1142
- const readyStateListener = () => {
1143
- // HEADERS_RECEIVED — the XHR equivalent of a fetch promise resolving.
1144
- // This listener is already attached for the readyState-4 fallback, and
1145
- // readystatechange fires whether or not we look at it, so capturing
1146
- // TTFB here costs one comparison and one clock read.
1147
- if (this.readyState === 2) {
1148
- const xhr = this;
1149
- if (xhr._buoyTtfb === undefined && xhr._buoyStartTime) {
1150
- xhr._buoyTtfb = Date.now() - xhr._buoyStartTime;
1151
- }
1152
- }
1153
- if (this.readyState === 4 && !processed) {
1154
- processResponse(false);
1155
- }
1156
- };
1157
-
1158
- // Add event listeners that will fire alongside user handlers
1159
- this.addEventListener("load", loadListener);
1160
- this.addEventListener("error", errorListener);
1161
- this.addEventListener("abort", abortListener);
1162
- this.addEventListener("readystatechange", readyStateListener);
1163
- if (condition) {
1164
- (0, _xhr.runConditionXhr)(this, url, condition, outcome, () => passthroughSend.call(this, data));
1165
- return;
1166
- }
1167
- if (outcome) {
1168
- // Listeners are attached above FIRST: `runOverride` drives the same
1169
- // readyState transitions a real response would, and the interceptor has
1170
- // to see them (readyState 2 is where TTFB is stamped).
1171
- const handled = (0, _applyToXhr.runOverride)(this, url, outcome, () => passthroughSend.call(this, data));
1172
- // `handled` is false only where this isn't React Native's own
1173
- // XMLHttpRequest and can't be driven by hand. Fall through and send for
1174
- // real rather than dropping the request on the floor.
1175
- if (handled) return;
1176
- }
1177
- return passthroughSend.call(this, data);
1178
- };
1179
- patchedSend[BUOY_XHR_INTERCEPTOR_FLAG] = true;
1180
- this.patchedXHROpen = patchedOpen;
1181
- this.patchedXHRSend = patchedSend;
1182
- this.patchedXHRSetRequestHeader = patchedSetRequestHeader;
1183
- XMLHttpRequest.prototype.open = this.patchedXHROpen;
1184
- XMLHttpRequest.prototype.send = this.patchedXHRSend;
1185
- XMLHttpRequest.prototype.setRequestHeader = this.patchedXHRSetRequestHeader;
1186
- }
1187
-
1188
- /**
1189
- * Stop listening and restore original networking methods
1190
- *
1191
- * This method restores the original fetch and XMLHttpRequest implementations,
1192
- * effectively disabling network monitoring.
1193
- */
1194
- stopListening() {
1195
- if (!this.isListening) {
1196
- return;
1197
- }
1198
-
1199
- // Silence any of our wrappers stranded inside another tool's chain — they
1200
- // check the generation on every call and now pass through without emitting.
1201
- this.fetchGeneration++;
1202
- this.xhrGeneration++;
1203
-
1204
- // Restore ONLY what is still literally ours. If another tool patched after
1205
- // us (impersonate, a debugger), assigning our saved originals would wipe
1206
- // ITS interceptor — the exact injury startListening defends against.
1207
- if (this.patchedFetch && globalThis.fetch === this.patchedFetch) {
1208
- globalThis.fetch = this.originalFetch;
1209
- }
1210
- if (this.patchedXHROpen && XMLHttpRequest.prototype.open === this.patchedXHROpen) {
1211
- XMLHttpRequest.prototype.open = this.originalXHROpen;
1212
- }
1213
- if (this.patchedXHRSend && XMLHttpRequest.prototype.send === this.patchedXHRSend) {
1214
- XMLHttpRequest.prototype.send = this.originalXHRSend;
1215
- }
1216
- if (this.patchedXHRSetRequestHeader && XMLHttpRequest.prototype.setRequestHeader === this.patchedXHRSetRequestHeader) {
1217
- XMLHttpRequest.prototype.setRequestHeader = this.originalXHRSetRequestHeader;
1218
- }
1219
- this.patchedFetch = null;
1220
- this.patchedXHROpen = null;
1221
- this.patchedXHRSend = null;
1222
- this.patchedXHRSetRequestHeader = null;
1223
- this.isListening = false;
1224
- if (__DEV__) {
1225
- // Network listener has stopped monitoring and restored original methods
1226
- }
1227
- }
1228
-
1229
- /**
1230
- * Add a listener for network events
1231
- *
1232
- * @param listener - Callback function to handle network events
1233
- * @returns Unsubscribe function to remove the listener
1234
- */
1235
- addListener(listener) {
1236
- this.listeners.push(listener);
1237
-
1238
- // Return unsubscribe function
1239
- return () => {
1240
- const index = this.listeners.indexOf(listener);
1241
- if (index > -1) {
1242
- this.listeners.splice(index, 1);
1243
- }
1244
- };
1245
- }
1246
-
1247
- // Remove all listeners
1248
- removeAllListeners() {
1249
- this.listeners = [];
1250
- }
1251
-
1252
- // Check if currently listening
1253
- get isActive() {
1254
- return this.isListening;
1255
- }
1256
-
1257
- // Get number of active listeners
1258
- get listenerCount() {
1259
- return this.listeners.length;
1260
- }
1261
- }
1262
-
1263
- /**
1264
- * Lazy singleton instance holder for NetworkListener
1265
- *
1266
- * This pattern ensures only one NetworkListener instance exists throughout
1267
- * the application lifecycle while deferring instantiation until first use.
1268
- */
1269
- let _networkListener = null;
1270
-
1271
- /**
1272
- * Get or create the singleton NetworkListener instance
1273
- *
1274
- * @returns The singleton NetworkListener instance
1275
- */
1276
- const getNetworkListener = () => {
1277
- if (!_networkListener) {
1278
- _networkListener = new NetworkListener();
1279
- }
1280
- return _networkListener;
1281
- };
1282
-
1283
- /**
1284
- * Access function for the singleton NetworkListener instance
1285
- *
1286
- * @returns Function that returns the NetworkListener instance
1287
- */
1288
- const networkListener = exports.networkListener = getNetworkListener;
1289
-
1290
- /**
1291
- * Start network traffic monitoring
1292
- *
1293
- * @example
1294
- * ```typescript
1295
- * startNetworkListener();
1296
- * console.log('Network monitoring started');
1297
- * ```
1298
- */
1299
- const startNetworkListener = () => getNetworkListener().startListening();
1300
-
1301
- /**
1302
- * Stop network traffic monitoring
1303
- */
1304
- exports.startNetworkListener = startNetworkListener;
1305
- const stopNetworkListener = () => _networkListener?.stopListening();
1306
-
1307
- /**
1308
- * Add a listener for network events
1309
- *
1310
- * @param listener - Callback function to handle network events
1311
- * @returns Unsubscribe function to remove the listener
1312
- *
1313
- * @example
1314
- * ```typescript
1315
- * const unsubscribe = addNetworkListener((event) => {
1316
- * console.log(`Network ${event.type}:`, event.request.url);
1317
- * });
1318
- *
1319
- * // Later...
1320
- * unsubscribe();
1321
- * ```
1322
- */
1323
- exports.stopNetworkListener = stopNetworkListener;
1324
- const addNetworkListener = listener => getNetworkListener().addListener(listener);
1325
-
1326
- /**
1327
- * Remove all registered network event listeners
1328
- */
1329
- exports.addNetworkListener = addNetworkListener;
1330
- const removeAllNetworkListeners = () => _networkListener?.removeAllListeners();
1331
-
1332
- /**
1333
- * Check if network monitoring is currently active
1334
- *
1335
- * @returns True if currently intercepting network traffic
1336
- */
1337
- exports.removeAllNetworkListeners = removeAllNetworkListeners;
1338
- const isNetworkListening = () => _networkListener?.isActive ?? false;
1339
-
1340
- /**
1341
- * Get the number of registered network event listeners
1342
- *
1343
- * @returns Number of active listeners
1344
- */
1345
- exports.isNetworkListening = isNetworkListening;
1346
- const getNetworkListenerCount = () => _networkListener?.listenerCount ?? 0;
1347
-
1348
- /**
1349
- * Whether both interceptors are actually reachable through the live global
1350
- * chain — the honest version of `isNetworkListening` (whose flag stays true
1351
- * even after another tool re-assigns `globalThis.fetch` over the wrapper).
1352
- */
1353
- exports.getNetworkListenerCount = getNetworkListenerCount;
1354
- const isNetworkInterceptionLive = () => _networkListener?.isInterceptionLive() ?? false;
1355
- exports.isNetworkInterceptionLive = isNetworkInterceptionLive;
7
+ ... [truncated, ${this.formatBytes(l)} omitted]`,size:a,truncated:true}}try{return{body:JSON.parse(c),size:a,truncated:false}}catch{return{body:c,size:a,truncated:false}}}catch(s){return{body:"~~~ unable to read body ~~~",size:0,truncated:false}}}formatBytes(e){if(e===0)return"0 B";const t=1024;const s=["B","KB","MB","GB"];const c=Math.floor(Math.log(e)/Math.log(t));return`${Math.round(e/Math.pow(t,c)*100)/100} ${s[c]}`}handleXHRResponse(e,t,s,c,a,n,l,R,f=false,i="xhr",d,L){const g=R?(0,_realNow.realNow)()-R:0;const h=e._buoyTtfb;const _=h===void 0?void 0:{ttfb:h,download:Math.max(0,g-h)};if(f||e.status===0){this.emit({type:"error",timestamp:new Date,duration:g,request:{id:t||"unknown",url:s,method:c,headers:a,data:n,params:l||void 0,client:i},error:{message:f?"Request failed":"Network error or request aborted"},override:d,condition:L});return}let r;let p=0;try{const o=e.response;if(e.responseType==="json"&&o){r=o;p=JSON.stringify(o).length}else if(e.responseType===""||e.responseType==="text"){if(e.responseText){p=e.responseText.length;try{r=JSON.parse(e.responseText)}catch{r=e.responseText}}}else if(e.responseType==="arraybuffer"){if(o){try{const y=new TextDecoder("utf-8").decode(o);p=y.length;try{r=JSON.parse(y)}catch{r=y}}catch(y){r=`[arraybuffer response - ${o.byteLength||0} bytes]`;p=o.byteLength||0}}else{r=`[arraybuffer response - no data]`;p=0}}else if(e.responseType==="blob"){if(o instanceof Blob){r=`[blob response - ${o.size} bytes, type: ${o.type||"unknown"}]`;p=o.size}else if(o){try{r=typeof o==="string"?JSON.parse(o):o;p=JSON.stringify(r).length}catch{r=`[blob response - unable to parse]`;p=0}}else{r=`[blob response - no data]`;p=0}}else if(o){if(typeof o==="string"){r=o;p=o.length;try{r=JSON.parse(o)}catch{}}else if(typeof o==="object"){r=o;p=JSON.stringify(o).length}else{r=String(o);p=String(o).length}}}catch(o){r="~~~ unable to read body ~~~"}const m={};try{const o=e.getAllResponseHeaders();if(o){o.split("\r\n").forEach(y=>{if(y){const q=y.indexOf(": ");if(q>0){const u=y.substring(0,q).toLowerCase();const b=y.substring(q+2);m[u]=b}}})}}catch{}if((0,_httpCompletion.xhrCompletionKind)(e.status)==="response"){this.emit({type:"response",timestamp:new Date,duration:g,timings:_,request:{id:t||"unknown",url:s,method:c,headers:a,data:n,params:l||void 0,client:i},response:{status:e.status,statusText:(0,_httpCompletion.httpStatusText)(e.status,e.statusText),headers:m,body:r,size:p},override:d,condition:L})}else{this.emit({type:"error",timestamp:new Date,duration:g,request:{id:t||"unknown",url:s,method:c,headers:a,data:n,params:l||void 0,client:i},response:{status:e.status,statusText:e.statusText,headers:m,body:r,size:p},error:{message:"Network Error"},override:d,condition:L})}}isFetchInterceptionLive(){return globalThis.fetch[BUOY_FETCH_INTERCEPTOR_FLAG]===true}isXhrInterceptionLive(){return XMLHttpRequest.prototype.send[BUOY_XHR_INTERCEPTOR_FLAG]===true}isInterceptionLive(){return this.isFetchInterceptionLive()&&this.isXhrInterceptionLive()}startListening(){const e=this.isListening&&this.isFetchInterceptionLive();const t=this.isListening&&this.isXhrInterceptionLive();if(e&&t){return}if(!e){this.installFetchInterceptor()}if(!t){this.installXhrInterceptor()}this.isListening=true;if(__DEV__){}}installFetchInterceptor(){const e=this;const t=globalThis.fetch.bind(globalThis);this.originalFetch=globalThis.fetch;const s=++this.fetchGeneration;const c=async(a,n)=>{const l=(0,_resolveRequestUrl.resolveRequestUrl)(typeof a==="string"?a:a instanceof URL?a.href:a.url);if(s!==e.fetchGeneration){return t(a,n)}if(e.shouldIgnoreUrl(l)){return t(a,n)}const R=(0,_realNow.realNow)();const f=(0,_requestId.nextRequestId)("fetch");const i=typeof a==="object"&&"method"in a?a:void 0;const d=n?.method??i?.method??"GET";const L=n?.signal!==void 0?n.signal:i?.signal;(0,_wait.throwIfAborted)(L);const g=(0,_store.snapshotNetworkCondition)(l);const{url:h,params:_}=e.parseUrl(l);let r={};const p=n?.headers??i?.headers;if(p){if(p instanceof Headers){p.forEach((u,b)=>{r[b]=u})}else if(Array.isArray(p)){p.forEach(([u,b])=>{r[u]=b})}else{r={...p}}}Object.assign(r,injectedHeaders(l));let m;if(n?.body){if(typeof n.body==="string"){try{m=JSON.parse(n.body)}catch{m=n.body}}else{m=n.body}}const o=r["X-Request-Client"]||r["x-request-client"]||"fetch";const y=g?.profile!=="offline"&&(0,_applyToFetch.canSynthesizeResponse)()?(0,_engine.overrideForRequest)(l,d,r):null;const q=y?(0,_types.markForOutcome)(y):void 0;e.emit({type:"request",timestamp:new Date,request:{id:f||"unknown",url:h,method:d,headers:r,data:m,params:_||void 0,client:o},override:q,condition:g});if(n?.body===void 0&&i?.body&&!i.bodyUsed){try{const u=i.clone();void e.readStreamedBody(u).then(({body:b})=>{try{m=JSON.parse(b)}catch{m=b}e.emit({type:"request-body",timestamp:new Date,request:{id:f||"unknown",url:h,method:d,data:m,client:o}})}).catch(()=>{})}catch{}}try{if(g||y)await(0,_wait.waitBeforeDispatch)((g?.delayMs??0)+(y?.delayMs??0),L);(0,_wait.throwIfAborted)(L);if(g?.profile==="offline")throw new TypeError("Network request failed (Buoy Offline)");if(y?.kind==="fail")throw(0,_applyToFetch.synthesizeFetchError)(y);let u;if(y?.kind==="respond"){u=(0,_applyToFetch.synthesizeResponse)(l,y)}else{const v=insideFetchHandoff;insideFetchHandoff=true;let N;try{N=t(a,n)}finally{insideFetchHandoff=v}u=await N}const b=(0,_realNow.realNow)()-R;const H=typeof u.body?.getReader==="function"||isStreamedContentType(u.headers?.get?.("content-type"));const T=u.clone();const{body:I,size:S}=H?{body:STREAMING_PLACEHOLDER,size:0}:await e.processResponseBody(T);const E=(0,_realNow.realNow)()-R;const X=Math.max(0,E-b);const O=X>0?{ttfb:b,download:X}:void 0;const x={};u.headers.forEach((v,N)=>{x[N.toLowerCase()]=v});e.emit({type:"response",timestamp:new Date,duration:E,timings:O,request:{id:f||"unknown",url:h,method:d,headers:r,data:m,params:_||void 0,client:o},response:{status:u.status,statusText:u.statusText,headers:x,body:I,size:S},override:q,condition:g});if(H){void e.readStreamedBody(T).then(v=>{let N=v.body;if(u.headers?.get?.("content-type")?.includes("json")){try{N=JSON.parse(v.body)}catch{}}e.emit({type:"response",timestamp:new Date,duration:(0,_realNow.realNow)()-R,timings:O,request:{id:f||"unknown",url:h,method:d,headers:r,data:m,params:_||void 0,client:o},response:{status:u.status,statusText:u.statusText,headers:x,body:N,size:v.size},override:q,condition:g})}).catch(()=>{})}return u}catch(u){const b=(0,_realNow.realNow)()-R;e.emit({type:"error",timestamp:new Date,duration:b,request:{id:f||"unknown",url:h,method:d,headers:r,data:m,params:_||void 0,client:o},error:{message:u instanceof Error?u.message:"Network error",stack:u instanceof Error?u.stack:void 0},override:q,condition:g});throw u}};c[BUOY_FETCH_INTERCEPTOR_FLAG]=true;this.patchedFetch=c;globalThis.fetch=this.patchedFetch}installXhrInterceptor(){const e=this;const t=XMLHttpRequest.prototype.open;const s=XMLHttpRequest.prototype.send;const c=XMLHttpRequest.prototype.setRequestHeader;this.originalXHROpen=t;this.originalXHRSend=s;this.originalXHRSetRequestHeader=c;const a=++this.xhrGeneration;const n=new WeakMap;const l=function(i,d,L,g,h){const _=t.call(this,i,d,L??true,g,h);if(a===e.xhrGeneration){n.get(this)?.();const r=this;r._buoyRequestId=(0,_requestId.nextRequestId)("xhr");r._method=i;r._url=(0,_resolveRequestUrl.resolveRequestUrl)(d);r._buoyStartTime=(0,_realNow.realNow)();r._requestHeaders={}}return _};const R=function(i,d){const L=this;if(a===e.xhrGeneration&&L._requestHeaders){L._requestHeaders[i]=d}return c.call(this,i,d)};const f=function(i){if(a!==e.xhrGeneration||insideFetchHandoff){return s.call(this,i)}if((0,_applyToXhr.hasPendingStandardRequest)(this))throw new DOMException("The request has already been sent.","InvalidStateError");if(this.readyState!==1||this._sent){return s.call(this,i)}const d=this;const L=d._buoyRequestId;const g=d._method||"GET";const h=d._url||"";const _=(0,_realNow.realNow)();d._buoyStartTime=_;d._buoyTtfb=void 0;const r=d._requestHeaders||{};Object.assign(r,injectedHeaders(h));if(e.shouldIgnoreUrl(h)){return s.call(this,i)}const{url:p,params:m}=e.parseUrl(h);let o;if(i){if(typeof i==="string"){try{o=JSON.parse(i)}catch{o=i}}else{o=i}}const y=r["X-Request-Client"]||r["x-request-client"];const q=r["Accept"]||r["accept"]||"";const u=q.includes("application/json")&&q.includes("text/plain")&&q.includes("*/*");const b=y||(u?"axios":"xhr");const H=(0,_store.snapshotNetworkCondition)(h);const T=H?.profile==="offline"?null:(0,_engine.overrideForRequest)(h,g,r);const I=T?(0,_types.markForOutcome)(T):void 0;e.emit({type:"request",timestamp:new Date,request:{id:L||"unknown",url:p,method:g,headers:r,data:o,params:m||void 0,client:b},override:I,condition:H});let S=false;const E=(k=false)=>{if(S)return;S=true;e.handleXHRResponse(this,L||"unknown",p,g,r,o,m,_||0,k,b,I,H);X()};const X=()=>{n.delete(this);this.removeEventListener("load",O);this.removeEventListener("error",x);this.removeEventListener("abort",v);this.removeEventListener("readystatechange",N)};n.set(this,X);const O=()=>{E(false)};const x=()=>{E(true)};const v=()=>{E(true)};const N=()=>{if(this.readyState===2){const k=this;if(k._buoyTtfb===void 0&&k._buoyStartTime){k._buoyTtfb=(0,_realNow.realNow)()-k._buoyStartTime}}if(this.readyState===4&&!S){E(false)}};this.addEventListener("load",O);this.addEventListener("error",x);this.addEventListener("abort",v);this.addEventListener("readystatechange",N);if(H){(0,_xhr.runConditionXhr)(this,h,H,T,()=>s.call(this,i));return}if(T){const k=(0,_applyToXhr.runOverride)(this,h,T,()=>s.call(this,i));if(k)return}return s.call(this,i)};f[BUOY_XHR_INTERCEPTOR_FLAG]=true;this.patchedXHROpen=l;this.patchedXHRSend=f;this.patchedXHRSetRequestHeader=R;XMLHttpRequest.prototype.open=this.patchedXHROpen;XMLHttpRequest.prototype.send=this.patchedXHRSend;XMLHttpRequest.prototype.setRequestHeader=this.patchedXHRSetRequestHeader}stopListening(){if(!this.isListening){return}this.fetchGeneration++;this.xhrGeneration++;if(this.patchedFetch&&globalThis.fetch===this.patchedFetch){globalThis.fetch=this.originalFetch}if(this.patchedXHROpen&&XMLHttpRequest.prototype.open===this.patchedXHROpen){XMLHttpRequest.prototype.open=this.originalXHROpen}if(this.patchedXHRSend&&XMLHttpRequest.prototype.send===this.patchedXHRSend){XMLHttpRequest.prototype.send=this.originalXHRSend}if(this.patchedXHRSetRequestHeader&&XMLHttpRequest.prototype.setRequestHeader===this.patchedXHRSetRequestHeader){XMLHttpRequest.prototype.setRequestHeader=this.originalXHRSetRequestHeader}this.patchedFetch=null;this.patchedXHROpen=null;this.patchedXHRSend=null;this.patchedXHRSetRequestHeader=null;this.isListening=false;if(__DEV__){}}adoptEarlyRequests(){const e=globalThis.__BUOY_EARLY_NETWORK__;if(e?.version!==1)return;for(const t of e.adopt()){try{if(t.kind==="fetch")this.adoptEarlyFetch(t);else this.adoptEarlyXhr(t)}catch{}}}adoptEarlyFetch(e){const{input:t,init:s}=e;const c=typeof t==="object"&&"method"in t?t:void 0;const a=(0,_resolveRequestUrl.resolveRequestUrl)(typeof t==="string"?t:t instanceof URL?t.href:t.url);if(this.shouldIgnoreUrl(a))return;const n=(0,_requestId.nextRequestId)("fetch");const l=s?.method??c?.method??"GET";const{url:R,params:f}=this.parseUrl(a);const i=headersToRecord(s?.headers??c?.headers);let d=s?.body;if(typeof d==="string"){try{d=JSON.parse(d)}catch{}}const L=i["X-Request-Client"]||i["x-request-client"]||"fetch";const g=()=>({id:n,url:R,method:l,headers:i,data:d,params:f||void 0,client:L});this.emit({type:"request",timestamp:new Date(e.start),request:g()});if(e.requestCopy){void this.readStreamedBody(e.requestCopy).then(({body:h})=>{try{d=JSON.parse(h)}catch{d=h}this.emit({type:"request-body",timestamp:new Date,request:{id:n,url:R,method:l,data:d,client:L}})}).catch(()=>{})}void e.settled.then(async({response:h,clone:_,error:r,headersAt:p})=>{if(!h){this.emit({type:"error",timestamp:new Date,duration:p-e.start,request:g(),error:{message:r instanceof Error?r.message:"Network error",stack:r instanceof Error?r.stack:void 0}});return}const{body:m,size:o}=_?await this.processResponseBody(_):{body:STREAMING_PLACEHOLDER,size:0};const y=Math.max(0,(0,_realNow.realNow)()-e.start);const q=p-e.start;const u={};h.headers.forEach((b,H)=>{u[H.toLowerCase()]=b});this.emit({type:"response",timestamp:new Date,duration:y,timings:y>q?{ttfb:q,download:y-q}:void 0,request:g(),response:{status:h.status,statusText:h.statusText,headers:u,body:m,size:o}})})}adoptEarlyXhr(e){const t=(0,_resolveRequestUrl.resolveRequestUrl)(e.url);if(this.shouldIgnoreUrl(t))return;const s=(0,_requestId.nextRequestId)("xhr");const c=e.method.toUpperCase();const{url:a,params:n}=this.parseUrl(t);let l=e.body;if(typeof l==="string"){try{l=JSON.parse(l)}catch{}}const R=e.headers["X-Request-Client"]||e.headers["x-request-client"]||"xhr";this.emit({type:"request",timestamp:new Date(e.start),request:{id:s,url:a,method:c,headers:e.headers,data:l,params:n||void 0,client:R}});const f=e.xhr;const i=()=>{const d=(f._buoyEarlyEnd??(0,_realNow.realNow)())-e.start;this.handleXHRResponse(f,s,a,c,e.headers,l,n,(0,_realNow.realNow)()-d,false,R)};if(f.readyState===4)i();else f.addEventListener("loadend",i,{once:true})}addListener(e){this.listeners.push(e);return()=>{const t=this.listeners.indexOf(e);if(t>-1){this.listeners.splice(t,1)}}}removeAllListeners(){this.listeners=[]}get isActive(){return this.isListening}get listenerCount(){return this.listeners.length}}let _networkListener=null;const getNetworkListener=()=>{if(!_networkListener){_networkListener=new NetworkListener}return _networkListener};const networkListener=exports.networkListener=getNetworkListener;const startNetworkListener=()=>getNetworkListener().startListening();exports.startNetworkListener=startNetworkListener;const stopNetworkListener=()=>_networkListener?.stopListening();exports.stopNetworkListener=stopNetworkListener;const addNetworkListener=w=>getNetworkListener().addListener(w);exports.addNetworkListener=addNetworkListener;const removeAllNetworkListeners=()=>_networkListener?.removeAllListeners();exports.removeAllNetworkListeners=removeAllNetworkListeners;const isNetworkListening=()=>_networkListener?.isActive??false;exports.isNetworkListening=isNetworkListening;const getNetworkListenerCount=()=>_networkListener?.listenerCount??0;exports.getNetworkListenerCount=getNetworkListenerCount;const isNetworkInterceptionLive=()=>_networkListener?.isInterceptionLive()??false;exports.isNetworkInterceptionLive=isNetworkInterceptionLive;