@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.
- package/README.md +11 -85
- package/lib/commonjs/index.js +1 -332
- 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 -403
- package/lib/commonjs/network/components/NetworkListScreen.web.js +1 -624
- 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 -135
- 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 -510
- package/lib/commonjs/network/overrides/engine.js +1 -93
- package/lib/commonjs/network/overrides/matchRule.js +1 -87
- package/lib/commonjs/network/overrides/overriddenRows.js +1 -37
- 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 -411
- package/lib/commonjs/network/utils/networkListener.js +4 -1352
- 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 -16
- package/lib/commonjs/network/utils/resolveRequestUrl.js +1 -15
- package/lib/commonjs/network/utils/selectSavedEvents.js +1 -39
- package/lib/commonjs/network/utils/startNetworkCapture.js +1 -24
- package/lib/commonjs/network/utils/withPinnedFirst.js +1 -32
- package/lib/commonjs/preset.js +1 -93
- package/lib/module/index.js +1 -104
- 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 -399
- package/lib/module/network/components/NetworkListScreen.web.js +1 -622
- 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 -131
- 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 -502
- package/lib/module/network/overrides/engine.js +1 -85
- package/lib/module/network/overrides/matchRule.js +1 -81
- package/lib/module/network/overrides/overriddenRows.js +1 -33
- 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 -409
- package/lib/module/network/utils/networkListener.js +4 -1341
- 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 -12
- package/lib/module/network/utils/resolveRequestUrl.js +1 -11
- package/lib/module/network/utils/selectSavedEvents.js +1 -35
- package/lib/module/network/utils/startNetworkCapture.js +1 -21
- package/lib/module/network/utils/withPinnedFirst.js +1 -28
- package/lib/module/preset.js +1 -91
- 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/resolveOverride.d.ts +2 -2
- package/lib/typescript/network/overrides/types.d.ts +9 -0
- package/lib/typescript/network/utils/networkListener.d.ts +11 -0
- package/lib/typescript/network/utils/realNow.d.ts +18 -0
- package/lib/web/index.mjs +73 -74
- package/package.json +3 -6
- 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/overriddenRows.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/requestId.d.ts.map +0 -1
- package/lib/typescript/network/utils/resolveRequestUrl.d.ts.map +0 -1
- package/lib/typescript/network/utils/selectSavedEvents.d.ts.map +0 -1
- package/lib/typescript/network/utils/startNetworkCapture.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,87 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.compilePattern = compilePattern;
|
|
7
|
-
exports.escapeRegex = escapeRegex;
|
|
8
|
-
exports.ruleMatches = ruleMatches;
|
|
9
|
-
/**
|
|
10
|
-
* Glob matching for override rules — a faithful port of Chrome DevTools'.
|
|
11
|
-
*
|
|
12
|
-
* Ported from `NetworkPersistenceManager.ts:1051` + `StringUtilities.ts:9` in
|
|
13
|
-
* the devtools-frontend source. Ported rather than reinvented on purpose: `*`
|
|
14
|
-
* globs are the syntax developers already have in their fingers from Chrome's
|
|
15
|
-
* "Override headers" `applyTo` field, and a pattern that behaves differently
|
|
16
|
-
* here than it does in the browser would be a quiet trap.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Chrome's escape set, character for character.
|
|
21
|
-
*
|
|
22
|
-
* `*` is deliberately ABSENT — it survives escaping so it can become `.*`
|
|
23
|
-
* below. Everything else that means something to a regex is neutered.
|
|
24
|
-
*/
|
|
25
|
-
const SPECIAL_CHARACTERS = "[]{}()\\.^$+|-,?";
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Escape a glob into regex source: neutralize regex metacharacters, then
|
|
29
|
-
* promote `*` to `.*`.
|
|
30
|
-
*
|
|
31
|
-
* `split`/`join` rather than `replaceAll` — same result, no reliance on how
|
|
32
|
-
* complete a given Hermes build's ES2021 surface is.
|
|
33
|
-
*/
|
|
34
|
-
function escapeRegex(pattern) {
|
|
35
|
-
let result = "";
|
|
36
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
37
|
-
const char = pattern.charAt(i);
|
|
38
|
-
if (SPECIAL_CHARACTERS.indexOf(char) !== -1) {
|
|
39
|
-
result += "\\";
|
|
40
|
-
}
|
|
41
|
-
result += char;
|
|
42
|
-
}
|
|
43
|
-
return result.split("*").join(".*");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Compiled patterns, cached by source.
|
|
48
|
-
*
|
|
49
|
-
* `resolveOverride` runs on EVERY request the app makes, against every rule.
|
|
50
|
-
* Recompiling a RegExp per request per rule is pure waste in the one place
|
|
51
|
-
* this feature is allowed to cost nothing.
|
|
52
|
-
*/
|
|
53
|
-
const compiled = new Map();
|
|
54
|
-
function compilePattern(pattern) {
|
|
55
|
-
const cached = compiled.get(pattern);
|
|
56
|
-
if (cached) return cached;
|
|
57
|
-
// Anchored, like Chrome's `^${...}$` — a pattern describes the whole URL, so
|
|
58
|
-
// `pokeapi.co` matches nothing and `*pokeapi.co*` matches everything on that
|
|
59
|
-
// host. Predictable beats clever; the rule editor writes full URLs for you.
|
|
60
|
-
//
|
|
61
|
-
// One exception: a pattern that starts with `/` is a PATH. Anchored to the
|
|
62
|
-
// full URL it could never match anything (every URL starts with a scheme),
|
|
63
|
-
// so it means "this path on any origin" — `/api/rewards` matches
|
|
64
|
-
// `http://localhost:3002/api/rewards`, and `/api/rewards*` also its queries.
|
|
65
|
-
const source = pattern.startsWith("/") ? `[a-zA-Z][a-zA-Z0-9+.-]*://[^/]*${escapeRegex(pattern)}` : escapeRegex(pattern);
|
|
66
|
-
const regex = new RegExp(`^${source}$`);
|
|
67
|
-
// Unbounded growth isn't possible in practice (patterns come from a capped
|
|
68
|
-
// rule list), but a user editing a pattern character by character churns
|
|
69
|
-
// entries, so keep the cache from becoming a leak.
|
|
70
|
-
if (compiled.size > 200) compiled.clear();
|
|
71
|
-
compiled.set(pattern, regex);
|
|
72
|
-
return regex;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Does this rule apply to this request? Ignores `enabled` — callers check it. */
|
|
76
|
-
function ruleMatches(rule, url, method) {
|
|
77
|
-
if (rule.methods && rule.methods.length > 0) {
|
|
78
|
-
if (!rule.methods.includes(method.toUpperCase())) return false;
|
|
79
|
-
}
|
|
80
|
-
try {
|
|
81
|
-
return compilePattern(rule.urlPattern).test(url);
|
|
82
|
-
} catch {
|
|
83
|
-
// A pattern that can't compile matches nothing rather than throwing inside
|
|
84
|
-
// the app's `send()`. The rule editor validates; this is the backstop.
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.compilePattern=compilePattern;exports.escapeRegex=escapeRegex;exports.ruleMatches=ruleMatches;const SPECIAL_CHARACTERS="[]{}()\\.^$+|-,?";function escapeRegex(e){let r="";for(let s=0;s<e.length;s++){const t=e.charAt(s);if(SPECIAL_CHARACTERS.indexOf(t)!==-1){r+="\\"}r+=t}return r.split("*").join(".*")}const compiled=new Map;function compilePattern(e){const r=compiled.get(e);if(r)return r;const s=e.startsWith("/")?`[a-zA-Z][a-zA-Z0-9+.-]*://[^/]*${escapeRegex(e)}`:escapeRegex(e);const t=new RegExp(`^${s}$`);if(compiled.size>200)compiled.clear();compiled.set(e,t);return t}function hasHeader(e,r){if(!e)return false;const s=r.toLowerCase();for(const t of Object.keys(e)){if(t.toLowerCase()===s&&e[t]!==void 0&&e[t]!=="")return true}return false}function ruleMatches(e,r,s,t){if(e.methods&&e.methods.length>0){if(!e.methods.includes(s.toUpperCase()))return false}if(e.requireHeader&&!hasHeader(t,e.requireHeader))return false;try{return compilePattern(e.urlPattern).test(r)}catch{return false}}
|
|
@@ -1,37 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.overriddenEvents = overriddenEvents;
|
|
7
|
-
var _matchRule = require("./matchRule");
|
|
8
|
-
/**
|
|
9
|
-
* Which captured requests belong in the list's OVERRIDDEN strip.
|
|
10
|
-
*
|
|
11
|
-
* A row belongs there when an active rule actually changed it (it carries the
|
|
12
|
-
* rule's override mark) or matched it after the rule existed (while overrides
|
|
13
|
-
* are paused those pass through, and the strip is how you find the rule again).
|
|
14
|
-
* Before a rule existed nothing overrode anything, so older matching requests
|
|
15
|
-
* are real server responses and stay in the list, with one exception: the
|
|
16
|
-
* newest of them, which is the request the rule was built from. That one hoists
|
|
17
|
-
* right away, so creating a rule shows where it points without waiting for the
|
|
18
|
-
* endpoint to be called again.
|
|
19
|
-
*/
|
|
20
|
-
function overriddenEvents(events, activeRules) {
|
|
21
|
-
if (activeRules.length === 0) return [];
|
|
22
|
-
const activeIds = new Set(activeRules.map(rule => rule.id));
|
|
23
|
-
const picked = new Set();
|
|
24
|
-
for (const rule of activeRules) {
|
|
25
|
-
let source;
|
|
26
|
-
for (const event of events) {
|
|
27
|
-
if (event.override && activeIds.has(event.override.ruleId)) {
|
|
28
|
-
picked.add(event.id);
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
if (!(0, _matchRule.ruleMatches)(rule, event.url, String(event.method || "GET"))) continue;
|
|
32
|
-
if (event.timestamp >= rule.createdAt) picked.add(event.id);else if (!source || event.timestamp > source.timestamp) source = event;
|
|
33
|
-
}
|
|
34
|
-
if (source) picked.add(source.id);
|
|
35
|
-
}
|
|
36
|
-
return events.filter(event => picked.has(event.id));
|
|
37
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.overriddenEvents=overriddenEvents;var _matchRule=require("./matchRule");function overriddenEvents(n,d){if(d.length===0)return[];const o=new Set(d.map(t=>t.id));const i=new Set;for(const t of d){let r;for(const e of n){if(e.override&&o.has(e.override.ruleId)){i.add(e.id);continue}if(!(0,_matchRule.ruleMatches)(t,e.url,String(e.method||"GET"),e.requestHeaders))continue;if(e.timestamp>=t.createdAt)i.add(e.id);else if(!r||e.timestamp>r.timestamp)r=e}if(r)i.add(r.id)}return n.filter(t=>i.has(t.id))}
|
|
@@ -1,550 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.overrideRulesStore = void 0;
|
|
7
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
8
|
-
var _networkListener = require("../utils/networkListener");
|
|
9
|
-
var _engine = require("./engine");
|
|
10
|
-
var _resolveOverride = require("./resolveOverride");
|
|
11
|
-
var _types = require("./types");
|
|
12
|
-
/**
|
|
13
|
-
* The override rule list — persisted, and the source of truth for the engine.
|
|
14
|
-
*
|
|
15
|
-
* Shape and persistence follow `networkSavedStore` / `ignoredPatternsStore`:
|
|
16
|
-
* lazy hydrate on first use, a `dirty` flag so a slow read can't clobber a
|
|
17
|
-
* mutation the user just made, and commit = set + emit + persist.
|
|
18
|
-
*
|
|
19
|
-
* Two things here are specific to overrides and worth reading:
|
|
20
|
-
*
|
|
21
|
-
* - `getRules()` is on the HOT PATH. It is called inside the app's own
|
|
22
|
-
* `XMLHttpRequest.send()`, for every request, so it does no work: the master
|
|
23
|
-
* switch collapses to returning a frozen empty array.
|
|
24
|
-
*
|
|
25
|
-
* - `syncKeepAlive()` keeps the interceptor installed while any rule is
|
|
26
|
-
* active. Without it a rule would stop firing the moment the Network tool
|
|
27
|
-
* closed, because the swizzle is reference-counted off capture subscribers.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* A rule as authored. `id`/`createdAt` and the runtime counters (`hits`,
|
|
32
|
-
* `seen`) are the store's business, never the caller's.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Handlers the desktop dashboard installs to forward mutations to the device.
|
|
37
|
-
*
|
|
38
|
-
* Rules only DO anything on the device — a rule is applied inside the app's own
|
|
39
|
-
* `fetch`/`XMLHttpRequest`, which the dashboard doesn't have. So in remote mode
|
|
40
|
-
* this store becomes a MIRROR: it renders the device's rules and forwards every
|
|
41
|
-
* edit, writing nothing to this machine's storage. Same seam
|
|
42
|
-
* `networkSavedStore` uses for pins.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* How long a local optimistic write suppresses incoming mirrors.
|
|
47
|
-
*
|
|
48
|
-
* An edit is applied locally AND forwarded; the device echoes it back on its
|
|
49
|
-
* next snapshot. Without this window the in-flight snapshot — the one already
|
|
50
|
-
* on the wire when the user tapped — would land first and visibly revert the
|
|
51
|
-
* edit for a frame.
|
|
52
|
-
*/
|
|
53
|
-
const REMOTE_ECHO_GRACE_MS = 800;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Launches-with-rules-on before overrides pause themselves.
|
|
57
|
-
*
|
|
58
|
-
* Persisting rules is the point ("force a 500, restart, watch the boot path"),
|
|
59
|
-
* but a body the app can't render turns that into a brick: the rule reinstates
|
|
60
|
-
* itself on every launch and the controls to remove it live inside an app that
|
|
61
|
-
* no longer draws. Verified in testing — the only exit was deleting the file
|
|
62
|
-
* off the simulator.
|
|
63
|
-
*
|
|
64
|
-
* Three is chosen so the intended workflow survives (force, relaunch, look,
|
|
65
|
-
* relaunch, look) while a wedge cannot outlive the second relaunch. The streak
|
|
66
|
-
* resets whenever a rule is created or edited, so an actively-used rule set
|
|
67
|
-
* never pauses.
|
|
68
|
-
*/
|
|
69
|
-
const MAX_UNTOUCHED_LAUNCHES = 3;
|
|
70
|
-
const EMPTY_RULES = Object.freeze([]);
|
|
71
|
-
|
|
72
|
-
/** Hit counts change per request; the UI shouldn't re-render per request. */
|
|
73
|
-
const HIT_EMIT_COALESCE_MS = 250;
|
|
74
|
-
const VALID_KINDS = ["respond", "fail", "delay"];
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Tolerate junk and older shapes rather than throwing the list away.
|
|
78
|
-
*
|
|
79
|
-
* Storage is forever: a shape that throws on parse would take the whole feature
|
|
80
|
-
* down on every launch until the user cleared app data.
|
|
81
|
-
*/
|
|
82
|
-
function parseRules(raw) {
|
|
83
|
-
if (!Array.isArray(raw)) return [];
|
|
84
|
-
const parsed = [];
|
|
85
|
-
for (const entry of raw) {
|
|
86
|
-
if (!entry || typeof entry !== "object") continue;
|
|
87
|
-
const rule = entry;
|
|
88
|
-
if (typeof rule.id !== "string" || typeof rule.urlPattern !== "string") {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (!rule.urlPattern) continue;
|
|
92
|
-
const kind = VALID_KINDS.includes(rule.kind) ? rule.kind : "respond";
|
|
93
|
-
parsed.push({
|
|
94
|
-
id: rule.id,
|
|
95
|
-
enabled: rule.enabled !== false,
|
|
96
|
-
name: typeof rule.name === "string" ? rule.name : undefined,
|
|
97
|
-
urlPattern: rule.urlPattern,
|
|
98
|
-
methods: Array.isArray(rule.methods) ? rule.methods.filter(m => typeof m === "string") : undefined,
|
|
99
|
-
kind,
|
|
100
|
-
status: typeof rule.status === "number" ? Math.min(_types.STATUS_MAX, Math.max(_types.STATUS_MIN, rule.status)) : undefined,
|
|
101
|
-
statusText: typeof rule.statusText === "string" ? rule.statusText : undefined,
|
|
102
|
-
headers: rule.headers && typeof rule.headers === "object" ? rule.headers : undefined,
|
|
103
|
-
body: typeof rule.body === "string" ? rule.body : undefined,
|
|
104
|
-
failKind: rule.failKind === "timeout" ? "timeout" : undefined,
|
|
105
|
-
delayMs: typeof rule.delayMs === "number" ? rule.delayMs : undefined,
|
|
106
|
-
times: typeof rule.times === "number" ? rule.times : null,
|
|
107
|
-
alternate: rule.alternate === true,
|
|
108
|
-
// Runtime-only: a rule that fired 3 times last launch has fired 0 times
|
|
109
|
-
// this one, and a persisted count would make `times` mean "ever" instead
|
|
110
|
-
// of "this session".
|
|
111
|
-
hits: 0,
|
|
112
|
-
seen: 0,
|
|
113
|
-
createdAt: typeof rule.createdAt === "number" ? rule.createdAt : 0
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
return parsed.slice(0, _types.MAX_RULES);
|
|
117
|
-
}
|
|
118
|
-
class OverrideRulesStore {
|
|
119
|
-
rules = [];
|
|
120
|
-
enabled = true;
|
|
121
|
-
listeners = new Set();
|
|
122
|
-
loaded = false;
|
|
123
|
-
loadPromise = null;
|
|
124
|
-
/** Set once a mutation happens so a slow initial load can't clobber it. */
|
|
125
|
-
dirty = false;
|
|
126
|
-
idCounter = 0;
|
|
127
|
-
state = {
|
|
128
|
-
enabled: true,
|
|
129
|
-
rules: [],
|
|
130
|
-
autoPaused: false
|
|
131
|
-
};
|
|
132
|
-
/** Consecutive launches that began with rules armed and nobody touching them. */
|
|
133
|
-
launchStreak = 0;
|
|
134
|
-
autoPaused = false;
|
|
135
|
-
/** Held while any rule is active — see `syncKeepAlive`. */
|
|
136
|
-
keepAlive = null;
|
|
137
|
-
hitEmitTimer = null;
|
|
138
|
-
/** Set on the desktop dashboard — see `disableEngine`. */
|
|
139
|
-
engineDisabled = false;
|
|
140
|
-
/** Set on the desktop dashboard — see `OverrideRulesRemote`. */
|
|
141
|
-
remote = null;
|
|
142
|
-
remoteEchoUntil = 0;
|
|
143
|
-
|
|
144
|
-
// ===========================================================================
|
|
145
|
-
// REMOTE MIRROR (desktop dashboard)
|
|
146
|
-
// ===========================================================================
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Turn this store into a mirror of a connected device's rules. Edits are
|
|
150
|
-
* applied locally for immediate feedback AND forwarded to the device, which
|
|
151
|
-
* is the source of truth; nothing is persisted to this machine.
|
|
152
|
-
*/
|
|
153
|
-
enableRemoteMode(remote) {
|
|
154
|
-
this.remote = remote;
|
|
155
|
-
// A dashboard must never run rules against its own traffic.
|
|
156
|
-
this.disableEngine();
|
|
157
|
-
}
|
|
158
|
-
disableRemoteMode() {
|
|
159
|
-
this.remote = null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Feed the device's rules in (mirror mode only). `force` bypasses the echo
|
|
164
|
-
* guard — used when tearing the mirror down, where there is no later snapshot
|
|
165
|
-
* to converge on.
|
|
166
|
-
*/
|
|
167
|
-
replaceRules(state, force = false) {
|
|
168
|
-
if (!this.remote) return;
|
|
169
|
-
// A local edit is still in flight — the snapshot on the wire predates it
|
|
170
|
-
// and would flicker the change away. Drop this one; the next carries the
|
|
171
|
-
// device's real answer.
|
|
172
|
-
if (!force && Date.now() < this.remoteEchoUntil) return;
|
|
173
|
-
this.enabled = state.enabled;
|
|
174
|
-
this.rules = state.rules;
|
|
175
|
-
this.rebuild();
|
|
176
|
-
this.emit();
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Mark a local optimistic write so the next in-flight mirror is ignored. */
|
|
180
|
-
markLocalEdit() {
|
|
181
|
-
if (this.remote) this.remoteEchoUntil = Date.now() + REMOTE_ECHO_GRACE_MS;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Never intercept traffic in this runtime.
|
|
186
|
-
*
|
|
187
|
-
* The desktop dashboard renders the real tool (`NetworkToolView`), so this
|
|
188
|
-
* module is imported there too — but the dashboard is a VIEWER. Its job is to
|
|
189
|
-
* drive rules on the connected device, never to swizzle its own `fetch` and
|
|
190
|
-
* override its own requests. The same seam `networkEventStore.disableCapture()`
|
|
191
|
-
* uses, for the same reason.
|
|
192
|
-
*/
|
|
193
|
-
disableEngine() {
|
|
194
|
-
this.engineDisabled = true;
|
|
195
|
-
if (this.keepAlive) {
|
|
196
|
-
this.keepAlive();
|
|
197
|
-
this.keepAlive = null;
|
|
198
|
-
if ((0, _networkListener.getNetworkListenerCount)() === 0) (0, _networkListener.stopNetworkListener)();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
getState() {
|
|
202
|
-
return this.state;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* The hot path — called inside `send()` for every request the app makes.
|
|
207
|
-
* Returns a frozen empty array when the master switch is off so the caller
|
|
208
|
-
* needs no second check.
|
|
209
|
-
*/
|
|
210
|
-
getRules() {
|
|
211
|
-
if (this.engineDisabled || !this.enabled) return EMPTY_RULES;
|
|
212
|
-
return this.rules;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* The rule's real body, fetching it from the device when this surface only
|
|
217
|
-
* has the stripped copy (see `OverrideRule.bodyOmitted`).
|
|
218
|
-
*
|
|
219
|
-
* On the device itself every body is already here, so this resolves
|
|
220
|
-
* immediately and the editor never notices the difference.
|
|
221
|
-
*/
|
|
222
|
-
async fetchRuleBody(id) {
|
|
223
|
-
const rule = this.rules.find(candidate => candidate.id === id);
|
|
224
|
-
if (!rule) return null;
|
|
225
|
-
if (!rule.bodyOmitted) return rule.body ?? null;
|
|
226
|
-
if (!this.remote?.getRuleBody) return null;
|
|
227
|
-
try {
|
|
228
|
-
return await this.remote.getRuleBody(id);
|
|
229
|
-
} catch {
|
|
230
|
-
// The editor keeps the empty body and the developer can retype it —
|
|
231
|
-
// better than an error state over a payload they can still replace.
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/** Rules that would actually fire — drives the "overrides active" banner. */
|
|
237
|
-
activeCount() {
|
|
238
|
-
if (!this.enabled) return 0;
|
|
239
|
-
return this.rules.filter(rule => rule.enabled && !(0, _resolveOverride.isSpent)(rule)).length;
|
|
240
|
-
}
|
|
241
|
-
subscribe(listener) {
|
|
242
|
-
this.listeners.add(listener);
|
|
243
|
-
void this.ensureLoaded();
|
|
244
|
-
return () => {
|
|
245
|
-
this.listeners.delete(listener);
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// ===========================================================================
|
|
250
|
-
// MUTATIONS
|
|
251
|
-
// ===========================================================================
|
|
252
|
-
|
|
253
|
-
setEnabled(enabled) {
|
|
254
|
-
if (this.enabled === enabled) return;
|
|
255
|
-
this.remote?.setEnabled(enabled);
|
|
256
|
-
this.markLocalEdit();
|
|
257
|
-
this.markTouched();
|
|
258
|
-
this.enabled = enabled;
|
|
259
|
-
this.commit(this.rules);
|
|
260
|
-
}
|
|
261
|
-
addRule(draft) {
|
|
262
|
-
if (this.rules.length >= _types.MAX_RULES) return null;
|
|
263
|
-
const rule = {
|
|
264
|
-
...draft,
|
|
265
|
-
id: `ovr_${Date.now().toString(36)}_${++this.idCounter}`,
|
|
266
|
-
hits: 0,
|
|
267
|
-
seen: 0,
|
|
268
|
-
createdAt: Date.now()
|
|
269
|
-
};
|
|
270
|
-
// Newest first: a rule you just made should win over an older, broader one,
|
|
271
|
-
// because first-match wins and you almost certainly meant the new one.
|
|
272
|
-
this.commit([rule, ...this.rules]);
|
|
273
|
-
return rule;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Create or replace a rule in one call.
|
|
278
|
-
*
|
|
279
|
-
* THE ONLY WRITE PATH THE UI SHOULD USE. `addRule`/`updateRule` are the
|
|
280
|
-
* primitives underneath and deliberately do NOT forward to the device — if
|
|
281
|
-
* the UI called them directly, an edit made on the dashboard would apply to
|
|
282
|
-
* the mirror and never reach the phone, which looks exactly like it worked.
|
|
283
|
-
*
|
|
284
|
-
* Returns the rule, or null when the list is full.
|
|
285
|
-
*/
|
|
286
|
-
upsertRule(draft) {
|
|
287
|
-
this.remote?.upsertRule(draft);
|
|
288
|
-
this.markLocalEdit();
|
|
289
|
-
this.markTouched();
|
|
290
|
-
const {
|
|
291
|
-
id,
|
|
292
|
-
...rest
|
|
293
|
-
} = draft;
|
|
294
|
-
if (id && this.rules.some(rule => rule.id === id)) {
|
|
295
|
-
this.updateRule(id, rest);
|
|
296
|
-
return this.rules.find(rule => rule.id === id) ?? null;
|
|
297
|
-
}
|
|
298
|
-
return this.addRule(rest);
|
|
299
|
-
}
|
|
300
|
-
updateRule(id, patch) {
|
|
301
|
-
const index = this.rules.findIndex(rule => rule.id === id);
|
|
302
|
-
if (index === -1) return;
|
|
303
|
-
const next = [...this.rules];
|
|
304
|
-
// Editing a rule resets its budget — otherwise a spent "fail once" rule
|
|
305
|
-
// stays silently dead after you change it and looks broken.
|
|
306
|
-
next[index] = {
|
|
307
|
-
...next[index],
|
|
308
|
-
...patch,
|
|
309
|
-
hits: 0,
|
|
310
|
-
seen: 0
|
|
311
|
-
};
|
|
312
|
-
this.commit(next);
|
|
313
|
-
}
|
|
314
|
-
setRuleEnabled(id, enabled) {
|
|
315
|
-
const index = this.rules.findIndex(rule => rule.id === id);
|
|
316
|
-
if (index === -1 || this.rules[index].enabled === enabled) return;
|
|
317
|
-
this.remote?.setRuleEnabled(id, enabled);
|
|
318
|
-
this.markLocalEdit();
|
|
319
|
-
this.markTouched();
|
|
320
|
-
const next = [...this.rules];
|
|
321
|
-
// Re-enabling a spent rule has to clear its budget too, or the toggle
|
|
322
|
-
// appears to do nothing.
|
|
323
|
-
next[index] = {
|
|
324
|
-
...next[index],
|
|
325
|
-
enabled,
|
|
326
|
-
hits: enabled ? 0 : next[index].hits,
|
|
327
|
-
seen: enabled ? 0 : next[index].seen
|
|
328
|
-
};
|
|
329
|
-
this.commit(next);
|
|
330
|
-
}
|
|
331
|
-
removeRule(id) {
|
|
332
|
-
const next = this.rules.filter(rule => rule.id !== id);
|
|
333
|
-
if (next.length === this.rules.length) return;
|
|
334
|
-
this.remote?.removeRule(id);
|
|
335
|
-
this.markLocalEdit();
|
|
336
|
-
this.markTouched();
|
|
337
|
-
this.commit(next);
|
|
338
|
-
}
|
|
339
|
-
clearRules() {
|
|
340
|
-
if (this.rules.length === 0) return;
|
|
341
|
-
this.remote?.clearRules();
|
|
342
|
-
this.markLocalEdit();
|
|
343
|
-
this.markTouched();
|
|
344
|
-
this.commit([]);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* One rule matched one request.
|
|
349
|
-
*
|
|
350
|
-
* Replaces the rule object rather than mutating it so memoized rows actually
|
|
351
|
-
* re-render, and coalesces the notification — this runs at request rate.
|
|
352
|
-
*/
|
|
353
|
-
/**
|
|
354
|
-
* One rule matched one request, applied or not.
|
|
355
|
-
*
|
|
356
|
-
* Deliberately silent — this fires on requests an alternating rule lets
|
|
357
|
-
* through, where nothing visible changed. `recordHit` owns the notification.
|
|
358
|
-
*/
|
|
359
|
-
recordMatch(ruleId) {
|
|
360
|
-
const index = this.rules.findIndex(rule => rule.id === ruleId);
|
|
361
|
-
if (index === -1) return;
|
|
362
|
-
const next = [...this.rules];
|
|
363
|
-
next[index] = {
|
|
364
|
-
...next[index],
|
|
365
|
-
seen: next[index].seen + 1
|
|
366
|
-
};
|
|
367
|
-
this.rules = next;
|
|
368
|
-
this.rebuild();
|
|
369
|
-
}
|
|
370
|
-
recordHit(ruleId) {
|
|
371
|
-
const index = this.rules.findIndex(rule => rule.id === ruleId);
|
|
372
|
-
if (index === -1) return;
|
|
373
|
-
const rule = this.rules[index];
|
|
374
|
-
const hits = rule.hits + 1;
|
|
375
|
-
const spent = typeof rule.times === "number" && hits >= rule.times;
|
|
376
|
-
const next = [...this.rules];
|
|
377
|
-
next[index] = {
|
|
378
|
-
...rule,
|
|
379
|
-
hits,
|
|
380
|
-
enabled: spent ? false : rule.enabled
|
|
381
|
-
};
|
|
382
|
-
if (spent) {
|
|
383
|
-
// Auto-expiry is a real state change (and releases the keep-alive), so it
|
|
384
|
-
// commits immediately instead of waiting on the coalesce window.
|
|
385
|
-
this.commit(next);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
this.rules = next;
|
|
389
|
-
this.rebuild();
|
|
390
|
-
this.scheduleHitEmit();
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// ===========================================================================
|
|
394
|
-
// LIFECYCLE
|
|
395
|
-
// ===========================================================================
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Hydrate from storage. Safe to call repeatedly; only the first call reads.
|
|
399
|
-
*
|
|
400
|
-
* Called at package import (see `preset.tsx`) rather than on first subscribe,
|
|
401
|
-
* because a rule has to fire from app boot — with the Network tool closed and
|
|
402
|
-
* nothing capturing.
|
|
403
|
-
*/
|
|
404
|
-
ensureLoaded() {
|
|
405
|
-
// Mirror mode reads the device's rules, never this machine's storage.
|
|
406
|
-
if (this.remote) return Promise.resolve();
|
|
407
|
-
if (this.loaded) return Promise.resolve();
|
|
408
|
-
if (this.loadPromise) return this.loadPromise;
|
|
409
|
-
this.loadPromise = (async () => {
|
|
410
|
-
try {
|
|
411
|
-
const stored = await _sharedUi.persistentStorage.getItem(_sharedUi.devToolsStorageKeys.network.overrides());
|
|
412
|
-
// A rule may have been created while this read was in flight — never
|
|
413
|
-
// overwrite user intent with older disk state.
|
|
414
|
-
if (stored && !this.dirty) {
|
|
415
|
-
const parsed = JSON.parse(stored);
|
|
416
|
-
this.enabled = parsed?.enabled !== false;
|
|
417
|
-
this.rules = parseRules(parsed?.rules);
|
|
418
|
-
this.launchStreak = typeof parsed?.launchStreak === "number" ? parsed.launchStreak : 0;
|
|
419
|
-
this.applyLaunchGuard();
|
|
420
|
-
this.rebuild();
|
|
421
|
-
}
|
|
422
|
-
} catch {
|
|
423
|
-
// Corrupt or unreadable — start empty rather than taking the tool down.
|
|
424
|
-
} finally {
|
|
425
|
-
this.loaded = true;
|
|
426
|
-
this.syncKeepAlive();
|
|
427
|
-
this.emit();
|
|
428
|
-
}
|
|
429
|
-
})();
|
|
430
|
-
return this.loadPromise;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Count this launch, and pause overrides if they've been armed and untouched
|
|
435
|
-
* for too many in a row. See MAX_UNTOUCHED_LAUNCHES for why.
|
|
436
|
-
*/
|
|
437
|
-
applyLaunchGuard() {
|
|
438
|
-
if (!this.enabled || this.activeCount() === 0) {
|
|
439
|
-
this.launchStreak = 0;
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
this.launchStreak += 1;
|
|
443
|
-
if (this.launchStreak >= MAX_UNTOUCHED_LAUNCHES) {
|
|
444
|
-
this.enabled = false;
|
|
445
|
-
this.autoPaused = true;
|
|
446
|
-
this.launchStreak = 0;
|
|
447
|
-
}
|
|
448
|
-
this.persist();
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Keep the interceptor installed for as long as a rule could fire.
|
|
453
|
-
*
|
|
454
|
-
* The swizzle is reference-counted off `networkListener`'s listener count
|
|
455
|
-
* (the event store adds one while capturing). Holding a no-op listener reuses
|
|
456
|
-
* that existing lifecycle instead of inventing a second one.
|
|
457
|
-
*/
|
|
458
|
-
syncKeepAlive() {
|
|
459
|
-
const needed = !this.engineDisabled && this.activeCount() > 0;
|
|
460
|
-
if (needed && !this.keepAlive) {
|
|
461
|
-
(0, _networkListener.startNetworkListener)();
|
|
462
|
-
this.keepAlive = (0, _networkListener.addNetworkListener)(() => {});
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
if (!needed && this.keepAlive) {
|
|
466
|
-
this.keepAlive();
|
|
467
|
-
this.keepAlive = null;
|
|
468
|
-
// Only tear the swizzle down if nothing else wants it — the Network tool
|
|
469
|
-
// may well still be open and capturing.
|
|
470
|
-
if ((0, _networkListener.getNetworkListenerCount)() === 0) (0, _networkListener.stopNetworkListener)();
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
scheduleHitEmit() {
|
|
474
|
-
if (this.hitEmitTimer !== null) return;
|
|
475
|
-
this.hitEmitTimer = setTimeout(() => {
|
|
476
|
-
this.hitEmitTimer = null;
|
|
477
|
-
this.emit();
|
|
478
|
-
}, HIT_EMIT_COALESCE_MS);
|
|
479
|
-
}
|
|
480
|
-
commit(next) {
|
|
481
|
-
this.dirty = true;
|
|
482
|
-
this.rules = next.slice(0, _types.MAX_RULES);
|
|
483
|
-
this.rebuild();
|
|
484
|
-
this.syncKeepAlive();
|
|
485
|
-
this.emit();
|
|
486
|
-
this.persist();
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/** Rebuild the derived view once per mutation so renders get stable refs. */
|
|
490
|
-
rebuild() {
|
|
491
|
-
this.state = {
|
|
492
|
-
enabled: this.enabled,
|
|
493
|
-
rules: this.rules,
|
|
494
|
-
autoPaused: this.autoPaused
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* The developer is actively working with the rules, so the launch guard has
|
|
500
|
-
* nothing to protect them from — reset it and clear any auto-pause notice.
|
|
501
|
-
*/
|
|
502
|
-
markTouched() {
|
|
503
|
-
this.launchStreak = 0;
|
|
504
|
-
this.autoPaused = false;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* The developer reached the override UI, which is proof the app still runs.
|
|
509
|
-
*
|
|
510
|
-
* The launch guard exists to escape a WEDGE — a body the app can't render,
|
|
511
|
-
* where the controls are unreachable because nothing draws. If this call
|
|
512
|
-
* happens, that isn't the situation, so the streak has nothing to protect
|
|
513
|
-
* against. Without it the guard nags anyone who legitimately keeps a rule
|
|
514
|
-
* armed for days, which is a real workflow ("force /checkout to 500 every
|
|
515
|
-
* morning") and not the failure mode it was built for.
|
|
516
|
-
*/
|
|
517
|
-
noteHealthyLaunch() {
|
|
518
|
-
if (this.launchStreak === 0 && !this.autoPaused) return;
|
|
519
|
-
this.markTouched();
|
|
520
|
-
this.persist();
|
|
521
|
-
this.rebuild();
|
|
522
|
-
this.emit();
|
|
523
|
-
}
|
|
524
|
-
persist() {
|
|
525
|
-
// The dashboard must not write the device's rules into its own storage —
|
|
526
|
-
// they'd outlive the connection and shadow the next device.
|
|
527
|
-
if (this.remote) return;
|
|
528
|
-
_sharedUi.persistentStorage.setItem(_sharedUi.devToolsStorageKeys.network.overrides(), JSON.stringify({
|
|
529
|
-
enabled: this.enabled,
|
|
530
|
-
rules: this.rules,
|
|
531
|
-
launchStreak: this.launchStreak
|
|
532
|
-
})).catch(() => {
|
|
533
|
-
// Silently fail — rules remain in memory for this session.
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
emit() {
|
|
537
|
-
this.listeners.forEach(listener => listener());
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/** Singleton — one rule list per JS runtime. */
|
|
542
|
-
const overrideRulesStore = exports.overrideRulesStore = new OverrideRulesStore();
|
|
543
|
-
|
|
544
|
-
// Register with the engine so `networkListener` can reach the rules without
|
|
545
|
-
// importing this module (which imports the listener — see engine.ts).
|
|
546
|
-
(0, _engine.setOverrideHooks)({
|
|
547
|
-
getRules: () => overrideRulesStore.getRules(),
|
|
548
|
-
recordHit: ruleId => overrideRulesStore.recordHit(ruleId),
|
|
549
|
-
recordMatch: ruleId => overrideRulesStore.recordMatch(ruleId)
|
|
550
|
-
});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.overrideRulesStore=void 0;var _sharedUi=require("@buoy-gg/shared-ui");var _networkListener=require("../utils/networkListener");var _engine=require("./engine");var _resolveOverride=require("./resolveOverride");var _types=require("./types");var _realNow=require("../utils/realNow");const REMOTE_ECHO_GRACE_MS=800;const MAX_UNTOUCHED_LAUNCHES=3;const EMPTY_RULES=Object.freeze([]);const HIT_EMIT_COALESCE_MS=250;const VALID_KINDS=["respond","fail","delay"];function parseRules(n){if(!Array.isArray(n))return[];const t=[];for(const i of n){if(!i||typeof i!=="object")continue;const e=i;if(typeof e.id!=="string"||typeof e.urlPattern!=="string"){continue}if(!e.urlPattern)continue;const s=VALID_KINDS.includes(e.kind)?e.kind:"respond";t.push({id:e.id,enabled:e.enabled!==false,name:typeof e.name==="string"?e.name:void 0,urlPattern:e.urlPattern,methods:Array.isArray(e.methods)?e.methods.filter(r=>typeof r==="string"):void 0,requireHeader:typeof e.requireHeader==="string"&&e.requireHeader?e.requireHeader:void 0,kind:s,status:typeof e.status==="number"?Math.min(_types.STATUS_MAX,Math.max(_types.STATUS_MIN,e.status)):void 0,statusText:typeof e.statusText==="string"?e.statusText:void 0,headers:e.headers&&typeof e.headers==="object"?e.headers:void 0,body:typeof e.body==="string"?e.body:void 0,failKind:e.failKind==="timeout"?"timeout":void 0,delayMs:typeof e.delayMs==="number"?e.delayMs:void 0,times:typeof e.times==="number"?e.times:null,alternate:e.alternate===true,hits:0,seen:0,createdAt:typeof e.createdAt==="number"?e.createdAt:0})}return t.slice(0,_types.MAX_RULES)}class OverrideRulesStore{rules=[];enabled=true;listeners=new Set;loaded=false;loadPromise=null;dirty=false;idCounter=0;state={enabled:true,rules:[],autoPaused:false};launchStreak=0;autoPaused=false;keepAlive=null;hitEmitTimer=null;engineDisabled=false;remote=null;remoteEchoUntil=0;enableRemoteMode(t){this.remote=t;this.disableEngine()}disableRemoteMode(){this.remote=null}replaceRules(t,i=false){if(!this.remote)return;if(!i&&(0,_realNow.realNow)()<this.remoteEchoUntil)return;this.enabled=t.enabled;this.rules=t.rules;this.rebuild();this.emit()}markLocalEdit(){if(this.remote)this.remoteEchoUntil=(0,_realNow.realNow)()+REMOTE_ECHO_GRACE_MS}disableEngine(){this.engineDisabled=true;if(this.keepAlive){this.keepAlive();this.keepAlive=null;if((0,_networkListener.getNetworkListenerCount)()===0)(0,_networkListener.stopNetworkListener)()}}getState(){return this.state}getRules(){if(this.engineDisabled||!this.enabled)return EMPTY_RULES;return this.rules}async fetchRuleBody(t){const i=this.rules.find(e=>e.id===t);if(!i)return null;if(!i.bodyOmitted)return i.body??null;if(!this.remote?.getRuleBody)return null;try{return await this.remote.getRuleBody(t)}catch{return null}}activeCount(){if(!this.enabled)return 0;return this.rules.filter(t=>t.enabled&&!(0,_resolveOverride.isSpent)(t)).length}subscribe(t){this.listeners.add(t);void this.ensureLoaded();return()=>{this.listeners.delete(t)}}setEnabled(t){if(this.enabled===t)return;this.remote?.setEnabled(t);this.markLocalEdit();this.markTouched();this.enabled=t;this.commit(this.rules)}addRule(t){if(this.rules.length>=_types.MAX_RULES)return null;const i={...t,id:`ovr_${Date.now().toString(36)}_${++this.idCounter}`,hits:0,seen:0,createdAt:Date.now()};this.commit([i,...this.rules]);return i}upsertRule(t){this.remote?.upsertRule(t);this.markLocalEdit();this.markTouched();const{id:i,...e}=t;if(i&&this.rules.some(s=>s.id===i)){this.updateRule(i,e);return this.rules.find(s=>s.id===i)??null}return this.addRule(e)}updateRule(t,i){const e=this.rules.findIndex(r=>r.id===t);if(e===-1)return;const s=[...this.rules];s[e]={...s[e],...i,hits:0,seen:0};this.commit(s)}setRuleEnabled(t,i){const e=this.rules.findIndex(r=>r.id===t);if(e===-1||this.rules[e].enabled===i)return;this.remote?.setRuleEnabled(t,i);this.markLocalEdit();this.markTouched();const s=[...this.rules];s[e]={...s[e],enabled:i,hits:i?0:s[e].hits,seen:i?0:s[e].seen};this.commit(s)}removeRule(t){const i=this.rules.filter(e=>e.id!==t);if(i.length===this.rules.length)return;this.remote?.removeRule(t);this.markLocalEdit();this.markTouched();this.commit(i)}clearRules(){if(this.rules.length===0)return;this.remote?.clearRules();this.markLocalEdit();this.markTouched();this.commit([])}recordMatch(t){const i=this.rules.findIndex(s=>s.id===t);if(i===-1)return;const e=[...this.rules];e[i]={...e[i],seen:e[i].seen+1};this.rules=e;this.rebuild()}recordHit(t){const i=this.rules.findIndex(u=>u.id===t);if(i===-1)return;const e=this.rules[i];const s=e.hits+1;const r=typeof e.times==="number"&&s>=e.times;const l=[...this.rules];l[i]={...e,hits:s,enabled:r?false:e.enabled};if(r){this.commit(l);return}this.rules=l;this.rebuild();this.scheduleHitEmit()}ensureLoaded(){if(this.remote)return Promise.resolve();if(this.loaded)return Promise.resolve();if(this.loadPromise)return this.loadPromise;this.loadPromise=(async()=>{try{const t=await _sharedUi.persistentStorage.getItem(_sharedUi.devToolsStorageKeys.network.overrides());if(t&&!this.dirty){const i=JSON.parse(t);this.enabled=i?.enabled!==false;this.rules=parseRules(i?.rules);this.launchStreak=typeof i?.launchStreak==="number"?i.launchStreak:0;this.applyLaunchGuard();this.rebuild()}}catch{}finally{this.loaded=true;this.syncKeepAlive();this.emit()}})();return this.loadPromise}applyLaunchGuard(){if(!this.enabled||this.activeCount()===0){this.launchStreak=0;return}this.launchStreak+=1;if(this.launchStreak>=MAX_UNTOUCHED_LAUNCHES){this.enabled=false;this.autoPaused=true;this.launchStreak=0}this.persist()}syncKeepAlive(){const t=!this.engineDisabled&&this.activeCount()>0;if(t&&!this.keepAlive){(0,_networkListener.startNetworkListener)();this.keepAlive=(0,_networkListener.addNetworkListener)(()=>{});return}if(!t&&this.keepAlive){this.keepAlive();this.keepAlive=null;if((0,_networkListener.getNetworkListenerCount)()===0)(0,_networkListener.stopNetworkListener)()}}scheduleHitEmit(){if(this.hitEmitTimer!==null)return;this.hitEmitTimer=setTimeout(()=>{this.hitEmitTimer=null;this.emit()},HIT_EMIT_COALESCE_MS)}commit(t){this.dirty=true;this.rules=t.slice(0,_types.MAX_RULES);this.rebuild();this.syncKeepAlive();this.emit();this.persist()}rebuild(){this.state={enabled:this.enabled,rules:this.rules,autoPaused:this.autoPaused}}markTouched(){this.launchStreak=0;this.autoPaused=false}noteHealthyLaunch(){if(this.launchStreak===0&&!this.autoPaused)return;this.markTouched();this.persist();this.rebuild();this.emit()}persist(){if(this.remote)return;_sharedUi.persistentStorage.setItem(_sharedUi.devToolsStorageKeys.network.overrides(),JSON.stringify({enabled:this.enabled,rules:this.rules,launchStreak:this.launchStreak})).catch(()=>{})}emit(){this.listeners.forEach(t=>t())}}const overrideRulesStore=exports.overrideRulesStore=new OverrideRulesStore;(0,_engine.setOverrideHooks)({getRules:()=>overrideRulesStore.getRules(),recordHit:n=>overrideRulesStore.recordHit(n),recordMatch:n=>overrideRulesStore.recordMatch(n)});
|