sootsim 0.1.140 → 0.1.142

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/dist-cli/bin.js +3 -3
  2. package/dist-cli/chunks/{agent-N767ME6M.js → agent-NAAIMWH7.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-ELHIUFZZ.js → agent-wrapper-XTURYUNI.js} +2 -2
  4. package/dist-cli/chunks/{app-fonts-OZ3OSNAJ.js → app-fonts-RSEABY54.js} +2 -2
  5. package/dist-cli/chunks/{assert-GGFUQBNB.js → assert-FLKFAP5S.js} +2 -2
  6. package/dist-cli/chunks/auto-bootstrap-UTDQXKT2.js +2 -0
  7. package/dist-cli/chunks/beta-WOG3OLTT.js +2 -0
  8. package/dist-cli/chunks/{camera-5PYEDLLB.js → camera-YW7TIZMC.js} +2 -2
  9. package/dist-cli/chunks/{chunk-QRRBQE6M.js → chunk-2SUXNIRB.js} +2 -2
  10. package/dist-cli/chunks/{chunk-GI43DDEI.js → chunk-3EETVUZK.js} +1 -1
  11. package/dist-cli/chunks/{chunk-WGDXZMKT.js → chunk-3ITFZJ7S.js} +2 -2
  12. package/dist-cli/chunks/{chunk-FYUDIUFX.js → chunk-3OPQ44BF.js} +2 -2
  13. package/dist-cli/chunks/chunk-5PZ7JGND.js +2 -0
  14. package/dist-cli/chunks/{chunk-N2XPC4Z7.js → chunk-6J5EKBUF.js} +2 -2
  15. package/dist-cli/chunks/{chunk-R5F7XGAO.js → chunk-AIS5XNOM.js} +3 -3
  16. package/dist-cli/chunks/chunk-BGIDIZGS.js +1 -0
  17. package/dist-cli/chunks/{chunk-5VTYMBUK.js → chunk-BZO4BFHL.js} +2 -2
  18. package/dist-cli/chunks/{chunk-HPTMLB7V.js → chunk-C3GZZTY2.js} +1 -1
  19. package/dist-cli/chunks/{chunk-BLQBPN76.js → chunk-CHTLYKGS.js} +2 -2
  20. package/dist-cli/chunks/{chunk-7SMEXHWJ.js → chunk-D6V27NP5.js} +1 -1
  21. package/dist-cli/chunks/{chunk-GYZNLDBX.js → chunk-FSKCHXES.js} +5 -5
  22. package/dist-cli/chunks/{chunk-AOICEZCA.js → chunk-GCCVK7DE.js} +2 -2
  23. package/dist-cli/chunks/{chunk-PBS47TD5.js → chunk-HAUBNMGA.js} +2 -2
  24. package/dist-cli/chunks/{chunk-ZGDJ5TFU.js → chunk-HJ6M2MSA.js} +2 -2
  25. package/dist-cli/chunks/{chunk-BM7QWZRI.js → chunk-HL3XSORU.js} +2 -2
  26. package/dist-cli/chunks/chunk-ITJU4NUU.js +1 -0
  27. package/dist-cli/chunks/{chunk-CQ3LV5VL.js → chunk-JVDKRU4Y.js} +2 -2
  28. package/dist-cli/chunks/{chunk-EMNVJOEL.js → chunk-KOZ5XDC7.js} +3 -3
  29. package/dist-cli/chunks/{chunk-4FBFOWV3.js → chunk-LTYJMDYI.js} +2 -2
  30. package/dist-cli/chunks/{chunk-X6AWKKL4.js → chunk-MO6Q4PLZ.js} +2 -2
  31. package/dist-cli/chunks/{chunk-ZN4Z2W7F.js → chunk-O4YHBU62.js} +2 -2
  32. package/dist-cli/chunks/{chunk-RAY2DLSK.js → chunk-OI5YSYX2.js} +1 -1
  33. package/dist-cli/chunks/{chunk-QOZ2Q4FH.js → chunk-P7FJ6IE4.js} +1 -1
  34. package/dist-cli/chunks/{chunk-4FGNOWB6.js → chunk-PQFNHOPP.js} +1 -1
  35. package/dist-cli/chunks/{chunk-AI65UVSN.js → chunk-QJ2H2D5V.js} +2 -2
  36. package/dist-cli/chunks/chunk-QJKMMHUB.js +406 -0
  37. package/dist-cli/chunks/{chunk-GL74P6NR.js → chunk-R42X5C7W.js} +1 -1
  38. package/dist-cli/chunks/{chunk-REIC7HRW.js → chunk-RNNX77AX.js} +1 -1
  39. package/dist-cli/chunks/{chunk-SSZBFWV7.js → chunk-RQPGGNZW.js} +1 -1
  40. package/dist-cli/chunks/chunk-SM3OY52S.js +119 -0
  41. package/dist-cli/chunks/chunk-SN3MJYP7.js +1 -0
  42. package/dist-cli/chunks/{chunk-O2SQNUDP.js → chunk-TKNYTKLF.js} +1 -1
  43. package/dist-cli/chunks/{chunk-BGXNYXBC.js → chunk-UCPLYSBA.js} +1 -1
  44. package/dist-cli/chunks/{chunk-C3FURRBF.js → chunk-UUKLS7ZA.js} +1 -1
  45. package/dist-cli/chunks/{chunk-ZVQOEMXS.js → chunk-VGCBZCSG.js} +54 -58
  46. package/dist-cli/chunks/{chunk-AP2MOP44.js → chunk-W5FZTYVP.js} +1 -1
  47. package/dist-cli/chunks/{chunk-UHTE6FLK.js → chunk-WEDVFO7O.js} +2 -2
  48. package/dist-cli/chunks/{chunk-XGYD25DJ.js → chunk-WHTKAFVO.js} +1 -1
  49. package/dist-cli/chunks/{chunk-77TL74AM.js → chunk-XNHS44BQ.js} +1 -1
  50. package/dist-cli/chunks/chunk-XZHTKBH7.js +5 -0
  51. package/dist-cli/chunks/{chunk-MRQRJINW.js → chunk-YG4V3MOQ.js} +1 -1
  52. package/dist-cli/chunks/{chunk-QXOZB7WD.js → chunk-YIWBIAVG.js} +2 -2
  53. package/dist-cli/chunks/{chunk-6KUVOSFF.js → chunk-ZWIFZAXU.js} +2 -2
  54. package/dist-cli/chunks/cli-version-IRAJSCVW.js +2 -0
  55. package/dist-cli/chunks/{compat-U3772TUK.js → compat-HLFCOAFE.js} +3 -3
  56. package/dist-cli/chunks/{config-6CZCIYKJ.js → config-YFXVAWO3.js} +2 -2
  57. package/dist-cli/chunks/{control-NVA25O65.js → control-I4OAASMO.js} +2 -2
  58. package/dist-cli/chunks/{cpu-profile-WRLTZYO6.js → cpu-profile-OKH4Y467.js} +2 -2
  59. package/dist-cli/chunks/{daemon-QQPGLGS6.js → daemon-SI2HN567.js} +2 -2
  60. package/dist-cli/chunks/{debug-RKYLSDZV.js → debug-AEHF4WWI.js} +3 -3
  61. package/dist-cli/chunks/{detox-ZRUQQU22.js → detox-HLZCQK7Z.js} +2 -2
  62. package/dist-cli/chunks/{device-HXTOLEWY.js → device-WDLN7VOT.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-QN4E4WA6.js → diagnose-3KKIW2KV.js} +2 -2
  64. package/dist-cli/chunks/drivers-G6Y3D5KN.js +2 -0
  65. package/dist-cli/chunks/{electron-ZHRC4WQ5.js → electron-XRQZK2WQ.js} +3 -3
  66. package/dist-cli/chunks/flow-U2Y6PHQY.js +2 -0
  67. package/dist-cli/chunks/help-7TWUPXUX.js +2 -0
  68. package/dist-cli/chunks/{hints-HCHKMDUE.js → hints-B63I5SSR.js} +2 -2
  69. package/dist-cli/chunks/{home-paths-3OHKRAE4.js → home-paths-BDQIBV2V.js} +2 -2
  70. package/dist-cli/chunks/{inspect-QXUBDKJJ.js → inspect-7XVNDGN4.js} +3 -3
  71. package/dist-cli/chunks/install-UJ4KBTQ4.js +2 -0
  72. package/dist-cli/chunks/{install-desktop-QCX44BGL.js → install-desktop-JBQQTH3I.js} +3 -3
  73. package/dist-cli/chunks/{keys-VEP2KPUF.js → keys-IMBTM4HL.js} +2 -2
  74. package/dist-cli/chunks/{launch-K4BPQLTP.js → launch-XU64UAEQ.js} +3 -3
  75. package/dist-cli/chunks/{login-UVC5XRKR.js → login-63BFWUJD.js} +4 -4
  76. package/dist-cli/chunks/{logout-3VW34IBA.js → logout-RHE2W3WL.js} +2 -2
  77. package/dist-cli/chunks/maestro-4CAIVMXK.js +94 -0
  78. package/dist-cli/chunks/optional-demo-registry-BTUMTEBH.js +2 -0
  79. package/dist-cli/chunks/{preview-TZYJMLEJ.js → preview-XZYX3SSZ.js} +2 -2
  80. package/dist-cli/chunks/{profile-HDJRF7YB.js → profile-NNNFLZ6K.js} +2 -2
  81. package/dist-cli/chunks/{react-RLJR26M7.js → react-45J3NYF6.js} +2 -2
  82. package/dist-cli/chunks/{record-2LDDHHUY.js → record-QEON42UG.js} +2 -2
  83. package/dist-cli/chunks/runtime-2LEYVXKV.js +2 -0
  84. package/dist-cli/chunks/{runtime-delivery-3F6O57E5.js → runtime-delivery-LGV6QPZS.js} +2 -2
  85. package/dist-cli/chunks/{screenshot-QAUSSEPK.js → screenshot-SW2ZM723.js} +2 -2
  86. package/dist-cli/chunks/{screenshot-mode-K6HEVK4K.js → screenshot-mode-DSEJREME.js} +2 -2
  87. package/dist-cli/chunks/{screenshots-TWMCGUBT.js → screenshots-QWUZGYAS.js} +2 -2
  88. package/dist-cli/chunks/{server-ZPIZCBW5.js → server-6PTAPUIC.js} +3 -3
  89. package/dist-cli/chunks/setup-repo-3EBO7HQE.js +2 -0
  90. package/dist-cli/chunks/{skills-NI3RH7CW.js → skills-OAKG5VHJ.js} +2 -2
  91. package/dist-cli/chunks/{start-G6CQL6HJ.js → start-3QVTLFG7.js} +4 -4
  92. package/dist-cli/chunks/store-HKA3ET4T.js +2 -0
  93. package/dist-cli/chunks/telemetry-6ORWTXX5.js +2 -0
  94. package/dist-cli/chunks/{test-V47D6EPK.js → test-PYY6TAZF.js} +3 -3
  95. package/dist-cli/chunks/{three-mode-G7N3LRHM.js → three-mode-4CJYCCE6.js} +2 -2
  96. package/dist-cli/chunks/{timeline-EBUOILTJ.js → timeline-C4WE3FQZ.js} +2 -2
  97. package/dist-cli/chunks/{upgrade-OQJLTCQP.js → upgrade-PY3F4JQ6.js} +2 -2
  98. package/dist-cli/chunks/upload-NFFWJLCQ.js +2 -0
  99. package/dist-cli/chunks/{version-3YJPPUE5.js → version-CVDLRIWC.js} +2 -2
  100. package/dist-cli/chunks/{web-ZKUX5ICX.js → web-DWUYDFQ3.js} +2 -2
  101. package/dist-cli/chunks/{what-happened-HOY3TA42.js → what-happened-6NQLDVJC.js} +2 -2
  102. package/dist-cli/chunks/{whoami-GOCSBVTG.js → whoami-WK3UUIOA.js} +2 -2
  103. package/dist-lib/agent-daemon-client.cjs +1 -1
  104. package/dist-lib/agent-events.cjs +1 -1
  105. package/dist-lib/agent-sessions.cjs +1 -1
  106. package/dist-lib/attached-projects.cjs +1 -1
  107. package/dist-lib/auth/shared-session.cjs +1 -1
  108. package/dist-lib/backend-origin.cjs +1 -1
  109. package/dist-lib/beta.cjs +1 -1
  110. package/dist-lib/beta.mjs +1 -1
  111. package/dist-lib/bridge-constants.cjs +1 -1
  112. package/dist-lib/cli-constants.cjs +1 -1
  113. package/dist-lib/config.cjs +5 -2
  114. package/dist-lib/detox/index.cjs +1 -1
  115. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  116. package/dist-lib/home-paths.cjs +1 -1
  117. package/dist-lib/host/bridge-host.cjs +2 -2
  118. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  119. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  121. package/dist-lib/host/websocket-proxy.cjs +1 -1
  122. package/dist-lib/index.cjs +1 -1
  123. package/dist-lib/metro.cjs +1 -1
  124. package/dist-lib/profiles.cjs +1 -1
  125. package/dist-lib/render-mode.cjs +1 -1
  126. package/dist-lib/scripts/dev-server-scanner.cjs +2 -2
  127. package/dist-lib/sdk.cjs +1 -1
  128. package/dist-lib/sdk.mjs +1 -1
  129. package/dist-lib/skills.cjs +360 -92
  130. package/dist-lib/vite.cjs +1 -1
  131. package/package.json +1 -1
  132. package/src/config.ts +13 -0
  133. package/dist-cli/chunks/auto-bootstrap-L5E6XMCK.js +0 -2
  134. package/dist-cli/chunks/beta-D74SDA2L.js +0 -2
  135. package/dist-cli/chunks/chunk-5EP5RRNS.js +0 -381
  136. package/dist-cli/chunks/chunk-BEA3AO36.js +0 -5
  137. package/dist-cli/chunks/chunk-EH6YDCSN.js +0 -119
  138. package/dist-cli/chunks/chunk-NQG4R5K2.js +0 -1
  139. package/dist-cli/chunks/chunk-Q5Z7P54I.js +0 -2
  140. package/dist-cli/chunks/chunk-S7I6ZCT2.js +0 -1
  141. package/dist-cli/chunks/chunk-WN3WZEE5.js +0 -1
  142. package/dist-cli/chunks/cli-version-EZRTEUXF.js +0 -2
  143. package/dist-cli/chunks/drivers-EVK22AX5.js +0 -2
  144. package/dist-cli/chunks/flow-ICCRVJVX.js +0 -2
  145. package/dist-cli/chunks/help-QMZ35MIQ.js +0 -2
  146. package/dist-cli/chunks/install-JSDZIE2T.js +0 -2
  147. package/dist-cli/chunks/maestro-HTFXXMLR.js +0 -80
  148. package/dist-cli/chunks/optional-demo-registry-5YYEAL46.js +0 -2
  149. package/dist-cli/chunks/runtime-QPYF2WQX.js +0 -2
  150. package/dist-cli/chunks/setup-repo-VHOZJEIV.js +0 -2
  151. package/dist-cli/chunks/store-F6MJUJYW.js +0 -2
  152. package/dist-cli/chunks/telemetry-DM2Y7OQJ.js +0 -2
  153. package/dist-cli/chunks/upload-W3YCA65H.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.140 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.142 | (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;
@@ -9982,7 +9982,7 @@ function hasOwnKeys(value) {
9982
9982
  }
9983
9983
  function hasSootSimConfig(config) {
9984
9984
  if (!config) return false;
9985
- return hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.env) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
9985
+ return hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
9986
9986
  }
