react-native-inapp-inspector 1.1.25 → 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.
Files changed (70) hide show
  1. package/android/build.gradle +36 -0
  2. package/android/src/main/AndroidManifest.xml +3 -0
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
  5. package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
  6. package/dist/commonjs/components/ErrorBoundary.js +9 -145
  7. package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
  8. package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
  9. package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
  10. package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
  11. package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
  12. package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +52 -23
  14. package/dist/commonjs/components/Inspector/LogDetail.js +4 -3
  15. package/dist/commonjs/components/Inspector/MainScreen.js +12 -3
  16. package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
  17. package/dist/commonjs/components/Inspector/TabBar.js +15 -3
  18. package/dist/commonjs/components/NetworkIcons.d.ts +11 -0
  19. package/dist/commonjs/components/NetworkIcons.js +96 -1
  20. package/dist/commonjs/components/SegmentedTabs.js +2 -1
  21. package/dist/commonjs/constants/version.d.ts +1 -1
  22. package/dist/commonjs/constants/version.js +1 -1
  23. package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
  24. package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
  25. package/dist/commonjs/customHooks/consoleLogger.js +70 -6
  26. package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
  27. package/dist/commonjs/customHooks/crashHandler.js +681 -0
  28. package/dist/commonjs/customHooks/networkLogger.js +64 -48
  29. package/dist/commonjs/helpers/gaAnalyticsRegistry.js +1 -1
  30. package/dist/commonjs/i18n/locales/en.json +131 -0
  31. package/dist/commonjs/index.d.ts +3 -1
  32. package/dist/commonjs/index.js +95 -1
  33. package/dist/commonjs/types/enums.d.ts +41 -0
  34. package/dist/commonjs/types/enums.js +37 -1
  35. package/dist/commonjs/types/interfaces.d.ts +51 -1
  36. package/dist/esm/components/ErrorBoundary.d.ts +1 -1
  37. package/dist/esm/components/ErrorBoundary.js +10 -113
  38. package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
  39. package/dist/esm/components/Inspector/CrashDetail.js +758 -0
  40. package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
  41. package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
  42. package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
  43. package/dist/esm/components/Inspector/CrashTab.js +689 -0
  44. package/dist/esm/components/Inspector/InspectorHeader.js +52 -23
  45. package/dist/esm/components/Inspector/LogDetail.js +4 -3
  46. package/dist/esm/components/Inspector/MainScreen.js +12 -3
  47. package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
  48. package/dist/esm/components/Inspector/TabBar.js +16 -4
  49. package/dist/esm/components/NetworkIcons.d.ts +11 -0
  50. package/dist/esm/components/NetworkIcons.js +83 -0
  51. package/dist/esm/components/SegmentedTabs.js +2 -1
  52. package/dist/esm/constants/version.d.ts +1 -1
  53. package/dist/esm/constants/version.js +1 -1
  54. package/dist/esm/customHooks/analyticsLogger.js +70 -44
  55. package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
  56. package/dist/esm/customHooks/consoleLogger.js +68 -5
  57. package/dist/esm/customHooks/crashHandler.d.ts +64 -0
  58. package/dist/esm/customHooks/crashHandler.js +659 -0
  59. package/dist/esm/customHooks/networkLogger.js +64 -48
  60. package/dist/esm/helpers/gaAnalyticsRegistry.js +1 -1
  61. package/dist/esm/i18n/locales/en.json +131 -0
  62. package/dist/esm/index.d.ts +3 -1
  63. package/dist/esm/index.js +73 -1
  64. package/dist/esm/types/enums.d.ts +41 -0
  65. package/dist/esm/types/enums.js +36 -0
  66. package/dist/esm/types/interfaces.d.ts +51 -1
  67. package/ios/NetworkInspectorModule.h +6 -0
  68. package/ios/NetworkInspectorModule.m +125 -0
  69. package/package.json +5 -2
  70. package/react-native-inapp-inspector.podspec +18 -0
