peachsim 0.1.657 → 0.1.659

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 (115) hide show
  1. package/README.md +21 -2
  2. package/cli/app-config.ts +25 -9
  3. package/cli/bridge-flow-runner.ts +10 -23
  4. package/cli/commands/compat.ts +234 -10
  5. package/cli/commands/do-chain.ts +5 -1
  6. package/cli/commands/inspect/actions.ts +33 -76
  7. package/cli/commands/inspect/resolve-target.ts +67 -2
  8. package/cli/commands/inspect.ts +16 -1
  9. package/cli/drivers/playwright-provisioning.ts +95 -0
  10. package/cli/ws-bridge.ts +7 -7
  11. package/dist-lib/agent-daemon-client.cjs +1 -1
  12. package/dist-lib/agent-events.cjs +1 -1
  13. package/dist-lib/agent-identity.cjs +1 -1
  14. package/dist-lib/agent-sessions.cjs +1 -1
  15. package/dist-lib/attached-projects.cjs +1 -1
  16. package/dist-lib/auth/shared-session.cjs +1 -1
  17. package/dist-lib/backend-origin.cjs +1 -1
  18. package/dist-lib/beta.cjs +1 -1
  19. package/dist-lib/beta.mjs +1 -1
  20. package/dist-lib/bridge-constants.cjs +1 -1
  21. package/dist-lib/bridge-contract-input.cjs +9 -1
  22. package/dist-lib/bridge-contract-input.mjs +9 -1
  23. package/dist-lib/bridge-contract.cjs +1 -1
  24. package/dist-lib/bridge-contract.mjs +1 -1
  25. package/dist-lib/capture-contract.cjs +1 -1
  26. package/dist-lib/capture-contract.mjs +1 -1
  27. package/dist-lib/cli-constants.cjs +1 -1
  28. package/dist-lib/cloud-contract.cjs +1 -1
  29. package/dist-lib/cloud-contract.mjs +1 -1
  30. package/dist-lib/cloud-test-args.cjs +1 -1
  31. package/dist-lib/cloud-test-args.mjs +1 -1
  32. package/dist-lib/cloud.cjs +1 -1
  33. package/dist-lib/cloud.mjs +1 -1
  34. package/dist-lib/config.cjs +3 -2
  35. package/dist-lib/detox/index.cjs +2 -2
  36. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  37. package/dist-lib/home-paths.cjs +1 -1
  38. package/dist-lib/host/bridge-host.cjs +78 -35
  39. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  40. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  41. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  42. package/dist-lib/host/replacement-module-handler.cjs +51 -8
  43. package/dist-lib/host/websocket-proxy.cjs +1 -1
  44. package/dist-lib/index.cjs +317 -70
  45. package/dist-lib/jump-to-source-babel.cjs +1 -1
  46. package/dist-lib/jump-to-source-native.cjs +1 -1
  47. package/dist-lib/kotlin-host.cjs +14 -4
  48. package/dist-lib/menu.cjs +1 -1
  49. package/dist-lib/menu.mjs +1 -1
  50. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  51. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  52. package/dist-lib/metro-production-bundle.cjs +1 -1
  53. package/dist-lib/metro-production-bundle.mjs +1 -1
  54. package/dist-lib/metro.cjs +300 -29
  55. package/dist-lib/native-app-identity.cjs +58 -25
  56. package/dist-lib/native-app-identity.mjs +57 -25
  57. package/dist-lib/plist.cjs +64 -0
  58. package/dist-lib/plist.mjs +29 -0
  59. package/dist-lib/profiles.cjs +1 -1
  60. package/dist-lib/public-brand.cjs +1 -1
  61. package/dist-lib/react-native-host-modules.cjs +1 -1
  62. package/dist-lib/react-native-host-modules.mjs +1 -1
  63. package/dist-lib/render-mode.cjs +1 -1
  64. package/dist-lib/scripts/dev-server-scanner.cjs +2 -2
  65. package/dist-lib/sdk.cjs +36 -49
  66. package/dist-lib/sdk.mjs +36 -49
  67. package/dist-lib/skills.cjs +257 -138
  68. package/dist-lib/swift-compile.cjs +1 -1
  69. package/dist-lib/swift-compile.mjs +1 -1
  70. package/dist-lib/swift-package.cjs +1 -1
  71. package/dist-lib/swift-package.mjs +1 -1
  72. package/dist-lib/swift-submit.cjs +1 -1
  73. package/dist-lib/swift-submit.mjs +1 -1
  74. package/dist-lib/swift.cjs +1 -1
  75. package/dist-lib/vite-swift.cjs +1 -1
  76. package/dist-lib/vite.cjs +51 -8
  77. package/kotlin/AppSources/android/app/Activity.kt +44 -0
  78. package/kotlin/AppSources/android/app/UiModeManager.kt +9 -0
  79. package/kotlin/AppSources/android/content/Context.kt +9 -2
  80. package/kotlin/AppSources/android/content/res/Configuration.kt +16 -0
  81. package/kotlin/AppSources/android/content/res/Resources.kt +7 -0
  82. package/kotlin/AppSources/android/graphics/Color.kt +13 -0
  83. package/kotlin/AppSources/android/os/Bundle.kt +9 -3
  84. package/kotlin/AppSources/android/view/View.kt +2 -0
  85. package/kotlin/AppSources/android/view/Window.kt +99 -8
  86. package/kotlin/AppSources/android/view/WindowInsetsController.kt +12 -0
  87. package/kotlin/AppSources/android/view/WindowManager.kt +16 -0
  88. package/kotlin/AppSources/androidx/activity/BackEventCompat.kt +17 -0
  89. package/kotlin/AppSources/androidx/activity/Cancellable.kt +5 -0
  90. package/kotlin/AppSources/androidx/activity/ComponentActivity.kt +22 -3
  91. package/kotlin/AppSources/androidx/activity/EdgeToEdge.kt +71 -0
  92. package/kotlin/AppSources/androidx/activity/OnBackPressedCallback.kt +28 -0
  93. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +144 -0
  94. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcherOwner.kt +7 -0
  95. package/kotlin/AppSources/androidx/activity/compose/BackHandler.kt +55 -0
  96. package/kotlin/AppSources/androidx/core/view/WindowCompat.kt +32 -0
  97. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +67 -12
  98. package/kotlin/AppSources/dev/peach/app/PeachWindowHost.kt +2 -1
  99. package/kotlin/AppSources/java/lang/Runnable.kt +6 -0
  100. package/kotlin/ComposeSources/PeachComposeHost.kt +11 -1
  101. package/package.json +8 -1
  102. package/skills/peach-setup/SKILL.md +27 -0
  103. package/src/android-theme.ts +167 -0
  104. package/src/android-xml.ts +70 -0
  105. package/src/bridge-contract-input.ts +10 -1
  106. package/src/bridge-contract.ts +7 -1
  107. package/src/config.ts +8 -0
  108. package/src/generated/android-framework-themes.json +6303 -0
  109. package/src/host/replacement-module-handler.ts +33 -0
  110. package/src/index.ts +1 -0
  111. package/src/kotlin-app.ts +86 -61
  112. package/src/kotlin-host.ts +13 -3
  113. package/src/metro-plugin.ts +46 -3
  114. package/src/native-app-identity.ts +75 -26
  115. package/src/sim-client.ts +2 -4
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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;
@@ -559,25 +559,25 @@ function normalizeSession(input) {
559
559
  }
