react-native-inapp-inspector 1.1.34 → 2.0.0

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 (122) hide show
  1. package/README.md +123 -101
  2. package/android/build.gradle +13 -1
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +904 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/ConsoleLogCard.js +57 -56
  6. package/dist/commonjs/components/CopyButton.js +10 -1
  7. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  8. package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
  9. package/dist/commonjs/components/Inspector/BundleTab.js +20 -20
  10. package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
  11. package/dist/commonjs/components/Inspector/CrashDetail.js +16 -12
  12. package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +28 -24
  14. package/dist/commonjs/components/Inspector/MainScreen.js +20 -103
  15. package/dist/commonjs/components/Inspector/NetworkTab.js +143 -10
  16. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  17. package/dist/commonjs/components/Inspector/ReduxDetail.js +24 -19
  18. package/dist/commonjs/components/Inspector/ReduxTab.js +30 -18
  19. package/dist/commonjs/components/Inspector/SettingsPanel.js +1578 -754
  20. package/dist/commonjs/components/Inspector/TabBar.js +6 -3
  21. package/dist/commonjs/components/LogCard.js +27 -9
  22. package/dist/commonjs/components/MetaAccordion.js +42 -0
  23. package/dist/commonjs/components/NetworkIcons.d.ts +10 -0
  24. package/dist/commonjs/components/NetworkIcons.js +53 -1
  25. package/dist/commonjs/components/Toast.js +3 -3
  26. package/dist/commonjs/components/TouchableScale.d.ts +5 -2
  27. package/dist/commonjs/components/TouchableScale.js +30 -32
  28. package/dist/commonjs/constants/index.d.ts +1 -2
  29. package/dist/commonjs/constants/index.js +4 -0
  30. package/dist/commonjs/constants/version.d.ts +1 -1
  31. package/dist/commonjs/constants/version.js +1 -1
  32. package/dist/commonjs/customHooks/analyticsLogger.d.ts +6 -0
  33. package/dist/commonjs/customHooks/analyticsLogger.js +16 -5
  34. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +2 -0
  35. package/dist/commonjs/customHooks/bundleAnalyzer.js +26 -17
  36. package/dist/commonjs/customHooks/consoleLogger.d.ts +2 -0
  37. package/dist/commonjs/customHooks/consoleLogger.js +10 -1
  38. package/dist/commonjs/customHooks/crashHandler.d.ts +2 -0
  39. package/dist/commonjs/customHooks/crashHandler.js +17 -1
  40. package/dist/commonjs/customHooks/networkLogger.d.ts +2 -0
  41. package/dist/commonjs/customHooks/networkLogger.js +12 -1
  42. package/dist/commonjs/customHooks/performanceTracker.d.ts +3 -1
  43. package/dist/commonjs/customHooks/performanceTracker.js +18 -4
  44. package/dist/commonjs/customHooks/reduxLogger.d.ts +2 -0
  45. package/dist/commonjs/customHooks/reduxLogger.js +10 -1
  46. package/dist/commonjs/helpers/index.js +10 -1
  47. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  48. package/dist/commonjs/helpers/settingsStore.js +59 -3
  49. package/dist/commonjs/i18n/locales/en.json +96 -28
  50. package/dist/commonjs/index.d.ts +1 -0
  51. package/dist/commonjs/index.js +307 -73
  52. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  53. package/dist/commonjs/native/NativeInspector.js +313 -0
  54. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  55. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  56. package/dist/commonjs/styles/AppColors.d.ts +28 -0
  57. package/dist/commonjs/styles/AppColors.js +30 -0
  58. package/dist/commonjs/styles/index.js +6 -6
  59. package/dist/commonjs/types/enums.d.ts +1 -1
  60. package/dist/commonjs/types/interfaces.d.ts +9 -0
  61. package/dist/esm/components/ConsoleLogCard.js +58 -57
  62. package/dist/esm/components/CopyButton.js +10 -1
  63. package/dist/esm/components/ErrorBoundary.js +3 -0
  64. package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
  65. package/dist/esm/components/Inspector/BundleTab.js +20 -20
  66. package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
  67. package/dist/esm/components/Inspector/CrashDetail.js +16 -12
  68. package/dist/esm/components/Inspector/CrashTab.js +97 -90
  69. package/dist/esm/components/Inspector/InspectorHeader.js +29 -25
  70. package/dist/esm/components/Inspector/MainScreen.js +21 -71
  71. package/dist/esm/components/Inspector/NetworkTab.js +145 -12
  72. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  73. package/dist/esm/components/Inspector/ReduxDetail.js +24 -19
  74. package/dist/esm/components/Inspector/ReduxTab.js +30 -18
  75. package/dist/esm/components/Inspector/SettingsPanel.js +1582 -758
  76. package/dist/esm/components/Inspector/TabBar.js +7 -4
  77. package/dist/esm/components/LogCard.js +27 -9
  78. package/dist/esm/components/MetaAccordion.js +42 -0
  79. package/dist/esm/components/NetworkIcons.d.ts +10 -0
  80. package/dist/esm/components/NetworkIcons.js +42 -0
  81. package/dist/esm/components/Toast.js +3 -3
  82. package/dist/esm/components/TouchableScale.d.ts +5 -2
  83. package/dist/esm/components/TouchableScale.js +31 -33
  84. package/dist/esm/constants/index.d.ts +1 -2
  85. package/dist/esm/constants/index.js +4 -0
  86. package/dist/esm/constants/version.d.ts +1 -1
  87. package/dist/esm/constants/version.js +1 -1
  88. package/dist/esm/customHooks/analyticsLogger.d.ts +6 -0
  89. package/dist/esm/customHooks/analyticsLogger.js +12 -4
  90. package/dist/esm/customHooks/bundleAnalyzer.d.ts +2 -0
  91. package/dist/esm/customHooks/bundleAnalyzer.js +23 -16
  92. package/dist/esm/customHooks/consoleLogger.d.ts +2 -0
  93. package/dist/esm/customHooks/consoleLogger.js +7 -0
  94. package/dist/esm/customHooks/crashHandler.d.ts +2 -0
  95. package/dist/esm/customHooks/crashHandler.js +14 -0
  96. package/dist/esm/customHooks/networkLogger.d.ts +2 -0
  97. package/dist/esm/customHooks/networkLogger.js +9 -0
  98. package/dist/esm/customHooks/performanceTracker.d.ts +3 -1
  99. package/dist/esm/customHooks/performanceTracker.js +15 -3
  100. package/dist/esm/customHooks/reduxLogger.d.ts +2 -0
  101. package/dist/esm/customHooks/reduxLogger.js +7 -0
  102. package/dist/esm/helpers/index.js +10 -1
  103. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  104. package/dist/esm/helpers/settingsStore.js +58 -3
  105. package/dist/esm/i18n/locales/en.json +96 -28
  106. package/dist/esm/index.d.ts +1 -0
  107. package/dist/esm/index.js +279 -59
  108. package/dist/esm/native/NativeInspector.d.ts +125 -0
  109. package/dist/esm/native/NativeInspector.js +289 -0
  110. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  111. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  112. package/dist/esm/styles/AppColors.d.ts +28 -0
  113. package/dist/esm/styles/AppColors.js +30 -0
  114. package/dist/esm/styles/index.js +6 -6
  115. package/dist/esm/types/enums.d.ts +1 -1
  116. package/dist/esm/types/interfaces.d.ts +9 -0
  117. package/ios/NetworkInspectorModule.h +6 -0
  118. package/ios/NetworkInspectorModule.m +720 -5
  119. package/package.json +15 -3
  120. package/react-native-inapp-inspector.podspec +7 -2
  121. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  122. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -6,6 +6,265 @@