@@ -0,0 +1,681 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupGlobalCrashHandler = exports.exportCrashReport = exports.simulateTestCrash = exports.recordCustomCrash = exports.handleInterceptedCrash = exports.emitCrashEvent = exports.subscribeCrashEvents = exports.clearCrashRecords = exports.getCrashRecords = exports.computeCrashFingerprint = exports.parseCrashStackTrace = exports.getCrashBreadcrumbs = exports.clearCrashBreadcrumbs = exports.recordUserActionBreadcrumb = exports.recordReduxBreadcrumb = exports.recordNetworkBreadcrumb = exports.recordNavigationBreadcrumb = exports.addCrashBreadcrumb = exports.setMaxCrashLogsLimit = exports.CrashExportFormat = void 0;
4
+ const react_native_1 = require("react-native");
5
+ const enums_1 = require("../types/enums");
6
+ Object.defineProperty(exports, "CrashExportFormat", { enumerable: true, get: function () { return enums_1.CrashExportFormat; } });
7
+ const consoleLogger_1 = require("./consoleLogger");
8
+ const i18n_1 = require("../i18n");
9
+ let crashListeners = [];
10
+ let isCrashHandlerInitialized = false;
11
+ let lastHandledErrorTimestamp = 0;
12
+ let crashRecordsStore = [];
13
+ let breadcrumbsStore = [];
14
+ const MAX_BREADCRUMBS = 50;
15
+ let maxStoredCrashes = 100;
16
+ const appStartTime = Date.now();
17
+ // ─── BREADCRUMB MANAGERS ───────────────────────────────────────────────────────
18
+ const setMaxCrashLogsLimit = (max) => {
19
+ maxStoredCrashes = Math.max(10, max);
20
+ if (crashRecordsStore.length > maxStoredCrashes) {
21
+ crashRecordsStore = crashRecordsStore.slice(0, maxStoredCrashes);
22
+ }
23
+ };
24
+ exports.setMaxCrashLogsLimit = setMaxCrashLogsLimit;
25
+ const addCrashBreadcrumb = (type, message, data) => {
26
+ try {
27
+ const entry = {
28
+ type,
29
+ message,
30
+ timestamp: Date.now(),
31
+ data,
32
+ };
33
+ breadcrumbsStore.unshift(entry);
34
+ if (breadcrumbsStore.length > MAX_BREADCRUMBS) {
35
+ breadcrumbsStore = breadcrumbsStore.slice(0, MAX_BREADCRUMBS);
36
+ }
37
+ }
38
+ catch { }
39
+ };
40
+ exports.addCrashBreadcrumb = addCrashBreadcrumb;
41
+ const recordNavigationBreadcrumb = (fromRoute, toRoute) => {
42
+ (0, exports.addCrashBreadcrumb)('navigation', (0, i18n_1.t)('crash.breadcrumbNavigation', { from: fromRoute || '/', to: toRoute || '/' }), {
43
+ from: fromRoute,
44
+ to: toRoute,
45
+ });
46
+ };
47
+ exports.recordNavigationBreadcrumb = recordNavigationBreadcrumb;
48
+ const recordNetworkBreadcrumb = (url, method, status, duration) => {
49
+ const statusStr = status != null ? ` [${status}]` : '';
50
+ const durStr = duration != null ? ` (${Math.round(duration)}ms)` : '';
51
+ (0, exports.addCrashBreadcrumb)('network', `${method.toUpperCase()} ${url}${statusStr}${durStr}`, {
52
+ url,
53
+ method,
54
+ status,
55
+ duration,
56
+ });
57
+ };
58
+ exports.recordNetworkBreadcrumb = recordNetworkBreadcrumb;
59
+ const recordReduxBreadcrumb = (actionType, payload) => {
60
+ (0, exports.addCrashBreadcrumb)('redux', (0, i18n_1.t)('crash.breadcrumbAction', { actionType }), {
61
+ type: actionType,
62
+ payloadSummary: typeof payload === 'object' && payload !== null
63
+ ? Object.keys(payload)
64
+ : typeof payload,
65
+ });
66
+ };
67
+ exports.recordReduxBreadcrumb = recordReduxBreadcrumb;
68
+ const recordUserActionBreadcrumb = (action, metadata) => {
69
+ (0, exports.addCrashBreadcrumb)('user', action, metadata);
70
+ };
71
+ exports.recordUserActionBreadcrumb = recordUserActionBreadcrumb;
72
+ const clearCrashBreadcrumbs = () => {
73
+ breadcrumbsStore = [];
74
+ };
75
+ exports.clearCrashBreadcrumbs = clearCrashBreadcrumbs;
76
+ const getCrashBreadcrumbs = () => {
77
+ return [...breadcrumbsStore];
78
+ };
79
+ exports.getCrashBreadcrumbs = getCrashBreadcrumbs;
80
+ // ─── STACK TRACE PARSING & INTELLIGENT SOURCE DETECTION ───────────────────────
81
+ /**
82
+ * Intelligent stack trace parser with multi-engine support:
83
+ * - Hermes bytecode & source maps
84
+ * - Android Java / Kotlin native frames
85
+ * - iOS Objective-C / Swift symbols
86
+ * - JavaScript standard V8 & SpiderMonkey
87
+ */
88
+ const parseCrashStackTrace = (stack) => {
89
+ if (!stack || typeof stack !== 'string')
90
+ return [];
91
+ const lines = stack.split('\n');
92
+ const frames = [];
93
+ for (const line of lines) {
94
+ const trimmed = line.trim();
95
+ if (!trimmed)
96
+ continue;
97
+ // Filter out internal wrapper noise
98
+ if (trimmed.includes('crashHandler.ts') ||
99
+ trimmed.includes('handleInterceptedCrash') ||
100
+ trimmed.includes('addLogFromCrash') ||
101
+ trimmed.includes('setupGlobalCrashHandler')) {
102
+ continue;
103
+ }
104
+ // Pattern 1: iOS/Hermes format: method@file:line:column or method@url:line:col
105
+ const iosMatch = trimmed.match(/^([^@]+)@(.*):(\d+):(\d+)$/);
106
+ if (iosMatch) {
107
+ const [, method, file, lineNum, colNum] = iosMatch;
108
+ const cleanFile = file.split('?')[0].split('/').pop() || file;
109
+ const isAppCode = !file.includes('node_modules') &&
110
+ !file.includes('react-native') &&
111
+ !file.includes('react-dom') &&
112
+ !file.includes('hermes') &&
113
+ !file.includes('internal');
114
+ frames.push({
115
+ method: method.trim(),
116
+ file: cleanFile,
117
+ lineNumber: parseInt(lineNum, 10),
118
+ column: parseInt(colNum, 10),
119
+ raw: trimmed,
120
+ isAppCode,
121
+ });
122
+ continue;
123
+ }
124
+ // Pattern 2: V8/Android JS format: at method (file:line:column)
125
+ const androidMatch = trimmed.match(/^at\s+(.+)\s+\((.+):(\d+):(\d+)\)$/);
126
+ if (androidMatch) {
127
+ const [, method, file, lineNum, colNum] = androidMatch;
128
+ const cleanFile = file.split('?')[0].split('/').pop() || file;
129
+ const isAppCode = !file.includes('node_modules') &&
130
+ !file.includes('react-native') &&
131
+ !file.includes('react-dom') &&
132
+ !file.includes('internal');
133
+ frames.push({
134
+ method: method.trim(),
135
+ file: cleanFile,
136
+ lineNumber: parseInt(lineNum, 10),
137
+ column: parseInt(colNum, 10),
138
+ raw: trimmed,
139
+ isAppCode,
140
+ });
141
+ continue;
142
+ }
143
+ // Pattern 3: Simple at file:line:column
144
+ const simpleMatch = trimmed.match(/^at\s+(.+):(\d+):(\d+)$/);
145
+ if (simpleMatch) {
146
+ const [, file, lineNum, colNum] = simpleMatch;
147
+ const cleanFile = file.split('?')[0].split('/').pop() || file;
148
+ const isAppCode = !file.includes('node_modules') && !file.includes('react-native');
149
+ frames.push({
150
+ method: '<anonymous>',
151
+ file: cleanFile,
152
+ lineNumber: parseInt(lineNum, 10),
153
+ column: parseInt(colNum, 10),
154
+ raw: trimmed,
155
+ isAppCode,
156
+ });
157
+ continue;
158
+ }
159
+ // Pattern 4: Java / Android Native Frame: at com.pkg.Class.method(Class.java:123)
160
+ const javaMatch = trimmed.match(/^at\s+([a-zA-Z0-9_$.]+)\(([a-zA-Z0-9_$]+\.java):(\d+)\)$/);
161
+ if (javaMatch) {
162
+ const [, methodPath, fileName, lineNum] = javaMatch;
163
+ const isAppCode = !methodPath.startsWith('com.facebook.react') &&
164
+ !methodPath.startsWith('android.');
165
+ frames.push({
166
+ method: methodPath,
167
+ file: fileName,
168
+ lineNumber: parseInt(lineNum, 10),
169
+ column: 0,
170
+ raw: trimmed,
171
+ isAppCode,
172
+ });
173
+ continue;
174
+ }
175
+ // Pattern 5: iOS Mach-O Symbol: 0 AppName 0x000000010... -[ViewController method] + 48
176
+ const machOMatch = trimmed.match(/^\d+\s+([^\s]+)\s+(0x[0-9a-fA-F]+)\s+(.+)$/);
177
+ if (machOMatch) {
178
+ const [, binaryName, address, symbol] = machOMatch;
179
+ const isAppCode = !binaryName.startsWith('lib') &&
180
+ !binaryName.startsWith('Core') &&
181
+ !binaryName.startsWith('React');
182
+ frames.push({
183
+ method: symbol,
184
+ file: binaryName,
185
+ lineNumber: 0,
186
+ column: 0,
187
+ raw: trimmed,
188
+ isAppCode,
189
+ });
190
+ continue;
191
+ }
192
+ // Pattern 6: Fallback for generic frame lines
193
+ frames.push({
194
+ method: trimmed.startsWith('at ') ? trimmed.slice(3) : trimmed,
195
+ file: 'runtime',
196
+ lineNumber: 0,
197
+ column: 0,
198
+ raw: trimmed,
199
+ isAppCode: false,
200
+ });
201
+ }
202
+ return frames;
203
+ };
204
+ exports.parseCrashStackTrace = parseCrashStackTrace;
205
+ // ─── DIAGNOSTICS & SYSTEM METRICS ─────────────────────────────────────────────
206
+ const getMemoryDiagnostics = () => {
207
+ try {
208
+ const performance = globalThis.performance;
209
+ if (performance && performance.memory) {
210
+ return {
211
+ usedJSHeapSize: Math.round(performance.memory.usedJSHeapSize / (1024 * 1024)),
212
+ totalJSHeapSize: Math.round(performance.memory.totalJSHeapSize / (1024 * 1024)),
213
+ };
214
+ }
215
+ }
216
+ catch { }
217
+ return undefined;
218
+ };
219
+ const getScreenDimensions = () => {
220
+ try {
221
+ const window = react_native_1.Dimensions.get('window');
222
+ return `${Math.round(window.width)}x${Math.round(window.height)} (${window.scale}x)`;
223
+ }
224
+ catch {
225
+ return (0, i18n_1.t)('crash.unknown');
226
+ }
227
+ };
228
+ /**
229
+ * Computes a unique fingerprint signature to group recurring crash occurrences.
230
+ */
231
+ const computeCrashFingerprint = (crash) => {
232
+ const type = crash.type || 'js';
233
+ const name = crash.name || 'Error';
234
+ const topFrame = crash.parsedStack && crash.parsedStack.length > 0
235
+ ? crash.parsedStack[0]
236
+ : null;
237
+ const location = topFrame
238
+ ? `${topFrame.file}:${topFrame.lineNumber}`
239
+ : 'unknown';
240
+ return `${type}_${name}_${location}`;
241
+ };
242
+ exports.computeCrashFingerprint = computeCrashFingerprint;
243
+ // ─── CRASH RECORD MANAGEMENT & INTERCEPTION ───────────────────────────────────
244
+ const getCrashRecords = () => {
245
+ return [...crashRecordsStore];
246
+ };
247
+ exports.getCrashRecords = getCrashRecords;
248
+ const clearCrashRecords = () => {
249
+ crashRecordsStore = [];
250
+ (0, exports.emitCrashEvent)({
251
+ error: null,
252
+ message: '__CLEARED__',
253
+ timestamp: Date.now(),
254
+ });
255
+ };
256
+ exports.clearCrashRecords = clearCrashRecords;
257
+ const subscribeCrashEvents = (listener) => {
258
+ crashListeners.push(listener);
259
+ return () => {
260
+ crashListeners = crashListeners.filter(l => l !== listener);
261
+ };
262
+ };
263
+ exports.subscribeCrashEvents = subscribeCrashEvents;
264
+ const emitCrashEvent = (payload) => {
265
+ if (payload.message !== '__CLEARED__') {
266
+ const now = Date.now();
267
+ // Throttle duplicate rapid crash emissions within 250ms
268
+ if (now - lastHandledErrorTimestamp < 250) {
269
+ return;
270
+ }
271
+ lastHandledErrorTimestamp = now;
272
+ }
273
+ crashListeners.forEach(listener => {
274
+ try {
275
+ listener(payload);
276
+ }
277
+ catch { }
278
+ });
279
+ };
280
+ exports.emitCrashEvent = emitCrashEvent;
281
+ /**
282
+ * Core exception interceptor. Ingests all forms of errors, parses call stacks,
283
+ * collects device telemetry, records breadcrumbs, and updates the store.
284
+ */
285
+ const handleInterceptedCrash = (errorOrTitle, rawStack, isFatal = false, customType, componentStack) => {
286
+ try {
287
+ const errorObj = errorOrTitle instanceof Error
288
+ ? errorOrTitle
289
+ : typeof errorOrTitle === 'object' && errorOrTitle !== null
290
+ ? errorOrTitle
291
+ : new Error(String(errorOrTitle || (0, i18n_1.t)('crash.runtimeException')));
292
+ const rawMsg = errorObj.message ||
293
+ (typeof errorOrTitle === 'string' ? errorOrTitle : (0, i18n_1.t)('crash.runtimeException'));
294
+ let stackString = '';
295
+ if (typeof rawStack === 'string') {
296
+ stackString = rawStack;
297
+ }
298
+ else if (Array.isArray(rawStack)) {
299
+ stackString = rawStack
300
+ .map(f => `at ${f.methodName || '<anonymous>'} (${f.file || 'unknown'}:${f.lineNumber || 0}:${f.column || 0})`)
301
+ .join('\n');
302
+ }
303
+ else {
304
+ stackString = errorObj.stack || new Error().stack || '';
305
+ }
306
+ // Determine Crash Type
307
+ let inferredType = customType || enums_1.CrashType.Js;
308
+ const lowMsg = rawMsg.toLowerCase();
309
+ if (!customType) {
310
+ if (lowMsg.includes('native') ||
311
+ lowMsg.includes('sigsegv') ||
312
+ lowMsg.includes('nsrange') ||
313
+ lowMsg.includes('nullpointerexception') ||
314
+ lowMsg.includes('fatal signal')) {
315
+ inferredType = enums_1.CrashType.Native;
316
+ }
317
+ else if (lowMsg.includes('promise') ||
318
+ lowMsg.includes('unhandled rejection') ||
319
+ lowMsg.includes('unhandledrejection')) {
320
+ inferredType = enums_1.CrashType.Promise;
321
+ }
322
+ else if (lowMsg.includes('render') ||
323
+ lowMsg.includes('errorboundary') ||
324
+ componentStack) {
325
+ inferredType = enums_1.CrashType.Render;
326
+ }
327
+ else {
328
+ inferredType = enums_1.CrashType.Js;
329
+ }
330
+ }
331
+ const now = new Date();
332
+ const dateStr = now.toLocaleDateString();
333
+ const timeStr = now.toLocaleTimeString();
334
+ const parsedStack = (0, exports.parseCrashStackTrace)(stackString);
335
+ const log = (0, consoleLogger_1.addLogFromCrash)(errorObj, `[${isFatal ? (0, i18n_1.t)('crash.logFatalCrash') : (0, i18n_1.t)('crash.logUnhandledError')}] ${rawMsg}`, stackString, isFatal);
336
+ const isHermes = typeof globalThis.HermesInternal !== 'undefined';
337
+ const isFabric = typeof globalThis.nativeFabricUIManager !== 'undefined';
338
+ const crashRecord = {
339
+ id: `crash_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`,
340
+ error: errorObj,
341
+ isFatal,
342
+ type: inferredType,
343
+ message: rawMsg,
344
+ name: errorObj.name || (isFatal ? (0, i18n_1.t)('crash.errorNameFatal') : (0, i18n_1.t)('crash.errorNameUnhandled')),
345
+ stack: stackString,
346
+ parsedStack,
347
+ componentStack: componentStack || undefined,
348
+ timestamp: Date.now(),
349
+ dateStr,
350
+ timeStr,
351
+ deviceInfo: {
352
+ platform: react_native_1.Platform.OS,
353
+ osVersion: String(react_native_1.Platform.Version),
354
+ rnVersion: react_native_1.Platform.constants?.reactNativeVersion
355
+ ? `${react_native_1.Platform.constants.reactNativeVersion.major}.${react_native_1.Platform.constants.reactNativeVersion.minor}.${react_native_1.Platform.constants.reactNativeVersion.patch}`
356
+ : (0, i18n_1.t)('crash.unknown'),
357
+ isHermes,
358
+ isFabric,
359
+ appState: react_native_1.AppState.currentState || 'active',
360
+ },
361
+ memoryInfo: getMemoryDiagnostics(),
362
+ breadcrumbs: [...breadcrumbsStore],
363
+ logId: log?.id,
364
+ };
365
+ crashRecordsStore.unshift(crashRecord);
366
+ if (crashRecordsStore.length > maxStoredCrashes) {
367
+ crashRecordsStore = crashRecordsStore.slice(0, maxStoredCrashes);
368
+ }
369
+ (0, exports.emitCrashEvent)({
370
+ error: errorObj,
371
+ isFatal,
372
+ message: `[${isFatal ? (0, i18n_1.t)('crash.logFatalCrash') : (0, i18n_1.t)('crash.logUnhandledError')}] ${rawMsg}`,
373
+ stack: stackString,
374
+ timestamp: Date.now(),
375
+ logId: log?.id,
376
+ crashRecord,
377
+ });
378
+ return crashRecord;
379
+ }
380
+ catch (err) {
381
+ const fallbackRecord = {
382
+ id: `crash_${Date.now()}`,
383
+ isFatal: true,
384
+ type: enums_1.CrashType.Js,
385
+ message: String(errorOrTitle || (0, i18n_1.t)('crash.unknownException')),
386
+ timestamp: Date.now(),
387
+ dateStr: new Date().toLocaleDateString(),
388
+ timeStr: new Date().toLocaleTimeString(),
389
+ };
390
+ return fallbackRecord;
391
+ }
392
+ };
393
+ exports.handleInterceptedCrash = handleInterceptedCrash;
394
+ /**
395
+ * Manually report a caught error with custom metadata.
396
+ */
397
+ const recordCustomCrash = (error, options) => {
398
+ return (0, exports.handleInterceptedCrash)(error, typeof error === 'object' ? error.stack : undefined, options?.isFatal ?? false, options?.type || enums_1.CrashType.Custom, options?.componentStack);
399
+ };
400
+ exports.recordCustomCrash = recordCustomCrash;
401
+ /**
402
+ * Simulates a crash for developer testing without crashing the host app.
403
+ * Uses real runtime Error objects — no mocked file paths or fake stacks.
404
+ */
405
+ const simulateTestCrash = (type = enums_1.CrashType.Js, customMessage, customStack) => {
406
+ const err = new Error(customMessage || _defaultSimMessage(type));
407
+ return (0, exports.handleInterceptedCrash)(type === enums_1.CrashType.Native ? err.message : err, customStack || err.stack, type === enums_1.CrashType.Native, type, type === enums_1.CrashType.Render ? _buildComponentStack() : undefined);
408
+ };
409
+ exports.simulateTestCrash = simulateTestCrash;
410
+ /** Generates a generic error message per type — no fake references. */
411
+ const _defaultSimMessage = (type) => {
412
+ switch (type) {
413
+ case 'native':
414
+ return (0, i18n_1.t)('crash.simNativeMessage');
415
+ case 'promise':
416
+ return (0, i18n_1.t)('crash.simPromiseMessage');
417
+ case 'render':
418
+ return (0, i18n_1.t)('crash.simRenderMessage');
419
+ default:
420
+ return (0, i18n_1.t)('crash.simJsMessage');
421
+ }
422
+ };
423
+ /** Builds a component stack from the real React tree if possible. */
424
+ const _buildComponentStack = () => {
425
+ try {
426
+ const err = new Error();
427
+ if (err.stack) {
428
+ // Return the live stack as a pseudo component-stack
429
+ return err.stack
430
+ .split('\n')
431
+ .slice(1, 6)
432
+ .map(line => ` ${line.trim()}`)
433
+ .join('\n');
434
+ }
435
+ }
436
+ catch { }
437
+ return undefined;
438
+ };
439
+ // ─── EXPORT & REPORTING UTILITIES ─────────────────────────────────────────────
440
+ /**
441
+ * Formats a crash into Text, Markdown, or JSON.
442
+ */
443
+ const exportCrashReport = (crash, format = 'text') => {
444
+ if (format === 'json') {
445
+ return JSON.stringify(crash, null, 2);
446
+ }
447
+ const uptimeSec = Math.round((crash.timestamp - appStartTime) / 1000);
448
+ if (format === 'markdown') {
449
+ const lines = [];
450
+ lines.push(`## ${(0, i18n_1.t)('crash.mdReportTitle', { name: crash.name || 'Error' })}`);
451
+ lines.push(`> **${crash.message}**`);
452
+ lines.push('');
453
+ lines.push(`- **${(0, i18n_1.t)('crash.reportType')}** \`${crash.type.toUpperCase()}\``);
454
+ lines.push(`- **${(0, i18n_1.t)('crash.reportFatal')}** \`${crash.isFatal
455
+ ? (0, i18n_1.t)('crash.mdSeverityFatal')
456
+ : (0, i18n_1.t)('crash.mdSeverityHandled')}\``);
457
+ lines.push(`- **${(0, i18n_1.t)('crash.reportTimestamp')}** ${crash.dateStr} ${crash.timeStr}`);
458
+ lines.push(`- **${(0, i18n_1.t)('crash.reportUptime')}** ${uptimeSec}s`);
459
+ lines.push(`- **${(0, i18n_1.t)('crash.reportPlatform')}** ${crash.deviceInfo?.platform?.toUpperCase()} (v${crash.deviceInfo?.osVersion})`);
460
+ lines.push(`- **${(0, i18n_1.t)('crash.reportReactNative')}** v${crash.deviceInfo?.rnVersion || 'N/A'}`);
461
+ lines.push(`- **${(0, i18n_1.t)('crash.jsEngine')}** ${crash.deviceInfo?.isHermes ? (0, i18n_1.t)('crash.hermesEngine') : (0, i18n_1.t)('crash.jsc')}`);
462
+ lines.push(`- **${(0, i18n_1.t)('crash.reportArchitecture')}** ${crash.deviceInfo?.isFabric
463
+ ? (0, i18n_1.t)('crash.reportFabricNew')
464
+ : (0, i18n_1.t)('crash.reportPaperLegacy')}`);
465
+ if (crash.memoryInfo) {
466
+ lines.push(`- **${(0, i18n_1.t)('crash.reportJsMemory')}** ${crash.memoryInfo.usedJSHeapSize} MB / ${crash.memoryInfo.totalJSHeapSize} MB`);
467
+ }
468
+ lines.push('');
469
+ lines.push(`### ${(0, i18n_1.t)('crash.mdStackTrace')}`);
470
+ lines.push('```');
471
+ lines.push(crash.stack || (0, i18n_1.t)('crash.reportNoStackTrace'));
472
+ lines.push('```');
473
+ if (crash.componentStack) {
474
+ lines.push('');
475
+ lines.push(`### ${(0, i18n_1.t)('crash.mdComponentHierarchy')}`);
476
+ lines.push('```');
477
+ lines.push(crash.componentStack);
478
+ lines.push('```');
479
+ }
480
+ if (crash.breadcrumbs && crash.breadcrumbs.length > 0) {
481
+ lines.push('');
482
+ lines.push(`### ${(0, i18n_1.t)('crash.mdRecentBreadcrumbs')}`);
483
+ crash.breadcrumbs.forEach(b => {
484
+ const time = new Date(b.timestamp).toLocaleTimeString();
485
+ lines.push(`- \`[${time}]\` **[${b.type.toUpperCase()}]** ${b.message}`);
486
+ });
487
+ }
488
+ return lines.join('\n');
489
+ }
490
+ // Standard Text format
491
+ const lines = [];
492
+ lines.push('====================================================');
493
+ lines.push(` ${(0, i18n_1.t)('crash.reportTitle')} `);
494
+ lines.push('====================================================');
495
+ lines.push(`${(0, i18n_1.t)('crash.reportErrorName')} ${crash.name || 'Error'}`);
496
+ lines.push(`${(0, i18n_1.t)('crash.reportMessage')} ${crash.message}`);
497
+ lines.push(`${(0, i18n_1.t)('crash.reportType')} ${crash.type.toUpperCase()}`);
498
+ lines.push(`${(0, i18n_1.t)('crash.reportFatal')} ${crash.isFatal ? (0, i18n_1.t)('crash.reportFatalYes') : (0, i18n_1.t)('crash.reportFatalNo')}`);
499
+ lines.push(`${(0, i18n_1.t)('crash.reportTimestamp')} ${crash.dateStr} ${crash.timeStr} (${crash.timestamp})`);
500
+ lines.push(`${(0, i18n_1.t)('crash.reportUptime')} ${(0, i18n_1.t)('crash.reportUptimeValue', {
501
+ seconds: uptimeSec,
502
+ })}`);
503
+ lines.push(`${(0, i18n_1.t)('crash.reportPlatform')} ${crash.deviceInfo?.platform?.toUpperCase()} (v${crash.deviceInfo?.osVersion})`);
504
+ lines.push(`${(0, i18n_1.t)('crash.reportReactNative')} ${crash.deviceInfo?.rnVersion || 'N/A'}`);
505
+ lines.push(`${(0, i18n_1.t)('crash.reportHermes')} ${crash.deviceInfo?.isHermes
506
+ ? (0, i18n_1.t)('crash.reportEnabled')
507
+ : (0, i18n_1.t)('crash.reportDisabled')}`);
508
+ lines.push(`${(0, i18n_1.t)('crash.reportArchitecture')} ${crash.deviceInfo?.isFabric
509
+ ? (0, i18n_1.t)('crash.reportFabricNew')
510
+ : (0, i18n_1.t)('crash.reportPaperLegacy')}`);
511
+ lines.push(`${(0, i18n_1.t)('crash.reportScreenSize')} ${getScreenDimensions()}`);
512
+ lines.push(`${(0, i18n_1.t)('crash.reportAppState')} ${crash.deviceInfo?.appState || 'active'}`);
513
+ if (crash.memoryInfo) {
514
+ lines.push(`${(0, i18n_1.t)('crash.reportJsMemory')} ${crash.memoryInfo.usedJSHeapSize} MB / ${crash.memoryInfo.totalJSHeapSize} MB`);
515
+ }
516
+ lines.push('----------------------------------------------------');
517
+ lines.push(`${(0, i18n_1.t)('crash.reportStackTrace')}`);
518
+ lines.push(crash.stack || (0, i18n_1.t)('crash.reportNoStackTrace'));
519
+ if (crash.componentStack) {
520
+ lines.push('----------------------------------------------------');
521
+ lines.push(`${(0, i18n_1.t)('crash.reportComponentHierarchy')}`);
522
+ lines.push(crash.componentStack);
523
+ }
524
+ if (crash.breadcrumbs && crash.breadcrumbs.length > 0) {
525
+ lines.push('----------------------------------------------------');
526
+ lines.push(`${(0, i18n_1.t)('crash.reportRecentBreadcrumbs')}`);
527
+ crash.breadcrumbs.forEach(b => {
528
+ const time = new Date(b.timestamp).toLocaleTimeString();
529
+ lines.push(` [${time}] [${b.type.toUpperCase()}] ${b.message}`);
530
+ });
531
+ }
532
+ lines.push('====================================================');
533
+ return lines.join('\n');
534
+ };
535
+ exports.exportCrashReport = exportCrashReport;
536
+ // ─── GLOBAL HANDLER INITIALIZATION ────────────────────────────────────────────
537
+ /**
538
+ * Natively intercepts and prevents crashes, native RedBox dialogues,
539
+ * unhandled JS errors, and unhandled promise rejections.
540
+ */
541
+ const setupGlobalCrashHandler = () => {
542
+ if (isCrashHandlerInitialized)
543
+ return;
544
+ isCrashHandlerInitialized = true;
545
+ // 1. Completely silence React Native LogBox & RedBox overlays
546
+ try {
547
+ if (react_native_1.LogBox && typeof react_native_1.LogBox.ignoreAllLogs === 'function') {
548
+ react_native_1.LogBox.ignoreAllLogs(true);
549
+ }
550
+ }
551
+ catch { }
552
+ try {
553
+ // @ts-ignore
554
+ const LogBoxData = require('react-native/Libraries/LogBox/Data/LogBoxData');
555
+ if (LogBoxData && typeof LogBoxData.setDisabled === 'function') {
556
+ LogBoxData.setDisabled(true);
557
+ }
558
+ }
559
+ catch { }
560
+ // 2. Intercept Native Module: NetworkInspectorModule (Android & iOS)
561
+ try {
562
+ const networkInspectorModule = react_native_1.NativeModules?.NetworkInspectorModule;
563
+ if (networkInspectorModule) {
564
+ if (typeof networkInspectorModule.enableNativeCrashProtection === 'function') {
565
+ networkInspectorModule.enableNativeCrashProtection();
566
+ }
567
+ const nativeEmitter = new react_native_1.NativeEventEmitter(networkInspectorModule);
568
+ nativeEmitter.addListener('onNativeCrash', (event) => {
569
+ const platform = event?.platform || react_native_1.Platform.OS;
570
+ const msg = `[${platform.toUpperCase()} ${(0, i18n_1.t)('crash.nativeCrashTitle')}] ${event?.message || (0, i18n_1.t)('crash.nativeUncaughtException')}`;
571
+ const stack = event?.stack || '';
572
+ (0, exports.handleInterceptedCrash)(msg, stack, true, 'native');
573
+ });
574
+ }
575
+ }
576
+ catch { }
577
+ // 3. Intercept Native ExceptionsManager & RedBox modules (Bridge & TurboModules)
578
+ try {
579
+ const tmReg = react_native_1.TurboModuleRegistry || globalThis.__turboModuleProxy;
580
+ const nativeExceptionsManager = react_native_1.NativeModules?.ExceptionsManager ||
581
+ (tmReg?.get ? tmReg.get('ExceptionsManager') : null);
582
+ if (nativeExceptionsManager) {
583
+ nativeExceptionsManager.reportFatalException = (title, stack, exceptionId) => {
584
+ (0, exports.handleInterceptedCrash)(title, stack, true, 'native');
585
+ try {
586
+ if (typeof nativeExceptionsManager.dismissRedbox === 'function') {
587
+ nativeExceptionsManager.dismissRedbox();
588
+ }
589
+ }
590
+ catch { }
591
+ };
592
+ nativeExceptionsManager.reportSoftException = (title, stack, exceptionId) => {
593
+ (0, exports.handleInterceptedCrash)(title, stack, false, 'js');
594
+ try {
595
+ if (typeof nativeExceptionsManager.dismissRedbox === 'function') {
596
+ nativeExceptionsManager.dismissRedbox();
597
+ }
598
+ }
599
+ catch { }
600
+ };
601
+ nativeExceptionsManager.reportException = (data) => {
602
+ const msg = data?.message || data?.title || (0, i18n_1.t)('crash.nativeException');
603
+ const stack = data?.stack || data?.rawStack;
604
+ (0, exports.handleInterceptedCrash)(msg, stack, data?.isFatal ?? true, 'js');
605
+ try {
606
+ if (typeof nativeExceptionsManager.dismissRedbox === 'function') {
607
+ nativeExceptionsManager.dismissRedbox();
608
+ }
609
+ }
610
+ catch { }
611
+ };
612
+ nativeExceptionsManager.updateExceptionMessage = () => { };
613
+ try {
614
+ if (typeof nativeExceptionsManager.dismissRedbox === 'function') {
615
+ nativeExceptionsManager.dismissRedbox();
616
+ }
617
+ }
618
+ catch { }
619
+ }
620
+ const nativeRedBox = react_native_1.NativeModules?.RedBox ||
621
+ react_native_1.NativeModules?.RCTRedBox ||
622
+ (tmReg?.get ? tmReg.get('RedBox') || tmReg.get('RCTRedBox') : null);
623
+ if (nativeRedBox) {
624
+ if (typeof nativeRedBox.showErrorMessage === 'function') {
625
+ nativeRedBox.showErrorMessage = () => { };
626
+ }
627
+ if (typeof nativeRedBox.showUserError === 'function') {
628
+ nativeRedBox.showUserError = () => { };
629
+ }
630
+ if (typeof nativeRedBox.dismiss === 'function') {
631
+ nativeRedBox.dismiss();
632
+ }
633
+ }
634
+ }
635
+ catch { }
636
+ // 4. Hook React Native ErrorUtils
637
+ try {
638
+ const globalObj = globalThis;
639
+ const errorUtils = globalObj.ErrorUtils;
640
+ if (errorUtils) {
641
+ if (typeof errorUtils.setGlobalHandler === 'function') {
642
+ errorUtils.setGlobalHandler((error, isFatal) => {
643
+ (0, exports.handleInterceptedCrash)(error, error?.stack, isFatal ?? false, 'js');
644
+ // Suppress calling default crashing handler to prevent termination & RedBox
645
+ });
646
+ }
647
+ if (typeof errorUtils.reportFatalError === 'function') {
648
+ errorUtils.reportFatalError = (error) => {
649
+ (0, exports.handleInterceptedCrash)(error, error?.stack, true, 'js');
650
+ };
651
+ }
652
+ if (typeof errorUtils.reportError === 'function') {
653
+ errorUtils.reportError = (error) => {
654
+ (0, exports.handleInterceptedCrash)(error, error?.stack, false, 'js');
655
+ };
656
+ }
657
+ }
658
+ }
659
+ catch { }
660
+ // 5. Hook global Promise unhandled rejections
661
+ try {
662
+ const globalObj = globalThis;
663
+ const handleUnhandledPromise = (eventOrError) => {
664
+ try {
665
+ const reason = eventOrError?.reason || eventOrError?.detail?.reason || eventOrError;
666
+ const message = reason?.message || String(reason || (0, i18n_1.t)('crash.unhandledPromiseRejection'));
667
+ const stack = reason?.stack || (eventOrError?.stack ?? new Error().stack);
668
+ (0, exports.handleInterceptedCrash)(reason, stack, false, 'promise');
669
+ }
670
+ catch { }
671
+ };
672
+ if (typeof globalObj.addEventListener === 'function') {
673
+ globalObj.addEventListener('unhandledrejection', handleUnhandledPromise);
674
+ }
675
+ if (typeof globalObj.onunhandledrejection !== 'undefined') {
676
+ globalObj.onunhandledrejection = handleUnhandledPromise;
677
+ }
678
+ }
679
+ catch { }
680
+ };
681
+ exports.setupGlobalCrashHandler = setupGlobalCrashHandler;