@userflow-tech/react-native 0.4.4 → 0.5.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 (431) hide show
  1. package/README.md +124 -34
  2. package/docs/anchoring.md +6 -1
  3. package/docs/deep-linking.md +26 -0
  4. package/docs/troubleshooting.md +50 -23
  5. package/expo-plugin/hostDependencyWarnings.d.ts +6 -4
  6. package/expo-plugin/hostDependencyWarnings.d.ts.map +1 -1
  7. package/expo-plugin/hostDependencyWarnings.js +8 -6
  8. package/expo-plugin/hostDependencyWarnings.js.map +1 -1
  9. package/lib/commonjs/anchors/AutoAnchorRegistry.js +144 -34
  10. package/lib/commonjs/anchors/AutoAnchorRegistry.js.map +1 -1
  11. package/lib/commonjs/anchors/animationFrame.js +29 -1
  12. package/lib/commonjs/anchors/animationFrame.js.map +1 -1
  13. package/lib/commonjs/anchors/appActive.js +61 -0
  14. package/lib/commonjs/anchors/appActive.js.map +1 -0
  15. package/lib/commonjs/anchors/elementConditionEvaluator.js +25 -8
  16. package/lib/commonjs/anchors/elementConditionEvaluator.js.map +1 -1
  17. package/lib/commonjs/anchors/elementTapMonitor.js +67 -12
  18. package/lib/commonjs/anchors/elementTapMonitor.js.map +1 -1
  19. package/lib/commonjs/anchors/elementTargetMatch.js +33 -7
  20. package/lib/commonjs/anchors/elementTargetMatch.js.map +1 -1
  21. package/lib/commonjs/anchors/fiber/fiberInternals.js +66 -0
  22. package/lib/commonjs/anchors/fiber/fiberInternals.js.map +1 -1
  23. package/lib/commonjs/anchors/fiber/indexHostFibers.js +4 -3
  24. package/lib/commonjs/anchors/fiber/indexHostFibers.js.map +1 -1
  25. package/lib/commonjs/anchors/fiber/resolveFiberPath.js +30 -15
  26. package/lib/commonjs/anchors/fiber/resolveFiberPath.js.map +1 -1
  27. package/lib/commonjs/anchors/fiberAnchorCache.js +20 -0
  28. package/lib/commonjs/anchors/fiberAnchorCache.js.map +1 -1
  29. package/lib/commonjs/anchors/gestureHandlerHandoff.js +304 -0
  30. package/lib/commonjs/anchors/gestureHandlerHandoff.js.map +1 -0
  31. package/lib/commonjs/anchors/resolveAnchorHost.js +6 -0
  32. package/lib/commonjs/anchors/resolveAnchorHost.js.map +1 -1
  33. package/lib/commonjs/anchors/resolveAutoAnchorHost.js +11 -6
  34. package/lib/commonjs/anchors/resolveAutoAnchorHost.js.map +1 -1
  35. package/lib/commonjs/anchors/resolveElementTargets.js +63 -8
  36. package/lib/commonjs/anchors/resolveElementTargets.js.map +1 -1
  37. package/lib/commonjs/anchors/semanticMatcher.js +43 -21
  38. package/lib/commonjs/anchors/semanticMatcher.js.map +1 -1
  39. package/lib/commonjs/anchors/tapSlop.js +16 -0
  40. package/lib/commonjs/anchors/tapSlop.js.map +1 -0
  41. package/lib/commonjs/anchors/targetPressMonitor.js +66 -12
  42. package/lib/commonjs/anchors/targetPressMonitor.js.map +1 -1
  43. package/lib/commonjs/authoring/fingerprintFromInstance.js +5 -9
  44. package/lib/commonjs/authoring/fingerprintFromInstance.js.map +1 -1
  45. package/lib/commonjs/content/RichContentRenderer.js +3 -3
  46. package/lib/commonjs/content/RichContentRenderer.js.map +1 -1
  47. package/lib/commonjs/content/openSafeUrl.js +3 -1
  48. package/lib/commonjs/content/openSafeUrl.js.map +1 -1
  49. package/lib/commonjs/expo-router/useUserflowExpoRouter.js +6 -0
  50. package/lib/commonjs/expo-router/useUserflowExpoRouter.js.map +1 -1
  51. package/lib/commonjs/index.js +19 -5
  52. package/lib/commonjs/index.js.map +1 -1
  53. package/lib/commonjs/installCheck.js +2 -2
  54. package/lib/commonjs/installCheck.js.map +1 -1
  55. package/lib/commonjs/navigation/navigateUrlUtils.js +105 -0
  56. package/lib/commonjs/navigation/navigateUrlUtils.js.map +1 -0
  57. package/lib/commonjs/navigation/openExternalNavigateUrl.js +14 -0
  58. package/lib/commonjs/navigation/openExternalNavigateUrl.js.map +1 -0
  59. package/lib/commonjs/navigation/openFlowNavigateUrl.js +47 -41
  60. package/lib/commonjs/navigation/openFlowNavigateUrl.js.map +1 -1
  61. package/lib/commonjs/pairing/PairingController.js +3 -1
  62. package/lib/commonjs/pairing/PairingController.js.map +1 -1
  63. package/lib/commonjs/runtime/BannerHost.js +38 -29
  64. package/lib/commonjs/runtime/BannerHost.js.map +1 -1
  65. package/lib/commonjs/runtime/TargetPressBridge.js +55 -7
  66. package/lib/commonjs/runtime/TargetPressBridge.js.map +1 -1
  67. package/lib/commonjs/runtime/UserflowOverlay.js +5 -11
  68. package/lib/commonjs/runtime/UserflowOverlay.js.map +1 -1
  69. package/lib/commonjs/runtime/UserflowProvider.js +66 -40
  70. package/lib/commonjs/runtime/UserflowProvider.js.map +1 -1
  71. package/lib/commonjs/runtime/bannerEngine.js +8 -2
  72. package/lib/commonjs/runtime/bannerEngine.js.map +1 -1
  73. package/lib/commonjs/runtime/bannerFlowSpacer.js +71 -0
  74. package/lib/commonjs/runtime/bannerFlowSpacer.js.map +1 -0
  75. package/lib/commonjs/runtime/flowEngine.js +11 -0
  76. package/lib/commonjs/runtime/flowEngine.js.map +1 -1
  77. package/lib/commonjs/runtime/previewStart.js +39 -0
  78. package/lib/commonjs/runtime/previewStart.js.map +1 -0
  79. package/lib/commonjs/runtime/pushBannerLayoutContext.js +98 -0
  80. package/lib/commonjs/runtime/pushBannerLayoutContext.js.map +1 -0
  81. package/lib/commonjs/screenTracker.js +12 -0
  82. package/lib/commonjs/screenTracker.js.map +1 -1
  83. package/lib/commonjs/surfaces/BannerBar.js +79 -61
  84. package/lib/commonjs/surfaces/BannerBar.js.map +1 -1
  85. package/lib/commonjs/surfaces/BannerSurface.js +26 -0
  86. package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
  87. package/lib/commonjs/surfaces/TooltipSurface.js +43 -41
  88. package/lib/commonjs/surfaces/TooltipSurface.js.map +1 -1
  89. package/lib/commonjs/surfaces/bannerBarLayout.js +77 -10
  90. package/lib/commonjs/surfaces/bannerBarLayout.js.map +1 -1
  91. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js +112 -63
  92. package/lib/commonjs/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  93. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
  94. package/lib/commonjs/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  95. package/lib/commonjs/surfaces/hostAccessibilityTrap.js +28 -0
  96. package/lib/commonjs/surfaces/hostAccessibilityTrap.js.map +1 -0
  97. package/lib/commonjs/surfaces/resolveBannerTheme.js +6 -3
  98. package/lib/commonjs/surfaces/resolveBannerTheme.js.map +1 -1
  99. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js +3 -2
  100. package/lib/commonjs/surfaces/tooltip/TooltipBubble.js.map +1 -1
  101. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js +134 -0
  102. package/lib/commonjs/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  103. package/lib/commonjs/version.js +1 -1
  104. package/lib/module/anchors/AutoAnchorRegistry.js +145 -35
  105. package/lib/module/anchors/AutoAnchorRegistry.js.map +1 -1
  106. package/lib/module/anchors/animationFrame.js +29 -2
  107. package/lib/module/anchors/animationFrame.js.map +1 -1
  108. package/lib/module/anchors/appActive.js +57 -0
  109. package/lib/module/anchors/appActive.js.map +1 -0
  110. package/lib/module/anchors/elementConditionEvaluator.js +26 -9
  111. package/lib/module/anchors/elementConditionEvaluator.js.map +1 -1
  112. package/lib/module/anchors/elementTapMonitor.js +67 -12
  113. package/lib/module/anchors/elementTapMonitor.js.map +1 -1
  114. package/lib/module/anchors/elementTargetMatch.js +32 -7
  115. package/lib/module/anchors/elementTargetMatch.js.map +1 -1
  116. package/lib/module/anchors/fiber/fiberInternals.js +65 -0
  117. package/lib/module/anchors/fiber/fiberInternals.js.map +1 -1
  118. package/lib/module/anchors/fiber/indexHostFibers.js +4 -3
  119. package/lib/module/anchors/fiber/indexHostFibers.js.map +1 -1
  120. package/lib/module/anchors/fiber/resolveFiberPath.js +30 -15
  121. package/lib/module/anchors/fiber/resolveFiberPath.js.map +1 -1
  122. package/lib/module/anchors/fiberAnchorCache.js +19 -0
  123. package/lib/module/anchors/fiberAnchorCache.js.map +1 -1
  124. package/lib/module/anchors/gestureHandlerHandoff.js +298 -0
  125. package/lib/module/anchors/gestureHandlerHandoff.js.map +1 -0
  126. package/lib/module/anchors/resolveAnchorHost.js +1 -0
  127. package/lib/module/anchors/resolveAnchorHost.js.map +1 -1
  128. package/lib/module/anchors/resolveAutoAnchorHost.js +11 -6
  129. package/lib/module/anchors/resolveAutoAnchorHost.js.map +1 -1
  130. package/lib/module/anchors/resolveElementTargets.js +63 -8
  131. package/lib/module/anchors/resolveElementTargets.js.map +1 -1
  132. package/lib/module/anchors/semanticMatcher.js +43 -21
  133. package/lib/module/anchors/semanticMatcher.js.map +1 -1
  134. package/lib/module/anchors/tapSlop.js +11 -0
  135. package/lib/module/anchors/tapSlop.js.map +1 -0
  136. package/lib/module/anchors/targetPressMonitor.js +53 -8
  137. package/lib/module/anchors/targetPressMonitor.js.map +1 -1
  138. package/lib/module/authoring/fingerprintFromInstance.js +6 -10
  139. package/lib/module/authoring/fingerprintFromInstance.js.map +1 -1
  140. package/lib/module/content/RichContentRenderer.js +3 -3
  141. package/lib/module/content/RichContentRenderer.js.map +1 -1
  142. package/lib/module/content/openSafeUrl.js +3 -1
  143. package/lib/module/content/openSafeUrl.js.map +1 -1
  144. package/lib/module/expo-router/useUserflowExpoRouter.js +7 -1
  145. package/lib/module/expo-router/useUserflowExpoRouter.js.map +1 -1
  146. package/lib/module/index.js +22 -7
  147. package/lib/module/index.js.map +1 -1
  148. package/lib/module/installCheck.js +2 -2
  149. package/lib/module/installCheck.js.map +1 -1
  150. package/lib/module/navigation/navigateUrlUtils.js +94 -0
  151. package/lib/module/navigation/navigateUrlUtils.js.map +1 -0
  152. package/lib/module/navigation/openExternalNavigateUrl.js +11 -0
  153. package/lib/module/navigation/openExternalNavigateUrl.js.map +1 -0
  154. package/lib/module/navigation/openFlowNavigateUrl.js +22 -36
  155. package/lib/module/navigation/openFlowNavigateUrl.js.map +1 -1
  156. package/lib/module/pairing/PairingController.js +3 -1
  157. package/lib/module/pairing/PairingController.js.map +1 -1
  158. package/lib/module/runtime/BannerHost.js +38 -29
  159. package/lib/module/runtime/BannerHost.js.map +1 -1
  160. package/lib/module/runtime/TargetPressBridge.js +56 -8
  161. package/lib/module/runtime/TargetPressBridge.js.map +1 -1
  162. package/lib/module/runtime/UserflowOverlay.js +5 -11
  163. package/lib/module/runtime/UserflowOverlay.js.map +1 -1
  164. package/lib/module/runtime/UserflowProvider.js +66 -40
  165. package/lib/module/runtime/UserflowProvider.js.map +1 -1
  166. package/lib/module/runtime/bannerEngine.js +7 -2
  167. package/lib/module/runtime/bannerEngine.js.map +1 -1
  168. package/lib/module/runtime/bannerFlowSpacer.js +65 -0
  169. package/lib/module/runtime/bannerFlowSpacer.js.map +1 -0
  170. package/lib/module/runtime/flowEngine.js +11 -0
  171. package/lib/module/runtime/flowEngine.js.map +1 -1
  172. package/lib/module/runtime/previewStart.js +35 -0
  173. package/lib/module/runtime/previewStart.js.map +1 -0
  174. package/lib/module/runtime/pushBannerLayoutContext.js +91 -0
  175. package/lib/module/runtime/pushBannerLayoutContext.js.map +1 -0
  176. package/lib/module/screenTracker.js +11 -0
  177. package/lib/module/screenTracker.js.map +1 -1
  178. package/lib/module/surfaces/BannerBar.js +80 -62
  179. package/lib/module/surfaces/BannerBar.js.map +1 -1
  180. package/lib/module/surfaces/BannerSurface.js +26 -0
  181. package/lib/module/surfaces/BannerSurface.js.map +1 -1
  182. package/lib/module/surfaces/TooltipSurface.js +44 -42
  183. package/lib/module/surfaces/TooltipSurface.js.map +1 -1
  184. package/lib/module/surfaces/bannerBarLayout.js +72 -11
  185. package/lib/module/surfaces/bannerBarLayout.js.map +1 -1
  186. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js +113 -64
  187. package/lib/module/surfaces/bannerWebView/BannerWebViewBar.js.map +1 -1
  188. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js +59 -11
  189. package/lib/module/surfaces/bannerWebView/buildCustomBannerHtml.js.map +1 -1
  190. package/lib/module/surfaces/hostAccessibilityTrap.js +24 -0
  191. package/lib/module/surfaces/hostAccessibilityTrap.js.map +1 -0
  192. package/lib/module/surfaces/resolveBannerTheme.js +6 -3
  193. package/lib/module/surfaces/resolveBannerTheme.js.map +1 -1
  194. package/lib/module/surfaces/tooltip/TooltipBubble.js +3 -2
  195. package/lib/module/surfaces/tooltip/TooltipBubble.js.map +1 -1
  196. package/lib/module/surfaces/tooltip/targetSearchSchedule.js +128 -0
  197. package/lib/module/surfaces/tooltip/targetSearchSchedule.js.map +1 -0
  198. package/lib/module/version.js +1 -1
  199. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  200. package/lib/typescript/commonjs/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts +0 -4
  202. package/lib/typescript/commonjs/src/anchors/animationFrame.d.ts.map +1 -1
  203. package/lib/typescript/commonjs/src/anchors/appActive.d.ts +8 -0
  204. package/lib/typescript/commonjs/src/anchors/appActive.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts +17 -0
  206. package/lib/typescript/commonjs/src/anchors/elementTapMonitor.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts +7 -0
  208. package/lib/typescript/commonjs/src/anchors/elementTargetMatch.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts +7 -0
  210. package/lib/typescript/commonjs/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  211. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  212. package/lib/typescript/commonjs/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  213. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  214. package/lib/typescript/commonjs/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  215. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts +5 -0
  216. package/lib/typescript/commonjs/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  217. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  218. package/lib/typescript/commonjs/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  219. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts +1 -0
  220. package/lib/typescript/commonjs/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  221. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  222. package/lib/typescript/commonjs/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  223. package/lib/typescript/commonjs/src/anchors/resolveElementTargets.d.ts.map +1 -1
  224. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts +2 -0
  225. package/lib/typescript/commonjs/src/anchors/semanticMatcher.d.ts.map +1 -1
  226. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts +9 -0
  227. package/lib/typescript/commonjs/src/anchors/tapSlop.d.ts.map +1 -0
  228. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts +13 -8
  229. package/lib/typescript/commonjs/src/anchors/targetPressMonitor.d.ts.map +1 -1
  230. package/lib/typescript/commonjs/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  231. package/lib/typescript/commonjs/src/content/RichContentRenderer.d.ts.map +1 -1
  232. package/lib/typescript/commonjs/src/content/openSafeUrl.d.ts.map +1 -1
  233. package/lib/typescript/commonjs/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  234. package/lib/typescript/commonjs/src/index.d.ts +6 -5
  235. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  236. package/lib/typescript/commonjs/src/installCheck.d.ts +1 -1
  237. package/lib/typescript/commonjs/src/installCheck.d.ts.map +1 -1
  238. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts +15 -0
  239. package/lib/typescript/commonjs/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  240. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  241. package/lib/typescript/commonjs/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  242. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  243. package/lib/typescript/commonjs/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  244. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts +6 -0
  245. package/lib/typescript/commonjs/src/pairing/PairingController.d.ts.map +1 -1
  246. package/lib/typescript/commonjs/src/runtime/BannerHost.d.ts.map +1 -1
  247. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts +3 -0
  248. package/lib/typescript/commonjs/src/runtime/TargetPressBridge.d.ts.map +1 -1
  249. package/lib/typescript/commonjs/src/runtime/UserflowOverlay.d.ts.map +1 -1
  250. package/lib/typescript/commonjs/src/runtime/UserflowProvider.d.ts.map +1 -1
  251. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts +5 -1
  252. package/lib/typescript/commonjs/src/runtime/bannerEngine.d.ts.map +1 -1
  253. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts +15 -0
  254. package/lib/typescript/commonjs/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  255. package/lib/typescript/commonjs/src/runtime/flowEngine.d.ts.map +1 -1
  256. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts +13 -0
  257. package/lib/typescript/commonjs/src/runtime/previewStart.d.ts.map +1 -0
  258. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts +17 -0
  259. package/lib/typescript/commonjs/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  260. package/lib/typescript/commonjs/src/screenTracker.d.ts +6 -0
  261. package/lib/typescript/commonjs/src/screenTracker.d.ts.map +1 -1
  262. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts +3 -1
  263. package/lib/typescript/commonjs/src/surfaces/BannerBar.d.ts.map +1 -1
  264. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts +5 -1
  265. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
  266. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts +1 -1
  267. package/lib/typescript/commonjs/src/surfaces/TooltipSurface.d.ts.map +1 -1
  268. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts +17 -0
  269. package/lib/typescript/commonjs/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  270. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  271. package/lib/typescript/commonjs/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  272. package/lib/typescript/commonjs/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  273. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  274. package/lib/typescript/commonjs/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  275. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts +4 -1
  276. package/lib/typescript/commonjs/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  277. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  278. package/lib/typescript/commonjs/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  279. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  280. package/lib/typescript/commonjs/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  281. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  282. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts +36 -2
  283. package/lib/typescript/module/src/anchors/AutoAnchorRegistry.d.ts.map +1 -1
  284. package/lib/typescript/module/src/anchors/animationFrame.d.ts +0 -4
  285. package/lib/typescript/module/src/anchors/animationFrame.d.ts.map +1 -1
  286. package/lib/typescript/module/src/anchors/appActive.d.ts +8 -0
  287. package/lib/typescript/module/src/anchors/appActive.d.ts.map +1 -0
  288. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts +17 -0
  289. package/lib/typescript/module/src/anchors/elementTapMonitor.d.ts.map +1 -1
  290. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts +7 -0
  291. package/lib/typescript/module/src/anchors/elementTargetMatch.d.ts.map +1 -1
  292. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts +7 -0
  293. package/lib/typescript/module/src/anchors/fiber/fiberInternals.d.ts.map +1 -1
  294. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts +3 -2
  295. package/lib/typescript/module/src/anchors/fiber/indexHostFibers.d.ts.map +1 -1
  296. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts +3 -0
  297. package/lib/typescript/module/src/anchors/fiber/resolveFiberPath.d.ts.map +1 -1
  298. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts +5 -0
  299. package/lib/typescript/module/src/anchors/fiberAnchorCache.d.ts.map +1 -1
  300. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts +70 -0
  301. package/lib/typescript/module/src/anchors/gestureHandlerHandoff.d.ts.map +1 -0
  302. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts +1 -0
  303. package/lib/typescript/module/src/anchors/resolveAnchorHost.d.ts.map +1 -1
  304. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts +11 -4
  305. package/lib/typescript/module/src/anchors/resolveAutoAnchorHost.d.ts.map +1 -1
  306. package/lib/typescript/module/src/anchors/resolveElementTargets.d.ts.map +1 -1
  307. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts +2 -0
  308. package/lib/typescript/module/src/anchors/semanticMatcher.d.ts.map +1 -1
  309. package/lib/typescript/module/src/anchors/tapSlop.d.ts +9 -0
  310. package/lib/typescript/module/src/anchors/tapSlop.d.ts.map +1 -0
  311. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts +13 -8
  312. package/lib/typescript/module/src/anchors/targetPressMonitor.d.ts.map +1 -1
  313. package/lib/typescript/module/src/authoring/fingerprintFromInstance.d.ts.map +1 -1
  314. package/lib/typescript/module/src/content/RichContentRenderer.d.ts.map +1 -1
  315. package/lib/typescript/module/src/content/openSafeUrl.d.ts.map +1 -1
  316. package/lib/typescript/module/src/expo-router/useUserflowExpoRouter.d.ts.map +1 -1
  317. package/lib/typescript/module/src/index.d.ts +6 -5
  318. package/lib/typescript/module/src/index.d.ts.map +1 -1
  319. package/lib/typescript/module/src/installCheck.d.ts +1 -1
  320. package/lib/typescript/module/src/installCheck.d.ts.map +1 -1
  321. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts +15 -0
  322. package/lib/typescript/module/src/navigation/navigateUrlUtils.d.ts.map +1 -0
  323. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts +3 -0
  324. package/lib/typescript/module/src/navigation/openExternalNavigateUrl.d.ts.map +1 -0
  325. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts +2 -5
  326. package/lib/typescript/module/src/navigation/openFlowNavigateUrl.d.ts.map +1 -1
  327. package/lib/typescript/module/src/pairing/PairingController.d.ts +6 -0
  328. package/lib/typescript/module/src/pairing/PairingController.d.ts.map +1 -1
  329. package/lib/typescript/module/src/runtime/BannerHost.d.ts.map +1 -1
  330. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts +3 -0
  331. package/lib/typescript/module/src/runtime/TargetPressBridge.d.ts.map +1 -1
  332. package/lib/typescript/module/src/runtime/UserflowOverlay.d.ts.map +1 -1
  333. package/lib/typescript/module/src/runtime/UserflowProvider.d.ts.map +1 -1
  334. package/lib/typescript/module/src/runtime/bannerEngine.d.ts +5 -1
  335. package/lib/typescript/module/src/runtime/bannerEngine.d.ts.map +1 -1
  336. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts +15 -0
  337. package/lib/typescript/module/src/runtime/bannerFlowSpacer.d.ts.map +1 -0
  338. package/lib/typescript/module/src/runtime/flowEngine.d.ts.map +1 -1
  339. package/lib/typescript/module/src/runtime/previewStart.d.ts +13 -0
  340. package/lib/typescript/module/src/runtime/previewStart.d.ts.map +1 -0
  341. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts +17 -0
  342. package/lib/typescript/module/src/runtime/pushBannerLayoutContext.d.ts.map +1 -0
  343. package/lib/typescript/module/src/screenTracker.d.ts +6 -0
  344. package/lib/typescript/module/src/screenTracker.d.ts.map +1 -1
  345. package/lib/typescript/module/src/surfaces/BannerBar.d.ts +3 -1
  346. package/lib/typescript/module/src/surfaces/BannerBar.d.ts.map +1 -1
  347. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts +5 -1
  348. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
  349. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts +1 -1
  350. package/lib/typescript/module/src/surfaces/TooltipSurface.d.ts.map +1 -1
  351. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts +17 -0
  352. package/lib/typescript/module/src/surfaces/bannerBarLayout.d.ts.map +1 -1
  353. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts +3 -1
  354. package/lib/typescript/module/src/surfaces/bannerWebView/BannerWebViewBar.d.ts.map +1 -1
  355. package/lib/typescript/module/src/surfaces/bannerWebView/buildCustomBannerHtml.d.ts.map +1 -1
  356. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts +14 -0
  357. package/lib/typescript/module/src/surfaces/hostAccessibilityTrap.d.ts.map +1 -0
  358. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts +4 -1
  359. package/lib/typescript/module/src/surfaces/resolveBannerTheme.d.ts.map +1 -1
  360. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts +7 -1
  361. package/lib/typescript/module/src/surfaces/tooltip/TooltipBubble.d.ts.map +1 -1
  362. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts +60 -0
  363. package/lib/typescript/module/src/surfaces/tooltip/targetSearchSchedule.d.ts.map +1 -0
  364. package/lib/typescript/module/src/version.d.ts +1 -1
  365. package/package.json +1 -1
  366. package/src/anchors/AutoAnchorRegistry.ts +166 -35
  367. package/src/anchors/animationFrame.ts +31 -1
  368. package/src/anchors/appActive.ts +63 -0
  369. package/src/anchors/elementConditionEvaluator.ts +29 -12
  370. package/src/anchors/elementTapMonitor.ts +68 -10
  371. package/src/anchors/elementTargetMatch.ts +34 -7
  372. package/src/anchors/fiber/fiberInternals.ts +68 -0
  373. package/src/anchors/fiber/indexHostFibers.ts +8 -3
  374. package/src/anchors/fiber/resolveFiberPath.ts +25 -16
  375. package/src/anchors/fiberAnchorCache.ts +19 -0
  376. package/src/anchors/gestureHandlerHandoff.ts +356 -0
  377. package/src/anchors/resolveAnchorHost.ts +2 -0
  378. package/src/anchors/resolveAutoAnchorHost.ts +18 -8
  379. package/src/anchors/resolveElementTargets.ts +64 -7
  380. package/src/anchors/semanticMatcher.ts +48 -27
  381. package/src/anchors/tapSlop.ts +18 -0
  382. package/src/anchors/targetPressMonitor.ts +56 -19
  383. package/src/authoring/fingerprintFromInstance.ts +4 -5
  384. package/src/content/RichContentRenderer.tsx +4 -3
  385. package/src/content/openSafeUrl.ts +3 -1
  386. package/src/expo-router/useUserflowExpoRouter.ts +10 -1
  387. package/src/index.ts +20 -9
  388. package/src/installCheck.ts +4 -4
  389. package/src/navigation/navigateUrlUtils.ts +100 -0
  390. package/src/navigation/openExternalNavigateUrl.ts +9 -0
  391. package/src/navigation/openFlowNavigateUrl.ts +36 -36
  392. package/src/pairing/PairingController.ts +9 -1
  393. package/src/runtime/BannerHost.tsx +41 -23
  394. package/src/runtime/TargetPressBridge.tsx +66 -9
  395. package/src/runtime/UserflowOverlay.tsx +7 -9
  396. package/src/runtime/UserflowProvider.tsx +56 -32
  397. package/src/runtime/bannerEngine.ts +10 -2
  398. package/src/runtime/bannerFlowSpacer.tsx +70 -0
  399. package/src/runtime/flowEngine.ts +11 -0
  400. package/src/runtime/previewStart.ts +49 -0
  401. package/src/runtime/pushBannerLayoutContext.tsx +121 -0
  402. package/src/screenTracker.ts +11 -0
  403. package/src/surfaces/BannerBar.tsx +93 -55
  404. package/src/surfaces/BannerSurface.tsx +59 -3
  405. package/src/surfaces/TooltipSurface.tsx +63 -51
  406. package/src/surfaces/bannerBarLayout.ts +80 -7
  407. package/src/surfaces/bannerWebView/BannerWebViewBar.tsx +149 -57
  408. package/src/surfaces/bannerWebView/buildCustomBannerHtml.ts +63 -11
  409. package/src/surfaces/hostAccessibilityTrap.ts +25 -0
  410. package/src/surfaces/resolveBannerTheme.ts +14 -3
  411. package/src/surfaces/tooltip/TooltipBubble.tsx +8 -1
  412. package/src/surfaces/tooltip/targetSearchSchedule.ts +140 -0
  413. package/src/version.ts +1 -1
  414. package/vendor/core/dist/engine/BannerSessionEngine.d.ts +29 -2
  415. package/vendor/core/dist/engine/BannerSessionEngine.d.ts.map +1 -1
  416. package/vendor/core/dist/engine/BannerSessionEngine.js +87 -6
  417. package/vendor/core/dist/engine/BannerSessionEngine.js.map +1 -1
  418. package/vendor/core/dist/engine/ClientConditionEngine.d.ts.map +1 -1
  419. package/vendor/core/dist/engine/ClientConditionEngine.js +30 -7
  420. package/vendor/core/dist/engine/ClientConditionEngine.js.map +1 -1
  421. package/vendor/core/dist/engine/PairingEngine.d.ts +6 -1
  422. package/vendor/core/dist/engine/PairingEngine.d.ts.map +1 -1
  423. package/vendor/core/dist/engine/PairingEngine.js +23 -3
  424. package/vendor/core/dist/engine/PairingEngine.js.map +1 -1
  425. package/vendor/core/dist/engine/handleActions.d.ts.map +1 -1
  426. package/vendor/core/dist/engine/handleActions.js +67 -2
  427. package/vendor/core/dist/engine/handleActions.js.map +1 -1
  428. package/vendor/core/dist/version.d.ts +1 -1
  429. package/vendor/core/dist/version.js +1 -1
  430. package/vendor/core/package.json +1 -1
  431. package/vendor/protocol/package.json +1 -1
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Taps that react-native-gesture-handler takes away from React Native.
3
+ *
4
+ * When a gesture-handler control activates, it cancels RN's touch stream, so
5
+ * the bridge sees `touchcancel` instead of `touchend`. The handler tag is read
6
+ * off the touched fiber (no import of gesture-handler), and the touch counts
7
+ * as a tap only if that handler later ends from ACTIVE on
8
+ * `onGestureHandlerStateChange`. Apps that never hit this path never subscribe.
9
+ *
10
+ * State numbers are gesture-handler's `State` enum. A future major that
11
+ * renumbers them degrades to "no tap", which is today's behavior.
12
+ */
13
+ import type { AnyFiber } from './fiber/fiberInternals';
14
+ import {
15
+ getMemoizedProps,
16
+ getReturn,
17
+ getStateNode,
18
+ MAX_ANCESTOR_WALK,
19
+ } from './fiber/fiberInternals';
20
+ import { type TouchPoint, withinTapSlop } from './tapSlop';
21
+
22
+ /** gesture-handler `State` values this hand-off acts on. */
23
+ export const GESTURE_HANDLER_STATE = {
24
+ FAILED: 1,
25
+ CANCELLED: 3,
26
+ ACTIVE: 4,
27
+ END: 5,
28
+ } as const;
29
+
30
+ /** Drop a handed-off touch that never reaches a terminal state. */
31
+ export const GESTURE_HANDLER_HANDOFF_TIMEOUT_MS = 10_000;
32
+
33
+ const STATE_CHANGE_EVENT = 'onGestureHandlerStateChange';
34
+
35
+ export interface HandedOffTouch {
36
+ identifier: number | string;
37
+ pageX: number;
38
+ pageY: number;
39
+ handlerTags: readonly number[];
40
+ /**
41
+ * The gesture completed as a tap. `end` is set when the payload carried
42
+ * window coordinates; otherwise only the start point is known.
43
+ */
44
+ onResolve: (end: TouchPoint | null) => void;
45
+ }
46
+
47
+ interface DeviceEventSubscription {
48
+ remove: () => void;
49
+ }
50
+
51
+ interface DeviceEventEmitterLike {
52
+ addListener(event: string, listener: (payload: unknown) => void): DeviceEventSubscription;
53
+ }
54
+
55
+ interface GestureStatePayload {
56
+ handlerTag: number;
57
+ state: number;
58
+ oldState?: number;
59
+ pointerInside?: boolean;
60
+ translationX?: number;
61
+ translationY?: number;
62
+ absoluteX?: number;
63
+ absoluteY?: number;
64
+ /** Present only so a long-press payload can be told apart from a tap. */
65
+ hasDuration: boolean;
66
+ }
67
+
68
+ interface PendingTouch extends HandedOffTouch {
69
+ id: number;
70
+ settled: boolean;
71
+ /** Tags that already reached a terminal state without counting as a tap. */
72
+ finishedTags: Set<number>;
73
+ timer: ReturnType<typeof setTimeout>;
74
+ }
75
+
76
+ /**
77
+ * Handler tags for the control under `fiber`.
78
+ *
79
+ * Old-API buttons keep `handlerTag` on the class instance. `GestureDetector`
80
+ * keeps a `gesture` prop whose `toGestureArray()` entries each have one.
81
+ * Negative tags (gesture-handler's own root helper) are ignored.
82
+ *
83
+ * The walk stops at the nearest fiber that has a tag. A native handler with
84
+ * `disallowInterruption` (gesture-handler `ScrollView`, `FlatList`,
85
+ * `RefreshControl`) is a container, not a tap, so a scroll that starts inside
86
+ * it is dropped instead of latching the row. Never throws.
87
+ */
88
+ export function collectGestureHandlerTags(fiber: AnyFiber | null): number[] {
89
+ if (fiber == null) return [];
90
+ try {
91
+ let current: AnyFiber | null = fiber;
92
+ let steps = 0;
93
+ while (current && steps < MAX_ANCESTOR_WALK) {
94
+ steps += 1;
95
+ const found = tagsOnFiber(current);
96
+ if (found.pressable.length > 0) return found.pressable;
97
+ // The nearest handler is a scroll container. An outer control did not
98
+ // receive this touch.
99
+ if (found.container) return [];
100
+ current = getReturn(current);
101
+ }
102
+ } catch {
103
+ return [];
104
+ }
105
+ return [];
106
+ }
107
+
108
+ export class GestureHandlerHandoff {
109
+ private readonly pending = new Map<number, PendingTouch>();
110
+ private nextId = 1;
111
+ private subscription: DeviceEventSubscription | null = null;
112
+
113
+ /**
114
+ * Keep `touch` until its handler finishes or it expires.
115
+ * Returns a stop function; calling it drops the touch without a tap.
116
+ */
117
+ track(touch: HandedOffTouch): () => void {
118
+ if (touch.handlerTags.length === 0) return () => {};
119
+ const id = this.nextId;
120
+ this.nextId += 1;
121
+ const pending: PendingTouch = {
122
+ ...touch,
123
+ id,
124
+ settled: false,
125
+ finishedTags: new Set(),
126
+ timer: setTimeout(() => {
127
+ this.drop(id);
128
+ }, GESTURE_HANDLER_HANDOFF_TIMEOUT_MS),
129
+ };
130
+ this.pending.set(id, pending);
131
+ this.ensureListening();
132
+ return () => this.drop(id);
133
+ }
134
+
135
+ /** Test helper. Drops every pending touch and unsubscribes. */
136
+ resetForTests(): void {
137
+ for (const touch of this.pending.values()) {
138
+ touch.settled = true;
139
+ clearTimeout(touch.timer);
140
+ }
141
+ this.pending.clear();
142
+ this.stopListeningIfIdle();
143
+ }
144
+
145
+ /** @internal Test hook: how many touches are still waiting. */
146
+ get pendingCount(): number {
147
+ return this.pending.size;
148
+ }
149
+
150
+ private ensureListening(): void {
151
+ if (this.subscription || this.pending.size === 0) return;
152
+ const emitter = deviceEventEmitter();
153
+ if (!emitter) return;
154
+ this.subscription = emitter.addListener(STATE_CHANGE_EVENT, (payload: unknown) => {
155
+ try {
156
+ this.handleStateChange(payload);
157
+ } catch {
158
+ // A bad payload must not break the next gesture.
159
+ }
160
+ });
161
+ }
162
+
163
+ private stopListeningIfIdle(): void {
164
+ if (this.pending.size > 0 || !this.subscription) return;
165
+ this.subscription.remove();
166
+ this.subscription = null;
167
+ }
168
+
169
+ private handleStateChange(event: unknown): void {
170
+ const payload = asPayload(event);
171
+ if (!payload) return;
172
+ const matching = [...this.pending.values()].filter(
173
+ (touch) => !touch.settled && touch.handlerTags.includes(payload.handlerTag),
174
+ );
175
+ for (const touch of matching) {
176
+ this.apply(touch, payload);
177
+ }
178
+ }
179
+
180
+ private apply(touch: PendingTouch, payload: GestureStatePayload): void {
181
+ if (touch.settled || !isTerminal(payload.state)) return;
182
+ if (isCompletedTap(touch, payload)) {
183
+ this.resolve(touch, endPoint(payload));
184
+ return;
185
+ }
186
+ touch.finishedTags.add(payload.handlerTag);
187
+ const allDone = touch.handlerTags.every((tag) => touch.finishedTags.has(tag));
188
+ if (allDone) this.drop(touch.id);
189
+ }
190
+
191
+ private resolve(touch: PendingTouch, end: TouchPoint | null): void {
192
+ const notify = touch.onResolve;
193
+ this.drop(touch.id);
194
+ try {
195
+ notify(end);
196
+ } catch {
197
+ // One monitor's failure must not skip the other touch on this event.
198
+ }
199
+ }
200
+
201
+ private drop(id: number): void {
202
+ const touch = this.pending.get(id);
203
+ if (!touch || touch.settled) return;
204
+ touch.settled = true;
205
+ clearTimeout(touch.timer);
206
+ this.pending.delete(id);
207
+ this.stopListeningIfIdle();
208
+ }
209
+ }
210
+
211
+ /** Shared singleton used by the tap monitors. */
212
+ export const gestureHandlerHandoff = new GestureHandlerHandoff();
213
+
214
+ interface FiberTags {
215
+ /** Tags that can count as a tap. */
216
+ pressable: number[];
217
+ /** The fiber has a container handler and no pressable tag. */
218
+ container: boolean;
219
+ }
220
+
221
+ function tagsOnFiber(fiber: AnyFiber): FiberTags {
222
+ try {
223
+ const pressable: number[] = [];
224
+ const seen = new Set<number>();
225
+ let container = false;
226
+ const add = (tag: number | undefined, isContainer: boolean): void => {
227
+ if (tag === undefined) return;
228
+ if (isContainer) {
229
+ container = true;
230
+ return;
231
+ }
232
+ if (seen.has(tag)) return;
233
+ seen.add(tag);
234
+ pressable.push(tag);
235
+ };
236
+
237
+ const props = getMemoizedProps(fiber);
238
+ add(handlerTagOf(getStateNode(fiber)), props?.disallowInterruption === true);
239
+
240
+ const gesture = props?.gesture;
241
+ if (gesture && typeof gesture === 'object' && 'toGestureArray' in gesture) {
242
+ const toArray = (gesture as { toGestureArray?: unknown }).toGestureArray;
243
+ if (typeof toArray === 'function') {
244
+ const list = (toArray as () => unknown).call(gesture);
245
+ if (Array.isArray(list)) {
246
+ for (const item of list) {
247
+ const tag = handlerTagOf(item) ?? handlerTagOf(readHandlers(item));
248
+ add(tag, disallowsInterruption(item));
249
+ }
250
+ }
251
+ }
252
+ }
253
+ return { pressable, container: container && pressable.length === 0 };
254
+ } catch {
255
+ return { pressable: [], container: false };
256
+ }
257
+ }
258
+
259
+ /** Gesture-handler scroll containers set this so a drag is not stolen. */
260
+ function disallowsInterruption(value: unknown): boolean {
261
+ if (!value || typeof value !== 'object' || !('config' in value)) return false;
262
+ const config = (value as { config?: unknown }).config;
263
+ if (!config || typeof config !== 'object' || !('disallowInterruption' in config)) return false;
264
+ return (config as { disallowInterruption?: unknown }).disallowInterruption === true;
265
+ }
266
+
267
+ function readHandlers(value: unknown): unknown {
268
+ if (!value || typeof value !== 'object' || !('handlers' in value)) return undefined;
269
+ return (value as { handlers?: unknown }).handlers;
270
+ }
271
+
272
+ /** A mounted gesture-handler tag. `-1` means "not attached yet". */
273
+ function handlerTagOf(value: unknown): number | undefined {
274
+ if (!value || typeof value !== 'object' || !('handlerTag' in value)) return undefined;
275
+ const tag = (value as { handlerTag?: unknown }).handlerTag;
276
+ if (typeof tag !== 'number' || !Number.isFinite(tag) || tag < 0) return undefined;
277
+ return tag;
278
+ }
279
+
280
+ function deviceEventEmitter(): DeviceEventEmitterLike | null {
281
+ try {
282
+ // Lazy so a tap that is never cancelled does not load React Native's emitter
283
+ // into unit tests that only exercise the monitors.
284
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
285
+ const reactNative = require('react-native') as {
286
+ DeviceEventEmitter?: DeviceEventEmitterLike;
287
+ };
288
+ const emitter = reactNative.DeviceEventEmitter;
289
+ if (!emitter || typeof emitter.addListener !== 'function') return null;
290
+ return emitter;
291
+ } catch {
292
+ return null;
293
+ }
294
+ }
295
+
296
+ function asPayload(event: unknown): GestureStatePayload | null {
297
+ if (!event || typeof event !== 'object') return null;
298
+ const raw = event as Record<string, unknown>;
299
+ const body =
300
+ raw.nativeEvent && typeof raw.nativeEvent === 'object'
301
+ ? (raw.nativeEvent as Record<string, unknown>)
302
+ : raw;
303
+ if (typeof body.handlerTag !== 'number' || typeof body.state !== 'number') return null;
304
+ return {
305
+ handlerTag: body.handlerTag,
306
+ state: body.state,
307
+ ...(typeof body.oldState === 'number' ? { oldState: body.oldState } : {}),
308
+ ...(typeof body.pointerInside === 'boolean' ? { pointerInside: body.pointerInside } : {}),
309
+ ...(typeof body.translationX === 'number' ? { translationX: body.translationX } : {}),
310
+ ...(typeof body.translationY === 'number' ? { translationY: body.translationY } : {}),
311
+ ...(typeof body.absoluteX === 'number' ? { absoluteX: body.absoluteX } : {}),
312
+ ...(typeof body.absoluteY === 'number' ? { absoluteY: body.absoluteY } : {}),
313
+ hasDuration: 'duration' in body && body.duration != null,
314
+ };
315
+ }
316
+
317
+ function isTerminal(state: number): boolean {
318
+ return (
319
+ state === GESTURE_HANDLER_STATE.FAILED ||
320
+ state === GESTURE_HANDLER_STATE.CANCELLED ||
321
+ state === GESTURE_HANDLER_STATE.END
322
+ );
323
+ }
324
+
325
+ function isCompletedTap(touch: PendingTouch, payload: GestureStatePayload): boolean {
326
+ if (payload.state !== GESTURE_HANDLER_STATE.END) return false;
327
+ if (payload.oldState !== GESTURE_HANDLER_STATE.ACTIVE) return false;
328
+ if (payload.pointerInside === false) return false;
329
+ if (payload.hasDuration) return false;
330
+ if (
331
+ payload.translationX !== undefined &&
332
+ payload.translationY !== undefined &&
333
+ !withinTapSlop(
334
+ { pageX: 0, pageY: 0 },
335
+ { pageX: payload.translationX, pageY: payload.translationY },
336
+ )
337
+ ) {
338
+ return false;
339
+ }
340
+ if (payload.absoluteX !== undefined && payload.absoluteY !== undefined) {
341
+ if (
342
+ !withinTapSlop(
343
+ { pageX: touch.pageX, pageY: touch.pageY },
344
+ { pageX: payload.absoluteX, pageY: payload.absoluteY },
345
+ )
346
+ ) {
347
+ return false;
348
+ }
349
+ }
350
+ return true;
351
+ }
352
+
353
+ function endPoint(payload: GestureStatePayload): TouchPoint | null {
354
+ if (payload.absoluteX === undefined || payload.absoluteY === undefined) return null;
355
+ return { pageX: payload.absoluteX, pageY: payload.absoluteY };
356
+ }
@@ -15,6 +15,8 @@ import {
15
15
  setCachedFiberAnchor,
16
16
  } from './fiberAnchorCache';