9987
9987
  function applySootSimConfigToUrl(url, config) {
9988
9988
  const parsed = new URL(url);
@@ -15325,10 +15325,6 @@ var init_app_state_reset = __esm({
15325
15325
  ),
15326
15326
  )
15327
15327
  } catch {}
15328
- try {
15329
- const names = await caches.keys()
15330
- await Promise.all(names.map((name) => caches.delete(name)))
15331
- } catch {}
15332
15328
  try {
15333
15329
  document.cookie.split(';').forEach((cookie) => {
15334
15330
  const name = cookie.split('=')[0]?.trim()
@@ -15465,36 +15461,241 @@ var init_settling = __esm({
15465
15461
  }
15466
15462
  });
15467
15463
 
15468
- // cli/flow-file.ts
15469
- function interpolateFlowEnv(value, scopedVars) {
15470
- if (typeof value === "string") {
15471
- return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
15472
- if (scopedVars?.has(name)) return scopedVars.get(name);
15473
- const resolved = process.env[name];
15474
- if (resolved === void 0) {
15475
- throw new Error(`missing environment variable for flow placeholder: ${name}`);
15476
- }
15477
- return resolved;
15478
- });
15464
+ // cli/maestro-js.ts
15465
+ function executeSyncHttp(url, method, params) {
15466
+ if (params?.multipartForm) {
15467
+ throw new Error("http: multipartForm is not supported by sootsim yet");
15479
15468
  }
15480
- if (Array.isArray(value)) {
15481
- return value.map((entry) => interpolateFlowEnv(entry, scopedVars));
15469
+ const childEnv = { ...process.env };
15470
+ for (const key of Object.keys(childEnv)) {
15471
+ if (key.startsWith("BUN_INSPECT") || key === "NODE_OPTIONS") {
15472
+ delete childEnv[key];
15473
+ }
15474
+ }
15475
+ const child = (0, import_child_process6.spawnSync)(process.execPath, ["-e", HTTP_CHILD_SRC], {
15476
+ input: JSON.stringify({ url, method, headers: params?.headers, body: params?.body }),
15477
+ encoding: "utf8",
15478
+ timeout: HTTP_TIMEOUT_MS,
15479
+ maxBuffer: 64 * 1024 * 1024,
15480
+ env: childEnv
15481
+ });
15482
+ if (child.error) {
15483
+ throw new Error(`http ${method} ${url} failed: ${child.error.message}`);
15482
15484
  }
15483
- if (value && typeof value === "object") {
15484
- return Object.fromEntries(
15485
- Object.entries(value).map(([key, entry]) => [
15486
- key,
15487
- interpolateFlowEnv(entry, scopedVars)
15488
- ])
15485
+ let parsed;
15486
+ try {
15487
+ parsed = JSON.parse(child.stdout || "");
15488
+ } catch {
15489
+ throw new Error(
15490
+ `http ${method} ${url} failed: no response (${(child.stderr || "").trim().slice(0, 200)})`
15489
15491
  );
15490
15492
  }
15491
- return value;
15493
+ const result = parsed;
15494
+ if (result.__error) {
15495
+ throw new Error(`http ${method} ${url} failed: ${result.__error}`);
15496
+ }
15497
+ return result;
15492
15498
  }
15493
- var import_yaml;
15494
- var init_flow_file = __esm({
15495
- "cli/flow-file.ts"() {
15499
+ function stringifyJs(value) {
15500
+ if (value === void 0) return "undefined";
15501
+ if (value === null) return "null";
15502
+ if (typeof value === "object") {
15503
+ try {
15504
+ return JSON.stringify(value);
15505
+ } catch {
15506
+ return String(value);
15507
+ }
15508
+ }
15509
+ return String(value);
15510
+ }
15511
+ function scriptConditionIsTruthy(value) {
15512
+ if (value === void 0 || value === null) return false;
15513
+ if (typeof value === "boolean") return value;
15514
+ const text = String(value);
15515
+ if (text.trim().length === 0) return false;
15516
+ if (text.toLowerCase() === "false") return false;
15517
+ if (text === "undefined" || text === "null") return false;
15518
+ const num = Number(text);
15519
+ if (!Number.isNaN(num) && num === 0) return false;
15520
+ return true;
15521
+ }
15522
+ var import_child_process6, vm, HTTP_TIMEOUT_MS, HTTP_CHILD_SRC, TEMPLATE_RE, ESCAPED_TEMPLATE_RE, BARE_IDENTIFIER_RE, MaestroJsContext;
15523
+ var init_maestro_js = __esm({
15524
+ "cli/maestro-js.ts"() {
15496
15525
  "use strict";
15497
- import_yaml = __toESM(require("yaml"), 1);
15526
+ import_child_process6 = require("child_process");
15527
+ vm = __toESM(require("vm"), 1);
15528
+ HTTP_TIMEOUT_MS = 3e5;
15529
+ HTTP_CHILD_SRC = `
15530
+ let input = '';
15531
+ process.stdin.on('data', (c) => { input += c });
15532
+ process.stdin.on('end', async () => {
15533
+ try {
15534
+ const req = JSON.parse(input);
15535
+ const res = await fetch(req.url, {
15536
+ method: req.method,
15537
+ headers: req.headers,
15538
+ body: req.body === undefined || req.body === null ? undefined : req.body,
15539
+ });
15540
+ const body = await res.text();
15541
+ const headers = {};
15542
+ for (const [k, v] of res.headers.entries()) {
15543
+ headers[k] = headers[k] ? headers[k] + ',' + v : v;
15544
+ }
15545
+ process.stdout.write(JSON.stringify({ ok: res.ok, status: res.status, body, headers }));
15546
+ } catch (err) {
15547
+ process.stdout.write(JSON.stringify({ __error: (err && err.message) || String(err) }));
15548
+ process.exitCode = 1;
15549
+ }
15550
+ });
15551
+ `;
15552
+ TEMPLATE_RE = /(?<!\\)\$\{([^$]*)\}/g;
15553
+ ESCAPED_TEMPLATE_RE = /\\(\$\{[^$]*\})/g;
15554
+ BARE_IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
15555
+ MaestroJsContext = class {
15556
+ context;
15557
+ envBinding = /* @__PURE__ */ new Map();
15558
+ envScopeStack = [];
15559
+ // env keys currently materialized as context globals — cleared and re-set
15560
+ // before each evaluation, mirroring upstream syncBindingsToContext.
15561
+ syncedEnvKeys = /* @__PURE__ */ new Set();
15562
+ output = {};
15563
+ maestro;
15564
+ constructor(opts = {}) {
15565
+ this.maestro = { copiedText: null, platform: opts.platform ?? "ios" };
15566
+ const onLog = opts.onLog ?? ((msg) => console.log(`[flow] js: ${msg}`));
15567
+ const sandbox = {
15568
+ output: this.output,
15569
+ maestro: this.maestro,
15570
+ http: {
15571
+ get: (url, params) => executeSyncHttp(url, "GET", params),
15572
+ post: (url, params) => executeSyncHttp(url, "POST", params),
15573
+ put: (url, params) => executeSyncHttp(url, "PUT", params),
15574
+ delete: (url, params) => executeSyncHttp(url, "DELETE", params),
15575
+ request: (url, params) => executeSyncHttp(url, (params?.method ?? "GET").toUpperCase(), params)
15576
+ },
15577
+ console: {
15578
+ log: (...args) => onLog(args.map((a) => typeof a === "string" ? a : stringifyJs(a)).join(" "))
15579
+ }
15580
+ };
15581
+ this.context = vm.createContext(sandbox);
15582
+ vm.runInContext(
15583
+ `
15584
+ globalThis.__maestroScope = new Proxy(globalThis, {
15585
+ has() { return true },
15586
+ get(target, key) { return target[key] },
15587
+ })
15588
+ function json(text) { return JSON.parse(text) }
15589
+ function relativePoint(x, y) {
15590
+ var xPercent = Math.ceil(x * 100) + '%'
15591
+ var yPercent = Math.ceil(y * 100) + '%'
15592
+ return xPercent + ',' + yPercent
15593
+ }
15594
+ `,
15595
+ this.context
15596
+ );
15597
+ for (const [key, value] of Object.entries(process.env)) {
15598
+ if (value !== void 0) this.envBinding.set(key, value);
15599
+ }
15600
+ }
15601
+ putEnv(key, value) {
15602
+ this.envBinding.set(key, value);
15603
+ }
15604
+ setCopiedText(text) {
15605
+ this.maestro.copiedText = text;
15606
+ }
15607
+ enterEnvScope() {
15608
+ this.envScopeStack.push(new Map(this.envBinding));
15609
+ }
15610
+ leaveEnvScope() {
15611
+ const previous = this.envScopeStack.pop();
15612
+ if (previous) {
15613
+ this.envBinding.clear();
15614
+ for (const [k, v] of previous) this.envBinding.set(k, v);
15615
+ }
15616
+ }
15617
+ // evaluate a JS snippet in the shared context. mirrors upstream
15618
+ // evalWithIIFE: the script is embedded in a template literal and run
15619
+ // through eval inside an IIFE so var/let/const stay scoped to this
15620
+ // evaluation while the last expression's value is returned.
15621
+ evaluate(script, opts = {}) {
15622
+ const env = opts.env;
15623
+ if (env && Object.keys(env).length > 0) {
15624
+ this.enterEnvScope();
15625
+ try {
15626
+ for (const [k, v] of Object.entries(env)) this.envBinding.set(k, v);
15627
+ return this.evalRaw(script);
15628
+ } finally {
15629
+ this.leaveEnvScope();
15630
+ }
15631
+ }
15632
+ return this.evalRaw(script);
15633
+ }
15634
+ evalRaw(script) {
15635
+ this.syncEnvToContext();
15636
+ const escaped = script.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
15637
+ return vm.runInContext(
15638
+ `(function(){ with(__maestroScope){ return eval(\`${escaped}\`) } })()`,
15639
+ this.context
15640
+ );
15641
+ }
15642
+ syncEnvToContext() {
15643
+ const globals = vm.runInContext("globalThis", this.context);
15644
+ for (const key of this.syncedEnvKeys) {
15645
+ if (!this.envBinding.has(key)) delete globals[key];
15646
+ }
15647
+ this.syncedEnvKeys = /* @__PURE__ */ new Set();
15648
+ for (const [key, value] of this.envBinding) {
15649
+ try {
15650
+ globals[key] = value;
15651
+ this.syncedEnvKeys.add(key);
15652
+ } catch {
15653
+ }
15654
+ }
15655
+ }
15656
+ // upstream Env.kt String.evaluateScripts: replace every unescaped `${expr}`
15657
+ // with the stringified evaluation result, then unescape `\${...}`.
15658
+ evaluateStringTemplate(value) {
15659
+ const replaced = value.replace(TEMPLATE_RE, (_match, expr) => {
15660
+ if (expr.trim().length === 0) return "";
15661
+ const result = this.evaluate(expr);
15662
+ if (result === void 0 && BARE_IDENTIFIER_RE.test(expr.trim())) {
15663
+ throw new Error(`missing variable for flow placeholder: ${expr.trim()}`);
15664
+ }
15665
+ return stringifyJs(result);
15666
+ });
15667
+ return replaced.replace(ESCAPED_TEMPLATE_RE, (_match, inner) => inner);
15668
+ }
15669
+ // walk a parsed step and evaluate every string template. nested command
15670
+ // lists are skipped — each nested command evaluates its own strings when
15671
+ // it executes (matching upstream per-command evaluateScripts timing, and
15672
+ // required for values that change between iterations, e.g. copyTextFrom
15673
+ // inside repeat).
15674
+ interpolateStep(step) {
15675
+ return this.interpolateValue(step, null);
15676
+ }
15677
+ interpolateValue(value, parentKey) {
15678
+ if (typeof value === "string") {
15679
+ if (parentKey === "evalScript") return value;
15680
+ return this.evaluateStringTemplate(value);
15681
+ }
15682
+ if (Array.isArray(value)) {
15683
+ if (parentKey === "commands" || parentKey === "onFlowStart" || parentKey === "onFlowComplete") {
15684
+ return value;
15685
+ }
15686
+ return value.map((entry) => this.interpolateValue(entry, parentKey));
15687
+ }
15688
+ if (value && typeof value === "object") {
15689
+ return Object.fromEntries(
15690
+ Object.entries(value).map(([key, entry]) => [
15691
+ key,
15692
+ this.interpolateValue(entry, key)
15693
+ ])
15694
+ );
15695
+ }
15696
+ return value;
15697
+ }
15698
+ };
15498
15699
  }
15499
15700
  });
15500
15701
 
@@ -15814,19 +16015,11 @@ var init_recording_access = __esm({
15814
16015
  var bridge_flow_runner_exports = {};
15815
16016
  __export(bridge_flow_runner_exports, {
15816
16017
  SootSimBridgeFlowRunner: () => SootSimBridgeFlowRunner,
15817
- clearFlowScopedVars: () => clearFlowScopedVars,
15818
16018
  normalizeFlowScreenshotSpec: () => normalizeFlowScreenshotSpec,
15819
16019
  parseFlowSteps: () => parseFlowSteps,
15820
16020
  resolveFlowScreenshotPath: () => resolveFlowScreenshotPath,
15821
- resolveFlowScreenshotPathWithMode: () => resolveFlowScreenshotPathWithMode,
15822
- setFlowScopedVar: () => setFlowScopedVar
16021
+ resolveFlowScreenshotPathWithMode: () => resolveFlowScreenshotPathWithMode
15823
16022
  });
15824
- function setFlowScopedVar(name, value) {
15825
- flowScopedVars.set(name, value);
15826
- }
15827
- function clearFlowScopedVars() {
15828
- flowScopedVars.clear();
15829
- }
15830
16023
  function sleep(ms) {
15831
16024
  return new Promise((resolve6) => setTimeout(resolve6, ms));
15832
16025
  }
@@ -15873,34 +16066,39 @@ function parsePoint(value) {
15873
16066
  function parseFlowSteps(content) {
15874
16067
  const parts = content.split(/^---$/m);
15875
16068
  const candidate = parts.length > 1 ? parts[parts.length - 1] : content;
15876
- const parsed = interpolateFlowEnv(import_yaml2.default.parse(candidate), flowScopedVars);
16069
+ const parsed = import_yaml.default.parse(candidate);
15877
16070
  return Array.isArray(parsed) ? parsed : [];
15878
16071
  }
15879
- var fs7, path7, import_settings2, import_yaml2, DEFAULT_TIMEOUT, SCREEN_W, SCREEN_H, CLI_PERF_SESSION_GLOBAL, MAESTRO_COPIED_TEXT_KEY, flowScopedVars, SootSimBridgeFlowRunner;
16072
+ var fs7, path7, import_settings2, import_yaml, DEFAULT_TIMEOUT, SCREEN_W, SCREEN_H, CLI_PERF_SESSION_GLOBAL, SootSimBridgeFlowRunner;
15880
16073
  var init_bridge_flow_runner = __esm({
15881
16074
  "cli/bridge-flow-runner.ts"() {
15882
16075
  "use strict";
15883
16076
  fs7 = __toESM(require("fs"), 1);
15884
16077
  path7 = __toESM(require("path"), 1);
15885
16078
  import_settings2 = require("sootsim-engine/settings");
15886
- import_yaml2 = __toESM(require("yaml"), 1);
16079
+ import_yaml = __toESM(require("yaml"), 1);
15887
16080
  init_frame_compose();
15888
16081
  init_app_state_reset();
15889
16082
  init_settling();
15890
- init_flow_file();
16083
+ init_maestro_js();
15891
16084
  init_recording_access();
15892
16085
  DEFAULT_TIMEOUT = 1e4;
15893
16086
  SCREEN_W = 393;
15894
16087
  SCREEN_H = 852;
15895
16088
  CLI_PERF_SESSION_GLOBAL = "__sootsimCliPerf";
15896
- MAESTRO_COPIED_TEXT_KEY = "maestroCopiedText";
15897
- flowScopedVars = /* @__PURE__ */ new Map();
15898
16089
  SootSimBridgeFlowRunner = class {
15899
16090
  constructor(bridge, opts) {
15900
16091
  this.bridge = bridge;
15901
16092
  this.opts = opts;
15902
16093
  }
15903
16094
  stepDelay = 0;
16095
+ // maestro-compatible JS context for the whole flow run (shared with
16096
+ // runFlow sub-flows): `${...}` templates, runScript, evalScript, output,
16097
+ // maestro.copiedText. one per runner = one per flow file, like upstream.
16098
+ js = new MaestroJsContext({
16099
+ platform: "ios",
16100
+ onLog: (msg) => console.log(`[flow] js: ${msg}`)
16101
+ });
15904
16102
  firstLaunchDone = false;
15905
16103
  profilingEnabled = false;
15906
16104
  recordingEnabled = false;
@@ -16717,15 +16915,12 @@ var init_bridge_flow_runner = __esm({
16717
16915
  }
16718
16916
  async launchApp(opts) {
16719
16917
  await this.refreshSimId();
16918
+ if (opts && typeof opts === "object" && opts.arguments) {
16919
+ console.log("[flow] launchApp.arguments ignored (sootsim has no native process)");
16920
+ }
16720
16921
  const wantsClear = !!(opts && typeof opts === "object" && opts.clearState);
16721
16922
  if (wantsClear) {
16722
- await this.evaluate(`(async () => {
16723
- await (${CLEAR_APP_STATE_EVAL})
16724
- try {
16725
- const regs = await navigator.serviceWorker?.getRegistrations?.()
16726
- await Promise.all((regs || []).map((reg) => reg.unregister()))
16727
- } catch {}
16728
- })()`);
16923
+ await this.evaluate(CLEAR_APP_STATE_EVAL, 6e4);
16729
16924
  }
16730
16925
  if (!this.firstLaunchDone && !wantsClear) {
16731
16926
  this.firstLaunchDone = true;
@@ -16900,7 +17095,8 @@ var init_bridge_flow_runner = __esm({
16900
17095
  );
16901
17096
  }
16902
17097
  // maestro copyTextFrom: find an element and capture its text for use in
16903
- // later steps via ${maestroCopiedText}. the matcher shape matches tapOn.
17098
+ // later steps via ${maestro.copiedText} (upstream form) or the legacy
17099
+ // ${maestroCopiedText}. the matcher shape matches tapOn.
16904
17100
  async copyTextFrom(target) {
16905
17101
  const opts = typeof target === "string" ? { id: target } : target;
16906
17102
  const node = await this.findElement(opts);
@@ -16908,36 +17104,52 @@ var init_bridge_flow_runner = __esm({
16908
17104
  throw new Error(`copyTextFrom: element not found: ${JSON.stringify(opts)}`);
16909
17105
  }
16910
17106
  const captured = node.text ?? node.testID ?? "";
16911
- flowScopedVars.set(MAESTRO_COPIED_TEXT_KEY, captured);
17107
+ this.js.setCopiedText(captured);
17108
+ this.js.putEnv("maestroCopiedText", captured);
16912
17109
  console.log(`[flow] copied text: ${JSON.stringify(captured)}`);
16913
17110
  }
16914
- // maestro evalScript: run arbitrary JS in the page via the bridge's eval
16915
- // channel. the return value is logged but not stored use copyTextFrom
16916
- // (or a side-effect on window) for flows that need to round-trip data.
16917
- async evalScript(code) {
16918
- const result = await this.evaluate(`(async () => {
16919
- try {
16920
- return { ok: true, value: await (async () => { ${code} })() }
16921
- } catch (err) {
16922
- return { ok: false, error: err && err.message ? err.message : String(err) }
16923
- }
16924
- })()`);
16925
- const r = result;
16926
- if (!r.ok) throw new Error(`evalScript failed: ${r.error}`);
16927
- if (r.value !== void 0) {
16928
- console.log(`[flow] evalScript ->`, r.value);
17111
+ // maestro evalScript: run JS in the flow's shared HOST-side context (the
17112
+ // same engine that owns `output` and env vars)never in the app page.
17113
+ // the canonical maestro form is `evalScript: ${output.x = 1}`, which is
17114
+ // pure template evaluation. a script with no `${}` at all would be a
17115
+ // silent no-op upstream; we charitably evaluate it as raw JS in the same
17116
+ // context so plain-JS evalScript blocks fail or work loudly.
17117
+ evalScript(code) {
17118
+ const hasTemplate = /(?<!\\)\$\{[^$]*\}/.test(code);
17119
+ const result = hasTemplate ? this.js.evaluateStringTemplate(code) : this.js.evaluate(code);
17120
+ if (result !== void 0 && result !== "") {
17121
+ console.log(`[flow] evalScript ->`, result);
16929
17122
  }
16930
17123
  }
16931
- // maestro openLink: navigate the sim to a URL via the bridge's eval
16932
- // channel. on-device maestro asks the OS to open the link; in sootsim we
16933
- // just drive window.location so the current app sees the deep link.
17124
+ // maestro runScript: execute a JS file in the shared flow context. env
17125
+ // vars are scoped to the script (upstream runInSubScope=true). paths
17126
+ // resolve relative to the flow file, like runFlow.
17127
+ runScript(target) {
17128
+ const spec = typeof target === "string" ? { file: target } : target;
17129
+ if (!spec.file) throw new Error("runScript requires a file");
17130
+ const scriptPath = path7.resolve(this.opts.flowDir, spec.file);
17131
+ const source = fs7.readFileSync(scriptPath, "utf8");
17132
+ this.js.evaluate(source, { env: spec.env });
17133
+ console.log(`[flow] runScript: ${spec.file} done`);
17134
+ }
17135
+ // maestro openLink: on-device maestro asks the OS to open the link, and
17136
+ // iOS routes an app-claimed URL into the running app as a Linking 'url'
17137
+ // event. sootsim mirrors that: dispatch through the engine's Linking
17138
+ // emulation so react-navigation useLinking / addEventListener('url')
17139
+ // handlers fire exactly like on a real device. an app that doesn't
17140
+ // handle the link simply ignores it (same as iOS routing to Safari —
17141
+ // the app under test sees nothing).
16934
17142
  async openLink(target) {
16935
17143
  const link = typeof target === "string" ? target : target.link;
16936
17144
  if (!link) throw new Error("openLink: missing link");
16937
- await this.evaluate(`window.location.href = ${JSON.stringify(link)}`);
17145
+ const result = await this.callTest(
17146
+ "openDeepLink",
17147
+ link
17148
+ );
17149
+ if (!result?.ok) {
17150
+ throw new Error(`openLink failed: ${result?.error || "no test bridge"}`);
17151
+ }
16938
17152
  await sleep(300);
16939
- await this.refreshSimId();
16940
- await this.waitForTree(3e4);
16941
17153
  }
16942
17154
  // evaluate a `when:` predicate. returns true if the step should run.
16943
17155
  async evaluateWhen(when) {
@@ -16952,10 +17164,10 @@ var init_bridge_flow_runner = __esm({
16952
17164
  return !el;
16953
17165
  }
16954
17166
  if (when.platform !== void 0) {
16955
- return when.platform.toLowerCase() === "ios";
17167
+ return when.platform.toLowerCase() === this.js.maestro.platform.toLowerCase();
16956
17168
  }
16957
17169
  if (when.true !== void 0) {
16958
- return when.true.toLowerCase() !== "false";
17170
+ return scriptConditionIsTruthy(when.true);
16959
17171
  }
16960
17172
  return true;
16961
17173
  }
@@ -16966,9 +17178,26 @@ var init_bridge_flow_runner = __esm({
16966
17178
  const value = entry ? entry[1] : void 0;
16967
17179
  return !!(value && typeof value === "object" && "optional" in value && value.optional);
16968
17180
  }
16969
- async runStep(step, stepIndex) {
16970
- const stepName = Object.keys(step).find((k) => k !== "when") || Object.keys(step)[0];
17181
+ async runStep(rawStep, stepIndex) {
17182
+ const stepName = Object.keys(rawStep).find((k) => k !== "when") || Object.keys(rawStep)[0];
16971
17183
  console.log(`[flow] step ${stepIndex + 1}: ${stepName}`);
17184
+ let step;
17185
+ try {
17186
+ step = this.js.interpolateStep(rawStep);
17187
+ } catch (error) {
17188
+ if (this.isOptional(rawStep)) {
17189
+ console.log(
17190
+ `[flow] (optional, skipped: ${error.message.slice(0, 80)})`
17191
+ );
17192
+ return;
17193
+ }
17194
+ this.lastFailedStep = {
17195
+ index: stepIndex,
17196
+ kind: stepName,
17197
+ target: rawStep[stepName]
17198
+ };
17199
+ throw error;
17200
+ }
16972
17201
  if (step.when) {
16973
17202
  const shouldRun = await this.evaluateWhen(step.when);
16974
17203
  if (!shouldRun) {
@@ -16977,7 +17206,9 @@ var init_bridge_flow_runner = __esm({
16977
17206
  }
16978
17207
  }
16979
17208
  try {
16980
- const hasExplicitWait = !!(step.extendedWaitUntil || step.waitFor || step.scrollUntilVisible || step.launchApp || step.runFlow);
17209
+ const hasExplicitWait = !!(step.extendedWaitUntil || step.waitFor || step.scrollUntilVisible || step.launchApp || step.runFlow || // runScript http calls are synchronous with a 5-minute upstream
17210
+ // timeout — the 10s watchdog would kill legitimate setup scripts.
17211
+ step.runScript);
16981
17212
  const STEP_WATCHDOG_MS = 1e4;
16982
17213
  const body = this.runStepInner(step);
16983
17214
  if (hasExplicitWait) {
@@ -17073,7 +17304,11 @@ var init_bridge_flow_runner = __esm({
17073
17304
  } else if (typeof step.eraseText === "number") {
17074
17305
  await this.eraseText(step.eraseText);
17075
17306
  } else if (step.repeat) {
17076
- for (let i = 0; i < step.repeat.times; i++) {
17307
+ const times = Number(step.repeat.times);
17308
+ if (!Number.isFinite(times)) {
17309
+ throw new Error(`repeat.times is not a number: ${step.repeat.times}`);
17310
+ }
17311
+ for (let i = 0; i < times; i++) {
17077
17312
  for (let j = 0; j < step.repeat.commands.length; j++) {
17078
17313
  await this.runStep(step.repeat.commands[j], j);
17079
17314
  }
@@ -17087,26 +17322,59 @@ var init_bridge_flow_runner = __esm({
17087
17322
  } else if (step.copyTextFrom !== void 0) {
17088
17323
  await this.copyTextFrom(step.copyTextFrom);
17089
17324
  } else if (typeof step.evalScript === "string") {
17090
- await this.evalScript(step.evalScript);
17325
+ this.evalScript(step.evalScript);
17326
+ } else if (step.runScript) {
17327
+ const rs = step.runScript;
17328
+ const innerWhen = typeof rs === "string" ? void 0 : rs.when;
17329
+ if (innerWhen && !await this.evaluateWhen(innerWhen)) {
17330
+ console.log(`[flow] runScript skipped (when predicate false)`);
17331
+ return;
17332
+ }
17333
+ this.runScript(rs);
17091
17334
  } else if (step.openLink !== void 0) {
17092
17335
  await this.openLink(step.openLink);
17093
17336
  } else if (step.runFlow) {
17094
17337
  const rf = step.runFlow;
17095
17338
  const file = typeof rf === "string" ? rf : rf.file;
17339
+ const commands = typeof rf === "string" ? void 0 : rf.commands;
17340
+ const env = typeof rf === "string" ? void 0 : rf.env;
17096
17341
  const innerWhen = typeof rf === "string" ? void 0 : rf.when;
17097
17342
  if (innerWhen) {
17098
17343
  const shouldRun = await this.evaluateWhen(innerWhen);
17099
17344
  if (!shouldRun) {
17100
- console.log(`[flow] runFlow skipped (when predicate false): ${file}`);
17345
+ console.log(
17346
+ `[flow] runFlow skipped (when predicate false): ${file ?? "inline commands"}`
17347
+ );
17101
17348
  return;
17102
17349
  }
17103
17350
  }
17104
- const flowPath = path7.resolve(this.opts.flowDir, file);
17105
- const flowContent = fs7.readFileSync(flowPath, "utf8");
17106
- const previousDir = this.opts.flowDir;
17107
- this.opts.flowDir = path7.dirname(flowPath);
17108
- await this.runFlow(parseFlowSteps(flowContent));
17109
- this.opts.flowDir = previousDir;
17351
+ if (!file && !commands) {
17352
+ throw new Error("runFlow requires either file or commands");
17353
+ }
17354
+ this.js.enterEnvScope();
17355
+ try {
17356
+ if (env) {
17357
+ for (const [k, v] of Object.entries(env)) this.js.putEnv(k, v);
17358
+ }
17359
+ if (file) {
17360
+ const flowPath = path7.resolve(this.opts.flowDir, file);
17361
+ const flowContent = fs7.readFileSync(flowPath, "utf8");
17362
+ const previousDir = this.opts.flowDir;
17363
+ this.opts.flowDir = path7.dirname(flowPath);
17364
+ try {
17365
+ await this.runFlow(parseFlowSteps(flowContent));
17366
+ } finally {
17367
+ ;
17368
+ this.opts.flowDir = previousDir;
17369
+ }
17370
+ } else if (commands) {
17371
+ for (let i = 0; i < commands.length; i++) {
17372
+ await this.runStep(commands[i], i);
17373
+ }
17374
+ }
17375
+ } finally {
17376
+ this.js.leaveEnvScope();
17377
+ }
17110
17378
  } else {
17111
17379
  throw new Error(`unsupported flow step: ${JSON.stringify(step)}`);
17112
17380
  }
@@ -17159,7 +17427,7 @@ var init_bridge_flow_runner = __esm({
17159
17427
  const explicitWait = typeof step.wait === "number" ? step.wait : 0;
17160
17428
  const waitForTimeout = typeof step.waitFor?.timeout === "number" ? step.waitFor.timeout : 0;
17161
17429
  const stepCeiling = Math.max(6e4, explicitWait + 15e3, waitForTimeout + 15e3);
17162
- if (step.runFlow) {
17430
+ if (step.runFlow || step.launchApp) {
17163
17431
  await this.runStep(step, i);
17164
17432
  continue;
17165
17433
  }
@@ -17489,7 +17757,7 @@ var skill5 = {
17489
17757
  async execute(params, context) {
17490
17758
  const fs9 = await import("fs");
17491
17759
  const path9 = await import("path");
17492
- const yaml3 = await import("yaml");
17760
+ const yaml2 = await import("yaml");
17493
17761
  const { DEFAULT_SOOTSIM_BRIDGE_PORT: DEFAULT_SOOTSIM_BRIDGE_PORT2 } = await Promise.resolve().then(() => (init_bridge_constants(), bridge_constants_exports));
17494
17762
  const { SootSimBridgeHost: SootSimBridgeHost2 } = await Promise.resolve().then(() => (init_bridge_host(), bridge_host_exports));
17495
17763
  const { createBridge: createBridge2 } = await Promise.resolve().then(() => (init_ws_bridge(), ws_bridge_exports));
@@ -17500,7 +17768,7 @@ var skill5 = {
17500
17768
  const flowContent = fs9.readFileSync(params.flowPath, "utf8");
17501
17769
  const fmMatch = flowContent.match(/^---\n([\s\S]*?)\n---\n?/);
17502
17770
  const stepsBody = fmMatch ? flowContent.slice(fmMatch[0].length) : flowContent;
17503
- const steps = yaml3.parse(stepsBody);
17771
+ const steps = yaml2.parse(stepsBody);
17504
17772
  const beforeIds = new Set(host.listSims().map((sim) => sim.id));
17505
17773
  await host.openUrl(context.url);
17506
17774
  let simId = null;
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.140 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.142 | (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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sootsim",
3
- "version": "0.1.140",
3
+ "version": "0.1.142",
4
4
  "description": "sootsim CLI + vite/metro plugins + skills registry. bridge client for driving the proprietary sootsim-engine over WebSocket.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "MIT",
package/src/config.ts CHANGED
@@ -20,6 +20,15 @@ export interface SootSimConfig<
20
20
  turboModules?: Record<string, NativeModuleResolution>
21
21
  nativeModules?: Record<string, NativeModuleResolution>
22
22
  env?: Record<string, string>
23
+ // generic per-sim network rewrites for the guest app's runtime fetches.
24
+ // `remap` keys/values are `host:port` (or bare `host`) — a tenant fetch whose
25
+ // url matches a key has its host:port swapped for the value before the
26
+ // request is issued. exists for third-party e2e suites that hardcode a port
27
+ // (e.g. bluesky's flows type `http://localhost:3000`) which collides with a
28
+ // host service; map it to the real bound port without editing the app.
29
+ network?: {
30
+ remap?: Record<string, string>
31
+ }
23
32
  settings?: Partial<Settings>
24
33
  initialState?: {
25
34
  authenticated?: boolean
@@ -51,6 +60,7 @@ export function hasSootSimConfig<
51
60
  hasOwnKeys(config.turboModules as Record<string, unknown> | undefined) ||
52
61
  hasOwnKeys(config.nativeModules as Record<string, unknown> | undefined) ||
53
62
  hasOwnKeys(config.env as Record<string, unknown> | undefined) ||
63
+ hasOwnKeys(config.network?.remap as Record<string, unknown> | undefined) ||
54
64
  hasOwnKeys(config.settings as Record<string, unknown> | undefined) ||
55
65
  hasOwnKeys(config.initialState as Record<string, unknown> | undefined)
56
66
  )
@@ -74,6 +84,9 @@ export function mergeSootSimConfig<
74
84
  ...(override.nativeModules || {}),
75
85
  },
76
86
  env: { ...(base.env || {}), ...(override.env || {}) },
87
+ network: {
88
+ remap: { ...(base.network?.remap || {}), ...(override.network?.remap || {}) },
89
+ },
77
90
  settings: {
78
91
  ...(base.settings || {}),
79
92
  ...(override.settings || {}),