@buoy-gg/network 7.0.12 → 7.0.14
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/lib/commonjs/index.js +52 -0
- package/lib/commonjs/network/components/NetworkDetailStepper.js +26 -14
- package/lib/commonjs/network/components/NetworkEventDetailView.js +86 -14
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +4 -0
- package/lib/commonjs/network/components/NetworkHeaderLive.js +32 -7
- package/lib/commonjs/network/components/NetworkHeaderMenu.js +183 -0
- package/lib/commonjs/network/components/NetworkListScreen.js +134 -8
- package/lib/commonjs/network/components/NetworkModal.js +198 -51
- package/lib/commonjs/network/components/NetworkNumberPrompt.js +178 -0
- package/lib/commonjs/network/components/NetworkOverrideEditor.js +797 -0
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.js +102 -0
- package/lib/commonjs/network/components/NetworkOverridesButton.js +88 -0
- package/lib/commonjs/network/components/NetworkOverridesScreen.js +489 -0
- package/lib/commonjs/network/components/response-editor/Explorer.js +973 -0
- package/lib/commonjs/network/components/response-editor/deleteNestedDataByPath.js +56 -0
- package/lib/commonjs/network/components/response-editor/editorContext.js +33 -0
- package/lib/commonjs/network/components/response-editor/updateNestedDataByPath.js +61 -0
- package/lib/commonjs/network/hooks/useNetworkSelection.js +53 -0
- package/lib/commonjs/network/hooks/useOverrideRules.js +36 -0
- package/lib/commonjs/network/overrides/applyToFetch.js +80 -0
- package/lib/commonjs/network/overrides/applyToXhr.js +190 -0
- package/lib/commonjs/network/overrides/engine.js +70 -0
- package/lib/commonjs/network/overrides/matchRule.js +81 -0
- package/lib/commonjs/network/overrides/overrideRulesStore.js +550 -0
- package/lib/commonjs/network/overrides/presets.js +70 -0
- package/lib/commonjs/network/overrides/resolveOverride.js +158 -0
- package/lib/commonjs/network/overrides/types.js +85 -0
- package/lib/commonjs/network/sync/networkSyncAdapter.js +230 -1
- package/lib/commonjs/network/types/index.js +5 -1
- package/lib/commonjs/network/utils/networkEventStore.js +52 -6
- package/lib/commonjs/network/utils/networkListener.js +90 -10
- package/lib/commonjs/network/utils/networkSelectionStore.js +193 -0
- package/lib/commonjs/preset.js +11 -0
- package/lib/module/index.js +19 -0
- package/lib/module/network/components/NetworkDetailStepper.js +27 -15
- package/lib/module/network/components/NetworkEventDetailView.js +87 -15
- package/lib/module/network/components/NetworkEventItemCompact.js +5 -1
- package/lib/module/network/components/NetworkHeaderLive.js +32 -7
- package/lib/module/network/components/NetworkHeaderMenu.js +177 -0
- package/lib/module/network/components/NetworkListScreen.js +135 -9
- package/lib/module/network/components/NetworkModal.js +200 -53
- package/lib/module/network/components/NetworkNumberPrompt.js +174 -0
- package/lib/module/network/components/NetworkOverrideEditor.js +794 -0
- package/lib/module/network/components/NetworkOverrideHeaderButton.js +99 -0
- package/lib/module/network/components/NetworkOverridesButton.js +85 -0
- package/lib/module/network/components/NetworkOverridesScreen.js +485 -0
- package/lib/module/network/components/response-editor/Explorer.js +972 -0
- package/lib/module/network/components/response-editor/deleteNestedDataByPath.js +51 -0
- package/lib/module/network/components/response-editor/editorContext.js +29 -0
- package/lib/module/network/components/response-editor/updateNestedDataByPath.js +56 -0
- package/lib/module/network/hooks/useNetworkSelection.js +46 -0
- package/lib/module/network/hooks/useOverrideRules.js +32 -0
- package/lib/module/network/overrides/applyToFetch.js +72 -0
- package/lib/module/network/overrides/applyToXhr.js +185 -0
- package/lib/module/network/overrides/engine.js +64 -0
- package/lib/module/network/overrides/matchRule.js +75 -0
- package/lib/module/network/overrides/overrideRulesStore.js +547 -0
- package/lib/module/network/overrides/presets.js +65 -0
- package/lib/module/network/overrides/resolveOverride.js +152 -0
- package/lib/module/network/overrides/types.js +80 -0
- package/lib/module/network/sync/networkSyncAdapter.js +230 -1
- package/lib/module/network/types/index.js +3 -1
- package/lib/module/network/utils/networkEventStore.js +52 -6
- package/lib/module/network/utils/networkListener.js +91 -10
- package/lib/module/network/utils/networkSelectionStore.js +188 -0
- package/lib/module/preset.js +13 -1
- package/lib/typescript/index.d.ts +9 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts +8 -5
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts +36 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts +6 -0
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts +13 -3
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts +46 -0
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.d.ts +33 -0
- package/lib/typescript/network/components/NetworkNumberPrompt.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverrideEditor.d.ts +17 -0
- package/lib/typescript/network/components/NetworkOverrideEditor.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts +27 -0
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverridesButton.d.ts +17 -0
- package/lib/typescript/network/components/NetworkOverridesButton.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts +29 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/Explorer.d.ts +17 -0
- package/lib/typescript/network/components/response-editor/Explorer.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts +9 -0
- package/lib/typescript/network/components/response-editor/deleteNestedDataByPath.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/editorContext.d.ts +29 -0
- package/lib/typescript/network/components/response-editor/editorContext.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts +10 -0
- package/lib/typescript/network/components/response-editor/updateNestedDataByPath.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts +26 -0
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useOverrideRules.d.ts +29 -0
- package/lib/typescript/network/hooks/useOverrideRules.d.ts.map +1 -0
- package/lib/typescript/network/overrides/__tests__/applyToFetch.test.d.ts +16 -0
- package/lib/typescript/network/overrides/__tests__/applyToFetch.test.d.ts.map +1 -0
- package/lib/typescript/network/overrides/__tests__/applyToXhr.test.d.ts +19 -0
- package/lib/typescript/network/overrides/__tests__/applyToXhr.test.d.ts.map +1 -0
- package/lib/typescript/network/overrides/__tests__/resolveOverride.test.d.ts +22 -0
- package/lib/typescript/network/overrides/__tests__/resolveOverride.test.d.ts.map +1 -0
- package/lib/typescript/network/overrides/applyToFetch.d.ts +50 -0
- package/lib/typescript/network/overrides/applyToFetch.d.ts.map +1 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts +43 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +1 -0
- package/lib/typescript/network/overrides/engine.d.ts +47 -0
- package/lib/typescript/network/overrides/engine.d.ts.map +1 -0
- package/lib/typescript/network/overrides/matchRule.d.ts +22 -0
- package/lib/typescript/network/overrides/matchRule.d.ts.map +1 -0
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts +205 -0
- package/lib/typescript/network/overrides/overrideRulesStore.d.ts.map +1 -0
- package/lib/typescript/network/overrides/presets.d.ts +38 -0
- package/lib/typescript/network/overrides/presets.d.ts.map +1 -0
- package/lib/typescript/network/overrides/resolveOverride.d.ts +46 -0
- package/lib/typescript/network/overrides/resolveOverride.d.ts.map +1 -0
- package/lib/typescript/network/overrides/types.d.ts +169 -0
- package/lib/typescript/network/overrides/types.d.ts.map +1 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +73 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/network/types/index.d.ts +10 -0
- package/lib/typescript/network/types/index.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts +14 -0
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkListener.d.ts +3 -0
- package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkSelectionStore.d.ts +117 -0
- package/lib/typescript/network/utils/networkSelectionStore.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -5,6 +5,25 @@
|
|
|
5
5
|
* Simple and reliable network interception for React Native
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { overrideForRequest } from "../overrides/engine";
|
|
9
|
+
import { runOverride } from "../overrides/applyToXhr";
|
|
10
|
+
import { canSynthesizeResponse, sleep, synthesizeFetchError, synthesizeResponse } from "../overrides/applyToFetch";
|
|
11
|
+
import { markForOutcome } from "../overrides/types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* True only while `originalFetch` is being CALLED (not awaited).
|
|
15
|
+
*
|
|
16
|
+
* Overrides hook both `fetch` and `XMLHttpRequest`, because Expo's native
|
|
17
|
+
* `expo/fetch` bypasses XHR while bare RN's whatwg-fetch polyfill runs straight
|
|
18
|
+
* through it. On bare RN that means one `fetch()` would otherwise be overridden
|
|
19
|
+
* twice — a doubled delay and a doubled hit count.
|
|
20
|
+
*
|
|
21
|
+
* whatwg-fetch creates and sends its XHR synchronously inside the Promise
|
|
22
|
+
* executor, so a flag raised across the call expression alone is enough to
|
|
23
|
+
* cover the inner `send()` without leaking across the await.
|
|
24
|
+
*/
|
|
25
|
+
let insideOverriddenFetch = false;
|
|
26
|
+
|
|
8
27
|
// Extended XMLHttpRequest interface for monkey-patching
|
|
9
28
|
|
|
10
29
|
// Event types for network operations
|
|
@@ -199,7 +218,7 @@ class NetworkListener {
|
|
|
199
218
|
* Handle XMLHttpRequest response processing
|
|
200
219
|
* This method processes the response and emits appropriate events
|
|
201
220
|
*/
|
|
202
|
-
handleXHRResponse(xhr, requestId, cleanUrl, method, requestHeaders, requestData, params, startTime, isError = false, clientType = "xhr") {
|
|
221
|
+
handleXHRResponse(xhr, requestId, cleanUrl, method, requestHeaders, requestData, params, startTime, isError = false, clientType = "xhr", override) {
|
|
203
222
|
const duration = startTime ? Date.now() - startTime : 0;
|
|
204
223
|
|
|
205
224
|
// XHR's duration was ALREADY total (measured at readyState 4 / load), so
|
|
@@ -228,7 +247,8 @@ class NetworkListener {
|
|
|
228
247
|
},
|
|
229
248
|
error: {
|
|
230
249
|
message: isError ? "Request failed" : "Network error or request aborted"
|
|
231
|
-
}
|
|
250
|
+
},
|
|
251
|
+
override
|
|
232
252
|
});
|
|
233
253
|
return;
|
|
234
254
|
}
|
|
@@ -367,7 +387,8 @@ class NetworkListener {
|
|
|
367
387
|
headers: responseHeaders,
|
|
368
388
|
body,
|
|
369
389
|
size: responseSize
|
|
370
|
-
}
|
|
390
|
+
},
|
|
391
|
+
override
|
|
371
392
|
});
|
|
372
393
|
} else {
|
|
373
394
|
this.emit({
|
|
@@ -392,7 +413,8 @@ class NetworkListener {
|
|
|
392
413
|
},
|
|
393
414
|
error: {
|
|
394
415
|
message: `HTTP ${xhr.status}: ${xhr.statusText}`
|
|
395
|
-
}
|
|
416
|
+
},
|
|
417
|
+
override
|
|
396
418
|
});
|
|
397
419
|
}
|
|
398
420
|
}
|
|
@@ -463,6 +485,18 @@ class NetworkListener {
|
|
|
463
485
|
// Determine client type from X-Request-Client header or default to fetch
|
|
464
486
|
const clientType = requestHeaders["X-Request-Client"] || requestHeaders["x-request-client"] || "fetch";
|
|
465
487
|
|
|
488
|
+
/**
|
|
489
|
+
* Response overrides, fetch side.
|
|
490
|
+
*
|
|
491
|
+
* Needed even though `send` below also resolves them: Expo installs a
|
|
492
|
+
* NATIVE `expo/fetch` as `globalThis.fetch`, which never constructs an
|
|
493
|
+
* XMLHttpRequest, so the XHR hook alone would never see these requests.
|
|
494
|
+
* Skipped when `canSynthesizeResponse()` is false so a runtime without a
|
|
495
|
+
* `Response` global degrades to a real request rather than a broken one.
|
|
496
|
+
*/
|
|
497
|
+
const outcome = canSynthesizeResponse() ? overrideForRequest(url, method) : null;
|
|
498
|
+
const override = outcome ? markForOutcome(outcome) : undefined;
|
|
499
|
+
|
|
466
500
|
// Emit request event
|
|
467
501
|
self.emit({
|
|
468
502
|
type: "request",
|
|
@@ -475,10 +509,28 @@ class NetworkListener {
|
|
|
475
509
|
data: requestData,
|
|
476
510
|
params: params || undefined,
|
|
477
511
|
client: clientType
|
|
478
|
-
}
|
|
512
|
+
},
|
|
513
|
+
override
|
|
479
514
|
});
|
|
480
515
|
try {
|
|
481
|
-
|
|
516
|
+
if (outcome) await sleep(outcome.delayMs);
|
|
517
|
+
if (outcome?.kind === "fail") throw synthesizeFetchError(outcome);
|
|
518
|
+
let response;
|
|
519
|
+
if (outcome?.kind === "respond") {
|
|
520
|
+
response = synthesizeResponse(url, outcome);
|
|
521
|
+
} else {
|
|
522
|
+
// The flag spans the CALL only — see `insideOverriddenFetch`. It has
|
|
523
|
+
// to be cleared before the await, or it would still be raised for
|
|
524
|
+
// every unrelated request that starts while this one is in flight.
|
|
525
|
+
insideOverriddenFetch = outcome != null;
|
|
526
|
+
let pending;
|
|
527
|
+
try {
|
|
528
|
+
pending = self.originalFetch(input, init);
|
|
529
|
+
} finally {
|
|
530
|
+
insideOverriddenFetch = false;
|
|
531
|
+
}
|
|
532
|
+
response = await pending;
|
|
533
|
+
}
|
|
482
534
|
// A fetch promise resolves when the HEADERS arrive, not the body — so
|
|
483
535
|
// this is time-to-first-byte, and it is where `duration` used to stop.
|
|
484
536
|
// That silently excluded download time, making an identical request
|
|
@@ -547,7 +599,8 @@ class NetworkListener {
|
|
|
547
599
|
headers: responseHeaders,
|
|
548
600
|
body,
|
|
549
601
|
size: responseSize
|
|
550
|
-
}
|
|
602
|
+
},
|
|
603
|
+
override
|
|
551
604
|
});
|
|
552
605
|
return response;
|
|
553
606
|
} catch (error) {
|
|
@@ -570,7 +623,8 @@ class NetworkListener {
|
|
|
570
623
|
error: {
|
|
571
624
|
message: error instanceof Error ? error.message : "Network error",
|
|
572
625
|
stack: error instanceof Error ? error.stack : undefined
|
|
573
|
-
}
|
|
626
|
+
},
|
|
627
|
+
override
|
|
574
628
|
});
|
|
575
629
|
throw error;
|
|
576
630
|
}
|
|
@@ -637,6 +691,22 @@ class NetworkListener {
|
|
|
637
691
|
const looksLikeAxios = acceptHeader.includes("application/json") && acceptHeader.includes("text/plain") && acceptHeader.includes("*/*");
|
|
638
692
|
const clientType = explicitClient || (looksLikeAxios ? "axios" : "xhr");
|
|
639
693
|
|
|
694
|
+
/**
|
|
695
|
+
* Response overrides.
|
|
696
|
+
*
|
|
697
|
+
* This is the layer that covers `axios` and raw XMLHttpRequest. It also
|
|
698
|
+
* covers `fetch` on BARE React Native, where the whatwg-fetch polyfill
|
|
699
|
+
* runs through XHR — which is why the fetch wrapper raises
|
|
700
|
+
* `insideOverriddenFetch` around its pass-through, so one request can't
|
|
701
|
+
* be overridden twice.
|
|
702
|
+
*
|
|
703
|
+
* Matching uses the raw `url` (query string included), which is what the
|
|
704
|
+
* developer sees in the list and writes into a rule. No-ops in production
|
|
705
|
+
* builds; see `overrideForRequest`.
|
|
706
|
+
*/
|
|
707
|
+
const outcome = insideOverriddenFetch ? null : overrideForRequest(url, method);
|
|
708
|
+
const override = outcome ? markForOutcome(outcome) : undefined;
|
|
709
|
+
|
|
640
710
|
// Emit request event
|
|
641
711
|
self.emit({
|
|
642
712
|
type: "request",
|
|
@@ -649,7 +719,8 @@ class NetworkListener {
|
|
|
649
719
|
data: requestData,
|
|
650
720
|
params: params || undefined,
|
|
651
721
|
client: clientType
|
|
652
|
-
}
|
|
722
|
+
},
|
|
723
|
+
override
|
|
653
724
|
});
|
|
654
725
|
|
|
655
726
|
// Track if we've already processed this request to avoid duplicate events
|
|
@@ -657,7 +728,7 @@ class NetworkListener {
|
|
|
657
728
|
const processResponse = (isError = false) => {
|
|
658
729
|
if (processed) return;
|
|
659
730
|
processed = true;
|
|
660
|
-
self.handleXHRResponse(this, requestId || "unknown", cleanUrl, method, requestHeaders, requestData, params, startTime || 0, isError, clientType);
|
|
731
|
+
self.handleXHRResponse(this, requestId || "unknown", cleanUrl, method, requestHeaders, requestData, params, startTime || 0, isError, clientType, override);
|
|
661
732
|
// Clean up event listeners after processing to prevent memory leaks
|
|
662
733
|
cleanup();
|
|
663
734
|
};
|
|
@@ -704,6 +775,16 @@ class NetworkListener {
|
|
|
704
775
|
this.addEventListener("error", errorListener);
|
|
705
776
|
this.addEventListener("abort", abortListener);
|
|
706
777
|
this.addEventListener("readystatechange", readyStateListener);
|
|
778
|
+
if (outcome) {
|
|
779
|
+
// Listeners are attached above FIRST: `runOverride` drives the same
|
|
780
|
+
// readyState transitions a real response would, and the interceptor has
|
|
781
|
+
// to see them (readyState 2 is where TTFB is stamped).
|
|
782
|
+
const handled = runOverride(this, url, outcome, () => self.originalXHRSend.call(this, data));
|
|
783
|
+
// `handled` is false only where this isn't React Native's own
|
|
784
|
+
// XMLHttpRequest and can't be driven by hand. Fall through and send for
|
|
785
|
+
// real rather than dropping the request on the floor.
|
|
786
|
+
if (handled) return;
|
|
787
|
+
}
|
|
707
788
|
return self.originalXHRSend.call(this, data);
|
|
708
789
|
};
|
|
709
790
|
this.isListening = true;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which request the detail view is showing, and where it sits in the list —
|
|
5
|
+
* the prev/next CURSOR.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this is a store and not `useState` in the modal
|
|
8
|
+
*
|
|
9
|
+
* It used to be `useState` in `NetworkModalInner`. Every tap on Previous/Next
|
|
10
|
+
* therefore dirtied the modal ROOT, which re-rendered `JsModal` (1833 lines,
|
|
11
|
+
* nine useStates, animations/gestures/persistence — and it can never bail out,
|
|
12
|
+
* because its `header` prop is a fresh object literal every render), the whole
|
|
13
|
+
* header, and all five screens. None of them care which request is selected;
|
|
14
|
+
* they only care THAT one is.
|
|
15
|
+
*
|
|
16
|
+
* This is the same argument `NetworkModal`'s own header comment already makes
|
|
17
|
+
* about network events ("with the subscription here, 15 events re-rendered
|
|
18
|
+
* JsModal + every header button 15×"). Stepping is that same class of
|
|
19
|
+
* high-frequency churn, and it was sitting in the same wrong place. Now the
|
|
20
|
+
* root subscribes to `hasSelection` — a boolean that flips twice a session —
|
|
21
|
+
* while the footer, the body and the header's pin actions subscribe to the
|
|
22
|
+
* parts they actually render.
|
|
23
|
+
*
|
|
24
|
+
* ## The three cursors
|
|
25
|
+
*
|
|
26
|
+
* `selectedId` is where the USER is. It moves on the tap, synchronously.
|
|
27
|
+
*
|
|
28
|
+
* `bodyId` is what the detail body is allowed to render, and it deliberately
|
|
29
|
+
* LAGS: drawing a request means handing its payload to the virtualized data
|
|
30
|
+
* viewer to flatten, and that has no business blocking the next tap. It
|
|
31
|
+
* catches up only once the selection has held still for `settleMs`, so a
|
|
32
|
+
* burst of taps renders the request you land on and none of the ones you flew
|
|
33
|
+
* past. (`useDeferredValue` was tried here first and is the wrong tool — it
|
|
34
|
+
* lowers the priority of that work without preventing it, so every settled
|
|
35
|
+
* step still queued a flatten and each one delayed the touch behind it.)
|
|
36
|
+
*
|
|
37
|
+
* `renderedId` is what actually made it to the screen, reported by the detail
|
|
38
|
+
* screen after commit. Nothing in the product reads it; it exists so the
|
|
39
|
+
* benchmark harness can measure "time until request N was really painted"
|
|
40
|
+
* separately from "time until the cursor reached N".
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* How long the selection must hold still before the body renders it. Short
|
|
45
|
+
* enough that a single step reads as immediate (the footer and header have
|
|
46
|
+
* already moved), long enough that a burst collapses to one render.
|
|
47
|
+
*/
|
|
48
|
+
export const DEFAULT_SETTLE_MS = 120;
|
|
49
|
+
class NetworkSelectionStore {
|
|
50
|
+
ids = [];
|
|
51
|
+
index = -1;
|
|
52
|
+
selectedId = null;
|
|
53
|
+
bodyId = null;
|
|
54
|
+
renderedId = null;
|
|
55
|
+
settleMs = DEFAULT_SETTLE_MS;
|
|
56
|
+
settleTimer = null;
|
|
57
|
+
subscribers = new Set();
|
|
58
|
+
snapshot = this.buildSnapshot();
|
|
59
|
+
buildSnapshot() {
|
|
60
|
+
return {
|
|
61
|
+
ids: this.ids,
|
|
62
|
+
index: this.index,
|
|
63
|
+
selectedId: this.selectedId,
|
|
64
|
+
bodyId: this.bodyId,
|
|
65
|
+
renderedId: this.renderedId
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* One immutable snapshot per mutation, so subscribers can identity-compare
|
|
71
|
+
* and `useSyncExternalStore` stays happy.
|
|
72
|
+
*/
|
|
73
|
+
emit() {
|
|
74
|
+
this.snapshot = this.buildSnapshot();
|
|
75
|
+
for (const subscriber of this.subscribers) subscriber(this.snapshot);
|
|
76
|
+
}
|
|
77
|
+
getState() {
|
|
78
|
+
return this.snapshot;
|
|
79
|
+
}
|
|
80
|
+
subscribe(subscriber) {
|
|
81
|
+
this.subscribers.add(subscriber);
|
|
82
|
+
return () => {
|
|
83
|
+
this.subscribers.delete(subscriber);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Publish the navigable order. Called by the stepper whenever its derived
|
|
89
|
+
* list changes (filter, search, pins, new requests arriving).
|
|
90
|
+
*
|
|
91
|
+
* The cursor is re-anchored BY ID, not by position: the live list is
|
|
92
|
+
* newest-first and new requests are prepended, so holding an index would
|
|
93
|
+
* silently slide the selection onto a different request every time one
|
|
94
|
+
* landed.
|
|
95
|
+
*/
|
|
96
|
+
setIds(ids) {
|
|
97
|
+
const sameIds = ids.length === this.ids.length && ids.every((id, i) => id === this.ids[i]);
|
|
98
|
+
if (sameIds) return;
|
|
99
|
+
this.ids = ids;
|
|
100
|
+
this.index = this.selectedId == null ? -1 : ids.indexOf(this.selectedId);
|
|
101
|
+
this.emit();
|
|
102
|
+
}
|
|
103
|
+
select(id) {
|
|
104
|
+
if (id === this.selectedId) return;
|
|
105
|
+
this.selectedId = id;
|
|
106
|
+
this.index = id == null ? -1 : this.ids.indexOf(id);
|
|
107
|
+
this.scheduleBody(id);
|
|
108
|
+
this.emit();
|
|
109
|
+
}
|
|
110
|
+
clear() {
|
|
111
|
+
this.select(null);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Move the cursor. O(1) — the whole reason `index` is stored rather than
|
|
116
|
+
* re-derived with a `findIndex` on every render.
|
|
117
|
+
*
|
|
118
|
+
* Returns the id it landed on, or null if the move was out of bounds (the
|
|
119
|
+
* footer disables its arrows at the ends, but the benchmark driver calls
|
|
120
|
+
* this directly and needs to know when it has run off the end).
|
|
121
|
+
*/
|
|
122
|
+
step(delta) {
|
|
123
|
+
if (this.index < 0) return null;
|
|
124
|
+
const next = this.index + delta;
|
|
125
|
+
if (next < 0 || next >= this.ids.length) return null;
|
|
126
|
+
const id = this.ids[next];
|
|
127
|
+
this.selectedId = id;
|
|
128
|
+
this.index = next;
|
|
129
|
+
this.scheduleBody(id);
|
|
130
|
+
this.emit();
|
|
131
|
+
return id;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Opening and closing skip the settle delay: there is no in-flight render to
|
|
136
|
+
* protect on a null↔id transition, and waiting there just reads as a hang.
|
|
137
|
+
* Only id → id steps debounce.
|
|
138
|
+
*/
|
|
139
|
+
scheduleBody(id) {
|
|
140
|
+
if (this.settleTimer != null) {
|
|
141
|
+
clearTimeout(this.settleTimer);
|
|
142
|
+
this.settleTimer = null;
|
|
143
|
+
}
|
|
144
|
+
if (id == null || this.bodyId == null || this.settleMs <= 0) {
|
|
145
|
+
this.bodyId = id;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.settleTimer = setTimeout(() => {
|
|
149
|
+
this.settleTimer = null;
|
|
150
|
+
if (this.bodyId === this.selectedId) return;
|
|
151
|
+
this.bodyId = this.selectedId;
|
|
152
|
+
this.emit();
|
|
153
|
+
}, this.settleMs);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Benchmark toggle — `?settle=0` measures the gate's contribution. */
|
|
157
|
+
setSettleMs(ms) {
|
|
158
|
+
this.settleMs = Math.max(0, ms);
|
|
159
|
+
}
|
|
160
|
+
getSettleMs() {
|
|
161
|
+
return this.settleMs;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Reported by the detail screen after commit. Instrumentation only. */
|
|
165
|
+
reportRendered(id) {
|
|
166
|
+
if (id === this.renderedId) return;
|
|
167
|
+
this.renderedId = id;
|
|
168
|
+
this.emit();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Test/bench helper — drop all cursor state without touching the events. */
|
|
172
|
+
reset() {
|
|
173
|
+
if (this.settleTimer != null) clearTimeout(this.settleTimer);
|
|
174
|
+
this.settleTimer = null;
|
|
175
|
+
this.ids = [];
|
|
176
|
+
this.index = -1;
|
|
177
|
+
this.selectedId = null;
|
|
178
|
+
this.bodyId = null;
|
|
179
|
+
this.renderedId = null;
|
|
180
|
+
this.emit();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export const networkSelectionStore = new NetworkSelectionStore();
|
|
184
|
+
|
|
185
|
+
/** Convenience for publishing an event list as the navigable order. */
|
|
186
|
+
export function toSelectionIds(events) {
|
|
187
|
+
return events.map(event => event.id);
|
|
188
|
+
}
|
package/lib/module/preset.js
CHANGED
|
@@ -19,6 +19,19 @@
|
|
|
19
19
|
|
|
20
20
|
import { NetworkIcon } from "@buoy-gg/shared-ui";
|
|
21
21
|
import { NetworkModal } from "./network/components/NetworkModal";
|
|
22
|
+
import { overrideRulesStore } from "./network/overrides/overrideRulesStore";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load persisted response-override rules as soon as the tool is registered.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately at import time rather than when the Network modal mounts: an
|
|
28
|
+
* override has to fire from app boot, with the tool closed and nothing
|
|
29
|
+
* capturing — that is the whole point of persisting rules ("force this endpoint
|
|
30
|
+
* to 500, then restart and watch the boot path"). Hydrating also re-installs
|
|
31
|
+
* the interceptor if any rule is still active. One storage read, dev-only.
|
|
32
|
+
*/
|
|
33
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
34
|
+
void overrideRulesStore.ensureLoaded();
|
|
22
35
|
|
|
23
36
|
/**
|
|
24
37
|
* Pre-configured network monitoring tool for FloatingDevTools.
|
|
@@ -28,7 +41,6 @@ import { NetworkModal } from "./network/components/NetworkModal";
|
|
|
28
41
|
* - Filter by status, method, URL
|
|
29
42
|
* - Timing information
|
|
30
43
|
*/
|
|
31
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
32
44
|
export const networkToolPreset = {
|
|
33
45
|
id: "network",
|
|
34
46
|
name: "NET",
|
|
@@ -20,9 +20,18 @@ export { useLiveNetworkEvent } from "./network/hooks/useLiveNetworkEvent";
|
|
|
20
20
|
export { useNetworkBadgeCounts } from "./network/hooks/useNetworkBadgeCounts";
|
|
21
21
|
export { useNetworkSaved, type UseNetworkSavedResult, } from "./network/hooks/useNetworkSaved";
|
|
22
22
|
export { splitPinned, type PinnedSplit } from "./network/utils/withPinnedFirst";
|
|
23
|
+
export { networkSelectionStore, DEFAULT_SETTLE_MS, type NetworkSelectionState, } from "./network/utils/networkSelectionStore";
|
|
23
24
|
export { TickProvider, useTickEveryMinute } from "./network/hooks/useTickEveryMinute";
|
|
24
25
|
export { formatBytes, formatDuration, formatHttpStatus, } from "./network/utils/formatting";
|
|
25
26
|
export type { NetworkEvent, NetworkStats, NetworkFilter, NetworkEventStatus, NetworkInsight, IgnoredPattern, IgnoredPatternMatchMode, } from "./network/types";
|
|
27
|
+
export { overrideRulesStore, type OverrideRulesState, type OverrideRuleDraft,
|
|
28
|
+
/**
|
|
29
|
+
* @internal — the desktop dashboard puts the rule store in remote-mirror mode
|
|
30
|
+
* so edits made there are forwarded to the device instead of stored locally.
|
|
31
|
+
*/
|
|
32
|
+
type OverrideRulesRemote, } from "./network/overrides/overrideRulesStore";
|
|
33
|
+
export { draftFromEvent } from "./network/overrides/presets";
|
|
34
|
+
export { MAX_RULES as MAX_OVERRIDE_RULES, FREE_TIER_RULE_LIMIT as FREE_TIER_OVERRIDE_LIMIT, STATUS_MIN as OVERRIDE_STATUS_MIN, STATUS_MAX as OVERRIDE_STATUS_MAX, type OverrideRule, type OverrideRuleKind, type OverrideFailKind, type NetworkOverrideMark, } from "./network/overrides/types";
|
|
26
35
|
export { useIgnoredPatterns, ignoredPatternsStore, urlMatchesIgnoredPattern, isUrlIgnored, } from "@buoy-gg/shared-ui";
|
|
27
36
|
export { networkSyncAdapter } from "./network/sync/networkSyncAdapter";
|
|
28
37
|
export { NetworkBodyResolverProvider, type NetworkBodyResolver, type ResolvedNetworkBody, } from "./network/sync/networkBodyResolver";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAOhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAClF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAOhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAClF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAIhF,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,qBAAqB,GAC3B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAKtF,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAKpC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB;AACtB;;;GAGG;AACH,KAAK,mBAAmB,GACzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,SAAS,IAAI,kBAAkB,EAC/B,oBAAoB,IAAI,wBAAwB,EAChD,UAAU,IAAI,mBAAmB,EACjC,UAAU,IAAI,mBAAmB,EACjC,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AAQnC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAO5C,gBAAgB;AAChB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE;;;GAGG;AACH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC"}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* It is its OWN leaf (rule3) because it is the only part of the detail screen
|
|
6
6
|
* that needs the event LIST. Subscribing here keeps the request stream out of
|
|
7
7
|
* the detail body's render path, which is the expensive subtree (headers,
|
|
8
|
-
* bodies, JSON viewers
|
|
9
|
-
*
|
|
8
|
+
* bodies, JSON viewers): a request arriving re-renders this ~30-node footer
|
|
9
|
+
* and nothing else.
|
|
10
10
|
*
|
|
11
11
|
* The order it steps through is EXACTLY what the list screen renders —
|
|
12
12
|
* `[...pinned, ...rest]` off the same `useNetworkEventList` + `splitPinned`
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
* "request N of <what the list is showing>", never "N of everything ever
|
|
17
17
|
* captured". Detail opened from the Saved screen steps through the SAVED
|
|
18
18
|
* list instead (with its own search applied), for the same reason.
|
|
19
|
+
*
|
|
20
|
+
* This component OWNS the navigable order but not the cursor: it publishes
|
|
21
|
+
* the ordered ids into `networkSelectionStore` and reads the cursor back.
|
|
22
|
+
* That split is what makes stepping O(1) (the store keeps the index, so
|
|
23
|
+
* there is no `findIndex` per render) and what lets the benchmark harness
|
|
24
|
+
* drive `step(+1)` directly without going through the UI.
|
|
19
25
|
*/
|
|
20
26
|
import { type IgnoredPattern } from "@buoy-gg/shared-ui";
|
|
21
27
|
import type { NetworkFilter } from "../types";
|
|
22
28
|
interface NetworkDetailStepperProps {
|
|
23
|
-
/** The request currently on screen. */
|
|
24
|
-
eventId: string;
|
|
25
29
|
paused: boolean;
|
|
26
30
|
filter: NetworkFilter;
|
|
27
31
|
ignoredPatterns: readonly IgnoredPattern[];
|
|
@@ -30,7 +34,6 @@ interface NetworkDetailStepperProps {
|
|
|
30
34
|
fromSaved: boolean;
|
|
31
35
|
/** The Saved screen's search box (lifted to the modal root so both agree). */
|
|
32
36
|
savedSearch: string;
|
|
33
|
-
onSelect: (eventId: string) => void;
|
|
34
37
|
/** Only a bottom sheet sits on the home indicator — see NetworkModal. */
|
|
35
38
|
applySafeAreaInset: boolean;
|
|
36
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkDetailStepper.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkDetailStepper.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"NetworkDetailStepper.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkDetailStepper.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,UAAU,CAAC;AAW5D,UAAU,yBAAyB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAMD,eAAO,MAAM,oBAAoB,iEAiE/B,CAAC"}
|
|
@@ -3,11 +3,46 @@ interface NetworkEventDetailViewProps {
|
|
|
3
3
|
event: NetworkEvent;
|
|
4
4
|
ignoredPatterns?: Set<string>;
|
|
5
5
|
onTogglePattern?: (pattern: string) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Mount the request/response BODY trees.
|
|
8
|
+
*
|
|
9
|
+
* False while the prev/next cursor is still moving. The body trees are ~93%
|
|
10
|
+
* of a step's cost (measured: 647ms/step with them, 43ms without, at a 150ms
|
|
11
|
+
* tap interval over 64KB payloads), and they are the only part of this view
|
|
12
|
+
* that can't keep up with a thumb. Everything else renders on every step.
|
|
13
|
+
*
|
|
14
|
+
* Note the mechanism: this doesn't skip the tree so much as ensure it gets
|
|
15
|
+
* unmounted by the next tap before its chunked flatten completes. Stepping
|
|
16
|
+
* therefore costs ~14ms/step instead of ~500ms, and exactly one tree
|
|
17
|
+
* flattens for real — the one you stop on.
|
|
18
|
+
*
|
|
19
|
+
* Defaults true so the Events tool — which renders this same view without a
|
|
20
|
+
* cursor — is unaffected.
|
|
21
|
+
*/
|
|
22
|
+
showBodies?: boolean;
|
|
6
23
|
}
|
|
7
24
|
/**
|
|
8
25
|
* Detailed inspector rendering request/response metadata for a single network event. Designed to
|
|
9
26
|
* mimic Sentry’s layout with collapsible sections, diff views, and quick copy affordances.
|
|
27
|
+
*
|
|
28
|
+
* MEMOIZED with profiling evidence, not by reflex. Two render paths hit this
|
|
29
|
+
* subtree with unchanged props:
|
|
30
|
+
*
|
|
31
|
+
* 1. `useLiveNetworkEvent` re-subscribes when the selected ID changes, and
|
|
32
|
+
* `subscribeToEvents` replays immediately — so every step through the
|
|
33
|
+
* prev/next footer committed the SAME event object twice, rendering this
|
|
34
|
+
* whole tree (expanded DataViewer included) a second time for nothing.
|
|
35
|
+
* 2. Any modal-root state change (header search, mode toggle) re-renders the
|
|
36
|
+
* shell, and the detail body has nothing to do with it.
|
|
37
|
+
*
|
|
38
|
+
* Both props besides `event` are stable by construction — `values` is memoized
|
|
39
|
+
* in `useIgnoredPatterns` and `toggle` is module-scope there — so the bail-out
|
|
40
|
+
* actually fires.
|
|
41
|
+
*
|
|
42
|
+
* Measured baseline (js-top, example-dev-build, 5 steps over 109–324KB
|
|
43
|
+
* pokeapi responses): 307ms of React work across 5 steps ≈ 61ms of JS per
|
|
44
|
+
* step. Case 1 alone is half of that, since it re-ran the whole body render.
|
|
10
45
|
*/
|
|
11
|
-
export declare
|
|
46
|
+
export declare const NetworkEventDetailView: import("react").NamedExoticComponent<NetworkEventDetailViewProps>;
|
|
12
47
|
export {};
|
|
13
48
|
//# sourceMappingURL=NetworkEventDetailView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkEventDetailView.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventDetailView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NetworkEventDetailView.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventDetailView.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,UAAU,CAAC;AA0B7D,UAAU,2BAA2B;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA+QD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,sBAAsB,mEA6gBjC,CAAC"}
|
|
@@ -14,6 +14,12 @@ interface NetworkEventItemCompactProps {
|
|
|
14
14
|
pinned?: boolean;
|
|
15
15
|
saved?: boolean;
|
|
16
16
|
onLongPress?: (event: NetworkEvent) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Rendered in the OVERRIDDEN strip. Distinct from `event.override`, which
|
|
19
|
+
* records that a PAST request was overridden — this says a rule covers the
|
|
20
|
+
* row right now, which is true even for a request captured before the rule.
|
|
21
|
+
*/
|
|
22
|
+
overridden?: boolean;
|
|
17
23
|
}
|
|
18
24
|
export declare const NetworkEventItemCompact: import("react").NamedExoticComponent<NetworkEventItemCompactProps>;
|
|
19
25
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkEventItemCompact.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventItemCompact.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NetworkEventItemCompact.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventItemCompact.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAM7C,UAAU,4BAA4B;IACpC,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkJD,eAAO,MAAM,uBAAuB,oEA4HnC,CAAC"}
|
|
@@ -43,7 +43,6 @@ interface NetworkSavedButtonProps {
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function NetworkSavedButton({ onPress }: NetworkSavedButtonProps): import("react").JSX.Element;
|
|
45
45
|
interface NetworkDetailPinActionsProps {
|
|
46
|
-
eventId: string | null;
|
|
47
46
|
isPro: boolean;
|
|
48
47
|
onUpgradePress: () => void;
|
|
49
48
|
}
|
|
@@ -60,8 +59,12 @@ interface NetworkDetailPinActionsProps {
|
|
|
60
59
|
* The event object is resolved at PRESS time rather than held as a prop: the
|
|
61
60
|
* live store has the fresh copy while the request is in flight, and the saved
|
|
62
61
|
* store is the only copy once it has been cleared or aged out.
|
|
62
|
+
*
|
|
63
|
+
* The selected id is SUBSCRIBED here rather than passed down, so stepping
|
|
64
|
+
* through requests doesn't have to re-render the modal root just to rebuild
|
|
65
|
+
* the header around a new prop.
|
|
63
66
|
*/
|
|
64
|
-
export declare function NetworkDetailPinActions({
|
|
67
|
+
export declare function NetworkDetailPinActions({ isPro, onUpgradePress, }: NetworkDetailPinActionsProps): import("react").JSX.Element;
|
|
65
68
|
interface NetworkHeaderCopyButtonProps {
|
|
66
69
|
isPro: boolean;
|
|
67
70
|
filter: NetworkFilter;
|
|
@@ -69,12 +72,19 @@ interface NetworkHeaderCopyButtonProps {
|
|
|
69
72
|
copySettings: CopySettings;
|
|
70
73
|
paused: boolean;
|
|
71
74
|
onUpgradePress: () => void;
|
|
75
|
+
/**
|
|
76
|
+
* Render as a labelled row instead of a bare glyph, for the header's
|
|
77
|
+
* overflow menu. The behaviour is identical — this button owns the lazy copy
|
|
78
|
+
* value, the Pro gate and the copied/failed feedback, and a menu row that
|
|
79
|
+
* reimplemented them would be a second copy path to keep in step.
|
|
80
|
+
*/
|
|
81
|
+
label?: string;
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
84
|
* Copy-all-requests button. Subscribes to a flip-guarded boolean (its
|
|
75
85
|
* enabled state); the copy text is generated at PRESS time from a live
|
|
76
86
|
* store read, so there's no per-event work and no stale closure.
|
|
77
87
|
*/
|
|
78
|
-
export declare function NetworkHeaderCopyButton({ isPro, filter, ignoredPatterns, copySettings, paused, onUpgradePress, }: NetworkHeaderCopyButtonProps): import("react").JSX.Element;
|
|
88
|
+
export declare function NetworkHeaderCopyButton({ isPro, filter, ignoredPatterns, copySettings, paused, onUpgradePress, label, }: NetworkHeaderCopyButtonProps): import("react").JSX.Element;
|
|
79
89
|
export {};
|
|
80
90
|
//# sourceMappingURL=NetworkHeaderLive.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkHeaderLive.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkHeaderLive.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAUL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"NetworkHeaderLive.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkHeaderLive.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAUL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAsB9D,KAAK,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpD,UAAU,wBAAwB;IAChC,YAAY,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,EAClC,YAAY,EACZ,cAAc,EACd,eAAe,EACf,MAAM,GACP,EAAE,wBAAwB,+BA4D1B;AAED,UAAU,uBAAuB;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;iDAEiD;AACjD,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,uBAAuB,+BAqBrE;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE,uBAAuB,+BAwBtE;AAED,UAAU,4BAA4B;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,cAAc,GACf,EAAE,4BAA4B,+BAuF9B;AAED,UAAU,4BAA4B;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,MAAM,EACN,eAAe,EACf,YAAY,EACZ,MAAM,EACN,cAAc,EACd,KAAK,GACN,EAAE,4BAA4B,+BAsD9B"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The header's overflow menu.
|
|
4
|
+
*
|
|
5
|
+
* The toolbar had grown to three status badges and six icon buttons, which on a
|
|
6
|
+
* 402pt screen meant the pending badge was being overlapped by the search
|
|
7
|
+
* button. Something had to leave, and the rule for deciding what is:
|
|
8
|
+
*
|
|
9
|
+
* **A control that COMMUNICATES STATE stays visible. A destination or a rare
|
|
10
|
+
* action goes in the menu.**
|
|
11
|
+
*
|
|
12
|
+
* That keeps the power toggle (are we capturing?), the overrides flask (is the
|
|
13
|
+
* app being lied to, and how many rules?), the status badges (what happened)
|
|
14
|
+
* and Clear (the reproduce loop is clear → repro → read, and burying the first
|
|
15
|
+
* step doubles it). Search stays because it's the only way through 500 rows.
|
|
16
|
+
*
|
|
17
|
+
* Filters, Saved and Copy leave. Filters is the interesting one — it does show
|
|
18
|
+
* state, so the ⋯ carries a dot when a filter is applied rather than hiding
|
|
19
|
+
* that fact.
|
|
20
|
+
*/
|
|
21
|
+
export declare function NetworkHeaderMenuButton({ onPress, isOpen, hasIndicator, }: {
|
|
22
|
+
onPress: () => void;
|
|
23
|
+
/** The menu is showing — the button holds a lit state and closes on tap. */
|
|
24
|
+
isOpen?: boolean;
|
|
25
|
+
/** Something behind the menu is active — currently a filter. */
|
|
26
|
+
hasIndicator?: boolean;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
export interface NetworkHeaderMenuProps {
|
|
29
|
+
onClose: () => void;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The dropdown itself, rendered in the modal BODY rather than the header —
|
|
34
|
+
* the header is a fixed-height row and anything overflowing it is clipped, so
|
|
35
|
+
* a panel anchored to the top-right of the body lands exactly where a dropdown
|
|
36
|
+
* from the ⋯ should appear.
|
|
37
|
+
*/
|
|
38
|
+
export declare function NetworkHeaderMenu({ onClose, children }: NetworkHeaderMenuProps): import("react").JSX.Element;
|
|
39
|
+
export declare function NetworkHeaderMenuItem({ icon, label, detail, onPress, }: {
|
|
40
|
+
icon: ReactNode;
|
|
41
|
+
label: string;
|
|
42
|
+
/** Right-aligned state, e.g. the active filter count. */
|
|
43
|
+
detail?: string;
|
|
44
|
+
onPress: () => void;
|
|
45
|
+
}): import("react").JSX.Element;
|
|
46
|
+
//# sourceMappingURL=NetworkHeaderMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NetworkHeaderMenu.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkHeaderMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC;;;;;;;;;;;;;;;;;;GAkBG;AAEH,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,MAAM,EACN,YAAY,GACb,EAAE;IACD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAkBA;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,sBAAsB,+BAY9E;AAED,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,KAAK,EACL,MAAM,EACN,OAAO,GACR,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BAYA"}
|