peachsim 0.1.939 → 0.1.1399

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 (396) hide show
  1. package/README.md +28 -22
  2. package/cli/app-catalog.ts +29 -14
  3. package/cli/app-fonts.ts +5 -3
  4. package/cli/bridge-flow-runner.ts +40 -29
  5. package/cli/cloud-client.ts +57 -21
  6. package/cli/cloud-test-tunnel.ts +12 -18
  7. package/cli/commands/agent.ts +1 -1
  8. package/cli/commands/app-fonts.ts +3 -3
  9. package/cli/commands/assert.ts +8 -8
  10. package/cli/commands/auth.ts +8 -3
  11. package/cli/commands/box/bundle-plane.ts +13 -1
  12. package/cli/commands/box.ts +16 -5
  13. package/cli/commands/compat.ts +1 -1
  14. package/cli/commands/config.ts +2 -2
  15. package/cli/commands/control.ts +125 -71
  16. package/cli/commands/cpu-profile.ts +45 -1
  17. package/cli/commands/daemon.ts +131 -33
  18. package/cli/commands/debug.ts +24 -17
  19. package/cli/commands/desktop.ts +7 -1
  20. package/cli/commands/detect.ts +101 -0
  21. package/cli/commands/detox.ts +6 -6
  22. package/cli/commands/device.ts +12 -12
  23. package/cli/commands/electron.ts +32 -14
  24. package/cli/commands/flow.ts +49 -41
  25. package/cli/commands/guest-host.ts +39 -1
  26. package/cli/commands/inspect/core.ts +14 -8
  27. package/cli/commands/inspect/list.ts +1 -1
  28. package/cli/commands/inspect/settle.ts +5 -3
  29. package/cli/commands/inspect/settling.ts +229 -44
  30. package/cli/commands/inspect/wait-idle.ts +3 -2
  31. package/cli/commands/inspect.ts +105 -93
  32. package/cli/commands/install-cli.ts +36 -15
  33. package/cli/commands/install-desktop.ts +68 -195
  34. package/cli/commands/kotlin.ts +6 -5
  35. package/cli/commands/login.ts +5 -5
  36. package/cli/commands/maestro-compare.ts +554 -0
  37. package/cli/commands/maestro-generate.ts +6 -3
  38. package/cli/commands/maestro.ts +22 -0
  39. package/cli/commands/mode.ts +2 -2
  40. package/cli/commands/no-bridge-hint.ts +6 -2
  41. package/cli/commands/platform.ts +35 -6
  42. package/cli/commands/preview.ts +6 -0
  43. package/cli/commands/project-report.ts +10 -12
  44. package/cli/commands/record.ts +37 -37
  45. package/cli/commands/runtime.ts +9 -9
  46. package/cli/commands/screenshot-layers.ts +4 -4
  47. package/cli/commands/screenshot-mode.ts +2 -2
  48. package/cli/commands/screenshot.ts +8 -8
  49. package/cli/commands/screenshots-capture.ts +2 -2
  50. package/cli/commands/serve.ts +9 -12
  51. package/cli/commands/setup.ts +29 -14
  52. package/cli/commands/shell-boolean-mode.ts +2 -2
  53. package/cli/commands/show-touches-mode.ts +2 -2
  54. package/cli/commands/skills.ts +11 -11
  55. package/cli/commands/swift.ts +29 -11
  56. package/cli/commands/telemetry.ts +2 -2
  57. package/cli/commands/test.ts +5 -0
  58. package/cli/commands/three-mode.ts +3 -3
  59. package/cli/commands/timeline.ts +4 -4
  60. package/cli/commands/upgrade.ts +2 -2
  61. package/cli/commands/upload.ts +15 -15
  62. package/cli/commands/what-happened.ts +2 -2
  63. package/cli/current-sim.ts +6 -3
  64. package/cli/desktop-companion.ts +55 -149
  65. package/cli/dev-build-freshness.ts +2 -2
  66. package/cli/drivers/desktop.ts +17 -9
  67. package/cli/drivers/electron.ts +1 -2
  68. package/cli/drivers/playwright-sim-host.ts +11 -2
  69. package/cli/drivers/playwright.ts +35 -2
  70. package/cli/drivers/types.ts +8 -0
  71. package/cli/maestro-compare.ts +670 -0
  72. package/cli/outbound-endpoints.ts +46 -8
  73. package/cli/recording-access.ts +6 -6
  74. package/cli/run-registry.ts +1 -1
  75. package/cli/runtime-compat.ts +5 -1
  76. package/cli/send-to-box.ts +3 -3
  77. package/cli/shell-init.ts +2 -1
  78. package/cli/ws-bridge.ts +9 -5
  79. package/detox/element-types.ts +1 -0
  80. package/detox/gestures.ts +3 -3
  81. package/detox/index.ts +67 -38
  82. package/detox/navigation-lifecycle.ts +9 -6
  83. package/detox/proof-frame.cjs +6 -4
  84. package/dist-lib/agent-daemon-client.cjs +1 -1
  85. package/dist-lib/agent-events.cjs +1 -1
  86. package/dist-lib/attached-projects.cjs +1 -1
  87. package/dist-lib/auth/shared-session.cjs +1 -1
  88. package/dist-lib/backend-origin.cjs +1 -1
  89. package/dist-lib/beta.cjs +1 -1
  90. package/dist-lib/beta.mjs +1 -1
  91. package/dist-lib/bridge-constants.cjs +1 -1
  92. package/dist-lib/bridge-contract-input.cjs +42 -17
  93. package/dist-lib/bridge-contract-input.mjs +41 -16
  94. package/dist-lib/bridge-contract.cjs +1 -4
  95. package/dist-lib/bridge-contract.mjs +1 -3
  96. package/dist-lib/bundle-channel.cjs +2918 -550
  97. package/dist-lib/capture-contract.cjs +65 -24
  98. package/dist-lib/capture-contract.mjs +63 -24
  99. package/dist-lib/cloud-contract.cjs +2 -2
  100. package/dist-lib/cloud-contract.mjs +2 -2
  101. package/dist-lib/cloud-test-args.cjs +1 -1
  102. package/dist-lib/cloud-test-args.mjs +1 -1
  103. package/dist-lib/cloud.cjs +3 -3
  104. package/dist-lib/cloud.mjs +3 -3
  105. package/dist-lib/config.cjs +15 -5
  106. package/dist-lib/config.mjs +154 -0
  107. package/dist-lib/detox/index.cjs +495 -139
  108. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  109. package/dist-lib/expo-swift-bundle.cjs +1168 -238
  110. package/dist-lib/expo-swift-metadata.cjs +34 -23
  111. package/dist-lib/expo-swift-metadata.mjs +32 -23
  112. package/dist-lib/home-paths.cjs +2 -2
  113. package/dist-lib/host/bridge-host.cjs +2938 -654
  114. package/dist-lib/host/fetch-proxy-handler.cjs +14 -1
  115. package/dist-lib/host/fetch-proxy-overrides.cjs +7 -1
  116. package/dist-lib/host/fetch-proxy-overrides.mjs +5 -1
  117. package/dist-lib/host/replacement-module-handler.cjs +2 -2
  118. package/dist-lib/host/websocket-proxy.cjs +1 -1
  119. package/dist-lib/index.cjs +999 -353
  120. package/dist-lib/jump-to-source-babel.cjs +1 -1
  121. package/dist-lib/jump-to-source-native.cjs +1 -1
  122. package/dist-lib/kotlin-host.cjs +332 -19
  123. package/dist-lib/menu.cjs +1 -1
  124. package/dist-lib/menu.mjs +1 -1
  125. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  126. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  127. package/dist-lib/metro-production-bundle.cjs +7 -7
  128. package/dist-lib/metro-production-bundle.mjs +5 -5
  129. package/dist-lib/metro.cjs +688 -250
  130. package/dist-lib/native-app-identity-project.cjs +2647 -0
  131. package/dist-lib/native-app-identity-project.mjs +1257 -0
  132. package/dist-lib/native-app-identity.cjs +325 -25
  133. package/dist-lib/native-app-identity.mjs +323 -25
  134. package/dist-lib/native-firebase-metadata.cjs +1 -1
  135. package/dist-lib/native-firebase-metadata.mjs +1 -1
  136. package/dist-lib/plist.cjs +1 -1
  137. package/dist-lib/plist.mjs +1 -1
  138. package/dist-lib/profiles.cjs +1 -1
  139. package/dist-lib/public-brand.cjs +1 -1
  140. package/dist-lib/react-native-host-modules.cjs +5 -1
  141. package/dist-lib/react-native-host-modules.mjs +5 -1
  142. package/dist-lib/scripts/dev-server-scanner.cjs +57 -32
  143. package/dist-lib/sdk.cjs +231 -51
  144. package/dist-lib/sdk.mjs +231 -51
  145. package/dist-lib/skills.cjs +4107 -1374
  146. package/dist-lib/swift-compile.cjs +290 -30
  147. package/dist-lib/swift-compile.mjs +290 -30
  148. package/dist-lib/swift-package.cjs +143 -12
  149. package/dist-lib/swift-package.mjs +137 -11
  150. package/dist-lib/swift-submit.cjs +47 -5
  151. package/dist-lib/swift-submit.mjs +42 -5
  152. package/dist-lib/swift-toolchain.cjs +7 -2
  153. package/dist-lib/swift.cjs +1 -1
  154. package/dist-lib/vite-swift.cjs +1392 -458
  155. package/dist-lib/vite.cjs +13 -17
  156. package/dist-lib/worklet-executable.cjs +248 -33
  157. package/dist-lib/worklet-executable.mjs +248 -33
  158. package/install.sh +10 -1
  159. package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Resources.kt +9 -9
  160. package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +20 -0
  161. package/kotlin/AndroidViewSources/android/view/View.kt +211 -0
  162. package/kotlin/AndroidViewSources/android/widget/FrameLayout.kt +61 -0
  163. package/kotlin/AndroidViewSources/android/widget/LinearLayout.kt +49 -0
  164. package/kotlin/AppSources/android/view/Window.kt +36 -0
  165. package/kotlin/AppSources/android/view/WindowInsets.kt +9 -0
  166. package/kotlin/AppSources/android/view/WindowInsetsController.kt +5 -0
  167. package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +48 -0
  168. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +20 -5
  169. package/kotlin/AppSources/androidx/compose/foundation/layout/WindowInsets.kt +15 -0
  170. package/kotlin/AppSources/androidx/compose/ui/platform/ComposeView.kt +2 -1
  171. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +42 -50
  172. package/kotlin/ComposeSources/PeachComposeHost.kt +200 -84
  173. package/kotlin/ExpoComposeSources/PeachAndroidView.kt +87 -0
  174. package/kotlin/ExpoComposeSources/PeachExpoViewMount.kt +24 -0
  175. package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +28 -0
  176. package/kotlin/ExpoComposeSources/expo/modules/kotlin/views/ExpoComposeView.kt +70 -0
  177. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +513 -0
  178. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +173 -0
  179. package/kotlin/ExpoSources/expo/modules/kotlin/AppContext.kt +20 -0
  180. package/kotlin/ExpoSources/expo/modules/kotlin/Promise.kt +19 -0
  181. package/kotlin/ExpoSources/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +198 -0
  182. package/kotlin/ExpoSources/expo/modules/kotlin/exception/CodedException.kt +126 -0
  183. package/kotlin/ExpoSources/expo/modules/kotlin/functions/Functions.kt +132 -0
  184. package/kotlin/ExpoSources/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +81 -0
  185. package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +98 -0
  186. package/kotlin/ExpoSources/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +236 -0
  187. package/kotlin/ExpoSources/expo/modules/kotlin/records/Record.kt +12 -0
  188. package/kotlin/ExpoSources/expo/modules/kotlin/runtime/Runtime.kt +9 -0
  189. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObject.kt +50 -0
  190. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +57 -0
  191. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedRef.kt +19 -0
  192. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/ConcreteTypedArrays.kt +158 -0
  193. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/GenericTypedArray.kt +8 -0
  194. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArray.kt +29 -0
  195. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArrayIterator.kt +9 -0
  196. package/kotlin/ExpoSources/expo/modules/kotlin/types/AnyType.kt +155 -0
  197. package/kotlin/ExpoSources/expo/modules/kotlin/types/Either.kt +91 -0
  198. package/kotlin/ExpoSources/expo/modules/kotlin/types/Enumerable.kt +6 -0
  199. package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +54 -0
  200. package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +23 -0
  201. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +71 -0
  202. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +35 -0
  203. package/kotlin/README.md +13 -3
  204. package/kotlin/Sources/android/os/Build.kt +17 -0
  205. package/kotlin/Sources/android/os/Handler.kt +35 -0
  206. package/kotlin/Sources/android/os/Looper.kt +17 -0
  207. package/kotlin/Sources/android/os/MessageQueue.kt +56 -0
  208. package/kotlin/Sources/android/os/SystemClock.kt +14 -0
  209. package/kotlin/Sources/android/util/Log.kt +64 -0
  210. package/kotlin/Sources/java/security/MessageDigest.kt +114 -0
  211. package/kotlin/Sources/kotlin/text/StringFormat.kt +9 -0
  212. package/kotlin/Sources/org/json/JSON.kt +44 -0
  213. package/kotlin/fixtures/counter/Counter.d.kt.ts +1 -0
  214. package/kotlin/fixtures/counter/Counter.kt +10 -3
  215. package/kotlin/fixtures/counter/CounterApp.native.tsx +1 -1
  216. package/kotlin/fixtures/counter/vite.config.ts +2 -2
  217. package/kotlin/fixtures/headless-counter/Headless.kt +7 -6
  218. package/kotlin/fixtures/import-animation/Animation.d.kt.ts +7 -0
  219. package/kotlin/fixtures/import-animation/Animation.kt +80 -0
  220. package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +13 -0
  221. package/kotlin/fixtures/import-animation/README.md +24 -0
  222. package/kotlin/fixtures/import-animation/app/_layout.tsx +4 -0
  223. package/kotlin/fixtures/import-animation/app/index+spa.tsx +1 -0
  224. package/kotlin/fixtures/import-animation/app/routes.d.ts +18 -0
  225. package/kotlin/fixtures/import-animation/package.json +23 -0
  226. package/kotlin/fixtures/import-animation/tsconfig.json +20 -0
  227. package/kotlin/fixtures/import-animation/vite.config.ts +20 -0
  228. package/kotlin/fixtures/import-editor/Editor.d.kt.ts +7 -0
  229. package/kotlin/fixtures/import-editor/Editor.kt +59 -0
  230. package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +23 -0
  231. package/kotlin/fixtures/import-editor/README.md +27 -0
  232. package/kotlin/fixtures/import-editor/app/_layout.tsx +4 -0
  233. package/kotlin/fixtures/import-editor/app/index+spa.tsx +1 -0
  234. package/kotlin/fixtures/import-editor/package.json +23 -0
  235. package/kotlin/fixtures/import-editor/tsconfig.json +20 -0
  236. package/kotlin/fixtures/import-editor/vite.config.ts +20 -0
  237. package/kotlin/fixtures/import-effects/Effects.d.kt.ts +7 -0
  238. package/kotlin/fixtures/import-effects/Effects.kt +87 -0
  239. package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +13 -0
  240. package/kotlin/fixtures/import-effects/README.md +19 -0
  241. package/kotlin/fixtures/import-effects/app/_layout.tsx +4 -0
  242. package/kotlin/fixtures/import-effects/app/index+spa.tsx +1 -0
  243. package/kotlin/fixtures/import-effects/package.json +23 -0
  244. package/kotlin/fixtures/import-effects/tsconfig.json +20 -0
  245. package/kotlin/fixtures/import-effects/vite.config.ts +20 -0
  246. package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +13 -0
  247. package/kotlin/fixtures/import-layout/Layout.d.kt.ts +7 -0
  248. package/kotlin/fixtures/import-layout/Layout.kt +72 -0
  249. package/kotlin/fixtures/import-layout/README.md +24 -0
  250. package/kotlin/fixtures/import-layout/app/_layout.tsx +4 -0
  251. package/kotlin/fixtures/import-layout/app/index+spa.tsx +1 -0
  252. package/kotlin/fixtures/import-layout/package.json +23 -0
  253. package/kotlin/fixtures/import-layout/tsconfig.json +20 -0
  254. package/kotlin/fixtures/import-layout/vite.config.ts +20 -0
  255. package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +18 -0
  256. package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +7 -0
  257. package/kotlin/fixtures/import-navigation/Navigation.kt +104 -0
  258. package/kotlin/fixtures/import-navigation/README.md +31 -0
  259. package/kotlin/fixtures/import-navigation/app/_layout.tsx +4 -0
  260. package/kotlin/fixtures/import-navigation/app/index+spa.tsx +1 -0
  261. package/kotlin/fixtures/import-navigation/package.json +23 -0
  262. package/kotlin/fixtures/import-navigation/tsconfig.json +20 -0
  263. package/kotlin/fixtures/import-navigation/vite.config.ts +20 -0
  264. package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +29 -0
  265. package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +7 -0
  266. package/kotlin/fixtures/import-overlays/Overlays.kt +93 -0
  267. package/kotlin/fixtures/import-overlays/README.md +34 -0
  268. package/kotlin/fixtures/import-overlays/app/_layout.tsx +4 -0
  269. package/kotlin/fixtures/import-overlays/app/index+spa.tsx +1 -0
  270. package/kotlin/fixtures/import-overlays/package.json +23 -0
  271. package/kotlin/fixtures/import-overlays/tsconfig.json +20 -0
  272. package/kotlin/fixtures/import-overlays/vite.config.ts +20 -0
  273. package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +13 -0
  274. package/kotlin/fixtures/import-scroll/README.md +23 -0
  275. package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +7 -0
  276. package/kotlin/fixtures/import-scroll/Scroll.kt +91 -0
  277. package/kotlin/fixtures/import-scroll/app/_layout.tsx +4 -0
  278. package/kotlin/fixtures/import-scroll/app/index+spa.tsx +1 -0
  279. package/kotlin/fixtures/import-scroll/package.json +23 -0
  280. package/kotlin/fixtures/import-scroll/tsconfig.json +20 -0
  281. package/kotlin/fixtures/import-scroll/vite.config.ts +20 -0
  282. package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +44 -0
  283. package/kotlin/fixtures/import-selection/README.md +32 -0
  284. package/kotlin/fixtures/import-selection/Selection.d.kt.ts +8 -0
  285. package/kotlin/fixtures/import-selection/Selection.kt +78 -0
  286. package/kotlin/fixtures/import-selection/app/_layout.tsx +4 -0
  287. package/kotlin/fixtures/import-selection/app/index+spa.tsx +1 -0
  288. package/kotlin/fixtures/import-selection/package.json +23 -0
  289. package/kotlin/fixtures/import-selection/tsconfig.json +20 -0
  290. package/kotlin/fixtures/import-selection/vite.config.ts +20 -0
  291. package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +13 -0
  292. package/kotlin/fixtures/import-surfaces/README.md +22 -0
  293. package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +7 -0
  294. package/kotlin/fixtures/import-surfaces/Surfaces.kt +64 -0
  295. package/kotlin/fixtures/import-surfaces/app/_layout.tsx +4 -0
  296. package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +1 -0
  297. package/kotlin/fixtures/import-surfaces/package.json +23 -0
  298. package/kotlin/fixtures/import-surfaces/tsconfig.json +20 -0
  299. package/kotlin/fixtures/import-surfaces/vite.config.ts +20 -0
  300. package/kotlin/fixtures/import-values/ImportValues.native.tsx +35 -0
  301. package/kotlin/fixtures/import-values/README.md +23 -0
  302. package/kotlin/fixtures/import-values/Values.d.kt.ts +11 -0
  303. package/kotlin/fixtures/import-values/Values.kt +66 -0
  304. package/kotlin/fixtures/import-values/app/_layout.tsx +4 -0
  305. package/kotlin/fixtures/import-values/app/index+spa.tsx +1 -0
  306. package/kotlin/fixtures/import-values/package.json +23 -0
  307. package/kotlin/fixtures/import-values/tsconfig.json +20 -0
  308. package/kotlin/fixtures/import-values/vite.config.ts +20 -0
  309. package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +38 -0
  310. package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +36 -0
  311. package/kotlin/fixtures/kotlin-app-variants/Ime.kt +3 -1
  312. package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +50 -0
  313. package/kotlin/fork/NOTICE +6 -1
  314. package/kotlin/fork/README.md +15 -7
  315. package/kotlin/fork/build.sh +2 -0
  316. package/kotlin/fork/compose-worker-globals.patch +186 -4
  317. package/kotlin/fork/fork.json +2 -2
  318. package/kotlin/fork/skiko-worker-clock.patch +12 -0
  319. package/kotlin/toolchain.json +12 -12
  320. package/kotlin/tools/expo-types.main.kts +115 -0
  321. package/package.json +17 -23
  322. package/scripts/dev-server-scanner.ts +92 -66
  323. package/skills/contrast/SKILL.md +2 -2
  324. package/skills/peach-debug/SKILL.md +1 -1
  325. package/skills/peach-perf/SKILL.md +1 -1
  326. package/skills/peach-setup/SKILL.md +2 -1
  327. package/skills/peach-test/SKILL.md +8 -0
  328. package/src/assetCatalogContract.ts +43 -1
  329. package/src/bridge-contract-input.ts +46 -19
  330. package/src/bridge-contract.ts +0 -1
  331. package/src/bundle-channel.mjs +197 -84
  332. package/src/bundleGlobalFooter.ts +38 -0
  333. package/src/capture-contract.ts +107 -33
  334. package/src/cloud-contract.ts +5 -1
  335. package/src/cloud.ts +5 -2
  336. package/src/config.ts +22 -3
  337. package/src/configured-bundle-producer.ts +1 -1
  338. package/src/desktop-host-delivery.ts +95 -1
  339. package/src/expo-kotlin-bundle.ts +29 -0
  340. package/src/expo-kotlin-host.ts +201 -0
  341. package/src/expo-swift-host.ts +96 -76
  342. package/src/expoKotlinContract.ts +70 -0
  343. package/src/expoSwiftContract.ts +4 -25
  344. package/src/home-paths.ts +7 -4
  345. package/src/host/bridge-host.ts +23 -3
  346. package/src/host/fetch-proxy-handler.ts +13 -0
  347. package/src/host/fetch-proxy-overrides.ts +11 -0
  348. package/src/host/replacement-module-handler.ts +5 -2
  349. package/src/index.ts +3 -1
  350. package/src/kotlin-host.ts +463 -19
  351. package/src/metro-plugin.ts +56 -28
  352. package/src/metro-production-bundle.ts +2 -2
  353. package/src/native-app-identity-project.ts +190 -0
  354. package/src/native-app-identity.ts +398 -34
  355. package/src/native-firebase-config.ts +69 -15
  356. package/src/native-seam-manifest.ts +6 -0
  357. package/src/one-widget-discovery.ts +2 -1
  358. package/src/openstep-plist.ts +107 -0
  359. package/src/react-native-host-modules.ts +4 -0
  360. package/src/runtime-assets.ts +2 -2
  361. package/src/sim-client.ts +2 -2
  362. package/src/swift-binary-modules.ts +3 -1
  363. package/src/swift-compile.ts +286 -38
  364. package/src/swift-distribution-contract.ts +4 -0
  365. package/src/swift-distribution.lock.json +123 -12
  366. package/src/swift-distribution.ts +7 -3
  367. package/src/swift-host.ts +298 -83
  368. package/src/swift-package-manifest.ts +29 -2
  369. package/src/swift-package.ts +180 -10
  370. package/src/swift-packages.ts +4 -0
  371. package/src/swift-standin-emit.ts +4 -1
  372. package/src/swift-standins.ts +274 -15
  373. package/src/swift-submit.ts +62 -7
  374. package/src/swift-toolchain.ts +6 -1
  375. package/src/swiftContract.ts +126 -3
  376. package/src/vite-plugin-one.ts +13 -16
  377. package/src/vite-plugin-swift.ts +4 -5
  378. package/src/vite-plugin.ts +360 -144
  379. package/src/vite.d.ts +1 -1
  380. package/src/worker-system-chunks.ts +221 -0
  381. package/src/worklet-executable.ts +348 -38
  382. package/src/worklets-babel.ts +1 -1
  383. package/src/xcode-project.ts +243 -127
  384. package/dist-lib/agent-identity.cjs +0 -39
  385. package/dist-lib/agent-sessions.cjs +0 -760
  386. package/dist-lib/cli-constants.cjs +0 -32
  387. package/dist-lib/render-mode.cjs +0 -44
  388. package/kotlin/AppSources/android/view/View.kt +0 -74
  389. package/kotlin/AppSources/android/widget/FrameLayout.kt +0 -6
  390. package/src/render-mode.ts +0 -15
  391. /package/kotlin/{AppSources → AndroidViewSources}/android/content/Context.kt +0 -0
  392. /package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Configuration.kt +0 -0
  393. /package/kotlin/{AppSources → AndroidViewSources}/android/graphics/Color.kt +0 -0
  394. /package/kotlin/{AppSources → AndroidViewSources}/android/util/DisplayMetrics.kt +0 -0
  395. /package/kotlin/{AppSources → AndroidViewSources}/dev/peach/app/JavaFormat.kt +0 -0
  396. /package/kotlin/{AppSources → Sources}/java/lang/Runnable.kt +0 -0
