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
package/dist-lib/vite.cjs CHANGED
@@ -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;
@@ -40,7 +40,7 @@ __export(vite_plugin_one_exports, {
40
40
  });
41
41
  module.exports = __toCommonJS(vite_plugin_one_exports);
42
42
  var import_fs2 = __toESM(require("fs"), 1);
43
- var import_node_crypto3 = require("node:crypto");
43
+ var import_node_crypto4 = require("node:crypto");
44
44
  var import_path2 = __toESM(require("path"), 1);
45
45
 
46
46
  // src/bridge-constants.ts
@@ -622,7 +622,7 @@ function findMonorepoRoot() {
622
622
  if (_monorepoRootCache !== void 0) return _monorepoRootCache;
623
623
  let dir = import_path.default.dirname((0, import_url.fileURLToPath)(__sootsim_import_meta_url));
624
624
  for (let i = 0; i < 12 && dir !== import_path.default.dirname(dir); i++) {
625
- 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"))) {
625
+ 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"))) {
626
626
  _monorepoRootCache = dir;
627
627
  return dir;
628
628
  }
@@ -636,7 +636,7 @@ function resolveDevCheckoutRuntimeRoot() {
636
636
  const root = findMonorepoRoot();
637
637
  if (!root) return null;
638
638
  const devBuild = import_path.default.join(root, "public/sootsim");
639
- 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"))) {
639
+ 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"))) {
640
640
  return devBuild;
641
641
  }
642
642
  return null;
@@ -713,10 +713,10 @@ function resolveRuntimeRootForHostname(hostname) {
713
713
  const version = runtimeVersionFromHostname(hostname);
714
714
  return version ? resolveInstalledRuntimeRoot(version) : null;
715
715
  }
716
- function resolveRuntimeRootForRequestHost(host) {
717
- if (!host) return resolveActiveRuntimeRoot();
716
+ function resolveRuntimeRootForRequestHost(host2) {
717
+ if (!host2) return resolveActiveRuntimeRoot();
718
718
  try {
719
- return resolveRuntimeRootForHostname(new URL(`http://${host}`).hostname);
719
+ return resolveRuntimeRootForHostname(new URL(`http://${host2}`).hostname);
720
720
  } catch {
721
721
  return null;
722
722
  }
@@ -757,12 +757,10 @@ function serveRuntimeFile(req, res, fullPath) {
757
757
  }
758
758
 
759
759
  // src/vite-plugin-swift.ts
760
- var import_node_child_process2 = require("node:child_process");
761
- var import_node_crypto2 = require("node:crypto");
762
- var import_node_fs3 = __toESM(require("node:fs"), 1);
760
+ var import_node_crypto3 = require("node:crypto");
761
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
763
762
  var import_node_os2 = __toESM(require("node:os"), 1);
764
- var import_node_path4 = __toESM(require("node:path"), 1);
765
- var import_node_util = require("node:util");
763
+ var import_node_path5 = __toESM(require("node:path"), 1);
766
764
 
767
765
  // ../sootsim-swift/src/bundleAssets.ts
768
766
  var RASTER_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -797,13 +795,13 @@ function imagesetName(contentsPath) {
797
795
  if (!leaf.endsWith(".imageset")) return null;
798
796
  return leaf.slice(0, -".imageset".length) || null;
799
797
  }
800
- function isFlatResourceImage(path8) {
801
- return path8.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path8));
798
+ function isFlatResourceImage(path9) {
799
+ return path9.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path9));
802
800
  }
