@grafana/faro-react-native 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/README.md +176 -30
  2. package/android/build.gradle +17 -12
  3. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashIds.kt +44 -0
  4. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashReporter.kt +134 -38
  5. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashSessionStore.kt +433 -0
  6. package/android/src/main/java/com/grafana/faro/reactnative/FaroCrashTraceCache.kt +11 -2
  7. package/android/src/main/java/com/grafana/faro/reactnative/FaroReactNativeModule.kt +128 -17
  8. package/android/src/main/java/com/grafana/faro/reactnative/FaroSessionProcess.kt +88 -0
  9. package/android/src/main/java/com/grafana/faro/reactnative/FrameMonitor.kt +34 -42
  10. package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterBelowApi30Test.kt +21 -0
  11. package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashReporterTest.kt +59 -0
  12. package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashSessionStoreTest.kt +268 -0
  13. package/android/src/test/java/com/grafana/faro/reactnative/FaroCrashTraceCacheTest.kt +52 -0
  14. package/android/src/test/java/com/grafana/faro/reactnative/FaroSessionProcessTest.kt +127 -0
  15. package/dist/cjs/config/getRNInstrumentations.js +6 -8
  16. package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
  17. package/dist/cjs/config/makeRNConfig.js +5 -5
  18. package/dist/cjs/config/makeRNConfig.js.map +1 -1
  19. package/dist/cjs/config/sampling.js.map +1 -1
  20. package/dist/cjs/config/types.js.map +1 -1
  21. package/dist/cjs/dataCollection/DataCollectionPolicy.js +1 -2
  22. package/dist/cjs/dataCollection/DataCollectionPolicy.js.map +1 -1
  23. package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
  24. package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
  25. package/dist/cjs/index.js +8 -3
  26. package/dist/cjs/index.js.map +1 -1
  27. package/dist/cjs/instrumentations/anr/instrumentation.js +10 -1
  28. package/dist/cjs/instrumentations/anr/instrumentation.js.map +1 -1
  29. package/dist/cjs/instrumentations/appState/index.js +8 -2
  30. package/dist/cjs/instrumentations/appState/index.js.map +1 -1
  31. package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +19 -6
  32. package/dist/cjs/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
  33. package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +331 -0
  34. package/dist/cjs/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
  35. package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
  36. package/dist/cjs/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
  37. package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
  38. package/dist/cjs/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
  39. package/dist/cjs/instrumentations/crashReporting/types.js.map +1 -1
  40. package/dist/cjs/instrumentations/errors/index.js +8 -3
  41. package/dist/cjs/instrumentations/errors/index.js.map +1 -1
  42. package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js +20 -31
  43. package/dist/cjs/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
  44. package/dist/cjs/instrumentations/frameMonitoring/types.js.map +1 -1
  45. package/dist/cjs/instrumentations/performance/index.js +2 -6
  46. package/dist/cjs/instrumentations/performance/index.js.map +1 -1
  47. package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js +48 -0
  48. package/dist/cjs/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
  49. package/dist/cjs/instrumentations/session/directSessionActivity.js +37 -0
  50. package/dist/cjs/instrumentations/session/directSessionActivity.js.map +1 -0
  51. package/dist/cjs/instrumentations/session/index.js +214 -45
  52. package/dist/cjs/instrumentations/session/index.js.map +1 -1
  53. package/dist/cjs/instrumentations/session/sessionActivity.js +43 -0
  54. package/dist/cjs/instrumentations/session/sessionActivity.js.map +1 -0
  55. package/dist/cjs/instrumentations/session/sessionAttributes.js +8 -8
  56. package/dist/cjs/instrumentations/session/sessionAttributes.js.map +1 -1
  57. package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +199 -24
  58. package/dist/cjs/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
  59. package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -13
  60. package/dist/cjs/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
  61. package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js +72 -0
  62. package/dist/cjs/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
  63. package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js +1 -1
  64. package/dist/cjs/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
  65. package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js +124 -96
  66. package/dist/cjs/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
  67. package/dist/cjs/instrumentations/session/sessionManager/types.js.map +1 -1
  68. package/dist/cjs/instrumentations/session/sessionProcess.js +96 -0
  69. package/dist/cjs/instrumentations/session/sessionProcess.js.map +1 -0
  70. package/dist/cjs/instrumentations/startup/index.js +2 -6
  71. package/dist/cjs/instrumentations/startup/index.js.map +1 -1
  72. package/dist/cjs/navigation/utils.js +3 -1
  73. package/dist/cjs/navigation/utils.js.map +1 -1
  74. package/dist/cjs/transports/fetch/index.js.map +1 -1
  75. package/dist/cjs/transports/fetch/transport.js +56 -25
  76. package/dist/cjs/transports/fetch/transport.js.map +1 -1
  77. package/dist/cjs/transports/fetch/types.js.map +1 -1
  78. package/dist/cjs/transports/offline/ConnectivityService.js +1 -1
  79. package/dist/cjs/transports/offline/ConnectivityService.js.map +1 -1
  80. package/dist/cjs/transports/offline/OfflineCache.js +2 -2
  81. package/dist/cjs/transports/offline/OfflineCache.js.map +1 -1
  82. package/dist/cjs/transports/offline/transport.js +3 -4
  83. package/dist/cjs/transports/offline/transport.js.map +1 -1
  84. package/dist/cjs/userPersistence/UserPersistence.js +1 -2
  85. package/dist/cjs/userPersistence/UserPersistence.js.map +1 -1
  86. package/dist/esm/config/getRNInstrumentations.js +6 -8
  87. package/dist/esm/config/getRNInstrumentations.js.map +1 -1
  88. package/dist/esm/config/makeRNConfig.js +5 -5
  89. package/dist/esm/config/makeRNConfig.js.map +1 -1
  90. package/dist/esm/config/sampling.js.map +1 -1
  91. package/dist/esm/config/types.js.map +1 -1
  92. package/dist/esm/dataCollection/DataCollectionPolicy.js +1 -2
  93. package/dist/esm/dataCollection/DataCollectionPolicy.js.map +1 -1
  94. package/dist/esm/generated/faroRNPackageMeta.js +1 -1
  95. package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
  96. package/dist/esm/index.js +4 -2
  97. package/dist/esm/index.js.map +1 -1
  98. package/dist/esm/instrumentations/anr/instrumentation.js +6 -1
  99. package/dist/esm/instrumentations/anr/instrumentation.js.map +1 -1
  100. package/dist/esm/instrumentations/appState/index.js +8 -2
  101. package/dist/esm/instrumentations/appState/index.js.map +1 -1
  102. package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js +15 -4
  103. package/dist/esm/instrumentations/crashReporting/BaseCrashReportingInstrumentation.js.map +1 -1
  104. package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js +230 -0
  105. package/dist/esm/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.js.map +1 -0
  106. package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js +2 -2
  107. package/dist/esm/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.js.map +1 -1
  108. package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js +2 -2
  109. package/dist/esm/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.js.map +1 -1
  110. package/dist/esm/instrumentations/crashReporting/types.js.map +1 -1
  111. package/dist/esm/instrumentations/errors/index.js +8 -3
  112. package/dist/esm/instrumentations/errors/index.js.map +1 -1
  113. package/dist/esm/instrumentations/frameMonitoring/instrumentation.js +17 -32
  114. package/dist/esm/instrumentations/frameMonitoring/instrumentation.js.map +1 -1
  115. package/dist/esm/instrumentations/frameMonitoring/types.js.map +1 -1
  116. package/dist/esm/instrumentations/performance/index.js +2 -6
  117. package/dist/esm/instrumentations/performance/index.js.map +1 -1
  118. package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js +34 -0
  119. package/dist/esm/instrumentations/session/FaroSessionActivityBoundary.js.map +1 -0
  120. package/dist/esm/instrumentations/session/directSessionActivity.js +32 -0
  121. package/dist/esm/instrumentations/session/directSessionActivity.js.map +1 -0
  122. package/dist/esm/instrumentations/session/index.js +207 -46
  123. package/dist/esm/instrumentations/session/index.js.map +1 -1
  124. package/dist/esm/instrumentations/session/sessionActivity.js +38 -0
  125. package/dist/esm/instrumentations/session/sessionActivity.js.map +1 -0
  126. package/dist/esm/instrumentations/session/sessionAttributes.js +7 -7
  127. package/dist/esm/instrumentations/session/sessionAttributes.js.map +1 -1
  128. package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js +190 -27
  129. package/dist/esm/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.js.map +1 -1
  130. package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js +31 -14
  131. package/dist/esm/instrumentations/session/sessionManager/VolatileSessionManager.js.map +1 -1
  132. package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js +56 -0
  133. package/dist/esm/instrumentations/session/sessionManager/persistentSessionRecord.js.map +1 -0
  134. package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js +1 -1
  135. package/dist/esm/instrumentations/session/sessionManager/sessionConstants.js.map +1 -1
  136. package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js +120 -44
  137. package/dist/esm/instrumentations/session/sessionManager/sessionManagerUtils.js.map +1 -1
  138. package/dist/esm/instrumentations/session/sessionManager/types.js.map +1 -1
  139. package/dist/esm/instrumentations/session/sessionProcess.js +89 -0
  140. package/dist/esm/instrumentations/session/sessionProcess.js.map +1 -0
  141. package/dist/esm/instrumentations/startup/index.js +2 -6
  142. package/dist/esm/instrumentations/startup/index.js.map +1 -1
  143. package/dist/esm/navigation/utils.js +2 -1
  144. package/dist/esm/navigation/utils.js.map +1 -1
  145. package/dist/esm/transports/fetch/index.js.map +1 -1
  146. package/dist/esm/transports/fetch/transport.js +38 -14
  147. package/dist/esm/transports/fetch/transport.js.map +1 -1
  148. package/dist/esm/transports/fetch/types.js.map +1 -1
  149. package/dist/esm/transports/offline/ConnectivityService.js +1 -1
  150. package/dist/esm/transports/offline/ConnectivityService.js.map +1 -1
  151. package/dist/esm/transports/offline/OfflineCache.js +2 -2
  152. package/dist/esm/transports/offline/OfflineCache.js.map +1 -1
  153. package/dist/esm/transports/offline/transport.js +3 -4
  154. package/dist/esm/transports/offline/transport.js.map +1 -1
  155. package/dist/esm/userPersistence/UserPersistence.js +1 -2
  156. package/dist/esm/userPersistence/UserPersistence.js.map +1 -1
  157. package/dist/types/config/getRNInstrumentations.d.ts +1 -2
  158. package/dist/types/config/sampling.d.ts +0 -3
  159. package/dist/types/config/types.d.ts +5 -3
  160. package/dist/types/dataCollection/DataCollectionPolicy.d.ts +1 -2
  161. package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
  162. package/dist/types/index.d.ts +4 -1
  163. package/dist/types/instrumentations/crashReporting/BaseCrashReportingInstrumentation.d.ts +14 -4
  164. package/dist/types/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.d.ts +27 -0
  165. package/dist/types/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.d.ts +2 -2
  166. package/dist/types/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.d.ts +2 -2
  167. package/dist/types/instrumentations/crashReporting/types.d.ts +6 -0
  168. package/dist/types/instrumentations/frameMonitoring/instrumentation.d.ts +2 -2
  169. package/dist/types/instrumentations/frameMonitoring/types.d.ts +4 -6
  170. package/dist/types/instrumentations/performance/index.d.ts +0 -2
  171. package/dist/types/instrumentations/session/FaroSessionActivityBoundary.d.ts +8 -0
  172. package/dist/types/instrumentations/session/directSessionActivity.d.ts +13 -0
  173. package/dist/types/instrumentations/session/index.d.ts +20 -1
  174. package/dist/types/instrumentations/session/sessionActivity.d.ts +9 -0
  175. package/dist/types/instrumentations/session/sessionAttributes.d.ts +6 -3
  176. package/dist/types/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.d.ts +16 -3
  177. package/dist/types/instrumentations/session/sessionManager/VolatileSessionManager.d.ts +9 -3
  178. package/dist/types/instrumentations/session/sessionManager/persistentSessionRecord.d.ts +4 -0
  179. package/dist/types/instrumentations/session/sessionManager/sessionManagerUtils.d.ts +19 -7
  180. package/dist/types/instrumentations/session/sessionManager/types.d.ts +6 -0
  181. package/dist/types/instrumentations/session/sessionProcess.d.ts +17 -0
  182. package/dist/types/instrumentations/startup/index.d.ts +2 -6
  183. package/dist/types/navigation/utils.d.ts +1 -0
  184. package/dist/types/transports/fetch/index.d.ts +1 -1
  185. package/dist/types/transports/fetch/transport.d.ts +8 -1
  186. package/dist/types/transports/fetch/types.d.ts +5 -0
  187. package/dist/types/transports/offline/ConnectivityService.d.ts +1 -1
  188. package/dist/types/transports/offline/OfflineCache.d.ts +2 -2
  189. package/dist/types/transports/offline/transport.d.ts +3 -4
  190. package/dist/types/userPersistence/UserPersistence.d.ts +1 -2
  191. package/ios/FaroCrashReporter.swift +214 -21
  192. package/ios/FaroReactNative.swift +24 -0
  193. package/ios/FaroReactNativeModule.h +2 -1
  194. package/ios/FaroReactNativeModule.mm +121 -6
  195. package/ios/RefreshRateVitals.swift +34 -38
  196. package/package.json +7 -5
  197. package/src/config/getRNInstrumentations.ts +6 -8
  198. package/src/config/makeRNConfig.test.ts +6 -0
  199. package/src/config/makeRNConfig.ts +10 -9
  200. package/src/config/sampling.ts +0 -3
  201. package/src/config/types.ts +5 -3
  202. package/src/dataCollection/DataCollectionPolicy.ts +1 -2
  203. package/src/generated/faroRNPackageMeta.ts +1 -1
  204. package/src/index.ts +8 -2
  205. package/src/initialize.test.ts +63 -2
  206. package/src/instrumentations/anr/instrumentation.ts +8 -1
  207. package/src/instrumentations/appState/index.ts +10 -2
  208. package/src/instrumentations/crashReporting/BaseCrashReportingInstrumentation.ts +28 -8
  209. package/src/instrumentations/crashReporting/RecoveredCrashReportingInstrumentation.ts +277 -0
  210. package/src/instrumentations/crashReporting/android/AndroidCrashReportingInstrumentation.ts +2 -2
  211. package/src/instrumentations/crashReporting/instrumentation.test.ts +433 -5
  212. package/src/instrumentations/crashReporting/ios/IosCrashReportingInstrumentation.ts +2 -2
  213. package/src/instrumentations/crashReporting/types.ts +7 -0
  214. package/src/instrumentations/errors/index.ts +12 -4
  215. package/src/instrumentations/errors/instrumentation.test.ts +35 -0
  216. package/src/instrumentations/frameMonitoring/instrumentation.test.ts +155 -0
  217. package/src/instrumentations/frameMonitoring/instrumentation.ts +19 -38
  218. package/src/instrumentations/frameMonitoring/types.ts +4 -6
  219. package/src/instrumentations/performance/index.ts +2 -6
  220. package/src/instrumentations/session/FaroSessionActivityBoundary.test.ts +60 -0
  221. package/src/instrumentations/session/FaroSessionActivityBoundary.tsx +41 -0
  222. package/src/instrumentations/session/beforeSendHook.test.ts +246 -0
  223. package/src/instrumentations/session/directSessionActivity.test.ts +158 -0
  224. package/src/instrumentations/session/directSessionActivity.ts +36 -0
  225. package/src/instrumentations/session/index.ts +269 -46
  226. package/src/instrumentations/session/persistentColdStart.test.ts +196 -0
  227. package/src/instrumentations/session/sessionActivity.test.ts +68 -0
  228. package/src/instrumentations/session/sessionActivity.ts +58 -0
  229. package/src/instrumentations/session/sessionAttributes.test.ts +37 -2
  230. package/src/instrumentations/session/sessionAttributes.ts +12 -6
  231. package/src/instrumentations/session/sessionCleanup.test.ts +410 -0
  232. package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.test.ts +552 -0
  233. package/src/instrumentations/session/sessionManager/MmkvPersistentSessionsManager.ts +249 -33
  234. package/src/instrumentations/session/sessionManager/VolatileSessionManager.ts +38 -18
  235. package/src/instrumentations/session/sessionManager/persistentSessionRecord.test.ts +103 -0
  236. package/src/instrumentations/session/sessionManager/persistentSessionRecord.ts +78 -0
  237. package/src/instrumentations/session/sessionManager/sampling.test.ts +1 -1
  238. package/src/instrumentations/session/sessionManager/sessionConstants.ts +1 -1
  239. package/src/instrumentations/session/sessionManager/sessionManagerUtils.test.ts +256 -18
  240. package/src/instrumentations/session/sessionManager/sessionManagerUtils.ts +180 -39
  241. package/src/instrumentations/session/sessionManager/types.ts +8 -0
  242. package/src/instrumentations/session/sessionManagerInstance.test.ts +65 -0
  243. package/src/instrumentations/session/sessionProcess.test.ts +136 -0
  244. package/src/instrumentations/session/sessionProcess.ts +114 -0
  245. package/src/instrumentations/session/startNewSession.test.ts +337 -0
  246. package/src/instrumentations/startup/index.ts +2 -6
  247. package/src/navigation/utils.ts +3 -1
  248. package/src/transports/fetch/index.ts +6 -1
  249. package/src/transports/fetch/transport.test.ts +86 -2
  250. package/src/transports/fetch/transport.ts +38 -16
  251. package/src/transports/fetch/types.ts +7 -0
  252. package/src/transports/offline/ConnectivityService.ts +1 -1
  253. package/src/transports/offline/OfflineCache.ts +2 -2
  254. package/src/transports/offline/transport.ts +3 -4
  255. package/src/userPersistence/UserPersistence.ts +1 -2
  256. package/dist/cjs/utils/throttle.js +0 -42
  257. package/dist/cjs/utils/throttle.js.map +0 -1
  258. package/dist/esm/utils/throttle.js +0 -34
  259. package/dist/esm/utils/throttle.js.map +0 -1
  260. package/dist/types/utils/throttle.d.ts +0 -9
  261. package/src/utils/throttle.test.ts +0 -141
  262. package/src/utils/throttle.ts +0 -34
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/transports/fetch/index.ts"],"names":[],"mappings":";;;AAAA,yCAA6C;AAApC,2GAAA,cAAc,OAAA","sourcesContent":["export { FetchTransport } from './transport';\nexport type { FetchTransportOptions, FetchTransportRequestOptions } from './types';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/transports/fetch/index.ts"],"names":[],"mappings":";;;AAAA,yCAA6C;AAApC,2GAAA,cAAc,OAAA","sourcesContent":["export { FetchTransport } from './transport';\nexport type {\n FetchTransportOptions,\n FetchTransportRequestOptions,\n FetchTransportSendOutcome,\n FetchTransportSendResult,\n} from './types';\n"]}
@@ -81,7 +81,6 @@ var DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000;
81
81
  var MAX_CONSECUTIVE_FAILURES = 3;
