@swmansion/argent 0.29.1-next.20 → 0.29.1-next.21
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 +1 -1
- package/dist/cli-cmds.mjs +1 -1
- package/dist/installer.mjs +1 -1
- package/dist/tool-server.cjs +1174 -164
- package/package.json +1 -1
- package/skills/argent-metro-debugger/SKILL.md +1 -1
package/dist/tool-server.cjs
CHANGED
|
@@ -11384,11 +11384,11 @@ var init_nl = __esm({
|
|
|
11384
11384
|
case "too_small": {
|
|
11385
11385
|
const adj = issue2.inclusive ? ">=" : ">";
|
|
11386
11386
|
const sizing = getSizing(issue2.origin);
|
|
11387
|
-
const
|
|
11387
|
+
const shortName2 = issue2.origin === "date" ? "vroeg" : issue2.origin === "string" ? "kort" : "klein";
|
|
11388
11388
|
if (sizing) {
|
|
11389
|
-
return `Te ${
|
|
11389
|
+
return `Te ${shortName2}: verwacht dat ${issue2.origin} ${adj}${issue2.minimum.toString()} ${sizing.unit} ${sizing.verb}`;
|
|
11390
11390
|
}
|
|
11391
|
-
return `Te ${
|
|
11391
|
+
return `Te ${shortName2}: verwacht dat ${issue2.origin} ${adj}${issue2.minimum.toString()} is`;
|
|
11392
11392
|
}
|
|
11393
11393
|
case "invalid_format": {
|
|
11394
11394
|
const _issue = issue2;
|
|
@@ -100666,7 +100666,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
100666
100666
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
100667
100667
|
function readCliVersion() {
|
|
100668
100668
|
if (true) {
|
|
100669
|
-
return "0.29.1-next.
|
|
100669
|
+
return "0.29.1-next.21";
|
|
100670
100670
|
}
|
|
100671
100671
|
return "0.0.0";
|
|
100672
100672
|
}
|
|
@@ -111742,10 +111742,10 @@ var PublishError = class _PublishError {
|
|
|
111742
111742
|
requestId;
|
|
111743
111743
|
errorCode;
|
|
111744
111744
|
reasonPhrase;
|
|
111745
|
-
constructor({ requestId, errorCode, reasonPhrase }) {
|
|
111745
|
+
constructor({ requestId, errorCode, reasonPhrase: reasonPhrase2 }) {
|
|
111746
111746
|
this.requestId = requestId;
|
|
111747
111747
|
this.errorCode = errorCode;
|
|
111748
|
-
this.reasonPhrase =
|
|
111748
|
+
this.reasonPhrase = reasonPhrase2;
|
|
111749
111749
|
}
|
|
111750
111750
|
async #encode(w) {
|
|
111751
111751
|
await w.u62(this.requestId);
|
|
@@ -111761,8 +111761,8 @@ var PublishError = class _PublishError {
|
|
|
111761
111761
|
static async #decode(r) {
|
|
111762
111762
|
const requestId = await r.u62();
|
|
111763
111763
|
const errorCode = Number(await r.u62());
|
|
111764
|
-
const
|
|
111765
|
-
return new _PublishError({ requestId, errorCode, reasonPhrase });
|
|
111764
|
+
const reasonPhrase2 = await r.string();
|
|
111765
|
+
return new _PublishError({ requestId, errorCode, reasonPhrase: reasonPhrase2 });
|
|
111766
111766
|
}
|
|
111767
111767
|
};
|
|
111768
111768
|
var PublishDone = class _PublishDone {
|
|
@@ -111770,10 +111770,10 @@ var PublishDone = class _PublishDone {
|
|
|
111770
111770
|
requestId;
|
|
111771
111771
|
statusCode;
|
|
111772
111772
|
reasonPhrase;
|
|
111773
|
-
constructor({ requestId, statusCode, reasonPhrase }) {
|
|
111773
|
+
constructor({ requestId, statusCode, reasonPhrase: reasonPhrase2 }) {
|
|
111774
111774
|
this.requestId = requestId;
|
|
111775
111775
|
this.statusCode = statusCode;
|
|
111776
|
-
this.reasonPhrase =
|
|
111776
|
+
this.reasonPhrase = reasonPhrase2;
|
|
111777
111777
|
}
|
|
111778
111778
|
async #encode(w, version3) {
|
|
111779
111779
|
if (version3 === Version.DRAFT_14 || version3 === Version.DRAFT_15 || version3 === Version.DRAFT_16) {
|
|
@@ -111795,8 +111795,8 @@ var PublishDone = class _PublishDone {
|
|
|
111795
111795
|
const requestId = version3 === Version.DRAFT_14 || version3 === Version.DRAFT_15 || version3 === Version.DRAFT_16 ? await r.u62() : void 0;
|
|
111796
111796
|
const statusCode = Number(await r.u62());
|
|
111797
111797
|
await r.u62();
|
|
111798
|
-
const
|
|
111799
|
-
return new _PublishDone({ requestId, statusCode, reasonPhrase });
|
|
111798
|
+
const reasonPhrase2 = await r.string();
|
|
111799
|
+
return new _PublishDone({ requestId, statusCode, reasonPhrase: reasonPhrase2 });
|
|
111800
111800
|
}
|
|
111801
111801
|
};
|
|
111802
111802
|
|
|
@@ -111876,10 +111876,10 @@ var PublishNamespaceError = class _PublishNamespaceError {
|
|
|
111876
111876
|
requestId;
|
|
111877
111877
|
errorCode;
|
|
111878
111878
|
reasonPhrase;
|
|
111879
|
-
constructor({ requestId, errorCode, reasonPhrase }) {
|
|
111879
|
+
constructor({ requestId, errorCode, reasonPhrase: reasonPhrase2 }) {
|
|
111880
111880
|
this.requestId = requestId;
|
|
111881
111881
|
this.errorCode = errorCode;
|
|
111882
|
-
this.reasonPhrase =
|
|
111882
|
+
this.reasonPhrase = reasonPhrase2;
|
|
111883
111883
|
}
|
|
111884
111884
|
async #encode(w) {
|
|
111885
111885
|
await w.u62(this.requestId);
|
|
@@ -111895,8 +111895,8 @@ var PublishNamespaceError = class _PublishNamespaceError {
|
|
|
111895
111895
|
static async #decode(r) {
|
|
111896
111896
|
const requestId = await r.u62();
|
|
111897
111897
|
const errorCode = Number(await r.u62());
|
|
111898
|
-
const
|
|
111899
|
-
return new _PublishNamespaceError({ requestId, errorCode, reasonPhrase });
|
|
111898
|
+
const reasonPhrase2 = await r.string();
|
|
111899
|
+
return new _PublishNamespaceError({ requestId, errorCode, reasonPhrase: reasonPhrase2 });
|
|
111900
111900
|
}
|
|
111901
111901
|
};
|
|
111902
111902
|
var PublishNamespaceDone = class _PublishNamespaceDone {
|
|
@@ -112249,10 +112249,10 @@ var RequestError = class _RequestError {
|
|
|
112249
112249
|
errorCode;
|
|
112250
112250
|
reasonPhrase;
|
|
112251
112251
|
retryInterval;
|
|
112252
|
-
constructor({ requestId, errorCode, reasonPhrase, retryInterval = 0n }) {
|
|
112252
|
+
constructor({ requestId, errorCode, reasonPhrase: reasonPhrase2, retryInterval = 0n }) {
|
|
112253
112253
|
this.requestId = requestId;
|
|
112254
112254
|
this.errorCode = errorCode;
|
|
112255
|
-
this.reasonPhrase =
|
|
112255
|
+
this.reasonPhrase = reasonPhrase2;
|
|
112256
112256
|
this.retryInterval = retryInterval;
|
|
112257
112257
|
}
|
|
112258
112258
|
async #encode(w, version3) {
|
|
@@ -112274,8 +112274,8 @@ var RequestError = class _RequestError {
|
|
|
112274
112274
|
const requestId = version3 === Version.DRAFT_14 || version3 === Version.DRAFT_15 || version3 === Version.DRAFT_16 ? await r.u62() : void 0;
|
|
112275
112275
|
const errorCode = Number(await r.u62());
|
|
112276
112276
|
const retryInterval = version3 === Version.DRAFT_14 || version3 === Version.DRAFT_15 ? 0n : await r.u62();
|
|
112277
|
-
const
|
|
112278
|
-
return new _RequestError({ requestId, errorCode, reasonPhrase, retryInterval });
|
|
112277
|
+
const reasonPhrase2 = await r.string();
|
|
112278
|
+
return new _RequestError({ requestId, errorCode, reasonPhrase: reasonPhrase2, retryInterval });
|
|
112279
112279
|
}
|
|
112280
112280
|
static async decode(r, version3) {
|
|
112281
112281
|
return decode7(r, (rd) => _RequestError.#decode(rd, version3));
|
|
@@ -112488,10 +112488,10 @@ var SubscribeError = class _SubscribeError {
|
|
|
112488
112488
|
requestId;
|
|
112489
112489
|
errorCode;
|
|
112490
112490
|
reasonPhrase;
|
|
112491
|
-
constructor({ requestId, errorCode, reasonPhrase }) {
|
|
112491
|
+
constructor({ requestId, errorCode, reasonPhrase: reasonPhrase2 }) {
|
|
112492
112492
|
this.requestId = requestId;
|
|
112493
112493
|
this.errorCode = errorCode;
|
|
112494
|
-
this.reasonPhrase =
|
|
112494
|
+
this.reasonPhrase = reasonPhrase2;
|
|
112495
112495
|
}
|
|
112496
112496
|
async #encode(w) {
|
|
112497
112497
|
await w.u62(this.requestId);
|
|
@@ -112507,8 +112507,8 @@ var SubscribeError = class _SubscribeError {
|
|
|
112507
112507
|
static async #decode(r) {
|
|
112508
112508
|
const requestId = await r.u62();
|
|
112509
112509
|
const errorCode = Number(await r.u62());
|
|
112510
|
-
const
|
|
112511
|
-
return new _SubscribeError({ requestId, errorCode, reasonPhrase });
|
|
112510
|
+
const reasonPhrase2 = await r.string();
|
|
112511
|
+
return new _SubscribeError({ requestId, errorCode, reasonPhrase: reasonPhrase2 });
|
|
112512
112512
|
}
|
|
112513
112513
|
};
|
|
112514
112514
|
var SubscribeUpdate = class _SubscribeUpdate {
|
|
@@ -114177,16 +114177,16 @@ var Subscriber2 = class {
|
|
|
114177
114177
|
console.debug(`subscribe written: id=${requestId} broadcast=${broadcast} track=${request3.name}`);
|
|
114178
114178
|
const respTypeId = await state3.stream.reader.u53();
|
|
114179
114179
|
if (respTypeId !== SubscribeOk.id) {
|
|
114180
|
-
let
|
|
114180
|
+
let reasonPhrase2 = "unknown error";
|
|
114181
114181
|
try {
|
|
114182
114182
|
if (respTypeId === RequestError.id) {
|
|
114183
114183
|
const err = version3 === Version.DRAFT_14 ? await SubscribeError.decode(state3.stream.reader, version3) : await RequestError.decode(state3.stream.reader, version3);
|
|
114184
|
-
|
|
114184
|
+
reasonPhrase2 = `code=${err.errorCode} reason=${err.reasonPhrase}`;
|
|
114185
114185
|
}
|
|
114186
114186
|
} catch {
|
|
114187
114187
|
}
|
|
114188
114188
|
state3.rejected = true;
|
|
114189
|
-
throw new Error(`SUBSCRIBE error: ${
|
|
114189
|
+
throw new Error(`SUBSCRIBE error: ${reasonPhrase2}`);
|
|
114190
114190
|
}
|
|
114191
114191
|
const ok = await SubscribeOk.decode(state3.stream.reader, version3);
|
|
114192
114192
|
try {
|
|
@@ -133157,8 +133157,8 @@ var nativeNetworkLogsTool = {
|
|
|
133157
133157
|
},
|
|
133158
133158
|
capability: { apple: { simulator: true }, appleRemote: { simulator: true } },
|
|
133159
133159
|
description: `Retrieve network requests captured at the native NSURLProtocol level.
|
|
133160
|
-
Unlike the JS-level network inspector (view-network-logs), this captures ALL network traffic from the app including native modules, Swift/Objective-C networking, and background transfers that
|
|
133161
|
-
Use when you need to inspect native-level HTTP traffic that is invisible to JS
|
|
133160
|
+
Unlike the JS-level network inspector (view-network-logs), this captures ALL network traffic from the app including native modules, Swift/Objective-C networking, and background transfers that never pass through JS XMLHttpRequest or fetch.
|
|
133161
|
+
Use when you need to inspect native-level HTTP traffic that is invisible to the JS-level capture.
|
|
133162
133162
|
Returns { status, count, events } where each event contains URL, method, status code, headers, and timing.
|
|
133163
133163
|
If status is restart_required: follow the message (usually restart-app), then retry. If status is service_stale: the app is already injected, so restarting it cannot help. Restart the tool-server (\`argent server stop && argent server start --detach\`) and retry. If the same status comes back after that restart, stop restarting: follow the message, which names the terminal fallback. If status is connect_pending: the app is injected and still connecting. Do not restart it, wait a few seconds and retry. If status is init_failed: the simulator's native-devtools environment could not be initialised. Follow the message (re-boot the simulator) rather than retrying this tool.
|
|
133164
133164
|
A not-running app comes back as one of those statuses rather than a failure. An app that is not connected does too, except on a device whose devtools agent argent attached to rather than armed itself: there it fails with \`NATIVE_DEVTOOLS_NOT_CONNECTED\`, since no restart of ours can complete a handshake we do not own. Failures are separate: an Apple system app is rejected outright (terminal: never retry it), while a missing host dependency or a udid that is not an Apple device is not.`,
|
|
@@ -134237,116 +134237,1038 @@ init_src2();
|
|
|
134237
134237
|
|
|
134238
134238
|
// ../tool-server/src/utils/debugger/scripts/network-interceptor.ts
|
|
134239
134239
|
var NETWORK_INTERCEPTOR_SCRIPT = `(function() {
|
|
134240
|
-
|
|
134241
|
-
|
|
134240
|
+
var g = globalThis;
|
|
134241
|
+
if (g.__argent_network_v2) return JSON.stringify({ installed: false, reason: 'already installed' });
|
|
134242
|
+
g.__argent_network_v2 = true;
|
|
134243
|
+
// The previous fetch-only script, injected by an older tool-server before an upgrade, stays
|
|
134244
|
+
// installed until the app reloads. Its fetch wrapper keeps the buffers below in a closure, so
|
|
134245
|
+
// empty them and stop them from growing: it then still reads each response, but keeps nothing.
|
|
134246
|
+
if (g.__argent_network_installed && Array.isArray(g.__argent_network_log)) {
|
|
134247
|
+
var oldLog = g.__argent_network_log;
|
|
134248
|
+
oldLog.length = 0;
|
|
134249
|
+
oldLog.push = function() { return 0; };
|
|
134250
|
+
var oldById = g.__argent_network_by_id;
|
|
134251
|
+
if (oldById && typeof oldById === 'object') {
|
|
134252
|
+
for (var oldKey in oldById) delete oldById[oldKey];
|
|
134253
|
+
Object.freeze(oldById);
|
|
134254
|
+
}
|
|
134255
|
+
}
|
|
134256
|
+
// The previous fetch-only script checks this flag; setting it stops an older tool-server
|
|
134257
|
+
// from replacing the buffer below with its own.
|
|
134258
|
+
g.__argent_network_installed = true;
|
|
134259
|
+
|
|
134260
|
+
var MAX_ENTRIES = 2000;
|
|
134261
|
+
var BODY_CAP = 1048576;
|
|
134262
|
+
var BUFFER_CAP = 52428800;
|
|
134263
|
+
var hasOwn = Object.prototype.hasOwnProperty;
|
|
134242
134264
|
|
|
134243
134265
|
var log = [];
|
|
134244
|
-
globalThis.__argent_network_log = log;
|
|
134245
134266
|
var byId = {};
|
|
134246
|
-
|
|
134247
|
-
|
|
134248
|
-
var
|
|
134249
|
-
|
|
134250
|
-
|
|
134267
|
+
g.__argent_network_log = log;
|
|
134268
|
+
g.__argent_network_by_id = byId;
|
|
134269
|
+
var nextId = 1;
|
|
134270
|
+
var bufferedChars = 0;
|
|
134271
|
+
// Above zero while an XHR is sent: the request React Native's network module gets then is the
|
|
134272
|
+
// XHR's, which has its record.
|
|
134273
|
+
var xhrSending = 0;
|
|
134251
134274
|
|
|
134252
|
-
function
|
|
134253
|
-
|
|
134254
|
-
|
|
134255
|
-
|
|
134256
|
-
|
|
134257
|
-
|
|
134275
|
+
function bodyChars(entry) {
|
|
134276
|
+
return (entry.responseBody ? entry.responseBody.length : 0) +
|
|
134277
|
+
(entry.request.postData ? entry.request.postData.length : 0) +
|
|
134278
|
+
(entry.bodyBytes ? entry.bodyBytes.byteLength : 0) + (entry.postBytes ? entry.postBytes.byteLength : 0) +
|
|
134279
|
+
(entry.bodyBase64 ? entry.bodyBase64.length : 0);
|
|
134280
|
+
}
|
|
134281
|
+
|
|
134282
|
+
function evict() {
|
|
134283
|
+
while (log.length > MAX_ENTRIES || (bufferedChars > BUFFER_CAP && log.length > 0)) {
|
|
134284
|
+
var removed = log.shift();
|
|
134285
|
+
bufferedChars -= bodyChars(removed);
|
|
134286
|
+
delete byId[removed.requestId];
|
|
134287
|
+
}
|
|
134288
|
+
}
|
|
134289
|
+
|
|
134290
|
+
// Runs on the app's JS thread for every text response. The loop costs about 200 ms per MiB in
|
|
134291
|
+
// Hermes with a debugger attached, so the native checks go first.
|
|
134292
|
+
function utf8Length(text) {
|
|
134293
|
+
try {
|
|
134294
|
+
if (/^[\\x00-\\x7f]*$/.test(text)) return text.length;
|
|
134295
|
+
if (typeof TextEncoder === 'function') return new TextEncoder().encode(text).length;
|
|
134296
|
+
} catch (e) {}
|
|
134297
|
+
var n = 0;
|
|
134298
|
+
for (var i = 0; i < text.length; i++) {
|
|
134299
|
+
var c = text.charCodeAt(i);
|
|
134300
|
+
if (c < 0x80) n += 1;
|
|
134301
|
+
else if (c < 0x800) n += 2;
|
|
134302
|
+
else if (c >= 0xd800 && c <= 0xdbff && i + 1 < text.length &&
|
|
134303
|
+
text.charCodeAt(i + 1) >= 0xdc00 && text.charCodeAt(i + 1) <= 0xdfff) { n += 4; i++; }
|
|
134304
|
+
else n += 3;
|
|
134305
|
+
}
|
|
134306
|
+
return n;
|
|
134307
|
+
}
|
|
134308
|
+
|
|
134309
|
+
// The UTF-8 size of a text body the interceptor keeps whole. A longer body is cut, and its size
|
|
134310
|
+
// is recorded in characters (bodyLength) instead: counting its bytes would take the app's JS
|
|
134311
|
+
// thread a time that grows with the body.
|
|
134312
|
+
function textSize(text) {
|
|
134313
|
+
return text.length > BODY_CAP ? undefined : utf8Length(text);
|
|
134314
|
+
}
|
|
134315
|
+
|
|
134316
|
+
// A body of one of these types is not text: its size is recorded and it is not read as text.
|
|
134317
|
+
var BINARY_TYPES = /^(image\\/(?!svg)|audio\\/|video\\/|font\\/|application\\/(octet-stream|pdf|zip|gzip|wasm|x-protobuf)$)/;
|
|
134318
|
+
function isBinaryType(mime) {
|
|
134319
|
+
return typeof mime === 'string' && BINARY_TYPES.test(mime.toLowerCase());
|
|
134320
|
+
}
|
|
134321
|
+
|
|
134322
|
+
function mimeTypeOf(headers) {
|
|
134323
|
+
return hasOwn.call(headers, 'content-type') ? String(headers['content-type']).split(';')[0].trim() : '';
|
|
134324
|
+
}
|
|
134325
|
+
|
|
134326
|
+
// The headers of every HeadersInit form: [name, value] pairs (an array or another iterable), a
|
|
134327
|
+
// Headers or Map, or a plain object. A name given twice keeps both values.
|
|
134328
|
+
function headersObject(source) {
|
|
134329
|
+
var out = {};
|
|
134330
|
+
if (!source || typeof source !== 'object') return out;
|
|
134331
|
+
var add = function(name, value) {
|
|
134332
|
+
name = String(name);
|
|
134333
|
+
out[name] = hasOwn.call(out, name) ? out[name] + ', ' + String(value) : String(value);
|
|
134258
134334
|
};
|
|
134335
|
+
try {
|
|
134336
|
+
// A Headers and a Map iterate as pairs too.
|
|
134337
|
+
if (Array.isArray(source) || (typeof Symbol === 'function' && typeof source[Symbol.iterator] === 'function')) {
|
|
134338
|
+
var pairs = Array.from(source);
|
|
134339
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
134340
|
+
if (pairs[i] && typeof pairs[i] === 'object' && pairs[i].length === 2) add(pairs[i][0], pairs[i][1]);
|
|
134341
|
+
}
|
|
134342
|
+
} else if (typeof source.forEach === 'function') {
|
|
134343
|
+
source.forEach(function(value, name) { add(name, value); });
|
|
134344
|
+
} else {
|
|
134345
|
+
for (var k in source) if (hasOwn.call(source, k)) add(k, source[k]);
|
|
134346
|
+
}
|
|
134347
|
+
} catch (e) {}
|
|
134348
|
+
return out;
|
|
134349
|
+
}
|
|
134350
|
+
|
|
134351
|
+
function parseHeaders(raw) {
|
|
134352
|
+
var out = {};
|
|
134353
|
+
if (typeof raw !== 'string') return out;
|
|
134354
|
+
var lines = raw.split(/\\r?\\n/);
|
|
134355
|
+
for (var i = 0; i < lines.length; i++) {
|
|
134356
|
+
var at = lines[i].indexOf(':');
|
|
134357
|
+
if (at <= 0) continue;
|
|
134358
|
+
var name = lines[i].slice(0, at).trim().toLowerCase();
|
|
134359
|
+
var value = lines[i].slice(at + 1).trim();
|
|
134360
|
+
out[name] = hasOwn.call(out, name) ? out[name] + ', ' + value : value;
|
|
134361
|
+
}
|
|
134362
|
+
return out;
|
|
134363
|
+
}
|
|
134364
|
+
|
|
134365
|
+
function describeFormData(form) {
|
|
134366
|
+
var parts = [];
|
|
134367
|
+
function file(value) {
|
|
134368
|
+
return '<file' + (value && value.name ? ' ' + value.name : '') + (value && value.type ? ' ' + value.type : '') + '>';
|
|
134369
|
+
}
|
|
134370
|
+
// React Native's FormData lists its parts through getParts().
|
|
134371
|
+
var list = form.getParts();
|
|
134372
|
+
for (var i = 0; i < list.length; i++) {
|
|
134373
|
+
parts.push(list[i].fieldName + '=' + (typeof list[i].string === 'string' ? list[i].string : file(list[i])));
|
|
134374
|
+
}
|
|
134375
|
+
return '[FormData] ' + parts.join('; ');
|
|
134376
|
+
}
|
|
134377
|
+
|
|
134378
|
+
// The text of UTF-8 bytes, cut at BODY_CAP on a character boundary; undefined when they are not
|
|
134379
|
+
// UTF-8 or no decoder exists (React Native has none; Expo installs one).
|
|
134380
|
+
function utf8Text(bytes) {
|
|
134381
|
+
if (typeof TextDecoder !== 'function') return undefined;
|
|
134382
|
+
var end = Math.min(bytes.byteLength, BODY_CAP);
|
|
134383
|
+
if (end < bytes.byteLength) while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--;
|
|
134384
|
+
try { return new TextDecoder('utf-8', { fatal: true }).decode(bytes.subarray(0, end)); } catch (e) { return undefined; }
|
|
134385
|
+
}
|
|
134386
|
+
|
|
134387
|
+
// Bodies of bytes are kept as bytes, 3 past the cap to let utf8Text step back to a character
|
|
134388
|
+
// boundary, and decoded only when the agent reads the record: Expo SDK 54 and 55 install a
|
|
134389
|
+
// TextDecoder written in JS, which would stop the app's JS thread for each request.
|
|
134390
|
+
// A copy even of a subclass whose slice() returns a view (the Buffer of the buffer package).
|
|
134391
|
+
function keepBytes(bytes) {
|
|
134392
|
+
return Uint8Array.prototype.slice.call(bytes, 0, BODY_CAP + 3);
|
|
134393
|
+
}
|
|
134394
|
+
|
|
134395
|
+
// The bytes of base64 text, which a response of React Native's network module can come as.
|
|
134396
|
+
var base64Values = null;
|
|
134397
|
+
function base64Bytes(text) {
|
|
134398
|
+
if (!base64Values) {
|
|
134399
|
+
base64Values = {};
|
|
134400
|
+
var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
134401
|
+
for (var d = 0; d < 64; d++) base64Values[digits.charAt(d)] = d;
|
|
134402
|
+
}
|
|
134403
|
+
var out = new Uint8Array(Math.floor(text.length * 3 / 4));
|
|
134404
|
+
var n = 0, bits = 0, acc = 0;
|
|
134405
|
+
for (var i = 0; i < text.length; i++) {
|
|
134406
|
+
var v = base64Values[text.charAt(i)];
|
|
134407
|
+
if (v === undefined) continue;
|
|
134408
|
+
acc = (acc << 6) | v;
|
|
134409
|
+
bits += 6;
|
|
134410
|
+
if (bits >= 8) {
|
|
134411
|
+
bits -= 8;
|
|
134412
|
+
out[n++] = (acc >> bits) & 0xff;
|
|
134413
|
+
acc &= (1 << bits) - 1;
|
|
134414
|
+
}
|
|
134415
|
+
}
|
|
134416
|
+
return out.subarray(0, n);
|
|
134417
|
+
}
|
|
134418
|
+
|
|
134419
|
+
function decodeBodies(entry) {
|
|
134420
|
+
if (!entry.postBytes && !entry.bodyBytes && entry.bodyBase64 === undefined) return;
|
|
134421
|
+
var before = bodyChars(entry);
|
|
134422
|
+
if (entry.bodyBase64 !== undefined) {
|
|
134423
|
+
entry.bodyBytes = base64Bytes(entry.bodyBase64);
|
|
134424
|
+
delete entry.bodyBase64;
|
|
134425
|
+
}
|
|
134426
|
+
if (entry.postBytes) {
|
|
134427
|
+
// Expo hands its native request the body as bytes: they show as text when they are UTF-8.
|
|
134428
|
+
var post = utf8Text(entry.postBytes);
|
|
134429
|
+
entry.request.postData = post === undefined ? '[binary ' + entry.postSize + ' bytes]' : post;
|
|
134430
|
+
if (post !== undefined && entry.postSize > BODY_CAP) entry.request.postDataTruncated = true;
|
|
134431
|
+
delete entry.postBytes;
|
|
134432
|
+
delete entry.postSize;
|
|
134433
|
+
}
|
|
134434
|
+
if (entry.bodyBytes) {
|
|
134435
|
+
var body = utf8Text(entry.bodyBytes);
|
|
134436
|
+
if (body !== undefined) {
|
|
134437
|
+
entry.responseBody = body;
|
|
134438
|
+
if (entry.encodedDataLength > BODY_CAP) entry.bodyTruncated = true;
|
|
134439
|
+
} else if (typeof TextDecoder === 'function') {
|
|
134440
|
+
// Bytes that are not UTF-8.
|
|
134441
|
+
entry.bodyOmitted = true;
|
|
134442
|
+
}
|
|
134443
|
+
delete entry.bodyBytes;
|
|
134444
|
+
}
|
|
134445
|
+
bufferedChars += bodyChars(entry) - before;
|
|
134446
|
+
evict();
|
|
134447
|
+
}
|
|
134448
|
+
g.__argent_network_decode = decodeBodies;
|
|
134449
|
+
|
|
134450
|
+
// What a request body shows, and whether that is only its start.
|
|
134451
|
+
function describeBody(body) {
|
|
134452
|
+
var text;
|
|
134453
|
+
try {
|
|
134454
|
+
if (body == null) return undefined;
|
|
134455
|
+
if (typeof body === 'string') text = body;
|
|
134456
|
+
else if (typeof FormData === 'function' && body instanceof FormData) text = describeFormData(body);
|
|
134457
|
+
else if (typeof Blob === 'function' && body instanceof Blob) text = '[Blob ' + body.size + ' bytes]';
|
|
134458
|
+
else if (typeof Uint8Array === 'function' && body instanceof Uint8Array) return { bytes: keepBytes(body), size: body.byteLength };
|
|
134459
|
+
else if (typeof ArrayBuffer === 'function' && (body instanceof ArrayBuffer || ArrayBuffer.isView(body))) {
|
|
134460
|
+
text = '[binary ' + body.byteLength + ' bytes]';
|
|
134461
|
+
}
|
|
134462
|
+
} catch (e) {}
|
|
134463
|
+
if (text === undefined) return undefined;
|
|
134464
|
+
return text.length > BODY_CAP ? { text: text.slice(0, BODY_CAP), cut: true } : { text: text, cut: false };
|
|
134465
|
+
}
|
|
134466
|
+
|
|
134467
|
+
function createRecord(resourceType, method, url, headers, body, startedAt) {
|
|
134468
|
+
var id = nextId++;
|
|
134469
|
+
var entry = {
|
|
134470
|
+
id: id,
|
|
134471
|
+
requestId: 'rn-net-' + id,
|
|
134472
|
+
state: 'pending',
|
|
134473
|
+
resourceType: resourceType,
|
|
134474
|
+
request: { url: url, method: method, headers: headers },
|
|
134475
|
+
timestamp: startedAt / 1000
|
|
134476
|
+
};
|
|
134477
|
+
var postData = describeBody(body);
|
|
134478
|
+
if (postData && postData.bytes) {
|
|
134479
|
+
entry.postBytes = postData.bytes;
|
|
134480
|
+
entry.postSize = postData.size;
|
|
134481
|
+
} else if (postData) {
|
|
134482
|
+
entry.request.postData = postData.text;
|
|
134483
|
+
if (postData.cut) entry.request.postDataTruncated = true;
|
|
134484
|
+
}
|
|
134259
134485
|
log.push(entry);
|
|
134260
|
-
byId[
|
|
134261
|
-
|
|
134262
|
-
|
|
134263
|
-
|
|
134264
|
-
|
|
134265
|
-
|
|
134266
|
-
|
|
134267
|
-
|
|
134268
|
-
|
|
134269
|
-
|
|
134270
|
-
|
|
134271
|
-
|
|
134272
|
-
|
|
134273
|
-
|
|
134274
|
-
|
|
134275
|
-
|
|
134276
|
-
|
|
134277
|
-
|
|
134278
|
-
|
|
134279
|
-
|
|
134280
|
-
|
|
134281
|
-
|
|
134282
|
-
|
|
134283
|
-
|
|
134284
|
-
|
|
134285
|
-
|
|
134286
|
-
|
|
134287
|
-
|
|
134288
|
-
|
|
134289
|
-
|
|
134290
|
-
|
|
134291
|
-
|
|
134292
|
-
|
|
134293
|
-
|
|
134294
|
-
|
|
134295
|
-
|
|
134296
|
-
|
|
134297
|
-
|
|
134298
|
-
|
|
134299
|
-
|
|
134300
|
-
|
|
134301
|
-
|
|
134302
|
-
|
|
134303
|
-
|
|
134304
|
-
|
|
134305
|
-
|
|
134306
|
-
|
|
134307
|
-
|
|
134308
|
-
|
|
134486
|
+
byId[entry.requestId] = entry;
|
|
134487
|
+
bufferedChars += bodyChars(entry);
|
|
134488
|
+
evict();
|
|
134489
|
+
return { entry: entry, startedAt: startedAt };
|
|
134490
|
+
}
|
|
134491
|
+
|
|
134492
|
+
function setResponse(rec, url, status, statusText, headers) {
|
|
134493
|
+
var e = rec.entry;
|
|
134494
|
+
e.response = { url: url || e.request.url, status: status, statusText: statusText || '', headers: headers, mimeType: mimeTypeOf(headers) };
|
|
134495
|
+
}
|
|
134496
|
+
|
|
134497
|
+
// A response header, by its name in lower case.
|
|
134498
|
+
function headerValue(response, wanted) {
|
|
134499
|
+
var headers = response && response.headers;
|
|
134500
|
+
if (!headers) return undefined;
|
|
134501
|
+
for (var name in headers) {
|
|
134502
|
+
if (hasOwn.call(headers, name) && name.toLowerCase() === wanted) return String(headers[name]);
|
|
134503
|
+
}
|
|
134504
|
+
return undefined;
|
|
134505
|
+
}
|
|
134506
|
+
|
|
134507
|
+
// The Content-Length of a response, when it gives the size of the body the app gets. With a
|
|
134508
|
+
// Content-Encoding (gzip, br) it is the compressed size, which iOS keeps on the decoded response.
|
|
134509
|
+
function contentLength(response) {
|
|
134510
|
+
var encoding = headerValue(response, 'content-encoding');
|
|
134511
|
+
if (encoding !== undefined && encoding.trim().toLowerCase() !== 'identity' && encoding.trim() !== '') return undefined;
|
|
134512
|
+
var value = headerValue(response, 'content-length');
|
|
134513
|
+
var n = Number(value);
|
|
134514
|
+
return value !== undefined && value !== '' && n >= 0 && Math.floor(n) === n ? n : undefined;
|
|
134515
|
+
}
|
|
134516
|
+
|
|
134517
|
+
function isBinaryResponse(rec) {
|
|
134518
|
+
return !!rec.entry.response && isBinaryType(rec.entry.response.mimeType);
|
|
134519
|
+
}
|
|
134520
|
+
|
|
134521
|
+
// The size of a text body, unless the body is binary: its text is then not stored (see storeBody).
|
|
134522
|
+
function textSizeOf(rec, text) {
|
|
134523
|
+
return isBinaryResponse(rec) ? undefined : textSize(text);
|
|
134524
|
+
}
|
|
134525
|
+
|
|
134526
|
+
// Records a body that is not kept, once: the details show its size instead.
|
|
134527
|
+
function omitBody(rec, byteLength) {
|
|
134528
|
+
var e = rec.entry;
|
|
134529
|
+
if (e.responseBody !== undefined || e.encodedDataLength !== undefined || e.bodyOmitted) return;
|
|
134530
|
+
e.bodyOmitted = true;
|
|
134531
|
+
if (typeof byteLength === 'number') e.encodedDataLength = byteLength;
|
|
134532
|
+
}
|
|
134533
|
+
|
|
134534
|
+
// Stores the body of a response, once: text, or bytes that keepBytes cut.
|
|
134535
|
+
function storeBody(rec, body, byteLength, truncated) {
|
|
134536
|
+
var e = rec.entry;
|
|
134537
|
+
if (e.responseBody !== undefined || e.encodedDataLength !== undefined || e.bodyOmitted) return;
|
|
134538
|
+
if (typeof body === 'string' && isBinaryResponse(rec)) {
|
|
134539
|
+
// A binary body read as text (an XHR's default responseType, Expo's text()) is not that text.
|
|
134540
|
+
// Its size is the one the server gave, else the length of the text, close to it: a byte that is
|
|
134541
|
+
// not UTF-8 reads as one character.
|
|
134542
|
+
var declared = e.state === 'failed' ? undefined : contentLength(e.response);
|
|
134543
|
+
omitBody(rec, declared);
|
|
134544
|
+
if (declared === undefined) e.bodyLength = body.length;
|
|
134545
|
+
return;
|
|
134546
|
+
}
|
|
134547
|
+
if (body instanceof Uint8Array && isBinaryResponse(rec)) return omitBody(rec, byteLength);
|
|
134548
|
+
if (typeof byteLength === 'number') e.encodedDataLength = byteLength;
|
|
134549
|
+
if (body instanceof Uint8Array) {
|
|
134550
|
+
e.bodyBytes = body;
|
|
134551
|
+
if (byId[e.requestId] === e) { bufferedChars += body.byteLength; evict(); }
|
|
134552
|
+
} else if (typeof body === 'string') {
|
|
134553
|
+
if (body.length > BODY_CAP) {
|
|
134554
|
+
// A cut body is not counted (see textSize): its size is the one the server gave, if any.
|
|
134555
|
+
e.bodyLength = body.length;
|
|
134556
|
+
if (e.encodedDataLength === undefined) e.encodedDataLength = contentLength(e.response);
|
|
134557
|
+
body = body.slice(0, BODY_CAP);
|
|
134558
|
+
truncated = true;
|
|
134559
|
+
}
|
|
134560
|
+
e.responseBody = body;
|
|
134561
|
+
if (truncated) e.bodyTruncated = true;
|
|
134562
|
+
if (byId[e.requestId] === e) { bufferedChars += body.length; evict(); }
|
|
134563
|
+
}
|
|
134564
|
+
}
|
|
134565
|
+
|
|
134566
|
+
// Ends a request that got a response and stores its body. A request that failed meanwhile (the
|
|
134567
|
+
// app aborted it while its body was read) stays failed.
|
|
134568
|
+
function complete(rec, body, byteLength, truncated) {
|
|
134569
|
+
if (rec.entry.state === 'failed') return;
|
|
134570
|
+
storeBody(rec, body, byteLength, truncated);
|
|
134571
|
+
rec.entry.state = 'finished';
|
|
134572
|
+
}
|
|
134573
|
+
|
|
134574
|
+
function fail(rec, errorText) {
|
|
134575
|
+
var e = rec.entry;
|
|
134576
|
+
e.state = 'failed';
|
|
134577
|
+
e.errorText = errorText;
|
|
134578
|
+
e.durationMs = Date.now() - rec.startedAt;
|
|
134579
|
+
}
|
|
134580
|
+
|
|
134581
|
+
// \u2500\u2500 XMLHttpRequest \u2500\u2500
|
|
134582
|
+
var XHR = g.XMLHttpRequest;
|
|
134583
|
+
if (typeof XHR === 'function') {
|
|
134584
|
+
var slots = new WeakMap();
|
|
134585
|
+
// The record and the end handler of each XHR's live request.
|
|
134586
|
+
var live = new WeakMap();
|
|
134587
|
+
var proto = XHR.prototype;
|
|
134588
|
+
var origOpen = proto.open;
|
|
134589
|
+
var origSetRequestHeader = proto.setRequestHeader;
|
|
134590
|
+
var origSend = proto.send;
|
|
134591
|
+
var origAbort = proto.abort;
|
|
134592
|
+
|
|
134593
|
+
var onHeaders = function(xhr, rec) {
|
|
134594
|
+
var raw;
|
|
134595
|
+
try { raw = xhr.getAllResponseHeaders(); } catch (e) {}
|
|
134596
|
+
setResponse(rec, xhr.responseURL, xhr.status, xhr.statusText, parseHeaders(raw));
|
|
134597
|
+
};
|
|
134598
|
+
|
|
134599
|
+
// React Native's fetch is whatwg-fetch over XHR. Each call builds a Request of whatwg-fetch, whose
|
|
134600
|
+
// constructor ends with _initBody, and then, in the same synchronous run, opens its XHR with that
|
|
134601
|
+
// Request's method and URL. Its Request class is the global one, so wrapping _initBody marks the
|
|
134602
|
+
// Request of each call, also of a fetch the app captured before the install, and the next open
|
|
134603
|
+
// that matches it, before the run ends, is the call's XHR. Any other XHR is the app's own, even
|
|
134604
|
+
// one that reads its response as a blob.
|
|
134605
|
+
var fetchRequest = null;
|
|
134606
|
+
var knowsFetch = false;
|
|
134607
|
+
try {
|
|
134608
|
+
var requestProto = typeof g.Request === 'function' && g.Request.prototype;
|
|
134609
|
+
if (requestProto && hasOwn.call(requestProto, '_initBody') && typeof requestProto._initBody === 'function' &&
|
|
134610
|
+
typeof Promise === 'function') {
|
|
134611
|
+
var origInitBody = requestProto._initBody;
|
|
134612
|
+
var markFetch = function() {
|
|
134613
|
+
fetchRequest = this;
|
|
134614
|
+
var marked = this;
|
|
134615
|
+
Promise.resolve().then(function() { if (fetchRequest === marked) fetchRequest = null; });
|
|
134616
|
+
return origInitBody.apply(this, arguments);
|
|
134309
134617
|
};
|
|
134618
|
+
requestProto._initBody = markFetch;
|
|
134619
|
+
knowsFetch = requestProto._initBody === markFetch;
|
|
134620
|
+
}
|
|
134621
|
+
} catch (e) {}
|
|
134622
|
+
// Without that Request class, an XHR shaped as React Native's fetch sends it: it reads every
|
|
134623
|
+
// response as a blob (as an arraybuffer where Blob is unavailable) and sets these handlers.
|
|
134624
|
+
// axios sets onloadend, not onload.
|
|
134625
|
+
var isFetchXhr = function(xhr, slot) {
|
|
134626
|
+
if (knowsFetch) return slot.fetch;
|
|
134627
|
+
return (xhr.responseType === 'blob' || xhr.responseType === 'arraybuffer') && typeof xhr.onload === 'function' &&
|
|
134628
|
+
typeof xhr.onabort === 'function' && typeof xhr.ontimeout === 'function';
|
|
134629
|
+
};
|
|
134630
|
+
|
|
134631
|
+
var isBinaryBlob = function(rec, blob) {
|
|
134632
|
+
var mime = rec.entry.response && rec.entry.response.mimeType;
|
|
134633
|
+
try { if (!mime) mime = blob.type; } catch (e) {}
|
|
134634
|
+
return isBinaryType(mime);
|
|
134635
|
+
};
|
|
134636
|
+
|
|
134637
|
+
// iOS resolves readAsText with null when the cut splits a UTF-8 character, so step back a
|
|
134638
|
+
// byte at a time, at most 3 times.
|
|
134639
|
+
// The size is read up front: RN's Blob throws on every access once the app closes it.
|
|
134640
|
+
// cutOf(end), when given, makes the start of the blob that is read instead of a slice.
|
|
134641
|
+
var readBlob = function(rec, blob, size, end, cutOf) {
|
|
134642
|
+
if (end === Math.min(size, BODY_CAP) && isBinaryBlob(rec, blob)) {
|
|
134643
|
+
omitBody(rec, size);
|
|
134644
|
+
return complete(rec, undefined, size, false);
|
|
134645
|
+
}
|
|
134646
|
+
var part = end < size ? (cutOf ? cutOf(end) : blob.slice(0, end)) : blob;
|
|
134647
|
+
// A slice holds its own reference to the native blob: release it, so the app's close() frees it.
|
|
134648
|
+
var release = function() { if (part !== blob && !cutOf) part.close(); };
|
|
134649
|
+
var reader = new FileReader();
|
|
134650
|
+
reader.onload = function() {
|
|
134651
|
+
release();
|
|
134652
|
+
if (typeof reader.result === 'string' || part === blob || end <= BODY_CAP - 3) {
|
|
134653
|
+
return complete(rec, reader.result, size, part !== blob);
|
|
134654
|
+
}
|
|
134655
|
+
// The app may have closed the blob since the first read.
|
|
134656
|
+
try { readBlob(rec, blob, size, end - 1, cutOf); } catch (e) { complete(rec, undefined, size, false); }
|
|
134657
|
+
};
|
|
134658
|
+
reader.onerror = function() {
|
|
134659
|
+
release();
|
|
134660
|
+
complete(rec, undefined, size, false);
|
|
134661
|
+
};
|
|
134662
|
+
reader.readAsText(part);
|
|
134663
|
+
};
|
|
134664
|
+
|
|
134665
|
+
var readBody = function(rec, xhr) {
|
|
134666
|
+
var type = xhr.responseType || '';
|
|
134667
|
+
if (type === '' || type === 'text') {
|
|
134668
|
+
var text = xhr.responseText;
|
|
134669
|
+
if (typeof text === 'string') return complete(rec, text, textSizeOf(rec, text), false);
|
|
134670
|
+
} else if (type === 'json') {
|
|
134671
|
+
// React Native keeps the text it received, which spares a serialization of the whole object.
|
|
134672
|
+
var raw = xhr._response;
|
|
134673
|
+
if (typeof raw === 'string') return complete(rec, raw, textSize(raw), false);
|
|
134674
|
+
var json = xhr.response == null ? undefined : JSON.stringify(xhr.response);
|
|
134675
|
+
// Re-serialized, so its length is not the size received: record no size.
|
|
134676
|
+
if (typeof json === 'string') return complete(rec, json, undefined, false);
|
|
134677
|
+
} else if (type === 'arraybuffer') {
|
|
134678
|
+
// Kept as bytes like Expo's bodies, and decoded when the agent reads the record.
|
|
134679
|
+
var buffer = xhr.response;
|
|
134680
|
+
if (buffer && typeof buffer.byteLength === 'number') return complete(rec, keepBytes(new Uint8Array(buffer)), buffer.byteLength);
|
|
134681
|
+
} else if (type === 'blob') {
|
|
134682
|
+
var blob = xhr.response;
|
|
134683
|
+
if (blob) return readBlob(rec, blob, blob.size, Math.min(blob.size, BODY_CAP));
|
|
134684
|
+
}
|
|
134685
|
+
complete(rec, undefined, undefined, false);
|
|
134686
|
+
};
|
|
134687
|
+
|
|
134688
|
+
// Listeners belong to one request and are removed when it ends.
|
|
134689
|
+
var listen = function(xhr, rec) {
|
|
134690
|
+
var handlers = {
|
|
134691
|
+
readystatechange: function() { if (xhr.readyState === 2) onHeaders(xhr, rec); },
|
|
134692
|
+
load: function() { end('load'); },
|
|
134693
|
+
error: function() { end('error'); },
|
|
134694
|
+
timeout: function() { end('timeout'); },
|
|
134695
|
+
abort: function() { end('abort'); }
|
|
134696
|
+
};
|
|
134697
|
+
var end = function(outcome, force) {
|
|
134698
|
+
// An end event that finds the XHR no longer DONE belongs to a request the app has already
|
|
134699
|
+
// replaced, and the abort patch below recorded how that request ended.
|
|
134700
|
+
if (!force && xhr.readyState !== 4) return;
|
|
134701
|
+
for (var type in handlers) xhr.removeEventListener(type, handlers[type]);
|
|
134702
|
+
if (live.has(xhr) && live.get(xhr).end === end) live.delete(xhr);
|
|
134703
|
+
if (outcome === 'abort') return fail(rec, 'aborted');
|
|
134704
|
+
if (outcome === 'timeout') return fail(rec, 'timeout');
|
|
134705
|
+
if (outcome === 'error') {
|
|
134706
|
+
var text = '';
|
|
134707
|
+
try { if (xhr.responseType === '' || xhr.responseType === 'text') text = xhr.responseText; } catch (e) {}
|
|
134708
|
+
return fail(rec, text ? String(text) : 'Network error');
|
|
134709
|
+
}
|
|
134710
|
+
// RN sets responseURL only after it announces HEADERS_RECEIVED: this is the final URL.
|
|
134711
|
+
if (rec.entry.response && xhr.responseURL) rec.entry.response.url = xhr.responseURL;
|
|
134712
|
+
rec.entry.durationMs = Date.now() - rec.startedAt;
|
|
134713
|
+
try { readBody(rec, xhr); } catch (e) { complete(rec, undefined, undefined, false); }
|
|
134714
|
+
};
|
|
134715
|
+
live.set(xhr, { rec: rec, end: end });
|
|
134716
|
+
// RN switches an XHR to incremental response updates once it has a readystatechange
|
|
134717
|
+
// listener. Restore the flag so these listeners do not change how the app gets data.
|
|
134718
|
+
var incremental = xhr._incrementalEvents;
|
|
134719
|
+
for (var type in handlers) xhr.addEventListener(type, handlers[type]);
|
|
134720
|
+
if (typeof incremental === 'boolean') xhr._incrementalEvents = incremental;
|
|
134721
|
+
};
|
|
134722
|
+
|
|
134723
|
+
proto.open = function(method, url) {
|
|
134724
|
+
var orphan = live.get(this);
|
|
134725
|
+
var previous = slots.get(this);
|
|
134726
|
+
var request = fetchRequest;
|
|
134727
|
+
fetchRequest = null;
|
|
134728
|
+
var slot = { method: String(method || 'GET').toUpperCase(), url: String(url), headers: {}, fetch: false };
|
|
134729
|
+
// whatwg-fetch opens '' as the page's URL, which React Native does not have.
|
|
134730
|
+
slot.fetch = !!request && String(request.method).toUpperCase() === slot.method && (String(request.url) === slot.url || request.url === '');
|
|
134731
|
+
// Set before the original open, which dispatches readystatechange: a handler may send from there.
|
|
134732
|
+
slots.set(this, slot);
|
|
134733
|
+
var result;
|
|
134734
|
+
try {
|
|
134735
|
+
result = origOpen.apply(this, arguments);
|
|
134736
|
+
} catch (e) {
|
|
134737
|
+
if (previous) slots.set(this, previous);
|
|
134738
|
+
else slots.delete(this);
|
|
134739
|
+
throw e;
|
|
134740
|
+
}
|
|
134741
|
+
// Open succeeds only on a reset XHR, so a request still listening here can never end: RN reset
|
|
134742
|
+
// it without an end event reaching these listeners (a re-send from inside an abort's own
|
|
134743
|
+
// handlers, or an app listener that stopped the event). Drop it before it takes a response.
|
|
134744
|
+
if (orphan) orphan.end('abort', true);
|
|
134745
|
+
return result;
|
|
134746
|
+
};
|
|
134310
134747
|
|
|
134311
|
-
|
|
134312
|
-
|
|
134313
|
-
|
|
134314
|
-
|
|
134315
|
-
|
|
134316
|
-
|
|
134317
|
-
|
|
134318
|
-
|
|
134319
|
-
|
|
134320
|
-
|
|
134321
|
-
|
|
134322
|
-
var
|
|
134323
|
-
|
|
134324
|
-
|
|
134325
|
-
|
|
134326
|
-
|
|
134327
|
-
|
|
134328
|
-
|
|
134329
|
-
|
|
134330
|
-
|
|
134331
|
-
|
|
134332
|
-
|
|
134333
|
-
|
|
134334
|
-
});
|
|
134748
|
+
proto.setRequestHeader = function(name, value) {
|
|
134749
|
+
var result = origSetRequestHeader.apply(this, arguments);
|
|
134750
|
+
var slot = slots.get(this);
|
|
134751
|
+
// React Native sends one value per lowercased name, the last one set. RN still accepts a
|
|
134752
|
+
// header after send, but it never reaches the wire, so it is not recorded.
|
|
134753
|
+
if (slot && slot.headers) {
|
|
134754
|
+
name = String(name).toLowerCase();
|
|
134755
|
+
value = String(value);
|
|
134756
|
+
// whatwg-fetch sets headers given as [name, value] pairs as if they were an object: each pair
|
|
134757
|
+
// as '0': 'name,value' and so on, then 'length', then each pair again under its own name. The
|
|
134758
|
+
// pair is recorded under its own name, so a redacted header stays redacted.
|
|
134759
|
+
var comma = value.indexOf(',');
|
|
134760
|
+
if (/^\\d+$/.test(name) && comma > 0) {
|
|
134761
|
+
slot.pairs = true;
|
|
134762
|
+
name = value.slice(0, comma).trim().toLowerCase();
|
|
134763
|
+
value = value.slice(comma + 1);
|
|
134764
|
+
} else if (name === 'length' && slot.pairs) {
|
|
134765
|
+
return result;
|
|
134766
|
+
}
|
|
134767
|
+
slot.headers[name] = value;
|
|
134768
|
+
}
|
|
134769
|
+
return result;
|
|
134770
|
+
};
|
|
134335
134771
|
|
|
134336
|
-
|
|
134337
|
-
|
|
134338
|
-
|
|
134339
|
-
|
|
134340
|
-
|
|
134341
|
-
|
|
134772
|
+
proto.send = function(body) {
|
|
134773
|
+
var slot = slots.get(this);
|
|
134774
|
+
var startedAt = Date.now();
|
|
134775
|
+
var result;
|
|
134776
|
+
xhrSending++;
|
|
134777
|
+
try {
|
|
134778
|
+
result = origSend.apply(this, arguments);
|
|
134779
|
+
} finally {
|
|
134780
|
+
xhrSending--;
|
|
134781
|
+
}
|
|
134782
|
+
if (!slot || !slot.headers) return result;
|
|
134783
|
+
var rec = createRecord(isFetchXhr(this, slot) ? 'Fetch' : 'XHR', slot.method, slot.url, slot.headers, body, startedAt);
|
|
134784
|
+
slot.headers = null;
|
|
134785
|
+
listen(this, rec);
|
|
134786
|
+
return result;
|
|
134787
|
+
};
|
|
134788
|
+
|
|
134789
|
+
// To reuse an XHR from its own handler, the app aborts it first, and RN runs that handler
|
|
134790
|
+
// before these listeners. Record a request that has already ended before abort resets the XHR.
|
|
134791
|
+
// RN keeps _aborted set until the next open, so an abort called from a handler of an abort
|
|
134792
|
+
// still counts as one. An app can also abort from its own handler of HEADERS_RECEIVED, which
|
|
134793
|
+
// runs before the listener that records the headers: keep them.
|
|
134794
|
+
// RN clears the response before it announces the abort, so the text an XHR has received while it
|
|
134795
|
+
// streams (react-native-sse aborts to close) is taken first. The request stays failed: the body
|
|
134796
|
+
// is only the part that arrived.
|
|
134797
|
+
proto.abort = function() {
|
|
134798
|
+
var request = live.get(this);
|
|
134799
|
+
if (request && this.readyState >= 2 && !request.rec.entry.response) onHeaders(this, request.rec);
|
|
134800
|
+
if (request && this.readyState === 4) {
|
|
134801
|
+
request.end(this._aborted ? 'abort' : this._hasError ? (this._timedOut ? 'timeout' : 'error') : 'load');
|
|
134802
|
+
}
|
|
134803
|
+
var partial;
|
|
134804
|
+
if (request && this.readyState === 3) {
|
|
134805
|
+
try { if (this.responseType === '' || this.responseType === 'text') partial = this.responseText; } catch (e) {}
|
|
134806
|
+
}
|
|
134807
|
+
var result = origAbort.apply(this, arguments);
|
|
134808
|
+
if (typeof partial === 'string' && partial && request.rec.entry.state === 'failed') {
|
|
134809
|
+
storeBody(request.rec, partial, textSizeOf(request.rec, partial), false);
|
|
134810
|
+
}
|
|
134811
|
+
return result;
|
|
134812
|
+
};
|
|
134813
|
+
}
|
|
134814
|
+
|
|
134815
|
+
// \u2500\u2500 Expo's native fetch \u2500\u2500
|
|
134816
|
+
// expo/fetch builds each request as new NativeRequest(response) and sends it with start(url, init,
|
|
134817
|
+
// body), whether the app calls it as the global fetch or imports it. The app gets that response
|
|
134818
|
+
// once start resolves.
|
|
134819
|
+
var expoFetch;
|
|
134820
|
+
try { expoFetch = g.expo && g.expo.modules && g.expo.modules.ExpoFetchModule; } catch (e) {}
|
|
134821
|
+
// Takes the whole body of a response from native, for the app to get at its first read. It stays
|
|
134822
|
+
// null when Expo's module cannot hand a body back (see below).
|
|
134823
|
+
var takeBody = null;
|
|
134824
|
+
|
|
134825
|
+
// Hands each result of an object's method to tap as well. track(1) runs when a call starts and
|
|
134826
|
+
// track(-1) once its promise settles. The caller gets a promise that settles like the original, so
|
|
134827
|
+
// a rejection it never handles is still reported as unhandled.
|
|
134828
|
+
function tapMethod(target, name, tap, track) {
|
|
134829
|
+
var method = target[name];
|
|
134830
|
+
if (typeof method !== 'function') return;
|
|
134831
|
+
target[name] = function() {
|
|
134832
|
+
var result = method.apply(this, arguments);
|
|
134833
|
+
if (!result || typeof result.then !== 'function') return result;
|
|
134834
|
+
track(1);
|
|
134835
|
+
return result.then(function(value) {
|
|
134836
|
+
try { tap(value); } catch (e) {}
|
|
134837
|
+
track(-1);
|
|
134838
|
+
return value;
|
|
134839
|
+
}, function(error) {
|
|
134840
|
+
track(-1);
|
|
134841
|
+
throw error;
|
|
134342
134842
|
});
|
|
134343
134843
|
};
|
|
134344
134844
|
}
|
|
134345
134845
|
|
|
134346
|
-
//
|
|
134347
|
-
//
|
|
134348
|
-
//
|
|
134349
|
-
//
|
|
134846
|
+
// Native Expo hands a body out once, so reading it here while the request runs, or through a clone,
|
|
134847
|
+
// would take it from the app or make it wait. The body is taken as the app reads it: its text() or
|
|
134848
|
+
// arrayBuffer() (json(), blob() and bytes() go through them), or the chunks of its body stream. A
|
|
134849
|
+
// 2xx body the app never reads is not recorded. The native side announces the end of every request with
|
|
134850
|
+
// readyForJSFinalization, a failed one too, and that event can come before start rejects: the
|
|
134851
|
+
// record ends only once start resolved. The body can be complete before JS gets that event: a
|
|
134852
|
+
// whole body the app reads, or didComplete after the chunks it streams, ends the record too. So an
|
|
134853
|
+
// app that aborts once it has the body (urql, graphql-sse) does not make the request fail. A read
|
|
134854
|
+
// of the app still in flight holds the record: on iOS, when the connection drops in the middle of
|
|
134855
|
+
// the body, the event can say nothing of the drop, and the read then never settles. The record
|
|
134856
|
+
// stays pending, as the app's read does.
|
|
134857
|
+
// Apps often do not read the body of an error response (if (!response.ok) throw ...), and that
|
|
134858
|
+
// body is what the agent needs. Once the request has ended, and the app has not started to read
|
|
134859
|
+
// the body in the turn after, such a body is taken from native. The app gets it at its first read.
|
|
134860
|
+
function observeExpoResponse(rec, response, started) {
|
|
134861
|
+
var chunks = [];
|
|
134862
|
+
var kept = 0;
|
|
134863
|
+
var streamed = 0;
|
|
134864
|
+
var endedAt = 0;
|
|
134865
|
+
var responded = false;
|
|
134866
|
+
var reading = 0;
|
|
134867
|
+
var read = false;
|
|
134868
|
+
var finalized = false;
|
|
134869
|
+
var stalled = false;
|
|
134870
|
+
var completed = false;
|
|
134871
|
+
// Keeps as many bytes of the chunks as keepBytes keeps of a whole body.
|
|
134872
|
+
var take = function(data) {
|
|
134873
|
+
if (!data || typeof data.byteLength !== 'number') return;
|
|
134874
|
+
var view = ArrayBuffer.isView(data) ? data : new Uint8Array(data);
|
|
134875
|
+
streamed += view.byteLength;
|
|
134876
|
+
if (kept >= BODY_CAP + 3) return;
|
|
134877
|
+
chunks.push(new Uint8Array(view.buffer.slice(view.byteOffset, view.byteOffset + Math.min(view.byteLength, BODY_CAP + 3 - kept))));
|
|
134878
|
+
kept += chunks[chunks.length - 1].byteLength;
|
|
134879
|
+
};
|
|
134880
|
+
// A whole body, which can come after the request ended.
|
|
134881
|
+
var takeWhole = function(data) {
|
|
134882
|
+
if (!data || typeof data.byteLength !== 'number' || rec.entry.state === 'failed') return;
|
|
134883
|
+
var bytes = ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data);
|
|
134884
|
+
storeBody(rec, keepBytes(bytes), bytes.byteLength);
|
|
134885
|
+
};
|
|
134886
|
+
var onData = function(data) { take(data); };
|
|
134887
|
+
var onFail = function(error) { if (rec.entry.state === 'pending') fail(rec, String(error)); };
|
|
134888
|
+
// Returns whether it holds the record for a take of the body.
|
|
134889
|
+
var tried = false;
|
|
134890
|
+
var takeUnread = function() {
|
|
134891
|
+
if (tried) return false;
|
|
134892
|
+
tried = true;
|
|
134893
|
+
var status = rec.entry.response.status;
|
|
134894
|
+
if (rec.entry.request.method === 'HEAD' || status === 204 || status === 205 || status === 304 ||
|
|
134895
|
+
rec.entry.response.headers['content-length'] === '0') {
|
|
134896
|
+
storeBody(rec, '', 0, false);
|
|
134897
|
+
return false;
|
|
134898
|
+
}
|
|
134899
|
+
if ((status >= 200 && status < 300) || !takeBody) return false;
|
|
134900
|
+
var release = function() {
|
|
134901
|
+
reading--;
|
|
134902
|
+
finish();
|
|
134903
|
+
};
|
|
134904
|
+
// The app gets a turn to start its own read: on iOS, SDK 55 resolves fetch only once the request
|
|
134905
|
+
// ended.
|
|
134906
|
+
reading++;
|
|
134907
|
+
setTimeout(function() {
|
|
134908
|
+
var body = read ? undefined : takeBody(response);
|
|
134909
|
+
if (!body) return release();
|
|
134910
|
+
body.then(function(data) {
|
|
134911
|
+
if (data != null) takeWhole(data);
|
|
134912
|
+
release();
|
|
134913
|
+
}, release);
|
|
134914
|
+
}, 0);
|
|
134915
|
+
return true;
|
|
134916
|
+
};
|
|
134917
|
+
var finish = function() {
|
|
134918
|
+
if (!endedAt || !responded || reading || stalled || rec.entry.state !== 'pending') return;
|
|
134919
|
+
if (!read && !streamed && takeUnread()) return;
|
|
134920
|
+
rec.entry.durationMs = endedAt - rec.startedAt;
|
|
134921
|
+
if (!streamed) return complete(rec, undefined, undefined, false);
|
|
134922
|
+
var bytes = new Uint8Array(kept);
|
|
134923
|
+
for (var i = 0, at = 0; i < chunks.length; at += chunks[i].byteLength, i++) bytes.set(chunks[i], at);
|
|
134924
|
+
complete(rec, bytes, streamed);
|
|
134925
|
+
};
|
|
134926
|
+
var bodyEnded = function() {
|
|
134927
|
+
if (!endedAt) endedAt = Date.now();
|
|
134928
|
+
};
|
|
134929
|
+
var ended = function() {
|
|
134930
|
+
bodyEnded();
|
|
134931
|
+
finish();
|
|
134932
|
+
};
|
|
134933
|
+
var track = function(step) {
|
|
134934
|
+
read = true;
|
|
134935
|
+
// A read that starts once the record ended without a body can hang too, and holds it again.
|
|
134936
|
+
if (step > 0 && rec.entry.state === 'finished' && rec.entry.encodedDataLength === undefined) rec.entry.state = 'pending';
|
|
134937
|
+
reading += step;
|
|
134938
|
+
finish();
|
|
134939
|
+
};
|
|
134940
|
+
var onEnd = function() {
|
|
134941
|
+
try { response.removeListener('readyForJSFinalization', onEnd); } catch (e) {}
|
|
134942
|
+
finalized = true;
|
|
134943
|
+
ended();
|
|
134944
|
+
};
|
|
134945
|
+
// Expo drops the stream listeners itself when the request ends.
|
|
134946
|
+
response.addListener('didReceiveResponseData', onData);
|
|
134947
|
+
response.addListener('didComplete', function() {
|
|
134948
|
+
completed = true;
|
|
134949
|
+
ended();
|
|
134950
|
+
});
|
|
134951
|
+
response.addListener('didFailWithError', onFail);
|
|
134952
|
+
response.addListener('readyForJSFinalization', onEnd);
|
|
134953
|
+
// startStreaming returns the whole body when it completed before the app opened its stream (on
|
|
134954
|
+
// iOS, SDK 55 resolves fetch only then), else null and the chunks follow as events.
|
|
134955
|
+
tapMethod(response, 'startStreaming', function(data) {
|
|
134956
|
+
// Once the request ended, native answers null only when the body did not complete: the app's
|
|
134957
|
+
// stream then never ends, and the record stays pending. On Android, SDK 55 can deliver the null
|
|
134958
|
+
// of a stream it started after the stream's didComplete and the end: that stream is whole.
|
|
134959
|
+
if (data == null) {
|
|
134960
|
+
if (finalized && !completed) stalled = true;
|
|
134961
|
+
return;
|
|
134962
|
+
}
|
|
134963
|
+
takeWhole(data);
|
|
134964
|
+
bodyEnded();
|
|
134965
|
+
}, track);
|
|
134966
|
+
tapMethod(response, 'arrayBuffer', function(data) {
|
|
134967
|
+
takeWhole(data);
|
|
134968
|
+
bodyEnded();
|
|
134969
|
+
}, track);
|
|
134970
|
+
tapMethod(response, 'text', function(text) {
|
|
134971
|
+
if (typeof text === 'string' && rec.entry.state !== 'failed') storeBody(rec, text, textSizeOf(rec, text), false);
|
|
134972
|
+
bodyEnded();
|
|
134973
|
+
}, track);
|
|
134974
|
+
started.then(function() {
|
|
134975
|
+
if (rec.entry.state === 'failed') return;
|
|
134976
|
+
setResponse(rec, response.url, response.status, response.statusText, headersObject(response.headers));
|
|
134977
|
+
responded = true;
|
|
134978
|
+
finish();
|
|
134979
|
+
}, function(err) {
|
|
134980
|
+
if (rec.entry.state === 'pending') fail(rec, err ? String(err.message || err) : 'Network error');
|
|
134981
|
+
});
|
|
134982
|
+
}
|
|
134983
|
+
|
|
134984
|
+
if (expoFetch && typeof expoFetch.NativeRequest === 'function' && typeof expoFetch.NativeRequest.prototype.start === 'function' &&
|
|
134985
|
+
typeof Proxy === 'function' && typeof Reflect === 'object') {
|
|
134986
|
+
var NativeRequest = expoFetch.NativeRequest;
|
|
134987
|
+
var nativeProto = NativeRequest.prototype;
|
|
134988
|
+
var origStart = nativeProto.start;
|
|
134989
|
+
var origCancel = nativeProto.cancel;
|
|
134990
|
+
// The response each request fills, and the record of each request sent.
|
|
134991
|
+
var responses = new WeakMap();
|
|
134992
|
+
var nativeRecords = new WeakMap();
|
|
134993
|
+
try {
|
|
134994
|
+
expoFetch.NativeRequest = new Proxy(NativeRequest, {
|
|
134995
|
+
construct: function(target, args, newTarget) {
|
|
134996
|
+
var request = Reflect.construct(target, args, newTarget);
|
|
134997
|
+
if (args[0] && typeof args[0] === 'object') responses.set(request, args[0]);
|
|
134998
|
+
return request;
|
|
134999
|
+
}
|
|
135000
|
+
});
|
|
135001
|
+
} catch (e) {}
|
|
135002
|
+
if (expoFetch.NativeRequest !== NativeRequest) {
|
|
135003
|
+
nativeProto.start = function(url, init, body) {
|
|
135004
|
+
var startedAt = Date.now();
|
|
135005
|
+
var promise = origStart.apply(this, arguments);
|
|
135006
|
+
var response = responses.get(this);
|
|
135007
|
+
if (!response || !promise || typeof promise.then !== 'function') return promise;
|
|
135008
|
+
try {
|
|
135009
|
+
var rec = createRecord('Fetch', String((init && init.method) || 'GET').toUpperCase(), String(url),
|
|
135010
|
+
lowerCaseHeaders(init && init.headers), body, startedAt);
|
|
135011
|
+
nativeRecords.set(this, rec);
|
|
135012
|
+
observeExpoResponse(rec, response, promise);
|
|
135013
|
+
} catch (e) {}
|
|
135014
|
+
return promise;
|
|
135015
|
+
};
|
|
135016
|
+
// expo/fetch cancels the request when the app aborts it, also while the app reads the body.
|
|
135017
|
+
if (typeof origCancel === 'function') {
|
|
135018
|
+
nativeProto.cancel = function() {
|
|
135019
|
+
var rec = nativeRecords.get(this);
|
|
135020
|
+
if (rec && rec.entry.state === 'pending') fail(rec, 'aborted');
|
|
135021
|
+
return origCancel.apply(this, arguments);
|
|
135022
|
+
};
|
|
135023
|
+
}
|
|
135024
|
+
// Native hands a body out once. A body taken for the record goes to the first read of the app
|
|
135025
|
+
// instead, through the native methods that expo/fetch reads with. Expo's own response, its
|
|
135026
|
+
// listeners and its body stream stay as they are.
|
|
135027
|
+
var responseProto = expoFetch.NativeResponse && expoFetch.NativeResponse.prototype;
|
|
135028
|
+
if (responseProto && typeof responseProto.arrayBuffer === 'function' && typeof responseProto.text === 'function' &&
|
|
135029
|
+
typeof responseProto.startStreaming === 'function' && typeof TextDecoder === 'function') {
|
|
135030
|
+
var takenBodies = new WeakMap();
|
|
135031
|
+
var origStartStreaming = responseProto.startStreaming;
|
|
135032
|
+
var handBack = function(name, convert) {
|
|
135033
|
+
var original = responseProto[name];
|
|
135034
|
+
var patched = function() {
|
|
135035
|
+
var body = takenBodies.get(this);
|
|
135036
|
+
if (!body) return original.apply(this, arguments);
|
|
135037
|
+
takenBodies.delete(this);
|
|
135038
|
+
var self = this, args = arguments;
|
|
135039
|
+
// Nothing taken (the body did not complete): native answers as it would have.
|
|
135040
|
+
return body.then(function(data) { return data == null ? original.apply(self, args) : convert(data); });
|
|
135041
|
+
};
|
|
135042
|
+
try { responseProto[name] = patched; } catch (e) {}
|
|
135043
|
+
return responseProto[name] === patched;
|
|
135044
|
+
};
|
|
135045
|
+
var handed = [
|
|
135046
|
+
handBack('arrayBuffer', function(data) {
|
|
135047
|
+
return data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ? data.buffer :
|
|
135048
|
+
data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
135049
|
+
}),
|
|
135050
|
+
handBack('startStreaming', function(data) { return data; }),
|
|
135051
|
+
// As native text(): a byte order mark stays, and bytes that are not UTF-8 become U+FFFD. This
|
|
135052
|
+
// decode runs on the app's JS thread, only for an error body the app reads a turn or more
|
|
135053
|
+
// after the request ended.
|
|
135054
|
+
handBack('text', function(data) { return new TextDecoder('utf-8', { ignoreBOM: true }).decode(data); })
|
|
135055
|
+
];
|
|
135056
|
+
if (handed[0] && handed[1] && handed[2]) {
|
|
135057
|
+
// Once the request ended, native startStreaming returns the whole body, or null when the body
|
|
135058
|
+
// did not complete, and does not wait.
|
|
135059
|
+
takeBody = function(response) {
|
|
135060
|
+
var body = origStartStreaming.call(response);
|
|
135061
|
+
if (!body || typeof body.then !== 'function') return undefined;
|
|
135062
|
+
takenBodies.set(response, body);
|
|
135063
|
+
return body;
|
|
135064
|
+
};
|
|
135065
|
+
}
|
|
135066
|
+
}
|
|
135067
|
+
}
|
|
135068
|
+
}
|
|
135069
|
+
|
|
135070
|
+
// \u2500\u2500 React Native's network module \u2500\u2500
|
|
135071
|
+
// A fetch library can send through React Native's network module (RCTNetworking) itself, not
|
|
135072
|
+
// through an XHR: react-native-fetch-api, which react-native-polyfill-globals installs as the global
|
|
135073
|
+
// fetch. Each request such a library sends there is recorded, so it has one record whatever the
|
|
135074
|
+
// app's wrappers do, and whenever the library was installed. The module is not global: it is found
|
|
135075
|
+
// in the module list of Metro's development build (__r.getModules()). A request that an XHR sends
|
|
135076
|
+
// (xhrSending) has its record from the XHR.
|
|
135077
|
+
// The native module answers by request id, with events of the module: the response, its data
|
|
135078
|
+
// (text, base64 text or a native blob, as the sender asked) or the parts of a text it streams, and
|
|
135079
|
+
// the end of the request. An aborted request gets no end event on iOS.
|
|
135080
|
+
var NETWORKING_PATH = /(^|[\\\\/])Libraries[\\\\/]Network[\\\\/]RCTNetworking(\\.[a-z]+)?\\.js$/;
|
|
135081
|
+
function findNetworking() {
|
|
135082
|
+
var modules;
|
|
135083
|
+
try { modules = typeof g.__r === 'function' && typeof g.__r.getModules === 'function' ? g.__r.getModules() : null; } catch (e) {}
|
|
135084
|
+
if (!modules || typeof modules !== 'object') return null;
|
|
135085
|
+
var matches = [];
|
|
135086
|
+
var check = function(mod, id) {
|
|
135087
|
+
if (mod && typeof mod.verboseName === 'string' && NETWORKING_PATH.test(mod.verboseName)) matches.push({ mod: mod, id: Number(id) });
|
|
135088
|
+
};
|
|
135089
|
+
if (typeof modules.forEach === 'function') modules.forEach(check);
|
|
135090
|
+
else for (var key in modules) if (hasOwn.call(modules, key)) check(modules[key], key);
|
|
135091
|
+
// A module the app has not loaded yet is loaded here, as its first request would.
|
|
135092
|
+
matches.sort(function(a, b) { return (b.mod.isInitialized ? 1 : 0) - (a.mod.isInitialized ? 1 : 0); });
|
|
135093
|
+
for (var i = 0; i < matches.length; i++) {
|
|
135094
|
+
var exports;
|
|
135095
|
+
try {
|
|
135096
|
+
exports = matches[i].mod.isInitialized ? matches[i].mod.publicModule && matches[i].mod.publicModule.exports : g.__r(matches[i].id);
|
|
135097
|
+
} catch (e) {}
|
|
135098
|
+
var api = exports && typeof exports === 'object' && exports.default ? exports.default : exports;
|
|
135099
|
+
if (api && typeof api.sendRequest === 'function' && typeof api.abortRequest === 'function' &&
|
|
135100
|
+
typeof api.addListener === 'function') return api;
|
|
135101
|
+
}
|
|
135102
|
+
return null;
|
|
135103
|
+
}
|
|
135104
|
+
|
|
135105
|
+
function lowerCaseHeaders(source) {
|
|
135106
|
+
var headers = headersObject(source);
|
|
135107
|
+
var out = {};
|
|
135108
|
+
for (var name in headers) {
|
|
135109
|
+
if (!hasOwn.call(headers, name)) continue;
|
|
135110
|
+
var lower = name.toLowerCase();
|
|
135111
|
+
out[lower] = hasOwn.call(out, lower) ? out[lower] + ', ' + headers[name] : headers[name];
|
|
135112
|
+
}
|
|
135113
|
+
return out;
|
|
135114
|
+
}
|
|
135115
|
+
|
|
135116
|
+
// A Blob of the native blob a response came as, for FileReader only: it does not count as a
|
|
135117
|
+
// reference to the native blob, so the app's own Blob of it still frees it, and nothing here closes it.
|
|
135118
|
+
function blobOf(data, size) {
|
|
135119
|
+
var copy = {};
|
|
135120
|
+
for (var key in data) if (hasOwn.call(data, key)) copy[key] = data[key];
|
|
135121
|
+
copy.size = size;
|
|
135122
|
+
var blob = Object.create(Blob.prototype);
|
|
135123
|
+
blob.data = copy;
|
|
135124
|
+
return blob;
|
|
135125
|
+
}
|
|
135126
|
+
|
|
135127
|
+
// Records one request sent through the module. Returns the request's watcher, which gets the
|
|
135128
|
+
// request id once the module hands it back.
|
|
135129
|
+
function watchRequest(networking, method, url, headers, data, responseType) {
|
|
135130
|
+
var rec = createRecord('Fetch', String(method || 'GET').toUpperCase(), String(url), lowerCaseHeaders(headers), data, Date.now());
|
|
135131
|
+
var requestId;
|
|
135132
|
+
var subscriptions = [];
|
|
135133
|
+
var received;
|
|
135134
|
+
// The text received so far of a response streamed in parts, up to one character past the cut.
|
|
135135
|
+
var streamed = '';
|
|
135136
|
+
var streamedChars = 0;
|
|
135137
|
+
var streamedBytes = 0;
|
|
135138
|
+
var stop = function() {
|
|
135139
|
+
for (var i = 0; i < subscriptions.length; i++) {
|
|
135140
|
+
try { subscriptions[i].remove(); } catch (e) {}
|
|
135141
|
+
}
|
|
135142
|
+
subscriptions = [];
|
|
135143
|
+
if (requestId !== undefined && watched[requestId] === watcher) delete watched[requestId];
|
|
135144
|
+
};
|
|
135145
|
+
var storeStreamed = function() {
|
|
135146
|
+
if (!streamedChars) return;
|
|
135147
|
+
var cut = streamedChars > BODY_CAP;
|
|
135148
|
+
storeBody(rec, streamed, cut ? undefined : streamedBytes, false);
|
|
135149
|
+
if (cut) rec.entry.bodyLength = streamedChars;
|
|
135150
|
+
};
|
|
135151
|
+
var handlers = {
|
|
135152
|
+
didReceiveNetworkResponse: function(args) {
|
|
135153
|
+
setResponse(rec, args[3], args[1], '', lowerCaseHeaders(args[2]));
|
|
135154
|
+
},
|
|
135155
|
+
didReceiveNetworkData: function(args) { received = args[1]; },
|
|
135156
|
+
didReceiveNetworkIncrementalData: function(args) {
|
|
135157
|
+
var text = args[1];
|
|
135158
|
+
if (typeof text !== 'string' || !text) return;
|
|
135159
|
+
streamedChars += text.length;
|
|
135160
|
+
if (streamedChars <= BODY_CAP) streamedBytes += utf8Length(text);
|
|
135161
|
+
if (streamed.length <= BODY_CAP) streamed += text.slice(0, BODY_CAP + 1 - streamed.length);
|
|
135162
|
+
},
|
|
135163
|
+
didCompleteNetworkResponse: function(args) {
|
|
135164
|
+
stop();
|
|
135165
|
+
if (rec.entry.state !== 'pending') return;
|
|
135166
|
+
if (args[2]) return fail(rec, 'timeout');
|
|
135167
|
+
if (args[1]) {
|
|
135168
|
+
storeStreamed();
|
|
135169
|
+
return fail(rec, String(args[1]));
|
|
135170
|
+
}
|
|
135171
|
+
rec.entry.durationMs = Date.now() - rec.startedAt;
|
|
135172
|
+
if (typeof received === 'string') {
|
|
135173
|
+
if (responseType !== 'base64') return complete(rec, received, textSizeOf(rec, received), false);
|
|
135174
|
+
var n = received.length;
|
|
135175
|
+
var padding = received.charAt(n - 1) === '=' ? (received.charAt(n - 2) === '=' ? 2 : 1) : 0;
|
|
135176
|
+
var size = Math.max(0, Math.floor(n * 3 / 4) - padding);
|
|
135177
|
+
// A binary body keeps no bytes (see storeBody).
|
|
135178
|
+
if (isBinaryResponse(rec)) {
|
|
135179
|
+
omitBody(rec, size);
|
|
135180
|
+
return complete(rec, undefined, size, false);
|
|
135181
|
+
}
|
|
135182
|
+
// Decoded when the agent reads the record, as bytes are.
|
|
135183
|
+
rec.entry.bodyBase64 = received.slice(0, Math.ceil((BODY_CAP + 3) / 3) * 4);
|
|
135184
|
+
if (byId[rec.entry.requestId] === rec.entry) { bufferedChars += rec.entry.bodyBase64.length; evict(); }
|
|
135185
|
+
return complete(rec, undefined, size, false);
|
|
135186
|
+
}
|
|
135187
|
+
if (received && typeof received === 'object' && typeof received.blobId === 'string') {
|
|
135188
|
+
var blobSize = Number(received.size) || 0;
|
|
135189
|
+
if (typeof Blob === 'function' && typeof FileReader === 'function' && typeof readBlob === 'function') {
|
|
135190
|
+
try {
|
|
135191
|
+
return readBlob(rec, blobOf(received, blobSize), blobSize, Math.min(blobSize, BODY_CAP), function(end) { return blobOf(received, end); });
|
|
135192
|
+
} catch (e) {}
|
|
135193
|
+
}
|
|
135194
|
+
if (isBinaryResponse(rec)) omitBody(rec, blobSize);
|
|
135195
|
+
return complete(rec, undefined, blobSize, false);
|
|
135196
|
+
}
|
|
135197
|
+
storeStreamed();
|
|
135198
|
+
complete(rec, undefined, undefined, false);
|
|
135199
|
+
}
|
|
135200
|
+
};
|
|
135201
|
+
var watcher = {
|
|
135202
|
+
sent: function(id) {
|
|
135203
|
+
requestId = id;
|
|
135204
|
+
watched[id] = watcher;
|
|
135205
|
+
},
|
|
135206
|
+
// The app aborted the request: what it streamed so far stays with the failed record.
|
|
135207
|
+
abort: function() {
|
|
135208
|
+
stop();
|
|
135209
|
+
if (rec.entry.state !== 'pending') return;
|
|
135210
|
+
storeStreamed();
|
|
135211
|
+
fail(rec, 'aborted');
|
|
135212
|
+
},
|
|
135213
|
+
// The module did not take the request: its send threw before it handed back an id.
|
|
135214
|
+
drop: function() {
|
|
135215
|
+
if (requestId !== undefined) return;
|
|
135216
|
+
stop();
|
|
135217
|
+
var at = log.lastIndexOf(rec.entry);
|
|
135218
|
+
if (at === -1) return;
|
|
135219
|
+
log.splice(at, 1);
|
|
135220
|
+
delete byId[rec.entry.requestId];
|
|
135221
|
+
bufferedChars -= bodyChars(rec.entry);
|
|
135222
|
+
}
|
|
135223
|
+
};
|
|
135224
|
+
// Each event carries the request id, which the module hands back only after the request starts.
|
|
135225
|
+
try {
|
|
135226
|
+
for (var event in handlers) {
|
|
135227
|
+
(function(handler) {
|
|
135228
|
+
subscriptions.push(networking.addListener(event, function(args) {
|
|
135229
|
+
if (requestId === undefined || !args || args[0] !== requestId) return;
|
|
135230
|
+
try { handler(args); } catch (e) {}
|
|
135231
|
+
}));
|
|
135232
|
+
})(handlers[event]);
|
|
135233
|
+
}
|
|
135234
|
+
} catch (e) {
|
|
135235
|
+
watcher.drop();
|
|
135236
|
+
return null;
|
|
135237
|
+
}
|
|
135238
|
+
return watcher;
|
|
135239
|
+
}
|
|
135240
|
+
|
|
135241
|
+
// The watcher of each request in flight, by request id.
|
|
135242
|
+
var watched = {};
|
|
135243
|
+
var networking = findNetworking();
|
|
135244
|
+
if (networking) {
|
|
135245
|
+
var origSendRequest = networking.sendRequest;
|
|
135246
|
+
var origAbortRequest = networking.abortRequest;
|
|
135247
|
+
networking.sendRequest = function(method, trackingName, url, headers, data, responseType, incrementalUpdates, timeout, callback) {
|
|
135248
|
+
if (xhrSending || typeof callback !== 'function') return origSendRequest.apply(this, arguments);
|
|
135249
|
+
var watcher = null;
|
|
135250
|
+
try { watcher = watchRequest(networking, method, url, headers, data, responseType); } catch (e) {}
|
|
135251
|
+
if (!watcher) return origSendRequest.apply(this, arguments);
|
|
135252
|
+
var args = Array.prototype.slice.call(arguments);
|
|
135253
|
+
args[8] = function(requestId) {
|
|
135254
|
+
try { if (typeof requestId === 'number' || typeof requestId === 'string') watcher.sent(requestId); } catch (e) {}
|
|
135255
|
+
return callback.apply(this, arguments);
|
|
135256
|
+
};
|
|
135257
|
+
try {
|
|
135258
|
+
return origSendRequest.apply(this, args);
|
|
135259
|
+
} catch (e) {
|
|
135260
|
+
watcher.drop();
|
|
135261
|
+
throw e;
|
|
135262
|
+
}
|
|
135263
|
+
};
|
|
135264
|
+
networking.abortRequest = function(requestId) {
|
|
135265
|
+
var watcher = hasOwn.call(watched, requestId) ? watched[requestId] : null;
|
|
135266
|
+
if (watcher) {
|
|
135267
|
+
try { watcher.abort(); } catch (e) {}
|
|
135268
|
+
}
|
|
135269
|
+
return origAbortRequest.apply(this, arguments);
|
|
135270
|
+
};
|
|
135271
|
+
}
|
|
134350
135272
|
|
|
134351
135273
|
return JSON.stringify({ installed: true });
|
|
134352
135274
|
})()`;
|
|
@@ -134382,9 +135304,10 @@ function makeNetworkLogReadScript(start2, limit, metroPort2) {
|
|
|
134382
135304
|
requestId: s.requestId,
|
|
134383
135305
|
state: s.state,
|
|
134384
135306
|
request: s.request ? { url: s.request.url, method: s.request.method } : undefined,
|
|
134385
|
-
response: s.response ? { status: s.response.status, statusText: s.response.statusText, mimeType: s.response.mimeType } : undefined,
|
|
135307
|
+
response: s.response ? { url: s.response.url, status: s.response.status, statusText: s.response.statusText, mimeType: s.response.mimeType } : undefined,
|
|
134386
135308
|
resourceType: s.resourceType,
|
|
134387
135309
|
encodedDataLength: s.encodedDataLength,
|
|
135310
|
+
bodyLength: s.bodyLength,
|
|
134388
135311
|
timestamp: s.timestamp,
|
|
134389
135312
|
durationMs: s.durationMs,
|
|
134390
135313
|
errorText: s.errorText
|
|
@@ -134400,6 +135323,7 @@ function makeNetworkDetailReadScript(requestId) {
|
|
|
134400
135323
|
if (!byId) return JSON.stringify({ error: 'Network interceptor not installed' });
|
|
134401
135324
|
var entry = byId[${JSON.stringify(requestId)}];
|
|
134402
135325
|
if (!entry) return JSON.stringify({ error: 'Request not found' });
|
|
135326
|
+
if (typeof globalThis.__argent_network_decode === 'function') globalThis.__argent_network_decode(entry);
|
|
134403
135327
|
|
|
134404
135328
|
return JSON.stringify({
|
|
134405
135329
|
id: entry.id,
|
|
@@ -134410,11 +135334,12 @@ function makeNetworkDetailReadScript(requestId) {
|
|
|
134410
135334
|
resourceType: entry.resourceType,
|
|
134411
135335
|
encodedDataLength: entry.encodedDataLength,
|
|
134412
135336
|
timestamp: entry.timestamp,
|
|
134413
|
-
wallTime: entry.wallTime,
|
|
134414
135337
|
durationMs: entry.durationMs,
|
|
134415
135338
|
errorText: entry.errorText,
|
|
134416
|
-
|
|
134417
|
-
|
|
135339
|
+
responseBody: entry.responseBody,
|
|
135340
|
+
bodyTruncated: entry.bodyTruncated,
|
|
135341
|
+
bodyLength: entry.bodyLength,
|
|
135342
|
+
bodyOmitted: entry.bodyOmitted
|
|
134418
135343
|
});
|
|
134419
135344
|
})()`;
|
|
134420
135345
|
}
|
|
@@ -149700,35 +150625,83 @@ When the debugger cannot be reached, this tool does not fail: it returns { statu
|
|
|
149700
150625
|
}
|
|
149701
150626
|
|
|
149702
150627
|
// ../tool-server/src/tools/network/network-logs.ts
|
|
150628
|
+
var import_node_http4 = require("node:http");
|
|
149703
150629
|
init_zod();
|
|
149704
150630
|
var import_bytes3 = __toESM(require_bytes());
|
|
149705
150631
|
init_device_info();
|
|
149706
150632
|
var ITEMS_PER_PAGE = 50;
|
|
150633
|
+
function notInstalled(installError) {
|
|
150634
|
+
return installError ? `Network interceptor could not be installed in the app's JS runtime: ${installError}` : "Network interceptor not installed: the app's JS runtime reloaded, which clears captured requests. Call view-network-logs again to reinstall it, then repeat the action that sends the requests.";
|
|
150635
|
+
}
|
|
149707
150636
|
function formatBytes2(bytes) {
|
|
149708
150637
|
return (0, import_bytes3.default)(bytes, { decimalPlaces: 1, unitSeparator: " " }) ?? `${bytes} B`;
|
|
149709
150638
|
}
|
|
149710
|
-
function
|
|
149711
|
-
|
|
149712
|
-
|
|
149713
|
-
|
|
150639
|
+
function reasonPhrase(status) {
|
|
150640
|
+
return import_node_http4.STATUS_CODES[status] ?? "";
|
|
150641
|
+
}
|
|
150642
|
+
function comparableUrl(url2) {
|
|
150643
|
+
let text = url2;
|
|
149714
150644
|
try {
|
|
149715
150645
|
const parsed = new URL(url2);
|
|
149716
|
-
|
|
150646
|
+
parsed.hash = "";
|
|
150647
|
+
text = parsed.search ? parsed.href : parsed.href.replace(/\?$/, "");
|
|
150648
|
+
} catch {
|
|
150649
|
+
}
|
|
150650
|
+
text = text.replace(/\+/g, " ");
|
|
150651
|
+
for (let i = 0; i < 4; i++) {
|
|
150652
|
+
let decoded;
|
|
150653
|
+
try {
|
|
150654
|
+
decoded = decodeURI(text);
|
|
150655
|
+
} catch {
|
|
150656
|
+
break;
|
|
150657
|
+
}
|
|
150658
|
+
if (decoded === text) break;
|
|
150659
|
+
text = decoded;
|
|
150660
|
+
}
|
|
150661
|
+
return text.replace(/%[0-9a-f]{2}/gi, (escape2) => escape2.toUpperCase());
|
|
150662
|
+
}
|
|
150663
|
+
function isRedirect(requestUrl, finalUrl) {
|
|
150664
|
+
if (!finalUrl || requestUrl === void 0 || finalUrl === requestUrl) return false;
|
|
150665
|
+
return comparableUrl(finalUrl) !== comparableUrl(requestUrl);
|
|
150666
|
+
}
|
|
150667
|
+
function shortName(url2, from2) {
|
|
150668
|
+
try {
|
|
150669
|
+
const parsed = new URL(url2);
|
|
150670
|
+
const path53 = parsed.pathname === "/" ? "" : parsed.pathname;
|
|
150671
|
+
let sameHost = true;
|
|
150672
|
+
try {
|
|
150673
|
+
sameHost = from2 === void 0 || new URL(from2).host === parsed.host;
|
|
150674
|
+
} catch {
|
|
150675
|
+
sameHost = false;
|
|
150676
|
+
}
|
|
150677
|
+
if (sameHost) return path53 || parsed.hostname;
|
|
150678
|
+
return parsed.host + path53;
|
|
149717
150679
|
} catch {
|
|
149718
|
-
|
|
150680
|
+
return url2;
|
|
149719
150681
|
}
|
|
150682
|
+
}
|
|
150683
|
+
function formatEntry(entry) {
|
|
150684
|
+
const method = entry.request?.method ?? "???";
|
|
150685
|
+
const url2 = entry.request?.url ?? "unknown";
|
|
150686
|
+
const name = shortName(url2);
|
|
150687
|
+
const finalUrl = entry.response?.url;
|
|
150688
|
+
const redirect = finalUrl && isRedirect(url2, finalUrl) ? `\u2192 ${shortName(finalUrl, url2)}` : "";
|
|
149720
150689
|
let status;
|
|
149721
150690
|
if (entry.state === "failed") {
|
|
149722
150691
|
status = entry.errorText ?? "failed";
|
|
149723
150692
|
} else if (entry.response) {
|
|
149724
|
-
status =
|
|
150693
|
+
status = [
|
|
150694
|
+
entry.response.status,
|
|
150695
|
+
entry.response.statusText,
|
|
150696
|
+
entry.state === "pending" ? "(pending)" : ""
|
|
150697
|
+
].filter((part) => part !== "").join(" ");
|
|
149725
150698
|
} else {
|
|
149726
150699
|
status = "pending";
|
|
149727
150700
|
}
|
|
149728
150701
|
const type = entry.resourceType ?? "";
|
|
149729
|
-
const size = entry.encodedDataLength != null ? formatBytes2(entry.encodedDataLength) : "";
|
|
150702
|
+
const size = entry.encodedDataLength != null ? formatBytes2(entry.encodedDataLength) : entry.bodyLength != null ? `~${formatBytes2(entry.bodyLength)}` : "";
|
|
149730
150703
|
const duration3 = entry.durationMs != null ? `${entry.durationMs} ms` : "";
|
|
149731
|
-
return `{id: ${entry.requestId}} "${method} ${name}"
|
|
150704
|
+
return [`{id: ${entry.requestId}}`, `"${method} ${name}"`, status, type, size, duration3, redirect].filter(Boolean).join(" ");
|
|
149732
150705
|
}
|
|
149733
150706
|
function recordToLogEntry(r, id) {
|
|
149734
150707
|
return {
|
|
@@ -149746,7 +150719,7 @@ function recordToLogEntry(r, id) {
|
|
|
149746
150719
|
function renderChromiumPage(records, pageIndexParam) {
|
|
149747
150720
|
const total = records.length;
|
|
149748
150721
|
if (total === 0) {
|
|
149749
|
-
return "No network traffic captured. Recording is active on the active tab
|
|
150722
|
+
return "No network traffic captured. Recording is active on the active tab. Navigate or reload the page to populate it (requests made before the device was attached are not captured).";
|
|
149750
150723
|
}
|
|
149751
150724
|
const pageCount = Math.ceil(total / ITEMS_PER_PAGE);
|
|
149752
150725
|
const pageIndex = pageIndexParam === "latest" ? pageCount - 1 : pageIndexParam;
|
|
@@ -149777,8 +150750,9 @@ var networkLogsTool = {
|
|
|
149777
150750
|
},
|
|
149778
150751
|
description: `Retrieve captured network (HTTP) requests from the running app.
|
|
149779
150752
|
Returns a paginated list of requests with method, URL, status, resource type, size, and duration.
|
|
150753
|
+
A size with "~" is estimated from the body's length in characters; on React Native, "\u2192 <url>" is the final URL after a redirect.
|
|
149780
150754
|
Each entry includes a requestId that can be passed to view-network-request-details for full details.
|
|
149781
|
-
On React Native (iOS / Android / Vega) interception is injected into the JS runtime: it
|
|
150755
|
+
On React Native (iOS / Android / Vega) interception is injected into the JS runtime: it records each request once: XMLHttpRequest (axios included), React Native's fetch, Expo's fetch, and a fetch library that sends through React Native's network module itself, such as react-native-fetch-api (found in a Metro development build). Capture starts at the first network-tool call; a reload clears it. On Chromium it reads the browser's native CDP Network domain (the active tab; all request types).
|
|
149782
150756
|
Use when inspecting outbound HTTP traffic or debugging API calls in the running app.
|
|
149783
150757
|
Fails if the app is not connected (RN) or the device is not reachable (Chromium).`,
|
|
149784
150758
|
zodSchema: zodSchema38,
|
|
@@ -149798,12 +150772,15 @@ Fails if the app is not connected (RN) or the device is not reachable (Chromium)
|
|
|
149798
150772
|
return renderChromiumPage(chromium.server.network.requests(), params.pageIndex);
|
|
149799
150773
|
}
|
|
149800
150774
|
const api = services.inspector;
|
|
149801
|
-
await api.cdp.evaluate(NETWORK_INTERCEPTOR_SCRIPT).
|
|
149802
|
-
|
|
150775
|
+
const installError = await api.cdp.evaluate(NETWORK_INTERCEPTOR_SCRIPT).then(
|
|
150776
|
+
() => void 0,
|
|
150777
|
+
(err) => err instanceof Error ? err.message : String(err)
|
|
150778
|
+
);
|
|
149803
150779
|
const countRaw = await api.cdp.evaluate(makeNetworkLogReadScript(0, 0, api.port));
|
|
149804
|
-
const { total } = JSON.parse(countRaw);
|
|
150780
|
+
const { total, interceptorInstalled } = JSON.parse(countRaw);
|
|
150781
|
+
if (!interceptorInstalled) return notInstalled(installError);
|
|
149805
150782
|
if (total === 0) {
|
|
149806
|
-
return "No network traffic captured. Make sure the app is running and making HTTP requests. Network interception is active
|
|
150783
|
+
return "No network traffic captured. Make sure the app is running and making HTTP requests. Network interception is active: it records XMLHttpRequest (axios included), React Native's fetch, Expo's fetch, and a fetch library that sends through React Native's network module, but not requests sent before capture started (at the first network-tool call).";
|
|
149807
150784
|
}
|
|
149808
150785
|
const pageCount = Math.ceil(total / ITEMS_PER_PAGE);
|
|
149809
150786
|
const pageIndex = params.pageIndex === "latest" ? pageCount - 1 : params.pageIndex;
|
|
@@ -149814,10 +150791,15 @@ Fails if the app is not connected (RN) or the device is not reachable (Chromium)
|
|
|
149814
150791
|
const readScript = makeNetworkLogReadScript(start2, ITEMS_PER_PAGE, api.port);
|
|
149815
150792
|
const raw = await api.cdp.evaluate(readScript);
|
|
149816
150793
|
const data = JSON.parse(raw);
|
|
149817
|
-
if (!data.interceptorInstalled)
|
|
149818
|
-
|
|
149819
|
-
|
|
149820
|
-
|
|
150794
|
+
if (!data.interceptorInstalled) return notInstalled(installError);
|
|
150795
|
+
const lines = data.entries.map(
|
|
150796
|
+
(entry) => formatEntry(
|
|
150797
|
+
entry.response ? {
|
|
150798
|
+
...entry,
|
|
150799
|
+
response: { ...entry.response, statusText: reasonPhrase(entry.response.status) }
|
|
150800
|
+
} : entry
|
|
150801
|
+
)
|
|
150802
|
+
);
|
|
149821
150803
|
return `=== NETWORK LOGS (page ${pageIndex + 1}/${pageCount}, ${data.total} total) ===
|
|
149822
150804
|
|
|
149823
150805
|
${lines.join("\n")}`;
|
|
@@ -149840,19 +150822,37 @@ var SENSITIVE_HEADER_PATTERNS = [
|
|
|
149840
150822
|
"apikey",
|
|
149841
150823
|
"x-api-key"
|
|
149842
150824
|
];
|
|
149843
|
-
function isSensitiveHeader(name) {
|
|
150825
|
+
function isSensitiveHeader(name, value) {
|
|
149844
150826
|
const lower = name.toLowerCase();
|
|
149845
|
-
|
|
150827
|
+
if (SENSITIVE_HEADER_PATTERNS.some((p) => lower.includes(p))) return true;
|
|
150828
|
+
return /^\d+$/.test(name) && value != null && isSensitiveHeader(String(value).split(",")[0].trim());
|
|
149846
150829
|
}
|
|
149847
150830
|
function redactHeaders(headers) {
|
|
149848
150831
|
if (!headers) return {};
|
|
149849
150832
|
const result = {};
|
|
149850
150833
|
for (const [key2, value] of Object.entries(headers)) {
|
|
149851
|
-
result[key2] = isSensitiveHeader(key2) ? "[REDACTED]" : value;
|
|
150834
|
+
result[key2] = isSensitiveHeader(key2, value) ? "[REDACTED]" : value;
|
|
149852
150835
|
}
|
|
149853
150836
|
return result;
|
|
149854
150837
|
}
|
|
149855
150838
|
var MAX_BODY_SIZE = 1e3;
|
|
150839
|
+
function cutMarker(fullSize, mimeType) {
|
|
150840
|
+
const mime = mimeType !== void 0 ? `, MIME: ${mimeType}` : "";
|
|
150841
|
+
return `[TRUNCATED: first ${MAX_BODY_SIZE} chars shown, full size: ${fullSize}${mime}]`;
|
|
150842
|
+
}
|
|
150843
|
+
function utf8Text(bytes) {
|
|
150844
|
+
try {
|
|
150845
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
150846
|
+
} catch {
|
|
150847
|
+
return bytes;
|
|
150848
|
+
}
|
|
150849
|
+
}
|
|
150850
|
+
function truncatePostData(postData, truncated) {
|
|
150851
|
+
if (postData == null || postData.length <= MAX_BODY_SIZE) return postData;
|
|
150852
|
+
const fullSize = truncated ? `more than ${postData.length} chars` : `${postData.length} chars`;
|
|
150853
|
+
return `${cutMarker(fullSize)}
|
|
150854
|
+
${postData.slice(0, MAX_BODY_SIZE)}...`;
|
|
150855
|
+
}
|
|
149856
150856
|
var zodSchema39 = external_exports.object({
|
|
149857
150857
|
port: metroPortField,
|
|
149858
150858
|
device_id: external_exports.string().describe(
|
|
@@ -149869,9 +150869,9 @@ var networkRequestTool = {
|
|
|
149869
150869
|
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to read network request ${params.requestId}: ${failureSignal2.error_code}`
|
|
149870
150870
|
},
|
|
149871
150871
|
description: `Get full details of a specific network request by its requestId (from view-network-logs).
|
|
149872
|
-
Returns request/response headers (sensitive headers redacted), status, timing, and optionally the response body.
|
|
149873
|
-
|
|
149874
|
-
Returns an error message string if the requestId is not found
|
|
150872
|
+
Returns request/response headers (sensitive headers redacted), status, timing, the request body, and optionally the response body.
|
|
150873
|
+
Request and response bodies over 1000 chars are cut to 1000 under a "[TRUNCATED: ...]" line that gives the full size; a binary response body is replaced by "[body omitted: binary <mime>, N bytes]". Use when you need headers, body, or timing for a specific request after listing logs.
|
|
150874
|
+
Returns an error message string if the requestId is not found: use view-network-logs to get valid requestId values.`,
|
|
149875
150875
|
zodSchema: zodSchema39,
|
|
149876
150876
|
capability: DEBUGGER_TOOL_CAPABILITY,
|
|
149877
150877
|
services: (params) => {
|
|
@@ -149904,7 +150904,7 @@ Returns an error message string if the requestId is not found; use view-network-
|
|
|
149904
150904
|
url: rec.url,
|
|
149905
150905
|
method: rec.method,
|
|
149906
150906
|
headers: redactHeaders(rec.requestHeaders),
|
|
149907
|
-
postData: rec.postData
|
|
150907
|
+
postData: truncatePostData(rec.postData)
|
|
149908
150908
|
};
|
|
149909
150909
|
}
|
|
149910
150910
|
if (rec.status != null) {
|
|
@@ -149920,9 +150920,13 @@ Returns an error message string if the requestId is not found; use view-network-
|
|
|
149920
150920
|
requestId: rec.requestId
|
|
149921
150921
|
});
|
|
149922
150922
|
if (out.body != null) {
|
|
149923
|
-
const body = out.base64Encoded ? Buffer.from(out.body, "base64")
|
|
149924
|
-
|
|
150923
|
+
const body = out.base64Encoded ? utf8Text(Buffer.from(out.body, "base64")) : out.body;
|
|
150924
|
+
if (typeof body !== "string") {
|
|
150925
|
+
resp.body = `[body omitted: binary ${resp.mimeType || "data"}, ${body.length} bytes]`;
|
|
150926
|
+
} else {
|
|
150927
|
+
resp.body = body.length > MAX_BODY_SIZE ? `${cutMarker(`${body.length} chars`, resp.mimeType)}
|
|
149925
150928
|
${body.slice(0, MAX_BODY_SIZE)}...` : body;
|
|
150929
|
+
}
|
|
149926
150930
|
}
|
|
149927
150931
|
} catch {
|
|
149928
150932
|
}
|
|
@@ -149947,28 +150951,34 @@ ${body.slice(0, MAX_BODY_SIZE)}...` : body;
|
|
|
149947
150951
|
resourceType: entry.resourceType,
|
|
149948
150952
|
durationMs: entry.durationMs,
|
|
149949
150953
|
encodedDataLength: entry.encodedDataLength,
|
|
149950
|
-
errorText: entry.errorText
|
|
149951
|
-
initiator: entry.initiator
|
|
150954
|
+
errorText: entry.errorText
|
|
149952
150955
|
};
|
|
149953
150956
|
if (entry.request) {
|
|
149954
150957
|
details.request = {
|
|
149955
150958
|
url: entry.request.url,
|
|
149956
150959
|
method: entry.request.method,
|
|
149957
150960
|
headers: redactHeaders(entry.request.headers),
|
|
149958
|
-
postData: entry.request.postData
|
|
150961
|
+
postData: truncatePostData(entry.request.postData, entry.request.postDataTruncated)
|
|
149959
150962
|
};
|
|
149960
150963
|
}
|
|
149961
150964
|
if (entry.response) {
|
|
149962
150965
|
const resp = {
|
|
149963
150966
|
status: entry.response.status,
|
|
149964
|
-
statusText: entry.response.
|
|
150967
|
+
statusText: reasonPhrase(entry.response.status),
|
|
149965
150968
|
headers: redactHeaders(entry.response.headers),
|
|
149966
150969
|
mimeType: entry.response.mimeType
|
|
149967
150970
|
};
|
|
149968
|
-
if (
|
|
150971
|
+
if (isRedirect(entry.request?.url, entry.response.url)) {
|
|
150972
|
+
resp.redirectedTo = entry.response.url;
|
|
150973
|
+
}
|
|
150974
|
+
if (params.includeBody && entry.bodyOmitted) {
|
|
150975
|
+
const size = entry.encodedDataLength != null ? `, ${entry.encodedDataLength} bytes` : entry.bodyLength != null ? `, ~${entry.bodyLength} bytes` : "";
|
|
150976
|
+
resp.body = `[body omitted: binary ${entry.response.mimeType || "data"}${size}]`;
|
|
150977
|
+
} else if (params.includeBody && entry.responseBody != null) {
|
|
149969
150978
|
const body = entry.responseBody;
|
|
149970
150979
|
if (body.length > MAX_BODY_SIZE) {
|
|
149971
|
-
|
|
150980
|
+
const fullSize = !entry.bodyTruncated ? `${body.length} chars` : entry.encodedDataLength != null ? `${entry.encodedDataLength} bytes` : entry.bodyLength != null ? `${entry.bodyLength} chars` : `more than ${body.length} chars`;
|
|
150981
|
+
resp.body = `${cutMarker(fullSize, entry.response.mimeType)}
|
|
149972
150982
|
${body.slice(0, MAX_BODY_SIZE)}...`;
|
|
149973
150983
|
} else {
|
|
149974
150984
|
resp.body = body;
|