17
17
 
18
+ export { subscribeAnchorScreenChange } from './fiberAnchorCache';
19
+
18
20
  export type AnchorResolution =
19
21
  | { status: 'found'; host: AnchorHost; fiber?: AnyFiber }
20
22
  | { status: 'ambiguous' }
@@ -9,7 +9,7 @@ import type { AutoAnchorInstance } from './AutoAnchorRegistry';
9
9
  import type { AnyFiber } from './fiber/fiberInternals';
10
10
  import { fiberFromPublicInstance } from './fiber/fiberInternals';
11
11
  import { createFiberMeasureInWindow } from './fiber/measureHostFiber';
12
- import { resolveFiberPath } from './fiber/resolveFiberPath';
12
+ import { resolveFiberPath, type FiberPathMissReason } from './fiber/resolveFiberPath';
13
13
  import { createVisibleInstanceFilter } from './isInstanceOnVisibleScreen';
14
14
  import { resolveAnchorHost } from './resolveAnchorHost';
15
15
  import { matchFingerprint, type MatchResult } from './semanticMatcher';
@@ -28,7 +28,15 @@ export type AutoAnchorResolution =
28
28
  }
29
29
  | { status: 'ambiguous'; match?: MatchResult }
30
30
  | { status: 'absent'; match?: MatchResult }