@@ -410,7 +410,13 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
410
410
  category: 'local_development',
411
411
  count: 1,
412
412
  },
413
- "packages/peachsim/src/one-widget-discovery.ts :: fetch :: new URL('/__server-scan', bundleUrl).href":
413
+ // a Live Activity or widget surface compiles One's extension from the
414
+ // package URL the app's own dev server advertised in its widget scan.
415
+ 'packages/peach-engine/src/ios/one-widget-entry.ts :: fetch :: widget.packageUrl': {
416
+ category: 'local_development',
417
+ count: 1,
418
+ },
419
+ "packages/peachsim/src/one-widget-discovery.ts :: fetch-alias :: new URL('/__server-scan', bundleUrl).href":
414
420
  {
415
421
  category: 'local_development',
416
422
  count: 1,
@@ -458,9 +464,9 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
458
464
  category: 'guest_app_network',
459
465
  count: 2,
460
466
  },
461
- 'packages/peach-engine/src/ExternalApp.tsx :: fetch :: buildBrowserAssetProxyUrl(devServerUrl)':
467
+ 'packages/peach-engine/src/bundle-fetch.ts :: fetch :: buildBrowserAssetProxyUrl(devServerUrl)':
462
468
  { category: 'guest_app_network', count: 1 },
463
- 'packages/peach-engine/src/ExternalApp.tsx :: fetch :: requestUrl': {
469
+ 'packages/peach-engine/src/bundle-fetch.ts :: fetch :: requestUrl': {
464
470
  category: 'guest_app_network',
465
471
  count: 1,
466
472
  },
@@ -468,6 +474,23 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
468
474
  category: 'same_origin_runtime',
469
475
  count: 1,
470
476
  },
