react-native-inapp-inspector 1.1.26 → 1.1.28

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.
Files changed (67) hide show
  1. package/android/build.gradle +1 -0
  2. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +34 -0
  3. package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
  4. package/dist/commonjs/components/ErrorBoundary.js +530 -100
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +176 -126
  6. package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -0
  7. package/dist/commonjs/components/Inspector/BundleTab.js +145 -21
  8. package/dist/commonjs/components/Inspector/CrashTab.js +0 -8
  9. package/dist/commonjs/components/Inspector/NetworkDetail.js +41 -0
  10. package/dist/commonjs/components/Inspector/PerformanceTab.d.ts +5 -0
  11. package/dist/commonjs/components/Inspector/PerformanceTab.js +588 -155
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +57 -22
  13. package/dist/commonjs/components/Inspector/TabBar.js +13 -1
  14. package/dist/commonjs/components/LogCard.js +75 -34
  15. package/dist/commonjs/components/NetworkIcons.d.ts +112 -102
  16. package/dist/commonjs/constants/version.d.ts +1 -1
  17. package/dist/commonjs/constants/version.js +1 -1
  18. package/dist/commonjs/customHooks/analyticsLogger.d.ts +1 -11
  19. package/dist/commonjs/customHooks/analyticsLogger.js +8 -1
  20. package/dist/commonjs/customHooks/bundleAnalyzer.js +121 -124
  21. package/dist/commonjs/customHooks/networkLogger.d.ts +1 -0
  22. package/dist/commonjs/customHooks/networkLogger.js +16 -0
  23. package/dist/commonjs/customHooks/performanceTracker.d.ts +74 -0
  24. package/dist/commonjs/customHooks/performanceTracker.js +546 -392
  25. package/dist/commonjs/customHooks/reduxLogger.d.ts +1 -0
  26. package/dist/commonjs/customHooks/reduxLogger.js +3 -1
  27. package/dist/commonjs/helpers/index.js +27 -3
  28. package/dist/commonjs/i18n/locales/en.json +225 -4
  29. package/dist/commonjs/index.d.ts +1 -1
  30. package/dist/commonjs/index.js +36 -2
  31. package/dist/commonjs/styles/index.d.ts +3 -1
  32. package/dist/commonjs/styles/index.js +8 -6
  33. package/dist/commonjs/types/interfaces.d.ts +3 -0
  34. package/dist/esm/components/ErrorBoundary.d.ts +1 -1
  35. package/dist/esm/components/ErrorBoundary.js +498 -101
  36. package/dist/esm/components/Inspector/AnalyticsTab.js +177 -127
  37. package/dist/esm/components/Inspector/BundleTab.d.ts +1 -0
  38. package/dist/esm/components/Inspector/BundleTab.js +145 -23
  39. package/dist/esm/components/Inspector/CrashTab.js +0 -8
  40. package/dist/esm/components/Inspector/NetworkDetail.js +41 -0
  41. package/dist/esm/components/Inspector/PerformanceTab.d.ts +5 -0
  42. package/dist/esm/components/Inspector/PerformanceTab.js +591 -158
  43. package/dist/esm/components/Inspector/SettingsPanel.js +57 -22
  44. package/dist/esm/components/Inspector/TabBar.js +13 -1
  45. package/dist/esm/components/LogCard.js +75 -34
  46. package/dist/esm/components/NetworkIcons.d.ts +112 -102
  47. package/dist/esm/constants/version.d.ts +1 -1
  48. package/dist/esm/constants/version.js +1 -1
  49. package/dist/esm/customHooks/analyticsLogger.d.ts +1 -11
  50. package/dist/esm/customHooks/analyticsLogger.js +6 -0
  51. package/dist/esm/customHooks/bundleAnalyzer.js +121 -124
  52. package/dist/esm/customHooks/networkLogger.d.ts +1 -0
  53. package/dist/esm/customHooks/networkLogger.js +16 -0
  54. package/dist/esm/customHooks/performanceTracker.d.ts +74 -0
  55. package/dist/esm/customHooks/performanceTracker.js +531 -391
  56. package/dist/esm/customHooks/reduxLogger.d.ts +1 -0
  57. package/dist/esm/customHooks/reduxLogger.js +1 -0
  58. package/dist/esm/helpers/index.js +27 -3
  59. package/dist/esm/i18n/locales/en.json +225 -4
  60. package/dist/esm/index.d.ts +1 -1
  61. package/dist/esm/index.js +24 -3
  62. package/dist/esm/styles/index.d.ts +3 -1
  63. package/dist/esm/styles/index.js +8 -6
  64. package/dist/esm/types/interfaces.d.ts +3 -0
  65. package/ios/NetworkInspectorModule.h +2 -0
  66. package/ios/NetworkInspectorModule.m +10 -0
  67. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { AppColors } from '../styles/AppColors';