31
- | { status: 'unknown'; match?: MatchResult };
31
+ | {
32
+ status: 'unknown';
33
+ match?: MatchResult;
34
+ /**
35
+ * Set when a fiber path did not resolve. Element conditions treat
36
+ * `no-match` as absent and `no-root` / `empty` as unknown.
37
+ */
38
+ fiberMiss?: FiberPathMissReason;
39
+ };
32
40
 
33
41
  type MeasureInWindowCallback = (x: number, y: number, width: number, height: number) => void;
34
42
 
@@ -121,7 +129,8 @@ export function resolveScoredAutoAnchor(fp: MobileFingerprint): AutoAnchorResolu
121
129
  if (visible.instance) return mapMatch(visible);
122
130
 
123
131
  if (visible.reason === 'no-candidates') {
124
- const any = matchFingerprint(fp);
132
+ // Nothing can commit between the two matches, so the first read stands.
133
+ const any = matchFingerprint(fp, { liveContentFresh: true });
125
134
  if (any.instance) return { status: 'absent', match: any };
126
135
  if (any.reason === 'all-contradicted') return mapMatch(any);
127
136
  }
@@ -136,10 +145,11 @@ export function resolveScoredAutoAnchor(fp: MobileFingerprint): AutoAnchorResolu
136
145
  * carries a real `testID` / `userflowId` / non-`uf_` `nativeID` still resolves
