@buoy-gg/network 7.0.15 → 7.0.16
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.web.js +395 -0
- package/lib/commonjs/network/components/DataViewer.web.js +879 -0
- package/lib/commonjs/network/components/NetworkCopyScreen.web.js +48 -0
- package/lib/commonjs/network/components/NetworkCopySettingsView.web.js +1242 -0
- package/lib/commonjs/network/components/NetworkDetailScreen.web.js +101 -0
- package/lib/commonjs/network/components/NetworkDetailStepper.web.js +296 -0
- package/lib/commonjs/network/components/NetworkEventDetailView.web.js +1498 -0
- package/lib/commonjs/network/components/NetworkEventItemCompact.web.js +802 -0
- package/lib/commonjs/network/components/NetworkFilterScreen.web.js +55 -0
- package/lib/commonjs/network/components/NetworkFilterViewV3.web.js +1310 -0
- package/lib/commonjs/network/components/NetworkHeaderLive.web.js +604 -0
- package/lib/commonjs/network/components/NetworkListScreen.js +2 -1
- package/lib/commonjs/network/components/NetworkListScreen.web.js +607 -0
- package/lib/commonjs/network/components/NetworkModal.js +12 -3
- package/lib/commonjs/network/components/NetworkNumberPrompt.web.js +232 -0
- package/lib/commonjs/network/components/NetworkOverrideEditor.web.js +942 -0
- package/lib/commonjs/network/components/NetworkOverrideHeaderButton.web.js +133 -0
- package/lib/commonjs/network/components/NetworkOverridesButton.web.js +143 -0
- package/lib/commonjs/network/components/NetworkOverridesScreen.js +160 -126
- package/lib/commonjs/network/components/NetworkOverridesScreen.web.js +830 -0
- package/lib/commonjs/network/components/NetworkSavedScreen.web.js +459 -0
- package/lib/commonjs/network/components/response-editor/Explorer.web.js +1136 -0
- package/lib/commonjs/network/hooks/useNetworkEventList.js +12 -2
- package/lib/commonjs/network/overrides/applyToXhr.js +276 -15
- package/lib/commonjs/network/utils/generateNetworkCopyText.js +32 -5
- package/lib/commonjs/network/utils/networkEventStore.js +12 -4
- package/lib/commonjs/network/utils/networkListener.js +1 -16
- package/lib/module/index.web.js +81 -0
- package/lib/module/network/components/DataViewer.web.js +875 -0
- package/lib/module/network/components/NetworkCopyScreen.web.js +44 -0
- package/lib/module/network/components/NetworkCopySettingsView.web.js +1237 -0
- package/lib/module/network/components/NetworkDetailScreen.web.js +97 -0
- package/lib/module/network/components/NetworkDetailStepper.web.js +294 -0
- package/lib/module/network/components/NetworkEventDetailView.web.js +1496 -0
- package/lib/module/network/components/NetworkEventItemCompact.web.js +800 -0
- package/lib/module/network/components/NetworkFilterScreen.web.js +51 -0
- package/lib/module/network/components/NetworkFilterViewV3.web.js +1306 -0
- package/lib/module/network/components/NetworkHeaderLive.web.js +598 -0
- package/lib/module/network/components/NetworkListScreen.js +2 -1
- package/lib/module/network/components/NetworkListScreen.web.js +605 -0
- package/lib/module/network/components/NetworkModal.js +12 -3
- package/lib/module/network/components/NetworkNumberPrompt.web.js +230 -0
- package/lib/module/network/components/NetworkOverrideEditor.web.js +939 -0
- package/lib/module/network/components/NetworkOverrideHeaderButton.web.js +131 -0
- package/lib/module/network/components/NetworkOverridesButton.web.js +141 -0
- package/lib/module/network/components/NetworkOverridesScreen.js +161 -127
- package/lib/module/network/components/NetworkOverridesScreen.web.js +826 -0
- package/lib/module/network/components/NetworkSavedScreen.web.js +457 -0
- package/lib/module/network/components/response-editor/Explorer.web.js +1133 -0
- package/lib/module/network/hooks/useNetworkEventList.js +12 -2
- package/lib/module/network/overrides/applyToXhr.js +275 -15
- package/lib/module/network/utils/generateNetworkCopyText.js +28 -1
- package/lib/module/network/utils/networkEventStore.js +12 -4
- package/lib/module/network/utils/networkListener.js +1 -16
- package/lib/typescript/index.web.d.ts +56 -0
- package/lib/typescript/index.web.d.ts.map +1 -0
- package/lib/typescript/network/components/DataViewer.web.d.ts +78 -0
- package/lib/typescript/network/components/DataViewer.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts +27 -0
- package/lib/typescript/network/components/NetworkCopyScreen.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts +68 -0
- package/lib/typescript/network/components/NetworkCopySettingsView.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts +45 -0
- package/lib/typescript/network/components/NetworkDetailScreen.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts +40 -0
- package/lib/typescript/network/components/NetworkDetailStepper.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts +67 -0
- package/lib/typescript/network/components/NetworkEventDetailView.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts +58 -0
- package/lib/typescript/network/components/NetworkEventItemCompact.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts +28 -0
- package/lib/typescript/network/components/NetworkFilterScreen.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts +74 -0
- package/lib/typescript/network/components/NetworkFilterViewV3.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts +112 -0
- package/lib/typescript/network/components/NetworkHeaderLive.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkListScreen.web.d.ts +63 -0
- package/lib/typescript/network/components/NetworkListScreen.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts +38 -0
- package/lib/typescript/network/components/NetworkNumberPrompt.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts +43 -0
- package/lib/typescript/network/components/NetworkOverrideEditor.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts +32 -0
- package/lib/typescript/network/components/NetworkOverrideHeaderButton.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts +26 -0
- package/lib/typescript/network/components/NetworkOverridesButton.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts +9 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts +69 -0
- package/lib/typescript/network/components/NetworkOverridesScreen.web.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts +67 -0
- package/lib/typescript/network/components/NetworkSavedScreen.web.d.ts.map +1 -0
- package/lib/typescript/network/components/response-editor/Explorer.web.d.ts +56 -0
- package/lib/typescript/network/components/response-editor/Explorer.web.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts +10 -0
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +1 -1
- package/lib/typescript/network/overrides/__tests__/applyToXhr.test.d.ts.map +1 -1
- package/lib/typescript/network/overrides/applyToXhr.d.ts +40 -14
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +1 -1
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts +34 -1
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkListener.d.ts +0 -1
- package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -31,11 +31,21 @@ function useNetworkEventList(options) {
|
|
|
31
31
|
filter,
|
|
32
32
|
ignoredPatterns,
|
|
33
33
|
paused,
|
|
34
|
+
visible = true,
|
|
34
35
|
isPro
|
|
35
36
|
} = options;
|
|
36
37
|
const [events, setEvents] = (0, _react.useState)(() => _networkEventStore.networkEventStore.getEvents());
|
|
37
38
|
(0, _react.useEffect)(() => {
|
|
38
|
-
|
|
39
|
+
// Hidden: neither subscribe nor sync — a hidden screen re-rendering for
|
|
40
|
+
// stream traffic is pure waste (the stable-router contract).
|
|
41
|
+
if (!visible) return undefined;
|
|
42
|
+
if (paused) {
|
|
43
|
+
// Pause freezes the STREAM, not the data. The store still holds the
|
|
44
|
+
// frozen events, and a screen entering visibility during a pause must
|
|
45
|
+
// show them rather than whatever it had at mount.
|
|
46
|
+
setEvents(_networkEventStore.networkEventStore.getEvents());
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
39
49
|
// Coalesce store emissions to one commit per frame: a burst of N
|
|
40
50
|
// concurrent requests emits 2N times (add + response each), and
|
|
41
51
|
// uncoalesced each emission is its own commit re-rendering the
|
|
@@ -58,7 +68,7 @@ function useNetworkEventList(options) {
|
|
|
58
68
|
unsubscribe();
|
|
59
69
|
if (rafId != null) cancelAnimationFrame(rafId);
|
|
60
70
|
};
|
|
61
|
-
}, [paused]);
|
|
71
|
+
}, [paused, visible]);
|
|
62
72
|
(0, _react.useEffect)(() => _networkEventStore.networkEventStore.onClear(() => {
|
|
63
73
|
setEvents([]);
|
|
64
74
|
}), []);
|
|
@@ -4,11 +4,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.canSynthesize = canSynthesize;
|
|
7
|
+
exports.canSynthesizeStandard = canSynthesizeStandard;
|
|
7
8
|
exports.runOverride = runOverride;
|
|
8
9
|
/**
|
|
9
|
-
* Perform an override outcome against
|
|
10
|
+
* Perform an override outcome against an XMLHttpRequest — React Native's OR a
|
|
11
|
+
* standards (browser/webview) one.
|
|
10
12
|
*
|
|
11
|
-
* WHY THIS IS POSSIBLE
|
|
13
|
+
* WHY THIS IS POSSIBLE ON RN: React Native's XMLHttpRequest
|
|
12
14
|
* (`react-native/Libraries/Network/XMLHttpRequest.js`) is pure JavaScript.
|
|
13
15
|
* `readyState`, `status`, `responseHeaders` and `responseURL` are plain
|
|
14
16
|
* writable fields, and `setResponseHeaders()` / `setReadyState()` are real
|
|
@@ -17,16 +19,33 @@ exports.runOverride = runOverride;
|
|
|
17
19
|
* `_hasError` → error, otherwise load; always loadend). So a response can be
|
|
18
20
|
* synthesized end to end without touching native code.
|
|
19
21
|
*
|
|
20
|
-
* WHY XHR IS THE ONLY PLACE WE HOOK:
|
|
21
|
-
* is `require('whatwg-fetch')` —
|
|
22
|
-
* XMLHttpRequest. Overriding here therefore
|
|
23
|
-
* XHR through one code path, and the fake
|
|
24
|
-
* polyfill so the tool records it without
|
|
22
|
+
* WHY XHR IS THE ONLY PLACE WE HOOK ON RN:
|
|
23
|
+
* `react-native/Libraries/Network/fetch.js` is `require('whatwg-fetch')` —
|
|
24
|
+
* RN's `fetch` IS the polyfill over XMLHttpRequest. Overriding here therefore
|
|
25
|
+
* covers `fetch`, `axios` and raw XHR through one code path, and the fake
|
|
26
|
+
* response flows back up through the polyfill so the tool records it without
|
|
27
|
+
* any extra plumbing.
|
|
25
28
|
*
|
|
26
29
|
* Verified field-for-field on React Native 0.81.4 and 0.85.3.
|
|
27
30
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
31
|
+
* WHY THERE IS A SECOND PATH: on a STANDARDS XMLHttpRequest (a webview port —
|
|
32
|
+
* Tauri/Capacitor — or react-native-web) none of the RN internals exist, and
|
|
33
|
+
* `status` / `readyState` / `response` are readonly ACCESSORS on
|
|
34
|
+
* `XMLHttpRequest.prototype`, so plain assignment silently no-ops (or throws
|
|
35
|
+
* in strict mode). This file used to feature-detect that and give up —
|
|
36
|
+
* "degraded, but never broken" — which was right for react-native-web as a
|
|
37
|
+
* by-product of an RN app, and wrong the moment the web ports made the
|
|
38
|
+
* browser XHR a first-class target: the row got FLAGGED as overridden (the
|
|
39
|
+
* rule matched) while the real request went out. The fix is
|
|
40
|
+
* `runStandardOverride` below: `Object.defineProperty` INSTANCE own-properties
|
|
41
|
+
* shadow the prototype accessors (legal on any object), and the lifecycle
|
|
42
|
+
* events are dispatched by hand through the XHR's own EventTarget — which
|
|
43
|
+
* fires `addEventListener` listeners AND the `onload`/`onloadend`/
|
|
44
|
+
* `onreadystatechange` handler attributes, exactly what axios's XHR adapter
|
|
45
|
+
* listens on.
|
|
46
|
+
*
|
|
47
|
+
* Several non-obvious behaviours are handled below; each one is a real bug if
|
|
48
|
+
* you skip it, and each is commented at the line that fixes it.
|
|
30
49
|
*/
|
|
31
50
|
|
|
32
51
|
const HEADERS_RECEIVED = 2;
|
|
@@ -45,22 +64,33 @@ const internalsOf = xhr => xhr;
|
|
|
45
64
|
let warnedUnsupported = false;
|
|
46
65
|
|
|
47
66
|
/**
|
|
48
|
-
* Can this XMLHttpRequest be driven
|
|
67
|
+
* Can this XMLHttpRequest be driven through React Native's internals?
|
|
49
68
|
*
|
|
50
|
-
* The
|
|
51
|
-
* than assumed
|
|
52
|
-
*
|
|
53
|
-
* the request goes out for real — degraded, but never broken.
|
|
69
|
+
* The RN technique rests on those internals, so it is feature-detected rather
|
|
70
|
+
* than assumed. A request that fails this check is NOT automatically a lost
|
|
71
|
+
* cause — `canSynthesizeStandard` covers the standards XHR next.
|
|
54
72
|
*/
|
|
55
73
|
function canSynthesize(xhr) {
|
|
56
74
|
const internals = internalsOf(xhr);
|
|
57
75
|
return typeof internals.setReadyState === "function" && typeof internals.setResponseHeaders === "function";
|
|
58
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Can this XMLHttpRequest be driven as a STANDARDS one?
|
|
80
|
+
*
|
|
81
|
+
* `dispatchEvent` is the load-bearing requirement: the whole standard path
|
|
82
|
+
* works by defining instance own-properties and then dispatching the
|
|
83
|
+
* lifecycle events through the XHR's own EventTarget. `removeEventListener`
|
|
84
|
+
* rules out half-mocks that could take the branch and then leak listeners.
|
|
85
|
+
*/
|
|
86
|
+
function canSynthesizeStandard(xhr) {
|
|
87
|
+
return typeof xhr.addEventListener === "function" && typeof xhr.removeEventListener === "function" && typeof xhr.dispatchEvent === "function";
|
|
88
|
+
}
|
|
59
89
|
function warnUnsupported() {
|
|
60
90
|
if (warnedUnsupported) return;
|
|
61
91
|
warnedUnsupported = true;
|
|
62
92
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
63
|
-
console.warn("[Buoy] Network overrides need React Native's
|
|
93
|
+
console.warn("[Buoy] Network overrides need an XMLHttpRequest they can drive (React " + "Native's, or a standards EventTarget one) and this environment " + "provides neither — requests will run normally.");
|
|
64
94
|
}
|
|
65
95
|
}
|
|
66
96
|
|
|
@@ -120,6 +150,230 @@ function fail(xhr, outcome) {
|
|
|
120
150
|
internals.setReadyState?.(DONE);
|
|
121
151
|
}
|
|
122
152
|
|
|
153
|
+
// ============================================================================
|
|
154
|
+
// STANDARDS XMLHttpRequest path (webview ports / react-native-web)
|
|
155
|
+
// ============================================================================
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Shadow a prototype accessor with an instance own-property.
|
|
159
|
+
*
|
|
160
|
+
* This is the entire trick: `xhr.status = 500` silently no-ops (or throws in
|
|
161
|
+
* strict mode) against the readonly getter on `XMLHttpRequest.prototype`, but
|
|
162
|
+
* an own DATA property on the instance is legal and takes precedence in every
|
|
163
|
+
* lookup. `configurable: true` so `readyState` can be redefined per
|
|
164
|
+
* transition. Works identically against RN's plain class fields, so nothing
|
|
165
|
+
* here is browser-only — the RN path just has a richer mechanism available.
|
|
166
|
+
*/
|
|
167
|
+
function defineOwn(xhr, name, value) {
|
|
168
|
+
Object.defineProperty(xhr, name, {
|
|
169
|
+
value,
|
|
170
|
+
configurable: true,
|
|
171
|
+
enumerable: true,
|
|
172
|
+
writable: false
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Build a lifecycle event to dispatch by hand.
|
|
178
|
+
*
|
|
179
|
+
* `ProgressEvent` where the spec uses one (load/loadend/error/timeout/abort)
|
|
180
|
+
* and the constructor exists; plain `Event` otherwise (Node test runs have no
|
|
181
|
+
* ProgressEvent); a `{ type }` literal as the last resort for event-target
|
|
182
|
+
* shims. Dispatching through the XHR's own EventTarget is what makes the
|
|
183
|
+
* `onload`/`onloadend`/`onreadystatechange` handler ATTRIBUTES fire too —
|
|
184
|
+
* they're listeners in the same list, which is exactly what axios's adapter
|
|
185
|
+
* registers.
|
|
186
|
+
*/
|
|
187
|
+
function makeEvent(type) {
|
|
188
|
+
if (type !== "readystatechange" && typeof ProgressEvent === "function") {
|
|
189
|
+
try {
|
|
190
|
+
return new ProgressEvent(type);
|
|
191
|
+
} catch {
|
|
192
|
+
// fall through
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (typeof Event === "function") {
|
|
196
|
+
try {
|
|
197
|
+
return new Event(type);
|
|
198
|
+
} catch {
|
|
199
|
+
// fall through
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
type
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function fireEvent(xhr, type) {
|
|
207
|
+
xhr.dispatchEvent(makeEvent(type));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Move the shadowed `readyState` and announce it — the interceptor's own
|
|
211
|
+
* listener stamps TTFB off the HEADERS_RECEIVED notification, same as a real
|
|
212
|
+
* response. */
|
|
213
|
+
function setStandardReadyState(xhr, state) {
|
|
214
|
+
defineOwn(xhr, "readyState", state);
|
|
215
|
+
fireEvent(xhr, "readystatechange");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* `xhr.response` in the caller's requested shape.
|
|
220
|
+
*
|
|
221
|
+
* A standards XHR types `response` off `responseType`; axios sets it when the
|
|
222
|
+
* caller asked for one (default '' → text). JSON is parsed (spec: null on
|
|
223
|
+
* parse failure), arraybuffer/blob are best-effort encodings of the rule's
|
|
224
|
+
* text body, everything else falls back to the string.
|
|
225
|
+
*/
|
|
226
|
+
function standardResponseFor(xhr, outcome) {
|
|
227
|
+
let responseType = "";
|
|
228
|
+
try {
|
|
229
|
+
responseType = xhr.responseType;
|
|
230
|
+
} catch {
|
|
231
|
+
// Some shims throw on access before send — treat as default text.
|
|
232
|
+
}
|
|
233
|
+
switch (responseType) {
|
|
234
|
+
case "":
|
|
235
|
+
case "text":
|
|
236
|
+
return outcome.body;
|
|
237
|
+
case "json":
|
|
238
|
+
try {
|
|
239
|
+
return JSON.parse(outcome.body);
|
|
240
|
+
} catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
case "arraybuffer":
|
|
244
|
+
if (typeof TextEncoder === "function") {
|
|
245
|
+
return new TextEncoder().encode(outcome.body).buffer;
|
|
246
|
+
}
|
|
247
|
+
return outcome.body;
|
|
248
|
+
case "blob":
|
|
249
|
+
if (typeof Blob === "function") {
|
|
250
|
+
const contentType = Object.entries(outcome.headers).find(([name]) => name.toLowerCase() === "content-type")?.[1] ?? "";
|
|
251
|
+
return new Blob([outcome.body], {
|
|
252
|
+
type: contentType
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return outcome.body;
|
|
256
|
+
default:
|
|
257
|
+
return outcome.body;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Synthesize a successful response on a standards XHR. */
|
|
262
|
+
function respondStandard(xhr, url, outcome, isAborted) {
|
|
263
|
+
const headers = outcome.headers;
|
|
264
|
+
// Spec shape: lowercase names, `name: value\r\n` per pair — axios splits on
|
|
265
|
+
// newlines and trims, raw XHR users get what a real response reads like.
|
|
266
|
+
const allHeaders = Object.entries(headers).map(([name, value]) => `${name.toLowerCase()}: ${value}\r\n`).join("");
|
|
267
|
+
defineOwn(xhr, "getAllResponseHeaders", () => allHeaders);
|
|
268
|
+
defineOwn(xhr, "getResponseHeader", name => {
|
|
269
|
+
const wanted = String(name).toLowerCase();
|
|
270
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
271
|
+
if (key.toLowerCase() === wanted) return value;
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
});
|
|
275
|
+
defineOwn(xhr, "responseURL", url);
|
|
276
|
+
defineOwn(xhr, "status", outcome.status);
|
|
277
|
+
defineOwn(xhr, "statusText", outcome.statusText);
|
|
278
|
+
defineOwn(xhr, "response", standardResponseFor(xhr, outcome));
|
|
279
|
+
// Own property, so it answers with the body even under a non-text
|
|
280
|
+
// `responseType` (where the real accessor would throw). axios reads it
|
|
281
|
+
// whenever `responseType` is '' or 'text'.
|
|
282
|
+
defineOwn(xhr, "responseText", outcome.body);
|
|
283
|
+
setStandardReadyState(xhr, HEADERS_RECEIVED);
|
|
284
|
+
if (isAborted()) return;
|
|
285
|
+
setStandardReadyState(xhr, LOADING);
|
|
286
|
+
if (isAborted()) return;
|
|
287
|
+
setStandardReadyState(xhr, DONE);
|
|
288
|
+
fireEvent(xhr, "load");
|
|
289
|
+
fireEvent(xhr, "loadend");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Synthesize a transport failure on a standards XHR — offline or timeout. */
|
|
293
|
+
function failStandard(xhr, outcome) {
|
|
294
|
+
// `status` stays 0 (the prototype getter's answer for a request whose real
|
|
295
|
+
// send never ran) — exactly what a genuine network error reports. The
|
|
296
|
+
// 'error'/'timeout' event is the entire signal; axios turns them into
|
|
297
|
+
// ERR_NETWORK / ECONNABORTED the same way whatwg-fetch turns RN's flags
|
|
298
|
+
// into TypeErrors.
|
|
299
|
+
setStandardReadyState(xhr, DONE);
|
|
300
|
+
fireEvent(xhr, outcome.failKind === "timeout" ? "timeout" : "error");
|
|
301
|
+
fireEvent(xhr, "loadend");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Apply an outcome to a standards XMLHttpRequest that has NOT been sent.
|
|
306
|
+
*
|
|
307
|
+
* The real `send()` is never invoked for respond/fail — the request must not
|
|
308
|
+
* touch the network. That leaves `abort()` with nothing to do natively (per
|
|
309
|
+
* spec, abort on a request whose send flag is unset dispatches NOTHING), so
|
|
310
|
+
* an instance shadow gives it the in-flight behaviour the app expects:
|
|
311
|
+
* cancel the pending outcome and dispatch abort + loadend. The shadow deletes
|
|
312
|
+
* itself the moment it can't matter anymore — after settling, and before a
|
|
313
|
+
* `delay` pass-through hands the request to the real network, where the
|
|
314
|
+
* prototype `abort()` must be reachable again.
|
|
315
|
+
*/
|
|
316
|
+
function runStandardOverride(xhr, url, outcome, passThrough) {
|
|
317
|
+
let timer = null;
|
|
318
|
+
let aborted = false;
|
|
319
|
+
let settled = false;
|
|
320
|
+
const restoreAbort = () => {
|
|
321
|
+
try {
|
|
322
|
+
delete xhr.abort;
|
|
323
|
+
} catch {
|
|
324
|
+
// Non-configurable shadow can't happen (we define it configurable), but
|
|
325
|
+
// never let cleanup throw into app code.
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
Object.defineProperty(xhr, "abort", {
|
|
329
|
+
configurable: true,
|
|
330
|
+
writable: true,
|
|
331
|
+
value: () => {
|
|
332
|
+
if (settled || aborted) return;
|
|
333
|
+
aborted = true;
|
|
334
|
+
settled = true;
|
|
335
|
+
if (timer !== null) {
|
|
336
|
+
clearTimeout(timer);
|
|
337
|
+
timer = null;
|
|
338
|
+
}
|
|
339
|
+
restoreAbort();
|
|
340
|
+
// The in-flight abort sequence: DONE, then 'abort', then 'loadend' —
|
|
341
|
+
// whatwg's request-error steps, which is also what RN's setReadyState
|
|
342
|
+
// dispatches off `_aborted`.
|
|
343
|
+
setStandardReadyState(xhr, DONE);
|
|
344
|
+
fireEvent(xhr, "abort");
|
|
345
|
+
fireEvent(xhr, "loadend");
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
const fire = () => {
|
|
349
|
+
timer = null;
|
|
350
|
+
if (aborted) return;
|
|
351
|
+
if (outcome.kind === "delay") {
|
|
352
|
+
// The real request is about to be genuinely in flight — put the
|
|
353
|
+
// prototype `abort()` back FIRST so cancelling reaches the network.
|
|
354
|
+
restoreAbort();
|
|
355
|
+
passThrough();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (outcome.kind === "fail") {
|
|
359
|
+
failStandard(xhr, outcome);
|
|
360
|
+
} else {
|
|
361
|
+
respondStandard(xhr, url, outcome, () => aborted);
|
|
362
|
+
}
|
|
363
|
+
settled = true;
|
|
364
|
+
restoreAbort();
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// ALWAYS asynchronous, even at 0ms — same contract as the RN path: callers
|
|
368
|
+
// legitimately attach handlers after `send()` returns (axios does).
|
|
369
|
+
timer = setTimeout(fire, outcome.delayMs);
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ============================================================================
|
|
374
|
+
// Entry point
|
|
375
|
+
// ============================================================================
|
|
376
|
+
|
|
123
377
|
/**
|
|
124
378
|
* Apply an outcome to a request that has NOT been sent yet.
|
|
125
379
|
*
|
|
@@ -130,6 +384,13 @@ function fail(xhr, outcome) {
|
|
|
130
384
|
*/
|
|
131
385
|
function runOverride(xhr, url, outcome, passThrough) {
|
|
132
386
|
if (!canSynthesize(xhr)) {
|
|
387
|
+
// Not React Native's XMLHttpRequest — a standards one (webview port,
|
|
388
|
+
// react-native-web) is driven through instance property shadowing
|
|
389
|
+
// instead. Order matters: RN's XHR also extends an EventTarget shim, so
|
|
390
|
+
// the RN check must win first.
|
|
391
|
+
if (canSynthesizeStandard(xhr)) {
|
|
392
|
+
return runStandardOverride(xhr, url, outcome, passThrough);
|
|
393
|
+
}
|
|
133
394
|
warnUnsupported();
|
|
134
395
|
return false;
|
|
135
396
|
}
|
|
@@ -4,15 +4,42 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.generateNetworkCopyText = generateNetworkCopyText;
|
|
7
|
-
var _formatting = require("./formatting");
|
|
8
7
|
/**
|
|
9
8
|
* Pure copy-text generation for the network tool (rule3: reusable logic
|
|
10
9
|
* extracted from the modal). Ported verbatim from the `generateCopyText`
|
|
11
10
|
* callback previously inlined in NetworkModal — kept pure so the header
|
|
12
11
|
* CopyButton can resolve it LAZILY at copy time (CopyButton accepts a
|
|
13
12
|
* function value) instead of rebuilding the string on every event.
|
|
13
|
+
*
|
|
14
|
+
* PLATFORM-FREE ON PURPOSE: this module is reused verbatim by the web ports
|
|
15
|
+
* (webview shims resolve `@buoy-gg/shared-ui` to a minimal storage-only shim),
|
|
16
|
+
* so it must not reach the RN component graph or the shared-ui barrel. That is
|
|
17
|
+
* why:
|
|
18
|
+
* - `CopySettings` is defined HERE rather than imported from
|
|
19
|
+
* NetworkCopySettingsView.tsx (which pulls in react-native + shared-ui RN
|
|
20
|
+
* chrome). The RN view keeps its own structurally-identical definition —
|
|
21
|
+
* keep the two shapes in sync.
|
|
22
|
+
* - `formatBytes` is a verbatim copy of
|
|
23
|
+
* shared/src/utils/formatting/dataFormatting.ts rather than the
|
|
24
|
+
* `./formatting` re-export of `@buoy-gg/shared-ui` (not in the web shim).
|
|
14
25
|
*/
|
|
15
26
|
|
|
27
|
+
/**
|
|
28
|
+
* What the copy exporter includes and how it formats it. Structurally
|
|
29
|
+
* identical to (and must stay in sync with) the `CopySettings` exported by
|
|
30
|
+
* NetworkCopySettingsView.tsx — see the platform-free note above for why the
|
|
31
|
+
* definition lives here.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** shared/src/utils/formatting/dataFormatting.ts `formatBytes` — verbatim. */
|
|
35
|
+
function formatBytes(bytes) {
|
|
36
|
+
if (bytes === undefined || bytes === null) return "N/A";
|
|
37
|
+
if (bytes === 0) return "0 B";
|
|
38
|
+
const k = 1024;
|
|
39
|
+
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
40
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
41
|
+
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
|
|
42
|
+
}
|
|
16
43
|
function shouldIncludePayload(data, bodySizeThresholdKb) {
|
|
17
44
|
if (bodySizeThresholdKb === -1) return true;
|
|
18
45
|
if (!data) return true;
|
|
@@ -99,8 +126,8 @@ function generateNetworkCopyText(events, copySettings) {
|
|
|
99
126
|
md += `**Timestamp:** ${new Date(event.timestamp).toISOString()}\n`;
|
|
100
127
|
}
|
|
101
128
|
if (copySettings.includeSizes) {
|
|
102
|
-
const reqSize = event.requestSize ?
|
|
103
|
-
const resSize = event.responseSize ?
|
|
129
|
+
const reqSize = event.requestSize ? formatBytes(event.requestSize) : "N/A";
|
|
130
|
+
const resSize = event.responseSize ? formatBytes(event.responseSize) : "N/A";
|
|
104
131
|
md += `**Request Size:** ${reqSize}\n`;
|
|
105
132
|
md += `**Response Size:** ${resSize}\n`;
|
|
106
133
|
}
|
|
@@ -115,7 +142,7 @@ function generateNetworkCopyText(events, copySettings) {
|
|
|
115
142
|
if (shouldIncludePayload(event.requestData, copySettings.bodySizeThreshold)) {
|
|
116
143
|
md += `\`\`\`json\n${JSON.stringify(event.requestData, null, 2)}\n\`\`\`\n`;
|
|
117
144
|
} else {
|
|
118
|
-
const size = event.requestSize ?
|
|
145
|
+
const size = event.requestSize ? formatBytes(event.requestSize) : "Unknown size";
|
|
119
146
|
md += `_Payload omitted (${size}). Exceeds ${copySettings.bodySizeThreshold}KB threshold._\n`;
|
|
120
147
|
}
|
|
121
148
|
}
|
|
@@ -127,7 +154,7 @@ function generateNetworkCopyText(events, copySettings) {
|
|
|
127
154
|
if (shouldIncludePayload(event.responseData, copySettings.bodySizeThreshold)) {
|
|
128
155
|
md += `\`\`\`json\n${JSON.stringify(event.responseData, null, 2)}\n\`\`\`\n`;
|
|
129
156
|
} else {
|
|
130
|
-
const size = event.responseSize ?
|
|
157
|
+
const size = event.responseSize ? formatBytes(event.responseSize) : "Unknown size";
|
|
131
158
|
md += `_Payload omitted (${size}). Exceeds ${copySettings.bodySizeThreshold}KB threshold._\n`;
|
|
132
159
|
}
|
|
133
160
|
}
|
|
@@ -111,8 +111,14 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
111
111
|
const now = Date.now();
|
|
112
112
|
const recent = this.recentRequests.get(requestKey);
|
|
113
113
|
|
|
114
|
-
// If same request within 50ms, likely a duplicate from XHR/fetch dual
|
|
115
|
-
|
|
114
|
+
// If same request within 50ms, likely a duplicate from XHR/fetch dual
|
|
115
|
+
// interception. The age must be non-negative: Date.now() is not
|
|
116
|
+
// monotonic, and after a backwards clock adjustment (iOS time sync) a
|
|
117
|
+
// future-dated entry would satisfy `< 50` for EVERY request to this
|
|
118
|
+
// endpoint until the clock caught up — capture going silent for minutes
|
|
119
|
+
// to hours, "fixed" only by Clear wiping this map (field-reported). A
|
|
120
|
+
// negative age falls through and re-stamps the entry with the new clock.
|
|
121
|
+
if (recent && now - recent.at >= 0 && now - recent.at < 50) {
|
|
116
122
|
// …but the duplicate is not always redundant. On React Native a
|
|
117
123
|
// `fetch()` is seen twice: once by the fetch wrapper, which is what
|
|
118
124
|
// creates this event, and again by the XHR wrapper underneath it —
|
|
@@ -129,11 +135,13 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
129
135
|
id: request.id
|
|
130
136
|
});
|
|
131
137
|
|
|
132
|
-
// Clean up old entries to prevent memory leak
|
|
138
|
+
// Clean up old entries to prevent memory leak. Future-dated entries are
|
|
139
|
+
// stale by definition (clock moved backwards) — drop those too, or they
|
|
140
|
+
// survive every sweep until the clock overtakes them.
|
|
133
141
|
if (this.recentRequests.size > 100) {
|
|
134
142
|
const cutoff = now - 5000;
|
|
135
143
|
for (const [key, entry] of this.recentRequests.entries()) {
|
|
136
|
-
if (entry.at < cutoff) {
|
|
144
|
+
if (entry.at < cutoff || entry.at > now) {
|
|
137
145
|
this.recentRequests.delete(key);
|
|
138
146
|
}
|
|
139
147
|
}
|
|
@@ -203,20 +203,6 @@ class NetworkListener {
|
|
|
203
203
|
return `${Math.round(bytes / Math.pow(k, i) * 100) / 100} ${sizes[i]}`;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
// Get response body size
|
|
207
|
-
getResponseSize(body) {
|
|
208
|
-
if (!body) return 0;
|
|
209
|
-
if (typeof body === "string") return body.length;
|
|
210
|
-
if (typeof body === "object") {
|
|
211
|
-
try {
|
|
212
|
-
return JSON.stringify(body).length;
|
|
213
|
-
} catch {
|
|
214
|
-
return 0;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return 0;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
206
|
/**
|
|
221
207
|
* Handle XMLHttpRequest response processing
|
|
222
208
|
* This method processes the response and emits appropriate events
|
|
@@ -544,8 +530,7 @@ class NetworkListener {
|
|
|
544
530
|
const responseClone = response.clone();
|
|
545
531
|
const {
|
|
546
532
|
body,
|
|
547
|
-
size: responseSize
|
|
548
|
-
truncated
|
|
533
|
+
size: responseSize
|
|
549
534
|
} = await self.processResponseBody(responseClone);
|
|
550
535
|
|
|
551
536
|
// Now the body is in — this is the comparable, total number.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @buoy-gg/network — Web Entry Point (webview ports: Tauri / Capacitor / web).
|
|
5
|
+
*
|
|
6
|
+
* Exposes the platform-free capture + data layer only. The capture core
|
|
7
|
+
* (networkListener) patches `globalThis.fetch` and `XMLHttpRequest.prototype`
|
|
8
|
+
* — standard web APIs — so it runs in a webview unchanged; the RN UI
|
|
9
|
+
* components (NetworkModal etc.) are NOT exported here. Their DOM twins are
|
|
10
|
+
* added feature-by-feature as the web tools port progresses.
|
|
11
|
+
*
|
|
12
|
+
* Consumers resolve `@buoy-gg/shared-ui` to a web shim (see
|
|
13
|
+
* example-tauri/src/shims/shared-ui.web.ts) providing localStorage-backed
|
|
14
|
+
* persistentStorage, devToolsStorageKeys, BaseEventStore, and the pure
|
|
15
|
+
* ignored-patterns helpers.
|
|
16
|
+
*
|
|
17
|
+
* @platform React DOM Web
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Capture core — fetch/XHR interception
|
|
21
|
+
export { networkListener, startNetworkListener, stopNetworkListener, addNetworkListener, removeAllNetworkListeners, isNetworkListening } from "./network/utils/networkListener";
|
|
22
|
+
// Event store + saved/pinned store
|
|
23
|
+
export { networkEventStore } from "./network/utils/networkEventStore";
|
|
24
|
+
export { networkSavedStore, flagsForEventId } from "./network/utils/networkSavedStore";
|
|
25
|
+
|
|
26
|
+
// Types
|
|
27
|
+
|
|
28
|
+
// Hooks (all platform-free: react + stores only)
|
|
29
|
+
export { useNetworkEvents, FREE_TIER_REQUEST_LIMIT } from "./network/hooks/useNetworkEvents";
|
|
30
|
+
export { useNetworkEventList } from "./network/hooks/useNetworkEventList";
|
|
31
|
+
export { useLiveNetworkEvent } from "./network/hooks/useLiveNetworkEvent";
|
|
32
|
+
export { useNetworkBadgeCounts } from "./network/hooks/useNetworkBadgeCounts";
|
|
33
|
+
|
|
34
|
+
// Selection cursor (detail view navigation) — platform-free store + hooks
|
|
35
|
+
export { useHasNetworkSelection, useSelectedNetworkEventId, useNetworkBodyEventId, useNetworkCursor } from "./network/hooks/useNetworkSelection";
|
|
36
|
+
export { networkSelectionStore, toSelectionIds } from "./network/utils/networkSelectionStore";
|
|
37
|
+
|
|
38
|
+
// DOM twins of the RN UI (added feature-by-feature; F2 = the LIST screen,
|
|
39
|
+
// F3 = the DETAIL view)
|
|
40
|
+
export { NetworkListScreen } from "./network/components/NetworkListScreen.web";
|
|
41
|
+
export { NetworkEventItemCompact } from "./network/components/NetworkEventItemCompact.web";
|
|
42
|
+
export { NetworkHeaderBadges, NetworkClearButton, NetworkSavedButton, NetworkDetailPinActions, NetworkHeaderCopyButton } from "./network/components/NetworkHeaderLive.web";
|
|
43
|
+
export { NetworkEventDetailView } from "./network/components/NetworkEventDetailView.web";
|
|
44
|
+
export { NetworkDetailStepper } from "./network/components/NetworkDetailStepper.web";
|
|
45
|
+
export { NetworkDetailScreen } from "./network/components/NetworkDetailScreen.web";
|
|
46
|
+
|
|
47
|
+
// F4 = FILTERS + SEARCH + IGNORED PATTERNS
|
|
48
|
+
export { NetworkFilterViewV3 } from "./network/components/NetworkFilterViewV3.web";
|
|
49
|
+
export { NetworkFilterScreen } from "./network/components/NetworkFilterScreen.web";
|
|
50
|
+
|
|
51
|
+
// F5 = SAVED/PINNED screen + COPY settings
|
|
52
|
+
export { NetworkSavedScreen } from "./network/components/NetworkSavedScreen.web";
|
|
53
|
+
export { NetworkCopySettingsView, DEFAULT_COPY_SETTINGS } from "./network/components/NetworkCopySettingsView.web";
|
|
54
|
+
export { NetworkCopyScreen } from "./network/components/NetworkCopyScreen.web";
|
|
55
|
+
export { generateNetworkCopyText } from "./network/utils/generateNetworkCopyText";
|
|
56
|
+
export { selectSavedEvents } from "./network/utils/selectSavedEvents";
|
|
57
|
+
export { FREE_TIER_SAVED_LIMIT, MAX_SAVED } from "./network/utils/networkSavedStore";
|
|
58
|
+
|
|
59
|
+
// F6 = RESPONSE OVERRIDES (screen + editor DOM twins on top of the
|
|
60
|
+
// platform-free rule store/engine the F1 capture core already applies)
|
|
61
|
+
export { NetworkOverridesScreen } from "./network/components/NetworkOverridesScreen.web";
|
|
62
|
+
export { NetworkOverrideEditor } from "./network/components/NetworkOverrideEditor.web";
|
|
63
|
+
export { NetworkNumberPrompt } from "./network/components/NetworkNumberPrompt.web";
|
|
64
|
+
export { NetworkOverridesButton } from "./network/components/NetworkOverridesButton.web";
|
|
65
|
+
export { NetworkOverrideHeaderButton } from "./network/components/NetworkOverrideHeaderButton.web";
|
|
66
|
+
export { useOverrideRules } from "./network/hooks/useOverrideRules";
|
|
67
|
+
// The persisted rule store — same `@react_buoy_network_overrides` storage key
|
|
68
|
+
// + JSON shape as native (persists through the shared-ui barrel, which web
|
|
69
|
+
// consumers resolve to their localStorage shim). Hosts call `ensureLoaded()`
|
|
70
|
+
// at boot, the way native preset.tsx does at import, so persisted rules fire
|
|
71
|
+
// from launch with the tool closed.
|
|
72
|
+
export { overrideRulesStore } from "./network/overrides/overrideRulesStore";
|
|
73
|
+
export { draftFromEvent, patternForUrl } from "./network/overrides/presets";
|
|
74
|
+
export { FREE_TIER_RULE_LIMIT, MAX_RULES, STATUS_MAX, STATUS_MIN } from "./network/overrides/types";
|
|
75
|
+
export { ruleMatches } from "./network/overrides/matchRule";
|
|
76
|
+
|
|
77
|
+
// Pure utils shared with the RN UI
|
|
78
|
+
export { filterNetworkEvents, applyIgnoredPatterns } from "./network/utils/filterNetworkEvents";
|
|
79
|
+
export { splitPinned } from "./network/utils/withPinnedFirst";
|
|
80
|
+
export { extractOperationName } from "./network/utils/extractOperationName";
|
|
81
|
+
export { generateCurl } from "./network/utils/generateCurl";
|