803
801
  function enumerateBundleImages(paths, readText) {
804
802
  const table = [];
805
803
  const seen = /* @__PURE__ */ new Set();
806
- const catalogs = paths.filter((path8) => baseOf(path8) === "Contents.json");
804
+ const catalogs = paths.filter((path9) => baseOf(path9) === "Contents.json");
807
805
  for (const contentsPath of catalogs) {
808
806
  const name = imagesetName(contentsPath);
809
807
  if (name === null || seen.has(name)) continue;
@@ -823,12 +821,12 @@ function enumerateBundleImages(paths, readText) {
823
821
  seen.add(name);
824
822
  table.push({ name, file: `${dir}/${file}` });
825
823
  }
826
- for (const path8 of paths) {
827
- if (!isFlatResourceImage(path8)) continue;
828
- const name = baseOf(path8).slice(0, -extOf(path8).length);
824
+ for (const path9 of paths) {
825
+ if (!isFlatResourceImage(path9)) continue;
826
+ const name = baseOf(path9).slice(0, -extOf(path9).length);
829
827
  if (name === "" || seen.has(name)) continue;
830
828
  seen.add(name);
831
- table.push({ name, file: path8 });
829
+ table.push({ name, file: path9 });
832
830
  }
833
831
  return table;
834
832
  }
@@ -836,7 +834,7 @@ function enumerateBundleJson(paths) {
836
834
  const found = [];
837
835
  const seen = /* @__PURE__ */ new Set();
838
836
  const ordered = [...paths].filter(
839
- (path8) => path8.split("/").includes("Resources") && path8.split("/").every((segment) => segment !== "" && segment !== "..") && extOf(path8) === ".json"
837
+ (path9) => path9.split("/").includes("Resources") && path9.split("/").every((segment) => segment !== "" && segment !== "..") && extOf(path9) === ".json"
840
838
  ).sort((a, b) => {
841
839
  const aDepth = a.split("/").length;
842
840
  const bDepth = b.split("/").length;
@@ -851,20 +849,331 @@ function enumerateBundleJson(paths) {
851
849
  return found;
852
850
  }
853
851
 
854
- // ../sootsim-swift/toolchain.json
855
- var toolchain_default = {
856
- release: "swift-6.3-RELEASE",
857
- wasmSdkChecksum: "9fa4016ee632c7e9e906608ec3b55cf13dfc4dff44e47574c5af58064dc33fd9"
858
- };
852
+ // ../sootsim-swift/src/entryModule.ts
853
+ function swiftEntryModuleEsm(args) {
854
+ return [
855
+ `import { createSwiftEntry } from 'peachsim/swift'`,
856
+ `export default createSwiftEntry({ url: ${JSON.stringify(args.url)}, hash: ${JSON.stringify(args.hash)}, rootId: ${JSON.stringify(args.rootId)}, resources: ${JSON.stringify(args.resources)}, bundleFiles: ${JSON.stringify(args.bundleFiles)} })`,
857
+ ""
858
+ ].join("\n");
859
+ }
859
860
 
860
- // src/vite-plugin-swift.ts
861
+ // src/swift-compile.ts
862
+ var import_node_child_process2 = require("node:child_process");
863
+ var import_node_crypto2 = require("node:crypto");
864
+ var import_node_fs3 = require("node:fs");
865
+ var import_node_path4 = __toESM(require("node:path"), 1);
866
+ var import_node_util = require("node:util");
867
+
868
+ // src/swift-package.ts
869
+ function swiftManifestTargets(manifest) {
870
+ const code = manifest.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
871
+ const targets = [];
872
+ const pattern = /\.(executableTarget|target)\s*\(/g;
873
+ let match;
874
+ while ((match = pattern.exec(code)) !== null) {
875
+ const executable = match[1] === "executableTarget";
876
+ let depth = 1;
877
+ let i = pattern.lastIndex;
878
+ let inString = false;
879
+ let escaped = false;
880
+ let flat = "";
881
+ while (i < code.length && depth > 0) {
882
+ const ch = code[i];
883
+ if (inString) {
884
+ if (escaped) escaped = false;
885
+ else if (ch === "\\") escaped = true;
886
+ else if (ch === '"') inString = false;
887
+ if (depth === 1) flat += ch;
888
+ } else if (ch === '"') {
889
+ inString = true;
890
+ if (depth === 1) flat += ch;
891
+ } else if (ch === "(") {
892
+ depth++;
893
+ if (depth === 1) flat += ch;
894
+ } else if (ch === ")") {
895
+ depth--;
896
+ if (depth >= 1) flat += ch;
897
+ } else if (depth === 1) {
898
+ flat += ch;
899
+ }
900
+ i++;
901
+ }
902
+ const name = /\bname\s*:\s*"([^"]+)"/.exec(flat)?.[1];
903
+ if (!name) continue;
904
+ const rawPath = /\bpath\s*:\s*"([^"]*)"/.exec(flat)?.[1];
905
+ const targetPath = rawPath == null || rawPath === "" ? `Sources/${name}` : rawPath.replace(/^\.\//, "").replace(/\/+$/, "");
906
+ targets.push({ name, path: targetPath, executable });
907
+ }
908
+ return targets;
909
+ }
910
+
911
+ // src/swift-compile.ts
861
912
  var run = (0, import_node_util.promisify)(import_node_child_process2.execFile);
862
- var TOOLCHAIN_BIN = import_node_path4.default.join(
863
- import_node_os2.default.homedir(),
864
- `Library/Developer/Toolchains/${toolchain_default.release}.xctoolchain/usr/bin`
865
- );
866
- var WASM_SDK = `${toolchain_default.release}_wasm`;
867
913
  var WASM_TRIPLE = "wasm32-unknown-wasip1";
914
+ function swiftModuleInputFiles(root) {
915
+ const files = ["Package.swift", "scripts/contrast-app-Package.swift"];
916
+ const walk = (dir) => {
917
+ for (const entry of (0, import_node_fs3.readdirSync)(import_node_path4.default.join(root, dir), { withFileTypes: true })) {
918
+ const relative2 = `${dir}/${entry.name}`;
919
+ if (entry.isDirectory()) walk(relative2);
920
+ else if (entry.name.endsWith(".swift")) files.push(relative2);
921
+ }
922
+ };
923
+ walk("Sources");
924
+ return files.sort();
925
+ }
926
+ var linkedModuleDigests = /* @__PURE__ */ new Map();
927
+ function linkedModuleDigest(root) {
928
+ const files = swiftModuleInputFiles(root);
929
+ const stampHash = (0, import_node_crypto2.createHash)("sha256");
930
+ for (const file of files) {
931
+ const stat = (0, import_node_fs3.statSync)(import_node_path4.default.join(root, file), { bigint: true });
932
+ stampHash.update(file);
933
+ stampHash.update(
934
+ `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}
935
+ `
936
+ );
937
+ }
938
+ const stamp = stampHash.digest("hex");
939
+ const cached = linkedModuleDigests.get(root);
940
+ if (cached?.stamp === stamp) return cached.digest;
941
+ const hash = (0, import_node_crypto2.createHash)("sha256");
942
+ for (const file of files) {
943
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
944
+ hash.update(
945
+ (0, import_node_crypto2.createHash)("sha256").update((0, import_node_fs3.readFileSync)(import_node_path4.default.join(root, file))).digest()
946
+ );
947
+ }
948
+ const digest = hash.digest();
949
+ linkedModuleDigests.set(root, { stamp, digest });
950
+ return digest;
951
+ }
952
+ function compileTargets(input) {
953
+ const manifest = input.files["Package.swift"];
954
+ return manifest === void 0 ? [] : swiftManifestTargets(manifest);
955
+ }
956
+ function swiftSiblingDependencies(files, targets) {
957
+ const names = new Set(targets.map((target) => target.name));
958
+ const siblings = /* @__PURE__ */ new Map();
959
+ for (const target of targets) {
960
+ const prefix = `${target.path}/`;
961
+ const deps = /* @__PURE__ */ new Set();
962
+ for (const [file, contents] of Object.entries(files)) {
963
+ if (file === "Package.swift" || !file.startsWith(prefix)) continue;
964
+ for (const match of contents.matchAll(/^\s*import\s+([A-Za-z_][A-Za-z0-9_]*)/gm)) {
965
+ if (names.has(match[1]) && match[1] !== target.name) deps.add(match[1]);
966
+ }
967
+ }
968
+ siblings.set(target.name, [...deps].sort());
969
+ }
970
+ return siblings;
971
+ }
972
+ var MULTI_MANIFEST_VERSION = 1;
973
+ function swiftArtifactHash(input, release, moduleRoot) {
974
+ const hash = (0, import_node_crypto2.createHash)("sha256");
975
+ hash.update(`swift-compile/v1
976
+ ${release}
977
+ `);
978
+ hash.update(linkedModuleDigest(moduleRoot));
979
+ for (const file of Object.keys(input.files).sort()) {
980
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
981
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(input.files[file]).digest());
982
+ }
983
+ for (const base of Object.keys(input.resources ?? {}).sort()) {
984
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(base).digest());
985
+ hash.update(
986
+ (0, import_node_crypto2.createHash)("sha256").update(input.resources?.[base] ?? "").digest()
987
+ );
988
+ }
989
+ for (const name of Object.keys(input.images ?? {}).sort()) {
990
+ const image = input.images?.[name];
991
+ if (!image) continue;
992
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(name).digest());
993
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(image.file).digest());
994
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(image.data).digest());
995
+ }
996
+ const targets = compileTargets(input);
997
+ if (targets.length > 0) {
998
+ hash.update(`swift-multi/v${MULTI_MANIFEST_VERSION}
999
+ `);
1000
+ hash.update(
1001
+ JSON.stringify({
1002
+ targets,
1003
+ siblings: [...swiftSiblingDependencies(input.files, targets)].sort(
1004
+ (a, b) => String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0
1005
+ )
1006
+ })
1007
+ );
1008
+ }
1009
+ return hash.digest("hex");
1010
+ }
1011
+ function emitSwiftPackageManifest(args) {
1012
+ const { targets, siblings, moduleRoot } = args;
1013
+ const executable = targets.find((target) => target.executable);
1014
+ if (!executable) throw new Error("swift-compile: no executable target to emit");
1015
+ const swiftSettings = 'swiftSettings: [.swiftLanguageMode(.v5), .unsafeFlags(["-Osize"])]';
1016
+ const stanza = (target) => {
1017
+ const deps = [
1018
+ '.product(name: "SwiftUI", package: "sootsim-swift")',
1019
+ ...(siblings.get(target.name) ?? []).map((sibling) => `"${sibling}"`)
1020
+ ].join(", ");
1021
+ const kind = target.executable ? ".executableTarget" : ".target";
1022
+ const link = target.executable ? `,
1023
+ linkerSettings: [
1024
+ .unsafeFlags([
1025
+ "-Xclang-linker", "-mexec-model=reactor",
1026
+ "-Xlinker", "--export-if-defined=__main_argc_argv",
1027
+ ]),
1028
+ .unsafeFlags(["-Xlinker", "--strip-all"], .when(configuration: .release)),
1029
+ ]` : "";
1030
+ return [
1031
+ ` ${kind}(`,
1032
+ ` name: "${target.name}",`,
1033
+ ` dependencies: [${deps}],`,
1034
+ ` path: "${target.path}",`,
1035
+ ` ${swiftSettings}${link}`,
1036
+ ` ),`
1037
+ ].join("\n");
1038
+ };
1039
+ return [
1040
+ "// swift-tools-version: 6.0",
1041
+ "import PackageDescription",
1042
+ "",
1043
+ "// generated by swift-compile from the project package partition.",
1044
+ "let package = Package(",
1045
+ ' name: "app",',
1046
+ ` products: [.executable(name: "${executable.name}", targets: ["${executable.name}"])],`,
1047
+ ` dependencies: [.package(path: "${moduleRoot}")],`,
1048
+ " targets: [",
1049
+ ...targets.map(stanza),
1050
+ " ]",
1051
+ ")",
1052
+ ""
1053
+ ].join("\n");
1054
+ }
1055
+ function writeSwiftProject(layout, input, moduleRoot) {
1056
+ const targets = compileTargets(input);
1057
+ if (targets.length === 0) {
1058
+ (0, import_node_fs3.rmSync)(layout.sources, { recursive: true, force: true });
1059
+ for (const [file, contents] of Object.entries(input.files)) {
1060
+ const target = import_node_path4.default.join(layout.sources, file);
1061
+ (0, import_node_fs3.mkdirSync)(import_node_path4.default.dirname(target), { recursive: true });
1062
+ (0, import_node_fs3.writeFileSync)(target, contents, "utf8");
1063
+ }
1064
+ const manifest = (0, import_node_fs3.readFileSync)(
1065
+ import_node_path4.default.join(moduleRoot, "scripts/contrast-app-Package.swift"),
1066
+ "utf8"
1067
+ ).replaceAll("__SOOTSIM_SWIFT_PACKAGE__", moduleRoot);
1068
+ (0, import_node_fs3.writeFileSync)(import_node_path4.default.join(layout.root, "Package.swift"), manifest, "utf8");
1069
+ return;
1070
+ }
1071
+ (0, import_node_fs3.mkdirSync)(layout.root, { recursive: true });
1072
+ for (const child of (0, import_node_fs3.readdirSync)(layout.root)) {
1073
+ if (child === ".build") continue;
1074
+ (0, import_node_fs3.rmSync)(import_node_path4.default.join(layout.root, child), { recursive: true, force: true });
1075
+ }
1076
+ for (const [file, contents] of Object.entries(input.files)) {
1077
+ if (file === "Package.swift") continue;
1078
+ const target = import_node_path4.default.join(layout.root, file);
1079
+ (0, import_node_fs3.mkdirSync)(import_node_path4.default.dirname(target), { recursive: true });
1080
+ (0, import_node_fs3.writeFileSync)(target, contents, "utf8");
1081
+ }
1082
+ (0, import_node_fs3.writeFileSync)(
1083
+ import_node_path4.default.join(layout.root, "Package.swift"),
1084
+ emitSwiftPackageManifest({
1085
+ targets,
1086
+ siblings: swiftSiblingDependencies(input.files, targets),
1087
+ moduleRoot
1088
+ }),
1089
+ "utf8"
1090
+ );
1091
+ }
1092
+ function streamText(value) {
1093
+ if (typeof value === "string") return value.trim();
1094
+ if (Buffer.isBuffer(value)) return value.toString("utf8").trim();
1095
+ return "";
1096
+ }
1097
+ function outputOf(error) {
1098
+ if (error && typeof error === "object") {
1099
+ const streams = [
1100
+ streamText(Reflect.get(error, "stdout")),
1101
+ streamText(Reflect.get(error, "stderr"))
1102
+ ].filter(Boolean);
1103
+ if (streams.length > 0) return streams.join("\n");
1104
+ }
1105
+ return error instanceof Error ? error.message : String(error);
1106
+ }
1107
+ function builtArtifact(layout) {
1108
+ const binDir = import_node_path4.default.join(layout.root, ".build", WASM_TRIPLE, "release");
1109
+ const artifacts = (0, import_node_fs3.existsSync)(binDir) ? (0, import_node_fs3.readdirSync)(binDir).filter((name) => name.endsWith(".wasm")).sort() : [];
1110
+ if (artifacts.length !== 1) {
1111
+ throw new Error(
1112
+ `swift-compile: the build produced ${artifacts.length} wasm artifacts; exactly one executable is the app`
1113
+ );
1114
+ }
1115
+ return import_node_path4.default.join(binDir, artifacts[0]);
1116
+ }
1117
+ function projectDiagnostic(output, layout) {
1118
+ const base = layout.multi ? layout.root : layout.sources;
1119
+ const prefix = `${base}${import_node_path4.default.sep}`;
1120
+ return output.split("\n").map((line) => line.startsWith(prefix) ? line.slice(prefix.length) : line).join("\n").trim();
1121
+ }
1122
+ var ROOT_LOCKS = Symbol.for("peachsim.swiftCompile.rootLocks");
1123
+ var host = globalThis;
1124
+ var rootLocks = host[ROOT_LOCKS] ??= /* @__PURE__ */ new Map();
1125
+ function serialize(root, work) {
1126
+ const current = rootLocks.get(root) ?? Promise.resolve();
1127
+ const next = current.then(work, work);
1128
+ rootLocks.set(
1129
+ root,
1130
+ next.catch(() => {
1131
+ })
1132
+ );
1133
+ return next;
1134
+ }
1135
+ function compileSwiftProjectUnit(input, options) {
1136
+ (0, import_node_fs3.mkdirSync)(options.buildRoot, { recursive: true });
1137
+ const root = (0, import_node_fs3.realpathSync)(options.buildRoot);
1138
+ const moduleRoot = import_node_path4.default.resolve(options.moduleRoot);
1139
+ const layout = {
1140
+ root,
1141
+ sources: import_node_path4.default.join(root, "Sources", "App"),
1142
+ multi: compileTargets(input).length > 0
1143
+ };
1144
+ const hash = swiftArtifactHash(input, options.release, moduleRoot);
1145
+ return serialize(root, async () => {
1146
+ writeSwiftProject(layout, input, moduleRoot);
1147
+ const swift = import_node_path4.default.join(options.toolchainBin, "swift");
1148
+ if (!(0, import_node_fs3.existsSync)(swift)) {
1149
+ throw new Error(
1150
+ `swift-compile: no swift.org toolchain at ${options.toolchainBin}; install ${options.release} and its matching wasm sdk`
1151
+ );
1152
+ }
1153
+ try {
1154
+ await run(
1155
+ swift,
1156
+ [
1157
+ "build",
1158
+ "--package-path",
1159
+ root,
1160
+ "--swift-sdk",
1161
+ `${options.release}_wasm`,
1162
+ "-c",
1163
+ "release",
1164
+ "-Xswiftc",
1165
+ "-Osize"
1166
+ ],
1167
+ { maxBuffer: 64 * 1024 * 1024, timeout: options.timeoutMs }
1168
+ );
1169
+ } catch (error) {
1170
+ throw new Error(projectDiagnostic(outputOf(error), layout), { cause: error });
1171
+ }
1172
+ return { hash, bytes: (0, import_node_fs3.readFileSync)(builtArtifact(layout)) };
1173
+ });
1174
+ }
1175
+
1176
+ // src/vite-plugin-swift.ts
868
1177
  var ARTIFACT_PATH = "/__rnx/swift/";
