@grafana/faro-react-native 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +72 -0
  2. package/android/build.gradle +145 -0
  3. package/dist/cjs/config/getRNInstrumentations.js +3 -1
  4. package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
  5. package/dist/cjs/config/makeRNConfig.js +7 -4
  6. package/dist/cjs/config/makeRNConfig.js.map +1 -1
  7. package/dist/cjs/config/types.js.map +1 -1
  8. package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
  9. package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
  10. package/dist/cjs/instrumentations/errors/index.js +3 -2
  11. package/dist/cjs/instrumentations/errors/index.js.map +1 -1
  12. package/dist/cjs/instrumentations/errors/stackTraceParser.js +98 -37
  13. package/dist/cjs/instrumentations/errors/stackTraceParser.js.map +1 -1
  14. package/dist/esm/config/getRNInstrumentations.js +3 -1
  15. package/dist/esm/config/getRNInstrumentations.js.map +1 -1
  16. package/dist/esm/config/makeRNConfig.js +7 -4
  17. package/dist/esm/config/makeRNConfig.js.map +1 -1
  18. package/dist/esm/config/types.js.map +1 -1
  19. package/dist/esm/generated/faroRNPackageMeta.js +1 -1
  20. package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
  21. package/dist/esm/instrumentations/errors/index.js +3 -2
  22. package/dist/esm/instrumentations/errors/index.js.map +1 -1
  23. package/dist/esm/instrumentations/errors/stackTraceParser.js +97 -37
  24. package/dist/esm/instrumentations/errors/stackTraceParser.js.map +1 -1
  25. package/dist/types/config/types.d.ts +5 -0
  26. package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
  27. package/dist/types/instrumentations/errors/index.d.ts +5 -0
  28. package/dist/types/instrumentations/errors/stackTraceParser.d.ts +19 -3
  29. package/ios/faro-upload-composed-source-map.sh +87 -0
  30. package/package.json +7 -10
  31. package/react-native.config.js +9 -1
  32. package/src/config/getRNInstrumentations.ts +5 -1
  33. package/src/config/makeRNConfig.test.ts +11 -0
  34. package/src/config/makeRNConfig.ts +7 -4
  35. package/src/config/types.ts +5 -0
  36. package/src/generated/faroRNPackageMeta.ts +1 -1
  37. package/src/initialize.test.ts +25 -0
  38. package/src/instrumentations/errors/index.ts +25 -8
  39. package/src/instrumentations/errors/stackTraceParser.test.ts +65 -0
  40. package/src/instrumentations/errors/stackTraceParser.ts +121 -37