2
+ import { t } from '../i18n';
2
3
  // ─── Real Host App Bundle Analyzer ──────────────────────────────────────────
3
4
  //
4
5
  // Dynamically measures and analyzes the real JavaScript bundle and packages
@@ -248,25 +249,32 @@ const extractBundleModules = (bundleText) => {
248
249
  const modules = [];
249
250
  const seenIds = new Set();
250
251
  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;
252
+ // 1. Primary Metro regex: matches }, <id>, [<deps>] or {<deps>}, "<path>"
253
+ const tailRegex = /\}\s*,\s*(\d+)\s*,\s*(?:\[[\s\S]*?\]|\{[\s\S]*?\})\s*,\s*(?:"([^"\r\n]+)"|'([^'\r\n]+)')/g;
253
254
  let match;
254
255
  while ((match = tailRegex.exec(bundleText)) !== null) {
255
256
  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
- : [];
257
+ const path = match[2] || match[3] || '';
264
258
  if (!seenIds.has(id) && path.length > 0) {
265
259
  seenIds.add(id);
266
- modules.push({ id, deps, path });
260
+ modules.push({ id, deps: [], path });
267
261
  }
268
262
  }
269
- // Secondary fallback if tailRegex didn't find modules
263
+ // 2. SourceURL comment extraction (Metro dev server embeds //# sourceURL=... for every module)
264
+ const sourceUrlRegex = /\/\/[#@]\s*sourceURL=(?:https?:\/\/[^/\n\r]+\/|file:\/\/)?([^?\r\n#\s]+)/g;
265
+ let srcMatch;
266
+ let autoId = 900000;
267
+ while ((srcMatch = sourceUrlRegex.exec(bundleText)) !== null) {
268
+ const path = srcMatch[1];
269
+ if (path && path.length > 0 && !path.endsWith('.bundle')) {
270
+ const id = autoId++;
271
+ if (!seenIds.has(id)) {
272
+ seenIds.add(id);
273
+ modules.push({ id, deps: [], path });
274
+ }
275
+ }
276
+ }
277
+ // 3. Fallback scan for __d calls
270
278
  if (modules.length === 0) {
271
279
  const defineRe = /__d\s*\(/g;
272
280
  let m;
@@ -281,22 +289,13 @@ const extractBundleModules = (bundleText) => {
281
289
  const idNum = parseInt(idMatch[1], 10);
282
290
  if (seenIds.has(idNum))
283
291
  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
292
  const pathRaw = args[3].trim();
294
293
  const pathMatch = pathRaw.match(/^["']([^"']+)["']$/);
295
294
  const path = pathMatch ? pathMatch[1] : pathRaw;
296
295
  if (path.length === 0 || path.length > 500)
297
296
  continue;
298
297
  seenIds.add(idNum);
299
- modules.push({ id: idNum, deps, path });
298
+ modules.push({ id: idNum, deps: [], path });
300
299
  }
301
300
  }
302
301
  }
@@ -318,8 +317,28 @@ const getStaticStartupModuleIds = (bundleText) => {
318
317
  }
319
318
  return ids;
320
319
  };
