@grafana/faro-react-native 1.3.1 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -30
- package/android/build.gradle +17 -12
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashIds.kt +44 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +134 -38
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashSessionStore.kt +433 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +11 -2
- package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +128 -17
- package/android/src/main/java/com/grafana/faro/reactnative/FaroSessionProcess.kt +88 -0
- package/android/src/main/java/com/grafana/faro/reactnative/FrameMonitor.kt +34 -42
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterBelowApi30Test.kt +21 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterTest.kt +59 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashSessionStoreTest.kt +268 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashTraceCacheTest.kt +52 -0
- package/android/src/test/java/com/grafana/faro/reactnative/FaroSessionProcessTest.kt +127 -0
- package/dist/cjs/config/getRNInstrumentations.js +6 -8
- package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
- package/dist/cjs/config/makeRNConfig.js +5 -5
- package/dist/cjs/config/makeRNConfig.js.map +1 -1
- package/dist/cjs/config/sampling.js.map +1 -1
- package/dist/cjs/config/types.js.map +1 -1
- package/dist/cjs/dataCollection/DataCollectionPolicy.js +1 -2
- package/dist/cjs/dataCollection/DataCollectionPolicy.js.map +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/cjs/index.js +8 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/instrumentations/anr/instrumentation.js +10 -1
- package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/appState/index.js +8 -2
- package/dist/cjs/instrumentations/appState/index.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +19 -6
- package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +331 -0
- package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
- package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
- package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/cjs/instrumentations/errors/index.js +8 -3
- package/dist/cjs/instrumentations/errors/index.js.map +1 -1
- package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js +20 -31
- package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
- package/dist/cjs/instrumentations/frameMonitoring/types.js.map +1 -1
- package/dist/cjs/instrumentations/performance/index.js +2 -6
- package/dist/cjs/instrumentations/performance/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js +48 -0
- package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
- package/dist/cjs/instrumentations/session/directSessionActivity.js +37 -0
- package/dist/cjs/instrumentations/session/directSessionActivity.js.map +1 -0
- package/dist/cjs/instrumentations/session/index.js +214 -45
- package/dist/cjs/instrumentations/session/index.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionActivity.js +43 -0
- package/dist/cjs/instrumentations/session/sessionActivity.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionAttributes.js +8 -8
- package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +199 -24
- package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -13
- package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js +72 -0
- package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
- package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js +124 -96
- package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionManager/types.js.map +1 -1
- package/dist/cjs/instrumentations/session/sessionProcess.js +96 -0
- package/dist/cjs/instrumentations/session/sessionProcess.js.map +1 -0
- package/dist/cjs/instrumentations/startup/index.js +2 -6
- package/dist/cjs/instrumentations/startup/index.js.map +1 -1
- package/dist/cjs/navigation/utils.js +3 -1
- package/dist/cjs/navigation/utils.js.map +1 -1
- package/dist/cjs/transports/fetch/index.js.map +1 -1
- package/dist/cjs/transports/fetch/transport.js +56 -25
- package/dist/cjs/transports/fetch/transport.js.map +1 -1
- package/dist/cjs/transports/fetch/types.js.map +1 -1
- package/dist/cjs/transports/offline/ConnectivityService.js +1 -1
- package/dist/cjs/transports/offline/ConnectivityService.js.map +1 -1
- package/dist/cjs/transports/offline/OfflineCache.js +2 -2
- package/dist/cjs/transports/offline/OfflineCache.js.map +1 -1
- package/dist/cjs/transports/offline/transport.js +3 -4
- package/dist/cjs/transports/offline/transport.js.map +1 -1
- package/dist/cjs/userPersistence/UserPersistence.js +1 -2
- package/dist/cjs/userPersistence/UserPersistence.js.map +1 -1
- package/dist/esm/config/getRNInstrumentations.js +6 -8
- package/dist/esm/config/getRNInstrumentations.js.map +1 -1
- package/dist/esm/config/makeRNConfig.js +5 -5
- package/dist/esm/config/makeRNConfig.js.map +1 -1
- package/dist/esm/config/sampling.js.map +1 -1
- package/dist/esm/config/types.js.map +1 -1
- package/dist/esm/dataCollection/DataCollectionPolicy.js +1 -2
- package/dist/esm/dataCollection/DataCollectionPolicy.js.map +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/instrumentations/anr/instrumentation.js +6 -1
- package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/appState/index.js +8 -2
- package/dist/esm/instrumentations/appState/index.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +15 -4
- package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +230 -0
- package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
- package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
- package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
- package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
- package/dist/esm/instrumentations/errors/index.js +8 -3
- package/dist/esm/instrumentations/errors/index.js.map +1 -1
- package/dist/esm/instrumentations/frameMonitoring/instrumentation.js +17 -32
- package/dist/esm/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
- package/dist/esm/instrumentations/frameMonitoring/types.js.map +1 -1
- package/dist/esm/instrumentations/performance/index.js +2 -6
- package/dist/esm/instrumentations/performance/index.js.map +1 -1
- package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js +34 -0
- package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
- package/dist/esm/instrumentations/session/directSessionActivity.js +32 -0
- package/dist/esm/instrumentations/session/directSessionActivity.js.map +1 -0
- package/dist/esm/instrumentations/session/index.js +207 -46
- package/dist/esm/instrumentations/session/index.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionActivity.js +38 -0
- package/dist/esm/instrumentations/session/sessionActivity.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionAttributes.js +7 -7
- package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +190 -27
- package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -14
- package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js +56 -0
- package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
- package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js +1 -1
- package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js +120 -44
- package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionManager/types.js.map +1 -1
- package/dist/esm/instrumentations/session/sessionProcess.js +89 -0
- package/dist/esm/instrumentations/session/sessionProcess.js.map +1 -0
- package/dist/esm/instrumentations/startup/index.js +2 -6
- package/dist/esm/instrumentations/startup/index.js.map +1 -1
- package/dist/esm/navigation/utils.js +2 -1
- package/dist/esm/navigation/utils.js.map +1 -1
- package/dist/esm/transports/fetch/index.js.map +1 -1
- package/dist/esm/transports/fetch/transport.js +38 -14
- package/dist/esm/transports/fetch/transport.js.map +1 -1
- package/dist/esm/transports/fetch/types.js.map +1 -1
- package/dist/esm/transports/offline/ConnectivityService.js +1 -1
- package/dist/esm/transports/offline/ConnectivityService.js.map +1 -1
- package/dist/esm/transports/offline/OfflineCache.js +2 -2
- package/dist/esm/transports/offline/OfflineCache.js.map +1 -1
- package/dist/esm/transports/offline/transport.js +3 -4
- package/dist/esm/transports/offline/transport.js.map +1 -1
- package/dist/esm/userPersistence/UserPersistence.js +1 -2
- package/dist/esm/userPersistence/UserPersistence.js.map +1 -1
- package/dist/types/config/getRNInstrumentations.d.ts +1 -2
- package/dist/types/config/sampling.d.ts +0 -3
- package/dist/types/config/types.d.ts +5 -3
- package/dist/types/dataCollection/DataCollectionPolicy.d.ts +1 -2
- package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +14 -4
- package/dist/types/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.d.ts +27 -0
- package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +2 -2
- package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +2 -2
- package/dist/types/instrumentations/crashReporting/types.d.ts +6 -0
- package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +2 -2
- package/dist/types/instrumentations/frameMonitoring/types.d.ts +4 -6
- package/dist/types/instrumentations/performance/index.d.ts +0 -2
- package/dist/types/instrumentations/session/FaroSessionActivityBoundary.d.ts +8 -0
- package/dist/types/instrumentations/session/directSessionActivity.d.ts +13 -0
- package/dist/types/instrumentations/session/index.d.ts +20 -1
- package/dist/types/instrumentations/session/sessionActivity.d.ts +9 -0
- package/dist/types/instrumentations/session/sessionAttributes.d.ts +6 -3
- package/dist/types/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.d.ts +16 -3
- package/dist/types/instrumentations/session/sessionManager/VolatileSessionManager.d.ts +9 -3
- package/dist/types/instrumentations/session/sessionManager/persistentSessionRecord.d.ts +4 -0
- package/dist/types/instrumentations/session/sessionManager/sessionManagerUtils.d.ts +19 -7
- package/dist/types/instrumentations/session/sessionManager/types.d.ts +6 -0
- package/dist/types/instrumentations/session/sessionProcess.d.ts +17 -0
- package/dist/types/instrumentations/startup/index.d.ts +2 -6
- package/dist/types/navigation/utils.d.ts +1 -0
- package/dist/types/transports/fetch/index.d.ts +1 -1
- package/dist/types/transports/fetch/transport.d.ts +8 -1
- package/dist/types/transports/fetch/types.d.ts +5 -0
- package/dist/types/transports/offline/ConnectivityService.d.ts +1 -1
- package/dist/types/transports/offline/OfflineCache.d.ts +2 -2
- package/dist/types/transports/offline/transport.d.ts +3 -4
- package/dist/types/userPersistence/UserPersistence.d.ts +1 -2
- package/ios/FaroCrashReporter.swift +214 -21
- package/ios/FaroReactNative.swift +24 -0
- package/ios/FaroReactNativeModule.h +2 -1
- package/ios/FaroReactNativeModule.mm +121 -6
- package/ios/RefreshRateVitals.swift +34 -38
- package/package.json +7 -5
- package/src/config/getRNInstrumentations.ts +6 -8
- package/src/config/makeRNConfig.test.ts +6 -0
- package/src/config/makeRNConfig.ts +10 -9
- package/src/config/sampling.ts +0 -3
- package/src/config/types.ts +5 -3
- package/src/dataCollection/DataCollectionPolicy.ts +1 -2
- package/src/generated/faroRNPackageMeta.ts +1 -1
- package/src/index.ts +8 -2
- package/src/initialize.test.ts +63 -2
- package/src/instrumentations/anr/instrumentation.ts +8 -1
- package/src/instrumentations/appState/index.ts +10 -2
- package/src/instrumentations/crashReporting/BaseCrashReportingInstrumentation.ts +28 -8
- package/src/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.ts +277 -0
- package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +2 -2
- package/src/instrumentations/crashReporting/instrumentation.test.ts +433 -5
- package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +2 -2
- package/src/instrumentations/crashReporting/types.ts +7 -0
- package/src/instrumentations/errors/index.ts +12 -4
- package/src/instrumentations/errors/instrumentation.test.ts +35 -0
- package/src/instrumentations/frameMonitoring/instrumentation.test.ts +155 -0
- package/src/instrumentations/frameMonitoring/instrumentation.ts +19 -38
- package/src/instrumentations/frameMonitoring/types.ts +4 -6
- package/src/instrumentations/performance/index.ts +2 -6
- package/src/instrumentations/session/FaroSessionActivityBoundary.test.ts +60 -0
- package/src/instrumentations/session/FaroSessionActivityBoundary.tsx +41 -0
- package/src/instrumentations/session/beforeSendHook.test.ts +246 -0
- package/src/instrumentations/session/directSessionActivity.test.ts +158 -0
- package/src/instrumentations/session/directSessionActivity.ts +36 -0
- package/src/instrumentations/session/index.ts +269 -46
- package/src/instrumentations/session/persistentColdStart.test.ts +196 -0
- package/src/instrumentations/session/sessionActivity.test.ts +68 -0
- package/src/instrumentations/session/sessionActivity.ts +58 -0
- package/src/instrumentations/session/sessionAttributes.test.ts +37 -2
- package/src/instrumentations/session/sessionAttributes.ts +12 -6
- package/src/instrumentations/session/sessionCleanup.test.ts +410 -0
- package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.test.ts +552 -0
- package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.ts +249 -33
- package/src/instrumentations/session/sessionManager/VolatileSessionManager.ts +38 -18
- package/src/instrumentations/session/sessionManager/persistentSessionRecord.test.ts +103 -0
- package/src/instrumentations/session/sessionManager/persistentSessionRecord.ts +78 -0
- package/src/instrumentations/session/sessionManager/sampling.test.ts +1 -1
- package/src/instrumentations/session/sessionManager/sessionConstants.ts +1 -1
- package/src/instrumentations/session/sessionManager/sessionManagerUtils.test.ts +256 -18
- package/src/instrumentations/session/sessionManager/sessionManagerUtils.ts +180 -39
- package/src/instrumentations/session/sessionManager/types.ts +8 -0
- package/src/instrumentations/session/sessionManagerInstance.test.ts +65 -0
- package/src/instrumentations/session/sessionProcess.test.ts +136 -0
- package/src/instrumentations/session/sessionProcess.ts +114 -0
- package/src/instrumentations/session/startNewSession.test.ts +337 -0
- package/src/instrumentations/startup/index.ts +2 -6
- package/src/navigation/utils.ts +3 -1
- package/src/transports/fetch/index.ts +6 -1
- package/src/transports/fetch/transport.test.ts +86 -2
- package/src/transports/fetch/transport.ts +38 -16
- package/src/transports/fetch/types.ts +7 -0
- package/src/transports/offline/ConnectivityService.ts +1 -1
- package/src/transports/offline/OfflineCache.ts +2 -2
- package/src/transports/offline/transport.ts +3 -4
- package/src/userPersistence/UserPersistence.ts +1 -2
- package/dist/cjs/utils/throttle.js +0 -42
- package/dist/cjs/utils/throttle.js.map +0 -1
- package/dist/esm/utils/throttle.js +0 -34
- package/dist/esm/utils/throttle.js.map +0 -1
- package/dist/types/utils/throttle.d.ts +0 -9
- package/src/utils/throttle.test.ts +0 -141
- package/src/utils/throttle.ts +0 -34
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
var __assign = (this && this.__assign) || function () {
|
|
18
|
+
__assign = Object.assign || function(t) {
|
|
19
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
20
|
+
s = arguments[i];
|
|
21
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
22
|
+
t[p] = s[p];
|
|
23
|
+
}
|
|
24
|
+
return t;
|
|
25
|
+
};
|
|
26
|
+
return __assign.apply(this, arguments);
|
|
27
|
+
};
|
|
28
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
29
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
38
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
39
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
40
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
41
|
+
function step(op) {
|
|
42
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
43
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
44
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
45
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
46
|
+
switch (op[0]) {
|
|
47
|
+
case 0: case 1: t = op; break;
|
|
48
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
49
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
50
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
51
|
+
default:
|
|
52
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
53
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
54
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
55
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
56
|
+
if (t[2]) _.ops.pop();
|
|
57
|
+
_.trys.pop(); continue;
|
|
58
|
+
}
|
|
59
|
+
op = body.call(thisArg, _);
|
|
60
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
61
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
65
|
+
exports.RecoveredCrashReportingInstrumentation = void 0;
|
|
66
|
+
var faro_core_1 = require("@grafana/faro-core");
|
|
67
|
+
var fetch_1 = require("../../transports/fetch");
|
|
68
|
+
var BaseCrashReportingInstrumentation_1 = require("./BaseCrashReportingInstrumentation");
|
|
69
|
+
// Keep aligned with FaroCrashSessionStore.MAX_CONTEXT_AGE_MS on Android.
|
|
70
|
+
var MAX_REPLAY_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
71
|
+
/**
|
|
72
|
+
* Shared recovered-crash delivery contract for native platforms.
|
|
73
|
+
*
|
|
74
|
+
* Native code owns crash-time persistence. JavaScript owns validation, transport
|
|
75
|
+
* acceptance, and acknowledgement so retryable failures never delete a report.
|
|
76
|
+
*/
|
|
77
|
+
var RecoveredCrashReportingInstrumentation = /** @class */ (function (_super) {
|
|
78
|
+
__extends(RecoveredCrashReportingInstrumentation, _super);
|
|
79
|
+
function RecoveredCrashReportingInstrumentation() {
|
|
80
|
+
var _this = _super !== null && _super.apply(this, arguments) || this;
|
|
81
|
+
_this.nativeModule = null;
|
|
82
|
+
_this.supportsSessionReplay = false;
|
|
83
|
+
_this.metasListenerRegistered = false;
|
|
84
|
+
_this.lastRecordedSessionSignature = null;
|
|
85
|
+
_this.activeSessionId = null;
|
|
86
|
+
_this.activeSessionActivatedAtMs = 0;
|
|
87
|
+
_this.recordSessionContextListener = function (meta) {
|
|
88
|
+
_this.recordSessionContext(meta);
|
|
89
|
+
};
|
|
90
|
+
return _this;
|
|
91
|
+
}
|
|
92
|
+
RecoveredCrashReportingInstrumentation.prototype.prepareCrashReporting = function (nativeModule) {
|
|
93
|
+
this.supportsSessionReplay =
|
|
94
|
+
typeof nativeModule.recordCrashSessionContext === 'function' &&
|
|
95
|
+
typeof nativeModule.getPendingCrashReports === 'function' &&
|
|
96
|
+
typeof nativeModule.acknowledgeCrashReports === 'function';
|
|
97
|
+
if (!this.supportsSessionReplay) {
|
|
98
|
+
this.logWarn('Native module does not support original-session crash replay; using legacy crash reporting');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.nativeModule = nativeModule;
|
|
102
|
+
this.recordSessionContext(this.metas.value);
|
|
103
|
+
if (!this.metasListenerRegistered) {
|
|
104
|
+
this.metas.addListener(this.recordSessionContextListener);
|
|
105
|
+
this.metasListenerRegistered = true;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
RecoveredCrashReportingInstrumentation.prototype.processCrashReports = function (nativeModule) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
110
|
+
var replayStartedAtMs, crashReports, processedReportIds, _i, crashReports_1, crashJson, parsedCrash, crash, reportId, sessionId, outcome, error_1;
|
|
111
|
+
var _a, _b;
|
|
112
|
+
return __generator(this, function (_c) {
|
|
113
|
+
switch (_c.label) {
|
|
114
|
+
case 0:
|
|
115
|
+
if (!!this.supportsSessionReplay) return [3 /*break*/, 2];
|
|
116
|
+
return [4 /*yield*/, _super.prototype.processCrashReports.call(this, nativeModule)];
|
|
117
|
+
case 1:
|
|
118
|
+
_c.sent();
|
|
119
|
+
return [2 /*return*/];
|
|
120
|
+
case 2:
|
|
121
|
+
_c.trys.push([2, 17, , 18]);
|
|
122
|
+
replayStartedAtMs = Date.now();
|
|
123
|
+
return [4 /*yield*/, nativeModule.getPendingCrashReports()];
|
|
124
|
+
case 3:
|
|
125
|
+
crashReports = (_c.sent());
|
|
126
|
+
if (!(crashReports === null || crashReports === void 0 ? void 0 : crashReports.length)) {
|
|
127
|
+
this.logDebug('No crash reports from previous session');
|
|
128
|
+
return [2 /*return*/];
|
|
129
|
+
}
|
|
130
|
+
processedReportIds = new Set();
|
|
131
|
+
_i = 0, crashReports_1 = crashReports;
|
|
132
|
+
_c.label = 4;
|
|
133
|
+
case 4:
|
|
134
|
+
if (!(_i < crashReports_1.length)) return [3 /*break*/, 16];
|
|
135
|
+
crashJson = crashReports_1[_i];
|
|
136
|
+
parsedCrash = void 0;
|
|
137
|
+
try {
|
|
138
|
+
parsedCrash = JSON.parse(crashJson);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
this.logError('Failed to parse recovered crash report', error);
|
|
142
|
+
return [3 /*break*/, 15];
|
|
143
|
+
}
|
|
144
|
+
if (!parsedCrash || typeof parsedCrash !== 'object' || Array.isArray(parsedCrash)) {
|
|
145
|
+
this.logWarn('Skipping malformed recovered crash report');
|
|
146
|
+
return [3 /*break*/, 15];
|
|
147
|
+
}
|
|
148
|
+
crash = parsedCrash;
|
|
149
|
+
reportId = (_a = crash.reportId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
150
|
+
if (!reportId) {
|
|
151
|
+
this.logWarn('Skipping malformed recovered crash report');
|
|
152
|
+
return [3 /*break*/, 15];
|
|
153
|
+
}
|
|
154
|
+
if (!!this.hasValidTimestamp(crash)) return [3 /*break*/, 6];
|
|
155
|
+
this.logWarn("Discarding malformed recovered crash ".concat(reportId));
|
|
156
|
+
return [4 /*yield*/, this.acknowledgeCrashReport(nativeModule, reportId)];
|
|
157
|
+
case 5:
|
|
158
|
+
_c.sent();
|
|
159
|
+
return [3 /*break*/, 15];
|
|
160
|
+
case 6:
|
|
161
|
+
if (processedReportIds.has(reportId)) {
|
|
162
|
+
this.logDebug("Skipping duplicate recovered crash report ".concat(reportId));
|
|
163
|
+
return [3 /*break*/, 15];
|
|
164
|
+
}
|
|
165
|
+
processedReportIds.add(reportId);
|
|
166
|
+
if (!!this.isWithinReplayWindow(crash.timestamp, replayStartedAtMs)) return [3 /*break*/, 8];
|
|
167
|
+
return [4 /*yield*/, this.acknowledgeCrashReport(nativeModule, reportId)];
|
|
168
|
+
case 7:
|
|
169
|
+
_c.sent();
|
|
170
|
+
return [3 /*break*/, 15];
|
|
171
|
+
case 8:
|
|
172
|
+
if (!this.shouldSkipCrashReport(crash)) return [3 /*break*/, 10];
|
|
173
|
+
return [4 /*yield*/, this.acknowledgeCrashReport(nativeModule, reportId)];
|
|
174
|
+
case 9:
|
|
175
|
+
_c.sent();
|
|
176
|
+
return [3 /*break*/, 15];
|
|
177
|
+
case 10:
|
|
178
|
+
sessionId = (_b = crash.sessionId) === null || _b === void 0 ? void 0 : _b.trim();
|
|
179
|
+
if (!!sessionId) return [3 /*break*/, 12];
|
|
180
|
+
this.logWarn("Discarding recovered crash ".concat(reportId, " because its original session context is unavailable"));
|
|
181
|
+
return [4 /*yield*/, this.acknowledgeCrashReport(nativeModule, reportId)];
|
|
182
|
+
case 11:
|
|
183
|
+
_c.sent();
|
|
184
|
+
return [3 /*break*/, 15];
|
|
185
|
+
case 12: return [4 /*yield*/, this.replayCrashReport(__assign(__assign({}, crash), { reportId: reportId, sessionId: sessionId }))];
|
|
186
|
+
case 13:
|
|
187
|
+
outcome = _c.sent();
|
|
188
|
+
if (!(outcome === 'accepted' || outcome === 'filtered')) return [3 /*break*/, 15];
|
|
189
|
+
return [4 /*yield*/, this.acknowledgeCrashReport(nativeModule, reportId)];
|
|
190
|
+
case 14:
|
|
191
|
+
_c.sent();
|
|
192
|
+
_c.label = 15;
|
|
193
|
+
case 15:
|
|
194
|
+
_i++;
|
|
195
|
+
return [3 /*break*/, 4];
|
|
196
|
+
case 16: return [3 /*break*/, 18];
|
|
197
|
+
case 17:
|
|
198
|
+
error_1 = _c.sent();
|
|
199
|
+
this.logError('Failed to process recovered crash reports', error_1);
|
|
200
|
+
return [3 /*break*/, 18];
|
|
201
|
+
case 18: return [2 /*return*/];
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
RecoveredCrashReportingInstrumentation.prototype.recordSessionContext = function (meta) {
|
|
207
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
208
|
+
var sessionId = (_b = (_a = meta.session) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.trim();
|
|
209
|
+
if (!sessionId || !this.nativeModule) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (this.activeSessionId !== sessionId) {
|
|
213
|
+
this.activeSessionId = sessionId;
|
|
214
|
+
this.activeSessionActivatedAtMs = Date.now();
|
|
215
|
+
this.activeSessionIsSampled = undefined;
|
|
216
|
+
}
|
|
217
|
+
var sampledValue = (_d = (_c = meta.session) === null || _c === void 0 ? void 0 : _c.attributes) === null || _d === void 0 ? void 0 : _d['isSampled'];
|
|
218
|
+
var incomingIsSampled = sampledValue === 'true' ? true : sampledValue === 'false' ? false : undefined;
|
|
219
|
+
if (incomingIsSampled !== undefined) {
|
|
220
|
+
this.activeSessionIsSampled = incomingIsSampled;
|
|
221
|
+
}
|
|
222
|
+
var isSampled = incomingIsSampled !== null && incomingIsSampled !== void 0 ? incomingIsSampled : this.activeSessionIsSampled;
|
|
223
|
+
var sessionContext = __assign(__assign(__assign(__assign({ sessionId: sessionId, activatedAt: this.activeSessionActivatedAtMs }, (isSampled === undefined ? {} : { isSampled: isSampled })), (((_e = meta.app) === null || _e === void 0 ? void 0 : _e.version) ? { appVersion: meta.app.version } : {})), (((_f = meta.app) === null || _f === void 0 ? void 0 : _f.release) ? { appRelease: meta.app.release } : {})), (((_g = meta.app) === null || _g === void 0 ? void 0 : _g.bundleId) ? { appBundleId: meta.app.bundleId } : {}));
|
|
224
|
+
var signature = JSON.stringify(sessionContext);
|
|
225
|
+
if (signature === this.lastRecordedSessionSignature) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
var stored = this.nativeModule.recordCrashSessionContext(sessionContext);
|
|
230
|
+
if (stored === true) {
|
|
231
|
+
this.lastRecordedSessionSignature = signature;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
this.logError('Failed to persist crash session context', error);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
RecoveredCrashReportingInstrumentation.prototype.replayCrashReport = function (crash) {
|
|
239
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
240
|
+
var fetchTransport, item, _i, _a, hook, modifiedItem, result;
|
|
241
|
+
return __generator(this, function (_b) {
|
|
242
|
+
switch (_b.label) {
|
|
243
|
+
case 0:
|
|
244
|
+
if (this.transports.isPaused()) {
|
|
245
|
+
return [2 /*return*/, 'retry'];
|
|
246
|
+
}
|
|
247
|
+
fetchTransport = this.transports.transports.find(function (transport) { return transport instanceof fetch_1.FetchTransport; });
|
|
248
|
+
if (!fetchTransport) {
|
|
249
|
+
this.logWarn('Fetch transport is unavailable; leaving recovered crash pending');
|
|
250
|
+
return [2 /*return*/, 'retry'];
|
|
251
|
+
}
|
|
252
|
+
item = this.buildRecoveredCrashItem(crash);
|
|
253
|
+
try {
|
|
254
|
+
for (_i = 0, _a = this.transports.getBeforeSendHooks(); _i < _a.length; _i++) {
|
|
255
|
+
hook = _a[_i];
|
|
256
|
+
modifiedItem = hook(item);
|
|
257
|
+
// Match faro-core's filter(Boolean) behavior for invalid hook results.
|
|
258
|
+
if (!modifiedItem) {
|
|
259
|
+
return [2 /*return*/, 'filtered'];
|
|
260
|
+
}
|
|
261
|
+
item = modifiedItem;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
this.logError('Failed to apply beforeSend hook to recovered crash', error);
|
|
266
|
+
return [2 /*return*/, 'retry'];
|
|
267
|
+
}
|
|
268
|
+
return [4 /*yield*/, fetchTransport.sendWithResult([item])];
|
|
269
|
+
case 1:
|
|
270
|
+
result = _b.sent();
|
|
271
|
+
return [2 /*return*/, result.outcome === 'accepted' ? 'accepted' : 'retry'];
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
RecoveredCrashReportingInstrumentation.prototype.buildRecoveredCrashItem = function (crash) {
|
|
277
|
+
var _a;
|
|
278
|
+
var _b = this.buildCrashReportDetails(crash), context = _b.context, errorMessage = _b.errorMessage, stackFrames = _b.stackFrames;
|
|
279
|
+
var currentMeta = this.metas.value;
|
|
280
|
+
var isSampledAttribute = crash.isSampled === undefined ? undefined : { isSampled: String(crash.isSampled) };
|
|
281
|
+
// Current user, view, and page metadata belong to the restarted session.
|
|
282
|
+
var meta = __assign(__assign(__assign(__assign(__assign({}, (currentMeta.sdk ? { sdk: __assign({}, currentMeta.sdk) } : {})), { app: __assign(__assign(__assign(__assign({}, ((_a = currentMeta.app) !== null && _a !== void 0 ? _a : {})), (crash.appVersion ? { version: crash.appVersion } : {})), (crash.appRelease ? { release: crash.appRelease } : {})), (crash.appBundleId ? { bundleId: crash.appBundleId } : {})) }), (currentMeta.os ? { os: __assign({}, currentMeta.os) } : {})), (currentMeta.device ? { device: __assign({}, currentMeta.device) } : {})), { session: __assign({ id: crash.sessionId }, (isSampledAttribute ? { attributes: isSampledAttribute } : {})) });
|
|
283
|
+
return {
|
|
284
|
+
type: faro_core_1.TransportItemType.EXCEPTION,
|
|
285
|
+
meta: meta,
|
|
286
|
+
payload: __assign({ type: 'crash', value: errorMessage, timestamp: new Date(crash.timestamp).toISOString(), context: context, fatal: true }, (stackFrames.length ? { stacktrace: { frames: stackFrames } } : {})),
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
RecoveredCrashReportingInstrumentation.prototype.acknowledgeCrashReport = function (nativeModule, reportId) {
|
|
290
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
291
|
+
var error_2;
|
|
292
|
+
return __generator(this, function (_a) {
|
|
293
|
+
switch (_a.label) {
|
|
294
|
+
case 0:
|
|
295
|
+
_a.trys.push([0, 2, , 3]);
|
|
296
|
+
return [4 /*yield*/, nativeModule.acknowledgeCrashReports([reportId])];
|
|
297
|
+
case 1:
|
|
298
|
+
_a.sent();
|
|
299
|
+
return [3 /*break*/, 3];
|
|
300
|
+
case 2:
|
|
301
|
+
error_2 = _a.sent();
|
|
302
|
+
this.logError("Failed to acknowledge recovered crash ".concat(reportId), error_2);
|
|
303
|
+
return [3 /*break*/, 3];
|
|
304
|
+
case 3: return [2 /*return*/];
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
RecoveredCrashReportingInstrumentation.prototype.hasValidTimestamp = function (crash) {
|
|
310
|
+
return typeof crash.timestamp === 'number' && Number.isFinite(crash.timestamp) && crash.timestamp > 0;
|
|
311
|
+
};
|
|
312
|
+
RecoveredCrashReportingInstrumentation.prototype.isWithinReplayWindow = function (timestampMs, replayStartedAtMs) {
|
|
313
|
+
return timestampMs <= replayStartedAtMs && replayStartedAtMs - timestampMs <= MAX_REPLAY_AGE_MS;
|
|
314
|
+
};
|
|
315
|
+
RecoveredCrashReportingInstrumentation.prototype.unpatch = function () {
|
|
316
|
+
if (this.metasListenerRegistered) {
|
|
317
|
+
this.metas.removeListener(this.recordSessionContextListener);
|
|
318
|
+
this.metasListenerRegistered = false;
|
|
319
|
+
}
|
|
320
|
+
this.nativeModule = null;
|
|
321
|
+
this.supportsSessionReplay = false;
|
|
322
|
+
this.lastRecordedSessionSignature = null;
|
|
323
|
+
this.activeSessionId = null;
|
|
324
|
+
this.activeSessionActivatedAtMs = 0;
|
|
325
|
+
this.activeSessionIsSampled = undefined;
|
|
326
|
+
_super.prototype.unpatch.call(this);
|
|
327
|
+
};
|
|
328
|
+
return RecoveredCrashReportingInstrumentation;
|
|
329
|
+
}(BaseCrashReportingInstrumentation_1.BaseCrashReportingInstrumentation));
|
|
330
|
+
exports.RecoveredCrashReportingInstrumentation = RecoveredCrashReportingInstrumentation;
|
|
331
|
+
//# sourceMappingURL=RecoveredCrashReportingInstrumentation.js.map
|
package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecoveredCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../src/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModules } from 'react-native';\n\nimport { TransportItemType } from '@grafana/faro-core';\nimport type { ExceptionEvent, Meta, TransportItem } from '@grafana/faro-core';\n\nimport { FetchTransport } from '../../transports/fetch';\n\nimport { BaseCrashReportingInstrumentation } from './BaseCrashReportingInstrumentation';\nimport type { CrashReport } from './types';\n\n// Keep aligned with FaroCrashSessionStore.MAX_CONTEXT_AGE_MS on Android.\nconst MAX_REPLAY_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\ntype RecoveredCrashReport = CrashReport & {\n reportId: string;\n sessionId: string;\n timestamp: number;\n};\n\n/**\n * Shared recovered-crash delivery contract for native platforms.\n *\n * Native code owns crash-time persistence. JavaScript owns validation, transport\n * acceptance, and acknowledgement so retryable failures never delete a report.\n */\nexport abstract class RecoveredCrashReportingInstrumentation extends BaseCrashReportingInstrumentation {\n private nativeModule: typeof NativeModules.FaroReactNativeModule | null = null;\n private supportsSessionReplay = false;\n private metasListenerRegistered = false;\n private lastRecordedSessionSignature: string | null = null;\n private activeSessionId: string | null = null;\n private activeSessionActivatedAtMs = 0;\n private activeSessionIsSampled: boolean | undefined;\n\n private readonly recordSessionContextListener = (meta: Meta): void => {\n this.recordSessionContext(meta);\n };\n\n protected override prepareCrashReporting(nativeModule: typeof NativeModules.FaroReactNativeModule): void {\n this.supportsSessionReplay =\n typeof nativeModule.recordCrashSessionContext === 'function' &&\n typeof nativeModule.getPendingCrashReports === 'function' &&\n typeof nativeModule.acknowledgeCrashReports === 'function';\n\n if (!this.supportsSessionReplay) {\n this.logWarn('Native module does not support original-session crash replay; using legacy crash reporting');\n return;\n }\n\n this.nativeModule = nativeModule;\n this.recordSessionContext(this.metas.value);\n if (!this.metasListenerRegistered) {\n this.metas.addListener(this.recordSessionContextListener);\n this.metasListenerRegistered = true;\n }\n }\n\n protected override async processCrashReports(\n nativeModule: typeof NativeModules.FaroReactNativeModule\n ): Promise<void> {\n if (!this.supportsSessionReplay) {\n await super.processCrashReports(nativeModule);\n return;\n }\n\n try {\n const replayStartedAtMs = Date.now();\n const crashReports = (await nativeModule.getPendingCrashReports()) as string[] | null;\n if (!crashReports?.length) {\n this.logDebug('No crash reports from previous session');\n return;\n }\n\n const processedReportIds = new Set<string>();\n for (const crashJson of crashReports) {\n let parsedCrash: unknown;\n try {\n parsedCrash = JSON.parse(crashJson);\n } catch (error) {\n this.logError('Failed to parse recovered crash report', error);\n continue;\n }\n if (!parsedCrash || typeof parsedCrash !== 'object' || Array.isArray(parsedCrash)) {\n this.logWarn('Skipping malformed recovered crash report');\n continue;\n }\n const crash = parsedCrash as CrashReport;\n\n const reportId = crash.reportId?.trim();\n if (!reportId) {\n this.logWarn('Skipping malformed recovered crash report');\n continue;\n }\n if (!this.hasValidTimestamp(crash)) {\n this.logWarn(`Discarding malformed recovered crash ${reportId}`);\n await this.acknowledgeCrashReport(nativeModule, reportId);\n continue;\n }\n if (processedReportIds.has(reportId)) {\n this.logDebug(`Skipping duplicate recovered crash report ${reportId}`);\n continue;\n }\n processedReportIds.add(reportId);\n\n if (!this.isWithinReplayWindow(crash.timestamp, replayStartedAtMs)) {\n await this.acknowledgeCrashReport(nativeModule, reportId);\n continue;\n }\n if (this.shouldSkipCrashReport(crash)) {\n await this.acknowledgeCrashReport(nativeModule, reportId);\n continue;\n }\n\n const sessionId = crash.sessionId?.trim();\n if (!sessionId) {\n this.logWarn(`Discarding recovered crash ${reportId} because its original session context is unavailable`);\n await this.acknowledgeCrashReport(nativeModule, reportId);\n continue;\n }\n\n const outcome = await this.replayCrashReport({\n ...crash,\n reportId,\n sessionId,\n });\n if (outcome === 'accepted' || outcome === 'filtered') {\n await this.acknowledgeCrashReport(nativeModule, reportId);\n }\n }\n } catch (error) {\n this.logError('Failed to process recovered crash reports', error);\n }\n }\n\n private recordSessionContext(meta: Meta): void {\n const sessionId = meta.session?.id?.trim();\n if (!sessionId || !this.nativeModule) {\n return;\n }\n\n if (this.activeSessionId !== sessionId) {\n this.activeSessionId = sessionId;\n this.activeSessionActivatedAtMs = Date.now();\n this.activeSessionIsSampled = undefined;\n }\n\n const sampledValue = meta.session?.attributes?.['isSampled'];\n const incomingIsSampled = sampledValue === 'true' ? true : sampledValue === 'false' ? false : undefined;\n if (incomingIsSampled !== undefined) {\n this.activeSessionIsSampled = incomingIsSampled;\n }\n const isSampled = incomingIsSampled ?? this.activeSessionIsSampled;\n const sessionContext = {\n sessionId,\n activatedAt: this.activeSessionActivatedAtMs,\n ...(isSampled === undefined ? {} : { isSampled }),\n ...(meta.app?.version ? { appVersion: meta.app.version } : {}),\n ...(meta.app?.release ? { appRelease: meta.app.release } : {}),\n ...(meta.app?.bundleId ? { appBundleId: meta.app.bundleId } : {}),\n };\n const signature = JSON.stringify(sessionContext);\n if (signature === this.lastRecordedSessionSignature) {\n return;\n }\n\n try {\n const stored = this.nativeModule.recordCrashSessionContext(sessionContext);\n if (stored === true) {\n this.lastRecordedSessionSignature = signature;\n }\n } catch (error) {\n this.logError('Failed to persist crash session context', error);\n }\n }\n\n private async replayCrashReport(crash: RecoveredCrashReport): Promise<'accepted' | 'filtered' | 'retry'> {\n if (this.transports.isPaused()) {\n return 'retry';\n }\n\n const fetchTransport = this.transports.transports.find(\n (transport): transport is FetchTransport => transport instanceof FetchTransport\n );\n if (!fetchTransport) {\n this.logWarn('Fetch transport is unavailable; leaving recovered crash pending');\n return 'retry';\n }\n\n // Recovered crashes intentionally bypass transport fan-out. Native storage\n // handles retries, and only FetchTransport reports collector acceptance before ack.\n let item: TransportItem = this.buildRecoveredCrashItem(crash);\n try {\n for (const hook of this.transports.getBeforeSendHooks()) {\n const modifiedItem = hook(item);\n // Match faro-core's filter(Boolean) behavior for invalid hook results.\n if (!modifiedItem) {\n return 'filtered';\n }\n item = modifiedItem;\n }\n } catch (error) {\n this.logError('Failed to apply beforeSend hook to recovered crash', error);\n return 'retry';\n }\n\n const result = await fetchTransport.sendWithResult([item]);\n return result.outcome === 'accepted' ? 'accepted' : 'retry';\n }\n\n private buildRecoveredCrashItem(crash: RecoveredCrashReport): TransportItem<ExceptionEvent> {\n const { context, errorMessage, stackFrames } = this.buildCrashReportDetails(crash);\n const currentMeta = this.metas.value;\n const isSampledAttribute = crash.isSampled === undefined ? undefined : { isSampled: String(crash.isSampled) };\n // Current user, view, and page metadata belong to the restarted session.\n const meta: Meta = {\n ...(currentMeta.sdk ? { sdk: { ...currentMeta.sdk } } : {}),\n app: {\n ...(currentMeta.app ?? {}),\n ...(crash.appVersion ? { version: crash.appVersion } : {}),\n ...(crash.appRelease ? { release: crash.appRelease } : {}),\n ...(crash.appBundleId ? { bundleId: crash.appBundleId } : {}),\n },\n ...(currentMeta.os ? { os: { ...currentMeta.os } } : {}),\n ...(currentMeta.device ? { device: { ...currentMeta.device } } : {}),\n session: {\n id: crash.sessionId,\n ...(isSampledAttribute ? { attributes: isSampledAttribute } : {}),\n },\n };\n\n return {\n type: TransportItemType.EXCEPTION,\n meta,\n payload: {\n type: 'crash',\n value: errorMessage,\n timestamp: new Date(crash.timestamp).toISOString(),\n context,\n fatal: true,\n ...(stackFrames.length ? { stacktrace: { frames: stackFrames } } : {}),\n },\n };\n }\n\n private async acknowledgeCrashReport(\n nativeModule: typeof NativeModules.FaroReactNativeModule,\n reportId: string\n ): Promise<void> {\n try {\n await nativeModule.acknowledgeCrashReports([reportId]);\n } catch (error) {\n this.logError(`Failed to acknowledge recovered crash ${reportId}`, error);\n }\n }\n\n private hasValidTimestamp(crash: CrashReport): crash is CrashReport & { timestamp: number } {\n return typeof crash.timestamp === 'number' && Number.isFinite(crash.timestamp) && crash.timestamp > 0;\n }\n\n private isWithinReplayWindow(timestampMs: number, replayStartedAtMs: number): boolean {\n return timestampMs <= replayStartedAtMs && replayStartedAtMs - timestampMs <= MAX_REPLAY_AGE_MS;\n }\n\n override unpatch(): void {\n if (this.metasListenerRegistered) {\n this.metas.removeListener(this.recordSessionContextListener);\n this.metasListenerRegistered = false;\n }\n this.nativeModule = null;\n this.supportsSessionReplay = false;\n this.lastRecordedSessionSignature = null;\n this.activeSessionId = null;\n this.activeSessionActivatedAtMs = 0;\n this.activeSessionIsSampled = undefined;\n super.unpatch();\n }\n}\n"]}
|
package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js
CHANGED
|
@@ -26,7 +26,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
26
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
27
|
exports.AndroidCrashReportingInstrumentation = void 0;
|
|
28
28
|
var faro_core_1 = require("@grafana/faro-core");
|
|
29
|
-
var
|
|
29
|
+
var RecoveredCrashReportingInstrumentation_1 = require("../RecoveredCrashReportingInstrumentation");
|
|
30
30
|
var crashErrorMessage_1 = require("./crashErrorMessage");
|
|
31
31
|
var parseAndroidCrashTrace_1 = require("./parseAndroidCrashTrace");
|
|
32
32
|
/**
|
|
@@ -82,6 +82,6 @@ var AndroidCrashReportingInstrumentation = /** @class */ (function (_super) {
|
|
|
82
82
|
return 'Native crash trace unavailable (ApplicationExitInfo traceInputStream was null at crash time)';
|
|
83
83
|
};
|
|
84
84
|
return AndroidCrashReportingInstrumentation;
|
|
85
|
-
}(
|
|
85
|
+
}(RecoveredCrashReportingInstrumentation_1.RecoveredCrashReportingInstrumentation));
|
|
86
86
|
exports.AndroidCrashReportingInstrumentation = AndroidCrashReportingInstrumentation;
|
|
87
87
|
//# sourceMappingURL=AndroidCrashReportingInstrumentation.js.map
|
package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AndroidCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAA6C;AAE7C,
|
|
1
|
+
{"version":3,"file":"AndroidCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAA6C;AAE7C,oGAAmG;AAGnG,yDAAsF;AACtF,mEAAgG;AAEhG;;;;;;;;;;;GAWG;AACH;IAA0D,wDAAsC;IAAhG;;QACW,UAAI,GAAG,0DAA0D,CAAC;QAClE,aAAO,GAAG,mBAAO,CAAC;;IAuC7B,CAAC;IArCW,8DAAe,GAAzB,UAA0B,KAAa;QACrC,OAAO,IAAA,+CAAsB,EAAC,KAAK,EAAE;YACnC,qBAAqB,EAAE,IAAI,CAAC,OAAO,CAAC,qBAAqB;SAC1D,CAAC,CAAC;IACL,CAAC;IAES,uEAAwB,GAAlC,UAAmC,KAAkB,EAAE,WAA2C;QAChG,OAAO,IAAA,4CAAwB,EAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC;IAES,oEAAqB,GAA/B,UAAgC,KAAkB;QAChD,OAAO,IAAA,yCAAqB,EAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAES,6DAAc,GAAxB,UAAyB,WAA2C;;QAClE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,gGAAgG;QAChG,qDAAW,CAAC,MAAA,WAAW,CAAC,QAAQ,mCAAI,EAAE,CAAC,SAAK,CAAC,MAAA,WAAW,CAAC,MAAM,mCAAI,EAAE,CAAC,SAAK,CAAC,MAAA,WAAW,CAAC,YAAY,mCAAI,EAAE,CAAC,QAAE;IAC/G,CAAC;IAES,mEAAoB,GAA9B,UAA+B,WAA2C,EAAE,OAA+B;QACzG,yEAAyE;QACzE,gFAAgF;QAChF,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,aAAa,EAAE,CAAC;YAC/B,OAAO,CAAC,qBAAqB,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC;QAC7D,CAAC;IACH,CAAC;IAES,qEAAsB,GAAhC;QACE,OAAO,+GAA+G,CAAC;IACzH,CAAC;IAES,yEAA0B,GAApC;QACE,OAAO,8FAA8F,CAAC;IACxG,CAAC;IACH,2CAAC;AAAD,CAAC,AAzCD,CAA0D,+EAAsC,GAyC/F;AAzCY,oFAAoC","sourcesContent":["import { VERSION } from '@grafana/faro-core';\n\nimport { RecoveredCrashReportingInstrumentation } from '../RecoveredCrashReportingInstrumentation';\nimport type { CrashReport } from '../types';\n\nimport { resolveCrashErrorMessage, shouldSkipCrashReport } from './crashErrorMessage';\nimport { parseAndroidCrashTrace, type ParsedAndroidCrashTrace } from './parseAndroidCrashTrace';\n\n/**\n * Android Crash Reporting Instrumentation.\n *\n * Uses ApplicationExitInfo API (Android 11+ / API 30+) to retrieve crash reports\n * from previous sessions. Captures:\n * - CRASH: Java/Kotlin exception crashes\n * - CRASH_NATIVE: Native (NDK) crashes\n * - LOW_MEMORY: App killed due to low memory\n * - EXCESSIVE_RESOURCE_USAGE: App killed due to excessive resource usage\n *\n * ANRs are reported separately by ANRInstrumentation, not via ApplicationExitInfo replay.\n */\nexport class AndroidCrashReportingInstrumentation extends RecoveredCrashReportingInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-crash-android';\n readonly version = VERSION;\n\n protected parseCrashTrace(trace: string): ParsedAndroidCrashTrace | null {\n return parseAndroidCrashTrace(trace, {\n releaseBundleFilename: this.options.releaseBundleFilename,\n });\n }\n\n protected resolveCrashErrorMessage(crash: CrashReport, parsedTrace: ParsedAndroidCrashTrace | null): string {\n return resolveCrashErrorMessage(crash, parsedTrace);\n }\n\n protected shouldSkipCrashReport(crash: CrashReport): boolean {\n return shouldSkipCrashReport(crash);\n }\n\n protected getStackFrames(parsedTrace: ParsedAndroidCrashTrace | null): any[] {\n if (!parsedTrace) {\n return [];\n }\n // Android has JS frames, Java/Kotlin frames (R8 retrace), and NDK tombstone rows (.so retrace).\n return [...(parsedTrace.jsFrames ?? []), ...(parsedTrace.frames ?? []), ...(parsedTrace.nativeFrames ?? [])];\n }\n\n protected buildPlatformContext(parsedTrace: ParsedAndroidCrashTrace | null, context: Record<string, string>): void {\n // Store the native Java/Kotlin exception class separately for drill-down\n // Plugin overview metrics key off exception_type = 'crash', so keep that stable\n if (parsedTrace?.exceptionType) {\n context['nativeExceptionType'] = parsedTrace.exceptionType;\n }\n }\n\n protected getParseFailureWarning(): string {\n return 'Native crash trace present but no frames parsed; check Android trace format or UncaughtExceptionHandler cache';\n }\n\n protected getTraceUnavailableWarning(): string {\n return 'Native crash trace unavailable (ApplicationExitInfo traceInputStream was null at crash time)';\n }\n}\n"]}
|
|
@@ -17,7 +17,7 @@ var __extends = (this && this.__extends) || (function () {
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.IosCrashReportingInstrumentation = void 0;
|
|
19
19
|
var faro_core_1 = require("@grafana/faro-core");
|
|
20
|
-
var
|
|
20
|
+
var RecoveredCrashReportingInstrumentation_1 = require("../RecoveredCrashReportingInstrumentation");
|
|
21
21
|
var crashErrorMessage_1 = require("./crashErrorMessage");
|
|
22
22
|
var parseIosCrashTrace_1 = require("./parseIosCrashTrace");
|
|
23
23
|
/**
|
|
@@ -73,6 +73,6 @@ var IosCrashReportingInstrumentation = /** @class */ (function (_super) {
|
|
|
73
73
|
return 'Native crash trace unavailable (PLCrashReporter trace was null at crash time)';
|
|
74
74
|
};
|
|
75
75
|
return IosCrashReportingInstrumentation;
|
|
76
|
-
}(
|
|
76
|
+
}(RecoveredCrashReportingInstrumentation_1.RecoveredCrashReportingInstrumentation));
|
|
77
77
|
exports.IosCrashReportingInstrumentation = IosCrashReportingInstrumentation;
|
|
78
78
|
//# sourceMappingURL=IosCrashReportingInstrumentation.js.map
|
package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IosCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,gDAA6C;AAE7C,
|
|
1
|
+
{"version":3,"file":"IosCrashReportingInstrumentation.js","sourceRoot":"","sources":["../../../../../src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,gDAA6C;AAE7C,oGAAmG;AAGnG,yDAAsF;AACtF,2DAAoF;AAEpF;;;;;;;;;;;;;GAaG;AACH;IAAsD,oDAAsC;IAA5F;;QACW,UAAI,GAAG,sDAAsD,CAAC;QAC9D,aAAO,GAAG,mBAAO,CAAC;;IAqC7B,CAAC;IAnCW,0DAAe,GAAzB,UAA0B,KAAa;QACrC,OAAO,IAAA,uCAAkB,EAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAES,mEAAwB,GAAlC,UAAmC,KAAkB,EAAE,WAAuC;QAC5F,OAAO,IAAA,4CAAwB,EAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC;IAES,gEAAqB,GAA/B,UAAgC,KAAkB;QAChD,OAAO,IAAA,yCAAqB,EAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAES,yDAAc,GAAxB,UAAyB,WAAuC;;QAC9D,OAAO,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,mCAAI,EAAE,CAAC;IACnC,CAAC;IAES,+DAAoB,GAA9B,UAA+B,WAAuC,EAAE,OAA+B;QACrG,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,aAAa,EAAE,CAAC;YAC/B,OAAO,CAAC,qBAAqB,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC;QAC7D,CAAC;QACD,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,UAAU,EAAE,CAAC;YAC5B,OAAO,CAAC,YAAY,CAAC,GAAG,WAAW,CAAC,UAAU,CAAC;QACjD,CAAC;QACD,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,iBAAiB,EAAE,CAAC;YACnC,OAAO,CAAC,mBAAmB,CAAC,GAAG,WAAW,CAAC,iBAAiB,CAAC;QAC/D,CAAC;IACH,CAAC;IAES,iEAAsB,GAAhC;QACE,OAAO,mFAAmF,CAAC;IAC7F,CAAC;IAES,qEAA0B,GAApC;QACE,OAAO,+EAA+E,CAAC;IACzF,CAAC;IACH,uCAAC;AAAD,CAAC,AAvCD,CAAsD,+EAAsC,GAuC3F;AAvCY,4EAAgC","sourcesContent":["import { VERSION } from '@grafana/faro-core';\n\nimport { RecoveredCrashReportingInstrumentation } from '../RecoveredCrashReportingInstrumentation';\nimport type { CrashReport } from '../types';\n\nimport { resolveCrashErrorMessage, shouldSkipCrashReport } from './crashErrorMessage';\nimport { type ParsedIosCrashTrace, parseIosCrashTrace } from './parseIosCrashTrace';\n\n/**\n * iOS Crash Reporting Instrumentation.\n *\n * Uses PLCrashReporter to capture native crashes from previous sessions:\n * - SIGSEGV: Segmentation fault / invalid memory access\n * - SIGABRT: Abnormal termination (e.g., assertion failure, uncaught exception)\n * - SIGBUS: Bus error / alignment/hardware error\n * - SIGILL: Illegal instruction\n * - SIGFPE: Floating point exception\n * - SIGTRAP: Breakpoint/debugger trap\n * - Mach exceptions (EXC_BAD_ACCESS, EXC_CRASH, etc.)\n *\n * Stack frames are symbolicated on the native side before being surfaced.\n */\nexport class IosCrashReportingInstrumentation extends RecoveredCrashReportingInstrumentation {\n readonly name = '@grafana/faro-react-native:instrumentation-crash-ios';\n readonly version = VERSION;\n\n protected parseCrashTrace(trace: string): ParsedIosCrashTrace | null {\n return parseIosCrashTrace(trace);\n }\n\n protected resolveCrashErrorMessage(crash: CrashReport, parsedTrace: ParsedIosCrashTrace | null): string {\n return resolveCrashErrorMessage(crash, parsedTrace);\n }\n\n protected shouldSkipCrashReport(crash: CrashReport): boolean {\n return shouldSkipCrashReport(crash);\n }\n\n protected getStackFrames(parsedTrace: ParsedIosCrashTrace | null): any[] {\n return parsedTrace?.frames ?? [];\n }\n\n protected buildPlatformContext(parsedTrace: ParsedIosCrashTrace | null, context: Record<string, string>): void {\n if (parsedTrace?.exceptionName) {\n context['nativeExceptionName'] = parsedTrace.exceptionName;\n }\n if (parsedTrace?.signalName) {\n context['signalName'] = parsedTrace.signalName;\n }\n if (parsedTrace?.signalDescription) {\n context['signalDescription'] = parsedTrace.signalDescription;\n }\n }\n\n protected getParseFailureWarning(): string {\n return 'Native crash trace present but no frames parsed; check iOS PLCrashReporter format';\n }\n\n protected getTraceUnavailableWarning(): string {\n return 'Native crash trace unavailable (PLCrashReporter trace was null at crash time)';\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/instrumentations/crashReporting/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Crash report data structure returned from native module.\n */\nexport interface CrashReport {\n trace?: string;\n signal?: string;\n timestamp?: number;\n description?: string;\n processName?: string;\n pid?: number;\n importance?: number;\n // iOS-specific fields\n reason?: string; // Signal name (e.g., 'SIGSEGV')\n status?: number; // Exit status code\n}\n\n/**\n * Configuration options for crash reporting instrumentation.\n */\nexport interface CrashReportingOptions {\n /**\n * Enable or disable crash reporting. Defaults to true.\n */\n enabled?: boolean;\n\n /**\n * Optional release bundle filename hint for R8 retracing.\n * e.g., \"index.android.bundle\"\n */\n releaseBundleFilename?: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/instrumentations/crashReporting/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Crash report data structure returned from native module.\n */\nexport interface CrashReport {\n reportId?: string;\n trace?: string;\n signal?: string;\n timestamp?: number;\n description?: string;\n processName?: string;\n pid?: number;\n importance?: number;\n // iOS-specific fields\n reason?: string; // Signal name (e.g., 'SIGSEGV')\n status?: number; // Exit status code\n // Recovered-crash session context\n sessionId?: string;\n isSampled?: boolean;\n appVersion?: string;\n appRelease?: string;\n appBundleId?: string;\n}\n\n/**\n * Configuration options for crash reporting instrumentation.\n */\nexport interface CrashReportingOptions {\n /**\n * Enable or disable crash reporting. Defaults to true.\n */\n enabled?: boolean;\n\n /**\n * Optional release bundle filename hint for R8 retracing.\n * e.g., \"index.android.bundle\"\n */\n releaseBundleFilename?: string;\n}\n"]}
|
|
@@ -76,8 +76,13 @@ var ErrorsInstrumentation = /** @class */ (function (_super) {
|
|
|
76
76
|
// Store the original error handler
|
|
77
77
|
this.originalErrorHandler = global.ErrorUtils.getGlobalHandler();
|
|
78
78
|
// Set our custom handler
|
|
79
|
-
global.ErrorUtils.setGlobalHandler(function (
|
|
79
|
+
global.ErrorUtils.setGlobalHandler(function (thrown, isFatal) {
|
|
80
80
|
try {
|
|
81
|
+
// JS can throw any value, so normalise before the Error-shaped paths
|
|
82
|
+
// below. String() can itself throw — on a null-prototype object, or one
|
|
83
|
+
// with a throwing toString — so keep it inside the try, or finally never
|
|
84
|
+
// runs and React Native's own handler never sees the crash.
|
|
85
|
+
var error = thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
81
86
|
// Check if error should be ignored
|
|
82
87
|
if (_this.shouldIgnoreError(error)) {
|
|
83
88
|
return;
|
|
@@ -94,7 +99,7 @@ var ErrorsInstrumentation = /** @class */ (function (_super) {
|
|
|
94
99
|
_this.api.pushError(enhancedError, {
|
|
95
100
|
type: enhancedError.name || 'Error',
|
|
96
101
|
context: context,
|
|
97
|
-
fatal: isFatal
|
|
102
|
+
fatal: isFatal,
|
|
98
103
|
stackFrames: stackFrames,
|
|
99
104
|
});
|
|
100
105
|
// Track error fingerprint for deduplication
|
|
@@ -108,7 +113,7 @@ var ErrorsInstrumentation = /** @class */ (function (_super) {
|
|
|
108
113
|
finally {
|
|
109
114
|
// Always call the original handler to maintain normal error behavior
|
|
110
115
|
if (_this.originalErrorHandler) {
|
|
111
|
-
_this.originalErrorHandler(
|
|
116
|
+
_this.originalErrorHandler(thrown, isFatal);
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/errors/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ErrorUtils } from 'react-native';\n\nimport { BaseInstrumentation } from '@grafana/faro-core';\n\nimport { ErrorMechanism, primitiveUnhandledType } from './const';\nexport { ErrorMechanism } from './const';\nexport type { ErrorMechanismType } from './const';\nimport { enhanceErrorWithContext } from './stackTraceParser';\n\n// Access the global ErrorUtils\ndeclare const global: {\n ErrorUtils: ErrorUtils;\n addEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n removeEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n};\n\ntype ErrorHandlerCallback = (error: Error | unknown, isFatal?: boolean) => void;\n\nexport interface ErrorsInstrumentationOptions {\n /**\n * Patterns to ignore errors by message\n * @example [/network timeout/i, /cancelled/i]\n */\n ignoreErrors?: RegExp[];\n\n /**\n * Enable error deduplication (default: true)\n * Prevents sending the same error multiple times within a time window\n */\n enableDeduplication?: boolean;\n\n /**\n * Deduplication time window in milliseconds (default: 5000)\n * Errors with same message/stack within this window are considered duplicates\n */\n deduplicationWindow?: number;\n\n /**\n * Maximum number of errors to track for deduplication (default: 50)\n * Older errors are removed when this limit is reached\n */\n maxDeduplicationEntries?: number;\n\n /**\n * Bundle filename for Hermes/minified release lines (`func@line:col`).\n * Must match the Metro plugin source map `file` field (default `bundle.js`).\n */\n releaseBundleFilename?: string;\n}\n\ninterface ErrorFingerprint {\n message: string;\n stack: string;\n timestamp: number;\n}\n\n/**\n * Errors instrumentation for React Native\n *\n * Features:\n * - Captures unhandled errors and promise rejections using ErrorUtils\n * - Parses React Native stack traces (dev, release, Metro bundler formats)\n * - Adds platform context (OS, version, Hermes engine)\n * - Error deduplication to prevent duplicate reports\n * - Configurable error filtering\n *\n * @example\n * ```tsx\n * import { ErrorsInstrumentation, initializeFaro } from '@grafana/faro-react-native';\n *\n * await initializeFaro({\n * // ...config\n * instrumentations: [\n * new ErrorsInstrumentation({\n * ignoreErrors: [/network timeout/i, /cancelled/i],\n * enableDeduplication: true,\n * deduplicationWindow: 5000,\n * }),\n * ],\n * });\n * ```\n */\nexport class ErrorsInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native-errors';\n readonly version = '1.0.0';\n\n private originalErrorHandler?: ErrorHandlerCallback;\n private unhandledRejectionListener?: (event: PromiseRejectionEvent) => void;\n private options: Required<Omit<ErrorsInstrumentationOptions, 'releaseBundleFilename'>> & {\n releaseBundleFilename?: string;\n };\n private errorFingerprints: ErrorFingerprint[] = [];\n\n constructor(options: ErrorsInstrumentationOptions = {}) {\n super();\n this.options = {\n ignoreErrors: options.ignoreErrors ?? [],\n enableDeduplication: options.enableDeduplication ?? true,\n deduplicationWindow: options.deduplicationWindow ?? 5000,\n maxDeduplicationEntries: options.maxDeduplicationEntries ?? 50,\n releaseBundleFilename: options.releaseBundleFilename,\n };\n }\n\n initialize(): void {\n // Capture unhandled JavaScript errors\n this.setupGlobalErrorHandler();\n\n // Capture unhandled promise rejections\n this.setupUnhandledRejectionHandler();\n }\n\n private setupGlobalErrorHandler(): void {\n // Store the original error handler\n this.originalErrorHandler = global.ErrorUtils.getGlobalHandler();\n\n // Set our custom handler\n global.ErrorUtils.setGlobalHandler((error: Error, isFatal?: boolean) => {\n try {\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(\n error,\n {\n mechanism: ErrorMechanism.UNCAUGHT,\n },\n { releaseBundleFilename: this.options.releaseBundleFilename }\n );\n\n // Push error to Faro with enhanced data (type from error.name, matches Web SDK)\n this.api.pushError(enhancedError, {\n type: enhancedError.name || 'Error',\n context,\n fatal: isFatal ?? false,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n } finally {\n // Always call the original handler to maintain normal error behavior\n if (this.originalErrorHandler) {\n this.originalErrorHandler(error, isFatal);\n }\n }\n });\n }\n\n private setupUnhandledRejectionHandler(): void {\n // React Native supports the standard unhandledrejection event\n this.unhandledRejectionListener = (event: PromiseRejectionEvent) => {\n try {\n const reason = event.reason;\n\n // Convert reason to an Error if it isn't one (matches Web SDK approach)\n const isPrimitiveRejection = !(reason instanceof Error);\n let error: Error;\n if (reason instanceof Error) {\n error = reason;\n } else {\n error = new Error(\n `Unhandled Promise Rejection: ${typeof reason === 'object' ? JSON.stringify(reason) : String(reason)}`\n );\n }\n\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(\n error,\n {\n mechanism: ErrorMechanism.UNHANDLED_REJECTION,\n },\n { releaseBundleFilename: this.options.releaseBundleFilename }\n );\n\n // Type: use actual error type for Error objects; 'UnhandledRejection' for primitives (Web SDK)\n const errorType = isPrimitiveRejection ? primitiveUnhandledType : enhancedError.name || 'Error';\n\n this.api.pushError(enhancedError, {\n type: errorType,\n context,\n fatal: false,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n }\n };\n\n // Add the listener\n global.addEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n /**\n * Check if error should be ignored based on ignoreErrors patterns\n */\n private shouldIgnoreError(error: Error): boolean {\n if (!error || !error.message) {\n return false;\n }\n\n return this.options.ignoreErrors.some((pattern) => pattern.test(error.message));\n }\n\n /**\n * Check if error is a duplicate based on message and stack\n */\n private isDuplicateError(error: Error): boolean {\n const message = error.message || '';\n const stack = error.stack || '';\n const now = Date.now();\n\n // Clean up old entries first\n this.cleanupOldFingerprints(now);\n\n // Check if we've seen this error recently\n return this.errorFingerprints.some((fingerprint) => {\n return (\n fingerprint.message === message &&\n fingerprint.stack === stack &&\n now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n });\n }\n\n /**\n * Add error fingerprint for deduplication tracking\n */\n private addErrorFingerprint(error: Error): void {\n const message = error.message || '';\n const stack = error.stack || '';\n const timestamp = Date.now();\n\n this.errorFingerprints.push({ message, stack, timestamp });\n\n // Limit the number of tracked fingerprints\n if (this.errorFingerprints.length > this.options.maxDeduplicationEntries) {\n this.errorFingerprints.shift();\n }\n }\n\n /**\n * Remove error fingerprints older than the deduplication window\n */\n private cleanupOldFingerprints(now: number): void {\n this.errorFingerprints = this.errorFingerprints.filter(\n (fingerprint) => now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n }\n\n unpatch(): void {\n // Restore original error handler\n if (this.originalErrorHandler) {\n global.ErrorUtils.setGlobalHandler(this.originalErrorHandler);\n }\n\n // Remove unhandled rejection listener\n if (this.unhandledRejectionListener) {\n global.removeEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n // Clear deduplication tracking\n this.errorFingerprints = [];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/errors/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ErrorUtils } from 'react-native';\n\nimport { BaseInstrumentation } from '@grafana/faro-core';\n\nimport { ErrorMechanism, primitiveUnhandledType } from './const';\nexport { ErrorMechanism } from './const';\nexport type { ErrorMechanismType } from './const';\nimport { enhanceErrorWithContext } from './stackTraceParser';\n\n// Access the global ErrorUtils\ndeclare const global: {\n ErrorUtils: ErrorUtils;\n addEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n removeEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n};\n\n// Mirrors RN's ErrorHandler: a thrown value is not necessarily an Error, and\n// isFatal is always supplied by the runtime.\ntype ErrorHandlerCallback = (error: unknown, isFatal: boolean) => void;\n\nexport interface ErrorsInstrumentationOptions {\n /**\n * Patterns to ignore errors by message\n * @example [/network timeout/i, /cancelled/i]\n */\n ignoreErrors?: RegExp[];\n\n /**\n * Enable error deduplication (default: true)\n * Prevents sending the same error multiple times within a time window\n */\n enableDeduplication?: boolean;\n\n /**\n * Deduplication time window in milliseconds (default: 5000)\n * Errors with same message/stack within this window are considered duplicates\n */\n deduplicationWindow?: number;\n\n /**\n * Maximum number of errors to track for deduplication (default: 50)\n * Older errors are removed when this limit is reached\n */\n maxDeduplicationEntries?: number;\n\n /**\n * Bundle filename for Hermes/minified release lines (`func@line:col`).\n * Must match the Metro plugin source map `file` field (default `bundle.js`).\n */\n releaseBundleFilename?: string;\n}\n\ninterface ErrorFingerprint {\n message: string;\n stack: string;\n timestamp: number;\n}\n\n/**\n * Errors instrumentation for React Native\n *\n * Features:\n * - Captures unhandled errors and promise rejections using ErrorUtils\n * - Parses React Native stack traces (dev, release, Metro bundler formats)\n * - Adds platform context (OS, version, Hermes engine)\n * - Error deduplication to prevent duplicate reports\n * - Configurable error filtering\n *\n * @example\n * ```tsx\n * import { ErrorsInstrumentation, initializeFaro } from '@grafana/faro-react-native';\n *\n * await initializeFaro({\n * // ...config\n * instrumentations: [\n * new ErrorsInstrumentation({\n * ignoreErrors: [/network timeout/i, /cancelled/i],\n * enableDeduplication: true,\n * deduplicationWindow: 5000,\n * }),\n * ],\n * });\n * ```\n */\nexport class ErrorsInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native-errors';\n readonly version = '1.0.0';\n\n private originalErrorHandler?: ErrorHandlerCallback;\n private unhandledRejectionListener?: (event: PromiseRejectionEvent) => void;\n private options: Required<Omit<ErrorsInstrumentationOptions, 'releaseBundleFilename'>> & {\n releaseBundleFilename?: string;\n };\n private errorFingerprints: ErrorFingerprint[] = [];\n\n constructor(options: ErrorsInstrumentationOptions = {}) {\n super();\n this.options = {\n ignoreErrors: options.ignoreErrors ?? [],\n enableDeduplication: options.enableDeduplication ?? true,\n deduplicationWindow: options.deduplicationWindow ?? 5000,\n maxDeduplicationEntries: options.maxDeduplicationEntries ?? 50,\n releaseBundleFilename: options.releaseBundleFilename,\n };\n }\n\n initialize(): void {\n // Capture unhandled JavaScript errors\n this.setupGlobalErrorHandler();\n\n // Capture unhandled promise rejections\n this.setupUnhandledRejectionHandler();\n }\n\n private setupGlobalErrorHandler(): void {\n // Store the original error handler\n this.originalErrorHandler = global.ErrorUtils.getGlobalHandler();\n\n // Set our custom handler\n global.ErrorUtils.setGlobalHandler((thrown: unknown, isFatal: boolean) => {\n try {\n // JS can throw any value, so normalise before the Error-shaped paths\n // below. String() can itself throw — on a null-prototype object, or one\n // with a throwing toString — so keep it inside the try, or finally never\n // runs and React Native's own handler never sees the crash.\n const error = thrown instanceof Error ? thrown : new Error(String(thrown));\n\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(\n error,\n {\n mechanism: ErrorMechanism.UNCAUGHT,\n },\n { releaseBundleFilename: this.options.releaseBundleFilename }\n );\n\n // Push error to Faro with enhanced data (type from error.name, matches Web SDK)\n this.api.pushError(enhancedError, {\n type: enhancedError.name || 'Error',\n context,\n fatal: isFatal,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n } finally {\n // Always call the original handler to maintain normal error behavior\n if (this.originalErrorHandler) {\n this.originalErrorHandler(thrown, isFatal);\n }\n }\n });\n }\n\n private setupUnhandledRejectionHandler(): void {\n // React Native supports the standard unhandledrejection event\n this.unhandledRejectionListener = (event: PromiseRejectionEvent) => {\n try {\n const reason = event.reason;\n\n // Convert reason to an Error if it isn't one (matches Web SDK approach)\n const isPrimitiveRejection = !(reason instanceof Error);\n let error: Error;\n if (reason instanceof Error) {\n error = reason;\n } else {\n error = new Error(\n `Unhandled Promise Rejection: ${typeof reason === 'object' ? JSON.stringify(reason) : String(reason)}`\n );\n }\n\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(\n error,\n {\n mechanism: ErrorMechanism.UNHANDLED_REJECTION,\n },\n { releaseBundleFilename: this.options.releaseBundleFilename }\n );\n\n // Type: use actual error type for Error objects; 'UnhandledRejection' for primitives (Web SDK)\n const errorType = isPrimitiveRejection ? primitiveUnhandledType : enhancedError.name || 'Error';\n\n this.api.pushError(enhancedError, {\n type: errorType,\n context,\n fatal: false,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n }\n };\n\n // Add the listener\n global.addEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n /**\n * Check if error should be ignored based on ignoreErrors patterns\n */\n private shouldIgnoreError(error: Error): boolean {\n if (!error || !error.message) {\n return false;\n }\n\n return this.options.ignoreErrors.some((pattern) => pattern.test(error.message));\n }\n\n /**\n * Check if error is a duplicate based on message and stack\n */\n private isDuplicateError(error: Error): boolean {\n const message = error.message || '';\n const stack = error.stack || '';\n const now = Date.now();\n\n // Clean up old entries first\n this.cleanupOldFingerprints(now);\n\n // Check if we've seen this error recently\n return this.errorFingerprints.some((fingerprint) => {\n return (\n fingerprint.message === message &&\n fingerprint.stack === stack &&\n now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n });\n }\n\n /**\n * Add error fingerprint for deduplication tracking\n */\n private addErrorFingerprint(error: Error): void {\n const message = error.message || '';\n const stack = error.stack || '';\n const timestamp = Date.now();\n\n this.errorFingerprints.push({ message, stack, timestamp });\n\n // Limit the number of tracked fingerprints\n if (this.errorFingerprints.length > this.options.maxDeduplicationEntries) {\n this.errorFingerprints.shift();\n }\n }\n\n /**\n * Remove error fingerprints older than the deduplication window\n */\n private cleanupOldFingerprints(now: number): void {\n this.errorFingerprints = this.errorFingerprints.filter(\n (fingerprint) => now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n }\n\n unpatch(): void {\n // Restore original error handler\n if (this.originalErrorHandler) {\n global.ErrorUtils.setGlobalHandler(this.originalErrorHandler);\n }\n\n // Remove unhandled rejection listener\n if (this.unhandledRejectionListener) {\n global.removeEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n // Clear deduplication tracking\n this.errorFingerprints = [];\n }\n}\n"]}
|