477
+ 'packages/peach-engine/src/engine/metal-air-compiler.ts :: fetch :: url': {
478
+ category: 'same_origin_runtime',
479
+ count: 1,
480
+ },
481
+ 'packages/peach-engine/src/engine/metal-source-compiler.ts :: fetch :: url': {
482
+ category: 'same_origin_runtime',
483
+ count: 1,
484
+ },
485
+ 'packages/peach-engine/src/engine/metal-shader-sources.ts :: fetch :: url': {
486
+ category: 'guest_app_network',
487
+ count: 1,
488
+ },
489
+ 'packages/peach-engine/src/render-worker/compositor-runtime/metal-effects.ts :: fetch :: uri':
490
+ {
491
+ category: 'guest_app_network',
492
+ count: 1,
493
+ },
471
494
  'packages/peach-engine/src-electron/tray.ts :: client.get :: parsed': {
472
495
  category: 'guest_app_network',
473
496
  count: 1,
@@ -535,6 +558,16 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
535
558
  },
536
559
  'packages/peach-engine/src/render-worker/app-in-worker.tsx :: fetch :: new URL(file, listUrl)':
537
560
  { category: 'same_origin_runtime', count: 1 },
561
+ // the rive runtime's system fallback font, the engine's own SF Pro asset.
562
+ 'packages/peach-engine/src/render-worker/rive-host.ts :: fetch :: url': {
563
+ category: 'same_origin_runtime',
564
+ count: 1,
565
+ },
566
+ // CDN assets a guest rive file names, fetched as the rive runtime's CDN loader does.
567
+ 'packages/peach-engine/src/render-worker/rive-host.ts :: fetch :: cdnUrl': {
568
+ category: 'guest_app_network',
569
+ count: 1,
570
+ },
538
571
  'packages/peach-engine/src/render-worker/host.ts :: fetch :: msg.fallbackUrl': {
539
572
  category: 'guest_app_network',
540
573
  count: 1,
@@ -561,10 +594,11 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
561
594
  { category: 'contrast_user_action', count: 1 },
562
595
  'packages/peach-engine/src/screenshots/capture-store.ts :: fetch :: captureApiUrl(ref)':
563
596
  { category: 'contrast_user_action', count: 1 },
564
- "packages/peach-engine/src/server-discovery.ts :: fetch :: '/__server-scan'": {
565
- category: 'local_development',
566
- count: 1,
567
- },
597
+ "packages/peach-engine/src/server-discovery.ts :: fetch :: requireSootSimRuntimeLocation().origin + '/__server-scan'":
598
+ {
599
+ category: 'local_development',
600
+ count: 1,
601
+ },
568
602
  'packages/peach-engine/src/server-discovery.ts :: fetch :: `${gatewayOrigin}/__scan`': {
569
603
  category: 'local_development',
570
604
  count: 1,
@@ -708,7 +742,11 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
708
742
  { category: 'local_development', count: 1 },
709
743
  'packages/peach-engine/src/engine/text-hyphenation-en-us.ts :: xml-http-request :: <none>':
710
744
  { category: 'same_origin_runtime', count: 1 },
711
- 'packages/peach-engine/src/expo-swift-modules.tsx :: fetch :: address': {
745
+ 'packages/peach-engine/src/expo-kotlin-compilation.ts :: fetch :: address': {
746
+ category: 'local_development',
747
+ count: 1,
748
+ },
749
+ 'packages/peach-engine/src/expo-native-modules.tsx :: fetch :: address': {
712
750
  category: 'local_development',
713
751
  count: 1,
714
752
  },
@@ -1,7 +1,7 @@
1
1
  import { readSharedDesktopAuthSession } from '../src/auth/shared-session'
2
2
  import { authHeaderValue, resolveCliAuth, type CliAuth } from './auth'
3
3
  import { resolveDefaultUploadOrigin } from './commands/upload'
4
- import { rnxExit } from './run-rnx'
4
+ import { peachExit } from './run-rnx'
5
5
 
6
6
  type BillingSubscriptionSnapshot = {
7
7
  plan?: 'free' | 'personal' | 'team'
@@ -59,7 +59,7 @@ export async function ensureCliRecordingEntitlement(
59
59
  ` • run \`peach login\`\n` +
60
60
  ` • set PEACH_API_KEY=sk_rnx_... (recommended for CI)\n\n`,
61
61
  )
62
- rnxExit(1)
62
+ peachExit(1)
63
63
  }
64
64
 
65
65
  if (auth.kind === 'github' && opts.allowGitHubAuth) return
@@ -75,7 +75,7 @@ export async function ensureCliRecordingEntitlement(
75
75
  `\n peach ${commandLabel} couldn't verify recording access.\n` +
76
76
  ` ${error instanceof Error ? error.message : String(error)}\n\n`,
77
77
  )
78
- rnxExit(1)
78
+ peachExit(1)
79
79
  }
80
80
 
81
81
  if (res.status === 401) {
@@ -83,7 +83,7 @@ export async function ensureCliRecordingEntitlement(
83
83
  `\n peach ${commandLabel} couldn't verify recording access with the current auth against ${billingOrigin}.\n` +
84
84
  ` sign in again for that origin, or provide a valid PEACH_API_KEY.\n\n`,
85
85
  )
86
- rnxExit(1)
86
+ peachExit(1)
87
87
  }
88
88
 
89
89
  if (!res.ok) {
@@ -92,7 +92,7 @@ export async function ensureCliRecordingEntitlement(
92
92
  `\n peach ${commandLabel} couldn't verify recording access (${res.status}).\n` +
93
93
  ` ${text || 'unexpected billing response'}\n\n`,
94
94
  )
95
- rnxExit(1)
95
+ peachExit(1)
96
96
  }
97
97
 
98
98
  const data = (await res.json()) as BillingSubscriptionSnapshot
@@ -104,5 +104,5 @@ export async function ensureCliRecordingEntitlement(
104
104
  ` current plan: ${plan}.\n` +
105
105
  ` free keeps basic 3d mode; the trial unlocks the full Personal surface.\n\n`,
106
106
  )
107
- rnxExit(1)
107
+ peachExit(1)
108
108
  }
@@ -175,7 +175,7 @@ export async function registerRun(args: RegisterRunArgs): Promise<RegisteredRun
175
175
  // drivers through this choke: fail exactly like a dead endpoint,
176
176
  // without touching the network. downstream (warn shape, null return,
177
177
  // require-registration exit) is byte-identical to a real failure.
178
- if (process.env.RNX_INJECT_REGISTRATION_FAILURE === '1') {
178
+ if (process.env.PEACH_INJECT_REGISTRATION_FAILURE === '1') {
179
179
  console.warn(' warn: run registration failed: injected failure')
180
180
  return null
181
181
  }
@@ -116,7 +116,11 @@ export function readRuntimeCompatibility(runtimeVersion = readActiveRuntime()):
116
116
  reason: 'runtime compatibility snapshot is invalid or belongs to another runtime',
117
117
  }
118
118
  }
