rnxsim 0.1.499 → 0.1.501

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 (71) hide show
  1. package/README.md +1 -1
  2. package/cli/bridge-flow-runner.ts +6 -2
  3. package/cli/cloud-client.ts +90 -7
  4. package/cli/cloud-dispatch.ts +27 -27
  5. package/cli/cloud-session.ts +61 -6
  6. package/cli/commands/control.ts +33 -31
  7. package/cli/commands/detox.ts +157 -159
  8. package/cli/commands/inspect/core.ts +2 -0
  9. package/cli/commands/platform.ts +170 -48
  10. package/cli/commands/preview.ts +1 -1
  11. package/cli/commands/test.ts +403 -95
  12. package/cli/main.ts +11 -0
  13. package/cli/outbound-endpoints.ts +10 -0
  14. package/cli/ws-bridge.ts +18 -19
  15. package/detox/jest-preset.cjs +1 -6
  16. package/dist-lib/agent-daemon-client.cjs +1 -1
  17. package/dist-lib/agent-events.cjs +1 -1
  18. package/dist-lib/agent-identity.cjs +1 -1
  19. package/dist-lib/agent-sessions.cjs +1 -1
  20. package/dist-lib/attached-projects.cjs +1 -1
  21. package/dist-lib/auth/shared-session.cjs +1 -1
  22. package/dist-lib/backend-origin.cjs +1 -1
  23. package/dist-lib/beta.cjs +1 -1
  24. package/dist-lib/beta.mjs +1 -1
  25. package/dist-lib/bridge-constants.cjs +1 -1
  26. package/dist-lib/bridge-contract-input.cjs +1 -1
  27. package/dist-lib/bridge-contract-input.mjs +1 -1
  28. package/dist-lib/bridge-contract.cjs +1 -1
  29. package/dist-lib/bridge-contract.mjs +1 -1
  30. package/dist-lib/capture-contract.cjs +1 -1
  31. package/dist-lib/capture-contract.mjs +1 -1
  32. package/dist-lib/cli-constants.cjs +1 -1
  33. package/dist-lib/cloud-contract.cjs +7 -1
  34. package/dist-lib/cloud-contract.mjs +5 -1
  35. package/dist-lib/cloud.cjs +1 -1
  36. package/dist-lib/cloud.mjs +1 -1
  37. package/dist-lib/config.cjs +1 -1
  38. package/dist-lib/detox/index.cjs +1 -1
  39. package/dist-lib/detox/jest-preset.cjs +1 -6
  40. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  41. package/dist-lib/home-paths.cjs +1 -1
  42. package/dist-lib/host/bridge-host.cjs +54 -29
  43. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  44. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  45. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  46. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  47. package/dist-lib/host/websocket-proxy.cjs +1 -1
  48. package/dist-lib/index.cjs +52 -22
  49. package/dist-lib/jump-to-source-babel.cjs +1 -1
  50. package/dist-lib/jump-to-source-native.cjs +1 -1
  51. package/dist-lib/menu.cjs +1 -1
  52. package/dist-lib/menu.mjs +1 -1
  53. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  54. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  55. package/dist-lib/metro-production-bundle.cjs +1 -1
  56. package/dist-lib/metro-production-bundle.mjs +1 -1
  57. package/dist-lib/metro.cjs +1 -1
  58. package/dist-lib/profiles.cjs +1 -1
  59. package/dist-lib/public-brand.cjs +1 -1
  60. package/dist-lib/react-native-host-modules.cjs +1 -1
  61. package/dist-lib/react-native-host-modules.mjs +1 -1
  62. package/dist-lib/render-mode.cjs +1 -1
  63. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  64. package/dist-lib/sdk.cjs +7 -8
  65. package/dist-lib/sdk.mjs +7 -8
  66. package/dist-lib/skills.cjs +128 -63
  67. package/dist-lib/vite.cjs +1 -1
  68. package/package.json +1 -1
  69. package/skills/rnx-test/SKILL.md +2 -2
  70. package/src/cloud-contract.ts +2 -0
  71. package/src/sim-client.ts +8 -10
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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;
@@ -742,6 +742,7 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
742
742
  result = await bridge.send(
743
743
  {
744
744
  type: "waitFor",
745
+ ...options.simId ? { simId: options.simId } : {},
745
746
  waitForOptions: { condition: { type: "ready" }, timeoutMs }
746
747
  },
747
748
  { timeoutMs: timeoutMs + 1e3 }
@@ -2046,21 +2047,19 @@ var SimClient = class {
2046
2047
  `sim ${this.simId} reported no URL to derive the shell from`
2047
2048
  );
2048
2049
  }
