@grafana/faro-react-native 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/android/build.gradle +118 -36
  2. package/android/consumer-proguard-rules.pro +2 -0
  3. package/android/src/main/java/com/grafana/faro/reactnative/ANRTracker.kt +61 -9
  4. package/android/src/main/java/com/grafana/faro/reactnative/AnrThreadDumpParser.kt +70 -0
  5. package/android/src/main/java/com/grafana/faro/reactnative/ApplicationExitTraceReader.kt +142 -0
  6. package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrCache.kt +163 -0
  7. package/android/src/main/java/com/grafana/faro/reactnative/FaroAnrReporter.kt +163 -0
  8. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +243 -50
  9. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +75 -0
  10. package/android/src/main/java/com/grafana/faro/reactnative/FaroNativeCrashTrace.kt +39 -0
  11. package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +99 -8
  12. package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativePackage.kt +1 -0
  13. package/android/src/main/java/com/grafana/faro/reactnative/FaroUncaughtExceptionHandler.kt +62 -0
  14. package/android/src/main/java/com/grafana/faro/reactnative/TombstoneBacktraceFormatter.kt +117 -0
  15. package/android/src/main/java/com/grafana/faro/reactnative/TombstoneModels.kt +35 -0
  16. package/android/src/main/java/com/grafana/faro/reactnative/TombstoneWireParser.kt +235 -0
  17. package/android/src/main/proto/tombstone.proto +62 -0
  18. package/android/src/test/java/com/grafana/faro/reactnative/AnrStackFiltersTest.kt +28 -0
  19. package/android/src/test/java/com/grafana/faro/reactnative/AnrThreadDumpParserTest.kt +38 -0
  20. package/android/src/test/java/com/grafana/faro/reactnative/FaroAnrCacheTest.kt +81 -0
  21. package/android/src/test/java/com/grafana/faro/reactnative/TombstoneWireParserTest.kt +51 -0
  22. package/dist/cjs/config/getRNInstrumentations.js +3 -1
  23. package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
  24. package/dist/cjs/config/makeRNConfig.js +48 -9
  25. package/dist/cjs/config/makeRNConfig.js.map +1 -1
  26. package/dist/cjs/config/types.js.map +1 -1
  27. package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
  28. package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
  29. package/dist/cjs/index.js +3 -2
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/initialize.js +14 -8
  32. package/dist/cjs/initialize.js.map +1 -1
  33. package/dist/cjs/instrumentations/anr/anrStack.js +19 -0
  34. package/dist/cjs/instrumentations/anr/anrStack.js.map +1 -0
  35. package/dist/cjs/instrumentations/anr/instrumentation.js +374 -72
  36. package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
  37. package/dist/cjs/instrumentations/anr/types.js.map +1 -1
  38. package/dist/cjs/instrumentations/console/index.js +5 -1
  39. package/dist/cjs/instrumentations/console/index.js.map +1 -1
  40. package/dist/cjs/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +110 -103
  41. package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
  42. package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +41 -0
  43. package/dist/cjs/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
  44. package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +87 -0
  45. package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
  46. package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js +123 -0
  47. package/dist/cjs/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
  48. package/dist/cjs/instrumentations/crashReporting/android/index.js +12 -0
  49. package/dist/cjs/instrumentations/crashReporting/android/index.js.map +1 -0
  50. package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js +11 -0
  51. package/dist/cjs/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
  52. package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +181 -0
  53. package/dist/cjs/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
  54. package/dist/cjs/instrumentations/crashReporting/index.js +35 -2
  55. package/dist/cjs/instrumentations/crashReporting/index.js.map +1 -1
  56. package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +78 -0
  57. package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
  58. package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js +103 -0
  59. package/dist/cjs/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
  60. package/dist/cjs/instrumentations/crashReporting/ios/index.js +11 -0
  61. package/dist/cjs/instrumentations/crashReporting/ios/index.js.map +1 -0
  62. package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js +48 -0
  63. package/dist/cjs/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
  64. package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
  65. package/dist/cjs/instrumentations/errors/index.js +2 -1
  66. package/dist/cjs/instrumentations/errors/index.js.map +1 -1
  67. package/dist/cjs/instrumentations/session/installationId.js +91 -0
  68. package/dist/cjs/instrumentations/session/installationId.js.map +1 -0
  69. package/dist/cjs/instrumentations/session/sessionAttributes.js +152 -89
  70. package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
  71. package/dist/cjs/metas/appBuildIdentity.js +95 -0
  72. package/dist/cjs/metas/appBuildIdentity.js.map +1 -0
  73. package/dist/cjs/testUtils/mockDeviceInfo.js +5 -0
  74. package/dist/cjs/testUtils/mockDeviceInfo.js.map +1 -1
  75. package/dist/cjs/testUtils/setupTests.js +7 -0
  76. package/dist/cjs/testUtils/setupTests.js.map +1 -1
  77. package/dist/esm/config/getRNInstrumentations.js +3 -1
  78. package/dist/esm/config/getRNInstrumentations.js.map +1 -1
  79. package/dist/esm/config/makeRNConfig.js +49 -10
  80. package/dist/esm/config/makeRNConfig.js.map +1 -1
  81. package/dist/esm/config/types.js.map +1 -1
  82. package/dist/esm/generated/faroRNPackageMeta.js +1 -1
  83. package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
  84. package/dist/esm/index.js +1 -1
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/initialize.js +11 -5
  87. package/dist/esm/initialize.js.map +1 -1
  88. package/dist/esm/instrumentations/anr/anrStack.js +16 -0
  89. package/dist/esm/instrumentations/anr/anrStack.js.map +1 -0
  90. package/dist/esm/instrumentations/anr/instrumentation.js +260 -67
  91. package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
  92. package/dist/esm/instrumentations/anr/types.js.map +1 -1
  93. package/dist/esm/instrumentations/console/index.js +5 -1
  94. package/dist/esm/instrumentations/console/index.js.map +1 -1
  95. package/dist/esm/instrumentations/crashReporting/{instrumentation.js → BaseCrashReportingInstrumentation.js} +76 -94
  96. package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -0
  97. package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js +18 -0
  98. package/dist/esm/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.js.map +1 -0
  99. package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +56 -0
  100. package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -0
  101. package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js +116 -0
  102. package/dist/esm/instrumentations/crashReporting/android/crashErrorMessage.js.map +1 -0
  103. package/dist/esm/instrumentations/crashReporting/android/index.js +4 -0
  104. package/dist/esm/instrumentations/crashReporting/android/index.js.map +1 -0
  105. package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js +8 -0
  106. package/dist/esm/instrumentations/crashReporting/android/nativeTombstoneTrace.js.map +1 -0
  107. package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js +174 -0
  108. package/dist/esm/instrumentations/crashReporting/android/parseAndroidCrashTrace.js.map +1 -0
  109. package/dist/esm/instrumentations/crashReporting/index.js +35 -1
  110. package/dist/esm/instrumentations/crashReporting/index.js.map +1 -1
  111. package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +56 -0
  112. package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -0
  113. package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js +98 -0
  114. package/dist/esm/instrumentations/crashReporting/ios/crashErrorMessage.js.map +1 -0
  115. package/dist/esm/instrumentations/crashReporting/ios/index.js +4 -0
  116. package/dist/esm/instrumentations/crashReporting/ios/index.js.map +1 -0
  117. package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js +44 -0
  118. package/dist/esm/instrumentations/crashReporting/ios/parseIosCrashTrace.js.map +1 -0
  119. package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
  120. package/dist/esm/instrumentations/errors/index.js +2 -1
  121. package/dist/esm/instrumentations/errors/index.js.map +1 -1
  122. package/dist/esm/instrumentations/session/installationId.js +48 -0
  123. package/dist/esm/instrumentations/session/installationId.js.map +1 -0
  124. package/dist/esm/instrumentations/session/sessionAttributes.js +83 -38
  125. package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
  126. package/dist/esm/metas/appBuildIdentity.js +56 -0
  127. package/dist/esm/metas/appBuildIdentity.js.map +1 -0
  128. package/dist/esm/testUtils/mockDeviceInfo.js +5 -0
  129. package/dist/esm/testUtils/mockDeviceInfo.js.map +1 -1
  130. package/dist/esm/testUtils/setupTests.js +7 -0
  131. package/dist/esm/testUtils/setupTests.js.map +1 -1
  132. package/dist/types/config/makeRNConfig.d.ts +4 -3
  133. package/dist/types/config/types.d.ts +2 -1
  134. package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
  135. package/dist/types/index.d.ts +2 -2
  136. package/dist/types/initialize.d.ts +4 -2
  137. package/dist/types/instrumentations/anr/anrStack.d.ts +7 -0
  138. package/dist/types/instrumentations/anr/instrumentation.d.ts +23 -28
  139. package/dist/types/instrumentations/anr/types.d.ts +5 -1
  140. package/dist/types/instrumentations/appState/index.d.ts +1 -1
  141. package/dist/types/instrumentations/console/index.d.ts +1 -1
  142. package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +56 -0
  143. package/dist/types/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.d.ts +12 -0
  144. package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +26 -0
  145. package/dist/types/instrumentations/crashReporting/android/crashErrorMessage.d.ts +23 -0
  146. package/dist/types/instrumentations/crashReporting/android/index.d.ts +4 -0
  147. package/dist/types/instrumentations/crashReporting/android/nativeTombstoneTrace.d.ts +2 -0
  148. package/dist/types/instrumentations/crashReporting/android/parseAndroidCrashTrace.d.ts +45 -0
  149. package/dist/types/instrumentations/crashReporting/index.d.ts +25 -2
  150. package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +28 -0
  151. package/dist/types/instrumentations/crashReporting/ios/crashErrorMessage.d.ts +14 -0
  152. package/dist/types/instrumentations/crashReporting/ios/index.d.ts +4 -0
  153. package/dist/types/instrumentations/crashReporting/ios/parseIosCrashTrace.d.ts +38 -0
  154. package/dist/types/instrumentations/crashReporting/types.d.ts +18 -51
  155. package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +1 -1
  156. package/dist/types/instrumentations/http/index.d.ts +1 -1
  157. package/dist/types/instrumentations/performance/index.d.ts +1 -1
  158. package/dist/types/instrumentations/session/index.d.ts +1 -1
  159. package/dist/types/instrumentations/session/installationId.d.ts +1 -0
  160. package/dist/types/instrumentations/session/sessionAttributes.d.ts +13 -4
  161. package/dist/types/instrumentations/startup/index.d.ts +1 -1
  162. package/dist/types/instrumentations/userActions/index.d.ts +1 -1
  163. package/dist/types/instrumentations/view/index.d.ts +1 -1
  164. package/dist/types/instrumentations/xhr/index.d.ts +1 -1
  165. package/dist/types/metas/appBuildIdentity.d.ts +16 -0
  166. package/dist/types/testUtils/mockFaroReactNativeTracing.d.ts +1 -1
  167. package/dist/types/transports/console/transport.d.ts +1 -1
  168. package/dist/types/transports/fetch/transport.d.ts +1 -1
  169. package/dist/types/transports/offline/transport.d.ts +1 -1
  170. package/package.json +3 -3
  171. package/src/config/getRNInstrumentations.ts +5 -1
  172. package/src/config/makeRNConfig.test.ts +59 -3
  173. package/src/config/makeRNConfig.ts +48 -9
  174. package/src/config/types.ts +2 -1
  175. package/src/generated/faroRNPackageMeta.ts +1 -1
  176. package/src/index.ts +2 -1
  177. package/src/initialize.test.ts +71 -15
  178. package/src/initialize.ts +11 -5
  179. package/src/instrumentations/anr/anrStack.test.ts +27 -0
  180. package/src/instrumentations/anr/anrStack.ts +19 -0
  181. package/src/instrumentations/anr/instrumentation.test.ts +45 -0
  182. package/src/instrumentations/anr/instrumentation.ts +301 -67
  183. package/src/instrumentations/anr/types.ts +7 -1
  184. package/src/instrumentations/console/index.ts +5 -1
  185. package/src/instrumentations/console/instrumentation.test.ts +5 -0
  186. package/src/instrumentations/crashReporting/{instrumentation.ts → BaseCrashReportingInstrumentation.ts} +119 -93
  187. package/src/instrumentations/crashReporting/NoOpCrashReportingInstrumentation.ts +23 -0
  188. package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +62 -0
  189. package/src/instrumentations/crashReporting/android/crashErrorMessage.test.ts +182 -0
  190. package/src/instrumentations/crashReporting/android/crashErrorMessage.ts +135 -0
  191. package/src/instrumentations/crashReporting/android/index.ts +4 -0
  192. package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.test.ts +19 -0
  193. package/src/instrumentations/crashReporting/android/nativeTombstoneTrace.ts +8 -0
  194. package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.test.ts +232 -0
  195. package/src/instrumentations/crashReporting/android/parseAndroidCrashTrace.ts +235 -0
  196. package/src/instrumentations/crashReporting/index.ts +43 -2
  197. package/src/instrumentations/crashReporting/instrumentation.test.ts +42 -0
  198. package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +62 -0
  199. package/src/instrumentations/crashReporting/ios/crashErrorMessage.test.ts +170 -0
  200. package/src/instrumentations/crashReporting/ios/crashErrorMessage.ts +111 -0
  201. package/src/instrumentations/crashReporting/ios/index.ts +4 -0
  202. package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.test.ts +86 -0
  203. package/src/instrumentations/crashReporting/ios/parseIosCrashTrace.ts +83 -0
  204. package/src/instrumentations/crashReporting/types.ts +19 -58
  205. package/src/instrumentations/errors/index.ts +2 -1
  206. package/src/instrumentations/errors/instrumentation.test.ts +8 -2
  207. package/src/instrumentations/errors/stackTraceParser.test.ts +1 -1
  208. package/src/instrumentations/session/installationId.ts +43 -0
  209. package/src/instrumentations/session/sessionAttributes.test.ts +260 -30
  210. package/src/instrumentations/session/sessionAttributes.ts +112 -22
  211. package/src/metas/appBuildIdentity.test.ts +48 -0
  212. package/src/metas/appBuildIdentity.ts +50 -0
  213. package/src/testUtils/mockDeviceInfo.ts +5 -0
  214. package/src/testUtils/setupTests.ts +7 -0
  215. package/dist/cjs/instrumentations/crashReporting/instrumentation.js.map +0 -1
  216. package/dist/esm/instrumentations/crashReporting/instrumentation.js.map +0 -1
  217. package/dist/types/instrumentations/crashReporting/instrumentation.d.ts +0 -58
