rnxsim 0.1.487 → 0.1.488

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 (57) hide show
  1. package/cli/outbound-endpoints.ts +2 -2
  2. package/cli/ws-bridge.ts +1 -1
  3. package/detox/index.ts +108 -9
  4. package/dist-lib/agent-daemon-client.cjs +2 -2
  5. package/dist-lib/agent-events.cjs +1 -1
  6. package/dist-lib/agent-identity.cjs +1 -1
  7. package/dist-lib/agent-sessions.cjs +1 -1
  8. package/dist-lib/attached-projects.cjs +1 -1
  9. package/dist-lib/auth/shared-session.cjs +1 -1
  10. package/dist-lib/backend-origin.cjs +1 -1
  11. package/dist-lib/beta.cjs +1 -1
  12. package/dist-lib/beta.mjs +1 -1
  13. package/dist-lib/bridge-constants.cjs +2 -2
  14. package/dist-lib/bridge-contract-input.cjs +1 -1
  15. package/dist-lib/bridge-contract-input.mjs +1 -1
  16. package/dist-lib/bridge-contract.cjs +1 -1
  17. package/dist-lib/bridge-contract.mjs +1 -1
  18. package/dist-lib/capture-contract.cjs +1 -1
  19. package/dist-lib/capture-contract.mjs +1 -1
  20. package/dist-lib/cli-constants.cjs +1 -1
  21. package/dist-lib/cloud-contract.cjs +1 -1
  22. package/dist-lib/cloud-contract.mjs +1 -1
  23. package/dist-lib/cloud.cjs +1 -1
  24. package/dist-lib/cloud.mjs +1 -1
  25. package/dist-lib/config.cjs +1 -1
  26. package/dist-lib/detox/index.cjs +99 -10
  27. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  28. package/dist-lib/home-paths.cjs +1 -1
  29. package/dist-lib/host/bridge-host.cjs +1 -1
  30. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  31. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  32. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  33. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  34. package/dist-lib/host/websocket-proxy.cjs +1 -1
  35. package/dist-lib/index.cjs +2 -2
  36. package/dist-lib/jump-to-source-babel.cjs +1 -1
  37. package/dist-lib/jump-to-source-native.cjs +1 -1
  38. package/dist-lib/menu.cjs +1 -1
  39. package/dist-lib/menu.mjs +1 -1
  40. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  41. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  42. package/dist-lib/metro-production-bundle.cjs +1 -1
  43. package/dist-lib/metro-production-bundle.mjs +1 -1
  44. package/dist-lib/metro.cjs +1 -1
  45. package/dist-lib/profiles.cjs +1 -1
  46. package/dist-lib/public-brand.cjs +1 -1
  47. package/dist-lib/react-native-host-modules.cjs +1 -1
  48. package/dist-lib/react-native-host-modules.mjs +1 -1
  49. package/dist-lib/render-mode.cjs +1 -1
  50. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  51. package/dist-lib/sdk.cjs +1 -1
  52. package/dist-lib/sdk.mjs +1 -1
  53. package/dist-lib/skills.cjs +3 -3
  54. package/dist-lib/vite.cjs +1 -1
  55. package/package.json +1 -1
  56. package/src/agent-daemon-client.ts +1 -1
  57. package/src/bridge-constants.ts +6 -1
@@ -485,7 +485,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
485
485
  count: 1,
486
486
  },
487
487
  // the inventory records the call expression, not the URL, so this entry can
488
- // no longer show its own destination. bridgeWsUrl() is localhost-only today;
488
+ // no longer show its own destination. bridgeWsUrl() is loopback-only today;
489
489
  // when a cloud box makes it return a remote wss:// endpoint, that egress
490
490
  // would land under this same local_development entry and go unnoticed. split
491
491
  // the declaration then, rather than letting one line cover both.
@@ -497,7 +497,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
497
497
  category: 'local_development',
498
498
  count: 1,
499
499
  },