2049
- const nextUrl = buildSimOpenUrl(currentUrl, target);
2050
- const expected = new URL(nextUrl);
2050
+ const nextUrl = new URL(buildSimOpenUrl(currentUrl, target));
2051
+ const token = `sim-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
2052
+ nextUrl.searchParams.set("inspectOpen", token);
2051
2053
  await this.send({
2052
2054
  type: "evaluate",
2053
- code: `window.location.href = ${JSON.stringify(nextUrl)}`
2055
+ code: `window.location.href = ${JSON.stringify(nextUrl.toString())}`
2054
2056
  });
2055
2057
  const deadline = Date.now() + (opts.timeoutMs ?? 3e4);
2056
2058
  while (Date.now() < deadline) {
2057
2059
  await sleep(250);
2058
2060
  try {
2059
2061
  const { url } = await inspectUrl(this);
2060
- const current = new URL(url);
2061
- if (current.pathname === expected.pathname && current.searchParams.get("open") === expected.searchParams.get("open")) {
2062
- return;
2063
- }
2062
+ if (new URL(url).searchParams.get("inspectOpen") === token) return;
2064
2063
  } catch {
2065
2064
  }
2066
2065
  }
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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);
@@ -646,6 +646,7 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
646
646
  result = await bridge.send(
647
647
  {
648
648
  type: "waitFor",
649
+ ...options.simId ? { simId: options.simId } : {},
649
650
  waitForOptions: { condition: { type: "ready" }, timeoutMs }
650
651
  },
651
652
  { timeoutMs: timeoutMs + 1e3 }
@@ -1950,21 +1951,19 @@ var SimClient = class {
1950
1951
  `sim ${this.simId} reported no URL to derive the shell from`
1951
1952
  );
1952
1953
  }
1953
- const nextUrl = buildSimOpenUrl(currentUrl, target);
1954
- const expected = new URL(nextUrl);
1954
+ const nextUrl = new URL(buildSimOpenUrl(currentUrl, target));
1955
+ const token = `sim-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
1956
+ nextUrl.searchParams.set("inspectOpen", token);
1955
1957
  await this.send({
1956
1958
  type: "evaluate",
1957
- code: `window.location.href = ${JSON.stringify(nextUrl)}`
1959
+ code: `window.location.href = ${JSON.stringify(nextUrl.toString())}`
1958
1960
  });
1959
1961
  const deadline = Date.now() + (opts.timeoutMs ?? 3e4);