@@ -0,0 +1,48 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import AsyncStorage from '@react-native-async-storage/async-storage';
11
+ const INSTALLATION_ID_STORAGE_KEY = '@grafana/faro-react-native/installation_id';
12
+ function createRandomBytes(length) {
13
+ const bytes = new Uint8Array(length);
14
+ const cryptoObject = globalThis.crypto;
15
+ if (cryptoObject && typeof cryptoObject.getRandomValues === 'function') {
16
+ cryptoObject.getRandomValues(bytes);
17
+ return bytes;
18
+ }
19
+ for (let i = 0; i < bytes.length; i++) {
20
+ bytes[i] = Math.floor(Math.random() * 256);
21
+ }
22
+ return bytes;
23
+ }
24
+ function generateInstallationId() {
25
+ var _a, _b;
26
+ const bytes = createRandomBytes(16);
27
+ bytes[6] = (((_a = bytes[6]) !== null && _a !== void 0 ? _a : 0) & 0x0f) | 0x40;
28
+ bytes[8] = (((_b = bytes[8]) !== null && _b !== void 0 ? _b : 0) & 0x3f) | 0x80;
29
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
30
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
31
+ }
32
+ export function getInstallationId() {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ try {
35
+ const storedInstallationId = yield AsyncStorage.getItem(INSTALLATION_ID_STORAGE_KEY);
36
+ if (storedInstallationId) {
37
+ return storedInstallationId;
38
+ }
39
+ const installationId = generateInstallationId();
40
+ yield AsyncStorage.setItem(INSTALLATION_ID_STORAGE_KEY, installationId);
41
+ return installationId;
42
+ }
43
+ catch (_error) {
44
+ return undefined;
45
+ }
46
+ });
47
+ }
48
+ //# sourceMappingURL=installationId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installationId.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/installationId.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,YAAY,MAAM,2CAA2C,CAAC;AAErE,MAAM,2BAA2B,GAAG,4CAA4C,CAAC;AAEjF,SAAS,iBAAiB,CAAC,MAAc;IACvC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC;IAEvC,IAAI,YAAY,IAAI,OAAO,YAAY,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QACvE,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,sBAAsB;;IAC7B,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACpC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAA,KAAK,CAAC,CAAC,CAAC,mCAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAA,KAAK,CAAC,CAAC,CAAC,mCAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAE3C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrF,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7G,CAAC;AAED,MAAM,UAAgB,iBAAiB;;QACrC,IAAI,CAAC;YACH,MAAM,oBAAoB,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;YACrF,IAAI,oBAAoB,EAAE,CAAC;gBACzB,OAAO,oBAAoB,CAAC;YAC9B,CAAC;YAED,MAAM,cAAc,GAAG,sBAAsB,EAAE,CAAC;YAChD,MAAM,YAAY,CAAC,OAAO,CAAC,2BAA2B,EAAE,cAAc,CAAC,CAAC;YACxE,OAAO,cAAc,CAAC;QACxB,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;CAAA","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\n\nconst INSTALLATION_ID_STORAGE_KEY = '@grafana/faro-react-native/installation_id';\n\nfunction createRandomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n const cryptoObject = globalThis.crypto;\n\n if (cryptoObject && typeof cryptoObject.getRandomValues === 'function') {\n cryptoObject.getRandomValues(bytes);\n return bytes;\n }\n\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n\n return bytes;\n}\n\nfunction generateInstallationId(): string {\n const bytes = createRandomBytes(16);\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nexport async function getInstallationId(): Promise<string | undefined> {\n try {\n const storedInstallationId = await AsyncStorage.getItem(INSTALLATION_ID_STORAGE_KEY);\n if (storedInstallationId) {\n return storedInstallationId;\n }\n\n const installationId = generateInstallationId();\n await AsyncStorage.setItem(INSTALLATION_ID_STORAGE_KEY, installationId);\n return installationId;\n } catch (_error) {\n return undefined;\n }\n}\n"]}
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { Platform } from 'react-native';
11
11
  import DeviceInfo from 'react-native-device-info';
12
+ import { getInstallationId } from './installationId';
12
13
  /**
13
14
  * React Native framework version from `Platform.constants` (host app runtime), not `@grafana/faro-react-native` semver.
14
15
  */
@@ -29,20 +30,35 @@ function getReactNativeVersion() {
29
30
  return 'unknown';
30
31
  }
31
32
  }