@@ -11,6 +11,7 @@ var __assign = (this && this.__assign) || function () {
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.normalizeHermesStackFilename = normalizeHermesStackFilename;
14
15
  exports.parseStackTraceLine = parseStackTraceLine;
15
16
  exports.parseStackTrace = parseStackTrace;
16
17
  exports.toFaroStackFrames = toFaroStackFrames;
@@ -40,67 +41,113 @@ var REACT_NATIVE_RELEASE_REGEX = /^(.+?)@(\d+):(\d+)$/;
40
41
  var REACT_NATIVE_ANONYMOUS_REGEX = /^\s*at\s+anonymous\s+\((.+?):(\d+):(\d+)\)$/;
41
42
  var METRO_BUNDLER_REGEX = /^\s*at\s+(?:Object\.)?(.+?)\s+\((.+?):(\d+):(\d+)\)$/;
42
43
  /**
43
- * Parse a single stack trace line into a structured frame
44
+ * Hermes (Android release) often reports the bundle segment as `address at index.android.bundle`
45
+ * inside parentheses. The collector hashes `frame.filename` for uploaded maps; it must match the
46
+ * source map `file` field (e.g. `index.android.bundle`), not the verbose Hermes label.
44
47
  */
45
- function parseStackTraceLine(line) {
48
+ function normalizeHermesStackFilename(filename) {
49
+ var _a, _b;
50
+ var trimmed = filename.trim();
51
+ var m = /^address\s+at\s+(.+)$/i.exec(trimmed);
52
+ return (_b = (_a = m === null || m === void 0 ? void 0 : m[1]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : filename;
53
+ }
54
+ /**
55
+ * iOS Simulator (and Android) stacks often carry full filesystem paths such as
56
+ * `…/Foo.app/main.jsbundle` while uploaded maps are keyed by the bundle basename (`main.jsbundle`)
57
+ * that matches Metro’s source map `file` field — same as ingest’s bundle lookup hashes.
58
+ */
59
+ function collapseBundledArtifactPath(filename, releaseBasename) {
60
+ var key = releaseBasename.trim();
61
+ if (!key || filename === key) {
62
+ return filename;
63
+ }
64
+ var suffix = "/".concat(key);
65
+ return filename.endsWith(suffix) ? key : filename;
66
+ }
67
+ var UNKNOWN_RELEASE_FILENAME = '<unknown>';
68
+ /**
69
+ * Parse a single stack trace line into a structured frame and record how it was parsed.
70
+ */
71
+ function parseStackTraceLineDetailed(line) {
46
72
  if (!line || typeof line !== 'string') {
47
- return null;
73
+ return { frame: null, kind: 'unparsed' };
48
74
  }
49
75
  var trimmedLine = line.trim();
50
76
  if (!trimmedLine) {
51
- return null;
77
+ return { frame: null, kind: 'unparsed' };
52
78
  }
53
79
  // Try standard React Native format: at functionName (file.js:123:45)
54
80
  var match = trimmedLine.match(REACT_NATIVE_STACK_REGEX);
55
81
  if (match && match[2] && match[3] && match[4]) {
56
82
  return {
57
- function: match[1] || '<anonymous>',
58
- filename: match[2],
59
- lineno: parseInt(match[3], 10),
60
- colno: parseInt(match[4], 10),
83
+ kind: 'react_native_paren',
84
+ frame: {
85
+ function: match[1] || '<anonymous>',
86
+ filename: normalizeHermesStackFilename(match[2]),
87
+ lineno: parseInt(match[3], 10),
88
+ colno: parseInt(match[4], 10),
89
+ },
61
90
  };
62
91
  }
63
92
  // Try Metro bundler format: at Object.functionName (/path/to/file.js:123:456)
64
93
  match = trimmedLine.match(METRO_BUNDLER_REGEX);
65
94
  if (match && match[2] && match[3] && match[4]) {
66
95
  return {
67
- function: match[1] || '<anonymous>',
68
- filename: match[2],
69
- lineno: parseInt(match[3], 10),
70
- colno: parseInt(match[4], 10),
96
+ kind: 'metro_paren',
97
+ frame: {
98
+ function: match[1] || '<anonymous>',
99
+ filename: normalizeHermesStackFilename(match[2]),
100
+ lineno: parseInt(match[3], 10),
101
+ colno: parseInt(match[4], 10),
102
+ },
71
103
  };
72
104
  }
73
105
  // Try anonymous format: at anonymous (file.js:123:45)
74
106
  match = trimmedLine.match(REACT_NATIVE_ANONYMOUS_REGEX);
75
107
  if (match && match[1] && match[2] && match[3]) {
76
108
  return {
77
- function: '<anonymous>',
78
- filename: match[1],
79
- lineno: parseInt(match[2], 10),
80
- colno: parseInt(match[3], 10),
109
+ kind: 'anonymous_paren',
110
+ frame: {
111
+ function: '<anonymous>',
112
+ filename: normalizeHermesStackFilename(match[1]),
113
+ lineno: parseInt(match[2], 10),
114
+ colno: parseInt(match[3], 10),
115
+ },
81
116
  };
82
117
  }
83
118
  // Try native format: at functionName (native)
84
119
  match = trimmedLine.match(REACT_NATIVE_NATIVE_REGEX);
85
120
  if (match) {
86
121
  return {
87
- function: match[1] || '<native>',
88
- filename: '<native>',
89
- isNative: true,
122
+ kind: 'native',
123
+ frame: {
124
+ function: match[1] || '<native>',
125
+ filename: '<native>',
126
+ isNative: true,
127
+ },
90
128
  };
91
129
  }
92
130
  // Try release/minified format: functionName@123:456
93
131
  match = trimmedLine.match(REACT_NATIVE_RELEASE_REGEX);
94
132
  if (match && match[2] && match[3]) {
95
133
  return {
96
- function: match[1] || '<anonymous>',
97
- filename: '<unknown>',
98
- lineno: parseInt(match[2], 10),
99
- colno: parseInt(match[3], 10),
134
+ kind: 'release_func_at',
135
+ frame: {
136
+ function: match[1] || '<anonymous>',
137
+ filename: UNKNOWN_RELEASE_FILENAME,
138
+ lineno: parseInt(match[2], 10),
139
+ colno: parseInt(match[3], 10),
140
+ releaseLine: true,
141
+ },
100
142
  };
101
143
  }
102
- // Couldn't parse this line
103
- return null;
144
+ return { frame: null, kind: 'unparsed' };
145
+ }
146
+ /**
147
+ * Parse a single stack trace line into a structured frame
148
+ */
149
+ function parseStackTraceLine(line) {
150
+ return parseStackTraceLineDetailed(line).frame;
104
151
  }
105
152
  /**
106
153
  * Parse a full stack trace string into an array of structured frames
@@ -113,7 +160,7 @@ function parseStackTrace(stackTrace) {
113
160
  var frames = [];
114
161
  for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
115
162
  var line = lines_1[_i];
116
- var frame = parseStackTraceLine(line);
163
+ var frame = parseStackTraceLineDetailed(line).frame;
117
164
  if (frame) {
118
165
  frames.push(frame);
119
166
  }
@@ -123,24 +170,38 @@ function parseStackTrace(stackTrace) {
123
170
  /**
124
171
  * Convert parsed stack frames to Faro's StackFrame format
125
172
  */
126
- function toFaroStackFrames(parsedFrames) {
127
- return parsedFrames.map(function (frame, _index) { return ({
128
- filename: frame.filename || '<unknown>',
129
- function: frame.function || '<anonymous>',
130
- lineno: frame.lineno,
131
- colno: frame.colno,
132
- }); });
173
+ function toFaroStackFrames(parsedFrames, options) {
174
+ var _a;
175
+ var releaseName = (_a = options === null || options === void 0 ? void 0 : options.releaseBundleFilename) !== null && _a !== void 0 ? _a : 'bundle.js';
176
+ var keyedBasename = typeof (options === null || options === void 0 ? void 0 : options.releaseBundleFilename) === 'string' && options.releaseBundleFilename.trim() !== ''
177
+ ? options.releaseBundleFilename.trim()
178
+ : null;
179
+ return parsedFrames.map(function (frame, _index) {
180
+ var filename = normalizeHermesStackFilename(frame.filename || '<unknown>');
181
+ if (frame.releaseLine) {
182
+ filename = releaseName;
183
+ }
184
+ else if (keyedBasename) {
185
+ filename = collapseBundledArtifactPath(filename, keyedBasename);
186
+ }
187
+ return {
188
+ filename: filename,
189
+ function: frame.function || '<anonymous>',
190
+ lineno: frame.lineno,
191
+ colno: frame.colno,
192
+ };
193
+ });
133
194
  }
134
195
  /**
135
196
  * Extract and parse stack frames from an Error object
136
197
  */
137
- function getStackFramesFromError(error) {
198
+ function getStackFramesFromError(error, options) {
138
199
  if (!error || !error.stack) {
139
200
  return [];
140
201
  }
141
202
  try {
142
203
  var parsedFrames = parseStackTrace(error.stack);
143
- return toFaroStackFrames(parsedFrames);
204
+ return toFaroStackFrames(parsedFrames, options);
144
205
  }
145
206
  catch (_e) {
146
207
  // If parsing fails, return empty array
@@ -161,8 +222,8 @@ function getPlatformErrorContext() {
161
222
  /**
162
223
  * Enhance error with React Native specific information
163
224
  */
164
- function enhanceErrorWithContext(error, additionalContext) {
165
- var stackFrames = getStackFramesFromError(error);
225
+ function enhanceErrorWithContext(error, additionalContext, stackParseOptions) {
226
+ var stackFrames = getStackFramesFromError(error, stackParseOptions);
166
227
  var platformContext = getPlatformErrorContext();
167
228
  return {
168
229
  error: error,
@@ -1 +1 @@
1
- {"version":3,"file":"stackTraceParser.js","sourceRoot":"","sources":["../../../../src/instrumentations/errors/stackTraceParser.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { ExceptionStackFrame as StackFrame } from '@grafana/faro-core';\n\n// Declare global Hermes runtime\ndeclare const HermesInternal: object | undefined;\n\n/**\n * Parse React Native stack traces into structured stack frames\n *\n * React Native stack traces have different formats depending on platform and environment:\n *\n * iOS/Android (Dev):\n * at functionName (file.js:123:45)\n * at anonymous (native)\n *\n * iOS/Android (Release):\n * functionName@123:456\n * @[native code]\n *\n * Metro bundler:\n * at Object.functionName (/path/to/file.js:123:456)\n */\n\nconst REACT_NATIVE_STACK_REGEX = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/;\nconst REACT_NATIVE_NATIVE_REGEX = /^\\s*at\\s+(.+?)\\s+\\(native\\)$/;\nconst REACT_NATIVE_RELEASE_REGEX = /^(.+?)@(\\d+):(\\d+)$/;\nconst REACT_NATIVE_ANONYMOUS_REGEX = /^\\s*at\\s+anonymous\\s+\\((.+?):(\\d+):(\\d+)\\)$/;\nconst METRO_BUNDLER_REGEX = /^\\s*at\\s+(?:Object\\.)?(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/;\n\nexport interface ParsedStackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n isNative?: boolean;\n}\n\n/**\n * Parse a single stack trace line into a structured frame\n */\nexport function parseStackTraceLine(line: string): ParsedStackFrame | null {\n if (!line || typeof line !== 'string') {\n return null;\n }\n\n const trimmedLine = line.trim();\n if (!trimmedLine) {\n return null;\n }\n\n // Try standard React Native format: at functionName (file.js:123:45)\n let match = trimmedLine.match(REACT_NATIVE_STACK_REGEX);\n if (match && match[2] && match[3] && match[4]) {\n return {\n function: match[1] || '<anonymous>',\n filename: match[2],\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n };\n }\n\n // Try Metro bundler format: at Object.functionName (/path/to/file.js:123:456)\n match = trimmedLine.match(METRO_BUNDLER_REGEX);\n if (match && match[2] && match[3] && match[4]) {\n return {\n function: match[1] || '<anonymous>',\n filename: match[2],\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n };\n }\n\n // Try anonymous format: at anonymous (file.js:123:45)\n match = trimmedLine.match(REACT_NATIVE_ANONYMOUS_REGEX);\n if (match && match[1] && match[2] && match[3]) {\n return {\n function: '<anonymous>',\n filename: match[1],\n lineno: parseInt(match[2], 10),\n colno: parseInt(match[3], 10),\n };\n }\n\n // Try native format: at functionName (native)\n match = trimmedLine.match(REACT_NATIVE_NATIVE_REGEX);\n if (match) {\n return {\n function: match[1] || '<native>',\n filename: '<native>',\n isNative: true,\n };\n }\n\n // Try release/minified format: functionName@123:456\n match = trimmedLine.match(REACT_NATIVE_RELEASE_REGEX);\n if (match && match[2] && match[3]) {\n return {\n function: match[1] || '<anonymous>',\n filename: '<unknown>',\n lineno: parseInt(match[2], 10),\n colno: parseInt(match[3], 10),\n };\n }\n\n // Couldn't parse this line\n return null;\n}\n\n/**\n * Parse a full stack trace string into an array of structured frames\n */\nexport function parseStackTrace(stackTrace: string): ParsedStackFrame[] {\n if (!stackTrace || typeof stackTrace !== 'string') {\n return [];\n }\n\n const lines = stackTrace.split('\\n');\n const frames: ParsedStackFrame[] = [];\n\n for (const line of lines) {\n const frame = parseStackTraceLine(line);\n if (frame) {\n frames.push(frame);\n }\n }\n\n return frames;\n}\n\n/**\n * Convert parsed stack frames to Faro's StackFrame format\n */\nexport function toFaroStackFrames(parsedFrames: ParsedStackFrame[]): StackFrame[] {\n return parsedFrames.map((frame, _index) => ({\n filename: frame.filename || '<unknown>',\n function: frame.function || '<anonymous>',\n lineno: frame.lineno,\n colno: frame.colno,\n }));\n}\n\n/**\n * Extract and parse stack frames from an Error object\n */\nexport function getStackFramesFromError(error: Error): StackFrame[] {\n if (!error || !error.stack) {\n return [];\n }\n\n try {\n const parsedFrames = parseStackTrace(error.stack);\n return toFaroStackFrames(parsedFrames);\n } catch (_e) {\n // If parsing fails, return empty array\n return [];\n }\n}\n\n/**\n * Get platform-specific error context\n */\nexport function getPlatformErrorContext(): Record<string, string> {\n return {\n platform: Platform.OS,\n platformVersion: Platform.Version?.toString() || 'unknown',\n isHermes: typeof HermesInternal !== 'undefined' ? 'true' : 'false',\n };\n}\n\n/**\n * Enhance error with React Native specific information\n */\nexport function enhanceErrorWithContext(\n error: Error,\n additionalContext?: Record<string, string>\n): {\n error: Error;\n stackFrames: StackFrame[];\n context: Record<string, string>;\n} {\n const stackFrames = getStackFramesFromError(error);\n const platformContext = getPlatformErrorContext();\n\n return {\n error,\n stackFrames,\n context: {\n ...platformContext,\n ...additionalContext,\n },\n };\n}\n"]}
1
+ {"version":3,"file":"stackTraceParser.js","sourceRoot":"","sources":["../../../../src/instrumentations/errors/stackTraceParser.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { ExceptionStackFrame as StackFrame } from '@grafana/faro-core';\n\n// Declare global Hermes runtime\ndeclare const HermesInternal: object | undefined;\n\n/**\n * Parse React Native stack traces into structured stack frames\n *\n * React Native stack traces have different formats depending on platform and environment:\n *\n * iOS/Android (Dev):\n * at functionName (file.js:123:45)\n * at anonymous (native)\n *\n * iOS/Android (Release):\n * functionName@123:456\n * @[native code]\n *\n * Metro bundler:\n * at Object.functionName (/path/to/file.js:123:456)\n */\n\nconst REACT_NATIVE_STACK_REGEX = /^\\s*at\\s+(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/;\nconst REACT_NATIVE_NATIVE_REGEX = /^\\s*at\\s+(.+?)\\s+\\(native\\)$/;\nconst REACT_NATIVE_RELEASE_REGEX = /^(.+?)@(\\d+):(\\d+)$/;\nconst REACT_NATIVE_ANONYMOUS_REGEX = /^\\s*at\\s+anonymous\\s+\\((.+?):(\\d+):(\\d+)\\)$/;\nconst METRO_BUNDLER_REGEX = /^\\s*at\\s+(?:Object\\.)?(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/;\n\n/**\n * Hermes (Android release) often reports the bundle segment as `address at index.android.bundle`\n * inside parentheses. The collector hashes `frame.filename` for uploaded maps; it must match the\n * source map `file` field (e.g. `index.android.bundle`), not the verbose Hermes label.\n */\nexport function normalizeHermesStackFilename(filename: string): string {\n const trimmed = filename.trim();\n const m = /^address\\s+at\\s+(.+)$/i.exec(trimmed);\n return m?.[1]?.trim() ?? filename;\n}\n\n/**\n * iOS Simulator (and Android) stacks often carry full filesystem paths such as\n * `…/Foo.app/main.jsbundle` while uploaded maps are keyed by the bundle basename (`main.jsbundle`)\n * that matches Metro’s source map `file` field — same as ingest’s bundle lookup hashes.\n */\nfunction collapseBundledArtifactPath(filename: string, releaseBasename: string): string {\n const key = releaseBasename.trim();\n if (!key || filename === key) {\n return filename;\n }\n const suffix = `/${key}`;\n return filename.endsWith(suffix) ? key : filename;\n}\n\nexport interface ParsedStackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n isNative?: boolean;\n /** Set when the line matched Hermes/minified `func@line:col` (no path). */\n releaseLine?: boolean;\n}\n\n/** Which parser branch handled a stack line (internal). */\ntype ParsedStackLineKind =\n | 'react_native_paren'\n | 'metro_paren'\n | 'anonymous_paren'\n | 'native'\n | 'release_func_at'\n | 'unparsed';\n\nconst UNKNOWN_RELEASE_FILENAME = '<unknown>';\n\nexport interface StackFrameParseOptions {\n /**\n * Hermes / minified RN release lines use `func@line:col` with no file path. Those frames are tagged\n * with `releaseLine` and use this as `filename` (default `bundle.js`). Match the Metro plugin’s\n * source map `file` field (or `sourceMapFile` option there).\n */\n releaseBundleFilename?: string;\n}\n\n/**\n * Parse a single stack trace line into a structured frame and record how it was parsed.\n */\nfunction parseStackTraceLineDetailed(line: string): { frame: ParsedStackFrame | null; kind: ParsedStackLineKind } {\n if (!line || typeof line !== 'string') {\n return { frame: null, kind: 'unparsed' };\n }\n\n const trimmedLine = line.trim();\n if (!trimmedLine) {\n return { frame: null, kind: 'unparsed' };\n }\n\n // Try standard React Native format: at functionName (file.js:123:45)\n let match = trimmedLine.match(REACT_NATIVE_STACK_REGEX);\n if (match && match[2] && match[3] && match[4]) {\n return {\n kind: 'react_native_paren',\n frame: {\n function: match[1] || '<anonymous>',\n filename: normalizeHermesStackFilename(match[2]),\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n },\n };\n }\n\n // Try Metro bundler format: at Object.functionName (/path/to/file.js:123:456)\n match = trimmedLine.match(METRO_BUNDLER_REGEX);\n if (match && match[2] && match[3] && match[4]) {\n return {\n kind: 'metro_paren',\n frame: {\n function: match[1] || '<anonymous>',\n filename: normalizeHermesStackFilename(match[2]),\n lineno: parseInt(match[3], 10),\n colno: parseInt(match[4], 10),\n },\n };\n }\n\n // Try anonymous format: at anonymous (file.js:123:45)\n match = trimmedLine.match(REACT_NATIVE_ANONYMOUS_REGEX);\n if (match && match[1] && match[2] && match[3]) {\n return {\n kind: 'anonymous_paren',\n frame: {\n function: '<anonymous>',\n filename: normalizeHermesStackFilename(match[1]),\n lineno: parseInt(match[2], 10),\n colno: parseInt(match[3], 10),\n },\n };\n }\n\n // Try native format: at functionName (native)\n match = trimmedLine.match(REACT_NATIVE_NATIVE_REGEX);\n if (match) {\n return {\n kind: 'native',\n frame: {\n function: match[1] || '<native>',\n filename: '<native>',\n isNative: true,\n },\n };\n }\n\n // Try release/minified format: functionName@123:456\n match = trimmedLine.match(REACT_NATIVE_RELEASE_REGEX);\n if (match && match[2] && match[3]) {\n return {\n kind: 'release_func_at',\n frame: {\n function: match[1] || '<anonymous>',\n filename: UNKNOWN_RELEASE_FILENAME,\n lineno: parseInt(match[2], 10),\n colno: parseInt(match[3], 10),\n releaseLine: true,\n },\n };\n }\n\n return { frame: null, kind: 'unparsed' };\n}\n\n/**\n * Parse a single stack trace line into a structured frame\n */\nexport function parseStackTraceLine(line: string): ParsedStackFrame | null {\n return parseStackTraceLineDetailed(line).frame;\n}\n\n/**\n * Parse a full stack trace string into an array of structured frames\n */\nexport function parseStackTrace(stackTrace: string): ParsedStackFrame[] {\n if (!stackTrace || typeof stackTrace !== 'string') {\n return [];\n }\n\n const lines = stackTrace.split('\\n');\n const frames: ParsedStackFrame[] = [];\n\n for (const line of lines) {\n const { frame } = parseStackTraceLineDetailed(line);\n if (frame) {\n frames.push(frame);\n }\n }\n\n return frames;\n}\n\n/**\n * Convert parsed stack frames to Faro's StackFrame format\n */\nexport function toFaroStackFrames(parsedFrames: ParsedStackFrame[], options?: StackFrameParseOptions): StackFrame[] {\n const releaseName = options?.releaseBundleFilename ?? 'bundle.js';\n const keyedBasename =\n typeof options?.releaseBundleFilename === 'string' && options.releaseBundleFilename.trim() !== ''\n ? options.releaseBundleFilename.trim()\n : null;\n\n return parsedFrames.map((frame, _index) => {\n let filename = normalizeHermesStackFilename(frame.filename || '<unknown>');\n if (frame.releaseLine) {\n filename = releaseName;\n } else if (keyedBasename) {\n filename = collapseBundledArtifactPath(filename, keyedBasename);\n }\n return {\n filename,\n function: frame.function || '<anonymous>',\n lineno: frame.lineno,\n colno: frame.colno,\n };\n });\n}\n\n/**\n * Extract and parse stack frames from an Error object\n */\nexport function getStackFramesFromError(error: Error, options?: StackFrameParseOptions): StackFrame[] {\n if (!error || !error.stack) {\n return [];\n }\n\n try {\n const parsedFrames = parseStackTrace(error.stack);\n return toFaroStackFrames(parsedFrames, options);\n } catch (_e) {\n // If parsing fails, return empty array\n return [];\n }\n}\n\n/**\n * Get platform-specific error context\n */\nexport function getPlatformErrorContext(): Record<string, string> {\n return {\n platform: Platform.OS,\n platformVersion: Platform.Version?.toString() || 'unknown',\n isHermes: typeof HermesInternal !== 'undefined' ? 'true' : 'false',\n };\n}\n\n/**\n * Enhance error with React Native specific information\n */\nexport function enhanceErrorWithContext(\n error: Error,\n additionalContext?: Record<string, string>,\n stackParseOptions?: StackFrameParseOptions\n): {\n error: Error;\n stackFrames: StackFrame[];\n context: Record<string, string>;\n} {\n const stackFrames = getStackFramesFromError(error, stackParseOptions);\n const platformContext = getPlatformErrorContext();\n\n return {\n error,\n stackFrames,\n context: {\n ...platformContext,\n ...additionalContext,\n },\n };\n}\n"]}
@@ -55,7 +55,9 @@ export function getRNInstrumentations(config = {}) {
55
55
  const instrumentations = [];
56
56
  // Error reporting (Flutter: enableFlutterErrorReporting)
57
57
  if (enableErrorReporting) {
58
- instrumentations.push(new ErrorsInstrumentation());
58
+ instrumentations.push(new ErrorsInstrumentation({
59
+ releaseBundleFilename: config.releaseBundleFilename,
60
+ }));
59
61
  }
60
62
  // Console capture - not in Flutter SDK, RN-specific
61
63
  if (enableConsoleCapture) {
@@ -1 +1 @@
1
- {"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth).\n * Property names are aligned with Flutter SDK FaroConfig:\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n // Aligned with Flutter SDK FaroConfig defaults\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting (Flutter: enableFlutterErrorReporting)\n if (enableErrorReporting) {\n instrumentations.push(new ErrorsInstrumentation());\n }\n\n // Console capture - not in Flutter SDK, RN-specific\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions - always enabled in Flutter, same here\n instrumentations.push(new SessionInstrumentation());\n\n // Views - always enabled in Flutter, same here\n instrumentations.push(new ViewInstrumentation());\n\n // App state - always enabled in Flutter, same here\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(new CrashReportingInstrumentation());\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
1
+ {"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth).\n * Property names are aligned with Flutter SDK FaroConfig:\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n // Aligned with Flutter SDK FaroConfig defaults\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting (Flutter: enableFlutterErrorReporting)\n if (enableErrorReporting) {\n instrumentations.push(\n new ErrorsInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n // Console capture - not in Flutter SDK, RN-specific\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions - always enabled in Flutter, same here\n instrumentations.push(new SessionInstrumentation());\n\n // Views - always enabled in Flutter, same here\n instrumentations.push(new ViewInstrumentation());\n\n // App state - always enabled in Flutter, same here\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(new CrashReportingInstrumentation());\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
@@ -48,9 +48,11 @@ function buildInstrumentations(config) {
48
48
  /**
49
49
  * React Native stacktrace parser. Uses getStackFramesFromError for RN-specific formats.
50
50
  */
51
- const parseStacktrace = (err) => ({
52
- frames: getStackFramesFromError(err),
53
- });
51
+ function createParseStacktrace(releaseBundleFilename) {
52
+ return (err) => ({
53
+ frames: getStackFramesFromError(err, { releaseBundleFilename }),
54
+ });
55
+ }
54
56
  /**
55
57
  * Creates a full Faro config from React Native flag-based config.
56
58
  *
@@ -65,9 +67,10 @@ export function makeRNConfig(config, preloadedSessionDeviceAttributes) {
65
67
  const customMetas = (_a = config.metas) !== null && _a !== void 0 ? _a : [];
66
68
  const transports = buildTransports(config);
67
69
  const instrumentations = buildInstrumentations(config);
70
+ const releaseBundleFilename = config.releaseBundleFilename;
68
71
  return Object.assign(Object.assign({ app: config.app }, (preloadedSessionDeviceAttributes != null && {
69
72
  preloadedSessionDeviceAttributes,
70
- })), { dedupe: (_b = config.dedupe) !== null && _b !== void 0 ? _b : true, globalObjectKey: (_c = config.globalObjectKey) !== null && _c !== void 0 ? _c : defaultGlobalObjectKey, internalLoggerLevel: (_d = config.internalLoggerLevel) !== null && _d !== void 0 ? _d : InternalLoggerLevel.ERROR, isolate: (_e = config.isolate) !== null && _e !== void 0 ? _e : false, parseStacktrace: (_f = config.parseStacktrace) !== null && _f !== void 0 ? _f : parseStacktrace, paused: (_g = config.paused) !== null && _g !== void 0 ? _g : false, preventGlobalExposure: (_h = config.preventGlobalExposure) !== null && _h !== void 0 ? _h : false, unpatchedConsole: (_j = config.unpatchedConsole) !== null && _j !== void 0 ? _j : defaultUnpatchedConsole, batching: {
73
+ })), { dedupe: (_b = config.dedupe) !== null && _b !== void 0 ? _b : true, globalObjectKey: (_c = config.globalObjectKey) !== null && _c !== void 0 ? _c : defaultGlobalObjectKey, internalLoggerLevel: (_d = config.internalLoggerLevel) !== null && _d !== void 0 ? _d : InternalLoggerLevel.ERROR, isolate: (_e = config.isolate) !== null && _e !== void 0 ? _e : false, parseStacktrace: (_f = config.parseStacktrace) !== null && _f !== void 0 ? _f : createParseStacktrace(releaseBundleFilename), paused: (_g = config.paused) !== null && _g !== void 0 ? _g : false, preventGlobalExposure: (_h = config.preventGlobalExposure) !== null && _h !== void 0 ? _h : false, unpatchedConsole: (_j = config.unpatchedConsole) !== null && _j !== void 0 ? _j : defaultUnpatchedConsole, batching: {
71
74
  enabled: false,
72
75
  sendTimeout: 250,
73
76
  itemLimit: 50,
@@ -1 +1 @@
1
- {"version":3,"file":"makeRNConfig.js","sourceRoot":"","sources":["../../../src/config/makeRNConfig.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultGlobalObjectKey, defaultUnpatchedConsole } from '@grafana/faro-core';\nimport type { Config } from '@grafana/faro-core';\n\nimport { getStackFramesFromError } from '../instrumentations/errors/stackTraceParser';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\nimport { defaultSessionTrackingConfig } from '../instrumentations/session/sessionManager/sessionConstants';\nimport { InternalLoggerLevel, LogLevel } from '../internalLogger';\nimport { getPageMeta } from '../metas/page';\nimport { getScreenMeta } from '../metas/screen';\nimport { getSdkMeta } from '../metas/sdk';\nimport { ConsoleTransport } from '../transports/console';\nimport { FetchTransport } from '../transports/fetch';\nimport { OfflineTransport } from '../transports/offline';\n\nimport { getRNInstrumentations } from './getRNInstrumentations';\nimport type { ReactNativeConfig, ReactNativeFullConfig } from './types';\n\nconst DEFAULT_OFFLINE_CACHE_MS = 3 * 24 * 60 * 60 * 1000; // 3 days\n\n/**\n * Builds transports. FetchTransport is always added when url is provided.\n * User can enable offline and console via enableTransports.\n */\nfunction buildTransports(config: ReactNativeConfig): Config['transports'] {\n const { enableTransports = { offline: false, console: false } } = config;\n\n if (!config.url) {\n throw new Error('url is required. Provide the Faro collector URL.');\n }\n\n const builtTransports: Config['transports'] = [];\n\n if (enableTransports.offline) {\n builtTransports.push(\n new OfflineTransport({\n maxCacheDurationMs: DEFAULT_OFFLINE_CACHE_MS,\n })\n );\n }\n\n builtTransports.push(\n new FetchTransport({\n url: config.url,\n apiKey: config.apiKey,\n })\n );\n\n if (enableTransports.console) {\n builtTransports.push(new ConsoleTransport({ level: LogLevel.DEBUG }));\n }\n\n const extraTransports = config.transports ?? [];\n return [...builtTransports, ...extraTransports];\n}\n\n/**\n * Builds instrumentations from config flags.\n */\nfunction buildInstrumentations(config: ReactNativeConfig): Config['instrumentations'] {\n const baseInstrumentations = getRNInstrumentations(config);\n const extraInstrumentations = config.instrumentations ?? [];\n return [...baseInstrumentations, ...extraInstrumentations];\n}\n\n/**\n * React Native stacktrace parser. Uses getStackFramesFromError for RN-specific formats.\n */\nconst parseStacktrace: Config['parseStacktrace'] = (err) => ({\n frames: getStackFramesFromError(err),\n});\n\n/**\n * Creates a full Faro config from React Native flag-based config.\n *\n * Based on flags, builds instrumentations and transports automatically.\n * Client just enables what they need; makeRNConfig does the rest.\n *\n * @param preloadedSessionDeviceAttributes Device/session fields for session meta (passed from async `initializeFaro`).\n */\nexport function makeRNConfig(\n config: ReactNativeConfig,\n preloadedSessionDeviceAttributes?: SessionAttributes\n): ReactNativeFullConfig {\n const defaultMetas = [getSdkMeta(), getPageMeta(), getScreenMeta()];\n const customMetas = config.metas ?? [];\n const transports = buildTransports(config);\n const instrumentations = buildInstrumentations(config);\n\n return {\n app: config.app,\n ...(preloadedSessionDeviceAttributes != null && {\n preloadedSessionDeviceAttributes,\n }),\n dedupe: config.dedupe ?? true,\n globalObjectKey: config.globalObjectKey ?? defaultGlobalObjectKey,\n internalLoggerLevel: config.internalLoggerLevel ?? InternalLoggerLevel.ERROR,\n isolate: config.isolate ?? false,\n parseStacktrace: config.parseStacktrace ?? parseStacktrace,\n paused: config.paused ?? false,\n preventGlobalExposure: config.preventGlobalExposure ?? false,\n unpatchedConsole: config.unpatchedConsole ?? defaultUnpatchedConsole,\n batching: {\n enabled: false,\n sendTimeout: 250,\n itemLimit: 50,\n },\n sessionTracking: {\n ...defaultSessionTrackingConfig,\n ...config.sessionTracking,\n },\n metas: [...defaultMetas, ...customMetas],\n instrumentations,\n transports,\n ignoreUrls: config.ignoreUrls ?? [],\n ignoreErrors: config.ignoreErrors,\n beforeSend: config.beforeSend,\n preserveOriginalError: config.preserveOriginalError,\n userActionsInstrumentation: config.userActionsOptions,\n consoleInstrumentation: config.consoleCaptureOptions,\n };\n}\n"]}
1
+ {"version":3,"file":"makeRNConfig.js","sourceRoot":"","sources":["../../../src/config/makeRNConfig.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultGlobalObjectKey, defaultUnpatchedConsole } from '@grafana/faro-core';\nimport type { Config } from '@grafana/faro-core';\n\nimport { getStackFramesFromError } from '../instrumentations/errors/stackTraceParser';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\nimport { defaultSessionTrackingConfig } from '../instrumentations/session/sessionManager/sessionConstants';\nimport { InternalLoggerLevel, LogLevel } from '../internalLogger';\nimport { getPageMeta } from '../metas/page';\nimport { getScreenMeta } from '../metas/screen';\nimport { getSdkMeta } from '../metas/sdk';\nimport { ConsoleTransport } from '../transports/console';\nimport { FetchTransport } from '../transports/fetch';\nimport { OfflineTransport } from '../transports/offline';\n\nimport { getRNInstrumentations } from './getRNInstrumentations';\nimport type { ReactNativeConfig, ReactNativeFullConfig } from './types';\n\nconst DEFAULT_OFFLINE_CACHE_MS = 3 * 24 * 60 * 60 * 1000; // 3 days\n\n/**\n * Builds transports. FetchTransport is always added when url is provided.\n * User can enable offline and console via enableTransports.\n */\nfunction buildTransports(config: ReactNativeConfig): Config['transports'] {\n const { enableTransports = { offline: false, console: false } } = config;\n\n if (!config.url) {\n throw new Error('url is required. Provide the Faro collector URL.');\n }\n\n const builtTransports: Config['transports'] = [];\n\n if (enableTransports.offline) {\n builtTransports.push(\n new OfflineTransport({\n maxCacheDurationMs: DEFAULT_OFFLINE_CACHE_MS,\n })\n );\n }\n\n builtTransports.push(\n new FetchTransport({\n url: config.url,\n apiKey: config.apiKey,\n })\n );\n\n if (enableTransports.console) {\n builtTransports.push(new ConsoleTransport({ level: LogLevel.DEBUG }));\n }\n\n const extraTransports = config.transports ?? [];\n return [...builtTransports, ...extraTransports];\n}\n\n/**\n * Builds instrumentations from config flags.\n */\nfunction buildInstrumentations(config: ReactNativeConfig): Config['instrumentations'] {\n const baseInstrumentations = getRNInstrumentations(config);\n const extraInstrumentations = config.instrumentations ?? [];\n return [...baseInstrumentations, ...extraInstrumentations];\n}\n\n/**\n * React Native stacktrace parser. Uses getStackFramesFromError for RN-specific formats.\n */\nfunction createParseStacktrace(releaseBundleFilename: string | undefined): Config['parseStacktrace'] {\n return (err) => ({\n frames: getStackFramesFromError(err, { releaseBundleFilename }),\n });\n}\n\n/**\n * Creates a full Faro config from React Native flag-based config.\n *\n * Based on flags, builds instrumentations and transports automatically.\n * Client just enables what they need; makeRNConfig does the rest.\n *\n * @param preloadedSessionDeviceAttributes Device/session fields for session meta (passed from async `initializeFaro`).\n */\nexport function makeRNConfig(\n config: ReactNativeConfig,\n preloadedSessionDeviceAttributes?: SessionAttributes\n): ReactNativeFullConfig {\n const defaultMetas = [getSdkMeta(), getPageMeta(), getScreenMeta()];\n const customMetas = config.metas ?? [];\n const transports = buildTransports(config);\n const instrumentations = buildInstrumentations(config);\n\n const releaseBundleFilename = config.releaseBundleFilename;\n return {\n app: config.app,\n ...(preloadedSessionDeviceAttributes != null && {\n preloadedSessionDeviceAttributes,\n }),\n dedupe: config.dedupe ?? true,\n globalObjectKey: config.globalObjectKey ?? defaultGlobalObjectKey,\n internalLoggerLevel: config.internalLoggerLevel ?? InternalLoggerLevel.ERROR,\n isolate: config.isolate ?? false,\n parseStacktrace: config.parseStacktrace ?? createParseStacktrace(releaseBundleFilename),\n paused: config.paused ?? false,\n preventGlobalExposure: config.preventGlobalExposure ?? false,\n unpatchedConsole: config.unpatchedConsole ?? defaultUnpatchedConsole,\n batching: {\n enabled: false,\n sendTimeout: 250,\n itemLimit: 50,\n },\n sessionTracking: {\n ...defaultSessionTrackingConfig,\n ...config.sessionTracking,\n },\n metas: [...defaultMetas, ...customMetas],\n instrumentations,\n transports,\n ignoreUrls: config.ignoreUrls ?? [],\n ignoreErrors: config.ignoreErrors,\n beforeSend: config.beforeSend,\n preserveOriginalError: config.preserveOriginalError,\n userActionsInstrumentation: config.userActionsOptions,\n consoleInstrumentation: config.consoleCaptureOptions,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Config } from '@grafana/faro-core';\n\nimport type { ANRInstrumentationOptions } from '../instrumentations/anr';\nimport type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\n\nimport type { Sampling } from './sampling';\n\n/**\n * React Native session tracking config.\n * Extends Config['sessionTracking'] with sampling (Flutter-style) and RN-specific props.\n * Excludes samplingRate and sampler in favor of sampling.\n *\n * When `persistent` is true, sessions are stored with [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv)\n * (synchronous). Add `react-native-mmkv` to your app and rebuild native projects.\n */\nexport type ReactNativeSessionTrackingConfig = Omit<\n NonNullable<Config['sessionTracking']>,\n 'samplingRate' | 'sampler'\n> & {\n /**\n * Session sampling. Use {@link SamplingRate} for fixed rate or\n * {@link SamplingFunction} for dynamic sampling.\n */\n sampling?: Sampling;\n};\n\n/**\n * Flags for enabling optional built-in transports.\n * FetchTransport is always instantiated when url is provided.\n */\nexport interface EnableTransportsConfig {\n /** Enable OfflineTransport for caching when offline (default: false) */\n offline?: boolean;\n /** Enable ConsoleTransport to log telemetry to Metro console for debugging (default: false) */\n console?: boolean;\n}\n\n/** RN-specific config: overrides + new props. Omit faro-core props we replace with RN equivalents. */\ntype ReactNativeConfigOverrides = {\n /** Application metadata (required) */\n app: Config['app'];\n sessionTracking?: ReactNativeSessionTrackingConfig;\n url?: string;\n apiKey?: string;\n enableTransports?: EnableTransportsConfig;\n /**\n * Which HTTP instrumentations to enable. When tracing is disabled, these control\n * HttpInstrumentation (fetch) and XHRInstrumentation (xhr). Default: both true.\n */\n enableHttpInstrumentation?: {\n /** HttpInstrumentation - patches fetch API (default: true) */\n fetch?: boolean;\n /** XHRInstrumentation - patches XMLHttpRequest (default: true) */\n xhr?: boolean;\n };\n enableTracing?: boolean;\n /** Passed to TracingInstrumentation. See TracingInstrumentationOptions in @grafana/faro-react-native-tracing. */\n tracingOptions?: Record<string, unknown>;\n cpuUsageVitals?: boolean;\n memoryUsageVitals?: boolean;\n refreshRateVitals?: boolean;\n fetchVitalsInterval?: number;\n frameMonitoringOptions?: FrameMonitoringOptions;\n enableErrorReporting?: boolean;\n enableCrashReporting?: boolean;\n anrTracking?: boolean;\n anrOptions?: ANRInstrumentationOptions;\n enableConsoleCapture?: boolean;\n consoleCaptureOptions?: Config['consoleInstrumentation'];\n enableUserActions?: boolean;\n userActionsOptions?: Config['userActionsInstrumentation'];\n};\n\n/**\n * React Native config: Config (omit replaced props) & overrides.\n * Omit: sessionTracking, consoleInstrumentation, userActionsInstrumentation.\n * instrumentations and transports inherited from Config for extras (built-ins come from flags).\n */\nexport type ReactNativeConfig = Partial<\n Omit<Config, 'sessionTracking' | 'consoleInstrumentation' | 'userActionsInstrumentation'>\n> &\n ReactNativeConfigOverrides;\n\n/**\n * Internal full config returned by `makeRNConfig`.\n * Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.\n */\nexport type ReactNativeFullConfig = Config & {\n preloadedSessionDeviceAttributes?: SessionAttributes;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Config } from '@grafana/faro-core';\n\nimport type { ANRInstrumentationOptions } from '../instrumentations/anr';\nimport type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\n\nimport type { Sampling } from './sampling';\n\n/**\n * React Native session tracking config.\n * Extends Config['sessionTracking'] with sampling (Flutter-style) and RN-specific props.\n * Excludes samplingRate and sampler in favor of sampling.\n *\n * When `persistent` is true, sessions are stored with [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv)\n * (synchronous). Add `react-native-mmkv` to your app and rebuild native projects.\n */\nexport type ReactNativeSessionTrackingConfig = Omit<\n NonNullable<Config['sessionTracking']>,\n 'samplingRate' | 'sampler'\n> & {\n /**\n * Session sampling. Use {@link SamplingRate} for fixed rate or\n * {@link SamplingFunction} for dynamic sampling.\n */\n sampling?: Sampling;\n};\n\n/**\n * Flags for enabling optional built-in transports.\n * FetchTransport is always instantiated when url is provided.\n */\nexport interface EnableTransportsConfig {\n /** Enable OfflineTransport for caching when offline (default: false) */\n offline?: boolean;\n /** Enable ConsoleTransport to log telemetry to Metro console for debugging (default: false) */\n console?: boolean;\n}\n\n/** RN-specific config: overrides + new props. Omit faro-core props we replace with RN equivalents. */\ntype ReactNativeConfigOverrides = {\n /** Application metadata (required) */\n app: Config['app'];\n sessionTracking?: ReactNativeSessionTrackingConfig;\n url?: string;\n apiKey?: string;\n enableTransports?: EnableTransportsConfig;\n /**\n * Which HTTP instrumentations to enable. When tracing is disabled, these control\n * HttpInstrumentation (fetch) and XHRInstrumentation (xhr). Default: both true.\n */\n enableHttpInstrumentation?: {\n /** HttpInstrumentation - patches fetch API (default: true) */\n fetch?: boolean;\n /** XHRInstrumentation - patches XMLHttpRequest (default: true) */\n xhr?: boolean;\n };\n enableTracing?: boolean;\n /** Passed to TracingInstrumentation. See TracingInstrumentationOptions in @grafana/faro-react-native-tracing. */\n tracingOptions?: Record<string, unknown>;\n cpuUsageVitals?: boolean;\n memoryUsageVitals?: boolean;\n refreshRateVitals?: boolean;\n fetchVitalsInterval?: number;\n frameMonitoringOptions?: FrameMonitoringOptions;\n enableErrorReporting?: boolean;\n enableCrashReporting?: boolean;\n anrTracking?: boolean;\n anrOptions?: ANRInstrumentationOptions;\n enableConsoleCapture?: boolean;\n consoleCaptureOptions?: Config['consoleInstrumentation'];\n enableUserActions?: boolean;\n userActionsOptions?: Config['userActionsInstrumentation'];\n /**\n * Hermes / minified release stack lines omit a file path. This must match the Metro plugin\n * source map top-level `file` (default `bundle.js`, or your `sourceMapFile` option there).\n */\n releaseBundleFilename?: string;\n};\n\n/**\n * React Native config: Config (omit replaced props) & overrides.\n * Omit: sessionTracking, consoleInstrumentation, userActionsInstrumentation.\n * instrumentations and transports inherited from Config for extras (built-ins come from flags).\n */\nexport type ReactNativeConfig = Partial<\n Omit<Config, 'sessionTracking' | 'consoleInstrumentation' | 'userActionsInstrumentation'>\n> &\n ReactNativeConfigOverrides;\n\n/**\n * Internal full config returned by `makeRNConfig`.\n * Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.\n */\nexport type ReactNativeFullConfig = Config & {\n preloadedSessionDeviceAttributes?: SessionAttributes;\n};\n"]}
@@ -2,5 +2,5 @@
2
2
  * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.
3
3
  */
4
4
  export const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native';
5
- export const FARO_REACT_NATIVE_NPM_VERSION = '1.1.0';
5
+ export const FARO_REACT_NATIVE_NPM_VERSION = '1.2.0';
6
6
  //# sourceMappingURL=faroRNPackageMeta.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAAqC,CAAC;AAChF,MAAM,CAAC,MAAM,6BAA6B,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.\n */\nexport const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;\nexport const FARO_REACT_NATIVE_NPM_VERSION = '1.1.0' as const;\n"]}
1
+ {"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAAqC,CAAC;AAChF,MAAM,CAAC,MAAM,6BAA6B,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.\n */\nexport const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;\nexport const FARO_REACT_NATIVE_NPM_VERSION = '1.2.0' as const;\n"]}
@@ -40,6 +40,7 @@ export class ErrorsInstrumentation extends BaseInstrumentation {
40
40
  enableDeduplication: (_b = options.enableDeduplication) !== null && _b !== void 0 ? _b : true,
41
41
  deduplicationWindow: (_c = options.deduplicationWindow) !== null && _c !== void 0 ? _c : 5000,
42
42
  maxDeduplicationEntries: (_d = options.maxDeduplicationEntries) !== null && _d !== void 0 ? _d : 50,
43
+ releaseBundleFilename: options.releaseBundleFilename,
43
44
  };
44
45
  }
45
46
  initialize() {
@@ -66,7 +67,7 @@ export class ErrorsInstrumentation extends BaseInstrumentation {
66
67
  const { error: enhancedError, stackFrames, context, } = enhanceErrorWithContext(error, {
67
68
  mechanism: ErrorMechanism.UNCAUGHT,
68
69
  isFatal: String(isFatal !== null && isFatal !== void 0 ? isFatal : false),
69
- });
70
+ }, { releaseBundleFilename: this.options.releaseBundleFilename });
70
71
  // Push error to Faro with enhanced data (type from error.name, matches Web SDK)
71
72
  this.api.pushError(enhancedError, {
72
73
  type: enhancedError.name || 'Error',
@@ -115,7 +116,7 @@ export class ErrorsInstrumentation extends BaseInstrumentation {
115
116
  // Enhance error with React Native context and stack frames
116
117
  const { error: enhancedError, stackFrames, context, } = enhanceErrorWithContext(error, {
117
118
  mechanism: ErrorMechanism.UNHANDLED_REJECTION,
118
- });
119
+ }, { releaseBundleFilename: this.options.releaseBundleFilename });
119
120
  // Type: use actual error type for Error objects; 'UnhandledRejection' for primitives (Web SDK)
120
121
  const errorType = isPrimitiveRejection ? primitiveUnhandledType : enhancedError.name || 'Error';
121
122
  this.api.pushError(enhancedError, {
@@ -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\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<ErrorsInstrumentationOptions>;\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 };\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(error, {\n mechanism: ErrorMechanism.UNCAUGHT,\n isFatal: String(isFatal ?? false),\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 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(error, {\n mechanism: ErrorMechanism.UNHANDLED_REJECTION,\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 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\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 isFatal: String(isFatal ?? false),\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 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 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"]}