sootsim 0.1.227 → 0.1.229

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 (175) hide show
  1. package/README.md +14 -14
  2. package/detox/element-types.ts +3 -3
  3. package/detox/expectations.ts +29 -24
  4. package/detox/gestures.ts +16 -16
  5. package/detox/index.ts +34 -32
  6. package/dist-cli/bin.js +4 -4
  7. package/dist-cli/chunks/{agent-HBVXCRJT.js → agent-O26YYN6Z.js} +2 -2
  8. package/dist-cli/chunks/{agent-wrapper-X376DPV2.js → agent-wrapper-GSMHXP6S.js} +2 -2
  9. package/dist-cli/chunks/{app-fonts-LIQTD5RP.js → app-fonts-TVTDVD7S.js} +2 -2
  10. package/dist-cli/chunks/{assert-X6PPLN72.js → assert-KEWRJSST.js} +2 -2
  11. package/dist-cli/chunks/{auth-INRWIM2C.js → auth-KQXLD6AC.js} +2 -2
  12. package/dist-cli/chunks/beta-BXBII74U.js +2 -0
  13. package/dist-cli/chunks/{camera-LGFHYCJ5.js → camera-RTNVNBMT.js} +2 -2
  14. package/dist-cli/chunks/{chunk-YB2LM6VB.js → chunk-32O3YKA2.js} +6 -6
  15. package/dist-cli/chunks/{chunk-SBIAPF6E.js → chunk-3FGXJMSF.js} +1 -1
  16. package/dist-cli/chunks/{chunk-QWRYD2QV.js → chunk-3FYUTNM4.js} +18 -18
  17. package/dist-cli/chunks/{chunk-A4BOYB55.js → chunk-3UE5OA64.js} +2 -2
  18. package/dist-cli/chunks/{chunk-LCJSTEIR.js → chunk-456MZ4LU.js} +2 -2
  19. package/dist-cli/chunks/{chunk-E75EBS77.js → chunk-4O73AHYA.js} +2 -2
  20. package/dist-cli/chunks/chunk-4WAZOAAS.js +2 -0
  21. package/dist-cli/chunks/{chunk-RNWJQ4UR.js → chunk-62GPXYH7.js} +1 -1
  22. package/dist-cli/chunks/chunk-6Q3M6FMW.js +24 -0
  23. package/dist-cli/chunks/chunk-6VQIZJLG.js +1 -0
  24. package/dist-cli/chunks/chunk-7FSM4I7A.js +1 -0
  25. package/dist-cli/chunks/{chunk-J2JAOIMN.js → chunk-7IBNJPSB.js} +2 -2
  26. package/dist-cli/chunks/{chunk-GUCFICA4.js → chunk-7LSM7YNF.js} +1 -1
  27. package/dist-cli/chunks/{chunk-J7IFVTWX.js → chunk-AB7XCG3A.js} +1 -1
  28. package/dist-cli/chunks/{chunk-MG6JE3AM.js → chunk-AZWH6YSL.js} +2 -2
  29. package/dist-cli/chunks/{chunk-ESK7B2BQ.js → chunk-BZOTL2IQ.js} +6 -6
  30. package/dist-cli/chunks/{chunk-2XXPF6LP.js → chunk-CE3XI55C.js} +2 -2
  31. package/dist-cli/chunks/{chunk-HRYKZ5YY.js → chunk-DXF6DHIR.js} +5 -5
  32. package/dist-cli/chunks/{chunk-DB3GMVOH.js → chunk-FF7GZMWQ.js} +2 -2
  33. package/dist-cli/chunks/chunk-GVOFX6LJ.js +118 -0
  34. package/dist-cli/chunks/{chunk-HXHB5A6R.js → chunk-HKBDDBKZ.js} +2 -2
  35. package/dist-cli/chunks/{chunk-UGWURWHN.js → chunk-HKZICBKI.js} +2 -2
  36. package/dist-cli/chunks/{chunk-E3DLUOFK.js → chunk-JK4AGL2U.js} +7 -7
  37. package/dist-cli/chunks/{chunk-NIN75M5H.js → chunk-OUJVQUX4.js} +2 -2
  38. package/dist-cli/chunks/chunk-OWSZCLYZ.js +2 -0
  39. package/dist-cli/chunks/{chunk-XESYTWFM.js → chunk-P3ORPGKZ.js} +29 -29
  40. package/dist-cli/chunks/{chunk-Q4FB222M.js → chunk-PNLTVAPK.js} +3 -3
  41. package/dist-cli/chunks/{chunk-4J7BIXEB.js → chunk-PY65YXHI.js} +2 -2
  42. package/dist-cli/chunks/{chunk-7LDHOQ7B.js → chunk-S4CJUMUZ.js} +34 -34
  43. package/dist-cli/chunks/{chunk-T6MEERRI.js → chunk-SCGYOJJP.js} +2 -2
  44. package/dist-cli/chunks/{chunk-HXSV263V.js → chunk-SHBXXKJW.js} +1 -1
  45. package/dist-cli/chunks/chunk-SPBHQVJ4.js +2 -0
  46. package/dist-cli/chunks/{chunk-F53C5HAB.js → chunk-SYZKYAPC.js} +1 -1
  47. package/dist-cli/chunks/{chunk-3L5EXWCP.js → chunk-THDVL3ST.js} +1 -1
  48. package/dist-cli/chunks/{chunk-HTVB3FW2.js → chunk-TJXRSDEU.js} +2 -2
  49. package/dist-cli/chunks/{chunk-NU755HCE.js → chunk-TWUPMUWQ.js} +1 -1
  50. package/dist-cli/chunks/{chunk-F5HOYUD2.js → chunk-U4OPCVAL.js} +1 -1
  51. package/dist-cli/chunks/chunk-UCVK6JDW.js +3 -0
  52. package/dist-cli/chunks/chunk-UGFZIHBA.js +2 -0
  53. package/dist-cli/chunks/{chunk-LM544J6V.js → chunk-V2TABN7W.js} +1 -1
  54. package/dist-cli/chunks/{chunk-D42IRY2Y.js → chunk-VKVRAWJR.js} +2 -2
  55. package/dist-cli/chunks/{chunk-7L5IKIHC.js → chunk-VUJ5FLL7.js} +1 -1
  56. package/dist-cli/chunks/{chunk-2BQOZATN.js → chunk-WRPO6J3Z.js} +3 -3
  57. package/dist-cli/chunks/chunk-XQHCHVHE.js +4 -0
  58. package/dist-cli/chunks/{chunk-NHE3YHDU.js → chunk-ZORXBO54.js} +2 -2
  59. package/dist-cli/chunks/cli-version-W42GGXV7.js +2 -0
  60. package/dist-cli/chunks/{compat-MH673TEJ.js → compat-HPS6RGWD.js} +3 -3
  61. package/dist-cli/chunks/{config-SSCDV5ZJ.js → config-EKIT7WKS.js} +2 -2
  62. package/dist-cli/chunks/{control-54R2XDLV.js → control-QFNWOCLW.js} +2 -2
  63. package/dist-cli/chunks/daemon-B6RUIEYN.js +2 -0
  64. package/dist-cli/chunks/{debug-PFP646II.js → debug-EU5JDWZR.js} +3 -3
  65. package/dist-cli/chunks/{desktop-BPYYJ3EM.js → desktop-OOCDJB3I.js} +3 -3
  66. package/dist-cli/chunks/{detox-BIUVHXYH.js → detox-BXXEPJ3F.js} +2 -2
  67. package/dist-cli/chunks/{device-BNSPNIP6.js → device-6B6NKGAW.js} +2 -2
  68. package/dist-cli/chunks/{diagnose-BPTDSGOB.js → diagnose-FOWBMKO6.js} +2 -2
  69. package/dist-cli/chunks/drivers-DC6SRA2P.js +2 -0
  70. package/dist-cli/chunks/{film-ILA6VSOG.js → film-MVJCHWBU.js} +3 -3
  71. package/dist-cli/chunks/flow-XO4D3LRA.js +2 -0
  72. package/dist-cli/chunks/help-WNPSM6ZU.js +2 -0
  73. package/dist-cli/chunks/{hidden-runtime-alias-TD7IVAPF.js → hidden-runtime-alias-OVGHDXOR.js} +2 -2
  74. package/dist-cli/chunks/{home-paths-H36JDTNO.js → home-paths-3W46OKY6.js} +2 -2
  75. package/dist-cli/chunks/inspect-4J5NJCXS.js +2 -0
  76. package/dist-cli/chunks/install-desktop-GAEFTQ2K.js +2 -0
  77. package/dist-cli/chunks/{login-5WGZX2ZD.js → login-CU72MJHE.js} +4 -4
  78. package/dist-cli/chunks/{logout-V53WTVIZ.js → logout-SF5EPW7M.js} +2 -2
  79. package/dist-cli/chunks/{maestro-HGKYRD54.js → maestro-NYFSOD2H.js} +3 -3
  80. package/dist-cli/chunks/{maestro-generate-6DNZSKDP.js → maestro-generate-VDAXEVCE.js} +3 -3
  81. package/dist-cli/chunks/{mode-YFMKORE5.js → mode-VSABDCU5.js} +2 -2
  82. package/dist-cli/chunks/optional-demo-registry-YQZA2MRD.js +2 -0
  83. package/dist-cli/chunks/{perf-EOIGFDWM.js → perf-5XMCN7A2.js} +2 -2
  84. package/dist-cli/chunks/{record-2F54QS6Y.js → record-NAYAEQPD.js} +10 -10
  85. package/dist-cli/chunks/runtime-LR63M7XX.js +2 -0
  86. package/dist-cli/chunks/screenshot-command-HFNYL2CY.js +151 -0
  87. package/dist-cli/chunks/{screenshots-capture-DIICN24R.js → screenshots-capture-JCBFMJFS.js} +3 -3
  88. package/dist-cli/chunks/{serve-66NC6PPZ.js → serve-Z5HVGJ3C.js} +5 -5
  89. package/dist-cli/chunks/{setup-ZYN4BTBR.js → setup-YKDBK3RX.js} +3 -3
  90. package/dist-cli/chunks/{skills-MDBIER7U.js → skills-DQOWHZBC.js} +2 -2
  91. package/dist-cli/chunks/{storage-AQ3SEO4Y.js → storage-TKTKWTWE.js} +2 -2
  92. package/dist-cli/chunks/store-FPAX23SK.js +2 -0
  93. package/dist-cli/chunks/telemetry-CWRFOESH.js +2 -0
  94. package/dist-cli/chunks/{timeline-VEGHQZ36.js → timeline-QJPIAP7L.js} +2 -2
  95. package/dist-cli/chunks/{upgrade-RNDCRPLJ.js → upgrade-4ZEV4BY2.js} +3 -3
  96. package/dist-cli/chunks/upload-OF4CO6WM.js +2 -0
  97. package/dist-cli/chunks/{version-XUMPKQUM.js → version-OVU37MXP.js} +2 -2
  98. package/dist-cli/chunks/{web-ESQGHVPD.js → web-JUDK5W4T.js} +2 -2
  99. package/dist-cli/chunks/{what-happened-6NJENKUF.js → what-happened-TKCIGNLL.js} +2 -2
  100. package/dist-lib/agent-daemon-client.cjs +1 -1
  101. package/dist-lib/agent-events.cjs +1 -1
  102. package/dist-lib/agent-identity.cjs +1 -1
  103. package/dist-lib/agent-sessions.cjs +1 -1
  104. package/dist-lib/attached-projects.cjs +1 -1
  105. package/dist-lib/auth/shared-session.cjs +2 -2
  106. package/dist-lib/backend-origin.cjs +18 -18
  107. package/dist-lib/beta.cjs +1 -1
  108. package/dist-lib/beta.mjs +1 -1
  109. package/dist-lib/bridge-constants.cjs +4 -4
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +321 -79
  113. package/dist-lib/dev-bundle-resolution.cjs +2 -2
  114. package/dist-lib/home-paths.cjs +1 -1
  115. package/dist-lib/host/bridge-host.cjs +18 -13
  116. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  117. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +1 -1
  121. package/dist-lib/jump-to-source-babel.cjs +1 -1
  122. package/dist-lib/menu.cjs +1 -1
  123. package/dist-lib/menu.mjs +1 -1
  124. package/dist-lib/metro.cjs +1 -1
  125. package/dist-lib/profiles.cjs +1 -1
  126. package/dist-lib/render-mode.cjs +1 -1
  127. package/dist-lib/scripts/dev-server-scanner.cjs +7 -7
  128. package/dist-lib/sdk.cjs +12 -4
  129. package/dist-lib/sdk.mjs +12 -4
  130. package/dist-lib/skills.cjs +456 -170
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +4 -4
  133. package/scripts/dev-server-scanner.ts +5 -5
  134. package/scripts/postinstall.cjs +1 -1
  135. package/scripts/preuninstall.cjs +1 -1
  136. package/skills/{soot → contrast}/SKILL.md +4 -4
  137. package/skills/sootsim-perf/SKILL.md +3 -1
  138. package/src/auth/shared-session.ts +1 -1
  139. package/src/backend-origin.ts +11 -11
  140. package/src/bridge-constants.ts +3 -3
  141. package/src/home-paths.ts +1 -1
  142. package/src/host/agent-host.ts +2 -2
  143. package/src/host/bridge-host.ts +21 -21
  144. package/src/native-dev-bundle-url.ts +3 -3
  145. package/src/runtime-assets.ts +3 -3
  146. package/src/runtime-delivery.ts +10 -4
  147. package/src/screenshots/compose.ts +4 -1
  148. package/src/screenshots/frame-compose.ts +4 -1
  149. package/src/screenshots/orchestrate.ts +7 -2
  150. package/src/vite-plugin-one.ts +3 -3
  151. package/src/vite-plugin.ts +3 -3
  152. package/dist-cli/chunks/beta-WLLSIVLW.js +0 -2
  153. package/dist-cli/chunks/chunk-35EAEAN4.js +0 -24
  154. package/dist-cli/chunks/chunk-44Z2X5R4.js +0 -2
  155. package/dist-cli/chunks/chunk-4BLOZ3AF.js +0 -2
  156. package/dist-cli/chunks/chunk-7K5K5NKB.js +0 -2
  157. package/dist-cli/chunks/chunk-7RWDQCIT.js +0 -1
  158. package/dist-cli/chunks/chunk-BVD2JFOX.js +0 -1
  159. package/dist-cli/chunks/chunk-HBEEYWK5.js +0 -2
  160. package/dist-cli/chunks/chunk-JNUGPBWC.js +0 -118
  161. package/dist-cli/chunks/chunk-KBSJA7HK.js +0 -3
  162. package/dist-cli/chunks/chunk-Z2E2DDIQ.js +0 -4
  163. package/dist-cli/chunks/cli-version-NNBCIQKL.js +0 -2
  164. package/dist-cli/chunks/daemon-GL5GQTWB.js +0 -2
  165. package/dist-cli/chunks/drivers-KBKJVF2Y.js +0 -2
  166. package/dist-cli/chunks/flow-L247VVBM.js +0 -2
  167. package/dist-cli/chunks/help-QIZEANNK.js +0 -2
  168. package/dist-cli/chunks/inspect-6OAVS27G.js +0 -2
  169. package/dist-cli/chunks/install-desktop-LKYAUUQY.js +0 -2
  170. package/dist-cli/chunks/optional-demo-registry-YGNESGBH.js +0 -2
  171. package/dist-cli/chunks/runtime-FYVV4DCQ.js +0 -2
  172. package/dist-cli/chunks/screenshot-command-R535TYXY.js +0 -151
  173. package/dist-cli/chunks/store-6ZGLOTZI.js +0 -2
  174. package/dist-cli/chunks/telemetry-V76UPVDV.js +0 -2
  175. package/dist-cli/chunks/upload-KRBS3Y7Q.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.227 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.229 | (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;
@@ -36,8 +36,8 @@ __export(detox_exports, {
36
36
  cleanup: () => cleanup,
37
37
  device: () => device,
38
38
  element: () => element,
39
- expect: () => sootExpect,
40
- waitFor: () => sootWaitFor
39
+ expect: () => sootsimExpect,
40
+ waitFor: () => sootsimWaitFor
41
41
  });
42
42
  module.exports = __toCommonJS(detox_exports);
43
43
  var fs = __toESM(require("fs"), 1);
@@ -46,7 +46,108 @@ var import_playwright = require("playwright");
46
46
 
47
47
  // ../../scripts/lib/reap-browser.ts
48
48
  var import_node_child_process = require("node:child_process");
49
+ var discoveredChromePids = /* @__PURE__ */ new WeakMap();
50
+ var reapPids = /* @__PURE__ */ new Set();
51
+ var installed = false;
52
+ function currentRuntime() {
53
+ return { platform: process.platform, env: process.env };
54
+ }
55
+ function softwareChromeAllowed(runtime = currentRuntime()) {
56
+ return runtime.platform === "linux" && runtime.env.GITHUB_ACTIONS === "true" && runtime.env.RUNNER_OS === "Linux";
57
+ }
58
+ function isSoftwareRendererArgument(value) {
59
+ const arg = String(value);
60
+ return /^--disable-gpu(?:=.*)?$/i.test(arg) || /^--enable-unsafe-swiftshader(?:=.*)?$/i.test(arg) || /^--use-angle=(?:swiftshader|swiftshader-webgl)$/i.test(arg) || /^--use-gl=swiftshader$/i.test(arg);
61
+ }
62
+ function softwareRendererArgumentInText(command) {
63
+ const unsafe = command.match(
64
+ /(?:^|\s)(--disable-gpu(?:=[^\s]+)?|--enable-unsafe-swiftshader(?:=[^\s]+)?|--use-angle=(?:swiftshader|swiftshader-webgl)|--use-gl=swiftshader)(?=\s|$)/i
65
+ );
66
+ return unsafe?.[1] ?? null;
67
+ }
68
+ function expectedAngle(platform) {
69
+ if (platform === "darwin") return "metal";
70
+ if (platform === "linux") return "vulkan";
71
+ return "default";
72
+ }
73
+ function assertGpuBrowserCommand(command, runtime = currentRuntime()) {
74
+ if (softwareChromeAllowed(runtime)) return;
75
+ const unsafe = softwareRendererArgumentInText(command);
76
+ if (unsafe) {
77
+ throw new Error(
78
+ `[reap-browser] refused software-rendering Chrome process argument ${unsafe}`
79
+ );
80
+ }
81
+ const angle = expectedAngle(runtime.platform);
82
+ const args = command.split(/\s+/);
83
+ if (!args.includes(`--use-angle=${angle}`) || !args.includes("--enable-gpu")) {
84
+ throw new Error(
85
+ `[reap-browser] Chrome process did not retain the required ${angle} GPU arguments`
86
+ );
87
+ }
88
+ if (runtime.platform === "darwin" && command.includes("/Applications/Google Chrome.app/")) {
89
+ throw new Error(
90
+ "[reap-browser] refused the user's installed Google Chrome; use Playwright channel chromium"
91
+ );
92
+ }
93
+ }
94
+ function collectGpuRendererStrings(info) {
95
+ if (!info || typeof info !== "object") return [];
96
+ const record = info;
97
+ const gpu = record.gpu;
98
+ if (!gpu || typeof gpu !== "object") return [];
99
+ const gpuRecord = gpu;
100
+ const values = [];
101
+ const aux = gpuRecord.auxAttributes;
102
+ if (aux && typeof aux === "object") {
103
+ for (const value of Object.values(aux)) {
104
+ if (typeof value === "string") values.push(value);
105
+ }
106
+ }
107
+ const devices = gpuRecord.devices;
108
+ if (Array.isArray(devices)) {
109
+ for (const device2 of devices) {
110
+ if (!device2 || typeof device2 !== "object") continue;
111
+ for (const value of Object.values(device2)) {
112
+ if (typeof value === "string") values.push(value);
113
+ }
114
+ }
115
+ }
116
+ return values;
117
+ }
118
+ function browserProcessPid(info) {
119
+ if (!info || typeof info !== "object") return null;
120
+ const record = info;
121
+ if (!Array.isArray(record.processInfo)) return null;
122
+ for (const entry of record.processInfo) {
123
+ if (!entry || typeof entry !== "object") continue;
124
+ const processInfo = entry;
125
+ if (processInfo.type === "browser" && typeof processInfo.id === "number" && Number.isInteger(processInfo.id) && processInfo.id > 0) {
126
+ return processInfo.id;
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+ function assertGpuRendererInfo(info, runtime = currentRuntime()) {
132
+ if (softwareChromeAllowed(runtime)) return;
133
+ const renderers = collectGpuRendererStrings(info);
134
+ const summary = renderers.join(" | ");
135
+ if (!summary || /swiftshader|llvmpipe|software rasterizer/i.test(summary)) {
136
+ throw new Error(
137
+ `[reap-browser] Chrome reported a software renderer${summary ? `: ${summary}` : ""}`
138
+ );
139
+ }
140
+ if (runtime.platform === "darwin" && !/metal/i.test(summary)) {
141
+ throw new Error(
142
+ `[reap-browser] Chrome did not report an ANGLE Metal renderer: ${summary}`
143
+ );
144
+ }
145
+ }
49
146
  function rootChromePid(b) {
147
+ if (b && typeof b === "object") {
148
+ const discovered = discoveredChromePids.get(b);
149
+ if (discovered !== void 0) return discovered;
150
+ }
50
151
  const w = b;
51
152
  if (w && typeof w.process === "function") {
52
153
  try {
@@ -76,8 +177,96 @@ function killTree(pid) {
76
177
  } catch {
77
178
  }
78
179
  }
79
- var reapPids = /* @__PURE__ */ new Set();
80
- var installed = false;
180
+ function browserControl(b) {
181
+ const candidate = b;
182
+ if (!candidate || typeof candidate !== "object") return null;
183
+ if (typeof candidate.newBrowserCDPSession === "function" || typeof candidate.process === "function") {
184
+ return candidate;
185
+ }
186
+ if (typeof candidate.browser !== "function") return null;
187
+ try {
188
+ const browser = candidate.browser();
189
+ return browser ? browserControl(browser) : null;
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+ function registerChromePid(browser, control, pid) {
195
+ if (browser && typeof browser === "object") discoveredChromePids.set(browser, pid);
196
+ discoveredChromePids.set(control, pid);
197
+ reapPids.add(pid);
198
+ installReaper();
199
+ }
200
+ async function discoverChromePid(browser) {
201
+ const known = rootChromePid(browser);
202
+ if (known !== null) return known;
203
+ const control = browserControl(browser);
204
+ if (!control || typeof control.newBrowserCDPSession !== "function") return null;
205
+ const session = await control.newBrowserCDPSession();
206
+ try {
207
+ const pid = browserProcessPid(await session.send("SystemInfo.getProcessInfo"));
208
+ if (pid === null) return null;
209
+ registerChromePid(browser, control, pid);
210
+ return pid;
211
+ } finally {
212
+ await session.detach?.();
213
+ }
214
+ }
215
+ async function rejectLaunchedBrowser(browser) {
216
+ const root = rootChromePid(browser);
217
+ if (root !== null) {
218
+ reapPids.delete(root);
219
+ killTree(root);
220
+ return;
221
+ }
222
+ const candidate = browser;
223
+ if (candidate && typeof candidate.close === "function") {
224
+ try {
225
+ await candidate.close();
226
+ } catch {
227
+ }
228
+ }
229
+ }
230
+ async function validateLaunchedGpuBrowser(browser, runtime, rendererTarget) {
231
+ if (softwareChromeAllowed(runtime)) return;
232
+ const root = rootChromePid(browser);
233
+ const launchedControl = browserControl(browser);
234
+ if (root !== null && launchedControl) {
235
+ registerChromePid(browser, launchedControl, root);
236
+ }
237
+ if (root !== null && (runtime.platform === "darwin" || runtime.platform === "linux")) {
238
+ const command = (0, import_node_child_process.execFileSync)("ps", ["-p", String(root), "-o", "command="], {
239
+ encoding: "utf8"
240
+ }).trim();
241
+ assertGpuBrowserCommand(command, runtime);
242
+ }
243
+ const rendererBrowser = rendererTarget ? await rendererTarget(browser) : browser;
244
+ const control = browserControl(rendererBrowser);
245
+ if (control && typeof control.newBrowserCDPSession === "function") {
246
+ const session = await control.newBrowserCDPSession();
247
+ try {
248
+ const pid = browserProcessPid(await session.send("SystemInfo.getProcessInfo"));
249
+ if (pid === null) {
250
+ throw new Error("[reap-browser] Chrome did not report its browser process id");
251
+ }
252
+ registerChromePid(browser, control, pid);
253
+ if (rendererBrowser && typeof rendererBrowser === "object") {
254
+ discoveredChromePids.set(rendererBrowser, pid);
255
+ }
256
+ if (runtime.platform === "darwin" || runtime.platform === "linux") {
257
+ const command = (0, import_node_child_process.execFileSync)("ps", ["-p", String(pid), "-o", "command="], {
258
+ encoding: "utf8"
259
+ }).trim();
260
+ assertGpuBrowserCommand(command, runtime);
261
+ }
262
+ assertGpuRendererInfo(await session.send("SystemInfo.getInfo"), runtime);
263
+ return;
264
+ } finally {
265
+ await session.detach?.();
266
+ }
267
+ }
268
+ throw new Error("[reap-browser] could not validate the launched Chrome GPU renderer");
269
+ }
81
270
  function installReaper() {
82
271
  if (installed) return;
83
272
  installed = true;
@@ -96,16 +285,24 @@ function installReaper() {
96
285
  }
97
286
  async function launchReapedScoped(launch) {
98
287
  const browser = await launch();
99
- const root = rootChromePid(browser);
288
+ let root = rootChromePid(browser);
289
+ if (root === null) {
290
+ try {
291
+ root = await discoverChromePid(browser);
292
+ } catch {
293
+ root = null;
294
+ }
295
+ }
100
296
  const mine = /* @__PURE__ */ new Set();
101
297
  if (root != null) {
102
298
  mine.add(root);
103
299
  reapPids.add(root);
104
300
  installReaper();
105
301
  }
106
- const evented = browser;
302
+ const evented = browserControl(browser) ?? browser;
107
303
  if (typeof evented.on === "function") {
108
- evented.on("disconnected", () => {
304
+ const disconnectEvent = typeof evented.newBrowserCDPSession === "function" ? "disconnected" : "close";
305
+ evented.on(disconnectEvent, () => {
109
306
  for (const pid of mine) reapPids.delete(pid);
110
307
  });
111
308
  }
@@ -119,8 +316,8 @@ async function launchReapedScoped(launch) {
119
316
  return { browser, reapNow };
120
317
  }
121
318
  var gpuChromeMissing = false;
122
- function gpuChromeArgs() {
123
- const angle = process.platform === "darwin" ? "metal" : process.platform === "linux" ? "vulkan" : "default";
319
+ function gpuChromeArgs(platform = process.platform) {
320
+ const angle = expectedAngle(platform);
124
321
  return [
125
322
  `--use-angle=${angle}`,
126
323
  "--enable-gpu",
@@ -129,9 +326,38 @@ function gpuChromeArgs() {
129
326
  "--ignore-gpu-blocklist"
130
327
  ];
131
328
  }
132
- function gpuChromeLaunchOptions(opts = {}) {
329
+ function gpuChromeLaunchOptions(opts = {}, runtime = currentRuntime()) {
133
330
  const callerArgs = Array.isArray(opts.args) ? opts.args : [];
331
+ const unsafe = callerArgs.find(isSoftwareRendererArgument);
332
+ if (unsafe !== void 0) {
333
+ throw new Error(
334
+ `[reap-browser] refused software-rendering Chrome launch argument ${String(unsafe)}`
335
+ );
336
+ }
337
+ if (opts.executablePath !== void 0) {
338
+ throw new Error(
339
+ "[reap-browser] executablePath bypasses Playwright Chrome for Testing and is not allowed"
340
+ );
341
+ }
342
+ if (opts.ignoreDefaultArgs === true) {
343
+ throw new Error(
344
+ "[reap-browser] ignoreDefaultArgs=true bypasses required Chrome launch defaults"
345
+ );
346
+ }
347
+ const managedGpuArgs = /* @__PURE__ */ new Set([
348
+ "--enable-gpu",
349
+ "--enable-gpu-rasterization",
350
+ "--enable-zero-copy",
351
+ "--ignore-gpu-blocklist"
352
+ ]);
353
+ const unmanagedCallerArgs = callerArgs.filter((arg) => {
354
+ const value = String(arg);
355
+ return !managedGpuArgs.has(value) && !value.startsWith("--use-angle=");
356
+ });
134
357
  const callerIgnored = Array.isArray(opts.ignoreDefaultArgs) ? opts.ignoreDefaultArgs : [];
358
+ const ignoredDefaults = [
359
+ .../* @__PURE__ */ new Set([...callerIgnored, "--disable-gpu", "--enable-unsafe-swiftshader"])
360
+ ];
135
361
  return {
136
362
  ...opts,
137
363
  // playwright's `chromium` channel is the full Google Chrome for Testing
@@ -139,22 +365,19 @@ function gpuChromeLaunchOptions(opts = {}) {
139
365
  // com.google.chrome.for.testing, so automation cannot become the target of
140
366
  // Dock open/quit events intended for the user's com.google.Chrome browser.
141
367
  channel: "chromium",
142
- args: [...gpuChromeArgs(), ...callerArgs],
143
- ignoreDefaultArgs: [...callerIgnored, "--enable-unsafe-swiftshader"]
368
+ args: [...unmanagedCallerArgs, ...gpuChromeArgs(runtime.platform)],
369
+ ignoreDefaultArgs: ignoredDefaults
144
370
  };
145
371
  }
146
- function preferGpuChrome(launch, opts) {
372
+ function preferGpuChrome(launch, opts, rendererTarget) {
147
373
  return async () => {
148
- const allowSoftwareFallback = process.env.CI === "true" || process.env.CI === "1" || process.env.GITHUB_ACTIONS === "true";
374
+ const runtime = currentRuntime();
375
+ const allowSoftwareFallback = softwareChromeAllowed(runtime);
376
+ const gpuOptions = gpuChromeLaunchOptions(opts, runtime);
149
377
  if (!gpuChromeMissing) {
378
+ let browser;
150
379
  try {
151
- const callerArgs = Array.isArray(opts.args) ? opts.args : [];
152
- if (!allowSoftwareFallback && callerArgs.some(
153
- (arg) => arg === "--enable-unsafe-swiftshader" || /^--use-angle=(?:swiftshader|swiftshader-webgl)$/i.test(String(arg))
154
- )) {
155
- throw new Error("[reap-browser] refused local SwiftShader launch arguments");
156
- }
157
- return await launch(gpuChromeLaunchOptions(opts));
380
+ browser = await launch(gpuOptions);
158
381
  } catch (err) {
159
382
  const msg = err instanceof Error ? err.message : String(err);
160
383
  const missing = /not found at|not installed|Chromium distribution|Executable doesn't exist|No such file/i.test(
@@ -169,15 +392,29 @@ function preferGpuChrome(launch, opts) {
169
392
  }
170
393
  gpuChromeMissing = true;
171
394
  console.warn(
172
- "[reap-browser] CI Chrome for Testing not found, falling back to chromium_headless_shell software rendering."
395
+ "[reap-browser] GitHub Actions Linux Chrome for Testing not found, falling back to chromium_headless_shell software rendering."
173
396
  );
397
+ return launch(softwareFallbackChromeLaunchOptions(opts));
174
398
  }
399
+ try {
400
+ await validateLaunchedGpuBrowser(browser, runtime, rendererTarget);
401
+ } catch (err) {
402
+ await rejectLaunchedBrowser(browser);
403
+ throw err;
404
+ }
405
+ return browser;
175
406
  }
176
- return launch(opts);
407
+ return launch(softwareFallbackChromeLaunchOptions(opts));
177
408
  };
178
409
  }
179
- async function launchReapedChromeScoped(launch, opts = {}) {
180
- return launchReapedScoped(preferGpuChrome(launch, opts));
410
+ function softwareFallbackChromeLaunchOptions(opts) {
411
+ const fallback = { ...opts };
412
+ delete fallback.channel;
413
+ delete fallback.executablePath;
414
+ return fallback;
415
+ }
416
+ async function launchReapedChromeScoped(launch, opts = {}, rendererTarget) {
417
+ return launchReapedScoped(preferGpuChrome(launch, opts, rendererTarget));
181
418
  }
182
419
 
183
420
  // detox/gestures.ts
@@ -324,36 +561,36 @@ async function readSootsimPageMetrics(page) {
324
561
  async function readSootsimInteractiveViewport(page) {
325
562
  return (await readSootsimPageMetrics(page)).interactiveWindow;
326
563
  }
327
- async function sootsimToPage(page, sootX, sootY) {
564
+ async function sootsimToPage(page, sootsimX, sootsimY) {
328
565
  const metrics = await readSootsimPageMetrics(page);
329
566
  const scaleX = metrics.canvas.width / metrics.window.width;
330
567
  const scaleY = metrics.canvas.height / metrics.window.height;
331
568
  return {
332
- x: metrics.canvas.left + sootX * scaleX,
333
- y: metrics.canvas.top + sootY * scaleY
569
+ x: metrics.canvas.left + sootsimX * scaleX,
570
+ y: metrics.canvas.top + sootsimY * scaleY
334
571
  };
335
572
  }
336
573
  async function dragScrollNode(page, node, pixels, direction) {
337
- const startSootX = node.absolutePosition.x + node.layout.width / 2;
338
- const startSootY = node.absolutePosition.y + node.layout.height / 2;
339
- let endSootX = startSootX;
340
- let endSootY = startSootY;
574
+ const startSootSimX = node.absolutePosition.x + node.layout.width / 2;
575
+ const startSootSimY = node.absolutePosition.y + node.layout.height / 2;
576
+ let endSootSimX = startSootSimX;
577
+ let endSootSimY = startSootSimY;
341
578
  switch (direction) {
342
579
  case "down":
343
- endSootY -= pixels;
580
+ endSootSimY -= pixels;
344
581
  break;
345
582
  case "up":
346
- endSootY += pixels;
583
+ endSootSimY += pixels;
347
584
  break;
348
585
  case "left":
349
- endSootX += pixels;
586
+ endSootSimX += pixels;
350
587
  break;
351
588
  case "right":
352
- endSootX -= pixels;
589
+ endSootSimX -= pixels;
353
590
  break;
354
591
  }
355
- const startPage = await sootsimToPage(page, startSootX, startSootY);
356
- const endPage = await sootsimToPage(page, endSootX, endSootY);
592
+ const startPage = await sootsimToPage(page, startSootSimX, startSootSimY);
593
+ const endPage = await sootsimToPage(page, endSootSimX, endSootSimY);
357
594
  const steps = Math.max(16, Math.ceil(pixels / 8));
358
595
  await page.mouse.move(startPage.x, startPage.y);
359
596
  await page.mouse.down();
@@ -519,11 +756,11 @@ function describeNodeVisibility(node, viewport) {
519
756
  });
520
757
  }
521
758
  function createExpect(findNode, getPage2) {
522
- return function sootExpect2(el) {
523
- return new SootExpectation(el._matcher, findNode, getPage2, false);
759
+ return function sootsimExpect2(el) {
760
+ return new SootSimExpectation(el._matcher, findNode, getPage2, false);
524
761
  };
525
762
  }
526
- var SootExpectation = class _SootExpectation {
763
+ var SootSimExpectation = class _SootSimExpectation {
527
764
  matcher;
528
765
  findNode;
529
766
  getPage;
@@ -535,7 +772,12 @@ var SootExpectation = class _SootExpectation {
535
772
  this.negated = negated;
536
773
  }
537
774
  get not() {
538
- return new _SootExpectation(this.matcher, this.findNode, this.getPage, !this.negated);
775
+ return new _SootSimExpectation(
776
+ this.matcher,
777
+ this.findNode,
778
+ this.getPage,
779
+ !this.negated
780
+ );
539
781
  }
540
782
  async toExist() {
541
783
  const node = await this.findNode(this.matcher);
@@ -700,11 +942,11 @@ var SootExpectation = class _SootExpectation {
700
942
  }
701
943
  };
702
944
  function createWaitFor(findNode, getPage2) {
703
- return function sootWaitFor2(el) {
704
- return new SootWaitForChain(el._matcher, findNode, getPage2);
945
+ return function sootsimWaitFor2(el) {
946
+ return new SootSimWaitForChain(el._matcher, findNode, getPage2);
705
947
  };
706
948
  }
707
- var SootWaitForChain = class {
949
+ var SootSimWaitForChain = class {
708
950
  matcher;
709
951
  findNode;
710
952
  getPage;
@@ -727,7 +969,7 @@ var SootWaitForChain = class {
727
969
  throw new Error(`waitFor toExist failed: ${JSON.stringify(this.matcher)}`);
728
970
  }
729
971
  };
730
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage);
972
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage);
731
973
  }
732
974
  toBeVisible() {
733
975
  this.assertionFn = async () => {
@@ -741,7 +983,7 @@ var SootWaitForChain = class {
741
983
  );
742
984
  }
743
985
  };
744
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage);
986
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage);
745
987
  }
746
988
  toBeNotVisible() {
747
989
  this.assertionFn = async () => {
@@ -751,7 +993,7 @@ var SootWaitForChain = class {
751
993
  throw new Error(`waitFor toBeNotVisible failed: ${JSON.stringify(this.matcher)}`);
752
994
  }
753
995
  };
754
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage);
996
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage);
755
997
  }
756
998
  toHaveText(expectedText) {
757
999
  this.assertionFn = async () => {
@@ -768,7 +1010,7 @@ var SootWaitForChain = class {
768
1010
  );
769
1011
  }
770
1012
  };
771
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage);
1013
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage);
772
1014
  }
773
1015
  toHaveValue(value) {
774
1016
  this.assertionFn = async () => {
@@ -779,10 +1021,10 @@ var SootWaitForChain = class {
779
1021
  throw new Error(`waitFor toHaveValue: expected "${value}" in "${actual}"`);
780
1022
  }
781
1023
  };
782
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage);
1024
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage);
783
1025
  }
784
1026
  };
785
- var SootWaitForAction = class {
1027
+ var SootSimWaitForAction = class {
786
1028
  assertionFn;
787
1029
  findNode;
788
1030
  getPage;
@@ -816,7 +1058,7 @@ var SootWaitForAction = class {
816
1058
  }
817
1059
  }
818
1060
  whileElement(matcher) {
819
- return new SootWaitForScrollAction(
1061
+ return new SootSimWaitForScrollAction(
820
1062
  this.assertionFn,
821
1063
  this.findNode,
822
1064
  this.getPage,
@@ -824,7 +1066,7 @@ var SootWaitForAction = class {
824
1066
  );
825
1067
  }
826
1068
  };
827
- var SootWaitForScrollAction = class {
1069
+ var SootSimWaitForScrollAction = class {
828
1070
  assertionFn;
829
1071
  findNode;
830
1072
  getPage;
@@ -1385,7 +1627,7 @@ async function dispatchSootsimEventInPage(type, detail) {
1385
1627
  { detail, type }
1386
1628
  );
1387
1629
  }
1388
- function createSootElement(matcher) {
1630
+ function createSootSimElement(matcher) {
1389
1631
  const el = {
1390
1632
  _matcher: matcher,
1391
1633
  async tap(point) {
@@ -1394,8 +1636,8 @@ function createSootElement(matcher) {
1394
1636
  const node = await findNodeByMatcher(matcher);
1395
1637
  if (!node) throw new Error(`element not found for tap: ${JSON.stringify(matcher)}`);
1396
1638
  const defaultPoint = point ? null : await getDefaultTapPoint(page, node);
1397
- const targetSootX = typeof point?.x === "number" ? node.absolutePosition.x + point.x : defaultPoint?.x ?? node.absolutePosition.x + node.layout.width / 2;
1398
- const targetSootY = typeof point?.y === "number" ? node.absolutePosition.y + point.y : defaultPoint?.y ?? node.absolutePosition.y + node.layout.height / 2;
1639
+ const targetSootSimX = typeof point?.x === "number" ? node.absolutePosition.x + point.x : defaultPoint?.x ?? node.absolutePosition.x + node.layout.width / 2;
1640
+ const targetSootSimY = typeof point?.y === "number" ? node.absolutePosition.y + point.y : defaultPoint?.y ?? node.absolutePosition.y + node.layout.height / 2;
1399
1641
  await page.evaluate(
1400
1642
  async ({ x, y }) => {
1401
1643
  const interactTap = window.SootSim?.bridges?.interact?.tap;
@@ -1409,7 +1651,7 @@ function createSootElement(matcher) {
1409
1651
  }
1410
1652
  await tap(x, y);
1411
1653
  },
1412
- { x: targetSootX, y: targetSootY }
1654
+ { x: targetSootSimX, y: targetSootSimY }
1413
1655
  );
1414
1656
  await page.waitForTimeout(50);
1415
1657
  if (_synchronizationEnabled) {
@@ -1465,12 +1707,12 @@ function createSootElement(matcher) {
1465
1707
  throw new Error(
1466
1708
  `target element not found: ${JSON.stringify(targetElement._matcher)}`
1467
1709
  );
1468
- const startSootX = srcNode.absolutePosition.x + srcNode.layout.width * normalizedStartX;
1469
- const startSootY = srcNode.absolutePosition.y + srcNode.layout.height * normalizedStartY;
1470
- const endSootX = tgtNode.absolutePosition.x + tgtNode.layout.width * normalizedEndX;
1471
- const endSootY = tgtNode.absolutePosition.y + tgtNode.layout.height * normalizedEndY;
1472
- const startPage = await sootsimToPage(page, startSootX, startSootY);
1473
- const endPage = await sootsimToPage(page, endSootX, endSootY);
1710
+ const startSootSimX = srcNode.absolutePosition.x + srcNode.layout.width * normalizedStartX;
1711
+ const startSootSimY = srcNode.absolutePosition.y + srcNode.layout.height * normalizedStartY;
1712
+ const endSootSimX = tgtNode.absolutePosition.x + tgtNode.layout.width * normalizedEndX;
1713
+ const endSootSimY = tgtNode.absolutePosition.y + tgtNode.layout.height * normalizedEndY;
1714
+ const startPage = await sootsimToPage(page, startSootSimX, startSootSimY);
1715
+ const endPage = await sootsimToPage(page, endSootSimX, endSootSimY);
1474
1716
  const steps = speed === "slow" ? 20 : 10;
1475
1717
  await page.mouse.move(startPage.x, startPage.y);
1476
1718
  await page.mouse.down();
@@ -1568,23 +1810,23 @@ function createSootElement(matcher) {
1568
1810
  throw new Error(`element not found for swipe: ${JSON.stringify(matcher)}`);
1569
1811
  const page = getPage();
1570
1812
  const { absolutePosition, layout } = node;
1571
- const startSootX = absolutePosition.x + layout.width * 0.5;
1572
- const startSootY = absolutePosition.y + layout.height * 0.5;
1573
- let endSootX = startSootX;
1574
- let endSootY = startSootY;
1813
+ const startSootSimX = absolutePosition.x + layout.width * 0.5;
1814
+ const startSootSimY = absolutePosition.y + layout.height * 0.5;
1815
+ let endSootSimX = startSootSimX;
1816
+ let endSootSimY = startSootSimY;
1575
1817
  const dist = direction === "up" || direction === "down" ? layout.height * percentage : layout.width * percentage;
1576
1818
  switch (direction) {
1577
1819
  case "up":
1578
- endSootY -= dist;
1820
+ endSootSimY -= dist;
1579
1821
  break;
1580
1822
  case "down":
1581
- endSootY += dist;
1823
+ endSootSimY += dist;
1582
1824
  break;
1583
1825
  case "left":
1584
- endSootX -= dist;
1826
+ endSootSimX -= dist;
1585
1827
  break;
1586
1828
  case "right":
1587
- endSootX += dist;
1829
+ endSootSimX += dist;
1588
1830
  break;
1589
1831
  }
1590
1832
  const steps = speed === "slow" ? 20 : speed === "fast" ? 8 : 12;
@@ -1598,10 +1840,10 @@ function createSootElement(matcher) {
1598
1840
  await drag(fromX, fromY, toX, toY, steps2, stepMs);
1599
1841
  },
1600
1842
  {
1601
- fromX: startSootX,
1602
- fromY: startSootY,
1603
- toX: endSootX,
1604
- toY: endSootY,
1843
+ fromX: startSootSimX,
1844
+ fromY: startSootSimY,
1845
+ toX: endSootSimX,
1846
+ toY: endSootSimY,
1605
1847
  steps,
1606
1848
  stepMs: stepDelay
1607
1849
  }
@@ -1695,7 +1937,7 @@ function createSootElement(matcher) {
1695
1937
  return el;
1696
1938
  }
1697
1939
  function element(matcher) {
1698
- return createSootElement(matcher);
1940
+ return createSootSimElement(matcher);
1699
1941
  }
1700
1942
  var device = {
1701
1943
  _currentUrl: "",
@@ -2061,8 +2303,8 @@ var device = {
2061
2303
  return getPage();
2062
2304
  }
2063
2305
  };
2064
- var sootExpect = createExpect(findNodeByMatcher, getPage);
2065
- var sootWaitFor = createWaitFor(findNodeByMatcher, getPage);
2306
+ var sootsimExpect = createExpect(findNodeByMatcher, getPage);
2307
+ var sootsimWaitFor = createWaitFor(findNodeByMatcher, getPage);
2066
2308
  async function cleanup() {
2067
2309
  await closeBrowser();
2068
2310
  }
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.227 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.229 | (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 __defProp = Object.defineProperty;
@@ -55,7 +55,7 @@ function applyPreviewProdBundle(parsed) {
55
55
  function normalizeNativeDevBundleUrl(bundleUrl) {
56
56
  try {
57
57
  const isAbsolute = isAbsoluteHttpUrl(bundleUrl);
58
- const parsed = new URL(bundleUrl, "http://soot.local");
58
+ const parsed = new URL(bundleUrl, "http://sootsim.local");
59
59
  parsed.pathname = parsed.pathname.replace(/\.\.bundle$/, ".bundle");
60
60
  if (!isNativeDevBundlePath(parsed.pathname)) return bundleUrl;
61
61
  parsed.searchParams.delete("transform.bytecode");
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.227 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.229 | (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;