rnxsim 0.1.313 → 0.1.315

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 (297) hide show
  1. package/LICENSE +11 -10
  2. package/README.md +5 -0
  3. package/cli/app-config.ts +65 -0
  4. package/cli/app-fonts.ts +408 -0
  5. package/cli/app-project.ts +231 -0
  6. package/cli/app-splash.ts +185 -0
  7. package/cli/app-state-reset.ts +24 -0
  8. package/cli/auth.ts +155 -0
  9. package/cli/bin.ts +594 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2830 -0
  12. package/cli/browser-evals.ts +96 -0
  13. package/cli/commands/agent-wrapper.ts +986 -0
  14. package/cli/commands/agent.ts +423 -0
  15. package/cli/commands/app-fonts.ts +98 -0
  16. package/cli/commands/assert.ts +541 -0
  17. package/cli/commands/auth.ts +59 -0
  18. package/cli/commands/camera.ts +266 -0
  19. package/cli/commands/cleanup.ts +169 -0
  20. package/cli/commands/compat.ts +87 -0
  21. package/cli/commands/config.ts +32 -0
  22. package/cli/commands/control.ts +2142 -0
  23. package/cli/commands/cpu-profile.ts +269 -0
  24. package/cli/commands/daemon-mac-app.ts +169 -0
  25. package/cli/commands/daemon.ts +874 -0
  26. package/cli/commands/debug.ts +719 -0
  27. package/cli/commands/desktop.ts +39 -0
  28. package/cli/commands/detect.ts +197 -0
  29. package/cli/commands/detox.ts +385 -0
  30. package/cli/commands/device.ts +133 -0
  31. package/cli/commands/diagnose.ts +589 -0
  32. package/cli/commands/electron.ts +95 -0
  33. package/cli/commands/film.ts +379 -0
  34. package/cli/commands/flow.ts +1124 -0
  35. package/cli/commands/inspect/actions.ts +622 -0
  36. package/cli/commands/inspect/core.ts +2405 -0
  37. package/cli/commands/inspect/count.ts +17 -0
  38. package/cli/commands/inspect/describe.ts +192 -0
  39. package/cli/commands/inspect/env.ts +23 -0
  40. package/cli/commands/inspect/find.ts +171 -0
  41. package/cli/commands/inspect/get-layout.ts +39 -0
  42. package/cli/commands/inspect/keyboard.ts +52 -0
  43. package/cli/commands/inspect/list.ts +58 -0
  44. package/cli/commands/inspect/memory.ts +215 -0
  45. package/cli/commands/inspect/redaction.ts +39 -0
  46. package/cli/commands/inspect/resolve-target.ts +82 -0
  47. package/cli/commands/inspect/screens.ts +78 -0
  48. package/cli/commands/inspect/settle.ts +22 -0
  49. package/cli/commands/inspect/settling.ts +158 -0
  50. package/cli/commands/inspect/shared.ts +353 -0
  51. package/cli/commands/inspect/sleep.ts +14 -0
  52. package/cli/commands/inspect/tree.ts +32 -0
  53. package/cli/commands/inspect/url.ts +17 -0
  54. package/cli/commands/inspect/wait-event.ts +210 -0
  55. package/cli/commands/inspect/wait-idle.ts +24 -0
  56. package/cli/commands/inspect/wait-ready.ts +74 -0
  57. package/cli/commands/inspect/wait-selector.ts +54 -0
  58. package/cli/commands/inspect/wait.ts +31 -0
  59. package/cli/commands/inspect.ts +4519 -0
  60. package/cli/commands/install-desktop.ts +351 -0
  61. package/cli/commands/login.ts +331 -0
  62. package/cli/commands/logout.ts +31 -0
  63. package/cli/commands/maestro-generate.ts +361 -0
  64. package/cli/commands/maestro.ts +453 -0
  65. package/cli/commands/mode.ts +57 -0
  66. package/cli/commands/no-bridge-hint.ts +80 -0
  67. package/cli/commands/perf.ts +66 -0
  68. package/cli/commands/permissions.ts +203 -0
  69. package/cli/commands/profile.ts +108 -0
  70. package/cli/commands/react.ts +353 -0
  71. package/cli/commands/record.ts +1434 -0
  72. package/cli/commands/report-issue.ts +305 -0
  73. package/cli/commands/reset.ts +85 -0
  74. package/cli/commands/runtime.ts +351 -0
  75. package/cli/commands/screenshot-command.ts +106 -0
  76. package/cli/commands/screenshot-layers.ts +143 -0
  77. package/cli/commands/screenshot-mode.ts +37 -0
  78. package/cli/commands/screenshot.ts +488 -0
  79. package/cli/commands/screenshots-capture.ts +607 -0
  80. package/cli/commands/screenshots.ts +127 -0
  81. package/cli/commands/serve.ts +168 -0
  82. package/cli/commands/setup.ts +545 -0
  83. package/cli/commands/shell-boolean-mode.ts +81 -0
  84. package/cli/commands/skills.ts +467 -0
  85. package/cli/commands/slides.ts +361 -0
  86. package/cli/commands/state.ts +87 -0
  87. package/cli/commands/storage.ts +58 -0
  88. package/cli/commands/telemetry.ts +54 -0
  89. package/cli/commands/three-mode.ts +763 -0
  90. package/cli/commands/timeline.ts +122 -0
  91. package/cli/commands/upgrade.ts +208 -0
  92. package/cli/commands/upload.ts +1225 -0
  93. package/cli/commands/version.ts +54 -0
  94. package/cli/commands/what-happened.ts +327 -0
  95. package/cli/current-sim.ts +204 -0
  96. package/cli/desktop-companion.ts +300 -0
  97. package/cli/drivers/electron.ts +70 -0
  98. package/cli/drivers/index.ts +20 -0
  99. package/cli/drivers/playwright-provisioning.ts +180 -0
  100. package/cli/drivers/playwright.ts +698 -0
  101. package/cli/drivers/registry.ts +65 -0
  102. package/cli/drivers/types.ts +102 -0
  103. package/cli/flow-file.ts +142 -0
  104. package/cli/flow-live-status.ts +120 -0
  105. package/cli/flow-session.ts +187 -0
  106. package/cli/help.ts +80 -0
  107. package/cli/hidden-runtime-alias.ts +19 -0
  108. package/cli/hints.ts +216 -0
  109. package/cli/inspect-notice-state.ts +114 -0
  110. package/cli/maestro-js.ts +334 -0
  111. package/cli/open-url.ts +8 -0
  112. package/cli/parent-pid.ts +204 -0
  113. package/cli/parse-args.ts +211 -0
  114. package/cli/prompt.ts +51 -0
  115. package/cli/recording-access.ts +107 -0
  116. package/cli/registry.ts +1 -0
  117. package/cli/resolve-assets.ts +63 -0
  118. package/cli/run-registry.ts +226 -0
  119. package/cli/runtime-notes.ts +66 -0
  120. package/cli/runtime-summary.ts +25 -0
  121. package/cli/setup-repository.ts +187 -0
  122. package/cli/telemetry.ts +187 -0
  123. package/cli/ws-bridge.ts +798 -0
  124. package/dist-cli/bin.js +5 -5
  125. package/dist-cli/chunks/{agent-XZ2KTPCU.js → agent-CBU2WDKV.js} +2 -2
  126. package/dist-cli/chunks/{agent-wrapper-JJYYW2WH.js → agent-wrapper-UNB4R27L.js} +2 -2
  127. package/dist-cli/chunks/{app-fonts-IXRNQG6B.js → app-fonts-YMLRF3JD.js} +2 -2
  128. package/dist-cli/chunks/{assert-54T5SK5F.js → assert-5N6HUCL5.js} +2 -2
  129. package/dist-cli/chunks/{auth-FI5UDI45.js → auth-BHDR3A6G.js} +2 -2
  130. package/dist-cli/chunks/{beta-JV6UKADW.js → beta-LWUVCPYK.js} +2 -2
  131. package/dist-cli/chunks/camera-CNO6B2JZ.js +33 -0
  132. package/dist-cli/chunks/{chunk-ZMJD5GEC.js → chunk-25VCR44B.js} +1 -1
  133. package/dist-cli/chunks/{chunk-WUSWBCWA.js → chunk-26CSIR76.js} +8 -9
  134. package/dist-cli/chunks/{chunk-WINYQ44O.js → chunk-26WMWUH4.js} +1 -1
  135. package/dist-cli/chunks/{chunk-BTWORNNG.js → chunk-2JZ53D4A.js} +1 -1
  136. package/dist-cli/chunks/{chunk-DCEMHR2Y.js → chunk-2POODCTW.js} +2 -2
  137. package/dist-cli/chunks/{chunk-5TEF3ET3.js → chunk-4LTBK3ZN.js} +2 -2
  138. package/dist-cli/chunks/{chunk-NMF2ZMZQ.js → chunk-54MIRTFE.js} +4 -4
  139. package/dist-cli/chunks/{chunk-WEXDAC74.js → chunk-56H6EVO4.js} +2 -2
  140. package/dist-cli/chunks/{chunk-F5ZRSS3C.js → chunk-657SQG4D.js} +1 -1
  141. package/dist-cli/chunks/{chunk-IJ5CAZZC.js → chunk-6ZMRJPKI.js} +1 -1
  142. package/dist-cli/chunks/{chunk-W6K4EFPH.js → chunk-AOGGOC4V.js} +2 -2
  143. package/dist-cli/chunks/chunk-BPCDO7ST.js +15 -0
  144. package/dist-cli/chunks/{chunk-D4FFVGI5.js → chunk-C5O5CTXJ.js} +1 -1
  145. package/dist-cli/chunks/{chunk-RTN5C5RL.js → chunk-CMM64PPR.js} +1 -1
  146. package/dist-cli/chunks/{chunk-BBULZ7CG.js → chunk-D3M36D7O.js} +62 -87
  147. package/dist-cli/chunks/{chunk-5DHC6KHQ.js → chunk-DNCNFZ7V.js} +1 -1
  148. package/dist-cli/chunks/{chunk-YIFT42WN.js → chunk-FNTTIR2P.js} +2 -2
  149. package/dist-cli/chunks/chunk-H362IX2I.js +4 -0
  150. package/dist-cli/chunks/{chunk-IJO63TDP.js → chunk-J6HMTUZH.js} +2 -2
  151. package/dist-cli/chunks/{chunk-RSZWCKNT.js → chunk-JZ6VTZ5B.js} +3 -3
  152. package/dist-cli/chunks/{chunk-MJRLLB4R.js → chunk-KQSJVNFE.js} +4 -4
  153. package/dist-cli/chunks/chunk-L4JZKVCC.js +6 -0
  154. package/dist-cli/chunks/{chunk-VFCMSYZK.js → chunk-LP2BTEJ6.js} +2 -2
  155. package/dist-cli/chunks/{chunk-2YR5BGA5.js → chunk-LT6UC7I3.js} +2 -2
  156. package/dist-cli/chunks/{chunk-OVFJFXUD.js → chunk-MCKQH4AM.js} +2 -2
  157. package/dist-cli/chunks/{chunk-TZFFR3SD.js → chunk-MF7WUHQ3.js} +2 -2
  158. package/dist-cli/chunks/{chunk-46EUUFJ5.js → chunk-MSSY4EEZ.js} +1 -1
  159. package/dist-cli/chunks/{chunk-WMIIKMGK.js → chunk-N5ZCYP5F.js} +2 -2
  160. package/dist-cli/chunks/{chunk-3NV2NCNX.js → chunk-NCKAD3V7.js} +2 -2
  161. package/dist-cli/chunks/{chunk-VNQEB4L7.js → chunk-O2KRHVTU.js} +2 -2
  162. package/dist-cli/chunks/{chunk-OZSSI4WN.js → chunk-OF2HTH25.js} +2 -2
  163. package/dist-cli/chunks/{chunk-GGRX24GF.js → chunk-OXS2IWO2.js} +2 -2
  164. package/dist-cli/chunks/{chunk-WWZIXIRD.js → chunk-PVL7XSPN.js} +1 -1
  165. package/dist-cli/chunks/{chunk-DZS6WPUI.js → chunk-PVPAP3TV.js} +1 -1
  166. package/dist-cli/chunks/{chunk-5YJCOWCH.js → chunk-PXNAZYGV.js} +1 -1
  167. package/dist-cli/chunks/chunk-QR3HTFRB.js +5 -0
  168. package/dist-cli/chunks/{chunk-5TPRP5QT.js → chunk-QR7MWAWE.js} +1 -1
  169. package/dist-cli/chunks/chunk-RJIEZ2NY.js +4 -0
  170. package/dist-cli/chunks/{chunk-UC6U3MML.js → chunk-RVM4EZ4E.js} +2 -2
  171. package/dist-cli/chunks/{chunk-2D2UPBBR.js → chunk-SZKSFMWS.js} +1 -1
  172. package/dist-cli/chunks/{chunk-7GN3LVWB.js → chunk-TEL7G234.js} +2 -2
  173. package/dist-cli/chunks/{chunk-GASE6UBA.js → chunk-U2HNIHBY.js} +1 -1
  174. package/dist-cli/chunks/chunk-UROXJVY3.js +27 -0
  175. package/dist-cli/chunks/{chunk-HI5TFJWN.js → chunk-WM32MGTJ.js} +2 -2
  176. package/dist-cli/chunks/{chunk-VZXWHRUZ.js → chunk-WMZ7NJAO.js} +89 -133
  177. package/dist-cli/chunks/{chunk-XEVZYVIW.js → chunk-WUXCH6WN.js} +10 -9
  178. package/dist-cli/chunks/chunk-XOXGC3EH.js +9 -0
  179. package/dist-cli/chunks/chunk-XSSHENNY.js +4 -0
  180. package/dist-cli/chunks/{chunk-OHAZNXLK.js → chunk-XZ6O67AR.js} +1 -1
  181. package/dist-cli/chunks/{chunk-QKDWYITG.js → chunk-Y2MWQKOS.js} +3 -3
  182. package/dist-cli/chunks/{chunk-YDGQTMQL.js → chunk-YY24QI25.js} +1 -1
  183. package/dist-cli/chunks/{chunk-WF3T4SVI.js → chunk-ZWX6DDIS.js} +2 -2
  184. package/dist-cli/chunks/{cleanup-P27PA6JI.js → cleanup-F3NUJ3SL.js} +2 -2
  185. package/dist-cli/chunks/cli-version-XIW6O3FG.js +4 -0
  186. package/dist-cli/chunks/{compat-ZD65FED3.js → compat-AX2SEPTH.js} +2 -2
  187. package/dist-cli/chunks/{config-XMJRNM2A.js → config-4QQO5JVU.js} +2 -2
  188. package/dist-cli/chunks/{control-KMIQT3QP.js → control-OCKKSOWA.js} +2 -2
  189. package/dist-cli/chunks/daemon-N5VLMYKQ.js +4 -0
  190. package/dist-cli/chunks/{debug-PT4HOP7N.js → debug-T6MWVEVX.js} +5 -5
  191. package/dist-cli/chunks/{desktop-S3FG72AK.js → desktop-NCW7R7CB.js} +3 -3
  192. package/dist-cli/chunks/{detox-B3D4IFCN.js → detox-G6GITO2X.js} +2 -2
  193. package/dist-cli/chunks/{device-XBNDSB2R.js → device-7BTEJCTH.js} +2 -2
  194. package/dist-cli/chunks/{diagnose-HMQXJE5N.js → diagnose-SC5LACE3.js} +2 -2
  195. package/dist-cli/chunks/{disk-cleanup-BLCZ5BSZ.js → disk-cleanup-KK56QQZA.js} +2 -2
  196. package/dist-cli/chunks/drivers-NPGVBWMG.js +4 -0
  197. package/dist-cli/chunks/{film-BJGTBYZB.js → film-WGYEHGIA.js} +3 -3
  198. package/dist-cli/chunks/flow-KQBIBM3N.js +4 -0
  199. package/dist-cli/chunks/help-G7CAKI65.js +4 -0
  200. package/dist-cli/chunks/{hidden-runtime-alias-ANOYADHM.js → hidden-runtime-alias-CWTZWH3L.js} +2 -2
  201. package/dist-cli/chunks/home-paths-EBT4XHAS.js +4 -0
  202. package/dist-cli/chunks/inspect-CCPZXVDU.js +4 -0
  203. package/dist-cli/chunks/install-desktop-NZ3VGTQZ.js +4 -0
  204. package/dist-cli/chunks/{login-FJ737MWG.js → login-GSYOTGVQ.js} +4 -4
  205. package/dist-cli/chunks/{logout-ZCNMMHMY.js → logout-JPRIR7YK.js} +2 -2
  206. package/dist-cli/chunks/{maestro-SZTNKLDF.js → maestro-LXKDNO5M.js} +3 -3
  207. package/dist-cli/chunks/{maestro-generate-DCFAIZ4H.js → maestro-generate-5ZIKOU53.js} +3 -3
  208. package/dist-cli/chunks/{mode-GRMQCRXR.js → mode-N3QHXCES.js} +2 -2
  209. package/dist-cli/chunks/{optional-demo-registry-W36EWFFB.js → optional-demo-registry-XDH6X24N.js} +2 -2
  210. package/dist-cli/chunks/{perf-QYBAAUZG.js → perf-VOPL26FM.js} +2 -2
  211. package/dist-cli/chunks/{permissions-3QCQ6VF4.js → permissions-CZFKM4UD.js} +2 -2
  212. package/dist-cli/chunks/{record-QPPC2S4E.js → record-FODSOGMA.js} +3 -3
  213. package/dist-cli/chunks/{report-issue-7NMFP4HK.js → report-issue-FBPCZKOV.js} +2 -2
  214. package/dist-cli/chunks/reset-3H35GFO3.js +4 -0
  215. package/dist-cli/chunks/runtime-4OVFRXV5.js +4 -0
  216. package/dist-cli/chunks/{screenshot-command-67AECJFB.js → screenshot-command-7S3FYVO7.js} +7 -7
  217. package/dist-cli/chunks/{screenshot-layers-ASWBYPJL.js → screenshot-layers-CDYHPNC3.js} +3 -3
  218. package/dist-cli/chunks/{screenshots-capture-PXA3HFQK.js → screenshots-capture-EBIIY2YU.js} +2 -2
  219. package/dist-cli/chunks/serve-NELNSCZP.js +44 -0
  220. package/dist-cli/chunks/{setup-7DWPMRSB.js → setup-DF5ZDDIV.js} +2 -2
  221. package/dist-cli/chunks/{skills-S3Y22TUA.js → skills-E2QZF6WZ.js} +2 -2
  222. package/dist-cli/chunks/state-MBZDKZ7U.js +14 -0
  223. package/dist-cli/chunks/{storage-XUIMJWAJ.js → storage-J5BMNVXM.js} +6 -6
  224. package/dist-cli/chunks/store-U3DXC7HH.js +4 -0
  225. package/dist-cli/chunks/telemetry-XIPJVT5E.js +4 -0
  226. package/dist-cli/chunks/{timeline-TMPLQPSP.js → timeline-MC6N5BHY.js} +2 -2
  227. package/dist-cli/chunks/{upgrade-7HDSIM7K.js → upgrade-7UTU53DH.js} +2 -2
  228. package/dist-cli/chunks/upload-2M2KOTYI.js +4 -0
  229. package/dist-cli/chunks/version-VNACNYQK.js +6 -0
  230. package/dist-cli/chunks/{web-DG3WBYD3.js → web-MGJ2TUKR.js} +2 -2
  231. package/dist-cli/chunks/{what-happened-XFVUTZR7.js → what-happened-7DLKH3OD.js} +3 -3
  232. package/dist-lib/agent-daemon-client.cjs +1 -1
  233. package/dist-lib/agent-events.cjs +1 -1
  234. package/dist-lib/agent-identity.cjs +1 -1
  235. package/dist-lib/agent-sessions.cjs +1 -1
  236. package/dist-lib/attached-projects.cjs +1 -1
  237. package/dist-lib/auth/shared-session.cjs +1 -1
  238. package/dist-lib/backend-origin.cjs +1 -1
  239. package/dist-lib/beta.cjs +1 -1
  240. package/dist-lib/beta.mjs +1 -1
  241. package/dist-lib/bridge-constants.cjs +1 -1
  242. package/dist-lib/bridge-contract.cjs +20 -0
  243. package/dist-lib/cli-constants.cjs +1 -1
  244. package/dist-lib/config.cjs +1 -1
  245. package/dist-lib/detox/index.cjs +1 -1
  246. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  247. package/dist-lib/home-paths.cjs +67 -28
  248. package/dist-lib/host/bridge-host.cjs +140 -12
  249. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  250. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  251. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  252. package/dist-lib/host/websocket-proxy.cjs +1 -1
  253. package/dist-lib/index.cjs +2815 -40
  254. package/dist-lib/jump-to-source-babel.cjs +1 -1
  255. package/dist-lib/menu.cjs +1 -1
  256. package/dist-lib/menu.mjs +1 -1
  257. package/dist-lib/metro.cjs +1 -1
  258. package/dist-lib/profiles.cjs +1 -1
  259. package/dist-lib/public-brand.cjs +1 -1
  260. package/dist-lib/render-mode.cjs +1 -1
  261. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  262. package/dist-lib/sdk.cjs +2549 -2061
  263. package/dist-lib/sdk.mjs +2543 -2061
  264. package/dist-lib/skills.cjs +480 -280
  265. package/dist-lib/vite.cjs +1 -1
  266. package/package.json +8 -2
  267. package/src/bridge-constants.ts +3 -4
  268. package/src/bridge-contract.ts +251 -0
  269. package/src/connect.ts +83 -0
  270. package/src/disk-cleanup.ts +30 -0
  271. package/src/home-paths.ts +81 -38
  272. package/src/host/bridge-host.ts +134 -6
  273. package/src/index.ts +27 -1
  274. package/src/sdk.ts +8 -0
  275. package/src/sim-client.ts +660 -0
  276. package/dist-cli/chunks/camera-VL73YIKP.js +0 -22
  277. package/dist-cli/chunks/chunk-4NPPOV2N.js +0 -5
  278. package/dist-cli/chunks/chunk-FSUYIVJ6.js +0 -9
  279. package/dist-cli/chunks/chunk-G2WW6L2C.js +0 -23
  280. package/dist-cli/chunks/chunk-KTHV3RUS.js +0 -26
  281. package/dist-cli/chunks/chunk-LF2ZVT7O.js +0 -6
  282. package/dist-cli/chunks/chunk-NFK7T35W.js +0 -4
  283. package/dist-cli/chunks/chunk-TIVZIMMW.js +0 -4
  284. package/dist-cli/chunks/cli-version-WWLPBDQ7.js +0 -4
  285. package/dist-cli/chunks/daemon-G2ME7NLB.js +0 -4
  286. package/dist-cli/chunks/drivers-LDECZGP2.js +0 -4
  287. package/dist-cli/chunks/flow-UEQNVTU7.js +0 -4
  288. package/dist-cli/chunks/help-T5FYSVGB.js +0 -4
  289. package/dist-cli/chunks/home-paths-GT3LFNOR.js +0 -4
  290. package/dist-cli/chunks/inspect-ZA6XF5LD.js +0 -4
  291. package/dist-cli/chunks/install-desktop-TIMUDHPL.js +0 -4
  292. package/dist-cli/chunks/runtime-XOAXMSTU.js +0 -4
  293. package/dist-cli/chunks/serve-BI2NBAXG.js +0 -44
  294. package/dist-cli/chunks/store-JTHEJLAZ.js +0 -4
  295. package/dist-cli/chunks/telemetry-ZYJGD2DB.js +0 -4
  296. package/dist-cli/chunks/upload-GMSZPWM6.js +0 -4
  297. package/dist-cli/chunks/version-HOCHZ37L.js +0 -6
