{"_id":"@grafana/faro-react-native","_rev":"10-8fbfaf2ce6d5dc467937f171b4b73c18","name":"@grafana/faro-react-native","dist-tags":{"next":"1.0.0-alpha.1","latest":"1.4.0"},"versions":{"1.0.0-alpha.1":{"name":"@grafana/faro-react-native","version":"1.0.0-alpha.1","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.0.0-alpha.1","maintainers":[{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"dcba7cd0afae5440cb1b0b2775e811dc34971a6c","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.0.0-alpha.1.tgz","fileCount":2226,"integrity":"sha512-mf/uGqF6JW8Nb886cyBJj95pYmuR5bnWOWAMIU1N0XxZe4EIa7B7yOqHaB0xPXIGzo5cfOShIV+bQXBHILXcHQ==","signatures":[{"sig":"MEYCIQCraj9sfi6GTSnVRt95oyvZhc9q2pFRkM6bF3DEd/xCBQIhAKGVaKfUIcav1EOyYuudwpw2RzSHKZFKKjnSGDn6nKa0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11643114},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"486ee5d65eb1e274a7e207e0c9c04cccffe98c2e","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"grafanabot","email":"bot@grafana.com"},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.3/node@v22.18.0+arm64 (darwin)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","dependencies":{"@grafana/faro-core":"^2.2.3","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.10.1","@types/react":"^19.1.1","@types/react-native":"^0.73.0"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","@react-navigation/native":">=6.0.0","@grafana/faro-react-native-tracing":">=1.0.0"},"peerDependenciesMeta":{"@react-navigation/native":{"optional":true},"@grafana/faro-react-native-tracing":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.0.0-alpha.1_1774886398485_0.9421892456804255","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@grafana/faro-react-native","version":"1.0.0","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.0.0","maintainers":[{"name":"gf_joshhunt","email":"josh.hunt@grafana.com"},{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"f09e420a144a77ff041e5eeed414d90615f10100","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.0.0.tgz","fileCount":564,"integrity":"sha512-6KXtbKHJBtsquopTs9rEB9EBQTDigVn4C/jHtPrxe6/DbwaFePhCtih2JUcEwJIPYjGP4evrEO7A8WbCYEkuoA==","signatures":[{"sig":"MEYCIQDiOYalcqjbgYVYp7q7Zhb1zdJmy+R7eGJDxqTh7PIltQIhANiaNEiBeA9qPvT1TWM+5YVjuOEwPFnUkBZ/7H5kAiwc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@grafana%2ffaro-react-native@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1988806},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"00172e294e05bcf2faa2193d01aabc43c3cc8134","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:786c2ee2-bc06-4efc-a445-97a93e7bf5dc"}},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.3/node@v24.14.1+x64 (linux)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{"@grafana/faro-core":"^2.2.3","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.10.1","@types/react":"^19.1.1","react-native-mmkv":"^4.3.1","@types/react-native":"^0.73.0"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0","@grafana/faro-react-native-tracing":">=1.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true},"@grafana/faro-react-native-tracing":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.0.0_1776251816618_0.7462683244849095","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@grafana/faro-react-native","version":"1.1.0","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.1.0","maintainers":[{"name":"gf_joshhunt","email":"josh.hunt@grafana.com"},{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"76e24dacc6101937c4b92ea024cd5c86adf004ea","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.1.0.tgz","fileCount":565,"integrity":"sha512-KRAZJFCk2ZVgdi1Z2SeVt9c7r119UO1lCc6q+GDeApQHFPQEbICNmgIFFPL6EGceVPQ32F5mj2xGr/q/p6abtA==","signatures":[{"sig":"MEYCIQC9QsgK4fQeNIoCeKavhFsFR6YAEAC6zGRjMBDeTIfd6AIhAObea6ysK3yBQ8zWpM24alBzPI3LzTweFhkeYObgt0dL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@grafana%2ffaro-react-native@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1999364},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"8ca367978d53ef163886a2e99690c6f8a61416dc","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:786c2ee2-bc06-4efc-a445-97a93e7bf5dc"}},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.3/node@v24.14.1+x64 (linux)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{"@grafana/faro-core":"^2.2.3","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.10.1","@types/react":"^19.1.1","react-native-mmkv":"^4.3.1","@types/react-native":"^0.73.0"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0","@grafana/faro-react-native-tracing":">=1.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true},"@grafana/faro-react-native-tracing":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.1.0_1778243645102_0.3803405009271379","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@grafana/faro-react-native","version":"1.2.0","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.2.0","maintainers":[{"name":"gf_joshhunt","email":"josh.hunt@grafana.com"},{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"cb5dd2db63df9147a4e9db27adde613f535f28b0","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.2.0.tgz","fileCount":566,"integrity":"sha512-wL+ZWVhfbKHlbb2zzuEdOscrtGDEumknrKya9Dy989W9j+hWOV6mhtXoa2pXeBAMMFrfkd02uV2t+yxMxK1MPQ==","signatures":[{"sig":"MEQCIAnK1npTwX1CQpBRRJgdX6OiceiUAvcv+XmF85m0UDdAAiAlZVqHEoBzjtgXyigEGM77eL6OXSEnAMTVjDlemms7Wg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2045411},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"65b49f4c649e3400c74495257d33bd28d9af4b58","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"grafanabot","email":"bot@grafana.com"},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.7/node@v24.6.0+arm64 (darwin)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.6.0","dependencies":{"@grafana/faro-core":"^2.2.3","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.12.3","@types/react":"19.2.14","react-native-mmkv":"4.3.1","@types/react-native":"0.73.0","react-native-nitro-modules":"0.35.6"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.2.0_1779870302813_0.9859379837069946","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@grafana/faro-react-native","version":"1.2.1","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.2.1","maintainers":[{"name":"gf_joshhunt","email":"josh.hunt@grafana.com"},{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"dea6009a0a1326eb7fce4916b057acdb2596f867","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.2.1.tgz","fileCount":566,"integrity":"sha512-TS8xI4p/i7OMxGnxPxmBIBa/CUcPn2mdltS8v5ra06KXolKeTA1bYvx6ycn64kP8h4wRAj8ugirhR1wDZtqlOA==","signatures":[{"sig":"MEUCIH2QmnygeUu5xVd7I4AWAynVf5NE8j04yU01z7sDRV/IAiEA9Vf1OHON0RpLmeBOpHK3sbaG0C9XKJmFzLdHxh+ydvk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@grafana%2ffaro-react-native@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2045562},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"ae48b3fc6f2fc4201d634edd38e2713f459dd008","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:056f8e31-f6d9-4797-a7e4-71301b426de9"}},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.7/node@v24.16.0+x64 (linux)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","dependencies":{"@grafana/faro-core":"^2.2.3","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.12.4","@types/react":"19.2.15","react-native-mmkv":"4.3.1","@types/react-native":"0.73.0","react-native-nitro-modules":"0.35.6"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.2.1_1780472196873_0.0681251239480225","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@grafana/faro-react-native","version":"1.3.0","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.3.0","maintainers":[{"name":"gf_joshhunt","email":"josh.hunt@grafana.com"},{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"6ce1731d8e199b1413d1574e43d70f7ec8de659b","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.3.0.tgz","fileCount":669,"integrity":"sha512-KeohD3S2xmiukL5YW3mOGDqvFiwpD9IzJ2EtZ50ktEsnA+6EMt2AtJ8RzJfAamGTYqemBpDiw51rqB4ID+C2xg==","signatures":[{"sig":"MEUCIQDoKi6dJB/eVMGn5BajeKqLL5qzZrlVB6XgZnJEaTYaAgIgf7veOO6RY29oPq+JW+PYeFKIx1RiX6mTke6Fh+1p6NQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@grafana%2ffaro-react-native@1.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2476284},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"7cd0482f59a7eefad5a812e468d259e17ad39ed6","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:056f8e31-f6d9-4797-a7e4-71301b426de9"}},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.7/node@v24.17.0+x64 (linux)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.17.0","dependencies":{"@grafana/faro-core":"^2.7.0","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.12.4","@types/react":"19.2.15","react-native-mmkv":"4.3.1","@types/react-native":"0.73.0","react-native-nitro-modules":"0.35.6"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.3.0_1782892184086_0.7973936555715904","host":"s3://npm-registry-packages-npm-production"}},"1.3.1":{"name":"@grafana/faro-react-native","version":"1.3.1","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"author":{"name":"Grafana Labs"},"license":"Apache-2.0","_id":"@grafana/faro-react-native@1.3.1","maintainers":[{"name":"gf_joshhunt","email":"josh.hunt@grafana.com"},{"name":"grafanabot","email":"bot@grafana.com"}],"homepage":"https://github.com/grafana/faro-react-native-sdk","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"388490f913801fa9be2ddf02ecf24e768d8a0c4c","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.3.1.tgz","fileCount":669,"integrity":"sha512-gxruvkAWu/wCJIj3YtaiXBswKqzXw4tDX63QNAP+MyUp1Wxnn9bGwo77db1Iyu8qC+2XBZaRf5+fqUcPeakw+g==","signatures":[{"sig":"MEUCIE/rH2EvXwSuShJZ1jMpqz2857dGYzAR2kka77xEi3E3AiEA21eKP3EHBF18jfD4CU8wjI1mU9juEaKPgN/JOPuQAeI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@grafana%2ffaro-react-native@1.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2476332},"main":"./dist/cjs/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","gitHead":"fed3084557bb9c34129fff2b190ad4758fad7daf","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:056f8e31-f6d9-4797-a7e4-71301b426de9"}},"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.7/node@v24.18.0+x64 (linux)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"@grafana/faro-core":"^2.8.2","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.12.4","@types/react":"19.2.15","react-native-mmkv":"4.3.1","@types/react-native":"0.73.0","react-native-nitro-modules":"0.36.1"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/faro-react-native_1.3.1_1784280140083_0.6503596232268163","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"_id":"@grafana/faro-react-native@1.4.0","bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"dist":{"shasum":"481895b9f86c7f387d38be38dab5fc0bb8fb8dba","tarball":"https://registry.npmjs.org/@grafana/faro-react-native/-/faro-react-native-1.4.0.tgz","fileCount":717,"integrity":"sha512-ngf62n1htJwvpw/U/XHj9X63+LJs1c9z3k0scGKh8UfhuVRZqcYHwVJeO4CUfriHogWeR/i+7QoUPqNdOa56lg==","signatures":[{"sig":"MEYCIQCpWMKc74B5swOvu9VaXhhY4gfp8XrBhvmz9KzfrXzvEQIhAKQ7VyFkzfB94AV+NSU2tPjvgn1YcpwbqoHSDeDQkDa8","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDlFhX4rAT13h4dKiNO/RzLPqQPbD203sX2buwC6NE8yAiBHZMVyq+tWjkRdNxlnvmR3+9ui15bA8IHof0bqSbAlig=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@grafana%2ffaro-react-native@1.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":2954904},"main":"./dist/cjs/index.js","name":"@grafana/faro-react-native","types":"./dist/types/index.d.ts","author":{"name":"Grafana Labs"},"module":"./dist/esm/index.js","gitHead":"38d87a3474585e1177c38d2e9acd04860a4236e6","license":"Apache-2.0","scripts":{"build":"run-s 'build:*'","clean":"rimraf dist/ yarn-error.log","start":"yarn watch","watch":"run-p 'watch:compile:*'","quality":"run-s 'quality:*'","version":"node ./scripts/sync-faro-rn-package-meta.mjs && git add src/generated/faroRNPackageMeta.ts","quality:lint":"run-p 'quality:lint:eslint' 'quality:lint:prettier'","quality:test":"../../node_modules/.bin/jest","build:compile":"run-p 'build:compile:*'","quality:format":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -w \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\"","build:compile:cjs":"tsc --build tsconfig.cjs.json","build:compile:esm":"tsc --build tsconfig.esm.json","sync:package-meta":"node ./scripts/sync-faro-rn-package-meta.mjs","watch:compile:cjs":"yarn build:compile:cjs -w","watch:compile:esm":"yarn build:compile:esm -w","quality:lint:eslint":"eslint --cache --cache-location ../../.cache/eslint/reactNative \"./**/*.{js,jsx,ts,tsx}\"","quality:circular-deps":"madge --circular .","quality:lint:prettier":"prettier --cache --cache-location=../../.cache/prettier/reactNative --ignore-path ../../.prettierignore -c \"./**/*.{js,jsx,ts,tsx,css,scss,md,yaml,yml,json}\""},"version":"1.4.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:056f8e31-f6d9-4797-a7e4-71301b426de9"}},"homepage":"https://github.com/grafana/faro-react-native-sdk","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"_npmVersion":"lerna/9.0.7/node@v24.20.0+x64 (linux)","description":"Faro instrumentations, metas, transports for React Native.","directories":{},"maintainers":[{"name":"grafanabot","email":"bot@grafana.com"}],"sideEffects":false,"_nodeVersion":"24.20.0","dependencies":{"@grafana/faro-core":"^2.8.2","react-native-device-info":"^11.1.0","@react-native-async-storage/async-storage":"^1.21.0"},"react-native":"./src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/node":"24.12.4","@types/react":"19.2.18","react-native-mmkv":"4.3.2","@types/react-native":"0.73.0","react-test-renderer":"19.2.3","@types/react-test-renderer":"19.1.0","react-native-nitro-modules":"0.36.1"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.70.0","react-native-mmkv":">=2.0.0","@react-navigation/native":">=6.0.0"},"peerDependenciesMeta":{"react-native-mmkv":{"optional":true},"@react-navigation/native":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/faro-react-native_1.4.0_1790255879067_0.7952954803150534"}}},"time":{"created":"2026-03-30T15:59:58.424Z","modified":"2026-09-24T13:17:59.606Z","1.0.0-alpha.1":"2026-03-30T15:59:58.786Z","1.0.0":"2026-04-15T11:16:56.854Z","1.1.0":"2026-05-08T12:34:05.375Z","1.2.0":"2026-05-27T08:25:03.053Z","1.2.1":"2026-06-03T07:36:37.060Z","1.3.0":"2026-07-01T07:49:44.274Z","1.3.1":"2026-07-17T09:22:20.325Z","1.4.0":"2026-09-24T13:17:59.238Z"},"bugs":{"url":"https://github.com/grafana/faro-react-native-sdk/issues"},"author":{"name":"Grafana Labs"},"license":"Apache-2.0","homepage":"https://github.com/grafana/faro-react-native-sdk","keywords":["observability","apm","rum","logs","traces","metrics","react-native","mobile"],"repository":{"url":"git+https://github.com/grafana/faro-react-native-sdk.git","type":"git","directory":"packages/react-native"},"description":"Faro instrumentations, metas, transports for React Native.","maintainers":[{"name":"grafanabot","email":"bot@grafana.com"}],"readme":"# @grafana/faro-react-native\n\nGrafana Faro React Native SDK - Real User Monitoring for React Native applications.\n\n## Installation\n\n```bash\nnpm install @grafana/faro-react-native\n# or\nyarn add @grafana/faro-react-native\n```\n\n## Quick Start\n\n```tsx\nimport { initializeFaro } from '@grafana/faro-react-native';\n\n// Initialize Faro in your app entry point (e.g., App.tsx or index.js)\ninitializeFaro({\n  // Required\n  app: {\n    name: 'your-app-name',\n    version: '1.0.0',\n    environment: 'production', // optional\n  },\n  url: 'https://faro-collector-prod-YOUR-REGION.grafana.net/collect/YOUR_TOKEN_HERE', // required when fetch transport enabled\n\n  // Transports - optional\n  apiKey: 'your-api-key',\n  enableTransports: {\n    fetch: true, // default\n    offline: false, // optional, caches when offline\n    console: false, // optional, logs to Metro for debugging\n  },\n  transports: [], // optional, extra transports\n\n  // Instrumentation flags - optional\n  enableConsoleCapture: true,\n  enableErrorReporting: true,\n  enableUserActions: true,\n  enableCrashReporting: false,\n  anrTracking: false, // optional, Android only\n  anrOptions: {}, // optional\n  cpuUsageVitals: true,\n  memoryUsageVitals: true,\n  refreshRateVitals: false,\n  fetchVitalsInterval: 30000, // optional, ms\n  frameMonitoringOptions: {}, // optional, when refreshRateVitals true\n  enableTracing: false, // optional, requires @grafana/faro-react-native-tracing\n  tracingOptions: {\n    instrumentationOptions: {\n      // Traces fetch() by default. Enable this for axios/direct XHR apps.\n      enableXhrInstrumentation: false,\n    },\n  }, // optional\n\n  // Instrumentation options - optional\n  consoleCaptureOptions: {},\n  userActionsOptions: {},\n  ignoreUrls: [],\n\n  // Session - optional\n  sessionTracking: {\n    enabled: true,\n    persistent: true,\n    maxSessionPersistenceTime: 15 * 60 * 1000,\n    // Optional: sampling: new SamplingRate(0.1) or new SamplingFunction((ctx) => ...)\n    // Omit sampling to record all sessions (default).\n    // generateSessionId, onSessionChange, session.attributes - optional\n  },\n\n  // faro-core - optional\n  dedupe: true,\n  metas: [],\n  instrumentations: [], // extra instrumentations (built-ins from flags above)\n  // ignoreErrors, beforeSend, preserveOriginalError, internalLoggerLevel, parseStacktrace - optional\n});\n```\n\n## Error symbolication\n\nThis section ties together **SDK configuration**, **release builds**, **uploaded source maps**, and **errors/crashes** so stacks from Hermes release binaries show up as your real source files in Grafana.\n\n### What you configure in the app\n\n1. **`initializeFaro({ app: { name: '…' }, … })`** — `app.name` must match **`appName`** in [@grafana/faro-metro-plugin](https://www.npmjs.com/package/@grafana/faro-metro-plugin) (`metro.config.js`).\n2. **`releaseBundleFilename`** (e.g. `index.android.bundle` or `main.jsbundle`) — must match the **bundle basename** your composed source map uses in its top-level **`file`** field (same as Metro’s `sourceMapFile` when you override it). This keeps stack **filenames** aligned with the map the backend will look up.\n3. **Metro** — wrap config with **`withFaroConfig`** so the release bundle gets the Faro preamble and the right map shape for Hermes.\n\nSee [Android release upload](#error-symbolication-android-release-upload) and [iOS release upload](#error-symbolication-ios-release-upload) below.\n\nYou do **not** set `app.bundleId` in `initializeFaro` for this flow.\n\n### What happens when you ship a new release\n\nFor **Android** and **iOS** release builds, the native pipeline (Metro → Hermes → **`compose-source-maps.js`**) produces a single **composed** `.map` file for that binary. **`@grafana/faro-react-native`** autolinking runs **`faro-upload-source-map`** (→ **`faro-cli metro upload`**) after that file exists, as long as you provide **`FARO_BUNDLE_ID`** and the **`FARO_SOURCEMAP_*`** variables (see [Android](#error-symbolication-android-release-upload) / [iOS](#error-symbolication-ios-release-upload) below). The map is stored in Grafana’s source map API **keyed by that bundle id**.\n\nThe Metro preamble, baked in at bundle time, sets a global like **`globalThis['__faroBundleId_<appName>']`** to the same **`FARO_BUNDLE_ID`** value so runtime telemetry can reference the correct map record.\n\n### What happens at runtime (errors and crashes)\n\nWhen the app starts, **`@grafana/faro-core`** runs **`getBundleId(app.name)`** during meta registration. That helper reads the preamble global above (see [getBundleId](https://github.com/grafana/faro-web-sdk/blob/main/packages/core/src/utils/sourceMaps.ts) in **`@grafana/faro-core`**) and sets **`meta.app.bundleId`** on outgoing Faro payloads. You get a stable link between “`this app binary`” and “`this uploaded map`” without hard-coding the id in JS.\n\nWhen the SDK reports an error or crash, it parses the stack into **structured frames** (`filename`, `lineno`, `colno`, …). Those values describe positions **in the shipped JS bundle / Hermes stack space** (and filenames normalized to match the map, e.g. stripping `address at …` prefixes so **`frame.filename`** matches the map’s **`file`** field). The SDK **does not** load the `.map` on the device and **does not** rewrite frames to `src/**/*.tsx` paths inside the payload sent to the collector.\n\n### How those frames connect to your real source files\n\n**In Grafana (Frontend Observability)**, the ingest stack resolves frames using the **composed** source map whose **`bundleId`** matches **`meta.app.bundleId`**. That step turns bundle-relative **line/column** (and the bundle **`file`** name) into human-readable paths such as **`src/...tsx`** in the UI. If **`meta.app.bundleId`** is missing or wrong, or the wrong map was uploaded, symbolication will not line up with this build.\n\nSo end-to-end: **same bundle id** in the binary preamble, in **`meta.app.bundleId`**, and in the source map upload → **correct map lookup** → **readable stacks**.\n\n---\n\n### Error symbolication: Android release upload\n\nHermes release builds produce the composed map at:\n\n`app/build/generated/sourcemaps/react/release/index.android.bundle.map`\n\nThis package’s **`android/build.gradle`** registers **`faroUploadComposedSourceMapAndroidRelease`** on your **`:app`** project and finalizes **`bundleReleaseJsAndAssets`** / **`createBundleReleaseJsAndAssets`**.\n\n**Setup:**\n\n1. `npm install @grafana/faro-react-native @grafana/faro-metro-plugin`\n2. Wrap **`metro.config.js`** with `withFaroConfig({...}, faroOpts)` (see the metro plugin README).\n3. Export **`FARO_BUNDLE_ID`** and **`FARO_SOURCEMAP_*`** (`FARO_SOURCEMAP_ENDPOINT`, `FARO_SOURCEMAP_APP_ID`, `FARO_SOURCEMAP_STACK_ID`, `FARO_SOURCEMAP_API_KEY`) before release builds.\n4. `yarn android --mode=release` (or `installRelease` / `assembleRelease` / `bundleRelease`).\n\n**No `android/app/build.gradle` edits** are required. The task runs **`node_modules/@grafana/faro-metro-plugin/bin/faro-upload-source-map.js`**. Use **`FARO_SKIP_SOURCEMAP_UPLOAD=1`** to skip upload when iterating offline.\n\n### Error symbolication: iOS release upload\n\n**`pod install`** adds **`[Faro] Upload composed source map (Release)`** (from **`react-native.config.js`**). On **Release**, it runs **`ios/faro-upload-composed-source-map.sh`**, which invokes the same shim after the composed **`main.jsbundle.map`** exists.\n\n**Setup:**\n\n1. Same installs and **`metro.config.js`** as Android.\n2. Ensure **`SOURCEMAP_FILE`** is set when **`react-native-xcode.sh`** runs (Release). React Native has **no default**; without it, **`main.jsbundle.map`** is not written. Prefer **`ios/.xcode.env`** or **`ios/.xcode.env.local`** (sourced before bundling):\n\n   ```sh\n   export SOURCEMAP_FILE=\"${DERIVED_FILE_DIR}/main.jsbundle.map\"\n   ```\n\n   **`DERIVED_FILE_DIR`** is set by Xcode for the bundle phase. Alternatively, set the same value as a **User-Defined** build setting on the app target (`SOURCEMAP_FILE = $(DERIVED_FILE_DIR)/main.jsbundle.map`). Exporting **`SOURCEMAP_FILE`** only in an outer shell before `yarn ios` is unreliable because **`${DERIVED_FILE_DIR}`** is not your shell’s variable.\n\n3. Export the same **`FARO_*`** variables for the Release build environment.\n\n**Debug** skips upload. **`FARO_SKIP_SOURCEMAP_UPLOAD=1`** skips on Release when needed.\n\n**Manual / CI:** run **`faro-cli metro upload`** with **`--map`** pointing at the **composed** map and the same flags.\n\n## Features\n\n### Core Instrumentations\n\n**Always enabled:**\n\n- **Session Instrumentation** - Tracks user sessions\n- **View Instrumentation** - Tracks screen/route changes\n- **App State Instrumentation** - Tracks when app goes to background/foreground\n- **Performance Instrumentation** - Monitors CPU usage, memory usage, and app startup time using native OS APIs\n- **Startup Instrumentation** - Automatically tracks app startup duration from process start\n- **HTTP Instrumentation** - Tracks `fetch()` requests and correlates them with user actions\n- **XHR Instrumentation** - Tracks `XMLHttpRequest` calls when tracing is disabled; with `enableTracing: true`, enable XHR tracing via `tracingOptions.instrumentationOptions.enableXhrInstrumentation`\n\n**Enabled by default** (opt-out via config):\n\n- **Console Instrumentation** - Captures console logs, warnings, and errors — `enableConsoleCapture` (default: true)\n- **Errors Instrumentation** - Captures unhandled errors and promise rejections — `enableErrorReporting` (default: true)\n- **User Actions Instrumentation** - Tracks user interactions with components — `enableUserActions` (default: true)\n\n**Disabled by default** (opt-in via config):\n\n- **Frame Monitoring Instrumentation** - Monitors refresh rate / frame drops — `refreshRateVitals` (default: false)\n- **ANR Instrumentation** - Detects Application Not Responding (Android only) — `anrTracking` (default: false)\n- **Crash Reporting Instrumentation** - Captures native crashes — `enableCrashReporting` (default: false)\n- **Tracing Instrumentation** - OpenTelemetry distributed tracing (requires `@grafana/faro-react-native-tracing`) — `enableTracing` (default: false)\n\nOn iOS and Android 11 or newer, recovered native crashes are reported on the next launch with the session ID\nand timestamp from the process that crashed. Failed deliveries remain pending for a later launch; Android\nretries reports for up to seven days, while PLCrashReporter keeps one pending iOS report. The newly started\nsession continues to identify live telemetry. If the original session context is unavailable, the recovered\ncrash is skipped rather than assigned to the newly started session.\n\n### React Integration\n\n- **Error Boundary** - Catch and report React component errors with `FaroErrorBoundary`\n\n### Tracking User Actions\n\nThe SDK provides intelligent user action tracking with:\n\n- **Intelligent Duration Tracking**: Automatically determines when actions complete\n- **HTTP Request Correlation**: Tracks HTTP requests triggered by user actions\n- **Automatic Lifecycle Management**: No manual `end()` calls needed with HOC\n- **Halt State**: Waits for pending async operations before ending actions\n\n#### 1. Using the HOC (Higher-Order Component) - Recommended\n\nWrap your touchable components with `withFaroUserAction` for automatic tracking. Create one HOC per action (as in the [demo app](../../demo)):\n\n```tsx\nimport { TouchableOpacity, Text } from 'react-native';\nimport { withFaroUserAction } from '@grafana/faro-react-native';\n\n// Create tracked components at module level (one per action name)\nconst SubmitButton = withFaroUserAction(TouchableOpacity, 'submit_form');\nconst LoadDataButton = withFaroUserAction(TouchableOpacity, 'load_data');\n\nfunction MyForm() {\n  return (\n    <SubmitButton onPress={handleSubmit}>\n      <Text>Submit</Text>\n    </SubmitButton>\n  );\n}\n\nfunction DataLoader() {\n  return (\n    <LoadDataButton onPress={handleLoadData}>\n      <Text>Load Data</Text>\n    </LoadDataButton>\n  );\n}\n```\n\n**Automatic Features:**\n\n- User action starts on press\n- HTTP requests triggered by the action are automatically correlated\n- Action ends ~100ms after the last activity (HTTP request completion)\n- If HTTP requests are pending, enters \"halt\" state and waits up to 10 seconds\n- No manual `end()` call required!\n\nYou can override the action name and add context per instance with `faroActionName` and `faroContext`:\n\n```tsx\n<SubmitButton\n  onPress={handleSubmit}\n  faroActionName=\"custom_action_name\"\n  faroContext={{ formType: 'contact', userId: '123' }}\n>\n  <Text>Submit</Text>\n</SubmitButton>\n```\n\n**Example with HTTP** (matches demo's FetchButton pattern):\n\n```tsx\nconst FetchButton = withFaroUserAction(TouchableOpacity, 'tap_with_http_request');\n\nfunction DataLoader() {\n  const handleLoad = async () => {\n    // HTTP request correlated with the action; action waits for it before ending\n    const response = await fetch('https://api.example.com/data');\n    const data = await response.json();\n    setData(data);\n  };\n\n  return (\n    <FetchButton onPress={handleLoad}>\n      <Text>Load Data</Text>\n    </FetchButton>\n  );\n}\n```\n\n#### 2. Using Manual Tracking\n\nFor workflows where you need explicit control. The action auto-ends after ~100ms if no HTTP requests are pending; call `action?.end()` when done for accurate duration with async work.\n\n**Simple (fire-and-forget)** — matches [demo](../../demo) pattern:\n\n```tsx\nimport { trackUserAction } from '@grafana/faro-react-native';\n\nfunction handleManualAction() {\n  trackUserAction('manual_user_action_demo', {\n    source: 'my_screen',\n    count: '1',\n  });\n  // Controller auto-ends after ~100ms\n}\n```\n\n**Complex (async work)** — call `end()` when done:\n\n```tsx\nasync function handleComplexAction() {\n  const action = trackUserAction('complex_workflow', {\n    step: '1',\n    userId: '123',\n  });\n\n  await performSomeWork();\n\n  // End when done for accurate duration (otherwise auto-ends ~100ms after start)\n  action?.end();\n}\n```\n\n#### How Intelligent Duration Tracking Works\n\n1. **User Action Starts**: When button is pressed or `trackUserAction()` is called\n2. **Monitor Activity**: Tracks HTTP requests started during the action\n3. **Detect Completion**:\n   - If no HTTP requests: Ends after ~100ms\n   - If HTTP requests pending: Enters \"halt\" state\n4. **Wait for HTTP**: Action stays in halt state until all HTTP requests complete\n5. **Auto-End**: Once all activity stops, action automatically ends\n6. **Timeout**: If pending operations take too long (>10s), action forcibly ends\n\n**Benefits:**\n\n- Accurate action duration including async operations\n- Correlate errors/events with the user action that triggered them\n- Better understanding of user flows and performance\n- No need to manually manage action lifecycle\n\n### User Identification\n\nAssociate telemetry data with specific users:\n\n```tsx\nimport { faro } from '@grafana/faro-react-native';\n\nfaro.api.setUser({\n  id: 'user-123',\n  username: 'john_doe',\n  email: 'john@example.com',\n  attributes: {\n    plan: 'premium',\n    signupDate: '2024-01-01',\n  },\n});\n```\n\n### Custom Events\n\nTrack custom business events:\n\n```tsx\nimport { faro } from '@grafana/faro-react-native';\n\nfaro.api.pushEvent('purchase_completed', {\n  productId: 'abc123',\n  amount: '99.99',\n  currency: 'USD',\n});\n```\n\n### Logging\n\nSend log messages with different severity levels using the `LogLevel` enum:\n\n```tsx\nimport { faro, LogLevel } from '@grafana/faro-react-native';\n\n// Default level (LogLevel.LOG) when no level is specified\nfaro.api.pushLog(['Application started']);\n\n// Explicit log levels\nfaro.api.pushLog(['User signed in'], { level: LogLevel.INFO });\nfaro.api.pushLog(['Cache hit ratio: 0.95'], { level: LogLevel.DEBUG });\nfaro.api.pushLog(['Retrying request, attempt 3'], { level: LogLevel.WARN });\nfaro.api.pushLog(['Payment processing failed'], { level: LogLevel.ERROR });\nfaro.api.pushLog(['Entering auth flow'], { level: LogLevel.TRACE });\n```\n\n**Available log levels** (from `LogLevel` enum):\n\n| Level            | Description                                        |\n| ---------------- | -------------------------------------------------- |\n| `LogLevel.TRACE` | Fine-grained diagnostic information                |\n| `LogLevel.DEBUG` | Detailed information useful during development     |\n| `LogLevel.INFO`  | General informational messages                     |\n| `LogLevel.LOG`   | Standard log messages (default when omitted)       |\n| `LogLevel.WARN`  | Potentially harmful situations                     |\n| `LogLevel.ERROR` | Error events that might still allow the app to run |\n\nYou can also attach additional context to log messages:\n\n```tsx\nfaro.api.pushLog(['Order placed'], {\n  level: LogLevel.INFO,\n  context: {\n    orderId: 'order-456',\n    userId: 'user-123',\n  },\n});\n```\n\n### Performance Measurements\n\nTrack performance metrics:\n\n```tsx\nimport { faro } from '@grafana/faro-react-native';\n\nconst startTime = Date.now();\nawait performHeavyOperation();\nconst duration = Date.now() - startTime;\n\nfaro.api.pushMeasurement({\n  type: 'heavy_operation',\n  values: {\n    duration,\n  },\n});\n```\n\n### Manual Error Tracking\n\nReport errors manually:\n\n```tsx\nimport { faro } from '@grafana/faro-react-native';\n\ntry {\n  await riskyOperation();\n} catch (error) {\n  faro.api.pushError(error, {\n    context: {\n      operation: 'riskyOperation',\n      userId: '123',\n    },\n  });\n}\n```\n\n### React Error Boundary\n\nCatch and report React component errors automatically with Faro's Error Boundary:\n\n#### Using the Component\n\n```tsx\nimport { FaroErrorBoundary } from '@grafana/faro-react-native';\nimport { Text, View } from 'react-native';\n\nfunction App() {\n  return (\n    <FaroErrorBoundary fallback={<Text>Something went wrong</Text>}>\n      <YourApp />\n    </FaroErrorBoundary>\n  );\n}\n```\n\n#### Using the HOC\n\n```tsx\nimport { withFaroErrorBoundary } from '@grafana/faro-react-native';\nimport { Text } from 'react-native';\n\nconst MyComponent = () => <Text>Hello</Text>;\n\nexport default withFaroErrorBoundary(MyComponent, {\n  fallback: <Text>Error occurred</Text>,\n});\n```\n\n#### Advanced Configuration\n\n```tsx\nimport { FaroErrorBoundary } from '@grafana/faro-react-native';\n\nfunction App() {\n  return (\n    <FaroErrorBoundary\n      // Static fallback UI\n      fallback={<ErrorScreen />}\n      // OR: Dynamic fallback with error details and reset function\n      fallback={(error, resetError) => (\n        <View>\n          <Text>Error: {error.message}</Text>\n          <Button title=\"Try Again\" onPress={resetError} />\n        </View>\n      )}\n      // Modify error before it's sent to Faro\n      beforeCapture={(error) => {\n        console.log('About to capture:', error);\n      }}\n      // React to errors\n      onError={(error) => {\n        console.error('Error caught:', error);\n      }}\n      // Lifecycle hooks\n      onMount={() => console.log('Error boundary mounted')}\n      onUnmount={(error) => console.log('Unmounting, had error:', error)}\n      onReset={(error) => console.log('Resetting from error:', error)}\n      // Pass additional options to faro.api.pushError\n      pushErrorOptions={{\n        context: {\n          screen: 'HomeScreen',\n        },\n      }}\n    >\n      <YourApp />\n    </FaroErrorBoundary>\n  );\n}\n```\n\n**Error Boundary Props:**\n\n| Prop               | Type                                             | Description                                                              |\n| ------------------ | ------------------------------------------------ | ------------------------------------------------------------------------ |\n| `fallback`         | `ReactElement \\| (error, reset) => ReactElement` | UI to show when an error occurs. Can be static or a render function.     |\n| `beforeCapture`    | `(error: Error) => void`                         | Called before error is sent to Faro. Use to modify or inspect the error. |\n| `onError`          | `(error: Error) => void`                         | Called after error is caught. Use for logging or analytics.              |\n| `onMount`          | `() => void`                                     | Called when error boundary mounts.                                       |\n| `onReset`          | `(error: Error \\| null) => void`                 | Called when error boundary is reset (via `resetError` function).         |\n| `onUnmount`        | `(error: Error \\| null) => void`                 | Called when error boundary unmounts. Receives error if one was caught.   |\n| `pushErrorOptions` | `PushErrorOptions`                               | Additional options passed to `faro.api.pushError()`.                     |\n| `children`         | `ReactNode \\| () => ReactNode`                   | Component(s) to wrap with error boundary.                                |\n\n**Features:**\n\n- ✅ **Automatic Error Reporting**: Errors are automatically sent to Faro\n- ✅ **Component Stack Traces**: Includes React component stack in error reports\n- ✅ **Custom Fallback UI**: Show user-friendly error messages\n- ✅ **Error Reset**: Programmatically recover from errors\n- ✅ **Lifecycle Hooks**: React to error boundary lifecycle events\n- ✅ **Flexible Configuration**: Static or dynamic fallback, custom error handling\n\n**Best Practices:**\n\n1. **Wrap your entire app** for global error catching:\n\n   ```tsx\n   <FaroErrorBoundary fallback={<GlobalErrorScreen />}>\n     <App />\n   </FaroErrorBoundary>\n   ```\n\n2. **Wrap critical sections** for granular error handling:\n\n   ```tsx\n   <FaroErrorBoundary fallback={<CheckoutError />}>\n     <CheckoutFlow />\n   </FaroErrorBoundary>\n   ```\n\n3. **Use dynamic fallback** for better UX:\n\n   ```tsx\n   fallback={(error, resetError) => (\n     <ErrorView error={error} onRetry={resetError} />\n   )}\n   ```\n\n4. **Combine with ErrorsInstrumentation** for comprehensive error tracking:\n   - Error Boundary catches React component errors\n   - ErrorsInstrumentation catches unhandled errors and promise rejections\n\n## Configuration\n\n### Instrumentation Flags\n\nInstrumentations are controlled by flags in `ReactNativeConfig`. Pass these to `initializeFaro()`:\n\n```tsx\n// Key instrumentation flags (all in ReactNativeConfig)\nenableErrorReporting?: boolean;     // default: true\nenableConsoleCapture?: boolean;     // default: true\nenableUserActions?: boolean;        // default: true\nenableCrashReporting?: boolean;     // default: false\nanrTracking?: boolean;              // default: false (Android only)\ncpuUsageVitals?: boolean;           // default: true\nmemoryUsageVitals?: boolean;        // default: true\nrefreshRateVitals?: boolean;        // default: false\n```\n\n### Console Instrumentation Configuration\n\nThe console instrumentation can be configured with advanced options:\n\n```tsx\nimport { initializeFaro, LogLevel } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: {\n    name: 'my-app',\n    version: '1.0.0',\n  },\n  consoleCaptureOptions: {\n    // Configure which log levels to capture\n    // By default: [LogLevel.DEBUG, LogLevel.TRACE, LogLevel.LOG] are disabled\n    disabledLevels: [LogLevel.DEBUG, LogLevel.TRACE],\n\n    // Treat console.error as log instead of error (default: false)\n    consoleErrorAsLog: false,\n\n    // Enable advanced error serialization for better error details (default: false)\n    // When enabled, payloads may become larger but include more error context\n    serializeErrors: true,\n\n    // Optional: Custom error serializer function\n    errorSerializer: (args) => {\n      return args\n        .map((arg) => {\n          if (typeof arg === 'object') {\n            return JSON.stringify(arg, null, 2); // Pretty print objects\n          }\n          return String(arg);\n        })\n        .join(' ');\n    },\n  },\n});\n```\n\n**Console Instrumentation Features:**\n\n- **Configurable Log Levels**: Choose which console methods to capture (log, info, warn, error, debug, trace)\n- **Smart Object Serialization**: Automatically converts objects to JSON strings instead of `[object Object]`\n- **Advanced Error Serialization**: Extract detailed error information including:\n  - Error message and type\n  - Stack frames with file, function, line, and column information\n  - Better handling of Error objects in console.error\n- **Flexible Error Handling**: Choose to send console.error as:\n  - Errors (default): Appears in error tracking views\n  - Logs: Appears in log views with error context\n- **Custom Serializers**: Provide your own logic for converting console arguments to strings\n- **Unpatch Support**: Clean up console patching when needed\n\n**Example Use Cases:**\n\n```tsx\n// Capture all console levels including debug\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: { name: 'my-app', version: '1.0.0' },\n  enableConsoleCapture: true,\n  consoleCaptureOptions: {\n    disabledLevels: [], // Capture everything\n  },\n});\n\n// Send console.error as logs instead of errors\ninitializeFaro({\n  // ...\n  consoleCaptureOptions: {\n    consoleErrorAsLog: true,\n  },\n});\n\n// Enable detailed error serialization\ninitializeFaro({\n  // ...\n  consoleCaptureOptions: {\n    serializeErrors: true, // Extract stack frames and error details\n  },\n});\n```\n\n### Errors Instrumentation Configuration\n\nThe errors instrumentation now includes enhanced features for React Native error tracking:\n\n```tsx\nimport { initializeFaro, ErrorsInstrumentation } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: {\n    name: 'my-app',\n    version: '1.0.0',\n  },\n  instrumentations: [\n    new ErrorsInstrumentation({\n      // Ignore specific errors by message pattern\n      ignoreErrors: [/network timeout/i, /cancelled/i, /aborted/i],\n\n      // Enable error deduplication (default: true)\n      // Prevents sending the same error multiple times within a time window\n      enableDeduplication: true,\n\n      // Deduplication time window in milliseconds (default: 5000)\n      // Errors with same message/stack within this window are considered duplicates\n      deduplicationWindow: 5000,\n\n      // Maximum number of errors to track for deduplication (default: 50)\n      maxDeduplicationEntries: 50,\n    }),\n  ],\n});\n```\n\n**Enhanced Errors Instrumentation Features:**\n\n- **React Native Stack Trace Parsing**: Automatically parses React Native stack traces into structured stack frames\n  - Supports multiple formats: Dev mode, Release/minified, Metro bundler, Native calls\n  - Extracts function name, filename, line number, and column number\n  - Handles platform-specific stack trace formats (iOS/Android)\n\n- **Platform Context**: Automatically includes platform information with every error:\n  - Platform OS (ios/android)\n  - Platform version\n  - JavaScript engine (Hermes detection)\n\n- **Error Deduplication**: Prevents duplicate error reports\n  - Tracks errors by message and stack trace\n  - Configurable time window (default: 5 seconds)\n  - Memory-efficient with configurable maximum entries\n\n- **Error Filtering**: Ignore specific errors using regex patterns\n  - Filter by error message\n  - Useful for ignoring known non-critical errors\n  - Reduces noise in error tracking\n\n- **Automatic Error Capture**:\n  - Unhandled JavaScript errors (via ErrorUtils)\n  - Unhandled promise rejections\n  - Preserves original error handlers\n\n**Example - Different Stack Trace Formats Handled:**\n\n```\n// Dev mode: at functionName (file.js:123:45)\n// Release: functionName@123:456\n// Native: at functionName (native)\n// Metro: at Object.functionName (/path/to/file.js:123:456)\n```\n\nAll formats are automatically parsed and converted to structured stack frames sent to Grafana Cloud.\n\n**Example - Platform Context Included:**\n\nEvery error report includes:\n\n```tsx\n{\n  platform: \"ios\",           // or \"android\"\n  platformVersion: \"17.0\",   // iOS/Android version\n  isHermes: \"true\"          // JavaScript engine\n}\n```\n\n**Use Cases:**\n\n```tsx\n// Ignore network-related errors\nnew ErrorsInstrumentation({\n  ignoreErrors: [/network/i, /fetch failed/i],\n});\n\n// Increase deduplication window for high-frequency errors\nnew ErrorsInstrumentation({\n  deduplicationWindow: 10000, // 10 seconds\n});\n\n// Disable deduplication for debugging\nnew ErrorsInstrumentation({\n  enableDeduplication: false,\n});\n```\n\n### Custom Configuration\n\n```tsx\nimport { initializeFaro } from '@grafana/faro-react-native';\nimport { ConsoleInstrumentation, ErrorsInstrumentation } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: {\n    name: 'my-app',\n    version: '1.0.0',\n    environment: 'production',\n  },\n  instrumentations: [new ConsoleInstrumentation(), new ErrorsInstrumentation()],\n  // Add custom metas\n  metas: [\n    // Your custom meta implementations\n  ],\n});\n```\n\n### Session Configuration\n\nSession persistence is enabled by default. The SDK does not bundle storage, so\napps using the default configuration must install the optional\n`react-native-mmkv` peer dependency:\n\n```bash\nyarn add react-native-mmkv\n```\n\nRebuild the native projects after installing or upgrading the SDK. A JavaScript-only\nor OTA update cannot add the native process-coordination methods; when they are\nunavailable, Faro uses an in-memory session rather than risking concurrent writes.\nApps using `persistent: false` use in-memory sessions and do not need MMKV.\n\nUse `maxSessionPersistenceTime` to control the inactivity and cold-start linking window. A session's\nmaximum lifetime is fixed at four hours.\n\nThe inactivity window is refreshed only by meaningful activity:\n\n| Activity source                                                           | Activity category |\n| ------------------------------------------------------------------------- | ----------------- |\n| Navigation and view changes                                               | Meaningful        |\n| Return from `background` to `active`                                      | Meaningful        |\n| Telemetry linked to a tracked user action                                 | Meaningful        |\n| React Native touch starts inside `FaroSessionActivityBoundary`            | Meaningful        |\n| Explicit `notifySessionActivity()` calls                                  | Meaningful        |\n| Other app lifecycle events                                                | Passive           |\n| Unmarked events, logs, errors, measurements, and traces                   | Passive           |\n| Unmarked HTTP and XHR requests                                            | Passive           |\n| Unmarked session, startup, ANR, frame, console, and performance telemetry | Passive           |\n\nPassive telemetry still checks whether the session has expired, but it does not\nkeep the session alive. To refresh inactivity for ordinary touches without\nemitting user-action telemetry, wrap the application once with\n`FaroSessionActivityBoundary`:\n\n```tsx\nimport { FaroSessionActivityBoundary } from '@grafana/faro-react-native';\n\nexport function App() {\n  return (\n    <FaroSessionActivityBoundary>\n      <AppNavigator />\n    </FaroSessionActivityBoundary>\n  );\n}\n```\n\nThe boundary observes touch-start responder negotiation from descendants\nwithout becoming the responder. This covers standard React Native presses,\nscrolling, dragging, and touches that focus inputs on Android and iOS.\nContinued keyboard input after focus is not another touch. Interactions\nrendered outside the boundary's React Native responder tree, or native and\naccessibility interactions that do not produce a React Native touch event, are\nalso not observed. Call `notifySessionActivity()` from those paths. The\nboundary renders a `flex: 1` view for root usage; pass `style` to override its\nlayout when wrapping a subtree. Use `withFaroUserAction` or `trackUserAction`\nonly when the interaction should also emit user-action telemetry and correlate\nrelated work.\n\n```tsx\nimport { initializeFaro, SamplingFunction, SamplingRate } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: {\n    name: 'my-app',\n    version: '1.0.0',\n  },\n  sessionTracking: {\n    enabled: true, // default: true\n    persistent: true, // default: true\n    // Inactivity and cold-start linking window (ms):\n    maxSessionPersistenceTime: 15 * 60 * 1000, // default: 15 min\n\n    // Optional: session sampling (omit = all sessions recorded)\n    // sampling: new SamplingRate(0.1), // 10% fixed\n    // sampling: new SamplingFunction((context) =>\n    //   context.meta.app?.environment === 'production' ? 0.1 : 1\n    // ),\n\n    // Optional: Custom session ID generator\n    generateSessionId: () => 'custom-session-id',\n\n    // Optional: Callback when session changes\n    onSessionChange: (previousSession, newSession) => {\n      console.log('Session changed:', previousSession?.id, '->', newSession?.id);\n    },\n\n    // Optional: Initial session attributes\n    session: {\n      attributes: {\n        customAttribute: 'value',\n      },\n    },\n  },\n});\n```\n\n**Session Types:**\n\n- **Persistent Sessions** (`persistent: true`, default): A versioned record is stored in MMKV. Every process start creates a new session and links the previous ID when the record is valid. The live session is never resumed across a process start.\n\n- **Volatile Sessions** (`persistent: false`): Stored in memory only. Each app launch creates a new session.\n\nThe persisted record contains only the current and previous session IDs, start and last-activity timestamps, the sampling decision, and a schema version. Session attributes, device state, and overrides remain in memory. Records older than `maxSessionPersistenceTime` are discarded. Unversioned records from earlier SDK versions, corrupt records, and unsupported schema versions are removed and start an unlinked session.\n\nPersistent storage is isolated by native process:\n\n- On Android, the main process keeps the existing MMKV storage ID. A process declared with `android:process` uses a separate record based on its full process name.\n- On iOS, the host app keeps the existing MMKV storage ID. Each extension uses a separate record based on its `Bundle.main.bundleIdentifier`. Each target that initializes Faro must include the Faro native module and MMKV. Extension persistence requires `react-native-mmkv` v3 or newer so Faro can enable MMKV's multi-process mode; older versions fall back to memory in extensions.\n- Each process maintains its own previous-session link. The `process_name` session attribute identifies which process or extension produced the telemetry.\n\nOnly one React Native runtime at a time may write a native process's record. An overlapping runtime that has already fallen back remains in memory for its lifetime. After the owning runtime is invalidated, a newly created replacement runtime can claim persistence. The SDK also falls back to memory when native process identity is unavailable, MMKV cannot be initialized safely, or exclusive ownership cannot be established, and logs a warning rather than risking concurrent writes.\n\nA single session shared across Android processes or between an iOS host app and extension is not supported. Configuring an App Group does not merge Faro session chains between a host app and its extensions because their record IDs remain distinct. If multiple host apps share the same App Group, they share Faro's fixed host record ID; only one of those apps may use `persistent: true`, and the others must use `persistent: false`. Shared sessions would require a multi-process-safe store and native writer coordination. Use `persistent: false` for runtimes where an independent persisted chain is not wanted.\n\n**Sampling:** Set `sessionTracking.sampling` to a `SamplingRate` (fixed 0–1) or `SamplingFunction` (dynamic, receives `context.meta`). Omit `sampling` to record all sessions. The decision is made once per session.\n\n**Defaults:** `persistent=true`, `maxSessionPersistenceTime=15min`. The maximum session lifetime is four hours.\n\n**Session events:**\n\nThe SDK emits `session_start` when a new session is created, including each cold start and when session metadata changes to a new session ID.\n\n**Starting a new linked session:**\n\nUse `startNewSession()` after changing or clearing the current user at an\napplication-defined boundary. It immediately creates a new session, links the\nprevious session ID, restarts the lifetime, inactivity, and sampling windows,\nand creates the normal `session_start` event. As with other telemetry, the event\nis not exported when the new session is sampled out. Persistent sessions attempt\nthe synchronous MMKV write before the call returns; write failures are logged\nand the new session remains active in memory.\n\n```tsx\nimport { startNewSession } from '@grafana/faro-react-native';\n\nfunction logout() {\n  faro.api.resetUser();\n  startNewSession();\n}\n\nfunction switchAccount(nextUserId: string) {\n  faro.api.setUser({ id: nextUserId });\n  startNewSession();\n}\n```\n\nAny active user action is ended before the new session starts so its buffered telemetry\nstays with the previous session. Calls made before Faro initializes, after\nteardown, or while session tracking is disabled have no effect.\n\n### Default Session Attributes\n\nEvery telemetry event automatically includes default session attributes with device and SDK information.\n\n**Automatically Collected Attributes:**\n\n| Attribute              | Description          | iOS Example      | Android Example       |\n| ---------------------- | -------------------- | ---------------- | --------------------- |\n| `faro_sdk_version`     | SDK version          | `2.0.2`          | `2.0.2`               |\n| `react_native_version` | React Native version | `0.75.1`         | `0.75.1`              |\n| `process_name`         | Process identity     | `com.acme.share` | `com.acme.app:sync`   |\n| `device_os`            | Operating system     | `iOS`            | `Android`             |\n| `device_os_version`    | OS version           | `17.0`           | `15`                  |\n| `device_os_detail`     | Detailed OS info     | `iOS 17.0`       | `Android 15 (SDK 35)` |\n| `device_manufacturer`  | Manufacturer         | `apple`          | `samsung`             |\n| `device_model`         | Raw model identifier | `iPhone16,1`     | `SM-A155F`            |\n| `device_model_name`    | Human-readable model | `iPhone 15 Pro`  | `SM-A155F`\\*          |\n| `device_brand`         | Device brand         | `iPhone`         | `samsung`             |\n| `device_is_physical`   | Physical or emulator | `true`           | `true`                |\n| `device_id`            | Unique device ID     | `uuid`           | `uuid`                |\n\n\\*Android does not provide a mapping from model codes to marketing names, so `device_model_name` equals `device_model`.\n\n**How It Works:**\n\n- Attributes are collected automatically during session initialization\n- No manual configuration needed\n- Uses existing `react-native-device-info` dependency\n- Attributes are included with every telemetry event (logs, errors, measurements, etc.)\n- Custom attributes can be added via `sessionTracking.session.attributes` (default attributes take precedence)\n\n**Example Grafana Query:**\n\n```logql\n# Filter events by device OS\n{app_name=\"my-app\"} | json | device_os=\"iOS\"\n\n# Filter by specific device model\n{app_name=\"my-app\"} | json | device_model=\"iPhone16,1\"\n\n# Group by manufacturer\n{app_name=\"my-app\"} | json | count by device_manufacturer\n\n# Filter emulator vs physical devices\n{app_name=\"my-app\"} | json | device_is_physical=\"false\"\n```\n\n### AppState Tracking\n\nThe SDK automatically tracks React Native app state changes (foreground/background/inactive). This is enabled by default and requires no additional configuration.\n\n```tsx\nimport { initializeFaro } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: {\n    name: 'my-app',\n    version: '1.0.0',\n  },\n});\n```\n\n**App States:**\n\n- **active**: App is running in the foreground\n- **background**: User switched to another app or home screen\n- **inactive**: Transitional state (incoming call, control center on iOS)\n- **unknown**: Initial state before first change (iOS only)\n- **extension**: App extension is running (iOS only)\n\n**App State Events:**\n\nThe SDK automatically emits `app_lifecycle_changed` events when the app state transitions:\n\n```typescript\n{\n  event_name: \"app_lifecycle_changed\",\n  fromState: \"active\",      // Previous AppState (active | background | inactive | unknown | extension)\n  toState: \"background\",    // New state\n  duration: \"5234\",         // Time spent in previous state (ms)\n  timestamp: \"1701518400000\" // Unix timestamp\n}\n```\n\n**Use Cases:**\n\n- Track user engagement (foreground vs background time)\n- Identify background-related crashes or errors\n- Measure session duration by app state\n- Optimize background task scheduling\n- Detect performance issues after returning from background\n\n**Example Queries (Grafana Explore with Loki):**\n\n```logql\n# View all app state changes\n{app_name=\"my-app\", kind=\"event\"}\n| json\n| event_name=\"app_lifecycle_changed\"\n\n# Count background transitions (toState=\"background\")\n{app_name=\"my-app\", kind=\"event\"}\n| json\n| event_name=\"app_lifecycle_changed\"\n| toState=\"background\"\n\n# Count foreground transitions (toState=\"active\")\n{app_name=\"my-app\", kind=\"event\"}\n| json\n| event_name=\"app_lifecycle_changed\"\n| toState=\"active\"\n\n# Average time in foreground (unwrap duration)\n{app_name=\"my-app\", kind=\"event\"}\n| json\n| event_name=\"app_lifecycle_changed\"\n| fromState=\"active\"\n| unwrap duration\n| avg\n```\n\nFor a complete example of app state tracking in action, see the [demo](../../demo) application.\n\n### Performance Instrumentation Configuration\n\nThe Performance Instrumentation provides comprehensive performance monitoring for React Native applications, including **CPU usage**, **memory usage**, and **app startup time tracking**.\n\n#### System Resource Monitoring (CPU & Memory)\n\nThe SDK automatically monitors system resources using **native OS-level APIs** for accurate metrics:\n\n**iOS Implementation:**\n\n- **CPU**: Uses `host_statistics()` with differential calculation for precise CPU percentage\n- **Memory**: Uses `task_info()` to measure RSS (Resident Set Size) in kilobytes\n\n**Android Implementation:**\n\n- **CPU**: Parses `/proc/[pid]/stat` with differential calculation\n- **Memory**: Parses `/proc/[pid]/status` for VmRSS in kilobytes\n\n**Configuration:**\n\n```tsx\nimport { initializeFaro } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: {\n    name: 'my-app',\n    version: '1.0.0',\n  },\n  // Enable performance monitoring (defaults)\n  cpuUsageVitals: true,\n  memoryUsageVitals: true,\n  // Collection interval in milliseconds (default: 30000 - 30 seconds)\n  fetchVitalsInterval: 30000,\n});\n```\n\n**Metrics Collected:**\n\n1. **CPU Usage** (`app_cpu_usage` measurement):\n   - `cpu_usage` - CPU usage percentage (0-100+)\n   - Collected periodically based on `fetchVitalsInterval`\n   - First reading establishes baseline (returns 0)\n   - Subsequent readings show actual CPU percentage\n\n2. **Memory Usage** (`app_memory` measurement):\n   - `mem_usage` - Memory usage in kilobytes (RSS)\n   - Collected periodically based on `fetchVitalsInterval`\n   - Measures physical memory currently used by the app\n\n**Example Grafana Queries (Loki):**\n\n```logql\n# Average CPU usage over time\n{app_name=\"my-app\", kind=\"measurement\"}\n| json\n| type=\"app_cpu_usage\"\n| unwrap cpu_usage\n| rate(1m)\n\n# Memory usage spikes\n{app_name=\"my-app\", kind=\"measurement\"}\n| json\n| type=\"app_memory\"\n| unwrap mem_usage\n| topk(10)\n\n# CPU usage during specific screen\n{app_name=\"my-app\", kind=\"measurement\"}\n| json\n| type=\"app_cpu_usage\"\n| view_name=\"HomeScreen\"\n| unwrap cpu_usage\n| avg\n```\n\n**Platform Requirements:**\n\n- **iOS**: Requires iOS 13.4+\n- **Android**: Requires API 21+ (Lollipop) for CPU monitoring, any version for memory\n\n**No Manual Setup Required!**\n\n- Native modules are automatically linked via CocoaPods (iOS) and Gradle (Android)\n- OS-level APIs are used - no permissions needed\n- Works out of the box with default configuration\n\n#### Startup Performance Monitoring\n\nThe SDK automatically tracks app startup time from process start to Faro initialization. No configuration needed; it is always enabled.\n\n**Startup Metric** (`app_startup` measurement):\n\n- `appStartDuration` - Time from process start to Faro init (milliseconds)\n- `coldStart` - 1 for cold start, 0 for warm start\n- Measured using native OS APIs:\n  - **iOS**: `sysctl()` with `KERN_PROC` to get process start time\n  - **Android**: Parses process start time from system\n\n**Example Query:**\n\n```logql\n# Average app startup time\n{app_name=\"my-app\", kind=\"measurement\"}\n| logfmt\n| type=\"app_startup\"\n| unwrap value_total_duration_ms\n| avg\n```\n\n#### Frame Monitoring (Refresh Rate, Slow & Frozen Frames)\n\nEnable with `refreshRateVitals: true`. Uses native frame callbacks\n(`CADisplayLink` on iOS, `Choreographer` on Android).\n\n**Defaults** (override via `frameMonitoringOptions`):\n\n- **Frozen frame threshold**: 700ms (aligned with Android Vitals)\n- **Slow frame target**: 60 FPS (event-based grouping; events ≥50ms count)\n- **Poll interval**: 30s (`refreshRatePollingInterval`)\n\nSlow and frozen frames are **polled** on both platforms (no duplicate\nAndroid event stream). Refresh rate may also emit on Android between polls\nwhen `refreshRateVitals` is enabled.\n\n**Metrics:**\n\n| Type               | Values                             | Notes                                                 |\n| ------------------ | ---------------------------------- | ----------------------------------------------------- |\n| `app_refresh_rate` | `refresh_rate`                     | Current FPS                                           |\n| `app_frames_rate`  | `slow_frames`                      | Count of slow frame **events**, not individual frames |\n| `app_frozen_frame` | `frozen_frames`, `frozen_duration` | Frames above threshold; duration in ms                |\n\n**Configuration example:**\n\n```tsx\ninitializeFaro({\n  url: 'https://your-faro-collector-url',\n  app: { name: 'my-app', version: '1.0.0' },\n  refreshRateVitals: true,\n  frameMonitoringOptions: {\n    frozenFrameThresholdMs: 700,\n    refreshRatePollingInterval: 30000,\n  },\n});\n```\n\n#### Performance Best Practices\n\n**For Production:**\n\n```tsx\ninitializeFaro({\n  url: '...',\n  app: { name: 'my-app', version: '1.0.0' },\n  cpuUsageVitals: true,\n  memoryUsageVitals: true,\n  fetchVitalsInterval: 30000, // 30 seconds - good balance\n});\n```\n\n**For Debugging/Testing:**\n\n```tsx\ninitializeFaro({\n  url: '...',\n  app: { name: 'my-app', version: '1.0.0' },\n  cpuUsageVitals: true,\n  memoryUsageVitals: true,\n  fetchVitalsInterval: 5000, // 5 seconds - more frequent for testing\n});\n```\n\n**Use Cases:**\n\n- **Detect Memory Leaks**: Monitor memory growth over time\n- **Identify CPU Bottlenecks**: Correlate CPU spikes with user actions\n- **Track Startup Performance**: Measure app launch time improvements\n- **Performance Regression Testing**: Compare metrics across app versions\n- **Resource-Based Crash Analysis**: Correlate crashes with high memory/CPU usage\n\n## Navigation Integration\n\nFaro provides seamless integration with React Navigation to automatically track screen changes.\n\n### Quick Start\n\n```tsx\nimport { useNavigationContainerRef } from '@react-navigation/native';\nimport { useFaroNavigation } from '@grafana/faro-react-native';\n\nfunction App() {\n  const navigationRef = useNavigationContainerRef();\n\n  // Automatically track navigation changes\n  useFaroNavigation(navigationRef);\n\n  return <NavigationContainer ref={navigationRef}>{/* Your navigation */}</NavigationContainer>;\n}\n```\n\n### Static Navigation API\n\nFor React Navigation 7+ static navigation:\n\n```tsx\nimport { createStaticNavigation, useNavigationContainerRef } from '@react-navigation/native';\nimport { createNativeStackNavigator } from '@react-navigation/native-stack';\nimport { useFaroNavigation } from '@grafana/faro-react-native';\n\nconst RootStack = createNativeStackNavigator({\n  screens: {\n    Home: { screen: HomeScreen, options: { title: 'Welcome' } },\n    Profile: { screen: ProfileScreen },\n  },\n});\n\nconst Navigation = createStaticNavigation(RootStack);\n\nfunction App() {\n  const navigationRef = useNavigationContainerRef();\n  useFaroNavigation(navigationRef);\n\n  return <Navigation ref={navigationRef} />;\n}\n```\n\nFor detailed integration guides, advanced usage, and troubleshooting, see [NAVIGATION_INTEGRATION.md](./NAVIGATION_INTEGRATION.md).\n\n## Enhanced Device Meta\n\nThe Faro React Native SDK automatically collects comprehensive device information to provide better context for debugging and analytics.\n\n### Automatically Collected Information\n\nThe SDK collects the following device information synchronously:\n\n- **Device Info**: Brand, model, device ID, type (mobile/tablet)\n- **System Info**: OS name, OS version, app version\n- **Locale/Language**: Device locales, timezone, primary language\n\n## Device Information\n\nThe SDK automatically collects device information and sends it as **session attributes** with every telemetry event.\n\n### Session Attributes\n\nAll device information is sent as session attributes (not browser meta):\n\n**Core Attributes:**\n\n- `faro_sdk_version` - SDK version (e.g., \"1.0.0\")\n- `react_native_version` - React Native version (e.g., \"0.75.1\")\n- `device_os` - Operating system name (\"iOS\" or \"Android\")\n- `device_os_version` - OS version (e.g., \"17.0\", \"14\")\n- `device_os_detail` - Detailed OS info (e.g., \"iOS 17.0\", \"Android 14 (SDK 34)\")\n- `device_manufacturer` - Manufacturer (e.g., \"apple\", \"samsung\")\n- `device_model` - Raw model identifier (e.g., \"iPhone16,1\", \"SM-A155F\")\n- `device_model_name` - Human-readable name (e.g., \"iPhone 15 Pro\")\n- `device_brand` - Device brand (e.g., \"iPhone\", \"samsung\")\n- `device_is_physical` - Physical device or emulator (\"true\" or \"false\")\n- `device_id` - Unique device identifier (UUID)\n\n**Additional Monitoring Attributes (React Native specific):**\n\n- `device_type` - Device type (\"mobile\" or \"tablet\")\n- `device_memory_total` - Total device memory in bytes\n- `device_memory_used` - Currently used memory in bytes\n- `device_battery_level` - Battery percentage (e.g., \"85\") - if available\n- `device_is_charging` - Whether charging (\"true\" or \"false\") - if available\n- `device_low_power_mode` - Low power mode enabled (\"true\" or \"false\") - if available\n- `device_carrier` - Mobile carrier name (e.g., \"Verizon\") - if available\n\nThese attributes are automatically collected during Faro initialization and included with all telemetry events.\n\n### Querying Device Information\n\n**Debug device-specific issues:**\n\n```logql\n{service_name=\"MyApp\", device_manufacturer=\"samsung\"}\n| logfmt\n| kind=\"exception\"\n```\n\n**Filter by OS version:**\n\n```logql\n{service_name=\"MyApp\", device_os=\"Android\", device_os_version=\"14\"}\n| logfmt\n```\n\n**Find emulator vs physical device issues:**\n\n```logql\n{service_name=\"MyApp\", device_is_physical=\"false\"}\n| logfmt\n| kind=\"exception\"\n```\n\n**Track memory-related issues:**\n\n```logql\n{service_name=\"MyApp\"}\n| logfmt\n| device_memory_used > 1000000000\n| kind=\"exception\"\n```\n\n**Monitor low battery scenarios:**\n\n```logql\n{service_name=\"MyApp\"}\n| logfmt\n| device_battery_level <= \"20\"\n| kind=\"exception\"\n```\n\n### Notes\n\n- All device info is collected automatically when Faro initializes\n- Session attributes are included with every telemetry event\n- All fields are optional and gracefully handle permission errors\n- The React Native SDK sends an empty `page` meta field to override faro-core's default web-specific page meta\n- Screen tracking is handled via `view` meta instead of `page` meta\n- Battery, carrier, and low power mode info may not be available on all devices/OS versions\n\n## TypeScript\n\nThe package is written in TypeScript and includes type definitions out of the box.\n\n```tsx\nimport type { ReactNativeConfig, WithFaroUserActionProps } from '@grafana/faro-react-native';\n```\n\n## Examples\n\nSee the [demo](../../demo) directory for a complete example application.\n\n## API Reference\n\n### Core API\n\n- `initializeFaro(config: ReactNativeConfig): void` - Initialize the Faro SDK\n- `faro.api.pushEvent(name: string, attributes?: Record<string, string>)` - Track custom events\n- `faro.api.pushLog(message: string[], options?: PushLogOptions)` - Send log messages. Use `options.level` with the `LogLevel` enum (`TRACE`, `DEBUG`, `INFO`, `LOG`, `WARN`, `ERROR`) to set severity. Defaults to `LogLevel.LOG`.\n- `faro.api.pushError(error: Error, options?: PushErrorOptions)` - Report errors\n- `faro.api.pushMeasurement(measurement: Measurement)` - Track performance\n- `faro.api.setUser(user: User)` - Identify users\n- `faro.api.resetUser()` - Clear user identification\n- `startNewSession()` - Top-level API that starts a new session linked to the current session\n\n### User Actions API\n\n- `withFaroUserAction<P>(Component, defaultActionName)` - HOC for tracking component interactions\n- `trackUserAction(actionName, context?)` - Manual user action tracking\n\n### Session Activity API\n\n- `FaroSessionActivityBoundary` - Refresh session inactivity for touch starts\n  inside a React Native responder subtree without emitting user-action telemetry\n- `notifySessionActivity()` - Refresh session inactivity for supported\n  interactions outside the boundary\n\n### Error Boundary API\n\n- `FaroErrorBoundary` - React component for catching and reporting component errors\n- `withFaroErrorBoundary<P>(Component, errorBoundaryProps)` - HOC for wrapping components with error boundary\n\n### Transports\n\nTransports control where and how telemetry data is sent.\n\n#### FetchTransport\n\nSends telemetry to a remote Faro collector (default):\n\n```tsx\nimport { initializeFaro, FetchTransport } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: 'https://faro-collector-prod-YOUR-REGION.grafana.net/collect/YOUR_TOKEN_HERE',\n  app: { name: 'my-app', version: '1.0.0' },\n  // FetchTransport is automatically configured from the url\n});\n```\n\n#### ConsoleTransport\n\nLogs telemetry to the console for debugging (useful during development). Enable via flag; no need to add it to `transports`:\n\n```tsx\ninitializeFaro({\n  url: 'https://faro-collector-prod-YOUR-REGION.grafana.net/collect/YOUR_TOKEN_HERE',\n  app: { name: 'my-app', version: '1.0.0' },\n  enableTransports: {\n    fetch: true,\n    console: true, // Logs to Metro with DEBUG level\n  },\n});\n```\n\nFor custom options (e.g. different log level), add it to `transports` and set `console: false` to avoid duplicates:\n\n```tsx\nimport { initializeFaro, ConsoleTransport, LogLevel } from '@grafana/faro-react-native';\n\ninitializeFaro({\n  url: '...',\n  app: { name: 'my-app', version: '1.0.0' },\n  enableTransports: { fetch: true, console: false },\n  transports: [new ConsoleTransport({ level: LogLevel.INFO })],\n});\n```\n\nThe ConsoleTransport prints formatted telemetry data to the console, showing:\n\n- All metadata (device info, session, user, etc.)\n- Event payloads (logs, errors, events, measurements)\n- Structured JSON format for easy inspection\n\n**Use Cases:**\n\n- Local development and debugging\n- Verify instrumentation is working correctly\n- Inspect exact structure of events before they reach Grafana\n- Test without sending data to production\n- Run alongside FetchTransport for dual output\n\n**Example Output:**\n\n```javascript\nconsole.debug('New event', {\n  meta: {\n    browser: { name: 'iOS', version: '18.0', ... },\n    session: { id: 'abc123', ... },\n    ...\n  },\n  logs: [{ message: 'Hello', level: 'info', ... }]\n})\n```\n\n### Instrumentations\n\n- `ConsoleInstrumentation` - Console logging\n- `ErrorsInstrumentation` - Error tracking\n- `SessionInstrumentation` - Session management\n- `ViewInstrumentation` - View/screen tracking\n- `AppStateInstrumentation` - App state changes\n- `UserActionInstrumentation` - User interaction tracking\n- `HttpInstrumentation` - HTTP request tracking with user action correlation\n- `PerformanceInstrumentation` - CPU and memory usage monitoring\n- `StartupInstrumentation` - App startup time tracking\n\n### React Components\n\n- `FaroErrorBoundary` - Error boundary component for catching React errors\n- `FaroSessionActivityBoundary` - Touch activity boundary for session inactivity\n- `withFaroErrorBoundary` - HOC for wrapping components with error boundary\n\n## Future Enhancements\n\n### TODO: PerformanceObserver Support\n\nReact Native Next (upcoming version) will include native `PerformanceObserver` support, which will enable greater feature parity with the web SDK's performance monitoring capabilities.\n\n**Planned Enhancements:**\n\n- Implement `PerformanceObserver`-based instrumentation similar to web SDK\n- Support for performance entry types: `mark`, `measure`, `event`, `longtask`\n- Real-time performance monitoring via observer callbacks\n- Better integration with React Native's native performance APIs\n- Enhanced performance timeline tracking\n\n**References:**\n\n- [React Native PerformanceObserver API Documentation](https://reactnative.dev/docs/next/global-PerformanceObserver)\n- Web SDK Performance Instrumentation: `packages/web-sdk/src/instrumentations/performance/`\n\n**Current State:**\nThe current implementation uses custom performance utilities (`performanceUtils.ts`) that provide basic timing and marker functionality. Once React Native's `PerformanceObserver` is stable, we can migrate to a more comprehensive solution that matches the web SDK's capabilities.\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md"}