rnxsim 0.1.365 → 0.1.367

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 (240) hide show
  1. package/README.md +14 -0
  2. package/cli/bin.ts +18 -0
  3. package/cli/bridge-flow-runner.ts +16 -153
  4. package/cli/commands/assert.ts +1 -1
  5. package/cli/commands/box/checkout-plane.ts +126 -0
  6. package/cli/commands/box/rnx-command.ts +68 -0
  7. package/cli/commands/box.ts +200 -0
  8. package/cli/commands/control.ts +15 -76
  9. package/cli/commands/detox.ts +18 -1
  10. package/cli/commands/do-chain.ts +412 -0
  11. package/cli/commands/flow.ts +2 -11
  12. package/cli/commands/inspect/core.ts +302 -725
  13. package/cli/commands/inspect/describe.ts +3 -76
  14. package/cli/commands/inspect/find.ts +3 -6
  15. package/cli/commands/inspect/get-layout.ts +14 -1
  16. package/cli/commands/inspect/resolve-target.ts +41 -47
  17. package/cli/commands/inspect/shared.ts +28 -123
  18. package/cli/commands/inspect/tree.ts +6 -15
  19. package/cli/commands/inspect/wait-ready.ts +2 -2
  20. package/cli/commands/inspect/wait-selector.ts +4 -8
  21. package/cli/commands/inspect.ts +23 -17
  22. package/cli/commands/install-desktop.ts +44 -7
  23. package/cli/commands/maestro-generate.ts +0 -2
  24. package/cli/commands/platform.ts +68 -0
  25. package/cli/commands/state.ts +5 -2
  26. package/cli/outbound-endpoints.ts +6 -1
  27. package/cli/parse-args.ts +13 -2
  28. package/cli/ws-bridge.ts +30 -2
  29. package/detox/index.ts +37 -18
  30. package/dist-cli/bin.js +5 -5
  31. package/dist-cli/chunks/{agent-BVMNQFKF.js → agent-EKKIAKP5.js} +2 -2
  32. package/dist-cli/chunks/{agent-wrapper-B6YFXGC3.js → agent-wrapper-MHLQUGA6.js} +2 -2
  33. package/dist-cli/chunks/{app-fonts-MEI6R6XR.js → app-fonts-HN6ADRTQ.js} +2 -2
  34. package/dist-cli/chunks/{assert-JGHI53HV.js → assert-IMR6QCN6.js} +3 -3
  35. package/dist-cli/chunks/{auth-STEOXUS7.js → auth-27LPMQNH.js} +2 -2
  36. package/dist-cli/chunks/{beta-HT3FLS5T.js → beta-6RXBB5AI.js} +2 -2
  37. package/dist-cli/chunks/box-DORLF3GP.js +1709 -0
  38. package/dist-cli/chunks/{camera-ZCLFSLON.js → camera-2G7JZ5DL.js} +2 -2
  39. package/dist-cli/chunks/{chunk-RI3NS77J.js → chunk-3QZJLNYY.js} +1 -1
  40. package/dist-cli/chunks/{chunk-FHWZPUGC.js → chunk-3VBVEDDA.js} +1 -1
  41. package/dist-cli/chunks/{chunk-EAFKG5R3.js → chunk-4BWGLTKU.js} +2 -2
  42. package/dist-cli/chunks/chunk-4XSNO374.js +423 -0
  43. package/dist-cli/chunks/{chunk-GZEUDDAW.js → chunk-5DGMFUZG.js} +2 -2
  44. package/dist-cli/chunks/{chunk-TKCGP3DD.js → chunk-6DBS5EGX.js} +2 -2
  45. package/dist-cli/chunks/{chunk-HIDS3PYC.js → chunk-6SO6I3D5.js} +2 -2
  46. package/dist-cli/chunks/chunk-75EFOPXE.js +32 -0
  47. package/dist-cli/chunks/{chunk-FIXRGPUT.js → chunk-A5AJFVOF.js} +1 -1
  48. package/dist-cli/chunks/{chunk-2SFMW5K6.js → chunk-AM2ODYAN.js} +1 -1
  49. package/dist-cli/chunks/{chunk-BGRGP3QR.js → chunk-AVOROGA3.js} +1 -1
  50. package/dist-cli/chunks/chunk-AXMMLO7D.js +130 -0
  51. package/dist-cli/chunks/{chunk-XXNPISER.js → chunk-BSEXEYNX.js} +2 -2
  52. package/dist-cli/chunks/{chunk-67ATAITF.js → chunk-CM6ZKZW7.js} +2 -2
  53. package/dist-cli/chunks/{chunk-MR3YPNUI.js → chunk-DNVABCEC.js} +2 -2
  54. package/dist-cli/chunks/{chunk-5AS7S6HY.js → chunk-E33A4AIR.js} +2 -2
  55. package/dist-cli/chunks/{chunk-DBJZUCFP.js → chunk-EFMOXYDD.js} +2 -2
  56. package/dist-cli/chunks/{chunk-ZTWUNK45.js → chunk-EV4P5UBP.js} +2 -2
  57. package/dist-cli/chunks/{chunk-EVBQDQVY.js → chunk-G5IUIGGC.js} +1 -1
  58. package/dist-cli/chunks/chunk-GMLOCV3E.js +312 -0
  59. package/dist-cli/chunks/{chunk-DQFOHIRF.js → chunk-GRN2FHVW.js} +1 -1
  60. package/dist-cli/chunks/{chunk-CYIE65QN.js → chunk-HFFXPUEP.js} +1 -1
  61. package/dist-cli/chunks/chunk-HG6T4OOR.js +4 -0
  62. package/dist-cli/chunks/{chunk-GK7PGYFH.js → chunk-HNT2OXPZ.js} +1 -1
  63. package/dist-cli/chunks/chunk-IPEURNLI.js +24 -0
  64. package/dist-cli/chunks/chunk-IR44SFBA.js +5 -0
  65. package/dist-cli/chunks/chunk-JMSLXZFW.js +4 -0
  66. package/dist-cli/chunks/{chunk-WGNDJARS.js → chunk-JPXQKLNH.js} +1 -1
  67. package/dist-cli/chunks/chunk-K3EC6JDE.js +4 -0
  68. package/dist-cli/chunks/chunk-KHX65HAU.js +4 -0
  69. package/dist-cli/chunks/{chunk-MACI34ST.js → chunk-LWOVKHRC.js} +2 -2
  70. package/dist-cli/chunks/{chunk-QIQL7Y5V.js → chunk-MQN5DWTI.js} +2 -2
  71. package/dist-cli/chunks/{chunk-5VHWQGGF.js → chunk-N3BXQEUF.js} +1 -1
  72. package/dist-cli/chunks/{chunk-O2ORLK6W.js → chunk-N5OSV26B.js} +2 -2
  73. package/dist-cli/chunks/chunk-PHNURIH3.js +4 -0
  74. package/dist-cli/chunks/{chunk-JRPU5TFI.js → chunk-PP2KNVXP.js} +3 -3
  75. package/dist-cli/chunks/{chunk-UR345KM3.js → chunk-PROKNJVY.js} +2 -2
  76. package/dist-cli/chunks/chunk-QXLLY4IM.js +7 -0
  77. package/dist-cli/chunks/{chunk-2CXHDTQ5.js → chunk-RNQTOGM7.js} +1 -1
  78. package/dist-cli/chunks/{chunk-M5KJCLUW.js → chunk-RPNKKVBN.js} +27 -27
  79. package/dist-cli/chunks/{chunk-EFXNSJ55.js → chunk-RSNLI2NH.js} +2 -2
  80. package/dist-cli/chunks/{chunk-25BTVHK2.js → chunk-RZGHV2WI.js} +2 -2
  81. package/dist-cli/chunks/chunk-SGGL3AFV.js +497 -0
  82. package/dist-cli/chunks/chunk-SH5ETJKG.js +4 -0
  83. package/dist-cli/chunks/{chunk-MRNW4GVL.js → chunk-T6J4YP56.js} +2 -2
  84. package/dist-cli/chunks/{chunk-R4FQZXJC.js → chunk-TWUE7WF5.js} +2 -2
  85. package/dist-cli/chunks/{chunk-X5ZOZVHC.js → chunk-U44VEQRN.js} +1 -1
  86. package/dist-cli/chunks/{chunk-XRO5WQXP.js → chunk-URLVQHC3.js} +1 -1
  87. package/dist-cli/chunks/{chunk-JAFF4RAU.js → chunk-VANZFTUE.js} +1 -1
  88. package/dist-cli/chunks/{chunk-6BAF6GFS.js → chunk-VB2YBH46.js} +1 -1
  89. package/dist-cli/chunks/{chunk-O6P4N2JD.js → chunk-VFU2SJ7M.js} +2 -2
  90. package/dist-cli/chunks/{chunk-XPOTXF5Q.js → chunk-VUBJ6FXU.js} +15 -15
  91. package/dist-cli/chunks/{chunk-XR4KID25.js → chunk-W5PATILQ.js} +2 -2
  92. package/dist-cli/chunks/chunk-WI5V4TYG.js +38 -0
  93. package/dist-cli/chunks/{chunk-NEH4T67X.js → chunk-XTRHQAOV.js} +2 -2
  94. package/dist-cli/chunks/chunk-XWUTJLBM.js +27 -0
  95. package/dist-cli/chunks/{chunk-TULWPOAY.js → chunk-XYRUQQUR.js} +2 -2
  96. package/dist-cli/chunks/chunk-YBFB3RM7.js +4 -0
  97. package/dist-cli/chunks/chunk-YGQVQ2VG.js +4 -0
  98. package/dist-cli/chunks/{chunk-J66US2OZ.js → chunk-YINACV32.js} +1 -1
  99. package/dist-cli/chunks/chunk-YV7ERO2V.js +5 -0
  100. package/dist-cli/chunks/{cleanup-G4TEYVKY.js → cleanup-GVFHQ3KH.js} +2 -2
  101. package/dist-cli/chunks/cli-version-DPX5MA2M.js +4 -0
  102. package/dist-cli/chunks/{compat-EZ5NP32J.js → compat-AYRTXYAM.js} +2 -2
  103. package/dist-cli/chunks/{config-27M2DGCS.js → config-K7LOOV62.js} +2 -2
  104. package/dist-cli/chunks/connect-H3CNY4IO.js +4 -0
  105. package/dist-cli/chunks/control-SMHZ2CF5.js +4 -0
  106. package/dist-cli/chunks/daemon-TEZUNI73.js +4 -0
  107. package/dist-cli/chunks/{debug-EXU3D7PC.js → debug-ELGHG57T.js} +3 -3
  108. package/dist-cli/chunks/{desktop-SHHPZAWQ.js → desktop-4G7FK3MP.js} +3 -3
  109. package/dist-cli/chunks/detox-JKXGEJIZ.js +52 -0
  110. package/dist-cli/chunks/{device-4NI26HG6.js → device-IV3AR6KG.js} +2 -2
  111. package/dist-cli/chunks/{diagnose-P4SFXH6J.js → diagnose-3ERGF7EW.js} +2 -2
  112. package/dist-cli/chunks/{disk-cleanup-BUAKWYMY.js → disk-cleanup-ALFEVZ4B.js} +2 -2
  113. package/dist-cli/chunks/do-chain-K2B4OHM7.js +5 -0
  114. package/dist-cli/chunks/drivers-KJYXA3M3.js +4 -0
  115. package/dist-cli/chunks/env-2KF6IYZE.js +4 -0
  116. package/dist-cli/chunks/{film-Y2BAQINB.js → film-UQXJFAD5.js} +3 -3
  117. package/dist-cli/chunks/flow-ASTUH56E.js +4 -0
  118. package/dist-cli/chunks/help-SNNYFELA.js +4 -0
  119. package/dist-cli/chunks/{hidden-runtime-alias-U2MAY2XV.js → hidden-runtime-alias-VJCWCFVO.js} +2 -2
  120. package/dist-cli/chunks/{home-paths-PAJMZCOS.js → home-paths-EGUZZ6J6.js} +2 -2
  121. package/dist-cli/chunks/inspect-XANAADQT.js +4 -0
  122. package/dist-cli/chunks/install-desktop-JKQA7UY6.js +4 -0
  123. package/dist-cli/chunks/{login-D53XKBEO.js → login-6KQGD7HK.js} +2 -2
  124. package/dist-cli/chunks/{logout-HZQKLNUJ.js → logout-2FCOVLBC.js} +2 -2
  125. package/dist-cli/chunks/{maestro-XSS2KX6L.js → maestro-HPSSEVJD.js} +3 -3
  126. package/dist-cli/chunks/{maestro-generate-DNDQX3JQ.js → maestro-generate-KQQ6JHMR.js} +5 -5
  127. package/dist-cli/chunks/{mode-LEVGOKI3.js → mode-SH7KRZET.js} +2 -2
  128. package/dist-cli/chunks/{optional-demo-registry-2XZIJNDG.js → optional-demo-registry-NAMCAVFX.js} +2 -2
  129. package/dist-cli/chunks/{perf-OKMAXDLB.js → perf-LWKAHXUL.js} +2 -2
  130. package/dist-cli/chunks/{permissions-VG5TBUTN.js → permissions-EZTKWTXV.js} +2 -2
  131. package/dist-cli/chunks/platform-KSOJIVN5.js +6 -0
  132. package/dist-cli/chunks/privacy-EJULWP7L.js +4 -0
  133. package/dist-cli/chunks/{record-BQYGZOMV.js → record-2IC7BAQN.js} +3 -3
  134. package/dist-cli/chunks/{report-issue-HCPZESWE.js → report-issue-MMX6HOQO.js} +2 -2
  135. package/dist-cli/chunks/reset-PKFZA4NC.js +4 -0
  136. package/dist-cli/chunks/resolve-target-AFKU3BRE.js +4 -0
  137. package/dist-cli/chunks/runtime-XHWWPOIN.js +4 -0
  138. package/dist-cli/chunks/{screenshot-command-DWEYPJE7.js → screenshot-command-JYUXDVSA.js} +3 -3
  139. package/dist-cli/chunks/{screenshot-layers-5GMBXLO7.js → screenshot-layers-OGRY6AA3.js} +2 -2
  140. package/dist-cli/chunks/{screenshots-capture-5KCI7IZD.js → screenshots-capture-QHDN65XS.js} +2 -2
  141. package/dist-cli/chunks/{serve-QNKTA2HR.js → serve-W4BKAG4J.js} +3 -3
  142. package/dist-cli/chunks/setup-WJPMPSAC.js +4 -0
  143. package/dist-cli/chunks/sim-client-UOQGFBPO.js +4 -0
  144. package/dist-cli/chunks/{skills-DOEALD3X.js → skills-OPPXOD63.js} +2 -2
  145. package/dist-cli/chunks/state-TDAN7BOM.js +14 -0
  146. package/dist-cli/chunks/{storage-E7WUWN3E.js → storage-CFB2YGLY.js} +2 -2
  147. package/dist-cli/chunks/store-4TEED4ZU.js +4 -0
  148. package/dist-cli/chunks/telemetry-S6N7CV6G.js +4 -0
  149. package/dist-cli/chunks/{timeline-IKNUGQ2A.js → timeline-ASOOVP4C.js} +2 -2
  150. package/dist-cli/chunks/{upgrade-VS3OKXE2.js → upgrade-6MVJISHG.js} +2 -2
  151. package/dist-cli/chunks/upload-CBNIRFEW.js +4 -0
  152. package/dist-cli/chunks/{version-F4XHNTBZ.js → version-5GS7CWRL.js} +2 -2
  153. package/dist-cli/chunks/{web-F72724QL.js → web-QRRMYMV4.js} +2 -2
  154. package/dist-cli/chunks/welcome-33FXMAMB.js +5 -0
  155. package/dist-cli/chunks/{what-happened-APK7YKOV.js → what-happened-GAZJGKXX.js} +2 -2
  156. package/dist-cli/chunks/ws-bridge-DIXUAPLQ.js +4 -0
  157. package/dist-lib/agent-daemon-client.cjs +1 -1
  158. package/dist-lib/agent-events.cjs +1 -1
  159. package/dist-lib/agent-identity.cjs +1 -1
  160. package/dist-lib/agent-sessions.cjs +1 -1
  161. package/dist-lib/attached-projects.cjs +1 -1
  162. package/dist-lib/auth/shared-session.cjs +1 -1
  163. package/dist-lib/backend-origin.cjs +1 -1
  164. package/dist-lib/beta.cjs +1 -1
  165. package/dist-lib/beta.mjs +1 -1
  166. package/dist-lib/bridge-constants.cjs +1 -1
  167. package/dist-lib/bridge-contract-input.cjs +336 -0
  168. package/dist-lib/bridge-contract-input.mjs +303 -0
  169. package/dist-lib/bridge-contract.cjs +29 -1
  170. package/dist-lib/bridge-contract.mjs +19 -0
  171. package/dist-lib/cli-constants.cjs +1 -1
  172. package/dist-lib/config.cjs +1 -1
  173. package/dist-lib/detox/index.cjs +35 -19
  174. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  175. package/dist-lib/home-paths.cjs +1 -1
  176. package/dist-lib/host/bridge-host.cjs +5 -1
  177. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  178. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  179. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  180. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  181. package/dist-lib/host/websocket-proxy.cjs +1 -1
  182. package/dist-lib/index.cjs +244 -594
  183. package/dist-lib/jump-to-source-babel.cjs +1 -1
  184. package/dist-lib/menu.cjs +1 -1
  185. package/dist-lib/menu.mjs +1 -1
  186. package/dist-lib/metro-production-bundle.cjs +1 -1
  187. package/dist-lib/metro-production-bundle.mjs +1 -1
  188. package/dist-lib/metro.cjs +1 -1
  189. package/dist-lib/profiles.cjs +1 -1
  190. package/dist-lib/public-brand.cjs +1 -1
  191. package/dist-lib/react-native-host-modules.cjs +1 -1
  192. package/dist-lib/react-native-host-modules.mjs +1 -1
  193. package/dist-lib/render-mode.cjs +1 -1
  194. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  195. package/dist-lib/sdk.cjs +303 -657
  196. package/dist-lib/sdk.mjs +301 -652
  197. package/dist-lib/skills.cjs +256 -339
  198. package/dist-lib/vite.cjs +1 -1
  199. package/package.json +10 -1
  200. package/skills/contrast/SKILL.md +3 -0
  201. package/skills/rnx-debug/SKILL.md +8 -0
  202. package/skills/rnx-test/SKILL.md +21 -0
  203. package/src/app-url.ts +40 -0
  204. package/src/bridge-contract-input.ts +359 -0
  205. package/src/bridge-contract.ts +121 -1
  206. package/src/host/bridge-host.ts +7 -12
  207. package/src/index.ts +7 -0
  208. package/src/sim-client.ts +24 -9
  209. package/dist-cli/chunks/chunk-2JFZ5Z7Q.js +0 -4
  210. package/dist-cli/chunks/chunk-4WFN4DFR.js +0 -5
  211. package/dist-cli/chunks/chunk-52ES4API.js +0 -38
  212. package/dist-cli/chunks/chunk-622E4JET.js +0 -767
  213. package/dist-cli/chunks/chunk-EFL2ASZO.js +0 -124
  214. package/dist-cli/chunks/chunk-IWPS3EKJ.js +0 -15
  215. package/dist-cli/chunks/chunk-MCHKBFUL.js +0 -4
  216. package/dist-cli/chunks/chunk-OSZ5HZSI.js +0 -24
  217. package/dist-cli/chunks/chunk-W3UIDI3W.js +0 -5
  218. package/dist-cli/chunks/chunk-XKRUNS7A.js +0 -32
  219. package/dist-cli/chunks/chunk-YE3M3YD4.js +0 -944
  220. package/dist-cli/chunks/chunk-ZCP4CRP6.js +0 -4
  221. package/dist-cli/chunks/chunk-ZGD7GZRI.js +0 -4
  222. package/dist-cli/chunks/chunk-ZGH5U4AJ.js +0 -27
  223. package/dist-cli/chunks/cli-version-ZM3PVKQ5.js +0 -4
  224. package/dist-cli/chunks/control-4QR43FXV.js +0 -4
  225. package/dist-cli/chunks/daemon-2U4CJ6AT.js +0 -4
  226. package/dist-cli/chunks/detox-RQ6FFSBS.js +0 -52
  227. package/dist-cli/chunks/drivers-OVCUPQTM.js +0 -4
  228. package/dist-cli/chunks/flow-ELTLKKZ3.js +0 -4
  229. package/dist-cli/chunks/help-CDEZAG2L.js +0 -4
  230. package/dist-cli/chunks/inspect-VVQRPJLS.js +0 -4
  231. package/dist-cli/chunks/install-desktop-YXSRKR35.js +0 -4
  232. package/dist-cli/chunks/privacy-MNRHREME.js +0 -4
  233. package/dist-cli/chunks/reset-R4E4W4YI.js +0 -4
  234. package/dist-cli/chunks/runtime-XRQZ7W34.js +0 -4
  235. package/dist-cli/chunks/setup-RNMM5GO5.js +0 -4
  236. package/dist-cli/chunks/state-3HH2IHIF.js +0 -14
  237. package/dist-cli/chunks/store-HUPZUCFG.js +0 -4
  238. package/dist-cli/chunks/telemetry-IJ53XR2Y.js +0 -4
  239. package/dist-cli/chunks/upload-A57NAAHF.js +0 -4
  240. package/dist-cli/chunks/welcome-B72RRMBW.js +0 -5
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.365 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.367 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1175,8 +1175,18 @@ var REACT_NATIVE_ROOT_EXPORT_WILL_IT_WORK_COVERAGE, REACT_NATIVE_ROOT_EXPORT_STR
1175
1175
  var init_react_native_root_export_coverage = __esm({
1176
1176
  "../compat/src/generated/react-native-root-export-coverage.ts"() {
1177
1177
  "use strict";
1178
- REACT_NATIVE_ROOT_EXPORT_WILL_IT_WORK_COVERAGE = 0.98;
1179
- REACT_NATIVE_ROOT_EXPORT_STRICT_COVERAGE = 0.653;
1178
+ REACT_NATIVE_ROOT_EXPORT_WILL_IT_WORK_COVERAGE = 0.99;
1179
+ REACT_NATIVE_ROOT_EXPORT_STRICT_COVERAGE = 0.765;
1180
+ }
1181
+ });
1182
+
1183
+ // ../compat/src/generated/react-native-webview-coverage.ts
1184
+ var REACT_NATIVE_WEBVIEW_WILL_IT_WORK_COVERAGE, REACT_NATIVE_WEBVIEW_STRICT_COVERAGE;
1185
+ var init_react_native_webview_coverage = __esm({
1186
+ "../compat/src/generated/react-native-webview-coverage.ts"() {
1187
+ "use strict";
1188
+ REACT_NATIVE_WEBVIEW_WILL_IT_WORK_COVERAGE = 0.74;
1189
+ REACT_NATIVE_WEBVIEW_STRICT_COVERAGE = 0.223;
1180
1190
  }
1181
1191
  });
