react-native-inapp-inspector 1.1.24 → 1.1.26
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/android/build.gradle +36 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
- package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
- package/dist/commonjs/components/ConsoleLogCard.js +49 -25
- package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
- package/dist/commonjs/components/ErrorBoundary.js +9 -145
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
- package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
- package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
- package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +65 -28
- package/dist/commonjs/components/Inspector/LogDetail.js +227 -114
- package/dist/commonjs/components/Inspector/MainScreen.js +13 -4
- package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
- package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
- package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
- package/dist/commonjs/components/Inspector/TabBar.js +15 -3
- package/dist/commonjs/components/NetworkIcons.d.ts +25 -0
- package/dist/commonjs/components/NetworkIcons.js +188 -1
- package/dist/commonjs/components/SegmentedTabs.js +33 -23
- package/dist/commonjs/components/TreeNode.js +13 -12
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
- package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/consoleLogger.js +153 -51
- package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
- package/dist/commonjs/customHooks/crashHandler.js +681 -0
- package/dist/commonjs/customHooks/networkLogger.js +74 -49
- package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
- package/dist/commonjs/customHooks/performanceTracker.js +44 -3
- package/dist/commonjs/decorators/index.d.ts +49 -0
- package/dist/commonjs/decorators/index.js +142 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
- package/dist/commonjs/helpers/index.d.ts +7 -4
- package/dist/commonjs/helpers/index.js +102 -64
- package/dist/commonjs/i18n/locales/en.json +182 -4
- package/dist/commonjs/index.d.ts +6 -0
- package/dist/commonjs/index.js +310 -8
- package/dist/commonjs/styles/index.d.ts +4 -1
- package/dist/commonjs/styles/index.js +22 -19
- package/dist/commonjs/types/enums.d.ts +156 -9
- package/dist/commonjs/types/enums.js +139 -2
- package/dist/commonjs/types/interfaces.d.ts +70 -1
- package/dist/esm/components/AnalyticsEventCard.js +95 -89
- package/dist/esm/components/ConsoleLogCard.js +49 -25
- package/dist/esm/components/ErrorBoundary.d.ts +1 -1
- package/dist/esm/components/ErrorBoundary.js +10 -113
- package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
- package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/esm/components/Inspector/BundleTab.js +827 -407
- package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashDetail.js +758 -0
- package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
- package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashTab.js +689 -0
- package/dist/esm/components/Inspector/InspectorContext.js +1 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +66 -29
- package/dist/esm/components/Inspector/LogDetail.js +227 -114
- package/dist/esm/components/Inspector/MainScreen.js +14 -5
- package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
- package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
- package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
- package/dist/esm/components/Inspector/TabBar.js +16 -4
- package/dist/esm/components/NetworkIcons.d.ts +25 -0
- package/dist/esm/components/NetworkIcons.js +161 -0
- package/dist/esm/components/SegmentedTabs.js +33 -23
- package/dist/esm/components/TreeNode.js +13 -12
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/analyticsLogger.js +70 -44
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
- package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
- package/dist/esm/customHooks/consoleLogger.js +151 -50
- package/dist/esm/customHooks/crashHandler.d.ts +64 -0
- package/dist/esm/customHooks/crashHandler.js +659 -0
- package/dist/esm/customHooks/networkLogger.js +74 -49
- package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
- package/dist/esm/customHooks/performanceTracker.js +38 -1
- package/dist/esm/decorators/index.d.ts +49 -0
- package/dist/esm/decorators/index.js +137 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
- package/dist/esm/helpers/index.d.ts +7 -4
- package/dist/esm/helpers/index.js +100 -63
- package/dist/esm/i18n/locales/en.json +182 -4
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +262 -8
- package/dist/esm/styles/index.d.ts +4 -1
- package/dist/esm/styles/index.js +22 -19
- package/dist/esm/types/enums.d.ts +156 -9
- package/dist/esm/types/enums.js +138 -3
- package/dist/esm/types/interfaces.d.ts +70 -1
- package/ios/NetworkInspectorModule.h +6 -0
- package/ios/NetworkInspectorModule.m +125 -0
- package/package.json +5 -2
- package/react-native-inapp-inspector.podspec +18 -0
|
@@ -33,61 +33,353 @@ const getSourceCodeModule = () => {
|
|
|
33
33
|
};
|
|
34
34
|
export const getHostScriptURL = () => {
|
|
35
35
|
// 1. NativeModules.SourceCode (bridged) or SourceCode TurboModule (bridgeless / new arch)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
try {
|
|
37
|
+
const scriptURL = getSourceCodeModule()?.scriptURL;
|
|
38
|
+
if (typeof scriptURL === 'string' && scriptURL.length > 0) {
|
|
39
|
+
return scriptURL;
|
|
40
|
+
}
|
|
39
41
|
}
|
|
40
|
-
|
|
42
|
+
catch { }
|
|
43
|
+
// 2. Android legacy & new arch: PlatformConstants.serverHost, DevSettings, or AndroidConstants
|
|
41
44
|
try {
|
|
42
|
-
const serverHost = NativeModules?.PlatformConstants?.serverHost
|
|
45
|
+
const serverHost = NativeModules?.PlatformConstants?.serverHost ||
|
|
46
|
+
NativeModules?.AndroidConstants?.serverHost ||
|
|
47
|
+
NativeModules?.DevSettings?.serverHost ||
|
|
48
|
+
NativeModules?.DevSettings?.getConstants?.()?.serverHost;
|
|
43
49
|
if (typeof serverHost === 'string' && serverHost.length > 0) {
|
|
44
|
-
|
|
50
|
+
const formattedHost = serverHost.startsWith('http') ? serverHost : `http://${serverHost}`;
|
|
51
|
+
return `${formattedHost}/index.bundle?platform=${Platform.OS}&dev=true`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch { }
|
|
55
|
+
// 3. Fallback to global dev config if defined
|
|
56
|
+
try {
|
|
57
|
+
const globalHost = globalThis?.__DEV_SERVER_URL__ ||
|
|
58
|
+
globalThis?.__METRO_SERVER_HOST__;
|
|
59
|
+
if (typeof globalHost === 'string' && globalHost.length > 0) {
|
|
60
|
+
return globalHost.startsWith('http')
|
|
61
|
+
? globalHost
|
|
62
|
+
: `http://${globalHost}/index.bundle?platform=${Platform.OS}&dev=true`;
|
|
45
63
|
}
|
|
46
64
|
}
|
|
47
65
|
catch { }
|
|
48
66
|
return '';
|
|
49
67
|
};
|
|
50
|
-
|
|
68
|
+
// Expanded list of common Metro & dev server ports
|
|
69
|
+
const DEV_SERVER_PORTS = [8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090, 8080, 19000, 19001, 3000];
|
|
51
70
|
const buildProbeUrls = (scriptURL) => {
|
|
52
71
|
const urls = [];
|
|
72
|
+
let extractedHost = null;
|
|
73
|
+
let extractedPort = null;
|
|
53
74
|
if (typeof scriptURL === 'string' && scriptURL.startsWith('http')) {
|
|
54
75
|
urls.push(scriptURL);
|
|
76
|
+
const urlMatch = scriptURL.match(/^https?:\/\/([^:/]+)(?::(\d+))?/);
|
|
77
|
+
if (urlMatch) {
|
|
78
|
+
extractedHost = urlMatch[1];
|
|
79
|
+
if (urlMatch[2]) {
|
|
80
|
+
extractedPort = parseInt(urlMatch[2], 10);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
55
83
|
}
|
|
56
|
-
|
|
57
|
-
const ports =
|
|
84
|
+
// Prioritize dynamically extracted port first
|
|
85
|
+
const ports = extractedPort
|
|
86
|
+
? Array.from(new Set([extractedPort, ...DEV_SERVER_PORTS]))
|
|
87
|
+
: DEV_SERVER_PORTS;
|
|
58
88
|
const hosts = Platform.OS === 'android'
|
|
59
|
-
? ['localhost', '127.0.0.1', '10.0.2.2']
|
|
89
|
+
? ['localhost', '127.0.0.1', '10.0.2.2', '10.0.3.2']
|
|
60
90
|
: ['localhost', '127.0.0.1'];
|
|
61
|
-
|
|
62
|
-
|
|
91
|
+
if (extractedHost && !hosts.includes(extractedHost)) {
|
|
92
|
+
hosts.unshift(extractedHost);
|
|
93
|
+
}
|
|
94
|
+
for (const port of ports) {
|
|
95
|
+
for (const host of hosts) {
|
|
63
96
|
urls.push(`http://${host}:${port}/index.bundle?platform=${Platform.OS}&dev=true`);
|
|
64
97
|
}
|
|
65
98
|
}
|
|
66
99
|
return Array.from(new Set(urls));
|
|
67
100
|
};
|
|
68
|
-
const
|
|
101
|
+
const promiseAny = (promises) => {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
let pendingCount = promises.length;
|
|
104
|
+
if (pendingCount === 0) {
|
|
105
|
+
reject(new Error('All promises rejected'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
promises.forEach(p => {
|
|
109
|
+
Promise.resolve(p)
|
|
110
|
+
.then(resolve)
|
|
111
|
+
.catch(() => {
|
|
112
|
+
pendingCount--;
|
|
113
|
+
if (pendingCount === 0) {
|
|
114
|
+
reject(new Error('All promises rejected'));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
const probeCandidateUrlsInParallel = async (urls, timeoutMs = 4000) => {
|
|
121
|
+
if (urls.length === 0)
|
|
122
|
+
return null;
|
|
123
|
+
const controller = new AbortController();
|
|
124
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
125
|
+
const fetchSingle = async (url) => {
|
|
126
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
127
|
+
if (!res.ok) {
|
|
128
|
+
throw new Error(`HTTP ${res.status}`);
|
|
129
|
+
}
|
|
130
|
+
const contentLength = res.headers.get('content-length');
|
|
131
|
+
const text = await res.text();
|
|
132
|
+
if (!text || text.length === 0) {
|
|
133
|
+
throw new Error('Empty response');
|
|
134
|
+
}
|
|
135
|
+
const bytes = contentLength ? parseInt(contentLength, 10) || text.length : text.length;
|
|
136
|
+
return { text, bytes, url };
|
|
137
|
+
};
|
|
69
138
|
try {
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
139
|
+
// Probe candidate dev servers concurrently — whichever responds first wins
|
|
140
|
+
const result = await promiseAny(urls.map(url => fetchSingle(url)));
|
|
141
|
+
controller.abort();
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const MAX_ARG_SCAN_LEN = 200000;
|
|
152
|
+
const scanCallArguments = (text, openParenIndex) => {
|
|
153
|
+
let depth = 0;
|
|
154
|
+
let bracketDepth = 0;
|
|
155
|
+
let quote = null;
|
|
156
|
+
const commas = [];
|
|
157
|
+
const end = Math.min(text.length, openParenIndex + MAX_ARG_SCAN_LEN);
|
|
158
|
+
for (let i = openParenIndex; i < end; i++) {
|
|
159
|
+
const ch = text[i];
|
|
160
|
+
if (quote) {
|
|
161
|
+
if (ch === '\\') {
|
|
162
|
+
i++;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (ch === quote)
|
|
166
|
+
quote = null;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
170
|
+
quote = ch;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (ch === '(') {
|
|
174
|
+
depth++;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (ch === ')') {
|
|
178
|
+
if (depth === 0)
|
|
179
|
+
return null;
|
|
180
|
+
depth--;
|
|
181
|
+
if (depth === 0) {
|
|
182
|
+
if (commas.length === 0)
|
|
183
|
+
return null;
|
|
184
|
+
const args = [];
|
|
185
|
+
let prev = openParenIndex + 1;
|
|
186
|
+
for (const c of commas) {
|
|
187
|
+
args.push(text.slice(prev, c));
|
|
188
|
+
prev = c + 1;
|
|
189
|
+
}
|
|
190
|
+
args.push(text.slice(prev, i));
|
|
191
|
+
return args;
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (ch === '[') {
|
|
196
|
+
bracketDepth++;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (ch === ']') {
|
|
200
|
+
if (bracketDepth > 0)
|
|
201
|
+
bracketDepth--;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (ch === ',' && depth === 1 && bracketDepth === 0)
|
|
205
|
+
commas.push(i);
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
};
|
|
209
|
+
// ─── Runtime Module & Define Tracking ────────────────────────────────────────
|
|
210
|
+
// Captures modules registered via Metro's global __d and executed via __r.
|
|
211
|
+
const executedModuleIds = new Set();
|
|
212
|
+
const runtimeModules = new Map();
|
|
213
|
+
export const trackRuntimeModuleExecution = () => {
|
|
214
|
+
try {
|
|
215
|
+
const g = globalThis;
|
|
216
|
+
const r = g.__r;
|
|
217
|
+
if (typeof r === 'function' && !r.__inspectorTracked) {
|
|
218
|
+
const tracked = function (moduleId, ...rest) {
|
|
219
|
+
executedModuleIds.add(Number(moduleId));
|
|
220
|
+
return r.call(this, moduleId, ...rest);
|
|
82
221
|
};
|
|
222
|
+
Object.defineProperty(tracked, '__inspectorTracked', { value: true, enumerable: false });
|
|
223
|
+
g.__r = tracked;
|
|
83
224
|
}
|
|
84
|
-
|
|
85
|
-
|
|
225
|
+
}
|
|
226
|
+
catch { }
|
|
227
|
+
};
|
|
228
|
+
export const trackRuntimeDefine = () => {
|
|
229
|
+
try {
|
|
230
|
+
const g = globalThis;
|
|
231
|
+
const originalD = g.__d;
|
|
232
|
+
if (typeof originalD === 'function' && !originalD.__inspectorTracked) {
|
|
233
|
+
const trackedD = function (factory, moduleId, dependencyMap, verboseName, ...rest) {
|
|
234
|
+
if (typeof verboseName === 'string' && verboseName.length > 0) {
|
|
235
|
+
runtimeModules.set(Number(moduleId), { id: Number(moduleId), path: verboseName });
|
|
236
|
+
}
|
|
237
|
+
return originalD.call(this, factory, moduleId, dependencyMap, verboseName, ...rest);
|
|
238
|
+
};
|
|
239
|
+
Object.defineProperty(trackedD, '__inspectorTracked', { value: true, enumerable: false });
|
|
240
|
+
g.__d = trackedD;
|
|
86
241
|
}
|
|
87
242
|
}
|
|
88
|
-
catch {
|
|
89
|
-
|
|
243
|
+
catch { }
|
|
244
|
+
};
|
|
245
|
+
trackRuntimeModuleExecution();
|
|
246
|
+
trackRuntimeDefine();
|
|
247
|
+
const extractBundleModules = (bundleText) => {
|
|
248
|
+
const modules = [];
|
|
249
|
+
const seenIds = new Set();
|
|
250
|
+
if (bundleText && bundleText.length > 0) {
|
|
251
|
+
// Primary high-speed Metro tail regex: matches }, <id>, [<deps>], "<path>"
|
|
252
|
+
const tailRegex = /\}\s*,\s*(\d+)\s*,\s*\[([\d,\s]*)\]\s*,\s*(?:"([^"\r\n]+)"|'([^'\r\n]+)')/g;
|
|
253
|
+
let match;
|
|
254
|
+
while ((match = tailRegex.exec(bundleText)) !== null) {
|
|
255
|
+
const id = parseInt(match[1], 10);
|
|
256
|
+
const depsStr = match[2];
|
|
257
|
+
const path = match[3] || match[4] || '';
|
|
258
|
+
const deps = depsStr
|
|
259
|
+
? depsStr
|
|
260
|
+
.split(',')
|
|
261
|
+
.map(s => parseInt(s.trim(), 10))
|
|
262
|
+
.filter(n => !isNaN(n))
|
|
263
|
+
: [];
|
|
264
|
+
if (!seenIds.has(id) && path.length > 0) {
|
|
265
|
+
seenIds.add(id);
|
|
266
|
+
modules.push({ id, deps, path });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Secondary fallback if tailRegex didn't find modules
|
|
270
|
+
if (modules.length === 0) {
|
|
271
|
+
const defineRe = /__d\s*\(/g;
|
|
272
|
+
let m;
|
|
273
|
+
while ((m = defineRe.exec(bundleText)) !== null) {
|
|
274
|
+
const openParen = m.index + m[0].length - 1;
|
|
275
|
+
const args = scanCallArguments(bundleText, openParen);
|
|
276
|
+
if (!args || args.length < 4)
|
|
277
|
+
continue;
|
|
278
|
+
const idMatch = args[1].trim().match(/^(\d+)$/);
|
|
279
|
+
if (!idMatch)
|
|
280
|
+
continue;
|
|
281
|
+
const idNum = parseInt(idMatch[1], 10);
|
|
282
|
+
if (seenIds.has(idNum))
|
|
283
|
+
continue;
|
|
284
|
+
const depsMatch = args[2].match(/\[([^\]]*)\]/);
|
|
285
|
+
const deps = [];
|
|
286
|
+
if (depsMatch) {
|
|
287
|
+
const depRe = /\d+/g;
|
|
288
|
+
let d;
|
|
289
|
+
while ((d = depRe.exec(depsMatch[1])) !== null) {
|
|
290
|
+
deps.push(parseInt(d[0], 10));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const pathRaw = args[3].trim();
|
|
294
|
+
const pathMatch = pathRaw.match(/^["']([^"']+)["']$/);
|
|
295
|
+
const path = pathMatch ? pathMatch[1] : pathRaw;
|
|
296
|
+
if (path.length === 0 || path.length > 500)
|
|
297
|
+
continue;
|
|
298
|
+
seenIds.add(idNum);
|
|
299
|
+
modules.push({ id: idNum, deps, path });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Merge any runtime modules captured via global.__d
|
|
304
|
+
runtimeModules.forEach((item, id) => {
|
|
305
|
+
if (!seenIds.has(id)) {
|
|
306
|
+
seenIds.add(id);
|
|
307
|
+
modules.push({ id, deps: [], path: item.path });
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return modules;
|
|
311
|
+
};
|
|
312
|
+
const getStaticStartupModuleIds = (bundleText) => {
|
|
313
|
+
const ids = new Set();
|
|
314
|
+
const startupRe = /__r\s*\(\s*(\d+)\s*\)/g;
|
|
315
|
+
let m;
|
|
316
|
+
while ((m = startupRe.exec(bundleText)) !== null) {
|
|
317
|
+
ids.add(parseInt(m[1], 10));
|
|
318
|
+
}
|
|
319
|
+
return ids;
|
|
320
|
+
};
|
|
321
|
+
const findCommonDirectoryPrefix = (paths) => {
|
|
322
|
+
const absPaths = paths.filter(p => p.startsWith('/'));
|
|
323
|
+
if (absPaths.length === 0)
|
|
324
|
+
return '';
|
|
325
|
+
if (absPaths.length === 1) {
|
|
326
|
+
const lastSlash = absPaths[0].lastIndexOf('/');
|
|
327
|
+
return lastSlash > 0 ? absPaths[0].slice(0, lastSlash + 1) : '';
|
|
328
|
+
}
|
|
329
|
+
const splitPaths = absPaths.map(p => p.split('/'));
|
|
330
|
+
let commonIndex = 0;
|
|
331
|
+
const first = splitPaths[0];
|
|
332
|
+
while (commonIndex < first.length - 1) {
|
|
333
|
+
const segment = first[commonIndex];
|
|
334
|
+
if (splitPaths.every(p => p[commonIndex] === segment)) {
|
|
335
|
+
commonIndex++;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (commonIndex === 0)
|
|
342
|
+
return '';
|
|
343
|
+
return first.slice(0, commonIndex).join('/') + '/';
|
|
344
|
+
};
|
|
345
|
+
const isExternalPackage = (rawPath) => {
|
|
346
|
+
// 1. Standard node_modules
|
|
347
|
+
if (rawPath.includes('node_modules/')) {
|
|
348
|
+
const pkgMatch = rawPath.match(/node_modules\/(?:@([^/]+)\/([^/]+)|([^/]+))/);
|
|
349
|
+
if (pkgMatch) {
|
|
350
|
+
const pkgName = pkgMatch[1] && pkgMatch[2] ? `@${pkgMatch[1]}/${pkgMatch[2]}` : pkgMatch[3];
|
|
351
|
+
return { isPkg: true, pkgName };
|
|
352
|
+
}
|
|
353
|
+
return { isPkg: true };
|
|
354
|
+
}
|
|
355
|
+
// 2. React Native In-App Inspector (when linked or embedded)
|
|
356
|
+
if (rawPath.includes('react-native-inapp-inspector')) {
|
|
357
|
+
return { isPkg: true, pkgName: 'react-native-inapp-inspector' };
|
|
358
|
+
}
|
|
359
|
+
// 3. Yarn berry / pnpm virtual stores
|
|
360
|
+
if (rawPath.includes('.yarn/') || rawPath.includes('.pnpm/')) {
|
|
361
|
+
return { isPkg: true };
|
|
362
|
+
}
|
|
363
|
+
// 4. Compiled package dist / build folders outside project source
|
|
364
|
+
if (rawPath.includes('/dist/esm/') ||
|
|
365
|
+
rawPath.includes('/dist/commonjs/') ||
|
|
366
|
+
rawPath.includes('/dist/cjs/') ||
|
|
367
|
+
rawPath.startsWith('dist/esm/') ||
|
|
368
|
+
rawPath.startsWith('dist/commonjs/') ||
|
|
369
|
+
rawPath.startsWith('dist/') ||
|
|
370
|
+
rawPath.includes('/react-native-inapp-inspector/dist/')) {
|
|
371
|
+
return { isPkg: true, pkgName: 'react-native-inapp-inspector' };
|
|
90
372
|
}
|
|
373
|
+
return { isPkg: false };
|
|
374
|
+
};
|
|
375
|
+
const isInternalModule = (rawPath) => {
|
|
376
|
+
return (rawPath.startsWith('<<') ||
|
|
377
|
+
rawPath.startsWith('[metro]') ||
|
|
378
|
+
rawPath.includes('metro-runtime') ||
|
|
379
|
+
rawPath.includes('polyfills/require.js') ||
|
|
380
|
+
rawPath.includes('prelude_commonjs') ||
|
|
381
|
+
rawPath.includes('Libraries/Core/InitializeCore.js') ||
|
|
382
|
+
rawPath.includes('@babel/runtime'));
|
|
91
383
|
};
|
|
92
384
|
/**
|
|
93
385
|
* Parses raw Metro bundle source code to discover real modules and packages in the host app.
|
|
@@ -96,79 +388,156 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
96
388
|
const isHermes = Boolean(globalThis.HermesInternal);
|
|
97
389
|
const totalDevMb = Number((totalBytes / (1024 * 1024)).toFixed(2));
|
|
98
390
|
const totalDevKb = Math.round(totalBytes / 1024);
|
|
391
|
+
const modules = extractBundleModules(bundleText);
|
|
392
|
+
const startupIds = getStaticStartupModuleIds(bundleText);
|
|
393
|
+
// Build module map for dependency graph traversal
|
|
394
|
+
const modMap = new Map();
|
|
395
|
+
for (const mod of modules) {
|
|
396
|
+
modMap.set(mod.id, mod);
|
|
397
|
+
}
|
|
398
|
+
// Transitive Reachability: traverse the dependency graph starting from
|
|
399
|
+
// entrypoints (index.js, App.tsx, app.json, startupIds, executedModuleIds)
|
|
400
|
+
const consumedIds = new Set();
|
|
401
|
+
const queue = [];
|
|
402
|
+
const markConsumed = (id) => {
|
|
403
|
+
if (!consumedIds.has(id)) {
|
|
404
|
+
consumedIds.add(id);
|
|
405
|
+
queue.push(id);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
// Seed with root entrypoints and runtime executions
|
|
409
|
+
markConsumed(0);
|
|
410
|
+
startupIds.forEach(id => markConsumed(id));
|
|
411
|
+
executedModuleIds.forEach(id => markConsumed(id));
|
|
412
|
+
for (const mod of modules) {
|
|
413
|
+
const lower = mod.path.toLowerCase().replace(/\\/g, '/');
|
|
414
|
+
if (lower.endsWith('/index.js') ||
|
|
415
|
+
lower.endsWith('/index.ts') ||
|
|
416
|
+
lower.endsWith('/index.tsx') ||
|
|
417
|
+
lower === 'index.js' ||
|
|
418
|
+
lower.endsWith('/app.tsx') ||
|
|
419
|
+
lower.endsWith('/app.js') ||
|
|
420
|
+
lower.endsWith('/app.jsx') ||
|
|
421
|
+
lower === 'app.tsx' ||
|
|
422
|
+
lower === 'app.js' ||
|
|
423
|
+
lower.endsWith('app.json')) {
|
|
424
|
+
markConsumed(mod.id);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Transitive BFS dependency traversal
|
|
428
|
+
while (queue.length > 0) {
|
|
429
|
+
const currId = queue.shift();
|
|
430
|
+
const currMod = modMap.get(currId);
|
|
431
|
+
if (currMod && currMod.deps) {
|
|
432
|
+
for (const depId of currMod.deps) {
|
|
433
|
+
markConsumed(depId);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
99
437
|
const discoveredPackagesMap = new Map();
|
|
438
|
+
const rawProjectItems = [];
|
|
439
|
+
for (const mod of modules) {
|
|
440
|
+
const rawPath = mod.path.replace(/\\/g, '/'); // Normalize Windows backslashes
|
|
441
|
+
const { isPkg, pkgName } = isExternalPackage(rawPath);
|
|
442
|
+
if (isPkg) {
|
|
443
|
+
if (pkgName && !pkgName.startsWith('.')) {
|
|
444
|
+
discoveredPackagesMap.set(pkgName, (discoveredPackagesMap.get(pkgName) || 0) + 1);
|
|
445
|
+
}
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
// Filter out internal Metro polyfills / virtual modules
|
|
449
|
+
if (isInternalModule(rawPath)) {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
rawProjectItems.push({ mod, rawPath });
|
|
453
|
+
}
|
|
454
|
+
// Calculate common prefix for absolute paths to cleanly extract host app project files
|
|
455
|
+
const commonPrefix = findCommonDirectoryPrefix(rawProjectItems.map(item => item.rawPath));
|
|
100
456
|
const discoveredFiles = [];
|
|
101
|
-
// Match module declarations: __d(function(...), id, [...], "path/to/module.js") or comments
|
|
102
|
-
// In Metro: __d(function(...), 42, [1, 2], "node_modules/lodash/index.js")
|
|
103
|
-
const modulePathRegex = /(?:__d\s*\([^,]+,[^,]+,[^,]+,["']([^"']+)["']|\/\/\s*@metro-module-path\s+([^\n\r]+)|["']((?:node_modules|\.|\/|[a-zA-Z0-9_-]+)\/[^"']+\.[a-zA-Z0-9]+)["'])/g;
|
|
104
|
-
let match;
|
|
105
457
|
let fileIdx = 0;
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
458
|
+
for (const { mod, rawPath } of rawProjectItems) {
|
|
459
|
+
let cleanPath = rawPath;
|
|
460
|
+
if (commonPrefix && cleanPath.startsWith(commonPrefix)) {
|
|
461
|
+
cleanPath = cleanPath.slice(commonPrefix.length);
|
|
462
|
+
}
|
|
463
|
+
// Clean leading ../ sequences, ./, and leading /
|
|
464
|
+
cleanPath = cleanPath.replace(/^(\.\.\/)+/, '').replace(/^\.\//, '').replace(/^\/+/, '');
|
|
465
|
+
if (!cleanPath)
|
|
466
|
+
cleanPath = rawPath.split('/').pop() || rawPath;
|
|
467
|
+
const isLocalFile = cleanPath.endsWith('.tsx') ||
|
|
468
|
+
cleanPath.endsWith('.ts') ||
|
|
469
|
+
cleanPath.endsWith('.jsx') ||
|
|
470
|
+
cleanPath.endsWith('.js') ||
|
|
471
|
+
cleanPath.endsWith('.mjs') ||
|
|
472
|
+
cleanPath.endsWith('.cjs') ||
|
|
473
|
+
cleanPath.endsWith('.json') ||
|
|
474
|
+
cleanPath.endsWith('.png') ||
|
|
475
|
+
cleanPath.endsWith('.jpg') ||
|
|
476
|
+
cleanPath.endsWith('.jpeg') ||
|
|
477
|
+
cleanPath.endsWith('.gif') ||
|
|
478
|
+
cleanPath.endsWith('.webp') ||
|
|
479
|
+
cleanPath.endsWith('.svg') ||
|
|
480
|
+
cleanPath.endsWith('.bmp') ||
|
|
481
|
+
cleanPath.endsWith('.ico') ||
|
|
482
|
+
cleanPath.endsWith('.ttf') ||
|
|
483
|
+
cleanPath.endsWith('.otf') ||
|
|
484
|
+
cleanPath.endsWith('.woff') ||
|
|
485
|
+
cleanPath.endsWith('.woff2') ||
|
|
486
|
+
cleanPath.endsWith('.css') ||
|
|
487
|
+
cleanPath.endsWith('.scss') ||
|
|
488
|
+
cleanPath.endsWith('.html') ||
|
|
489
|
+
cleanPath.endsWith('.md') ||
|
|
490
|
+
cleanPath.endsWith('.graphql') ||
|
|
491
|
+
cleanPath.endsWith('.gql');
|
|
492
|
+
if (!isLocalFile)
|
|
111
493
|
continue;
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
discoveredPackagesMap.set(pkgName, (discoveredPackagesMap.get(pkgName) || 0) + 1);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
494
|
+
const ext = cleanPath.split('.').pop()?.toUpperCase() || 'JS';
|
|
495
|
+
const name = cleanPath.split('/').pop() || cleanPath;
|
|
496
|
+
let category = 'javascript';
|
|
497
|
+
let color = AppColors.indigo500;
|
|
498
|
+
if (ext === 'TSX' || ext === 'TS') {
|
|
499
|
+
category = 'typescript';
|
|
500
|
+
color = AppColors.sky500;
|
|
122
501
|
}
|
|
123
|
-
else if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
category = 'image';
|
|
142
|
-
color = AppColors.pink500;
|
|
143
|
-
}
|
|
144
|
-
else if (ext === 'TTF' || ext === 'OTF') {
|
|
145
|
-
category = 'font';
|
|
146
|
-
color = AppColors.purple500;
|
|
147
|
-
}
|
|
148
|
-
else if (ext === 'JSON') {
|
|
149
|
-
category = 'json';
|
|
150
|
-
color = AppColors.emerald500;
|
|
151
|
-
}
|
|
152
|
-
// Approximate module size from total and count
|
|
153
|
-
const approxKb = Math.max(2, Math.round((totalDevKb * 0.15) / Math.max(seenPaths.size, 20)));
|
|
154
|
-
discoveredFiles.push({
|
|
155
|
-
id: `host-file-${fileIdx++}`,
|
|
156
|
-
name,
|
|
157
|
-
path: rawPath,
|
|
158
|
-
ext,
|
|
159
|
-
category,
|
|
160
|
-
sizeKb: approxKb,
|
|
161
|
-
meta: `Active Host App Module • ${category.toUpperCase()}`,
|
|
162
|
-
color,
|
|
163
|
-
status: 'optimal',
|
|
164
|
-
advice: 'Bundled into Host App Development Runtime',
|
|
165
|
-
isConsumed: true,
|
|
166
|
-
});
|
|
502
|
+
else if (ext === 'PNG' ||
|
|
503
|
+
ext === 'JPG' ||
|
|
504
|
+
ext === 'JPEG' ||
|
|
505
|
+
ext === 'GIF' ||
|
|
506
|
+
ext === 'WEBP' ||
|
|
507
|
+
ext === 'SVG' ||
|
|
508
|
+
ext === 'BMP' ||
|
|
509
|
+
ext === 'ICO') {
|
|
510
|
+
category = 'image';
|
|
511
|
+
color = AppColors.pink500;
|
|
512
|
+
}
|
|
513
|
+
else if (ext === 'TTF' || ext === 'OTF' || ext === 'WOFF' || ext === 'WOFF2') {
|
|
514
|
+
category = 'font';
|
|
515
|
+
color = AppColors.purple500;
|
|
516
|
+
}
|
|
517
|
+
else if (ext === 'JSON' || ext === 'CSS' || ext === 'SCSS' || ext === 'HTML' || ext === 'MD' || ext === 'GRAPHQL' || ext === 'GQL') {
|
|
518
|
+
category = 'json';
|
|
519
|
+
color = AppColors.emerald500;
|
|
167
520
|
}
|
|
168
|
-
|
|
169
|
-
|
|
521
|
+
// Approximate module size from total and module count
|
|
522
|
+
const approxKb = Math.max(2, Math.round((totalDevKb * 0.15) / Math.max(modules.length, 20)));
|
|
523
|
+
const isConsumed = consumedIds.has(mod.id);
|
|
524
|
+
discoveredFiles.push({
|
|
525
|
+
id: `host-file-${fileIdx++}`,
|
|
526
|
+
name,
|
|
527
|
+
path: cleanPath,
|
|
528
|
+
ext,
|
|
529
|
+
category,
|
|
530
|
+
sizeKb: approxKb,
|
|
531
|
+
meta: `Active Host App Module • ${category.toUpperCase()}`,
|
|
532
|
+
color,
|
|
533
|
+
status: isConsumed ? 'optimal' : 'warning',
|
|
534
|
+
advice: isConsumed
|
|
535
|
+
? 'In-Use: Bundled and active in host application dependency tree'
|
|
536
|
+
: 'Not consumed: Defined in the bundle but not referenced in active execution tree',
|
|
537
|
+
isConsumed,
|
|
538
|
+
});
|
|
170
539
|
}
|
|
171
|
-
// If no
|
|
540
|
+
// If no modules could be parsed (e.g. Hermes bytecode / obfuscated bundle), synthesize from loaded modules
|
|
172
541
|
if (discoveredFiles.length === 0) {
|
|
173
542
|
discoveredFiles.push({
|
|
174
543
|
id: 'hf-1',
|
|
@@ -357,7 +726,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
357
726
|
const iosComponents = [
|
|
358
727
|
{
|
|
359
728
|
id: 'ios-c1',
|
|
360
|
-
name: '
|
|
729
|
+
name: 'Frameworks & Dynamic Pods',
|
|
361
730
|
category: 'frameworks',
|
|
362
731
|
sizeMb: nativeFrameworksMb,
|
|
363
732
|
pct: Number(((nativeFrameworksMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -367,7 +736,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
367
736
|
},
|
|
368
737
|
{
|
|
369
738
|
id: 'ios-c2',
|
|
370
|
-
name: '
|
|
739
|
+
name: 'Mach-O Executable (ARM64)',
|
|
371
740
|
category: 'native',
|
|
372
741
|
sizeMb: nativeMachoMb,
|
|
373
742
|
pct: Number(((nativeMachoMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -377,7 +746,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
377
746
|
},
|
|
378
747
|
{
|
|
379
748
|
id: 'ios-c3',
|
|
380
|
-
name: 'Asset Catalog (Assets.car
|
|
749
|
+
name: 'Asset Catalog (Assets.car)',
|
|
381
750
|
category: 'assets',
|
|
382
751
|
sizeMb: assetsCatalogMb,
|
|
383
752
|
pct: Number(((assetsCatalogMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -387,7 +756,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
387
756
|
},
|
|
388
757
|
{
|
|
389
758
|
id: 'ios-c4',
|
|
390
|
-
name: 'Hermes Bytecode (main.jsbundle
|
|
759
|
+
name: 'Hermes Bytecode (main.jsbundle)',
|
|
391
760
|
category: 'js',
|
|
392
761
|
sizeMb: releaseJsMb,
|
|
393
762
|
pct: Number(((releaseJsMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -397,7 +766,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
397
766
|
},
|
|
398
767
|
{
|
|
399
768
|
id: 'ios-c5',
|
|
400
|
-
name: '
|
|
769
|
+
name: 'Metadata & Code Signatures',
|
|
401
770
|
category: 'meta',
|
|
402
771
|
sizeMb: metadataMb,
|
|
403
772
|
pct: Number(((metadataMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -409,7 +778,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
409
778
|
const androidComponents = [
|
|
410
779
|
{
|
|
411
780
|
id: 'and-c1',
|
|
412
|
-
name: 'Native C++
|
|
781
|
+
name: 'Native C++ Libraries (.so)',
|
|
413
782
|
category: 'native',
|
|
414
783
|
sizeMb: androidCppMb,
|
|
415
784
|
pct: Number(((androidCppMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -419,7 +788,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
419
788
|
},
|
|
420
789
|
{
|
|
421
790
|
id: 'and-c2',
|
|
422
|
-
name: 'Compiled DEX
|
|
791
|
+
name: 'Compiled DEX (classes.dex)',
|
|
423
792
|
category: 'frameworks',
|
|
424
793
|
sizeMb: androidDexMb,
|
|
425
794
|
pct: Number(((androidDexMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -429,7 +798,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
429
798
|
},
|
|
430
799
|
{
|
|
431
800
|
id: 'and-c3',
|
|
432
|
-
name: 'Android Resources
|
|
801
|
+
name: 'Android Resources (res/)',
|
|
433
802
|
category: 'assets',
|
|
434
803
|
sizeMb: androidResMb,
|
|
435
804
|
pct: Number(((androidResMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -439,7 +808,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
439
808
|
},
|
|
440
809
|
{
|
|
441
810
|
id: 'and-c4',
|
|
442
|
-
name: 'Hermes Bytecode (
|
|
811
|
+
name: 'Hermes Bytecode (Android)',
|
|
443
812
|
category: 'js',
|
|
444
813
|
sizeMb: releaseJsMb,
|
|
445
814
|
pct: Number(((releaseJsMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -449,7 +818,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
449
818
|
},
|
|
450
819
|
{
|
|
451
820
|
id: 'and-c5',
|
|
452
|
-
name: '
|
|
821
|
+
name: 'Manifest & Signatures (META-INF)',
|
|
453
822
|
category: 'meta',
|
|
454
823
|
sizeMb: 2.6,
|
|
455
824
|
pct: Number(((2.6 / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -465,7 +834,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
465
834
|
const androidApkComponents = [
|
|
466
835
|
{
|
|
467
836
|
id: 'apk-c1',
|
|
468
|
-
name: 'Multi-ABI
|
|
837
|
+
name: 'Multi-ABI C++ Libraries (.so)',
|
|
469
838
|
category: 'native',
|
|
470
839
|
sizeMb: androidMultiAbiCppMb,
|
|
471
840
|
pct: Number(((androidMultiAbiCppMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -485,7 +854,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
485
854
|
},
|
|
486
855
|
{
|
|
487
856
|
id: 'apk-c3',
|
|
488
|
-
name: 'Android Resources & Assets (res
|
|
857
|
+
name: 'Android Resources & Assets (res/)',
|
|
489
858
|
category: 'assets',
|
|
490
859
|
sizeMb: androidResMb,
|
|
491
860
|
pct: Number(((androidResMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -495,7 +864,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
495
864
|
},
|
|
496
865
|
{
|
|
497
866
|
id: 'apk-c4',
|
|
498
|
-
name: 'Hermes Bytecode Bundle (
|
|
867
|
+
name: 'Hermes Bytecode Bundle (Android)',
|
|
499
868
|
category: 'js',
|
|
500
869
|
sizeMb: releaseJsMb,
|
|
501
870
|
pct: Number(((releaseJsMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -505,7 +874,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
505
874
|
},
|
|
506
875
|
{
|
|
507
876
|
id: 'apk-c5',
|
|
508
|
-
name: '
|
|
877
|
+
name: 'Manifest & Signatures (META-INF)',
|
|
509
878
|
category: 'meta',
|
|
510
879
|
sizeMb: 3.2,
|
|
511
880
|
pct: Number(((3.2 / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -521,7 +890,7 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
521
890
|
totalDevMb,
|
|
522
891
|
totalDevKb,
|
|
523
892
|
isHermes,
|
|
524
|
-
moduleCount:
|
|
893
|
+
moduleCount: modules.length || discoveredFiles.length + packagesList.length,
|
|
525
894
|
packageCount: packagesList.length,
|
|
526
895
|
filesCount: discoveredFiles.length,
|
|
527
896
|
splitUp: {
|
|
@@ -578,9 +947,13 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
|
|
|
578
947
|
};
|
|
579
948
|
/**
|
|
580
949
|
* Automatically fetch and analyze the real running bundle for the host app.
|
|
950
|
+
* Supports dynamic port detection (8081, 8082, 8083, etc.) and parallel dev server probing.
|
|
581
951
|
*/
|
|
582
|
-
export const analyzeHostAppBundle = async () => {
|
|
583
|
-
if (
|
|
952
|
+
export const analyzeHostAppBundle = async (forceRefresh = false) => {
|
|
953
|
+
if (forceRefresh) {
|
|
954
|
+
cachedAnalysis = null;
|
|
955
|
+
}
|
|
956
|
+
if (cachedAnalysis && !forceRefresh)
|
|
584
957
|
return cachedAnalysis;
|
|
585
958
|
if (isAnalyzing) {
|
|
586
959
|
return new Promise(resolve => {
|
|
@@ -589,19 +962,11 @@ export const analyzeHostAppBundle = async () => {
|
|
|
589
962
|
}
|
|
590
963
|
isAnalyzing = true;
|
|
591
964
|
const scriptURL = getHostScriptURL();
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
if (probe.ok && probe.text && probe.text.length > 0) {
|
|
598
|
-
fetched = { text: probe.text, bytes: probe.bytes ?? probe.text.length };
|
|
599
|
-
fetchedUrl = url;
|
|
600
|
-
break;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
if (fetched) {
|
|
604
|
-
const result = parseBundleSource(fetched.text, fetched.bytes, fetchedUrl);
|
|
965
|
+
const probeUrls = buildProbeUrls(scriptURL);
|
|
966
|
+
// Probe all candidate dev-server ports and hosts in parallel for instant response
|
|
967
|
+
const fetched = await probeCandidateUrlsInParallel(probeUrls, 3500);
|
|
968
|
+
if (fetched && fetched.text && fetched.text.length > 0) {
|
|
969
|
+
const result = parseBundleSource(fetched.text, fetched.bytes, fetched.url);
|
|
605
970
|
cachedAnalysis = result;
|
|
606
971
|
isAnalyzing = false;
|
|
607
972
|
subscribers.forEach(cb => cb(result));
|
|
@@ -618,4 +983,7 @@ export const analyzeHostAppBundle = async () => {
|
|
|
618
983
|
subscribers.length = 0;
|
|
619
984
|
return result;
|
|
620
985
|
};
|
|
986
|
+
export const clearCachedBundleAnalysis = () => {
|
|
987
|
+
cachedAnalysis = null;
|
|
988
|
+
};
|
|
621
989
|
export const getCachedBundleAnalysis = () => cachedAnalysis;
|