6
6
  static NetworkInspectorModule *sharedInstance = nil;
7
7
  static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler = NULL;
8
8
 
9
+ @interface InAppInspectorFloatingView : UIView
10
+ @property (nonatomic, copy) void (^onTapBlock)(void);
11
+ @property (nonatomic, strong) UIView *badgeDot;
12
+ - (void)updateBadgeVisible:(BOOL)visible;
13
+ @end
14
+
15
+ @interface InAppInspectorOwlView : UIView
16
+ @end
17
+
18
+ @implementation InAppInspectorOwlView
19
+
20
+ - (instancetype)initWithFrame:(CGRect)frame {
21
+ if (self = [super initWithFrame:frame]) {
22
+ self.backgroundColor = [UIColor clearColor];
23
+ self.userInteractionEnabled = NO;
24
+ }
25
+ return self;
26
+ }
27
+
28
+ - (void)drawRect:(CGRect)rect {
29
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
30
+ if (!ctx) return;
31
+
32
+ CGFloat w = rect.size.width;
33
+ CGFloat scale = w / 256.0;
34
+
35
+ CGContextSaveGState(ctx);
36
+ CGContextScaleCTM(ctx, scale, scale);
37
+
38
+ // 1. Owl Body with Ears
39
+ UIBezierPath *bodyPath = [UIBezierPath bezierPath];
40
+ [bodyPath moveToPoint:CGPointMake(62, 150)];
41
+ [bodyPath addCurveToPoint:CGPointMake(90, 58) controlPoint1:CGPointMake(58, 104) controlPoint2:CGPointMake(70, 70)];
42
+ [bodyPath addLineToPoint:CGPointMake(98, 42)]; // Left ear
43
+ [bodyPath addLineToPoint:CGPointMake(116, 62)];
44
+ [bodyPath addQuadCurveToPoint:CGPointMake(140, 62) controlPoint:CGPointMake(128, 57)];
45
+ [bodyPath addLineToPoint:CGPointMake(158, 42)]; // Right ear
46
+ [bodyPath addLineToPoint:CGPointMake(166, 58)];
47
+ [bodyPath addCurveToPoint:CGPointMake(194, 150) controlPoint1:CGPointMake(186, 70) controlPoint2:CGPointMake(198, 104)];
48
+ [bodyPath addCurveToPoint:CGPointMake(152, 212) controlPoint1:CGPointMake(198, 180) controlPoint2:CGPointMake(184, 204)];
49
+ [bodyPath addCurveToPoint:CGPointMake(104, 212) controlPoint1:CGPointMake(140, 216) controlPoint2:CGPointMake(116, 216)];
50
+ [bodyPath addCurveToPoint:CGPointMake(62, 150) controlPoint1:CGPointMake(72, 204) controlPoint2:CGPointMake(58, 180)];
51
+ [bodyPath closePath];
52
+
53
+ [[UIColor colorWithRed:32.0/255.0 green:46.0/255.0 blue:85.0/255.0 alpha:1.0] setFill];
54
+ [bodyPath fill];
55
+
56
+ [[UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:1.0] setStroke];
57
+ bodyPath.lineWidth = 4.0;
58
+ [bodyPath stroke];
59
+
60
+ // 2. Wings
61
+ UIBezierPath *leftWing = [UIBezierPath bezierPath];
62
+ [leftWing moveToPoint:CGPointMake(74, 124)];
63
+ [leftWing addCurveToPoint:CGPointMake(86, 204) controlPoint1:CGPointMake(58, 154) controlPoint2:CGPointMake(60, 190)];
64
+ [leftWing addCurveToPoint:CGPointMake(88, 126) controlPoint1:CGPointMake(79, 176) controlPoint2:CGPointMake(77, 148)];
65
+ [leftWing closePath];
66
+ [[UIColor colorWithRed:26.0/255.0 green:37.0/255.0 blue:69.0/255.0 alpha:1.0] setFill];
67
+ [leftWing fill];
68
+
69
+ UIBezierPath *rightWing = [UIBezierPath bezierPath];
70
+ [rightWing moveToPoint:CGPointMake(182, 124)];
71
+ [rightWing addCurveToPoint:CGPointMake(170, 204) controlPoint1:CGPointMake(198, 154) controlPoint2:CGPointMake(196, 190)];
72
+ [rightWing addCurveToPoint:CGPointMake(168, 126) controlPoint1:CGPointMake(177, 176) controlPoint2:CGPointMake(179, 148)];
73
+ [rightWing closePath];
74
+ [[UIColor colorWithRed:26.0/255.0 green:37.0/255.0 blue:69.0/255.0 alpha:1.0] setFill];
75
+ [rightWing fill];
76
+
77
+ // 3. Belly Plate
78
+ UIBezierPath *belly = [UIBezierPath bezierPath];
79
+ [belly moveToPoint:CGPointMake(128, 126)];
80
+ [belly addCurveToPoint:CGPointMake(162, 174) controlPoint1:CGPointMake(151, 126) controlPoint2:CGPointMake(164, 148)];
81
+ [belly addCurveToPoint:CGPointMake(128, 212) controlPoint1:CGPointMake(160, 198) controlPoint2:CGPointMake(146, 212)];
82
+ [belly addCurveToPoint:CGPointMake(94, 174) controlPoint1:CGPointMake(110, 212) controlPoint2:CGPointMake(96, 198)];
83
+ [belly addCurveToPoint:CGPointMake(128, 126) controlPoint1:CGPointMake(92, 148) controlPoint2:CGPointMake(105, 126)];
84
+ [belly closePath];
85
+ [[UIColor colorWithRed:51.0/255.0 green:71.0/255.0 blue:122.0/255.0 alpha:1.0] setFill];
86
+ [belly fill];
87
+
88
+ // 4. Chest Screen `< / >`
89
+ UIBezierPath *screen = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(107, 161, 42, 32) cornerRadius:8];
90
+ [[UIColor colorWithRed:12.0/255.0 green:20.0/255.0 blue:38.0/255.0 alpha:1.0] setFill];
91
+ [screen fill];
92
+ [[UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:0.8] setStroke];
93
+ screen.lineWidth = 2.0;
94
+ [screen stroke];
95
+
96
+ UIBezierPath *codeLines = [UIBezierPath bezierPath];
97
+ [codeLines moveToPoint:CGPointMake(122, 170)];
98
+ [codeLines addLineToPoint:CGPointMake(115, 177)];
99
+ [codeLines addLineToPoint:CGPointMake(122, 184)];
100
+ [codeLines moveToPoint:CGPointMake(134, 170)];
101
+ [codeLines addLineToPoint:CGPointMake(141, 177)];
102
+ [codeLines addLineToPoint:CGPointMake(134, 184)];
103
+ [codeLines moveToPoint:CGPointMake(130, 168)];
104
+ [codeLines addLineToPoint:CGPointMake(126, 186)];
105
+ [[UIColor colorWithRed:143.0/255.0 green:208.0/255.0 blue:236.0/255.0 alpha:1.0] setStroke];
106
+ codeLines.lineWidth = 3.0;
107
+ codeLines.lineCapStyle = kCGLineCapRound;
108
+ codeLines.lineJoinStyle = kCGLineJoinRound;
109
+ [codeLines stroke];
110
+
111
+ // 5. Normal Right Eye
112
+ UIBezierPath *rightEye = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(136, 90, 34, 34)];
113
+ [[UIColor colorWithRed:251.0/255.0 green:191.0/255.0 blue:36.0/255.0 alpha:1.0] setFill];
114
+ [rightEye fill];
115
+ [[UIColor colorWithRed:26.0/255.0 green:18.0/255.0 blue:5.0/255.0 alpha:1.0] setStroke];
116
+ rightEye.lineWidth = 2.2;
117
+ [rightEye stroke];
118
+
119
+ UIBezierPath *rightPupil = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(145, 100, 16, 16)];
120
+ [[UIColor colorWithRed:10.0/255.0 green:14.0/255.0 blue:24.0/255.0 alpha:1.0] setFill];
121
+ [rightPupil fill];
122
+
123
+ UIBezierPath *rightGlint = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(153, 98, 6, 6)];
124
+ [[UIColor whiteColor] setFill];
125
+ [rightGlint fill];
126
+
127
+ // 6. Beak
128
+ UIBezierPath *beak = [UIBezierPath bezierPath];
129
+ [beak moveToPoint:CGPointMake(123.5, 123)];
130
+ [beak addQuadCurveToPoint:CGPointMake(132.5, 123) controlPoint:CGPointMake(128, 121)];
131
+ [beak addQuadCurveToPoint:CGPointMake(128, 134.5) controlPoint:CGPointMake(131, 132)];
132
+ [beak addQuadCurveToPoint:CGPointMake(123.5, 123) controlPoint:CGPointMake(125, 132)];
133
+ [beak closePath];
134
+ [[UIColor colorWithRed:251.0/255.0 green:146.0/255.0 blue:60.0/255.0 alpha:1.0] setFill];
135
+ [beak fill];
136
+
137
+ // 7. Magnifier Debug Left Eye
138
+ UIBezierPath *leftEye = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(75, 83, 40, 40)];
139
+ [[UIColor colorWithRed:251.0/255.0 green:191.0/255.0 blue:36.0/255.0 alpha:1.0] setFill];
140
+ [leftEye fill];
141
+ [[UIColor colorWithRed:26.0/255.0 green:18.0/255.0 blue:5.0/255.0 alpha:1.0] setStroke];
142
+ leftEye.lineWidth = 2.6;
143
+ [leftEye stroke];
144
+
145
+ UIBezierPath *leftPupil = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(85, 94, 20, 20)];
146
+ [[UIColor colorWithRed:10.0/255.0 green:14.0/255.0 blue:24.0/255.0 alpha:1.0] setFill];
147
+ [leftPupil fill];
148
+
149
+ UIBezierPath *leftGlint = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(95, 92, 7, 7)];
150
+ [[UIColor whiteColor] setFill];
151
+ [leftGlint fill];
152
+
153
+ // Magnifier Lens Ring & Handle
154
+ UIBezierPath *handle = [UIBezierPath bezierPath];
155
+ [handle moveToPoint:CGPointMake(75, 123)];
156
+ [handle addLineToPoint:CGPointMake(54, 147)];
157
+ [[UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:1.0] setStroke];
158
+ handle.lineWidth = 8.5;
159
+ handle.lineCapStyle = kCGLineCapRound;
160
+ [handle stroke];
161
+
162
+ UIBezierPath *lensRing = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(67, 75, 56, 56)];
163
+ lensRing.lineWidth = 7.0;
164
+ [lensRing stroke];
165
+
166
+ // Rosy Cheeks
167
+ UIBezierPath *leftCheek = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(74, 121, 18, 12)];
168
+ [[UIColor colorWithRed:251.0/255.0 green:113.0/255.0 blue:133.0/255.0 alpha:0.5] setFill];
169
+ [leftCheek fill];
170
+
171
+ UIBezierPath *rightCheek = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(158, 116, 18, 12)];
172
+ [[UIColor colorWithRed:251.0/255.0 green:113.0/255.0 blue:133.0/255.0 alpha:0.5] setFill];
173
+ [rightCheek fill];
174
+
175
+ CGContextRestoreGState(ctx);
176
+ }
177
+
178
+ @end
179
+
180
+ @implementation InAppInspectorFloatingView
181
+
182
+ - (instancetype)initWithFrame:(CGRect)frame {
183
+ self = [super initWithFrame:frame];
184
+ if (self) {
185
+ self.layer.cornerRadius = frame.size.width / 2.0;
186
+ self.layer.masksToBounds = NO;
187
+ self.backgroundColor = [UIColor colorWithRed:15.0/255.0 green:23.0/255.0 blue:42.0/255.0 alpha:0.95];
188
+
189
+ // Glow shadow
190
+ self.layer.shadowColor = [UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:0.65].CGColor;
191
+ self.layer.shadowOffset = CGSizeMake(0, 5);
192
+ self.layer.shadowRadius = 10;
193
+ self.layer.shadowOpacity = 1.0;
194
+ self.layer.borderWidth = 2.2;
195
+ self.layer.borderColor = [UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:0.9].CGColor;
196
+
197
+ // Native drawn Inspector Owl icon (Enhanced & Larger)
198
+ CGFloat iconSize = frame.size.width * 0.94;
199
+ CGFloat iconOffset = (frame.size.width - iconSize) / 2.0;
200
+ InAppInspectorOwlView *owlView = [[InAppInspectorOwlView alloc] initWithFrame:CGRectMake(iconOffset, iconOffset, iconSize, iconSize)];
201
+ [self addSubview:owlView];
202
+
203
+ // Status dot badge
204
+ _badgeDot = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width - 14, 4, 10, 10)];
205
+ _badgeDot.layer.cornerRadius = 5;
206
+ _badgeDot.backgroundColor = [UIColor colorWithRed:34.0/255.0 green:197.0/255.0 blue:94.0/255.0 alpha:1.0];
207
+ _badgeDot.layer.borderColor = [UIColor colorWithRed:15.0/255.0 green:23.0/255.0 blue:42.0/255.0 alpha:1.0].CGColor;
208
+ _badgeDot.layer.borderWidth = 1.5;
209
+ _badgeDot.hidden = YES;
210
+ [self addSubview:_badgeDot];
211
+
212
+ // Gestures (Main UI thread only)
213
+ UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
214
+ [self addGestureRecognizer:pan];
215
+
216
+ UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
217
+ [self addGestureRecognizer:tap];
218
+
219
+ [tap requireGestureRecognizerToFail:pan];
220
+ }
221
+ return self;
222
+ }
223
+
224
+ - (void)updateBadgeVisible:(BOOL)visible {
225
+ self.badgeDot.hidden = !visible;
226
+ }
227
+
228
+ - (void)handleTap:(UITapGestureRecognizer *)gesture {
229
+ if (gesture.state == UIGestureRecognizerStateEnded) {
230
+ if (self.onTapBlock) {
231
+ self.onTapBlock();
232
+ }
233
+ }
234
+ }
235
+
236
+ - (void)handlePan:(UIPanGestureRecognizer *)pan {
237
+ UIView *superview = self.superview;
238
+ if (!superview) return;
239
+
240
+ CGPoint translation = [pan translationInView:superview];
241
+ CGPoint newCenter = CGPointMake(self.center.x + translation.x, self.center.y + translation.y);
242
+
243
+ CGFloat halfW = self.bounds.size.width / 2.0;
244
+ CGFloat halfH = self.bounds.size.height / 2.0;
245
+ CGFloat minX = halfW + 10;
246
+ CGFloat maxX = superview.bounds.size.width - halfW - 10;
247
+ CGFloat minY = halfH + 44;
248
+ CGFloat maxY = superview.bounds.size.height - halfH - 44;
249
+
250
+ newCenter.x = MAX(minX, MIN(maxX, newCenter.x));
251
+ newCenter.y = MAX(minY, MIN(maxY, newCenter.y));
252
+
253
+ self.center = newCenter;
254
+ [pan setTranslation:CGPointZero inView:superview];
255
+
256
+ if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
257
+ CGFloat targetX = (self.center.x < superview.bounds.size.width / 2.0) ? minX : maxX;
258
+ [UIView animateWithDuration:0.25 delay:0 usingSpringWithDamping:0.75 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
259
+ self.center = CGPointMake(targetX, self.center.y);
260
+ } completion:nil];
261
+ }
262
+ }
263
+
264
+ @end
265
+
266
+ static InAppInspectorFloatingView *floatingButtonView = nil;
267
+
9
268
  static void NativeSignalHandler(int signalNumber) {
10
269
  void* callstack[128];
11
270
  int frames = backtrace(callstack, 128);
@@ -58,22 +317,57 @@ static void NativeExceptionHandler(NSException *exception) {
58
317
 
59
318
  @implementation NetworkInspectorModule {
60
319
  bool hasListeners;
320
+ CADisplayLink *displayLink;
321
+ CFTimeInterval lastFpsTimestamp;
322
+ NSInteger frameCount;
323
+ double currentCalculatedFps;
324
+ dispatch_queue_t _networkQueue;
325
+ dispatch_queue_t _consoleQueue;
326
+ dispatch_queue_t _analyticsQueue;
327
+ dispatch_queue_t _reduxQueue;
328
+ dispatch_queue_t _crashQueue;
329
+ dispatch_queue_t _metricsQueue;
330
+ NSMutableArray *_nativeNetworkLogs;
331
+ NSMutableArray *_nativeConsoleLogs;
332
+ NSMutableArray *_nativeAnalyticsEvents;
333
+ NSMutableArray *_nativeCrashRecords;
61
334
  }
62
335
 
63
336
  RCT_EXPORT_MODULE(NetworkInspectorModule);
64
337
 
65
- + (BOOL)requiresMainQueueSetup {
66
- return YES;
67
- }
68
-
69
338
  - (instancetype)init {
70
339
  if (self = [super init]) {
71
340
  sharedInstance = self;
341
+ self->currentCalculatedFps = 60.0;
342
+ self->_networkQueue = dispatch_queue_create("com.inappinspector.network", DISPATCH_QUEUE_SERIAL);
343
+ self->_consoleQueue = dispatch_queue_create("com.inappinspector.console", DISPATCH_QUEUE_SERIAL);
344
+ self->_analyticsQueue = dispatch_queue_create("com.inappinspector.analytics", DISPATCH_QUEUE_SERIAL);
345
+ self->_reduxQueue = dispatch_queue_create("com.inappinspector.redux", DISPATCH_QUEUE_SERIAL);
346
+ self->_crashQueue = dispatch_queue_create("com.inappinspector.crash", DISPATCH_QUEUE_SERIAL);
347
+ self->_metricsQueue = dispatch_queue_create("com.inappinspector.metrics", DISPATCH_QUEUE_SERIAL);
348
+ self->_nativeNetworkLogs = [NSMutableArray array];
349
+ self->_nativeConsoleLogs = [NSMutableArray array];
350
+ self->_nativeAnalyticsEvents = [NSMutableArray array];
351
+ self->_nativeCrashRecords = [NSMutableArray array];
72
352
  [self installHandlers];
353
+ [[NSNotificationCenter defaultCenter] addObserver:self
354
+ selector:@selector(handleMotionShakeNotification:)
355
+ name:@"RCTShowDevMenuNotification"
356
+ object:nil];
73
357
  }
74
358
  return self;
75
359
  }
76
360
 
361
+ + (BOOL)requiresMainQueueSetup {
362
+ return YES;
363
+ }
364
+
365
+ - (void)handleMotionShakeNotification:(NSNotification *)notification {
366
+ if (hasListeners) {
367
+ [self sendEventWithName:@"onDeviceShake" body:@{}];
368
+ }
369
+ }
370
+
77
371
  - (void)startObserving {
78
372
  hasListeners = YES;
79
373
  }
@@ -83,7 +377,7 @@ RCT_EXPORT_MODULE(NetworkInspectorModule);
83
377
  }
84
378
 
85
379
  - (NSArray<NSString *> *)supportedEvents {
86
- return @[@"onNativeCrash"];
380
+ return @[@"onNativeCrash", @"onFloatingButtonPress", @"onDeviceShake"];
87
381
  }
88
382
 
89
383
  - (void)installHandlers {
@@ -122,4 +416,425 @@ RCT_EXPORT_METHOD(enableNativeCrashProtection:(RCTPromiseResolveBlock)resolve
122
416
  resolve(@(YES));
123
417
  }
124
418
 
419
+ RCT_EXPORT_METHOD(getDeviceMetrics:(RCTPromiseResolveBlock)resolve
420
+ rejecter:(RCTPromiseRejectBlock)reject) {
421
+ NSMutableDictionary *metrics = [NSMutableDictionary dictionary];
422
+
423
+ // 1. Memory / RAM Metrics
424
+ unsigned long long physicalMemory = [NSProcessInfo processInfo].physicalMemory;
425
+ [metrics setObject:@(physicalMemory) forKey:@"totalRAM"];
426
+
427
+ // App Resident Memory via mach task
428
+ struct mach_task_basic_info info;
429
+ mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
430
+ if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS) {
431
+ [metrics setObject:@(info.resident_size) forKey:@"residentMemory"];
432
+ [metrics setObject:@(info.virtual_size) forKey:@"virtualMemory"];
433
+ }
434
+
435
+ // Free Memory
436
+ mach_port_t host_port = mach_host_self();
437
+ mach_msg_type_number_t host_size = sizeof(vm_statistics64_data_t) / sizeof(integer_t);
438
+ vm_size_t pagesize;
439
+ vm_statistics64_data_t vm_stat;
440
+ host_page_size(host_port, &pagesize);
441
+ if (host_statistics64(host_port, HOST_VM_INFO64, (host_info64_t)&vm_stat, &host_size) == KERN_SUCCESS) {
442
+ unsigned long long freeMem = (vm_stat.free_count + vm_stat.inactive_count) * pagesize;
443
+ [metrics setObject:@(freeMem) forKey:@"freeRAM"];
444
+ [metrics setObject:@(physicalMemory - freeMem) forKey:@"usedRAM"];
445
+ }
446
+
447
+ // 2. Storage Metrics
448
+ NSError *error = nil;
449
+ NSDictionary *fsAttrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
450
+ if (fsAttrs) {
451
+ NSNumber *freeSize = [fsAttrs objectForKey:NSFileSystemFreeSize];
452
+ NSNumber *totalSize = [fsAttrs objectForKey:NSFileSystemSize];
453
+ if (freeSize) [metrics setObject:freeSize forKey:@"freeStorage"];
454
+ if (totalSize) [metrics setObject:totalSize forKey:@"totalStorage"];
455
+ }
456
+
457
+ // 3. Battery Level & State
458
+ [UIDevice currentDevice].batteryMonitoringEnabled = YES;
459
+ float batteryLevel = [UIDevice currentDevice].batteryLevel;
460
+ if (batteryLevel >= 0.0f) {
461
+ [metrics setObject:@(batteryLevel * 100.0f) forKey:@"batteryPercent"];
462
+ }
463
+ UIDeviceBatteryState bState = [UIDevice currentDevice].batteryState;
464
+ BOOL isCharging = (bState == UIDeviceBatteryStateCharging || bState == UIDeviceBatteryStateFull);
465
+ [metrics setObject:@(isCharging) forKey:@"isCharging"];
466
+
467
+ // 4. Device & Hardware Identifiers
468
+ [metrics setObject:[UIDevice currentDevice].model forKey:@"deviceModel"];
469
+ [metrics setObject:[UIDevice currentDevice].systemName forKey:@"deviceBrand"];
470
+ [metrics setObject:[UIDevice currentDevice].systemVersion forKey:@"osVersion"];
471
+ [metrics setObject:@"arm64" forKey:@"cpuAbi"];
472
+
473
+ resolve(metrics);
474
+ }
475
+
476
+ RCT_EXPORT_METHOD(showFloatingButton:(NSDictionary *)options
477
+ resolver:(RCTPromiseResolveBlock)resolve
478
+ rejecter:(RCTPromiseRejectBlock)reject) {
479
+ dispatch_async(dispatch_get_main_queue(), ^{
480
+ UIWindow *targetWindow = nil;
481
+ if (@available(iOS 13.0, *)) {
482
+ for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
483
+ if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
484
+ for (UIWindow *w in scene.windows) {
485
+ if (w.isKeyWindow) {
486
+ targetWindow = w;
487
+ break;
488
+ }
489
+ }
490
+ }
491
+ if (targetWindow) break;
492
+ }
493
+ }
494
+ if (!targetWindow) {
495
+ targetWindow = [UIApplication sharedApplication].keyWindow;
496
+ if (!targetWindow && [UIApplication sharedApplication].windows.count > 0) {
497
+ targetWindow = [UIApplication sharedApplication].windows.firstObject;
498
+ }
499
+ }
500
+
501
+ if (!targetWindow) {
502
+ resolve(@(NO));
503
+ return;
504
+ }
505
+
506
+ CGFloat size = 64.0;
507
+ if (options && options[@"size"]) {
508
+ size = [options[@"size"] doubleValue];
509
+ }
510
+
511
+ CGFloat initialX = targetWindow.bounds.size.width - size - 20.0;
512
+ CGFloat initialY = targetWindow.bounds.size.height - size - 110.0;
513
+ if (options && options[@"x"]) {
514
+ initialX = [options[@"x"] doubleValue];
515
+ }
516
+ if (options && options[@"y"]) {
517
+ initialY = [options[@"y"] doubleValue];
518
+ }
519
+
520
+ if (floatingButtonView == nil) {
521
+ floatingButtonView = [[InAppInspectorFloatingView alloc] initWithFrame:CGRectMake(initialX, initialY, size, size)];
522
+ __weak NetworkInspectorModule *weakSelf = self;
523
+ floatingButtonView.onTapBlock = ^{
524
+ NetworkInspectorModule *strongSelf = weakSelf;
525
+ if (strongSelf && strongSelf->hasListeners) {
526
+ [strongSelf sendEventWithName:@"onFloatingButtonPress" body:@{}];
527
+ }
528
+ };
529
+ }
530
+
531
+ if (floatingButtonView.superview == nil) {
532
+ [targetWindow addSubview:floatingButtonView];
533
+ } else {
534
+ [targetWindow bringSubviewToFront:floatingButtonView];
535
+ }
536
+
537
+ floatingButtonView.hidden = NO;
538
+ floatingButtonView.alpha = 0.0;
539
+ [UIView animateWithDuration:0.2 animations:^{
540
+ floatingButtonView.alpha = 1.0;
541
+ }];
542
+
543
+ resolve(@(YES));
544
+ });
545
+ }
546
+
547
+ RCT_EXPORT_METHOD(hideFloatingButton:(RCTPromiseResolveBlock)resolve
548
+ rejecter:(RCTPromiseRejectBlock)reject) {
549
+ dispatch_async(dispatch_get_main_queue(), ^{
550
+ if (floatingButtonView != nil) {
551
+ [UIView animateWithDuration:0.15 animations:^{
552
+ floatingButtonView.alpha = 0.0;
553
+ } completion:^(BOOL finished) {
554
+ floatingButtonView.hidden = YES;
555
+ }];
556
+ }
557
+ resolve(@(YES));
558
+ });
559
+ }
560
+
561
+ RCT_EXPORT_METHOD(setFloatingButtonBadge:(BOOL)hasBadge
562
+ resolver:(RCTPromiseResolveBlock)resolve
563
+ rejecter:(RCTPromiseRejectBlock)reject) {
564
+ dispatch_async(dispatch_get_main_queue(), ^{
565
+ if (floatingButtonView != nil) {
566
+ [floatingButtonView updateBadgeVisible:hasBadge];
567
+ }
568
+ resolve(@(YES));
569
+ });
570
+ }
571
+
572
+ RCT_EXPORT_METHOD(setFloatingButtonPosition:(double)x y:(double)y
573
+ resolver:(RCTPromiseResolveBlock)resolve
574
+ rejecter:(RCTPromiseRejectBlock)reject) {
575
+ dispatch_async(dispatch_get_main_queue(), ^{
576
+ if (floatingButtonView != nil && floatingButtonView.superview != nil) {
577
+ floatingButtonView.frame = CGRectMake(x, y, floatingButtonView.frame.size.width, floatingButtonView.frame.size.height);
578
+ }
579
+ resolve(@(YES));
580
+ });
581
+ }
582
+
583
+ RCT_EXPORT_METHOD(startFpsMonitoring:(RCTPromiseResolveBlock)resolve
584
+ rejecter:(RCTPromiseRejectBlock)reject) {
585
+ dispatch_async(dispatch_get_main_queue(), ^{
586
+ if (self->displayLink == nil) {
587
+ self->lastFpsTimestamp = 0;
588
+ self->frameCount = 0;
589
+ self->currentCalculatedFps = 60.0;
590
+ self->displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleFpsTick:)];
591
+ [self->displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
592
+ }
593
+ resolve(@(YES));
594
+ });
595
+ }
596
+
597
+ - (void)handleFpsTick:(CADisplayLink *)link {
598
+ if (self->lastFpsTimestamp == 0) {
599
+ self->lastFpsTimestamp = link.timestamp;
600
+ return;
601
+ }
602
+ self->frameCount++;
603
+ CFTimeInterval elapsed = link.timestamp - self->lastFpsTimestamp;
604
+ if (elapsed >= 0.5) {
605
+ self->currentCalculatedFps = (double)self->frameCount / elapsed;
606
+ self->frameCount = 0;
607
+ self->lastFpsTimestamp = link.timestamp;
608
+ }
609
+ }
610
+
611
+ RCT_EXPORT_METHOD(stopFpsMonitoring:(RCTPromiseResolveBlock)resolve
612
+ rejecter:(RCTPromiseRejectBlock)reject) {
613
+ dispatch_async(dispatch_get_main_queue(), ^{
614
+ if (self->displayLink != nil) {
615
+ [self->displayLink invalidate];
616
+ self->displayLink = nil;
617
+ }
618
+ resolve(@(YES));
619
+ });
620
+ }
621
+
622
+ RCT_EXPORT_METHOD(getFpsMetrics:(RCTPromiseResolveBlock)resolve
623
+ rejecter:(RCTPromiseRejectBlock)reject) {
624
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
625
+ [dict setObject:@(self->currentCalculatedFps > 0 ? self->currentCalculatedFps : 60.0) forKey:@"fps"];
626
+ [dict setObject:@(60.0) forKey:@"targetFps"];
627
+ resolve(dict);
628
+ }
629
+
630
+ RCT_EXPORT_METHOD(getNativeStorageItem:(NSString *)key
631
+ resolver:(RCTPromiseResolveBlock)resolve
632
+ rejecter:(RCTPromiseRejectBlock)reject) {
633
+ NSString *prefKey = [NSString stringWithFormat:@"inapp_inspector_%@", key ?: @""];
634
+ NSString *val = [[NSUserDefaults standardUserDefaults] stringForKey:prefKey];
635
+ resolve(val ?: [NSNull null]);
636
+ }
637
+
638
+ RCT_EXPORT_METHOD(setNativeStorageItem:(NSString *)key
639
+ value:(NSString *)value
640
+ resolver:(RCTPromiseResolveBlock)resolve
641
+ rejecter:(RCTPromiseRejectBlock)reject) {
642
+ NSString *prefKey = [NSString stringWithFormat:@"inapp_inspector_%@", key ?: @""];
643
+ if (value == nil || [value isKindOfClass:[NSNull class]]) {
644
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:prefKey];
645
+ } else {
646
+ [[NSUserDefaults standardUserDefaults] setObject:value forKey:prefKey];
647
+ }
648
+ [[NSUserDefaults standardUserDefaults] synchronize];
649
+ resolve(@(YES));
650
+ }
651
+
652
+ RCT_EXPORT_METHOD(triggerHaptic:(NSString *)style
653
+ resolver:(RCTPromiseResolveBlock)resolve
654
+ rejecter:(RCTPromiseRejectBlock)reject) {
655
+ dispatch_async(dispatch_get_main_queue(), ^{
656
+ if (@available(iOS 10.0, *)) {
657
+ NSString *s = [style lowercaseString] ?: @"light";
658
+ if ([s isEqualToString:@"medium"]) {
659
+ UIImpactFeedbackGenerator *gen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
660
+ [gen prepare];
661
+ [gen impactOccurred];
662
+ } else if ([s isEqualToString:@"heavy"]) {
663
+ UIImpactFeedbackGenerator *gen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy];
664
+ [gen prepare];
665
+ [gen impactOccurred];
666
+ } else if ([s isEqualToString:@"success"]) {
667
+ UINotificationFeedbackGenerator *gen = [[UINotificationFeedbackGenerator alloc] init];
668
+ [gen prepare];
669
+ [gen notificationOccurred:UINotificationFeedbackTypeSuccess];
670
+ } else if ([s isEqualToString:@"warning"]) {
671
+ UINotificationFeedbackGenerator *gen = [[UINotificationFeedbackGenerator alloc] init];
672
+ [gen prepare];
673
+ [gen notificationOccurred:UINotificationFeedbackTypeWarning];
674
+ } else if ([s isEqualToString:@"error"]) {
675
+ UINotificationFeedbackGenerator *gen = [[UINotificationFeedbackGenerator alloc] init];
676
+ [gen prepare];
677
+ [gen notificationOccurred:UINotificationFeedbackTypeError];
678
+ } else {
679
+ UIImpactFeedbackGenerator *gen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
680
+ [gen prepare];
681
+ [gen impactOccurred];
682
+ }
683
+ }
684
+ resolve(@(YES));
685
+ });
686
+ }
687
+
688
+ RCT_EXPORT_METHOD(getNativeSystemMetrics:(RCTPromiseResolveBlock)resolve
689
+ rejecter:(RCTPromiseRejectBlock)reject) {
690
+ dispatch_async(self->_metricsQueue, ^{
691
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
692
+
693
+ if (@available(iOS 11.0, *)) {
694
+ NSProcessInfoThermalState thermal = [[NSProcessInfo processInfo] thermalState];
695
+ NSString *thermalStr = @"nominal";
696
+ if (thermal == NSProcessInfoThermalStateFair) thermalStr = @"fair";
697
+ else if (thermal == NSProcessInfoThermalStateSerious) thermalStr = @"serious";
698
+ else if (thermal == NSProcessInfoThermalStateCritical) thermalStr = @"critical";
699
+ [dict setObject:thermalStr forKey:@"thermalState"];
700
+ } else {
701
+ [dict setObject:@"nominal" forKey:@"thermalState"];
702
+ }
703
+
704
+ struct mach_task_basic_info info;
705
+ mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT;
706
+ kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
707
+ if (kerr == KERN_SUCCESS) {
708
+ double ramMB = (double)info.resident_size / (1024.0 * 1024.0);
709
+ [dict setObject:@(ramMB) forKey:@"residentRamMb"];
710
+ }
711
+
712
+ [dict setObject:@(self->currentCalculatedFps > 0 ? self->currentCalculatedFps : 60.0) forKey:@"fps"];
713
+ [dict setObject:@([[NSProcessInfo processInfo] activeProcessorCount]) forKey:@"activeCpuCores"];
714
+ [dict setObject:@([[NSProcessInfo processInfo] physicalMemory] / (1024.0 * 1024.0)) forKey:@"totalPhysicalRamMb"];
715
+
716
+ resolve(dict);
717
+ });
718
+ }
719
+
720
+ RCT_EXPORT_METHOD(pushNativeLogRecord:(NSString *)pageKey
721
+ jsonPayload:(NSString *)jsonPayload
722
+ resolve:(RCTPromiseResolveBlock)resolve
723
+ reject:(RCTPromiseRejectBlock)reject) {
724
+ if (!jsonPayload || jsonPayload.length == 0) {
725
+ resolve(@(NO));
726
+ return;
727
+ }
728
+
729
+ dispatch_queue_t targetQueue = self->_networkQueue;
730
+ NSMutableArray *targetStore = self->_nativeNetworkLogs;
731
+ if ([pageKey isEqualToString:@"logs"]) {
732
+ targetQueue = self->_consoleQueue;
733
+ targetStore = self->_nativeConsoleLogs;
734
+ } else if ([pageKey isEqualToString:@"analytics"]) {
735
+ targetQueue = self->_analyticsQueue;
736
+ targetStore = self->_nativeAnalyticsEvents;
737
+ } else if ([pageKey isEqualToString:@"crash"]) {
738
+ targetQueue = self->_crashQueue;
739
+ targetStore = self->_nativeCrashRecords;
740
+ }
741
+
742
+ dispatch_async(targetQueue, ^{
743
+ NSData *data = [jsonPayload dataUsingEncoding:NSUTF8StringEncoding];
744
+ NSDictionary *dict = nil;
745
+ if (data) {
746
+ dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
747
+ }
748
+
749
+ id itemId = dict ? dict[@"id"] : nil;
750
+ BOOL updated = NO;
751
+
752
+ if (itemId != nil) {
753
+ NSString *idPattern = [NSString stringWithFormat:@"\"id\":%@", itemId];
754
+ NSString *idStrPattern = [NSString stringWithFormat:@"\"id\":\"%@\"", itemId];
755
+ for (NSUInteger i = 0; i < targetStore.count; i++) {
756
+ NSString *existingJson = targetStore[i];
757
+ if ([existingJson containsString:idPattern] || [existingJson containsString:idStrPattern]) {
758
+ [targetStore replaceObjectAtIndex:i withObject:jsonPayload];
759
+ updated = YES;
760
+ break;
761
+ }
762
+ }
763
+ }
764
+
765
+ if (!updated) {
766
+ [targetStore insertObject:jsonPayload atIndex:0];
767
+ if (targetStore.count > 2000) {
768
+ [targetStore removeLastObject];
769
+ }
770
+ }
771
+ resolve(@(YES));
772
+ });
773
+ }
774
+
775
+ RCT_EXPORT_METHOD(getNativeCachedPage:(NSString *)pageKey
776
+ offset:(NSInteger)offset
777
+ limit:(NSInteger)limit
778
+ query:(NSString *)query
779
+ resolve:(RCTPromiseResolveBlock)resolve
780
+ reject:(RCTPromiseRejectBlock)reject) {
781
+ dispatch_queue_t targetQueue = self->_networkQueue;
782
+ NSMutableArray *targetStore = self->_nativeNetworkLogs;
783
+ if ([pageKey isEqualToString:@"logs"]) {
784
+ targetQueue = self->_consoleQueue;
785
+ targetStore = self->_nativeConsoleLogs;
786
+ } else if ([pageKey isEqualToString:@"analytics"]) {
787
+ targetQueue = self->_analyticsQueue;
788
+ targetStore = self->_nativeAnalyticsEvents;
789
+ } else if ([pageKey isEqualToString:@"crash"]) {
790
+ targetQueue = self->_crashQueue;
791
+ targetStore = self->_nativeCrashRecords;
792
+ }
793
+
794
+ dispatch_async(targetQueue, ^{
795
+ NSMutableArray *results = [NSMutableArray array];
796
+ NSString *cleanQuery = query ? [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString : @"";
797
+
798
+ NSInteger startIndex = MAX(0, offset);
799
+ NSInteger itemsCollected = 0;
800
+ NSInteger actualLimit = limit > 0 ? limit : 50;
801
+
802
+ for (NSInteger i = startIndex; i < targetStore.count && itemsCollected < actualLimit; i++) {
803
+ NSString *itemJson = targetStore[i];
804
+ if (cleanQuery.length > 0) {
805
+ if ([itemJson.lowercaseString containsString:cleanQuery]) {
806
+ [results addObject:itemJson];
807
+ itemsCollected++;
808
+ }
809
+ } else {
810
+ [results addObject:itemJson];
811
+ itemsCollected++;
812
+ }
813
+ }
814
+
815
+ NSError *error = nil;
816
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{
817
+ @"pageKey": pageKey ?: @"apis",
818
+ @"total": @(targetStore.count),
819
+ @"offset": @(offset),
820
+ @"items": results
821
+ } options:0 error:&error];
822
+
823
+ if (error || !jsonData) {
824
+ resolve(@"{\"items\":[],\"total\":0}");
825
+ } else {
826
+ NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
827
+ resolve(jsonStr);
828
+ }
829
+ });
830
+ }
831
+
832
+ #ifdef RCT_NEW_ARCH_ENABLED
833
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
834
+ (const facebook::react::ObjCTurboModule::InitParams &)params
835
+ {
836
+ return std::make_shared<facebook::react::NativeNetworkInspectorSpecJSI>(params);
837
+ }
838
+ #endif
839
+
125
840
  @end