@buoy-gg/network 7.0.36 → 7.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -12
- package/lib/commonjs/index.js +26 -1
- package/lib/commonjs/index.web.js +26 -1
- package/lib/commonjs/network/components/NetworkEventDetailView.js +6 -1
- package/lib/commonjs/network/components/NetworkHeaderMenu.js +4 -0
- package/lib/commonjs/network/components/NetworkModal.js +24 -8
- package/lib/commonjs/network/conditions/NetworkConditionsControl.js +235 -0
- package/lib/commonjs/network/conditions/NetworkConditionsControl.test.js +136 -0
- package/lib/commonjs/network/conditions/NetworkConditionsIcon.js +33 -0
- package/lib/commonjs/network/conditions/NetworkFloatingTool.js +310 -0
- package/lib/commonjs/network/conditions/NetworkFloatingTool.test.js +219 -0
- package/lib/commonjs/network/conditions/store.js +75 -0
- package/lib/commonjs/network/conditions/wait.js +43 -0
- package/lib/commonjs/network/conditions/xhr.js +110 -0
- package/lib/commonjs/network/hooks/useNetworkEvents.js +1 -1
- package/lib/commonjs/network/overrides/applyToXhr.js +10 -8
- package/lib/commonjs/network/overrides/engine.js +2 -1
- package/lib/commonjs/network/sync/networkSyncAdapter.js +26 -1
- package/lib/commonjs/network/utils/generateNetworkCopyText.js +2 -0
- package/lib/commonjs/network/utils/networkEventStore.js +5 -77
- package/lib/commonjs/network/utils/networkListener.js +96 -51
- package/lib/commonjs/network/utils/networkSavedStore.js +2 -2
- package/lib/commonjs/network/utils/selectSavedEvents.js +2 -2
- package/lib/commonjs/preset.js +2 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.web.js +2 -1
- package/lib/module/network/components/NetworkEventDetailView.js +6 -1
- package/lib/module/network/components/NetworkHeaderMenu.js +4 -0
- package/lib/module/network/components/NetworkModal.js +25 -9
- package/lib/module/network/conditions/NetworkConditionsControl.js +231 -0
- package/lib/module/network/conditions/NetworkConditionsControl.test.js +135 -0
- package/lib/module/network/conditions/NetworkConditionsIcon.js +29 -0
- package/lib/module/network/conditions/NetworkFloatingTool.js +304 -0
- package/lib/module/network/conditions/NetworkFloatingTool.test.js +218 -0
- package/lib/module/network/conditions/store.js +65 -0
- package/lib/module/network/conditions/wait.js +38 -0
- package/lib/module/network/conditions/xhr.js +106 -0
- package/lib/module/network/hooks/useNetworkEvents.js +1 -1
- package/lib/module/network/overrides/applyToXhr.js +9 -8
- package/lib/module/network/overrides/engine.js +2 -1
- package/lib/module/network/sync/networkSyncAdapter.js +26 -1
- package/lib/module/network/utils/generateNetworkCopyText.js +2 -0
- package/lib/module/network/utils/networkEventStore.js +6 -77
- package/lib/module/network/utils/networkListener.js +97 -52
- package/lib/module/network/utils/networkSavedStore.js +2 -2
- package/lib/module/network/utils/selectSavedEvents.js +2 -2
- package/lib/module/preset.js +3 -2
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +2 -0
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts +2 -1
- package/lib/typescript/network/components/NetworkHeaderMenu.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts +3 -0
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts +12 -0
- package/lib/typescript/network/conditions/NetworkConditionsControl.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts +2 -0
- package/lib/typescript/network/conditions/NetworkConditionsControl.test.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts +4 -0
- package/lib/typescript/network/conditions/NetworkConditionsIcon.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts +15 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.d.ts.map +1 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts +2 -0
- package/lib/typescript/network/conditions/NetworkFloatingTool.test.d.ts.map +1 -0
- package/lib/typescript/network/conditions/store.d.ts +30 -0
- package/lib/typescript/network/conditions/store.d.ts.map +1 -0
- package/lib/typescript/network/conditions/wait.d.ts +4 -0
- package/lib/typescript/network/conditions/wait.d.ts.map +1 -0
- package/lib/typescript/network/conditions/xhr.d.ts +5 -0
- package/lib/typescript/network/conditions/xhr.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkEvents.d.ts +1 -1
- package/lib/typescript/network/overrides/applyToXhr.d.ts +2 -0
- package/lib/typescript/network/overrides/applyToXhr.d.ts.map +1 -1
- package/lib/typescript/network/overrides/engine.d.ts +0 -10
- package/lib/typescript/network/overrides/engine.d.ts.map +1 -1
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts +17 -0
- package/lib/typescript/network/sync/networkSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/network/types/index.d.ts +1 -0
- package/lib/typescript/network/types/index.d.ts.map +1 -1
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkEventStore.d.ts +0 -15
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkListener.d.ts +2 -1
- package/lib/typescript/network/utils/networkListener.d.ts.map +1 -1
- package/lib/typescript/network/utils/networkSavedStore.d.ts +2 -2
- package/lib/typescript/network/utils/selectSavedEvents.d.ts +2 -2
- package/lib/typescript/preset.d.ts +0 -16
- package/lib/typescript/preset.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.networkConditionsRuntimeId = exports.getNetworkCondition = exports.conditionsCanApply = exports.NETWORK_CONDITIONS = void 0;
|
|
7
|
+
exports.setNetworkCondition = setNetworkCondition;
|
|
8
|
+
exports.snapshotNetworkCondition = snapshotNetworkCondition;
|
|
9
|
+
exports.subscribeNetworkConditions = void 0;
|
|
10
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
|
+
var _networkListener = require("../utils/networkListener");
|
|
12
|
+
const NETWORK_CONDITIONS = exports.NETWORK_CONDITIONS = {
|
|
13
|
+
normal: {
|
|
14
|
+
label: "Normal",
|
|
15
|
+
delayMs: 0
|
|
16
|
+
},
|
|
17
|
+
offline: {
|
|
18
|
+
label: "Offline (requests)",
|
|
19
|
+
delayMs: 0
|
|
20
|
+
},
|
|
21
|
+
slow: {
|
|
22
|
+
label: "Slow (+500 ms)",
|
|
23
|
+
delayMs: 500
|
|
24
|
+
},
|
|
25
|
+
verySlow: {
|
|
26
|
+
label: "Very slow (+2000 ms)",
|
|
27
|
+
delayMs: 2000
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const networkConditionsRuntimeId = exports.networkConditionsRuntimeId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
31
|
+
let profile = "normal";
|
|
32
|
+
let keepAlive;
|
|
33
|
+
const listeners = new Set();
|
|
34
|
+
const conditionsCanApply = () => typeof __DEV__ !== "undefined" && __DEV__ && (0, _sharedUi.hasAccountAccess)();
|
|
35
|
+
exports.conditionsCanApply = conditionsCanApply;
|
|
36
|
+
const getNetworkCondition = () => profile;
|
|
37
|
+
exports.getNetworkCondition = getNetworkCondition;
|
|
38
|
+
const subscribeNetworkConditions = listener => {
|
|
39
|
+
listeners.add(listener);
|
|
40
|
+
return () => {
|
|
41
|
+
listeners.delete(listener);
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
exports.subscribeNetworkConditions = subscribeNetworkConditions;
|
|
45
|
+
function setNetworkCondition(value) {
|
|
46
|
+
if (typeof value !== "string" || !Object.prototype.hasOwnProperty.call(NETWORK_CONDITIONS, value)) {
|
|
47
|
+
throw new Error("Choose normal, offline, slow, or verySlow.");
|
|
48
|
+
}
|
|
49
|
+
if (value !== "normal" && !conditionsCanApply()) {
|
|
50
|
+
throw new Error("Network conditions require an admitted account and a development build.");
|
|
51
|
+
}
|
|
52
|
+
if (value === profile) return profile;
|
|
53
|
+
if (value !== "normal" && !keepAlive) {
|
|
54
|
+
(0, _networkListener.startNetworkListener)();
|
|
55
|
+
keepAlive = (0, _networkListener.addNetworkListener)(() => {});
|
|
56
|
+
} else if (value === "normal") {
|
|
57
|
+
keepAlive?.();
|
|
58
|
+
keepAlive = undefined;
|
|
59
|
+
if ((0, _networkListener.getNetworkListenerCount)() === 0) (0, _networkListener.stopNetworkListener)();
|
|
60
|
+
}
|
|
61
|
+
profile = value;
|
|
62
|
+
listeners.forEach(listener => listener());
|
|
63
|
+
return profile;
|
|
64
|
+
}
|
|
65
|
+
function snapshotNetworkCondition(url) {
|
|
66
|
+
if (profile === "normal" || !conditionsCanApply() || !/^https?:\/\//i.test(url)) return;
|
|
67
|
+
return Object.freeze({
|
|
68
|
+
profile,
|
|
69
|
+
delayMs: NETWORK_CONDITIONS[profile].delayMs
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
(0, _sharedUi.subscribeAccountAccess)(() => {
|
|
73
|
+
if (!(0, _sharedUi.hasAccountAccess)()) setNetworkCondition("normal");
|
|
74
|
+
listeners.forEach(listener => listener());
|
|
75
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.throwIfAborted = throwIfAborted;
|
|
7
|
+
exports.waitBeforeDispatch = waitBeforeDispatch;
|
|
8
|
+
function throwIfAborted(signal) {
|
|
9
|
+
if (!signal?.aborted) return;
|
|
10
|
+
if (signal.reason !== undefined) throw signal.reason;
|
|
11
|
+
const error = new Error("The request was aborted.");
|
|
12
|
+
error.name = "AbortError";
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Wait before dispatch, releasing the timer and listener on cancellation. */
|
|
17
|
+
function waitBeforeDispatch(ms, signal) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
try {
|
|
20
|
+
throwIfAborted(signal);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
reject(error);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const cleanup = () => signal?.removeEventListener("abort", abort);
|
|
26
|
+
const abort = () => {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
cleanup();
|
|
29
|
+
try {
|
|
30
|
+
throwIfAborted(signal);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
reject(error);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const timer = setTimeout(() => {
|
|
36
|
+
cleanup();
|
|
37
|
+
resolve();
|
|
38
|
+
}, ms);
|
|
39
|
+
signal?.addEventListener("abort", abort, {
|
|
40
|
+
once: true
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.runConditionXhr = runConditionXhr;
|
|
7
|
+
var _applyToXhr = require("../overrides/applyToXhr");
|
|
8
|
+
/** Active simulation uses a send-to-completion deadline, including the wait. */
|
|
9
|
+
function runConditionXhr(target, url, condition, outcome, dispatch) {
|
|
10
|
+
if (!(0, _applyToXhr.canSynthesize)(target)) throw new Error("Network conditions require React Native XMLHttpRequest.");
|
|
11
|
+
const xhr = target;
|
|
12
|
+
if (xhr.readyState !== 1 || xhr._sent) throw new Error("Request is not open or has already been sent.");
|
|
13
|
+
const started = Date.now();
|
|
14
|
+
let timeout = xhr.timeout;
|
|
15
|
+
let settled = false;
|
|
16
|
+
let dispatching = false;
|
|
17
|
+
let deadline;
|
|
18
|
+
let pending;
|
|
19
|
+
const originalAbort = xhr.abort;
|
|
20
|
+
const originalOpen = xhr.open;
|
|
21
|
+
const abortDescriptor = Object.getOwnPropertyDescriptor(xhr, "abort");
|
|
22
|
+
const openDescriptor = Object.getOwnPropertyDescriptor(xhr, "open");
|
|
23
|
+
const timeoutDescriptor = Object.getOwnPropertyDescriptor(xhr, "timeout");
|
|
24
|
+
const restore = (key, descriptor) => {
|
|
25
|
+
if (descriptor) Object.defineProperty(xhr, key, descriptor);else delete xhr[key];
|
|
26
|
+
};
|
|
27
|
+
const cleanup = () => {
|
|
28
|
+
if (settled) return;
|
|
29
|
+
settled = true;
|
|
30
|
+
clearTimeout(pending);
|
|
31
|
+
clearTimeout(deadline);
|
|
32
|
+
xhr.removeEventListener("loadend", cleanup);
|
|
33
|
+
restore("abort", abortDescriptor);
|
|
34
|
+
restore("open", openDescriptor);
|
|
35
|
+
restore("timeout", timeoutDescriptor);
|
|
36
|
+
xhr.timeout = timeout;
|
|
37
|
+
};
|
|
38
|
+
const fail = timedOut => {
|
|
39
|
+
if (settled) return;
|
|
40
|
+
// DONE suppresses abort events while the native transport is cancelled.
|
|
41
|
+
Object.assign(xhr, {
|
|
42
|
+
readyState: 4
|
|
43
|
+
});
|
|
44
|
+
originalAbort.call(xhr);
|
|
45
|
+
xhr._aborted = false;
|
|
46
|
+
xhr._hasError = true;
|
|
47
|
+
xhr._timedOut = timedOut;
|
|
48
|
+
xhr._response = timedOut ? "Request timed out (Buoy conditions)" : "Network request failed (Buoy Offline)";
|
|
49
|
+
xhr.setReadyState(4);
|
|
50
|
+
cleanup();
|
|
51
|
+
};
|
|
52
|
+
const expired = () => timeout > 0 && Date.now() - started >= timeout;
|
|
53
|
+
const scheduleDeadline = () => {
|
|
54
|
+
clearTimeout(deadline);
|
|
55
|
+
if (timeout > 0) deadline = setTimeout(() => fail(true), Math.max(0, timeout - (Date.now() - started)));
|
|
56
|
+
};
|
|
57
|
+
Object.defineProperty(xhr, "timeout", {
|
|
58
|
+
configurable: true,
|
|
59
|
+
get: () => dispatching ? 0 : timeout,
|
|
60
|
+
set: value => {
|
|
61
|
+
timeout = Math.max(0, Number(value) || 0);
|
|
62
|
+
scheduleDeadline();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
Object.defineProperty(xhr, "abort", {
|
|
66
|
+
configurable: true,
|
|
67
|
+
value: () => {
|
|
68
|
+
if (!settled) {
|
|
69
|
+
originalAbort.call(xhr);
|
|
70
|
+
cleanup();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(xhr, "open", {
|
|
75
|
+
configurable: true,
|
|
76
|
+
value: (...args) => {
|
|
77
|
+
const result = Reflect.apply(originalOpen, xhr, args);
|
|
78
|
+
cleanup();
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
xhr._sent = true;
|
|
83
|
+
xhr._requestId = null;
|
|
84
|
+
xhr.addEventListener("loadend", cleanup);
|
|
85
|
+
scheduleDeadline();
|
|
86
|
+
pending = setTimeout(() => {
|
|
87
|
+
if (settled) return;
|
|
88
|
+
if (expired()) {
|
|
89
|
+
fail(true);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (condition.profile === "offline") {
|
|
93
|
+
fail(false);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (outcome && outcome.kind !== "delay") {
|
|
97
|
+
(0, _applyToXhr.completeNativeOverride)(xhr, url, outcome);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
xhr._sent = false;
|
|
101
|
+
dispatching = true;
|
|
102
|
+
try {
|
|
103
|
+
dispatch();
|
|
104
|
+
} catch {
|
|
105
|
+
fail(false);
|
|
106
|
+
} finally {
|
|
107
|
+
dispatching = false;
|
|
108
|
+
}
|
|
109
|
+
}, condition.delayMs + (outcome?.delayMs ?? 0));
|
|
110
|
+
}
|
|
@@ -18,7 +18,7 @@ var _formatGraphQLVariables = require("../utils/formatGraphQLVariables");
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* @deprecated Read the live limit with `useLimit("network.requests")` — this
|
|
21
|
-
* only reports the FREE tier's value and
|
|
21
|
+
* only reports the FREE tier's value and ignores the Weekend Pass. Kept
|
|
22
22
|
* because it is part of the package's public exports.
|
|
23
23
|
*/
|
|
24
24
|
const FREE_TIER_REQUEST_LIMIT = exports.FREE_TIER_REQUEST_LIMIT = _sharedUi.TIER_LIMITS["network.requests"].free;
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.canSynthesize = canSynthesize;
|
|
7
7
|
exports.canSynthesizeStandard = canSynthesizeStandard;
|
|
8
|
+
exports.completeNativeOverride = completeNativeOverride;
|
|
8
9
|
exports.runOverride = runOverride;
|
|
9
10
|
/**
|
|
10
11
|
* Perform an override outcome against an XMLHttpRequest — React Native's OR a
|
|
@@ -406,16 +407,12 @@ function runOverride(xhr, url, outcome, passThrough) {
|
|
|
406
407
|
// had already aborted. Claiming it means abort() dispatches DONE + 'abort'
|
|
407
408
|
// and whatwg-fetch rejects with AbortError, as it should.
|
|
408
409
|
internals._sent = true;
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
// `send()` — which we're skipping — so it would survive here, and
|
|
413
|
-
// `abort()` would hand that string to `RCTNetworking.abortRequest()`.
|
|
414
|
-
// Nulling it makes abort a clean no-op on the native side.
|
|
415
|
-
internals._requestId = null;
|
|
416
|
-
}
|
|
410
|
+
|
|
411
|
+
// There is no native request to cancel until a delayed send is dispatched.
|
|
412
|
+
internals._requestId = null;
|
|
417
413
|
let timer = null;
|
|
418
414
|
const onAbort = () => {
|
|
415
|
+
xhr.removeEventListener("abort", onAbort);
|
|
419
416
|
if (timer !== null) {
|
|
420
417
|
clearTimeout(timer);
|
|
421
418
|
timer = null;
|
|
@@ -448,4 +445,9 @@ function runOverride(xhr, url, outcome, passThrough) {
|
|
|
448
445
|
// 'load' before they exist.
|
|
449
446
|
timer = setTimeout(fire, outcome.delayMs);
|
|
450
447
|
return true;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Complete an already scheduled native outcome without adding another timer. */
|
|
451
|
+
function completeNativeOverride(xhr, url, outcome) {
|
|
452
|
+
if (outcome.kind === "fail") fail(xhr, outcome);else if (outcome.kind === "respond") respond(xhr, url, outcome);
|
|
451
453
|
}
|
|
@@ -8,6 +8,7 @@ exports.inspectEngine = inspectEngine;
|
|
|
8
8
|
exports.overrideForRequest = overrideForRequest;
|
|
9
9
|
exports.overridesCanApply = overridesCanApply;
|
|
10
10
|
exports.setOverrideHooks = setOverrideHooks;
|
|
11
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
12
|
var _resolveOverride = require("./resolveOverride");
|
|
12
13
|
/**
|
|
13
14
|
* The seam between the interceptor and the rule store.
|
|
@@ -42,7 +43,7 @@ function setOverrideHooks(next) {
|
|
|
42
43
|
function overridesCanApply() {
|
|
43
44
|
// `__DEV__` is undefined outside React Native (unit tests, the desktop
|
|
44
45
|
// bundle); only an explicit `false` — a real release build — disables it.
|
|
45
|
-
return !(typeof __DEV__ !== "undefined" && !__DEV__);
|
|
46
|
+
return (0, _sharedUi.hasAccountAccess)() && !(typeof __DEV__ !== "undefined" && !__DEV__);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/** Why overrides are inert, for a caller that has to explain it to a person. */
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.networkSyncAdapter = exports.SNAPSHOT_HEADER_VALUE_LIMIT = exports.SNAPSHOT_EVENTS_BUDGET = exports.SNAPSHOT_BODY_INLINE_LIMIT = void 0;
|
|
7
|
+
var _store = require("../conditions/store");
|
|
7
8
|
var _networkEventStore = require("../utils/networkEventStore");
|
|
8
9
|
var _networkSavedStore = require("../utils/networkSavedStore");
|
|
9
10
|
var _overrideRulesStore = require("../overrides/overrideRulesStore");
|
|
@@ -476,10 +477,13 @@ function baseFromRequest(params) {
|
|
|
476
477
|
* `NetworkPanel`/`NetworkInline` accept either shape.
|
|
477
478
|
*/
|
|
478
479
|
const networkSyncAdapter = exports.networkSyncAdapter = {
|
|
480
|
+
dispose: () => {
|
|
481
|
+
_overrideRulesStore.overrideRulesStore.setEnabled(false);
|
|
482
|
+
},
|
|
479
483
|
// v5: honest request/responseSize + explicit request/responseBodyOmitted
|
|
480
484
|
// flags, and a total snapshot budget so the list degrades instead of the
|
|
481
485
|
// whole panel being dropped at the emit layer.
|
|
482
|
-
version:
|
|
486
|
+
version: 6,
|
|
483
487
|
getSnapshot: () => {
|
|
484
488
|
// Piggyback the interception health check on the watch loop: while a
|
|
485
489
|
// dashboard/MCP watch is open this runs continuously, so a clobbered
|
|
@@ -488,6 +492,12 @@ const networkSyncAdapter = exports.networkSyncAdapter = {
|
|
|
488
492
|
// reads when healthy.
|
|
489
493
|
_networkEventStore.networkEventStore.ensureInterception();
|
|
490
494
|
return {
|
|
495
|
+
conditions: {
|
|
496
|
+
version: 1,
|
|
497
|
+
runtimeId: _store.networkConditionsRuntimeId,
|
|
498
|
+
profile: (0, _store.getNetworkCondition)(),
|
|
499
|
+
available: (0, _store.conditionsCanApply)()
|
|
500
|
+
},
|
|
491
501
|
events: fitEventsToBudget(_networkEventStore.networkEventStore.getEvents()),
|
|
492
502
|
/**
|
|
493
503
|
* Response-override rules live on the DEVICE — that's the only place they
|
|
@@ -508,17 +518,32 @@ const networkSyncAdapter = exports.networkSyncAdapter = {
|
|
|
508
518
|
// Pinning with no traffic in flight still has to reach the dashboard, so
|
|
509
519
|
// both stores drive the snapshot.
|
|
510
520
|
const unsubscribeEvents = _networkEventStore.networkEventStore.subscribeToEvents(onChange);
|
|
521
|
+
const unsubscribeConditions = (0, _store.subscribeNetworkConditions)(onChange);
|
|
511
522
|
const unsubscribeSaved = _networkSavedStore.networkSavedStore.subscribe(onChange);
|
|
512
523
|
// Same reasoning for rules: editing one with no traffic in flight must
|
|
513
524
|
// still reach the dashboard. Subscribing also hydrates the rule list.
|
|
514
525
|
const unsubscribeOverrides = _overrideRulesStore.overrideRulesStore.subscribe(onChange);
|
|
515
526
|
return () => {
|
|
527
|
+
unsubscribeConditions();
|
|
516
528
|
unsubscribeEvents();
|
|
517
529
|
unsubscribeSaved();
|
|
518
530
|
unsubscribeOverrides();
|
|
519
531
|
};
|
|
520
532
|
},
|
|
521
533
|
actions: {
|
|
534
|
+
getNetworkConditions: () => ({
|
|
535
|
+
runtimeId: _store.networkConditionsRuntimeId,
|
|
536
|
+
profile: (0, _store.getNetworkCondition)(),
|
|
537
|
+
available: (0, _store.conditionsCanApply)()
|
|
538
|
+
}),
|
|
539
|
+
setNetworkConditions: params => {
|
|
540
|
+
const profile = params && typeof params === "object" ? params.profile : undefined;
|
|
541
|
+
return {
|
|
542
|
+
runtimeId: _store.networkConditionsRuntimeId,
|
|
543
|
+
profile: (0, _store.setNetworkCondition)(profile),
|
|
544
|
+
available: (0, _store.conditionsCanApply)()
|
|
545
|
+
};
|
|
546
|
+
},
|
|
522
547
|
clearEvents: () => {
|
|
523
548
|
_networkEventStore.networkEventStore.clearEvents();
|
|
524
549
|
},
|
|
@@ -81,6 +81,7 @@ function generateNetworkCopyText(events, copySettings) {
|
|
|
81
81
|
obj.statusText = event.statusText;
|
|
82
82
|
}
|
|
83
83
|
if (copySettings.includeDuration) obj.duration = event.duration;
|
|
84
|
+
if (event.condition) obj.condition = event.condition;
|
|
84
85
|
if (copySettings.includeTimestamp) obj.timestamp = event.timestamp;
|
|
85
86
|
if (copySettings.includeClient) obj.requestClient = event.requestClient;
|
|
86
87
|
if (copySettings.includeSizes) {
|
|
@@ -119,6 +120,7 @@ function generateNetworkCopyText(events, copySettings) {
|
|
|
119
120
|
if (copySettings.includeClient && event.requestClient) {
|
|
120
121
|
md += `**Client:** ${event.requestClient}\n`;
|
|
121
122
|
}
|
|
123
|
+
if (event.condition) md += `Conditions: ${event.condition.profile} (+${event.condition.delayMs} ms)\n`;
|
|
122
124
|
if (copySettings.includeDuration && event.duration) {
|
|
123
125
|
md += `**Duration:** ${event.duration}ms\n`;
|
|
124
126
|
}
|
|
@@ -46,14 +46,6 @@ var _networkSavedStore = require("./networkSavedStore");
|
|
|
46
46
|
const BOOT_BUFFER_MAX = 200;
|
|
47
47
|
class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
48
48
|
pendingRequests = new Map();
|
|
49
|
-
/**
|
|
50
|
-
* Recently seen `METHOD:url` keys → when, and which event id we kept.
|
|
51
|
-
*
|
|
52
|
-
* The timestamp powers the dual-interception dedup below. The id is what lets
|
|
53
|
-
* a DROPPED duplicate still contribute something the kept event doesn't know
|
|
54
|
-
* — see `applyOverrideMark`.
|
|
55
|
-
*/
|
|
56
|
-
recentRequests = new Map();
|
|
57
49
|
rawListenerUnsubscribe = null;
|
|
58
50
|
/** Raw events captured before the first consumer subscribed — see startBootCapture. */
|
|
59
51
|
bootBuffer = [];
|
|
@@ -85,6 +77,7 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
85
77
|
* state therefore never gains events it didn't ask for).
|
|
86
78
|
*/
|
|
87
79
|
startBootCapture() {
|
|
80
|
+
if ((0, _sharedUi.getAccountAccess)().status === "locked") return;
|
|
88
81
|
if (this.mirrorMode || this.bootBufferUnsubscribe || this.isCapturing()) {
|
|
89
82
|
return;
|
|
90
83
|
}
|
|
@@ -139,6 +132,7 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
139
132
|
* Stop capturing network events
|
|
140
133
|
*/
|
|
141
134
|
stopCapturing() {
|
|
135
|
+
this.takeBootBuffer();
|
|
142
136
|
// Remove our raw listener
|
|
143
137
|
if (this.rawListenerUnsubscribe) {
|
|
144
138
|
this.rawListenerUnsubscribe();
|
|
@@ -205,57 +199,11 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
205
199
|
* Process a network listener event
|
|
206
200
|
*/
|
|
207
201
|
processNetworkEvent(event) {
|
|
202
|
+
if ((0, _sharedUi.getAccountAccess)().status === "locked") return;
|
|
208
203
|
const {
|
|
209
204
|
request
|
|
210
205
|
} = event;
|
|
211
206
|
if (event.type === "request") {
|
|
212
|
-
// Check for duplicate request based on URL, method, and timing.
|
|
213
|
-
// The clock is the event's CAPTURE time, not Date.now(): boot-buffered
|
|
214
|
-
// events are flushed in one synchronous burst, and wall-clock "now"
|
|
215
|
-
// would put two genuinely separate boot requests to the same endpoint
|
|
216
|
-
// inside the 50ms dual-interception window, silently dropping one. For
|
|
217
|
-
// live events the two clocks are the same (the raw event is emitted
|
|
218
|
-
// synchronously at capture).
|
|
219
|
-
const requestKey = `${request.method}:${request.url}`;
|
|
220
|
-
const now = event.timestamp instanceof Date ? event.timestamp.getTime() : Date.now();
|
|
221
|
-
const recent = this.recentRequests.get(requestKey);
|
|
222
|
-
|
|
223
|
-
// If same request within 50ms, likely a duplicate from XHR/fetch dual
|
|
224
|
-
// interception. The age must be non-negative: Date.now() is not
|
|
225
|
-
// monotonic, and after a backwards clock adjustment (iOS time sync) a
|
|
226
|
-
// future-dated entry would satisfy `< 50` for EVERY request to this
|
|
227
|
-
// endpoint until the clock caught up — capture going silent for minutes
|
|
228
|
-
// to hours, "fixed" only by Clear wiping this map (field-reported). A
|
|
229
|
-
// negative age falls through and re-stamps the entry with the new clock.
|
|
230
|
-
if (recent && now - recent.at >= 0 && now - recent.at < 50) {
|
|
231
|
-
// …but the duplicate is not always redundant. On React Native a
|
|
232
|
-
// `fetch()` is seen twice: once by the fetch wrapper, which is what
|
|
233
|
-
// creates this event, and again by the XHR wrapper underneath it —
|
|
234
|
-
// and ONLY the XHR layer resolves overrides, because that's where the
|
|
235
|
-
// request can actually be intercepted. So the mark arrives on the copy
|
|
236
|
-
// we're about to throw away. Move it across before dropping.
|
|
237
|
-
if (event.override) {
|
|
238
|
-
this.applyOverrideMark(recent.id, event.override);
|
|
239
|
-
}
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
this.recentRequests.set(requestKey, {
|
|
243
|
-
at: now,
|
|
244
|
-
id: request.id
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Clean up old entries to prevent memory leak. Future-dated entries are
|
|
248
|
-
// stale by definition (clock moved backwards) — drop those too, or they
|
|
249
|
-
// survive every sweep until the clock overtakes them.
|
|
250
|
-
if (this.recentRequests.size > 100) {
|
|
251
|
-
const cutoff = now - 5000;
|
|
252
|
-
for (const [key, entry] of this.recentRequests.entries()) {
|
|
253
|
-
if (entry.at < cutoff || entry.at > now) {
|
|
254
|
-
this.recentRequests.delete(key);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
207
|
// Build full URL with query params if present
|
|
260
208
|
const queryString = request.params ? `?${new URLSearchParams(request.params).toString()}` : "";
|
|
261
209
|
const fullUrl = `${request.url}${queryString}`;
|
|
@@ -287,7 +235,8 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
287
235
|
graphqlVariables,
|
|
288
236
|
// Present when this request came straight through the XHR wrapper
|
|
289
237
|
// (axios, raw XHR). For `fetch` it arrives later — see the dedup branch.
|
|
290
|
-
override: event.override
|
|
238
|
+
override: event.override,
|
|
239
|
+
condition: event.condition
|
|
291
240
|
};
|
|
292
241
|
this.pendingRequests.set(request.id, networkEvent);
|
|
293
242
|
this.events = [networkEvent, ...this.events].slice(0, this.maxEvents);
|
|
@@ -331,26 +280,6 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
331
280
|
}
|
|
332
281
|
}
|
|
333
282
|
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Stamp an override mark onto an already-recorded event.
|
|
337
|
-
*
|
|
338
|
-
* Only ever moves a mark from a dropped duplicate onto the event that was
|
|
339
|
-
* kept, so it never overwrites one that is already there.
|
|
340
|
-
*/
|
|
341
|
-
applyOverrideMark(id, override) {
|
|
342
|
-
const index = this.events.findIndex(e => e.id === id);
|
|
343
|
-
if (index === -1 || this.events[index].override) return;
|
|
344
|
-
const updatedEvent = {
|
|
345
|
-
...this.events[index],
|
|
346
|
-
override
|
|
347
|
-
};
|
|
348
|
-
this.events = this.events.map((e, i) => i === index ? updatedEvent : e);
|
|
349
|
-
const pending = this.pendingRequests.get(id);
|
|
350
|
-
if (pending) this.pendingRequests.set(id, updatedEvent);
|
|
351
|
-
this.notify(updatedEvent);
|
|
352
|
-
this.notifyArrayListeners();
|
|
353
|
-
}
|
|
354
283
|
extractHost(url) {
|
|
355
284
|
try {
|
|
356
285
|
const urlObj = new URL(url);
|
|
@@ -388,7 +317,6 @@ class NetworkEventStore extends _sharedUi.BaseEventStore {
|
|
|
388
317
|
clearEvents() {
|
|
389
318
|
super.clearEvents();
|
|
390
319
|
this.pendingRequests.clear();
|
|
391
|
-
this.recentRequests.clear();
|
|
392
320
|
}
|
|
393
321
|
|
|
394
322
|
/**
|