137
146
  * under Babel `stampOnly: true` (no auto-registry population).
138
147
  *
139
- * Scored matches run over `autoAnchorRegistry` and fail closed: weak or
140
- * non-unique evidence is `unknown`, not `absent`, so ELEMENT gates stay blocked
141
- * rather than inventing a truth value. Hosts on a covered navigator screen
142
- * are skipped the same way the fiber scan skips them.
148
+ * Scored matches run over `autoAnchorRegistry`. A covered screen and a
149
+ * contradicted fingerprint are `absent`. Weak, missing, or tied evidence stays
150
+ * `unknown` here; element conditions decide which of those count as absent.
151
+ * Hosts on a covered navigator screen are skipped the same way the fiber scan
152
+ * skips them.
143
153
  */
144
154
  export function resolveAutoAnchorHost(
145
155
  autoData: string | MobileAutoData | null | undefined,
@@ -159,7 +169,7 @@ export function resolveAutoAnchorHost(
159
169
  };
160
170
  }
161
171
  if (fiberHit.status === 'ambiguous') return { status: 'ambiguous' };
162
- return { status: 'unknown' };
172
+ return { status: 'unknown', fiberMiss: fiberHit.reason };
163
173
  }
164
174
 
165
175
  const exactId = exactIdFromFingerprint(fp);