500
- 'packages/sootsim/src/agent-daemon-client.ts :: websocket :: `ws://localhost:${this.port}`':
500
+ 'packages/sootsim/src/agent-daemon-client.ts :: websocket :: `ws://127.0.0.1:${this.port}`':
501
501
  { category: 'local_development', count: 1 },
502
502
  "packages/sootsim/src/auth/shared-session.ts :: fetch :: `${origin.replace(/\\/$/, '')}/api/auth/me`":
503
503
  { category: 'contrast_user_action', count: 1 },
package/cli/ws-bridge.ts CHANGED
@@ -75,7 +75,7 @@ export function resolvePlacement(): RnxPlacement {
75
75
  * this function. a cloud endpoint added here must get its own declaration in
76
76
  * `outbound-endpoints.ts` instead of inheriting the local_development one. */
77
77
  export function bridgeWsUrl(wsPort: number): string {
78
- return `ws://localhost:${wsPort}`
78
+ return `ws://127.0.0.1:${wsPort}`
79
79
  }
80
80
 
81
81
  // resolved once per CLI process so a multi-step command keeps one world for
package/detox/index.ts CHANGED
@@ -801,6 +801,102 @@ async function dispatchStatusBarOverride(config: StatusBarConfig) {
801
801
  await page.waitForTimeout(50)
802
802
  }
803
803
 
804
+ const MAX_SETTLED_SPEED_PT_PER_SEC = 2.0
805
+
806
+ async function waitForFrameClock(page: Page, minMs = 25): Promise<void> {
807
+ const start = performance.now()
808
+ await page.evaluate(
809
+ () =>
810
+ new Promise<void>((resolve) => {
811
+ const timeoutId = setTimeout(() => resolve(), 50)
812
+ requestAnimationFrame(() => {
813
+ clearTimeout(timeoutId)
814
+ resolve()
815
+ })
816
+ }),
817
+ )
818
+ const elapsed = performance.now() - start
819
+ if (elapsed < minMs) {
820
+ await page.waitForTimeout(minMs - elapsed)
821
+ }
822
+ }
823
+
824
+ async function findStableNodeByMatcher(
825
+ matcher: Matcher,
826
+ action = 'tap',
827
+ maxWaitMs = 1500,
828
+ ): Promise<any> {
829
+ const page = getPage()
830
+ const start = performance.now()
831
+ let lastPos: { x: number; y: number } | null = null
832
+ let lastTime: number | null = null
833
+ let lastRate: { vx: number; vy: number; speed: number } | null = null
834
+ let sampleCount = 0
835
+ let stablePolls = 0
836
+ const requiredStable = 2
837
+ let hadNumericLayout = false
838
+ let disappearedMidWait = false
839
+
840
+ while (performance.now() - start < maxWaitMs) {
841
+ const node = await findNodeByMatcher(matcher)
842
+ if (!node) {
843
+ if (hadNumericLayout) {
844
+ disappearedMidWait = true
845
+ } else {
846
+ return null
847
+ }
848
+ } else {
849
+ disappearedMidWait = false
850
+ const pos = node.absolutePosition ?? node.layout
851
+ if (pos && typeof pos.x === 'number' && typeof pos.y === 'number') {
852
+ hadNumericLayout = true
853
+ sampleCount++
854
+ const now = performance.now()
855
+ if (lastPos && lastTime !== null) {
856
+ const dtSec = (now - lastTime) / 1000
857
+ if (dtSec > 0.005) {
858
+ const vx = Math.abs(pos.x - lastPos.x) / dtSec
859
+ const vy = Math.abs(pos.y - lastPos.y) / dtSec
860
+ const speed = Math.sqrt(vx * vx + vy * vy)
861
+ lastRate = { vx, vy, speed }
862
+ if (speed < MAX_SETTLED_SPEED_PT_PER_SEC) {
863
+ stablePolls++
864
+ if (stablePolls >= requiredStable) {
865
+ return node
866
+ }
867
+ } else {
868
+ stablePolls = 0
869
+ }
870
+ }
871
+ }
872
+ lastPos = { x: pos.x, y: pos.y }
873
+ lastTime = now
874
+ }
875
+ }
876
+ await waitForFrameClock(page, 25)
877
+ }
878
+
879
+ const elapsed = Math.round(performance.now() - start)
880
+ if (disappearedMidWait) {
881
+ throw new Error(
882
+ `element disappeared while waiting to come to rest for ${action}: ${JSON.stringify(matcher)} after ${elapsed}ms`,
883
+ )
884
+ }
885
+ if (!hadNumericLayout) {
886
+ throw new Error(
887
+ `element layout is not available for ${action}: ${JSON.stringify(matcher)} after ${elapsed}ms`,
888
+ )
889
+ }
890
+ if (sampleCount < 2 || lastRate === null) {
891
+ throw new Error(
892
+ `element did not come to rest for ${action}: ${JSON.stringify(matcher)}; only one sample obtained after ${elapsed}ms`,
893
+ )
894
+ }
895
+ throw new Error(
896
+ `element did not come to rest for ${action}: ${JSON.stringify(matcher)}; still moving ${lastRate.vx.toFixed(1)} points/sec x / ${lastRate.vy.toFixed(1)} points/sec y after ${elapsed}ms`,
897
+ )
898
+ }
899
+
804
900
  function createSootSimElement(matcher: Matcher): SootSimElement {
805
901
  const el: SootSimElement = {
806
902
  _matcher: matcher,
@@ -808,7 +904,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
808
904
  async tap(point) {
809
905
  const page = getPage()
810
906
  await waitForSootsimWorkletsIdle(page, 3000)
811
- const node = await findNodeByMatcher(matcher)
907
+ const node = await findStableNodeByMatcher(matcher, 'tap')
812
908
  if (!node) throw new Error(`element not found for tap: ${JSON.stringify(matcher)}`)
813
909
  if (!isNodeVisible(node)) {
814
910
  throw new Error(
@@ -837,7 +933,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
837
933
  // center with a small inter-tap pause that stays inside RNGH's
838
934
  // default maxDelay window of 200ms.
839
935
  const page = getPage()
840
- const node = await findNodeByMatcher(matcher)
936
+ const node = await findStableNodeByMatcher(matcher, 'multiTap')
841
937
  if (!node)
842
938
  throw new Error(`element not found for multiTap: ${JSON.stringify(matcher)}`)
843
939
  if (!isNodeVisible(node)) {
@@ -867,7 +963,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
867
963
  durationArg = 1000,
868
964
  ) {
869
965
  const page = getPage()
870
- const node = await findNodeByMatcher(matcher)
966
+ const node = await findStableNodeByMatcher(matcher, 'longPress')
871
967
  if (!node)
872
968
  throw new Error(`element not found for longPress: ${JSON.stringify(matcher)}`)
873
969
  if (!isNodeVisible(node)) {
@@ -911,12 +1007,15 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
911
1007
  ) {
912
1008
  const page = getPage()
913
1009
  // resolve source element
914
- const srcNode = await findNodeByMatcher(matcher)
1010
+ const srcNode = await findStableNodeByMatcher(matcher, 'longPressAndDrag')
915
1011
  if (!srcNode)
916
1012
  throw new Error(`source element not found: ${JSON.stringify(matcher)}`)
917
1013
 
918
1014
  // resolve target element
919
- const tgtNode = await findNodeByMatcher(targetElement._matcher)
1015
+ const tgtNode = await findStableNodeByMatcher(
1016
+ targetElement._matcher,
1017
+ 'longPressAndDrag',
1018
+ )
920
1019
  if (!tgtNode)
921
1020
  throw new Error(
922
1021
  `target element not found: ${JSON.stringify(targetElement._matcher)}`,
@@ -1098,7 +1197,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1098
1197
  normalizedStartingPointX = 0.5,
1099
1198
  normalizedStartingPointY = 0.5,
1100
1199
  ) {
1101
- const node = await findNodeByMatcher(matcher)
1200
+ const node = await findStableNodeByMatcher(matcher, 'swipe')
1102
1201
  if (!node)
1103
1202
  throw new Error(`element not found for swipe: ${JSON.stringify(matcher)}`)
1104
1203
 
@@ -1172,7 +1271,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1172
1271
  throw new Error(`picker value must be a string, got ${typeof value}`)
1173
1272
  }
1174
1273
 
1175
- const picker = await findNodeByMatcher(matcher)
1274
+ const picker = await findStableNodeByMatcher(matcher, 'setColumnToValue')
1176
1275
  if (!picker) {
1177
1276
  throw new Error(
1178
1277
  `element not found for setColumnToValue: ${JSON.stringify(matcher)}`,
@@ -1249,7 +1348,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1249
1348
  // velocity tracking sees a realistic delta. angle (radians) tilts the
1250
1349
  // pinch axis off horizontal — matches Detox's third arg.
1251
1350
  const page = getPage()
1252
- const node = await findNodeByMatcher(matcher)
1351
+ const node = await findStableNodeByMatcher(matcher, 'pinch')
1253
1352
  if (!node)
1254
1353
  throw new Error(`element not found for pinch: ${JSON.stringify(matcher)}`)
1255
1354
  if (!node.nativePinchEnabled) {
@@ -1284,7 +1383,7 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
1284
1383
  // sign (its 'rotation' delta is positive for a counter-clockwise
1285
1384
  // motion of the rear finger relative to the front finger).
1286
1385
  const page = getPage()
1287
- const node = await findNodeByMatcher(matcher)
1386
+ const node = await findStableNodeByMatcher(matcher, 'rotate')
1288
1387
  if (!node)
1289
1388
  throw new Error(`element not found for rotate: ${JSON.stringify(matcher)}`)
1290
1389
  await waitForSootsimGestureHandler(page, node.nodeId, 'RotationGestureHandler')
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -241,7 +241,7 @@ var AgentDaemonClient = class {
241
241
  constructor(opts = {}) {
242
242
  this.port = opts.port ?? DEFAULT_SOOTSIM_BRIDGE_PORT;
243
243
  this.commandTimeoutMs = opts.commandTimeoutMs ?? 15e3;
244
- this.ws = new import_ws.WebSocket(`ws://localhost:${this.port}`);
244
+ this.ws = new import_ws.WebSocket(`ws://127.0.0.1:${this.port}`);
245
245
  this.ready = new Promise((resolve, reject) => {
246
246
  const onOpen = () => {
247
247
  this.ws.off("error", onError);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -136,7 +136,7 @@ function resolveSootsimWebSocketProxyUrl(input) {
136
136
  }
137
137
  function resolveSootsimBridgeWebSocketUrl(input) {
138
138
  return resolveSootsimWebSocketProxyUrl({
139
- targetUrl: `ws://localhost:${input.bridgePort}`,
139
+ targetUrl: `ws://127.0.0.1:${input.bridgePort}`,
140
140
  href: input.href
141
141
  });
142
142
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_DEFAULT_MS = 500;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -2448,13 +2448,99 @@ async function dispatchStatusBarOverride(config) {
2448
2448
  );
2449
2449
  await page.waitForTimeout(50);
2450
2450
  }
2451
+ var MAX_SETTLED_SPEED_PT_PER_SEC = 2;
2452
+ async function waitForFrameClock(page, minMs = 25) {
2453
+ const start = performance.now();
2454
+ await page.evaluate(
2455
+ () => new Promise((resolve) => {
2456
+ const timeoutId = setTimeout(() => resolve(), 50);
2457
+ requestAnimationFrame(() => {
2458
+ clearTimeout(timeoutId);
2459
+ resolve();
2460
+ });
2461
+ })
2462
+ );
2463
+ const elapsed = performance.now() - start;
2464
+ if (elapsed < minMs) {
2465
+ await page.waitForTimeout(minMs - elapsed);
2466
+ }
2467
+ }
2468
+ async function findStableNodeByMatcher(matcher, action = "tap", maxWaitMs = 1500) {
2469
+ const page = getPage();
2470
+ const start = performance.now();
2471
+ let lastPos = null;
2472
+ let lastTime = null;
2473
+ let lastRate = null;
2474
+ let sampleCount = 0;
2475
+ let stablePolls = 0;
2476
+ const requiredStable = 2;
2477
+ let hadNumericLayout = false;
2478
+ let disappearedMidWait = false;
2479
+ while (performance.now() - start < maxWaitMs) {
2480
+ const node = await findNodeByMatcher(matcher);
2481
+ if (!node) {
2482
+ if (hadNumericLayout) {
2483
+ disappearedMidWait = true;
2484
+ } else {
2485
+ return null;
2486
+ }
2487
+ } else {
2488
+ disappearedMidWait = false;
2489
+ const pos = node.absolutePosition ?? node.layout;
2490
+ if (pos && typeof pos.x === "number" && typeof pos.y === "number") {
2491
+ hadNumericLayout = true;
2492
+ sampleCount++;
2493
+ const now = performance.now();
2494
+ if (lastPos && lastTime !== null) {
2495
+ const dtSec = (now - lastTime) / 1e3;
2496
+ if (dtSec > 5e-3) {
2497
+ const vx = Math.abs(pos.x - lastPos.x) / dtSec;
2498
+ const vy = Math.abs(pos.y - lastPos.y) / dtSec;
2499
+ const speed = Math.sqrt(vx * vx + vy * vy);
2500
+ lastRate = { vx, vy, speed };
2501
+ if (speed < MAX_SETTLED_SPEED_PT_PER_SEC) {
2502
+ stablePolls++;
2503
+ if (stablePolls >= requiredStable) {
2504
+ return node;
2505
+ }
2506
+ } else {
2507
+ stablePolls = 0;
2508
+ }
2509
+ }
2510
+ }
2511
+ lastPos = { x: pos.x, y: pos.y };
2512
+ lastTime = now;
2513
+ }
2514
+ }
2515
+ await waitForFrameClock(page, 25);
2516
+ }
2517
+ const elapsed = Math.round(performance.now() - start);
2518
+ if (disappearedMidWait) {
2519
+ throw new Error(
2520
+ `element disappeared while waiting to come to rest for ${action}: ${JSON.stringify(matcher)} after ${elapsed}ms`
2521
+ );
2522
+ }
2523
+ if (!hadNumericLayout) {
2524
+ throw new Error(
2525
+ `element layout is not available for ${action}: ${JSON.stringify(matcher)} after ${elapsed}ms`
2526
+ );
2527
+ }
2528
+ if (sampleCount < 2 || lastRate === null) {
2529
+ throw new Error(
2530
+ `element did not come to rest for ${action}: ${JSON.stringify(matcher)}; only one sample obtained after ${elapsed}ms`
2531
+ );
2532
+ }
2533
+ throw new Error(
2534
+ `element did not come to rest for ${action}: ${JSON.stringify(matcher)}; still moving ${lastRate.vx.toFixed(1)} points/sec x / ${lastRate.vy.toFixed(1)} points/sec y after ${elapsed}ms`
2535
+ );
2536
+ }
2451
2537
  function createSootSimElement(matcher) {
2452
2538
  const el = {
2453
2539
  _matcher: matcher,
2454
2540
  async tap(point) {
2455
2541
  const page = getPage();
2456
2542
  await waitForSootsimWorkletsIdle(page, 3e3);
2457
- const node = await findNodeByMatcher(matcher);
2543
+ const node = await findStableNodeByMatcher(matcher, "tap");
2458
2544
  if (!node) throw new Error(`element not found for tap: ${JSON.stringify(matcher)}`);
2459
2545
  if (!isNodeVisible(node)) {
2460
2546
  throw new Error(
@@ -2471,7 +2557,7 @@ function createSootSimElement(matcher) {
2471
2557
  },
2472
2558
  async multiTap(times) {
2473
2559
  const page = getPage();
2474
- const node = await findNodeByMatcher(matcher);
2560
+ const node = await findStableNodeByMatcher(matcher, "multiTap");
2475
2561
  if (!node)
2476
2562
  throw new Error(`element not found for multiTap: ${JSON.stringify(matcher)}`);
2477
2563
  if (!isNodeVisible(node)) {
@@ -2496,7 +2582,7 @@ function createSootSimElement(matcher) {
2496
2582
  // where the point is relative to the element's origin
2497
2583
  async longPress(pointOrDuration, durationArg = 1e3) {
2498
2584
  const page = getPage();
2499
- const node = await findNodeByMatcher(matcher);
2585
+ const node = await findStableNodeByMatcher(matcher, "longPress");
2500
2586
  if (!node)
2501
2587
  throw new Error(`element not found for longPress: ${JSON.stringify(matcher)}`);
2502
2588
  if (!isNodeVisible(node)) {
@@ -2526,10 +2612,13 @@ function createSootSimElement(matcher) {
2526
2612
  },
2527
2613
  async longPressAndDrag(duration, normalizedStartX, normalizedStartY, targetElement, normalizedEndX, normalizedEndY, speed = "fast", holdDuration = 0) {
2528
2614
  const page = getPage();
2529
- const srcNode = await findNodeByMatcher(matcher);
2615
+ const srcNode = await findStableNodeByMatcher(matcher, "longPressAndDrag");
2530
2616
  if (!srcNode)
2531
2617
  throw new Error(`source element not found: ${JSON.stringify(matcher)}`);
2532
- const tgtNode = await findNodeByMatcher(targetElement._matcher);
2618
+ const tgtNode = await findStableNodeByMatcher(
2619
+ targetElement._matcher,
2620
+ "longPressAndDrag"
2621
+ );
2533
2622
  if (!tgtNode)
2534
2623
  throw new Error(
2535
2624
  `target element not found: ${JSON.stringify(targetElement._matcher)}`
@@ -2651,7 +2740,7 @@ function createSootSimElement(matcher) {
2651
2740
  }
2652
2741
  },
2653
2742
  async swipe(direction, speed = "fast", percentage = 0.75, normalizedStartingPointX = 0.5, normalizedStartingPointY = 0.5) {
2654
- const node = await findNodeByMatcher(matcher);
2743
+ const node = await findStableNodeByMatcher(matcher, "swipe");
2655
2744
  if (!node)
2656
2745
  throw new Error(`element not found for swipe: ${JSON.stringify(matcher)}`);
2657
2746
  const page = getPage();
@@ -2704,7 +2793,7 @@ function createSootSimElement(matcher) {
2704
2793
  if (typeof value !== "string") {
2705
2794
  throw new Error(`picker value must be a string, got ${typeof value}`);
2706
2795
  }
2707
- const picker = await findNodeByMatcher(matcher);
2796
+ const picker = await findStableNodeByMatcher(matcher, "setColumnToValue");
2708
2797
  if (!picker) {
2709
2798
  throw new Error(
2710
2799
  `element not found for setColumnToValue: ${JSON.stringify(matcher)}`
@@ -2760,7 +2849,7 @@ function createSootSimElement(matcher) {
2760
2849
  },
2761
2850
  async pinch(scale, speed = "fast", angle = 0) {
2762
2851
  const page = getPage();
2763
- const node = await findNodeByMatcher(matcher);
2852
+ const node = await findStableNodeByMatcher(matcher, "pinch");
2764
2853
  if (!node)
2765
2854
  throw new Error(`element not found for pinch: ${JSON.stringify(matcher)}`);
2766
2855
  if (!node.nativePinchEnabled) {
@@ -2789,7 +2878,7 @@ function createSootSimElement(matcher) {
2789
2878
  },
2790
2879
  async rotate(radians, speed = "fast") {
2791
2880
  const page = getPage();
2792
- const node = await findNodeByMatcher(matcher);
2881
+ const node = await findStableNodeByMatcher(matcher, "rotate");
2793
2882
  if (!node)
2794
2883
  throw new Error(`element not found for rotate: ${JSON.stringify(matcher)}`);
2795
2884
  await waitForSootsimGestureHandler(page, node.nodeId, "RotationGestureHandler");
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
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";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -904,7 +904,7 @@ var DEFAULT_SOOTSIM_SHELL_URL = "http://localhost:5173/";
904
904
 
905
905
  // cli/ws-bridge.ts
906
906
  function bridgeWsUrl(wsPort) {
907
- return `ws://localhost:${wsPort}`;
907
+ return `ws://127.0.0.1:${wsPort}`;
908
908
  }
909
909
  var resolvedWorld = null;
910
910
  function liveDevBridgeLockfiles() {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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/dist-lib/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "rnx";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-fingerprint-registry.ts
4
4
  var RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-production-bundle.ts
4
4
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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;
@@ -19233,7 +19233,7 @@ function resolveSootsimWebSocketProxyUrl(input) {
19233
19233
  }
19234
19234
  function resolveSootsimBridgeWebSocketUrl(input) {
19235
19235
  return resolveSootsimWebSocketProxyUrl({
19236
- targetUrl: `ws://localhost:${input.bridgePort}`,
19236
+ targetUrl: `ws://127.0.0.1:${input.bridgePort}`,
19237
19237
  href: input.href
19238
19238
  });
19239
19239
  }
@@ -30949,7 +30949,7 @@ function resolvePlacement() {
30949
30949
  return process.env[RNX_PLACEMENT_ENV] === "local-box" ? "local-box" : "host";
30950
30950
  }
30951
30951
  function bridgeWsUrl(wsPort) {
30952
- return `ws://localhost:${wsPort}`;
30952
+ return `ws://127.0.0.1:${wsPort}`;
30953
30953
  }
30954
30954
  function liveDevBridgeLockfiles() {
30955
30955
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.487 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.488 | (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": "rnxsim",
3
- "version": "0.1.487",
3
+ "version": "0.1.488",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for rnx.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -95,7 +95,7 @@ export class AgentDaemonClient {
95
95
  constructor(opts: AgentDaemonClientOptions = {}) {
96
96
  this.port = opts.port ?? DEFAULT_SOOTSIM_BRIDGE_PORT
97
97
  this.commandTimeoutMs = opts.commandTimeoutMs ?? 15_000
98
- this.ws = new WebSocket(`ws://localhost:${this.port}`)
98
+ this.ws = new WebSocket(`ws://127.0.0.1:${this.port}`)
99
99
  this.ready = new Promise<void>((resolve, reject) => {
100
100
  const onOpen = () => {
101
101
  this.ws.off('error', onError)
@@ -148,8 +148,13 @@ export function resolveSootsimBridgeWebSocketUrl(input: {
148
148
  bridgePort: number
149
149
  href: string
150
150
  }): string {
151
+ // 127.0.0.1, not localhost. the daemon binds ipv4 only (bridge-host bindOnce)
152
+ // so that localhost's ::1/127.0.0.1 coinflip cannot hide it; the health probe
153
+ // already talks to 127.0.0.1. targeting localhost here undoes that: when node
154
+ // tries ::1 first and does not fall back, vite's proxy answers 502 and the
155
+ // browser logs a console error that fails e2e.
151
156
  return resolveSootsimWebSocketProxyUrl({
152
- targetUrl: `ws://localhost:${input.bridgePort}`,
157
+ targetUrl: `ws://127.0.0.1:${input.bridgePort}`,
153
158
  href: input.href,
154
159
  })
155
160
  }