119
- return { runtimeVersion, data: snapshot.data, reason: null }
119
+ return {
120
+ runtimeVersion,
121
+ data: snapshot.data,
122
+ reason: null,
123
+ }
120
124
  } catch {
121
125
  return {
122
126
  runtimeVersion,
@@ -11,12 +11,12 @@ import {
11
11
  getRuntimePlatform,
12
12
  listDeviceModels,
13
13
  } from '../../peach-engine/src/settings/devices.ts'
14
- import { toRNXProductionBundleUrl } from '../src/metro-production-bundle.ts'
14
+ import { isRecord } from '../src/is-record'
15
+ import { toPeachProductionBundleUrl } from '../src/metro-production-bundle.ts'
15
16
  import { createCloudBox } from './cloud-client.ts'
16
17
  import type { StorageSnapshot } from '../../peach-engine/src/preview/storage-snapshot-contract.ts'
17
18
  import type { DeviceModel } from '../../peach-engine/src/settings/devices.ts'
18
19
  import type { BridgeCommandType } from '../src/bridge-contract.ts'
19
- import { isRecord } from '../src/is-record'
20
20
 
21
21
  export interface SendToBoxOptions {
22
22
  /** runs one bridge command against the sim running the app. */
@@ -79,7 +79,7 @@ export async function sendToBox(options: SendToBoxOptions): Promise<SendToBoxRes
79
79
  }
80
80
  const storage: StorageSnapshot = dumped
81
81
  const created = await createCloudBox({
82
- bundlePath: toRNXProductionBundleUrl(bundleUrl),
82
+ bundlePath: toPeachProductionBundleUrl(bundleUrl),
83
83
  device:
84
84
  options.device ??
85
85
  cloudDevice(isRecord(captured.deviceSpec) ? captured.deviceSpec.model : null),
package/cli/shell-init.ts CHANGED
@@ -52,7 +52,8 @@ export function renderRnxShellInit(shell: string): string {
52
52
  ;;
53
53
  ios)
54
54
  for _peach_shell_session in "$@"; do
55
- if [ "$_peach_shell_session" = '--remote' ]; then
55
+ if [ "$_peach_shell_session" = '--remote' ] ||
56
+ [ "$_peach_shell_session" = '--nano' ]; then
56
57
  _peach_shell_create=1
57
58
  break
58
59
  fi
package/cli/ws-bridge.ts CHANGED
@@ -114,11 +114,11 @@ function resolveDefaultDevBridgeLockfile(): DevBridgeLockfile | null {
114
114
  if (lockfiles.length === 0) return null
115
115
 
116
116
  if (
117
- process.env.SOOTSIM_BRIDGE_PORT !== undefined ||
117
+ process.env.PEACH_BRIDGE_PORT !== undefined ||
118
118
  process.env.PORT_OFFSET !== undefined
119
119
  ) {
120
120
  const requestedPort = resolveSootsimBridgePort({
121
- explicitPort: process.env.SOOTSIM_BRIDGE_PORT,
121
+ explicitPort: process.env.PEACH_BRIDGE_PORT,
122
122
  portOffset: process.env.PORT_OFFSET,
123
123
  })
124
124
  return lockfiles.find((lockfile) => lockfile.bridgePort === requestedPort) ?? null
@@ -175,11 +175,11 @@ function resolveBridgeWorldFresh(): BridgeWorld {
175
175
  }
176
176
  const lock = readDaemonLockfile()
177
177
  const requestedPort = resolveSootsimBridgePort({
178
- explicitPort: process.env.SOOTSIM_BRIDGE_PORT,
178
+ explicitPort: process.env.PEACH_BRIDGE_PORT,
179
179
  portOffset: process.env.PORT_OFFSET,
180
180
  })
181
181
  const explicitWorld =
182
- process.env.SOOTSIM_BRIDGE_PORT !== undefined || process.env.PORT_OFFSET !== undefined
182
+ process.env.PEACH_BRIDGE_PORT !== undefined || process.env.PORT_OFFSET !== undefined
183
183
  if (
184
184
  lock &&
185
185
  isDaemonLockfileFresh(lock) &&
@@ -680,7 +680,11 @@ export function createBridge(wsPort: number, opts: WsBridgeOptions = {}): WsBrid
680
680
  // used by scripts that prefer clean stdout.
681
681
  function maybeNoticeTarget(cmdType: string, simId: string | undefined) {
682
682
  if (noticedTarget) return
683
- if (process.env.SOOTSIM_QUIET_TARGET_NOTICE === '1') return
683
+ if (
684
+ process.env.PEACH_QUIET_TARGET_NOTICE === '1' ||
685
+ process.env.SOOTSIM_QUIET_TARGET_NOTICE === '1'
686
+ )
687
+ return
684
688
  if (cmdType.startsWith('bridge:') || cmdType === 'focus' || cmdType === 'close')
685
689
  return
686
690
  noticedTarget = true
@@ -19,6 +19,7 @@ export interface SootSimElement {
19
19
  holdDuration?: number,
20
20
  ): Promise<void>
21
21
  typeText(text: string): Promise<void>
22
+ tapReturnKey(): Promise<void>
22
23
  replaceText(text: string): Promise<void>
23
24
  clearText(): Promise<void>
24
25
  scroll(
package/detox/gestures.ts CHANGED
@@ -339,9 +339,9 @@ export async function dragScrollNode(
339
339
  let recognitionSootSimY = startSootSimY
340
340
  let endSootSimX = startSootSimX
341
341
  let endSootSimY = startSootSimY
342
- // commit recognition separately: ios consumes the entire first move. cross
343
- // the threshold clearly so browser coordinate rounding cannot defer it.
344
- const slop = 2 * scrollTouchSlop(DETOX_PLATFORM, 1)
342
+ // recognition consumes only axis slop. compensate that distance once so
343
+ // excess travel on the first move remains part of the requested scroll.
344
+ const slop = scrollTouchSlop(DETOX_PLATFORM, 1)
345
345
  const travel = pixels + slop
346
346
 
347
347
  switch (direction) {
package/detox/index.ts CHANGED
@@ -62,8 +62,8 @@ export type { SootSimElement } from './element-types'
62
62
  declare global {
63
63
  interface Window {
64
64
  // installed by takeScreenshotStagesFast; each input action calls it with
65
- // its capture token after successfully handing its input to the engine so
66
- // only that capture's stages anchor at that dispatch.
65
+ // its capture token immediately before invoking the input bridge so
66
+ // only that capture's stages anchor at dispatch, before input completes.
67
67
  __sootsimAnchorTimedInput?: (token: string) => void
68
68
  }
69
69
  }
@@ -71,7 +71,7 @@ declare global {
71
71
  const BASE_URL = process.env.PEACH_URL || 'http://localhost:5173'
72
72
  const SCREENSHOT_DIR =
73
73
  process.env.PEACH_SCREENSHOT_DIR ||
74
- path.join(process.cwd(), 'test', 'detox-driver', 'screenshots')
74
+ path.join(process.cwd(), 'test', 'detox-driver', 'screenshots', String(process.pid))
75
75
 
76
76
  // shared state -- playwright page and browser
77
77
  let _browser: Browser | null = null
@@ -560,7 +560,10 @@ export async function waitForSootsimIdle(
560
560
  layoutDirty = stats?.layoutDirty === true && stats?.renderRequested === true
561
561
  } catch {}
562
562
  }
563
+ let webViewStarting = false
563
564
  if (root) {
565
+ const PREVIEW_PROGRESS_REGEX =
566
+ /^(creating webview|loading html|fetching bundle|waiting for first render|starting preview|loading simulator|fetching app code|waiting for native preview|starting design|loading canvas|fetching screens|painting the canvas|waiting for webview|waiting for preview|waiting for canvas|preview stalled)/i
564
567
  const walk = (node: any) => {
565
568
  if (node.layout && node.layout.width > 0) {
566
569
  layout.push(
@@ -570,16 +573,39 @@ export async function waitForSootsimIdle(
570
573
  Math.round(node.layout.height),
571
574
  )
572
575
  }
576
+ const tid = node.testID || node.props?.testID || node.props?.testId
577
+ if (
578
+ typeof tid === 'string' &&
579
+ (tid === 'web-preview-progress' || tid.endsWith('-preview-progress'))
580
+ ) {
581
+ webViewStarting = true
582
+ }
583
+ const text =
584
+ node.textContent ||
585
+ (typeof node.props?.children === 'string' ? node.props.children : '')
586
+ if (typeof text === 'string' && PREVIEW_PROGRESS_REGEX.test(text.trim())) {
587
+ webViewStarting = true
588
+ }
573
589
  for (const child of node.children || []) walk(child)
574
590
  }
575
591
  walk(root)
576
592
  }
593
+ if (!webViewStarting && typeof document !== 'undefined') {
594
+ if (
595
+ document.querySelector(
596
+ '[data-testid$="-preview-progress"], [data-testid="web-preview-progress"], [data-testid="ios-preview-progress"], [data-testid="design-preview-progress"], [data-sootsim-webview-loading="true"], [data-sootsim-webview-mode="placeholder"]',
597
+ )
598
+ ) {
599
+ webViewStarting = true
600
+ }
601
+ }
577
602
  return {
578
603
  animating,
579
604
  auxSurfaceDebug,
580
605
  layout,
581
606
  layoutDirty,
582
607
  renderStatsAvailable,
608
+ webViewStarting,
583
609
  }
584
610
  }
585
611
 
@@ -593,6 +619,7 @@ export async function waitForSootsimIdle(
593
619
  (!strict || snapshot.renderStatsAvailable) &&
594
620
  !snapshot.animating &&
595
621
  !snapshot.layoutDirty &&
622
+ !snapshot.webViewStarting &&
596
623
  isLayoutStable(stableLayout, snapshot.layout)
597
624
  ) {
598
625
  stable++
@@ -613,7 +640,7 @@ export async function waitForSootsimIdle(
613
640
  : !lastSnapshot
614
641
  ? 'deadline spent before the first idle poll'
615
642
  : strict
616
- ? `strict idle state unavailable: renderStats=${lastSnapshot?.renderStatsAvailable === true} animating=${lastSnapshot?.animating === true} layoutDirty=${lastSnapshot?.layoutDirty === true} layoutNodes=${(lastSnapshot?.layout.length ?? 0) / 4} aux=${JSON.stringify(lastSnapshot?.auxSurfaceDebug)}`
643
+ ? `strict idle state unavailable: renderStats=${lastSnapshot?.renderStatsAvailable === true} animating=${lastSnapshot?.animating === true} layoutDirty=${lastSnapshot?.layoutDirty === true} webViewStarting=${lastSnapshot?.webViewStarting === true} layoutNodes=${(lastSnapshot?.layout.length ?? 0) / 4} aux=${JSON.stringify(lastSnapshot?.auxSurfaceDebug)}`
617
644
  : 'layout fallback',
618
645
  }
619
646
  },
@@ -761,9 +788,9 @@ async function dispatchTapAtSootsimPoint(
761
788
  async ({ x, y, token }) => {
762
789
  const interactTap = window.SootSim?.bridges?.interact?.tap
763
790
  if (typeof interactTap === 'function') {
791
+ if (token) window.__sootsimAnchorTimedInput?.(token)
764
792
  const result = await interactTap(x, y)
765
793
  if (result && (typeof result !== 'object' || result.hit !== false)) {
766
- if (token) window.__sootsimAnchorTimedInput?.(token)
767
794
  return
768
795
  }
769
796
  }
@@ -771,8 +798,8 @@ async function dispatchTapAtSootsimPoint(
771
798
  if (typeof tap !== 'function') {
772
799
  throw new Error('sootsim tap bridge is not installed')
773
800
  }
774
- await tap(x, y)
775
801
  if (token) window.__sootsimAnchorTimedInput?.(token)
802
+ await tap(x, y)
776
803
  },
777
804
  { x, y, token },
778
805
  )
@@ -1171,8 +1198,8 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1171
1198
  if (typeof longPress !== 'function') {
1172
1199
  throw new Error('sootsim longPress bridge is not installed')
1173
1200
  }
1174
- await longPress(x, y, duration)
1175
1201
  if (token) window.__sootsimAnchorTimedInput?.(token)
1202
+ await longPress(x, y, duration)
1176
1203
  },
1177
1204
  { duration, x: target.x, y: target.y, token: captureToken },
1178
1205
  )
@@ -1254,6 +1281,12 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1254
1281
  await page.keyboard.type(text)
1255
1282
  },
1256
1283
 
1284
+ async tapReturnKey() {
1285
+ const page = getPage()
1286
+ await focusElementForTextEntry(el)
1287
+ await page.keyboard.press('Enter')
1288
+ },
1289
+
1257
1290
  async replaceText(text: string) {
1258
1291
  const page = getPage()
1259
1292
  const node = await findNodeByMatcher(matcher)
@@ -1449,8 +1482,8 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1449
1482
  if (typeof drag !== 'function') {
1450
1483
  throw new Error('sootsim interact bridge is not installed')
1451
1484
  }
1452
- await drag(fromX, fromY, toX, toY, steps, stepMs)
1453
1485
  if (token) window.__sootsimAnchorTimedInput?.(token)
1486
+ await drag(fromX, fromY, toX, toY, steps, stepMs)
1454
1487
  },
1455
1488
  {
1456
1489
  fromX: startSootSimX,
@@ -2168,8 +2201,10 @@ export const device = {
2168
2201
 
2169
2202
  // park every render clock before the interaction, detect the first changed
2170
2203
  // stepped frame inside the named component, then advance exact 60fps frames
2171
- // to each requested offset. native extraction also chooses the first frame
2172
- // at or after an offset, so both sides use the same rounding convention.
2204
+ // to each requested offset from that frame. native extraction anchors at
2205
+ // its detection frame's pts, so both legs zero at the first painted change
2206
+ // and native also chooses the first frame at or after an offset, which is
2207
+ // the same rounding convention from the same zero.
2173
2208
  async takeScreenshotStagesFromVisibleChangeFast(
2174
2209
  namePrefix: string,
2175
2210
  stages: Array<{
@@ -2473,9 +2508,8 @@ export const device = {
2473
2508
  controller.initialFrame.virtualTimestampMs + input.hold.durationMs,
2474
2509
  )
2475
2510
  }
2476
- // an anchor is the frame before its visible change, which the detector
2477
- // has already stepped past; its bitmap is kept so a 0ms stage can still
2478
- // capture the anchor frame itself.
2511
+ // an anchor is its visible-change frame; its bitmap is kept so a 0ms
2512
+ // stage can still capture the anchor frame itself.
2479
2513
  const keptFrames = new Map<number, ImageBitmap>()
2480
2514
  try {
2481
2515
  const baselineBitmap = await captureBitmap()
@@ -2503,7 +2537,6 @@ export const device = {
2503
2537
 
2504
2538
  let visibleStart: FrameReceipt | null = null
2505
2539
  let detectedMeasurement: MotionChangeMeasurement | null = null
2506
- let previousReceipt: FrameReceipt | null = null
2507
2540
  let previousBitmap: ImageBitmap | null = null
2508
2541
  // android can publish mount layout at p=0 before a clock step.
2509
2542
  if (input.platform === 'android') {
@@ -2541,30 +2574,27 @@ export const device = {
2541
2574
  width: bounds.width,
2542
2575
  })
2543
2576
  if (measurement.passedMinimum) {
2544
- candidateBitmap.close()
2577
+ previousBitmap?.close()
2578
+ previousBitmap = null
2545
2579
  visibleStart = receipt
2546
2580
  detectedMeasurement = measurement
2581
+ keptFrames.set(receipt.frameIndex, candidateBitmap)
2547
2582
  break
2548
2583
  }
2549
2584
  previousBitmap?.close()
2550
2585
  previousBitmap = candidateBitmap
2551
- previousReceipt = receipt
2552
- }
2553
- if (previousReceipt && previousBitmap) {
2554
- keptFrames.set(previousReceipt.frameIndex, previousBitmap)
2555
2586
  }
2556
2587
  if (!visibleStart || !detectedMeasurement) {
2588
+ previousBitmap?.close()
2557
2589
  throw new Error(
2558
2590
  `peach motion had no visible change before ${input.detectionTimeoutMs}ms`,
2559
2591
  )
2560
2592
  }
2561
- // android screenrecord stages measure from the detected frame.
2562
- // ios morphs retain the preceding event tick: the browser paints
2563
- // p=0 unchanged while native's touch highlight trips its gate.
2564
- const driveStart =
2565
- input.platform === 'android'
2566
- ? visibleStart
2567
- : (previousReceipt ?? visibleStart)
2593
+ // stages on every platform measure from the first frame that
2594
+ // differs from baseline: native extraction anchors at its
2595
+ // detection frame's pts, so the drive starts here too. the
2596
+ // preceding tick is still baseline on both legs.
2597
+ const driveStart = visibleStart
2568
2598
  let currentReceipt = visibleStart
2569
2599
  const resolvedAnchors = new Map<string, FrameReceipt>()
2570
2600
  const anchorRecords: Array<{
@@ -2575,9 +2605,8 @@ export const device = {
2575
2605
  virtualTimestampMs: number
2576
2606
  }> = []
2577
2607
  // a second drive (the pop after a swipe's pan) re-detects from the
2578
- // search start: the release tick renders no change (the phase flip
2579
- // lands the frame after), so the new drive is the tick before the
2580
- // first consecutive pair past the gate, mirroring the drive rule.
2608
+ // search start: the new drive is the passing frame itself, the
2609
+ // frame native reports as the onset atSec, mirroring the drive rule.
2581
2610
  const resolveAnchor = async (stage: {
2582
2611
  label: string
2583
2612
  anchor?: { id: string; notBeforeMs: number; onsetThresholdPercent?: number }
@@ -2647,14 +2676,14 @@ export const device = {
2647
2676
  } else {
2648
2677
  latePeakPercent = Math.max(latePeakPercent, measurement.changePercent)
2649
2678
  }
2650
- if (measurement.passedMinimum) {
2651
- candidateBitmap.close()
2652
- onsetMeasurement = measurement
2653
- break
2654
- }
2679
+ const passed = measurement.passedMinimum
2655
2680
  anchorBitmap.close()
2656
2681
  anchorBitmap = candidateBitmap
2657
2682
  anchorReceipt = receipt
2683
+ if (passed) {
2684
+ onsetMeasurement = measurement
2685
+ break
2686
+ }
2658
2687
  }
2659
2688
  if (!onsetMeasurement) {
2660
2689
  anchorBitmap.close()
@@ -3230,7 +3259,7 @@ export const device = {
3230
3259
  const simId = await page.evaluate(() => window.SootSim?.state.simId)
3231
3260
  if (!simId) throw new Error('peach location requires a registered harness sim')
3232
3261
  const port = resolveSootsimBridgePortForHttpPort({
3233
- explicitPort: process.env.SOOTSIM_BRIDGE_PORT,
3262
+ explicitPort: process.env.PEACH_BRIDGE_PORT,
3234
3263
  httpPort: new URL(page.url()).port,
3235
3264
  baseHttpPort: 5173,
3236
3265
  })
@@ -3320,9 +3349,9 @@ export const device = {
3320
3349
  async ({ x: tx, y: ty, token }) => {
3321
3350
  const interactTap = window.SootSim?.bridges?.interact?.tap
3322
3351
  if (typeof interactTap === 'function') {
3352
+ if (token) window.__sootsimAnchorTimedInput?.(token)
3323
3353
  const result = await interactTap(tx, ty)
3324
3354
  if (result && (typeof result !== 'object' || result.hit !== false)) {
3325
- if (token) window.__sootsimAnchorTimedInput?.(token)
3326
3355
  return
3327
3356
  }
3328
3357
  }
@@ -3330,8 +3359,8 @@ export const device = {
3330
3359
  if (typeof tap !== 'function') {
3331
3360
  throw new Error('sootsim tap bridge is not installed')
3332
3361
  }
3333
- await tap(tx, ty)
3334
3362
  if (token) window.__sootsimAnchorTimedInput?.(token)
3363
+ await tap(tx, ty)
3335
3364
  },
3336
3365
  { x, y, token: captureToken },
3337
3366
  )
@@ -3353,11 +3382,11 @@ export const device = {
3353
3382
  if (typeof longPress !== 'function') {
3354
3383
  throw new Error('sootsim longPress bridge is not installed')
3355
3384
  }
3385
+ if (token) window.__sootsimAnchorTimedInput?.(token)
3356
3386
  const hit = await longPress(px, py, ms)
3357
3387
  if (!hit) {
3358
3388
  throw new Error(`sootsim longPress missed at ${px},${py}`)
3359
3389
  }
3360
- if (token) window.__sootsimAnchorTimedInput?.(token)
3361
3390
  },
3362
3391
  { x, y, ms: durationMs, token: captureToken },
3363
3392
  )
@@ -91,12 +91,15 @@ function recordPageDiagnostic(page: Page, diagnostic: PageDiagnostic, persist =
91
91
  if (persist) writeAttemptEvent(diagnostic)
92
92
  }
93
93
 
94
- function isTopLevelDocumentRequest(page: Page, request: Request) {
95
- return (
96
- request.isNavigationRequest() &&
97
- request.resourceType() === 'document' &&
98
- request.frame() === page.mainFrame()
99
- )
94
+ export function isTopLevelDocumentRequest(page: Page, request: Request) {
95
+ if (!request.isNavigationRequest() || request.resourceType() !== 'document') {
96
+ return false
97
+ }
98
+ try {
99
+ return request.frame() === page.mainFrame()
100
+ } catch {
101
+ return false
102
+ }
100
103
  }
101
104
 
102
105
  function recordNavigationMilestoneFor(
@@ -1,3 +1,5 @@
1
+ const { abs, max } = Math
2
+
1
3
  // proof captures reserve the top and bottom of a device frame for platform
2
4
  // chrome. those pixels can be fully opaque while the app surface is blank or
3
5
  // still fading in, so evidence density is measured only in the content band.
@@ -71,10 +73,10 @@ function measureProofFrameContent(input) {
71
73
  for (let x = 0; x < width; x++) {
72
74
  const offset = (y * width + x) * 4
73
75
  if (
74
- Math.max(
75
- Math.abs(data[offset] - dominant.red),
76
- Math.abs(data[offset + 1] - dominant.green),
77
- Math.abs(data[offset + 2] - dominant.blue),
76
+ max(
77
+ abs(data[offset] - dominant.red),
78
+ abs(data[offset + 1] - dominant.green),
79
+ abs(data[offset + 2] - dominant.blue),
78
80
  ) >= PROOF_CONTENT_DEFINITION.channelThreshold
79
81
  ) {
80
82
  contentPixels++
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;