321
- const findCommonDirectoryPrefix = (paths) => {
322
- const absPaths = paths.filter(p => p.startsWith('/'));
320
+ /**
321
+ * Dynamically computes the host app's project root directory without hardcoding any folder names.
322
+ * Uses node_modules boundary, root entry points (index/App/package.json), and common directory analysis.
323
+ */
324
+ const detectProjectRoot = (allRawPaths) => {
325
+ const normPaths = allRawPaths.map(p => p.replace(/\\/g, '/'));
326
+ // 1. Extract from node_modules path (most definitive in Metro dev bundles)
327
+ for (const norm of normPaths) {
328
+ const nmIdx = norm.indexOf('/node_modules/');
329
+ if (nmIdx !== -1) {
330
+ return norm.slice(0, nmIdx + 1); // e.g. "/Users/user/Projects/my-app/"
331
+ }
332
+ }
333
+ // 2. Extract from root entrypoint files (e.g. /path/to/project/index.js or App.tsx)
334
+ for (const norm of normPaths) {
335
+ const match = norm.match(/^(.*\/)(?:index\.[jt]sx?|app\.[jt]sx?|package\.json)(?:[?#].*)?$/i);
336
+ if (match && match[1]) {
337
+ return match[1];
338
+ }
339
+ }
340
+ // 3. Fallback: lowest common directory ancestor among all absolute project paths
341
+ const absPaths = normPaths.filter(p => p.startsWith('/') && !p.includes('/node_modules/'));
323
342
  if (absPaths.length === 0)
324
343
  return '';
325
344
  if (absPaths.length === 1) {
@@ -327,20 +346,42 @@ const findCommonDirectoryPrefix = (paths) => {
327
346
  return lastSlash > 0 ? absPaths[0].slice(0, lastSlash + 1) : '';
328
347
  }
329
348
  const splitPaths = absPaths.map(p => p.split('/'));
330
- let commonIndex = 0;
349
+ let commonLen = 0;
331
350
  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++;
351
+ const minLen = Math.min(...splitPaths.map(sp => sp.length));
352
+ while (commonLen < minLen - 1) {
353
+ const seg = first[commonLen];
354
+ if (splitPaths.every(sp => sp[commonLen] === seg)) {
355
+ commonLen++;
336
356
  }
337
357
  else {
338
358
  break;
339
359
  }
340
360
  }
341
- if (commonIndex === 0)
342
- return '';
343
- return first.slice(0, commonIndex).join('/') + '/';
361
+ return commonLen > 0 ? first.slice(0, commonLen).join('/') + '/' : '';
362
+ };
363
+ /**
364
+ * Normalizes absolute or relative module paths so they retain their full
365
+ * folder hierarchy from the host project root dynamically.
366
+ */
367
+ const normalizeProjectRelativePath = (rawPath, projectRoot) => {
368
+ let clean = rawPath.replace(/\\/g, '/');
369
+ // Strip query parameters e.g. ?platform=ios or hashes
370
+ const qIdx = clean.search(/[?#]/);
371
+ if (qIdx !== -1) {
372
+ clean = clean.slice(0, qIdx);
373
+ }
374
+ // Strip file:// or http://.../ dev server URL prefixes
375
+ clean = clean.replace(/^(?:https?:\/\/[^/]+\/|file:\/\/)/, '');
376
+ // Strip dynamically detected project root prefix
377
+ if (projectRoot && clean.startsWith(projectRoot)) {
378
+ clean = clean.slice(projectRoot.length);
379
+ }
380
+ // Clean leading ../ sequences, ./, and leading /
381
+ clean = clean.replace(/^(\.\.\/)+/, '').replace(/^\.\//, '').replace(/^\/+/, '');
382
+ if (!clean)
383
+ clean = rawPath.split('/').pop() || rawPath;
384
+ return clean;
344
385
  };
345
386
  const isExternalPackage = (rawPath) => {
346
387
  // 1. Standard node_modules
@@ -451,19 +492,12 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
451
492
  }
452
493
  rawProjectItems.push({ mod, rawPath });
453
494
  }
454
- // Calculate common prefix for absolute paths to cleanly extract host app project files
455
- const commonPrefix = findCommonDirectoryPrefix(rawProjectItems.map(item => item.rawPath));
495
+ // Dynamically calculate project root prefix to cleanly extract all host app folders & files
496
+ const projectRoot = detectProjectRoot(modules.map(item => item.path));
456
497
  const discoveredFiles = [];
457
498
  let fileIdx = 0;
458
499
  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;
500
+ const cleanPath = normalizeProjectRelativePath(rawPath, projectRoot);
467
501
  const isLocalFile = cleanPath.endsWith('.tsx') ||
468
502
  cleanPath.endsWith('.ts') ||
469
503
  cleanPath.endsWith('.jsx') ||
@@ -537,43 +571,6 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
537
571
  isConsumed,
538
572
  });
539
573
  }
540
- // If no modules could be parsed (e.g. Hermes bytecode / obfuscated bundle), synthesize from loaded modules
541
- if (discoveredFiles.length === 0) {
542
- discoveredFiles.push({
543
- id: 'hf-1',
544
- name: 'index.js (App Entry)',
545
- path: 'index.js',
546
- ext: 'JS',
547
- category: 'javascript',
548
- sizeKb: Math.round(totalDevKb * 0.08),
549
- meta: 'React Native Root Entrypoint',
550
- color: AppColors.indigo500,
551
- status: 'optimal',
552
- isConsumed: true,
553
- }, {
554
- id: 'hf-2',
555
- name: 'App.tsx',
556
- path: 'src/App.tsx',
557
- ext: 'TSX',
558
- category: 'typescript',
559
- sizeKb: Math.round(totalDevKb * 0.12),
560
- meta: 'Root Navigation & Provider Container',
561
- color: AppColors.sky500,
562
- status: 'optimal',
563
- isConsumed: true,
564
- }, {
565
- id: 'hf-3',
566
- name: 'HomeScreen.tsx',
567
- path: 'src/screens/HomeScreen.tsx',
568
- ext: 'TSX',
569
- category: 'typescript',
570
- sizeKb: Math.round(totalDevKb * 0.06),
571
- meta: 'Main Dashboard Screen View',
572
- color: AppColors.sky500,
573
- status: 'optimal',
574
- isConsumed: true,
575
- });
576
- }
577
574
  // Dynamic color palette generator based on package name hash
578
575
  const getPackageColor = (name) => {
579
576
  const colors = [
@@ -726,105 +723,105 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
726
723
  const iosComponents = [
727
724
  {
728
725
  id: 'ios-c1',
729
- name: 'Frameworks & Dynamic Pods',
726
+ name: t('bundle.iosComp1Name'),
730
727
  category: 'frameworks',
731
728
  sizeMb: nativeFrameworksMb,
732
729
  pct: Number(((nativeFrameworksMb / iosInstallMb) * 100).toFixed(1)),
733
730
  color: AppColors.indigo500,
734
- description: `React, Hermes, and ${packagesList.length} native pod frameworks.`,
735
- advice: 'Ensure Dead Code Stripping (STRIP_INSTALLED_PRODUCT = YES) in Release mode.',
731
+ description: t('bundle.iosComp1Desc', { count: packagesList.length }),
732
+ advice: t('bundle.iosComp1Advice'),
736
733
  },
737
734
  {
738
735
  id: 'ios-c2',
739
- name: 'Mach-O Executable (ARM64)',
736
+ name: t('bundle.iosComp2Name'),
740
737
  category: 'native',
741
738
  sizeMb: nativeMachoMb,
742
739
  pct: Number(((nativeMachoMb / iosInstallMb) * 100).toFixed(1)),
743
740
  color: AppColors.sky500,
744
- description: 'Host App compiled Swift/Objective-C and C++ native bridges.',
745
- advice: 'Enable Monolithic LTO (Link-Time Optimization) in Xcode Scheme.',
741
+ description: t('bundle.iosComp2Desc'),
742
+ advice: t('bundle.iosComp2Advice'),
746
743
  },
747
744
  {
748
745
  id: 'ios-c3',
749
- name: 'Asset Catalog (Assets.car)',
746
+ name: t('bundle.iosComp3Name'),
750
747
  category: 'assets',
751
748
  sizeMb: assetsCatalogMb,
752
749
  pct: Number(((assetsCatalogMb / iosInstallMb) * 100).toFixed(1)),
753
750
  color: AppColors.pink500,
754
- description: 'AppIcons, splash screens, vector glyphs, and bundled fonts.',
755
- advice: 'Compile images into Xcode Asset Catalog for automatic App Thinning.',
751
+ description: t('bundle.iosComp3Desc'),
752
+ advice: t('bundle.iosComp3Advice'),
756
753
  },
757
754
  {
758
755
  id: 'ios-c4',
759
- name: 'Hermes Bytecode (main.jsbundle)',
756
+ name: t('bundle.iosComp4Name'),
760
757
  category: 'js',
761
758
  sizeMb: releaseJsMb,
762
759
  pct: Number(((releaseJsMb / iosInstallMb) * 100).toFixed(1)),
763
760
  color: AppColors.emerald500,
764
- description: `Host app JavaScript compiled AOT into Hermes bytecode (${discoveredFiles.length} files).`,
765
- advice: 'AOT bytecode loads with 0ms compile latency on device launch.',
761
+ description: t('bundle.iosComp4Desc', { count: discoveredFiles.length }),
762
+ advice: t('bundle.iosComp4Advice'),
766
763
  },
767
764
  {
768
765
  id: 'ios-c5',
769
- name: 'Metadata & Code Signatures',
766
+ name: t('bundle.iosComp5Name'),
770
767
  category: 'meta',
771
768
  sizeMb: metadataMb,
772
769
  pct: Number(((metadataMb / iosInstallMb) * 100).toFixed(1)),
773
770
  color: AppColors.amber500,
774
- description: '_CodeSignature, Info.plist, and entitlements block.',
775
- advice: 'Standard Apple Code Signing & provisioning signature.',
771
+ description: t('bundle.iosComp5Desc'),
772
+ advice: t('bundle.iosComp5Advice'),
776
773
  },
777
774
  ];
778
775
  const androidComponents = [
779
776
  {
780
777
  id: 'and-c1',
781
- name: 'Native C++ Libraries (.so)',
778
+ name: t('bundle.andComp1Name'),
782
779
  category: 'native',
783
780
  sizeMb: androidCppMb,
784
781
  pct: Number(((androidCppMb / androidInstallMb) * 100).toFixed(1)),
785
782
  color: AppColors.sky500,
786
- description: `libhermes.so, libfbjni.so, and ${packagesList.length} C++ native adapters.`,
787
- advice: 'Deploy with Android App Bundle (.aab) to deliver per-ABI split APKs.',
783
+ description: t('bundle.andComp1Desc', { count: packagesList.length }),
784
+ advice: t('bundle.andComp1Advice'),
788
785
  },
789
786
  {
790
787
  id: 'and-c2',
791
- name: 'Compiled DEX (classes.dex)',
788
+ name: t('bundle.andComp2Name'),
792
789
  category: 'frameworks',
793
790
  sizeMb: androidDexMb,
794
791
  pct: Number(((androidDexMb / androidInstallMb) * 100).toFixed(1)),
795
792
  color: AppColors.indigo500,
796
- description: 'Compiled Java & Kotlin runtime, AndroidX, and React Native bridges.',
797
- advice: 'Enable R8 / ProGuard shrinking (minifyEnabled true) in build.gradle.',
793
+ description: t('bundle.andComp2Desc'),
794
+ advice: t('bundle.andComp2Advice'),
798
795
  },
799
796
  {
800
797
  id: 'and-c3',
801
- name: 'Android Resources (res/)',
798
+ name: t('bundle.andComp3Name'),
802
799
  category: 'assets',
803
800
  sizeMb: androidResMb,
804
801
  pct: Number(((androidResMb / androidInstallMb) * 100).toFixed(1)),
805
802
  color: AppColors.pink500,
806
- description: 'Drawables, vector XMLs, mipmap densities, resources.arsc, fonts.',
807
- advice: 'Use WebP and VectorDrawables to avoid multi-density asset duplication.',
803
+ description: t('bundle.andComp3Desc'),
804
+ advice: t('bundle.andComp3Advice'),
808
805
  },
809
806
  {
810
807
  id: 'and-c4',
811
- name: 'Hermes Bytecode (Android)',
808
+ name: t('bundle.andComp4Name'),
812
809
  category: 'js',
813
810
  sizeMb: releaseJsMb,
814
811
  pct: Number(((releaseJsMb / androidInstallMb) * 100).toFixed(1)),
815
812
  color: AppColors.emerald500,
816
- description: `Host app JavaScript compiled into Hermes bytecode (${discoveredFiles.length} files).`,
817
- advice: 'Pre-compiled bytecode during assembleRelease gradle task.',
813
+ description: t('bundle.andComp4Desc', { count: discoveredFiles.length }),
814
+ advice: t('bundle.andComp4Advice'),
818
815
  },
819
816
  {
820
817
  id: 'and-c5',
821
- name: 'Manifest & Signatures (META-INF)',
818
+ name: t('bundle.andComp5Name'),
822
819
  category: 'meta',
823
820
  sizeMb: 2.6,
824
821
  pct: Number(((2.6 / androidInstallMb) * 100).toFixed(1)),
825
822
  color: AppColors.amber500,
826
- description: 'AndroidManifest.xml, signing certs, v2/v3/v4 APK Signature Scheme blocks.',
827
- advice: 'Official Google Play signing & signature block.',
823
+ description: t('bundle.andComp5Desc'),
824
+ advice: t('bundle.andComp5Advice'),
828
825
  },
829
826
  ];
830
827
  // Universal Standalone APK metrics (Multi-ABI FAT APK: arm64 + v7a + x86_64)
@@ -834,53 +831,53 @@ export const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = tr
834
831
  const androidApkComponents = [
835
832
  {
836
833
  id: 'apk-c1',
837
- name: 'Multi-ABI C++ Libraries (.so)',
834
+ name: t('bundle.apkComp1Name'),
838
835
  category: 'native',
839
836
  sizeMb: androidMultiAbiCppMb,
840
837
  pct: Number(((androidMultiAbiCppMb / androidApkInstallMb) * 100).toFixed(1)),
841
838
  color: AppColors.sky500,
842
- description: 'Universal multi-architecture shared libraries (.so) bundled for direct sideloading.',
843
- advice: 'Use Android App Bundle (.aab) for Google Play to reduce install size by 60%.',
839
+ description: t('bundle.apkComp1Desc'),
840
+ advice: t('bundle.apkComp1Advice'),
844
841
  },
845
842
  {
846
843
  id: 'apk-c2',
847
- name: 'Compiled DEX Bytecode (classes.dex)',
844
+ name: t('bundle.apkComp2Name'),
848
845
  category: 'frameworks',
849
846
  sizeMb: androidDexMb,
850
847
  pct: Number(((androidDexMb / androidApkInstallMb) * 100).toFixed(1)),
851
848
  color: AppColors.indigo500,
852
- description: 'Compiled Java & Kotlin runtime, AndroidX libraries, and native bridge modules.',
853
- advice: 'Enable R8 / ProGuard shrinking (minifyEnabled true) and shrinkResources true.',
849
+ description: t('bundle.apkComp2Desc'),
850
+ advice: t('bundle.apkComp2Advice'),
854
851
  },
855
852
  {
856
853
  id: 'apk-c3',
857
- name: 'Android Resources & Assets (res/)',
854
+ name: t('bundle.apkComp3Name'),
858
855
  category: 'assets',
859
856
  sizeMb: androidResMb,
860
857
  pct: Number(((androidResMb / androidApkInstallMb) * 100).toFixed(1)),
861
858
  color: AppColors.pink500,
862
- description: 'Drawables, vector XMLs, mipmap densities, resources.arsc, fonts.',
863
- advice: 'Use WebP and VectorDrawables to avoid multi-density asset duplication.',
859
+ description: t('bundle.apkComp3Desc'),
860
+ advice: t('bundle.apkComp3Advice'),
864
861
  },
865
862
  {
866
863
  id: 'apk-c4',
867
- name: 'Hermes Bytecode Bundle (Android)',
864
+ name: t('bundle.apkComp4Name'),
868
865
  category: 'js',
869
866
  sizeMb: releaseJsMb,
870
867
  pct: Number(((releaseJsMb / androidApkInstallMb) * 100).toFixed(1)),
871
868
  color: AppColors.emerald500,
872
- description: `Host app JavaScript compiled into Hermes bytecode (${discoveredFiles.length} files).`,
873
- advice: 'Pre-compiled bytecode during assembleRelease gradle task.',
869
+ description: t('bundle.apkComp4Desc', { count: discoveredFiles.length }),
870
+ advice: t('bundle.apkComp4Advice'),
874
871
  },
875
872
  {
876
873
  id: 'apk-c5',
877
- name: 'Manifest & Signatures (META-INF)',
874
+ name: t('bundle.apkComp5Name'),
878
875
  category: 'meta',
879
876
  sizeMb: 3.2,
880
877
  pct: Number(((3.2 / androidApkInstallMb) * 100).toFixed(1)),
881
878
  color: AppColors.amber500,
882
- description: 'AndroidManifest.xml, signing certs, JAR & v2/v3/v4 APK Signature Scheme.',
883
- advice: 'Enterprise sideload & direct install signature block.',
879
+ description: t('bundle.apkComp5Desc'),
880
+ advice: t('bundle.apkComp5Advice'),
884
881
  },
885
882
  ];
886
883
  return {
@@ -8,6 +8,7 @@ type NetworkLog = {
8
8
  duration?: number;
9
9
  startTime: number;
10
10
  caller?: string;
11
+ client?: string;
11
12
  requestHeaders?: Record<string, string>;
12
13
  responseHeaders?: Record<string, string>;
13
14
  };
@@ -145,15 +145,26 @@ export const setupNetworkLogger = () => {
145
145
  return originalFetch(url, options);
146
146
  let caller = "Unknown";
147
147
  let requestHeaders;
148
+ let client = 'fetch';
148
149
  try {
149
150
  requestHeaders = normaliseHeaders(options?.headers);
150
151
  caller = getCallerFromStack(); // ✅ Capture call line
152
+ if (requestHeaders?.['apollographql-client-name'] || finalUrl?.toLowerCase().includes('/graphql')) {
153
+ client = 'apollo';
154
+ }
155
+ else if (requestHeaders?.['x-requested-with']?.toLowerCase().includes('xmlhttprequest')) {
156
+ client = 'xhr';
157
+ }
158
+ else if (caller?.toLowerCase().includes('axios')) {
159
+ client = 'axios';
160
+ }
151
161
  addOrUpdateLog({
152
162
  id,
153
163
  url: finalUrl,
154
164
  method,
155
165
  startTime: start,
156
166
  caller,
167
+ client,
157
168
  request: method === "GET" ? undefined : parseRequestData(options?.body),
158
169
  requestHeaders,
159
170
  });
@@ -193,6 +204,7 @@ export const setupNetworkLogger = () => {
193
204
  duration,
194
205
  startTime: start,
195
206
  caller,
207
+ client,
196
208
  responseHeaders,
197
209
  });
198
210
  }
@@ -209,6 +221,7 @@ export const setupNetworkLogger = () => {
209
221
  startTime: start,
210
222
  response: error,
211
223
  caller,
224
+ client,
212
225
  duration: Date.now() - start,
213
226
  });
214
227
  }
@@ -264,6 +277,7 @@ export const addAxiosInterceptors = (axiosInstance) => {
264
277
  method,
265
278
  startTime: start,
266
279
  caller,
280
+ client: 'axios',
267
281
  request: method === "GET" ? undefined : parseRequestData(config.data),
268
282
  requestHeaders: normaliseHeaders(config.headers),
269
283
  });
@@ -289,6 +303,7 @@ export const addAxiosInterceptors = (axiosInstance) => {
289
303
  startTime: start || Date.now(),
290
304
  duration: start != null ? Date.now() - start : undefined,
291
305
  caller,
306
+ client: 'axios',
292
307
  responseHeaders: normaliseHeaders(response.headers),
293
308
  });
294
309
  return response;
@@ -311,6 +326,7 @@ export const addAxiosInterceptors = (axiosInstance) => {
311
326
  startTime: start || Date.now(),
312
327
  duration: start != null ? Date.now() - start : undefined,
313
328
  caller,
329
+ client: 'axios',
314
330
  responseHeaders: normaliseHeaders(error.response?.headers),
315
331
  });
316
332
  }
@@ -5,6 +5,11 @@ export interface PerformanceEvent {
5
5
  category: 'render' | 'navigation' | 'memory' | 'io' | 'bridge';
6
6
  fps: number;
7
7
  durationMs: number;
8
+ droppedFrames?: number;
9
+ frameTimeMs?: number;
10
+ frameBudgetPct?: number;
11
+ bottleneckThread?: 'JS Thread' | 'UI Thread' | 'Bridge' | 'Balanced';
12
+ screenName?: string;
8
13
  label: string;
9
14
  detail: string;
10
15
  source?: string;
@@ -54,7 +59,76 @@ export interface ComponentRenderProfile {
54
59
  fixKeys: PerformanceFixKey[];
55
60
  severity: 'optimal' | 'warning' | 'critical';
56
61
  }
62
+ /**
63
+ * Dynamically generates tailored Before/After performance optimization code snippets
64
+ * based on the target component's real identifier, props, and detected anti-pattern.
65
+ */
66
+ export declare const generateFixSnippet: (fixKey: string, componentName?: string, context?: {
67
+ propName?: string;
68
+ actionName?: string;
69
+ itemHeight?: number;
70
+ }) => PerformanceFixKey;
71
+ /**
72
+ * Dynamically resolves caller file path or runtime source from the active JavaScript stack trace.
73
+ * Does not assume fixed directory hierarchies (such as src/ or components/) and extracts
74
+ * whatever relative path or symbol the host project structure uses.
75
+ */
76
+ export declare function getCallerSourceFile(fallbackName?: string): string;
77
+ export declare const getInitialRenderProfiles: () => ComponentRenderProfile[];
78
+ export declare const getInitialPerformanceEvents: () => PerformanceEvent[];
57
79
  export declare const INITIAL_EVENTS: PerformanceEvent[];
80
+ export declare const subscribeRenderProfiles: (listener: (profiles: ComponentRenderProfile[]) => void) => () => void;
81
+ export declare const getRenderProfiles: () => ComponentRenderProfile[];
82
+ export declare const registerComponentProfile: (profile: ComponentRenderProfile) => void;
83
+ export declare const trackComponentRender: (params: {
84
+ name: string;
85
+ type?: ComponentRenderProfile["type"];
86
+ sourceFile?: string;
87
+ renderTimeMs?: number;
88
+ isWasteful?: boolean;
89
+ reason?: string;
90
+ fixKeys?: PerformanceFixKey[];
91
+ }) => void;
92
+ /**
93
+ * Dynamically queries Hermes or JavaScript runtime memory statistics.
94
+ */
95
+ export declare const getHermesMemoryStats: () => LiveMemoryStats;
96
+ /**
97
+ * Dynamically records and evaluates screen navigation transition performance.
98
+ */
99
+ export declare const trackNavigationTransition: (screenName: string, durationMs: number, options?: {
100
+ ttiMs?: number;
101
+ droppedFrames?: number;
102
+ source?: string;
103
+ }) => PerformanceEvent;
104
+ /**
105
+ * React Hook that automatically profiles screen mounts and navigation transitions.
106
+ */
107
+ export declare const useNavigationProfiler: (screenName: string, options?: {
108
+ onTransitionEnd?: (durationMs: number) => void;
109
+ }) => void;
110
+ /**
111
+ * Measures synchronous execution duration and alerts on frame budget spikes (>16ms).
112
+ */
113
+ export declare const trackHeavyTask: <T>(taskName: string, syncFn: () => T, options?: {
114
+ thresholdMs?: number;
115
+ source?: string;
116
+ }) => T;
117
+ /**
118
+ * Measures async operation / Promise latency and records slow network or disk tasks.
119
+ */
120
+ export declare const measureAsync: <T>(taskName: string, asyncFn: () => Promise<T>, options?: {
121
+ thresholdMs?: number;
122
+ source?: string;
123
+ }) => Promise<T>;
124
+ /**
125
+ * Custom React Hook to profile any component or screen renders dynamically in runtime.
126
+ */
127
+ export declare const useComponentProfiler: (componentName: string, options?: {
128
+ type?: ComponentRenderProfile["type"];
129
+ sourceFile?: string;
130
+ propsToCompare?: Record<string, any>;
131
+ }) => void;
58
132
  export declare const usePerformanceTracker: () => {
59
133
  isRecording: boolean;
60
134
  setIsRecording: import("react").Dispatch<import("react").SetStateAction<boolean>>;