@@ -6,9 +6,16 @@
6
6
  * `testID`, then a real `nativeID`) use that same multi-host lookup, and fall
7
7
  * through to scoring only when nothing matched.
8
8
  */
9
- import { decodeMobileAutoData, type FlowSelectorFragment } from '@userflow-tech/protocol';
9
+ import {
10
+ decodeMobileAutoData,
11
+ type FlowSelectorFragment,
12
+ type MobileAutoData,
13
+ } from '@userflow-tech/protocol';
14
+
15
+ import { screenIdFromLocation, getCurrentScreenLocation } from '../screenTracker';
10
16
 
11
17
  import { type AnchorHost, anchorRegistry } from './AnchorRegistry';
18
+ import { autoAnchorRegistry } from './AutoAnchorRegistry';
12
19
  import type { ElementTap } from './elementTapMonitor';
13
20
  import { anchorIdsAboveTap } from './elementTargetMatch';
14
21
  import { fiberScanRoot } from './fiber/FiberScanRoot';
@@ -85,21 +92,38 @@ function resolveAutoTargets(autoData: string | null): ElementTargetResolution {
85
92
  if (!decoded || !fingerprint) return { status: 'unknown' };
86
93
 
87
94
  if (decoded.resolver === 'fiber' && decoded.fiberPath) {
88
- return fromAutoResolution(resolveAutoAnchorHost(decoded));
95
+ return conditionVerdict(resolveAutoAnchorHost(decoded), decoded);
89
96
  }
90
97
 
91
98
  const exactId = exactIdFromFingerprint(fingerprint);
99
+ let exactIdAbsent = false;
92
100
  if (exactId) {
93
101
  const manual = resolveManualTargets(exactId);
94
102
  if (manual.status === 'found') return manual;
103
+ // "Absent" before a scan root exists only means we have not looked yet.
104
+ exactIdAbsent = fiberScanRoot.getFiber() != null;
95
105
  }
96
106
 
97
107
  // The exact id already missed above, so go straight to scoring rather than
98
108
  // letting resolveAutoAnchorHost scan for it again.
99
- return fromAutoResolution(resolveScoredAutoAnchor(fingerprint));
109
+ return conditionVerdict(resolveScoredAutoAnchor(fingerprint), decoded, exactIdAbsent);
100
110
  }