560
560
  function readSharedDesktopAuthSession() {
561
561
  const filepath = getSharedDesktopAuthFilePath();
562
- if (!(0, import_node_fs14.existsSync)(filepath)) return null;
562
+ if (!(0, import_node_fs15.existsSync)(filepath)) return null;
563
563
  try {
564
- const parsed = JSON.parse((0, import_node_fs14.readFileSync)(filepath, "utf8"));
564
+ const parsed = JSON.parse((0, import_node_fs15.readFileSync)(filepath, "utf8"));
565
565
  const normalized = normalizeSession(parsed);
566
566
  if (!normalized) {
567
- (0, import_node_fs14.rmSync)(filepath, { force: true });
567
+ (0, import_node_fs15.rmSync)(filepath, { force: true });
568
568
  return null;
569
569
  }
570
570
  return normalized;
571
571
  } catch {
572
- (0, import_node_fs14.rmSync)(filepath, { force: true });
572
+ (0, import_node_fs15.rmSync)(filepath, { force: true });
573
573
  return null;
574
574
  }
575
575
  }
576
- var import_node_fs14, import_node_path15, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
576
+ var import_node_fs15, import_node_path15, SESSION_VERSION, SESSION_FILE_ENV, DEFAULT_ORIGIN;
577
577
  var init_shared_session = __esm({
578
578
  "src/auth/shared-session.ts"() {
579
579
  "use strict";
580
- import_node_fs14 = require("node:fs");
580
+ import_node_fs15 = require("node:fs");
581
581
  import_node_path15 = require("node:path");
582
582
  init_home_paths();
583
583
  SESSION_VERSION = 1;
@@ -3950,7 +3950,7 @@ function detectFramework(deps) {
3950
3950
  }
3951
3951
  function detectProject(dir) {
3952
3952
  const pkgPath = (0, import_node_path16.join)(dir, "package.json");
3953
- if (!(0, import_node_fs15.existsSync)(pkgPath)) return null;
3953
+ if (!(0, import_node_fs16.existsSync)(pkgPath)) return null;
3954
3954
  const pkg = readProjectManifest(dir);
3955
3955
  if (!pkg) return null;
3956
3956
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
@@ -3968,19 +3968,19 @@ function detectProject(dir) {
3968
3968
  // apps write .cjs as often as .js, and missing one reads as "this project
3969
3969
  // has no bundler config" and sends the user to create a second one.
3970
3970
  hasViteConfig: CONFIG_EXTENSIONS.some(
3971
- (ext) => (0, import_node_fs15.existsSync)((0, import_node_path16.join)(dir, `vite.config.${ext}`))
3971
+ (ext) => (0, import_node_fs16.existsSync)((0, import_node_path16.join)(dir, `vite.config.${ext}`))
3972
3972
  ),
3973
3973
  hasMetroConfig: CONFIG_EXTENSIONS.some(
3974
- (ext) => (0, import_node_fs15.existsSync)((0, import_node_path16.join)(dir, `metro.config.${ext}`))
3974
+ (ext) => (0, import_node_fs16.existsSync)((0, import_node_path16.join)(dir, `metro.config.${ext}`))
3975
3975
  ),
3976
3976
  devCommand: typeof devCommand === "string" ? devCommand : null
3977
3977
  };
3978
3978
  }
3979
3979
  function readProjectMetadata(path12) {
3980
3980
  try {
3981
- const info = (0, import_node_fs15.lstatSync)(path12);
3981
+ const info = (0, import_node_fs16.lstatSync)(path12);
3982
3982
  if (!info.isFile() || info.size > 4 * 1024 * 1024) return null;
3983
- return (0, import_node_fs15.readFileSync)(path12, "utf8");
3983
+ return (0, import_node_fs16.readFileSync)(path12, "utf8");
3984
3984
  } catch {
3985
3985
  return null;
3986
3986
  }
@@ -4017,11 +4017,11 @@ function readProjectManifest(dir) {
4017
4017
  devDependencies: record(metadataValue(parsed, "devDependencies"))
4018
4018
  };
4019
4019
  }
4020
- var import_node_fs15, import_node_path16, import_json5, import_yaml, CONFIG_EXTENSIONS;
4020
+ var import_node_fs16, import_node_path16, import_json5, import_yaml, CONFIG_EXTENSIONS;
4021
4021
  var init_detect = __esm({
4022
4022
  "cli/commands/detect.ts"() {
4023
4023
  "use strict";
4024
- import_node_fs15 = require("node:fs");
4024
+ import_node_fs16 = require("node:fs");
4025
4025
  import_node_path16 = require("node:path");
4026
4026
  import_json5 = __toESM(require("json5"), 1);
4027
4027
  import_yaml = require("yaml");
@@ -4063,17 +4063,17 @@ function readImmutableBundleSource(bundlePath) {
4063
4063
  if ((0, import_node_path17.extname)(filepath) !== ".js") {
4064
4064
  throw new Error(`${REMOTE_COMMAND} requires one local Metro .js bundle`);
4065
4065
  }
4066
- const descriptor = (0, import_node_fs16.openSync)(filepath, "r");
4066
+ const descriptor = (0, import_node_fs17.openSync)(filepath, "r");
4067
4067
  try {
4068
- const before = (0, import_node_fs16.fstatSync)(descriptor, { bigint: true });
4068
+ const before = (0, import_node_fs17.fstatSync)(descriptor, { bigint: true });
4069
4069
  if (!before.isFile()) {
4070
4070
  throw new Error(`${REMOTE_COMMAND} bundle must be a regular local file`);
4071
4071
  }
4072
4072
  if (before.size <= 0n) {
4073
4073
  throw new Error(`${REMOTE_COMMAND} bundle is empty`);
4074
4074
  }
4075
- const bytes = (0, import_node_fs16.readFileSync)(descriptor);
4076
- const after = (0, import_node_fs16.fstatSync)(descriptor, { bigint: true });
4075
+ const bytes = (0, import_node_fs17.readFileSync)(descriptor);
4076
+ const after = (0, import_node_fs17.fstatSync)(descriptor, { bigint: true });
4077
4077
  if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeNs !== after.mtimeNs || bytes.length !== Number(before.size)) {
4078
4078
  throw new Error(`${REMOTE_COMMAND} bundle changed while it was being read`);
4079
4079
  }
@@ -4088,7 +4088,7 @@ function readImmutableBundleSource(bundlePath) {
4088
4088
  }
4089
4089
  return source;
4090
4090
  } finally {
4091
- (0, import_node_fs16.closeSync)(descriptor);
4091
+ (0, import_node_fs17.closeSync)(descriptor);
4092
4092
  }
4093
4093
  }
4094
4094
  async function fetchBundleSource(bundleUrl) {
@@ -4136,14 +4136,14 @@ async function loadBundleSource(bundleInput) {
4136
4136
  }
4137
4137
  function listMetroAssetDest(assetsPath) {
4138
4138
  const directory = (0, import_node_path17.resolve)(assetsPath);
4139
- if (!(0, import_node_fs16.existsSync)(directory)) return { directory, files: [], truncated: false };
4140
- const skipNested = (0, import_node_fs16.existsSync)((0, import_node_path17.join)(directory, "package.json"));
4139
+ if (!(0, import_node_fs17.existsSync)(directory)) return { directory, files: [], truncated: false };
4140
+ const skipNested = (0, import_node_fs17.existsSync)((0, import_node_path17.join)(directory, "package.json"));
4141
4141
  const files = [];
4142
4142
  let truncated = false;
4143
4143
  const walk = (dir, prefix) => {
4144
4144
  let entries;
4145
4145
  try {
4146
- entries = (0, import_node_fs16.readdirSync)(dir, { withFileTypes: true });
4146
+ entries = (0, import_node_fs17.readdirSync)(dir, { withFileTypes: true });
4147
4147
  } catch {
4148
4148
  return;
4149
4149
  }
@@ -4190,7 +4190,7 @@ function distinctByContent(directory, options) {
4190
4190
  for (const relative2 of options) {
4191
4191
  let bytes;
4192
4192
  try {
4193
- bytes = (0, import_node_fs16.readFileSync)((0, import_node_path17.join)(directory, relative2));
4193
+ bytes = (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(directory, relative2));
4194
4194
  } catch {
4195
4195
  continue;
4196
4196
  }
@@ -4222,7 +4222,7 @@ function placeMetroDescriptor(listing, descriptor) {
4222
4222
  let bytes;
4223
4223
  try {
4224
4224
  bytes = combination.map(
4225
- (relative2) => (0, import_node_fs16.readFileSync)((0, import_node_path17.join)(listing.directory, relative2))
4225
+ (relative2) => (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(listing.directory, relative2))
4226
4226
  );
4227
4227
  } catch {
4228
4228
  continue;
@@ -4393,7 +4393,7 @@ async function embedBundleAssets(bundlePath, command, assetPolicy) {
4393
4393
  );
4394
4394
  }
4395
4395
  const relative2 = placement?.files.get(variant.scale);
4396
- if (relative2 && dest) return (0, import_node_fs16.readFileSync)((0, import_node_path17.join)(dest.directory, relative2));
4396
+ if (relative2 && dest) return (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(dest.directory, relative2));
4397
4397
  if (placement && placement.files.size > 0) return null;
4398
4398
  if (placement && placement.ambiguous.length > 0 && dest) {
4399
4399
  throw new Error(
@@ -4568,12 +4568,12 @@ async function createCloudBox(options) {
4568
4568
  warnings: artifact.warnings
4569
4569
  };
4570
4570
  }
4571
- var import_node_crypto6, import_node_fs16, import_node_path17, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS;
4571
+ var import_node_crypto6, import_node_fs17, import_node_path17, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS;
4572
4572
  var init_cloud_client = __esm({
4573
4573
  "cli/cloud-client.ts"() {
4574
4574
  "use strict";
4575
4575
  import_node_crypto6 = require("node:crypto");
4576
- import_node_fs16 = require("node:fs");
4576
+ import_node_fs17 = require("node:fs");
4577
4577
  import_node_path17 = require("node:path");
4578
4578
  init_metroAssetArtifact();
4579
4579
  init_metroCloudArtifact();
@@ -4856,7 +4856,7 @@ function hasOwnKeys(value) {
4856
4856
  }
4857
4857
  function hasPeachConfig(config) {
4858
4858
  if (!config) return false;
4859
- return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
4859
+ return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
4860
4860
  }
4861
4861
  function applyPeachConfigToUrl(url, config) {
4862
4862
  const parsed = new URL(url);
@@ -8706,6 +8706,7 @@ var PlaneHost = class {
8706
8706
  };
8707
8707
 
8708
8708
  // src/host/replacement-module-handler.ts
8709
+ var import_node_fs14 = require("node:fs");
8709
8710
  var import_promises5 = __toESM(require("node:fs/promises"), 1);
8710
8711
  var import_node_path14 = __toESM(require("node:path"), 1);
8711
8712
 
@@ -8835,12 +8836,15 @@ function transformsFor(filePath) {
8835
8836
  );
8836
8837
  }
8837
8838
  }
8839
+ function isLocalSpecifier(specifier) {
8840
+ return specifier.startsWith("./") || specifier.startsWith("../");
8841
+ }
8838
8842
  function isContrastKitSpecifier(specifier) {
8839
8843
  return specifier === "contrast-kit" || specifier.startsWith("contrast-kit/");
8840
8844
  }
8841
8845
  function unsupportedSpecifier(specifier) {
8842
8846
  return new Error(
8843
- `replacement modules may only import react, react-native, contrast-kit, and the packages rnx stubs for this app; '${specifier}' is none of those. vendor the code into the replacement file, or add the package to peach.config so the simulator bridges it.`
8847
+ `replacement modules may only import react, react-native, contrast-kit, the packages rnx stubs for this app, and other project files by relative path; '${specifier}' is none of those. vendor the code into a project file, or add the package to peach.config so the simulator bridges it.`
8844
8848
  );
8845
8849
  }
8846
8850
  function bridgeDeclaration(name2, specifier) {
@@ -8865,11 +8869,9 @@ function compileReplacementModule(input) {
8865
8869
  const transformed = (0, import_dist.transform)(input.source, {
8866
8870
  transforms: transformsFor(input.filePath),
8867
8871
  filePath: input.filePath,
8868
- // classic runtime, matching what the app's own Metro build does for a file
8869
- // that has no automatic-runtime pragma: the file imports React itself.
8870
- jsxRuntime: "classic",
8871
- jsxPragma: "React.createElement",
8872
- jsxFragmentPragma: "React.Fragment",
8872
+ // the automatic runtime, as @react-native/babel-preset and babel-preset-expo
8873
+ // compile the app's own files: jsx needs no React import.
8874
+ jsxRuntime: "automatic",
8873
8875
  production: true,
8874
8876
  sourceMapOptions: { compiledFilename: input.filePath }
8875
8877
  });
@@ -8896,6 +8898,13 @@ function compileReplacementModule(input) {
8896
8898
  return name2;
8897
8899
  };
8898
8900
  const edits = [];
8901
+ const pointAtLocalModule = (specifierToken, specifier) => {
8902
+ edits.push({
8903
+ start: specifierToken.start,
8904
+ end: specifierToken.end,
8905
+ text: JSON.stringify(input.localModuleUrl(specifier))
8906
+ });
8907
+ };
8899
8908
  for (let index = 0; index < tokens.length; index++) {
8900
8909
  const token = tokens[index];
8901
8910
  if (token.type === EXPORT_KEYWORD) {
@@ -8915,6 +8924,10 @@ function compileReplacementModule(input) {
8915
8924
  }
8916
8925
  if (fromAt > 0 && tokens[fromAt] && read(tokens[fromAt]) === "from" && tokens[fromAt + 1]?.type === import_types.TokenType.string) {
8917
8926
  const specifier2 = readSpecifier(tokens[fromAt + 1]);
8927
+ if (isLocalSpecifier(specifier2)) {
8928
+ pointAtLocalModule(tokens[fromAt + 1], specifier2);
8929
+ continue;
8930
+ }
8918
8931
  throw new Error(
8919
8932
  `a replacement module cannot re-export from another module, and this one re-exports from '${specifier2}'. import what you need and export the binding.`
8920
8933
  );
@@ -8967,6 +8980,10 @@ function compileReplacementModule(input) {
8967
8980
  const specifierToken = tokens[cursor];
8968
8981
  if (!specifierToken) throw new Error("replacement module import is unterminated");
8969
8982
  const specifier = readSpecifier(specifierToken);
8983
+ if (isLocalSpecifier(specifier)) {
8984
+ pointAtLocalModule(specifierToken, specifier);
8985
+ continue;
8986
+ }
8970
8987
  if (specifier !== "react" && specifier !== "react-native" && !JSX_RUNTIME_SPECIFIERS.has(specifier) && !isContrastKitSpecifier(specifier) && !input.runtimePackages.has(specifier)) {
8971
8988
  throw unsupportedSpecifier(specifier);
8972
8989
  }
@@ -9015,6 +9032,25 @@ function sendJavaScript(req, res, source) {
9015
9032
  });
9016
9033
  res.end(req.method === "HEAD" ? void 0 : source);
9017
9034
  }
9035
+ function resolveLocalImport(fromFile, specifier) {
9036
+ const base = import_node_path14.default.resolve(import_node_path14.default.dirname(fromFile), specifier);
9037
+ const candidates = [
9038
+ ...REPLACEMENT_MODULE_EXTENSIONS.some(
9039
+ (ext) => ext === import_node_path14.default.extname(base).toLowerCase()
9040
+ ) ? [base] : [],
9041
+ ...REPLACEMENT_MODULE_EXTENSIONS.map((ext) => base + ext),
9042
+ ...REPLACEMENT_MODULE_EXTENSIONS.map((ext) => import_node_path14.default.join(base, `index${ext}`))
9043
+ ];
9044
+ const found = candidates.find(
9045
+ (candidate) => (0, import_node_fs14.statSync)(candidate, { throwIfNoEntry: false })?.isFile()
9046
+ );
9047
+ if (!found) {
9048
+ throw new Error(
9049
+ `'${specifier}' imported by ${fromFile} matches no ${REPLACEMENT_MODULE_EXTENSIONS.join(", ")} file`
9050
+ );
9051
+ }
9052
+ return found;
9053
+ }
9018
9054
  function isReplacementModuleRequestUrl(rawUrl) {
9019
9055
  if (!rawUrl) return false;
9020
9056
  try {
@@ -9105,7 +9141,14 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
9105
9141
  const compiled = compileReplacementModule({
9106
9142
  filePath: resolvedTargetPath,
9107
9143
  source: await import_promises5.default.readFile(resolvedTargetPath, "utf8"),
9108
- runtimePackages
9144
+ runtimePackages,
9145
+ // the imported file's url is this request's with only its path changed,
9146
+ // so the token, cache key and runtime packages carry over.
9147
+ localModuleUrl: (specifier) => {
9148
+ const local = new URL(url.href);
9149
+ local.searchParams.set("path", resolveLocalImport(targetPath, specifier));
9150
+ return `${local.pathname}${local.search}`;
9151
+ }
9109
9152
  });
9110
9153
  const map = Buffer.from(JSON.stringify(compiled.sourceMap), "utf8").toString("base64");
9111
9154
  sendJavaScript(
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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;
@@ -37,6 +37,7 @@ __export(replacement_module_handler_exports, {
37
37
  isReplacementModuleRequestUrl: () => isReplacementModuleRequestUrl
38
38
  });
39
39
  module.exports = __toCommonJS(replacement_module_handler_exports);
40
+ var import_node_fs = require("node:fs");
40
41
  var import_promises = __toESM(require("node:fs/promises"), 1);
41
42
  var import_node_path = __toESM(require("node:path"), 1);
42
43
 
@@ -166,12 +167,15 @@ function transformsFor(filePath) {
166
167
  );
167
168
  }
168
169
  }
170
+ function isLocalSpecifier(specifier) {
171
+ return specifier.startsWith("./") || specifier.startsWith("../");
172
+ }
169
173
  function isContrastKitSpecifier(specifier) {
170
174
  return specifier === "contrast-kit" || specifier.startsWith("contrast-kit/");
171
175
  }
172
176
  function unsupportedSpecifier(specifier) {
173
177
  return new Error(
174
- `replacement modules may only import react, react-native, contrast-kit, and the packages rnx stubs for this app; '${specifier}' is none of those. vendor the code into the replacement file, or add the package to peach.config so the simulator bridges it.`
178
+ `replacement modules may only import react, react-native, contrast-kit, the packages rnx stubs for this app, and other project files by relative path; '${specifier}' is none of those. vendor the code into a project file, or add the package to peach.config so the simulator bridges it.`
175
179
  );
176
180
  }
177
181
  function bridgeDeclaration(name, specifier) {
@@ -196,11 +200,9 @@ function compileReplacementModule(input) {
196
200
  const transformed = (0, import_dist.transform)(input.source, {
197
201
  transforms: transformsFor(input.filePath),
198
202
  filePath: input.filePath,
199
- // classic runtime, matching what the app's own Metro build does for a file
200
- // that has no automatic-runtime pragma: the file imports React itself.
201
- jsxRuntime: "classic",
202
- jsxPragma: "React.createElement",
203
- jsxFragmentPragma: "React.Fragment",
203
+ // the automatic runtime, as @react-native/babel-preset and babel-preset-expo
204
+ // compile the app's own files: jsx needs no React import.
205
+ jsxRuntime: "automatic",
204
206
  production: true,
205
207
  sourceMapOptions: { compiledFilename: input.filePath }
206
208
  });
@@ -227,6 +229,13 @@ function compileReplacementModule(input) {
227
229
  return name;
228
230
  };
229
231
  const edits = [];
232
+ const pointAtLocalModule = (specifierToken, specifier) => {
233
+ edits.push({
234
+ start: specifierToken.start,
235
+ end: specifierToken.end,
236
+ text: JSON.stringify(input.localModuleUrl(specifier))
237
+ });
238
+ };
230
239
  for (let index = 0; index < tokens.length; index++) {
231
240
  const token = tokens[index];
232
241
  if (token.type === EXPORT_KEYWORD) {
@@ -246,6 +255,10 @@ function compileReplacementModule(input) {
246
255
  }
247
256
  if (fromAt > 0 && tokens[fromAt] && read(tokens[fromAt]) === "from" && tokens[fromAt + 1]?.type === import_types.TokenType.string) {
248
257
  const specifier2 = readSpecifier(tokens[fromAt + 1]);
258
+ if (isLocalSpecifier(specifier2)) {
259
+ pointAtLocalModule(tokens[fromAt + 1], specifier2);
260
+ continue;
261
+ }
249
262
  throw new Error(
250
263
  `a replacement module cannot re-export from another module, and this one re-exports from '${specifier2}'. import what you need and export the binding.`
251
264
  );
@@ -298,6 +311,10 @@ function compileReplacementModule(input) {
298
311
  const specifierToken = tokens[cursor];
299
312
  if (!specifierToken) throw new Error("replacement module import is unterminated");
300
313
  const specifier = readSpecifier(specifierToken);
314
+ if (isLocalSpecifier(specifier)) {
315
+ pointAtLocalModule(specifierToken, specifier);
316
+ continue;
317
+ }
301
318
  if (specifier !== "react" && specifier !== "react-native" && !JSX_RUNTIME_SPECIFIERS.has(specifier) && !isContrastKitSpecifier(specifier) && !input.runtimePackages.has(specifier)) {
302
319
  throw unsupportedSpecifier(specifier);
303
320
  }
@@ -354,6 +371,25 @@ function sendJavaScript(req, res, source) {
354
371
  });
355
372
  res.end(req.method === "HEAD" ? void 0 : source);
356
373
  }
374
+ function resolveLocalImport(fromFile, specifier) {
375
+ const base = import_node_path.default.resolve(import_node_path.default.dirname(fromFile), specifier);
376
+ const candidates = [
377
+ ...REPLACEMENT_MODULE_EXTENSIONS.some(
378
+ (ext) => ext === import_node_path.default.extname(base).toLowerCase()
379
+ ) ? [base] : [],
380
+ ...REPLACEMENT_MODULE_EXTENSIONS.map((ext) => base + ext),
381
+ ...REPLACEMENT_MODULE_EXTENSIONS.map((ext) => import_node_path.default.join(base, `index${ext}`))
382
+ ];
383
+ const found = candidates.find(
384
+ (candidate) => (0, import_node_fs.statSync)(candidate, { throwIfNoEntry: false })?.isFile()
385
+ );
386
+ if (!found) {
387
+ throw new Error(
388
+ `'${specifier}' imported by ${fromFile} matches no ${REPLACEMENT_MODULE_EXTENSIONS.join(", ")} file`
389
+ );
390
+ }
391
+ return found;
392
+ }
357
393
  function isReplacementModuleRequestUrl(rawUrl) {
358
394
  if (!rawUrl) return false;
359
395
  try {
@@ -444,7 +480,14 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
444
480
  const compiled = compileReplacementModule({
445
481
  filePath: resolvedTargetPath,
446
482
  source: await import_promises.default.readFile(resolvedTargetPath, "utf8"),
447
- runtimePackages
483
+ runtimePackages,
484
+ // the imported file's url is this request's with only its path changed,
485
+ // so the token, cache key and runtime packages carry over.
486
+ localModuleUrl: (specifier) => {
487
+ const local = new URL(url.href);
488
+ local.searchParams.set("path", resolveLocalImport(targetPath, specifier));
489
+ return `${local.pathname}${local.search}`;
490
+ }
448
491
  });
449
492
  const map = Buffer.from(JSON.stringify(compiled.sourceMap), "utf8").toString("base64");
450
493
  sendJavaScript(
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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;