@buoy-gg/network 7.0.40 → 7.0.42
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.
- package/README.md +11 -79
- package/lib/commonjs/index.js +1 -325
- package/lib/commonjs/index.web.js +1 -420
- package/lib/commonjs/network/components/DataViewer.web.js +1 -879
- package/lib/commonjs/network/components/NetworkCopyScreen.web.js +1 -48
- package/lib/commonjs/network/components/NetworkCopySettingsView.js +27 -755
- package/lib/commonjs/network/components/NetworkCopySettingsView.web.js +14 -1242
- package/lib/commonjs/network/components/NetworkDetailScreen.web.js +1 -101
- package/lib/commonjs/network/components/NetworkDetailStepper.js +1 -108
- package/lib/commonjs/network/components/NetworkDetailStepper.web.js +1 -298
- package/lib/commonjs/network/components/NetworkEventDetailView.js +15 -1130
- package/lib/commonjs/network/components/NetworkEventDetailView.web.js +15 -1484
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +2 -401
- package/lib/commonjs/network/components/NetworkEventItemCompact.web.js +2 -820
- package/lib/commonjs/network/components/NetworkFilterScreen.web.js +1 -55
- package/lib/commonjs/network/components/NetworkFilterViewV3.js +1 -442
- package/lib/commonjs/network/components/NetworkFilterViewV3.web.js +1 -1310
- package/lib/commonjs/network/components/NetworkHeaderLive.js +1 -375
- package/lib/commonjs/network/components/NetworkHeaderLive.web.js +1 -623
- package/lib/commonjs/network/components/NetworkHeaderMenu.js +1 -190
- package/lib/commonjs/network/components/NetworkListScreen.js +1 -404
- package/lib/commonjs/network/components/NetworkListScreen.web.js +1 -625
- package/lib/commonjs/network/components/NetworkModal.js +1 -786
- package/lib/commonjs/network/components/NetworkNumberPrompt.js +1 -178
- package/lib/commonjs/network/components/NetworkNumberPrompt.web.js +1 -232
- package/lib/commonjs/network/components/NetworkOverrideEditor.js +1 -814
- package/lib/commonjs/network/components/NetworkOverrideEditor.web.js +1 -942
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.js +1 -107
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.web.js +1 -133
- package/lib/commonjs/network/components/NetworkOverridesButton.js +1 -90
- package/lib/commonjs/network/components/NetworkOverridesButton.web.js +1 -143
- package/lib/commonjs/network/components/NetworkOverridesScreen.js +1 -543
- package/lib/commonjs/network/components/NetworkOverridesScreen.web.js +1 -830
- package/lib/commonjs/network/components/NetworkSavedScreen.js +1 -226
- package/lib/commonjs/network/components/NetworkSavedScreen.web.js +1 -463
- package/lib/commonjs/network/components/NetworkUpgradeBanner.js +1 -76
- package/lib/commonjs/network/components/response-editor/Explorer.js +1 -973
- package/lib/commonjs/network/components/response-editor/Explorer.web.js +1 -1136
- package/lib/commonjs/network/components/response-editor/deleteNestedDataByPath.js +1 -56
- package/lib/commonjs/network/components/response-editor/editorContext.js +1 -33
- package/lib/commonjs/network/components/response-editor/updateNestedDataByPath.js +1 -61
- package/lib/commonjs/network/conditions/NetworkConditionsControl.js +1 -235
- package/lib/commonjs/network/conditions/NetworkConditionsControl.test.js +1 -136
- package/lib/commonjs/network/conditions/NetworkConditionsIcon.js +1 -33
- package/lib/commonjs/network/conditions/NetworkFloatingTool.js +1 -310
- package/lib/commonjs/network/conditions/NetworkFloatingTool.test.js +1 -219
- package/lib/commonjs/network/conditions/store.js +1 -75
- package/lib/commonjs/network/conditions/wait.js +1 -43
- package/lib/commonjs/network/conditions/xhr.js +1 -110
- package/lib/commonjs/network/hooks/useLiveNetworkEvent.js +1 -62
- package/lib/commonjs/network/hooks/useNetworkBadgeCounts.js +1 -83
- package/lib/commonjs/network/hooks/useNetworkEventList.js +1 -108
- package/lib/commonjs/network/hooks/useNetworkEvents.js +1 -318
- package/lib/commonjs/network/hooks/useNetworkHasMatches.js +1 -56
- package/lib/commonjs/network/hooks/useNetworkSaved.js +1 -38
- package/lib/commonjs/network/hooks/useNetworkSelection.js +1 -53
- package/lib/commonjs/network/hooks/useOverrideRules.js +1 -36
- package/lib/commonjs/network/hooks/useTickEveryMinute.js +1 -21
- package/lib/commonjs/network/overrides/applyToFetch.js +1 -80
- package/lib/commonjs/network/overrides/applyToXhr.js +2 -453
- package/lib/commonjs/network/overrides/engine.js +1 -93
- package/lib/commonjs/network/overrides/matchRule.js +1 -81
- package/lib/commonjs/network/overrides/overriddenRows.js +1 -0
- package/lib/commonjs/network/overrides/overrideRulesStore.js +1 -550
- package/lib/commonjs/network/overrides/presets.js +1 -70
- package/lib/commonjs/network/overrides/resolveOverride.js +1 -158
- package/lib/commonjs/network/overrides/types.js +1 -90
- package/lib/commonjs/network/sync/networkBodyResolver.js +1 -39
- package/lib/commonjs/network/sync/networkSyncAdapter.js +1 -882
- package/lib/commonjs/network/sync/overrideHonesty.release.test.js +1 -36
- package/lib/commonjs/network/types/index.js +1 -5
- package/lib/commonjs/network/utils/extractOperationName.js +1 -80
- package/lib/commonjs/network/utils/filterNetworkEvents.js +1 -96
- package/lib/commonjs/network/utils/formatGraphQLVariables.js +1 -219
- package/lib/commonjs/network/utils/formatting.js +1 -30
- package/lib/commonjs/network/utils/generateCurl.js +2 -71
- package/lib/commonjs/network/utils/generateNetworkCopyText.js +37 -166
- package/lib/commonjs/network/utils/httpCompletion.js +1 -37
- package/lib/commonjs/network/utils/networkEventStore.js +1 -393
- package/lib/commonjs/network/utils/networkListener.js +4 -1259
- package/lib/commonjs/network/utils/networkSavedStore.js +3 -577
- package/lib/commonjs/network/utils/networkSelectionStore.js +1 -193
- package/lib/commonjs/network/utils/realNow.js +1 -0
- package/lib/commonjs/network/utils/requestId.js +1 -0
- package/lib/commonjs/network/utils/resolveRequestUrl.js +1 -0
- package/lib/commonjs/network/utils/selectSavedEvents.js +1 -39
- package/lib/commonjs/network/utils/startNetworkCapture.js +1 -0
- package/lib/commonjs/network/utils/withPinnedFirst.js +1 -32
- package/lib/commonjs/preset.js +1 -102
- package/lib/module/index.js +1 -103
- package/lib/module/index.web.js +1 -82
- package/lib/module/network/components/DataViewer.web.js +1 -875
- package/lib/module/network/components/NetworkCopyScreen.web.js +1 -44
- package/lib/module/network/components/NetworkCopySettingsView.js +27 -750
- package/lib/module/network/components/NetworkCopySettingsView.web.js +14 -1237
- package/lib/module/network/components/NetworkDetailScreen.web.js +1 -97
- package/lib/module/network/components/NetworkDetailStepper.js +1 -104
- package/lib/module/network/components/NetworkDetailStepper.web.js +1 -296
- package/lib/module/network/components/NetworkEventDetailView.js +15 -1129
- package/lib/module/network/components/NetworkEventDetailView.web.js +15 -1482
- package/lib/module/network/components/NetworkEventItemCompact.js +2 -398
- package/lib/module/network/components/NetworkEventItemCompact.web.js +2 -818
- package/lib/module/network/components/NetworkFilterScreen.web.js +1 -51
- package/lib/module/network/components/NetworkFilterViewV3.js +1 -438
- package/lib/module/network/components/NetworkFilterViewV3.web.js +1 -1306
- package/lib/module/network/components/NetworkHeaderLive.js +1 -366
- package/lib/module/network/components/NetworkHeaderLive.web.js +1 -617
- package/lib/module/network/components/NetworkHeaderMenu.js +1 -184
- package/lib/module/network/components/NetworkListScreen.js +1 -400
- package/lib/module/network/components/NetworkListScreen.web.js +1 -623
- package/lib/module/network/components/NetworkModal.js +1 -781
- package/lib/module/network/components/NetworkNumberPrompt.js +1 -174
- package/lib/module/network/components/NetworkNumberPrompt.web.js +1 -230
- package/lib/module/network/components/NetworkOverrideEditor.js +1 -811
- package/lib/module/network/components/NetworkOverrideEditor.web.js +1 -939
- package/lib/module/network/components/NetworkOverrideHeaderButton.js +1 -104
- package/lib/module/network/components/NetworkOverrideHeaderButton.web.js +1 -131
- package/lib/module/network/components/NetworkOverridesButton.js +1 -87
- package/lib/module/network/components/NetworkOverridesButton.web.js +1 -141
- package/lib/module/network/components/NetworkOverridesScreen.js +1 -539
- package/lib/module/network/components/NetworkOverridesScreen.web.js +1 -826
- package/lib/module/network/components/NetworkSavedScreen.js +1 -222
- package/lib/module/network/components/NetworkSavedScreen.web.js +1 -461
- package/lib/module/network/components/NetworkUpgradeBanner.js +1 -72
- package/lib/module/network/components/response-editor/Explorer.js +1 -972
- package/lib/module/network/components/response-editor/Explorer.web.js +1 -1133
- package/lib/module/network/components/response-editor/deleteNestedDataByPath.js +1 -51
- package/lib/module/network/components/response-editor/editorContext.js +1 -29
- package/lib/module/network/components/response-editor/updateNestedDataByPath.js +1 -56
- package/lib/module/network/conditions/NetworkConditionsControl.js +1 -231
- package/lib/module/network/conditions/NetworkConditionsControl.test.js +1 -135
- package/lib/module/network/conditions/NetworkConditionsIcon.js +1 -29
- package/lib/module/network/conditions/NetworkFloatingTool.js +1 -304
- package/lib/module/network/conditions/NetworkFloatingTool.test.js +1 -218
- package/lib/module/network/conditions/store.js +1 -65
- package/lib/module/network/conditions/wait.js +1 -38
- package/lib/module/network/conditions/xhr.js +1 -106
- package/lib/module/network/hooks/useLiveNetworkEvent.js +1 -58
- package/lib/module/network/hooks/useNetworkBadgeCounts.js +1 -79
- package/lib/module/network/hooks/useNetworkEventList.js +1 -104
- package/lib/module/network/hooks/useNetworkEvents.js +1 -314
- package/lib/module/network/hooks/useNetworkHasMatches.js +1 -52
- package/lib/module/network/hooks/useNetworkSaved.js +1 -34
- package/lib/module/network/hooks/useNetworkSelection.js +1 -46
- package/lib/module/network/hooks/useOverrideRules.js +1 -32
- package/lib/module/network/hooks/useTickEveryMinute.js +1 -11
- package/lib/module/network/overrides/applyToFetch.js +1 -72
- package/lib/module/network/overrides/applyToXhr.js +2 -446
- package/lib/module/network/overrides/engine.js +1 -85
- package/lib/module/network/overrides/matchRule.js +1 -75
- package/lib/module/network/overrides/overriddenRows.js +1 -0
- package/lib/module/network/overrides/overrideRulesStore.js +1 -547
- package/lib/module/network/overrides/presets.js +1 -65
- package/lib/module/network/overrides/resolveOverride.js +1 -152
- package/lib/module/network/overrides/types.js +1 -86
- package/lib/module/network/sync/networkBodyResolver.js +1 -34
- package/lib/module/network/sync/networkSyncAdapter.js +1 -880
- package/lib/module/network/sync/overrideHonesty.release.test.js +1 -35
- package/lib/module/network/types/index.js +1 -3
- package/lib/module/network/utils/extractOperationName.js +1 -76
- package/lib/module/network/utils/filterNetworkEvents.js +1 -92
- package/lib/module/network/utils/formatGraphQLVariables.js +1 -213
- package/lib/module/network/utils/formatting.js +1 -9
- package/lib/module/network/utils/generateCurl.js +2 -67
- package/lib/module/network/utils/generateNetworkCopyText.js +37 -162
- package/lib/module/network/utils/httpCompletion.js +1 -32
- package/lib/module/network/utils/networkEventStore.js +1 -391
- package/lib/module/network/utils/networkListener.js +4 -1249
- package/lib/module/network/utils/networkSavedStore.js +3 -572
- package/lib/module/network/utils/networkSelectionStore.js +1 -188
- package/lib/module/network/utils/realNow.js +1 -0
- package/lib/module/network/utils/requestId.js +1 -0
- package/lib/module/network/utils/resolveRequestUrl.js +1 -0
- package/lib/module/network/utils/selectSavedEvents.js +1 -35
- package/lib/module/network/utils/startNetworkCapture.js +1 -0
- package/lib/module/network/utils/withPinnedFirst.js +1 -28
- package/lib/module/preset.js +1 -100
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts +1 -0
- package/lib/typescript/network/overrides/engine.d.ts +1 -1
- package/lib/typescript/network/overrides/matchRule.d.ts +1 -1
- package/lib/typescript/network/overrides/overriddenRows.d.ts +16 -0
- package/lib/typescript/network/overrides/resolveOverride.d.ts +2 -2
- package/lib/typescript/network/overrides/types.d.ts +9 -0
- package/lib/typescript/network/utils/networkListener.d.ts +14 -4
- package/lib/typescript/network/utils/networkSavedStore.d.ts +4 -4
- package/lib/typescript/network/utils/realNow.d.ts +18 -0
- package/lib/typescript/network/utils/requestId.d.ts +2 -0
- package/lib/typescript/network/utils/resolveRequestUrl.d.ts +3 -0
- package/lib/typescript/network/utils/startNetworkCapture.d.ts +6 -0
- package/lib/web/index.mjs +145 -0
- package/package.json +32 -10
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/index.web.d.ts.map +0 -1
- package/lib/typescript/network/components/DataViewer.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkFilterViewV3.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkListScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideEditor.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesButton.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkSavedScreen.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts.map +0 -1
- package/lib/typescript/network/components/NetworkUpgradeBanner.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/Explorer.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/Explorer.web.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/editorContext.d.ts.map +0 -1
- package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts.map +0 -1
- package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts.map +0 -1
- package/lib/typescript/network/conditions/store.d.ts.map +0 -1
- package/lib/typescript/network/conditions/wait.d.ts.map +0 -1
- package/lib/typescript/network/conditions/xhr.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useLiveNetworkEvent.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkBadgeCounts.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkEvents.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkHasMatches.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkSaved.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useOverrideRules.d.ts.map +0 -1
- package/lib/typescript/network/hooks/useTickEveryMinute.d.ts.map +0 -1
- package/lib/typescript/network/overrides/applyToFetch.d.ts.map +0 -1
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +0 -1
- package/lib/typescript/network/overrides/engine.d.ts.map +0 -1
- package/lib/typescript/network/overrides/matchRule.d.ts.map +0 -1
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts.map +0 -1
- package/lib/typescript/network/overrides/presets.d.ts.map +0 -1
- package/lib/typescript/network/overrides/resolveOverride.d.ts.map +0 -1
- package/lib/typescript/network/overrides/types.d.ts.map +0 -1
- package/lib/typescript/network/sync/networkBodyResolver.d.ts.map +0 -1
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/network/sync/overrideHonesty.release.test.d.ts.map +0 -1
- package/lib/typescript/network/types/index.d.ts.map +0 -1
- package/lib/typescript/network/utils/extractOperationName.d.ts.map +0 -1
- package/lib/typescript/network/utils/filterNetworkEvents.d.ts.map +0 -1
- package/lib/typescript/network/utils/formatGraphQLVariables.d.ts.map +0 -1
- package/lib/typescript/network/utils/formatting.d.ts.map +0 -1
- package/lib/typescript/network/utils/generateCurl.d.ts.map +0 -1
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +0 -1
- package/lib/typescript/network/utils/httpCompletion.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkListener.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkSavedStore.d.ts.map +0 -1
- package/lib/typescript/network/utils/networkSelectionStore.d.ts.map +0 -1
- package/lib/typescript/network/utils/selectSavedEvents.d.ts.map +0 -1
- package/lib/typescript/network/utils/withPinnedFirst.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
|
@@ -1,106 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { canSynthesize, completeNativeOverride } from "../overrides/applyToXhr";
|
|
4
|
-
/** Active simulation uses a send-to-completion deadline, including the wait. */
|
|
5
|
-
export function runConditionXhr(target, url, condition, outcome, dispatch) {
|
|
6
|
-
if (!canSynthesize(target)) throw new Error("Network conditions require React Native XMLHttpRequest.");
|
|
7
|
-
const xhr = target;
|
|
8
|
-
if (xhr.readyState !== 1 || xhr._sent) throw new Error("Request is not open or has already been sent.");
|
|
9
|
-
const started = Date.now();
|
|
10
|
-
let timeout = xhr.timeout;
|
|
11
|
-
let settled = false;
|
|
12
|
-
let dispatching = false;
|
|
13
|
-
let deadline;
|
|
14
|
-
let pending;
|
|
15
|
-
const originalAbort = xhr.abort;
|
|
16
|
-
const originalOpen = xhr.open;
|
|
17
|
-
const abortDescriptor = Object.getOwnPropertyDescriptor(xhr, "abort");
|
|
18
|
-
const openDescriptor = Object.getOwnPropertyDescriptor(xhr, "open");
|
|
19
|
-
const timeoutDescriptor = Object.getOwnPropertyDescriptor(xhr, "timeout");
|
|
20
|
-
const restore = (key, descriptor) => {
|
|
21
|
-
if (descriptor) Object.defineProperty(xhr, key, descriptor);else delete xhr[key];
|
|
22
|
-
};
|
|
23
|
-
const cleanup = () => {
|
|
24
|
-
if (settled) return;
|
|
25
|
-
settled = true;
|
|
26
|
-
clearTimeout(pending);
|
|
27
|
-
clearTimeout(deadline);
|
|
28
|
-
xhr.removeEventListener("loadend", cleanup);
|
|
29
|
-
restore("abort", abortDescriptor);
|
|
30
|
-
restore("open", openDescriptor);
|
|
31
|
-
restore("timeout", timeoutDescriptor);
|
|
32
|
-
xhr.timeout = timeout;
|
|
33
|
-
};
|
|
34
|
-
const fail = timedOut => {
|
|
35
|
-
if (settled) return;
|
|
36
|
-
// DONE suppresses abort events while the native transport is cancelled.
|
|
37
|
-
Object.assign(xhr, {
|
|
38
|
-
readyState: 4
|
|
39
|
-
});
|
|
40
|
-
originalAbort.call(xhr);
|
|
41
|
-
xhr._aborted = false;
|
|
42
|
-
xhr._hasError = true;
|
|
43
|
-
xhr._timedOut = timedOut;
|
|
44
|
-
xhr._response = timedOut ? "Request timed out (Buoy conditions)" : "Network request failed (Buoy Offline)";
|
|
45
|
-
xhr.setReadyState(4);
|
|
46
|
-
cleanup();
|
|
47
|
-
};
|
|
48
|
-
const expired = () => timeout > 0 && Date.now() - started >= timeout;
|
|
49
|
-
const scheduleDeadline = () => {
|
|
50
|
-
clearTimeout(deadline);
|
|
51
|
-
if (timeout > 0) deadline = setTimeout(() => fail(true), Math.max(0, timeout - (Date.now() - started)));
|
|
52
|
-
};
|
|
53
|
-
Object.defineProperty(xhr, "timeout", {
|
|
54
|
-
configurable: true,
|
|
55
|
-
get: () => dispatching ? 0 : timeout,
|
|
56
|
-
set: value => {
|
|
57
|
-
timeout = Math.max(0, Number(value) || 0);
|
|
58
|
-
scheduleDeadline();
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
Object.defineProperty(xhr, "abort", {
|
|
62
|
-
configurable: true,
|
|
63
|
-
value: () => {
|
|
64
|
-
if (!settled) {
|
|
65
|
-
originalAbort.call(xhr);
|
|
66
|
-
cleanup();
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
Object.defineProperty(xhr, "open", {
|
|
71
|
-
configurable: true,
|
|
72
|
-
value: (...args) => {
|
|
73
|
-
const result = Reflect.apply(originalOpen, xhr, args);
|
|
74
|
-
cleanup();
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
xhr._sent = true;
|
|
79
|
-
xhr._requestId = null;
|
|
80
|
-
xhr.addEventListener("loadend", cleanup);
|
|
81
|
-
scheduleDeadline();
|
|
82
|
-
pending = setTimeout(() => {
|
|
83
|
-
if (settled) return;
|
|
84
|
-
if (expired()) {
|
|
85
|
-
fail(true);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (condition.profile === "offline") {
|
|
89
|
-
fail(false);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (outcome && outcome.kind !== "delay") {
|
|
93
|
-
completeNativeOverride(xhr, url, outcome);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
xhr._sent = false;
|
|
97
|
-
dispatching = true;
|
|
98
|
-
try {
|
|
99
|
-
dispatch();
|
|
100
|
-
} catch {
|
|
101
|
-
fail(false);
|
|
102
|
-
} finally {
|
|
103
|
-
dispatching = false;
|
|
104
|
-
}
|
|
105
|
-
}, condition.delayMs + (outcome?.delayMs ?? 0));
|
|
106
|
-
}
|
|
1
|
+
"use strict";import{canSynthesize as _,completeNativeOverride as k,runOverride as q}from"../overrides/applyToXhr";import{realClearTimeout as M,realNow as y,realSetTimeout as x}from"../utils/realNow";export function runConditionXhr(d,u,o,r,b){if(!_(d)){const t={id:"condition",enabled:true,urlPattern:u,kind:"delay",hits:0,seen:0,createdAt:Date.now()};const i=o.profile==="offline"?{kind:"fail",rule:t,failKind:"network",delayMs:0}:r?{...r,delayMs:o.delayMs+r.delayMs}:{kind:"delay",rule:t,delayMs:o.delayMs};if(!q(d,u,i,b))throw new Error("Network conditions are unavailable for this XMLHttpRequest implementation.");return}const e=d;if(e.readyState!==1||e._sent)throw new Error("Request is not open or has already been sent.");const m=y();let n=e.timeout;let a=false;let f=false;let c;let O;const h=e.abort;const g=e.open;const v=Object.getOwnPropertyDescriptor(e,"abort");const j=Object.getOwnPropertyDescriptor(e,"open");const D=Object.getOwnPropertyDescriptor(e,"timeout");const p=(t,i)=>{if(i)Object.defineProperty(e,t,i);else delete e[t]};const l=()=>{if(a)return;a=true;clearTimeout(O);M(c);e.removeEventListener("loadend",l);p("abort",v);p("open",j);p("timeout",D);e.timeout=n};const s=t=>{if(a)return;Object.assign(e,{readyState:4});h.call(e);e._aborted=false;e._hasError=true;e._timedOut=t;e._response=t?"Request timed out (Buoy conditions)":"Network request failed (Buoy Offline)";e.setReadyState(4);l()};const P=()=>n>0&&y()-m>=n;const w=()=>{M(c);if(n>0)c=x(()=>s(true),Math.max(0,n-(y()-m)))};Object.defineProperty(e,"timeout",{configurable:true,get:()=>f?0:n,set:t=>{n=Math.max(0,Number(t)||0);w()}});Object.defineProperty(e,"abort",{configurable:true,value:()=>{if(!a){h.call(e);l()}}});Object.defineProperty(e,"open",{configurable:true,value:(...t)=>{const i=Reflect.apply(g,e,t);l();return i}});e._sent=true;e._requestId=null;e.addEventListener("loadend",l);w();O=setTimeout(()=>{if(a)return;if(P()){s(true);return}if(o.profile==="offline"){s(false);return}if(r&&r.kind!=="delay"){k(e,u,r);return}e._sent=false;f=true;try{b()}catch{s(false)}finally{f=false}},o.delayMs+(r?.delayMs??0))}
|
|
@@ -1,58 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Resolves a selected event ID to the LIVE event object in the store.
|
|
5
|
-
*
|
|
6
|
-
* The detail view used to be handed the `NetworkEvent` captured at press
|
|
7
|
-
* time. The store updates immutably — a response/error replaces the pending
|
|
8
|
-
* event with a NEW object — so a request opened while pending stayed
|
|
9
|
-
* "Pending" for as long as the detail view was open, and only showed its
|
|
10
|
-
* real status after going back and re-opening the row (which re-read the
|
|
11
|
-
* store). This hook closes that gap: callers keep the ID as UI state and the
|
|
12
|
-
* leaf that renders the detail subscribes for its current value.
|
|
13
|
-
*
|
|
14
|
-
* Subscribing here (not at the modal root) keeps the event stream out of the
|
|
15
|
-
* shell's render path — same rule the list/header leaves follow.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { useEffect, useState } from "react";
|
|
19
|
-
import { networkEventStore } from "../utils/networkEventStore";
|
|
20
|
-
import { networkSavedStore } from "../utils/networkSavedStore";
|
|
21
|
-
/**
|
|
22
|
-
* Live store first, then the pinned/saved snapshots. The fallback is what
|
|
23
|
-
* makes a pinned request openable after a Clear, after it aged past the
|
|
24
|
-
* 500-event cap, or in a later app launch — cases where the live store has
|
|
25
|
-
* nothing and the persisted snapshot is the only copy left.
|
|
26
|
-
*/
|
|
27
|
-
function readEvent(eventId) {
|
|
28
|
-
return networkEventStore.getEvents().find(e => e.id === eventId) ?? networkSavedStore.getEventById(eventId);
|
|
29
|
-
}
|
|
30
|
-
export function useLiveNetworkEvent(eventId, options = {}) {
|
|
31
|
-
const {
|
|
32
|
-
paused = false
|
|
33
|
-
} = options;
|
|
34
|
-
const [entry, setEntry] = useState(null);
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
// `paused` is the power toggle's freeze-stream semantic: a frozen tool
|
|
37
|
-
// keeps showing the version it froze on.
|
|
38
|
-
if (!eventId || paused) return undefined;
|
|
39
|
-
// subscribeToEvents replays the current array immediately, so the open
|
|
40
|
-
// detail catches up on subscribe as well as on every later update.
|
|
41
|
-
return networkEventStore.subscribeToEvents(events => {
|
|
42
|
-
const next = events.find(e => e.id === eventId);
|
|
43
|
-
// Gone from the store (cleared, or aged past maxEvents) — keep the last
|
|
44
|
-
// known version rather than blanking an open detail view.
|
|
45
|
-
if (!next) return;
|
|
46
|
-
setEntry(prev => prev && prev.id === eventId && prev.event === next ? prev : {
|
|
47
|
-
id: eventId,
|
|
48
|
-
event: next
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}, [eventId, paused]);
|
|
52
|
-
if (!eventId) return null;
|
|
53
|
-
// A store read during render covers the frame where `eventId` just changed:
|
|
54
|
-
// the effect's first emission lands a commit later, and until then `entry`
|
|
55
|
-
// still describes the previously selected request.
|
|
56
|
-
if (entry && entry.id === eventId) return entry.event;
|
|
57
|
-
return readEvent(eventId);
|
|
58
|
-
}
|
|
1
|
+
"use strict";import{useEffect as c,useState as E}from"react";import{networkEventStore as u}from"../utils/networkEventStore";import{networkSavedStore as m}from"../utils/networkSavedStore";function a(t){return u.getEvents().find(n=>n.id===t)??m.getEventById(t)}export function useLiveNetworkEvent(t,n={}){const{paused:o=false}=n;const[e,f]=E(null);c(()=>{if(!t||o)return void 0;return u.subscribeToEvents(s=>{const i=s.find(r=>r.id===t);if(!i)return;f(r=>r&&r.id===t&&r.event===i?r:{id:t,event:i})})},[t,o]);if(!t)return null;if(e&&e.id===t)return e.event;return a(t)}
|
|
@@ -1,79 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Narrow store subscription for the header's live counters — the ONLY
|
|
5
|
-
* header data that should update per network event (the success/error/
|
|
6
|
-
* pending chips and the clear button's has-events state).
|
|
7
|
-
*
|
|
8
|
-
* Holds derived COUNTS in state, not the events array, with an equality
|
|
9
|
-
* guard — so consumers re-render only when a count actually changes
|
|
10
|
-
* (e.g. an ignored-pattern-matched event arriving doesn't re-render the
|
|
11
|
-
* chips). rule2: external store, narrowest possible subscription.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { useEffect, useState } from "react";
|
|
15
|
-
import { networkEventStore } from "../utils/networkEventStore";
|
|
16
|
-
import { applyIgnoredPatterns } from "../utils/filterNetworkEvents";
|
|
17
|
-
function computeCounts(events, ignoredPatterns) {
|
|
18
|
-
let successful = 0;
|
|
19
|
-
let failed = 0;
|
|
20
|
-
let pending = 0;
|
|
21
|
-
for (const event of applyIgnoredPatterns(events, ignoredPatterns)) {
|
|
22
|
-
if (event.status && event.status >= 200 && event.status < 300) {
|
|
23
|
-
successful++;
|
|
24
|
-
} else if (event.error || event.status && event.status >= 400) {
|
|
25
|
-
failed++;
|
|
26
|
-
} else if (!event.status && !event.error) {
|
|
27
|
-
pending++;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
successful,
|
|
32
|
-
failed,
|
|
33
|
-
pending,
|
|
34
|
-
total: events.length
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function countsEqual(a, b) {
|
|
38
|
-
return a.successful === b.successful && a.failed === b.failed && a.pending === b.pending && a.total === b.total;
|
|
39
|
-
}
|
|
40
|
-
export function useNetworkBadgeCounts(options) {
|
|
41
|
-
const {
|
|
42
|
-
ignoredPatterns,
|
|
43
|
-
paused
|
|
44
|
-
} = options;
|
|
45
|
-
const [counts, setCounts] = useState(() => computeCounts(networkEventStore.getEvents(), ignoredPatterns));
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (paused) return undefined;
|
|
48
|
-
// Frame-coalesced like useNetworkEventList: a request burst updates
|
|
49
|
-
// the chips once per frame, not once per emission. subscribeToEvents
|
|
50
|
-
// replays immediately, which also recomputes when ignoredPatterns
|
|
51
|
-
// change (this effect re-runs).
|
|
52
|
-
let rafId = null;
|
|
53
|
-
let pending = null;
|
|
54
|
-
const unsubscribe = networkEventStore.subscribeToEvents(events => {
|
|
55
|
-
pending = events;
|
|
56
|
-
if (rafId != null) return;
|
|
57
|
-
rafId = requestAnimationFrame(() => {
|
|
58
|
-
rafId = null;
|
|
59
|
-
if (!pending) return;
|
|
60
|
-
const next = computeCounts(pending, ignoredPatterns);
|
|
61
|
-
pending = null;
|
|
62
|
-
setCounts(prev => countsEqual(prev, next) ? prev : next);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
return () => {
|
|
66
|
-
unsubscribe();
|
|
67
|
-
if (rafId != null) cancelAnimationFrame(rafId);
|
|
68
|
-
};
|
|
69
|
-
}, [paused, ignoredPatterns]);
|
|
70
|
-
useEffect(() => networkEventStore.onClear(() => {
|
|
71
|
-
setCounts({
|
|
72
|
-
successful: 0,
|
|
73
|
-
failed: 0,
|
|
74
|
-
pending: 0,
|
|
75
|
-
total: 0
|
|
76
|
-
});
|
|
77
|
-
}), []);
|
|
78
|
-
return counts;
|
|
79
|
-
}
|
|
1
|
+
"use strict";import{useEffect as a,useState as m}from"react";import{networkEventStore as l}from"../utils/networkEventStore";import{applyIgnoredPatterns as g}from"../utils/filterNetworkEvents";function c(n,e){let s=0;let r=0;let u=0;for(const t of g(n,e)){if(t.status&&t.status>=200&&t.status<300){s++}else if(t.error||t.status&&t.status>=400){r++}else if(!t.status&&!t.error){u++}}return{successful:s,failed:r,pending:u,total:n.length}}function E(n,e){return n.successful===e.successful&&n.failed===e.failed&&n.pending===e.pending&&n.total===e.total}export function useNetworkBadgeCounts(n){const{ignoredPatterns:e,paused:s}=n;const[r,u]=m(()=>c(l.getEvents(),e));a(()=>{if(s)return void 0;let t=null;let o=null;const d=l.subscribeToEvents(p=>{o=p;if(t!=null)return;t=requestAnimationFrame(()=>{t=null;if(!o)return;const i=c(o,e);o=null;u(f=>E(f,i)?f:i)})});return()=>{d();if(t!=null)cancelAnimationFrame(t)}},[s,e]);a(()=>l.onClear(()=>{u({successful:0,failed:0,pending:0,total:0})}),[]);return r}
|
|
@@ -1,104 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Narrow store subscription for components that render the network event
|
|
5
|
-
* LIST (or need the filtered list). Decomposed from `useNetworkEvents`
|
|
6
|
-
* per rule2 ("external store + narrow subscriptions") so the subscription
|
|
7
|
-
* lives in the leaf that consumes it — the modal root no longer
|
|
8
|
-
* re-renders per network event.
|
|
9
|
-
*
|
|
10
|
-
* Profiling evidence (tool-bench, 2026-07-06): with the subscription at
|
|
11
|
-
* the modal root, 15 network events re-rendered the ENTIRE modal 15× —
|
|
12
|
-
* JsModalComponent/header buttons ×15, all visible rows ×105.
|
|
13
|
-
*
|
|
14
|
-
* `paused` gates the subscription (the power toggle's freeze-stream
|
|
15
|
-
* semantic, and screens pass `paused || !visible` so hidden screens don't
|
|
16
|
-
* subscribe at all). Store clears are honored even while paused so a
|
|
17
|
-
* frozen list can't show deleted events (parity with the old hook's
|
|
18
|
-
* clearEvents local reset).
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { useEffect, useMemo, useState } from "react";
|
|
22
|
-
import { useLimit } from "@buoy-gg/shared-ui";
|
|
23
|
-
import { networkEventStore } from "../utils/networkEventStore";
|
|
24
|
-
import { applyIgnoredPatterns, filterNetworkEvents } from "../utils/filterNetworkEvents";
|
|
25
|
-
export function useNetworkEventList(options) {
|
|
26
|
-
const {
|
|
27
|
-
filter,
|
|
28
|
-
ignoredPatterns,
|
|
29
|
-
paused,
|
|
30
|
-
visible = true,
|
|
31
|
-
isPro
|
|
32
|
-
} = options;
|
|
33
|
-
const tierLimit = useLimit("network.requests");
|
|
34
|
-
// `isPro` stays an option so callers can force the unlimited view.
|
|
35
|
-
const requestLimit = isPro ? Infinity : tierLimit;
|
|
36
|
-
const [events, setEvents] = useState(() => networkEventStore.getEvents());
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
// Hidden: neither subscribe nor sync — a hidden screen re-rendering for
|
|
39
|
-
// stream traffic is pure waste (the stable-router contract).
|
|
40
|
-
if (!visible) return undefined;
|
|
41
|
-
if (paused) {
|
|
42
|
-
// Pause freezes the STREAM, not the data. The store still holds the
|
|
43
|
-
// frozen events, and a screen entering visibility during a pause must
|
|
44
|
-
// show them rather than whatever it had at mount.
|
|
45
|
-
setEvents(networkEventStore.getEvents());
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
// Coalesce store emissions to one commit per frame: a burst of N
|
|
49
|
-
// concurrent requests emits 2N times (add + response each), and
|
|
50
|
-
// uncoalesced each emission is its own commit re-rendering the
|
|
51
|
-
// changed row's whole subtree. Profiling (tool-bench 2026-07-06):
|
|
52
|
-
// a rapid 25-request session produced 70 commits at ~220 renders
|
|
53
|
-
// each. subscribeToEvents replays the current array immediately, so
|
|
54
|
-
// resuming from pause catches up on the next frame.
|
|
55
|
-
let rafId = null;
|
|
56
|
-
let pending = null;
|
|
57
|
-
const unsubscribe = networkEventStore.subscribeToEvents(next => {
|
|
58
|
-
pending = next;
|
|
59
|
-
if (rafId != null) return;
|
|
60
|
-
rafId = requestAnimationFrame(() => {
|
|
61
|
-
rafId = null;
|
|
62
|
-
if (pending) setEvents(pending);
|
|
63
|
-
pending = null;
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
return () => {
|
|
67
|
-
unsubscribe();
|
|
68
|
-
if (rafId != null) cancelAnimationFrame(rafId);
|
|
69
|
-
};
|
|
70
|
-
}, [paused, visible]);
|
|
71
|
-
useEffect(() => networkEventStore.onClear(() => {
|
|
72
|
-
setEvents([]);
|
|
73
|
-
}), []);
|
|
74
|
-
|
|
75
|
-
// Derivations are plain useMemo over the subscribed array — linear
|
|
76
|
-
// passes over ≤maxEvents items, recomputed only when inputs change.
|
|
77
|
-
// Split in two so the pre-filter count is observable: `totalEvents` below
|
|
78
|
-
// is the denominator the empty state needs, and recomputing it separately
|
|
79
|
-
// would mean a second pass over the same array.
|
|
80
|
-
const candidateEvents = useMemo(() => applyIgnoredPatterns(events, ignoredPatterns), [events, ignoredPatterns]);
|
|
81
|
-
const filteredEvents = useMemo(() => filterNetworkEvents(candidateEvents, filter), [candidateEvents, filter]);
|
|
82
|
-
|
|
83
|
-
// Free tier: events beyond the limit in the ORIGINAL order are locked,
|
|
84
|
-
// so locked events stay locked even when searching (ported verbatim).
|
|
85
|
-
const lockedEventIds = useMemo(() => {
|
|
86
|
-
const lockedIds = new Set();
|
|
87
|
-
events.slice(requestLimit).forEach(event => {
|
|
88
|
-
lockedIds.add(event.id);
|
|
89
|
-
});
|
|
90
|
-
return lockedIds;
|
|
91
|
-
}, [events, requestLimit]);
|
|
92
|
-
const visibleEvents = useMemo(() => {
|
|
93
|
-
if (lockedEventIds.size === 0) return filteredEvents;
|
|
94
|
-
return filteredEvents.filter(event => !lockedEventIds.has(event.id));
|
|
95
|
-
}, [filteredEvents, lockedEventIds]);
|
|
96
|
-
const lockedEventCount = filteredEvents.length - visibleEvents.length;
|
|
97
|
-
return {
|
|
98
|
-
events: filteredEvents,
|
|
99
|
-
visibleEvents,
|
|
100
|
-
hasLockedEvents: lockedEventCount > 0,
|
|
101
|
-
lockedEventCount,
|
|
102
|
-
totalEvents: candidateEvents.length
|
|
103
|
-
};
|
|
104
|
-
}
|
|
1
|
+
"use strict";import{useEffect as p,useMemo as s,useState as I}from"react";import{useLimit as L}from"@buoy-gg/shared-ui";import{networkEventStore as i}from"../utils/networkEventStore";import{applyIgnoredPatterns as q,filterNetworkEvents as P}from"../utils/filterNetworkEvents";export function useNetworkEventList(g){const{filter:c,ignoredPatterns:d,paused:f,visible:a=true,isPro:k}=g;const b=L("network.requests");const v=k?Infinity:b;const[r,o]=I(()=>i.getEvents());p(()=>{if(!a)return void 0;if(f){o(i.getEvents());return void 0}let e=null;let t=null;const h=i.subscribeToEvents(w=>{t=w;if(e!=null)return;e=requestAnimationFrame(()=>{e=null;if(t)o(t);t=null})});return()=>{h();if(e!=null)cancelAnimationFrame(e)}},[f,a]);p(()=>i.onClear(()=>{o([])}),[]);const u=s(()=>q(r,d),[r,d]);const n=s(()=>P(u,c),[u,c]);const l=s(()=>{const e=new Set;r.slice(v).forEach(t=>{e.add(t.id)});return e},[r,v]);const m=s(()=>{if(l.size===0)return n;return n.filter(e=>!l.has(e.id))},[n,l]);const E=n.length-m.length;return{events:n,visibleEvents:m,hasLockedEvents:E>0,lockedEventCount:E,totalEvents:u.length}}
|
|
@@ -1,314 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Hook for accessing network events and controls
|
|
5
|
-
*
|
|
6
|
-
* Uses self-managing Subscribable pattern - the network listener automatically
|
|
7
|
-
* starts when this hook mounts and stops when all subscribers unmount.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
11
|
-
import { networkEventStore } from "../utils/networkEventStore";
|
|
12
|
-
import { useLimit, TIER_LIMITS } from "@buoy-gg/shared-ui";
|
|
13
|
-
import { searchGraphQLVariables } from "../utils/formatGraphQLVariables";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @deprecated Read the live limit with `useLimit("network.requests")` — this
|
|
17
|
-
* only reports the FREE tier's value and ignores the Weekend Pass. Kept
|
|
18
|
-
* because it is part of the package's public exports.
|
|
19
|
-
*/
|
|
20
|
-
export const FREE_TIER_REQUEST_LIMIT = TIER_LIMITS["network.requests"].free;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Custom hook for accessing network events and controls
|
|
24
|
-
*
|
|
25
|
-
* This hook provides a complete interface for network monitoring, including
|
|
26
|
-
* event filtering, statistics calculation, and interception control.
|
|
27
|
-
*
|
|
28
|
-
* The network listener automatically starts when this hook mounts and stops
|
|
29
|
-
* when all components using it unmount (Subscribable pattern from TanStack Query).
|
|
30
|
-
*
|
|
31
|
-
* @returns Object containing filtered events, statistics, controls, and utilities
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* function NetworkMonitor() {
|
|
36
|
-
* const {
|
|
37
|
-
* events,
|
|
38
|
-
* stats,
|
|
39
|
-
* filter,
|
|
40
|
-
* setFilter,
|
|
41
|
-
* clearEvents,
|
|
42
|
-
* isCapturing
|
|
43
|
-
* } = useNetworkEvents();
|
|
44
|
-
*
|
|
45
|
-
* return (
|
|
46
|
-
* <div>
|
|
47
|
-
* <p>Total requests: {stats.totalRequests}</p>
|
|
48
|
-
* <p>Success rate: {stats.successfulRequests}/{stats.totalRequests}</p>
|
|
49
|
-
* <p>Capturing: {isCapturing ? 'Yes' : 'No'}</p>
|
|
50
|
-
* </div>
|
|
51
|
-
* );
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
|
-
*
|
|
55
|
-
* @performance Uses memoization for expensive filtering and statistics calculations
|
|
56
|
-
* @performance Optimizes string operations and array processing for large datasets
|
|
57
|
-
* @performance Includes Set-based lookups for O(1) filter matching
|
|
58
|
-
*
|
|
59
|
-
* @param options.isPro - Force the unlimited view. Defaults to false, in
|
|
60
|
-
* which case the current tier's limit from `useLimit("network.requests")`
|
|
61
|
-
* applies.
|
|
62
|
-
*/
|
|
63
|
-
export function useNetworkEvents(options = {}) {
|
|
64
|
-
const {
|
|
65
|
-
isPro = false
|
|
66
|
-
} = options;
|
|
67
|
-
const tierLimit = useLimit("network.requests");
|
|
68
|
-
// `isPro` stays an option so callers can force the unlimited view.
|
|
69
|
-
const requestLimit = isPro ? Infinity : tierLimit;
|
|
70
|
-
const [events, setEvents] = useState([]);
|
|
71
|
-
const [filter, setFilter] = useState({});
|
|
72
|
-
const [isCapturing, setIsCapturing] = useState(true);
|
|
73
|
-
const unsubscribeRef = useRef(null);
|
|
74
|
-
|
|
75
|
-
// Subscribe/unsubscribe to event store based on isCapturing state.
|
|
76
|
-
// Each subscriber is independent — pausing this hook's subscription
|
|
77
|
-
// does not affect other subscribers (e.g. the events tool).
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (isCapturing) {
|
|
80
|
-
unsubscribeRef.current = networkEventStore.subscribeToEvents(setEvents);
|
|
81
|
-
}
|
|
82
|
-
return () => {
|
|
83
|
-
if (unsubscribeRef.current) {
|
|
84
|
-
unsubscribeRef.current();
|
|
85
|
-
unsubscribeRef.current = null;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}, [isCapturing]);
|
|
89
|
-
|
|
90
|
-
// Clear all events (also update local state in case we're paused/unsubscribed)
|
|
91
|
-
const clearEvents = useCallback(() => {
|
|
92
|
-
networkEventStore.clearEvents();
|
|
93
|
-
setEvents([]);
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
// Pause: unsubscribe this hook from the store (other subscribers unaffected)
|
|
97
|
-
const pauseCapturing = useCallback(() => {
|
|
98
|
-
setIsCapturing(false);
|
|
99
|
-
}, []);
|
|
100
|
-
|
|
101
|
-
// Resume: resubscribe this hook to the store
|
|
102
|
-
const resumeCapturing = useCallback(() => {
|
|
103
|
-
setIsCapturing(true);
|
|
104
|
-
}, []);
|
|
105
|
-
|
|
106
|
-
// Memoize search text processing to avoid repeated toLowerCase calls
|
|
107
|
-
// Performance: Expensive string operations repeated for every event on every filter
|
|
108
|
-
const searchLower = useMemo(() => {
|
|
109
|
-
return filter.searchText ? filter.searchText.toLowerCase() : null;
|
|
110
|
-
}, [filter.searchText]);
|
|
111
|
-
|
|
112
|
-
// Memoize method filter Set for O(1) lookup instead of Array.includes
|
|
113
|
-
// Performance: Converting array.includes to Set.has for faster lookups with large method lists
|
|
114
|
-
const methodSet = useMemo(() => {
|
|
115
|
-
return filter.method && filter.method.length > 0 ? new Set(filter.method) : null;
|
|
116
|
-
}, [filter.method]);
|
|
117
|
-
|
|
118
|
-
// Memoize content type Set for O(1) lookup
|
|
119
|
-
// Performance: Converting array.some to Set.has for faster content type matching
|
|
120
|
-
const contentTypeSet = useMemo(() => {
|
|
121
|
-
return filter.contentType && filter.contentType.length > 0 ? new Set(filter.contentType) : null;
|
|
122
|
-
}, [filter.contentType]);
|
|
123
|
-
|
|
124
|
-
// Filter events with optimized string operations and Set lookups
|
|
125
|
-
// Performance: Complex multi-stage filtering with string operations and content type matching
|
|
126
|
-
const filteredEvents = useMemo(() => {
|
|
127
|
-
let filtered = [...events];
|
|
128
|
-
if (methodSet) {
|
|
129
|
-
filtered = filtered.filter(e => methodSet.has(e.method));
|
|
130
|
-
}
|
|
131
|
-
if (filter.status && filter.status !== "all") {
|
|
132
|
-
switch (filter.status) {
|
|
133
|
-
case "success":
|
|
134
|
-
filtered = filtered.filter(e => e.status && e.status >= 200 && e.status < 300);
|
|
135
|
-
break;
|
|
136
|
-
case "error":
|
|
137
|
-
filtered = filtered.filter(e => e.error || e.status && e.status >= 400);
|
|
138
|
-
break;
|
|
139
|
-
case "pending":
|
|
140
|
-
filtered = filtered.filter(e => !e.status && !e.error);
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (searchLower) {
|
|
145
|
-
filtered = filtered.filter(e => e.url.toLowerCase().includes(searchLower) || e.method.toLowerCase().includes(searchLower) || e.path?.toLowerCase().includes(searchLower) || e.host?.toLowerCase().includes(searchLower) || e.error && e.error.toLowerCase().includes(searchLower) ||
|
|
146
|
-
// Search by GraphQL operation name (e.g., "GetUser", "CreatePost")
|
|
147
|
-
e.operationName && e.operationName.toLowerCase().includes(searchLower) ||
|
|
148
|
-
// Search by GraphQL variable values (e.g., "Sandshrew", "123", "true")
|
|
149
|
-
// This enables finding specific requests like "GetPokemon › Sandshrew" by typing "Sandshrew"
|
|
150
|
-
searchGraphQLVariables(e.graphqlVariables, searchLower));
|
|
151
|
-
}
|
|
152
|
-
if (filter.host) {
|
|
153
|
-
filtered = filtered.filter(e => e.host === filter.host);
|
|
154
|
-
}
|
|
155
|
-
if (contentTypeSet) {
|
|
156
|
-
filtered = filtered.filter(e => {
|
|
157
|
-
const headers = e.responseHeaders || e.requestHeaders;
|
|
158
|
-
const contentType = headers?.["content-type"] || headers?.["Content-Type"] || "";
|
|
159
|
-
for (const type of contentTypeSet) {
|
|
160
|
-
switch (type) {
|
|
161
|
-
case "JSON":
|
|
162
|
-
if (contentType.includes("json")) return true;
|
|
163
|
-
break;
|
|
164
|
-
case "XML":
|
|
165
|
-
if (contentType.includes("xml")) return true;
|
|
166
|
-
break;
|
|
167
|
-
case "HTML":
|
|
168
|
-
if (contentType.includes("html")) return true;
|
|
169
|
-
break;
|
|
170
|
-
case "TEXT":
|
|
171
|
-
if (contentType.includes("text")) return true;
|
|
172
|
-
break;
|
|
173
|
-
case "IMAGE":
|
|
174
|
-
if (contentType.includes("image")) return true;
|
|
175
|
-
break;
|
|
176
|
-
case "VIDEO":
|
|
177
|
-
if (contentType.includes("video")) return true;
|
|
178
|
-
break;
|
|
179
|
-
case "AUDIO":
|
|
180
|
-
if (contentType.includes("audio")) return true;
|
|
181
|
-
break;
|
|
182
|
-
case "FORM":
|
|
183
|
-
if (contentType.includes("form")) return true;
|
|
184
|
-
break;
|
|
185
|
-
case "OTHER":
|
|
186
|
-
if (!contentType || !contentType.includes("json") && !contentType.includes("xml") && !contentType.includes("html") && !contentType.includes("text") && !contentType.includes("image") && !contentType.includes("video") && !contentType.includes("audio") && !contentType.includes("form")) {
|
|
187
|
-
return true;
|
|
188
|
-
}
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return false;
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
return filtered;
|
|
196
|
-
}, [events, filter, searchLower, methodSet, contentTypeSet]);
|
|
197
|
-
|
|
198
|
-
// Events beyond the tier limit are "locked". Keyed off the ORIGINAL event
|
|
199
|
-
// order, not the filtered results, so locked events stay locked when
|
|
200
|
-
// searching. slice(Infinity) is empty, so Pro locks nothing.
|
|
201
|
-
const lockedEventIds = useMemo(() => {
|
|
202
|
-
const lockedIds = new Set();
|
|
203
|
-
events.slice(requestLimit).forEach(event => {
|
|
204
|
-
lockedIds.add(event.id);
|
|
205
|
-
});
|
|
206
|
-
return lockedIds;
|
|
207
|
-
}, [events, requestLimit]);
|
|
208
|
-
|
|
209
|
-
// Check if there are locked events in the current filtered results
|
|
210
|
-
const lockedEventsInFilter = useMemo(() => {
|
|
211
|
-
return filteredEvents.filter(event => lockedEventIds.has(event.id));
|
|
212
|
-
}, [filteredEvents, lockedEventIds]);
|
|
213
|
-
const hasLockedEvents = lockedEventsInFilter.length > 0;
|
|
214
|
-
const lockedEventCount = lockedEventsInFilter.length;
|
|
215
|
-
|
|
216
|
-
// Helper to check if a specific event is locked
|
|
217
|
-
const isEventLocked = useCallback(eventId => {
|
|
218
|
-
return lockedEventIds.has(eventId);
|
|
219
|
-
}, [lockedEventIds]);
|
|
220
|
-
|
|
221
|
-
// Memoize expensive statistics calculation by categorizing events in single pass
|
|
222
|
-
// Performance: Multiple array.filter operations replaced with single loop for better performance
|
|
223
|
-
const stats = useMemo(() => {
|
|
224
|
-
let successful = 0;
|
|
225
|
-
let failed = 0;
|
|
226
|
-
let pending = 0;
|
|
227
|
-
let totalSent = 0;
|
|
228
|
-
let totalReceived = 0;
|
|
229
|
-
let durationSum = 0;
|
|
230
|
-
let durationCount = 0;
|
|
231
|
-
|
|
232
|
-
// Single pass through events for all statistics
|
|
233
|
-
for (const event of events) {
|
|
234
|
-
// Categorize status
|
|
235
|
-
if (event.status && event.status >= 200 && event.status < 300) {
|
|
236
|
-
successful++;
|
|
237
|
-
} else if (event.error || event.status && event.status >= 400) {
|
|
238
|
-
failed++;
|
|
239
|
-
} else if (!event.status && !event.error) {
|
|
240
|
-
pending++;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Accumulate data sizes
|
|
244
|
-
totalSent += event.requestSize || 0;
|
|
245
|
-
totalReceived += event.responseSize || 0;
|
|
246
|
-
|
|
247
|
-
// Accumulate durations
|
|
248
|
-
if (event.duration) {
|
|
249
|
-
durationSum += event.duration;
|
|
250
|
-
durationCount++;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
const avgDuration = durationCount > 0 ? durationSum / durationCount : 0;
|
|
254
|
-
return {
|
|
255
|
-
totalRequests: events.length,
|
|
256
|
-
successfulRequests: successful,
|
|
257
|
-
failedRequests: failed,
|
|
258
|
-
pendingRequests: pending,
|
|
259
|
-
totalDataSent: totalSent,
|
|
260
|
-
totalDataReceived: totalReceived,
|
|
261
|
-
averageDuration: Math.round(avgDuration)
|
|
262
|
-
};
|
|
263
|
-
}, [events]);
|
|
264
|
-
|
|
265
|
-
// Memoize unique hosts extraction with single pass instead of map + filter + Set
|
|
266
|
-
// Performance: Avoiding array.map().filter() chain, using single loop with Set for deduplication
|
|
267
|
-
const hosts = useMemo(() => {
|
|
268
|
-
const hostSet = new Set();
|
|
269
|
-
for (const event of events) {
|
|
270
|
-
if (event.host) {
|
|
271
|
-
hostSet.add(event.host);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return Array.from(hostSet);
|
|
275
|
-
}, [events]);
|
|
276
|
-
|
|
277
|
-
// Memoize unique methods extraction with single pass
|
|
278
|
-
// Performance: Avoiding array.map() + Set constructor, using single loop for better performance
|
|
279
|
-
const methods = useMemo(() => {
|
|
280
|
-
const methodSet = new Set();
|
|
281
|
-
for (const event of events) {
|
|
282
|
-
methodSet.add(event.method);
|
|
283
|
-
}
|
|
284
|
-
return Array.from(methodSet);
|
|
285
|
-
}, [events]);
|
|
286
|
-
return {
|
|
287
|
-
/** Filtered events (all shown, but some may be locked for free users) */
|
|
288
|
-
events: filteredEvents,
|
|
289
|
-
/** All events before filtering (for stats) */
|
|
290
|
-
allEvents: events,
|
|
291
|
-
stats,
|
|
292
|
-
filter,
|
|
293
|
-
setFilter,
|
|
294
|
-
clearEvents,
|
|
295
|
-
/** Whether this hook is subscribed and receiving network events */
|
|
296
|
-
isCapturing,
|
|
297
|
-
/** Unsubscribe this hook from the network store (other subscribers unaffected) */
|
|
298
|
-
pauseCapturing,
|
|
299
|
-
/** Resubscribe this hook to the network store */
|
|
300
|
-
resumeCapturing,
|
|
301
|
-
hosts,
|
|
302
|
-
methods,
|
|
303
|
-
/** Whether there are locked events due to free tier limit */
|
|
304
|
-
hasLockedEvents,
|
|
305
|
-
/** Number of events locked due to free tier limit */
|
|
306
|
-
lockedEventCount,
|
|
307
|
-
/** Check if a specific event is locked (by ID) */
|
|
308
|
-
isEventLocked,
|
|
309
|
-
/** The live request limit for this tier (Infinity when unlimited). */
|
|
310
|
-
requestLimit,
|
|
311
|
-
/** Get subscriber counts for debugging */
|
|
312
|
-
getSubscriberCounts: () => networkEventStore.getSubscriberCounts()
|
|
313
|
-
};
|
|
314
|
-
}
|
|
1
|
+
"use strict";import{useState as k,useEffect as V,useCallback as f,useMemo as a,useRef as j}from"react";import{networkEventStore as S}from"../utils/networkEventStore";import{useLimit as z,TIER_LIMITS as G}from"@buoy-gg/shared-ui";import{searchGraphQLVariables as Q}from"../utils/formatGraphQLVariables";export const FREE_TIER_REQUEST_LIMIT=G["network.requests"].free;export function useNetworkEvents(C={}){const{isPro:I=false}=C;const R=z("network.requests");const h=I?Infinity:R;const[n,T]=k([]);const[r,y]=k({});const[m,g]=k(true);const c=j(null);V(()=>{if(m){c.current=S.subscribeToEvents(T)}return()=>{if(c.current){c.current();c.current=null}}},[m]);const q=f(()=>{S.clearEvents();T([])},[]);const x=f(()=>{g(false)},[]);const M=f(()=>{g(true)},[]);const u=a(()=>{return r.searchText?r.searchText.toLowerCase():null},[r.searchText]);const p=a(()=>{return r.method&&r.method.length>0?new Set(r.method):null},[r.method]);const E=a(()=>{return r.contentType&&r.contentType.length>0?new Set(r.contentType):null},[r.contentType]);const v=a(()=>{let t=[...n];if(p){t=t.filter(e=>p.has(e.method))}if(r.status&&r.status!=="all"){switch(r.status){case"success":t=t.filter(e=>e.status&&e.status>=200&&e.status<300);break;case"error":t=t.filter(e=>e.error||e.status&&e.status>=400);break;case"pending":t=t.filter(e=>!e.status&&!e.error);break}}if(u){t=t.filter(e=>e.url.toLowerCase().includes(u)||e.method.toLowerCase().includes(u)||e.path?.toLowerCase().includes(u)||e.host?.toLowerCase().includes(u)||e.error&&e.error.toLowerCase().includes(u)||e.operationName&&e.operationName.toLowerCase().includes(u)||Q(e.graphqlVariables,u))}if(r.host){t=t.filter(e=>e.host===r.host)}if(E){t=t.filter(e=>{const i=e.responseHeaders||e.requestHeaders;const s=i?.["content-type"]||i?.["Content-Type"]||"";for(const d of E){switch(d){case"JSON":if(s.includes("json"))return true;break;case"XML":if(s.includes("xml"))return true;break;case"HTML":if(s.includes("html"))return true;break;case"TEXT":if(s.includes("text"))return true;break;case"IMAGE":if(s.includes("image"))return true;break;case"VIDEO":if(s.includes("video"))return true;break;case"AUDIO":if(s.includes("audio"))return true;break;case"FORM":if(s.includes("form"))return true;break;case"OTHER":if(!s||!s.includes("json")&&!s.includes("xml")&&!s.includes("html")&&!s.includes("text")&&!s.includes("image")&&!s.includes("video")&&!s.includes("audio")&&!s.includes("form")){return true}break}}return false})}return t},[n,r,u,p,E]);const l=a(()=>{const t=new Set;n.slice(h).forEach(e=>{t.add(e.id)});return t},[n,h]);const w=a(()=>{return v.filter(t=>l.has(t.id))},[v,l]);const D=w.length>0;const O=w.length;const A=f(t=>{return l.has(t)},[l]);const F=a(()=>{let t=0;let e=0;let i=0;let s=0;let d=0;let L=0;let b=0;for(const o of n){if(o.status&&o.status>=200&&o.status<300){t++}else if(o.error||o.status&&o.status>=400){e++}else if(!o.status&&!o.error){i++}s+=o.requestSize||0;d+=o.responseSize||0;if(o.duration){L+=o.duration;b++}}const _=b>0?L/b:0;return{totalRequests:n.length,successfulRequests:t,failedRequests:e,pendingRequests:i,totalDataSent:s,totalDataReceived:d,averageDuration:Math.round(_)}},[n]);const H=a(()=>{const t=new Set;for(const e of n){if(e.host){t.add(e.host)}}return Array.from(t)},[n]);const N=a(()=>{const t=new Set;for(const e of n){t.add(e.method)}return Array.from(t)},[n]);return{events:v,allEvents:n,stats:F,filter:r,setFilter:y,clearEvents:q,isCapturing:m,pauseCapturing:x,resumeCapturing:M,hosts:H,methods:N,hasLockedEvents:D,lockedEventCount:O,isEventLocked:A,requestLimit:h,getSubscriberCounts:()=>S.getSubscriberCounts()}}
|