package/LICENSE CHANGED
@@ -1,16 +1,17 @@
1
- MIT License
1
+ Proprietary Software License
2
2
 
3
- Copyright (c) Tamagui LLC and sootsim contributors
3
+ Copyright (c) Tamagui LLC. All rights reserved.
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ This package (rnxsim) is proprietary software of Tamagui LLC, licensed under
6
+ the rnx Software License Agreement: https://rnxsim.com/legal/license
11
7
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
8
+ In summary (the agreement governs): personal, non-commercial use is free;
9
+ commercial use requires an active Pro subscription after a single 30-day
10
+ evaluation period; embedding or redistributing the software requires a
11
+ separate written agreement with Tamagui LLC.
12
+
13
+ Versions of this package published before this license change remain under
14
+ the license they were published with.
14
15
 
15
16
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
17
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
package/README.md CHANGED
@@ -157,6 +157,11 @@ guest-app fetch/WebSocket traffic so the cross-origin tenant worker can reach
157
157
  localhost. The `AgentHost` extension owns one FIFO reader per agent session and
158
158
  fans agent events out to every subscriber.
159
159
 
160
+ During source development, each Vite bridge owns one
161
+ `~/.sootsim/dev-bridges/<port>.json` heartbeat record. CLI defaults select the
162
+ current `PORT_OFFSET`, then the current checkout, then the canonical `:7668`
163
+ bridge, so concurrent worktrees cannot replace or delete one another's address.
164
+
160
165
  **The sim** is a browser/Electron *page* launched by a CLI driver. Inside that