32
- /**
33
- * Get device ID using react-native-device-info
34
- * Returns unique device identifier or 'unknown' on error
35
- */
36
- function getDeviceId() {
37
- return __awaiter(this, void 0, void 0, function* () {
38
- try {
39
- // getUniqueId returns a UUID that persists across app installations
40
- return yield DeviceInfo.getUniqueId();
41
- }
42
- catch (_error) {
43
- return 'unknown';
44
- }
45
- });
33
+ function getStructuredDeviceBrand(model, brand) {
34
+ if (Platform.OS !== 'ios') {
35
+ return brand;
36
+ }
37
+ return model.toLowerCase().includes('ipad') ? 'iPad' : 'iPhone';
38
+ }
39
+ function getStructuredDeviceManufacturer(manufacturer) {
40
+ return Platform.OS === 'ios' ? manufacturer.toLowerCase() : manufacturer;
41
+ }
42
+ function getStructuredDeviceModelIdentifier(model) {
43
+ // Android getDeviceId() returns a board code, so use Build.MODEL for the Faro
44
+ // model identifier.
45
+ if (Platform.OS === 'android') {
46
+ return model;
47
+ }
48
+ // React Native reports iPhone and iPadOS apps as Platform.OS === 'ios'.
49
+ // react-native-device-info exposes the Apple hardware identifier here.
50
+ if (Platform.OS === 'ios') {
51
+ return DeviceInfo.getDeviceId();
52
+ }
53
+ return undefined;
54
+ }
55
+ function getMobileDeviceType(isTablet) {
56
+ // iPadOS is still reported as Platform.OS === 'ios'; DeviceInfo.isTablet()
57
+ // distinguishes iPad/tablet form factor from phone form factor.
58
+ if (Platform.OS === 'ios' || Platform.OS === 'android') {
59
+ return isTablet ? 'tablet' : 'mobile';
60
+ }
61
+ return undefined;
46
62
  }
47
63
  /**
48
64
  * Get OS detail string matching Flutter SDK format
@@ -65,6 +81,19 @@ function getDeviceOsDetail() {
65
81
  return `${systemName} ${systemVersion}`;
66
82
  });
67
83
  }
84
+ function getDeviceOsBuildId() {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ try {
87
+ // RN device-info exposes a useful OS build id on both Android and iOS.
88
+ // Keep it omitted when unavailable instead of sending "unknown".
89
+ const buildId = yield DeviceInfo.getBuildId();
90
+ return buildId && buildId !== 'unknown' ? buildId : undefined;
91
+ }
92
+ catch (_error) {
93
+ return undefined;
94
+ }
95
+ });
96
+ }
68
97
  /**
69
98
  * Session attributes without device props when async collection or DeviceInfo is unavailable.
70
99
  * No synchronous DeviceInfo reads — use {@link getSessionAttributes} or the package async `initializeFaro`.
@@ -91,20 +120,29 @@ export function minimalSessionDeviceAttributes() {
91
120
  * - device_carrier
92
121
  */
93
122
  export function getSessionAttributes() {
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ return (yield collectMobileMeta()).sessionAttributes;
125
+ });
126
+ }
127
+ function collectMobileMeta() {
94
128
  return __awaiter(this, void 0, void 0, function* () {
95
129
  try {
96
- // Get device ID asynchronously
97
- const deviceId = yield getDeviceId();
98
- const deviceOsDetail = yield getDeviceOsDetail();
130
+ const [deviceOsDetail, deviceOsBuildId, installationId] = yield Promise.all([
131
+ getDeviceOsDetail(),
132
+ getDeviceOsBuildId(),
133
+ getInstallationId(),
134
+ ]);
99
135
  // Get synchronous device info
100
136
  const systemName = DeviceInfo.getSystemName();
101
137
  const systemVersion = DeviceInfo.getSystemVersion();
102
138
  const manufacturer = DeviceInfo.getManufacturerSync();
103
139
  const model = DeviceInfo.getModel();
140
+ const modelIdentifier = getStructuredDeviceModelIdentifier(model);
104
141
  const deviceName = DeviceInfo.getDeviceNameSync();
105
142
  const brand = DeviceInfo.getBrand();
106
143
  const isEmulator = DeviceInfo.isEmulatorSync();
107
144
  const isTablet = DeviceInfo.isTablet();
145
+ const deviceType = getMobileDeviceType(isTablet);
108
146
  // Memory info
109
147
  const totalMemory = DeviceInfo.getTotalMemorySync();
110
148
  const usedMemory = DeviceInfo.getUsedMemorySync();
@@ -146,32 +184,39 @@ export function getSessionAttributes() {
146
184
  catch (_error) {
147
185
  // Carrier not available
148
186
  }
149
- const attributes = {
150
- react_native_version: reactNativeVersion,
151
- device_os: systemName,
152
- device_os_version: systemVersion,
153
- device_os_detail: deviceOsDetail,
154
- device_manufacturer: manufacturer.toLowerCase(),
155
- device_model: model,
156
- device_model_name: deviceName,
157
- device_brand: brand,
158
- device_is_physical: String(!isEmulator),
159
- device_id: deviceId,
160
- device_type: isTablet ? 'tablet' : 'mobile',
161
- device_memory_total: String(totalMemory),
162
- device_memory_used: String(usedMemory),
163
- device_battery_level: batteryLevel,
164
- device_is_charging: isCharging,
165
- device_low_power_mode: lowPowerMode,
166
- device_carrier: carrier,
187
+ const attributes = Object.assign(Object.assign(Object.assign({ react_native_version: reactNativeVersion, device_os: systemName, device_os_version: systemVersion, device_os_detail: deviceOsDetail, device_manufacturer: manufacturer.toLowerCase(), device_model: model, device_model_name: deviceName, device_brand: brand, device_is_physical: String(!isEmulator) }, (installationId ? { device_id: installationId } : {})), (deviceType ? { device_type: deviceType } : {})), { device_memory_total: String(totalMemory), device_memory_used: String(usedMemory), device_battery_level: batteryLevel, device_is_charging: isCharging, device_low_power_mode: lowPowerMode, device_carrier: carrier });
188
+ const appMeta = installationId ? { installationId } : {};
189
+ const structuredDeviceBrand = getStructuredDeviceBrand(model, brand);
190
+ const structuredDeviceManufacturer = getStructuredDeviceManufacturer(manufacturer);
191
+ const osMeta = Object.assign(Object.assign({}, (deviceOsBuildId ? { build_id: deviceOsBuildId } : {})), { detail: deviceOsDetail, name: systemName, version: systemVersion });
192
+ return {
193
+ sessionAttributes: attributes,
194
+ meta: {
195
+ app: appMeta,
196
+ device: Object.assign(Object.assign(Object.assign({ brand: structuredDeviceBrand, is_physical: !isEmulator, manufacturer: structuredDeviceManufacturer }, (modelIdentifier ? { model_identifier: modelIdentifier } : {})), { model_name: model }), (deviceType ? { type: deviceType } : {})),
197
+ os: osMeta,
198
+ },
167
199
  };
168
- return attributes;
169
200
  }
170
201
  catch (_error) {
171
- return minimalSessionDeviceAttributes();
202
+ const installationId = yield getInstallationId();
203
+ return {
204
+ sessionAttributes: minimalSessionDeviceAttributes(),
205
+ meta: {
206
+ app: installationId ? { installationId } : {},
207
+ },
208
+ };
172
209
  }
173
210
  });
174
211
  }
212
+ /**
213
+ * Get structured mobile meta plus flat session attributes for async `initializeFaro`.
214
+ */
215
+ export function loadMobileMetaForInit() {
216
+ return __awaiter(this, void 0, void 0, function* () {
217
+ return collectMobileMeta();
218
+ });
219
+ }
175
220
  /**
176
221
  * Await full async session device attributes (battery, carrier, etc.), then fall back to
177
222
  * {@link minimalSessionDeviceAttributes} if anything throws. Used by async `initializeFaro`.
@@ -179,7 +224,7 @@ export function getSessionAttributes() {
179
224
  export function loadSessionDeviceAttributesForInit() {
180
225
  return __awaiter(this, void 0, void 0, function* () {
181
226
  try {
182
- return yield getSessionAttributes();
227
+ return (yield loadMobileMetaForInit()).sessionAttributes;
183
228
  }
184
229
  catch (_a) {
185
230
  return minimalSessionDeviceAttributes();
@@ -1 +1 @@
1
- {"version":3,"file":"sessionAttributes.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/sessionAttributes.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\nimport DeviceInfo from 'react-native-device-info';\n\n/**\n * Session attributes for React Native\n * These attributes are automatically included with every telemetry event\n *\n * Core attributes match Flutter SDK format, with additional mobile-specific\n * monitoring fields (memory, device type, battery, etc.)\n *\n * SDK name, core version, and npm adapter are on Faro meta `sdk` (`getSdkMeta` in `metas/sdk.ts`).\n *\n * `react_native_version` is the host app's React Native **framework** version from `Platform`, not the Faro package.\n */\nexport interface SessionAttributes {\n /** Host app's React Native framework version (e.g. \"0.75.1\") from `Platform.constants`. */\n react_native_version: string;\n\n /** Operating system (\"iOS\" or \"Android\") */\n device_os?: string;\n\n /** OS version (e.g., \"17.0\" for iOS, \"15\" for Android) */\n device_os_version?: string;\n\n /** Detailed OS info (e.g., \"iOS 17.0\" or \"Android 15 (SDK 35)\") */\n device_os_detail?: string;\n\n /** Device manufacturer (e.g., \"apple\", \"samsung\") */\n device_manufacturer?: string;\n\n /** Raw model identifier (e.g., \"iPhone16,1\", \"SM-A155F\") */\n device_model?: string;\n\n /** Human-readable model name (e.g., \"iPhone 15 Pro\") */\n device_model_name?: string;\n\n /** Device brand (e.g., \"iPhone\", \"samsung\") */\n device_brand?: string;\n\n /** Whether device is physical or emulator (\"true\" or \"false\") */\n device_is_physical?: string;\n\n /** Unique device ID (UUID) */\n device_id?: string;\n\n /** Device type (\"mobile\" or \"tablet\") */\n device_type?: string;\n\n /** Total device memory in bytes */\n device_memory_total?: string;\n\n /** Currently used memory in bytes */\n device_memory_used?: string;\n\n /** Battery level percentage (e.g., \"85\") - empty if unavailable */\n device_battery_level?: string;\n\n /** Whether device is charging (\"true\" or \"false\") - empty if unavailable */\n device_is_charging?: string;\n\n /** Whether low power mode is enabled (\"true\" or \"false\") - empty if unavailable */\n device_low_power_mode?: string;\n\n /** Mobile carrier name (e.g., \"Verizon\") - empty if unavailable */\n device_carrier?: string;\n}\n\n/**\n * React Native framework version from `Platform.constants` (host app runtime), not `@grafana/faro-react-native` semver.\n */\nfunction getReactNativeVersion(): string {\n try {\n const version = Platform.constants.reactNativeVersion;\n if (version && typeof version === 'object') {\n const { major, minor, patch, prerelease } = version as {\n major: number;\n minor: number;\n patch: number;\n prerelease?: number;\n };\n let versionString = `${major}.${minor}.${patch}`;\n if (prerelease) {\n versionString += `-rc.${prerelease}`;\n }\n return versionString;\n }\n return 'unknown';\n } catch (_error) {\n return 'unknown';\n }\n}\n\n/**\n * Get device ID using react-native-device-info\n * Returns unique device identifier or 'unknown' on error\n */\nasync function getDeviceId(): Promise<string> {\n try {\n // getUniqueId returns a UUID that persists across app installations\n return await DeviceInfo.getUniqueId();\n } catch (_error) {\n return 'unknown';\n }\n}\n\n/**\n * Get OS detail string matching Flutter SDK format\n * iOS: \"iOS 17.0\"\n * Android: \"Android 15 (SDK 35)\"\n */\nasync function getDeviceOsDetail(): Promise<string> {\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n\n if (Platform.OS === 'android') {\n try {\n const apiLevel = await DeviceInfo.getApiLevel();\n return `${systemName} ${systemVersion} (SDK ${apiLevel})`;\n } catch (_error) {\n return `${systemName} ${systemVersion}`;\n }\n }\n\n return `${systemName} ${systemVersion}`;\n}\n\n/**\n * Session attributes without device props when async collection or DeviceInfo is unavailable.\n * No synchronous DeviceInfo reads — use {@link getSessionAttributes} or the package async `initializeFaro`.\n */\nexport function minimalSessionDeviceAttributes(): SessionAttributes {\n return {\n react_native_version: getReactNativeVersion(),\n };\n}\n\n/**\n * Get all session attributes\n * These attributes are automatically included with every telemetry event\n *\n * Core attributes matching Flutter SDK:\n * - react_native_version (RN framework in the host app)\n * - device_os, device_os_version, device_os_detail\n * - device_manufacturer, device_model, device_model_name\n * - device_brand, device_is_physical, device_id\n *\n * Additional monitoring attributes:\n * - device_type (mobile/tablet)\n * - device_memory_total, device_memory_used\n * - device_battery_level, device_is_charging, device_low_power_mode\n * - device_carrier\n */\nexport async function getSessionAttributes(): Promise<SessionAttributes> {\n try {\n // Get device ID asynchronously\n const deviceId = await getDeviceId();\n const deviceOsDetail = await getDeviceOsDetail();\n\n // Get synchronous device info\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n const manufacturer = DeviceInfo.getManufacturerSync();\n const model = DeviceInfo.getModel();\n const deviceName = DeviceInfo.getDeviceNameSync();\n const brand = DeviceInfo.getBrand();\n const isEmulator = DeviceInfo.isEmulatorSync();\n const isTablet = DeviceInfo.isTablet();\n\n // Memory info\n const totalMemory = DeviceInfo.getTotalMemorySync();\n const usedMemory = DeviceInfo.getUsedMemorySync();\n const reactNativeVersion = getReactNativeVersion();\n\n // Try to get async device info (battery, carrier)\n let batteryLevel: string | undefined;\n let isCharging: string | undefined;\n let lowPowerMode: string | undefined;\n let carrier: string | undefined;\n\n try {\n const battery = await DeviceInfo.getBatteryLevel();\n if (battery >= 0) {\n batteryLevel = String(Math.round(battery * 100));\n }\n } catch (_error) {\n // Battery info not available\n }\n\n try {\n isCharging = String(await DeviceInfo.isBatteryCharging());\n } catch (_error) {\n // Charging status not available\n }\n\n try {\n if ('isPowerSaveMode' in DeviceInfo) {\n lowPowerMode = String(\n await (DeviceInfo as typeof DeviceInfo & { isPowerSaveMode: () => Promise<boolean> }).isPowerSaveMode()\n );\n }\n } catch (_error) {\n // Low power mode not available\n }\n\n try {\n const carrierName = await DeviceInfo.getCarrier();\n if (carrierName && carrierName !== 'unknown') {\n carrier = carrierName;\n }\n } catch (_error) {\n // Carrier not available\n }\n\n const attributes: SessionAttributes = {\n react_native_version: reactNativeVersion,\n device_os: systemName,\n device_os_version: systemVersion,\n device_os_detail: deviceOsDetail,\n device_manufacturer: manufacturer.toLowerCase(),\n device_model: model,\n device_model_name: deviceName,\n device_brand: brand,\n device_is_physical: String(!isEmulator),\n device_id: deviceId,\n device_type: isTablet ? 'tablet' : 'mobile',\n device_memory_total: String(totalMemory),\n device_memory_used: String(usedMemory),\n device_battery_level: batteryLevel,\n device_is_charging: isCharging,\n device_low_power_mode: lowPowerMode,\n device_carrier: carrier,\n };\n\n return attributes;\n } catch (_error) {\n return minimalSessionDeviceAttributes();\n }\n}\n\n/**\n * Await full async session device attributes (battery, carrier, etc.), then fall back to\n * {@link minimalSessionDeviceAttributes} if anything throws. Used by async `initializeFaro`.\n */\nexport async function loadSessionDeviceAttributesForInit(): Promise<SessionAttributes> {\n try {\n return await getSessionAttributes();\n } catch {\n return minimalSessionDeviceAttributes();\n }\n}\n"]}
1
+ {"version":3,"file":"sessionAttributes.js","sourceRoot":"","sources":["../../../../src/instrumentations/session/sessionAttributes.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\nimport DeviceInfo from 'react-native-device-info';\n\nimport type { Meta } from '@grafana/faro-core';\n\nimport { getInstallationId } from './installationId';\n\n/**\n * Session attributes for React Native\n * These attributes are automatically included with every telemetry event\n *\n * Core attributes match Flutter SDK format, with additional mobile-specific\n * monitoring fields (memory, device type, battery, etc.)\n *\n * SDK name, core version, and npm adapter are on Faro meta `sdk` (`getSdkMeta` in `metas/sdk.ts`).\n *\n * `react_native_version` is the host app's React Native **framework** version from `Platform`, not the Faro package.\n */\nexport interface SessionAttributes {\n /** Host app's React Native framework version (e.g. \"0.75.1\") from `Platform.constants`. */\n react_native_version: string;\n\n /** Operating system (\"iOS\" or \"Android\") */\n device_os?: string;\n\n /** OS version (e.g., \"17.0\" for iOS, \"15\" for Android) */\n device_os_version?: string;\n\n /** Detailed OS info (e.g., \"iOS 17.0\" or \"Android 15 (SDK 35)\") */\n device_os_detail?: string;\n\n /** Device manufacturer (e.g., \"apple\", \"samsung\", \"Google\") */\n device_manufacturer?: string;\n\n /** Device model in flat session attributes (e.g., \"iPhone 15 Pro\", \"SM-A155F\") */\n device_model?: string;\n\n /** Human-readable model name (e.g., \"iPhone 15 Pro\") */\n device_model_name?: string;\n\n /** Device brand in flat session attributes (e.g., \"Apple\", \"samsung\") */\n device_brand?: string;\n\n /** Whether device is physical or emulator (\"true\" or \"false\") */\n device_is_physical?: string;\n\n /** Temporary flat attribute mirroring `app.installationId` during migration. */\n device_id?: string;\n\n /** Device type (\"mobile\" or \"tablet\") */\n device_type?: string;\n\n /** Total device memory in bytes */\n device_memory_total?: string;\n\n /** Currently used memory in bytes */\n device_memory_used?: string;\n\n /** Battery level percentage (e.g., \"85\") - empty if unavailable */\n device_battery_level?: string;\n\n /** Whether device is charging (\"true\" or \"false\") - empty if unavailable */\n device_is_charging?: string;\n\n /** Whether low power mode is enabled (\"true\" or \"false\") - empty if unavailable */\n device_low_power_mode?: string;\n\n /** Mobile carrier name (e.g., \"Verizon\") - empty if unavailable */\n device_carrier?: string;\n}\n\nexport interface PreloadedMobileMeta {\n sessionAttributes: SessionAttributes;\n meta: Pick<Meta, 'app' | 'device' | 'os'>;\n}\n\n/**\n * React Native framework version from `Platform.constants` (host app runtime), not `@grafana/faro-react-native` semver.\n */\nfunction getReactNativeVersion(): string {\n try {\n const version = Platform.constants.reactNativeVersion;\n if (version && typeof version === 'object') {\n const { major, minor, patch, prerelease } = version as {\n major: number;\n minor: number;\n patch: number;\n prerelease?: number;\n };\n let versionString = `${major}.${minor}.${patch}`;\n if (prerelease) {\n versionString += `-rc.${prerelease}`;\n }\n return versionString;\n }\n return 'unknown';\n } catch (_error) {\n return 'unknown';\n }\n}\n\nfunction getStructuredDeviceBrand(model: string, brand: string): string {\n if (Platform.OS !== 'ios') {\n return brand;\n }\n\n return model.toLowerCase().includes('ipad') ? 'iPad' : 'iPhone';\n}\n\nfunction getStructuredDeviceManufacturer(manufacturer: string): string {\n return Platform.OS === 'ios' ? manufacturer.toLowerCase() : manufacturer;\n}\n\nfunction getStructuredDeviceModelIdentifier(model: string): string | undefined {\n // Android getDeviceId() returns a board code, so use Build.MODEL for the Faro\n // model identifier.\n if (Platform.OS === 'android') {\n return model;\n }\n\n // React Native reports iPhone and iPadOS apps as Platform.OS === 'ios'.\n // react-native-device-info exposes the Apple hardware identifier here.\n if (Platform.OS === 'ios') {\n return DeviceInfo.getDeviceId();\n }\n\n return undefined;\n}\n\nfunction getMobileDeviceType(isTablet: boolean): 'mobile' | 'tablet' | undefined {\n // iPadOS is still reported as Platform.OS === 'ios'; DeviceInfo.isTablet()\n // distinguishes iPad/tablet form factor from phone form factor.\n if (Platform.OS === 'ios' || Platform.OS === 'android') {\n return isTablet ? 'tablet' : 'mobile';\n }\n\n return undefined;\n}\n\n/**\n * Get OS detail string matching Flutter SDK format\n * iOS: \"iOS 17.0\"\n * Android: \"Android 15 (SDK 35)\"\n */\nasync function getDeviceOsDetail(): Promise<string> {\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n\n if (Platform.OS === 'android') {\n try {\n const apiLevel = await DeviceInfo.getApiLevel();\n return `${systemName} ${systemVersion} (SDK ${apiLevel})`;\n } catch (_error) {\n return `${systemName} ${systemVersion}`;\n }\n }\n\n return `${systemName} ${systemVersion}`;\n}\n\nasync function getDeviceOsBuildId(): Promise<string | undefined> {\n try {\n // RN device-info exposes a useful OS build id on both Android and iOS.\n // Keep it omitted when unavailable instead of sending \"unknown\".\n const buildId = await DeviceInfo.getBuildId();\n return buildId && buildId !== 'unknown' ? buildId : undefined;\n } catch (_error) {\n return undefined;\n }\n}\n\n/**\n * Session attributes without device props when async collection or DeviceInfo is unavailable.\n * No synchronous DeviceInfo reads — use {@link getSessionAttributes} or the package async `initializeFaro`.\n */\nexport function minimalSessionDeviceAttributes(): SessionAttributes {\n return {\n react_native_version: getReactNativeVersion(),\n };\n}\n\n/**\n * Get all session attributes\n * These attributes are automatically included with every telemetry event\n *\n * Core attributes matching Flutter SDK:\n * - react_native_version (RN framework in the host app)\n * - device_os, device_os_version, device_os_detail\n * - device_manufacturer, device_model, device_model_name\n * - device_brand, device_is_physical, device_id\n *\n * Additional monitoring attributes:\n * - device_type (mobile/tablet)\n * - device_memory_total, device_memory_used\n * - device_battery_level, device_is_charging, device_low_power_mode\n * - device_carrier\n */\nexport async function getSessionAttributes(): Promise<SessionAttributes> {\n return (await collectMobileMeta()).sessionAttributes;\n}\n\nasync function collectMobileMeta(): Promise<PreloadedMobileMeta> {\n try {\n const [deviceOsDetail, deviceOsBuildId, installationId] = await Promise.all([\n getDeviceOsDetail(),\n getDeviceOsBuildId(),\n getInstallationId(),\n ]);\n\n // Get synchronous device info\n const systemName = DeviceInfo.getSystemName();\n const systemVersion = DeviceInfo.getSystemVersion();\n const manufacturer = DeviceInfo.getManufacturerSync();\n const model = DeviceInfo.getModel();\n const modelIdentifier = getStructuredDeviceModelIdentifier(model);\n const deviceName = DeviceInfo.getDeviceNameSync();\n const brand = DeviceInfo.getBrand();\n const isEmulator = DeviceInfo.isEmulatorSync();\n const isTablet = DeviceInfo.isTablet();\n const deviceType = getMobileDeviceType(isTablet);\n\n // Memory info\n const totalMemory = DeviceInfo.getTotalMemorySync();\n const usedMemory = DeviceInfo.getUsedMemorySync();\n const reactNativeVersion = getReactNativeVersion();\n\n // Try to get async device info (battery, carrier)\n let batteryLevel: string | undefined;\n let isCharging: string | undefined;\n let lowPowerMode: string | undefined;\n let carrier: string | undefined;\n\n try {\n const battery = await DeviceInfo.getBatteryLevel();\n if (battery >= 0) {\n batteryLevel = String(Math.round(battery * 100));\n }\n } catch (_error) {\n // Battery info not available\n }\n\n try {\n isCharging = String(await DeviceInfo.isBatteryCharging());\n } catch (_error) {\n // Charging status not available\n }\n\n try {\n if ('isPowerSaveMode' in DeviceInfo) {\n lowPowerMode = String(\n await (DeviceInfo as typeof DeviceInfo & { isPowerSaveMode: () => Promise<boolean> }).isPowerSaveMode()\n );\n }\n } catch (_error) {\n // Low power mode not available\n }\n\n try {\n const carrierName = await DeviceInfo.getCarrier();\n if (carrierName && carrierName !== 'unknown') {\n carrier = carrierName;\n }\n } catch (_error) {\n // Carrier not available\n }\n\n const attributes: SessionAttributes = {\n react_native_version: reactNativeVersion,\n device_os: systemName,\n device_os_version: systemVersion,\n device_os_detail: deviceOsDetail,\n device_manufacturer: manufacturer.toLowerCase(),\n device_model: model,\n device_model_name: deviceName,\n device_brand: brand,\n device_is_physical: String(!isEmulator),\n ...(installationId ? { device_id: installationId } : {}),\n ...(deviceType ? { device_type: deviceType } : {}),\n device_memory_total: String(totalMemory),\n device_memory_used: String(usedMemory),\n device_battery_level: batteryLevel,\n device_is_charging: isCharging,\n device_low_power_mode: lowPowerMode,\n device_carrier: carrier,\n };\n const appMeta = installationId ? { installationId } : {};\n const structuredDeviceBrand = getStructuredDeviceBrand(model, brand);\n const structuredDeviceManufacturer = getStructuredDeviceManufacturer(manufacturer);\n const osMeta = {\n ...(deviceOsBuildId ? { build_id: deviceOsBuildId } : {}),\n detail: deviceOsDetail,\n name: systemName,\n version: systemVersion,\n };\n\n return {\n sessionAttributes: attributes,\n meta: {\n app: appMeta,\n device: {\n brand: structuredDeviceBrand,\n is_physical: !isEmulator,\n manufacturer: structuredDeviceManufacturer,\n ...(modelIdentifier ? { model_identifier: modelIdentifier } : {}),\n model_name: model,\n ...(deviceType ? { type: deviceType } : {}),\n },\n os: osMeta,\n },\n };\n } catch (_error) {\n const installationId = await getInstallationId();\n\n return {\n sessionAttributes: minimalSessionDeviceAttributes(),\n meta: {\n app: installationId ? { installationId } : {},\n },\n };\n }\n}\n\n/**\n * Get structured mobile meta plus flat session attributes for async `initializeFaro`.\n */\nexport async function loadMobileMetaForInit(): Promise<PreloadedMobileMeta> {\n return collectMobileMeta();\n}\n\n/**\n * Await full async session device attributes (battery, carrier, etc.), then fall back to\n * {@link minimalSessionDeviceAttributes} if anything throws. Used by async `initializeFaro`.\n */\nexport async function loadSessionDeviceAttributesForInit(): Promise<SessionAttributes> {\n try {\n return (await loadMobileMetaForInit()).sessionAttributes;\n } catch {\n return minimalSessionDeviceAttributes();\n }\n}\n"]}
@@ -0,0 +1,56 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import DeviceInfo from 'react-native-device-info';
11
+ import { globalObject } from '@grafana/faro-core';
12
+ const BUNDLE_ID_SEPARATOR = '@';
13
+ /** Metro / webpack preamble bundle id for JS source maps (`__faroBundleId_<appName>`). */
14
+ export function getMetroInjectedBundleId(appName) {
15
+ if (!appName) {
16
+ return undefined;
17
+ }
18
+ const key = `__faroBundleId_${appName}`;
19
+ const fromGlobal = globalObject[key];
20
+ if (typeof fromGlobal === 'string' && fromGlobal !== '') {
21
+ return fromGlobal;
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Encodes the Android/iOS build identity used as `meta.app.bundleId` for
27
+ * server-side symbol retrace. Must match the symbols upload bundle id
28
+ * (`{applicationId}@{versionCode}@{versionName}`).
29
+ */
30
+ export function formatSymbolsBundleId(applicationId, versionCode, versionName) {
31
+ return `${applicationId}${BUNDLE_ID_SEPARATOR}${versionCode}${BUNDLE_ID_SEPARATOR}${versionName}`;
32
+ }
33
+ /**
34
+ * Reads the installed build's identity from `react-native-device-info` and
35
+ * returns the encoded bundle id when all parts are available.
36
+ *
37
+ * Values must match what the symbol uploader (Gradle plugin / faro-cli) sends
38
+ * for the same build so the collector can locate the mapping for retrace.
39
+ */
40
+ export function loadAppSymbolsBundleIdForInit() {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ try {
43
+ const applicationId = DeviceInfo.getBundleId();
44
+ const versionName = DeviceInfo.getVersion();
45
+ const versionCodeRaw = DeviceInfo.getBuildNumber();
46
+ if (!applicationId || versionCodeRaw === '' || versionCodeRaw == null || !versionName) {
47
+ return undefined;
48
+ }
49
+ return formatSymbolsBundleId(applicationId, String(versionCodeRaw), versionName);
50
+ }
51
+ catch (_a) {
52
+ return undefined;
53
+ }
54
+ });
55
+ }
56
+ //# sourceMappingURL=appBuildIdentity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appBuildIdentity.js","sourceRoot":"","sources":["../../../src/metas/appBuildIdentity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,0FAA0F;AAC1F,MAAM,UAAU,wBAAwB,CAAC,OAA2B;IAClE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,kBAAkB,OAAO,EAAE,CAAC;IACxC,MAAM,UAAU,GAAI,YAAwC,CAAC,GAAG,CAAC,CAAC;IAClE,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;QACxD,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,aAAqB,EAAE,WAAmB,EAAE,WAAmB;IACnG,OAAO,GAAG,aAAa,GAAG,mBAAmB,GAAG,WAAW,GAAG,mBAAmB,GAAG,WAAW,EAAE,CAAC;AACpG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAgB,6BAA6B;;QACjD,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;YAC/C,MAAM,WAAW,GAAG,UAAU,CAAC,UAAU,EAAE,CAAC;YAC5C,MAAM,cAAc,GAAG,UAAU,CAAC,cAAc,EAAE,CAAC;YAEnD,IAAI,CAAC,aAAa,IAAI,cAAc,KAAK,EAAE,IAAI,cAAc,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtF,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,OAAO,qBAAqB,CAAC,aAAa,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,CAAC;QACnF,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;CAAA","sourcesContent":["import DeviceInfo from 'react-native-device-info';\n\nimport { globalObject } from '@grafana/faro-core';\n\nconst BUNDLE_ID_SEPARATOR = '@';\n\n/** Metro / webpack preamble bundle id for JS source maps (`__faroBundleId_<appName>`). */\nexport function getMetroInjectedBundleId(appName: string | undefined): string | undefined {\n if (!appName) {\n return undefined;\n }\n const key = `__faroBundleId_${appName}`;\n const fromGlobal = (globalObject as Record<string, unknown>)[key];\n if (typeof fromGlobal === 'string' && fromGlobal !== '') {\n return fromGlobal;\n }\n return undefined;\n}\n\n/**\n * Encodes the Android/iOS build identity used as `meta.app.bundleId` for\n * server-side symbol retrace. Must match the symbols upload bundle id\n * (`{applicationId}@{versionCode}@{versionName}`).\n */\nexport function formatSymbolsBundleId(applicationId: string, versionCode: string, versionName: string): string {\n return `${applicationId}${BUNDLE_ID_SEPARATOR}${versionCode}${BUNDLE_ID_SEPARATOR}${versionName}`;\n}\n\n/**\n * Reads the installed build's identity from `react-native-device-info` and\n * returns the encoded bundle id when all parts are available.\n *\n * Values must match what the symbol uploader (Gradle plugin / faro-cli) sends\n * for the same build so the collector can locate the mapping for retrace.\n */\nexport async function loadAppSymbolsBundleIdForInit(): Promise<string | undefined> {\n try {\n const applicationId = DeviceInfo.getBundleId();\n const versionName = DeviceInfo.getVersion();\n const versionCodeRaw = DeviceInfo.getBuildNumber();\n\n if (!applicationId || versionCodeRaw === '' || versionCodeRaw == null || !versionName) {\n return undefined;\n }\n\n return formatSymbolsBundleId(applicationId, String(versionCodeRaw), versionName);\n } catch {\n return undefined;\n }\n}\n"]}
@@ -6,10 +6,15 @@ export function mockDeviceInfo() {
6
6
  default: {
7
7
  getBrand: jest.fn(() => 'Apple'),
8
8
  getDeviceId: jest.fn(() => 'iPhone14,2'),
9
+ getDeviceNameSync: jest.fn(() => "Test's iPhone"),
10
+ getUniqueId: jest.fn(() => Promise.resolve('test-device-id')),
9
11
  getModel: jest.fn(() => 'iPhone 13 Pro'),
12
+ getManufacturerSync: jest.fn(() => 'Apple'),
10
13
  getSystemName: jest.fn(() => 'iOS'),
11
14
  getSystemVersion: jest.fn(() => '16.0'),
12
15
  getVersion: jest.fn(() => '1.0.0'),
16
+ getApiLevel: jest.fn(() => Promise.resolve(35)),
17
+ getBuildId: jest.fn(() => Promise.resolve('22B83')),
13
18
  isTablet: jest.fn(() => false),
14
19
  isEmulatorSync: jest.fn(() => false),
15
20
  getTotalMemorySync: jest.fn(() => 6442450944), // 6GB in bytes
@@ -1 +1 @@
1
- {"version":3,"file":"mockDeviceInfo.js","sourceRoot":"","sources":["../../../src/testUtils/mockDeviceInfo.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE;YACP,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;YAChC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;YACxC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC;YACxC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;YACnC,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;YACvC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;YAClC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;YAC9B,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;YACpC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,eAAe;YAC9D,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,eAAe;YAC7D,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrD,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACtD,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACtD,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SACzD;KACF,CAAC,CAAC,CAAC;AACN,CAAC","sourcesContent":["/**\n * Mock react-native-device-info for testing\n */\nexport function mockDeviceInfo(): void {\n jest.mock('react-native-device-info', () => ({\n default: {\n getBrand: jest.fn(() => 'Apple'),\n getDeviceId: jest.fn(() => 'iPhone14,2'),\n getModel: jest.fn(() => 'iPhone 13 Pro'),\n getSystemName: jest.fn(() => 'iOS'),\n getSystemVersion: jest.fn(() => '16.0'),\n getVersion: jest.fn(() => '1.0.0'),\n isTablet: jest.fn(() => false),\n isEmulatorSync: jest.fn(() => false),\n getTotalMemorySync: jest.fn(() => 6442450944), // 6GB in bytes\n getUsedMemorySync: jest.fn(() => 2147483648), // 2GB in bytes\n getBatteryLevel: jest.fn(() => Promise.resolve(0.75)),\n getCarrier: jest.fn(() => Promise.resolve('T-Mobile')),\n isPowerSaveMode: jest.fn(() => Promise.resolve(false)),\n isBatteryCharging: jest.fn(() => Promise.resolve(false)),\n },\n }));\n}\n"]}
1
+ {"version":3,"file":"mockDeviceInfo.js","sourceRoot":"","sources":["../../../src/testUtils/mockDeviceInfo.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3C,OAAO,EAAE;YACP,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;YAChC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;YACxC,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC;YACjD,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAC7D,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC;YACxC,mBAAmB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;YAC3C,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;YACnC,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;YACvC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;YAClC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC/C,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACnD,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;YAC9B,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;YACpC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,eAAe;YAC9D,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,eAAe;YAC7D,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACrD,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACtD,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACtD,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SACzD;KACF,CAAC,CAAC,CAAC;AACN,CAAC","sourcesContent":["/**\n * Mock react-native-device-info for testing\n */\nexport function mockDeviceInfo(): void {\n jest.mock('react-native-device-info', () => ({\n default: {\n getBrand: jest.fn(() => 'Apple'),\n getDeviceId: jest.fn(() => 'iPhone14,2'),\n getDeviceNameSync: jest.fn(() => \"Test's iPhone\"),\n getUniqueId: jest.fn(() => Promise.resolve('test-device-id')),\n getModel: jest.fn(() => 'iPhone 13 Pro'),\n getManufacturerSync: jest.fn(() => 'Apple'),\n getSystemName: jest.fn(() => 'iOS'),\n getSystemVersion: jest.fn(() => '16.0'),\n getVersion: jest.fn(() => '1.0.0'),\n getApiLevel: jest.fn(() => Promise.resolve(35)),\n getBuildId: jest.fn(() => Promise.resolve('22B83')),\n isTablet: jest.fn(() => false),\n isEmulatorSync: jest.fn(() => false),\n getTotalMemorySync: jest.fn(() => 6442450944), // 6GB in bytes\n getUsedMemorySync: jest.fn(() => 2147483648), // 2GB in bytes\n getBatteryLevel: jest.fn(() => Promise.resolve(0.75)),\n getCarrier: jest.fn(() => Promise.resolve('T-Mobile')),\n isPowerSaveMode: jest.fn(() => Promise.resolve(false)),\n isBatteryCharging: jest.fn(() => Promise.resolve(false)),\n },\n }));\n}\n"]}
@@ -8,10 +8,17 @@ jest.mock('react-native-device-info', () => {
8
8
  const mockDeviceInfo = {
9
9
  getBrand: jest.fn(() => 'Apple'),
10
10
  getDeviceId: jest.fn(() => 'iPhone14,2'),
11
+ getDeviceNameSync: jest.fn(() => "Test's iPhone"),
12
+ getUniqueId: jest.fn(() => Promise.resolve('test-device-id')),
11
13
  getModel: jest.fn(() => 'iPhone 13 Pro'),
14
+ getManufacturerSync: jest.fn(() => 'Apple'),
12
15
  getSystemName: jest.fn(() => 'iOS'),
13
16
  getSystemVersion: jest.fn(() => '16.0'),
14
17
  getVersion: jest.fn(() => '1.0.0'),
18
+ getBundleId: jest.fn(() => 'com.example.myapp'),
19
+ getBuildNumber: jest.fn(() => '42'),
20
+ getApiLevel: jest.fn(() => Promise.resolve(35)),
21
+ getBuildId: jest.fn(() => Promise.resolve('22B83')),
15
22
  isTablet: jest.fn(() => false),
16
23
  isEmulatorSync: jest.fn(() => false),
17
24
  getTotalMemorySync: jest.fn(() => 6442450944), // 6GB in bytes
@@ -1 +1 @@
1
- {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../../../src/testUtils/setupTests.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Jest setup file for React Native tests\n * Mocks third-party React Native dependencies\n */\n\n// Mock react-native-device-info\njest.mock('react-native-device-info', () => {\n const mockDeviceInfo = {\n getBrand: jest.fn(() => 'Apple'),\n getDeviceId: jest.fn(() => 'iPhone14,2'),\n getModel: jest.fn(() => 'iPhone 13 Pro'),\n getSystemName: jest.fn(() => 'iOS'),\n getSystemVersion: jest.fn(() => '16.0'),\n getVersion: jest.fn(() => '1.0.0'),\n isTablet: jest.fn(() => false),\n isEmulatorSync: jest.fn(() => false),\n getTotalMemorySync: jest.fn(() => 6442450944), // 6GB in bytes\n getUsedMemorySync: jest.fn(() => 2147483648), // 2GB in bytes\n getBatteryLevel: jest.fn(() => Promise.resolve(0.75)),\n getCarrier: jest.fn(() => Promise.resolve('T-Mobile')),\n isPowerSaveMode: jest.fn(() => Promise.resolve(false)),\n isBatteryCharging: jest.fn(() => Promise.resolve(false)),\n };\n return {\n __esModule: true,\n default: mockDeviceInfo,\n ...mockDeviceInfo,\n };\n});\n\n// Mock @react-native-async-storage/async-storage\n// Using a global storage object that can be accessed by tests\n(global as any).mockAsyncStorage = {};\n\nconst mockAsyncStorageImpl = {\n getItem: jest.fn((key: string) => {\n const storage = (global as any).mockAsyncStorage;\n return Promise.resolve(storage[key] ?? null);\n }),\n setItem: jest.fn((key: string, value: string) => {\n const storage = (global as any).mockAsyncStorage;\n storage[key] = value;\n return Promise.resolve();\n }),\n removeItem: jest.fn((key: string) => {\n const storage = (global as any).mockAsyncStorage;\n delete storage[key];\n return Promise.resolve();\n }),\n clear: jest.fn(() => {\n (global as any).mockAsyncStorage = {};\n return Promise.resolve();\n }),\n getAllKeys: jest.fn(() => {\n const storage = (global as any).mockAsyncStorage;\n return Promise.resolve(Object.keys(storage));\n }),\n multiGet: jest.fn((keys: string[]) => {\n const storage = (global as any).mockAsyncStorage;\n return Promise.resolve(keys.map((key) => [key, storage[key] ?? null]));\n }),\n multiSet: jest.fn((keyValuePairs: Array<[string, string]>) => {\n const storage = (global as any).mockAsyncStorage;\n keyValuePairs.forEach(([key, value]) => {\n storage[key] = value;\n });\n return Promise.resolve();\n }),\n multiRemove: jest.fn((keys: string[]) => {\n const storage = (global as any).mockAsyncStorage;\n keys.forEach((key) => delete storage[key]);\n return Promise.resolve();\n }),\n};\n\njest.mock('@react-native-async-storage/async-storage', () => ({\n __esModule: true,\n default: mockAsyncStorageImpl,\n}));\n\n// Ensure Platform.Version is properly mocked\n// The react-native preset mocks Platform, but we need to ensure Version is accessible\njest.mock(\n 'react-native/Libraries/Utilities/Platform',\n () => ({\n OS: 'ios',\n Version: '16.0',\n select: jest.fn((obj) => obj.ios || obj.default),\n }),\n { virtual: true }\n);\n\n// Mock fetch API globals for HTTP instrumentation tests\nif (typeof global.Response === 'undefined') {\n global.Response = class Response {\n ok: boolean;\n status: number;\n statusText: string;\n body: any;\n\n constructor(body?: BodyInit | null, init?: ResponseInit) {\n this.body = body;\n this.status = init?.status ?? 200;\n this.statusText = init?.statusText ?? 'OK';\n this.ok = this.status >= 200 && this.status < 300;\n }\n } as any;\n}\n\nif (typeof global.Request === 'undefined') {\n global.Request = class Request {\n url: string;\n method: string;\n\n constructor(input: RequestInfo | URL, init?: RequestInit) {\n if (typeof input === 'string') {\n this.url = input;\n this.method = init?.method ?? 'GET';\n } else if (input instanceof URL) {\n this.url = input.href;\n this.method = init?.method ?? 'GET';\n } else {\n // input is a Request object\n const req = input as Request;\n this.url = req.url;\n this.method = init?.method ?? req.method ?? 'GET';\n }\n }\n } as any;\n}\n\nif (typeof global.URL === 'undefined') {\n global.URL = class URL {\n href: string;\n\n constructor(url: string) {\n this.href = url;\n }\n } as any;\n}\n\n/**\n * Minimal XMLHttpRequest for Jest (Node has no XHR). Patches in XHRInstrumentation\n * tests attach to this prototype; send() completes asynchronously like a real request.\n */\nif (typeof global.XMLHttpRequest === 'undefined') {\n class MockXMLHttpRequest {\n static readonly DONE = 4;\n\n readyState = 0;\n status = 200;\n statusText = 'OK';\n onreadystatechange: ((this: XMLHttpRequest, ev: Event) => unknown) | null = null;\n\n private readonly listeners = new Map<string, Set<EventListener>>();\n\n open(_method: string, _url: string | URL): void {\n // Real open is replaced by instrumentation; baseline is a no-op.\n }\n\n send(_body?: Document | XMLHttpRequestBodyInit | null): void {\n // Synchronous completion matches test doubles and ensures Faro pushEvent runs in the same turn as send().\n this.readyState = MockXMLHttpRequest.DONE;\n const ev = { type: 'readystatechange' } as Event;\n if (this.onreadystatechange) {\n this.onreadystatechange.call(this as unknown as XMLHttpRequest, ev);\n }\n const load = this.listeners.get('load');\n if (load) {\n load.forEach((fn) => {\n fn.call(this as unknown as XMLHttpRequest, ev);\n });\n }\n }\n\n setRequestHeader(_name: string, _value: string): void {}\n\n getResponseHeader(_name: string): string | null {\n return null;\n }\n\n addEventListener(type: string, listener: EventListener): void {\n let set = this.listeners.get(type);\n if (!set) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n }\n\n removeEventListener(type: string, listener: EventListener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n abort(): void {}\n }\n\n global.XMLHttpRequest = MockXMLHttpRequest as unknown as typeof XMLHttpRequest;\n}\n"]}
1
+ {"version":3,"file":"setupTests.js","sourceRoot":"","sources":["../../../src/testUtils/setupTests.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Jest setup file for React Native tests\n * Mocks third-party React Native dependencies\n */\n\n// Mock react-native-device-info\njest.mock('react-native-device-info', () => {\n const mockDeviceInfo = {\n getBrand: jest.fn(() => 'Apple'),\n getDeviceId: jest.fn(() => 'iPhone14,2'),\n getDeviceNameSync: jest.fn(() => \"Test's iPhone\"),\n getUniqueId: jest.fn(() => Promise.resolve('test-device-id')),\n getModel: jest.fn(() => 'iPhone 13 Pro'),\n getManufacturerSync: jest.fn(() => 'Apple'),\n getSystemName: jest.fn(() => 'iOS'),\n getSystemVersion: jest.fn(() => '16.0'),\n getVersion: jest.fn(() => '1.0.0'),\n getBundleId: jest.fn(() => 'com.example.myapp'),\n getBuildNumber: jest.fn(() => '42'),\n getApiLevel: jest.fn(() => Promise.resolve(35)),\n getBuildId: jest.fn(() => Promise.resolve('22B83')),\n isTablet: jest.fn(() => false),\n isEmulatorSync: jest.fn(() => false),\n getTotalMemorySync: jest.fn(() => 6442450944), // 6GB in bytes\n getUsedMemorySync: jest.fn(() => 2147483648), // 2GB in bytes\n getBatteryLevel: jest.fn(() => Promise.resolve(0.75)),\n getCarrier: jest.fn(() => Promise.resolve('T-Mobile')),\n isPowerSaveMode: jest.fn(() => Promise.resolve(false)),\n isBatteryCharging: jest.fn(() => Promise.resolve(false)),\n };\n return {\n __esModule: true,\n default: mockDeviceInfo,\n ...mockDeviceInfo,\n };\n});\n\n// Mock @react-native-async-storage/async-storage\n// Using a global storage object that can be accessed by tests\n(global as any).mockAsyncStorage = {};\n\nconst mockAsyncStorageImpl = {\n getItem: jest.fn((key: string) => {\n const storage = (global as any).mockAsyncStorage;\n return Promise.resolve(storage[key] ?? null);\n }),\n setItem: jest.fn((key: string, value: string) => {\n const storage = (global as any).mockAsyncStorage;\n storage[key] = value;\n return Promise.resolve();\n }),\n removeItem: jest.fn((key: string) => {\n const storage = (global as any).mockAsyncStorage;\n delete storage[key];\n return Promise.resolve();\n }),\n clear: jest.fn(() => {\n (global as any).mockAsyncStorage = {};\n return Promise.resolve();\n }),\n getAllKeys: jest.fn(() => {\n const storage = (global as any).mockAsyncStorage;\n return Promise.resolve(Object.keys(storage));\n }),\n multiGet: jest.fn((keys: string[]) => {\n const storage = (global as any).mockAsyncStorage;\n return Promise.resolve(keys.map((key) => [key, storage[key] ?? null]));\n }),\n multiSet: jest.fn((keyValuePairs: Array<[string, string]>) => {\n const storage = (global as any).mockAsyncStorage;\n keyValuePairs.forEach(([key, value]) => {\n storage[key] = value;\n });\n return Promise.resolve();\n }),\n multiRemove: jest.fn((keys: string[]) => {\n const storage = (global as any).mockAsyncStorage;\n keys.forEach((key) => delete storage[key]);\n return Promise.resolve();\n }),\n};\n\njest.mock('@react-native-async-storage/async-storage', () => ({\n __esModule: true,\n default: mockAsyncStorageImpl,\n}));\n\n// Ensure Platform.Version is properly mocked\n// The react-native preset mocks Platform, but we need to ensure Version is accessible\njest.mock(\n 'react-native/Libraries/Utilities/Platform',\n () => ({\n OS: 'ios',\n Version: '16.0',\n select: jest.fn((obj) => obj.ios || obj.default),\n }),\n { virtual: true }\n);\n\n// Mock fetch API globals for HTTP instrumentation tests\nif (typeof global.Response === 'undefined') {\n global.Response = class Response {\n ok: boolean;\n status: number;\n statusText: string;\n body: any;\n\n constructor(body?: BodyInit | null, init?: ResponseInit) {\n this.body = body;\n this.status = init?.status ?? 200;\n this.statusText = init?.statusText ?? 'OK';\n this.ok = this.status >= 200 && this.status < 300;\n }\n } as any;\n}\n\nif (typeof global.Request === 'undefined') {\n global.Request = class Request {\n url: string;\n method: string;\n\n constructor(input: RequestInfo | URL, init?: RequestInit) {\n if (typeof input === 'string') {\n this.url = input;\n this.method = init?.method ?? 'GET';\n } else if (input instanceof URL) {\n this.url = input.href;\n this.method = init?.method ?? 'GET';\n } else {\n // input is a Request object\n const req = input as Request;\n this.url = req.url;\n this.method = init?.method ?? req.method ?? 'GET';\n }\n }\n } as any;\n}\n\nif (typeof global.URL === 'undefined') {\n global.URL = class URL {\n href: string;\n\n constructor(url: string) {\n this.href = url;\n }\n } as any;\n}\n\n/**\n * Minimal XMLHttpRequest for Jest (Node has no XHR). Patches in XHRInstrumentation\n * tests attach to this prototype; send() completes asynchronously like a real request.\n */\nif (typeof global.XMLHttpRequest === 'undefined') {\n class MockXMLHttpRequest {\n static readonly DONE = 4;\n\n readyState = 0;\n status = 200;\n statusText = 'OK';\n onreadystatechange: ((this: XMLHttpRequest, ev: Event) => unknown) | null = null;\n\n private readonly listeners = new Map<string, Set<EventListener>>();\n\n open(_method: string, _url: string | URL): void {\n // Real open is replaced by instrumentation; baseline is a no-op.\n }\n\n send(_body?: Document | XMLHttpRequestBodyInit | null): void {\n // Synchronous completion matches test doubles and ensures Faro pushEvent runs in the same turn as send().\n this.readyState = MockXMLHttpRequest.DONE;\n const ev = { type: 'readystatechange' } as Event;\n if (this.onreadystatechange) {\n this.onreadystatechange.call(this as unknown as XMLHttpRequest, ev);\n }\n const load = this.listeners.get('load');\n if (load) {\n load.forEach((fn) => {\n fn.call(this as unknown as XMLHttpRequest, ev);\n });\n }\n }\n\n setRequestHeader(_name: string, _value: string): void {}\n\n getResponseHeader(_name: string): string | null {\n return null;\n }\n\n addEventListener(type: string, listener: EventListener): void {\n let set = this.listeners.get(type);\n if (!set) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n }\n\n removeEventListener(type: string, listener: EventListener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n abort(): void {}\n }\n\n global.XMLHttpRequest = MockXMLHttpRequest as unknown as typeof XMLHttpRequest;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { SessionAttributes } from '../instrumentations/session/sessionAttributes';
1
+ import type { PreloadedMobileMeta } from '../instrumentations/session/sessionAttributes';
2
2
  import type { ReactNativeConfig, ReactNativeFullConfig } from './types';
3
3
  /**
4
4
  * Creates a full Faro config from React Native flag-based config.
@@ -6,6 +6,7 @@ import type { ReactNativeConfig, ReactNativeFullConfig } from './types';
6
6
  * Based on flags, builds instrumentations and transports automatically.
7
7
  * Client just enables what they need; makeRNConfig does the rest.
8
8
  *
9
- * @param preloadedSessionDeviceAttributes Device/session fields for session meta (passed from async `initializeFaro`).
9
+ * @param preloadedMobileMeta Device/session fields and structured mobile meta (passed from async `initializeFaro`).
10
+ * @param appSymbolsBundleId Encoded `meta.app.bundleId` for server-side symbol retrace.
10
11
  */
11
- export declare function makeRNConfig(config: ReactNativeConfig, preloadedSessionDeviceAttributes?: SessionAttributes): ReactNativeFullConfig;
12
+ export declare function makeRNConfig(config: ReactNativeConfig, preloadedMobileMeta?: PreloadedMobileMeta, appSymbolsBundleId?: string): ReactNativeFullConfig;
@@ -1,7 +1,7 @@
1
1
  import type { Config } from '@grafana/faro-core';
2
2
  import type { ANRInstrumentationOptions } from '../instrumentations/anr';
3
3
  import type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';
4
- import type { SessionAttributes } from '../instrumentations/session/sessionAttributes';
4
+ import type { PreloadedMobileMeta, SessionAttributes } from '../instrumentations/session/sessionAttributes';
5
5
  import type { Sampling } from './sampling';
6
6
  /**
7
7
  * React Native session tracking config.
@@ -79,6 +79,7 @@ export type ReactNativeConfig = Partial<Omit<Config, 'sessionTracking' | 'consol
79
79
  * Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.
80
80
  */
81
81
  export type ReactNativeFullConfig = Config & {
82
+ preloadedMobileMeta?: PreloadedMobileMeta['meta'];
82
83
  preloadedSessionDeviceAttributes?: SessionAttributes;
83
84
  };
84
85
  export {};
@@ -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.2.1";
5
+ export declare const FARO_REACT_NATIVE_NPM_VERSION: "1.3.0";
@@ -7,8 +7,8 @@ export { ErrorMechanism, ErrorsInstrumentation } from './instrumentations/errors
7
7
  export type { ErrorMechanismType, ErrorsInstrumentationOptions } from './instrumentations/errors';
8
8
  export { ConsoleInstrumentation } from './instrumentations/console';
9
9
  export { SessionInstrumentation } from './instrumentations/session';
10
- export { getSessionAttributes, loadSessionDeviceAttributesForInit, minimalSessionDeviceAttributes, } from './instrumentations/session/sessionAttributes';
11
- export type { SessionAttributes } from './instrumentations/session/sessionAttributes';
10
+ export { getSessionAttributes, loadMobileMetaForInit, loadSessionDeviceAttributesForInit, minimalSessionDeviceAttributes, } from './instrumentations/session/sessionAttributes';
11
+ export type { PreloadedMobileMeta, SessionAttributes } from './instrumentations/session/sessionAttributes';
12
12
  export { ViewInstrumentation } from './instrumentations/view';
13
13
  export { AppStateInstrumentation } from './instrumentations/appState';
14
14
  export { UserActionInstrumentation } from './instrumentations/userActions';
@@ -1,7 +1,9 @@
1
1
  import { type Faro } from '@grafana/faro-core';
2
2
  import type { ReactNativeConfig } from './config/types';
3
3
  /**
4
- * Awaits async device/session attribute collection, then initializes Faro with core `initializeFaro`.
5
- * On failure during collection, uses `minimalSessionDeviceAttributes` before init.
4
+ * Awaits async device/session attribute collection and the build identity used
5
+ * for server-side symbol retrace, then initializes Faro with core
6
+ * `initializeFaro`. On failure during collection, uses
7
+ * `minimalSessionDeviceAttributes` / an empty build identity before init.
6
8
  */
7
9
  export declare function initializeFaro(config: ReactNativeConfig): Promise<Faro>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns true when an ANR stack was sampled on the crash-killer path instead of
3
+ * the blocked main thread (common when ANRTracker fires during process teardown).
4
+ *
5
+ * Uses only Android framework and Faro SDK frame names — no app-specific identifiers.
6
+ */
7
+ export declare function isInvalidAnrCaptureStack(stack: string | undefined): boolean;