82
82
  var FAILURE_BACKOFF_MS = 30000; // 30 seconds
83
83
  var TOO_MANY_REQUESTS = 429;
84
- var ACCEPTED = 202;
85
84
  var FetchTransport = /** @class */ (function (_super) {
86
85
  __extends(FetchTransport, _super);
87
86
  function FetchTransport(options) {
@@ -171,29 +170,51 @@ var FetchTransport = /** @class */ (function (_super) {
171
170
  };
172
171
  FetchTransport.prototype.send = function (items) {
173
172
  return __awaiter(this, void 0, void 0, function () {
174
- var now, _a;
173
+ return __generator(this, function (_a) {
174
+ switch (_a.label) {
175
+ case 0: return [4 /*yield*/, this.sendWithResult(items)];
176
+ case 1:
177
+ _a.sent();
178
+ return [2 /*return*/];
179
+ }
180
+ });
181
+ });
182
+ };
183
+ /**
184
+ * Sends a batch and reports whether the collector accepted it.
185
+ * The request header uses the live session for collector accounting. Payload
186
+ * metadata remains independent so delayed signals can retain their original
187
+ * session.
188
+ */
189
+ FetchTransport.prototype.sendWithResult = function (items) {
190
+ return __awaiter(this, void 0, void 0, function () {
191
+ var now, sessionId_1, response, _a;
175
192
  var _this = this;
176
- return __generator(this, function (_b) {
177
- switch (_b.label) {
193
+ var _b;
194
+ return __generator(this, function (_c) {
195
+ switch (_c.label) {
178
196
  case 0:
179
- // DEBUG: Log at the very start of send
180
- this.logDebug("FetchTransport.send() called with ".concat(items.length, " items"));
181
- _b.label = 1;
197
+ this.logDebug("FetchTransport.sendWithResult() called with ".concat(items.length, " items"));
198
+ if (items.length === 0) {
199
+ return [2 /*return*/, { outcome: 'skipped' }];
200
+ }
201
+ _c.label = 1;
182
202
  case 1:
183
- _b.trys.push([1, 4, , 5]);
203
+ _c.trys.push([1, 4, , 5]);
184
204
  now = new Date(this.getNow());
185
205
  // Check if we're in backoff period
186
206
  if (this.disabledUntil > now) {
187
207
  this.logDebug("FetchTransport: in backoff period until ".concat(this.disabledUntil));
188
- return [2 /*return*/, Promise.resolve()];
208
+ return [2 /*return*/, { outcome: 'skipped' }];
189
209
  }
190
- // Wait for session to be ready before sending
191
- // This prevents 400 errors from missing X-Faro-Session-Id header
210
+ // The collector uses the header for session validation and accounting,
211
+ // independently of the session carried by each payload.
192
212
  return [4 /*yield*/, this.waitForSession()];
193
213
  case 2:
194
- // Wait for session to be ready before sending
195
- // This prevents 400 errors from missing X-Faro-Session-Id header
196
- _b.sent();
214
+ // The collector uses the header for session validation and accounting,
215
+ // independently of the session carried by each payload.
216
+ _c.sent();
217
+ sessionId_1 = (_b = this.metas.value.session) === null || _b === void 0 ? void 0 : _b.id;
197
218
  return [4 /*yield*/, this.promiseBuffer.add(function () {
198
219
  var transportBody = (0, faro_core_1.getTransportBody)(items);
199
220
  var body = JSON.stringify(transportBody);
@@ -206,11 +227,6 @@ var FetchTransport = /** @class */ (function (_super) {
206
227
  }
207
228
  var _b = _this.options, url = _b.url, requestOptions = _b.requestOptions, apiKey = _b.apiKey;
208
229
  var _c = requestOptions !== null && requestOptions !== void 0 ? requestOptions : {}, headers = _c.headers, restOfRequestOptions = __rest(_c, ["headers"]);
209
- var sessionId;
210
- var sessionMeta = _this.metas.value.session;
211
- if (sessionMeta != null) {
212
- sessionId = sessionMeta.id;
213
- }
214
230
  // DEBUG: Log fetch attempt
215
231
  _this.logDebug("FetchTransport: sending ".concat(items.length, " items to ").concat(url));
216
232
  // Create an AbortController for timeout
@@ -219,7 +235,7 @@ var FetchTransport = /** @class */ (function (_super) {
219
235
  controller.abort();
220
236
  _this.logDebug("FetchTransport: request timed out after 10s");
221
237
  }, 10000);
222
- return fetch(url, __assign({ method: 'POST', headers: __assign(__assign(__assign({ 'Content-Type': 'application/json' }, (headers !== null && headers !== void 0 ? headers : {})), (apiKey ? { 'x-api-key': apiKey } : {})), (sessionId ? { 'x-faro-session-id': sessionId } : {})), body: body, signal: controller.signal }, (restOfRequestOptions !== null && restOfRequestOptions !== void 0 ? restOfRequestOptions : {})))
238
+ return fetch(url, __assign({ method: 'POST', headers: __assign(__assign(__assign({ 'Content-Type': 'application/json' }, (headers !== null && headers !== void 0 ? headers : {})), (apiKey ? { 'x-api-key': apiKey } : {})), (sessionId_1 ? { 'x-faro-session-id': sessionId_1 } : {})), body: body, signal: controller.signal }, (restOfRequestOptions !== null && restOfRequestOptions !== void 0 ? restOfRequestOptions : {})))
223
239
  .then(function (response) { return __awaiter(_this, void 0, void 0, function () {
224
240
  var text;
225
241
  return __generator(this, function (_a) {
@@ -234,7 +250,7 @@ var FetchTransport = /** @class */ (function (_super) {
234
250
  this.disabledUntil = this.getRetryAfterDate(response);
235
251
  this.logDebug("FetchTransport: rate limited, disabled until ".concat(this.disabledUntil));
236
252
  }
237
- if (!(response.status !== ACCEPTED && response.status !== 200)) return [3 /*break*/, 2];
253
+ if (!(response.status < 200 || response.status >= 300)) return [3 /*break*/, 2];
238
254
  return [4 /*yield*/, response.text().catch(function () { return ''; })];
239
255
  case 1:
240
256
  text = _a.sent();
@@ -259,14 +275,29 @@ var FetchTransport = /** @class */ (function (_super) {
259
275
  }
260
276
  // Do NOT log errors to console - this causes infinite loops in React Native
261
277
  // when the DevTools console override intercepts even unpatchedConsole calls
278
+ return undefined;
262
279
  });
263
280
  })];
264
281
  case 3:
265
- _b.sent();
266
- return [3 /*break*/, 5];
282
+ response = _c.sent();
283
+ if (!response) {
284
+ return [2 /*return*/, { outcome: 'failed' }];
285
+ }
286
+ if (response.status >= 200 && response.status < 300) {
287
+ return [2 /*return*/, {
288
+ outcome: 'accepted',
289
+ status: response.status,
290
+ }];
291
+ }
292
+ return [2 /*return*/, {
293
+ outcome: 'rejected',
294
+ status: response.status,
295
+ }];
267
296
  case 4:
268
- _a = _b.sent();
269
- return [3 /*break*/, 5];
297
+ _a = _c.sent();
298
+ // Buffer full error - Do NOT log to console as it creates infinite loops
299
+ // The error is typically "Task buffer full" when the device is offline
300
+ return [2 /*return*/, { outcome: 'failed' }];
270
301
  case 5: return [2 /*return*/];
271
302
  }
272
303
  });
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/transports/fetch/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseTransport, createPromiseBuffer, getTransportBody, VERSION } from '@grafana/faro-core';\nimport type { Patterns, PromiseBuffer, TransportItem } from '@grafana/faro-core';\n\nimport type { FetchTransportOptions } from './types';\n\nconst DEFAULT_BUFFER_SIZE = 30;\nconst DEFAULT_CONCURRENCY = 5;\nconst DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000;\nconst MAX_CONSECUTIVE_FAILURES = 3;\nconst FAILURE_BACKOFF_MS = 30000; // 30 seconds\n\nconst TOO_MANY_REQUESTS = 429;\nconst ACCEPTED = 202;\n\nexport class FetchTransport extends BaseTransport {\n readonly name = '@grafana/faro-react-native:transport-fetch';\n readonly version = VERSION;\n\n promiseBuffer: PromiseBuffer<Response | void>;\n\n private readonly rateLimitBackoffMs: number;\n private readonly getNow: () => number;\n private disabledUntil: Date;\n private consecutiveFailures: number = 0;\n private sessionReadyPromise: Promise<void> | null = null;\n private sessionReadyResolve: (() => void) | null = null;\n private sessionReady: boolean = false;\n private metasListenerRegistered: boolean = false;\n\n constructor(private options: FetchTransportOptions) {\n super();\n\n this.rateLimitBackoffMs = options.defaultRateLimitBackoffMs ?? DEFAULT_RATE_LIMIT_BACKOFF_MS;\n this.getNow = options.getNow ?? (() => Date.now());\n // Align with getNow so tests (and apps) that supply a clock do not see spurious backoff\n this.disabledUntil = new Date(this.getNow());\n\n this.promiseBuffer = createPromiseBuffer({\n size: options.bufferSize ?? DEFAULT_BUFFER_SIZE,\n concurrency: options.concurrency ?? DEFAULT_CONCURRENCY,\n });\n }\n\n /**\n * Register a listener for metas changes to detect when session becomes available.\n * Uses faro-core's metas listener pattern instead of polling with setTimeout.\n */\n private registerSessionListener(): void {\n if (this.metasListenerRegistered || !this.metas?.addListener) {\n return;\n }\n\n this.metasListenerRegistered = true;\n\n this.metas.addListener((meta) => {\n if (meta.session?.id && this.sessionReadyResolve) {\n this.sessionReady = true;\n this.sessionReadyResolve();\n this.sessionReadyResolve = null;\n }\n });\n }\n\n /**\n * Wait for session to be available before sending.\n * This prevents 400 errors from the collector due to missing X-Faro-Session-Id header.\n *\n * Only waits if session tracking is enabled. If disabled, returns immediately.\n */\n private waitForSession(): Promise<void> {\n // Only wait for session if session tracking is enabled\n const sessionTrackingEnabled = this.config?.sessionTracking?.enabled;\n if (!sessionTrackingEnabled) {\n return Promise.resolve();\n }\n\n // Already have session\n if (this.sessionReady) {\n return Promise.resolve();\n }\n\n // Check if session is now available\n const sessionMeta = this.metas?.value?.session;\n if (sessionMeta?.id) {\n this.sessionReady = true;\n return Promise.resolve();\n }\n\n // Return existing promise if we're already waiting\n if (this.sessionReadyPromise) {\n return this.sessionReadyPromise;\n }\n\n // Register listener to be notified when session becomes available\n this.registerSessionListener();\n\n // Create a promise that resolves when session becomes available via listener\n this.sessionReadyPromise = new Promise<void>((resolve) => {\n this.sessionReadyResolve = resolve;\n\n // Also check immediately in case session was set between our check and listener registration\n const sessionMeta = this.metas?.value?.session;\n if (sessionMeta?.id) {\n this.sessionReady = true;\n resolve();\n this.sessionReadyResolve = null;\n }\n });\n\n return this.sessionReadyPromise;\n }\n\n async send(items: TransportItem[]): Promise<void> {\n // DEBUG: Log at the very start of send\n this.logDebug(`FetchTransport.send() called with ${items.length} items`);\n\n try {\n const now = new Date(this.getNow());\n\n // Check if we're in backoff period\n if (this.disabledUntil > now) {\n this.logDebug(`FetchTransport: in backoff period until ${this.disabledUntil}`);\n return Promise.resolve();\n }\n\n // Wait for session to be ready before sending\n // This prevents 400 errors from missing X-Faro-Session-Id header\n await this.waitForSession();\n\n await this.promiseBuffer.add(() => {\n const transportBody = getTransportBody(items);\n const body = JSON.stringify(transportBody);\n\n // DEBUG: Log measurement payloads to see what's being sent\n if (transportBody.measurements && transportBody.measurements.length > 0) {\n for (const m of transportBody.measurements) {\n this.logDebug(`FetchTransport: measurement payload - type=${m.type}, values=${JSON.stringify(m.values)}`);\n }\n }\n\n const { url, requestOptions, apiKey } = this.options;\n\n const { headers, ...restOfRequestOptions } = requestOptions ?? {};\n\n let sessionId;\n const sessionMeta = this.metas.value.session;\n if (sessionMeta != null) {\n sessionId = sessionMeta.id;\n }\n\n // DEBUG: Log fetch attempt\n this.logDebug(`FetchTransport: sending ${items.length} items to ${url}`);\n\n // Create an AbortController for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n this.logDebug(`FetchTransport: request timed out after 10s`);\n }, 10000);\n\n return fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(headers ?? {}),\n ...(apiKey ? { 'x-api-key': apiKey } : {}),\n ...(sessionId ? { 'x-faro-session-id': sessionId } : {}),\n },\n body,\n signal: controller.signal,\n // Note: React Native doesn't support keepalive\n // keepalive: body.length <= BEACON_BODY_SIZE_LIMIT,\n ...(restOfRequestOptions ?? {}),\n })\n .then(async (response) => {\n clearTimeout(timeoutId);\n // DEBUG: Log response status\n this.logDebug(`FetchTransport: response status ${response.status}`);\n\n // Reset failure counter on success\n this.consecutiveFailures = 0;\n\n if (response.status === TOO_MANY_REQUESTS) {\n this.disabledUntil = this.getRetryAfterDate(response);\n this.logDebug(`FetchTransport: rate limited, disabled until ${this.disabledUntil}`);\n }\n\n // Log non-success responses for debugging\n if (response.status !== ACCEPTED && response.status !== 200) {\n const text = await response.text().catch(() => '');\n this.logDebug(`FetchTransport: non-success response: ${response.status} ${text.slice(0, 200)}`);\n }\n\n return response;\n })\n .catch((error) => {\n clearTimeout(timeoutId);\n // DEBUG: Log the error\n this.logDebug(`FetchTransport: fetch failed - ${error?.message || error}`);\n\n // Increment failure counter\n this.consecutiveFailures++;\n\n // After MAX_CONSECUTIVE_FAILURES, enable circuit breaker to prevent infinite loops\n if (this.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {\n this.disabledUntil = new Date(this.getNow() + FAILURE_BACKOFF_MS);\n // Reset counter so we can try again after backoff\n this.consecutiveFailures = 0;\n this.logDebug(`FetchTransport: circuit breaker activated, disabled for 30s`);\n }\n\n // Do NOT log errors to console - this causes infinite loops in React Native\n // when the DevTools console override intercepts even unpatchedConsole calls\n });\n });\n } catch {\n // Buffer full error - Do NOT log to console as it creates infinite loops\n // The error is typically \"Task buffer full\" when the device is offline\n }\n }\n\n override getIgnoreUrls(): Patterns {\n return ([this.options.url] as Patterns).concat(this.config.ignoreUrls ?? []);\n }\n\n override isBatched(): boolean {\n return true;\n }\n\n private getRetryAfterDate(response: Response): Date {\n const now = this.getNow();\n const retryAfterHeader = response.headers.get('Retry-After');\n\n if (retryAfterHeader) {\n const delay = Number(retryAfterHeader);\n\n if (!isNaN(delay)) {\n return new Date(delay * 1000 + now);\n }\n\n const date = Date.parse(retryAfterHeader);\n\n if (!isNaN(date)) {\n return new Date(date);\n }\n }\n\n return new Date(now + this.rateLimitBackoffMs);\n }\n}\n"]}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/transports/fetch/transport.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAmG;AAKnG,IAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,IAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,IAAM,6BAA6B,GAAG,IAAI,CAAC;AAC3C,IAAM,wBAAwB,GAAG,CAAC,CAAC;AACnC,IAAM,kBAAkB,GAAG,KAAK,CAAC,CAAC,aAAa;AAE/C,IAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B;IAAoC,kCAAa;IAe/C,wBAAoB,OAA8B;;QAChD,YAAA,MAAK,WAAE,SAAC;QADU,aAAO,GAAP,OAAO,CAAuB;QAdzC,UAAI,GAAG,4CAA4C,CAAC;QACpD,aAAO,GAAG,mBAAO,CAAC;QAOnB,yBAAmB,GAAW,CAAC,CAAC;QAChC,yBAAmB,GAAyB,IAAI,CAAC;QACjD,yBAAmB,GAAwB,IAAI,CAAC;QAChD,kBAAY,GAAY,KAAK,CAAC;QAC9B,6BAAuB,GAAY,KAAK,CAAC;QAK/C,KAAI,CAAC,kBAAkB,GAAG,MAAA,OAAO,CAAC,yBAAyB,mCAAI,6BAA6B,CAAC;QAC7F,KAAI,CAAC,MAAM,GAAG,MAAA,OAAO,CAAC,MAAM,mCAAI,CAAC,cAAM,OAAA,IAAI,CAAC,GAAG,EAAE,EAAV,CAAU,CAAC,CAAC;QACnD,wFAAwF;QACxF,KAAI,CAAC,aAAa,GAAG,IAAI,IAAI,CAAC,KAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAE7C,KAAI,CAAC,aAAa,GAAG,IAAA,+BAAmB,EAAC;YACvC,IAAI,EAAE,MAAA,OAAO,CAAC,UAAU,mCAAI,mBAAmB;YAC/C,WAAW,EAAE,MAAA,OAAO,CAAC,WAAW,mCAAI,mBAAmB;SACxD,CAAC,CAAC;;IACL,CAAC;IAED;;;OAGG;IACK,gDAAuB,GAA/B;QAAA,iBAcC;;QAbC,IAAI,IAAI,CAAC,uBAAuB,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,KAAK,0CAAE,WAAW,CAAA,EAAE,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QAEpC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,UAAC,IAAI;;YAC1B,IAAI,CAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,EAAE,KAAI,KAAI,CAAC,mBAAmB,EAAE,CAAC;gBACjD,KAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,KAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,KAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;YAClC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,uCAAc,GAAtB;QAAA,iBAyCC;;QAxCC,uDAAuD;QACvD,IAAM,sBAAsB,GAAG,MAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,eAAe,0CAAE,OAAO,CAAC;QACrE,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,uBAAuB;QACvB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,oCAAoC;QACpC,IAAM,WAAW,GAAG,MAAA,MAAA,IAAI,CAAC,KAAK,0CAAE,KAAK,0CAAE,OAAO,CAAC;QAC/C,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,EAAE,EAAE,CAAC;YACpB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,mDAAmD;QACnD,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,mBAAmB,CAAC;QAClC,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAE/B,6EAA6E;QAC7E,IAAI,CAAC,mBAAmB,GAAG,IAAI,OAAO,CAAO,UAAC,OAAO;;YACnD,KAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC;YAEnC,6FAA6F;YAC7F,IAAM,WAAW,GAAG,MAAA,MAAA,KAAI,CAAC,KAAK,0CAAE,KAAK,0CAAE,OAAO,CAAC;YAC/C,IAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,EAAE,EAAE,CAAC;gBACpB,KAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,OAAO,EAAE,CAAC;gBACV,KAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;YAClC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAClC,CAAC;IAEK,6BAAI,GAAV,UAAW,KAAsB;;;;4BAC/B,qBAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAA;;wBAAhC,SAAgC,CAAC;;;;;KAClC;IAED;;;;;OAKG;IACG,uCAAc,GAApB,UAAqB,KAAsB;;;;;;;;wBACzC,IAAI,CAAC,QAAQ,CAAC,sDAA+C,KAAK,CAAC,MAAM,WAAQ,CAAC,CAAC;wBAEnF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;4BACvB,sBAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAC;wBAChC,CAAC;;;;wBAGO,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;wBAEpC,mCAAmC;wBACnC,IAAI,IAAI,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC;4BAC7B,IAAI,CAAC,QAAQ,CAAC,kDAA2C,IAAI,CAAC,aAAa,CAAE,CAAC,CAAC;4BAC/E,sBAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAC;wBAChC,CAAC;wBAED,uEAAuE;wBACvE,wDAAwD;wBACxD,qBAAM,IAAI,CAAC,cAAc,EAAE,EAAA;;wBAF3B,uEAAuE;wBACvE,wDAAwD;wBACxD,SAA2B,CAAC;wBACtB,cAAY,MAAA,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,0CAAE,EAAE,CAAC;wBAE9B,qBAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;gCAC5C,IAAM,aAAa,GAAG,IAAA,4BAAgB,EAAC,KAAK,CAAC,CAAC;gCAC9C,IAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;gCAE3C,2DAA2D;gCAC3D,IAAI,aAAa,CAAC,YAAY,IAAI,aAAa,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oCACxE,KAAgB,UAA0B,EAA1B,KAAA,aAAa,CAAC,YAAY,EAA1B,cAA0B,EAA1B,IAA0B,EAAE,CAAC;wCAAxC,IAAM,CAAC,SAAA;wCACV,KAAI,CAAC,QAAQ,CAAC,qDAA8C,CAAC,CAAC,IAAI,sBAAY,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAE,CAAC,CAAC;oCAC5G,CAAC;gCACH,CAAC;gCAEK,IAAA,KAAkC,KAAI,CAAC,OAAO,EAA5C,GAAG,SAAA,EAAE,cAAc,oBAAA,EAAE,MAAM,YAAiB,CAAC;gCAErD,IAAM,KAAuC,cAAc,aAAd,cAAc,cAAd,cAAc,GAAI,EAAE,EAAzD,OAAO,aAAA,EAAK,oBAAoB,cAAlC,WAAoC,CAAuB,CAAC;gCAElE,2BAA2B;gCAC3B,KAAI,CAAC,QAAQ,CAAC,kCAA2B,KAAK,CAAC,MAAM,uBAAa,GAAG,CAAE,CAAC,CAAC;gCAEzE,wCAAwC;gCACxC,IAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;gCACzC,IAAM,SAAS,GAAG,UAAU,CAAC;oCAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oCACnB,KAAI,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAAC;gCAC/D,CAAC,EAAE,KAAK,CAAC,CAAC;gCAEV,OAAO,KAAK,CAAC,GAAG,aACd,MAAM,EAAE,MAAM,EACd,OAAO,+BACL,cAAc,EAAE,kBAAkB,IAC/B,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,EAAE,CAAC,GACf,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GACvC,CAAC,WAAS,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,WAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAE1D,IAAI,MAAA,EACJ,MAAM,EAAE,UAAU,CAAC,MAAM,IAGtB,CAAC,oBAAoB,aAApB,oBAAoB,cAApB,oBAAoB,GAAI,EAAE,CAAC,EAC/B;qCACC,IAAI,CAAC,UAAO,QAAQ;;;;;gDACnB,YAAY,CAAC,SAAS,CAAC,CAAC;gDACxB,6BAA6B;gDAC7B,IAAI,CAAC,QAAQ,CAAC,0CAAmC,QAAQ,CAAC,MAAM,CAAE,CAAC,CAAC;gDAEpE,mCAAmC;gDACnC,IAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC;gDAE7B,IAAI,QAAQ,CAAC,MAAM,KAAK,iBAAiB,EAAE,CAAC;oDAC1C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;oDACtD,IAAI,CAAC,QAAQ,CAAC,uDAAgD,IAAI,CAAC,aAAa,CAAE,CAAC,CAAC;gDACtF,CAAC;qDAGG,CAAA,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAA,EAA/C,wBAA+C;gDACpC,qBAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,cAAM,OAAA,EAAE,EAAF,CAAE,CAAC,EAAA;;gDAA5C,IAAI,GAAG,SAAqC;gDAClD,IAAI,CAAC,QAAQ,CAAC,gDAAyC,QAAQ,CAAC,MAAM,cAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAE,CAAC,CAAC;;oDAGlG,sBAAO,QAAQ,EAAC;;;qCACjB,CAAC;qCACD,KAAK,CAAC,UAAC,KAAK;oCACX,YAAY,CAAC,SAAS,CAAC,CAAC;oCACxB,uBAAuB;oCACvB,KAAI,CAAC,QAAQ,CAAC,yCAAkC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,OAAO,KAAI,KAAK,CAAE,CAAC,CAAC;oCAE3E,4BAA4B;oCAC5B,KAAI,CAAC,mBAAmB,EAAE,CAAC;oCAE3B,mFAAmF;oCACnF,IAAI,KAAI,CAAC,mBAAmB,IAAI,wBAAwB,EAAE,CAAC;wCACzD,KAAI,CAAC,aAAa,GAAG,IAAI,IAAI,CAAC,KAAI,CAAC,MAAM,EAAE,GAAG,kBAAkB,CAAC,CAAC;wCAClE,kDAAkD;wCAClD,KAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC;wCAC7B,KAAI,CAAC,QAAQ,CAAC,6DAA6D,CAAC,CAAC;oCAC/E,CAAC;oCAED,4EAA4E;oCAC5E,4EAA4E;oCAC5E,OAAO,SAAS,CAAC;gCACnB,CAAC,CAAC,CAAC;4BACP,CAAC,CAAC,EAAA;;wBAhFI,QAAQ,GAAG,SAgFf;wBAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;4BACd,sBAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAC;wBAC/B,CAAC;wBACD,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;4BACpD,sBAAO;oCACL,OAAO,EAAE,UAAU;oCACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;iCACxB,EAAC;wBACJ,CAAC;wBACD,sBAAO;gCACL,OAAO,EAAE,UAAU;gCACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;6BACxB,EAAC;;;wBAEF,yEAAyE;wBACzE,uEAAuE;wBACvE,sBAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAC;;;;;KAEhC;IAEQ,sCAAa,GAAtB;;QACE,OAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAc,CAAC,MAAM,CAAC,MAAA,IAAI,CAAC,MAAM,CAAC,UAAU,mCAAI,EAAE,CAAC,CAAC;IAC/E,CAAC;IAEQ,kCAAS,GAAlB;QACE,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,0CAAiB,GAAzB,UAA0B,QAAkB;QAC1C,IAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,IAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAE7D,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;YAEvC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClB,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC;YACtC,CAAC;YAED,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;YAE1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjB,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACjD,CAAC;IACH,qBAAC;AAAD,CAAC,AAnQD,CAAoC,yBAAa,GAmQhD;AAnQY,wCAAc","sourcesContent":["import { BaseTransport, createPromiseBuffer, getTransportBody, VERSION } from '@grafana/faro-core';\nimport type { Patterns, PromiseBuffer, TransportItem } from '@grafana/faro-core';\n\nimport type { FetchTransportOptions, FetchTransportSendResult } from './types';\n\nconst DEFAULT_BUFFER_SIZE = 30;\nconst DEFAULT_CONCURRENCY = 5;\nconst DEFAULT_RATE_LIMIT_BACKOFF_MS = 5000;\nconst MAX_CONSECUTIVE_FAILURES = 3;\nconst FAILURE_BACKOFF_MS = 30000; // 30 seconds\n\nconst TOO_MANY_REQUESTS = 429;\nexport class FetchTransport extends BaseTransport {\n readonly name = '@grafana/faro-react-native:transport-fetch';\n readonly version = VERSION;\n\n promiseBuffer: PromiseBuffer<Response | void>;\n\n private readonly rateLimitBackoffMs: number;\n private readonly getNow: () => number;\n private disabledUntil: Date;\n private consecutiveFailures: number = 0;\n private sessionReadyPromise: Promise<void> | null = null;\n private sessionReadyResolve: (() => void) | null = null;\n private sessionReady: boolean = false;\n private metasListenerRegistered: boolean = false;\n\n constructor(private options: FetchTransportOptions) {\n super();\n\n this.rateLimitBackoffMs = options.defaultRateLimitBackoffMs ?? DEFAULT_RATE_LIMIT_BACKOFF_MS;\n this.getNow = options.getNow ?? (() => Date.now());\n // Align with getNow so tests (and apps) that supply a clock do not see spurious backoff\n this.disabledUntil = new Date(this.getNow());\n\n this.promiseBuffer = createPromiseBuffer({\n size: options.bufferSize ?? DEFAULT_BUFFER_SIZE,\n concurrency: options.concurrency ?? DEFAULT_CONCURRENCY,\n });\n }\n\n /**\n * Register a listener for metas changes to detect when session becomes available.\n * Uses faro-core's metas listener pattern instead of polling with setTimeout.\n */\n private registerSessionListener(): void {\n if (this.metasListenerRegistered || !this.metas?.addListener) {\n return;\n }\n\n this.metasListenerRegistered = true;\n\n this.metas.addListener((meta) => {\n if (meta.session?.id && this.sessionReadyResolve) {\n this.sessionReady = true;\n this.sessionReadyResolve();\n this.sessionReadyResolve = null;\n }\n });\n }\n\n /**\n * Wait for session to be available before sending.\n * This prevents 400 errors from the collector due to missing X-Faro-Session-Id header.\n *\n * Only waits if session tracking is enabled. If disabled, returns immediately.\n */\n private waitForSession(): Promise<void> {\n // Only wait for session if session tracking is enabled\n const sessionTrackingEnabled = this.config?.sessionTracking?.enabled;\n if (!sessionTrackingEnabled) {\n return Promise.resolve();\n }\n\n // Already have session\n if (this.sessionReady) {\n return Promise.resolve();\n }\n\n // Check if session is now available\n const sessionMeta = this.metas?.value?.session;\n if (sessionMeta?.id) {\n this.sessionReady = true;\n return Promise.resolve();\n }\n\n // Return existing promise if we're already waiting\n if (this.sessionReadyPromise) {\n return this.sessionReadyPromise;\n }\n\n // Register listener to be notified when session becomes available\n this.registerSessionListener();\n\n // Create a promise that resolves when session becomes available via listener\n this.sessionReadyPromise = new Promise<void>((resolve) => {\n this.sessionReadyResolve = resolve;\n\n // Also check immediately in case session was set between our check and listener registration\n const sessionMeta = this.metas?.value?.session;\n if (sessionMeta?.id) {\n this.sessionReady = true;\n resolve();\n this.sessionReadyResolve = null;\n }\n });\n\n return this.sessionReadyPromise;\n }\n\n async send(items: TransportItem[]): Promise<void> {\n await this.sendWithResult(items);\n }\n\n /**\n * Sends a batch and reports whether the collector accepted it.\n * The request header uses the live session for collector accounting. Payload\n * metadata remains independent so delayed signals can retain their original\n * session.\n */\n async sendWithResult(items: TransportItem[]): Promise<FetchTransportSendResult> {\n this.logDebug(`FetchTransport.sendWithResult() called with ${items.length} items`);\n\n if (items.length === 0) {\n return { outcome: 'skipped' };\n }\n\n try {\n const now = new Date(this.getNow());\n\n // Check if we're in backoff period\n if (this.disabledUntil > now) {\n this.logDebug(`FetchTransport: in backoff period until ${this.disabledUntil}`);\n return { outcome: 'skipped' };\n }\n\n // The collector uses the header for session validation and accounting,\n // independently of the session carried by each payload.\n await this.waitForSession();\n const sessionId = this.metas.value.session?.id;\n\n const response = await this.promiseBuffer.add(() => {\n const transportBody = getTransportBody(items);\n const body = JSON.stringify(transportBody);\n\n // DEBUG: Log measurement payloads to see what's being sent\n if (transportBody.measurements && transportBody.measurements.length > 0) {\n for (const m of transportBody.measurements) {\n this.logDebug(`FetchTransport: measurement payload - type=${m.type}, values=${JSON.stringify(m.values)}`);\n }\n }\n\n const { url, requestOptions, apiKey } = this.options;\n\n const { headers, ...restOfRequestOptions } = requestOptions ?? {};\n\n // DEBUG: Log fetch attempt\n this.logDebug(`FetchTransport: sending ${items.length} items to ${url}`);\n\n // Create an AbortController for timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n this.logDebug(`FetchTransport: request timed out after 10s`);\n }, 10000);\n\n return fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(headers ?? {}),\n ...(apiKey ? { 'x-api-key': apiKey } : {}),\n ...(sessionId ? { 'x-faro-session-id': sessionId } : {}),\n },\n body,\n signal: controller.signal,\n // Note: React Native doesn't support keepalive\n // keepalive: body.length <= BEACON_BODY_SIZE_LIMIT,\n ...(restOfRequestOptions ?? {}),\n })\n .then(async (response) => {\n clearTimeout(timeoutId);\n // DEBUG: Log response status\n this.logDebug(`FetchTransport: response status ${response.status}`);\n\n // Reset failure counter on success\n this.consecutiveFailures = 0;\n\n if (response.status === TOO_MANY_REQUESTS) {\n this.disabledUntil = this.getRetryAfterDate(response);\n this.logDebug(`FetchTransport: rate limited, disabled until ${this.disabledUntil}`);\n }\n\n // Log non-success responses for debugging\n if (response.status < 200 || response.status >= 300) {\n const text = await response.text().catch(() => '');\n this.logDebug(`FetchTransport: non-success response: ${response.status} ${text.slice(0, 200)}`);\n }\n\n return response;\n })\n .catch((error) => {\n clearTimeout(timeoutId);\n // DEBUG: Log the error\n this.logDebug(`FetchTransport: fetch failed - ${error?.message || error}`);\n\n // Increment failure counter\n this.consecutiveFailures++;\n\n // After MAX_CONSECUTIVE_FAILURES, enable circuit breaker to prevent infinite loops\n if (this.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {\n this.disabledUntil = new Date(this.getNow() + FAILURE_BACKOFF_MS);\n // Reset counter so we can try again after backoff\n this.consecutiveFailures = 0;\n this.logDebug(`FetchTransport: circuit breaker activated, disabled for 30s`);\n }\n\n // Do NOT log errors to console - this causes infinite loops in React Native\n // when the DevTools console override intercepts even unpatchedConsole calls\n return undefined;\n });\n });\n\n if (!response) {\n return { outcome: 'failed' };\n }\n if (response.status >= 200 && response.status < 300) {\n return {\n outcome: 'accepted',\n status: response.status,\n };\n }\n return {\n outcome: 'rejected',\n status: response.status,\n };\n } catch {\n // Buffer full error - Do NOT log to console as it creates infinite loops\n // The error is typically \"Task buffer full\" when the device is offline\n return { outcome: 'failed' };\n }\n }\n\n override getIgnoreUrls(): Patterns {\n return ([this.options.url] as Patterns).concat(this.config.ignoreUrls ?? []);\n }\n\n override isBatched(): boolean {\n return true;\n }\n\n private getRetryAfterDate(response: Response): Date {\n const now = this.getNow();\n const retryAfterHeader = response.headers.get('Retry-After');\n\n if (retryAfterHeader) {\n const delay = Number(retryAfterHeader);\n\n if (!isNaN(delay)) {\n return new Date(delay * 1000 + now);\n }\n\n const date = Date.parse(retryAfterHeader);\n\n if (!isNaN(date)) {\n return new Date(date);\n }\n }\n\n return new Date(now + this.rateLimitBackoffMs);\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/transports/fetch/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface FetchTransportRequestOptions extends Omit<RequestInit, 'body' | 'headers'> {\n headers?: Record<string, string>;\n}\n\nexport interface FetchTransportOptions {\n // url of the collector endpoint\n url: string;\n\n // will be added as `x-api-key` header\n apiKey?: string;\n // how many requests to buffer in total\n bufferSize?: number;\n // how many requests to execute concurrently\n concurrency?: number;\n // if rate limit response does not include a Retry-After header,\n // how many milliseconds to back off before attempting a request.\n // intermediate events will be dropped, not buffered\n defaultRateLimitBackoffMs?: number;\n // get current date. for mocking purposes in tests\n getNow?: ClockFn;\n // addition options for global.Fetch\n requestOptions?: FetchTransportRequestOptions;\n}\n\nexport type ClockFn = () => number;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/transports/fetch/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface FetchTransportRequestOptions extends Omit<RequestInit, 'body' | 'headers'> {\n headers?: Record<string, string>;\n}\n\nexport interface FetchTransportOptions {\n // url of the collector endpoint\n url: string;\n\n // will be added as `x-api-key` header\n apiKey?: string;\n // how many requests to buffer in total\n bufferSize?: number;\n // how many requests to execute concurrently\n concurrency?: number;\n // if rate limit response does not include a Retry-After header,\n // how many milliseconds to back off before attempting a request.\n // intermediate events will be dropped, not buffered\n defaultRateLimitBackoffMs?: number;\n // get current date. for mocking purposes in tests\n getNow?: ClockFn;\n // addition options for global.Fetch\n requestOptions?: FetchTransportRequestOptions;\n}\n\nexport type ClockFn = () => number;\n\nexport type FetchTransportSendOutcome = 'accepted' | 'rejected' | 'failed' | 'skipped';\n\nexport interface FetchTransportSendResult {\n outcome: FetchTransportSendOutcome;\n status?: number;\n}\n"]}
@@ -55,7 +55,7 @@ var DEFAULT_CHECK_INTERVAL_MS = 30000;
55
55
  * Uses @react-native-community/netinfo when available, otherwise falls back
56
56
  * to basic fetch-based connectivity detection.
57
57
  *
58
- * Implementation follows Flutter SDK's InternetConnectivityService pattern.
58
+ * Monitors network connectivity for offline transport flush behavior.
59
59
  */
60
60
  var DefaultConnectivityService = /** @class */ (function () {
61
61
  function DefaultConnectivityService(checkIntervalMs) {
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectivityService.js","sourceRoot":"","sources":["../../../../src/transports/offline/ConnectivityService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConnectivityService } from './types';\n\n// Try to use NetInfo if available, otherwise fall back to basic online detection\nlet NetInfo: {\n fetch: () => Promise<{ isConnected: boolean | null }>;\n addEventListener: (callback: (state: { isConnected: boolean | null }) => void) => () => void;\n} | null = null;\n\ntry {\n NetInfo = require('@react-native-community/netinfo').default;\n} catch {\n // NetInfo not available, will fall back to basic detection\n}\n\n/**\n * Default connectivity check interval in milliseconds.\n */\nconst DEFAULT_CHECK_INTERVAL_MS = 30000;\n\n/**\n * Service for detecting network connectivity changes.\n *\n * Uses @react-native-community/netinfo when available, otherwise falls back\n * to basic fetch-based connectivity detection.\n *\n * Implementation follows Flutter SDK's InternetConnectivityService pattern.\n */\nexport class DefaultConnectivityService implements ConnectivityService {\n private _isOnline: boolean = true;\n private readonly subscribers: Set<(isOnline: boolean) => void> = new Set();\n private unsubscribeNetInfo: (() => void) | null = null;\n private checkIntervalId: ReturnType<typeof setInterval> | null = null;\n private readonly checkIntervalMs: number;\n\n constructor(checkIntervalMs: number = DEFAULT_CHECK_INTERVAL_MS) {\n this.checkIntervalMs = checkIntervalMs;\n this.initialize();\n }\n\n get isOnline(): boolean {\n return this._isOnline;\n }\n\n subscribe(callback: (isOnline: boolean) => void): () => void {\n this.subscribers.add(callback);\n // Immediately notify subscriber of current state\n callback(this._isOnline);\n\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n dispose(): void {\n if (this.unsubscribeNetInfo) {\n this.unsubscribeNetInfo();\n this.unsubscribeNetInfo = null;\n }\n if (this.checkIntervalId) {\n clearInterval(this.checkIntervalId);\n this.checkIntervalId = null;\n }\n this.subscribers.clear();\n }\n\n private initialize(): void {\n if (NetInfo) {\n this.initializeWithNetInfo();\n } else {\n this.initializeWithPolling();\n }\n }\n\n private initializeWithNetInfo(): void {\n if (!NetInfo) return;\n\n // Initial check\n NetInfo.fetch().then((state) => {\n this.setOnline(state.isConnected ?? true);\n });\n\n // Subscribe to changes\n this.unsubscribeNetInfo = NetInfo.addEventListener((state) => {\n this.setOnline(state.isConnected ?? true);\n });\n }\n\n private initializeWithPolling(): void {\n // Initial check\n this.checkConnectivity();\n\n // Poll periodically\n this.checkIntervalId = setInterval(() => {\n this.checkConnectivity();\n }, this.checkIntervalMs);\n }\n\n private async checkConnectivity(): Promise<void> {\n try {\n // Simple fetch-based connectivity check\n // We use a small, fast endpoint\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n await fetch('https://one.one.one.one/cdn-cgi/trace', {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n this.setOnline(true);\n } catch {\n this.setOnline(false);\n }\n }\n\n private setOnline(isOnline: boolean): void {\n if (this._isOnline !== isOnline) {\n this._isOnline = isOnline;\n this.notifySubscribers();\n }\n }\n\n private notifySubscribers(): void {\n this.subscribers.forEach((subscriber) => {\n try {\n subscriber(this._isOnline);\n } catch {\n // Ignore subscriber errors\n }\n });\n }\n}\n"]}
1
+ {"version":3,"file":"ConnectivityService.js","sourceRoot":"","sources":["../../../../src/transports/offline/ConnectivityService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConnectivityService } from './types';\n\n// Try to use NetInfo if available, otherwise fall back to basic online detection\nlet NetInfo: {\n fetch: () => Promise<{ isConnected: boolean | null }>;\n addEventListener: (callback: (state: { isConnected: boolean | null }) => void) => () => void;\n} | null = null;\n\ntry {\n NetInfo = require('@react-native-community/netinfo').default;\n} catch {\n // NetInfo not available, will fall back to basic detection\n}\n\n/**\n * Default connectivity check interval in milliseconds.\n */\nconst DEFAULT_CHECK_INTERVAL_MS = 30000;\n\n/**\n * Service for detecting network connectivity changes.\n *\n * Uses @react-native-community/netinfo when available, otherwise falls back\n * to basic fetch-based connectivity detection.\n *\n * Monitors network connectivity for offline transport flush behavior.\n */\nexport class DefaultConnectivityService implements ConnectivityService {\n private _isOnline: boolean = true;\n private readonly subscribers: Set<(isOnline: boolean) => void> = new Set();\n private unsubscribeNetInfo: (() => void) | null = null;\n private checkIntervalId: ReturnType<typeof setInterval> | null = null;\n private readonly checkIntervalMs: number;\n\n constructor(checkIntervalMs: number = DEFAULT_CHECK_INTERVAL_MS) {\n this.checkIntervalMs = checkIntervalMs;\n this.initialize();\n }\n\n get isOnline(): boolean {\n return this._isOnline;\n }\n\n subscribe(callback: (isOnline: boolean) => void): () => void {\n this.subscribers.add(callback);\n // Immediately notify subscriber of current state\n callback(this._isOnline);\n\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n dispose(): void {\n if (this.unsubscribeNetInfo) {\n this.unsubscribeNetInfo();\n this.unsubscribeNetInfo = null;\n }\n if (this.checkIntervalId) {\n clearInterval(this.checkIntervalId);\n this.checkIntervalId = null;\n }\n this.subscribers.clear();\n }\n\n private initialize(): void {\n if (NetInfo) {\n this.initializeWithNetInfo();\n } else {\n this.initializeWithPolling();\n }\n }\n\n private initializeWithNetInfo(): void {\n if (!NetInfo) return;\n\n // Initial check\n NetInfo.fetch().then((state) => {\n this.setOnline(state.isConnected ?? true);\n });\n\n // Subscribe to changes\n this.unsubscribeNetInfo = NetInfo.addEventListener((state) => {\n this.setOnline(state.isConnected ?? true);\n });\n }\n\n private initializeWithPolling(): void {\n // Initial check\n this.checkConnectivity();\n\n // Poll periodically\n this.checkIntervalId = setInterval(() => {\n this.checkConnectivity();\n }, this.checkIntervalMs);\n }\n\n private async checkConnectivity(): Promise<void> {\n try {\n // Simple fetch-based connectivity check\n // We use a small, fast endpoint\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n await fetch('https://one.one.one.one/cdn-cgi/trace', {\n method: 'HEAD',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n this.setOnline(true);\n } catch {\n this.setOnline(false);\n }\n }\n\n private setOnline(isOnline: boolean): void {\n if (this._isOnline !== isOnline) {\n this._isOnline = isOnline;\n this.notifySubscribers();\n }\n }\n\n private notifySubscribers(): void {\n this.subscribers.forEach((subscriber) => {\n try {\n subscriber(this._isOnline);\n } catch {\n // Ignore subscriber errors\n }\n });\n }\n}\n"]}
@@ -53,7 +53,7 @@ var DEFAULT_MAX_CACHE_SIZE = 1000;
53
53
  * File-based offline cache implementation using AsyncStorage.
54
54
  *
55
55
  * Stores cached telemetry payloads as JSONL (one JSON object per line)
56
- * following the Flutter SDK's offline transport pattern.
56
+ * Persistent offline telemetry cache backed by AsyncStorage.
57
57
  *
58
58
  * Uses a mutex pattern for thread-safe file access.
59
59
  */
@@ -178,7 +178,7 @@ var AsyncStorageOfflineCache = /** @class */ (function () {
178
178
  };
179
179
  /**
180
180
  * Execute an operation with a mutex lock to ensure thread-safe access.
181
- * Follows Flutter SDK's Completer-based lock pattern.
181
+ * Async lock to serialize cache read/write operations.
182
182
  */
183
183
  AsyncStorageOfflineCache.prototype.withLock = function (operation) {
184
184
  return __awaiter(this, void 0, void 0, function () {
@@ -1 +1 @@
1
- {"version":3,"file":"OfflineCache.js","sourceRoot":"","sources":["../../../../src/transports/offline/OfflineCache.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\n\nimport type { CachedPayload, OfflineCache } from './types';\n\n/**\n * Default storage key prefix for the offline cache.\n */\nconst DEFAULT_STORAGE_KEY_PREFIX = 'faro_offline_cache';\n\n/**\n * Default maximum number of cached payloads.\n */\nconst DEFAULT_MAX_CACHE_SIZE = 1000;\n\n/**\n * File-based offline cache implementation using AsyncStorage.\n *\n * Stores cached telemetry payloads as JSONL (one JSON object per line)\n * following the Flutter SDK's offline transport pattern.\n *\n * Uses a mutex pattern for thread-safe file access.\n */\nexport class AsyncStorageOfflineCache implements OfflineCache {\n private readonly storageKey: string;\n private readonly maxCacheSize: number;\n private lockPromise: Promise<void> = Promise.resolve();\n\n constructor(options: { storageKeyPrefix?: string; maxCacheSize?: number } = {}) {\n this.storageKey = `${options.storageKeyPrefix ?? DEFAULT_STORAGE_KEY_PREFIX}_data`;\n this.maxCacheSize = options.maxCacheSize ?? DEFAULT_MAX_CACHE_SIZE;\n }\n\n async write(payload: CachedPayload): Promise<void> {\n await this.withLock(async () => {\n const payloads = await this.readAllInternal();\n\n // Add new payload\n payloads.push(payload);\n\n // Trim to max size (remove oldest entries)\n while (payloads.length > this.maxCacheSize) {\n payloads.shift();\n }\n\n await this.writeAll(payloads);\n });\n }\n\n async readAll(): Promise<CachedPayload[]> {\n return this.withLock(async () => {\n return this.readAllInternal();\n });\n }\n\n async clear(): Promise<void> {\n await this.withLock(async () => {\n await AsyncStorage.removeItem(this.storageKey);\n });\n }\n\n async removeByTimestamps(timestamps: number[]): Promise<void> {\n if (timestamps.length === 0) return;\n\n const timestampSet = new Set(timestamps);\n\n await this.withLock(async () => {\n const payloads = await this.readAllInternal();\n const remaining = payloads.filter((p) => !timestampSet.has(p.timestamp));\n await this.writeAll(remaining);\n });\n }\n\n async getCount(): Promise<number> {\n const payloads = await this.readAllInternal();\n return payloads.length;\n }\n\n /**\n * Execute an operation with a mutex lock to ensure thread-safe access.\n * Follows Flutter SDK's Completer-based lock pattern.\n */\n private async withLock<T>(operation: () => Promise<T>): Promise<T> {\n const currentLock = this.lockPromise;\n let resolveLock: () => void;\n this.lockPromise = new Promise<void>((resolve) => {\n resolveLock = resolve;\n });\n\n try {\n await currentLock;\n return await operation();\n } finally {\n resolveLock!();\n }\n }\n\n private async readAllInternal(): Promise<CachedPayload[]> {\n try {\n const data = await AsyncStorage.getItem(this.storageKey);\n if (!data) {\n return [];\n }\n\n const parsed = JSON.parse(data);\n if (!Array.isArray(parsed)) {\n return [];\n }\n\n return parsed.filter(this.isValidCachedPayload);\n } catch {\n // If parsing fails, return empty array\n return [];\n }\n }\n\n private async writeAll(payloads: CachedPayload[]): Promise<void> {\n if (payloads.length === 0) {\n await AsyncStorage.removeItem(this.storageKey);\n } else {\n await AsyncStorage.setItem(this.storageKey, JSON.stringify(payloads));\n }\n }\n\n private isValidCachedPayload(payload: unknown): payload is CachedPayload {\n if (typeof payload !== 'object' || payload === null) {\n return false;\n }\n\n const p = payload as Record<string, unknown>;\n return typeof p['timestamp'] === 'number' && Array.isArray(p['items']);\n }\n}\n"]}
1
+ {"version":3,"file":"OfflineCache.js","sourceRoot":"","sources":["../../../../src/transports/offline/OfflineCache.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\n\nimport type { CachedPayload, OfflineCache } from './types';\n\n/**\n * Default storage key prefix for the offline cache.\n */\nconst DEFAULT_STORAGE_KEY_PREFIX = 'faro_offline_cache';\n\n/**\n * Default maximum number of cached payloads.\n */\nconst DEFAULT_MAX_CACHE_SIZE = 1000;\n\n/**\n * File-based offline cache implementation using AsyncStorage.\n *\n * Stores cached telemetry payloads as JSONL (one JSON object per line)\n * Persistent offline telemetry cache backed by AsyncStorage.\n *\n * Uses a mutex pattern for thread-safe file access.\n */\nexport class AsyncStorageOfflineCache implements OfflineCache {\n private readonly storageKey: string;\n private readonly maxCacheSize: number;\n private lockPromise: Promise<void> = Promise.resolve();\n\n constructor(options: { storageKeyPrefix?: string; maxCacheSize?: number } = {}) {\n this.storageKey = `${options.storageKeyPrefix ?? DEFAULT_STORAGE_KEY_PREFIX}_data`;\n this.maxCacheSize = options.maxCacheSize ?? DEFAULT_MAX_CACHE_SIZE;\n }\n\n async write(payload: CachedPayload): Promise<void> {\n await this.withLock(async () => {\n const payloads = await this.readAllInternal();\n\n // Add new payload\n payloads.push(payload);\n\n // Trim to max size (remove oldest entries)\n while (payloads.length > this.maxCacheSize) {\n payloads.shift();\n }\n\n await this.writeAll(payloads);\n });\n }\n\n async readAll(): Promise<CachedPayload[]> {\n return this.withLock(async () => {\n return this.readAllInternal();\n });\n }\n\n async clear(): Promise<void> {\n await this.withLock(async () => {\n await AsyncStorage.removeItem(this.storageKey);\n });\n }\n\n async removeByTimestamps(timestamps: number[]): Promise<void> {\n if (timestamps.length === 0) return;\n\n const timestampSet = new Set(timestamps);\n\n await this.withLock(async () => {\n const payloads = await this.readAllInternal();\n const remaining = payloads.filter((p) => !timestampSet.has(p.timestamp));\n await this.writeAll(remaining);\n });\n }\n\n async getCount(): Promise<number> {\n const payloads = await this.readAllInternal();\n return payloads.length;\n }\n\n /**\n * Execute an operation with a mutex lock to ensure thread-safe access.\n * Async lock to serialize cache read/write operations.\n */\n private async withLock<T>(operation: () => Promise<T>): Promise<T> {\n const currentLock = this.lockPromise;\n let resolveLock: () => void;\n this.lockPromise = new Promise<void>((resolve) => {\n resolveLock = resolve;\n });\n\n try {\n await currentLock;\n return await operation();\n } finally {\n resolveLock!();\n }\n }\n\n private async readAllInternal(): Promise<CachedPayload[]> {\n try {\n const data = await AsyncStorage.getItem(this.storageKey);\n if (!data) {\n return [];\n }\n\n const parsed = JSON.parse(data);\n if (!Array.isArray(parsed)) {\n return [];\n }\n\n return parsed.filter(this.isValidCachedPayload);\n } catch {\n // If parsing fails, return empty array\n return [];\n }\n }\n\n private async writeAll(payloads: CachedPayload[]): Promise<void> {\n if (payloads.length === 0) {\n await AsyncStorage.removeItem(this.storageKey);\n } else {\n await AsyncStorage.setItem(this.storageKey, JSON.stringify(payloads));\n }\n }\n\n private isValidCachedPayload(payload: unknown): payload is CachedPayload {\n if (typeof payload !== 'object' || payload === null) {\n return false;\n }\n\n const p = payload as Record<string, unknown>;\n return typeof p['timestamp'] === 'number' && Array.isArray(p['items']);\n }\n}\n"]}
@@ -66,8 +66,7 @@ var DEFAULT_MAX_CACHE_DURATION_MS = 3 * 24 * 60 * 60 * 1000;
66
66
  * When the device is offline, telemetry is cached to AsyncStorage.
67
67
  * When connectivity is restored, cached telemetry is replayed through the wrapped transports.
68
68
  *
69
- * Implementation follows Flutter SDK's OfflineTransport pattern:
70
- * - Uses AsyncStorage for persistent caching (matching Flutter's SharedPreferences)
69
+ * Uses AsyncStorage for persistent offline telemetry caching.
71
70
  * - Respects maxCacheDuration to skip expired entries
72
71
  * - Uses mutex pattern for thread-safe cache access
73
72
  * - Excludes itself when replaying to prevent infinite loops
@@ -225,7 +224,7 @@ var OfflineTransport = /** @class */ (function (_super) {
225
224
  };
226
225
  /**
227
226
  * Replay cached payloads through other transports.
228
- * Follows Flutter SDK's _readFromFile pattern.
227
+ * Reads cached telemetry from AsyncStorage.
229
228
  */
230
229
  OfflineTransport.prototype.replayCachedPayloads = function () {
231
230
  return __awaiter(this, void 0, void 0, function () {
@@ -292,7 +291,7 @@ var OfflineTransport = /** @class */ (function (_super) {
292
291
  };
293
292
  /**
294
293
  * Send items to all other registered transports.
295
- * Follows Flutter SDK's _sendCachedData pattern.
294
+ * Sends cached telemetry when connectivity is restored.
296
295
  */
297
296
  OfflineTransport.prototype.sendToOtherTransports = function (items) {
298
297
  return __awaiter(this, void 0, void 0, function () {
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/transports/offline/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseTransport, VERSION } from '@grafana/faro-core';\nimport type { Patterns, Transport, TransportItem } from '@grafana/faro-core';\n\nimport { DefaultConnectivityService } from './ConnectivityService';\nimport { AsyncStorageOfflineCache } from './OfflineCache';\nimport type { CachedPayload, ConnectivityService, OfflineCache, OfflineTransportOptions } from './types';\n\n/**\n * Default maximum cache duration: 3 days in milliseconds.\n */\nconst DEFAULT_MAX_CACHE_DURATION_MS = 3 * 24 * 60 * 60 * 1000;\n\n/**\n * OfflineTransport - Caches telemetry when offline and replays when online.\n *\n * This transport wraps other transports and provides offline caching functionality.\n * When the device is offline, telemetry is cached to AsyncStorage.\n * When connectivity is restored, cached telemetry is replayed through the wrapped transports.\n *\n * Implementation follows Flutter SDK's OfflineTransport pattern:\n * - Uses AsyncStorage for persistent caching (matching Flutter's SharedPreferences)\n * - Respects maxCacheDuration to skip expired entries\n * - Uses mutex pattern for thread-safe cache access\n * - Excludes itself when replaying to prevent infinite loops\n *\n * @example\n * ```typescript\n * import { initializeFaro, FetchTransport, OfflineTransport } from '@grafana/faro-react-native';\n *\n * initializeFaro({\n * url: 'https://collector.example.com',\n * transports: [\n * new OfflineTransport({\n * maxCacheDurationMs: 3 * 24 * 60 * 60 * 1000, // 3 days\n * }),\n * new FetchTransport({ url: 'https://collector.example.com' }),\n * ],\n * });\n * ```\n */\nexport class OfflineTransport extends BaseTransport {\n readonly name = '@grafana/faro-react-native:transport-offline';\n readonly version = VERSION;\n\n private readonly cache: OfflineCache;\n private readonly connectivityService: ConnectivityService;\n private readonly maxCacheDurationMs: number;\n private readonly otherTransports: Transport[] = [];\n private unsubscribeConnectivity: (() => void) | null = null;\n private isReplaying: boolean = false;\n\n constructor(options: OfflineTransportOptions = {}) {\n super();\n\n this.maxCacheDurationMs = options.maxCacheDurationMs ?? DEFAULT_MAX_CACHE_DURATION_MS;\n\n this.cache = new AsyncStorageOfflineCache({\n storageKeyPrefix: options.storageKeyPrefix,\n maxCacheSize: options.maxCacheSize,\n });\n\n this.connectivityService = new DefaultConnectivityService(options.connectivityCheckIntervalMs);\n\n // Subscribe to connectivity changes\n this.unsubscribeConnectivity = this.connectivityService.subscribe((isOnline) => {\n if (isOnline && !this.isReplaying) {\n this.replayCachedPayloads();\n }\n });\n }\n\n /**\n * Send telemetry items.\n *\n * When offline, items are cached for later replay.\n * When online, this transport does nothing (other transports handle sending).\n */\n async send(items: TransportItem | TransportItem[]): Promise<void> {\n const itemsArray = Array.isArray(items) ? items : [items];\n\n if (itemsArray.length === 0) {\n return;\n }\n\n if (!this.connectivityService.isOnline) {\n // Cache items when offline\n const payload: CachedPayload = {\n timestamp: Date.now(),\n items: itemsArray,\n };\n\n try {\n await this.cache.write(payload);\n this.logDebug(`Cached ${itemsArray.length} items for offline replay`);\n } catch (error) {\n this.logError('Failed to cache offline payload', error);\n }\n }\n // When online, do nothing - other transports handle sending\n }\n\n override getIgnoreUrls(): Patterns {\n return [];\n }\n\n override isBatched(): boolean {\n return true;\n }\n\n /**\n * Register other transports for replay.\n * Called by the transport system after initialization.\n */\n setOtherTransports(transports: Transport[]): void {\n this.otherTransports.length = 0;\n for (const transport of transports) {\n if (transport !== this) {\n this.otherTransports.push(transport);\n }\n }\n }\n\n /**\n * Get the current number of cached payloads.\n */\n async getCachedCount(): Promise<number> {\n return this.cache.getCount();\n }\n\n /**\n * Manually trigger replay of cached payloads.\n * Useful for testing or when you want to force a replay attempt.\n */\n async forceReplay(): Promise<void> {\n if (this.connectivityService.isOnline) {\n await this.replayCachedPayloads();\n }\n }\n\n /**\n * Clear all cached payloads.\n */\n async clearCache(): Promise<void> {\n await this.cache.clear();\n }\n\n /**\n * Clean up resources.\n */\n dispose(): void {\n if (this.unsubscribeConnectivity) {\n this.unsubscribeConnectivity();\n this.unsubscribeConnectivity = null;\n }\n this.connectivityService.dispose();\n }\n\n /**\n * Replay cached payloads through other transports.\n * Follows Flutter SDK's _readFromFile pattern.\n */\n private async replayCachedPayloads(): Promise<void> {\n if (this.isReplaying) {\n return;\n }\n\n this.isReplaying = true;\n\n try {\n const payloads = await this.cache.readAll();\n\n if (payloads.length === 0) {\n return;\n }\n\n this.logDebug(`Replaying ${payloads.length} cached payloads`);\n\n const now = Date.now();\n const successfulTimestamps: number[] = [];\n\n for (const payload of payloads) {\n // Skip expired payloads\n if (now - payload.timestamp > this.maxCacheDurationMs) {\n this.logDebug(`Skipping expired payload from ${new Date(payload.timestamp).toISOString()}`);\n successfulTimestamps.push(payload.timestamp);\n continue;\n }\n\n const success = await this.sendToOtherTransports(payload.items);\n\n if (success) {\n successfulTimestamps.push(payload.timestamp);\n }\n }\n\n // Remove successfully sent or expired payloads\n if (successfulTimestamps.length > 0) {\n await this.cache.removeByTimestamps(successfulTimestamps);\n this.logDebug(`Removed ${successfulTimestamps.length} payloads from cache`);\n }\n } catch (error) {\n this.logError('Failed to replay cached payloads', error);\n } finally {\n this.isReplaying = false;\n }\n }\n\n /**\n * Send items to all other registered transports.\n * Follows Flutter SDK's _sendCachedData pattern.\n */\n private async sendToOtherTransports(items: TransportItem[]): Promise<boolean> {\n if (this.otherTransports.length === 0) {\n this.logWarn('No other transports registered for offline replay');\n return false;\n }\n\n try {\n for (const transport of this.otherTransports) {\n await transport.send(items);\n }\n return true;\n } catch (error) {\n this.logError('Failed to send cached payload to transports', error);\n return false;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../src/transports/offline/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseTransport, VERSION } from '@grafana/faro-core';\nimport type { Patterns, Transport, TransportItem } from '@grafana/faro-core';\n\nimport { DefaultConnectivityService } from './ConnectivityService';\nimport { AsyncStorageOfflineCache } from './OfflineCache';\nimport type { CachedPayload, ConnectivityService, OfflineCache, OfflineTransportOptions } from './types';\n\n/**\n * Default maximum cache duration: 3 days in milliseconds.\n */\nconst DEFAULT_MAX_CACHE_DURATION_MS = 3 * 24 * 60 * 60 * 1000;\n\n/**\n * OfflineTransport - Caches telemetry when offline and replays when online.\n *\n * This transport wraps other transports and provides offline caching functionality.\n * When the device is offline, telemetry is cached to AsyncStorage.\n * When connectivity is restored, cached telemetry is replayed through the wrapped transports.\n *\n * Uses AsyncStorage for persistent offline telemetry caching.\n * - Respects maxCacheDuration to skip expired entries\n * - Uses mutex pattern for thread-safe cache access\n * - Excludes itself when replaying to prevent infinite loops\n *\n * @example\n * ```typescript\n * import { initializeFaro, FetchTransport, OfflineTransport } from '@grafana/faro-react-native';\n *\n * initializeFaro({\n * url: 'https://collector.example.com',\n * transports: [\n * new OfflineTransport({\n * maxCacheDurationMs: 3 * 24 * 60 * 60 * 1000, // 3 days\n * }),\n * new FetchTransport({ url: 'https://collector.example.com' }),\n * ],\n * });\n * ```\n */\nexport class OfflineTransport extends BaseTransport {\n readonly name = '@grafana/faro-react-native:transport-offline';\n readonly version = VERSION;\n\n private readonly cache: OfflineCache;\n private readonly connectivityService: ConnectivityService;\n private readonly maxCacheDurationMs: number;\n private readonly otherTransports: Transport[] = [];\n private unsubscribeConnectivity: (() => void) | null = null;\n private isReplaying: boolean = false;\n\n constructor(options: OfflineTransportOptions = {}) {\n super();\n\n this.maxCacheDurationMs = options.maxCacheDurationMs ?? DEFAULT_MAX_CACHE_DURATION_MS;\n\n this.cache = new AsyncStorageOfflineCache({\n storageKeyPrefix: options.storageKeyPrefix,\n maxCacheSize: options.maxCacheSize,\n });\n\n this.connectivityService = new DefaultConnectivityService(options.connectivityCheckIntervalMs);\n\n // Subscribe to connectivity changes\n this.unsubscribeConnectivity = this.connectivityService.subscribe((isOnline) => {\n if (isOnline && !this.isReplaying) {\n this.replayCachedPayloads();\n }\n });\n }\n\n /**\n * Send telemetry items.\n *\n * When offline, items are cached for later replay.\n * When online, this transport does nothing (other transports handle sending).\n */\n async send(items: TransportItem | TransportItem[]): Promise<void> {\n const itemsArray = Array.isArray(items) ? items : [items];\n\n if (itemsArray.length === 0) {\n return;\n }\n\n if (!this.connectivityService.isOnline) {\n // Cache items when offline\n const payload: CachedPayload = {\n timestamp: Date.now(),\n items: itemsArray,\n };\n\n try {\n await this.cache.write(payload);\n this.logDebug(`Cached ${itemsArray.length} items for offline replay`);\n } catch (error) {\n this.logError('Failed to cache offline payload', error);\n }\n }\n // When online, do nothing - other transports handle sending\n }\n\n override getIgnoreUrls(): Patterns {\n return [];\n }\n\n override isBatched(): boolean {\n return true;\n }\n\n /**\n * Register other transports for replay.\n * Called by the transport system after initialization.\n */\n setOtherTransports(transports: Transport[]): void {\n this.otherTransports.length = 0;\n for (const transport of transports) {\n if (transport !== this) {\n this.otherTransports.push(transport);\n }\n }\n }\n\n /**\n * Get the current number of cached payloads.\n */\n async getCachedCount(): Promise<number> {\n return this.cache.getCount();\n }\n\n /**\n * Manually trigger replay of cached payloads.\n * Useful for testing or when you want to force a replay attempt.\n */\n async forceReplay(): Promise<void> {\n if (this.connectivityService.isOnline) {\n await this.replayCachedPayloads();\n }\n }\n\n /**\n * Clear all cached payloads.\n */\n async clearCache(): Promise<void> {\n await this.cache.clear();\n }\n\n /**\n * Clean up resources.\n */\n dispose(): void {\n if (this.unsubscribeConnectivity) {\n this.unsubscribeConnectivity();\n this.unsubscribeConnectivity = null;\n }\n this.connectivityService.dispose();\n }\n\n /**\n * Replay cached payloads through other transports.\n * Reads cached telemetry from AsyncStorage.\n */\n private async replayCachedPayloads(): Promise<void> {\n if (this.isReplaying) {\n return;\n }\n\n this.isReplaying = true;\n\n try {\n const payloads = await this.cache.readAll();\n\n if (payloads.length === 0) {\n return;\n }\n\n this.logDebug(`Replaying ${payloads.length} cached payloads`);\n\n const now = Date.now();\n const successfulTimestamps: number[] = [];\n\n for (const payload of payloads) {\n // Skip expired payloads\n if (now - payload.timestamp > this.maxCacheDurationMs) {\n this.logDebug(`Skipping expired payload from ${new Date(payload.timestamp).toISOString()}`);\n successfulTimestamps.push(payload.timestamp);\n continue;\n }\n\n const success = await this.sendToOtherTransports(payload.items);\n\n if (success) {\n successfulTimestamps.push(payload.timestamp);\n }\n }\n\n // Remove successfully sent or expired payloads\n if (successfulTimestamps.length > 0) {\n await this.cache.removeByTimestamps(successfulTimestamps);\n this.logDebug(`Removed ${successfulTimestamps.length} payloads from cache`);\n }\n } catch (error) {\n this.logError('Failed to replay cached payloads', error);\n } finally {\n this.isReplaying = false;\n }\n }\n\n /**\n * Send items to all other registered transports.\n * Sends cached telemetry when connectivity is restored.\n */\n private async sendToOtherTransports(items: TransportItem[]): Promise<boolean> {\n if (this.otherTransports.length === 0) {\n this.logWarn('No other transports registered for offline replay');\n return false;\n }\n\n try {\n for (const transport of this.otherTransports) {\n await transport.send(items);\n }\n return true;\n } catch (error) {\n this.logError('Failed to send cached payload to transports', error);\n return false;\n }\n }\n}\n"]}
@@ -55,8 +55,7 @@ var DEFAULT_STORAGE_KEY = 'faro_persisted_user';
55
55
  * Stores user information persistently so it can be restored on app restart.
56
56
  * This ensures early telemetry events include user identification.
57
57
  *
58
- * Implementation follows Flutter SDK's UserPersistence pattern:
59
- * - Uses AsyncStorage for persistence (matching Flutter's SharedPreferences)
58
+ * Uses AsyncStorage for persistent user data across app sessions.
60
59
  * - Stores user as JSON
61
60
  * - Handles null/cleared users
62
61
  *
@@ -1 +1 @@
1
- {"version":3,"file":"UserPersistence.js","sourceRoot":"","sources":["../../../src/userPersistence/UserPersistence.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0HA,sDAEC;AAUD,8DAGC;AAKD,gDAEC;AAKD,gDAEC;AAvJD,4FAAqE;AAMrE;;GAEG;AACH,IAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH;IAGE,qCAAY,OAAoC;QAApC,wBAAA,EAAA,YAAoC;;QAC9C,IAAI,CAAC,UAAU,GAAG,MAAA,OAAO,CAAC,UAAU,mCAAI,mBAAmB,CAAC;IAC9D,CAAC;IAEK,8CAAQ,GAAd;;;;;;;wBAEqB,qBAAM,uBAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAA;;wBAAtD,QAAQ,GAAG,SAA2C;wBAC5D,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;4BACtB,sBAAO,IAAI,EAAC;wBACd,CAAC;wBAEK,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;wBACtC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;4BACpC,sBAAO,IAAI,EAAC;wBACd,CAAC;wBAED,sBAAO,QAAQ,EAAC;;;wBAEhB,sBAAO,IAAI,EAAC;;;;;KAEf;IAEK,8CAAQ,GAAd,UAAe,IAAqB;;;;;;;6BAE5B,CAAA,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA,EAArC,wBAAqC;wBACvC,qBAAM,IAAI,CAAC,SAAS,EAAE,EAAA;;wBAAtB,SAAsB,CAAC;wBACvB,sBAAO;;wBAGH,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;wBACtC,qBAAM,uBAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAA;;wBAArD,SAAqD,CAAC;;;;;;;;;KAIzD;IAEK,+CAAS,GAAf;;;;;;;wBAEI,qBAAM,uBAAY,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAA;;wBAA9C,SAA8C,CAAC;;;;;;;;;KAIlD;IAEK,sDAAgB,GAAtB;;;;;;;wBAEqB,qBAAM,uBAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAA;;wBAAtD,QAAQ,GAAG,SAA2C;wBAC5D,sBAAO,QAAQ,KAAK,IAAI,EAAC;;;wBAEzB,sBAAO,KAAK,EAAC;;;;;KAEhB;IAED;;OAEG;IACK,+CAAS,GAAjB,UAAkB,IAAc;QAC9B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IACpF,CAAC;IAED;;OAEG;IACK,qDAAe,GAAvB,UAAwB,GAAY;QAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,0EAA0E;QAC1E,2CAA2C;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IACH,kCAAC;AAAD,CAAC,AA3ED,IA2EC;AA3EY,kEAA2B;AA6ExC;;GAEG;AACH,SAAgB,qBAAqB,CAAC,OAAoC;IAApC,wBAAA,EAAA,YAAoC;IACxE,OAAO,IAAI,2BAA2B,CAAC,OAAO,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,IAAI,qBAAqB,GAA2B,IAAI,CAAC;AAEzD;;GAEG;AACH,SAAgB,yBAAyB,CAAC,OAAoC;IAApC,wBAAA,EAAA,YAAoC;IAC5E,qBAAqB,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACvD,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB;IAChC,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,WAAmC;IACpE,qBAAqB,GAAG,WAAW,CAAC;AACtC,CAAC","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\n\nimport type { MetaUser } from '@grafana/faro-core';\n\nimport type { UserPersistence, UserPersistenceOptions } from './types';\n\n/**\n * Default storage key for user data.\n */\nconst DEFAULT_STORAGE_KEY = 'faro_persisted_user';\n\n/**\n * UserPersistence implementation using AsyncStorage.\n *\n * Stores user information persistently so it can be restored on app restart.\n * This ensures early telemetry events include user identification.\n *\n * Implementation follows Flutter SDK's UserPersistence pattern:\n * - Uses AsyncStorage for persistence (matching Flutter's SharedPreferences)\n * - Stores user as JSON\n * - Handles null/cleared users\n *\n * @example\n * ```typescript\n * import { createUserPersistence } from '@grafana/faro-react-native';\n *\n * // Load user on app start\n * const persistence = createUserPersistence();\n * const user = await persistence.loadUser();\n *\n * if (user) {\n * faro.api.setUser(user);\n * }\n *\n * // Save user when logged in\n * const loggedInUser = { id: '123', email: 'user@example.com' };\n * await persistence.saveUser(loggedInUser);\n *\n * // Clear on logout\n * await persistence.clearUser();\n * ```\n */\nexport class AsyncStorageUserPersistence implements UserPersistence {\n private readonly storageKey: string;\n\n constructor(options: UserPersistenceOptions = {}) {\n this.storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;\n }\n\n async loadUser(): Promise<MetaUser | null> {\n try {\n const userJson = await AsyncStorage.getItem(this.storageKey);\n if (userJson === null) {\n return null;\n }\n\n const userData = JSON.parse(userJson);\n if (!this.isValidMetaUser(userData)) {\n return null;\n }\n\n return userData;\n } catch {\n return null;\n }\n }\n\n async saveUser(user: MetaUser | null): Promise<void> {\n try {\n if (user === null || this.isCleared(user)) {\n await this.clearUser();\n return;\n }\n\n const userJson = JSON.stringify(user);\n await AsyncStorage.setItem(this.storageKey, userJson);\n } catch {\n // Log failure but don't throw\n }\n }\n\n async clearUser(): Promise<void> {\n try {\n await AsyncStorage.removeItem(this.storageKey);\n } catch {\n // Log failure but don't throw\n }\n }\n\n async hasPersistedUser(): Promise<boolean> {\n try {\n const userJson = await AsyncStorage.getItem(this.storageKey);\n return userJson !== null;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if user is cleared (all fields empty/undefined).\n */\n private isCleared(user: MetaUser): boolean {\n return !user.id && !user.email && !user.username && !user.fullName && !user.roles;\n }\n\n /**\n * Validate that the parsed object is a valid MetaUser.\n */\n private isValidMetaUser(obj: unknown): obj is MetaUser {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n // MetaUser has optional string fields, so any object is technically valid\n // We just need to make sure it's an object\n return true;\n }\n}\n\n/**\n * Factory function to create a UserPersistence instance.\n */\nexport function createUserPersistence(options: UserPersistenceOptions = {}): UserPersistence {\n return new AsyncStorageUserPersistence(options);\n}\n\n/**\n * Global user persistence instance.\n */\nlet globalUserPersistence: UserPersistence | null = null;\n\n/**\n * Initialize the global user persistence.\n */\nexport function initializeUserPersistence(options: UserPersistenceOptions = {}): UserPersistence {\n globalUserPersistence = createUserPersistence(options);\n return globalUserPersistence;\n}\n\n/**\n * Get the global user persistence instance.\n */\nexport function getUserPersistence(): UserPersistence | null {\n return globalUserPersistence;\n}\n\n/**\n * Set a custom user persistence instance.\n */\nexport function setUserPersistence(persistence: UserPersistence | null): void {\n globalUserPersistence = persistence;\n}\n"]}
1
+ {"version":3,"file":"UserPersistence.js","sourceRoot":"","sources":["../../../src/userPersistence/UserPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\n\nimport type { MetaUser } from '@grafana/faro-core';\n\nimport type { UserPersistence, UserPersistenceOptions } from './types';\n\n/**\n * Default storage key for user data.\n */\nconst DEFAULT_STORAGE_KEY = 'faro_persisted_user';\n\n/**\n * UserPersistence implementation using AsyncStorage.\n *\n * Stores user information persistently so it can be restored on app restart.\n * This ensures early telemetry events include user identification.\n *\n * Uses AsyncStorage for persistent user data across app sessions.\n * - Stores user as JSON\n * - Handles null/cleared users\n *\n * @example\n * ```typescript\n * import { createUserPersistence } from '@grafana/faro-react-native';\n *\n * // Load user on app start\n * const persistence = createUserPersistence();\n * const user = await persistence.loadUser();\n *\n * if (user) {\n * faro.api.setUser(user);\n * }\n *\n * // Save user when logged in\n * const loggedInUser = { id: '123', email: 'user@example.com' };\n * await persistence.saveUser(loggedInUser);\n *\n * // Clear on logout\n * await persistence.clearUser();\n * ```\n */\nexport class AsyncStorageUserPersistence implements UserPersistence {\n private readonly storageKey: string;\n\n constructor(options: UserPersistenceOptions = {}) {\n this.storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;\n }\n\n async loadUser(): Promise<MetaUser | null> {\n try {\n const userJson = await AsyncStorage.getItem(this.storageKey);\n if (userJson === null) {\n return null;\n }\n\n const userData = JSON.parse(userJson);\n if (!this.isValidMetaUser(userData)) {\n return null;\n }\n\n return userData;\n } catch {\n return null;\n }\n }\n\n async saveUser(user: MetaUser | null): Promise<void> {\n try {\n if (user === null || this.isCleared(user)) {\n await this.clearUser();\n return;\n }\n\n const userJson = JSON.stringify(user);\n await AsyncStorage.setItem(this.storageKey, userJson);\n } catch {\n // Log failure but don't throw\n }\n }\n\n async clearUser(): Promise<void> {\n try {\n await AsyncStorage.removeItem(this.storageKey);\n } catch {\n // Log failure but don't throw\n }\n }\n\n async hasPersistedUser(): Promise<boolean> {\n try {\n const userJson = await AsyncStorage.getItem(this.storageKey);\n return userJson !== null;\n } catch {\n return false;\n }\n }\n\n /**\n * Check if user is cleared (all fields empty/undefined).\n */\n private isCleared(user: MetaUser): boolean {\n return !user.id && !user.email && !user.username && !user.fullName && !user.roles;\n }\n\n /**\n * Validate that the parsed object is a valid MetaUser.\n */\n private isValidMetaUser(obj: unknown): obj is MetaUser {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n // MetaUser has optional string fields, so any object is technically valid\n // We just need to make sure it's an object\n return true;\n }\n}\n\n/**\n * Factory function to create a UserPersistence instance.\n */\nexport function createUserPersistence(options: UserPersistenceOptions = {}): UserPersistence {\n return new AsyncStorageUserPersistence(options);\n}\n\n/**\n * Global user persistence instance.\n */\nlet globalUserPersistence: UserPersistence | null = null;\n\n/**\n * Initialize the global user persistence.\n */\nexport function initializeUserPersistence(options: UserPersistenceOptions = {}): UserPersistence {\n globalUserPersistence = createUserPersistence(options);\n return globalUserPersistence;\n}\n\n/**\n * Get the global user persistence instance.\n */\nexport function getUserPersistence(): UserPersistence | null {\n return globalUserPersistence;\n}\n\n/**\n * Set a custom user persistence instance.\n */\nexport function setUserPersistence(persistence: UserPersistence | null): void {\n globalUserPersistence = persistence;\n}\n"]}
@@ -19,8 +19,7 @@ function toRegExpArray(patterns) {
19
19
  /**
20
20
  * Returns the default set of instrumentations for React Native.
21
21
  *
22
- * Reads all options from the Faro config (single source of truth).
23
- * Property names are aligned with Flutter SDK FaroConfig:
22
+ * Reads all options from the Faro config (single source of truth):
24
23
  * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals
25
24
  * - enableCrashReporting, enableErrorReporting
26
25
  * - fetchVitalsInterval, ignoreUrls
@@ -40,7 +39,6 @@ function toRegExpArray(patterns) {
40
39
  * ```
41
40
  */
42
41
  export function getRNInstrumentations(config = {}) {
43
- // Aligned with Flutter SDK FaroConfig defaults
44
42
  const {
45
43
  // Error & crash tracking
46
44
  enableErrorReporting = true, enableCrashReporting = false, anrTracking = false, anrOptions = {},
@@ -53,21 +51,21 @@ export function getRNInstrumentations(config = {}) {
53
51
  // Tracing
54
52
  enableTracing = false, tracingOptions = {}, } = config;
55
53
  const instrumentations = [];
56
- // Error reporting (Flutter: enableFlutterErrorReporting)
54
+ // Error reporting
57
55
  if (enableErrorReporting) {
58
56
  instrumentations.push(new ErrorsInstrumentation({
59
57
  releaseBundleFilename: config.releaseBundleFilename,
60
58
  }));
61
59
  }
62
- // Console capture - not in Flutter SDK, RN-specific
60
+ // Console capture
63
61
  if (enableConsoleCapture) {
64
62
  instrumentations.push(new ConsoleInstrumentation());
65
63
  }
66
- // Sessions - always enabled in Flutter, same here
64
+ // Sessions
67
65
  instrumentations.push(new SessionInstrumentation());
68
- // Views - always enabled in Flutter, same here
66
+ // Views
69
67
  instrumentations.push(new ViewInstrumentation());
70
- // App state - always enabled in Flutter, same here
68
+ // App state
71
69
  instrumentations.push(new AppStateInstrumentation());
72
70
  // User actions - enabled by default, opt-out via enableUserActions
73
71
  if (enableUserActions) {
@@ -1 +1 @@
1
- {"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth).\n * Property names are aligned with Flutter SDK FaroConfig:\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n // Aligned with Flutter SDK FaroConfig defaults\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting (Flutter: enableFlutterErrorReporting)\n if (enableErrorReporting) {\n instrumentations.push(\n new ErrorsInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n // Console capture - not in Flutter SDK, RN-specific\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions - always enabled in Flutter, same here\n instrumentations.push(new SessionInstrumentation());\n\n // Views - always enabled in Flutter, same here\n instrumentations.push(new ViewInstrumentation());\n\n // App state - always enabled in Flutter, same here\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(\n new CrashReportingInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
1
+ {"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth):\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting\n if (enableErrorReporting) {\n instrumentations.push(\n new ErrorsInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n // Console capture\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions\n instrumentations.push(new SessionInstrumentation());\n\n // Views\n instrumentations.push(new ViewInstrumentation());\n\n // App state\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(\n new CrashReportingInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
@@ -101,12 +101,12 @@ export function makeRNConfig(config, preloadedMobileMeta, appSymbolsBundleId) {
101
101
  const instrumentations = buildInstrumentations(config);
102
102
  const installationId = (_c = config.app.installationId) !== null && _c !== void 0 ? _c : preloadedAppMeta === null || preloadedAppMeta === void 0 ? void 0 : preloadedAppMeta.installationId;
103
103
  const releaseBundleFilename = config.releaseBundleFilename;
104
- // Merge bundleId (from config, Metro, or DeviceInfo) with preloaded app meta
105
104
  const bundleIdValue = resolveAppBundleId(config.app, (_d = config.app) === null || _d === void 0 ? void 0 : _d.name, appSymbolsBundleId);
106
- const appMetasIfPresent = preloadedAppMeta || bundleIdValue
107
- ? [{ app: Object.assign(Object.assign({}, preloadedAppMeta), (bundleIdValue && { bundleId: bundleIdValue })) }]
108
- : [];
109
- return Object.assign(Object.assign({ app: Object.assign(Object.assign({}, config.app), (installationId && { installationId })) }, (preloadedMobileMeta != null && {
105
+ const app = Object.assign(Object.assign(Object.assign(Object.assign({}, preloadedAppMeta), config.app), (installationId && { installationId })), (bundleIdValue && { bundleId: bundleIdValue }));
106
+ // Core merges metadata shallowly. Supply the complete app object so native
107
+ // metadata cannot replace configured fields such as version and environment.
108
+ const appMetasIfPresent = preloadedAppMeta || bundleIdValue ? [{ app }] : [];
109
+ return Object.assign(Object.assign({ app }, (preloadedMobileMeta != null && {
110
110
  preloadedMobileMeta: preloadedMobileMeta.meta,
111
111
  preloadedSessionDeviceAttributes: preloadedMobileMeta.sessionAttributes,
112
112
  })), { dedupe: (_e = config.dedupe) !== null && _e !== void 0 ? _e : true, globalObjectKey: (_f = config.globalObjectKey) !== null && _f !== void 0 ? _f : defaultGlobalObjectKey, internalLoggerLevel: (_g = config.internalLoggerLevel) !== null && _g !== void 0 ? _g : InternalLoggerLevel.ERROR, isolate: (_h = config.isolate) !== null && _h !== void 0 ? _h : false, parseStacktrace: (_j = config.parseStacktrace) !== null && _j !== void 0 ? _j : createParseStacktrace(releaseBundleFilename), paused: (_k = config.paused) !== null && _k !== void 0 ? _k : false, preventGlobalExposure: (_l = config.preventGlobalExposure) !== null && _l !== void 0 ? _l : false, unpatchedConsole: (_m = config.unpatchedConsole) !== null && _m !== void 0 ? _m : defaultUnpatchedConsole, batching: {