1960
1962
  while (Date.now() < deadline) {
1961
1963
  await sleep(250);
1962
1964
  try {
1963
1965
  const { url } = await inspectUrl(this);
1964
- const current = new URL(url);
1965
- if (current.pathname === expected.pathname && current.searchParams.get("open") === expected.searchParams.get("open")) {
1966
- return;
1967
- }
1966
+ if (new URL(url).searchParams.get("inspectOpen") === token) return;
1968
1967
  } catch {
1969
1968
  }
1970
1969
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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;
@@ -27477,7 +27477,10 @@ function createCloudSession(input) {
27477
27477
  simId: input.simId.trim(),
27478
27478
  apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
27479
27479
  claimId: input.claimId.trim(),
27480
- token: input.token
27480
+ token: input.token,
27481
+ ...input.platform ? { platform: input.platform } : {},
27482
+ ...input.device?.trim() ? { device: input.device.trim() } : {},
27483
+ ...input.artifact ? { artifact: input.artifact } : {}
27481
27484
  };
27482
27485
  if (!session.boxId || !session.boxToken || !session.simId || !session.claimId || !session.token) {
27483
27486
  throw new Error("cloud session requires box, simulator, origin, claim, and tokens");
@@ -27490,7 +27493,10 @@ function createCloudBoxSession(input) {
27490
27493
  service: "box",
27491
27494
  boxId: input.boxId.trim(),
27492
27495
  boxToken: input.boxToken,
27493
- apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim())
27496
+ apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
27497
+ ...input.platform ? { platform: input.platform } : {},
27498
+ ...input.device?.trim() ? { device: input.device.trim() } : {},
27499
+ ...input.artifact ? { artifact: input.artifact } : {}
27494
27500
  };
27495
27501
  if (!session.boxId || !session.boxToken) {
27496
27502
  throw new Error("cloud box session requires a box, an origin, and a box token");
@@ -27521,9 +27527,22 @@ function parseCloudSession(encoded) {
27521
27527
  if (typeof boxId !== "string" || typeof boxToken !== "string" || typeof apiOrigin !== "string") {
27522
27528
  throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
27523
27529
  }
27530
+ const platform = parsed.platform === "ios" || parsed.platform === "android" ? parsed.platform : void 0;
27531
+ const device = typeof parsed.device === "string" ? parsed.device : void 0;
27532
+ const artifact = isRecord4(parsed.artifact) && typeof parsed.artifact.sha256 === "string" ? {
27533
+ sha256: parsed.artifact.sha256,
27534
+ bytes: typeof parsed.artifact.bytes === "number" ? parsed.artifact.bytes : void 0
27535
+ } : void 0;
27524
27536
  try {
27525
27537
  if (parsed.service === "box") {
27526
- return createCloudBoxSession({ boxId, boxToken, apiOrigin });
27538
+ return createCloudBoxSession({
27539
+ boxId,
27540
+ boxToken,
27541
+ apiOrigin,
27542
+ platform,
27543
+ device,
27544
+ artifact
27545
+ });
27527
27546
  }
27528
27547
  const simId = parsed.simId;
27529
27548
  const claimId = parsed.claimId;
@@ -27531,7 +27550,17 @@ function parseCloudSession(encoded) {
27531
27550
  if (parsed.service !== "sim" || typeof simId !== "string" || typeof claimId !== "string" || typeof token !== "string") {
27532
27551
  throw new Error("unreadable cloud session");
27533
27552
  }
27534
- return createCloudSession({ boxId, boxToken, simId, apiOrigin, claimId, token });
27553
+ return createCloudSession({
27554
+ boxId,
27555
+ boxToken,
27556
+ simId,
27557
+ apiOrigin,
27558
+ claimId,
27559
+ token,
27560
+ platform,
27561
+ device,
27562
+ artifact
27563
+ });
27535
27564
  } catch {
27536
27565
  throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
27537
27566
  }
@@ -27555,26 +27584,6 @@ var init_cloud_session = __esm({
27555
27584
  });
27556
27585
 
27557
27586
  // cli/commands/detect.ts
27558
- function findProjectRoot(cwd) {
27559
- let dir = cwd;
27560
- while (true) {
27561
- if ((0, import_fs2.existsSync)((0, import_path2.join)(dir, "turbo.json")) || (0, import_fs2.existsSync)((0, import_path2.join)(dir, "nx.json")) || (0, import_fs2.existsSync)((0, import_path2.join)(dir, "lerna.json")) || (0, import_fs2.existsSync)((0, import_path2.join)(dir, "pnpm-workspace.yaml"))) {
27562
- return dir;
27563
- }
27564
- const pkgPath = (0, import_path2.join)(dir, "package.json");
27565
- if ((0, import_fs2.existsSync)(pkgPath)) {
27566
- try {
27567
- const pkg = JSON.parse((0, import_fs2.readFileSync)(pkgPath, "utf8"));
27568
- if (pkg.workspaces) return dir;
27569
- } catch {
27570
- }
27571
- }
27572
- const parent = (0, import_path2.dirname)(dir);
27573
- if (parent === dir) break;
27574
- dir = parent;
27575
- }
27576
- return cwd;
27577
- }
27578
27587
  function detectFramework2(deps) {
27579
27588
  if (deps.one) return "one";
27580
27589
  if (deps.expo) return "expo";
@@ -27851,9 +27860,28 @@ function searchMetroAssetDest(bundlePath, descriptors) {
27851
27860
  (0, import_node_path15.resolve)(bundleDirectory, "..", "assets")
27852
27861
  ];
27853
27862
  const cwd = process.cwd();
27854
- if (detectProject(cwd)?.hasMetroConfig) {
27855
- const root = findProjectRoot(cwd);
27856
- candidates.push((0, import_node_path15.join)(root, "dist"), (0, import_node_path15.join)(root, "build"));
27863
+ let projectDirectory = null;
27864
+ for (const start of [bundleDirectory, cwd]) {
27865
+ let directory = (0, import_node_path15.resolve)(start);
27866
+ while (true) {
27867
+ if (detectProject(directory)?.hasMetroConfig) {
27868
+ projectDirectory = directory;
27869
+ break;
27870
+ }
27871
+ const parent = (0, import_node_path15.dirname)(directory);
27872
+ if (parent === directory) break;
27873
+ directory = parent;
27874
+ }
27875
+ if (projectDirectory) break;
27876
+ }
27877
+ if (projectDirectory) {
27878
+ candidates.push(
27879
+ (0, import_node_path15.join)(projectDirectory, "assets"),
27880
+ (0, import_node_path15.join)(projectDirectory, "dist"),
27881
+ (0, import_node_path15.join)(projectDirectory, "dist", "assets"),
27882
+ (0, import_node_path15.join)(projectDirectory, "build"),
27883
+ (0, import_node_path15.join)(projectDirectory, "build", "assets")
27884
+ );
27857
27885
  }
27858
27886
  const searched = [];
27859
27887
  const holders = [];
@@ -28050,6 +28078,10 @@ function readCreateResponse(value, expectedBytes, expectedSha256) {
28050
28078
  };
28051
28079
  }
28052
28080
  async function createCloudBox(options) {
28081
+ const platform = options.platform ?? "ios";
28082
+ if (platform === "android") {
28083
+ throw new UnsupportedRemoteHostError("android", "nano");
28084
+ }
28053
28085
  const apiOrigin = resolveCloudOrigin(options.apiOrigin);
28054
28086
  const artifact = await produceCloudArtifact(options.bundlePath);
28055
28087
  const boxName = options.boxName?.trim();
@@ -28057,7 +28089,7 @@ async function createCloudBox(options) {
28057
28089
  authorization: options.authorization,
28058
28090
  "x-rnx-box-size": "nano",
28059
28091
  "x-rnx-artifact-sha256": artifact.sha256,
28060
- "x-rnx-platform": "ios",
28092
+ "x-rnx-platform": platform,
28061
28093
  "x-rnx-device": options.device,
28062
28094
  ...options.accountId ? { "x-rnx-account-id": options.accountId } : null,
28063
28095
  ...boxName ? { "x-rnx-box-name": boxName } : null
@@ -28103,7 +28135,13 @@ async function createCloudBox(options) {
28103
28135
  simId: created.receipt.simulator.simId,
28104
28136
  apiOrigin,
28105
28137
  claimId: created.receipt.simulator.claim.id,
28106
- token: created.simulatorToken
28138
+ token: created.simulatorToken,
28139
+ platform,
28140
+ device: options.device,
28141
+ artifact: {
28142
+ sha256: artifact.sha256,
28143
+ bytes: artifact.bytes.length
28144
+ }
28107
28145
  });
28108
28146
  return {
28109
28147
  receipt: created.receipt,
@@ -28146,7 +28184,7 @@ function createCloudBridgeForParsed(parsed) {
28146
28184
  if (!session) return null;
28147
28185
  return session.service === "box" ? new CloudBoxBridge(session) : new CloudBridge(session);
28148
28186
  }
28149
- var import_node_crypto5, import_node_fs14, import_node_path15, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS, CloudBridge, CloudBoxBridge;
28187
+ var import_node_crypto5, import_node_fs14, import_node_path15, UnsupportedRemoteHostError, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS, CloudBridge, CloudBoxBridge;
28150
28188
  var init_cloud_client = __esm({
28151
28189
  "cli/cloud-client.ts"() {
28152
28190
  "use strict";
@@ -28164,9 +28202,22 @@ var init_cloud_client = __esm({
28164
28202
  init_auth();
28165
28203
  init_cloud_session();
28166
28204
  init_detect();
28205
+ UnsupportedRemoteHostError = class extends Error {
28206
+ code = "unsupported_host";
28207
+ platform;
28208
+ host;
28209
+ constructor(platform, host, message) {
28210
+ super(
28211
+ message ?? `rnx remote ${platform} does not support --${host}: rnxsim/cloud only creates iOS simulators`
28212
+ );
28213
+ this.name = "UnsupportedRemoteHostError";
28214
+ this.platform = platform;
28215
+ this.host = host;
28216
+ }
28217
+ };
28167
28218
  DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
28168
28219
  MAX_RNX_CLOUD_BUNDLE_BYTES = RNX_CLOUD_MAX_ARTIFACT_BYTES;
28169
- REMOTE_COMMAND = "rnx ios --remote";
28220
+ REMOTE_COMMAND = "rnx remote";
28170
28221
  MAX_ASSET_DEST_FILES = 25e3;
28171
28222
  ACCOUNT_CHOICE_REFUSALS = /* @__PURE__ */ new Set([
28172
28223
  "accountId is required",
@@ -31103,15 +31154,17 @@ function createBridge(wsPort, opts = {}) {
31103
31154
  let noticedTarget = false;
31104
31155
  const hostPidBySimId = /* @__PURE__ */ new Map();
31105
31156
  const lineageLookupBySimId = /* @__PURE__ */ new Map();
31106
- function pickReloadedSuccessor(sims, retiredSimId) {
31157
+ function pickReloadedTarget(sims, retiredSimId) {
31107
31158
  const pinnedHostPid = hostPidBySimId.get(retiredSimId);
31108
31159
  if (pinnedHostPid === void 0) return null;
31109
- const successors = (Array.isArray(sims) ? sims : []).filter(
31110
- (sim) => sim.id !== retiredSimId && sim.readyState === "open" && sim.meta?.sootsimHostPid === pinnedHostPid
31160
+ const matches = (Array.isArray(sims) ? sims : []).filter(
31161
+ (sim) => sim.readyState === "open" && sim.meta?.sootsimHostPid === pinnedHostPid
31111
31162
  );
31112
- if (successors.length !== 1) return null;
31113
- hostPidBySimId.set(successors[0].id, pinnedHostPid);
31114
- return successors[0].id;
31163
+ const restored = matches.find((sim) => sim.id === retiredSimId);
31164
+ if (restored) return restored.id;
31165
+ if (matches.length !== 1) return null;
31166
+ hostPidBySimId.set(matches[0].id, pinnedHostPid);
31167
+ return matches[0].id;
31115
31168
  }
31116
31169
  function maybeNoticeTarget(cmdType, simId) {
31117
31170
  if (noticedTarget) return;
@@ -31229,7 +31282,7 @@ function createBridge(wsPort, opts = {}) {
31229
31282
  const message = error instanceof Error ? error.message : String(error);
31230
31283
  const missingSimMessage = `no sim connected with id ${sentSimId}`;
31231
31284
  if (opts.simIdSource !== "flag" && sentSimId && cmd.simId === void 0 && (message === missingSimMessage || message.startsWith(`${missingSimMessage};`))) {
31232
- const successor = pickReloadedSuccessor(
31285
+ const successor = pickReloadedTarget(
31233
31286
  await sendOnce(void 0, { type: "bridge:list-sims" }),
31234
31287
  sentSimId
31235
31288
  );
@@ -31255,10 +31308,10 @@ function createBridge(wsPort, opts = {}) {
31255
31308
  },
31256
31309
  // a caller holding its own pinned sim id (a flow runner sets simId on every
31257
31310
  // command, so it never reaches the pin-following path in send) asks here
31258
- // after a reload retires that id. null means nothing proves what replaced
31259
- // it, and the caller must fail rather than guess.
31311
+ // after a reload retires its socket. the page can reconnect under a new id
31312
+ // or restore the old id; null means no unique same-host target exists.
31260
31313
  async resolveReloadedSim(retiredSimId) {
31261
- return pickReloadedSuccessor(await this.listSims(), retiredSimId);
31314
+ return pickReloadedTarget(await this.listSims(), retiredSimId);
31262
31315
  },
31263
31316
  async focusSim(simId) {
31264
31317
  return this.send({ type: "focus", simId });
@@ -32574,6 +32627,7 @@ async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
32574
32627
  result = await bridge.send(
32575
32628
  {
32576
32629
  type: "waitFor",
32630
+ ...options.simId ? { simId: options.simId } : {},
32577
32631
  waitForOptions: { condition: { type: "ready" }, timeoutMs }
32578
32632
  },
32579
32633
  { timeoutMs: timeoutMs + 1e3 }
@@ -34875,11 +34929,13 @@ async function runOpenCommand(args, opts = {}) {
34875
34929
  printBridgeWorldNotice(parsed.wsPort, baseUrl);
34876
34930
  const savedSimId = readCurrentSimId();
34877
34931
  const shouldReuseCurrentSim = !openInNewSim;
34932
+ const token = `cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
34933
+ const simMatchesToken = (sim) => sim.url?.includes(`inspectOpen=${token}`) ?? false;
34878
34934
  if (shouldReuseCurrentSim) {
34879
34935
  const bridge = createBridgeFromParsed(parsed);
34880
34936
  const simHint = parsed.simId ? ` --sim ${parsed.simId}` : "";
34881
34937
  let fallBackToNewSim = false;
34882
- let preNavSimIds = /* @__PURE__ */ new Set();
34938
+ let navigatedShellUrl = null;
34883
34939
  try {
34884
34940
  let targetSim = null;
34885
34941
  let targetShellUrl = null;
@@ -34929,17 +34985,18 @@ async function runOpenCommand(args, opts = {}) {
34929
34985
  }
34930
34986
  }
34931
34987
  if (!fallBackToNewSim && targetSim) {
34932
- preNavSimIds = new Set(sims.map((sim) => sim.id));
34933
34988
  const resolvedBaseUrl = hasExplicitBaseUrl || looksLikeSootsimUrl(target) ? baseUrl : deriveCurrentSimBaseUrl(targetSim, baseUrl);
34934
- const shellUrl = targetShellUrl ?? await buildShellUrl(
34989
+ navigatedShellUrl = targetShellUrl ?? await buildShellUrl(
34935
34990
  target,
34936
34991
  applyRNXConfigToUrl(resolvedBaseUrl, runtimeConfig),
34937
34992
  buildShellUrlOpts
34938
34993
  );
34994
+ const navigationUrl = new URL(navigatedShellUrl);
34995
+ navigationUrl.searchParams.set("inspectOpen", token);
34939
34996
  bridge.send({
34940
34997
  type: "evaluate",
34941
34998
  simId: targetSim.id,
34942
- code: `window.location.href = ${JSON.stringify(shellUrl)}`
34999
+ code: `window.location.href = ${JSON.stringify(navigationUrl.toString())}`
34943
35000
  }).catch(() => {
34944
35001
  });
34945
35002
  }
@@ -34954,18 +35011,25 @@ async function runOpenCommand(args, opts = {}) {
34954
35011
  });
34955
35012
  rnxExit(1);
34956
35013
  }
34957
- if (!fallBackToNewSim && targetSim) {
34958
- await sleep2(1500);
34959
- const navSim = targetSim;
34960
- const reloadedSim = await waitForSimMatch(
35014
+ if (!fallBackToNewSim && targetSim && navigatedShellUrl) {
35015
+ const navigatedSim = await waitForSimMatch(
34961
35016
  parsed.wsPort,
34962
35017
  parsed.commandTimeoutMs,
34963
- (sim) => sim.readyState === "open" && simUsesDriver(sim, driverId) && (sim.id === navSim.id || !preNavSimIds.has(sim.id) && sim.isPrimary),
35018
+ (sim) => sim.readyState === "open" && simUsesDriver(sim, driverId) && simMatchesToken(sim),
34964
35019
  { attempts: Math.round(30 * flowTimeoutScale()) }
34965
35020
  );
34966
- if (reloadedSim && reloadedSim.id !== navSim.id) {
34967
- targetSim = reloadedSim;
35021
+ if (!navigatedSim) {
35022
+ console.error(
35023
+ ` timed out waiting for sim ${targetSim.id} to load ${navigatedShellUrl}`
35024
+ );
35025
+ await printBridgeFailureDiagnostics(bridge, {
35026
+ errorsCommand: `rnx get errors 5${simHint}`,
35027
+ warningsCommand: `rnx get warnings 5${simHint}`,
35028
+ requestsCommand: `rnx get requests 5${simHint}`
35029
+ });
35030
+ rnxExit(1);
34968
35031
  }
35032
+ targetSim = navigatedSim;
34969
35033
  const ready = await waitForSimReady(
34970
35034
  parsed.wsPort,
34971
35035
  parsed.commandTimeoutMs,
@@ -34985,13 +35049,12 @@ async function runOpenCommand(args, opts = {}) {
34985
35049
  }
34986
35050
  ready.bridge.close();
34987
35051
  saveCurrentSimId(targetSim.id);
34988
- const resolvedBaseUrl = hasExplicitBaseUrl || looksLikeSootsimUrl(target) ? baseUrl : deriveCurrentSimBaseUrl(targetSim, baseUrl);
34989
- const shellUrl = targetShellUrl ?? await buildShellUrl(
34990
- target,
34991
- applyRNXConfigToUrl(resolvedBaseUrl, runtimeConfig),
34992
- buildShellUrlOpts
35052
+ printOpenedSim(
35053
+ navigatedShellUrl,
35054
+ { ...targetSim, url: navigatedShellUrl },
35055
+ "current sim",
35056
+ quiet
34993
35057
  );
34994
- printOpenedSim(shellUrl, { ...targetSim, url: shellUrl }, "current sim", quiet);
34995
35058
  await maybeDescribeAfterOpen(parsed.wsPort, targetSim.id, args);
34996
35059
  return parsed.wsPort;
34997
35060
  }
@@ -35000,7 +35063,6 @@ async function runOpenCommand(args, opts = {}) {
35000
35063
  }
35001
35064
  }
35002
35065
  const configuredBaseUrl = applyRNXConfigToUrl(baseUrl, runtimeConfig);
35003
- const token = `cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
35004
35066
  const openUrl2 = await buildOpenUrl(target, configuredBaseUrl, token, buildShellUrlOpts);
35005
35067
  const ownerPid = getStableOwnerPid();
35006
35068
  if (!ownerPid) {
@@ -35009,7 +35071,6 @@ async function runOpenCommand(args, opts = {}) {
35009
35071
  );
35010
35072
  rnxExit(1);
35011
35073
  }
35012
- const simMatchesToken = (sim) => sim.url?.includes(`inspectOpen=${token}`) ?? false;
35013
35074
  const driver = getDriver(driverId);
35014
35075
  if (!driver) {
35015
35076
  console.error(` unknown driver "${driverId}" \u2014 run \`rnx list --drivers\``);
@@ -36562,7 +36623,9 @@ var init_bridge_flow_runner = __esm({
36562
36623
  });
36563
36624
  }
36564
36625
  async waitForTree(timeout = DEFAULT_TIMEOUT) {
36565
- let status = await inspectWaitReady(this.bridge, scaledTimeout(timeout));
36626
+ let status = await inspectWaitReady(this.bridge, scaledTimeout(timeout), {
36627
+ simId: this.opts.simId
36628
+ });
36566
36629
  if (status.ready) return;
36567
36630
  if (status.externalError) {
36568
36631
  throw new Error(`app failed to load: ${status.externalError}`);
@@ -36584,7 +36647,9 @@ var init_bridge_flow_runner = __esm({
36584
36647
  );
36585
36648
  this.opts.simId = successor;
36586
36649
  saveCurrentSimId(successor);
36587
- status = await inspectWaitReady(this.bridge, scaledTimeout(timeout));
36650
+ status = await inspectWaitReady(this.bridge, scaledTimeout(timeout), {
36651
+ simId: this.opts.simId
36652
+ });
36588
36653
  if (status.ready) return;
36589
36654
  }
36590
36655
  }
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.499 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.501 | (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.499",
3
+ "version": "0.1.501",
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",
@@ -238,7 +238,7 @@ upstream detox surface.
238
238
  ```js
239
239
  // rnx-detox.config.cjs
240
240
  module.exports = {
241
- preset: 'rnxsim/detox/jest-preset',
241
+ preset: 'rnxsim/detox',
242
242
  rootDir: __dirname,
243
243
  testMatch: ['<rootDir>/e2e/**/*.test.ts'],
244
244
  }
@@ -294,7 +294,7 @@ rnx detox init # scaffold config + e2e/example.test.ts
294
294
  - **"no rnx shell reachable"** — start one yourself
295
295
  (`rnx open <port>`) or pass `--port <n>`.
296
296
  - **"jest cannot find module 'detox'"** — the preset isn't wired. confirm
297
- `preset: 'rnxsim/detox/jest-preset'` is in the jest config jest
297
+ `preset: 'rnxsim/detox'` is in the jest config jest
298
298
  actually loaded (`--config` overrides package.json).
299
299
  - **matchers fail on elements that exist** — canvas has no DOM. use
300
300
  `by.id` (testID); `rnx describe` shows what the shell sees.
@@ -3,6 +3,8 @@ import type { BridgeCommandType } from './bridge-contract.ts'
3
3
  export const RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024
4
4
  export const RNX_CLOUD_DESIGN_MAX_STORAGE_ENTRIES = 256
5
5
  export const RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024
6
+ export const RNX_CLOUD_TEST_FILES_MAX_COUNT = 256
7
+ export const RNX_CLOUD_TEST_FILES_MAX_BYTES = 500_000
6
8
 
7
9
  export const RNX_CLOUD_COMMAND_TYPES = Object.freeze([
8
10
  'state',
package/src/sim-client.ts CHANGED
@@ -310,24 +310,22 @@ export class SimClient implements WsBridge, InspectBridge {
310
310
  `sim ${this.simId} reported no URL to derive the shell from`,
311
311
  )
312
312
  }
313
- const nextUrl = buildSimOpenUrl(currentUrl, target)
314
- const expected = new URL(nextUrl)
313
+ const nextUrl = new URL(buildSimOpenUrl(currentUrl, target))
314
+ // the page being replaced keeps answering until the browser commits the
315
+ // navigation, and it may already sit on this target. only a one-off token
316
+ // in the new URL proves the reloaded page is the one answering.
317
+ const token = `sim-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
318
+ nextUrl.searchParams.set('inspectOpen', token)
315
319
  await this.send({
316
320
  type: 'evaluate',
317
- code: `window.location.href = ${JSON.stringify(nextUrl)}`,
321
+ code: `window.location.href = ${JSON.stringify(nextUrl.toString())}`,
318
322
  })
319
323
  const deadline = Date.now() + (opts.timeoutMs ?? 30_000)
320
324
  while (Date.now() < deadline) {
321
325
  await sleep(250)
322
326
  try {
323
327
  const { url } = await inspectUrl(this)
324
- const current = new URL(url)
325
- if (
326
- current.pathname === expected.pathname &&
327
- current.searchParams.get('open') === expected.searchParams.get('open')
328
- ) {
329
- return
330
- }
328
+ if (new URL(url).searchParams.get('inspectOpen') === token) return
331
329
  } catch {
332
330
  // page is mid-reload; keep polling until the deadline.
333
331
  }