rnxsim 0.1.519 → 0.1.521

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/cli/cloud-client.ts +419 -287
  2. package/cli/cloud-dispatch.ts +2 -21
  3. package/cli/cloud-session.ts +31 -85
  4. package/cli/commands/box.ts +15 -11
  5. package/cli/commands/inspect.ts +34 -1
  6. package/cli/commands/platform.ts +184 -232
  7. package/cli/main.ts +10 -2
  8. package/cli/outbound-endpoints.ts +21 -6
  9. package/dist-lib/agent-daemon-client.cjs +1 -1
  10. package/dist-lib/agent-events.cjs +1 -1
  11. package/dist-lib/agent-identity.cjs +1 -1
  12. package/dist-lib/agent-sessions.cjs +1 -1
  13. package/dist-lib/attached-projects.cjs +1 -1
  14. package/dist-lib/auth/shared-session.cjs +1 -1
  15. package/dist-lib/backend-origin.cjs +1 -1
  16. package/dist-lib/beta.cjs +1 -1
  17. package/dist-lib/beta.mjs +1 -1
  18. package/dist-lib/bridge-constants.cjs +1 -1
  19. package/dist-lib/bridge-contract-input.cjs +7 -2
  20. package/dist-lib/bridge-contract-input.mjs +7 -2
  21. package/dist-lib/bridge-contract.cjs +1 -1
  22. package/dist-lib/bridge-contract.mjs +1 -1
  23. package/dist-lib/capture-contract.cjs +1 -1
  24. package/dist-lib/capture-contract.mjs +1 -1
  25. package/dist-lib/cli-constants.cjs +1 -1
  26. package/dist-lib/cloud-contract.cjs +1 -1
  27. package/dist-lib/cloud-contract.mjs +1 -1
  28. package/dist-lib/cloud.cjs +18 -2
  29. package/dist-lib/cloud.mjs +18 -2
  30. package/dist-lib/config.cjs +1 -1
  31. package/dist-lib/detox/index.cjs +1 -1
  32. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  33. package/dist-lib/home-paths.cjs +1 -1
  34. package/dist-lib/host/bridge-host.cjs +112 -39
  35. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  36. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  37. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  38. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  39. package/dist-lib/host/websocket-proxy.cjs +1 -1
  40. package/dist-lib/index.cjs +44 -117
  41. package/dist-lib/jump-to-source-babel.cjs +1 -1
  42. package/dist-lib/jump-to-source-native.cjs +1 -1
  43. package/dist-lib/menu.cjs +30 -1
  44. package/dist-lib/menu.mjs +27 -1
  45. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  46. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  47. package/dist-lib/metro-production-bundle.cjs +1 -1
  48. package/dist-lib/metro-production-bundle.mjs +1 -1
  49. package/dist-lib/metro.cjs +1 -1
  50. package/dist-lib/profiles.cjs +1 -1
  51. package/dist-lib/public-brand.cjs +1 -1
  52. package/dist-lib/react-native-host-modules.cjs +1 -1
  53. package/dist-lib/react-native-host-modules.mjs +1 -1
  54. package/dist-lib/render-mode.cjs +1 -1
  55. package/dist-lib/scripts/dev-server-scanner.cjs +41 -4
  56. package/dist-lib/sdk.cjs +1 -1
  57. package/dist-lib/sdk.mjs +1 -1
  58. package/dist-lib/skills.cjs +138 -164
  59. package/dist-lib/swift.cjs +766 -24
  60. package/dist-lib/vite.cjs +103 -30
  61. package/package.json +1 -1
  62. package/scripts/dev-server-scanner.ts +72 -5
  63. package/src/bridge-contract-input.ts +10 -0
  64. package/src/bridge-contract.ts +5 -0
  65. package/src/cloud-contract.ts +7 -1
  66. package/src/cloud.ts +45 -6
  67. package/src/connect.ts +1 -1
  68. package/src/menu.ts +44 -0
  69. package/src/swift.ts +2 -0
  70. package/src/vite-plugin-one.ts +12 -0
  71. package/src/vite-plugin-swift.ts +141 -39
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -320,37 +320,26 @@ function normalizeCloudOrigin(source) {
320
320
  return parsed.origin;
321
321
  }
