@buoy-gg/network 7.0.36 → 7.0.38
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 +45 -12
- package/lib/commonjs/index.js +26 -1
- package/lib/commonjs/index.web.js +26 -1
- package/lib/commonjs/network/components/NetworkEventDetailView.js +6 -1
- package/lib/commonjs/network/components/NetworkHeaderMenu.js +4 -0
- package/lib/commonjs/network/components/NetworkModal.js +24 -8
- package/lib/commonjs/network/conditions/NetworkConditionsControl.js +235 -0
- package/lib/commonjs/network/conditions/NetworkConditionsControl.test.js +136 -0
- package/lib/commonjs/network/conditions/NetworkConditionsIcon.js +33 -0
- package/lib/commonjs/network/conditions/NetworkFloatingTool.js +310 -0
- package/lib/commonjs/network/conditions/NetworkFloatingTool.test.js +219 -0
- package/lib/commonjs/network/conditions/store.js +75 -0
- package/lib/commonjs/network/conditions/wait.js +43 -0
- package/lib/commonjs/network/conditions/xhr.js +110 -0
- package/lib/commonjs/network/hooks/useNetworkEvents.js +1 -1
- package/lib/commonjs/network/overrides/applyToXhr.js +10 -8
- package/lib/commonjs/network/overrides/engine.js +2 -1
- package/lib/commonjs/network/sync/networkSyncAdapter.js +26 -1
- package/lib/commonjs/network/utils/generateNetworkCopyText.js +2 -0
- package/lib/commonjs/network/utils/networkEventStore.js +5 -77
- package/lib/commonjs/network/utils/networkListener.js +96 -51
- package/lib/commonjs/network/utils/networkSavedStore.js +2 -2
- package/lib/commonjs/network/utils/selectSavedEvents.js +2 -2
- package/lib/commonjs/preset.js +2 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.web.js +2 -1
- package/lib/module/network/components/NetworkEventDetailView.js +6 -1
- package/lib/module/network/components/NetworkHeaderMenu.js +4 -0
- package/lib/module/network/components/NetworkModal.js +25 -9
- package/lib/module/network/conditions/NetworkConditionsControl.js +231 -0
- package/lib/module/network/conditions/NetworkConditionsControl.test.js +135 -0
- package/lib/module/network/conditions/NetworkConditionsIcon.js +29 -0
- package/lib/module/network/conditions/NetworkFloatingTool.js +304 -0
- package/lib/module/network/conditions/NetworkFloatingTool.test.js +218 -0
- package/lib/module/network/conditions/store.js +65 -0
- package/lib/module/network/conditions/wait.js +38 -0
- package/lib/module/network/conditions/xhr.js +106 -0
- package/lib/module/network/hooks/useNetworkEvents.js +1 -1
- package/lib/module/network/overrides/applyToXhr.js +9 -8
- package/lib/module/network/overrides/engine.js +2 -1
- package/lib/module/network/sync/networkSyncAdapter.js +26 -1
- package/lib/module/network/utils/generateNetworkCopyText.js +2 -0
- package/lib/module/network/utils/networkEventStore.js +6 -77
- package/lib/module/network/utils/networkListener.js +97 -52
- package/lib/module/network/utils/networkSavedStore.js +2 -2
- package/lib/module/network/utils/selectSavedEvents.js +2 -2
- package/lib/module/preset.js +3 -2
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +2 -0
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts +2 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts +3 -0
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts +12 -0
- package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts +2 -0
- package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts +4 -0
- package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts +15 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts +2 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts.map +1 -0
- package/lib/typescript/network/conditions/store.d.ts +30 -0
- package/lib/typescript/network/conditions/store.d.ts.map +1 -0
- package/lib/typescript/network/conditions/wait.d.ts +4 -0
- package/lib/typescript/network/conditions/wait.d.ts.map +1 -0
- package/lib/typescript/network/conditions/xhr.d.ts +5 -0
- package/lib/typescript/network/conditions/xhr.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkEvents.d.ts +1 -1
- package/lib/typescript/network/overrides/applyToXhr.d.ts +2 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +1 -1
- package/lib/typescript/network/overrides/engine.d.ts +0 -10
- package/lib/typescript/network/overrides/engine.d.ts.map +1 -1
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +17 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/network/types/index.d.ts +1 -0
- package/lib/typescript/network/types/index.d.ts.map +1 -1
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts +0 -15
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkListener.d.ts +2 -1
- package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkSavedStore.d.ts +2 -2
- package/lib/typescript/network/utils/selectSavedEvents.d.ts +2 -2
- package/lib/typescript/preset.d.ts +0 -16
- package/lib/typescript/preset.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@buoy-gg/network) [](https://www.npmjs.com/package/@buoy-gg/network)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Inspect HTTP requests captured through your React Native app’s global `fetch` and `XMLHttpRequest` hooks. Open a request to see its URL, status, headers, timing, and captured body.
|
|
6
6
|
|
|
7
|
-
Part of [Buoy](https://github.com/Buoy-gg/buoy)
|
|
7
|
+
Part of [Buoy](https://github.com/Buoy-gg/buoy). Install the package, complete account setup, and follow the integration steps below.
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -14,9 +14,27 @@ Part of [Buoy](https://github.com/Buoy-gg/buoy) — devtools that live inside yo
|
|
|
14
14
|
npm install @buoy-gg/core @buoy-gg/network
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Before you start
|
|
18
|
+
|
|
19
|
+
Use a development build with `@buoy-gg/core` and a Free or Pro Buoy account key. From your app’s directory, sign in:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx --package=@buoy-gg/core buoy login
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For Expo, initialize Buoy before rendering the menu:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { Buoy } from "@buoy-gg/core";
|
|
29
|
+
|
|
30
|
+
Buoy.init({ licenseKey: process.env.EXPO_PUBLIC_BUOY_KEY });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The login command writes the Expo key to `.env.local`. For React Native CLI, pass the key from your app’s environment configuration; React Native does not load `.env.local` automatically. Mount `FloatingDevTools` inside the same providers as your screens and restart the development server after installation. The [Quick Start](https://buoy.gg/buoy/latest/docs/quick-start) shows the complete root component setup.
|
|
34
|
+
|
|
17
35
|
## Quick start
|
|
18
36
|
|
|
19
|
-
|
|
37
|
+
After initialization, render the menu in your app root:
|
|
20
38
|
|
|
21
39
|
```tsx
|
|
22
40
|
import { FloatingDevTools } from "@buoy-gg/core";
|
|
@@ -33,30 +51,45 @@ export default function App() {
|
|
|
33
51
|
|
|
34
52
|
Want a different label or color? Build the tool with `createNetworkTool({ name, colorPreset })` and pass it via `apps={[...]}`.
|
|
35
53
|
|
|
54
|
+
## Check the integration
|
|
55
|
+
|
|
56
|
+
Open Network, trigger a new request in your app, and inspect its URL and status. Requests made before instrumentation and account admission, or through a separate native transport, may not appear. A cache hit may make no request. Saved bodies retain capture limits; saving does not recover truncated data.
|
|
57
|
+
|
|
36
58
|
## What you get
|
|
37
59
|
|
|
38
|
-
- **
|
|
39
|
-
- **
|
|
60
|
+
- **Supported requests** — intercepts global `fetch` and `XMLHttpRequest`. Clients using those paths can be captured; separate native transports and previously saved references may bypass the hooks.
|
|
61
|
+
- **Early capture** — development instrumentation can capture before the panel opens, once its hooks and account admission are ready. It cannot recover earlier requests.
|
|
40
62
|
- **GraphQL gets special treatment** — operation names are extracted from queries, mutations, and subscriptions and shown with variables in arrow notation: `GetUser › 123`. No more guessing which `/graphql` request is which.
|
|
41
|
-
- **Full request detail** — request/response headers, payloads, HTTP status, and duration for
|
|
63
|
+
- **Full request detail** — request/response headers, payloads, HTTP status, and duration for captured calls, with status-colored rows.
|
|
42
64
|
- **Filter and search** — narrow by status code, HTTP method, or URL pattern; search captured events; pause/resume capture; clear history.
|
|
43
|
-
- **Pin the one that failed** — pin a request to hold it at the top of the list (ignoring your filters), or save it to a separate favorites list. Both keep
|
|
65
|
+
- **Pin the one that failed** — pin a request to hold it at the top of the list (ignoring your filters), or save it to a separate favorites list. Both keep the captured snapshot, including any body truncation, so it survives Clear and the 500-request cap. Restart recovery requires a successful storage write. Long-press a row, or use the buttons in the request detail header.
|
|
44
66
|
- **Step between requests** — the detail view has a Previous/Next footer that walks exactly what the list was showing (same pins, filters, and search — or your Saved list), so comparing two calls doesn't mean going back and finding your place.
|
|
45
67
|
- **Ignore the noise** — a shared ignored-domains/URL filter (`useIgnoredPatterns`) hides endpoints you never want to see, shared with the Events tool.
|
|
46
68
|
- **Hooks for custom UIs** — `useNetworkEvents`, `useNetworkEventList`, `useNetworkBadgeCounts`, and `useNetworkSaved` expose the capture stream with narrow per-consumer subscriptions.
|
|
47
69
|
|
|
48
|
-
## Desktop & AI
|
|
49
70
|
|
|
50
|
-
|
|
71
|
+
## Desktop & AI
|
|
51
72
|
|
|
52
|
-
|
|
73
|
+
To connect a React Native app to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) or MCP, install `@buoy-gg/external-sync` and follow the [Desktop connection guide](https://buoy.gg/buoy/latest/docs/desktop). Sign in to Desktop separately. [MCP setup](https://buoy.gg/buoy/latest/docs/mcp) also requires a process account and Pro access. Available remote actions depend on the tool and app integration.
|
|
53
74
|
|
|
54
|
-
|
|
75
|
+
## Account and plan requirements
|
|
55
76
|
|
|
56
|
-
|
|
77
|
+
Use a Free or Pro Buoy account. History limits and paid features vary by tool; see [pricing](https://buoy.gg/pricing). Production access requires Pro where supported. Development-only hooks and actions remain unavailable in release builds.
|
|
57
78
|
|
|
58
79
|
---
|
|
59
80
|
|
|
60
81
|
📚 [Full docs](https://buoy.gg/buoy/latest/docs/tools/network) · [All Buoy tools](https://github.com/Buoy-gg/buoy)
|
|
61
82
|
|
|
62
83
|
Proprietary software. © Buoy LLC. [Terms](https://buoy.gg/terms)
|
|
84
|
+
|
|
85
|
+
## Network conditions
|
|
86
|
+
|
|
87
|
+
In development builds, open **⋯ → Network throttling** to minimize Network and show compact controls over the app. Restore Network from its minimized icon. Tap the signal icon to cycle through No throttling, Slow, Very slow and Offline. Each tap applies the profile immediately and updates the icon and delay. The profile text is a label and can be dragged to move the strip. Close restores Normal and dismisses the controls. Drag the background, delay label or grip to move the strip. Buttons keep their tap actions. In React Native development builds, an open strip returns after reload at its saved position, including when hidden at the edge. Close keeps it closed across reloads. The condition resets to No throttling. Tap the grip to hide or restore it. Free and Pro accounts can use Network conditions after account admission.
|
|
88
|
+
|
|
89
|
+
Offline rejects new HTTP(S) calls through the installed global fetch and React Native XHR hooks without sending them. Slow and Very slow add one delay before dispatch. They do not limit bandwidth or change device connectivity. Imported native transports, images and WebSockets can bypass these hooks.
|
|
90
|
+
|
|
91
|
+
Each call keeps the profile selected when it starts. Offline takes precedence over authored overrides without consuming their counters. Latency adds to an authored delay. A finite XHR timeout under active conditions covers the whole call from `send()`, including artificial waits; Normal preserves native timeout behavior.
|
|
92
|
+
|
|
93
|
+
Conditions remain active when the panel is closed or its list is paused. They reset on a full JS reload or account access loss and are never persisted. Captured requests retain their condition stamp for saved snapshots, detail views and export. Desktop and MCP can control a connected device that advertises the conditions actions.
|
|
94
|
+
|
|
95
|
+
Network conditions are a development preview. The native compatibility matrix across transports, runtimes and devices is still being worked through, so confirm the behaviour you rely on in your own build before you depend on it.
|
package/lib/commonjs/index.js
CHANGED
|
@@ -39,6 +39,12 @@ Object.defineProperty(exports, "MAX_SAVED", {
|
|
|
39
39
|
return _networkSavedStore.MAX_SAVED;
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
|
+
Object.defineProperty(exports, "NETWORK_CONDITIONS", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _store.NETWORK_CONDITIONS;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
42
48
|
Object.defineProperty(exports, "NetworkBodyResolverProvider", {
|
|
43
49
|
enumerable: true,
|
|
44
50
|
get: function () {
|
|
@@ -159,6 +165,12 @@ Object.defineProperty(exports, "generateCurl", {
|
|
|
159
165
|
return _generateCurl.generateCurl;
|
|
160
166
|
}
|
|
161
167
|
});
|
|
168
|
+
Object.defineProperty(exports, "getNetworkCondition", {
|
|
169
|
+
enumerable: true,
|
|
170
|
+
get: function () {
|
|
171
|
+
return _store.getNetworkCondition;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
162
174
|
Object.defineProperty(exports, "ignoredPatternsStore", {
|
|
163
175
|
enumerable: true,
|
|
164
176
|
get: function () {
|
|
@@ -219,12 +231,24 @@ Object.defineProperty(exports, "removeAllNetworkListeners", {
|
|
|
219
231
|
return _networkListener.removeAllNetworkListeners;
|
|
220
232
|
}
|
|
221
233
|
});
|
|
234
|
+
Object.defineProperty(exports, "setNetworkCondition", {
|
|
235
|
+
enumerable: true,
|
|
236
|
+
get: function () {
|
|
237
|
+
return _store.setNetworkCondition;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
222
240
|
Object.defineProperty(exports, "splitPinned", {
|
|
223
241
|
enumerable: true,
|
|
224
242
|
get: function () {
|
|
225
243
|
return _withPinnedFirst.splitPinned;
|
|
226
244
|
}
|
|
227
245
|
});
|
|
246
|
+
Object.defineProperty(exports, "subscribeNetworkConditions", {
|
|
247
|
+
enumerable: true,
|
|
248
|
+
get: function () {
|
|
249
|
+
return _store.subscribeNetworkConditions;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
228
252
|
Object.defineProperty(exports, "urlMatchesIgnoredPattern", {
|
|
229
253
|
enumerable: true,
|
|
230
254
|
get: function () {
|
|
@@ -297,4 +321,5 @@ var _networkBodyResolver = require("./network/sync/networkBodyResolver");
|
|
|
297
321
|
var _networkEventStore = require("./network/utils/networkEventStore");
|
|
298
322
|
var _networkSavedStore = require("./network/utils/networkSavedStore");
|
|
299
323
|
var _generateCurl = require("./network/utils/generateCurl");
|
|
300
|
-
var _networkListener = require("./network/utils/networkListener");
|
|
324
|
+
var _networkListener = require("./network/utils/networkListener");
|
|
325
|
+
var _store = require("./network/conditions/store");
|
|
@@ -39,6 +39,12 @@ Object.defineProperty(exports, "MAX_SAVED", {
|
|
|
39
39
|
return _networkSavedStore.MAX_SAVED;
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
|
+
Object.defineProperty(exports, "NETWORK_CONDITIONS", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _store.NETWORK_CONDITIONS;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
42
48
|
Object.defineProperty(exports, "NetworkClearButton", {
|
|
43
49
|
enumerable: true,
|
|
44
50
|
get: function () {
|
|
@@ -219,6 +225,12 @@ Object.defineProperty(exports, "generateNetworkCopyText", {
|
|
|
219
225
|
return _generateNetworkCopyText.generateNetworkCopyText;
|
|
220
226
|
}
|
|
221
227
|
});
|
|
228
|
+
Object.defineProperty(exports, "getNetworkCondition", {
|
|
229
|
+
enumerable: true,
|
|
230
|
+
get: function () {
|
|
231
|
+
return _store.getNetworkCondition;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
222
234
|
Object.defineProperty(exports, "isNetworkListening", {
|
|
223
235
|
enumerable: true,
|
|
224
236
|
get: function () {
|
|
@@ -279,6 +291,12 @@ Object.defineProperty(exports, "selectSavedEvents", {
|
|
|
279
291
|
return _selectSavedEvents.selectSavedEvents;
|
|
280
292
|
}
|
|
281
293
|
});
|
|
294
|
+
Object.defineProperty(exports, "setNetworkCondition", {
|
|
295
|
+
enumerable: true,
|
|
296
|
+
get: function () {
|
|
297
|
+
return _store.setNetworkCondition;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
282
300
|
Object.defineProperty(exports, "splitPinned", {
|
|
283
301
|
enumerable: true,
|
|
284
302
|
get: function () {
|
|
@@ -297,6 +315,12 @@ Object.defineProperty(exports, "stopNetworkListener", {
|
|
|
297
315
|
return _networkListener.stopNetworkListener;
|
|
298
316
|
}
|
|
299
317
|
});
|
|
318
|
+
Object.defineProperty(exports, "subscribeNetworkConditions", {
|
|
319
|
+
enumerable: true,
|
|
320
|
+
get: function () {
|
|
321
|
+
return _store.subscribeNetworkConditions;
|
|
322
|
+
}
|
|
323
|
+
});
|
|
300
324
|
Object.defineProperty(exports, "toSelectionIds", {
|
|
301
325
|
enumerable: true,
|
|
302
326
|
get: function () {
|
|
@@ -392,4 +416,5 @@ var _matchRule = require("./network/overrides/matchRule");
|
|
|
392
416
|
var _filterNetworkEvents = require("./network/utils/filterNetworkEvents");
|
|
393
417
|
var _withPinnedFirst = require("./network/utils/withPinnedFirst");
|
|
394
418
|
var _extractOperationName = require("./network/utils/extractOperationName");
|
|
395
|
-
var _generateCurl = require("./network/utils/generateCurl");
|
|
419
|
+
var _generateCurl = require("./network/utils/generateCurl");
|
|
420
|
+
var _store = require("./network/conditions/store");
|
|
@@ -447,7 +447,12 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
447
447
|
style: styles.pendingBadgeText,
|
|
448
448
|
children: "Pending"
|
|
449
449
|
})]
|
|
450
|
-
}) : null, event.
|
|
450
|
+
}) : null, event.condition && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
451
|
+
style: {
|
|
452
|
+
color: "#FFCC66"
|
|
453
|
+
},
|
|
454
|
+
children: ["Conditions: ", event.condition.profile, " (+", event.condition.delayMs, " ms)"]
|
|
455
|
+
}), event.duration ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
451
456
|
style: styles.httpDuration,
|
|
452
457
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Clock, {
|
|
453
458
|
size: 10,
|
|
@@ -82,11 +82,15 @@ function NetworkHeaderMenuItem({
|
|
|
82
82
|
icon,
|
|
83
83
|
label,
|
|
84
84
|
detail,
|
|
85
|
+
testID,
|
|
85
86
|
onPress
|
|
86
87
|
}) {
|
|
87
88
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
88
89
|
onPress: onPress,
|
|
89
90
|
"sentry-label": "ignore header menu item",
|
|
91
|
+
testID: testID,
|
|
92
|
+
accessibilityRole: "button",
|
|
93
|
+
accessibilityLabel: label,
|
|
90
94
|
style: styles.item,
|
|
91
95
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
92
96
|
style: styles.itemIcon,
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.NetworkModal = NetworkModal;
|
|
7
7
|
exports.NetworkToolView = NetworkToolView;
|
|
8
|
+
var _NetworkFloatingTool = require("../conditions/NetworkFloatingTool");
|
|
8
9
|
var _react = require("react");
|
|
9
10
|
var _reactNative = require("react-native");
|
|
10
11
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
@@ -183,7 +184,8 @@ function NetworkModalInner({
|
|
|
183
184
|
enableSharedModalDimensions = false,
|
|
184
185
|
inline = false,
|
|
185
186
|
paused: pausedProp,
|
|
186
|
-
onPausedChange
|
|
187
|
+
onPausedChange,
|
|
188
|
+
remoteConditions
|
|
187
189
|
}) {
|
|
188
190
|
const {
|
|
189
191
|
isPro
|
|
@@ -213,6 +215,12 @@ function NetworkModalInner({
|
|
|
213
215
|
const [showSavedView, setShowSavedView] = (0, _react.useState)(false);
|
|
214
216
|
const [showOverridesView, setShowOverridesView] = (0, _react.useState)(false);
|
|
215
217
|
const [showHeaderMenu, setShowHeaderMenu] = (0, _react.useState)(false);
|
|
218
|
+
const [minimizeRequest, setMinimizeRequest] = (0, _react.useState)(0);
|
|
219
|
+
const [remoteConditionsSource] = (0, _react.useState)(_NetworkFloatingTool.createRemoteConditionsSource);
|
|
220
|
+
(0, _react.useEffect)(() => {
|
|
221
|
+
remoteConditionsSource.update(remoteConditions);
|
|
222
|
+
}, [remoteConditionsSource, remoteConditions]);
|
|
223
|
+
(0, _react.useEffect)(() => () => remoteConditionsSource.update(undefined), [remoteConditionsSource]);
|
|
216
224
|
/**
|
|
217
225
|
* Overrides are a DEVICE capability — a rule runs inside the app's own
|
|
218
226
|
* `fetch`/`XMLHttpRequest`, which the dashboard doesn't have.
|
|
@@ -493,7 +501,7 @@ function NetworkModalInner({
|
|
|
493
501
|
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PowerToggleButton, {
|
|
494
502
|
isEnabled: !paused,
|
|
495
503
|
onToggle: () => setPaused(prev => !prev),
|
|
496
|
-
accessibilityLabel: "
|
|
504
|
+
accessibilityLabel: "Pause network list",
|
|
497
505
|
testID: "buoy-network-power"
|
|
498
506
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_NetworkHeaderLive.NetworkClearButton, {
|
|
499
507
|
paused: paused
|
|
@@ -591,6 +599,18 @@ function NetworkModalInner({
|
|
|
591
599
|
}), showHeaderMenu ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_NetworkHeaderMenu.NetworkHeaderMenu, {
|
|
592
600
|
onClose: () => setShowHeaderMenu(false),
|
|
593
601
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_NetworkHeaderMenu.NetworkHeaderMenuItem, {
|
|
602
|
+
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Wifi, {
|
|
603
|
+
size: 14,
|
|
604
|
+
color: _sharedUi.night.color.textSecondary
|
|
605
|
+
}),
|
|
606
|
+
label: "Network throttling",
|
|
607
|
+
testID: "buoy-network-menu-throttling",
|
|
608
|
+
onPress: () => {
|
|
609
|
+
setShowHeaderMenu(false);
|
|
610
|
+
(0, _NetworkFloatingTool.openNetworkFloatingTool)(remoteConditions ? remoteConditionsSource : undefined);
|
|
611
|
+
if (!inline) setMinimizeRequest(value => value + 1);
|
|
612
|
+
}
|
|
613
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_NetworkHeaderMenu.NetworkHeaderMenuItem, {
|
|
594
614
|
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Filter, {
|
|
595
615
|
size: 14,
|
|
596
616
|
color: hasActiveFilter ? _sharedUi.night.color.accent : _sharedUi.night.color.textSecondary
|
|
@@ -629,12 +649,7 @@ function NetworkModalInner({
|
|
|
629
649
|
})]
|
|
630
650
|
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.UpgradeModal, {
|
|
631
651
|
visible: showUpgradeModal,
|
|
632
|
-
onClose: () => setShowUpgradeModal(false)
|
|
633
|
-
// "saved" is a count limit a free key can raise; "overrides" is one it
|
|
634
|
-
// can't (1 on both tiers) and "export" is Pro-only — freeKeyRaises
|
|
635
|
-
// sorts those out, so the mapping here can stay literal.
|
|
636
|
-
,
|
|
637
|
-
limitKey: upgradeReason === "saved" ? "network.saved" : upgradeReason === "overrides" ? "network.overrideRules" : undefined,
|
|
652
|
+
onClose: () => setShowUpgradeModal(false),
|
|
638
653
|
featureName: upgradeReason === "saved" ? "Saved Requests" : upgradeReason === "overrides" ? "Response Overrides" : "Network Export",
|
|
639
654
|
description: upgradeReason === "saved" ? "Keep the requests that matter. Saved requests survive Clear, app restarts, and the 500-request history cap." : upgradeReason === "overrides" ? "Force any endpoint to fail, stall or return something else — no backend changes, no code changes. Free keeps one active rule." : "Export network requests to share with your team, debug APIs, or feed into AI tools.",
|
|
640
655
|
benefits: upgradeReason === "overrides" ? [`Run ${_types.MAX_RULES} rules at once (free keeps ${_types.FREE_TIER_RULE_LIMIT})`, "Force a 500, a 401 or an empty list in one tap", "Simulate offline, timeouts and slow connections", "Rules survive an app reload, so you can test the boot path"] : upgradeReason === "saved" ? [`Save up to ${_networkSavedStore.MAX_SAVED} requests (free keeps ${_networkSavedStore.FREE_TIER_SAVED_LIMIT})`, "Failed requests stay put through a Clear", "Snapshots survive an app restart", "Export saved requests to teammates or AI assistants"] : ["Export to JSON, Markdown, or plain text", "Customizable copy presets (URLs only, LLM-ready, full data)", "Share request details with teammates", "Feed network data directly to AI assistants"]
|
|
@@ -657,6 +672,7 @@ function NetworkModalInner({
|
|
|
657
672
|
visible: visible,
|
|
658
673
|
onClose: onClose,
|
|
659
674
|
onMinimize: onMinimize,
|
|
675
|
+
minimizeRequest: minimizeRequest,
|
|
660
676
|
persistenceKey: persistenceKey,
|
|
661
677
|
variant: "night",
|
|
662
678
|
header: {
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NetworkConditionsControl = NetworkConditionsControl;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
|
+
var _store = require("./store");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
const options = [{
|
|
13
|
+
profile: "normal",
|
|
14
|
+
label: "No throttling"
|
|
15
|
+
}, {
|
|
16
|
+
profile: "slow",
|
|
17
|
+
label: "Slow"
|
|
18
|
+
}, {
|
|
19
|
+
profile: "verySlow",
|
|
20
|
+
label: "Very slow"
|
|
21
|
+
}, {
|
|
22
|
+
profile: "offline",
|
|
23
|
+
label: "Offline"
|
|
24
|
+
}];
|
|
25
|
+
const summaries = {
|
|
26
|
+
normal: "No added delay",
|
|
27
|
+
slow: "+500 ms before each new request",
|
|
28
|
+
verySlow: "+2 seconds before each new request",
|
|
29
|
+
offline: "New requests fail before sending"
|
|
30
|
+
};
|
|
31
|
+
function NetworkConditionsControl({
|
|
32
|
+
remote
|
|
33
|
+
}) {
|
|
34
|
+
const local = (0, _react.useSyncExternalStore)(_store.subscribeNetworkConditions, _store.getNetworkCondition, _store.getNetworkCondition);
|
|
35
|
+
const localAvailable = (0, _react.useSyncExternalStore)(_store.subscribeNetworkConditions, _store.conditionsCanApply, _store.conditionsCanApply);
|
|
36
|
+
const [expanded, setExpanded] = (0, _react.useState)(false);
|
|
37
|
+
const [error, setError] = (0, _react.useState)();
|
|
38
|
+
const [pending, setPending] = (0, _react.useState)(false);
|
|
39
|
+
const busy = (0, _react.useRef)(false);
|
|
40
|
+
const mounted = (0, _react.useRef)(true);
|
|
41
|
+
(0, _react.useEffect)(() => {
|
|
42
|
+
mounted.current = true;
|
|
43
|
+
return () => {
|
|
44
|
+
mounted.current = false;
|
|
45
|
+
};
|
|
46
|
+
}, []);
|
|
47
|
+
const profile = remote ? remote.profile : local;
|
|
48
|
+
const available = remote ? remote.available : localAvailable;
|
|
49
|
+
const active = profile !== null && profile !== "normal";
|
|
50
|
+
const tint = profile === "offline" ? _sharedUi.night.color.danger : active ? _sharedUi.night.color.warning : _sharedUi.night.color.accent;
|
|
51
|
+
async function select(value) {
|
|
52
|
+
if (!available || busy.current) return;
|
|
53
|
+
if (value === profile) {
|
|
54
|
+
setExpanded(false);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
busy.current = true;
|
|
58
|
+
setPending(true);
|
|
59
|
+
setError(undefined);
|
|
60
|
+
try {
|
|
61
|
+
if (remote) await remote.onChange(value);else (0, _store.setNetworkCondition)(value);
|
|
62
|
+
if (mounted.current) setExpanded(false);
|
|
63
|
+
} catch (failure) {
|
|
64
|
+
if (mounted.current) setError(failure instanceof Error ? failure.message : String(failure));
|
|
65
|
+
} finally {
|
|
66
|
+
busy.current = false;
|
|
67
|
+
if (mounted.current) setPending(false);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const label = options.find(option => option.profile === profile)?.label ?? "Unavailable";
|
|
71
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
72
|
+
style: styles.container,
|
|
73
|
+
testID: "buoy-network-throttling-screen",
|
|
74
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
75
|
+
style: styles.heading,
|
|
76
|
+
children: "Connection profile"
|
|
77
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
78
|
+
testID: "buoy-network-conditions",
|
|
79
|
+
accessibilityRole: "button",
|
|
80
|
+
accessibilityLabel: `Network throttling: ${label}`,
|
|
81
|
+
accessibilityState: {
|
|
82
|
+
expanded,
|
|
83
|
+
disabled: !available || pending
|
|
84
|
+
},
|
|
85
|
+
disabled: !available || pending,
|
|
86
|
+
onPress: () => setExpanded(value => !value),
|
|
87
|
+
style: [styles.dropdown, expanded && styles.dropdownOpen, !available && styles.disabled],
|
|
88
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
89
|
+
style: [styles.value, active && {
|
|
90
|
+
color: tint
|
|
91
|
+
}],
|
|
92
|
+
children: label
|
|
93
|
+
}), pending ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
94
|
+
size: "small",
|
|
95
|
+
color: _sharedUi.night.color.textSecondary
|
|
96
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
|
|
97
|
+
size: 16,
|
|
98
|
+
color: _sharedUi.night.color.textSecondary
|
|
99
|
+
})]
|
|
100
|
+
}), expanded && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
101
|
+
accessibilityRole: "radiogroup",
|
|
102
|
+
accessibilityLabel: "Connection profile",
|
|
103
|
+
style: styles.options,
|
|
104
|
+
children: options.map(option => {
|
|
105
|
+
const selected = profile === option.profile;
|
|
106
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
107
|
+
testID: `buoy-network-condition-${option.profile}`,
|
|
108
|
+
accessibilityRole: "radio",
|
|
109
|
+
accessibilityLabel: option.label,
|
|
110
|
+
accessibilityState: {
|
|
111
|
+
checked: selected,
|
|
112
|
+
disabled: !available || pending
|
|
113
|
+
},
|
|
114
|
+
disabled: !available || pending,
|
|
115
|
+
onPress: () => void select(option.profile),
|
|
116
|
+
style: [styles.option, selected && styles.selected],
|
|
117
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
118
|
+
style: styles.optionBody,
|
|
119
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
120
|
+
style: [styles.optionTitle, selected && {
|
|
121
|
+
color: tint
|
|
122
|
+
}],
|
|
123
|
+
children: option.label
|
|
124
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
125
|
+
style: styles.caption,
|
|
126
|
+
children: summaries[option.profile]
|
|
127
|
+
})]
|
|
128
|
+
}), selected && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
|
|
129
|
+
size: 16,
|
|
130
|
+
color: tint
|
|
131
|
+
})]
|
|
132
|
+
}, option.profile);
|
|
133
|
+
})
|
|
134
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
135
|
+
style: styles.caption,
|
|
136
|
+
accessibilityLiveRegion: "polite",
|
|
137
|
+
children: pending ? "Applying to device…" : !available ? remote ? "Conditions unavailable on this device" : "Requires a signed-in account and a development build" : profile ? summaries[profile] : "Waiting for device status"
|
|
138
|
+
}), !!error && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
139
|
+
accessibilityRole: "alert",
|
|
140
|
+
style: styles.error,
|
|
141
|
+
children: error
|
|
142
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
143
|
+
style: styles.help,
|
|
144
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
145
|
+
style: styles.heading,
|
|
146
|
+
children: "How it works"
|
|
147
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
148
|
+
style: styles.caption,
|
|
149
|
+
children: "Affects new HTTP requests through global fetch and XHR. Slow adds a wait before sending; download speed stays unchanged."
|
|
150
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
151
|
+
style: styles.caption,
|
|
152
|
+
children: "Stays active when you close this tool or pause capture. Choose No throttling to stop. Reloading the app resets it."
|
|
153
|
+
})]
|
|
154
|
+
})]
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const styles = _reactNative.StyleSheet.create({
|
|
158
|
+
container: {
|
|
159
|
+
padding: _sharedUi.night.gutter,
|
|
160
|
+
gap: 10
|
|
161
|
+
},
|
|
162
|
+
heading: {
|
|
163
|
+
fontSize: _sharedUi.night.font.caption,
|
|
164
|
+
fontWeight: "600",
|
|
165
|
+
color: _sharedUi.night.color.textSecondary
|
|
166
|
+
},
|
|
167
|
+
dropdown: {
|
|
168
|
+
flexDirection: "row",
|
|
169
|
+
alignItems: "center",
|
|
170
|
+
justifyContent: "space-between",
|
|
171
|
+
gap: 12,
|
|
172
|
+
minHeight: 44,
|
|
173
|
+
paddingHorizontal: 12,
|
|
174
|
+
paddingVertical: 10,
|
|
175
|
+
borderRadius: _sharedUi.night.radius.segment,
|
|
176
|
+
borderWidth: 1,
|
|
177
|
+
borderColor: _sharedUi.night.color.separator,
|
|
178
|
+
backgroundColor: _sharedUi.night.color.surfaceElevated
|
|
179
|
+
},
|
|
180
|
+
dropdownOpen: {
|
|
181
|
+
borderColor: _sharedUi.night.color.accentBorderStrong
|
|
182
|
+
},
|
|
183
|
+
value: {
|
|
184
|
+
flex: 1,
|
|
185
|
+
fontSize: _sharedUi.night.font.body,
|
|
186
|
+
color: _sharedUi.night.color.text
|
|
187
|
+
},
|
|
188
|
+
options: {
|
|
189
|
+
padding: 4,
|
|
190
|
+
borderRadius: _sharedUi.night.radius.row,
|
|
191
|
+
borderWidth: 1,
|
|
192
|
+
borderColor: _sharedUi.night.color.border,
|
|
193
|
+
backgroundColor: _sharedUi.night.color.surface
|
|
194
|
+
},
|
|
195
|
+
option: {
|
|
196
|
+
flexDirection: "row",
|
|
197
|
+
alignItems: "center",
|
|
198
|
+
gap: 12,
|
|
199
|
+
minHeight: 54,
|
|
200
|
+
padding: 10,
|
|
201
|
+
borderRadius: _sharedUi.night.radius.segment
|
|
202
|
+
},
|
|
203
|
+
optionBody: {
|
|
204
|
+
flex: 1,
|
|
205
|
+
gap: 4
|
|
206
|
+
},
|
|
207
|
+
optionTitle: {
|
|
208
|
+
fontSize: _sharedUi.night.font.caption,
|
|
209
|
+
fontWeight: "600",
|
|
210
|
+
color: _sharedUi.night.color.text
|
|
211
|
+
},
|
|
212
|
+
selected: {
|
|
213
|
+
backgroundColor: _sharedUi.night.color.surfaceElevated
|
|
214
|
+
},
|
|
215
|
+
disabled: {
|
|
216
|
+
opacity: 0.45
|
|
217
|
+
},
|
|
218
|
+
caption: {
|
|
219
|
+
fontSize: _sharedUi.night.font.caption,
|
|
220
|
+
lineHeight: 19,
|
|
221
|
+
color: _sharedUi.night.color.textSecondary
|
|
222
|
+
},
|
|
223
|
+
help: {
|
|
224
|
+
marginTop: 14,
|
|
225
|
+
paddingTop: 16,
|
|
226
|
+
gap: 10,
|
|
227
|
+
borderTopWidth: 1,
|
|
228
|
+
borderTopColor: _sharedUi.night.color.separator
|
|
229
|
+
},
|
|
230
|
+
error: {
|
|
231
|
+
fontSize: _sharedUi.night.font.caption,
|
|
232
|
+
lineHeight: 18,
|
|
233
|
+
color: _sharedUi.night.color.danger
|
|
234
|
+
}
|
|
235
|
+
});
|