869
1178
  var RESOURCE_PATH = "/__rnx/swift-res/";
870
1179
  var VIRTUAL_PREFIX = "\0rnx-swift:";
@@ -885,11 +1194,11 @@ function projectFor(packagePath) {
885
1194
  function sourceFiles(root) {
886
1195
  const found = [];
887
1196
  const walk = (dir) => {
888
- for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
1197
+ for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
889
1198
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
890
- const full = import_node_path4.default.join(dir, entry.name);
1199
+ const full = import_node_path5.default.join(dir, entry.name);
891
1200
  if (entry.isDirectory()) {
892
- if (full !== root && import_node_fs3.default.existsSync(import_node_path4.default.join(full, "Package.swift"))) continue;
1201
+ if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
893
1202
  walk(full);
894
1203
  } else if (entry.name.endsWith(".swift")) found.push(full);
895
1204
  }
@@ -900,12 +1209,12 @@ function sourceFiles(root) {
900
1209
  function resourceCandidateFiles(root) {
901
1210
  const found = [];
902
1211
  const walk = (dir) => {
903
- for (const entry of import_node_fs3.default.readdirSync(dir, { withFileTypes: true })) {
1212
+ for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
904
1213
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".build")
905
1214
  continue;
906
- const full = import_node_path4.default.join(dir, entry.name);
1215
+ const full = import_node_path5.default.join(dir, entry.name);
907
1216
  if (entry.isDirectory()) {
908
- if (full !== root && import_node_fs3.default.existsSync(import_node_path4.default.join(full, "Package.swift"))) continue;
1217
+ if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
909
1218
  walk(full);
910
1219
  } else {
911
1220
  found.push(full);
@@ -928,19 +1237,20 @@ var RESOURCE_CONTENT_TYPES = {
928
1237
  function buildSwiftResourceTable(project, baseUrl) {
929
1238
  const absolute = resourceCandidateFiles(project.packagePath);
930
1239
  const relative2 = absolute.map(
931
- (file) => import_node_path4.default.relative(project.packagePath, file).split(import_node_path4.default.sep).join(import_node_path4.default.posix.sep)
1240
+ (file) => import_node_path5.default.relative(project.packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep)
932
1241
  );
933
1242
  const byRelative = new Map(absolute.map((file, index) => [relative2[index], file]));
934
1243
  const found = enumerateBundleImages(relative2, (candidate) => {
935
1244
  const file = byRelative.get(candidate);
936
- if (!file || import_node_path4.default.basename(file) !== "Contents.json") return null;
1245
+ if (!file || import_node_path5.default.basename(file) !== "Contents.json") return null;
937
1246
  try {
938
- return import_node_fs3.default.readFileSync(file, "utf8");
1247
+ return import_node_fs4.default.readFileSync(file, "utf8");
939
1248
  } catch {
940
1249
  return null;
941
1250
  }
942
1251
  });
943
1252
  const table = {};
1253
+ const images = {};
944
1254
  const watched = [];
945
1255
  const registered = /* @__PURE__ */ new Map();
946
1256
  for (const { name, file } of found) {
@@ -948,27 +1258,31 @@ function buildSwiftResourceTable(project, baseUrl) {
948
1258
  if (!disk) continue;
949
1259
  let bytes;
950
1260
  try {
951
- bytes = import_node_fs3.default.readFileSync(disk);
1261
+ bytes = import_node_fs4.default.readFileSync(disk);
952
1262
  } catch {
953
1263
  continue;
954
1264
  }
955
- const hash = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 16);
956
- const ext = import_node_path4.default.posix.extname(file).toLowerCase();
1265
+ const hash = (0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex").slice(0, 16);
1266
+ const ext = import_node_path5.default.posix.extname(file).toLowerCase();
957
1267
  table[name] = `${baseUrl}${RESOURCE_PATH}${hash}${ext}`;
1268
+ images[name] = {
1269
+ file: import_node_path5.default.posix.basename(file),
1270
+ data: bytes.toString("base64")
1271
+ };
958
1272
  watched.push(file);
959
1273
  const contents = file.replace(/\/[^/]+$/, "/Contents.json");
960
1274
  if (byRelative.has(contents)) watched.push(contents);
961
1275
  registered.set(`${hash}${ext}`, disk);
962
1276
  }
963
1277
  project.resourceFiles = registered;
964
- return { table, watched };
1278
+ return { table, images, watched };
965
1279
  }
966
1280
  var BUNDLE_MAX_FILES = 128;
967
1281
  var BUNDLE_MAX_FILE_BYTES = 2 * 1024 * 1024;
968
1282
  var BUNDLE_MAX_TOTAL_BYTES = 8 * 1024 * 1024;
969
1283
  function buildSwiftBundleFiles(packagePath) {
970
1284
  const relative2 = resourceCandidateFiles(packagePath).map(
971
- (file) => import_node_path4.default.relative(packagePath, file).split(import_node_path4.default.sep).join(import_node_path4.default.posix.sep)
1285
+ (file) => import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep)
972
1286
  );
973
1287
  const files = {};
974
1288
  const watched = [];
@@ -978,7 +1292,7 @@ function buildSwiftBundleFiles(packagePath) {
978
1292
  watched.push(file);
979
1293
  let bytes;
980
1294
  try {
981
- bytes = import_node_fs3.default.readFileSync(import_node_path4.default.join(packagePath, ...file.split("/")));
1295
+ bytes = import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, ...file.split("/")));
982
1296
  } catch {
983
1297
  continue;
984
1298
  }
@@ -1002,8 +1316,8 @@ function serveSwiftResource(project, key) {
1002
1316
  if (!disk) return null;
1003
1317
  try {
1004
1318
  return {
1005
- bytes: import_node_fs3.default.readFileSync(disk),
1006
- contentType: RESOURCE_CONTENT_TYPES[import_node_path4.default.extname(disk).toLowerCase()] ?? "application/octet-stream"
1319
+ bytes: import_node_fs4.default.readFileSync(disk),
1320
+ contentType: RESOURCE_CONTENT_TYPES[import_node_path5.default.extname(disk).toLowerCase()] ?? "application/octet-stream"
1007
1321
  };
1008
1322
  } catch {
1009
1323
  return null;
@@ -1012,32 +1326,53 @@ function serveSwiftResource(project, key) {
1012
1326
  function linkedModuleRoots(packagePath) {
1013
1327
  let manifest;
1014
1328
  try {
1015
- manifest = import_node_fs3.default.readFileSync(import_node_path4.default.join(packagePath, "Package.swift"), "utf8");
1329
+ manifest = import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, "Package.swift"), "utf8");
1016
1330
  } catch {
1017
1331
  return [];
1018
1332
  }
1019
1333
  const roots = /* @__PURE__ */ new Set();
1020
1334
  const dependency = /\.package\(\s*path:\s*"([^"]+)"\s*\)/g;
1021
1335
  for (const match of manifest.replace(/^\s*\/\/.*$/gm, "").matchAll(dependency)) {
1022
- const root = import_node_path4.default.resolve(packagePath, match[1]);
1023
- if (import_node_fs3.default.existsSync(import_node_path4.default.join(root, "Package.swift"))) roots.add(root);
1336
+ const root = import_node_path5.default.resolve(packagePath, match[1]);
1337
+ if (import_node_fs4.default.existsSync(import_node_path5.default.join(root, "Package.swift"))) roots.add(root);
1024
1338
  }
1025
1339
  return [...roots].sort();
1026
1340
  }
1027
1341
  function sourceStamp(packagePath) {
1028
- const hash = (0, import_node_crypto2.createHash)("sha256");
1029
- for (const root of [packagePath, ...linkedModuleRoots(packagePath)]) {
1030
- hash.update(root);
1031
- for (const file of sourceFiles(root).sort()) {
1032
- hash.update(import_node_path4.default.relative(root, file));
1033
- hash.update(String(import_node_fs3.default.statSync(file).mtimeMs));
1342
+ const hash = (0, import_node_crypto3.createHash)("sha256");
1343
+ hash.update(packagePath);
1344
+ const projectInputs = /* @__PURE__ */ new Set([
1345
+ import_node_path5.default.join(packagePath, "Package.swift"),
1346
+ ...sourceFiles(packagePath),
1347
+ ...buildSwiftBundleFiles(packagePath).watched.map(
1348
+ (file) => import_node_path5.default.join(packagePath, ...file.split(import_node_path5.default.posix.sep))
1349
+ ),
1350
+ ...buildSwiftResourceTable(projectFor(packagePath), "").watched.map(
1351
+ (file) => import_node_path5.default.join(packagePath, ...file.split(import_node_path5.default.posix.sep))
1352
+ )
1353
+ ]);
1354
+ for (const file of [...projectInputs].sort()) {
1355
+ hash.update(import_node_path5.default.relative(packagePath, file));
1356
+ hash.update(String(import_node_fs4.default.statSync(file).mtimeMs));
1357
+ }
1358
+ const moduleRoot = linkedModuleRoots(packagePath).find(
1359
+ (root) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, "toolchain.json"))
1360
+ );
1361
+ if (moduleRoot) {
1362
+ hash.update(moduleRoot);
1363
+ for (const relative2 of [
1364
+ ...swiftModuleInputFiles(moduleRoot),
1365
+ "toolchain.json"
1366
+ ].sort()) {
1367
+ hash.update(relative2);
1368
+ hash.update(String(import_node_fs4.default.statSync(import_node_path5.default.join(moduleRoot, relative2)).mtimeMs));
1034
1369
  }
1035
1370
  }
1036
1371
  return hash.digest("hex").slice(0, 16);
1037
1372
  }
1038
1373
  function entryRootId(packagePath, file) {
1039
- const relative2 = import_node_path4.default.relative(packagePath, file).split(import_node_path4.default.sep).join(import_node_path4.default.posix.sep);
1040
- return `${import_node_path4.default.basename(packagePath)}${import_node_path4.default.posix.sep}${relative2}`;
1374
+ const relative2 = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1375
+ return `${import_node_path5.default.basename(packagePath)}${import_node_path5.default.posix.sep}${relative2}`;
1041
1376
  }
1042
1377
  function serverOrigin() {
1043
1378
  const address = devServer?.httpServer?.address();
@@ -1048,70 +1383,60 @@ function serverOrigin() {
1048
1383
  function artifactUrl(project, artifact) {
1049
1384
  return `${serverOrigin()}${ARTIFACT_PATH}${artifact.hash}.wasm`;
1050
1385
  }
1051
- function builtArtifact(packagePath) {
1052
- const binDir = import_node_path4.default.join(packagePath, ".build", WASM_TRIPLE, "release");
1053
- const artifacts = import_node_fs3.default.existsSync(binDir) ? import_node_fs3.default.readdirSync(binDir).filter((name) => name.endsWith(".wasm")).sort() : [];
1054
- if (artifacts.length !== 1) {
1055
- throw new Error(
1056
- `swift project ${packagePath} built ${artifacts.length} wasm artifacts; exactly one executable is the app`
1057
- );
1386
+ function compileInput(packagePath, entry) {
1387
+ const files = {};
1388
+ for (const file of sourceFiles(packagePath)) {
1389
+ const relative2 = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1390
+ files[relative2] = import_node_fs4.default.readFileSync(file, "utf8");
1058
1391
  }
1059
- return import_node_path4.default.join(binDir, artifacts[0]);
1060
- }
1061
- function streamText(value) {
1062
- if (typeof value === "string") return value.trim();
1063
- if (Buffer.isBuffer(value)) return value.toString("utf8").trim();
1064
- return "";
1392
+ const manifest = import_node_path5.default.join(packagePath, "Package.swift");
1393
+ if (import_node_fs4.default.existsSync(manifest)) files["Package.swift"] = import_node_fs4.default.readFileSync(manifest, "utf8");
1394
+ const resources = buildSwiftBundleFiles(packagePath).files;
1395
+ const images = buildSwiftResourceTable(projectFor(packagePath), "").images;
1396
+ return {
1397
+ files,
1398
+ resources,
1399
+ images,
1400
+ entry
1401
+ };
1065
1402
  }
1066
- async function compile(packagePath, stamp) {
1067
- const swift = import_node_path4.default.join(TOOLCHAIN_BIN, "swift");
1068
- if (!import_node_fs3.default.existsSync(swift)) {
1403
+ async function compile(packagePath, entry, stamp) {
1404
+ const moduleRoot = linkedModuleRoots(packagePath).find(
1405
+ (root) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, "toolchain.json"))
1406
+ );
1407
+ if (!moduleRoot) {
1069
1408
  throw new Error(
1070
- `no swift.org toolchain at ${TOOLCHAIN_BIN}. install it and the ${WASM_SDK} sdk; see packages/sootsim-swift/README.md`
1409
+ `swift project ${packagePath} has no linked rnx SwiftUI package with toolchain.json`
1071
1410
  );
1072
1411
  }
1073
- try {
1074
- await run(
1075
- swift,
1076
- [
1077
- "build",
1078
- "--package-path",
1079
- packagePath,
1080
- "--swift-sdk",
1081
- WASM_SDK,
1082
- "-c",
1083
- "release",
1084
- "-Xswiftc",
1085
- "-Osize"
1086
- ],
1087
- { maxBuffer: 64 * 1024 * 1024 }
1088
- );
1089
- } catch (error) {
1090
- const streams = error && typeof error === "object" ? [
1091
- streamText(Reflect.get(error, "stdout")),
1092
- streamText(Reflect.get(error, "stderr"))
1093
- ].filter(Boolean) : [];
1094
- const detail = streams.length > 0 ? streams.join("\n") : error instanceof Error ? error.message : String(error);
1095
- throw new Error(`swift build failed for ${packagePath}
1096
- ${detail}`);
1412
+ const toolchain = JSON.parse(
1413
+ import_node_fs4.default.readFileSync(import_node_path5.default.join(moduleRoot, "toolchain.json"), "utf8")
1414
+ );
1415
+ const release = toolchain && typeof toolchain === "object" ? Reflect.get(toolchain, "release") : void 0;
1416
+ if (typeof release !== "string") {
1417
+ throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`);
1097
1418
  }
1098
- const bytes = import_node_fs3.default.readFileSync(builtArtifact(packagePath));
1099
- return {
1100
- hash: (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 16),
1101
- bytes,
1102
- stamp
1103
- };
1419
+ const artifact = await compileSwiftProjectUnit(compileInput(packagePath, entry), {
1420
+ buildRoot: process.env.CONTRAST_SWIFT_BUILD_ROOT ?? import_node_path5.default.join(import_node_os2.default.tmpdir(), "contrast-swift-build"),
1421
+ moduleRoot,
1422
+ release,
1423
+ toolchainBin: import_node_path5.default.join(
1424
+ import_node_os2.default.homedir(),
1425
+ `Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`
1426
+ )
1427
+ });
1428
+ return { ...artifact, stamp };
1104
1429
  }
1105
- function artifactFor(project) {
1430
+ function artifactFor(project, entry) {
1106
1431
  const stamp = sourceStamp(project.packagePath);
1107
1432
  if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact);
1108
1433
  if (project.building) {
1109
1434
  const running = project.building;
1110
1435
  if (running.stamp === stamp) return running.promise;
1111
- const again = () => artifactFor(project);
1436
+ const again = () => artifactFor(project, entry);
1112
1437
  return running.promise.then(again, again);
1113
1438
  }
1114
- const promise = compile(project.packagePath, stamp).then(
1439
+ const promise = compile(project.packagePath, entry, stamp).then(
1115
1440
  (artifact) => {
1116
1441
  project.artifact = artifact;
1117
1442
  project.building = null;
@@ -1126,12 +1451,12 @@ function artifactFor(project) {
1126
1451
  return promise;
1127
1452
  }
1128
1453
  function packageFor(file) {
1129
- let dir = import_node_path4.default.dirname(file);
1454
+ let dir = import_node_path5.default.dirname(file);
1130
1455
  const stop = devServer?.config.root;
1131
1456
  for (; ; ) {
1132
- if (import_node_fs3.default.existsSync(import_node_path4.default.join(dir, "Package.swift"))) return dir;
1133
- if (stop !== void 0 && dir === stop || import_node_path4.default.dirname(dir) === dir) break;
1134
- dir = import_node_path4.default.dirname(dir);
1457
+ if (import_node_fs4.default.existsSync(import_node_path5.default.join(dir, "Package.swift"))) return dir;
1458
+ if (stop !== void 0 && dir === stop || import_node_path5.default.dirname(dir) === dir) break;
1459
+ dir = import_node_path5.default.dirname(dir);
1135
1460
  }
1136
1461
  throw new Error(
1137
1462
  `no Package.swift above ${file}${stop === void 0 ? "" : ` (searched up to ${stop})`}; a .swift import needs a swiftpm package like the one in packages/sootsim-swift/example`
@@ -1155,16 +1480,18 @@ function swiftPlugin(options = {}) {
1155
1480
  async load(id) {
1156
1481
  if (!id.startsWith(VIRTUAL_PREFIX)) return null;
1157
1482
  const file = id.slice(VIRTUAL_PREFIX.length);
1158
- if (!import_node_fs3.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1483
+ if (!import_node_fs4.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1159
1484
  const packagePath = options.packagePath ?? packageFor(file);
1160
1485
  const project = projectFor(packagePath);
1486
+ const entry = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1161
1487
  for (const source of sourceFiles(packagePath)) this.addWatchFile(source);
1488
+ this.addWatchFile(import_node_path5.default.join(packagePath, "Package.swift"));
1162
1489
  for (const root of linkedModuleRoots(packagePath)) {
1163
1490
  for (const source of sourceFiles(root)) this.addWatchFile(source);
1164
1491
  }
1165
1492
  let artifact;
1166
1493
  try {
1167
- artifact = await artifactFor(project);
1494
+ artifact = await artifactFor(project, entry);
1168
1495
  } catch (error) {
1169
1496
  if (!project.artifact) throw error;
1170
1497
  this.warn(error instanceof Error ? error.message : String(error));
@@ -1174,7 +1501,9 @@ function swiftPlugin(options = {}) {
1174
1501
  try {
1175
1502
  const built = buildSwiftResourceTable(project, serverOrigin());
1176
1503
  resources = built.table;
1177
- for (const watched of built.watched) this.addWatchFile(watched);
1504
+ for (const watched of built.watched) {
1505
+ this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split(import_node_path5.default.posix.sep)));
1506
+ }
1178
1507
  } catch (error) {
1179
1508
  this.warn(
1180
1509
  `swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
@@ -1185,7 +1514,7 @@ function swiftPlugin(options = {}) {
1185
1514
  const packed = buildSwiftBundleFiles(packagePath);
1186
1515
  bundleFiles = packed.files;
1187
1516
  for (const watched of packed.watched) {
1188
- this.addWatchFile(import_node_path4.default.join(packagePath, ...watched.split(import_node_path4.default.posix.sep)));
1517
+ this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split(import_node_path5.default.posix.sep)));
1189
1518
  }
1190
1519
  if (packed.skipped.length > 0) {
1191
1520
  this.warn(
@@ -1197,11 +1526,13 @@ function swiftPlugin(options = {}) {
1197
1526
  `swift bundle files for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
1198
1527
  );
1199
1528
  }
1200
- return [
1201
- `import { createSwiftEntry } from 'rnxsim/swift'`,
1202
- `export default createSwiftEntry({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)}, rootId: ${JSON.stringify(entryRootId(packagePath, file))}, resources: ${JSON.stringify(resources)}, bundleFiles: ${JSON.stringify(bundleFiles)} })`,
1203
- ""
1204
- ].join("\n");
1529
+ return swiftEntryModuleEsm({
1530
+ url: artifactUrl(project, artifact),
1531
+ hash: artifact.hash,
1532
+ rootId: entryRootId(packagePath, file),
1533
+ resources,
1534
+ bundleFiles
1535
+ });
1205
1536
  },
1206
1537
  configureServer(server) {
1207
1538
  devServer = server;
@@ -1242,8 +1573,8 @@ function swiftPlugin(options = {}) {
1242
1573
  }
1243
1574
 
1244
1575
  // src/rolldown-plugin-swift.ts
1245
- var import_node_fs4 = __toESM(require("node:fs"), 1);
1246
- var import_node_path5 = __toESM(require("node:path"), 1);
1576
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
1577
+ var import_node_path6 = __toESM(require("node:path"), 1);
1247
1578
 
1248
1579
  // src/swift-submit.ts
1249
1580
  var SWIFT_COMPILE_PATH = "/api/swift-compile";
@@ -1317,15 +1648,15 @@ function projectFor2(packagePath) {
1317
1648
  function packageFiles(root) {
1318
1649
  const found = [];
1319
1650
  const walk = (dir) => {
1320
- for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
1651
+ for (const entry of import_node_fs5.default.readdirSync(dir, { withFileTypes: true })) {
1321
1652
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === ".build")
1322
1653
  continue;
1323
- const full = import_node_path5.default.join(dir, entry.name);
1654
+ const full = import_node_path6.default.join(dir, entry.name);
1324
1655
  if (entry.isDirectory()) {
1325
- if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
1656
+ if (full !== root && import_node_fs5.default.existsSync(import_node_path6.default.join(full, "Package.swift"))) continue;
1326
1657
  walk(full);
1327
1658
  } else {
1328
- found.push(import_node_path5.default.relative(root, full).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep));
1659
+ found.push(import_node_path6.default.relative(root, full).split(import_node_path6.default.sep).join(import_node_path6.default.posix.sep));
1329
1660
  }
1330
1661
  }
1331
1662
  };
@@ -1336,8 +1667,8 @@ function sourceFiles2(packagePath) {
1336
1667
  const files = {};
1337
1668
  for (const relative2 of packageFiles(packagePath)) {
1338
1669
  if (!relative2.endsWith(".swift")) continue;
1339
- files[relative2] = import_node_fs4.default.readFileSync(
1340
- import_node_path5.default.join(packagePath, ...relative2.split("/")),
1670
+ files[relative2] = import_node_fs5.default.readFileSync(
1671
+ import_node_path6.default.join(packagePath, ...relative2.split("/")),
1341
1672
  "utf8"
1342
1673
  );
1343
1674
  }
@@ -1345,20 +1676,20 @@ function sourceFiles2(packagePath) {
1345
1676
  }
1346
1677
  function bundlePack(packagePath) {
1347
1678
  const relative2 = packageFiles(packagePath);
1348
- const disk = (file) => import_node_path5.default.join(packagePath, ...file.split("/"));
1679
+ const disk = (file) => import_node_path6.default.join(packagePath, ...file.split("/"));
1349
1680
  const resources = {};
1350
1681
  const watched = [];
1351
1682
  for (const { base, file } of enumerateBundleJson(relative2)) {
1352
- resources[base] = import_node_fs4.default.readFileSync(disk(file)).toString("base64");
1683
+ resources[base] = import_node_fs5.default.readFileSync(disk(file)).toString("base64");
1353
1684
  watched.push(file);
1354
1685
  }
1355
1686
  const images = {};
1356
1687
  const byRelative = new Map(relative2.map((file) => [file, disk(file)]));
1357
1688
  for (const { name, file } of enumerateBundleImages(relative2, (candidate) => {
1358
1689
  const absolute = byRelative.get(candidate);
1359
- if (!absolute || import_node_path5.default.basename(absolute) !== "Contents.json") return null;
1690
+ if (!absolute || import_node_path6.default.basename(absolute) !== "Contents.json") return null;
1360
1691
  try {
1361
- return import_node_fs4.default.readFileSync(absolute, "utf8");
1692
+ return import_node_fs5.default.readFileSync(absolute, "utf8");
1362
1693
  } catch {
1363
1694
  return null;
1364
1695
  }
@@ -1366,19 +1697,19 @@ function bundlePack(packagePath) {
1366
1697
  const absolute = byRelative.get(file);
1367
1698
  if (!absolute) continue;
1368
1699
  images[name] = {
1369
- file: import_node_path5.default.posix.basename(file),
1370
- data: import_node_fs4.default.readFileSync(absolute).toString("base64")
1700
+ file: import_node_path6.default.posix.basename(file),
1701
+ data: import_node_fs5.default.readFileSync(absolute).toString("base64")
1371
1702
  };
1372
1703
  watched.push(file);
1373
1704
  }
1374
1705
  return { resources, images, watched };
1375
1706
  }
1376
1707
  function packageFor2(file, stop) {
1377
- let dir = import_node_path5.default.dirname(file);
1708
+ let dir = import_node_path6.default.dirname(file);
1378
1709
  for (; ; ) {
1379
- if (import_node_fs4.default.existsSync(import_node_path5.default.join(dir, "Package.swift"))) return dir;
1380
- if (stop !== void 0 && dir === stop || import_node_path5.default.dirname(dir) === dir) break;
1381
- dir = import_node_path5.default.dirname(dir);
1710
+ if (import_node_fs5.default.existsSync(import_node_path6.default.join(dir, "Package.swift"))) return dir;
1711
+ if (stop !== void 0 && dir === stop || import_node_path6.default.dirname(dir) === dir) break;
1712
+ dir = import_node_path6.default.dirname(dir);
1382
1713
  }
1383
1714
  throw new Error(
1384
1715
  `no Package.swift above ${file}; a .swift import needs a swiftpm package like the one in packages/sootsim-swift/example`
@@ -1407,17 +1738,17 @@ function swiftRemotePlugin(options) {
1407
1738
  async load(id) {
1408
1739
  if (!id.startsWith(VIRTUAL_PREFIX2)) return null;
1409
1740
  const file = id.slice(VIRTUAL_PREFIX2.length);
1410
- if (!import_node_fs4.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1741
+ if (!import_node_fs5.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1411
1742
  const packagePath = options.packagePath ?? packageFor2(file);
1412
1743
  const project = projectFor2(packagePath);
1413
1744
  const files = sourceFiles2(packagePath);
1414
- const entry = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1745
+ const entry = import_node_path6.default.relative(packagePath, file).split(import_node_path6.default.sep).join(import_node_path6.default.posix.sep);
1415
1746
  for (const source of Object.keys(files)) {
1416
- this.addWatchFile(import_node_path5.default.join(packagePath, ...source.split("/")));
1747
+ this.addWatchFile(import_node_path6.default.join(packagePath, ...source.split("/")));
1417
1748
  }
1418
1749
  const pack = bundlePack(packagePath);
1419
1750
  for (const watched of pack.watched) {
1420
- this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split("/")));
1751
+ this.addWatchFile(import_node_path6.default.join(packagePath, ...watched.split("/")));
1421
1752
  }
1422
1753
  try {
1423
1754
  project.address = await compileSwiftProject(
@@ -1430,11 +1761,13 @@ function swiftRemotePlugin(options) {
1430
1761
  this.warn(error instanceof Error ? error.message : String(error));
1431
1762
  }
1432
1763
  const address = project.address;
1433
- return [
1434
- `import { createSwiftEntry } from 'rnxsim/swift'`,
1435
- `export default createSwiftEntry({ url: ${JSON.stringify(address.url)}, hash: ${JSON.stringify(address.hash)}, rootId: ${JSON.stringify(entryRootId(packagePath, file))}, resources: ${JSON.stringify(address.imageUrls ?? {})}, bundleFiles: ${JSON.stringify(pack.resources)} })`,
1436
- ""
1437
- ].join("\n");
1764
+ return swiftEntryModuleEsm({
1765
+ url: address.url,
1766
+ hash: address.hash,
1767
+ rootId: entryRootId(packagePath, file),
1768
+ resources: address.imageUrls ?? {},
1769
+ bundleFiles: pack.resources
1770
+ });
1438
1771
  }
1439
1772
  };
1440
1773
  }
@@ -1459,7 +1792,7 @@ function rnxPlugin(options = {}) {
1459
1792
  if (options.enabled === false) return;
1460
1793
  const bundleUrl = options.bundleUrl || "/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false";
1461
1794
  const appName = resolveRnxDesktopAppName(options.open, server.config.root);
1462
- const replacementToken = (0, import_node_crypto3.randomUUID)();
1795
+ const replacementToken = (0, import_node_crypto4.randomUUID)();
1463
1796
  const configuredPort = server.config.server.port || 8081;
1464
1797
  const listeningPort = () => {
1465
1798
  const address = server.httpServer?.address();