161
166
  page run three workers: the **shell worker** owns device and native state, the
162
167
  **compositor worker** paints every visible CanvasKit surface, and the **tenant
@@ -0,0 +1,65 @@
1
+ import { createRequire } from 'node:module'
2
+ import { isValidRuntimeVersion } from '@contrast/runtime-delivery'
3
+ import { createJiti } from 'jiti'
4
+ import { rnxPublicBrand } from '../src/public-brand'
5
+ import { resolveAppProject, type ResolvedAppProject } from './app-project'
6
+ import type { SootSimConfig } from '../src/config'
7
+
8
+ const require = createRequire(import.meta.url)
9
+
10
+ function isOptionalRecord(value: unknown): boolean {
11
+ return (
12
+ value === undefined ||
13
+ (value !== null && typeof value === 'object' && !Array.isArray(value))
14
+ )
15
+ }
16
+
17
+ function isSootSimConfig(value: unknown): value is SootSimConfig {
18
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false
19
+ const runtimeVersion = Reflect.get(value, 'runtimeVersion')
20
+ const network = Reflect.get(value, 'network')
21
+ return (
22
+ (runtimeVersion === undefined ||
23
+ (typeof runtimeVersion === 'string' &&
24
+ runtimeVersion === runtimeVersion.trim() &&
25
+ isValidRuntimeVersion(runtimeVersion))) &&
26
+ isOptionalRecord(Reflect.get(value, 'modules')) &&
27
+ isOptionalRecord(Reflect.get(value, 'turboModules')) &&
28
+ isOptionalRecord(Reflect.get(value, 'nativeModules')) &&
29
+ isOptionalRecord(Reflect.get(value, 'env')) &&
30
+ isOptionalRecord(network) &&
31
+ isOptionalRecord(Reflect.get(value, 'settings')) &&
32
+ isOptionalRecord(Reflect.get(value, 'initialState'))
33
+ )
34
+ }
35
+
36
+ export async function loadSootSimAppConfig(
37
+ project: ResolvedAppProject | null,
38
+ ): Promise<SootSimConfig | undefined> {
39
+ if (!project?.configPath) return undefined
40
+ try {
41
+ const jiti = createJiti(import.meta.url, {
42
+ interopDefault: true,
43
+ alias: {
44
+ [`${rnxPublicBrand.packageName}/config`]: require.resolve(
45
+ `${rnxPublicBrand.packageName}/config`,
46
+ ),
47
+ },
48
+ })
49
+ const loaded: unknown = await jiti.import(project.configPath, { default: true })
50
+ if (!isSootSimConfig(loaded)) {
51
+ throw new Error('the default export must be an rnx config object')
52
+ }
53
+ return loaded
54
+ } catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error)
56
+ throw new Error(`could not load ${project.configPath}: ${message}`)
57
+ }
58
+ }
59
+
60
+ export async function resolveSootSimAppConfig(
61
+ startDir: string = process.cwd(),
62
+ ): Promise<{ project: ResolvedAppProject | null; config: SootSimConfig | undefined }> {
63
+ const project = resolveAppProject(startDir)
64
+ return { project, config: await loadSootSimAppConfig(project) }
65
+ }
@@ -0,0 +1,408 @@
1
+ import { createHash } from 'node:crypto'
2
+ import fs from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import path from 'node:path'
5
+ import {
6
+ expoFontPathsFromPlugins,
7
+ metroFontSpecsFromPaths,
8
+ type ConfigPluginFontSpec,
9
+ } from 'sootsim-engine/engine/config-plugin-fonts'
10
+ import { resolveListeningPid, resolveProcessCwd } from '../scripts/dev-server-scanner'
11
+ import { parsePlistSource } from '../src/plist.ts'
12
+ import { resolveAppProject } from './app-project'
13
+
14
+ const require = createRequire(import.meta.url)
15
+
16
+ // the filesystem scan and the engine's metro-manifest scan share the same spec
17
+ // shape and the same path → spec logic (config-plugin-fonts.ts).
18
+ export type DiscoveredAppFontSpec = ConfigPluginFontSpec
19
+
20
+ type ExpoConfigGetter = (dir: string, opts: Record<string, unknown>) => { exp?: unknown }
21
+
22
+ function localOriginOf(value: string): string | null {
23
+ try {
24
+ const url = new URL(value)
25
+ const host = url.hostname.replace(/^\[|\]$/g, '').toLowerCase()
26
+ if (
27
+ host === 'localhost' ||
28
+ host.endsWith('.localhost') ||
29
+ host === '0.0.0.0' ||
30
+ host === '::1' ||
31
+ /^127(?:\.\d{1,3}){3}$/.test(host)
32
+ ) {
33
+ return url.origin
34
+ }
35
+ } catch {}
36
+ return null
37
+ }
38
+
39
+ function hasPackageJson(dir: string): boolean {
40
+ try {
41
+ return fs.statSync(path.join(dir, 'package.json')).isFile()
42
+ } catch {
43
+ return false
44
+ }
45
+ }
46
+ export function discoverNativeLinkedAppFonts(opts: {
47
+ bundleUrl: string
48
+ repoDir?: string
49
+ exactProjectDir?: boolean
50
+ platform?: string
51
+ }): DiscoveredAppFontSpec[] {
52
+ const metroOrigin = localOriginOf(opts.bundleUrl)
53
+ if (!metroOrigin) return []
54
+
55
+ const explicit = opts.repoDir || process.env.CONTRAST_REPO_DIR
56
+ const startDir = explicit ? path.resolve(explicit) : process.cwd()
57
+
58
+ // if we're using cwd() (no explicit repo) and there's no package.json,
59
+ // assume we're not in a project tree — bail immediately instead of
60
+ // recursing the filesystem.
61
+ if (!explicit && !hasPackageJson(startDir)) return []
62
+
63
+ const project = resolveAppProject(startDir, { trustStart: opts.exactProjectDir })
64
+ const selectedAppDir = project?.appDir ?? null
65
+ if (!selectedAppDir) return []
66
+
67
+ const platform = (opts.platform || process.env.CONTRAST_PLATFORM || 'ios').toLowerCase()
68
+
69
+ let getConfig: ExpoConfigGetter | null = null
70
+ // use the app config dirs + startDir as resolve paths. real repos hoist
71
+ // @expo/config to the root node_modules so startDir is sufficient; tests
72
+ // that mock @expo/config can set CONTRAST_REPO_DIR to point where the mock lives.
73
+ if (explicit || project) {
74
+ try {
75
+ const modulePath = require.resolve('@expo/config', {
76
+ paths: [selectedAppDir, startDir],
77
+ })
78
+ const mod = require(modulePath) as { getConfig?: unknown }
79
+ if (typeof mod.getConfig === 'function') {
80
+ getConfig = mod.getConfig as ExpoConfigGetter
81
+ }
82
+ } catch {}
83
+ }
84
+ let plugins: unknown = []
85
+ if (getConfig) {
86
+ try {
87
+ const result = getConfig(selectedAppDir, { skipSDKVersionRequirement: true })
88
+ if (result?.exp && typeof result.exp === 'object') {
89
+ const resolvedPlugins = (result.exp as Record<string, unknown>).plugins
90
+ if (Array.isArray(resolvedPlugins)) {
91
+ plugins = resolvedPlugins
92
+ }
93
+ }
94
+ } catch {}
95
+ }
96
+
97
+ const fontPaths = expoFontPathsFromPlugins(
98
+ plugins,
99
+ platform === 'android' ? 'android' : 'ios',
100
+ ).slice(0, 256)
101
+ // the walk is bounded by WORK, not by a clock: at most 64 readdirs, at most 64
102
+ // queued dirs, 4 levels deep, 256 paths. those ceilings are what keep a stray
103
+ // node_modules from being crawled, and no machine can blow through them. a
104
+ // wall-clock budget on top of that only makes the RESULT depend on how busy
105
+ // the box is, and silently — a user on a loaded machine would get fewer fonts
106
+ // and no error at all.
107
+ let scannedFontDirectories = 0
108
+
109
+ const collectFontFiles = (root: string, metroPathPrefix: string): void => {
110
+ const pending: Array<{ dir: string; depth: number }> = [{ dir: root, depth: 0 }]
111
+ let nextIndex = 0
112
+ while (
113
+ nextIndex < pending.length &&
114
+ scannedFontDirectories < 64 &&
115
+ fontPaths.length < 256
116
+ ) {
117
+ const current = pending[nextIndex++]
118
+ if (!current) continue
119
+ scannedFontDirectories++
120
+ let entries: fs.Dirent[]
121
+ try {
122
+ entries = fs.readdirSync(current.dir, { withFileTypes: true })
123
+ } catch {
124
+ continue
125
+ }
126
+ for (const entry of entries) {
127
+ if (entry.isSymbolicLink()) continue
128
+ const fullPath = path.join(current.dir, entry.name)
129
+ if (entry.isDirectory()) {
130
+ if (current.depth < 4 && pending.length < 64 && !entry.name.startsWith('.')) {
131
+ pending.push({ dir: fullPath, depth: current.depth + 1 })
132
+ }
133
+ continue
134
+ }
135
+ if (!entry.isFile() || !/\.(ttf|otf|woff|woff2)$/i.test(entry.name)) continue
136
+ const relative = path.relative(root, fullPath).split(path.sep).join('/')
137
+ fontPaths.push(path.posix.join(metroPathPrefix, relative))
138
+ if (fontPaths.length >= 256) break
139
+ }
140
+ }
141
+ }
142
+
143
+ // @react-native-vector-icons links fonts from its configured local font
144
+ // directory and each installed icon package into the native app. those files
145
+ // never enter the metro module graph, so SootSim must stage them through the
146
+ // same native-linked font wire as expo-font config-plugin assets. Font Awesome
147
+ // Pro installs its licensed faces under the local directory rather than
148
+ // publishing them in the npm package.
149
+ try {
150
+ const packageJson: object = JSON.parse(
151
+ fs.readFileSync(path.join(selectedAppDir, 'package.json'), 'utf8'),
152
+ )
153
+ const dependencyNames = new Set<string>()
154
+ for (const field of ['dependencies', 'devDependencies', 'optionalDependencies']) {
155
+ const dependencies = Reflect.get(packageJson, field)
156
+ if (!dependencies || typeof dependencies !== 'object') continue
157
+ for (const name of Object.keys(dependencies)) dependencyNames.add(name)
158
+ }
159
+ const vectorIconDependencies = [...dependencyNames].filter(
160
+ (name) =>
161
+ name === 'react-native-vector-icons' ||
162
+ name.startsWith('@react-native-vector-icons/'),
163
+ )
164
+ if (vectorIconDependencies.length > 0) {
165
+ const config = Reflect.get(packageJson, 'reactNativeVectorIcons')
166
+ const configuredFontDir =
167
+ config && typeof config === 'object' ? Reflect.get(config, 'fontDir') : null
168
+ const configuredDir =
169
+ typeof configuredFontDir === 'string' ? configuredFontDir : 'rnvi-fonts'
170
+ const root = path.resolve(selectedAppDir, configuredDir)
171
+ const relativeRoot = path.relative(selectedAppDir, root)
172
+ if (!relativeRoot.startsWith('..') && !path.isAbsolute(relativeRoot)) {
173
+ collectFontFiles(root, relativeRoot.split(path.sep).join('/'))
174
+ }
175
+
176
+ const vectorIconRequire = createRequire(path.join(selectedAppDir, 'package.json'))
177
+ for (const dependencyName of vectorIconDependencies) {
178
+ try {
179
+ const entryPath = vectorIconRequire.resolve(dependencyName)
180
+ const dependencyPath = dependencyName.split('/').join(path.sep)
181
+ const marker = `${path.sep}node_modules${path.sep}${dependencyPath}${path.sep}`
182
+ const markerIndex = entryPath.lastIndexOf(marker)
183
+ if (markerIndex === -1) continue
184
+ const packageRoot = entryPath.slice(0, markerIndex + marker.length - 1)
185
+ const fontsDir =
186
+ dependencyName === 'react-native-vector-icons' ? 'Fonts' : 'fonts'
187
+ collectFontFiles(
188
+ path.join(packageRoot, fontsDir),
189
+ path.posix.join('node_modules', dependencyName, fontsDir),
190
+ )
191
+ } catch {}
192
+ }
193
+ }
194
+ } catch {}
195
+
196
+ // react-native.config.js `assets` is the RN CLI's native asset-linking
197
+ // source: `npx react-native-asset` copies every font in those directories
198
+ // into the app bundle and Info.plist UIAppFonts, and the JS never requires
199
+ // the files, so they enter no module graph. apps like mattermost register
200
+ // icon sets (createIconSetFromFontello) purely by family name against a
201
+ // font linked this way.
202
+ try {
203
+ const configPath = path.join(selectedAppDir, 'react-native.config.js')
204
+ if (fs.statSync(configPath).isFile()) {
205
+ const appConfigRequire = createRequire(path.join(selectedAppDir, 'package.json'))
206
+ const rnConfig: unknown = appConfigRequire(configPath)
207
+ const assets =
208
+ rnConfig && typeof rnConfig === 'object' ? Reflect.get(rnConfig, 'assets') : null
209
+ if (Array.isArray(assets)) {
210
+ for (const assetDir of assets) {
211
+ if (typeof assetDir !== 'string') continue
212
+ const root = path.resolve(selectedAppDir, assetDir)
213
+ const relativeRoot = path.relative(selectedAppDir, root)
214
+ if (relativeRoot.startsWith('..') || path.isAbsolute(relativeRoot)) continue
215
+ collectFontFiles(root, relativeRoot.split(path.sep).join('/'))
216
+ }
217
+ }
218
+ }
219
+ } catch {}
220
+
221
+ // bare iOS projects can embed fonts directly in an Xcode target without a
222
+ // react-native.config.js assets declaration. UIAppFonts is the native
223
+ // contract, so stage those referenced files through Metro's asset endpoint.
224
+ if (platform === 'ios') {
225
+ const iosDir = path.join(selectedAppDir, 'ios')
226
+ try {
227
+ const targetDirs = fs
228
+ .readdirSync(iosDir, { withFileTypes: true })
229
+ .filter(
230
+ (entry) =>
231
+ entry.isDirectory() &&
232
+ !entry.isSymbolicLink() &&
233
+ entry.name !== 'Pods' &&
234
+ entry.name !== 'build' &&
235
+ !entry.name.startsWith('.'),
236
+ )
237
+ .slice(0, 64)
238
+ const plistPaths = [path.join(iosDir, 'Info.plist')]
239
+ for (const entry of targetDirs) {
240
+ plistPaths.push(path.join(iosDir, entry.name, 'Info.plist'))
241
+ }
242
+ for (const plistPath of plistPaths) {
243
+ let parsed: unknown
244
+ try {
245
+ parsed = parsePlistSource(fs.readFileSync(plistPath, 'utf8'))
246
+ } catch {
247
+ continue
248
+ }
249
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) continue
250
+ const declaredFonts = Reflect.get(parsed, 'UIAppFonts')
251
+ if (!Array.isArray(declaredFonts)) continue
252
+ for (const declaredFont of declaredFonts) {
253
+ if (typeof declaredFont !== 'string') continue
254
+ const candidates = [
255
+ path.resolve(path.dirname(plistPath), declaredFont),
256
+ path.resolve(iosDir, declaredFont),
257
+ ]
258
+ const fontPath = candidates.find((candidate) => {
259
+ const relative = path.relative(selectedAppDir, candidate)
260
+ if (relative.startsWith('..') || path.isAbsolute(relative)) return false
261
+ try {
262
+ return fs.statSync(candidate).isFile()
263
+ } catch {
264
+ return false
265
+ }
266
+ })
267
+ if (!fontPath) continue
268
+ fontPaths.push(
269
+ path.relative(selectedAppDir, fontPath).split(path.sep).join('/'),
270
+ )
271
+ }
272
+ }
273
+ } catch {}
274
+ }
275
+
276
+ return metroFontSpecsFromPaths(fontPaths, metroOrigin)
277
+ }
278
+
279
+ // resolve a metro dev server's project root. expo serves a manifest at the
280
+ // server origin carrying `extra.expoClient._internal.projectRoot` — the absolute
281
+ // dir of the app being served. the local `sootsim open <port>` path runs from the
282
+ // CLI's cwd, which almost never owns the bundle being opened (e.g.
283
+ // `sootsim open 8081` run from ~/soot against a 3pc metro), so scanning cwd for
284
+ // font declarations finds nothing.
285
+ //
286
+ // a bare react-native packager serves no such manifest — it answers `/` with an
287
+ // HTML page — so fall back to asking the OS which directory the listening
288
+ // process runs from. bare apps are exactly the ones that link fonts through
289
+ // react-native.config.js `assets`, so without this their fonts are never
290
+ // discoverable and the appFonts wire comes out empty.
291
+ export async function resolveMetroProjectRoot(
292
+ bundleUrl: string,
293
+ opts?: { timeoutMs?: number },
294
+ ): Promise<string | null> {
295
+ const origin = localOriginOf(bundleUrl)
296
+ if (!origin) return null
297
+ const controller = new AbortController()
298
+ const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 1500)
299
+ let servedManifest = false
300
+ try {
301
+ const res = await fetch(`${origin}/`, {
302
+ headers: { 'expo-platform': 'ios', Accept: 'application/json' },
303
+ signal: controller.signal,
304
+ })
305
+ if (res.ok) {
306
+ const manifest = (await res.json()) as {
307
+ extra?: { expoClient?: { _internal?: { projectRoot?: unknown } } }
308
+ }
309
+ servedManifest = true
310
+ const root = manifest?.extra?.expoClient?._internal?.projectRoot
311
+ if (typeof root === 'string' && root.length > 0) return root
312
+ }
313
+ } catch {
314
+ } finally {
315
+ clearTimeout(timer)
316
+ }
317
+
318
+ // a server that answered with a manifest has already told us everything it
319
+ // knows. if it did not name a project root there is none to learn, and asking
320
+ // the OS about the listening process would answer with whatever directory
321
+ // that process happens to sit in — which is the CLI's own cwd trap this
322
+ // function exists to avoid. only a server that served no manifest at all gets
323
+ // the process lookup.
324
+ if (servedManifest) return null
325
+
326
+ const port = Number(new URL(origin).port)
327
+ if (!Number.isFinite(port) || port <= 0) return null
328
+ const pid = await resolveListeningPid(port)
329
+ return pid === null ? null : resolveProcessCwd(pid)
330
+ }
331
+
332
+ export function encodeAppFontsWire(specs: DiscoveredAppFontSpec[]): string {
333
+ return specs
334
+ .map((spec) => `${encodeURIComponent(spec.url)}::${encodeURIComponent(spec.family)}`)
335
+ .join(',')
336
+ }
337
+
338
+ export function parseAppFontUrls(wire: string): string[] {
339
+ const urls: string[] = []
340
+ for (const entry of wire.split(',')) {
341
+ const trimmed = entry.trim()
342
+ if (!trimmed) continue
343
+ const sep = trimmed.indexOf('::')
344
+ const urlPart = sep === -1 ? trimmed : trimmed.slice(0, sep)
345
+ try {
346
+ urls.push(decodeURIComponent(urlPart))
347
+ } catch {
348
+ urls.push(urlPart)
349
+ }
350
+ }
351
+ return urls.filter(Boolean)
352
+ }
353
+
354
+ // content type for a config-plugin font url, by extension. config fonts only
355
+ // ever ship as ttf/otf/woff(2); fall back to octet-stream for anything else
356
+ // so R2 still serves bytes the engine can register.
357
+ export function fontContentType(url: string): string {
358
+ const ext = (url.split('?')[0].split('.').pop() || '').toLowerCase()
359
+ if (ext === 'ttf') return 'font/ttf'
360
+ if (ext === 'otf') return 'font/otf'
361
+ if (ext === 'woff') return 'font/woff'
362
+ if (ext === 'woff2') return 'font/woff2'
363
+ return 'application/octet-stream'
364
+ }
365
+
366
+ export type FetchedAppFont = {
367
+ url: string
368
+ // sha256(url) — the share-store key both the build and preview upload paths
369
+ // use, and what /api/preview/share/fetch?url=<fontUrl> resolves against.
370
+ urlhash: string
371
+ contentType: string
372
+ bytes: Uint8Array
373
+ }
374
+
375
+ // fetch every config-plugin font declared on an engine `?appFonts=` wire.
376
+ // these typefaces live in the native binary, never in the metro bundle's
377
+ // __packager_asset graph, so neither asset capture nor recorded fetches see
378
+ // them — both the preview (`sootsim record upload`) and build (`sootsim app-fonts
379
+ // stage`) paths backfill by re-fetching the declared urls off the still-live
380
+ // metro. one implementation so both pipelines store identical bytes + keys.
381
+ export async function fetchAppFontFiles(
382
+ wire: string,
383
+ hooks?: {
384
+ onStaged?: (info: { url: string; byteLength: number }) => void
385
+ onError?: (url: string, err: unknown) => void
386
+ },
387
+ ): Promise<FetchedAppFont[]> {
388
+ const out: FetchedAppFont[] = []
389
+ const seen = new Set<string>()
390
+ for (const url of parseAppFontUrls(wire)) {
391
+ const urlhash = createHash('sha256').update(url).digest('hex')
392
+ if (seen.has(urlhash)) continue
393
+ seen.add(urlhash)
394
+ try {
395
+ const res = await fetch(url)
396
+ if (!res.ok) {
397
+ hooks?.onError?.(url, new Error(`${res.status} ${res.statusText}`))
398
+ continue
399
+ }
400
+ const bytes = new Uint8Array(await res.arrayBuffer())
401
+ out.push({ url, urlhash, contentType: fontContentType(url), bytes })
402
+ hooks?.onStaged?.({ url, byteLength: bytes.byteLength })
403
+ } catch (err) {
404
+ hooks?.onError?.(url, err)
405
+ }
406
+ }
407
+ return out
408
+ }