@grafana/faro-react-native 1.0.0 → 1.1.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.
- package/README.md +8 -3
- package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
- package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/cjs/util/performanceObserverPolyfill.js +32 -3
- package/dist/cjs/util/performanceObserverPolyfill.js.map +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js +1 -1
- package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
- package/dist/esm/util/performanceObserverPolyfill.js +32 -3
- package/dist/esm/util/performanceObserverPolyfill.js.map +1 -1
- package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
- package/dist/types/util/performanceObserverPolyfill.d.ts +5 -2
- package/package.json +2 -2
- package/src/generated/faroRNPackageMeta.ts +1 -1
- package/src/util/performanceObserverPolyfill.test.ts +48 -0
- package/src/util/performanceObserverPolyfill.ts +43 -3
package/README.md
CHANGED
|
@@ -47,7 +47,12 @@ initializeFaro({
|
|
|
47
47
|
fetchVitalsInterval: 30000, // optional, ms
|
|
48
48
|
frameMonitoringOptions: {}, // optional, when refreshRateVitals true
|
|
49
49
|
enableTracing: false, // optional, requires @grafana/faro-react-native-tracing
|
|
50
|
-
tracingOptions: {
|
|
50
|
+
tracingOptions: {
|
|
51
|
+
instrumentationOptions: {
|
|
52
|
+
// Traces fetch() by default. Enable this for axios/direct XHR apps.
|
|
53
|
+
enableXhrInstrumentation: false,
|
|
54
|
+
},
|
|
55
|
+
}, // optional
|
|
51
56
|
|
|
52
57
|
// Instrumentation options - optional
|
|
53
58
|
consoleCaptureOptions: {},
|
|
@@ -85,8 +90,8 @@ initializeFaro({
|
|
|
85
90
|
- **App State Instrumentation** - Tracks when app goes to background/foreground
|
|
86
91
|
- **Performance Instrumentation** - Monitors CPU usage, memory usage, and app startup time using native OS APIs
|
|
87
92
|
- **Startup Instrumentation** - Automatically tracks app startup duration from process start
|
|
88
|
-
- **HTTP Instrumentation** - Tracks
|
|
89
|
-
- **XHR Instrumentation** - Tracks XMLHttpRequest calls
|
|
93
|
+
- **HTTP Instrumentation** - Tracks `fetch()` requests and correlates them with user actions
|
|
94
|
+
- **XHR Instrumentation** - Tracks `XMLHttpRequest` calls when tracing is disabled; with `enableTracing: true`, enable XHR tracing via `tracingOptions.instrumentationOptions.enableXhrInstrumentation`
|
|
90
95
|
|
|
91
96
|
**Enabled by default** (opt-out via config):
|
|
92
97
|
|
|
@@ -5,5 +5,5 @@ exports.FARO_REACT_NATIVE_NPM_VERSION = exports.FARO_REACT_NATIVE_NPM_NAME = voi
|
|
|
5
5
|
* NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.
|
|
6
6
|
*/
|
|
7
7
|
exports.FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native';
|
|
8
|
-
exports.FARO_REACT_NATIVE_NPM_VERSION = '1.
|
|
8
|
+
exports.FARO_REACT_NATIVE_NPM_VERSION = '1.1.0';
|
|
9
9
|
//# sourceMappingURL=faroRNPackageMeta.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,0BAA0B,GAAG,4BAAqC,CAAC;AACnE,QAAA,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
|
+
{"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,0BAA0B,GAAG,4BAAqC,CAAC;AACnE,QAAA,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"]}
|
|
@@ -20,6 +20,8 @@ var globalObj = (typeof globalThis !== 'undefined' && globalThis) ||
|
|
|
20
20
|
(typeof global !== 'undefined' && global) ||
|
|
21
21
|
(typeof window !== 'undefined' && window) ||
|
|
22
22
|
{};
|
|
23
|
+
var RESOURCE_ENTRY_TYPE = 'resource';
|
|
24
|
+
var FARO_RESOURCE_TIMING_PATCH = '__faroResourceTimingPatch';
|
|
23
25
|
/**
|
|
24
26
|
* No-op PerformanceObserver that matches the Web API interface.
|
|
25
27
|
* Prevents crashes when React Native's native implementation throws.
|
|
@@ -41,14 +43,18 @@ var NoopPerformanceObserver = /** @class */ (function () {
|
|
|
41
43
|
return NoopPerformanceObserver;
|
|
42
44
|
}());
|
|
43
45
|
/**
|
|
44
|
-
* Apply
|
|
45
|
-
*
|
|
46
|
+
* Apply React Native performance API compatibility patches.
|
|
47
|
+
*
|
|
48
|
+
* Resource Timing lookup is patched on all platforms because OTel web
|
|
49
|
+
* instrumentation can probe it. The PerformanceObserver replacement is
|
|
50
|
+
* iOS-only to avoid the native bad_variant_access crash.
|
|
46
51
|
*/
|
|
47
52
|
function applyPerformanceObserverPolyfill() {
|
|
53
|
+
var record = globalObj;
|
|
54
|
+
patchUnsupportedResourceTimingLookup(record);
|
|
48
55
|
if (react_native_1.Platform.OS !== 'ios') {
|
|
49
56
|
return;
|
|
50
57
|
}
|
|
51
|
-
var record = globalObj;
|
|
52
58
|
var existing = record['PerformanceObserver'];
|
|
53
59
|
if (existing && existing.name === 'NoopPerformanceObserver') {
|
|
54
60
|
return; // Already applied
|
|
@@ -60,4 +66,27 @@ function applyPerformanceObserverPolyfill() {
|
|
|
60
66
|
// Ignore if global is frozen (e.g. in some test environments)
|
|
61
67
|
}
|
|
62
68
|
}
|
|
69
|
+
function patchUnsupportedResourceTimingLookup(record) {
|
|
70
|
+
var performance = record['performance'];
|
|
71
|
+
if (!(performance === null || performance === void 0 ? void 0 : performance.getEntriesByType) || performance.getEntriesByType[FARO_RESOURCE_TIMING_PATCH]) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
var originalGetEntriesByType = performance.getEntriesByType.bind(performance);
|
|
75
|
+
var patchedGetEntriesByType = (function (entryType) {
|
|
76
|
+
if (entryType === RESOURCE_ENTRY_TYPE) {
|
|
77
|
+
// OTel web fetch instrumentation probes browser Resource Timing APIs.
|
|
78
|
+
// React Native does not expose resource entries, and calling through emits
|
|
79
|
+
// "Deprecated API for given entry type." warnings in development.
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return originalGetEntriesByType(entryType);
|
|
83
|
+
});
|
|
84
|
+
patchedGetEntriesByType[FARO_RESOURCE_TIMING_PATCH] = true;
|
|
85
|
+
try {
|
|
86
|
+
performance.getEntriesByType = patchedGetEntriesByType;
|
|
87
|
+
}
|
|
88
|
+
catch (_a) {
|
|
89
|
+
// Ignore if performance is read-only in a test or host environment.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
63
92
|
//# sourceMappingURL=performanceObserverPolyfill.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"performanceObserverPolyfill.js","sourceRoot":"","sources":["../../../src/util/performanceObserverPolyfill.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;
|
|
1
|
+
{"version":3,"file":"performanceObserverPolyfill.js","sourceRoot":"","sources":["../../../src/util/performanceObserverPolyfill.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;AAqEH,4EAkBC;AArFD,6CAAwC;AAExC,IAAM,SAAS,GACb,CAAC,OAAO,UAAU,KAAK,WAAW,IAAI,UAAU,CAAC;IACjD,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC;IACzC,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC;IACzC,EAAE,CAAC;AAEL,IAAM,mBAAmB,GAAG,UAAU,CAAC;AACvC,IAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAqB/D;;;GAGG;AACH;IACE,iCACE,SAIS;QAET,+DAA+D;IACjE,CAAC;IAED,yCAAO,GAAP,UAAQ,QAAkC;QACxC,QAAQ;IACV,CAAC;IAED,4CAAU,GAAV;QACE,QAAQ;IACV,CAAC;IAED,6CAAW,GAAX;QACE,OAAO,EAAE,CAAC;IACZ,CAAC;IAEe,2CAAmB,GAAa,EAAE,CAAC;IACrD,8BAAC;CAAA,AAxBD,IAwBC;AAED;;;;;;GAMG;AACH,SAAgB,gCAAgC;IAC9C,IAAM,MAAM,GAAG,SAAoC,CAAC;IACpD,oCAAoC,CAAC,MAAM,CAAC,CAAC;IAE7C,IAAI,uBAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,IAAM,QAAQ,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC/C,IAAI,QAAQ,IAAK,QAAyC,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;QAC9F,OAAO,CAAC,kBAAkB;IAC5B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,GAAG,uBAAuB,CAAC;IAC1D,CAAC;IAAC,WAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;AACH,CAAC;AAED,SAAS,oCAAoC,CAAC,MAA+B;IAC3E,IAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAI3B,CAAC;IAEd,IAAI,CAAC,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,CAAA,IAAI,WAAW,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAC/F,OAAO;IACT,CAAC;IAED,IAAM,wBAAwB,GAAG,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChF,IAAM,uBAAuB,GAAG,CAAC,UAAC,SAAiB;QACjD,IAAI,SAAS,KAAK,mBAAmB,EAAE,CAAC;YACtC,sEAAsE;YACtE,2EAA2E;YAC3E,kEAAkE;YAClE,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC,CAAwC,CAAC;IAE1C,uBAAuB,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC;IAE3D,IAAI,CAAC;QACH,WAAW,CAAC,gBAAgB,GAAG,uBAAuB,CAAC;IACzD,CAAC;IAAC,WAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;AACH,CAAC","sourcesContent":["/**\n * PerformanceObserver polyfill for React Native iOS\n *\n * React Native 0.84's native PerformanceObserver has a bug on iOS that throws\n * `bad_variant_access` when the observer callback runs and calls getEntries().\n * This happens in NativePerformance.createObserver when the native bridge\n * returns data that causes a C++ variant access error.\n *\n * OpenTelemetry's FetchInstrumentation (and other libs) may use PerformanceObserver\n * for resource timing. We provide a no-op polyfill on React Native iOS to prevent\n * the crash while preserving refresh rate vitals and other functionality.\n *\n * @see https://github.com/facebook/react-native/issues (PerformanceObserver iOS)\n */\n\nimport { Platform } from 'react-native';\n\nconst globalObj =\n (typeof globalThis !== 'undefined' && globalThis) ||\n (typeof global !== 'undefined' && global) ||\n (typeof window !== 'undefined' && window) ||\n {};\n\nconst RESOURCE_ENTRY_TYPE = 'resource';\nconst FARO_RESOURCE_TIMING_PATCH = '__faroResourceTimingPatch';\n\ninterface PerformanceEntry {\n readonly duration: number;\n readonly entryType: string;\n readonly name: string;\n readonly startTime: number;\n}\n\ninterface PerformanceObserverEntryList {\n getEntries(): PerformanceEntry[];\n getEntriesByName(name: string, type?: string): PerformanceEntry[];\n getEntriesByType(type: string): PerformanceEntry[];\n}\n\ninterface PerformanceObserverInit {\n entryTypes?: string[];\n type?: string;\n buffered?: boolean;\n}\n\n/**\n * No-op PerformanceObserver that matches the Web API interface.\n * Prevents crashes when React Native's native implementation throws.\n */\nclass NoopPerformanceObserver {\n constructor(\n _callback: (\n list: PerformanceObserverEntryList,\n observer: NoopPerformanceObserver,\n options?: PerformanceObserverInit\n ) => void\n ) {\n // Callback is never invoked - avoids triggering the native bug\n }\n\n observe(_options?: PerformanceObserverInit): void {\n // No-op\n }\n\n disconnect(): void {\n // No-op\n }\n\n takeRecords(): PerformanceEntry[] {\n return [];\n }\n\n static readonly supportedEntryTypes: string[] = [];\n}\n\n/**\n * Apply React Native performance API compatibility patches.\n *\n * Resource Timing lookup is patched on all platforms because OTel web\n * instrumentation can probe it. The PerformanceObserver replacement is\n * iOS-only to avoid the native bad_variant_access crash.\n */\nexport function applyPerformanceObserverPolyfill(): void {\n const record = globalObj as Record<string, unknown>;\n patchUnsupportedResourceTimingLookup(record);\n\n if (Platform.OS !== 'ios') {\n return;\n }\n\n const existing = record['PerformanceObserver'];\n if (existing && (existing as unknown as { name?: string }).name === 'NoopPerformanceObserver') {\n return; // Already applied\n }\n\n try {\n record['PerformanceObserver'] = NoopPerformanceObserver;\n } catch {\n // Ignore if global is frozen (e.g. in some test environments)\n }\n}\n\nfunction patchUnsupportedResourceTimingLookup(record: Record<string, unknown>): void {\n const performance = record['performance'] as\n | {\n getEntriesByType?: ((entryType: string) => PerformanceEntry[]) & Record<string, boolean>;\n }\n | undefined;\n\n if (!performance?.getEntriesByType || performance.getEntriesByType[FARO_RESOURCE_TIMING_PATCH]) {\n return;\n }\n\n const originalGetEntriesByType = performance.getEntriesByType.bind(performance);\n const patchedGetEntriesByType = ((entryType: string): PerformanceEntry[] => {\n if (entryType === RESOURCE_ENTRY_TYPE) {\n // OTel web fetch instrumentation probes browser Resource Timing APIs.\n // React Native does not expose resource entries, and calling through emits\n // \"Deprecated API for given entry type.\" warnings in development.\n return [];\n }\n\n return originalGetEntriesByType(entryType);\n }) as typeof performance.getEntriesByType;\n\n patchedGetEntriesByType[FARO_RESOURCE_TIMING_PATCH] = true;\n\n try {\n performance.getEntriesByType = patchedGetEntriesByType;\n } catch {\n // Ignore if performance is read-only in a test or host environment.\n }\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.
|
|
5
|
+
export const FARO_REACT_NATIVE_NPM_VERSION = '1.1.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
|
+
{"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"]}
|
|
@@ -17,6 +17,8 @@ const globalObj = (typeof globalThis !== 'undefined' && globalThis) ||
|
|
|
17
17
|
(typeof global !== 'undefined' && global) ||
|
|
18
18
|
(typeof window !== 'undefined' && window) ||
|
|
19
19
|
{};
|
|
20
|
+
const RESOURCE_ENTRY_TYPE = 'resource';
|
|
21
|
+
const FARO_RESOURCE_TIMING_PATCH = '__faroResourceTimingPatch';
|
|
20
22
|
/**
|
|
21
23
|
* No-op PerformanceObserver that matches the Web API interface.
|
|
22
24
|
* Prevents crashes when React Native's native implementation throws.
|
|
@@ -37,14 +39,18 @@ class NoopPerformanceObserver {
|
|
|
37
39
|
}
|
|
38
40
|
NoopPerformanceObserver.supportedEntryTypes = [];
|
|
39
41
|
/**
|
|
40
|
-
* Apply
|
|
41
|
-
*
|
|
42
|
+
* Apply React Native performance API compatibility patches.
|
|
43
|
+
*
|
|
44
|
+
* Resource Timing lookup is patched on all platforms because OTel web
|
|
45
|
+
* instrumentation can probe it. The PerformanceObserver replacement is
|
|
46
|
+
* iOS-only to avoid the native bad_variant_access crash.
|
|
42
47
|
*/
|
|
43
48
|
export function applyPerformanceObserverPolyfill() {
|
|
49
|
+
const record = globalObj;
|
|
50
|
+
patchUnsupportedResourceTimingLookup(record);
|
|
44
51
|
if (Platform.OS !== 'ios') {
|
|
45
52
|
return;
|
|
46
53
|
}
|
|
47
|
-
const record = globalObj;
|
|
48
54
|
const existing = record['PerformanceObserver'];
|
|
49
55
|
if (existing && existing.name === 'NoopPerformanceObserver') {
|
|
50
56
|
return; // Already applied
|
|
@@ -56,4 +62,27 @@ export function applyPerformanceObserverPolyfill() {
|
|
|
56
62
|
// Ignore if global is frozen (e.g. in some test environments)
|
|
57
63
|
}
|
|
58
64
|
}
|
|
65
|
+
function patchUnsupportedResourceTimingLookup(record) {
|
|
66
|
+
const performance = record['performance'];
|
|
67
|
+
if (!(performance === null || performance === void 0 ? void 0 : performance.getEntriesByType) || performance.getEntriesByType[FARO_RESOURCE_TIMING_PATCH]) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const originalGetEntriesByType = performance.getEntriesByType.bind(performance);
|
|
71
|
+
const patchedGetEntriesByType = ((entryType) => {
|
|
72
|
+
if (entryType === RESOURCE_ENTRY_TYPE) {
|
|
73
|
+
// OTel web fetch instrumentation probes browser Resource Timing APIs.
|
|
74
|
+
// React Native does not expose resource entries, and calling through emits
|
|
75
|
+
// "Deprecated API for given entry type." warnings in development.
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
return originalGetEntriesByType(entryType);
|
|
79
|
+
});
|
|
80
|
+
patchedGetEntriesByType[FARO_RESOURCE_TIMING_PATCH] = true;
|
|
81
|
+
try {
|
|
82
|
+
performance.getEntriesByType = patchedGetEntriesByType;
|
|
83
|
+
}
|
|
84
|
+
catch (_a) {
|
|
85
|
+
// Ignore if performance is read-only in a test or host environment.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
59
88
|
//# sourceMappingURL=performanceObserverPolyfill.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"performanceObserverPolyfill.js","sourceRoot":"","sources":["../../../src/util/performanceObserverPolyfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,SAAS,GACb,CAAC,OAAO,UAAU,KAAK,WAAW,IAAI,UAAU,CAAC;IACjD,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC;IACzC,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC;IACzC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"performanceObserverPolyfill.js","sourceRoot":"","sources":["../../../src/util/performanceObserverPolyfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,SAAS,GACb,CAAC,OAAO,UAAU,KAAK,WAAW,IAAI,UAAU,CAAC;IACjD,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC;IACzC,CAAC,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC;IACzC,EAAE,CAAC;AAEL,MAAM,mBAAmB,GAAG,UAAU,CAAC;AACvC,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAqB/D;;;GAGG;AACH,MAAM,uBAAuB;IAC3B,YACE,SAIS;QAET,+DAA+D;IACjE,CAAC;IAED,OAAO,CAAC,QAAkC;QACxC,QAAQ;IACV,CAAC;IAED,UAAU;QACR,QAAQ;IACV,CAAC;IAED,WAAW;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;;AAEe,2CAAmB,GAAa,EAAE,CAAC;AAGrD;;;;;;GAMG;AACH,MAAM,UAAU,gCAAgC;IAC9C,MAAM,MAAM,GAAG,SAAoC,CAAC;IACpD,oCAAoC,CAAC,MAAM,CAAC,CAAC;IAE7C,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC/C,IAAI,QAAQ,IAAK,QAAyC,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;QAC9F,OAAO,CAAC,kBAAkB;IAC5B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,GAAG,uBAAuB,CAAC;IAC1D,CAAC;IAAC,WAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;AACH,CAAC;AAED,SAAS,oCAAoC,CAAC,MAA+B;IAC3E,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAI3B,CAAC;IAEd,IAAI,CAAC,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,CAAA,IAAI,WAAW,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAC/F,OAAO;IACT,CAAC;IAED,MAAM,wBAAwB,GAAG,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChF,MAAM,uBAAuB,GAAG,CAAC,CAAC,SAAiB,EAAsB,EAAE;QACzE,IAAI,SAAS,KAAK,mBAAmB,EAAE,CAAC;YACtC,sEAAsE;YACtE,2EAA2E;YAC3E,kEAAkE;YAClE,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC,CAAwC,CAAC;IAE1C,uBAAuB,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC;IAE3D,IAAI,CAAC;QACH,WAAW,CAAC,gBAAgB,GAAG,uBAAuB,CAAC;IACzD,CAAC;IAAC,WAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;AACH,CAAC","sourcesContent":["/**\n * PerformanceObserver polyfill for React Native iOS\n *\n * React Native 0.84's native PerformanceObserver has a bug on iOS that throws\n * `bad_variant_access` when the observer callback runs and calls getEntries().\n * This happens in NativePerformance.createObserver when the native bridge\n * returns data that causes a C++ variant access error.\n *\n * OpenTelemetry's FetchInstrumentation (and other libs) may use PerformanceObserver\n * for resource timing. We provide a no-op polyfill on React Native iOS to prevent\n * the crash while preserving refresh rate vitals and other functionality.\n *\n * @see https://github.com/facebook/react-native/issues (PerformanceObserver iOS)\n */\n\nimport { Platform } from 'react-native';\n\nconst globalObj =\n (typeof globalThis !== 'undefined' && globalThis) ||\n (typeof global !== 'undefined' && global) ||\n (typeof window !== 'undefined' && window) ||\n {};\n\nconst RESOURCE_ENTRY_TYPE = 'resource';\nconst FARO_RESOURCE_TIMING_PATCH = '__faroResourceTimingPatch';\n\ninterface PerformanceEntry {\n readonly duration: number;\n readonly entryType: string;\n readonly name: string;\n readonly startTime: number;\n}\n\ninterface PerformanceObserverEntryList {\n getEntries(): PerformanceEntry[];\n getEntriesByName(name: string, type?: string): PerformanceEntry[];\n getEntriesByType(type: string): PerformanceEntry[];\n}\n\ninterface PerformanceObserverInit {\n entryTypes?: string[];\n type?: string;\n buffered?: boolean;\n}\n\n/**\n * No-op PerformanceObserver that matches the Web API interface.\n * Prevents crashes when React Native's native implementation throws.\n */\nclass NoopPerformanceObserver {\n constructor(\n _callback: (\n list: PerformanceObserverEntryList,\n observer: NoopPerformanceObserver,\n options?: PerformanceObserverInit\n ) => void\n ) {\n // Callback is never invoked - avoids triggering the native bug\n }\n\n observe(_options?: PerformanceObserverInit): void {\n // No-op\n }\n\n disconnect(): void {\n // No-op\n }\n\n takeRecords(): PerformanceEntry[] {\n return [];\n }\n\n static readonly supportedEntryTypes: string[] = [];\n}\n\n/**\n * Apply React Native performance API compatibility patches.\n *\n * Resource Timing lookup is patched on all platforms because OTel web\n * instrumentation can probe it. The PerformanceObserver replacement is\n * iOS-only to avoid the native bad_variant_access crash.\n */\nexport function applyPerformanceObserverPolyfill(): void {\n const record = globalObj as Record<string, unknown>;\n patchUnsupportedResourceTimingLookup(record);\n\n if (Platform.OS !== 'ios') {\n return;\n }\n\n const existing = record['PerformanceObserver'];\n if (existing && (existing as unknown as { name?: string }).name === 'NoopPerformanceObserver') {\n return; // Already applied\n }\n\n try {\n record['PerformanceObserver'] = NoopPerformanceObserver;\n } catch {\n // Ignore if global is frozen (e.g. in some test environments)\n }\n}\n\nfunction patchUnsupportedResourceTimingLookup(record: Record<string, unknown>): void {\n const performance = record['performance'] as\n | {\n getEntriesByType?: ((entryType: string) => PerformanceEntry[]) & Record<string, boolean>;\n }\n | undefined;\n\n if (!performance?.getEntriesByType || performance.getEntriesByType[FARO_RESOURCE_TIMING_PATCH]) {\n return;\n }\n\n const originalGetEntriesByType = performance.getEntriesByType.bind(performance);\n const patchedGetEntriesByType = ((entryType: string): PerformanceEntry[] => {\n if (entryType === RESOURCE_ENTRY_TYPE) {\n // OTel web fetch instrumentation probes browser Resource Timing APIs.\n // React Native does not expose resource entries, and calling through emits\n // \"Deprecated API for given entry type.\" warnings in development.\n return [];\n }\n\n return originalGetEntriesByType(entryType);\n }) as typeof performance.getEntriesByType;\n\n patchedGetEntriesByType[FARO_RESOURCE_TIMING_PATCH] = true;\n\n try {\n performance.getEntriesByType = patchedGetEntriesByType;\n } catch {\n // Ignore if performance is read-only in a test or host environment.\n }\n}\n"]}
|
|
@@ -2,4 +2,4 @@
|
|
|
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 declare const FARO_REACT_NATIVE_NPM_NAME: "@grafana/faro-react-native";
|
|
5
|
-
export declare const FARO_REACT_NATIVE_NPM_VERSION: "1.
|
|
5
|
+
export declare const FARO_REACT_NATIVE_NPM_VERSION: "1.1.0";
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
* @see https://github.com/facebook/react-native/issues (PerformanceObserver iOS)
|
|
14
14
|
*/
|
|
15
15
|
/**
|
|
16
|
-
* Apply
|
|
17
|
-
*
|
|
16
|
+
* Apply React Native performance API compatibility patches.
|
|
17
|
+
*
|
|
18
|
+
* Resource Timing lookup is patched on all platforms because OTel web
|
|
19
|
+
* instrumentation can probe it. The PerformanceObserver replacement is
|
|
20
|
+
* iOS-only to avoid the native bad_variant_access crash.
|
|
18
21
|
*/
|
|
19
22
|
export declare function applyPerformanceObserverPolyfill(): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/faro-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Faro instrumentations, metas, transports for React Native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"observability",
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"publishConfig": {
|
|
88
88
|
"access": "public"
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "8ca367978d53ef163886a2e99690c6f8a61416dc"
|
|
91
91
|
}
|
|
@@ -2,4 +2,4 @@
|
|
|
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' as const;
|
|
5
|
-
export const FARO_REACT_NATIVE_NPM_VERSION = '1.
|
|
5
|
+
export const FARO_REACT_NATIVE_NPM_VERSION = '1.1.0' as const;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { applyPerformanceObserverPolyfill } from './performanceObserverPolyfill';
|
|
2
|
+
|
|
3
|
+
describe('applyPerformanceObserverPolyfill', () => {
|
|
4
|
+
const originalPerformance = global.performance;
|
|
5
|
+
const originalPerformanceObserver = global.PerformanceObserver;
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
Object.defineProperty(global, 'performance', {
|
|
9
|
+
configurable: true,
|
|
10
|
+
value: originalPerformance,
|
|
11
|
+
writable: true,
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(global, 'PerformanceObserver', {
|
|
14
|
+
configurable: true,
|
|
15
|
+
value: originalPerformanceObserver,
|
|
16
|
+
writable: true,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('returns an empty list for unsupported resource timing lookups', () => {
|
|
21
|
+
const getEntriesByType = jest.fn(() => [{ entryType: 'mark', name: 'test' }]);
|
|
22
|
+
Object.defineProperty(global, 'performance', {
|
|
23
|
+
configurable: true,
|
|
24
|
+
value: { getEntriesByType },
|
|
25
|
+
writable: true,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
applyPerformanceObserverPolyfill();
|
|
29
|
+
|
|
30
|
+
expect(global.performance.getEntriesByType('resource')).toEqual([]);
|
|
31
|
+
expect(getEntriesByType).not.toHaveBeenCalledWith('resource');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('delegates non-resource timing lookups to the original implementation', () => {
|
|
35
|
+
const markEntry = { entryType: 'mark', name: 'test' };
|
|
36
|
+
const getEntriesByType = jest.fn(() => [markEntry]);
|
|
37
|
+
Object.defineProperty(global, 'performance', {
|
|
38
|
+
configurable: true,
|
|
39
|
+
value: { getEntriesByType },
|
|
40
|
+
writable: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
applyPerformanceObserverPolyfill();
|
|
44
|
+
|
|
45
|
+
expect(global.performance.getEntriesByType('mark')).toEqual([markEntry]);
|
|
46
|
+
expect(getEntriesByType).toHaveBeenCalledWith('mark');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -21,6 +21,9 @@ const globalObj =
|
|
|
21
21
|
(typeof window !== 'undefined' && window) ||
|
|
22
22
|
{};
|
|
23
23
|
|
|
24
|
+
const RESOURCE_ENTRY_TYPE = 'resource';
|
|
25
|
+
const FARO_RESOURCE_TIMING_PATCH = '__faroResourceTimingPatch';
|
|
26
|
+
|
|
24
27
|
interface PerformanceEntry {
|
|
25
28
|
readonly duration: number;
|
|
26
29
|
readonly entryType: string;
|
|
@@ -71,15 +74,20 @@ class NoopPerformanceObserver {
|
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
/**
|
|
74
|
-
* Apply
|
|
75
|
-
*
|
|
77
|
+
* Apply React Native performance API compatibility patches.
|
|
78
|
+
*
|
|
79
|
+
* Resource Timing lookup is patched on all platforms because OTel web
|
|
80
|
+
* instrumentation can probe it. The PerformanceObserver replacement is
|
|
81
|
+
* iOS-only to avoid the native bad_variant_access crash.
|
|
76
82
|
*/
|
|
77
83
|
export function applyPerformanceObserverPolyfill(): void {
|
|
84
|
+
const record = globalObj as Record<string, unknown>;
|
|
85
|
+
patchUnsupportedResourceTimingLookup(record);
|
|
86
|
+
|
|
78
87
|
if (Platform.OS !== 'ios') {
|
|
79
88
|
return;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
const record = globalObj as Record<string, unknown>;
|
|
83
91
|
const existing = record['PerformanceObserver'];
|
|
84
92
|
if (existing && (existing as unknown as { name?: string }).name === 'NoopPerformanceObserver') {
|
|
85
93
|
return; // Already applied
|
|
@@ -91,3 +99,35 @@ export function applyPerformanceObserverPolyfill(): void {
|
|
|
91
99
|
// Ignore if global is frozen (e.g. in some test environments)
|
|
92
100
|
}
|
|
93
101
|
}
|
|
102
|
+
|
|
103
|
+
function patchUnsupportedResourceTimingLookup(record: Record<string, unknown>): void {
|
|
104
|
+
const performance = record['performance'] as
|
|
105
|
+
| {
|
|
106
|
+
getEntriesByType?: ((entryType: string) => PerformanceEntry[]) & Record<string, boolean>;
|
|
107
|
+
}
|
|
108
|
+
| undefined;
|
|
109
|
+
|
|
110
|
+
if (!performance?.getEntriesByType || performance.getEntriesByType[FARO_RESOURCE_TIMING_PATCH]) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const originalGetEntriesByType = performance.getEntriesByType.bind(performance);
|
|
115
|
+
const patchedGetEntriesByType = ((entryType: string): PerformanceEntry[] => {
|
|
116
|
+
if (entryType === RESOURCE_ENTRY_TYPE) {
|
|
117
|
+
// OTel web fetch instrumentation probes browser Resource Timing APIs.
|
|
118
|
+
// React Native does not expose resource entries, and calling through emits
|
|
119
|
+
// "Deprecated API for given entry type." warnings in development.
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return originalGetEntriesByType(entryType);
|
|
124
|
+
}) as typeof performance.getEntriesByType;
|
|
125
|
+
|
|
126
|
+
patchedGetEntriesByType[FARO_RESOURCE_TIMING_PATCH] = true;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
performance.getEntriesByType = patchedGetEntriesByType;
|
|
130
|
+
} catch {
|
|
131
|
+
// Ignore if performance is read-only in a test or host environment.
|
|
132
|
+
}
|
|
133
|
+
}
|