@nathanclaire/edge-telemetry-sdk 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/README.md +831 -186
  2. package/dist/appLifecycle-CECgRVu_.cjs +2 -0
  3. package/dist/appLifecycle-CECgRVu_.cjs.map +1 -0
  4. package/dist/appLifecycle-DQErzJIR.js +22 -0
  5. package/dist/appLifecycle-DQErzJIR.js.map +1 -0
  6. package/dist/batch-CwoO7noL.js +56 -0
  7. package/dist/batch-CwoO7noL.js.map +1 -0
  8. package/dist/batch-DNu8Xr9T.cjs +2 -0
  9. package/dist/batch-DNu8Xr9T.cjs.map +1 -0
  10. package/dist/crashCapture-BOJ8nSAZ.cjs +4 -0
  11. package/dist/crashCapture-BOJ8nSAZ.cjs.map +1 -0
  12. package/dist/crashCapture-BW8WuyaU.js +70 -0
  13. package/dist/crashCapture-BW8WuyaU.js.map +1 -0
  14. package/dist/crashHandler.web-8F_k2veo.js +29 -0
  15. package/dist/crashHandler.web-8F_k2veo.js.map +1 -0
  16. package/dist/crashHandler.web-CWmF56mJ.cjs +2 -0
  17. package/dist/crashHandler.web-CWmF56mJ.cjs.map +1 -0
  18. package/dist/crashHandlerNative.native-C2kSikDt.cjs +2 -0
  19. package/dist/crashHandlerNative.native-C2kSikDt.cjs.map +1 -0
  20. package/dist/crashHandlerNative.native-FupMnr1U.js +39 -0
  21. package/dist/crashHandlerNative.native-FupMnr1U.js.map +1 -0
  22. package/dist/deviceInfo.native-BJ-3Jj1T.js +53 -0
  23. package/dist/deviceInfo.native-BJ-3Jj1T.js.map +1 -0
  24. package/dist/deviceInfo.native-BoeTZcD6.cjs +2 -0
  25. package/dist/deviceInfo.native-BoeTZcD6.cjs.map +1 -0
  26. package/dist/deviceInfo.web-3L3IDQQA.cjs +2 -0
  27. package/dist/deviceInfo.web-3L3IDQQA.cjs.map +1 -0
  28. package/dist/deviceInfo.web-BFB1-pcA.js +41 -0
  29. package/dist/deviceInfo.web-BFB1-pcA.js.map +1 -0
  30. package/dist/frameTracker-CHG6sIx9.cjs +2 -0
  31. package/dist/frameTracker-CHG6sIx9.cjs.map +1 -0
  32. package/dist/frameTracker-DdB8W_kC.js +54 -0
  33. package/dist/frameTracker-DdB8W_kC.js.map +1 -0
  34. package/dist/httpAttributes-BrrtM8zh.js +70 -0
  35. package/dist/httpAttributes-BrrtM8zh.js.map +1 -0
  36. package/dist/httpAttributes-D0m6UID7.cjs +2 -0
  37. package/dist/httpAttributes-D0m6UID7.cjs.map +1 -0
  38. package/dist/index-BtLlV1HA.js +57 -0
  39. package/dist/index-BtLlV1HA.js.map +1 -0
  40. package/dist/{deviceInfo.native-D2k0LDhI.js → index-C2XQ-EYw.js} +362 -411
  41. package/dist/index-C2XQ-EYw.js.map +1 -0
  42. package/dist/index-CXsVZY35.cjs +2 -0
  43. package/dist/index-CXsVZY35.cjs.map +1 -0
  44. package/dist/index-DLquF88M.cjs +2 -0
  45. package/dist/index-DLquF88M.cjs.map +1 -0
  46. package/dist/index.native.cjs +1 -1
  47. package/dist/index.native.cjs.map +1 -1
  48. package/dist/index.native.js +94 -59
  49. package/dist/index.native.js.map +1 -1
  50. package/dist/index.web.cjs +1 -1
  51. package/dist/index.web.cjs.map +1 -1
  52. package/dist/index.web.js +88 -33
  53. package/dist/index.web.js.map +1 -1
  54. package/dist/interactionWeb.web-Bl_cZlgh.cjs +2 -0
  55. package/dist/interactionWeb.web-Bl_cZlgh.cjs.map +1 -0
  56. package/dist/interactionWeb.web-CJAviVQ7.js +109 -0
  57. package/dist/interactionWeb.web-CJAviVQ7.js.map +1 -0
  58. package/dist/interceptFetchWeb.web-T88kO-bE.cjs +2 -0
  59. package/dist/interceptFetchWeb.web-T88kO-bE.cjs.map +1 -0
  60. package/dist/interceptFetchWeb.web-UXC2Lb6X.js +61 -0
  61. package/dist/interceptFetchWeb.web-UXC2Lb6X.js.map +1 -0
  62. package/dist/interceptHttpNative.native-BVpFuoNB.cjs +2 -0
  63. package/dist/interceptHttpNative.native-BVpFuoNB.cjs.map +1 -0
  64. package/dist/interceptHttpNative.native-rX-lPNST.js +18 -0
  65. package/dist/interceptHttpNative.native-rX-lPNST.js.map +1 -0
  66. package/dist/loadingTime-Bhomd9qq.cjs +2 -0
  67. package/dist/loadingTime-Bhomd9qq.cjs.map +1 -0
  68. package/dist/loadingTime-ZkYvFijA.js +70 -0
  69. package/dist/loadingTime-ZkYvFijA.js.map +1 -0
  70. package/dist/memoryNative.native-C42h9SjK.js +54 -0
  71. package/dist/memoryNative.native-C42h9SjK.js.map +1 -0
  72. package/dist/memoryNative.native-CA-7w9w1.cjs +2 -0
  73. package/dist/memoryNative.native-CA-7w9w1.cjs.map +1 -0
  74. package/dist/memoryStore-BIGZmmfD.cjs +2 -0
  75. package/dist/memoryStore-BIGZmmfD.cjs.map +1 -0
  76. package/dist/memoryStore-F5I-GQKM.js +193 -0
  77. package/dist/memoryStore-F5I-GQKM.js.map +1 -0
  78. package/dist/nativeSender-CfQaRuDK.cjs +2 -0
  79. package/dist/nativeSender-CfQaRuDK.cjs.map +1 -0
  80. package/dist/nativeSender-pgVSIv-X.js +59 -0
  81. package/dist/nativeSender-pgVSIv-X.js.map +1 -0
  82. package/dist/navigationRef-BzYbs8cm.js +28 -0
  83. package/dist/navigationRef-BzYbs8cm.js.map +1 -0
  84. package/dist/navigationRef-C-_qhb43.cjs +2 -0
  85. package/dist/navigationRef-C-_qhb43.cjs.map +1 -0
  86. package/dist/{navigationTracker-Cts68dgG.js → navigationTracker-BmywA_Bg.js} +4 -4
  87. package/dist/navigationTracker-BmywA_Bg.js.map +1 -0
  88. package/dist/navigationTracker-HkwVyywz.cjs +2 -0
  89. package/dist/navigationTracker-HkwVyywz.cjs.map +1 -0
  90. package/dist/navigationWeb.web-CW_z7Ca4.cjs +2 -0
  91. package/dist/navigationWeb.web-CW_z7Ca4.cjs.map +1 -0
  92. package/dist/navigationWeb.web-CcwgKuIZ.js +55 -0
  93. package/dist/navigationWeb.web-CcwgKuIZ.js.map +1 -0
  94. package/dist/{networkInfo.native-BK5fpAOL.js → networkInfo.native-CGrx9cnH.js} +15 -15
  95. package/dist/{networkInfo.native-BK5fpAOL.js.map → networkInfo.native-CGrx9cnH.js.map} +1 -1
  96. package/dist/{networkInfo.native-cxuw93Uy.cjs → networkInfo.native-YVySdv3S.cjs} +2 -2
  97. package/dist/{networkInfo.native-cxuw93Uy.cjs.map → networkInfo.native-YVySdv3S.cjs.map} +1 -1
  98. package/dist/{networkInfo.web-twcNn_hc.js → networkInfo.web-B2YlLNsI.js} +3 -3
  99. package/dist/networkInfo.web-B2YlLNsI.js.map +1 -0
  100. package/dist/networkInfo.web-PltJTHsy.cjs +2 -0
  101. package/dist/networkInfo.web-PltJTHsy.cjs.map +1 -0
  102. package/dist/runtimeReady.native-Bh7EcYmK.js +14 -0
  103. package/dist/runtimeReady.native-Bh7EcYmK.js.map +1 -0
  104. package/dist/runtimeReady.native-jUmuE1lV.cjs +2 -0
  105. package/dist/runtimeReady.native-jUmuE1lV.cjs.map +1 -0
  106. package/dist/runtimeReady.web-C8fdIbVR.js +25 -0
  107. package/dist/runtimeReady.web-C8fdIbVR.js.map +1 -0
  108. package/dist/runtimeReady.web-DHW682RN.cjs +2 -0
  109. package/dist/runtimeReady.web-DHW682RN.cjs.map +1 -0
  110. package/dist/seedRuntimeReady-BKwkBEAF.cjs +2 -0
  111. package/dist/seedRuntimeReady-BKwkBEAF.cjs.map +1 -0
  112. package/dist/seedRuntimeReady-Cz8nOKoo.js +10 -0
  113. package/dist/seedRuntimeReady-Cz8nOKoo.js.map +1 -0
  114. package/dist/src/adapters/appLifecycle.d.ts +8 -1
  115. package/dist/src/adapters/batch.d.ts +2 -0
  116. package/dist/src/adapters/crashCapture.d.ts +38 -9
  117. package/dist/src/adapters/failedEvents.d.ts +43 -0
  118. package/dist/src/adapters/frameAggregate.d.ts +8 -3
  119. package/dist/src/adapters/frameTracker.d.ts +30 -0
  120. package/dist/src/adapters/httpAttributes.d.ts +18 -0
  121. package/dist/src/adapters/loadingTime.d.ts +67 -0
  122. package/dist/src/adapters/native/interceptHttpNative.native.d.ts +18 -0
  123. package/dist/src/adapters/native/memoryNative.native.d.ts +15 -11
  124. package/dist/src/adapters/native/runtimeReady.native.d.ts +1 -0
  125. package/dist/src/adapters/native/store.native.d.ts +10 -0
  126. package/dist/src/adapters/nativeSender.d.ts +2 -2
  127. package/dist/src/adapters/navigationRef.d.ts +21 -0
  128. package/dist/src/adapters/navigationTracker.d.ts +1 -1
  129. package/dist/src/adapters/screenTiming.d.ts +8 -2
  130. package/dist/src/adapters/seedRuntimeReady.d.ts +8 -0
  131. package/dist/src/adapters/traceHeader.d.ts +33 -0
  132. package/dist/src/adapters/traceManager.d.ts +131 -0
  133. package/dist/src/adapters/uiInteraction.d.ts +111 -0
  134. package/dist/src/adapters/viewManager.d.ts +161 -0
  135. package/dist/src/adapters/viewport.d.ts +15 -0
  136. package/dist/src/adapters/web/interactionWeb.web.d.ts +36 -0
  137. package/dist/src/adapters/web/interceptFetchWeb.web.d.ts +3 -11
  138. package/dist/src/adapters/web/navigationWeb.web.d.ts +10 -0
  139. package/dist/src/adapters/web/runtimeReady.web.d.ts +5 -0
  140. package/dist/src/adapters/web/store.web.d.ts +12 -0
  141. package/dist/src/adapters/web/webVitals.web.d.ts +49 -0
  142. package/dist/src/adapters/webSender.d.ts +2 -2
  143. package/dist/src/adapters/webVitals.d.ts +25 -0
  144. package/dist/src/adapters/xhrIntercept.d.ts +53 -0
  145. package/dist/src/core/beforeSend.d.ts +20 -0
  146. package/dist/src/core/debug.d.ts +7 -0
  147. package/dist/src/core/memoryStore.d.ts +23 -0
  148. package/dist/src/core/store.d.ts +41 -0
  149. package/dist/src/core/telemetry.d.ts +305 -35
  150. package/dist/src/core/userProfile.d.ts +12 -0
  151. package/dist/src/core/utils/json.d.ts +7 -0
  152. package/dist/src/core/utils/uuid.d.ts +10 -1
  153. package/dist/src/createTelemetry.native.d.ts +7 -0
  154. package/dist/src/createTelemetry.web.d.ts +7 -0
  155. package/dist/src/index.base.d.ts +17 -1
  156. package/dist/src/index.native.d.ts +46 -9
  157. package/dist/src/index.web.d.ts +33 -2
  158. package/dist/store.native-BYBJcy6o.js +32 -0
  159. package/dist/store.native-BYBJcy6o.js.map +1 -0
  160. package/dist/store.native-TPqXngmF.cjs +2 -0
  161. package/dist/store.native-TPqXngmF.cjs.map +1 -0
  162. package/dist/store.web-CvqqN-_Z.cjs +2 -0
  163. package/dist/store.web-CvqqN-_Z.cjs.map +1 -0
  164. package/dist/store.web-DJAlN0nc.js +31 -0
  165. package/dist/store.web-DJAlN0nc.js.map +1 -0
  166. package/dist/telemetry-ByDJIz_9.js +1280 -0
  167. package/dist/telemetry-ByDJIz_9.js.map +1 -0
  168. package/dist/telemetry-CrU7i1Jc.cjs +2 -0
  169. package/dist/telemetry-CrU7i1Jc.cjs.map +1 -0
  170. package/dist/uiInteraction-CdzvEI1p.cjs +2 -0
  171. package/dist/uiInteraction-CdzvEI1p.cjs.map +1 -0
  172. package/dist/uiInteraction-DAqxxz0q.js +104 -0
  173. package/dist/uiInteraction-DAqxxz0q.js.map +1 -0
  174. package/dist/viewport-Dd85GUNO.cjs +2 -0
  175. package/dist/viewport-Dd85GUNO.cjs.map +1 -0
  176. package/dist/viewport-Dzmt5QwK.js +15 -0
  177. package/dist/viewport-Dzmt5QwK.js.map +1 -0
  178. package/dist/web-vitals.attribution-CVCPiX-h.cjs +2 -0
  179. package/dist/web-vitals.attribution-CVCPiX-h.cjs.map +1 -0
  180. package/dist/web-vitals.attribution-xw9N5MKQ.js +488 -0
  181. package/dist/web-vitals.attribution-xw9N5MKQ.js.map +1 -0
  182. package/dist/webSender-BIwslfpT.js +63 -0
  183. package/dist/webSender-BIwslfpT.js.map +1 -0
  184. package/dist/webSender-CVtE4sOF.cjs +2 -0
  185. package/dist/webSender-CVtE4sOF.cjs.map +1 -0
  186. package/dist/webVitals.web-CNIpsKlA.js +85 -0
  187. package/dist/webVitals.web-CNIpsKlA.js.map +1 -0
  188. package/dist/webVitals.web-DpWJ-iYp.cjs +2 -0
  189. package/dist/webVitals.web-DpWJ-iYp.cjs.map +1 -0
  190. package/dist/xhrIntercept-BADex6Cq.cjs +2 -0
  191. package/dist/xhrIntercept-BADex6Cq.cjs.map +1 -0
  192. package/dist/xhrIntercept-BNiA41Mj.js +53 -0
  193. package/dist/xhrIntercept-BNiA41Mj.js.map +1 -0
  194. package/docs/migration-v4.md +190 -0
  195. package/package.json +12 -4
  196. package/dist/appLifecycle-Cynw0kAv.js +0 -14
  197. package/dist/appLifecycle-Cynw0kAv.js.map +0 -1
  198. package/dist/appLifecycle-DvJIt7ym.cjs +0 -2
  199. package/dist/appLifecycle-DvJIt7ym.cjs.map +0 -1
  200. package/dist/batch-CLBFgYPC.cjs +0 -2
  201. package/dist/batch-CLBFgYPC.cjs.map +0 -1
  202. package/dist/batch-DLG_gtOm.js +0 -12
  203. package/dist/batch-DLG_gtOm.js.map +0 -1
  204. package/dist/crashCapture-BYTM4FLC.cjs +0 -2
  205. package/dist/crashCapture-BYTM4FLC.cjs.map +0 -1
  206. package/dist/crashCapture-D_rkkBVa.js +0 -31
  207. package/dist/crashCapture-D_rkkBVa.js.map +0 -1
  208. package/dist/crashHandler.web-BSxDpFTo.cjs +0 -2
  209. package/dist/crashHandler.web-BSxDpFTo.cjs.map +0 -1
  210. package/dist/crashHandler.web-D59bmhg9.js +0 -32
  211. package/dist/crashHandler.web-D59bmhg9.js.map +0 -1
  212. package/dist/crashHandlerNative.native-DeAFze4E.cjs +0 -2
  213. package/dist/crashHandlerNative.native-DeAFze4E.cjs.map +0 -1
  214. package/dist/crashHandlerNative.native-d7qJkzA6.js +0 -41
  215. package/dist/crashHandlerNative.native-d7qJkzA6.js.map +0 -1
  216. package/dist/debug-DsvQ9y3W.js +0 -68
  217. package/dist/debug-DsvQ9y3W.js.map +0 -1
  218. package/dist/debug-rnOOxFpN.cjs +0 -2
  219. package/dist/debug-rnOOxFpN.cjs.map +0 -1
  220. package/dist/deviceInfo.native-7hbRYixR.cjs +0 -2
  221. package/dist/deviceInfo.native-7hbRYixR.cjs.map +0 -1
  222. package/dist/deviceInfo.native-D2k0LDhI.js.map +0 -1
  223. package/dist/deviceInfo.web-ByNAlEJh.cjs +0 -2
  224. package/dist/deviceInfo.web-ByNAlEJh.cjs.map +0 -1
  225. package/dist/deviceInfo.web-DzQyEku6.js +0 -40
  226. package/dist/deviceInfo.web-DzQyEku6.js.map +0 -1
  227. package/dist/frameAggregate-C_HoVFcK.cjs +0 -2
  228. package/dist/frameAggregate-C_HoVFcK.cjs.map +0 -1
  229. package/dist/frameAggregate-Cwh1juw1.js +0 -17
  230. package/dist/frameAggregate-Cwh1juw1.js.map +0 -1
  231. package/dist/frameDropsNative.native-CP7zrXDf.js +0 -23
  232. package/dist/frameDropsNative.native-CP7zrXDf.js.map +0 -1
  233. package/dist/frameDropsNative.native-DKqEDqip.cjs +0 -2
  234. package/dist/frameDropsNative.native-DKqEDqip.cjs.map +0 -1
  235. package/dist/frameDropsWeb.web-C9Lt3Fuj.js +0 -22
  236. package/dist/frameDropsWeb.web-C9Lt3Fuj.js.map +0 -1
  237. package/dist/frameDropsWeb.web-azpwQCUz.cjs +0 -2
  238. package/dist/frameDropsWeb.web-azpwQCUz.cjs.map +0 -1
  239. package/dist/httpAttributes-BV50UKQU.js +0 -19
  240. package/dist/httpAttributes-BV50UKQU.js.map +0 -1
  241. package/dist/httpAttributes-xXOD9trQ.cjs +0 -2
  242. package/dist/httpAttributes-xXOD9trQ.cjs.map +0 -1
  243. package/dist/interaction-CH8yoY5-.js +0 -23
  244. package/dist/interaction-CH8yoY5-.js.map +0 -1
  245. package/dist/interaction-DcNFBW7a.cjs +0 -2
  246. package/dist/interaction-DcNFBW7a.cjs.map +0 -1
  247. package/dist/interceptFetchNative.native-VlhtMG-U.js +0 -49
  248. package/dist/interceptFetchNative.native-VlhtMG-U.js.map +0 -1
  249. package/dist/interceptFetchNative.native-jEAldH14.cjs +0 -2
  250. package/dist/interceptFetchNative.native-jEAldH14.cjs.map +0 -1
  251. package/dist/interceptFetchWeb.web-DFzZliik.cjs +0 -2
  252. package/dist/interceptFetchWeb.web-DFzZliik.cjs.map +0 -1
  253. package/dist/interceptFetchWeb.web-DfsMfoRP.js +0 -77
  254. package/dist/interceptFetchWeb.web-DfsMfoRP.js.map +0 -1
  255. package/dist/memoryNative.native-BWLh2S9L.js +0 -48
  256. package/dist/memoryNative.native-BWLh2S9L.js.map +0 -1
  257. package/dist/memoryNative.native-DCXSOd4Q.cjs +0 -2
  258. package/dist/memoryNative.native-DCXSOd4Q.cjs.map +0 -1
  259. package/dist/memoryWeb.web-B3YuV-cB.js +0 -48
  260. package/dist/memoryWeb.web-B3YuV-cB.js.map +0 -1
  261. package/dist/memoryWeb.web-CheXAcJn.cjs +0 -2
  262. package/dist/memoryWeb.web-CheXAcJn.cjs.map +0 -1
  263. package/dist/nativeSender-BE_mzw_S.js +0 -70
  264. package/dist/nativeSender-BE_mzw_S.js.map +0 -1
  265. package/dist/nativeSender-DyAUGY7c.cjs +0 -2
  266. package/dist/nativeSender-DyAUGY7c.cjs.map +0 -1
  267. package/dist/navigationNative.native-3tO5JxSd.cjs +0 -2
  268. package/dist/navigationNative.native-3tO5JxSd.cjs.map +0 -1
  269. package/dist/navigationNative.native-BUCGPQX2.js +0 -25
  270. package/dist/navigationNative.native-BUCGPQX2.js.map +0 -1
  271. package/dist/navigationTracker-B2GMwyKk.cjs +0 -2
  272. package/dist/navigationTracker-B2GMwyKk.cjs.map +0 -1
  273. package/dist/navigationTracker-Cts68dgG.js.map +0 -1
  274. package/dist/navigationWeb.web-B0necr5j.cjs +0 -2
  275. package/dist/navigationWeb.web-B0necr5j.cjs.map +0 -1
  276. package/dist/navigationWeb.web-CORzE_Id.js +0 -46
  277. package/dist/navigationWeb.web-CORzE_Id.js.map +0 -1
  278. package/dist/networkInfo.web-DJmw1sNK.cjs +0 -2
  279. package/dist/networkInfo.web-DJmw1sNK.cjs.map +0 -1
  280. package/dist/networkInfo.web-twcNn_hc.js.map +0 -1
  281. package/dist/src/adapters/interaction.d.ts +0 -17
  282. package/dist/src/adapters/native/frameDropsNative.native.d.ts +0 -16
  283. package/dist/src/adapters/native/interceptFetchNative.native.d.ts +0 -23
  284. package/dist/src/adapters/native/navigationNative.native.d.ts +0 -12
  285. package/dist/src/adapters/web/frameDropsWeb.web.d.ts +0 -16
  286. package/dist/src/adapters/web/memoryWeb.web.d.ts +0 -26
  287. package/dist/telemetry-Co4MYdwa.cjs +0 -2
  288. package/dist/telemetry-Co4MYdwa.cjs.map +0 -1
  289. package/dist/telemetry-KI0x4gzC.js +0 -389
  290. package/dist/telemetry-KI0x4gzC.js.map +0 -1
  291. package/dist/v4-BysszJq8.js +0 -31
  292. package/dist/v4-BysszJq8.js.map +0 -1
  293. package/dist/v4-CqdK_b8Y.cjs +0 -2
  294. package/dist/v4-CqdK_b8Y.cjs.map +0 -1
  295. package/dist/webSender-B27tmT1w.js +0 -42
  296. package/dist/webSender-B27tmT1w.js.map +0 -1
  297. package/dist/webSender-BWi4x1df.cjs +0 -2
  298. package/dist/webSender-BWi4x1df.cjs.map +0 -1
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The quiet window, and **the same constant as §4.6's dead-click window** — one constant, not
3
+ * two. `ui.dead` (web only, not built yet) must import this rather than declare a second 1000.
4
+ */
5
+ export declare const QUIET_WINDOW_MS = 1000;
6
+ /** Hard cap (§4.5.2). A capped view emits **null, not the cap**: at 30 s, 30 s and 90 s are the same row. */
7
+ export declare const LOADING_TIME_CAP_MS = 30000;
8
+ /** §4.5's `view.loading_time_outcome` domain — never null, exactly four values. */
9
+ export type LoadingTimeOutcome = "settled" | "no_activity" | "capped" | "abandoned";
10
+ export type LoadingTimeResult = {
11
+ /** Milliseconds, or null — and null has three causes, which is why the outcome ships beside it. */
12
+ loadingTime: number | null;
13
+ outcome: LoadingTimeOutcome;
14
+ };
15
+ export type NetworkSettleOpts = {
16
+ /**
17
+ * `initial_load` only: the view starts **busy** until the platform's own runtime-ready
18
+ * marker (`loadEventEnd` on web, `performance.rnStartupTiming` on native), then settles.
19
+ * Every other `load_type` runs the same column with no seed.
20
+ */
21
+ awaitRuntimeReady?: boolean;
22
+ };
23
+ /**
24
+ * One view's settle state. Constructed per view and handed the requests that **started** in
25
+ * it — a request in flight across a view boundary belongs to the old view and does not hold
26
+ * the new one open (§4.5.2), which is why the completion callback is bound to this instance
27
+ * rather than looked up on whatever view is live when the response lands.
28
+ */
29
+ export declare class NetworkSettle {
30
+ private readonly viewStart;
31
+ private started;
32
+ private inFlight;
33
+ private lastEnd?;
34
+ /** Frozen the first time quiet is *observed*, so a later request cannot un-settle the view. */
35
+ private settledAt?;
36
+ private awaitingRuntimeReady;
37
+ private runtimeReadyAt?;
38
+ constructor(viewStart: number, opts?: NetworkSettleOpts);
39
+ /** `view.request_count` — requests **started** in this view, failures included (§4.5). */
40
+ get requestCount(): number;
41
+ /**
42
+ * The platform's runtime-ready marker, or `undefined` where the platform has none —
43
+ * Old Architecture, or a runtime that omits it (§4.3). Either way this releases the gate:
44
+ * a marker that never arrives must not make every launch report `abandoned`.
45
+ */
46
+ seedRuntimeReady(at?: number): void;
47
+ /**
48
+ * A request started in this view. Returns its completion callback — idempotent, because an
49
+ * XHR `loadend` can fire more than once for a reused instance and an unbalanced in-flight
50
+ * count would hold the view open forever.
51
+ */
52
+ requestStarted(now?: number): (endedAt?: number) => void;
53
+ /**
54
+ * The view's verdict at exit. Idempotent, not pure: it may freeze the settle timestamp as
55
+ * a side effect, which is deliberate — once quiet has been *observed* it cannot be undone
56
+ * by a later call, and calling this twice must not produce two different answers.
57
+ */
58
+ resolve(now?: number): LoadingTimeResult;
59
+ /**
60
+ * Settle is the *completion* timestamp, not the moment we noticed it — the quiet window is
61
+ * detection delay and is subtracted back out. That is free precisely because the `view`
62
+ * event emits at view exit rather than at settle, so nothing waits on the window.
63
+ */
64
+ private freezeIfQuiet;
65
+ /** When the network last went quiet, or undefined while the view is still busy. */
66
+ private quietSince;
67
+ }
@@ -0,0 +1,18 @@
1
+ import { Telemetry } from '../../core/telemetry';
2
+ /**
3
+ * Captures all JS-originated HTTP on React Native by patching `XMLHttpRequest` — and nothing
4
+ * else. RN's `global.fetch` is `XMLHttpRequest` underneath (contract §4.4), so patching both
5
+ * would emit **two** `http.request` events per `fetch()` call; the one chokepoint catches
6
+ * `fetch` *and* axios, which is why an axios app's HTTP dashboard was empty before #95.
7
+ *
8
+ * Outside the boundary and uncountable: rn-fetch-blob, RN Firebase, native Apollo links,
9
+ * expo-file-system, `Image` loading and `WebSocket` — none of them touch JS HTTP.
10
+ */
11
+ export declare class NetworkTrackerNative {
12
+ private telemetry;
13
+ private unpatch?;
14
+ constructor(telemetry: Telemetry);
15
+ start(): Promise<void>;
16
+ /** Restores the original XHR prototype, removing the interception. */
17
+ stop(): void;
18
+ }
@@ -1,25 +1,29 @@
1
1
  import { Telemetry } from '../../core/telemetry';