1182
1192
 
@@ -1220,6 +1230,7 @@ var init_registry = __esm({
1220
1230
  "use strict";
1221
1231
  init_capability_inventory_coverage();
1222
1232
  init_react_native_root_export_coverage();
1233
+ init_react_native_webview_coverage();
1223
1234
  init_unsupported_packages();
1224
1235
  COMPAT_CATEGORIES = [
1225
1236
  "All",
@@ -1371,10 +1382,11 @@ var init_registry = __esm({
1371
1382
  versions: [
1372
1383
  {
1373
1384
  range: ">=11.0.0",
1374
- coverage: 0.55,
1375
- note: "native-seam only: the published package runs unchanged and routes RNCWebView through the tenant-to-shell hosted iframe surface. chromium can load cross-origin source.uri pages under the shell COEP through a credentialless frame; same-origin URI and source.html keep normal cookie and persistent-storage semantics. the iframe keeps loading while parked behind a canvas placeholder and becomes interactive when promoted live. paired native and SootSim runtime conformance covers local-page onOpenWindow target delivery.",
1376
- working: "source.uri/source.html, Chromium cross-origin URI loading under the shell COEP, same-origin URI cookies, source.html persistent localStorage, document-start ReactNativeWebView bootstrap and injectedJavaScriptBeforeContentLoaded/injectedJavaScript on source.html and percent-encoded data: URI pages, strict ReactNativeWebView.postMessage bridge envelope, onMessage synthetic event with navigation fields, onOpenWindow target delivery for source.html and percent-encoded data: URI pages, onLoad/Start/End, onNavigationStateChange, ref.reload/stopLoading/injectJavaScript/postMessage, renderLoading, javaScriptEnabled sandbox, layout, inherited clipping and visibility, parked/live presentation",
1377
- missing: "Safari/WebKit cross-origin URI pages without COEP-compatible response headers remain permanently loading because WebKit ignores iframe credentialless; Chromium credentialless cross-origin URI frames send no credentials, including cookies set by their own origin, and use ephemeral storage; iframe pixels in canvas screenshots; script injection, ReactNativeWebView bootstrap, and onOpenWindow for remote pages, which get no bridge at all because a browser cannot script a cross-origin frame; the re-encoded data: URI a page reports as its own location.href, so onMessage carries the re-encoded url while onLoad and onNavigationStateChange carry the original where iOS reports one identical url for all three; cross-origin ref.goBack/goForward and truthful canGoBack/canGoForward values; failed HTTP/refused navigation callbacks (onError/onHttpError and renderError); onShouldStartLoadWithRequest, allowsBackForwardNavigationGestures, userAgent, dataDetectorTypes, cacheEnabled, incognito, scalesPageToFit, originWhitelist enforcement"
1385
+ coverage: REACT_NATIVE_WEBVIEW_WILL_IT_WORK_COVERAGE,
1386
+ strictCoverage: REACT_NATIVE_WEBVIEW_STRICT_COVERAGE,
1387
+ note: "native-seam only: the published package runs unchanged and routes RNCWebView through the tenant-to-shell hosted iframe surface. coverage is generated from app-source use in six pinned applications; equal-surface strict coverage includes the shared, iOS, and Android public prop and ref surface. paired native and SootSim runtime conformance covers local HTML, base URL, URI loading, load and navigation callbacks, both injection phases, two-way messaging, imperative URI reload and injection, content scrolling, parent translation, and onOpenWindow target delivery.",
1388
+ working: "source.uri/source.html/source.baseUrl, Chromium cross-origin URI loading under the shell COEP, same-origin URI cookies, source.html persistent localStorage, document-start ReactNativeWebView bootstrap and injectedJavaScriptBeforeContentLoaded/injectedJavaScript on source.html, strict ReactNativeWebView.postMessage bridge envelope in both directions, onMessage synthetic event with navigation fields, onOpenWindow target delivery for source.html, successful onLoad/Start/End and onNavigationStateChange, ref.reload on URI pages, ref.injectJavaScript/postMessage, internal content scrolling, renderLoading, javaScriptEnabled sandbox, layout, inherited clipping and visibility, parked/live presentation",
1389
+ missing: "Safari/WebKit cross-origin URI pages without COEP-compatible response headers remain permanently loading because WebKit ignores iframe credentialless; Chromium credentialless cross-origin URI frames send no credentials, including cookies set by their own origin, and use ephemeral storage; iframe pixels in canvas screenshots; script injection, ReactNativeWebView bootstrap, and onOpenWindow for remote pages, which get no bridge at all because a browser cannot script a cross-origin frame; cross-origin ref.goBack/goForward/stopLoading and truthful canGoBack/canGoForward values; failed HTTP/refused navigation callbacks (onError/onHttpError and renderError); onShouldStartLoadWithRequest, allowsBackForwardNavigationGestures, userAgent, dataDetectorTypes, cacheEnabled, incognito, scalesPageToFit, originWhitelist enforcement"
1378
1390
  }
1379
1391
  ]
1380
1392
  },
@@ -20244,6 +20256,10 @@ var init_bridge_host = __esm({
20244
20256
  return msg?.action === "type" ? "typed text" : "used keyboard";
20245
20257
  case "tree":
20246
20258
  return "dumped tree";
20259
+ case "query":
20260
+ return "queried semantic tree";
20261
+ case "resolve":
20262
+ return "resolved semantic selector";
20247
20263
  case "focus":
20248
20264
  return "focused sim";
20249
20265
  case "close":
@@ -20692,7 +20708,9 @@ var init_current_sim = __esm({
20692
20708
  var ws_bridge_exports = {};
20693
20709
  __export(ws_bridge_exports, {
20694
20710
  BridgeSimLockedError: () => BridgeSimLockedError,
20711
+ RNX_PLACEMENT_ENV: () => RNX_PLACEMENT_ENV,
20695
20712
  bridgePortForShellBaseUrl: () => bridgePortForShellBaseUrl,
20713
+ bridgeWsUrl: () => bridgeWsUrl,
20696
20714
  callInBridge: () => callInBridge,
20697
20715
  callInBridgeWrite: () => callInBridgeWrite,
20698
20716
  checkSimHealth: () => checkSimHealth,
@@ -20703,8 +20721,15 @@ __export(ws_bridge_exports, {
20703
20721
  printBridgeWorldNotice: () => printBridgeWorldNotice,
20704
20722
  resolveBridgeWorld: () => resolveBridgeWorld,
20705
20723
  resolveDefaultBridgePort: () => resolveDefaultBridgePort,
20724
+ resolvePlacement: () => resolvePlacement,
20706
20725
  worldForBridgePort: () => worldForBridgePort
20707
20726
  });
20727
+ function resolvePlacement() {
20728
+ return process.env[RNX_PLACEMENT_ENV] === "local-box" ? "local-box" : "host";
20729
+ }
20730
+ function bridgeWsUrl(wsPort) {
20731
+ return `ws://localhost:${wsPort}`;
20732
+ }
20708
20733
  function liveDevBridgeLockfiles() {
20709
20734
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
20710
20735
  }
@@ -20925,7 +20950,7 @@ function createBridge(wsPort, opts = {}) {
20925
20950
  let nextId = 1;
20926
20951
  const commandTimeoutMs = opts.commandTimeoutMs ?? 15e3;
20927
20952
  const pending = /* @__PURE__ */ new Map();
20928
- const ws = new import_ws3.WebSocket(`ws://localhost:${wsPort}`);
20953
+ const ws = new import_ws3.WebSocket(bridgeWsUrl(wsPort));
20929
20954
  const identity = getCliIdentity();
20930
20955
  const ready = new Promise((resolve7, reject) => {
20931
20956
  ws.on("open", () => {
@@ -20945,7 +20970,7 @@ function createBridge(wsPort, opts = {}) {
20945
20970
  });
20946
20971
  ws.on("error", (err) => {
20947
20972
  const cause = err.message ? `: ${err.message}` : "";
20948
- reject(new Error(`could not connect to ws://localhost:${wsPort}${cause}`));
20973
+ reject(new Error(`could not connect to ${bridgeWsUrl(wsPort)}${cause}`));
20949
20974
  });
20950
20975
  });
20951
20976
  ready.catch(() => {
@@ -21148,7 +21173,7 @@ async function callInBridge(bridge, path18, ...args) {
21148
21173
  async function callInBridgeWrite(bridge, path18, ...args) {
21149
21174
  return bridge.send({ type: "call", path: path18, args, acquireLock: true });
21150
21175
  }
21151
- var import_node_fs9, import_node_path11, import_ws3, resolvedWorld, BridgeSimLockedError;
21176
+ var import_node_fs9, import_node_path11, import_ws3, RNX_PLACEMENT_ENV, resolvedWorld, BridgeSimLockedError;
21152
21177
  var init_ws_bridge = __esm({
21153
21178
  "cli/ws-bridge.ts"() {
21154
21179
  "use strict";
@@ -21159,6 +21184,7 @@ var init_ws_bridge = __esm({
21159
21184
  init_cli_constants();
21160
21185
  init_home_paths();
21161
21186
  init_current_sim();
21187
+ RNX_PLACEMENT_ENV = "RNX_PLACEMENT";
21162
21188
  resolvedWorld = null;
21163
21189
  BridgeSimLockedError = class extends Error {
21164
21190
  lock;
@@ -21171,6 +21197,45 @@ var init_ws_bridge = __esm({
21171
21197
  }
21172
21198
  });
21173
21199
 
21200
+ // src/app-url.ts
21201
+ function normalizeAppUrlTarget(target) {
21202
+ const trimmed = target.trim();
21203
+ if (!trimmed) {
21204
+ throw new Error("expected <url-or-route>");
21205
+ }
21206
+ if (APP_URL_SCHEME_RE.test(trimmed) || trimmed.startsWith("/")) {
21207
+ return trimmed;
21208
+ }
21209
+ return `/${trimmed}`;
21210
+ }
21211
+ function isAppDeepLinkTarget(target) {
21212
+ const trimmed = target.trim();
21213
+ if (!trimmed) return false;
21214
+ if (trimmed.startsWith("/")) return true;
21215
+ if (!APP_URL_SCHEME_RE.test(trimmed)) return false;
21216
+ return !/^https?:\/\//i.test(trimmed);
21217
+ }
21218
+ async function openAppUrl(bridge, target) {
21219
+ const normalized = normalizeAppUrlTarget(target);
21220
+ const result = await bridge.send({
21221
+ type: "call",
21222
+ path: "SootSim.bridges.test.openDeepLink",
21223
+ args: [normalized]
21224
+ });
21225
+ if (result !== null && typeof result === "object" && Reflect.get(result, "ok") === false) {
21226
+ const error = Reflect.get(result, "error");
21227
+ throw new Error(typeof error === "string" ? error : "deep-link dispatch failed");
21228
+ }
21229
+ return normalized;
21230
+ }
21231
+ var APP_URL_SCHEME_RE;
21232
+ var init_app_url = __esm({
21233
+ "src/app-url.ts"() {
21234
+ "use strict";
21235
+ APP_URL_SCHEME_RE = /^[A-Za-z][A-Za-z0-9+.-]*:/;
21236
+ }
21237
+ });
21238
+
21174
21239
  // src/dev-bundle-resolution.ts
21175
21240
  function buildUnresolvedTargetError(targetLabel) {
21176
21241
  return new Error(
@@ -21839,7 +21904,107 @@ var init_app_splash = __esm({
21839
21904
  }
21840
21905
  });
21841
21906
 
21907
+ // src/bridge-contract.ts
21908
+ var init_bridge_contract = __esm({
21909
+ "src/bridge-contract.ts"() {
21910
+ "use strict";
21911
+ }
21912
+ });
21913
+
21842
21914
  // cli/commands/inspect/core.ts
21915
+ function isSemanticGeometry(value) {
21916
+ return value !== null && typeof value === "object" && [
21917
+ Reflect.get(value, "x"),
21918
+ Reflect.get(value, "y"),
21919
+ Reflect.get(value, "width"),
21920
+ Reflect.get(value, "height")
21921
+ ].every(
21922
+ (coordinate) => typeof coordinate === "number" ? Number.isFinite(coordinate) : false
21923
+ );
21924
+ }
21925
+ function isSimSemanticTree(value) {
21926
+ if (!Array.isArray(value)) return false;
21927
+ return value.every((node) => {
21928
+ if (node === null || typeof node !== "object" || !Number.isInteger(Reflect.get(node, "nodeId")) || typeof Reflect.get(node, "type") !== "string" || !isSemanticGeometry(Reflect.get(node, "geometry"))) {
21929
+ return false;
21930
+ }
21931
+ for (const key of ["testID", "text", "role", "label"]) {
21932
+ const value2 = Reflect.get(node, key);
21933
+ if (value2 !== void 0 && typeof value2 !== "string") return false;
21934
+ }
21935
+ const pressable = Reflect.get(node, "pressable");
21936
+ if (pressable !== void 0 && pressable !== true) return false;
21937
+ const children = Reflect.get(node, "children");
21938
+ return children === void 0 || isSimSemanticTree(children);
21939
+ });
21940
+ }
21941
+ function isSemanticQueryResult(value) {
21942
+ const total = value !== null && typeof value === "object" ? Reflect.get(value, "total") : void 0;
21943
+ return value !== null && typeof value === "object" && typeof total === "number" && Number.isInteger(total) && total >= 0 && isSimSemanticTree(Reflect.get(value, "nodes"));
21944
+ }
21945
+ function globRegex(glob) {
21946
+ const escaped = glob.replace(/[.+^$(){}|[\]\\]/g, "\\$&");
21947
+ return new RegExp(`^${escaped.replace(/\*/g, ".*").replace(/\?/g, ".")}$`, "i");
21948
+ }
21949
+ function formatSemanticTree(nodes, opts = {}) {
21950
+ const filter = opts.filter?.toLowerCase() ?? "";
21951
+ const testIDRegex = opts.testIdLike ? globRegex(opts.testIdLike) : null;
21952
+ const onlyRegex = opts.onlyGlob ? globRegex(opts.onlyGlob) : null;
21953
+ const lines = [];
21954
+ const visit = (node, indent) => {
21955
+ const joined = [node.role, node.label, node.text, node.testID].filter((value) => typeof value === "string").join(" ");
21956
+ const matches = (!filter || joined.toLowerCase().includes(filter)) && (!testIDRegex || testIDRegex.test(node.testID ?? "")) && (!onlyRegex || onlyRegex.test(joined));
21957
+ if (matches) {
21958
+ const parts = [
21959
+ node.role ? `[${node.role}]` : node.pressable ? "[tap]" : `<${node.type}>`
21960
+ ];
21961
+ if (node.text) {
21962
+ const text = opts.fullText || node.text.length <= 50 ? node.text : `${node.text.slice(0, 49).trimEnd()}\u2026`;
21963
+ parts.push(JSON.stringify(text));
21964
+ }
21965
+ if (node.testID) parts.push(`#${node.testID}`);
21966
+ if (node.label && node.label !== node.text) {
21967
+ parts.push(`label=${JSON.stringify(node.label)}`);
21968
+ }
21969
+ if (!opts.hideXy) {
21970
+ const geometry = node.geometry;
21971
+ parts.push(
21972
+ `@(${Math.round(geometry.x)},${Math.round(geometry.y)}) ${Math.round(geometry.width)}x${Math.round(geometry.height)}`
21973
+ );
21974
+ }
21975
+ if (node.pressable) parts.push("(tap)");
21976
+ lines.push(`${" ".repeat(indent + 1)}${parts.join(" ")}`);
21977
+ }
21978
+ for (const child of node.children ?? []) visit(child, indent + 1);
21979
+ };
21980
+ let roots = nodes;
21981
+ if (opts.subtreeRoot) {
21982
+ const pending = [...nodes];
21983
+ let found;
21984
+ while (pending.length > 0 && !found) {
21985
+ const candidate = pending.shift();
21986
+ if (!candidate) continue;
21987
+ if (candidate.testID === opts.subtreeRoot) found = candidate;
21988
+ else pending.push(...candidate.children ?? []);
21989
+ }
21990
+ if (!found) return `__SUBTREE_NOT_FOUND__:${opts.subtreeRoot}`;
21991
+ roots = [found];
21992
+ }
21993
+ for (const root of roots) visit(root, 0);
21994
+ return lines.join("\n");
21995
+ }
21996
+ async function inspectDescribe(bridge, dumpOpts) {
21997
+ const dumpDepth = dumpOpts.maxDepth ?? 12;
21998
+ const result = await bridge.send({
21999
+ type: "query",
22000
+ query: { kind: "tree", depth: dumpDepth }
22001
+ });
22002
+ if (!isSemanticQueryResult(result)) return {};
22003
+ return {
22004
+ tree: formatSemanticTree(result.nodes, dumpOpts),
22005
+ nodeCount: result.total
22006
+ };
22007
+ }
21843
22008
  function scoreLayoutTextContrast(input) {
21844
22009
  const required = input.fontSize >= 18 || input.fontSize >= 14 && (input.fontWeight ?? 400) >= 700 ? 3 : 4.5;
21845
22010
  const { normal, background } = input;
@@ -22053,9 +22218,6 @@ function layoutGetNativeEval(opts = {}) {
22053
22218
  type: typeof n.type === 'string' ? n.type : 'node',
22054
22219
  text,
22055
22220
  box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
22056
- borderRadius: 0,
22057
- padding: { t: 0, r: 0, b: 0, l: 0 },
22058
- fontSize: 0,
22059
22221
  }
22060
22222
  if (!includeStyle) return base
22061
22223
  // computedStyle has the resolved values; style is the curated subset \u2014
@@ -22124,11 +22286,56 @@ function layoutGetNativeEval(opts = {}) {
22124
22286
  return elements
22125
22287
  })()`;
22126
22288
  }
22127
- function readyProbeHasContent(probe3) {
22128
- return probe3.liveFrameActive || probe3.targets > 0 || probe3.nodes >= READY_CONTENT_NODE_FLOOR;
22289
+ function waitReadyReason(status) {
22290
+ if (status.bridgeError) {
22291
+ return `sim unreachable: ${status.bridgeError} \u2014 the target tab is closed or no sim is connected (check \`rnx list\`, reopen with \`rnx open\`)`;
22292
+ }
22293
+ const base = status.externalError ? `guest app errored: ${status.externalError}` : status.loadingText ? `still showing "${status.loadingText}"` : status.externalReady === false ? "guest app is still loading" : status.flag !== true && status.targets > 0 ? "native content is rendered but the ready signal has not settled" : status.flag !== true ? "guest app has not emitted sootsim:externalAppReady" : status.targets <= 0 ? "ready flag emitted but no visible app content is inspectable yet" : "node tree is still changing";
22294
+ return !status.externalError && status.suppressedEntryError ? `${base} (suppressed entry error: ${status.suppressedEntryError})` : base;
22129
22295
  }
22130
- function readyProbeHasTargetContent(probe3) {
22131
- return probe3.liveFrameActive || probe3.targets > 0;
22296
+ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
22297
+ const start = Date.now();
22298
+ let result;
22299
+ try {
22300
+ result = await bridge.send(
22301
+ {
22302
+ type: "waitFor",
22303
+ waitForOptions: { condition: { type: "ready" }, timeoutMs }
22304
+ },
22305
+ { timeoutMs: timeoutMs + 1e3 }
22306
+ );
22307
+ } catch (error) {
22308
+ const bridgeError = error instanceof Error ? error.message : String(error);
22309
+ if (bridgeError.startsWith("multiple sims are connected:") || bridgeError.startsWith("saved sim ")) {
22310
+ throw error;
22311
+ }
22312
+ result = {
22313
+ matched: false,
22314
+ elapsedMs: Date.now() - start,
22315
+ polls: 0,
22316
+ error: bridgeError
22317
+ };
22318
+ }
22319
+ const probe3 = result.probe;
22320
+ const status = {
22321
+ ready: result.matched,
22322
+ elapsedMs: result.elapsedMs,
22323
+ nodes: probe3?.nodes ?? 0,
22324
+ targets: probe3?.targets ?? 0,
22325
+ liveFrameActive: probe3?.liveFrameActive ?? false,
22326
+ liveFrameChannels: probe3?.liveFrameChannels ?? 0,
22327
+ liveFramePublishes: probe3?.liveFramePublishes ?? 0,
22328
+ flag: probe3?.flag,
22329
+ loadingText: probe3?.loadingText ?? "",
22330
+ externalReady: probe3?.externalReady ?? null,
22331
+ externalStatus: probe3?.externalStatus ?? "",
22332
+ externalError: probe3?.externalError ?? "",
22333
+ suppressedEntryError: probe3?.suppressedEntryError ?? "",
22334
+ errors: probe3?.errors ?? 0,
22335
+ bridgeError: result.error ?? ""
22336
+ };
22337
+ if (!status.ready && options.onProgress) options.onProgress(status);
22338
+ return status;
22132
22339
  }
22133
22340
  function consoleEntriesEval(level, limit) {
22134
22341
  const getter = level === "error" ? "getErrors" : "getWarnings";
@@ -22182,156 +22389,12 @@ async function getShellState(bridge, readyTimeoutMs = 0) {
22182
22389
  }
22183
22390
  }
22184
22391
  }
22185
- var LAYOUT_GET_NATIVE_EVAL, WAIT_READY_PROBE, READY_CONTENT_NODE_FLOOR, READY_NODE_STABLE_MS, READY_PROBE_POLL_MS, MERGED_CONSOLE_COUNT_EVAL;
22392
+ var LAYOUT_GET_NATIVE_EVAL, MERGED_CONSOLE_COUNT_EVAL;
22186
22393
  var init_core = __esm({
22187
22394
  "cli/commands/inspect/core.ts"() {
22188
22395
  "use strict";
22396
+ init_bridge_contract();
22189
22397
  LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval();
22190
- WAIT_READY_PROBE = `(async () => {
22191
- const t = window.__sootsimTest
22192
- let nodes = 0
22193
- try { nodes = (await t?.getNodeCount?.()) || 0 } catch {}
22194
- let targets = 0
22195
- let liveFrameActive = false
22196
- let liveFrameChannels = 0
22197
- let liveFramePublishes = 0
22198
- let loadingText = ''
22199
- const cleanText = (value) =>
22200
- typeof value === 'string' ? value.replace(/\\s+/g, ' ').trim() : ''
22201
- const loadingSignal = (value) => {
22202
- const text = cleanText(value)
22203
- if (!text) return ''
22204
- return /^(opening app\\b|looking for your app\\b|building bundle\\b|preparing rnx runtime\\b|loading native bundle\\b|loading rnx stubs\\b|loading bundle loader\\b|loading native stubs\\b|loading compat bridge\\b|loading react-native shim\\b|loading react bridge\\b|loading reconciler\\b|loading replacement modules\\b|injecting rnx modules\\b|warming sqlite engine\\b|capturing root component\\b|reloading app\\b|loading(?:[.!\u2026]*)?$|connecting(?:[.!\u2026]*)?$)/i.test(text)
22205
- ? text
22206
- : ''
22207
- }
22208
- try {
22209
- const loading = window.__sootsimConnectRNLoading
22210
- if (loading && loading.active === true) {
22211
- loadingText = [loading.title, loading.detail].map(cleanText).filter(Boolean).join(' ')
22212
- }
22213
- } catch {}
22214
- try {
22215
- if (typeof t?.listInspectable === 'function') {
22216
- const list = await t.listInspectable({})
22217
- if (Array.isArray(list)) {
22218
- const screenW = window.innerWidth || 0
22219
- const screenH = window.innerHeight || 0
22220
- const isVisible = (n) => {
22221
- const layout = n?.layout
22222
- if (!layout || layout.width <= 0 || layout.height <= 0) return false
22223
- const abs = n?.absolutePosition || n?.absolute
22224
- if (!abs) return true
22225
- const device = n?.device || {}
22226
- const w = Number(device.width) || screenW
22227
- const h = Number(device.height) || screenH
22228
- return (
22229
- abs.x + layout.width > 0 &&
22230
- abs.y + layout.height > 0 &&
22231
- abs.x < w &&
22232
- abs.y < h
22233
- )
22234
- }
22235
- const hasContentSignal = (n) => {
22236
- const role = n?.accessibilityRole || n?.role
22237
- const label = cleanText(n?.accessibilityLabel || n?.label)
22238
- const handlers = Array.isArray(n?.handlers) ? n.handlers : []
22239
- const text = cleanText(n?.text)
22240
- const testID = cleanText(n?.testID || n?.testId)
22241
- const loading = loadingSignal(text) || loadingSignal(label) || loadingSignal(testID)
22242
- if (!loadingText && loading) {
22243
- loadingText = loading
22244
- }
22245
- if (handlers.length > 0) return true
22246
- if (role) return true
22247
- if (n?.isTextInput) return true
22248
- if (text) return true
22249
- if (label) return true
22250
- if (testID && !/^splash|loading/i.test(testID)) return true
22251
- return false
22252
- }
22253
- targets = list.filter(n => isVisible(n) && hasContentSignal(n)).length
22254
- }
22255
- }
22256
- } catch {}
22257
- try {
22258
- const stats = await t?.getImageLoaderStats?.()
22259
- const presentation = stats?.liveFramePresentation
22260
- liveFrameChannels = Number(presentation?.activeChannels) || 0
22261
- liveFramePublishes = Number(presentation?.publishes) || 0
22262
- const previous = window.__sootsimWaitReadyLiveFramePublishes
22263
- liveFrameActive =
22264
- liveFrameChannels > 0 &&
22265
- typeof previous === 'number' &&
22266
- liveFramePublishes > previous
22267
- window.__sootsimWaitReadyLiveFramePublishes = liveFramePublishes
22268
- } catch {}
22269
- let externalReady = null
22270
- let externalStatus = ''
22271
- let externalError = ''
22272
- let suppressedEntryError = ''
22273
- try {
22274
- const getExternalAppState = t?.getExternalAppState
22275
- const external = typeof getExternalAppState === 'function'
22276
- ? await Promise.race([
22277
- getExternalAppState(),
22278
- new Promise((resolve) => setTimeout(
22279
- () => resolve({
22280
- state: {
22281
- ready: false,
22282
- loading: true,
22283
- status: 'waiting for tenant worker...',
22284
- },
22285
- }),
22286
- 1000,
22287
- )),
22288
- ])
22289
- : null
22290
- const state = external?.state
22291
- if (state && typeof state === 'object') {
22292
- externalReady = state.ready === true
22293
- if (typeof state.status === 'string') externalStatus = state.status
22294
- if (typeof state.error === 'string') externalError = state.error
22295
- }
22296
- if (!externalError && Array.isArray(external?.entryErrors)) {
22297
- // suppressed-and-continued entry errors (bundle-loader's patched __r
22298
- // swallows entry throws and moves on) must NOT gate readiness \u2014 the
22299
- // app frequently runs fine after one (e.g. metro's "Unknown named
22300
- // module" probing noise). they surface separately as a diagnostic
22301
- // for the timeout path.
22302
- const isReal = (item) => item && typeof item.message === 'string' && item.message.trim()
22303
- const fatal = external.entryErrors.find((item) => isReal(item) && item.suppressed !== true)
22304
- if (fatal) externalError = fatal.message
22305
- const suppressed = external.entryErrors.find((item) => isReal(item) && item.suppressed === true)
22306
- if (suppressed) suppressedEntryError = suppressed.message
22307
- }
22308
- // once the app has errored its last status ("injecting sootsim
22309
- // modules...") is stale \u2014 reporting it makes a dead app read as one that
22310
- // is still booting.
22311
- if (externalError) loadingText = ''
22312
- else if (externalReady === false && externalStatus) loadingText = externalStatus
22313
- } catch {}
22314
- let errors = 0
22315
- try { errors = window.__sootsimConsole?.count?.()?.errors ?? 0 } catch {}
22316
- return {
22317
- flag: (window).__sootsimExternalAppReady,
22318
- at: (window).__sootsimExternalAppReadyAt || 0,
22319
- nodes,
22320
- targets,
22321
- liveFrameActive,
22322
- liveFrameChannels,
22323
- liveFramePublishes,
22324
- errors,
22325
- loadingText,
22326
- externalReady,
22327
- externalStatus,
22328
- externalError,
22329
- suppressedEntryError,
22330
- }
22331
- })()`;
22332
- READY_CONTENT_NODE_FLOOR = 100;
22333
- READY_NODE_STABLE_MS = 750;
22334
- READY_PROBE_POLL_MS = 150;
22335
22398
  MERGED_CONSOLE_COUNT_EVAL = `(() => {
22336
22399
  const norm = (s) => (typeof s === 'string' ? s : (() => { try { return JSON.stringify(s) } catch { return String(s) } })())
22337
22400
  const key = (lvl, ts, args) => lvl + '|' + Math.round((ts || 0) / 250) + '|' + (Array.isArray(args) ? args.map(norm).join(' ') : norm(args))
@@ -24002,7 +24065,6 @@ __export(control_exports, {
24002
24065
  buildShellUrl: () => buildShellUrl,
24003
24066
  closeSimsBulk: () => closeSimsBulk,
24004
24067
  ensureBridgeForOpen: () => ensureBridgeForOpen,
24005
- normalizeDeepLinkTarget: () => normalizeDeepLinkTarget,
24006
24068
  planBulkCloseTargets: () => planBulkCloseTargets,
24007
24069
  playwrightHostPidForSim: () => playwrightHostPidForSim,
24008
24070
  printConnectedSims: () => printConnectedSims,
@@ -24063,26 +24125,6 @@ function resolveDriverHeadless(args, driverId) {
24063
24125
  function normalizeKnownTarget(target) {
24064
24126
  return target.trim();
24065
24127
  }
24066
- function normalizeDeepLinkTarget(target) {
24067
- const trimmed = target.trim();
24068
- if (!trimmed) {
24069
- throw new Error("expected <url-or-route>");
24070
- }
24071
- if (DEEP_LINK_SCHEME_RE.test(trimmed) || trimmed.startsWith("/")) {
24072
- return trimmed;
24073
- }
24074
- return `/${trimmed}`;
24075
- }
24076
- function isObjectRecord(value) {
24077
- return value !== null && typeof value === "object";
24078
- }
24079
- function shouldOpenAsDeepLink(target) {
24080
- const trimmed = target.trim();
24081
- if (!trimmed) return false;
24082
- if (trimmed.startsWith("/")) return true;
24083
- if (!DEEP_LINK_SCHEME_RE.test(trimmed)) return false;
24084
- return !/^https?:\/\//i.test(trimmed);
24085
- }
24086
24128
  async function ensureBridgeForOpen(preferredPort, explicitPort = false) {
24087
24129
  const lock = readDaemonLockfile();
24088
24130
  assertExplicitBridgeLock(preferredPort, explicitPort, lock);
@@ -24843,15 +24885,11 @@ async function maybeDescribeAfterOpen(wsPort, simId, args) {
24843
24885
  try {
24844
24886
  let readyProbe = null;
24845
24887
  try {
24846
- readyProbe = await bridge.send({
24847
- type: "evaluate",
24848
- code: WAIT_READY_PROBE
24849
- });
24888
+ readyProbe = await inspectWaitReady(bridge, 1);
24850
24889
  } catch {
24851
24890
  }
24852
24891
  if (readyProbe) {
24853
- const readyEnough = readyProbe.flag === true && !readyProbe.loadingText && readyProbeHasContent(readyProbe);
24854
- if (!readyEnough) {
24892
+ if (!readyProbe.ready) {
24855
24893
  const reason = readyProbe.loadingText ? `still showing "${readyProbe.loadingText}"` : readyProbe.flag !== true ? "guest ready event has not fired" : readyProbe.targets <= 0 ? "no visible app content is inspectable yet" : "app tree is still stabilizing";
24856
24894
  console.log(
24857
24895
  ` app still loading: ${reason} (nodes: ${readyProbe.nodes}, targets: ${readyProbe.targets})`
@@ -24859,26 +24897,16 @@ async function maybeDescribeAfterOpen(wsPort, simId, args) {
24859
24897
  console.log(" before interacting, run: rnx wait ready --max-ms 120000");
24860
24898
  }
24861
24899
  }
24862
- const dumpOpts = { describe: true, verbose: false, filter: "" };
24863
- const code = `(async () => {
24864
- const t = window.__sootsimTest
24865
- const ms = window.SootSim?.bridges?.mainShell
24866
- if (!t) return null
24867
- let shell = null
24868
- try { shell = ms?.getState ? await ms.getState() : null } catch {}
24869
- const tree = await t.dumpTree(12, ${JSON.stringify(dumpOpts)})
24870
- return { tree, shell }
24871
- })()`;
24872
- const result = await bridge.send({ type: "evaluate", code });
24900
+ const result = await inspectDescribe(bridge, {
24901
+ describe: true,
24902
+ verbose: false,
24903
+ filter: "",
24904
+ compact: true,
24905
+ hideXy: false,
24906
+ maxDepth: 12
24907
+ });
24873
24908
  if (!result?.tree) return;
24874
24909
  console.log("");
24875
- if (result.shell?.state) {
24876
- const parts = [
24877
- `state=${result.shell.state}`,
24878
- result.shell.activeApp ? `app=${result.shell.activeApp}` : null
24879
- ].filter(Boolean);
24880
- console.log(` shell: ${parts.join(" ")}`);
24881
- }
24882
24910
  console.log(result.tree);
24883
24911
  } finally {
24884
24912
  bridge.close();
@@ -24932,26 +24960,10 @@ async function runLinkCommand(args, opts = {}) {
24932
24960
  console.error(" examples: rnx open /settings | rnx open myapp://settings");
24933
24961
  process.exit(1);
24934
24962
  }
24935
- let target;
24936
- try {
24937
- target = normalizeDeepLinkTarget(parsed.positional[0] ?? "");
24938
- } catch (err) {
24939
- console.error(` rnx open: ${err instanceof Error ? err.message : String(err)}`);
24940
- process.exit(1);
24941
- }
24942
24963
  const bridge = createBridgeFromParsed(parsed);
24943
24964
  const simHint = parsed.simId ? ` --sim ${parsed.simId}` : "";
24944
24965
  try {
24945
- const result = await bridge.send({
24946
- type: "call",
24947
- path: "SootSim.bridges.test.openDeepLink",
24948
- args: [target]
24949
- });
24950
- if (isObjectRecord(result) && result.ok === false) {
24951
- throw new Error(
24952
- typeof result.error === "string" ? result.error : "deep-link dispatch failed"
24953
- );
24954
- }
24966
+ const target = await openAppUrl(bridge, parsed.positional[0] ?? "");
24955
24967
  console.log(` opened deep link: ${target}`);
24956
24968
  } catch (err) {
24957
24969
  console.error(` link failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -25014,7 +25026,7 @@ async function runOpenCommand(args, opts = {}) {
25014
25026
  process.exit(1);
25015
25027
  }
25016
25028
  const target = parsed.positional[0] || "";
25017
- if (shouldOpenAsDeepLink(target)) {
25029
+ if (isAppDeepLinkTarget(target)) {
25018
25030
  if (openInNewSim) {
25019
25031
  console.error(
25020
25032
  " rnx open: deep links target the current app; remove --new, --profile, or --ephemeral"
@@ -25551,7 +25563,7 @@ async function runCloseCommand(args, opts = {}) {
25551
25563
  bridge.close();
25552
25564
  }
25553
25565
  }
25554
- var import_node_child_process6, import_node_fs13, import_node_os3, import_node_path16, import_settings, import_ws5, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS, DEEP_LINK_SCHEME_RE;
25566
+ var import_node_child_process6, import_node_fs13, import_node_os3, import_node_path16, import_settings, import_ws5, DEFAULT_DRIVER_CONNECT_TIMEOUT_MS, DRIVER_CONNECT_INTERVAL_MS, DRIVER_CONNECT_HOST_GRACE_MS, OPEN_BRIDGE_SPAWN_TIMEOUT_MS, SHELL_APPS;
25555
25567
  var init_control = __esm({
25556
25568
  "cli/commands/control.ts"() {
25557
25569
  "use strict";
@@ -25562,6 +25574,7 @@ var init_control = __esm({
25562
25574
  import_settings = require("sootsim-engine/settings");
25563
25575
  import_ws5 = require("ws");
25564
25576
  init_dev_server_scanner();
25577
+ init_app_url();
25565
25578
  init_backend_origin();
25566
25579
  init_bridge_constants();
25567
25580
  init_config();
@@ -25598,7 +25611,6 @@ var init_control = __esm({
25598
25611
  photos: "/app/photos",
25599
25612
  camera: "/app/camera"
25600
25613
  };
25601
- DEEP_LINK_SCHEME_RE = /^[A-Za-z][A-Za-z0-9+.-]*:/;
25602
25614
  }
25603
25615
  });
25604
25616
 
@@ -26702,16 +26714,16 @@ __export(bridge_flow_runner_exports, {
26702
26714
  function sleep3(ms) {
26703
26715
  return new Promise((resolve7) => setTimeout(resolve7, ms));
26704
26716
  }
26705
- function isObjectRecord2(value) {
26717
+ function isObjectRecord(value) {
26706
26718
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
26707
26719
  }
26708
26720
  function readStringKey(value, key) {
26709
- if (!isObjectRecord2(value)) return null;
26721
+ if (!isObjectRecord(value)) return null;
26710
26722
  const raw = value[key];
26711
26723
  return typeof raw === "string" && raw.length > 0 ? raw : null;
26712
26724
  }
26713
26725
  function readBooleanKey(value, key) {
26714
- if (!isObjectRecord2(value)) return false;
26726
+ if (!isObjectRecord(value)) return false;
26715
26727
  return value[key] === true;
26716
26728
  }
26717
26729
  function readRecentShellAppId(state) {
@@ -26846,7 +26858,6 @@ var init_bridge_flow_runner = __esm({
26846
26858
  lastRecordingDurationMs = null;
26847
26859
  lastRecordingFrameStats = null;
26848
26860
  flowTraceSteps = [];
26849
- simRouteHint = null;
26850
26861
  // set by runStep when a step throws — lets the outer flow command
26851
26862
  // dump a rich per-step failure bundle (screenshot + describe.json +
26852
26863
  // console + network) keyed by the failing step.
@@ -26854,50 +26865,6 @@ var init_bridge_flow_runner = __esm({
26854
26865
  get simId() {
26855
26866
  return this.opts.simId;
26856
26867
  }
26857
- setSimRouteHint(urlLike) {
26858
- this.simRouteHint = this.normalizeSimRoute(urlLike);
26859
- }
26860
- normalizeSimRoute(urlLike) {
26861
- if (!urlLike) return null;
26862
- try {
26863
- const url = new URL(urlLike);
26864
- url.searchParams.delete("inspectOpen");
26865
- const search = url.searchParams.toString();
26866
- return `${url.origin}${url.pathname}${search ? `?${search}` : ""}`;
26867
- } catch {
26868
- return urlLike;
26869
- }
26870
- }
26871
- // sim ids rotate on `window.location.reload()` when the shell reconnects.
26872
- // after launchApp we re-resolve the primary sim so the rest of the flow
26873
- // doesn't send commands to a dead id.
26874
- async refreshSimId() {
26875
- if (!this.opts.simId) return;
26876
- try {
26877
- const sims = await this.bridge.listSims();
26878
- const current = sims.find((b) => b.id === this.opts.simId);
26879
- if (current) {
26880
- this.simRouteHint = this.normalizeSimRoute(current.url || current.origin);
26881
- }
26882
- const stillThere = sims.find(
26883
- (b) => b.id === this.opts.simId && b.readyState === "open"
26884
- );
26885
- if (stillThere) return;
26886
- if (!this.simRouteHint) return;
26887
- const hinted = this.simRouteHint && sims.find(
26888
- (b) => b.readyState === "open" && this.normalizeSimRoute(b.url || b.origin) === this.simRouteHint
26889
- );
26890
- if (!hinted) return;
26891
- const primary = sims.find((b) => b.isPrimary && b.readyState === "open");
26892
- const firstOpen = sims.find((b) => b.readyState === "open");
26893
- const next = hinted ?? primary ?? firstOpen;
26894
- if (next) {
26895
- this.opts.simId = next.id;
26896
- console.log(` [flow] sim rotated to ${next.id}`);
26897
- }
26898
- } catch {
26899
- }
26900
- }
26901
26868
  async evaluate(code, timeoutMs) {
26902
26869
  return this.bridge.send(
26903
26870
  {
@@ -26926,63 +26893,17 @@ var init_bridge_flow_runner = __esm({
26926
26893
  });
26927
26894
  }
26928
26895
  async waitForTree(timeout = DEFAULT_TIMEOUT) {
26929
- const deadline = Date.now() + scaledTimeout(timeout);
26930
- const startedAt = Date.now();
26931
- let lastError = null;
26932
- let consecutiveBridgeFailures = 0;
26933
- let lastProgressAt = 0;
26934
- let lastNodes = -1;
26935
- let nodeStableSince = startedAt;
26936
- while (Date.now() < deadline) {
26937
- try {
26938
- const probe3 = await this.evaluate(
26939
- WAIT_READY_PROBE,
26940
- // the readiness probe is cheap — cap it well under the bridge-wide
26941
- // command timeout so a non-responsive sim is detected in seconds,
26942
- // not after a full 15s-per-poll stall.
26943
- 8e3
26944
- );
26945
- consecutiveBridgeFailures = 0;
26946
- if (probe3?.externalError) {
26947
- throw new Error(`app failed to load: ${probe3.externalError}`);
26948
- }
26949
- if (probe3) {
26950
- const now = Date.now();
26951
- if (probe3.nodes !== lastNodes) {
26952
- lastNodes = probe3.nodes;
26953
- nodeStableSince = now;
26954
- }
26955
- const hasReadySignal = probe3.flag === true && readyProbeHasContent(probe3);
26956
- const hasRenderedContentWithoutReadySignal = probe3.flag !== true && readyProbeHasTargetContent(probe3);
26957
- const ready = (hasReadySignal || hasRenderedContentWithoutReadySignal) && probe3.externalReady !== false && !probe3.externalError && !probe3.loadingText && now - nodeStableSince >= READY_NODE_STABLE_MS;
26958
- if (ready) return;
26959
- }
26960
- } catch (err) {
26961
- lastError = err instanceof Error ? err : new Error(String(err));
26962
- if (lastError.message.startsWith("app failed to load:")) {
26963
- throw lastError;
26964
- }
26965
- consecutiveBridgeFailures++;
26966
- if (consecutiveBridgeFailures >= 4) {
26967
- throw new Error(
26968
- `target sim is not responding to the bridge (${lastError.message}). it is likely a stale orphan \u2014 run \`rnx list\`, then close it with \`rnx close <id>\` or start a fresh one with \`rnx open --new <port>\`.`
26969
- );
26970
- }
26971
- await this.refreshSimId();
26972
- }
26973
- const elapsed = Date.now() - startedAt;
26974
- if (elapsed - lastProgressAt >= 5e3) {
26975
- lastProgressAt = elapsed;
26976
- console.log(
26977
- ` [flow] waiting for app to be ready\u2026 (${Math.round(elapsed / 1e3)}s)`
26978
- );
26979
- }
26980
- await sleep3(READY_PROBE_POLL_MS);
26896
+ const status = await inspectWaitReady(this.bridge, scaledTimeout(timeout));
26897
+ if (status.ready) return;
26898
+ if (status.externalError) {
26899
+ throw new Error(`app failed to load: ${status.externalError}`);
26981
26900
  }
26982
- if (lastError?.message.startsWith("app failed to load:")) {
26983
- throw lastError;
26901
+ if (status.bridgeError) {
26902
+ throw new Error(
26903
+ `target sim is not responding to the bridge (${status.bridgeError}). it is likely a stale orphan; run \`rnx list\`, then close it with \`rnx close <id>\` or start a fresh one with \`rnx open --new <port>\`.`
26904
+ );
26984
26905
  }
26985
- throw new Error(`app not ready after ${timeout}ms`);
26906
+ throw new Error(`app not ready after ${timeout}ms: ${waitReadyReason(status)}`);
26986
26907
  }
26987
26908
  async ensureRecordingStarted() {
26988
26909
  if (!this.opts.recordingOutputDir) {
@@ -27960,8 +27881,6 @@ var init_bridge_flow_runner = __esm({
27960
27881
  async reloadGuestApp() {
27961
27882
  await this.evaluate("window.location.reload()").catch(() => {
27962
27883
  });
27963
- await sleep3(500);
27964
- await this.refreshSimId();
27965
27884
  }
27966
27885
  async resetGuestAppData(launchOptions) {
27967
27886
  const reset = await this.bridge.send(
@@ -27998,7 +27917,6 @@ var init_bridge_flow_runner = __esm({
27998
27917
  const deadline = Date.now() + 1e4;
27999
27918
  let lastError = null;
28000
27919
  while (Date.now() < deadline) {
28001
- await this.refreshSimId();
28002
27920
  try {
28003
27921
  if (this.recordingEnabled) await this.ensureRecordingStarted();
28004
27922
  if (this.profilingEnabled) await this.startProfile();
@@ -28013,11 +27931,10 @@ var init_bridge_flow_runner = __esm({
28013
27931
  );
28014
27932
  }
28015
27933
  async launchApp(opts) {
28016
- await this.refreshSimId();
28017
27934
  let hasLaunchArguments = false;
28018
27935
  let launchArguments;
28019
- if (isObjectRecord2(opts) && Object.prototype.hasOwnProperty.call(opts, "arguments")) {
28020
- if (!isObjectRecord2(opts.arguments)) {
27936
+ if (isObjectRecord(opts) && Object.prototype.hasOwnProperty.call(opts, "arguments")) {
27937
+ if (!isObjectRecord(opts.arguments)) {
28021
27938
  throw new Error("launchApp.arguments must be a JSON object");
28022
27939
  }
28023
27940
  hasLaunchArguments = true;
@@ -28069,7 +27986,7 @@ var init_bridge_flow_runner = __esm({
28069
27986
  await this.waitForTree(12e4);
28070
27987
  if (this.opts.onAfterLaunch) {
28071
27988
  try {
28072
- await this.opts.onAfterLaunch();
27989
+ await this.opts.onAfterLaunch(this.opts.simId);
28073
27990
  } catch (err) {
28074
27991
  console.warn(
28075
27992
  ` warn: onAfterLaunch hook threw: ${err instanceof Error ? err.message : err}`
@@ -28102,7 +28019,7 @@ var init_bridge_flow_runner = __esm({
28102
28019
  }
28103
28020
  resolveShellLaunchAppId(opts, state) {
28104
28021
  if (typeof opts === "string" && opts.length > 0) return opts;
28105
- if (isObjectRecord2(opts)) {
28022
+ if (isObjectRecord(opts)) {
28106
28023
  const explicit = readStringKey(opts, "appId") || readStringKey(opts, "id");
28107
28024
  if (explicit) return explicit;
28108
28025
  }