@buoy-gg/network 7.0.10 → 7.0.11
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/network/components/NetworkDetailStepper.js +92 -0
- package/lib/commonjs/network/utils/selectSavedEvents.js +34 -0
- package/lib/module/network/components/NetworkDetailStepper.js +88 -0
- package/lib/module/network/utils/selectSavedEvents.js +30 -0
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts +37 -0
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +1 -0
- package/lib/typescript/network/utils/selectSavedEvents.d.ts +19 -0
- package/lib/typescript/network/utils/selectSavedEvents.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NetworkDetailStepper = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
+
var _useNetworkEventList = require("../hooks/useNetworkEventList");
|
|
10
|
+
var _useNetworkSaved = require("../hooks/useNetworkSaved");
|
|
11
|
+
var _withPinnedFirst = require("../utils/withPinnedFirst");
|
|
12
|
+
var _selectSavedEvents = require("../utils/selectSavedEvents");
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
/**
|
|
15
|
+
* Previous/Next footer for the request DETAIL screen — step through requests
|
|
16
|
+
* without bouncing back to the list.
|
|
17
|
+
*
|
|
18
|
+
* It is its OWN leaf (rule3) because it is the only part of the detail screen
|
|
19
|
+
* that needs the event LIST. Subscribing here keeps the request stream out of
|
|
20
|
+
* the detail body's render path, which is the expensive subtree (headers,
|
|
21
|
+
* bodies, JSON viewers, diff views): a request arriving re-renders this
|
|
22
|
+
* ~30-node footer and nothing else.
|
|
23
|
+
*
|
|
24
|
+
* The order it steps through is EXACTLY what the list screen renders —
|
|
25
|
+
* `[...pinned, ...rest]` off the same `useNetworkEventList` + `splitPinned`
|
|
26
|
+
* derivation — so "Next" always lands on the row below the one the user
|
|
27
|
+
* tapped. Because the live filter is an input, typing in the search box or
|
|
28
|
+
* flipping a status chip re-scopes the stepper in place; the counter is
|
|
29
|
+
* "request N of <what the list is showing>", never "N of everything ever
|
|
30
|
+
* captured". Detail opened from the Saved screen steps through the SAVED
|
|
31
|
+
* list instead (with its own search applied), for the same reason.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const NetworkDetailStepper = exports.NetworkDetailStepper = /*#__PURE__*/(0, _react.memo)(function NetworkDetailStepper({
|
|
35
|
+
eventId,
|
|
36
|
+
paused,
|
|
37
|
+
filter,
|
|
38
|
+
ignoredPatterns,
|
|
39
|
+
isPro,
|
|
40
|
+
fromSaved,
|
|
41
|
+
savedSearch,
|
|
42
|
+
onSelect
|
|
43
|
+
}) {
|
|
44
|
+
const {
|
|
45
|
+
visibleEvents
|
|
46
|
+
} = (0, _useNetworkEventList.useNetworkEventList)({
|
|
47
|
+
filter,
|
|
48
|
+
ignoredPatterns,
|
|
49
|
+
paused,
|
|
50
|
+
isPro
|
|
51
|
+
});
|
|
52
|
+
const {
|
|
53
|
+
pinnedEvents,
|
|
54
|
+
pinnedLiveIds,
|
|
55
|
+
savedRecords
|
|
56
|
+
} = (0, _useNetworkSaved.useNetworkSaved)();
|
|
57
|
+
const ordered = (0, _react.useMemo)(() => {
|
|
58
|
+
if (fromSaved) {
|
|
59
|
+
return (0, _selectSavedEvents.selectSavedEvents)(savedRecords, isPro, savedSearch).events;
|
|
60
|
+
}
|
|
61
|
+
// Same split the list screen does — pins render above the FlatList, so
|
|
62
|
+
// they come first here too.
|
|
63
|
+
const {
|
|
64
|
+
pinned,
|
|
65
|
+
rest
|
|
66
|
+
} = (0, _withPinnedFirst.splitPinned)(visibleEvents, pinnedEvents, pinnedLiveIds);
|
|
67
|
+
return pinned.length === 0 ? rest : [...pinned, ...rest];
|
|
68
|
+
}, [fromSaved, savedRecords, savedSearch, isPro, visibleEvents, pinnedEvents, pinnedLiveIds]);
|
|
69
|
+
const index = ordered.findIndex(event => event.id === eventId);
|
|
70
|
+
|
|
71
|
+
// Not in the list at all: the filter changed under it while it was open, it
|
|
72
|
+
// aged past the 500-request cap, or it is a saved snapshot with no live twin.
|
|
73
|
+
// There is no coherent "next" in that state, so show no footer rather than a
|
|
74
|
+
// counter that lies.
|
|
75
|
+
if (index < 0) return null;
|
|
76
|
+
|
|
77
|
+
// Both lists are newest-first, so "Previous" walks toward the NEWER request —
|
|
78
|
+
// the same direction as scrolling up, which is what the arrow means here.
|
|
79
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.EventStepperFooter, {
|
|
80
|
+
currentIndex: index,
|
|
81
|
+
totalItems: ordered.length,
|
|
82
|
+
itemLabel: "Request",
|
|
83
|
+
onPrevious: () => {
|
|
84
|
+
const target = ordered[index - 1];
|
|
85
|
+
if (target) onSelect(target.id);
|
|
86
|
+
},
|
|
87
|
+
onNext: () => {
|
|
88
|
+
const target = ordered[index + 1];
|
|
89
|
+
if (target) onSelect(target.id);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.selectSavedEvents = selectSavedEvents;
|
|
7
|
+
var _filterNetworkEvents = require("./filterNetworkEvents");
|
|
8
|
+
var _networkSavedStore = require("./networkSavedStore");
|
|
9
|
+
/**
|
|
10
|
+
* The SAVED screen's visible list, as a pure function.
|
|
11
|
+
*
|
|
12
|
+
* Two places need the exact same list: the screen that renders it
|
|
13
|
+
* (`NetworkSavedScreen`) and the detail footer that steps through it
|
|
14
|
+
* (`NetworkDetailStepper`). If they derived it independently, "Next" would
|
|
15
|
+
* eventually disagree with the row below — the same drift `withPinnedFirst`
|
|
16
|
+
* exists to prevent for the live list.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
function selectSavedEvents(savedRecords, isPro, search) {
|
|
20
|
+
/**
|
|
21
|
+
* A downgrade (or a limit change) can leave a free user holding more saves
|
|
22
|
+
* than the free tier allows. Rather than deleting their data, the overflow
|
|
23
|
+
* is LOCKED — oldest first, so the newest saves stay reachable.
|
|
24
|
+
*/
|
|
25
|
+
const limit = isPro ? _networkSavedStore.MAX_SAVED : _networkSavedStore.FREE_TIER_SAVED_LIMIT;
|
|
26
|
+
const unlocked = savedRecords.slice(0, limit);
|
|
27
|
+
const events = unlocked.map(record => record.event);
|
|
28
|
+
return {
|
|
29
|
+
events: search ? (0, _filterNetworkEvents.filterNetworkEvents)(events, {
|
|
30
|
+
searchText: search
|
|
31
|
+
}) : events,
|
|
32
|
+
lockedCount: savedRecords.length - unlocked.length
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Previous/Next footer for the request DETAIL screen — step through requests
|
|
5
|
+
* without bouncing back to the list.
|
|
6
|
+
*
|
|
7
|
+
* It is its OWN leaf (rule3) because it is the only part of the detail screen
|
|
8
|
+
* that needs the event LIST. Subscribing here keeps the request stream out of
|
|
9
|
+
* the detail body's render path, which is the expensive subtree (headers,
|
|
10
|
+
* bodies, JSON viewers, diff views): a request arriving re-renders this
|
|
11
|
+
* ~30-node footer and nothing else.
|
|
12
|
+
*
|
|
13
|
+
* The order it steps through is EXACTLY what the list screen renders —
|
|
14
|
+
* `[...pinned, ...rest]` off the same `useNetworkEventList` + `splitPinned`
|
|
15
|
+
* derivation — so "Next" always lands on the row below the one the user
|
|
16
|
+
* tapped. Because the live filter is an input, typing in the search box or
|
|
17
|
+
* flipping a status chip re-scopes the stepper in place; the counter is
|
|
18
|
+
* "request N of <what the list is showing>", never "N of everything ever
|
|
19
|
+
* captured". Detail opened from the Saved screen steps through the SAVED
|
|
20
|
+
* list instead (with its own search applied), for the same reason.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { memo, useMemo } from "react";
|
|
24
|
+
import { EventStepperFooter } from "@buoy-gg/shared-ui";
|
|
25
|
+
import { useNetworkEventList } from "../hooks/useNetworkEventList";
|
|
26
|
+
import { useNetworkSaved } from "../hooks/useNetworkSaved";
|
|
27
|
+
import { splitPinned } from "../utils/withPinnedFirst";
|
|
28
|
+
import { selectSavedEvents } from "../utils/selectSavedEvents";
|
|
29
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
30
|
+
export const NetworkDetailStepper = /*#__PURE__*/memo(function NetworkDetailStepper({
|
|
31
|
+
eventId,
|
|
32
|
+
paused,
|
|
33
|
+
filter,
|
|
34
|
+
ignoredPatterns,
|
|
35
|
+
isPro,
|
|
36
|
+
fromSaved,
|
|
37
|
+
savedSearch,
|
|
38
|
+
onSelect
|
|
39
|
+
}) {
|
|
40
|
+
const {
|
|
41
|
+
visibleEvents
|
|
42
|
+
} = useNetworkEventList({
|
|
43
|
+
filter,
|
|
44
|
+
ignoredPatterns,
|
|
45
|
+
paused,
|
|
46
|
+
isPro
|
|
47
|
+
});
|
|
48
|
+
const {
|
|
49
|
+
pinnedEvents,
|
|
50
|
+
pinnedLiveIds,
|
|
51
|
+
savedRecords
|
|
52
|
+
} = useNetworkSaved();
|
|
53
|
+
const ordered = useMemo(() => {
|
|
54
|
+
if (fromSaved) {
|
|
55
|
+
return selectSavedEvents(savedRecords, isPro, savedSearch).events;
|
|
56
|
+
}
|
|
57
|
+
// Same split the list screen does — pins render above the FlatList, so
|
|
58
|
+
// they come first here too.
|
|
59
|
+
const {
|
|
60
|
+
pinned,
|
|
61
|
+
rest
|
|
62
|
+
} = splitPinned(visibleEvents, pinnedEvents, pinnedLiveIds);
|
|
63
|
+
return pinned.length === 0 ? rest : [...pinned, ...rest];
|
|
64
|
+
}, [fromSaved, savedRecords, savedSearch, isPro, visibleEvents, pinnedEvents, pinnedLiveIds]);
|
|
65
|
+
const index = ordered.findIndex(event => event.id === eventId);
|
|
66
|
+
|
|
67
|
+
// Not in the list at all: the filter changed under it while it was open, it
|
|
68
|
+
// aged past the 500-request cap, or it is a saved snapshot with no live twin.
|
|
69
|
+
// There is no coherent "next" in that state, so show no footer rather than a
|
|
70
|
+
// counter that lies.
|
|
71
|
+
if (index < 0) return null;
|
|
72
|
+
|
|
73
|
+
// Both lists are newest-first, so "Previous" walks toward the NEWER request —
|
|
74
|
+
// the same direction as scrolling up, which is what the arrow means here.
|
|
75
|
+
return /*#__PURE__*/_jsx(EventStepperFooter, {
|
|
76
|
+
currentIndex: index,
|
|
77
|
+
totalItems: ordered.length,
|
|
78
|
+
itemLabel: "Request",
|
|
79
|
+
onPrevious: () => {
|
|
80
|
+
const target = ordered[index - 1];
|
|
81
|
+
if (target) onSelect(target.id);
|
|
82
|
+
},
|
|
83
|
+
onNext: () => {
|
|
84
|
+
const target = ordered[index + 1];
|
|
85
|
+
if (target) onSelect(target.id);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The SAVED screen's visible list, as a pure function.
|
|
5
|
+
*
|
|
6
|
+
* Two places need the exact same list: the screen that renders it
|
|
7
|
+
* (`NetworkSavedScreen`) and the detail footer that steps through it
|
|
8
|
+
* (`NetworkDetailStepper`). If they derived it independently, "Next" would
|
|
9
|
+
* eventually disagree with the row below — the same drift `withPinnedFirst`
|
|
10
|
+
* exists to prevent for the live list.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { filterNetworkEvents } from "./filterNetworkEvents";
|
|
14
|
+
import { FREE_TIER_SAVED_LIMIT, MAX_SAVED } from "./networkSavedStore";
|
|
15
|
+
export function selectSavedEvents(savedRecords, isPro, search) {
|
|
16
|
+
/**
|
|
17
|
+
* A downgrade (or a limit change) can leave a free user holding more saves
|
|
18
|
+
* than the free tier allows. Rather than deleting their data, the overflow
|
|
19
|
+
* is LOCKED — oldest first, so the newest saves stay reachable.
|
|
20
|
+
*/
|
|
21
|
+
const limit = isPro ? MAX_SAVED : FREE_TIER_SAVED_LIMIT;
|
|
22
|
+
const unlocked = savedRecords.slice(0, limit);
|
|
23
|
+
const events = unlocked.map(record => record.event);
|
|
24
|
+
return {
|
|
25
|
+
events: search ? filterNetworkEvents(events, {
|
|
26
|
+
searchText: search
|
|
27
|
+
}) : events,
|
|
28
|
+
lockedCount: savedRecords.length - unlocked.length
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Previous/Next footer for the request DETAIL screen — step through requests
|
|
3
|
+
* without bouncing back to the list.
|
|
4
|
+
*
|
|
5
|
+
* It is its OWN leaf (rule3) because it is the only part of the detail screen
|
|
6
|
+
* that needs the event LIST. Subscribing here keeps the request stream out of
|
|
7
|
+
* the detail body's render path, which is the expensive subtree (headers,
|
|
8
|
+
* bodies, JSON viewers, diff views): a request arriving re-renders this
|
|
9
|
+
* ~30-node footer and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* The order it steps through is EXACTLY what the list screen renders —
|
|
12
|
+
* `[...pinned, ...rest]` off the same `useNetworkEventList` + `splitPinned`
|
|
13
|
+
* derivation — so "Next" always lands on the row below the one the user
|
|
14
|
+
* tapped. Because the live filter is an input, typing in the search box or
|
|
15
|
+
* flipping a status chip re-scopes the stepper in place; the counter is
|
|
16
|
+
* "request N of <what the list is showing>", never "N of everything ever
|
|
17
|
+
* captured". Detail opened from the Saved screen steps through the SAVED
|
|
18
|
+
* list instead (with its own search applied), for the same reason.
|
|
19
|
+
*/
|
|
20
|
+
import { type IgnoredPattern } from "@buoy-gg/shared-ui";
|
|
21
|
+
import type { NetworkFilter } from "../types";
|
|
22
|
+
interface NetworkDetailStepperProps {
|
|
23
|
+
/** The request currently on screen. */
|
|
24
|
+
eventId: string;
|
|
25
|
+
paused: boolean;
|
|
26
|
+
filter: NetworkFilter;
|
|
27
|
+
ignoredPatterns: readonly IgnoredPattern[];
|
|
28
|
+
isPro: boolean;
|
|
29
|
+
/** Detail was opened from the Saved screen — step through THAT list. */
|
|
30
|
+
fromSaved: boolean;
|
|
31
|
+
/** The Saved screen's search box (lifted to the modal root so both agree). */
|
|
32
|
+
savedSearch: string;
|
|
33
|
+
onSelect: (eventId: string) => void;
|
|
34
|
+
}
|
|
35
|
+
export declare const NetworkDetailStepper: import("react").NamedExoticComponent<NetworkDetailStepperProps>;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=NetworkDetailStepper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NetworkDetailStepper.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkDetailStepper.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,UAAU,CAAC;AAM5D,UAAU,yBAAyB;IACjC,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,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,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,eAAO,MAAM,oBAAoB,iEAiE/B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SAVED screen's visible list, as a pure function.
|
|
3
|
+
*
|
|
4
|
+
* Two places need the exact same list: the screen that renders it
|
|
5
|
+
* (`NetworkSavedScreen`) and the detail footer that steps through it
|
|
6
|
+
* (`NetworkDetailStepper`). If they derived it independently, "Next" would
|
|
7
|
+
* eventually disagree with the row below — the same drift `withPinnedFirst`
|
|
8
|
+
* exists to prevent for the live list.
|
|
9
|
+
*/
|
|
10
|
+
import type { NetworkEvent } from "../types";
|
|
11
|
+
import { type SavedNetworkRecord } from "./networkSavedStore";
|
|
12
|
+
export interface SavedEventSelection {
|
|
13
|
+
/** Unlocked saves, minus anything the search box excludes. */
|
|
14
|
+
events: NetworkEvent[];
|
|
15
|
+
/** Saves past the tier ceiling — rendered as the upgrade banner, not rows. */
|
|
16
|
+
lockedCount: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function selectSavedEvents(savedRecords: readonly SavedNetworkRecord[], isPro: boolean, search: string): SavedEventSelection;
|
|
19
|
+
//# sourceMappingURL=selectSavedEvents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectSavedEvents.d.ts","sourceRoot":"","sources":["../../../../src/network/utils/selectSavedEvents.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,SAAS,kBAAkB,EAAE,EAC3C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,GACb,mBAAmB,CAarB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/network",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.11",
|
|
4
4
|
"description": "In-app network inspector for React Native — requests, responses, timing & errors live, with GraphQL operation parsing. Part of Buoy devtools.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@buoy-gg/shared-ui": "^7.0.
|
|
29
|
+
"@buoy-gg/shared-ui": "^7.0.11"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"react": "*",
|