sootsim 0.1.84 → 0.1.86

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 (153) hide show
  1. package/detox/element-types.ts +36 -0
  2. package/detox/expectations.ts +1 -1
  3. package/detox/index.ts +2 -35
  4. package/dist-cli/bin.js +3 -3
  5. package/dist-cli/chunks/{agent-2CWD6W6P.js → agent-S3WLX5Z4.js} +2 -2
  6. package/dist-cli/chunks/{agent-wrapper-5W3LOX6S.js → agent-wrapper-PFPEQTPG.js} +2 -2
  7. package/dist-cli/chunks/{assert-ZOMAMKRT.js → assert-RQD66YGE.js} +2 -2
  8. package/dist-cli/chunks/auto-bootstrap-PWF7OYCV.js +2 -0
  9. package/dist-cli/chunks/beta-2HH7F2DQ.js +2 -0
  10. package/dist-cli/chunks/{chunk-D4HUVLZR.js → chunk-2IYMBWHL.js} +1 -1
  11. package/dist-cli/chunks/{chunk-DUUSJDES.js → chunk-3GCSX5H5.js} +1 -1
  12. package/dist-cli/chunks/{chunk-EQCKGC4B.js → chunk-3TNIXR6J.js} +1 -1
  13. package/dist-cli/chunks/{chunk-4OWVPRZV.js → chunk-535UNERF.js} +2 -2
  14. package/dist-cli/chunks/{chunk-4K7BH2D4.js → chunk-5DFVKWYQ.js} +3 -3
  15. package/dist-cli/chunks/{chunk-AJVTY6KY.js → chunk-5NW6W7YF.js} +1 -1
  16. package/dist-cli/chunks/{chunk-XQ2OBHBE.js → chunk-5ZYANOOI.js} +2 -2
  17. package/dist-cli/chunks/{chunk-ELJLF4SG.js → chunk-6A7IWFXR.js} +63 -62
  18. package/dist-cli/chunks/{chunk-73UZXB4B.js → chunk-6LG7WJMD.js} +2 -2
  19. package/dist-cli/chunks/{chunk-OOOR7NT2.js → chunk-7YZHI7V6.js} +1 -1
  20. package/dist-cli/chunks/{chunk-TL7SIZ7S.js → chunk-CMAANHYQ.js} +1 -1
  21. package/dist-cli/chunks/{chunk-7NWNTUJF.js → chunk-CQAQTU5K.js} +1 -1
  22. package/dist-cli/chunks/{chunk-C3DPQZ4J.js → chunk-CSJS4MRN.js} +2 -2
  23. package/dist-cli/chunks/{chunk-QMSJR5R2.js → chunk-DKW7Q4F3.js} +2 -2
  24. package/dist-cli/chunks/{chunk-EQ7TFQ2F.js → chunk-EDWDFOPL.js} +1 -1
  25. package/dist-cli/chunks/{chunk-WNVNU2OW.js → chunk-FF5KD3BS.js} +2 -2
  26. package/dist-cli/chunks/{chunk-PPKKA5VW.js → chunk-FO52BFW4.js} +2 -2
  27. package/dist-cli/chunks/chunk-I3JMONYJ.js +2 -0
  28. package/dist-cli/chunks/{chunk-7YHDJLO2.js → chunk-JB467MUR.js} +45 -45
  29. package/dist-cli/chunks/{chunk-HYPJW65U.js → chunk-JBYW57OA.js} +2 -2
  30. package/dist-cli/chunks/{chunk-SQX5CAYG.js → chunk-JITAVV2G.js} +1 -1
  31. package/dist-cli/chunks/{chunk-V2GQ4WXJ.js → chunk-JMILXXI4.js} +2 -2
  32. package/dist-cli/chunks/chunk-KGYC4SZA.js +2 -0
  33. package/dist-cli/chunks/{chunk-BKBL6K2G.js → chunk-KQYOS5SM.js} +1 -1
  34. package/dist-cli/chunks/{chunk-VH7F45CN.js → chunk-M6GOCS27.js} +1 -1
  35. package/dist-cli/chunks/{chunk-3HXQ7MJK.js → chunk-NBBH2PVW.js} +2 -2
  36. package/dist-cli/chunks/chunk-NIRJVBXJ.js +1 -0
  37. package/dist-cli/chunks/{chunk-SQZAC7C4.js → chunk-NLH7FNSG.js} +1 -1
  38. package/dist-cli/chunks/{chunk-RIXUH3NK.js → chunk-OBHJKTWA.js} +2 -2
  39. package/dist-cli/chunks/{chunk-KU6MSPAH.js → chunk-OCTDP37S.js} +2 -2
  40. package/dist-cli/chunks/{chunk-SFGUPL2X.js → chunk-P7IKKZTG.js} +2 -2
  41. package/dist-cli/chunks/{chunk-5XCXOLG2.js → chunk-PVMX5UNR.js} +2 -2
  42. package/dist-cli/chunks/chunk-QUYC7CVV.js +1 -0
  43. package/dist-cli/chunks/{chunk-YCIA4BHJ.js → chunk-STHMWSVN.js} +2 -2
  44. package/dist-cli/chunks/{chunk-AWSQUOAS.js → chunk-TSNBQ4ZV.js} +10 -10
  45. package/dist-cli/chunks/{chunk-BCBNVJVG.js → chunk-UWSP2AT7.js} +1 -1
  46. package/dist-cli/chunks/{chunk-RF4R2U46.js → chunk-V7CFSKMC.js} +2 -2
  47. package/dist-cli/chunks/{chunk-TK3OJSEO.js → chunk-VFGAEMSI.js} +2 -2
  48. package/dist-cli/chunks/{chunk-4OPRODFA.js → chunk-VUYCS6QI.js} +2 -2
  49. package/dist-cli/chunks/chunk-XB4QIINM.js +24 -0
  50. package/dist-cli/chunks/{chunk-P7WDNKOS.js → chunk-XFJYEKYK.js} +3 -3
  51. package/dist-cli/chunks/{chunk-SV7FOGJ3.js → chunk-XZE53P4L.js} +2 -2
  52. package/dist-cli/chunks/chunk-Y7BXSTVX.js +1 -0
  53. package/dist-cli/chunks/cli-version-TGWWTYQX.js +2 -0
  54. package/dist-cli/chunks/{compat-FWSEEGEH.js → compat-I2U3P4KP.js} +3 -3
  55. package/dist-cli/chunks/{config-CYI2WAGP.js → config-S73CCGP5.js} +2 -2
  56. package/dist-cli/chunks/{control-UXY7YQVX.js → control-QR6MY7RA.js} +2 -2
  57. package/dist-cli/chunks/{cpu-profile-IKAE3KTY.js → cpu-profile-RFYCTVAF.js} +2 -2
  58. package/dist-cli/chunks/{daemon-ZUMF53YB.js → daemon-D5MV2B22.js} +2 -2
  59. package/dist-cli/chunks/{debug-P6KULKKS.js → debug-ZYEI75AG.js} +3 -3
  60. package/dist-cli/chunks/{detox-SPWAZCYG.js → detox-J5IH52RV.js} +2 -2
  61. package/dist-cli/chunks/{device-JWEPK6I2.js → device-NOBLSUOD.js} +2 -2
  62. package/dist-cli/chunks/{diagnose-IZODTXV2.js → diagnose-B6J5ZUHV.js} +2 -2
  63. package/dist-cli/chunks/drivers-RRHVOU6S.js +2 -0
  64. package/dist-cli/chunks/{electron-R5GP6RVB.js → electron-PSX4KDCC.js} +3 -3
  65. package/dist-cli/chunks/flow-FWNVFKMP.js +2 -0
  66. package/dist-cli/chunks/{hints-DYDNYX7N.js → hints-ZE4I3YO3.js} +2 -2
  67. package/dist-cli/chunks/{home-paths-GLMX5OKL.js → home-paths-N76MJE3D.js} +2 -2
  68. package/dist-cli/chunks/{inspect-FJOPCTY2.js → inspect-V2TXTDOG.js} +3 -3
  69. package/dist-cli/chunks/install-4PINRR2O.js +2 -0
  70. package/dist-cli/chunks/{install-desktop-YPJZMZM5.js → install-desktop-6ZRTRRCU.js} +3 -3
  71. package/dist-cli/chunks/{keys-GSYPHWNY.js → keys-L2RN4URM.js} +2 -2
  72. package/dist-cli/chunks/{launch-4G2PKW5X.js → launch-BJGXPNZR.js} +3 -3
  73. package/dist-cli/chunks/{login-KJQGHA64.js → login-HYNEMAYR.js} +4 -4
  74. package/dist-cli/chunks/{logout-XM2SYH5C.js → logout-AO4YS27T.js} +2 -2
  75. package/dist-cli/chunks/{maestro-EOWGI7DG.js → maestro-PRACYFKV.js} +2 -2
  76. package/dist-cli/chunks/{preview-F73TKK37.js → preview-ZTANXVEK.js} +2 -2
  77. package/dist-cli/chunks/{profile-22FDKBUO.js → profile-FNMAGUDB.js} +2 -2
  78. package/dist-cli/chunks/{react-5L6VPFUP.js → react-6ZV2FQIM.js} +2 -2
  79. package/dist-cli/chunks/{record-JZXCQ4IN.js → record-MLFVJZ6Y.js} +2 -2
  80. package/dist-cli/chunks/runtime-5762IE56.js +2 -0
  81. package/dist-cli/chunks/{runtime-delivery-LXUM3R4A.js → runtime-delivery-ATYW2SQR.js} +2 -2
  82. package/dist-cli/chunks/{screenshot-HDRRG33Q.js → screenshot-UOMYMFZ4.js} +2 -2
  83. package/dist-cli/chunks/{screenshot-mode-WY63LZIX.js → screenshot-mode-MWSVD4YG.js} +2 -2
  84. package/dist-cli/chunks/{screenshots-MPV2ENL5.js → screenshots-GSA3VCWB.js} +2 -2
  85. package/dist-cli/chunks/server-YPFC6POG.js +40 -0
  86. package/dist-cli/chunks/setup-repo-QBQ4VWFO.js +2 -0
  87. package/dist-cli/chunks/{skills-BQ73YOBF.js → skills-YE5OPWMQ.js} +2 -2
  88. package/dist-cli/chunks/{start-2WU4W6ZU.js → start-BSSQ5U2V.js} +4 -4
  89. package/dist-cli/chunks/store-EG4SONAH.js +2 -0
  90. package/dist-cli/chunks/telemetry-XXN4LRDS.js +2 -0
  91. package/dist-cli/chunks/{test-OVO4CQTG.js → test-5JMLBH2O.js} +3 -3
  92. package/dist-cli/chunks/{three-mode-BKM3KFM7.js → three-mode-TRBWZJQY.js} +2 -2
  93. package/dist-cli/chunks/{timeline-MDXGEDQL.js → timeline-YMZPIEB4.js} +2 -2
  94. package/dist-cli/chunks/{upgrade-JGQABWVF.js → upgrade-JLAS7FIF.js} +2 -2
  95. package/dist-cli/chunks/upload-K6UNCFQH.js +2 -0
  96. package/dist-cli/chunks/{web-WYFAYQ72.js → web-D6S5UXOO.js} +2 -2
  97. package/dist-cli/chunks/{what-happened-PZW2KW6A.js → what-happened-65NXWU2S.js} +2 -2
  98. package/dist-cli/chunks/{whoami-7ATWJQS6.js → whoami-6BSB6FQC.js} +2 -2
  99. package/dist-lib/agent-daemon-client.cjs +1 -1
  100. package/dist-lib/agent-events.cjs +1 -1
  101. package/dist-lib/agent-sessions.cjs +1 -1
  102. package/dist-lib/attached-projects.cjs +1 -1
  103. package/dist-lib/auth/shared-session.cjs +1 -1
  104. package/dist-lib/backend-origin.cjs +1 -1
  105. package/dist-lib/beta.cjs +1 -1
  106. package/dist-lib/beta.mjs +15 -0
  107. package/dist-lib/bridge-constants.cjs +1 -1
  108. package/dist-lib/cli-constants.cjs +1 -1
  109. package/dist-lib/config.cjs +1 -1
  110. package/dist-lib/detox/index.cjs +1 -1
  111. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  112. package/dist-lib/home-paths.cjs +1 -1
  113. package/dist-lib/host/bridge-host.cjs +244 -35
  114. package/dist-lib/host/fetch-proxy-handler.cjs +24 -4
  115. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  116. package/dist-lib/host/fetch-proxy-overrides.mjs +33 -0
  117. package/dist-lib/host/websocket-proxy.cjs +207 -0
  118. package/dist-lib/index.cjs +136 -138
  119. package/dist-lib/metro.cjs +31 -26
  120. package/dist-lib/profiles.cjs +1 -1
  121. package/dist-lib/render-mode.cjs +1 -1
  122. package/dist-lib/scripts/demo-app-registry.cjs +14 -3
  123. package/dist-lib/scripts/dev-server-scanner.cjs +14 -3
  124. package/dist-lib/skills.cjs +9737 -76
  125. package/dist-lib/vite.cjs +129 -39
  126. package/package.json +8 -6
  127. package/scripts/demo-app-registry.ts +17 -1
  128. package/src/host/bridge-host.ts +8 -1
  129. package/src/host/fetch-proxy-handler.ts +26 -3
  130. package/src/host/websocket-proxy.ts +201 -0
  131. package/src/metro-plugin.ts +9 -77
  132. package/src/runtime-assets.ts +84 -0
  133. package/src/skills/builtin/compat-check.ts +1 -1
  134. package/src/vite-plugin-one.ts +60 -58
  135. package/dist-cli/chunks/auto-bootstrap-NYYSMTIM.js +0 -2
  136. package/dist-cli/chunks/beta-4K2SQACK.js +0 -2
  137. package/dist-cli/chunks/chunk-67ZZ2CM5.js +0 -1
  138. package/dist-cli/chunks/chunk-D3ZSBIIY.js +0 -2
  139. package/dist-cli/chunks/chunk-FUCGLWNN.js +0 -1
  140. package/dist-cli/chunks/chunk-IILJQCZA.js +0 -2
  141. package/dist-cli/chunks/chunk-PS2G44GT.js +0 -24
  142. package/dist-cli/chunks/chunk-ZSMMJMPA.js +0 -1
  143. package/dist-cli/chunks/cli-version-QB4VH24H.js +0 -2
  144. package/dist-cli/chunks/drivers-MK6WJKBC.js +0 -2
  145. package/dist-cli/chunks/flow-6O4GEOPJ.js +0 -2
  146. package/dist-cli/chunks/install-A3TUGGHN.js +0 -2
  147. package/dist-cli/chunks/runtime-EEBX7CFV.js +0 -2
  148. package/dist-cli/chunks/server-5LBMCJ3G.js +0 -35
  149. package/dist-cli/chunks/setup-repo-SZSYNKNI.js +0 -2
  150. package/dist-cli/chunks/store-RE45SUBF.js +0 -2
  151. package/dist-cli/chunks/telemetry-DG6GJLCP.js +0 -2
  152. package/dist-cli/chunks/upload-UJNUA4ZV.js +0 -2
  153. package/dist-lib/vite-base.cjs +0 -6937
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.86 | (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;
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  // src/host/fetch-proxy-handler.ts
33
33
  var fetch_proxy_handler_exports = {};
34
34
  __export(fetch_proxy_handler_exports, {
35
+ buildAppApiProxyHeaders: () => buildAppApiProxyHeaders,
35
36
  buildFetchProxyHeaders: () => buildFetchProxyHeaders,
36
37
  handleAppApiRequest: () => handleAppApiRequest,
37
38
  handleFetchProxyRequest: () => handleFetchProxyRequest,
@@ -97,6 +98,14 @@ var FETCH_PROXY_CORS_HEADERS = {
97
98
  "access-control-expose-headers": "*",
98
99
  "access-control-max-age": "3600"
99
100
  };
101
+ var APP_API_HEADER_REWRITES = /* @__PURE__ */ new Set([
102
+ "host",
103
+ "origin",
104
+ "referer",
105
+ "sec-fetch-site",
106
+ "sec-fetch-mode",
107
+ "sec-fetch-dest"
108
+ ]);
100
109
  function applyFetchProxyCors(res) {
101
110
  for (const [key, value] of Object.entries(FETCH_PROXY_CORS_HEADERS)) {
102
111
  res.setHeader(key, value);
@@ -129,6 +138,18 @@ function buildFetchProxyHeaders(reqHeaders, targetUrl) {
129
138
  );
130
139
  return headers;
131
140
  }
141
+ function buildAppApiProxyHeaders(reqHeaders, targetUrl) {
142
+ const headers = {};
143
+ for (const [key, value] of Object.entries(reqHeaders)) {
144
+ if (!value) continue;
145
+ if (APP_API_HEADER_REWRITES.has(key.toLowerCase())) continue;
146
+ headers[key] = value;
147
+ }
148
+ headers.host = targetUrl.host;
149
+ headers.origin = targetUrl.origin;
150
+ headers.referer = `${targetUrl.origin}/`;
151
+ return headers;
152
+ }
132
153
  function isFetchProxyRequestUrl(rawUrl) {
133
154
  return rawUrl?.startsWith("/__fetch-proxy?") || rawUrl?.startsWith("/__proxy?") || false;
134
155
  }
@@ -239,9 +260,7 @@ function handleAppApiRequest(req, res) {
239
260
  return true;
240
261
  }
241
262
  const transport = targetUrl.protocol === "https:" ? import_https.default : import_http.default;
242
- const fwdHeaders = { ...req.headers };
243
- delete fwdHeaders.host;
244
- fwdHeaders.host = targetUrl.host;
263
+ const fwdHeaders = buildAppApiProxyHeaders(req.headers, targetUrl);
245
264
  const proxyReq = transport.request(
246
265
  {
247
266
  hostname: targetUrl.hostname,
@@ -273,6 +292,7 @@ function handleAppApiRequest(req, res) {
273
292
  }
274
293
  // Annotate the CommonJS export names for ESM import in node:
275
294
  0 && (module.exports = {
295
+ buildAppApiProxyHeaders,
276
296
  buildFetchProxyHeaders,
277
297
  handleAppApiRequest,
278
298
  handleFetchProxyRequest,
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.86 | (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;
@@ -0,0 +1,33 @@
1
+ /*! sootsim v0.1.86 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+
3
+ // src/host/fetch-proxy-overrides.ts
4
+ var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
5
+ var HOST_HEADER_OVERRIDES = [
6
+ {
7
+ hostSuffix: "uniswap.org",
8
+ headers: {
9
+ origin: "https://app.uniswap.org",
10
+ referer: "https://app.uniswap.org/"
11
+ }
12
+ }
13
+ ];
14
+ function headerOverridesFor(hostname) {
15
+ const host = hostname.toLowerCase();
16
+ for (const override of HOST_HEADER_OVERRIDES) {
17
+ if (host === override.hostSuffix || host.endsWith(`.${override.hostSuffix}`)) {
18
+ return override.headers;
19
+ }
20
+ }
21
+ return {};
22
+ }
23
+ function getFetchProxyTargetHeaders(targetUrl) {
24
+ return {
25
+ "accept-encoding": "identity",
26
+ "user-agent": FETCH_PROXY_BROWSER_USER_AGENT,
27
+ ...headerOverridesFor(targetUrl.hostname)
28
+ };
29
+ }
30
+ export {
31
+ FETCH_PROXY_BROWSER_USER_AGENT,
32
+ getFetchProxyTargetHeaders
33
+ };
@@ -0,0 +1,207 @@
1
+ /*! sootsim v0.1.86 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
+ "use strict";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/host/websocket-proxy.ts
23
+ var websocket_proxy_exports = {};
24
+ __export(websocket_proxy_exports, {
25
+ WEBSOCKET_PROXY_PATH: () => WEBSOCKET_PROXY_PATH,
26
+ handleWebSocketProxyUpgrade: () => handleWebSocketProxyUpgrade,
27
+ isWebSocketProxyRequestUrl: () => isWebSocketProxyRequestUrl
28
+ });
29
+ module.exports = __toCommonJS(websocket_proxy_exports);
30
+ var import_ws = require("ws");
31
+ var WEBSOCKET_PROXY_PATH = "/__websocket-proxy";
32
+ var STRIP_UPSTREAM_HEADERS = /* @__PURE__ */ new Set([
33
+ "host",
34
+ "connection",
35
+ "upgrade",
36
+ "transfer-encoding",
37
+ "content-length",
38
+ "sec-websocket-accept",
39
+ "sec-websocket-extensions",
40
+ "sec-websocket-key",
41
+ "sec-websocket-protocol",
42
+ "sec-websocket-version"
43
+ ]);
44
+ function rejectUpgrade(socket, status, message) {
45
+ try {
46
+ socket.write(
47
+ `HTTP/1.1 ${status} ${message}\r
48
+ Connection: close\r
49
+ Content-Type: text/plain\r
50
+ Content-Length: ${message.length}\r
51
+ \r
52
+ ${message}`
53
+ );
54
+ } catch {
55
+ }
56
+ socket.destroy();
57
+ }
58
+ function isSameOriginUpgrade(req) {
59
+ const origin = req.headers.origin;
60
+ const host = req.headers.host;
61
+ if (!origin || !host) return false;
62
+ try {
63
+ return new URL(origin).host === host;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+ function decodeProxyHeaders(encoded) {
69
+ if (!encoded) return {};
70
+ const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
71
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
72
+ const parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
73
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
74
+ const headers = {};
75
+ for (const [key, value] of Object.entries(parsed)) {
76
+ if (value == null) continue;
77
+ if (STRIP_UPSTREAM_HEADERS.has(key.toLowerCase())) continue;
78
+ headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
79
+ }
80
+ return headers;
81
+ }
82
+ function getDefaultWebSocketOrigin(targetUrl) {
83
+ const origin = new URL(targetUrl.href);
84
+ origin.protocol = targetUrl.protocol === "wss:" ? "https:" : "http:";
85
+ return origin.origin;
86
+ }
87
+ function getRequestedProtocols(req) {
88
+ const header = req.headers["sec-websocket-protocol"];
89
+ const value = Array.isArray(header) ? header.join(",") : header || "";
90
+ return value.split(",").map((part) => part.trim()).filter(Boolean);
91
+ }
92
+ function safeClose(ws, code, reason) {
93
+ if (ws.readyState === import_ws.WebSocket.CLOSED || ws.readyState === import_ws.WebSocket.CLOSING) {
94
+ return;
95
+ }
96
+ try {
97
+ ws.close(code, reason);
98
+ } catch {
99
+ ws.terminate();
100
+ }
101
+ }
102
+ function connectProxyPair(clientWs, upstream) {
103
+ let closing = false;
104
+ const closeBoth = (source, target, code, reason) => {
105
+ if (closing) return;
106
+ closing = true;
107
+ safeClose(target, code, reason.toString());
108
+ if (source.readyState === import_ws.WebSocket.OPEN) {
109
+ safeClose(source, code, reason.toString());
110
+ }
111
+ };
112
+ clientWs.on("message", (data, isBinary) => {
113
+ if (upstream.readyState === import_ws.WebSocket.OPEN) {
114
+ upstream.send(data, { binary: isBinary });
115
+ }
116
+ });
117
+ upstream.on("message", (data, isBinary) => {
118
+ if (clientWs.readyState === import_ws.WebSocket.OPEN) {
119
+ clientWs.send(data, { binary: isBinary });
120
+ }
121
+ });
122
+ clientWs.on("close", (code, reason) => closeBoth(clientWs, upstream, code, reason));
123
+ upstream.on("close", (code, reason) => closeBoth(upstream, clientWs, code, reason));
124
+ clientWs.on("error", () => safeClose(upstream, 1011, "proxy client error"));
125
+ upstream.on("error", () => safeClose(clientWs, 1011, "upstream websocket error"));
126
+ }
127
+ function createUpstreamWebSocket(targetUrl, protocols, headers) {
128
+ const upstreamHeaders = { ...headers };
129
+ if (!Object.keys(upstreamHeaders).some((key) => key.toLowerCase() === "origin")) {
130
+ upstreamHeaders.origin = getDefaultWebSocketOrigin(targetUrl);
131
+ }
132
+ return new import_ws.WebSocket(targetUrl.href, protocols, {
133
+ headers: upstreamHeaders
134
+ });
135
+ }
136
+ function isWebSocketProxyRequestUrl(rawUrl) {
137
+ if (!rawUrl) return false;
138
+ try {
139
+ return new URL(rawUrl, "http://localhost").pathname === WEBSOCKET_PROXY_PATH;
140
+ } catch {
141
+ return false;
142
+ }
143
+ }
144
+ function handleWebSocketProxyUpgrade(req, socket, head) {
145
+ if (!isWebSocketProxyRequestUrl(req.url)) return false;
146
+ if (!isSameOriginUpgrade(req)) {
147
+ rejectUpgrade(socket, 403, "forbidden websocket proxy origin");
148
+ return true;
149
+ }
150
+ let targetUrl;
151
+ let headers;
152
+ try {
153
+ const requestUrl = new URL(req.url || "/", "http://localhost");
154
+ const target = requestUrl.searchParams.get("url");
155
+ if (!target) {
156
+ rejectUpgrade(socket, 400, "missing websocket proxy url");
157
+ return true;
158
+ }
159
+ targetUrl = new URL(target);
160
+ if (targetUrl.protocol !== "ws:" && targetUrl.protocol !== "wss:") {
161
+ rejectUpgrade(socket, 400, "invalid websocket proxy protocol");
162
+ return true;
163
+ }
164
+ headers = decodeProxyHeaders(requestUrl.searchParams.get("headers"));
165
+ } catch {
166
+ rejectUpgrade(socket, 400, "invalid websocket proxy request");
167
+ return true;
168
+ }
169
+ const protocols = getRequestedProtocols(req);
170
+ const upstream = createUpstreamWebSocket(targetUrl, protocols, headers);
171
+ let completed = false;
172
+ socket.once("close", () => {
173
+ if (!completed) upstream.terminate();
174
+ });
175
+ upstream.once("open", () => {
176
+ if (completed) return;
177
+ completed = true;
178
+ const selectedProtocol = upstream.protocol;
179
+ const proxyServer = new import_ws.WebSocketServer({
180
+ noServer: true,
181
+ clientTracking: false,
182
+ handleProtocols(requestedProtocols) {
183
+ return selectedProtocol || requestedProtocols.values().next().value || false;
184
+ }
185
+ });
186
+ proxyServer.handleUpgrade(req, socket, head, (clientWs) => {
187
+ connectProxyPair(clientWs, upstream);
188
+ });
189
+ });
190
+ upstream.once("error", () => {
191
+ if (completed) return;
192
+ completed = true;
193
+ rejectUpgrade(socket, 502, "upstream websocket error");
194
+ });
195
+ upstream.once("close", () => {
196
+ if (completed) return;
197
+ completed = true;
198
+ rejectUpgrade(socket, 502, "upstream websocket closed");
199
+ });
200
+ return true;
201
+ }
202
+ // Annotate the CommonJS export names for ESM import in node:
203
+ 0 && (module.exports = {
204
+ WEBSOCKET_PROXY_PATH,
205
+ handleWebSocketProxyUpgrade,
206
+ isWebSocketProxyRequestUrl
207
+ });
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.84 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.86 | (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;
@@ -40,11 +40,45 @@ __export(src_exports, {
40
40
  module.exports = __toCommonJS(src_exports);
41
41
 
42
42
  // src/vite-plugin-one.ts
43
+ var import_fs2 = __toESM(require("fs"), 1);
44
+ var import_path2 = __toESM(require("path"), 1);
45
+
46
+ // src/runtime-assets.ts
43
47
  var import_fs = __toESM(require("fs"), 1);
44
48
  var import_path = __toESM(require("path"), 1);
45
- var sootsimRoot = import_path.default.resolve(import_path.default.dirname(new URL(__sootsim_import_meta_url).pathname), "..");
46
- var distDir = import_path.default.join(sootsimRoot, "dist-plugin");
47
- var publicDir = import_path.default.join(sootsimRoot, "public");
49
+
50
+ // src/home-paths.ts
51
+ var import_node_fs = __toESM(require("node:fs"), 1);
52
+ var import_node_os = require("node:os");
53
+ var import_node_path = __toESM(require("node:path"), 1);
54
+ var SOOTSIM_HOME_ENV = "SOOTSIM_HOME";
55
+ var ACTIVE_RUNTIME_FILE = "active";
56
+ function sootsimHomeDir() {
57
+ const override = process.env[SOOTSIM_HOME_ENV];
58
+ if (override && override.length > 0) return import_node_path.default.resolve(override);
59
+ return import_node_path.default.join((0, import_node_os.homedir)(), ".sootsim");
60
+ }
61
+ function runtimesDir() {
62
+ return import_node_path.default.join(sootsimHomeDir(), "runtimes");
63
+ }
64
+ function runtimeDir(version) {
65
+ return import_node_path.default.join(runtimesDir(), version);
66
+ }
67
+ function activeRuntimeFile() {
68
+ return import_node_path.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
69
+ }
70
+ function readActiveRuntime() {
71
+ try {
72
+ const value = import_node_fs.default.readFileSync(activeRuntimeFile(), "utf8").trim();
73
+ return value.length > 0 ? value : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
79
+
80
+ // src/runtime-assets.ts
81
+ var SOOTSIM_RUNTIME_MISSING_MESSAGE = "[sootsim] no engine runtime installed \u2014 run `sootsim setup-repo` in your project, or `sootsim runtime install`";
48
82
  var MIME_TYPES = {
49
83
  ".js": "application/javascript",
50
84
  ".mjs": "application/javascript",
@@ -52,17 +86,64 @@ var MIME_TYPES = {
52
86
  ".html": "text/html",
53
87
  ".wasm": "application/wasm",
54
88
  ".json": "application/json",
89
+ ".jpg": "image/jpeg",
90
+ ".jpeg": "image/jpeg",
55
91
  ".png": "image/png",
56
92
  ".svg": "image/svg+xml",
93
+ ".webp": "image/webp",
94
+ ".glb": "model/gltf-binary",
57
95
  ".ttf": "font/ttf",
58
96
  ".otf": "font/otf",
59
97
  ".woff": "font/woff",
60
98
  ".woff2": "font/woff2",
61
99
  ".mp3": "audio/mpeg",
62
- ".wav": "audio/wav",
63
- ".jpg": "image/jpeg",
64
- ".webp": "image/webp"
100
+ ".wav": "audio/wav"
65
101
  };
102
+ var ROOT_RUNTIME_PATHS = [
103
+ "/assets/",
104
+ "/engine/",
105
+ "/engine-tenant/",
106
+ "/photos/",
107
+ "/three-mode/",
108
+ "/canvaskit.wasm",
109
+ "/fonts/",
110
+ "/icons/",
111
+ "/sounds/",
112
+ "/spike/",
113
+ "/test-wallpaper.jpg",
114
+ "/preview-sw.js"
115
+ ];
116
+ function resolveActiveRuntimeRoot() {
117
+ const active = readActiveRuntime();
118
+ if (!active) return null;
119
+ const dir = runtimeDir(active);
120
+ if (!import_fs.default.existsSync(import_path.default.join(dir, "index.html"))) return null;
121
+ return dir;
122
+ }
123
+ function isRootRuntimeAssetPath(pathname) {
124
+ return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p));
125
+ }
126
+ function resolveRuntimeFilePath(runtimeRoot, pathname) {
127
+ if (!pathname.startsWith("/")) return null;
128
+ if (pathname.includes("\0") || pathname.includes("\\")) return null;
129
+ for (const segment of pathname.split("/")) {
130
+ if (segment === "..") return null;
131
+ }
132
+ const fullPath = import_path.default.resolve(runtimeRoot, pathname.replace(/^\/+/, ""));
133
+ const rootWithSep = runtimeRoot.endsWith(import_path.default.sep) ? runtimeRoot : runtimeRoot + import_path.default.sep;
134
+ if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null;
135
+ if (!import_fs.default.existsSync(fullPath) || !import_fs.default.statSync(fullPath).isFile()) return null;
136
+ return fullPath;
137
+ }
138
+ function serveRuntimeFile(res, fullPath) {
139
+ const ext = import_path.default.extname(fullPath);
140
+ res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
141
+ res.setHeader("cache-control", "max-age=31536000,immutable");
142
+ import_fs.default.createReadStream(fullPath).pipe(res);
143
+ }
144
+
145
+ // src/vite-plugin-one.ts
146
+ var sootsimRoot = import_path2.default.resolve(import_path2.default.dirname(new URL(__sootsim_import_meta_url).pathname), "..");
66
147
  function sootsimPlugin(options = {}) {
67
148
  if (options.enabled === false) return [];
68
149
  const prefix2 = options.prefix || "/__soot";
@@ -89,14 +170,27 @@ function sootsimPlugin(options = {}) {
89
170
  });
90
171
  server.middlewares.use((req, res, next) => {
91
172
  const url = req.url || "";
92
- if (url === prefix2 || url === prefix2 + "/" || url.startsWith(prefix2 + "/?")) {
93
- const htmlPath = import_path.default.join(distDir, "index.html");
94
- if (!import_fs.default.existsSync(htmlPath)) {
173
+ const pathname = url.split("?")[0];
174
+ const runtimeRoot = resolveActiveRuntimeRoot();
175
+ if (!runtimeRoot) {
176
+ if (pathname === prefix2 || pathname === prefix2 + "/" || pathname.startsWith(prefix2 + "/")) {
95
177
  res.statusCode = 500;
96
- res.end("[sootsim] dist-plugin not built. run: bun scripts/build-plugin.ts");
178
+ res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE);
97
179
  return;
98
180
  }
99
- let html = import_fs.default.readFileSync(htmlPath, "utf8");
181
+ next();
182
+ return;
183
+ }
184
+ if (isRootRuntimeAssetPath(pathname)) {
185
+ const fullPath = resolveRuntimeFilePath(runtimeRoot, pathname);
186
+ if (fullPath) {
187
+ serveRuntimeFile(res, fullPath);
188
+ return;
189
+ }
190
+ }
191
+ if (pathname === prefix2 || pathname === prefix2 + "/") {
192
+ const htmlPath = import_path2.default.join(runtimeRoot, "index.html");
193
+ let html = import_fs2.default.readFileSync(htmlPath, "utf8");
100
194
  html = html.replace(
101
195
  "</head>",
102
196
  `<script>history.replaceState(null,'','${prefix2}/?bundle=${encodeURIComponent(bundleUrl)}')</script></head>`
@@ -105,31 +199,25 @@ function sootsimPlugin(options = {}) {
105
199
  res.end(html);
106
200
  return;
107
201
  }
108
- if (url.startsWith(prefix2 + "/")) {
109
- const filePath = url.slice(prefix2.length).split("?")[0];
110
- const fullPath = import_path.default.join(distDir, filePath);
111
- if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
112
- const ext = import_path.default.extname(fullPath);
113
- res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
114
- res.setHeader("cache-control", "max-age=31536000,immutable");
115
- import_fs.default.createReadStream(fullPath).pipe(res);
116
- return;
117
- }
118
- const publicPath = import_path.default.join(publicDir, filePath);
119
- if (import_fs.default.existsSync(publicPath) && import_fs.default.statSync(publicPath).isFile()) {
120
- const ext = import_path.default.extname(publicPath);
121
- res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
122
- import_fs.default.createReadStream(publicPath).pipe(res);
202
+ if (pathname.startsWith(prefix2 + "/")) {
203
+ const fullPath = resolveRuntimeFilePath(
204
+ runtimeRoot,
205
+ pathname.slice(prefix2.length)
206
+ );
207
+ if (fullPath) {
208
+ serveRuntimeFile(res, fullPath);
123
209
  return;
124
210
  }
125
- }
126
- const staticRoots = ["/canvaskit.wasm", "/fonts/", "/icons/", "/sounds/"];
127
- if (staticRoots.some((p) => url.startsWith(p))) {
128
- const fullPath = import_path.default.join(publicDir, url.split("?")[0]);
129
- if (import_fs.default.existsSync(fullPath) && import_fs.default.statSync(fullPath).isFile()) {
130
- const ext = import_path.default.extname(fullPath);
131
- res.setHeader("content-type", MIME_TYPES[ext] || "application/octet-stream");
132
- import_fs.default.createReadStream(fullPath).pipe(res);
211
+ const ext = import_path2.default.extname(pathname);
212
+ if (!ext) {
213
+ const htmlPath = import_path2.default.join(runtimeRoot, "index.html");
214
+ let html = import_fs2.default.readFileSync(htmlPath, "utf8");
215
+ html = html.replace(
216
+ "</head>",
217
+ `<script>history.replaceState(null,'','${prefix2}/?bundle=${encodeURIComponent(bundleUrl)}')</script></head>`
218
+ );
219
+ res.setHeader("content-type", "text/html");
220
+ res.end(html);
133
221
  return;
134
222
  }
135
223
  }
@@ -138,7 +226,9 @@ function sootsimPlugin(options = {}) {
138
226
  const port = server.config.server.port || 8081;
139
227
  const sootsimUrl = `http://localhost:${port}${prefix2}/`;
140
228
  console.log(`[sootsim] serving at ${sootsimUrl}`);
141
- openElectronApp(sootsimUrl);
229
+ if (options.open !== false) {
230
+ openElectronApp(sootsimUrl);
231
+ }
142
232
  }
143
233
  }
144
234
  ];
@@ -154,10 +244,10 @@ async function openElectronApp(sootsimUrl) {
154
244
  }
155
245
  const candidates = [
156
246
  "/Applications/sootsim.app",
157
- import_path.default.join(process.env.HOME || "", "Applications/sootsim.app"),
158
- import_path.default.join(sootsimRoot, "release/mac-arm64/sootsim.app")
247
+ import_path2.default.join(process.env.HOME || "", "Applications/sootsim.app"),
248
+ import_path2.default.join(sootsimRoot, "release/mac-arm64/sootsim.app")
159
249
  ];
160
- let appPath = candidates.find((p) => import_fs.default.existsSync(p));
250
+ let appPath = candidates.find((p) => import_fs2.default.existsSync(p));
161
251
  if (!appPath) {
162
252
  try {
163
253
  const found = execSync(
@@ -177,95 +267,9 @@ async function openElectronApp(sootsimUrl) {
177
267
  }
178
268
 
179
269
  // src/metro-plugin.ts
180
- var import_fs2 = __toESM(require("fs"), 1);
181
- var import_path2 = __toESM(require("path"), 1);
182
-
183
- // src/home-paths.ts
184
- var import_node_fs = __toESM(require("node:fs"), 1);
185
- var import_node_os = require("node:os");
186
- var import_node_path = __toESM(require("node:path"), 1);
187
- var SOOTSIM_HOME_ENV = "SOOTSIM_HOME";
188
- var ACTIVE_RUNTIME_FILE = "active";
189
- function sootsimHomeDir() {
190
- const override = process.env[SOOTSIM_HOME_ENV];
191
- if (override && override.length > 0) return import_node_path.default.resolve(override);
192
- return import_node_path.default.join((0, import_node_os.homedir)(), ".sootsim");
193
- }
194
- function runtimesDir() {
195
- return import_node_path.default.join(sootsimHomeDir(), "runtimes");
196
- }
197
- function runtimeDir(version) {
198
- return import_node_path.default.join(runtimesDir(), version);
199
- }
200
- function activeRuntimeFile() {
201
- return import_node_path.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
202
- }
203
- function readActiveRuntime() {
204
- try {
205
- const value = import_node_fs.default.readFileSync(activeRuntimeFile(), "utf8").trim();
206
- return value.length > 0 ? value : null;
207
- } catch {
208
- return null;
209
- }
210
- }
211
- var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
212
-
213
- // src/metro-plugin.ts
270
+ var import_fs3 = __toESM(require("fs"), 1);
271
+ var import_path3 = __toESM(require("path"), 1);
214
272
  var prefix = "/__soot";
215
- function resolveRuntimeRoot() {
216
- const active = readActiveRuntime();
217
- if (!active) return null;
218
- const dir = runtimeDir(active);
219
- if (!import_fs2.default.existsSync(import_path2.default.join(dir, "index.html"))) return null;
220
- return dir;
221
- }
222
- var RUNTIME_MISSING_MESSAGE = "[sootsim] no engine runtime installed \u2014 run `sootsim setup-repo` in your project, or `sootsim runtime install`";
223
- var MIME_TYPES2 = {
224
- ".js": "application/javascript",
225
- ".css": "text/css",
226
- ".html": "text/html",
227
- ".wasm": "application/wasm",
228
- ".json": "application/json",
229
- ".jpg": "image/jpeg",
230
- ".jpeg": "image/jpeg",
231
- ".png": "image/png",
232
- ".svg": "image/svg+xml",
233
- ".webp": "image/webp",
234
- ".glb": "model/gltf-binary",
235
- ".ttf": "font/ttf",
236
- ".otf": "font/otf",
237
- ".mp3": "audio/mpeg",
238
- ".wav": "audio/wav"
239
- };
240
- var ROOT_RUNTIME_PATHS = [
241
- "/assets/",
242
- "/engine/",
243
- "/engine-tenant/",
244
- "/photos/",
245
- "/three-mode/",
246
- "/canvaskit.wasm",
247
- "/fonts/",
248
- "/icons/",
249
- "/sounds/",
250
- "/spike/",
251
- "/test-wallpaper.jpg",
252
- "/preview-sw.js"
253
- ];
254
- function isRootRuntimeAssetPath(pathname) {
255
- return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p));
256
- }
257
- function resolveRuntimeFilePath(runtimeRoot, pathname) {
258
- if (!pathname.startsWith("/")) return null;
259
- if (pathname.includes("\0") || pathname.includes("\\")) return null;
260
- for (const segment of pathname.split("/")) {
261
- if (segment === "..") return null;
262
- }
263
- const fullPath = import_path2.default.resolve(runtimeRoot, pathname.replace(/^\/+/, ""));
264
- const rootWithSep = runtimeRoot.endsWith(import_path2.default.sep) ? runtimeRoot : runtimeRoot + import_path2.default.sep;
265
- if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null;
266
- if (!import_fs2.default.existsSync(fullPath) || !import_fs2.default.statSync(fullPath).isFile()) return null;
267
- return fullPath;
268
- }
269
273
  function withSootsim(config, options = {}) {
270
274
  if (options.enabled === false) return config;
271
275
  const bundleUrl = options.bundleUrl || "/index.bundle?platform=ios&dev=true&hot=true&minify=false";
@@ -289,11 +293,11 @@ function withSootsim(config, options = {}) {
289
293
  server.use((req, res, next) => {
290
294
  const url = req.url || "";
291
295
  const pathname = url.split("?")[0];
292
- const runtimeRoot = resolveRuntimeRoot();
296
+ const runtimeRoot = resolveActiveRuntimeRoot();
293
297
  if (!runtimeRoot) {
294
298
  if (pathname === prefix || pathname === prefix + "/") {
295
299
  res.statusCode = 500;
296
- res.end(RUNTIME_MISSING_MESSAGE);
300
+ res.end(SOOTSIM_RUNTIME_MISSING_MESSAGE);
297
301
  return;
298
302
  }
299
303
  next();
@@ -317,10 +321,10 @@ function withSootsim(config, options = {}) {
317
321
  }
318
322
  }
319
323
  if (pathname === prefix || pathname === prefix + "/" || pathname.startsWith(prefix + "/")) {
320
- const ext = import_path2.default.extname(pathname);
324
+ const ext = import_path3.default.extname(pathname);
321
325
  if (!ext) {
322
- const htmlPath = import_path2.default.join(runtimeRoot, "index.html");
323
- let html = import_fs2.default.readFileSync(htmlPath, "utf8");
326
+ const htmlPath = import_path3.default.join(runtimeRoot, "index.html");
327
+ let html = import_fs3.default.readFileSync(htmlPath, "utf8");
324
328
  html = html.replace(
325
329
  "</head>",
326
330
  `<script>history.replaceState(null,'','${prefix}/?bundle=${encodeURIComponent(bundleUrl)}')</script></head>`
@@ -340,12 +344,6 @@ function withSootsim(config, options = {}) {
340
344
  return { ...config, server: serverConfig };
341
345
  }
342
346
  var metro_plugin_default = withSootsim;
343
- function serveRuntimeFile(res, fullPath) {
344
- const ext = import_path2.default.extname(fullPath);
345
- res.setHeader("content-type", MIME_TYPES2[ext] || "application/octet-stream");
346
- res.setHeader("cache-control", "max-age=31536000,immutable");
347
- import_fs2.default.createReadStream(fullPath).pipe(res);
348
- }
349
347
 
350
348
  // src/bridge-constants.ts
351
349
  var DEFAULT_SOOTSIM_BRIDGE_PORT = 7668;