peachsim 0.1.527 → 0.1.571

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 (169) hide show
  1. package/README.md +28 -28
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/app-splash.ts +1 -1
  4. package/cli/bin.ts +1 -1
  5. package/cli/bridge-diagnostics.ts +1 -1
  6. package/cli/bridge-flow-runner.ts +72 -12
  7. package/cli/cloud-client.ts +273 -6
  8. package/cli/cloud-dispatch.ts +23 -2
  9. package/cli/cloud-session.ts +60 -4
  10. package/cli/cloud-test-result-files.ts +97 -0
  11. package/cli/cloud-test-tunnel.ts +354 -0
  12. package/cli/commands/box/bundle-plane.ts +2 -2
  13. package/cli/commands/box/checkout-plane.ts +1 -1
  14. package/cli/commands/box/rnx-command.ts +2 -2
  15. package/cli/commands/box/search-command.ts +2 -2
  16. package/cli/commands/box.ts +8 -8
  17. package/cli/commands/compat.ts +1 -1
  18. package/cli/commands/control.ts +22 -3
  19. package/cli/commands/detox.ts +1 -1
  20. package/cli/commands/device.ts +1 -1
  21. package/cli/commands/diagnose.ts +1 -1
  22. package/cli/commands/film.ts +1 -1
  23. package/cli/commands/flow.ts +60 -3
  24. package/cli/commands/inspect/actions.ts +1 -1
  25. package/cli/commands/inspect/core.ts +6 -7
  26. package/cli/commands/inspect/find.ts +2 -1
  27. package/cli/commands/inspect/resolve-target.ts +5 -3
  28. package/cli/commands/inspect/shared.ts +1 -1
  29. package/cli/commands/inspect/wait-event.ts +1 -1
  30. package/cli/commands/inspect/wait-selector.ts +15 -4
  31. package/cli/commands/inspect.ts +19 -14
  32. package/cli/commands/maestro.ts +582 -69
  33. package/cli/commands/permissions.ts +1 -1
  34. package/cli/commands/platform.ts +205 -8
  35. package/cli/commands/preview.ts +2 -2
  36. package/cli/commands/record.ts +2 -2
  37. package/cli/commands/report-issue.ts +2 -2
  38. package/cli/commands/screenshot-layers.ts +1 -1
  39. package/cli/commands/screenshot.ts +1 -1
  40. package/cli/commands/setup.ts +2 -2
  41. package/cli/commands/test.ts +600 -117
  42. package/cli/commands/three-mode.ts +1 -1
  43. package/cli/commands/upload.ts +5 -5
  44. package/cli/commands/what-happened.ts +1 -1
  45. package/cli/desktop-companion.ts +3 -3
  46. package/cli/drivers/playwright-sim-host.ts +2 -2
  47. package/cli/flow-file.ts +7 -0
  48. package/cli/flow-live-status.ts +65 -8
  49. package/cli/help-core.ts +3 -3
  50. package/cli/hidden-runtime-alias.ts +1 -1
  51. package/cli/maestro-report.ts +180 -0
  52. package/cli/main.ts +1 -1
  53. package/cli/outbound-endpoints.ts +187 -136
  54. package/cli/parse-args.ts +1 -1
  55. package/cli/registry.ts +1 -1
  56. package/cli/resolve-assets.ts +1 -1
  57. package/cli/run-registry.ts +10 -1
  58. package/cli/send-to-box.ts +4 -4
  59. package/cli/ws-bridge.ts +3 -5
  60. package/detox/expectations.ts +1 -1
  61. package/detox/gestures.ts +2 -2
  62. package/detox/index.ts +71 -29
  63. package/detox/jest-preset.cjs +5 -5
  64. package/detox/jest-setup-after-env.cjs +1 -1
  65. package/dist-lib/agent-daemon-client.cjs +2 -2
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +1 -1
  68. package/dist-lib/agent-sessions.cjs +2 -2
  69. package/dist-lib/attached-projects.cjs +2 -2
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +1 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +1 -1
  76. package/dist-lib/bridge-contract-input.mjs +1 -1
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +94 -3
  80. package/dist-lib/capture-contract.mjs +93 -3
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +116 -0
  85. package/dist-lib/cloud-test-args.mjs +86 -0
  86. package/dist-lib/cloud.cjs +2 -2
  87. package/dist-lib/cloud.mjs +2 -2
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +106 -25
  90. package/dist-lib/detox/jest-preset.cjs +5 -5
  91. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  92. package/dist-lib/home-paths.cjs +1 -1
  93. package/dist-lib/host/bridge-host.cjs +150 -65
  94. package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
  95. package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
  96. package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
  97. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  98. package/dist-lib/host/websocket-proxy.cjs +1 -1
  99. package/dist-lib/index.cjs +224 -41
  100. package/dist-lib/jump-to-source-babel.cjs +1 -1
  101. package/dist-lib/jump-to-source-native.cjs +1 -1
  102. package/dist-lib/menu.cjs +2 -2
  103. package/dist-lib/menu.mjs +2 -2
  104. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  105. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  106. package/dist-lib/metro-production-bundle.cjs +3 -3
  107. package/dist-lib/metro-production-bundle.mjs +3 -3
  108. package/dist-lib/metro.cjs +8 -8
  109. package/dist-lib/profiles.cjs +1 -1
  110. package/dist-lib/public-brand.cjs +2 -2
  111. package/dist-lib/react-native-host-modules.cjs +1 -1
  112. package/dist-lib/react-native-host-modules.mjs +1 -1
  113. package/dist-lib/render-mode.cjs +1 -1
  114. package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
  115. package/dist-lib/sdk.cjs +3 -2
  116. package/dist-lib/sdk.mjs +3 -2
  117. package/dist-lib/skills.cjs +478 -266
  118. package/dist-lib/swift-compile.cjs +372 -0
  119. package/dist-lib/swift-compile.mjs +339 -0
  120. package/dist-lib/swift-package.cjs +78 -0
  121. package/dist-lib/swift-package.mjs +52 -0
  122. package/dist-lib/swift-submit.cjs +1 -1
  123. package/dist-lib/swift-submit.mjs +1 -1
  124. package/dist-lib/swift.cjs +583 -16
  125. package/dist-lib/vite.cjs +483 -150
  126. package/package.json +30 -9
  127. package/scripts/dev-server-scanner.ts +41 -14
  128. package/scripts/optional-demo-registry.ts +3 -3
  129. package/skills/contrast/SKILL.md +9 -9
  130. package/skills/rnx-debug/SKILL.md +14 -4
  131. package/skills/rnx-perf/SKILL.md +5 -5
  132. package/skills/rnx-setup/SKILL.md +11 -11
  133. package/skills/rnx-test/SKILL.md +14 -14
  134. package/skills/rnx-visual/SKILL.md +6 -6
  135. package/src/agent-sessions.ts +3 -3
  136. package/src/attached-projects.ts +1 -1
  137. package/src/bridge-contract-input.ts +1 -1
  138. package/src/bridge-contract.ts +23 -5
  139. package/src/capture-contract.ts +145 -3
  140. package/src/cloud-test-args.ts +100 -0
  141. package/src/config.ts +2 -2
  142. package/src/connect.ts +2 -2
  143. package/src/home-paths.ts +3 -3
  144. package/src/host/bridge-host.ts +2 -2
  145. package/src/host/fetch-proxy-handler.ts +4 -3
  146. package/src/host/fetch-proxy-overrides.ts +41 -0
  147. package/src/host/plane-host.ts +2 -2
  148. package/src/index.ts +3 -0
  149. package/src/metro-plugin.ts +4 -4
  150. package/src/metro-production-bundle.ts +2 -2
  151. package/src/native-seam-manifest.ts +6 -6
  152. package/src/public-brand.ts +1 -1
  153. package/src/rolldown-plugin-swift.ts +16 -12
  154. package/src/runtime-assets.ts +8 -6
  155. package/src/screenshots/frame-compose.ts +24 -56
  156. package/src/screenshots/org-deck.ts +1 -1
  157. package/src/screenshots/registry.ts +4 -4
  158. package/src/screenshots/schema.ts +1 -1
  159. package/src/sdk.ts +2 -2
  160. package/src/settings-bundle.ts +1 -1
  161. package/src/sim-client.ts +2 -4
  162. package/src/skills/builtin/compat-check.ts +1 -1
  163. package/src/swift-compile.ts +359 -0
  164. package/src/swift-package.ts +69 -0
  165. package/src/swift-submit.ts +3 -2
  166. package/src/vite-plugin-one.ts +1 -1
  167. package/src/vite-plugin-swift.ts +114 -113
  168. package/src/vite-plugin.ts +30 -17
  169. package/src/worklets-babel.ts +2 -2
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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;
@@ -114,7 +114,7 @@ var init_optional_demo_registry = __esm({
114
114
  import_node_fs = require("node:fs");
115
115
  import_node_path2 = require("node:path");
116
116
  import_node_url = require("node:url");
117
- REGISTRY_REPO_PATH = "packages/sootsim/scripts/demo-app-registry.ts";
117
+ REGISTRY_REPO_PATH = "packages/peachsim/scripts/demo-app-registry.ts";
118
118
  MAX_ANCESTOR_STEPS = 8;
119
119
  cached = null;
120
120
  }
@@ -128,7 +128,7 @@ var init_public_brand = __esm({
128
128
  name = "rnx";
129
129
  rnxPublicBrand = Object.freeze({
130
130
  name,
131
- packageName: "rnxsim",
131
+ packageName: "peachsim",
132
132
  commandName: "rnx",
133
133
  domain: "rnxsim.com",
134
134
  origin: "https://rnxsim.com"
@@ -721,7 +721,7 @@ var init_auth = __esm({
721
721
  }
722
722
  });
723
723
 
724
- // ../sootsim-engine/src/preview/storage-snapshot-contract.ts
724
+ // ../peach-engine/src/preview/storage-snapshot-contract.ts
725
725
  function isRecord3(value) {
726
726
  return !!value && typeof value === "object" && !Array.isArray(value);
727
727
  }
@@ -768,12 +768,12 @@ function isStorageSnapshot(value) {
768
768
  return isRecord3(value) && Array.isArray(value.entries) && value.entries.every(isStorageSnapshotEntry) && (value.secureStore == null || Array.isArray(value.secureStore) && value.secureStore.every(isSecureStoreSnapshotEntry)) && (value.indexedDB == null || Array.isArray(value.indexedDB) && value.indexedDB.every(isIndexedDBSnapshot)) && (value.route == null || isPreviewRouteSnapshot(value.route));
769
769
  }
770
770
  var init_storage_snapshot_contract = __esm({
771
- "../sootsim-engine/src/preview/storage-snapshot-contract.ts"() {
771
+ "../peach-engine/src/preview/storage-snapshot-contract.ts"() {
772
772
  "use strict";
773
773
  }
774
774
  });
775
775
 
776
- // ../sootsim-engine/src/tenant-runtime-environment.ts
776
+ // ../peach-engine/src/tenant-runtime-environment.ts
777
777
  function readBrowserLocation() {
778
778
  try {
779
779
  return typeof location === "undefined" ? null : location;
@@ -783,25 +783,25 @@ function readBrowserLocation() {
783
783
  }
784
784
  var nativeRuntimeLocation;
785
785
  var init_tenant_runtime_environment = __esm({
786
- "../sootsim-engine/src/tenant-runtime-environment.ts"() {
786
+ "../peach-engine/src/tenant-runtime-environment.ts"() {
787
787
  "use strict";
788
788
  nativeRuntimeLocation = readBrowserLocation();
789
789
  }
790
790
  });
791
791
 
792
- // ../sootsim-engine/src/development-mode.ts
792
+ // ../peach-engine/src/development-mode.ts
793
793
  var bridgeConstants, DEFAULT_SOOTSIM_BRIDGE_PORT2, DAEMON_RUNTIME_PORT_MAX;
794
794
  var init_development_mode = __esm({
795
- "../sootsim-engine/src/development-mode.ts"() {
795
+ "../peach-engine/src/development-mode.ts"() {
796
796
  "use strict";
797
- bridgeConstants = __toESM(require("rnxsim/bridge-constants"), 1);
797
+ bridgeConstants = __toESM(require("peachsim/bridge-constants"), 1);
798
798
  init_tenant_runtime_environment();
799
799
  ({ DEFAULT_SOOTSIM_BRIDGE_PORT: DEFAULT_SOOTSIM_BRIDGE_PORT2 } = bridgeConstants);
800
800
  DAEMON_RUNTIME_PORT_MAX = DEFAULT_SOOTSIM_BRIDGE_PORT2 + 10;
801
801
  }
802
802
  });
803
803
 
804
- // ../sootsim-engine/src/settings/devices.ts
804
+ // ../peach-engine/src/settings/devices.ts
805
805
  function modernButtons(action, volumeUp, volumeDown, power) {
806
806
  return {
807
807
  width: 3,
@@ -842,31 +842,44 @@ function resolveDevicePosture(model, posture) {
842
842
  const supported = devices[model].supportedPostures;
843
843
  return posture && supported?.includes(posture) ? posture : supported?.[0] ?? "closed";
844
844
  }
845
- function getDevice(model, posture) {
845
+ function deviceDefaultOrientation(model, posture) {
846
+ if (model === "iphone-duo" && resolveDevicePosture(model, posture) === "open") {
847
+ return "landscape";
848
+ }
849
+ return "portrait";
850
+ }
851
+ function getDevice(model, posture, orientation) {
846
852
  const device = devices[model];
847
853
  if (!device.supportedPostures) return device;
848
854
  const resolvedPosture = resolveDevicePosture(model, posture);
849
855
  if (resolvedPosture === "closed") return device;
856
+ const resolvedOrientation = orientation ?? deviceDefaultOrientation(model, resolvedPosture);
857
+ const isRotatedPortrait = resolvedOrientation === "portrait";
850
858
  return {
851
859
  ...device,
852
860
  // inner display size from apple's ios 27 ui kit. corners and insets are
853
- // measured from apple's 2d inner product art: every display corner fits a
854
- // 53pt circle within 0.4px, and the silhouette sits 19.5pt outside the
855
- // display on every edge as 16pt of black glass plus a 3.5pt rail.
856
- width: 951,
857
- height: 669,
861
+ // measured from apple's 2d inner product art at 3x: every display corner
862
+ // fits a 53pt circle within 0.4px, the silhouette sits 20.67pt outside the
863
+ // display on every edge as 16.33pt of black glass plus a 4.33pt rail, and
864
+ // the body itself curves at 75pt. the barrel is hidden when open.
865
+ width: isRotatedPortrait ? 669 : 951,
866
+ height: isRotatedPortrait ? 951 : 669,
858
867
  statusBarHeight: 44,
859
868
  cornerRadius: cornerRadii(53),
860
869
  continuousCornerRadius: cornerRadii(53),
861
- frameInsets: uniformFrameInsets(19.5),
862
- housing: { rail: uniformFrameInsets(3.5), hingeBarrel: null },
870
+ frameInsets: uniformFrameInsets(20.6667),
871
+ frameCornerRadius: cornerRadii(75),
872
+ housing: { rail: uniformFrameInsets(4.3333), hingeBarrel: null },
863
873
  cameraCutout: null,
864
874
  physicalPPI: 430,
865
875
  posture: "open",
866
- // open inner display keeps horizontal bars in this slice; preserve the
867
- // previous trailing status lane so open layouts do not pick up the closed
868
- // 84pt Vertical Bar inset.
869
- safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 72 }
876
+ // open inner display in landscape carries the same trailing column as
877
+ // the closed outer display, so it takes the same 84pt inset: a ~50pt
878
+ // control at a ~23pt inset needs 73pt, and anything narrower would shift
879
+ // controls horizontally on unfold, defeating the continuous experience
880
+ // Apple states as the reason for the design. native bottom inset is 34;
881
+ // rotated portrait is top 82 / bottom 34 / right 0.
882
+ safeAreaInsets: isRotatedPortrait ? { top: 82, bottom: 34, left: 0, right: 0 } : { top: 0, bottom: 34, left: 0, right: 84 }
870
883
  };
871
884
  }
872
885
  function listDeviceModels() {
@@ -877,9 +890,9 @@ function getRuntimePlatform(model) {
877
890
  }
878
891
  var import_menu, LEGACY_HARDWARE_BUTTONS, PIXEL_HARDWARE_BUTTONS, GALAXY_HARDWARE_BUTTONS, ANDROID_GESTURE_NAV_BAR_HEIGHT, ANDROID_OS_VERSION, ANDROID_API_LEVEL, PIXEL_8_WIDTH, PIXEL_8_HEIGHT, PIXEL_8_STATUS_BAR_HEIGHT, devices, DEFAULT_DEVICE, DEFAULT_DEVICE_BY_PLATFORM;
879
892
  var init_devices = __esm({
880
- "../sootsim-engine/src/settings/devices.ts"() {
893
+ "../peach-engine/src/settings/devices.ts"() {
881
894
  "use strict";
882
- import_menu = require("rnxsim/menu");
895
+ import_menu = require("peachsim/menu");
883
896
  init_development_mode();
884
897
  LEGACY_HARDWARE_BUTTONS = {
885
898
  width: 2.5,
@@ -912,6 +925,7 @@ var init_devices = __esm({
912
925
  PIXEL_8_STATUS_BAR_HEIGHT = 132 / 2.625;
913
926
  devices = {
914
927
  "canvas-workspace": iosDevice({
928
+ model: "canvas-workspace",
915
929
  name: "Canvas Workspace",
916
930
  width: 1440,
917
931
  height: 900,
@@ -937,6 +951,7 @@ var init_devices = __esm({
937
951
  experimental: true
938
952
  }),
939
953
  "iphone-se": iosDevice({
954
+ model: "iphone-se",
940
955
  name: "iPhone SE",
941
956
  width: 375,
942
957
  height: 667,
@@ -957,6 +972,7 @@ var init_devices = __esm({
957
972
  hardwareButtons: LEGACY_HARDWARE_BUTTONS
958
973
  }),
959
974
  "iphone-14": iosDevice({
975
+ model: "iphone-14",
960
976
  name: "iPhone 14",
961
977
  width: 390,
962
978
  height: 844,
@@ -979,6 +995,7 @@ var init_devices = __esm({
979
995
  )
980
996
  }),
981
997
  "iphone-15": iosDevice({
998
+ model: "iphone-15",
982
999
  name: "iPhone 15",
983
1000
  width: 393,
984
1001
  height: 852,
@@ -1001,6 +1018,7 @@ var init_devices = __esm({
1001
1018
  )
1002
1019
  }),
1003
1020
  "iphone-15-plus": iosDevice({
1021
+ model: "iphone-15-plus",
1004
1022
  name: "iPhone 15 Plus",
1005
1023
  width: 430,
1006
1024
  height: 932,
@@ -1023,6 +1041,7 @@ var init_devices = __esm({
1023
1041
  )
1024
1042
  }),
1025
1043
  "iphone-15-pro": iosDevice({
1044
+ model: "iphone-15-pro",
1026
1045
  name: "iPhone 15 Pro",
1027
1046
  width: 393,
1028
1047
  height: 852,
@@ -1045,6 +1064,7 @@ var init_devices = __esm({
1045
1064
  )
1046
1065
  }),
1047
1066
  "iphone-15-pro-max": iosDevice({
1067
+ model: "iphone-15-pro-max",
1048
1068
  name: "iPhone 15 Pro Max",
1049
1069
  width: 430,
1050
1070
  height: 932,
@@ -1067,6 +1087,7 @@ var init_devices = __esm({
1067
1087
  )
1068
1088
  }),
1069
1089
  "iphone-16": iosDevice({
1090
+ model: "iphone-16",
1070
1091
  name: "iPhone 16",
1071
1092
  width: 393,
1072
1093
  height: 852,
@@ -1075,7 +1096,7 @@ var init_devices = __esm({
1075
1096
  homeIndicatorHeight: 34,
1076
1097
  // 2026-07-10: cornerRadius corrected 58 → 53.9 to Apple's own framebuffer
1077
1098
  // mask (extracted from the simdevicetype bundle PDF; see
1078
- // packages/sootsim-engine/assets/device-frame-geometry). radius is a paint clip only — the
1099
+ // packages/peach-engine/assets/device-frame-geometry). radius is a paint clip only — the
1079
1100
  // calibrated conformance-oracle layout fields below are unchanged.
1080
1101
  cornerRadius: cornerRadii(53.9),
1081
1102
  continuousCornerRadius: cornerRadii(73.402),
@@ -1095,6 +1116,7 @@ var init_devices = __esm({
1095
1116
  )
1096
1117
  }),
1097
1118
  "iphone-16-plus": iosDevice({
1119
+ model: "iphone-16-plus",
1098
1120
  name: "iPhone 16 Plus",
1099
1121
  width: 430,
1100
1122
  height: 932,
@@ -1117,6 +1139,7 @@ var init_devices = __esm({
1117
1139
  )
1118
1140
  }),
1119
1141
  "iphone-16-pro": iosDevice({
1142
+ model: "iphone-16-pro",
1120
1143
  name: "iPhone 16 Pro",
1121
1144
  width: 402,
1122
1145
  height: 874,
@@ -1141,6 +1164,7 @@ var init_devices = __esm({
1141
1164
  )
1142
1165
  }),
1143
1166
  "iphone-16-pro-max": iosDevice({
1167
+ model: "iphone-16-pro-max",
1144
1168
  name: "iPhone 16 Pro Max",
1145
1169
  width: 440,
1146
1170
  height: 956,
@@ -1166,6 +1190,7 @@ var init_devices = __esm({
1166
1190
  )
1167
1191
  }),
1168
1192
  "iphone-17": iosDevice({
1193
+ model: "iphone-17",
1169
1194
  name: "iPhone 17",
1170
1195
  width: 402,
1171
1196
  height: 874,
@@ -1189,6 +1214,7 @@ var init_devices = __esm({
1189
1214
  )
1190
1215
  }),
1191
1216
  "iphone-17-air": iosDevice({
1217
+ model: "iphone-17-air",
1192
1218
  // apple markets this as "iphone air" (not "iphone 17 air"). the real air
1193
1219
  // is 420x912 with thin bezels and a lower-sitting island. it is not the
1194
1220
  // 430x932 Plus body it used to be cloned from.
@@ -1229,6 +1255,7 @@ var init_devices = __esm({
1229
1255
  )
1230
1256
  }),
1231
1257
  "iphone-17-pro": iosDevice({
1258
+ model: "iphone-17-pro",
1232
1259
  name: "iPhone 17 Pro",
1233
1260
  width: 402,
1234
1261
  height: 874,
@@ -1256,6 +1283,7 @@ var init_devices = __esm({
1256
1283
  )
1257
1284
  }),
1258
1285
  "iphone-17-pro-max": iosDevice({
1286
+ model: "iphone-17-pro-max",
1259
1287
  name: "iPhone 17 Pro Max",
1260
1288
  width: 440,
1261
1289
  height: 956,
@@ -1285,6 +1313,7 @@ var init_devices = __esm({
1285
1313
  // measurements are still settling (nate, 2026-09-15).
1286
1314
  developmentOnly: true,
1287
1315
  ...iosDevice({
1316
+ model: "iphone-duo",
1288
1317
  name: "iPhone Duo",
1289
1318
  width: 466,
1290
1319
  height: 678,
@@ -1300,16 +1329,25 @@ var init_devices = __esm({
1300
1329
  bottomRight: 61,
1301
1330
  bottomLeft: 5
1302
1331
  },
1303
- // display edge to silhouette. left is black glass 8.6 + rail 5.4 + hinge
1304
- // barrel 14.1; right is black glass 8.7 + rail 7.7.
1305
- frameInsets: { top: 19, right: 16.4, bottom: 16.3, left: 28.1 },
1332
+ // display edge to silhouette, measured off apple's 2d outer product art
1333
+ // at 3x. the barrel is not part of the body: it protrudes 11.67pt past
1334
+ // the left silhouette, so the full outer width is frame + protrusion.
1335
+ frameInsets: { top: 16.3333, right: 16.3333, bottom: 16, left: 14 },
1336
+ // the spine is a flat cut, so its corners are tiny; the free side keeps
1337
+ // the 75pt body curve the pro bodies use.
1338
+ frameCornerRadius: {
1339
+ topLeft: 4.3333,
1340
+ topRight: 75,
1341
+ bottomRight: 75,
1342
+ bottomLeft: 3.3333
1343
+ },
1306
1344
  housing: {
1307
- rail: { top: 10.4, right: 7.7, bottom: 7.7, left: 19.5 },
1308
- hingeBarrel: { width: 14.1, endInset: 6.4 }
1345
+ rail: { top: 7.5, right: 8, bottom: 7.3333, left: 5.3333 },
1346
+ hingeBarrel: { width: 16.6667, endInset: 7.3333, protrusion: 11.6667 }
1309
1347
  },
1310
1348
  dynamicIsland: null,
1311
1349
  // 84pt trailing Vertical Bar on the outer display (Apple iOS 27 UI kit).
1312
- safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 84 },
1350
+ safeAreaInsets: { top: 0, bottom: 34, left: 0, right: 84 },
1313
1351
  iosVersion: "27.1",
1314
1352
  keyboardHeight: 0,
1315
1353
  physicalPPI: 460,
@@ -1325,6 +1363,7 @@ var init_devices = __esm({
1325
1363
  }
1326
1364
  },
1327
1365
  "pixel-8": {
1366
+ model: "pixel-8",
1328
1367
  platform: "android",
1329
1368
  name: "Pixel 8",
1330
1369
  width: PIXEL_8_WIDTH,
@@ -1373,6 +1412,7 @@ var init_devices = __esm({
1373
1412
  hardwareButtons: PIXEL_HARDWARE_BUTTONS
1374
1413
  },
1375
1414
  "pixel-8-three-button": {
1415
+ model: "pixel-8-three-button",
1376
1416
  platform: "android",
1377
1417
  name: "Pixel 8 (3-button)",
1378
1418
  width: PIXEL_8_WIDTH,
@@ -1418,6 +1458,7 @@ var init_devices = __esm({
1418
1458
  hardwareButtons: PIXEL_HARDWARE_BUTTONS
1419
1459
  },
1420
1460
  "pixel-9-pro-xl": {
1461
+ model: "pixel-9-pro-xl",
1421
1462
  platform: "android",
1422
1463
  name: "Pixel 9 Pro XL",
1423
1464
  width: 448,
@@ -1454,6 +1495,7 @@ var init_devices = __esm({
1454
1495
  hardwareButtons: PIXEL_HARDWARE_BUTTONS
1455
1496
  },
1456
1497
  "pixel-10": {
1498
+ model: "pixel-10",
1457
1499
  platform: "android",
1458
1500
  name: "Pixel 10",
1459
1501
  width: 360,
@@ -1491,6 +1533,7 @@ var init_devices = __esm({
1491
1533
  hardwareButtons: PIXEL_HARDWARE_BUTTONS
1492
1534
  },
1493
1535
  "galaxy-s24": {
1536
+ model: "galaxy-s24",
1494
1537
  platform: "android",
1495
1538
  name: "Galaxy S24",
1496
1539
  width: 360,
@@ -1528,6 +1571,7 @@ var init_devices = __esm({
1528
1571
  hardwareButtons: GALAXY_HARDWARE_BUTTONS
1529
1572
  },
1530
1573
  "galaxy-s24-ultra": {
1574
+ model: "galaxy-s24-ultra",
1531
1575
  platform: "android",
1532
1576
  name: "Galaxy S24 Ultra",
1533
1577
  width: 384,
@@ -1576,7 +1620,7 @@ var init_devices = __esm({
1576
1620
  // src/metro-production-bundle.ts
1577
1621
  function toRNXProductionBundleUrl(bundleUrl) {
1578
1622
  const relative2 = bundleUrl.startsWith("/");
1579
- const url = new URL(bundleUrl, "http://rnxsim.local");
1623
+ const url = new URL(bundleUrl, "http://peachsim.local");
1580
1624
  url.searchParams.set("dev", "false");
1581
1625
  url.searchParams.set("hot", "false");
1582
1626
  url.searchParams.set("minify", "true");
@@ -2571,7 +2615,7 @@ var import_metro_production_bundle, import_terser, MODULE_PATHS_REQUIRED, SOURCE
2571
2615
  var init_metroCloudArtifact = __esm({
2572
2616
  "../contrast-bundler/src/metroCloudArtifact.ts"() {
2573
2617
  "use strict";
2574
- import_metro_production_bundle = require("rnxsim/metro-production-bundle");
2618
+ import_metro_production_bundle = require("peachsim/metro-production-bundle");
2575
2619
  import_terser = require("terser");
2576
2620
  init_metroAssetArtifact();
2577
2621
  init_metroDebugSourceMap();
@@ -2586,7 +2630,7 @@ var init_metroCloudArtifact = __esm({
2586
2630
  }
2587
2631
  });
2588
2632
 
2589
- // ../sootsim-engine/src/metro-fingerprint-registry-client.ts
2633
+ // ../peach-engine/src/metro-fingerprint-registry-client.ts
2590
2634
  function pointerUrl(options) {
2591
2635
  return options.pointerUrl ?? new URL(import_metro_fingerprint_registry.RNX_METRO_FINGERPRINT_POINTER_PATH, DEFAULT_REGISTRY_ORIGIN).toString();
2592
2636
  }
@@ -2751,19 +2795,19 @@ async function loadMetroFingerprintRegistry(options = {}) {
2751
2795
  }
2752
2796
  var import_metro_fingerprint_registry, REGISTRY_CACHE_NAME, DEFAULT_REGISTRY_ORIGIN, memoryReceipts;
2753
2797
  var init_metro_fingerprint_registry_client = __esm({
2754
- "../sootsim-engine/src/metro-fingerprint-registry-client.ts"() {
2798
+ "../peach-engine/src/metro-fingerprint-registry-client.ts"() {
2755
2799
  "use strict";
2756
- import_metro_fingerprint_registry = require("rnxsim/metro-fingerprint-registry");
2800
+ import_metro_fingerprint_registry = require("peachsim/metro-fingerprint-registry");
2757
2801
  REGISTRY_CACHE_NAME = "rnx-metro-fingerprints-v1";
2758
2802
  DEFAULT_REGISTRY_ORIGIN = "https://contrast.dev";
2759
2803
  memoryReceipts = /* @__PURE__ */ new Map();
2760
2804
  }
2761
2805
  });
2762
2806
 
2763
- // ../compat/src/stub-manifest.ts
2807
+ // ../peach-compat/src/stub-manifest.ts
2764
2808
  var COMPAT_REACT_NATIVE_DEEP_STUBS;
2765
2809
  var init_stub_manifest = __esm({
2766
- "../compat/src/stub-manifest.ts"() {
2810
+ "../peach-compat/src/stub-manifest.ts"() {
2767
2811
  "use strict";
2768
2812
  COMPAT_REACT_NATIVE_DEEP_STUBS = {
2769
2813
  usePressability: {
@@ -2921,17 +2965,17 @@ var init_stub_manifest = __esm({
2921
2965
  }
2922
2966
  });
2923
2967
 
2924
- // ../sootsim-engine/src/metro-asset-path.ts
2968
+ // ../peach-engine/src/metro-asset-path.ts
2925
2969
  function isMetroAssetRegistryModulePath(modulePath) {
2926
- return modulePath.includes("polyfill:assets-registry") || modulePath.includes("@react-native/assets-registry/registry") || modulePath.includes("assets-registry/registry") || modulePath.includes("react-native/src/asset-registry");
2970
+ return modulePath.includes("polyfill:assets-registry") || modulePath.includes("@react-native/assets-registry/registry") || modulePath.includes("assets-registry/registry") || modulePath.includes("react-native/asset-registry") || modulePath.includes("react-native/src/asset-registry");
2927
2971
  }
2928
2972
  var init_metro_asset_path = __esm({
2929
- "../sootsim-engine/src/metro-asset-path.ts"() {
2973
+ "../peach-engine/src/metro-asset-path.ts"() {
2930
2974
  "use strict";
2931
2975
  }
2932
2976
  });
2933
2977
 
2934
- // ../sootsim-engine/src/stub-pattern-matcher.ts
2978
+ // ../peach-engine/src/stub-pattern-matcher.ts
2935
2979
  function matchesModuleEntry(modulePath, pattern) {
2936
2980
  if (!modulePath || !pattern) return false;
2937
2981
  if (modulePath === pattern) return true;
@@ -2959,12 +3003,12 @@ function isReactNativeWorkletsGeneratedWorkletModule(modulePath) {
2959
3003
  return subpath?.startsWith(".worklets/") === true;
2960
3004
  }
2961
3005
  var init_stub_pattern_matcher = __esm({
2962
- "../sootsim-engine/src/stub-pattern-matcher.ts"() {
3006
+ "../peach-engine/src/stub-pattern-matcher.ts"() {
2963
3007
  "use strict";
2964
3008
  }
2965
3009
  });
2966
3010
 
2967
- // ../sootsim-engine/src/metro-fingerprint.ts
3011
+ // ../peach-engine/src/metro-fingerprint.ts
2968
3012
  function compareStrings(left, right) {
2969
3013
  return left < right ? -1 : left > right ? 1 : 0;
2970
3014
  }
@@ -3528,11 +3572,11 @@ async function inferMetroModuleIdentity(args) {
3528
3572
  }
3529
3573
  var import_metro_production_bundle2, import_react_native_host_modules, IDENTIFIER, MAX_CANDIDATE_EMBEDDINGS, MIN_DUPLICATE_ROOT_EXACT_FACTORIES, REQUIRED_INTERCEPT_PACKAGES;
3530
3574
  var init_metro_fingerprint = __esm({
3531
- "../sootsim-engine/src/metro-fingerprint.ts"() {
3575
+ "../peach-engine/src/metro-fingerprint.ts"() {
3532
3576
  "use strict";
3533
3577
  init_stub_manifest();
3534
- import_metro_production_bundle2 = require("rnxsim/metro-production-bundle");
3535
- import_react_native_host_modules = require("rnxsim/react-native-host-modules");
3578
+ import_metro_production_bundle2 = require("peachsim/metro-production-bundle");
3579
+ import_react_native_host_modules = require("peachsim/react-native-host-modules");
3536
3580
  init_metro_asset_path();
3537
3581
  init_stub_pattern_matcher();
3538
3582
  IDENTIFIER = "[A-Za-z_$][A-Za-z0-9_$]*";
@@ -5087,7 +5131,7 @@ async function resolveLocalIconPath(hostname, port, rawIcon) {
5087
5131
  const res = await httpGet(hostname, port, candidate, "HEAD", ENRICH_TIMEOUT_MS);
5088
5132
  if (res?.statusCode === 200) {
5089
5133
  localIconPathCache.set(port, candidate);
5090
- portCache.delete(port);
5134
+ invalidatePortCache(port);
5091
5135
  return;
5092
5136
  }
5093
5137
  }
@@ -5209,7 +5253,7 @@ async function fetchManifestEnrichment(result, buildIconProxyUrl) {
5209
5253
  iconPath: parsed.iconPath,
5210
5254
  bundleId: parsed.bundleId
5211
5255
  });
5212
- portCache.delete(result.port);
5256
+ invalidatePortCache(result.port);
5213
5257
  }
5214
5258
  var knownNonPatched = /* @__PURE__ */ new Set();
5215
5259
  var knownNonExpo = /* @__PURE__ */ new Set();
@@ -5249,16 +5293,19 @@ async function probePort(port, buildIconProxyUrl, listeningProcess) {
5249
5293
  const framework = launchUrl2.includes(
5250
5294
  "/one/metro-entry.bundle"
5251
5295
  ) ? "one" : "expo";
5252
- return applyManifest(
5253
- {
5254
- port,
5255
- framework,
5256
- bundleUrl: withRuntimeConfig(port, launchUrl2, owner.cwd),
5257
- hmrUrl: `${buildLoopbackProbeBaseUrl("ws:", probeHostname, port)}/hot`,
5258
- lastSeen: Date.now(),
5259
- ...owner
5260
- },
5261
- manifestRes,
5296
+ return enrichWithManifest(
5297
+ applyManifest(
5298
+ {
5299
+ port,
5300
+ framework,
5301
+ bundleUrl: withRuntimeConfig(port, launchUrl2, owner.cwd),
5302
+ hmrUrl: `${buildLoopbackProbeBaseUrl("ws:", probeHostname, port)}/hot`,
5303
+ lastSeen: Date.now(),
5304
+ ...owner
5305
+ },
5306
+ manifestRes,
5307
+ buildIconProxyUrl
5308
+ ),
5262
5309
  buildIconProxyUrl
5263
5310
  );
5264
5311
  }
@@ -5336,6 +5383,11 @@ function isSootSimSelfServer(server) {
5336
5383
  return false;
5337
5384
  }
5338
5385
  var portCache = /* @__PURE__ */ new Map();
5386
+ var portCacheInvalidatedAt = /* @__PURE__ */ new Map();
5387
+ function invalidatePortCache(port) {
5388
+ portCache.delete(port);
5389
+ portCacheInvalidatedAt.set(port, Date.now());
5390
+ }
5339
5391
  var NEGATIVE_CACHE_TTL_MS = 3e4;
5340
5392
  var WEAK_RESULT_CACHE_TTL_MS = 1500;
5341
5393
  function isWeakCachedResult(result) {
@@ -5390,6 +5442,9 @@ async function scanDevServers(opts = {}) {
5390
5442
  for (const p of [...localIconPathCache.keys()]) {
5391
5443
  if (!currentPorts.has(p)) localIconPathCache.delete(p);
5392
5444
  }
5445
+ for (const p of [...portCacheInvalidatedAt.keys()]) {
5446
+ if (!currentPorts.has(p)) portCacheInvalidatedAt.delete(p);
5447
+ }
5393
5448
  const results = [];
5394
5449
  const toProbe = [];
5395
5450
  for (const { port, pid } of processes) {
@@ -5408,12 +5463,16 @@ async function scanDevServers(opts = {}) {
5408
5463
  toProbe.push({ port, pid });
5409
5464
  }
5410
5465
  if (toProbe.length > 0) {
5466
+ const scanStartedAt = Date.now();
5411
5467
  const probed = await Promise.all(
5412
5468
  toProbe.map((p) => probePort(p.port, opts.buildIconProxyUrl, p))
5413
5469
  );
5414
5470
  probed.forEach((result, i) => {
5415
5471
  const { port, pid } = toProbe[i];
5416
- if (pid !== 0) portCache.set(port, { pid, result, cachedAt: Date.now() });
5472
+ const invalidatedAt = portCacheInvalidatedAt.get(port) ?? 0;
5473
+ if (pid !== 0 && invalidatedAt < scanStartedAt) {
5474
+ portCache.set(port, { pid, result, cachedAt: Date.now() });
5475
+ }
5417
5476
  if (result) results.push(result);
5418
5477
  });
5419
5478
  }
@@ -5905,7 +5964,7 @@ function findMonorepoRoot() {
5905
5964
  if (_monorepoRootCache !== void 0) return _monorepoRootCache;
5906
5965
  let dir = import_path.default.dirname((0, import_url.fileURLToPath)(__sootsim_import_meta_url));
5907
5966
  for (let i = 0; i < 12 && dir !== import_path.default.dirname(dir); i++) {
5908
- if (import_fs.default.existsSync(import_path.default.join(dir, "packages/sootsim-engine/package.json")) && import_fs.default.existsSync(import_path.default.join(dir, "public/sootsim"))) {
5967
+ if (import_fs.default.existsSync(import_path.default.join(dir, "packages/peach-engine/package.json")) && import_fs.default.existsSync(import_path.default.join(dir, "public/sootsim"))) {
5909
5968
  _monorepoRootCache = dir;
5910
5969
  return dir;
5911
5970
  }
@@ -5919,7 +5978,7 @@ function resolveDevCheckoutRuntimeRoot() {
5919
5978
  const root = findMonorepoRoot();
5920
5979
  if (!root) return null;
5921
5980
  const devBuild = import_path.default.join(root, "public/sootsim");
5922
- if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js"))) {
5981
+ if (import_fs.default.existsSync(import_path.default.join(devBuild, "index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine/index.html")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.js")) && import_fs.default.existsSync(import_path.default.join(devBuild, "engine-tenant/worker.prod.js"))) {
5923
5982
  return devBuild;
5924
5983
  }
5925
5984
  return null;
@@ -7398,6 +7457,32 @@ function headerOverridesFor(hostname) {
7398
7457
  }
7399
7458
  return void 0;
7400
7459
  }
7460
+ var PUBLIC_RPC_TENANT = "rnx-public-rpc";
7461
+ var QUICKNODE_HOST_SUFFIX = ".quiknode.pro";
7462
+ var PUBLIC_RPC_BY_QUICKNODE_CHAIN = {
7463
+ "": "https://ethereum-rpc.publicnode.com",
7464
+ "arbitrum-mainnet": "https://arbitrum-one-rpc.publicnode.com",
7465
+ "avalanche-mainnet": "https://avalanche-c-chain-rpc.publicnode.com",
7466
+ "base-mainnet": "https://base-rpc.publicnode.com",
7467
+ "blast-mainnet": "https://blast-rpc.publicnode.com",
7468
+ bsc: "https://bsc-rpc.publicnode.com",
7469
+ "celo-mainnet": "https://celo-rpc.publicnode.com",
7470
+ matic: "https://polygon-bor-rpc.publicnode.com",
7471
+ optimism: "https://optimism-rpc.publicnode.com",
7472
+ "soneium-mainnet": "https://soneium-rpc.publicnode.com",
7473
+ "unichain-mainnet": "https://unichain-rpc.publicnode.com"
7474
+ };
7475
+ function resolveFetchProxyTargetUrl(requested) {
7476
+ const host = requested.hostname.toLowerCase();
7477
+ if (!host.endsWith(QUICKNODE_HOST_SUFFIX)) return requested;
7478
+ const tenant = host.slice(0, -QUICKNODE_HOST_SUFFIX.length);
7479
+ if (tenant !== PUBLIC_RPC_TENANT && !tenant.startsWith(`${PUBLIC_RPC_TENANT}.`)) {
7480
+ return requested;
7481
+ }
7482
+ const chain = tenant === PUBLIC_RPC_TENANT ? "" : tenant.slice(PUBLIC_RPC_TENANT.length + 1);
7483
+ const replacement = PUBLIC_RPC_BY_QUICKNODE_CHAIN[chain];
7484
+ return replacement ? new URL(replacement) : requested;
7485
+ }
7401
7486
  function getFetchProxyTargetHeaders(targetUrl) {
7402
7487
  const hostOverrides = headerOverridesFor(targetUrl.hostname);
7403
7488
  return {
@@ -7518,7 +7603,7 @@ async function handleFetchProxyRequest(req, res) {
7518
7603
  }
7519
7604
  let upstreamUrl;
7520
7605
  try {
7521
- upstreamUrl = new URL(targetUrl);
7606
+ upstreamUrl = resolveFetchProxyTargetUrl(new URL(targetUrl));
7522
7607
  } catch {
7523
7608
  applyFetchProxyCors(res);
7524
7609
  res.writeHead(400, { "Content-Type": "text/plain" });
@@ -7781,7 +7866,7 @@ async function openUrl(url, options = {}) {
7781
7866
  var import_node_fs12 = __toESM(require("node:fs"), 1);
7782
7867
  var import_node_path12 = __toESM(require("node:path"), 1);
7783
7868
 
7784
- // ../rnx-box/src/planeProtocol.ts
7869
+ // ../peach-box/src/planeProtocol.ts
7785
7870
  var PLANE_FLAG_READ_ONLY = 1;
7786
7871
  var PLANE_FLAG_BINARY = 2;
7787
7872
  function encodeBase64(bytes) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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;
@@ -78,6 +78,32 @@ function headerOverridesFor(hostname) {
78
78
  }
79
79
  return void 0;
80
80
  }
81
+ var PUBLIC_RPC_TENANT = "rnx-public-rpc";
82
+ var QUICKNODE_HOST_SUFFIX = ".quiknode.pro";
83
+ var PUBLIC_RPC_BY_QUICKNODE_CHAIN = {
84
+ "": "https://ethereum-rpc.publicnode.com",
85
+ "arbitrum-mainnet": "https://arbitrum-one-rpc.publicnode.com",
86
+ "avalanche-mainnet": "https://avalanche-c-chain-rpc.publicnode.com",
87
+ "base-mainnet": "https://base-rpc.publicnode.com",
88
+ "blast-mainnet": "https://blast-rpc.publicnode.com",
89
+ bsc: "https://bsc-rpc.publicnode.com",
90
+ "celo-mainnet": "https://celo-rpc.publicnode.com",
91
+ matic: "https://polygon-bor-rpc.publicnode.com",
92
+ optimism: "https://optimism-rpc.publicnode.com",
93
+ "soneium-mainnet": "https://soneium-rpc.publicnode.com",
94
+ "unichain-mainnet": "https://unichain-rpc.publicnode.com"
95
+ };
96
+ function resolveFetchProxyTargetUrl(requested) {
97
+ const host = requested.hostname.toLowerCase();
98
+ if (!host.endsWith(QUICKNODE_HOST_SUFFIX)) return requested;
99
+ const tenant = host.slice(0, -QUICKNODE_HOST_SUFFIX.length);
100
+ if (tenant !== PUBLIC_RPC_TENANT && !tenant.startsWith(`${PUBLIC_RPC_TENANT}.`)) {
101
+ return requested;
102
+ }
103
+ const chain = tenant === PUBLIC_RPC_TENANT ? "" : tenant.slice(PUBLIC_RPC_TENANT.length + 1);
104
+ const replacement = PUBLIC_RPC_BY_QUICKNODE_CHAIN[chain];
105
+ return replacement ? new URL(replacement) : requested;
106
+ }
81
107
  function getFetchProxyTargetHeaders(targetUrl) {
82
108
  const hostOverrides = headerOverridesFor(targetUrl.hostname);
83
109
  return {
@@ -201,7 +227,7 @@ async function handleFetchProxyRequest(req, res) {
201
227
  }
202
228
  let upstreamUrl;
203
229
  try {
204
- upstreamUrl = new URL(targetUrl);
230
+ upstreamUrl = resolveFetchProxyTargetUrl(new URL(targetUrl));
205
231
  } catch {
206
232
  applyFetchProxyCors(res);
207
233
  res.writeHead(400, { "Content-Type": "text/plain" });