322
322
  function createCloudSession(input) {
323
+ const boxId = input.boxId?.trim() ?? "";
324
+ const boxToken = input.boxToken ?? "";
325
+ if (boxId === "" !== (boxToken === "")) {
326
+ throw new Error("cloud session names a box with both its id and its token");
327
+ }
323
328
  const session = {
324
329
  version: CLOUD_SESSION_VERSION,
325
330
  service: "sim",
326
- boxId: input.boxId.trim(),
327
- boxToken: input.boxToken,
328
331
  simId: input.simId.trim(),
329
332
  apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
330
333
  claimId: input.claimId.trim(),
331
334
  token: input.token,
335
+ ...input.streamUrl ? { streamUrl: input.streamUrl } : {},
332
336
  ...input.platform ? { platform: input.platform } : {},
333
337
  ...input.device?.trim() ? { device: input.device.trim() } : {},
334
- ...input.artifact ? { artifact: input.artifact } : {}
335
- };
336
- if (!session.boxId || !session.boxToken || !session.simId || !session.claimId || !session.token) {
337
- throw new Error("cloud session requires box, simulator, origin, claim, and tokens");
338
- }
339
- return session;
340
- }
341
- function createCloudBoxSession(input) {
342
- const session = {
343
- version: CLOUD_SESSION_VERSION,
344
- service: "box",
345
- boxId: input.boxId.trim(),
346
- boxToken: input.boxToken,
347
- apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
348
- ...input.platform ? { platform: input.platform } : {},
349
- ...input.device?.trim() ? { device: input.device.trim() } : {},
350
- ...input.artifact ? { artifact: input.artifact } : {}
338
+ ...input.artifact ? { artifact: input.artifact } : {},
339
+ ...boxId && boxToken ? { boxId, boxToken } : {}
351
340
  };
352
- if (!session.boxId || !session.boxToken) {
353
- throw new Error("cloud box session requires a box, an origin, and a box token");
341
+ if (!session.simId || !session.claimId || !session.token) {
342
+ throw new Error("cloud session requires a simulator, an origin, a claim, and a token");
354
343
  }
355
344
  return session;
356
345
  }
@@ -372,10 +361,8 @@ function parseCloudSession(encoded) {
372
361
  if (!isRecord(parsed) || parsed.version !== CLOUD_SESSION_VERSION) {
373
362
  throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
374
363
  }
375
- const boxId = parsed.boxId;
376
- const boxToken = parsed.boxToken;
377
364
  const apiOrigin = parsed.apiOrigin;
378
- if (typeof boxId !== "string" || typeof boxToken !== "string" || typeof apiOrigin !== "string") {
365
+ if (typeof apiOrigin !== "string") {
379
366
  throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
380
367
  }
381
368
  const platform = parsed.platform === "ios" || parsed.platform === "android" ? parsed.platform : void 0;
@@ -384,17 +371,10 @@ function parseCloudSession(encoded) {
384
371
  sha256: parsed.artifact.sha256,
385
372
  bytes: typeof parsed.artifact.bytes === "number" ? parsed.artifact.bytes : void 0
386
373
  } : void 0;
374
+ const streamUrl = typeof parsed.streamUrl === "string" ? parsed.streamUrl : void 0;
375
+ const boxId = typeof parsed.boxId === "string" ? parsed.boxId : void 0;
376
+ const boxToken = typeof parsed.boxToken === "string" ? parsed.boxToken : void 0;
387
377
  try {
388
- if (parsed.service === "box") {
389
- return createCloudBoxSession({
390
- boxId,
391
- boxToken,
392
- apiOrigin,
393
- platform,
394
- device,
395
- artifact
396
- });
397
- }
398
378
  const simId = parsed.simId;
399
379
  const claimId = parsed.claimId;
400
380
  const token = parsed.token;
@@ -402,15 +382,16 @@ function parseCloudSession(encoded) {
402
382
  throw new Error("unreadable cloud session");
403
383
  }
404
384
  return createCloudSession({
405
- boxId,
406
- boxToken,
407
385
  simId,
408
386
  apiOrigin,
409
387
  claimId,
410
388
  token,
389
+ streamUrl,
411
390
  platform,
412
391
  device,
413
- artifact
392
+ artifact,
393
+ boxId,
394
+ boxToken
414
395
  });
415
396
  } catch {
416
397
  throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
@@ -435,7 +416,8 @@ function failureSentence(text) {
435
416
  }
436
417
  if (!isRecord2(parsed)) return null;
437
418
  if (isRecord2(parsed.error) && typeof parsed.error.message === "string") {
438
- return parsed.error.message;
419
+ const code = typeof parsed.error.code === "string" && parsed.error.code ? ` [${parsed.error.code}]` : "";
420
+ return `${parsed.error.message}${code}`;
439
421
  }
440
422
  if (typeof parsed.message === "string") return parsed.message;
441
423
  if (typeof parsed.error === "string") return parsed.error;
@@ -455,14 +437,6 @@ function responseError(status, text) {
455
437
  function resolveCloudSessionForParsed(parsed) {
456
438
  const session = readCloudSession();
457
439
  if (!session) return null;
458
- if (session.service === "box") {
459
- if (parsed.simIdSource === "flag" && parsed.simId !== session.boxId) {
460
- throw new Error(
461
- `this shell is connected to box ${session.boxId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
462
- );
463
- }
464
- return session;
465
- }
466
440
  if (parsed.simIdSource === "flag" && parsed.simId !== session.simId) {
467
441
  throw new Error(
468
442
  `this shell is connected to remote simulator ${session.simId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
@@ -558,77 +532,10 @@ var CloudBridge = class {
558
532
  close() {
559
533
  }
560
534
  };
561
- var CloudBoxBridge = class {
562
- constructor(session) {
563
- this.session = session;
564
- }
565
- plane = "cloud";
566
- async send(command, options) {
567
- const id = (0, import_node_crypto.randomUUID)();
568
- const payload = {};
569
- for (const [key, value] of Object.entries(command)) {
570
- if (key !== "id" && key !== "simId") payload[key] = value;
571
- }
572
- let response;
573
- try {
574
- response = await fetch(
575
- `${this.session.apiOrigin}/v1/boxes/${encodeURIComponent(this.session.boxId)}/commands`,
576
- {
577
- method: "POST",
578
- headers: {
579
- authorization: `Bearer ${this.session.boxToken}`,
580
- "content-type": "application/json"
581
- },
582
- body: JSON.stringify({ id, command: payload }),
583
- ...options?.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
584
- }
585
- );
586
- } catch (error) {
587
- throw new Error(
588
- `could not reach box ${this.session.boxId} at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
589
- );
590
- }
591
- const text = await response.text();
592
- if (!response.ok) throw responseError(response.status, text);
593
- let parsed;
594
- try {
595
- parsed = JSON.parse(text);
596
- } catch {
597
- throw new Error("box command returned unreadable JSON");
598
- }
599
- if (!isRecord2(parsed) || parsed.id !== id) {
600
- throw new Error("box command returned an invalid command receipt");
601
- }
602
- if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
603
- if (!Object.hasOwn(parsed, "result")) {
604
- throw new Error("box command receipt has no result");
605
- }
606
- return parsed.result;
607
- }
608
- listSims() {
609
- return Promise.reject(new Error("a box does not expose a local simulator list"));
610
- }
611
- focusSim() {
612
- return Promise.reject(new Error("a box simulator has no browser window to focus"));
613
- }
614
- resolveReloadedSim() {
615
- return Promise.resolve(null);
616
- }
617
- closeSim() {
618
- return Promise.reject(new Error("close a box simulator with rnx box stop"));
619
- }
620
- claim() {
621
- return Promise.reject(
622
- new Error("a box simulator is held by its Box page and has nothing to claim")
623
- );
624
- }
625
- close() {
626
- }
627
- };
628
535
  function createCloudBridgeForParsed(parsed) {
629
536
  const session = resolveCloudSessionForParsed(parsed);
630
537
  if (!session) return null;
631
- return session.service === "box" ? new CloudBoxBridge(session) : new CloudBridge(session);
538
+ return new CloudBridge(session);
632
539
  }
633
540
 
634
541
  // cli/current-sim.ts
@@ -3060,7 +2967,7 @@ async function connect(options = {}) {
3060
2967
  const bridge2 = createCloudBridgeForParsed(target);
3061
2968
  if (!bridge2)
3062
2969
  throw new SimConnectError("remote simulator session disappeared while connecting");
3063
- return new SimClient(bridge2, cloud.service === "box" ? cloud.boxId : cloud.simId, {
2970
+ return new SimClient(bridge2, cloud.simId, {
3064
2971
  commandTimeoutMs: options.timeoutMs ?? 15e3
3065
2972
  });
3066
2973
  }
@@ -3922,17 +3829,37 @@ var import_node_child_process2 = require("node:child_process");
3922
3829
  var import_node_os2 = __toESM(require("node:os"), 1);
3923
3830
  var import_node_path6 = __toESM(require("node:path"), 1);
3924
3831
  var import_node_util = require("node:util");
3832
+
3833
+ // ../sootsim-swift/toolchain.json
3834
+ var toolchain_default = {
3835
+ release: "swift-6.3-RELEASE",
3836
+ wasmSdkChecksum: "9fa4016ee632c7e9e906608ec3b55cf13dfc4dff44e47574c5af58064dc33fd9"
3837
+ };
3838
+
3839
+ // src/vite-plugin-swift.ts
3925
3840
  var run = (0, import_node_util.promisify)(import_node_child_process2.execFile);
3926
3841
  var TOOLCHAIN_BIN = import_node_path6.default.join(
3927
3842
  import_node_os2.default.homedir(),
3928
- "Library/Developer/Toolchains/swift-6.2-RELEASE.xctoolchain/usr/bin"
3843
+ `Library/Developer/Toolchains/${toolchain_default.release}.xctoolchain/usr/bin`
3929
3844
  );
3845
+ var WASM_SDK = `${toolchain_default.release}_wasm`;
3930
3846
 
3931
3847
  // src/vite-plugin-one.ts
3932
3848
  function rnxPlugin(options = {}) {
3933
3849
  const prefix2 = options.prefix || "/__soot";
3934
3850
  return {
3935
3851
  name: "sootsim-one",
3852
+ // use-latest-callback ships a CJS main (module.exports, no default) with
3853
+ // an esm.mjs shim that re-imports it. react-native-bottom-tabs
3854
+ // default-imports it from source served live, so vite hands the browser
3855
+ // the raw CJS file and the import fails with 'does not provide an export
3856
+ // named default' before anything mounts. forcing it through dep
3857
+ // optimization lets esbuild's CJS interop answer the default import, and
3858
+ // every bare import of it (including the nested copy's) resolves to the
3859
+ // one pre-bundled instance.
3860
+ config() {
3861
+ return { optimizeDeps: { include: ["use-latest-callback"] } };
3862
+ },
3936
3863
  configureServer(server) {
3937
3864
  if (options.enabled === false) return;
3938
3865
  const bundleUrl = options.bundleUrl || "/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -25,6 +25,8 @@ __export(menu_exports, {
25
25
  SOOTSIM_AMBIENT_GLOW_LABELS: () => SOOTSIM_AMBIENT_GLOW_LABELS,
26
26
  SOOTSIM_AMBIENT_GLOW_LEVELS: () => SOOTSIM_AMBIENT_GLOW_LEVELS,
27
27
  SOOTSIM_ENGINE_EVENT_NAME: () => SOOTSIM_ENGINE_EVENT_NAME,
28
+ SOOTSIM_HOME_SCREEN_VERSIONS: () => SOOTSIM_HOME_SCREEN_VERSIONS,
29
+ SOOTSIM_HOME_SCREEN_VERSION_LABELS: () => SOOTSIM_HOME_SCREEN_VERSION_LABELS,
28
30
  SOOTSIM_SHELL_COMMAND_NAME: () => SOOTSIM_SHELL_COMMAND_NAME,
29
31
  buildSootSimDeviceChoiceDefs: () => buildSootSimDeviceChoiceDefs,
30
32
  buildSootSimMenuDefs: () => buildSootSimMenuDefs,
@@ -34,6 +36,7 @@ __export(menu_exports, {
34
36
  getShortcutDocSections: () => getShortcutDocSections,
35
37
  getShortcutDocSectionsFromDefs: () => getShortcutDocSectionsFromDefs,
36
38
  isSootSimAmbientGlowLevel: () => isSootSimAmbientGlowLevel,
39
+ isSootSimHomeScreenVersion: () => isSootSimHomeScreenVersion,
37
40
  matchesAccelerator: () => matchesAccelerator
38
41
  });
39
42
  module.exports = __toCommonJS(menu_exports);
@@ -60,6 +63,14 @@ var SOOTSIM_AMBIENT_GLOW_LABELS = {
60
63
  soft: "Soft",
61
64
  full: "Full"
62
65
  };
66
+ var SOOTSIM_HOME_SCREEN_VERSIONS = ["ios26", "ios27"];
67
+ function isSootSimHomeScreenVersion(value) {
68
+ return value === "ios26" || value === "ios27";
69
+ }
70
+ var SOOTSIM_HOME_SCREEN_VERSION_LABELS = {
71
+ ios26: "iOS 26",
72
+ ios27: "iOS 27"
73
+ };
63
74
  function buildSootSimMenuDefs(state, deviceChoices) {
64
75
  return [
65
76
  appMenu(),
@@ -393,6 +404,21 @@ function windowMenu(state) {
393
404
  actionPayload: level
394
405
  }))
395
406
  },
407
+ // both eras stay selectable while the ios 27 home surfaces land one at a
408
+ // time, and the device's own os picks the starting value. the switch is
409
+ // the iPhone Duo's, so it is offered exactly where the Duo is.
410
+ ...state.developmentMode ? [
411
+ {
412
+ label: "Home Screen",
413
+ items: SOOTSIM_HOME_SCREEN_VERSIONS.map((version) => ({
414
+ label: SOOTSIM_HOME_SCREEN_VERSION_LABELS[version],
415
+ type: "radio",
416
+ checked: state.homeScreenVersion === version,
417
+ action: "set-home-screen",
418
+ actionPayload: version
419
+ }))
420
+ }
421
+ ] : [],
396
422
  {
397
423
  label: "Wireframe",
398
424
  type: "checkbox",
@@ -604,6 +630,8 @@ function matchesAccelerator(event, accelerator) {
604
630
  SOOTSIM_AMBIENT_GLOW_LABELS,
605
631
  SOOTSIM_AMBIENT_GLOW_LEVELS,
606
632
  SOOTSIM_ENGINE_EVENT_NAME,
633
+ SOOTSIM_HOME_SCREEN_VERSIONS,
634
+ SOOTSIM_HOME_SCREEN_VERSION_LABELS,
607
635
  SOOTSIM_SHELL_COMMAND_NAME,
608
636
  buildSootSimDeviceChoiceDefs,
609
637
  buildSootSimMenuDefs,
@@ -613,5 +641,6 @@ function matchesAccelerator(event, accelerator) {
613
641
  getShortcutDocSections,
614
642
  getShortcutDocSectionsFromDefs,
615
643
  isSootSimAmbientGlowLevel,
644
+ isSootSimHomeScreenVersion,
616
645
  matchesAccelerator
617
646
  });
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "rnx";
@@ -22,6 +22,14 @@ var SOOTSIM_AMBIENT_GLOW_LABELS = {
22
22
  soft: "Soft",
23
23
  full: "Full"
24
24
  };
25
+ var SOOTSIM_HOME_SCREEN_VERSIONS = ["ios26", "ios27"];
26
+ function isSootSimHomeScreenVersion(value) {
27
+ return value === "ios26" || value === "ios27";
28
+ }
29
+ var SOOTSIM_HOME_SCREEN_VERSION_LABELS = {
30
+ ios26: "iOS 26",
31
+ ios27: "iOS 27"
32
+ };
25
33
  function buildSootSimMenuDefs(state, deviceChoices) {
26
34
  return [
27
35
  appMenu(),
@@ -355,6 +363,21 @@ function windowMenu(state) {
355
363
  actionPayload: level
356
364
  }))
357
365
  },
366
+ // both eras stay selectable while the ios 27 home surfaces land one at a
367
+ // time, and the device's own os picks the starting value. the switch is
368
+ // the iPhone Duo's, so it is offered exactly where the Duo is.
369
+ ...state.developmentMode ? [
370
+ {
371
+ label: "Home Screen",
372
+ items: SOOTSIM_HOME_SCREEN_VERSIONS.map((version) => ({
373
+ label: SOOTSIM_HOME_SCREEN_VERSION_LABELS[version],
374
+ type: "radio",
375
+ checked: state.homeScreenVersion === version,
376
+ action: "set-home-screen",
377
+ actionPayload: version
378
+ }))
379
+ }
380
+ ] : [],
358
381
  {
359
382
  label: "Wireframe",
360
383
  type: "checkbox",
@@ -565,6 +588,8 @@ export {
565
588
  SOOTSIM_AMBIENT_GLOW_LABELS,
566
589
  SOOTSIM_AMBIENT_GLOW_LEVELS,
567
590
  SOOTSIM_ENGINE_EVENT_NAME,
591
+ SOOTSIM_HOME_SCREEN_VERSIONS,
592
+ SOOTSIM_HOME_SCREEN_VERSION_LABELS,
568
593
  SOOTSIM_SHELL_COMMAND_NAME,
569
594
  buildSootSimDeviceChoiceDefs,
570
595
  buildSootSimMenuDefs,
@@ -574,5 +599,6 @@ export {
574
599
  getShortcutDocSections,
575
600
  getShortcutDocSectionsFromDefs,
576
601
  isSootSimAmbientGlowLevel,
602
+ isSootSimHomeScreenVersion,
577
603
  matchesAccelerator
578
604
  };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -826,12 +826,32 @@ function safeParseManifest(body) {
826
826
  return null;
827
827
  }
828
828
  }
829
+ var localIconPathCache = /* @__PURE__ */ new Map();
830
+ var localIconProbeInFlight = /* @__PURE__ */ new Set();
831
+ function metroAssetPath(rawIcon) {
832
+ return `/assets/${rawIcon.replace(/^\.?\//, "")}`;
833
+ }
834
+ function staticAssetPath(rawIcon) {
835
+ return `/${rawIcon.replace(/^\.?\//, "")}`;
836
+ }
837
+ async function resolveLocalIconPath(hostname, port, rawIcon) {
838
+ const candidates = [metroAssetPath(rawIcon), staticAssetPath(rawIcon)];
839
+ for (const candidate of candidates) {
840
+ const res = await httpGet(hostname, port, candidate, "HEAD", ENRICH_TIMEOUT_MS);
841
+ if (res?.statusCode === 200) {
842
+ localIconPathCache.set(port, candidate);
843
+ portCache.delete(port);
844
+ return;
845
+ }
846
+ }
847
+ }
829
848
  function applyManifest(result, manifestRes, buildIconProxyUrl) {
830
849
  if (!manifestRes) return result;
831
850
  try {
832
851
  const manifest = JSON.parse(manifestRes.body);
833
852
  const client = manifest?.extra?.expoClient || manifest?.extra || {};
834
853
  if (client.name) result.projectName = client.name;
854
+ if (typeof client.primaryColor === "string") result.primaryColor = client.primaryColor;
835
855
  if (client.ios?.bundleIdentifier) result.bundleId = client.ios.bundleIdentifier;
836
856
  if (result.framework === "metro" && client.sdkVersion) result.framework = "expo";
837
857
  const rawLaunchUrl = manifest?.launchAsset?.url;
@@ -863,9 +883,9 @@ function applyManifest(result, manifestRes, buildIconProxyUrl) {
863
883
  resolvedIconUrl = rawIcon;
864
884
  }
865
885
  } else {
866
- const cleanPath = rawIcon.replace(/^\.?\//, "");
867
- const assetPath = cleanPath.startsWith("assets/") ? `/${cleanPath}` : `/assets/${cleanPath}`;
868
- const localUrl = `${buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port)}${assetPath}`;
886
+ const base = buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port);
887
+ const assetPath = localIconPathCache.get(result.port) ?? metroAssetPath(rawIcon);
888
+ const localUrl = `${base}${assetPath}`;
869
889
  resolvedIconUrl = buildIconProxyUrl ? buildIconProxyUrl(localUrl) : localUrl;
870
890
  }
871
891
  if (resolvedIconUrl) {
@@ -890,7 +910,20 @@ function __applyManifestForTests(result, manifestBody, buildIconProxyUrl) {
890
910
  var ENRICH_TIMEOUT_MS = 2e4;
891
911
  var enrichmentCache = /* @__PURE__ */ new Map();
892
912
  var enrichmentInFlight = /* @__PURE__ */ new Set();
913
+ function ensureLocalIconPathResolved(result) {
914
+ const rawIcon = result.iconPath;
915
+ if (!rawIcon) return;
916
+ if (rawIcon.startsWith("http://") || rawIcon.startsWith("https://")) return;
917
+ if (localIconPathCache.has(result.port)) return;
918
+ if (localIconProbeInFlight.has(result.port)) return;
919
+ localIconProbeInFlight.add(result.port);
920
+ const hostname = normalizeHostname(new URL(result.bundleUrl).hostname);
921
+ void resolveLocalIconPath(hostname, result.port, rawIcon).finally(
922
+ () => localIconProbeInFlight.delete(result.port)
923
+ );
924
+ }
893
925
  function enrichWithManifest(result, buildIconProxyUrl) {
926
+ ensureLocalIconPathResolved(result);
894
927
  if (result.projectName) return result;
895
928
  const cached2 = enrichmentCache.get(result.port);
896
929
  if (cached2) {
@@ -1129,6 +1162,9 @@ async function scanDevServers(opts = {}) {
1129
1162
  for (const p of [...enrichmentCache.keys()]) {
1130
1163
  if (!currentPorts.has(p)) enrichmentCache.delete(p);
1131
1164
  }
1165
+ for (const p of [...localIconPathCache.keys()]) {
1166
+ if (!currentPorts.has(p)) localIconPathCache.delete(p);
1167
+ }
1132
1168
  const results = [];
1133
1169
  const toProbe = [];
1134
1170
  for (const { port, pid } of processes) {
@@ -1142,6 +1178,7 @@ async function scanDevServers(opts = {}) {
1142
1178
  knownNonExpo.delete(port);
1143
1179
  knownOne.delete(port);
1144
1180
  enrichmentCache.delete(port);
1181
+ localIconPathCache.delete(port);
1145
1182
  }
1146
1183
  toProbe.push({ port, pid });
1147
1184
  }
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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);