2
2
  /**
3
- * Logs JavaScript memory usage and pressure level at regular intervals
4
- * in a React Native environment.
3
+ * §5.2 / #105 `memory_usage`: RSS, **native only**, sampled every 30 s. The rationale for
4
+ * every part of that sentence lives in CLAUDE.md's `memory_usage` section; this file is the
5
+ * mechanism.
5
6
  */
6
7
  export declare class TelemetryMemoryUsageNative {
7
8
  private telemetry;
8
9
  private intervalId;
9
10
  constructor(telemetry: Telemetry);
10
11
  /**
11
- * Records a memory_usage metric (value = used heap MB) per sample, on the v3 metric path.
12
- */
13
- recordMemoryUsage(): void;
14
- /**
15
- * Starts periodic memory usage logging at the given interval.
16
- * Returns a resolved Promise for async startup flows.
12
+ * One sample: `value` = resident MB, with the device total alongside so headroom is
13
+ * readable without a second join. An unusable read emits nothing — a fabricated 0 would
14
+ * drag every percentile down and read as a memory *win*.
17
15
  *
18
- * @param intervalMs Sampling interval in milliseconds (default: 30s)
16
+ * The two reads are guarded separately on purpose: `value` is what §5.2 makes primary,
17
+ * so a device-info build whose `getTotalMemory()` throws must still ship the resident
18
+ * figure with `memory.total_mb` simply absent.
19
19
  */
20
- start(intervalMs?: number): Promise<void>;
20
+ recordMemoryUsage(): Promise<void>;
21
21
  /**
22
- * Stops the periodic memory logging if it was started.
22
+ * Starts the periodic sampler. v3 never called this — `trackMemoryUsage()` fired the
23
+ * one-shot read and applied `.catch` to its `void` return — so the metric was
24
+ * single-shot at best.
23
25
  */
26
+ start(): Promise<void>;
27
+ /** Stops the periodic sampler. Called by `Telemetry.shutdown()`. */
24
28
  stop(): void;
25
29
  }