101
111
 
102
- function fromAutoResolution(resolution: AutoAnchorResolution): ElementTargetResolution {
112
+ /**
113
+ * Turn an AUTO resolution into the value an element condition can report.
114
+ *
115
+ * A miss (nothing scored, or an exact id that a scan did not find) is absent,
116
+ * matching web. A tie stays unknown on the screen where the element was picked,
117
+ * because the target may be one of the look-alikes, and is absent on any other
118
+ * screen. An empty registry with no exact id, a missing scan root, and a
119
+ * missing fingerprint stay unknown so `stampOnly` cannot open a "not present"
120
+ * gate.
121
+ */
122
+ function conditionVerdict(
123
+ resolution: AutoAnchorResolution,
124
+ decoded: MobileAutoData,
125
+ exactIdAbsent = false,
126
+ ): ElementTargetResolution {
103
127
  if (resolution.status === 'found') {
104
128
  // Scored matches only try the direct ref pointer, which Paper release
105
129
  // builds null out. Without the index fallback a tap could never match.
@@ -112,12 +136,45 @@ function fromAutoResolution(resolution: AutoAnchorResolution): ElementTargetReso
112
136
  };
113
137
  }
114
138
  if (resolution.status === 'absent') return { status: 'absent' };
115
- // Ambiguous and unknown both fail closed. Ambiguous is a real tie, not a
116
- // broken selector, so it does not warn.
117
- if (resolution.status === 'ambiguous') return { status: 'unknown', silent: true };
139
+
140
+ if (resolution.status === 'unknown') {
141
+ if (resolution.fiberMiss === 'no-root' || resolution.fiberMiss === 'empty') {
142
+ return { status: 'unknown' };
143
+ }
144
+ if (resolution.fiberMiss === 'no-match') return { status: 'absent' };
145
+ }
146
+
147
+ const reason = resolution.match?.reason;
148
+ const tie =
149
+ resolution.status === 'ambiguous' ||
150
+ reason === 'not-unique' ||
151
+ reason === 'ambiguous-explicit-id';
152
+ if (tie) {
153
+ if (offPickedScreen(decoded.screenId)) return { status: 'absent' };
154
+ // A real tie is not a broken selector, so it does not warn. A weak
155
+ // not-unique miss still does, matching the previous unknown report.
156
+ return reason === 'not-unique' ? { status: 'unknown' } : { status: 'unknown', silent: true };
157
+ }
158
+
159
+ if (reason === 'below-threshold') return { status: 'absent' };
160
+
161
+ if (reason === 'no-candidates') {
162
+ const nothingToMatch = autoAnchorRegistry.allInstances().length === 0 && !exactIdAbsent;
163
+ if (nothingToMatch) return { status: 'unknown' };
164
+ return { status: 'absent' };
165
+ }
166
+
118
167
  return { status: 'unknown' };
119
168
  }
