@buoy-gg/network 3.0.1 → 4.0.1
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 +39 -0
- package/lib/commonjs/network/components/NetworkEventDetailView.js +61 -7
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +2 -2
- package/lib/commonjs/network/components/NetworkModal.js +14 -126
- package/lib/commonjs/network/sync/networkBodyResolver.js +39 -0
- package/lib/commonjs/network/sync/networkSyncAdapter.js +69 -0
- package/lib/module/index.js +14 -0
- package/lib/module/network/components/NetworkEventDetailView.js +61 -7
- package/lib/module/network/components/NetworkEventItemCompact.js +2 -2
- package/lib/module/network/components/NetworkModal.js +13 -127
- package/lib/module/network/sync/networkBodyResolver.js +34 -0
- package/lib/module/network/sync/networkSyncAdapter.js +65 -0
- package/lib/typescript/index.d.ts +4 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/sync/networkBodyResolver.d.ts +27 -0
- package/lib/typescript/network/sync/networkBodyResolver.d.ts.map +1 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +41 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -0
- package/lib/typescript/network/types/index.d.ts +4 -12
- package/lib/typescript/network/types/index.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/commonjs/index.js
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "NetworkBodyResolverProvider", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _networkBodyResolver.NetworkBodyResolverProvider;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
6
12
|
Object.defineProperty(exports, "NetworkEventDetailView", {
|
|
7
13
|
enumerable: true,
|
|
8
14
|
get: function () {
|
|
@@ -57,6 +63,18 @@ Object.defineProperty(exports, "formatHttpStatus", {
|
|
|
57
63
|
return _formatting.formatHttpStatus;
|
|
58
64
|
}
|
|
59
65
|
});
|
|
66
|
+
Object.defineProperty(exports, "ignoredPatternsStore", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
get: function () {
|
|
69
|
+
return _sharedUi.ignoredPatternsStore;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
Object.defineProperty(exports, "isUrlIgnored", {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
get: function () {
|
|
75
|
+
return _sharedUi.isUrlIgnored;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
60
78
|
Object.defineProperty(exports, "networkEventStore", {
|
|
61
79
|
enumerable: true,
|
|
62
80
|
get: function () {
|
|
@@ -69,6 +87,12 @@ Object.defineProperty(exports, "networkListener", {
|
|
|
69
87
|
return _networkListener.networkListener;
|
|
70
88
|
}
|
|
71
89
|
});
|
|
90
|
+
Object.defineProperty(exports, "networkSyncAdapter", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
get: function () {
|
|
93
|
+
return _networkSyncAdapter.networkSyncAdapter;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
72
96
|
Object.defineProperty(exports, "networkToolPreset", {
|
|
73
97
|
enumerable: true,
|
|
74
98
|
get: function () {
|
|
@@ -81,6 +105,18 @@ Object.defineProperty(exports, "removeAllNetworkListeners", {
|
|
|
81
105
|
return _networkListener.removeAllNetworkListeners;
|
|
82
106
|
}
|
|
83
107
|
});
|
|
108
|
+
Object.defineProperty(exports, "urlMatchesIgnoredPattern", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get: function () {
|
|
111
|
+
return _sharedUi.urlMatchesIgnoredPattern;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
Object.defineProperty(exports, "useIgnoredPatterns", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: function () {
|
|
117
|
+
return _sharedUi.useIgnoredPatterns;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
84
120
|
Object.defineProperty(exports, "useNetworkEvents", {
|
|
85
121
|
enumerable: true,
|
|
86
122
|
get: function () {
|
|
@@ -100,5 +136,8 @@ var _NetworkEventItemCompact = require("./network/components/NetworkEventItemCom
|
|
|
100
136
|
var _useNetworkEvents = require("./network/hooks/useNetworkEvents");
|
|
101
137
|
var _useTickEveryMinute = require("./network/hooks/useTickEveryMinute");
|
|
102
138
|
var _formatting = require("./network/utils/formatting");
|
|
139
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
140
|
+
var _networkSyncAdapter = require("./network/sync/networkSyncAdapter");
|
|
141
|
+
var _networkBodyResolver = require("./network/sync/networkBodyResolver");
|
|
103
142
|
var _networkEventStore = require("./network/utils/networkEventStore");
|
|
104
143
|
var _networkListener = require("./network/utils/networkListener");
|
|
@@ -10,6 +10,7 @@ var _sharedUi = require("@buoy-gg/shared-ui");
|
|
|
10
10
|
var _formatting = require("../utils/formatting");
|
|
11
11
|
var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
|
|
12
12
|
var _formatGraphQLVariables = require("../utils/formatGraphQLVariables");
|
|
13
|
+
var _networkBodyResolver = require("../sync/networkBodyResolver");
|
|
13
14
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
15
|
// Component for collapsible sections matching Sentry style
|
|
15
16
|
const CollapsibleSection = ({
|
|
@@ -189,6 +190,37 @@ function NetworkEventDetailView({
|
|
|
189
190
|
}
|
|
190
191
|
}, [showUpgradeModal, isPro]);
|
|
191
192
|
|
|
193
|
+
// Large bodies are omitted from synced snapshots (see networkSyncAdapter) and
|
|
194
|
+
// fetched on demand through the resolver. On the device there is no resolver
|
|
195
|
+
// and bodies are always inline, so this is a no-op there.
|
|
196
|
+
const resolveBody = (0, _networkBodyResolver.useNetworkBodyResolver)();
|
|
197
|
+
const requestBodyOmitted = event.requestData === undefined && (event.requestSize ?? 0) > 0;
|
|
198
|
+
const responseBodyOmitted = event.responseData === undefined && (event.responseSize ?? 0) > 0;
|
|
199
|
+
const [resolvedBody, setResolvedBody] = (0, _react.useState)(null);
|
|
200
|
+
const [isLoadingBody, setIsLoadingBody] = (0, _react.useState)(false);
|
|
201
|
+
(0, _react.useEffect)(() => {
|
|
202
|
+
setResolvedBody(null);
|
|
203
|
+
if (!resolveBody || !requestBodyOmitted && !responseBodyOmitted) {
|
|
204
|
+
setIsLoadingBody(false);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
let cancelled = false;
|
|
208
|
+
setIsLoadingBody(true);
|
|
209
|
+
resolveBody(event.id).then(body => {
|
|
210
|
+
if (!cancelled) setResolvedBody(body);
|
|
211
|
+
}).catch(() => {}).finally(() => {
|
|
212
|
+
if (!cancelled) setIsLoadingBody(false);
|
|
213
|
+
});
|
|
214
|
+
return () => {
|
|
215
|
+
cancelled = true;
|
|
216
|
+
};
|
|
217
|
+
}, [event.id, resolveBody, requestBodyOmitted, responseBodyOmitted]);
|
|
218
|
+
|
|
219
|
+
// Effective bodies: inline data when present, otherwise the lazily resolved
|
|
220
|
+
// body. `null`/`undefined` both collapse to undefined ("no body").
|
|
221
|
+
const requestData = event.requestData ?? resolvedBody?.requestData ?? undefined;
|
|
222
|
+
const responseData = event.responseData ?? resolvedBody?.responseData ?? undefined;
|
|
223
|
+
|
|
192
224
|
// Generate full request details for copying
|
|
193
225
|
const getFullRequestDetails = () => {
|
|
194
226
|
const requestDetails = {
|
|
@@ -199,9 +231,9 @@ function NetworkEventDetailView({
|
|
|
199
231
|
timestamp: new Date(event.timestamp).toISOString(),
|
|
200
232
|
duration: event.duration ? `${event.duration}ms` : "N/A",
|
|
201
233
|
requestHeaders: event.requestHeaders,
|
|
202
|
-
requestData
|
|
234
|
+
requestData,
|
|
203
235
|
responseHeaders: event.responseHeaders,
|
|
204
|
-
responseData
|
|
236
|
+
responseData,
|
|
205
237
|
requestSize: event.requestSize ? (0, _formatting.formatBytes)(event.requestSize) : "N/A",
|
|
206
238
|
responseSize: event.responseSize ? (0, _formatting.formatBytes)(event.responseSize) : "N/A",
|
|
207
239
|
error: event.error,
|
|
@@ -298,7 +330,7 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
298
330
|
})]
|
|
299
331
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(UrlBreakdown, {
|
|
300
332
|
url: event.url,
|
|
301
|
-
requestData:
|
|
333
|
+
requestData: requestData,
|
|
302
334
|
operationName: event.operationName,
|
|
303
335
|
graphqlVariables: event.graphqlVariables,
|
|
304
336
|
requestClient: event.requestClient,
|
|
@@ -401,7 +433,7 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
401
433
|
style: styles.emptyText,
|
|
402
434
|
children: "No response headers yet"
|
|
403
435
|
})
|
|
404
|
-
}),
|
|
436
|
+
}), requestData !== undefined ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CollapsibleSection, {
|
|
405
437
|
title: "Request Body",
|
|
406
438
|
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileJson, {
|
|
407
439
|
size: 14,
|
|
@@ -412,14 +444,25 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
412
444
|
style: styles.dataViewerContainer,
|
|
413
445
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
|
|
414
446
|
title: "",
|
|
415
|
-
data:
|
|
447
|
+
data: requestData,
|
|
416
448
|
showTypeFilter: true,
|
|
417
449
|
rawMode: true,
|
|
418
450
|
initialExpanded: true,
|
|
419
451
|
disableCopy: !isPro
|
|
420
452
|
})
|
|
421
453
|
})
|
|
422
|
-
}) :
|
|
454
|
+
}) : requestBodyOmitted ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CollapsibleSection, {
|
|
455
|
+
title: "Request Body",
|
|
456
|
+
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileJson, {
|
|
457
|
+
size: 14,
|
|
458
|
+
color: _sharedUi.macOSColors.semantic.info
|
|
459
|
+
}),
|
|
460
|
+
defaultOpen: true,
|
|
461
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
462
|
+
style: styles.emptyText,
|
|
463
|
+
children: isLoadingBody ? `Loading request body (${(0, _formatting.formatBytes)(event.requestSize ?? 0)})…` : "Request body unavailable"
|
|
464
|
+
})
|
|
465
|
+
}) : null, responseData !== undefined ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CollapsibleSection, {
|
|
423
466
|
title: "Response Body",
|
|
424
467
|
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileJson, {
|
|
425
468
|
size: 14,
|
|
@@ -430,13 +473,24 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
430
473
|
style: styles.dataViewerContainer,
|
|
431
474
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
|
|
432
475
|
title: "",
|
|
433
|
-
data:
|
|
476
|
+
data: responseData,
|
|
434
477
|
showTypeFilter: true,
|
|
435
478
|
rawMode: true,
|
|
436
479
|
initialExpanded: true,
|
|
437
480
|
disableCopy: !isPro
|
|
438
481
|
})
|
|
439
482
|
})
|
|
483
|
+
}) : responseBodyOmitted ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CollapsibleSection, {
|
|
484
|
+
title: "Response Body",
|
|
485
|
+
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.FileJson, {
|
|
486
|
+
size: 14,
|
|
487
|
+
color: _sharedUi.macOSColors.semantic.success
|
|
488
|
+
}),
|
|
489
|
+
defaultOpen: true,
|
|
490
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
491
|
+
style: styles.emptyText,
|
|
492
|
+
children: isLoadingBody ? `Loading response body (${(0, _formatting.formatBytes)(event.responseSize ?? 0)})…` : "Response body unavailable"
|
|
493
|
+
})
|
|
440
494
|
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(CollapsibleSection, {
|
|
441
495
|
title: "Filter Options",
|
|
442
496
|
icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Filter, {
|
|
@@ -159,7 +159,7 @@ const NetworkEventItemCompact = exports.NetworkEventItemCompact = /*#__PURE__*/(
|
|
|
159
159
|
event: event,
|
|
160
160
|
isPending: isPending,
|
|
161
161
|
statusColor: statusColor
|
|
162
|
-
}), event.requestClient
|
|
162
|
+
}), event.requestClient ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
163
163
|
style: [styles.topRightBadge, {
|
|
164
164
|
backgroundColor: event.requestClient === "fetch" ? "rgba(74, 144, 226, 0.15)" : event.requestClient === "graphql" ? "rgba(229, 53, 171, 0.15)" : event.requestClient === "grpc-web" ? "rgba(16, 185, 129, 0.15)" : event.requestClient === "xhr" ? "rgba(245, 158, 11, 0.15)" : "rgba(147, 51, 234, 0.15)"
|
|
165
165
|
}],
|
|
@@ -169,7 +169,7 @@ const NetworkEventItemCompact = exports.NetworkEventItemCompact = /*#__PURE__*/(
|
|
|
169
169
|
}],
|
|
170
170
|
children: event.requestClient === "graphql" ? "GQL" : event.requestClient === "grpc-web" ? "gRPC" : event.requestClient === "xhr" ? "XHR" : event.requestClient
|
|
171
171
|
})
|
|
172
|
-
})]
|
|
172
|
+
}) : null]
|
|
173
173
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
174
174
|
style: styles.leftSection,
|
|
175
175
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.MethodBadge, {
|
|
@@ -15,33 +15,6 @@ var _NetworkCopySettingsView = require("./NetworkCopySettingsView");
|
|
|
15
15
|
var _useNetworkEvents = require("../hooks/useNetworkEvents");
|
|
16
16
|
var _formatting = require("../utils/formatting");
|
|
17
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
|
-
/**
|
|
19
|
-
* Returns true when `url` matches the ignored `pattern` according to its mode.
|
|
20
|
-
*
|
|
21
|
-
* `contains` → case-insensitive substring on the full URL (legacy behavior).
|
|
22
|
-
* `exact` → smart equality: pattern starting with `/` compares URL.pathname,
|
|
23
|
-
* full URLs compare origin+pathname (ignoring query/hash), bare
|
|
24
|
-
* values compare URL.host. Falls back to literal equality if URL
|
|
25
|
-
* parsing fails (e.g. relative URLs).
|
|
26
|
-
*/function urlMatchesIgnoredPattern(url, pattern) {
|
|
27
|
-
const lowerUrl = url.toLowerCase();
|
|
28
|
-
const lowerValue = pattern.value.toLowerCase();
|
|
29
|
-
if (pattern.mode === "contains") {
|
|
30
|
-
return lowerUrl.includes(lowerValue);
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const parsed = new URL(url);
|
|
34
|
-
if (lowerValue.startsWith("/")) {
|
|
35
|
-
return parsed.pathname.toLowerCase() === lowerValue;
|
|
36
|
-
}
|
|
37
|
-
if (lowerValue.startsWith("http://") || lowerValue.startsWith("https://")) {
|
|
38
|
-
return `${parsed.origin}${parsed.pathname}`.toLowerCase() === lowerValue;
|
|
39
|
-
}
|
|
40
|
-
return parsed.host.toLowerCase() === lowerValue;
|
|
41
|
-
} catch {
|
|
42
|
-
return lowerUrl === lowerValue;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
18
|
// Decompose by Responsibility: Extract empty state component
|
|
46
19
|
function EmptyState({
|
|
47
20
|
isEnabled
|
|
@@ -104,15 +77,20 @@ function NetworkModalInner({
|
|
|
104
77
|
const [searchText, setSearchText] = (0, _react.useState)("");
|
|
105
78
|
const [isSearchActive, setIsSearchActive] = (0, _react.useState)(false);
|
|
106
79
|
const searchInputRef = (0, _react.useRef)(null);
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
80
|
+
// Ignored domains/URL patterns now live in a shared, persisted singleton store
|
|
81
|
+
// (@buoy-gg/shared-ui) so the Network tool and the Events tool stay in sync and
|
|
82
|
+
// share the exact same filter logic. The four handlers below are thin wrappers
|
|
83
|
+
// exposed by the hook; `values` is the derived Set used for presence checks.
|
|
84
|
+
const {
|
|
85
|
+
patterns: ignoredPatterns,
|
|
86
|
+
values: ignoredPatternValues,
|
|
87
|
+
add: addIgnoredPattern,
|
|
88
|
+
remove: removeIgnoredPattern,
|
|
89
|
+
toggle: toggleIgnoredPattern,
|
|
90
|
+
toggleMode: toggleIgnoredPatternMode
|
|
91
|
+
} = (0, _sharedUi.useIgnoredPatterns)();
|
|
113
92
|
const [copySettings, setCopySettings] = (0, _react.useState)(_NetworkCopySettingsView.DEFAULT_COPY_SETTINGS);
|
|
114
93
|
const flatListRef = (0, _react.useRef)(null);
|
|
115
|
-
const hasLoadedFilters = (0, _react.useRef)(false);
|
|
116
94
|
const hasLoadedCopySettings = (0, _react.useRef)(false);
|
|
117
95
|
const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
|
|
118
96
|
|
|
@@ -123,62 +101,6 @@ function NetworkModalInner({
|
|
|
123
101
|
}
|
|
124
102
|
}, [showUpgradeModal, isPro]);
|
|
125
103
|
|
|
126
|
-
// Load persisted filters on mount
|
|
127
|
-
(0, _react.useEffect)(() => {
|
|
128
|
-
if (!visible || hasLoadedFilters.current) return;
|
|
129
|
-
const loadFilters = async () => {
|
|
130
|
-
try {
|
|
131
|
-
const storedPatterns = await _sharedUi.persistentStorage.getItem(_sharedUi.devToolsStorageKeys.network.ignoredDomains());
|
|
132
|
-
if (storedPatterns) {
|
|
133
|
-
// Tolerate legacy string[] format — entries without a mode default to "contains".
|
|
134
|
-
const raw = JSON.parse(storedPatterns);
|
|
135
|
-
const migrated = [];
|
|
136
|
-
for (const entry of raw) {
|
|
137
|
-
if (typeof entry === "string" && entry.trim()) {
|
|
138
|
-
migrated.push({
|
|
139
|
-
value: entry,
|
|
140
|
-
mode: "contains"
|
|
141
|
-
});
|
|
142
|
-
} else if (entry && typeof entry === "object" && typeof entry.value === "string") {
|
|
143
|
-
const e = entry;
|
|
144
|
-
migrated.push({
|
|
145
|
-
value: e.value,
|
|
146
|
-
mode: e.mode === "exact" ? "exact" : "contains"
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// Always ensure Buoy license API is hidden
|
|
151
|
-
if (!migrated.some(p => p.value === "api.keygen.sh")) {
|
|
152
|
-
migrated.push({
|
|
153
|
-
value: "api.keygen.sh",
|
|
154
|
-
mode: "contains"
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
setIgnoredPatterns(migrated);
|
|
158
|
-
}
|
|
159
|
-
} catch (_error) {
|
|
160
|
-
// Silently fail - filters will use defaults
|
|
161
|
-
} finally {
|
|
162
|
-
hasLoadedFilters.current = true;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
loadFilters();
|
|
166
|
-
}, [visible]);
|
|
167
|
-
|
|
168
|
-
// Save filters when they change
|
|
169
|
-
(0, _react.useEffect)(() => {
|
|
170
|
-
if (!hasLoadedFilters.current) return; // Don't save on initial load
|
|
171
|
-
|
|
172
|
-
const saveFilters = async () => {
|
|
173
|
-
try {
|
|
174
|
-
await _sharedUi.persistentStorage.setItem(_sharedUi.devToolsStorageKeys.network.ignoredDomains(), JSON.stringify(ignoredPatterns));
|
|
175
|
-
} catch (_error) {
|
|
176
|
-
// Silently fail - filters will remain in memory
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
saveFilters();
|
|
180
|
-
}, [ignoredPatterns]);
|
|
181
|
-
|
|
182
104
|
// Load persisted copy settings on mount
|
|
183
105
|
(0, _react.useEffect)(() => {
|
|
184
106
|
if (!visible || hasLoadedCopySettings.current) return;
|
|
@@ -234,49 +156,15 @@ function NetworkModalInner({
|
|
|
234
156
|
}
|
|
235
157
|
}, [isSearchActive]);
|
|
236
158
|
|
|
237
|
-
// Derived set of pattern values (for components that only care about presence).
|
|
238
|
-
const ignoredPatternValues = (0, _react.useMemo)(() => new Set(ignoredPatterns.map(p => p.value)), [ignoredPatterns]);
|
|
239
|
-
|
|
240
159
|
// Filter events based on ignored patterns (mode-aware)
|
|
241
160
|
const filteredEvents = (0, _react.useMemo)(() => {
|
|
242
161
|
if (ignoredPatterns.length === 0) return events;
|
|
243
162
|
return events.filter(event => {
|
|
244
|
-
const isFiltered = ignoredPatterns.some(pattern => urlMatchesIgnoredPattern(event.url, pattern));
|
|
163
|
+
const isFiltered = ignoredPatterns.some(pattern => (0, _sharedUi.urlMatchesIgnoredPattern)(event.url, pattern));
|
|
245
164
|
return !isFiltered;
|
|
246
165
|
});
|
|
247
166
|
}, [events, ignoredPatterns]);
|
|
248
167
|
|
|
249
|
-
// Add or no-op (used by add-pattern UI and suggested-items taps)
|
|
250
|
-
const addIgnoredPattern = (0, _react.useCallback)(pattern => {
|
|
251
|
-
const value = pattern.value.trim();
|
|
252
|
-
if (!value) return;
|
|
253
|
-
setIgnoredPatterns(prev => prev.some(p => p.value === value) ? prev : [...prev, {
|
|
254
|
-
value,
|
|
255
|
-
mode: pattern.mode
|
|
256
|
-
}]);
|
|
257
|
-
}, []);
|
|
258
|
-
|
|
259
|
-
// Remove by value (used by the filter list X button and the detail view chips)
|
|
260
|
-
const removeIgnoredPattern = (0, _react.useCallback)(value => {
|
|
261
|
-
setIgnoredPatterns(prev => prev.filter(p => p.value !== value));
|
|
262
|
-
}, []);
|
|
263
|
-
|
|
264
|
-
// Toggle presence (used by the detail-view domain/url chips — adds as contains)
|
|
265
|
-
const toggleIgnoredPattern = (0, _react.useCallback)(value => {
|
|
266
|
-
setIgnoredPatterns(prev => prev.some(p => p.value === value) ? prev.filter(p => p.value !== value) : [...prev, {
|
|
267
|
-
value,
|
|
268
|
-
mode: "contains"
|
|
269
|
-
}]);
|
|
270
|
-
}, []);
|
|
271
|
-
|
|
272
|
-
// Flip an existing pattern's mode between contains <-> exact
|
|
273
|
-
const toggleIgnoredPatternMode = (0, _react.useCallback)(value => {
|
|
274
|
-
setIgnoredPatterns(prev => prev.map(p => p.value === value ? {
|
|
275
|
-
...p,
|
|
276
|
-
mode: p.mode === "contains" ? "exact" : "contains"
|
|
277
|
-
} : p));
|
|
278
|
-
}, []);
|
|
279
|
-
|
|
280
168
|
// Helper to check if payload should be included based on size
|
|
281
169
|
const shouldIncludePayload = (0, _react.useCallback)(data => {
|
|
282
170
|
if (copySettings.bodySizeThreshold === -1) return true;
|
|
@@ -417,7 +305,7 @@ function NetworkModalInner({
|
|
|
417
305
|
let successful = 0;
|
|
418
306
|
let failed = 0;
|
|
419
307
|
let pending = 0;
|
|
420
|
-
const eventsForStats = ignoredPatterns.length === 0 ? allEvents : allEvents.filter(event => !ignoredPatterns.some(pattern => urlMatchesIgnoredPattern(event.url, pattern)));
|
|
308
|
+
const eventsForStats = ignoredPatterns.length === 0 ? allEvents : allEvents.filter(event => !ignoredPatterns.some(pattern => (0, _sharedUi.urlMatchesIgnoredPattern)(event.url, pattern)));
|
|
421
309
|
for (const event of eventsForStats) {
|
|
422
310
|
if (event.status && event.status >= 200 && event.status < 300) {
|
|
423
311
|
successful++;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NetworkBodyResolverProvider = NetworkBodyResolverProvider;
|
|
7
|
+
exports.useNetworkBodyResolver = useNetworkBodyResolver;
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
/**
|
|
11
|
+
* The full request/response bodies for a single network event, fetched on
|
|
12
|
+
* demand. Either field may be `null` when the event genuinely has no body.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolves request/response bodies that were omitted from a synced snapshot.
|
|
17
|
+
*
|
|
18
|
+
* Large bodies (see `networkSyncAdapter`) are stripped from the list snapshot
|
|
19
|
+
* so they aren't re-streamed every ~200ms; the detail view fetches them lazily
|
|
20
|
+
* through this resolver when a request is opened.
|
|
21
|
+
*
|
|
22
|
+
* On the device this is never provided — bodies are always inline — so the
|
|
23
|
+
* detail view simply falls back to the inline data. The desktop dashboard
|
|
24
|
+
* provides a resolver that fetches the body over the devtool protocol.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const NetworkBodyResolverContext = /*#__PURE__*/(0, _react.createContext)(null);
|
|
28
|
+
function NetworkBodyResolverProvider({
|
|
29
|
+
resolver,
|
|
30
|
+
children
|
|
31
|
+
}) {
|
|
32
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(NetworkBodyResolverContext.Provider, {
|
|
33
|
+
value: resolver,
|
|
34
|
+
children: children
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function useNetworkBodyResolver() {
|
|
38
|
+
return (0, _react.useContext)(NetworkBodyResolverContext);
|
|
39
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.networkSyncAdapter = exports.SNAPSHOT_BODY_INLINE_LIMIT = void 0;
|
|
7
|
+
var _networkEventStore = require("../utils/networkEventStore");
|
|
8
|
+
/**
|
|
9
|
+
* Request/response bodies larger than this (using the cached size proxy already
|
|
10
|
+
* on the event — no per-snapshot stringify) are NOT streamed in the list
|
|
11
|
+
* snapshot. These are the multi-MB payloads (images, large JSON list responses)
|
|
12
|
+
* that, re-sent every ~200ms for up to 500 events, freeze the desktop
|
|
13
|
+
* dashboard's main thread while it deserializes and re-renders them. Smaller
|
|
14
|
+
* bodies stay inline so the common case needs no round-trip (instant detail
|
|
15
|
+
* view, and they remain available to copy/export). Stripped bodies are fetched
|
|
16
|
+
* on demand via the `getEventBody` action when a request's detail view opens.
|
|
17
|
+
*/
|
|
18
|
+
const SNAPSHOT_BODY_INLINE_LIMIT = exports.SNAPSHOT_BODY_INLINE_LIMIT = 16 * 1024;
|
|
19
|
+
const stripLargeBodies = event => {
|
|
20
|
+
const stripRequest = event.requestData !== undefined && (event.requestSize ?? 0) > SNAPSHOT_BODY_INLINE_LIMIT;
|
|
21
|
+
const stripResponse = event.responseData !== undefined && (event.responseSize ?? 0) > SNAPSHOT_BODY_INLINE_LIMIT;
|
|
22
|
+
if (!stripRequest && !stripResponse) return event;
|
|
23
|
+
// Keep sizes/headers/metadata; only drop the heavy data. `undefined` keys are
|
|
24
|
+
// omitted by JSON serialization, so this shrinks the wire payload, and the
|
|
25
|
+
// dashboard treats "data missing but size > 0" as "fetch on demand".
|
|
26
|
+
const stripped = {
|
|
27
|
+
...event
|
|
28
|
+
};
|
|
29
|
+
if (stripRequest) stripped.requestData = undefined;
|
|
30
|
+
if (stripResponse) stripped.responseData = undefined;
|
|
31
|
+
return stripped;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sync adapter for the network tool, consumed by @buoy-gg/external-sync's
|
|
36
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
37
|
+
* this package doesn't need a dependency on it).
|
|
38
|
+
*
|
|
39
|
+
* Subscribing starts the network interceptor, so requests are only captured
|
|
40
|
+
* while a dashboard is watching the network panel.
|
|
41
|
+
*
|
|
42
|
+
* v2: large bodies are stripped from the snapshot and fetched lazily via
|
|
43
|
+
* `getEventBody` (see SNAPSHOT_BODY_INLINE_LIMIT).
|
|
44
|
+
*/
|
|
45
|
+
const networkSyncAdapter = exports.networkSyncAdapter = {
|
|
46
|
+
version: 2,
|
|
47
|
+
getSnapshot: () => _networkEventStore.networkEventStore.getEvents().map(stripLargeBodies),
|
|
48
|
+
subscribe: onChange => _networkEventStore.networkEventStore.subscribeToEvents(onChange),
|
|
49
|
+
actions: {
|
|
50
|
+
clearEvents: () => {
|
|
51
|
+
_networkEventStore.networkEventStore.clearEvents();
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Return the full (un-stripped) request/response bodies for one event,
|
|
55
|
+
* fetched on demand by the dashboard when its detail view opens. Returns
|
|
56
|
+
* null if the event is no longer in the store.
|
|
57
|
+
*/
|
|
58
|
+
getEventBody: params => {
|
|
59
|
+
const id = params && typeof params === "object" ? params.id : undefined;
|
|
60
|
+
if (!id) return null;
|
|
61
|
+
const event = _networkEventStore.networkEventStore.getEventById(id);
|
|
62
|
+
if (!event) return null;
|
|
63
|
+
return {
|
|
64
|
+
requestData: event.requestData ?? null,
|
|
65
|
+
responseData: event.responseData ?? null
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
package/lib/module/index.js
CHANGED
|
@@ -37,6 +37,20 @@ export { formatBytes, formatDuration, formatHttpStatus } from "./network/utils/f
|
|
|
37
37
|
// TYPES (For TypeScript users)
|
|
38
38
|
// =============================================================================
|
|
39
39
|
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// SHARED IGNORED-PATTERN FILTERING
|
|
42
|
+
// Re-exported from @buoy-gg/shared-ui so consumers wiring the Network detail
|
|
43
|
+
// page into other tools (e.g. the Events tool) can share the exact same
|
|
44
|
+
// ignored-domains/URL filter store + matching logic.
|
|
45
|
+
// =============================================================================
|
|
46
|
+
export { useIgnoredPatterns, ignoredPatternsStore, urlMatchesIgnoredPattern, isUrlIgnored } from "@buoy-gg/shared-ui";
|
|
47
|
+
|
|
48
|
+
// =============================================================================
|
|
49
|
+
// EXTERNAL SYNC (Adapter for @buoy-gg/external-sync's useExternalSync)
|
|
50
|
+
// =============================================================================
|
|
51
|
+
export { networkSyncAdapter } from "./network/sync/networkSyncAdapter";
|
|
52
|
+
export { NetworkBodyResolverProvider } from "./network/sync/networkBodyResolver";
|
|
53
|
+
|
|
40
54
|
// =============================================================================
|
|
41
55
|
// INTERNAL EXPORTS (For @buoy-gg/* packages only - not part of public API)
|
|
42
56
|
// These are re-exported for cross-package communication within the monorepo.
|
|
@@ -7,6 +7,7 @@ import { formatBytes, formatDuration, formatHttpStatus } from "../utils/formatti
|
|
|
7
7
|
import { formatRelativeTime } from "@buoy-gg/shared-ui";
|
|
8
8
|
import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
|
|
9
9
|
import { formatGraphQLDisplay } from "../utils/formatGraphQLVariables";
|
|
10
|
+
import { useNetworkBodyResolver } from "../sync/networkBodyResolver";
|
|
10
11
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
12
|
// Component for collapsible sections matching Sentry style
|
|
12
13
|
const CollapsibleSection = ({
|
|
@@ -186,6 +187,37 @@ export function NetworkEventDetailView({
|
|
|
186
187
|
}
|
|
187
188
|
}, [showUpgradeModal, isPro]);
|
|
188
189
|
|
|
190
|
+
// Large bodies are omitted from synced snapshots (see networkSyncAdapter) and
|
|
191
|
+
// fetched on demand through the resolver. On the device there is no resolver
|
|
192
|
+
// and bodies are always inline, so this is a no-op there.
|
|
193
|
+
const resolveBody = useNetworkBodyResolver();
|
|
194
|
+
const requestBodyOmitted = event.requestData === undefined && (event.requestSize ?? 0) > 0;
|
|
195
|
+
const responseBodyOmitted = event.responseData === undefined && (event.responseSize ?? 0) > 0;
|
|
196
|
+
const [resolvedBody, setResolvedBody] = useState(null);
|
|
197
|
+
const [isLoadingBody, setIsLoadingBody] = useState(false);
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
setResolvedBody(null);
|
|
200
|
+
if (!resolveBody || !requestBodyOmitted && !responseBodyOmitted) {
|
|
201
|
+
setIsLoadingBody(false);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let cancelled = false;
|
|
205
|
+
setIsLoadingBody(true);
|
|
206
|
+
resolveBody(event.id).then(body => {
|
|
207
|
+
if (!cancelled) setResolvedBody(body);
|
|
208
|
+
}).catch(() => {}).finally(() => {
|
|
209
|
+
if (!cancelled) setIsLoadingBody(false);
|
|
210
|
+
});
|
|
211
|
+
return () => {
|
|
212
|
+
cancelled = true;
|
|
213
|
+
};
|
|
214
|
+
}, [event.id, resolveBody, requestBodyOmitted, responseBodyOmitted]);
|
|
215
|
+
|
|
216
|
+
// Effective bodies: inline data when present, otherwise the lazily resolved
|
|
217
|
+
// body. `null`/`undefined` both collapse to undefined ("no body").
|
|
218
|
+
const requestData = event.requestData ?? resolvedBody?.requestData ?? undefined;
|
|
219
|
+
const responseData = event.responseData ?? resolvedBody?.responseData ?? undefined;
|
|
220
|
+
|
|
189
221
|
// Generate full request details for copying
|
|
190
222
|
const getFullRequestDetails = () => {
|
|
191
223
|
const requestDetails = {
|
|
@@ -196,9 +228,9 @@ export function NetworkEventDetailView({
|
|
|
196
228
|
timestamp: new Date(event.timestamp).toISOString(),
|
|
197
229
|
duration: event.duration ? `${event.duration}ms` : "N/A",
|
|
198
230
|
requestHeaders: event.requestHeaders,
|
|
199
|
-
requestData
|
|
231
|
+
requestData,
|
|
200
232
|
responseHeaders: event.responseHeaders,
|
|
201
|
-
responseData
|
|
233
|
+
responseData,
|
|
202
234
|
requestSize: event.requestSize ? formatBytes(event.requestSize) : "N/A",
|
|
203
235
|
responseSize: event.responseSize ? formatBytes(event.responseSize) : "N/A",
|
|
204
236
|
error: event.error,
|
|
@@ -295,7 +327,7 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
295
327
|
})]
|
|
296
328
|
}), /*#__PURE__*/_jsx(UrlBreakdown, {
|
|
297
329
|
url: event.url,
|
|
298
|
-
requestData:
|
|
330
|
+
requestData: requestData,
|
|
299
331
|
operationName: event.operationName,
|
|
300
332
|
graphqlVariables: event.graphqlVariables,
|
|
301
333
|
requestClient: event.requestClient,
|
|
@@ -398,7 +430,7 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
398
430
|
style: styles.emptyText,
|
|
399
431
|
children: "No response headers yet"
|
|
400
432
|
})
|
|
401
|
-
}),
|
|
433
|
+
}), requestData !== undefined ? /*#__PURE__*/_jsx(CollapsibleSection, {
|
|
402
434
|
title: "Request Body",
|
|
403
435
|
icon: /*#__PURE__*/_jsx(FileJson, {
|
|
404
436
|
size: 14,
|
|
@@ -409,14 +441,25 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
409
441
|
style: styles.dataViewerContainer,
|
|
410
442
|
children: /*#__PURE__*/_jsx(DataViewer, {
|
|
411
443
|
title: "",
|
|
412
|
-
data:
|
|
444
|
+
data: requestData,
|
|
413
445
|
showTypeFilter: true,
|
|
414
446
|
rawMode: true,
|
|
415
447
|
initialExpanded: true,
|
|
416
448
|
disableCopy: !isPro
|
|
417
449
|
})
|
|
418
450
|
})
|
|
419
|
-
}) :
|
|
451
|
+
}) : requestBodyOmitted ? /*#__PURE__*/_jsx(CollapsibleSection, {
|
|
452
|
+
title: "Request Body",
|
|
453
|
+
icon: /*#__PURE__*/_jsx(FileJson, {
|
|
454
|
+
size: 14,
|
|
455
|
+
color: macOSColors.semantic.info
|
|
456
|
+
}),
|
|
457
|
+
defaultOpen: true,
|
|
458
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
459
|
+
style: styles.emptyText,
|
|
460
|
+
children: isLoadingBody ? `Loading request body (${formatBytes(event.requestSize ?? 0)})…` : "Request body unavailable"
|
|
461
|
+
})
|
|
462
|
+
}) : null, responseData !== undefined ? /*#__PURE__*/_jsx(CollapsibleSection, {
|
|
420
463
|
title: "Response Body",
|
|
421
464
|
icon: /*#__PURE__*/_jsx(FileJson, {
|
|
422
465
|
size: 14,
|
|
@@ -427,13 +470,24 @@ ${JSON.stringify(requestDetails.responseData, null, 2)}
|
|
|
427
470
|
style: styles.dataViewerContainer,
|
|
428
471
|
children: /*#__PURE__*/_jsx(DataViewer, {
|
|
429
472
|
title: "",
|
|
430
|
-
data:
|
|
473
|
+
data: responseData,
|
|
431
474
|
showTypeFilter: true,
|
|
432
475
|
rawMode: true,
|
|
433
476
|
initialExpanded: true,
|
|
434
477
|
disableCopy: !isPro
|
|
435
478
|
})
|
|
436
479
|
})
|
|
480
|
+
}) : responseBodyOmitted ? /*#__PURE__*/_jsx(CollapsibleSection, {
|
|
481
|
+
title: "Response Body",
|
|
482
|
+
icon: /*#__PURE__*/_jsx(FileJson, {
|
|
483
|
+
size: 14,
|
|
484
|
+
color: macOSColors.semantic.success
|
|
485
|
+
}),
|
|
486
|
+
defaultOpen: true,
|
|
487
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
488
|
+
style: styles.emptyText,
|
|
489
|
+
children: isLoadingBody ? `Loading response body (${formatBytes(event.responseSize ?? 0)})…` : "Response body unavailable"
|
|
490
|
+
})
|
|
437
491
|
}) : null, /*#__PURE__*/_jsx(CollapsibleSection, {
|
|
438
492
|
title: "Filter Options",
|
|
439
493
|
icon: /*#__PURE__*/_jsx(Filter, {
|
|
@@ -156,7 +156,7 @@ export const NetworkEventItemCompact = /*#__PURE__*/memo(({
|
|
|
156
156
|
event: event,
|
|
157
157
|
isPending: isPending,
|
|
158
158
|
statusColor: statusColor
|
|
159
|
-
}), event.requestClient
|
|
159
|
+
}), event.requestClient ? /*#__PURE__*/_jsx(View, {
|
|
160
160
|
style: [styles.topRightBadge, {
|
|
161
161
|
backgroundColor: event.requestClient === "fetch" ? "rgba(74, 144, 226, 0.15)" : event.requestClient === "graphql" ? "rgba(229, 53, 171, 0.15)" : event.requestClient === "grpc-web" ? "rgba(16, 185, 129, 0.15)" : event.requestClient === "xhr" ? "rgba(245, 158, 11, 0.15)" : "rgba(147, 51, 234, 0.15)"
|
|
162
162
|
}],
|
|
@@ -166,7 +166,7 @@ export const NetworkEventItemCompact = /*#__PURE__*/memo(({
|
|
|
166
166
|
}],
|
|
167
167
|
children: event.requestClient === "graphql" ? "GQL" : event.requestClient === "grpc-web" ? "gRPC" : event.requestClient === "xhr" ? "XHR" : event.requestClient
|
|
168
168
|
})
|
|
169
|
-
})]
|
|
169
|
+
}) : null]
|
|
170
170
|
}), /*#__PURE__*/_jsxs(View, {
|
|
171
171
|
style: styles.leftSection,
|
|
172
172
|
children: [/*#__PURE__*/_jsx(MethodBadge, {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useRef, useMemo, useCallback, useEffect } from "react";
|
|
4
4
|
import { View, Text, StyleSheet, TouchableOpacity, TextInput, FlatList } from "react-native";
|
|
5
|
-
import { Globe, Trash2, Power, Search, Filter, CheckCircle, XCircle, Clock, X, Copy, JsModal, ModalHeader, devToolsStorageKeys, macOSColors, persistentStorage, CopyButton, TabSelector, useFeatureGate, UpgradeModal, ProBadge, Zap, PowerToggleButton } from "@buoy-gg/shared-ui";
|
|
5
|
+
import { Globe, Trash2, Power, Search, Filter, CheckCircle, XCircle, Clock, X, Copy, JsModal, ModalHeader, devToolsStorageKeys, macOSColors, persistentStorage, CopyButton, TabSelector, useFeatureGate, UpgradeModal, ProBadge, Zap, PowerToggleButton, useIgnoredPatterns, urlMatchesIgnoredPattern } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { NetworkEventItemCompact } from "./NetworkEventItemCompact";
|
|
7
7
|
import { NetworkFilterViewV3 } from "./NetworkFilterViewV3";
|
|
8
8
|
import { TickProvider } from "../hooks/useTickEveryMinute";
|
|
@@ -10,36 +10,7 @@ import { NetworkEventDetailView } from "./NetworkEventDetailView";
|
|
|
10
10
|
import { NetworkCopySettingsView, DEFAULT_COPY_SETTINGS } from "./NetworkCopySettingsView";
|
|
11
11
|
import { useNetworkEvents } from "../hooks/useNetworkEvents";
|
|
12
12
|
import { formatBytes } from "../utils/formatting";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Returns true when `url` matches the ignored `pattern` according to its mode.
|
|
16
|
-
*
|
|
17
|
-
* `contains` → case-insensitive substring on the full URL (legacy behavior).
|
|
18
|
-
* `exact` → smart equality: pattern starting with `/` compares URL.pathname,
|
|
19
|
-
* full URLs compare origin+pathname (ignoring query/hash), bare
|
|
20
|
-
* values compare URL.host. Falls back to literal equality if URL
|
|
21
|
-
* parsing fails (e.g. relative URLs).
|
|
22
|
-
*/
|
|
23
13
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
24
|
-
function urlMatchesIgnoredPattern(url, pattern) {
|
|
25
|
-
const lowerUrl = url.toLowerCase();
|
|
26
|
-
const lowerValue = pattern.value.toLowerCase();
|
|
27
|
-
if (pattern.mode === "contains") {
|
|
28
|
-
return lowerUrl.includes(lowerValue);
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
const parsed = new URL(url);
|
|
32
|
-
if (lowerValue.startsWith("/")) {
|
|
33
|
-
return parsed.pathname.toLowerCase() === lowerValue;
|
|
34
|
-
}
|
|
35
|
-
if (lowerValue.startsWith("http://") || lowerValue.startsWith("https://")) {
|
|
36
|
-
return `${parsed.origin}${parsed.pathname}`.toLowerCase() === lowerValue;
|
|
37
|
-
}
|
|
38
|
-
return parsed.host.toLowerCase() === lowerValue;
|
|
39
|
-
} catch {
|
|
40
|
-
return lowerUrl === lowerValue;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
14
|
// Decompose by Responsibility: Extract empty state component
|
|
44
15
|
function EmptyState({
|
|
45
16
|
isEnabled
|
|
@@ -102,15 +73,20 @@ function NetworkModalInner({
|
|
|
102
73
|
const [searchText, setSearchText] = useState("");
|
|
103
74
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
104
75
|
const searchInputRef = useRef(null);
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
76
|
+
// Ignored domains/URL patterns now live in a shared, persisted singleton store
|
|
77
|
+
// (@buoy-gg/shared-ui) so the Network tool and the Events tool stay in sync and
|
|
78
|
+
// share the exact same filter logic. The four handlers below are thin wrappers
|
|
79
|
+
// exposed by the hook; `values` is the derived Set used for presence checks.
|
|
80
|
+
const {
|
|
81
|
+
patterns: ignoredPatterns,
|
|
82
|
+
values: ignoredPatternValues,
|
|
83
|
+
add: addIgnoredPattern,
|
|
84
|
+
remove: removeIgnoredPattern,
|
|
85
|
+
toggle: toggleIgnoredPattern,
|
|
86
|
+
toggleMode: toggleIgnoredPatternMode
|
|
87
|
+
} = useIgnoredPatterns();
|
|
111
88
|
const [copySettings, setCopySettings] = useState(DEFAULT_COPY_SETTINGS);
|
|
112
89
|
const flatListRef = useRef(null);
|
|
113
|
-
const hasLoadedFilters = useRef(false);
|
|
114
90
|
const hasLoadedCopySettings = useRef(false);
|
|
115
91
|
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
|
116
92
|
|
|
@@ -121,62 +97,6 @@ function NetworkModalInner({
|
|
|
121
97
|
}
|
|
122
98
|
}, [showUpgradeModal, isPro]);
|
|
123
99
|
|
|
124
|
-
// Load persisted filters on mount
|
|
125
|
-
useEffect(() => {
|
|
126
|
-
if (!visible || hasLoadedFilters.current) return;
|
|
127
|
-
const loadFilters = async () => {
|
|
128
|
-
try {
|
|
129
|
-
const storedPatterns = await persistentStorage.getItem(devToolsStorageKeys.network.ignoredDomains());
|
|
130
|
-
if (storedPatterns) {
|
|
131
|
-
// Tolerate legacy string[] format — entries without a mode default to "contains".
|
|
132
|
-
const raw = JSON.parse(storedPatterns);
|
|
133
|
-
const migrated = [];
|
|
134
|
-
for (const entry of raw) {
|
|
135
|
-
if (typeof entry === "string" && entry.trim()) {
|
|
136
|
-
migrated.push({
|
|
137
|
-
value: entry,
|
|
138
|
-
mode: "contains"
|
|
139
|
-
});
|
|
140
|
-
} else if (entry && typeof entry === "object" && typeof entry.value === "string") {
|
|
141
|
-
const e = entry;
|
|
142
|
-
migrated.push({
|
|
143
|
-
value: e.value,
|
|
144
|
-
mode: e.mode === "exact" ? "exact" : "contains"
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
// Always ensure Buoy license API is hidden
|
|
149
|
-
if (!migrated.some(p => p.value === "api.keygen.sh")) {
|
|
150
|
-
migrated.push({
|
|
151
|
-
value: "api.keygen.sh",
|
|
152
|
-
mode: "contains"
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
setIgnoredPatterns(migrated);
|
|
156
|
-
}
|
|
157
|
-
} catch (_error) {
|
|
158
|
-
// Silently fail - filters will use defaults
|
|
159
|
-
} finally {
|
|
160
|
-
hasLoadedFilters.current = true;
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
loadFilters();
|
|
164
|
-
}, [visible]);
|
|
165
|
-
|
|
166
|
-
// Save filters when they change
|
|
167
|
-
useEffect(() => {
|
|
168
|
-
if (!hasLoadedFilters.current) return; // Don't save on initial load
|
|
169
|
-
|
|
170
|
-
const saveFilters = async () => {
|
|
171
|
-
try {
|
|
172
|
-
await persistentStorage.setItem(devToolsStorageKeys.network.ignoredDomains(), JSON.stringify(ignoredPatterns));
|
|
173
|
-
} catch (_error) {
|
|
174
|
-
// Silently fail - filters will remain in memory
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
saveFilters();
|
|
178
|
-
}, [ignoredPatterns]);
|
|
179
|
-
|
|
180
100
|
// Load persisted copy settings on mount
|
|
181
101
|
useEffect(() => {
|
|
182
102
|
if (!visible || hasLoadedCopySettings.current) return;
|
|
@@ -232,9 +152,6 @@ function NetworkModalInner({
|
|
|
232
152
|
}
|
|
233
153
|
}, [isSearchActive]);
|
|
234
154
|
|
|
235
|
-
// Derived set of pattern values (for components that only care about presence).
|
|
236
|
-
const ignoredPatternValues = useMemo(() => new Set(ignoredPatterns.map(p => p.value)), [ignoredPatterns]);
|
|
237
|
-
|
|
238
155
|
// Filter events based on ignored patterns (mode-aware)
|
|
239
156
|
const filteredEvents = useMemo(() => {
|
|
240
157
|
if (ignoredPatterns.length === 0) return events;
|
|
@@ -244,37 +161,6 @@ function NetworkModalInner({
|
|
|
244
161
|
});
|
|
245
162
|
}, [events, ignoredPatterns]);
|
|
246
163
|
|
|
247
|
-
// Add or no-op (used by add-pattern UI and suggested-items taps)
|
|
248
|
-
const addIgnoredPattern = useCallback(pattern => {
|
|
249
|
-
const value = pattern.value.trim();
|
|
250
|
-
if (!value) return;
|
|
251
|
-
setIgnoredPatterns(prev => prev.some(p => p.value === value) ? prev : [...prev, {
|
|
252
|
-
value,
|
|
253
|
-
mode: pattern.mode
|
|
254
|
-
}]);
|
|
255
|
-
}, []);
|
|
256
|
-
|
|
257
|
-
// Remove by value (used by the filter list X button and the detail view chips)
|
|
258
|
-
const removeIgnoredPattern = useCallback(value => {
|
|
259
|
-
setIgnoredPatterns(prev => prev.filter(p => p.value !== value));
|
|
260
|
-
}, []);
|
|
261
|
-
|
|
262
|
-
// Toggle presence (used by the detail-view domain/url chips — adds as contains)
|
|
263
|
-
const toggleIgnoredPattern = useCallback(value => {
|
|
264
|
-
setIgnoredPatterns(prev => prev.some(p => p.value === value) ? prev.filter(p => p.value !== value) : [...prev, {
|
|
265
|
-
value,
|
|
266
|
-
mode: "contains"
|
|
267
|
-
}]);
|
|
268
|
-
}, []);
|
|
269
|
-
|
|
270
|
-
// Flip an existing pattern's mode between contains <-> exact
|
|
271
|
-
const toggleIgnoredPatternMode = useCallback(value => {
|
|
272
|
-
setIgnoredPatterns(prev => prev.map(p => p.value === value ? {
|
|
273
|
-
...p,
|
|
274
|
-
mode: p.mode === "contains" ? "exact" : "contains"
|
|
275
|
-
} : p));
|
|
276
|
-
}, []);
|
|
277
|
-
|
|
278
164
|
// Helper to check if payload should be included based on size
|
|
279
165
|
const shouldIncludePayload = useCallback(data => {
|
|
280
166
|
if (copySettings.bodySizeThreshold === -1) return true;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The full request/response bodies for a single network event, fetched on
|
|
7
|
+
* demand. Either field may be `null` when the event genuinely has no body.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolves request/response bodies that were omitted from a synced snapshot.
|
|
12
|
+
*
|
|
13
|
+
* Large bodies (see `networkSyncAdapter`) are stripped from the list snapshot
|
|
14
|
+
* so they aren't re-streamed every ~200ms; the detail view fetches them lazily
|
|
15
|
+
* through this resolver when a request is opened.
|
|
16
|
+
*
|
|
17
|
+
* On the device this is never provided — bodies are always inline — so the
|
|
18
|
+
* detail view simply falls back to the inline data. The desktop dashboard
|
|
19
|
+
* provides a resolver that fetches the body over the devtool protocol.
|
|
20
|
+
*/
|
|
21
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
22
|
+
const NetworkBodyResolverContext = /*#__PURE__*/createContext(null);
|
|
23
|
+
export function NetworkBodyResolverProvider({
|
|
24
|
+
resolver,
|
|
25
|
+
children
|
|
26
|
+
}) {
|
|
27
|
+
return /*#__PURE__*/_jsx(NetworkBodyResolverContext.Provider, {
|
|
28
|
+
value: resolver,
|
|
29
|
+
children: children
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function useNetworkBodyResolver() {
|
|
33
|
+
return useContext(NetworkBodyResolverContext);
|
|
34
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { networkEventStore } from "../utils/networkEventStore";
|
|
4
|
+
/**
|
|
5
|
+
* Request/response bodies larger than this (using the cached size proxy already
|
|
6
|
+
* on the event — no per-snapshot stringify) are NOT streamed in the list
|
|
7
|
+
* snapshot. These are the multi-MB payloads (images, large JSON list responses)
|
|
8
|
+
* that, re-sent every ~200ms for up to 500 events, freeze the desktop
|
|
9
|
+
* dashboard's main thread while it deserializes and re-renders them. Smaller
|
|
10
|
+
* bodies stay inline so the common case needs no round-trip (instant detail
|
|
11
|
+
* view, and they remain available to copy/export). Stripped bodies are fetched
|
|
12
|
+
* on demand via the `getEventBody` action when a request's detail view opens.
|
|
13
|
+
*/
|
|
14
|
+
export const SNAPSHOT_BODY_INLINE_LIMIT = 16 * 1024;
|
|
15
|
+
const stripLargeBodies = event => {
|
|
16
|
+
const stripRequest = event.requestData !== undefined && (event.requestSize ?? 0) > SNAPSHOT_BODY_INLINE_LIMIT;
|
|
17
|
+
const stripResponse = event.responseData !== undefined && (event.responseSize ?? 0) > SNAPSHOT_BODY_INLINE_LIMIT;
|
|
18
|
+
if (!stripRequest && !stripResponse) return event;
|
|
19
|
+
// Keep sizes/headers/metadata; only drop the heavy data. `undefined` keys are
|
|
20
|
+
// omitted by JSON serialization, so this shrinks the wire payload, and the
|
|
21
|
+
// dashboard treats "data missing but size > 0" as "fetch on demand".
|
|
22
|
+
const stripped = {
|
|
23
|
+
...event
|
|
24
|
+
};
|
|
25
|
+
if (stripRequest) stripped.requestData = undefined;
|
|
26
|
+
if (stripResponse) stripped.responseData = undefined;
|
|
27
|
+
return stripped;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Sync adapter for the network tool, consumed by @buoy-gg/external-sync's
|
|
32
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
33
|
+
* this package doesn't need a dependency on it).
|
|
34
|
+
*
|
|
35
|
+
* Subscribing starts the network interceptor, so requests are only captured
|
|
36
|
+
* while a dashboard is watching the network panel.
|
|
37
|
+
*
|
|
38
|
+
* v2: large bodies are stripped from the snapshot and fetched lazily via
|
|
39
|
+
* `getEventBody` (see SNAPSHOT_BODY_INLINE_LIMIT).
|
|
40
|
+
*/
|
|
41
|
+
export const networkSyncAdapter = {
|
|
42
|
+
version: 2,
|
|
43
|
+
getSnapshot: () => networkEventStore.getEvents().map(stripLargeBodies),
|
|
44
|
+
subscribe: onChange => networkEventStore.subscribeToEvents(onChange),
|
|
45
|
+
actions: {
|
|
46
|
+
clearEvents: () => {
|
|
47
|
+
networkEventStore.clearEvents();
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Return the full (un-stripped) request/response bodies for one event,
|
|
51
|
+
* fetched on demand by the dashboard when its detail view opens. Returns
|
|
52
|
+
* null if the event is no longer in the store.
|
|
53
|
+
*/
|
|
54
|
+
getEventBody: params => {
|
|
55
|
+
const id = params && typeof params === "object" ? params.id : undefined;
|
|
56
|
+
if (!id) return null;
|
|
57
|
+
const event = networkEventStore.getEventById(id);
|
|
58
|
+
if (!event) return null;
|
|
59
|
+
return {
|
|
60
|
+
requestData: event.requestData ?? null,
|
|
61
|
+
responseData: event.responseData ?? null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
@@ -14,7 +14,10 @@ export { NetworkEventItemCompact } from "./network/components/NetworkEventItemCo
|
|
|
14
14
|
export { useNetworkEvents } from "./network/hooks/useNetworkEvents";
|
|
15
15
|
export { TickProvider, useTickEveryMinute } from "./network/hooks/useTickEveryMinute";
|
|
16
16
|
export { formatBytes, formatDuration, formatHttpStatus, } from "./network/utils/formatting";
|
|
17
|
-
export type { NetworkEvent, NetworkStats, NetworkFilter, NetworkEventStatus, NetworkInsight, } from "./network/types";
|
|
17
|
+
export type { NetworkEvent, NetworkStats, NetworkFilter, NetworkEventStatus, NetworkInsight, IgnoredPattern, IgnoredPatternMatchMode, } from "./network/types";
|
|
18
|
+
export { useIgnoredPatterns, ignoredPatternsStore, urlMatchesIgnoredPattern, isUrlIgnored, } from "@buoy-gg/shared-ui";
|
|
19
|
+
export { networkSyncAdapter } from "./network/sync/networkSyncAdapter";
|
|
20
|
+
export { NetworkBodyResolverProvider, type NetworkBodyResolver, type ResolvedNetworkBody, } from "./network/sync/networkBodyResolver";
|
|
18
21
|
/** @internal */
|
|
19
22
|
export { networkEventStore } from "./network/utils/networkEventStore";
|
|
20
23
|
/** @internal */
|
|
@@ -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;AAKhE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAKvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,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,
|
|
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;AAKhE,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAKvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,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,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,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkEventDetailView.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventDetailView.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"NetworkEventDetailView.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventDetailView.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAc7C,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;CAC7C;AAqKD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,eAA2B,EAC3B,eAA0B,GAC3B,EAAE,2BAA2B,+BAmd7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkModal.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NetworkModal.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkModal.tsx"],"names":[],"mappings":"AA8CA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAu/BD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAMpD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The full request/response bodies for a single network event, fetched on
|
|
4
|
+
* demand. Either field may be `null` when the event genuinely has no body.
|
|
5
|
+
*/
|
|
6
|
+
export interface ResolvedNetworkBody {
|
|
7
|
+
requestData?: unknown;
|
|
8
|
+
responseData?: unknown;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolves request/response bodies that were omitted from a synced snapshot.
|
|
12
|
+
*
|
|
13
|
+
* Large bodies (see `networkSyncAdapter`) are stripped from the list snapshot
|
|
14
|
+
* so they aren't re-streamed every ~200ms; the detail view fetches them lazily
|
|
15
|
+
* through this resolver when a request is opened.
|
|
16
|
+
*
|
|
17
|
+
* On the device this is never provided — bodies are always inline — so the
|
|
18
|
+
* detail view simply falls back to the inline data. The desktop dashboard
|
|
19
|
+
* provides a resolver that fetches the body over the devtool protocol.
|
|
20
|
+
*/
|
|
21
|
+
export type NetworkBodyResolver = (id: string) => Promise<ResolvedNetworkBody | null>;
|
|
22
|
+
export declare function NetworkBodyResolverProvider({ resolver, children, }: {
|
|
23
|
+
resolver: NetworkBodyResolver | null;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}): import("react").JSX.Element;
|
|
26
|
+
export declare function useNetworkBodyResolver(): NetworkBodyResolver | null;
|
|
27
|
+
//# sourceMappingURL=networkBodyResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"networkBodyResolver.d.ts","sourceRoot":"","sources":["../../../../src/network/sync/networkBodyResolver.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,EAAE,EAAE,MAAM,KACP,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;AAMzC,wBAAgB,2BAA2B,CAAC,EAC1C,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAMA;AAED,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,IAAI,CAEnE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { NetworkEvent } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Request/response bodies larger than this (using the cached size proxy already
|
|
4
|
+
* on the event — no per-snapshot stringify) are NOT streamed in the list
|
|
5
|
+
* snapshot. These are the multi-MB payloads (images, large JSON list responses)
|
|
6
|
+
* that, re-sent every ~200ms for up to 500 events, freeze the desktop
|
|
7
|
+
* dashboard's main thread while it deserializes and re-renders them. Smaller
|
|
8
|
+
* bodies stay inline so the common case needs no round-trip (instant detail
|
|
9
|
+
* view, and they remain available to copy/export). Stripped bodies are fetched
|
|
10
|
+
* on demand via the `getEventBody` action when a request's detail view opens.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SNAPSHOT_BODY_INLINE_LIMIT: number;
|
|
13
|
+
/**
|
|
14
|
+
* Sync adapter for the network tool, consumed by @buoy-gg/external-sync's
|
|
15
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
16
|
+
* this package doesn't need a dependency on it).
|
|
17
|
+
*
|
|
18
|
+
* Subscribing starts the network interceptor, so requests are only captured
|
|
19
|
+
* while a dashboard is watching the network panel.
|
|
20
|
+
*
|
|
21
|
+
* v2: large bodies are stripped from the snapshot and fetched lazily via
|
|
22
|
+
* `getEventBody` (see SNAPSHOT_BODY_INLINE_LIMIT).
|
|
23
|
+
*/
|
|
24
|
+
export declare const networkSyncAdapter: {
|
|
25
|
+
version: number;
|
|
26
|
+
getSnapshot: () => NetworkEvent[];
|
|
27
|
+
subscribe: (onChange: () => void) => () => void;
|
|
28
|
+
actions: {
|
|
29
|
+
clearEvents: () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Return the full (un-stripped) request/response bodies for one event,
|
|
32
|
+
* fetched on demand by the dashboard when its detail view opens. Returns
|
|
33
|
+
* null if the event is no longer in the store.
|
|
34
|
+
*/
|
|
35
|
+
getEventBody: (params: unknown) => {
|
|
36
|
+
requestData: {} | null;
|
|
37
|
+
responseData: {} | null;
|
|
38
|
+
} | null;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=networkSyncAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"networkSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/network/sync/networkSyncAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,QAAY,CAAC;AAmBpD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB;;;0BAGP,MAAM,IAAI;;;QAM9B;;;;WAIG;+BACoB,OAAO;;;;;CAcjC,CAAC"}
|
|
@@ -76,19 +76,11 @@ export interface NetworkFilter {
|
|
|
76
76
|
/** Human readable status classifications derived from request/response metadata. */
|
|
77
77
|
export type NetworkEventStatus = "pending" | "success" | "error" | "timeout";
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* - `exact`: smart equality — if pattern starts with `/`, matches URL.pathname;
|
|
83
|
-
* if it starts with `http://`/`https://`, matches origin+pathname; otherwise
|
|
84
|
-
* matches URL.host.
|
|
79
|
+
* Ignored-pattern types now live in @buoy-gg/shared-ui so the Network and Events
|
|
80
|
+
* tools share one canonical definition + filter store. Re-exported here for
|
|
81
|
+
* backwards compatibility with existing `../types` imports.
|
|
85
82
|
*/
|
|
86
|
-
export type IgnoredPatternMatchMode
|
|
87
|
-
/** A single network-events exclude pattern with its match mode. */
|
|
88
|
-
export interface IgnoredPattern {
|
|
89
|
-
value: string;
|
|
90
|
-
mode: IgnoredPatternMatchMode;
|
|
91
|
-
}
|
|
83
|
+
export type { IgnoredPattern, IgnoredPatternMatchMode, } from "@buoy-gg/shared-ui";
|
|
92
84
|
/**
|
|
93
85
|
* Insight surfaces highlight notable traffic patterns or issues for the current session.
|
|
94
86
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/network/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EACF,KAAK,GACL,MAAM,GACN,KAAK,GACL,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,GAAG,UAAU,CAAC;IACnE;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE7E
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/network/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EACF,KAAK,GACL,MAAM,GACN,KAAK,GACL,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,GAAG,UAAU,CAAC;IACnE;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE7E;;;;GAIG;AACH,YAAY,EACV,cAAc,EACd,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,cAAc,CAAC;IAC5D,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/network",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "network package",
|
|
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": "
|
|
29
|
+
"@buoy-gg/shared-ui": "4.0.1"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"react": "*",
|