@@ -0,0 +1 @@
1
+ export declare function runtimeReadyAt(): Promise<number | undefined>;
@@ -0,0 +1,10 @@
1
+ import { AsyncStore } from '../../core/store';
2
+ /**
3
+ * AsyncStorage-backed Store for the native build (#89). Reads settle later, so a
4
+ * caller cannot assume a write has landed before the process dies — the crash-loss
5
+ * window narrows here, it does not close. The port keeps that visible.
6
+ *
7
+ * A rejected AsyncStorage call — no native module linked, SQLite/disk full — is
8
+ * `unavailable`, distinct from a key that simply isn't there.
9
+ */
10
+ export declare function nativeStore(): AsyncStore;
@@ -1,3 +1,3 @@
1
1
  import { Sender } from '../core/telemetry';
2
- export declare function nativeSender(endpoint?: string, apiKey?: string): Sender;
3
- export declare function replayFailedNative(endpoint?: string, apiKey?: string): Promise<void>;
2
+ import { Store } from '../core/store';
3
+ export declare function nativeSender(endpoint?: string, apiKey?: string, store?: Store): Sender;
@@ -0,0 +1,21 @@
1
+ import { Telemetry } from '../core/telemetry';
2
+ /**
3
+ * React Navigation route tracking, **shared by both builds** (§4.5.1, #96).
4
+ *
5
+ * `getCurrentRoute()` is a navigation-tree API, not a native one — it works identically on
6
+ * RN-Web — and `route.name` is rung 2 of the name ladder. Wiring it once means one
7
+ * `GROUP BY view.name` spans web and native, instead of web reverse-engineering a template
8
+ * out of a URL the app is already holding in memory.
9
+ *
10
+ * Everything platform-specific stays in `Telemetry.recordRouteChange`, which gates the two
11
+ * deprecated native feeds (§4.11) on an entry-supplied flag.
12
+ *
13
+ * Usage: pass your navigationContainerRef.
14
+ */
15
+ export declare class NavigationRefTracker {
16
+ private telemetry;
17
+ private currentRoute?;
18
+ constructor(telemetry: Telemetry);
19
+ attach(navigationRef: any): void;
20
+ private getActiveRouteName;
21
+ }
@@ -2,5 +2,5 @@ import { Telemetry } from '../core/telemetry';
2
2
  export declare class NavigationTracker {
3
3
  private telemetry;
4
4
  constructor(telemetry: Telemetry);
5
- recordRouteChange(from: string, to: string): void;
5
+ recordRouteChange(from: string, to: string): Promise<void>;
6
6
  }