120
169
 
170
+ /** True when the picked screen is known and the app is on a different one. */
171
+ function offPickedScreen(pickedScreenId: string | undefined): boolean {
172
+ if (!pickedScreenId || pickedScreenId === 'unknown') return false;
173
+ const current = screenIdFromLocation(getCurrentScreenLocation());
174
+ if (current === 'unknown') return false;
175
+ return current !== pickedScreenId;
176
+ }
177
+
121
178
  function resolveManualTargets(id: string): ElementTargetResolution {
122
179
  const registered = anchorRegistry.getAll(id);
123
180
  if (registered.length > 0) {
@@ -190,18 +190,21 @@ function intrinsicNonTextAgrees(fp: MobileFingerprint, inst: AutoAnchorInstance)
190
190
  );
191
191
  }
192
192
 
193
+ /**
194
+ * An id or kind conflict, or a childText disagreement no text-independent
195
+ * identity (authoritative id, matching icon/image) vouches for.
196
+ */
197
+ function isContradicted(fp: MobileFingerprint, inst: AutoAnchorInstance): boolean {
198
+ if (contradictsStrong(fp, inst)) return true;
199
+ return childTextDisagrees(fp, inst) && !idAgrees(fp, inst) && !intrinsicNonTextAgrees(fp, inst);
200
+ }
201
+
193
202
  function scoreInstance(
194
203
  fp: MobileFingerprint,
195
204
  inst: AutoAnchorInstance,
196
205
  ambiguous: boolean,
197
206
  ): number {
198
- if (contradictsStrong(fp, inst)) return -Infinity;
199
-
200
- // A childText disagreement is disqualifying unless a text-independent identity
201
- // vouches for the element (authoritative id, or matching icon/image).
202
- if (childTextDisagrees(fp, inst) && !idAgrees(fp, inst) && !intrinsicNonTextAgrees(fp, inst)) {
203
- return -Infinity;
204
- }
207
+ if (isContradicted(fp, inst)) return -Infinity;
205
208
 
206
209
  let score = 0;
207
210
 
@@ -231,11 +234,14 @@ function scoreInstance(
231
234
 
232
235
  score += scoreAncestors(fp.ancestors ?? [], inst.ancestors ?? []);
233
236
 
234
- const window = autoAnchorRegistry.siblingWindow(inst.instanceId, 2);
235
- score += scoreSiblingWindow(fp.previousSiblings ?? [], window.previous);
236
- // Score next siblings once. The POC added a reversed pass *and* a direct pass,
237
- // which double-counted the next window.
238
- score += scoreSiblingWindow(fp.nextSiblings ?? [], window.next);
237
+ // The window scans the registry, so skip it when there is nothing to compare.
238
+ if (fp.previousSiblings?.length || fp.nextSiblings?.length) {
239
+ const window = autoAnchorRegistry.siblingWindow(inst.instanceId, 2);
240
+ score += scoreSiblingWindow(fp.previousSiblings ?? [], window.previous);
241
+ // Score next siblings once. The POC added a reversed pass *and* a direct pass,
242
+ // which double-counted the next window.
243
+ score += scoreSiblingWindow(fp.nextSiblings ?? [], window.next);
244
+ }
239
245
 
240
246
  if (ambiguous && !inst.manualId) score -= 40;
241
247
 
@@ -246,6 +252,8 @@ type InstanceFilter = (inst: AutoAnchorInstance) => boolean;
246
252
 
247
253
  interface MatchOptions {
248
254
  instanceFilter?: InstanceFilter;
255
+ /** The caller refreshed live content earlier in this same synchronous call. */
256
+ liveContentFresh?: boolean;
249
257
  }
250
258
 
251
259
  /** Each instance's visibility is computed once per match. */
@@ -260,10 +268,7 @@ function memoizeFilter(filter: InstanceFilter): InstanceFilter {
260
268
  };
261
269
  }
262
270
 
263
- function candidatePool(
264
- fp: MobileFingerprint,
265
- instanceFilter?: InstanceFilter,
266
- ): AutoAnchorInstance[] {
271
+ function candidatePool(fp: MobileFingerprint): AutoAnchorInstance[] {
267
272
  // Explicit ids are authoritative: a miss is no-candidates, not a full-registry search.
268
273
  let pool: AutoAnchorInstance[];
269
274
  if (fp.manualId) {
@@ -286,29 +291,45 @@ function candidatePool(
286
291
  if (filtered.length > 0) pool = filtered;
287
292
  }
288
293
  }
289
- return instanceFilter ? pool.filter(instanceFilter) : pool;
294
+ return pool;
290
295
  }
291
296
 
292
297
  function miss(reason: MatchMissReason, score = 0, secondScore = 0): MatchResult {
293
298
  return { instance: null, score, secondScore, reason };
294
299
  }
295
300
 
296
- function matchFingerprintCore(fp: MobileFingerprint, instanceFilter?: InstanceFilter): MatchResult {
301
+ function matchFingerprintCore(
302
+ fp: MobileFingerprint,
303
+ instanceFilter?: InstanceFilter,
304
+ liveContentFresh = false,
305
+ ): MatchResult {
297
306
  // Live text / testID must be applied *before* pooling. A shared Babel helper
298
307
  // (`const field = () => <TextInput testID={id} />`) reuses one auto-anchor
299
308
  // callback, so the runtime bag is last-writer-wins until we read the host.
309
+ // An unchanged host is a fiber and props-identity check after one shared
310
+ // ancestor walk per pass, so refreshing every instance stays cheap; the
311
+ // per-instance screen walk is what is not, and it only runs for the pool
312
+ // below.
313
+ const all = autoAnchorRegistry.allInstances();
314
+ if (all.length > 0 && !liveContentFresh) {
315
+ autoAnchorRegistry.refreshLiveContent(all);
316
+ }
317
+
300
318
  // A filter's verdict is memoized so covered-screen checks run once per instance.
301
319
  const filter = instanceFilter ? memoizeFilter(instanceFilter) : undefined;
302
- const broad = filter
303
- ? autoAnchorRegistry.allInstances().filter(filter)
304
- : autoAnchorRegistry.allInstances();
305
- if (broad.length > 0) {
306
- autoAnchorRegistry.refreshLiveContent(broad);
320
+ const candidates = candidatePool(fp);
321
+ // A contradiction does not depend on visibility, so settle it before the
322
+ // covered-screen check. Visibility of contradicted instances only matters to
323
+ // tell `all-contradicted` from `no-candidates` when nothing else is left.
324
+ const viable: AutoAnchorInstance[] = [];
325
+ const contradicted: AutoAnchorInstance[] = [];
326
+ for (const inst of candidates) {
327
+ (isContradicted(fp, inst) ? contradicted : viable).push(inst);
307
328
  }
308
-
309
- const pool = candidatePool(fp, filter);
329
+ const pool = filter ? viable.filter(filter) : viable;
310
330
  if (pool.length === 0) {
311
- return miss('no-candidates');
331
+ const anyContradictedCandidate = filter ? contradicted.some(filter) : contradicted.length > 0;
332
+ return miss(anyContradictedCandidate ? 'all-contradicted' : 'no-candidates');
312
333
  }
313
334
 
314
335
  // Visibility can clear a registry-wide ambiguous flag (a covered twin) but
@@ -367,7 +388,7 @@ function matchFingerprintCore(fp: MobileFingerprint, instanceFilter?: InstanceFi
367
388
  * can flag weak (`structural`) targets.
368
389
  */
369
390
  export function matchFingerprint(fp: MobileFingerprint, opts?: MatchOptions): MatchResult {
370
- const result = matchFingerprintCore(fp, opts?.instanceFilter);
391
+ const result = matchFingerprintCore(fp, opts?.instanceFilter, opts?.liveContentFresh);
371
392
  if (fp.identityStrength) result.identityStrength = fp.identityStrength;
372
393
  return result;
373
394
  }
@@ -0,0 +1,18 @@
1
+ /** Max finger travel (px) between start and end to count as a tap, not a scroll. */
2
+ export const TARGET_PRESS_TAP_SLOP = 10;
3
+
4
+ export interface TouchPoint {
5
+ pageX: number;
6
+ pageY: number;
7
+ }
8
+
9
+ /** True when the finger moved no more than `slop` between start and end. */
10
+ export function withinTapSlop(
11
+ start: TouchPoint,
12
+ end: TouchPoint,
13
+ slop: number = TARGET_PRESS_TAP_SLOP,
14
+ ): boolean {
15
+ const dx = end.pageX - start.pageX;
16
+ const dy = end.pageY - start.pageY;
17
+ return dx * dx + dy * dy <= slop * slop;
18
+ }