@@ -4,6 +4,12 @@ export declare class ScreenTimingTracker {
4
4
  private startTimes;
5
5
  private lastScreen?;
6
6
  constructor(telemetry: Telemetry);
7
- startScreen(screen: string): void;
8
- endScreen(screen: string): void;
7
+ startScreen(screen: string): Promise<void>;
8
+ /**
9
+ * Arm the dwell clock without emitting `navigation`. The route-change path (§4.11) has
10
+ * already emitted its own `navigation` with `method: "push"`, and emitting a second one
11
+ * with `method: "screen_start"` for the same transition would double the table.
12
+ */
13
+ markStart(screen: string): void;
14
+ endScreen(screen: string): Promise<void>;
9
15
  }
@@ -0,0 +1,8 @@
1
+ import { ViewManager } from './viewManager';
2
+ export type RuntimeReadyReader = () => Promise<number | undefined>;
3
+ /**
4
+ * Fire-and-forget on purpose: on web the marker arrives with the load event, and blocking init
5
+ * on it would put every host call behind the page's own images. The caller awaits the *module*
6
+ * import instead, so the seed is armed before the instance is visible.
7
+ */
8
+ export declare function seedRuntimeReady(views: ViewManager, read: RuntimeReadyReader, build: string): void;
@@ -0,0 +1,33 @@
1
+ /** Lowercase on purpose: header names are compared case-insensitively (§6.4). */
2
+ export declare const TRACEPARENT = "traceparent";
3
+ /** §6.5's ladder. Precedence is declaration order; **absent means not traced**. */
4
+ export type TraceOutcome = "skipped_off_allowlist" | "skipped_no_cors" | "skipped_consumer_set" | "adopted" | "injected_attributed" | "injected_expired" | "injected_unattributed";
5
+ /**
6
+ * Constructor-only, exact match, empty by default — v4 is **dark on upgrade**, so nobody's
7
+ * CORS breaks until they opt in. Listing a host is the consumer's assertion that that host
8
+ * allows the header; that is why regexes and predicates were rejected rather than deferred.
9
+ */
10
+ export declare function normalizeAllowlist(entries: unknown): ReadonlySet<string>;
11
+ /** Matching is on `hostname` — **ports ignored**, exact-only, no wildcards, no same-origin exemption. */
12
+ export declare function allowsHost(allowlist: ReadonlySet<string>, url: string): boolean;
13
+ /**
14
+ * W3C `traceparent`, version `00`. Used for exactly one decision: is the consumer's own
15
+ * header valid (`adopted`) or not (`skipped_consumer_set`)? An all-zero trace-id or span-id
16
+ * is invalid by the spec. ⚠ Its price, said out loud: Android *repairs* a malformed consumer
17
+ * header; §6.4's never-strip forbids that here, so it goes out broken and is unattributed on
18
+ * both ends — visible in the outcome distribution rather than silently fixed.
19
+ */
20
+ export declare function parseTraceparent(value: string | null | undefined): {
21
+ traceId: string;
22
+ spanId: string;
23
+ } | undefined;
24
+ /** Flags are always `01`: an unsampled session injects no header at all, not a `flags=00` id. */
25
+ export declare function formatTraceparent(traceId: string, spanId: string): string;
26
+ /** Presence check only — the one purpose §6.4's read-scope invariant permits. */
27
+ export declare function readHeader(headers: unknown, name: string): string | undefined;
28
+ /**
29
+ * A **copy** with our header appended. The consumer's `Headers`/`Request`/object is never
30
+ * mutated, which is how never-strip is satisfied by construction rather than by discipline.
31
+ * An array of pairs is a valid `HeadersInit` everywhere, so no `Headers` global is required.
32
+ */
33
+ export declare function withHeader(headers: unknown, name: string, value: string): [string, string][];
@@ -0,0 +1,131 @@
1
+ /** §6.1's four root types. */
2
+ export type TraceRootType = "launch" | "interaction" | "navigation" | "request";
3
+ /** A flat bag of wire keys. An absent key means the SDK had nothing (§6.1). */
4
+ export type TraceAttributes = Record<string, string | number>;
5
+ /** What the interceptor observed at `send()`, and nothing it had to interpret. */
6
+ export type RequestTraceCtx = {
7
+ url: string;
8
+ /** An unsampled session injects no header at all (§6.5). Sampling stays session-level. */
9
+ sampled: boolean;
10
+ /**
11
+ * The consumer's own `traceparent`, captured by the interceptor **at their write time**.
12
+ * Absent (not empty-string) means they set none — never sniffed back off the request.
13
+ */
14
+ consumerTraceparent?: string | null;
15
+ /** fetch-only. `mode` is a fetch concept XHR cannot express, so native never sets it. */
16
+ noCors?: boolean;
17
+ };
18
+ export type RequestTrace = {
19
+ /** The one header value to write, or nothing at all. Never a `flags=00` placeholder. */
20
+ header?: string;
21
+ finish: (endedAt: number) => TraceAttributes;
22
+ };
23
+ export declare class TraceManager {
24
+ private root?;
25
+ /** Bare hosts, punycode-normalized, ports ignored. Empty by default: v4 is dark on upgrade. */
26
+ private readonly allowlist;
27
+ /** Kept whether or not it is still the carrier: `app.start` reports it at any age. */
28
+ private launch;
29
+ /**
30
+ * @param launchStartMs what `app.start` reports as `span.start_time` — and **neither
31
+ * value is a fork time** (§6.2). Web passes `performance.timeOrigin` (true navigation
32
+ * start); native leaves it defaulted to `initialize()`, because everything before the
33
+ * JS bundle loads is invisible and JS cannot see process fork. Do not compare the two
34
+ * platforms' launch envelopes as if they measured the same interval.
35
+ */
36
+ constructor(launchStartMs?: number, traceHostAllowlist?: string[]);
37
+ /**
38
+ * §4.1's `sdk.trace_allowlist_size` — **count only, never the hosts**, so an operator can
39
+ * tell "nobody opted in" apart from "the header is being stripped" without the SDK
40
+ * shipping a customer's internal hostnames to the collector.
41
+ */
42
+ allowlistSize(): number;
43
+ /**
44
+ * `app.start`'s own row — a root, so `span.id === rum.action.id` and there is no
45
+ * `parent.span.id`. No `span.duration_ms` either: roots derive theirs server-side.
46
+ */
47
+ launchRootAttributes(): TraceAttributes;
48
+ /**
49
+ * Tier 1 for a `view`, captured at view **entry** — the action that opened the screen,
50
+ * not the one that closed it (§6.3). A **point span**: `span.start_time` and never
51
+ * `span.duration_ms`, because view dwell is not span duration and a width here would
52
+ * stretch every tap-that-navigates envelope across the whole time the user sat there.
53
+ */
54
+ viewSpan(entryAt: number): TraceAttributes;
55
+ /**
56
+ * Tier 1 for a `ui.interaction`, captured at the click's **mint** (§4.6/§6.2). The one
57
+ * unconditional mint in the table: a tap is a new user action by definition, so it
58
+ * *replaces* whatever root was live rather than joining it — which is what makes the
59
+ * request a tap fires a child of the tap and not of the route change before it.
60
+ *
61
+ * A root, so `span.id === rum.action.id`, no `parent.span.id`, and no `span.duration_ms`
62
+ * (§6.1 — a root's width is derived server-side).
63
+ */
64
+ interactionSpan(mintedAt: number): TraceAttributes;
65
+ /**
66
+ * Tier 1 for an `http.request`, captured at **send** — the root live then is the parent,
67
+ * and `span.start_time` is the send — plus §6.5's outcome ladder and the one header the
68
+ * SDK ever writes. Returns the finisher, so duration is stamped from the same pair of
69
+ * timestamps the row's `http.duration_ms` uses.
70
+ *
71
+ * ponytail: a `send()` that throws synchronously never calls the finisher, so a root it
72
+ * minted lives on for up to 2 s with no row describing it. Rare enough to leave; give
73
+ * the finisher a `discard()` sibling if the orphan ratio ever shows up.
74
+ */
75
+ requestTrace(startedAt: number, ctx: RequestTraceCtx): RequestTrace;
76
+ /**
77
+ * §6.5's ladder above the `injected_*` split, in table order — the precedence *is* the
78
+ * order of these returns. `undefined` means the attribute is omitted entirely: absent
79
+ * means not traced, which is the honest report for a consumer who never opted in and for
80
+ * a sampled-out session (which injects no header at all, not a `flags=00` id).
81
+ */
82
+ private decide;
83
+ /**
84
+ * Tier 2 — annotation-only (§6.3): join the trace without occupying a span. No
85
+ * `span.id`, no parent, no duration, and **no minting** — §6.2 lists the four events
86
+ * that mint and these are not among them, so an untraced crash carries no trace keys
87
+ * rather than inventing a root nothing else will ever join.
88
+ */
89
+ annotate(now?: number): TraceAttributes;
90
+ /**
91
+ * Drop the carrier. Background clears it on both builds, so a resumed app's first fetch
92
+ * mints its own root rather than joining an action from before the user left (§6.2);
93
+ * session rotation clears it because `trace.id` never spans a `session.id` (§6.6).
94
+ */
95
+ clear(): void;
96
+ /**
97
+ * Retire the launch root and mint a replacement, keeping the *reported* launch time —
98
+ * the process really did start then. For a session rotation that lands **before**
99
+ * `app.start`, which is the expired-record cold launch and therefore §4.3's most common
100
+ * path of all.
101
+ *
102
+ * Clearing alone would not do: `app.start` reports the launch root at any age, so its
103
+ * root row would ship under the **new** session while the initial `view` it fathered
104
+ * shipped under the **old** one. `trace.id` would span a `session.id` (§6.6, invariant 2)
105
+ * and `GROUP BY rum.action.id` would never reassemble the launch envelope. Re-minting
106
+ * keeps the retired trace wholly inside the retired session.
107
+ *
108
+ * ⚠ The retired trace then has children and no root row — the initial `view` row is a
109
+ * rootless child. That is the same condition process death already produces by design
110
+ * (a killed view emits nothing), and it is the cheaper of the two wrongs.
111
+ */
112
+ restartLaunchRoot(): void;
113
+ /**
114
+ * Join the live root, or mint one of `mintAs`. A span's *start* extends the root.
115
+ *
116
+ * `attribution` is read **before** `liveRoot()` drops an expired carrier, which is the
117
+ * only place the two unattributed causes are still distinguishable: a carrier that just
118
+ * aged out is `injected_expired` (context lost), no carrier at all is
119
+ * `injected_unattributed` (no action) — the split §6.5 asks the backend to keep.
120
+ */
121
+ private attach;
122
+ private mint;
123
+ /**
124
+ * The carrier if it is still live, dropping it otherwise. Both boundaries are strict —
125
+ * a root sitting exactly on one has not crossed it — matching the session boundaries.
126
+ *
127
+ * A **negative** delta is clamped to expired (§6.7): `Date.now()` can move backwards on
128
+ * an NTP correction, and reading that as "fresh" would hold one root open indefinitely.
129
+ */
130
+ private liveRoot;
131
+ }
@@ -0,0 +1,111 @@
1
+ /** Rungs 2-5 are capped at 64 (§4.6). Rung 1 is not — see `resolveUiName`. */
2
+ export declare const UI_NAME_MAX = 64;
3
+ /**
4
+ * §4.6's three unnamed values, two of which live here. `unnamed` is an **instrumentation
5
+ * gap someone should close**; `surface` is whitespace with nothing to fix. Collapsing them
6
+ * into one recreates the original disease in miniature — `40% unnamed` with no way to tell
7
+ * a missing `data-edge-action-name` from people tapping padding.
8
+ */
9
+ export declare const UI_UNNAMED = "unnamed";
10
+ export declare const UI_SURFACE = "surface";
11
+ /** Six values on web, two (`edge_action | none`) on native (§4.6). */
12
+ export type UiNameSource = "edge_action" | "test_id" | "aria_label" | "title" | "text" | "none";
13
+ /** The bare minimum of an element this module reads. */
14
+ export type UiElement = {
15
+ tagName?: string;
16
+ getAttribute?(name: string): string | null;
17
+ textContent?: string | null;
18
+ };
19
+ export declare function isActionable(el: UiElement | undefined | null): boolean;
20
+ /**
21
+ * Text entry and downloads are excluded from dead-click judgement (§4.6): typing mutates
22
+ * nothing the observer can see, and a download or a `_blank` anchor does its work outside
23
+ * this document entirely. Both would be false accusations.
24
+ */
25
+ export declare function isDeadClickExempt(el: UiElement | undefined | null): boolean;
26
+ /**
27
+ * Rungs 2-5's normalization (§4.6): trim, lowercase, non-alphanumeric → `_`, collapse runs,
28
+ * cap 64. Edge underscores are dropped too, so `"Add to cart!"` is `add_to_cart` and not
29
+ * `add_to_cart_` — a cap or a trailing `!` must not change the name a consumer sees.
30
+ * Returns `""` when nothing survived, which the ladder reads as "this rung had nothing".
31
+ */
32
+ export declare function normalizeUiName(raw: string | null | undefined): string;
33
+ /** What one click resolved to, before the trace and view snapshot are attached. */
34
+ export type UiNameResolution = {
35
+ /** `ui.target` — a derived name, or `unnamed` / `surface` (§4.6). */
36
+ target: string;
37
+ nameSource: UiNameSource;
38
+ /** `ui.tag` — the *resolved* element's tag, lowercased; the click target when none resolved. */
39
+ tag: string;
40
+ /**
41
+ * The node rage identity is keyed on — the resolved element, or the raw target when
42
+ * nothing resolved. **Never the derived name** (§4.6): three clicks on three different
43
+ * `unnamed` divs are indistinguishable on the wire, which is the whole reason rage runs
44
+ * client-side.
45
+ */
46
+ node: unknown;
47
+ /** Whether the click landed on something role-actionable — the `ui.dead` gate. */
48
+ actionable: boolean;
49
+ };
50
+ /**
51
+ * §4.6's five-rung ladder, gated on element role.
52
+ *
53
+ * Rung 1 (`data-edge-action-name`) is scanned over the **whole** path, works on role-less
54
+ * elements, always wins, and passes through **unnormalized and uncapped** — it is explicit
55
+ * author intent and a consumer must be able to predict the value they just set. Rungs 2-5
56
+ * are read off the nearest role-actionable ancestor *only*, and normalized.
57
+ *
58
+ * @param path `composedPath()`, innermost first.
59
+ * @param pointerCursor whether the click target's computed cursor is `pointer` — the one
60
+ * signal that separates `unnamed` (a real control nobody instrumented) from `surface`.
61
+ */
62
+ export declare function resolveUiName(path: readonly UiElement[], opts?: {
63
+ pointerCursor?: boolean;
64
+ }): UiNameResolution;
65
+ /**
66
+ * §4.6's key block, shared so a web click and a native tap cannot drift on a key name.
67
+ * Everything platform-specific is decided by the caller: web resolves the ladder against the
68
+ * DOM, native is handed the name outright.
69
+ *
70
+ * `ui.x`/`ui.y` default to `0` — the key is never-null in §4.6 and a keyboard activation
71
+ * (web) or a `trackTap(name)` (native) genuinely has no viewport coordinate. `ui.rage` is
72
+ * **omitted when false**, deliberately asymmetric with `ui.dead`, which the web caller adds
73
+ * later or not at all.
74
+ */
75
+ export declare function uiAttributes(a: {
76
+ type: "click" | "tap";
77
+ target: string;
78
+ nameSource: UiNameSource;
79
+ tag: string;
80
+ x?: number;
81
+ y?: number;
82
+ rage?: boolean;
83
+ }): Record<string, any>;
84
+ /** §4.6's threshold: **≥3** clicks inside the window. */
85
+ export declare const RAGE_CLICK_THRESHOLD = 3;
86
+ /**
87
+ * §4.6 states the same 1000 ms as the dead-click window, but the two are **not the same
88
+ * constant** and must not be bound: the dead-click window is §4.5.2's `QUIET_WINDOW_MS`
89
+ * because the contract says so, and retuning network settle must not silently retune what
90
+ * counts as user frustration.
91
+ */
92
+ export declare const RAGE_WINDOW_MS = 1000;
93
+ /**
94
+ * ≥3 clicks within a sliding window **on the same live element node**, flagged **once per
95
+ * burst** on the crossing click — so `rage bursts = count of flagged rows` and a fourth and
96
+ * fifth angry click do not each mint their own burst.
97
+ *
98
+ * ⚠ Identity is the node reference the caller hands in, never `ui.target`. The tracker is
99
+ * per-`Telemetry`, so the node references it holds die with the page; it keeps at most
100
+ * `RAGE_CLICK_THRESHOLD` timestamps for one node at a time and drops the node the moment a
101
+ * click lands elsewhere, which is why there is no eviction policy to get wrong.
102
+ */
103
+ export declare class RageTracker {
104
+ private readonly windowMs;
105
+ private node;
106
+ private hits;
107
+ private flagged;
108
+ constructor(windowMs: number);
109
+ /** @returns `true` on the click that crosses the threshold, and only that one. */
110
+ record(node: unknown, now: number): boolean;
111
+ }
@@ -0,0 +1,161 @@
1
+ import { TraceAttributes } from './traceManager';
2
+ /** §4.5.1's ladder. `none` is the literal `"unknown"` name, not an absent one. */
3
+ export type ViewNameSource = "explicit" | "route" | "url" | "none";
4
+ /** §4.5's four `view.load_type` values — one per lifetime boundary that mints a successor. */
5
+ export type ViewLoadType = "initial_load" | "route_change" | "resume" | "session_rotation";
6
+ /** `view.name` before anything has named the view. A literal, never null (§4.5.1). */
7
+ export declare const UNKNOWN_VIEW_NAME = "unknown";
8
+ /**
9
+ * How many retired views `nameOf()` can still name (§3.1). Bounded because an unbounded
10
+ * map is a leak on a long session; 16 because a span outliving that many boundaries is a
11
+ * request §4.5.2's 30 s settle cap has already given up on.
12
+ */
13
+ export declare const MAX_RETIRED_VIEW_NAMES = 16;
14
+ /**
15
+ * All ViewManager needs of core. Structural, so the manager stays unit-testable alone.
16
+ * `trace` is optional for that reason and for that reason only — every real `Telemetry`
17
+ * has one, and a view with no trace source simply carries no §6 keys.
18
+ */
19
+ type Emitter = {
20
+ log(name: string, data?: Record<string, any>): unknown;
21
+ trace?: {
22
+ viewSpan(entryAt: number): TraceAttributes;
23
+ };
24
+ };
25
+ export declare class ViewManager {
26
+ private telemetry;
27
+ private view;
28
+ /** Origin only, and only where there is one — omitted on native (§4.5). */
29
+ private readonly host?;
30
+ /** §4.6's aliveness subscribers. Empty on native and on a web build with no DOM. */
31
+ private readonly activityListeners;
32
+ /** §5.1's boundary subscribers — the frame window, awaited before the successor mints. */
33
+ private readonly boundaryListeners;
34
+ /** Retired `view.id` → final `view.name`, for `nameOf()`. Bounded; oldest evicted. */
35
+ private readonly retiredNames;
36
+ constructor(telemetry: Emitter);
37
+ /** The two keys the Context block carries on every row. Name resolves at log time. */
38
+ get id(): string;
39
+ get name(): string;
40
+ /**
41
+ * §3.1 — `view.name` for a **frozen** `view.id`. A span-carrying row pins itself to the
42
+ * view live at span start, and its name is still resolved at log time, so an
43
+ * `http.request` that outlives a route change reports the departing view's *current*
44
+ * best name rather than a stale copy taken at send.
45
+ *
46
+ * ponytail: a bounded ring of retired names, not a full history. A span outliving
47
+ * `MAX_RETIRED_VIEW_NAMES` boundaries is a request the settle cap (30 s) already gave up
48
+ * on; it degrades to `"unknown"`, which is what a never-named view reports anyway.
49
+ */
50
+ nameOf(id: string): string;
51
+ /**
52
+ * The name ladder (§4.5.1), and the only path that mints a view from a *name*.
53
+ *
54
+ * - a **higher** rung re-stamps the name and never changes `view.id` — that is what
55
+ * "rank beats order" means, and it is why a route name arriving after a URL-derived
56
+ * one upgrades the view in place instead of splitting it in two;
57
+ * - a **lower** rung is ignored outright, whenever it arrives — ⚠ *including its
58
+ * boundary*. A host that mixes rung 1 with `attachNavigation` therefore pins the view
59
+ * to the `screenStart` name until the next `screenStart`: route changes stop minting
60
+ * successors, and dwell keeps accruing under the explicit name. That is what §4.5.1's
61
+ * "a lower rung arriving later does not overwrite a higher one" costs, and the two
62
+ * rungs describing one navigation (the upgrade window) is the case it exists for.
63
+ * It needs a contract ruling, not a local invention — see CLAUDE.md's known gaps;
64
+ * - the **same** rung naming a different screen is a genuine navigation, so it ends the
65
+ * view and mints a successor.
66
+ *
67
+ * Rungs 1 and 2 are handed through unnormalized: a host naming a screen "Step 2 of 3"
68
+ * must not receive "Step {id} of {id}". The caller normalizes rung 3, not this method.
69
+ */
70
+ navigate(name: string, source: ViewNameSource): Promise<void>;
71
+ /** `view.error_count` — `app.crash` rows sharing this `view.id`. A closed enumeration. */
72
+ countError(): void;
73
+ /** `view.action_count` — interaction rows sharing this `view.id`. */
74
+ countAction(): void;
75
+ /**
76
+ * An HTTP request started (§4.5.2). Returns its completion callback, **bound to the view
77
+ * that was live at start** — a request that finishes after a route change belongs to the
78
+ * view it started in and does not hold the arriving one open. The collector's own POST
79
+ * never reaches here; the interceptors filter it before calling.
80
+ */
81
+ requestStarted(now?: number): (endedAt?: number) => void;
82
+ /**
83
+ * §4.6's dead-click gate, in the two places this manager already sees: a request started
84
+ * and a view minted (a route change, a resume, a session rotation). The web tracker adds
85
+ * DOM mutation on top, which it can observe on its own; these two it cannot.
86
+ *
87
+ * Deliberately fires for the background and rotation boundaries too. Those are not
88
+ * navigations the click caused, so counting them as aliveness **under-reports** dead
89
+ * clicks — which is the direction §4.6 requires: `ui.dead` must never falsely accuse.
90
+ *
91
+ * @returns an unsubscribe, so a tracker that is torn down does not keep the manager alive.
92
+ */
93
+ onActivity(fn: () => void): () => void;
94
+ private notifyActivity;
95
+ /**
96
+ * §5.1's view-boundary reset. A subscriber is **awaited inside `beginView`, before the
97
+ * successor replaces the current view**, so anything it emits is stamped with the
98
+ * departing `view.id`. That ordering is the whole feature: a fixed 10 s frame window
99
+ * straddling a route change otherwise charges the departing screen's dropped frames to
100
+ * the arriving one — backwards for the one query the metric exists to serve.
101
+ *
102
+ * ⚠ **It does not fire on the `session_rotation` boundary**, and that is deliberate.
103
+ * `newSession()` installs the new `session.id` *before* minting the successor view, so a
104
+ * row emitted there would carry the new `session.id` with the departing `view.id` — the
105
+ * one thing §4.5's "`view.id` never spans a `session.id`" forbids. Emitting *before* the
106
+ * rotation instead is worse: `logMetric` re-checks session expiry, `lastActivity` is
107
+ * still stale at that point, and the emit would rotate the session a second time. The
108
+ * accepted residue is a frame window that carries across a rotation — at most one
109
+ * unflushed window, of a session that ended by idleness.
110
+ *
111
+ * The successor's load type is handed to the subscriber so it can tell the two firing
112
+ * boundaries apart: `frame_render_time` wants both, while §5.3's running CLS/INP values
113
+ * want **`"resume"` — the background boundary — only**. Emitting a page-load-scoped vital
114
+ * at a soft navigation would ship a second row for the same page load and stamp it with
115
+ * a `view.id` that is not the initial view's.
116
+ *
117
+ * @returns an unsubscribe, so a torn-down subscriber does not keep the manager alive.
118
+ */
119
+ onBoundary(fn: (successorLoadType: ViewLoadType) => unknown): () => void;
120
+ /**
121
+ * The platform's runtime-ready marker — `loadEventEnd` on web,
122
+ * `performance.rnStartupTiming` on native — or `undefined` where the platform has none.
123
+ *
124
+ * Forwarded to whatever view is current, which is safe because a view that was never gated
125
+ * *ignores* the seed — enforced in `NetworkSettle`, not assumed here. On web the `load`
126
+ * event routinely arrives after the first route change, and flooring that view's settle at
127
+ * the page's load time would charge the launch's cost to a route change.
128
+ */
129
+ seedRuntimeReady(at?: number): void;
130
+ /**
131
+ * Emit the `view` event for the current view. Its `view.name` is authoritative and its
132
+ * `view.name_source` reports the *final* rung; the Context-block copy on rows emitted
133
+ * early in the view may still carry a lower rung's name (§4.5.1).
134
+ *
135
+ * `view.loading_time` is **omitted when null**, following this contract's general
136
+ * absent-means-the-SDK-had-nothing discipline (§4.11 names `navigation.from_screen` as the
137
+ * only explicit wire null). `view.loading_time_outcome` always ships: it is what tells the
138
+ * three null causes apart, and reading p75-of-settled beside %-capped is the whole point.
139
+ */
140
+ endView(): Promise<void>;
141
+ /**
142
+ * Mint the successor. Called directly by session rotation, which has to mint *after*
143
+ * the new `session.id` is in place — `view.id` never spans a `session.id` (§4.5).
144
+ * An unnamed successor carries the departing view's name: backgrounding and a session
145
+ * rotation do not move the user off the screen they were on.
146
+ */
147
+ beginView(successorLoadType: ViewLoadType, name?: string, source?: ViewNameSource): Promise<void>;
148
+ /** A boundary that stays inside one session: emit, then mint the successor. */
149
+ exit(successorLoadType: ViewLoadType, name?: string, source?: ViewNameSource): Promise<void>;
150
+ /**
151
+ * The background boundary. The successor is minted immediately — `view.id` must never
152
+ * be absent — but its clock starts paused, so a night spent backgrounded does not land
153
+ * as dwell on whatever screen the user left open.
154
+ */
155
+ background(): Promise<void>;
156
+ /** The matching foreground edge: re-arm the paused clock. */
157
+ foreground(): void;
158
+ private sinceResume;
159
+ private timeSpent;
160
+ }
161
+ export {};