peachsim 0.1.577 → 0.1.579

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 (178) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -9
  3. package/cli/app-catalog.ts +77 -0
  4. package/cli/app-fonts.ts +4 -20
  5. package/cli/app-splash.ts +2 -18
  6. package/cli/bridge-flow-runner.ts +505 -139
  7. package/cli/cloud-client.ts +1 -4
  8. package/cli/cloud-session.ts +1 -4
  9. package/cli/cloud-test-result-files.ts +3 -0
  10. package/cli/cloud-test-tunnel.ts +2 -5
  11. package/cli/command-registry.ts +2 -0
  12. package/cli/commands/agent.ts +149 -23
  13. package/cli/commands/assert.ts +1 -4
  14. package/cli/commands/box/peach-command.ts +62 -0
  15. package/cli/commands/box/plane-share.ts +0 -70
  16. package/cli/commands/box/rnx-command.ts +1 -60
  17. package/cli/commands/box/search-command.ts +14 -0
  18. package/cli/commands/box.ts +2 -2
  19. package/cli/commands/control.ts +121 -27
  20. package/cli/commands/cpu-profile.ts +23 -10
  21. package/cli/commands/daemon.ts +1 -9
  22. package/cli/commands/detox.ts +26 -18
  23. package/cli/commands/device.ts +129 -1
  24. package/cli/commands/film.ts +25 -16
  25. package/cli/commands/flow.ts +9 -2
  26. package/cli/commands/inspect/actions.ts +75 -1
  27. package/cli/commands/inspect/core.ts +81 -2
  28. package/cli/commands/inspect/describe.ts +2 -6
  29. package/cli/commands/inspect/env.ts +1 -1
  30. package/cli/commands/inspect/settling.ts +20 -3
  31. package/cli/commands/inspect/shared.ts +1 -4
  32. package/cli/commands/inspect/sleep.ts +1 -1
  33. package/cli/commands/inspect.ts +16 -49
  34. package/cli/commands/install-desktop.ts +8 -6
  35. package/cli/commands/login.ts +3 -5
  36. package/cli/commands/maestro-generate.ts +1 -4
  37. package/cli/commands/maestro.ts +38 -21
  38. package/cli/commands/no-bridge-hint.ts +0 -14
  39. package/cli/commands/permissions.ts +2 -2
  40. package/cli/commands/platform.ts +13 -6
  41. package/cli/commands/screenshot.ts +6 -1
  42. package/cli/commands/skills.ts +1 -1
  43. package/cli/commands/test.ts +6 -2
  44. package/cli/commands/three-mode.ts +162 -19
  45. package/cli/commands/upload.ts +3 -18
  46. package/cli/current-sim.ts +0 -1
  47. package/cli/desktop-companion.ts +40 -8
  48. package/cli/drivers/playwright-provisioning.ts +1 -4
  49. package/cli/flow-export.ts +1 -8
  50. package/cli/flow-session.ts +0 -4
  51. package/cli/hints.ts +1 -16
  52. package/cli/inspect-notice-state.ts +0 -4
  53. package/cli/main.ts +2 -4
  54. package/cli/outbound-endpoints.ts +1 -24
  55. package/cli/parse-args.ts +1 -2
  56. package/cli/rnx-in-process.ts +3 -1
  57. package/cli/run-rnx-core.ts +4 -2
  58. package/cli/run-rnx.ts +18 -10
  59. package/cli/send-to-box.ts +1 -4
  60. package/cli/telemetry.ts +1 -1
  61. package/cli/ws-bridge.ts +1 -5
  62. package/detox/gestures.ts +20 -6
  63. package/detox/index.ts +379 -123
  64. package/detox/navigation-lifecycle.ts +22 -13
  65. package/dist-lib/agent-daemon-client.cjs +21 -15
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +3 -17
  68. package/dist-lib/agent-sessions.cjs +5 -4
  69. package/dist-lib/attached-projects.cjs +1 -77
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +11 -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 +7 -5
  76. package/dist-lib/bridge-contract-input.mjs +7 -5
  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 +30 -7
  80. package/dist-lib/capture-contract.mjs +30 -7
  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 +1 -1
  85. package/dist-lib/cloud-test-args.mjs +1 -1
  86. package/dist-lib/cloud.cjs +17 -11
  87. package/dist-lib/cloud.mjs +17 -11
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +321 -122
  90. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  91. package/dist-lib/home-paths.cjs +1 -1
  92. package/dist-lib/host/bridge-host.cjs +427 -212
  93. package/dist-lib/host/fetch-proxy-handler.cjs +5 -28
  94. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  95. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  96. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  97. package/dist-lib/host/websocket-proxy.cjs +1 -1
  98. package/dist-lib/index.cjs +315 -58
  99. package/dist-lib/jump-to-source-babel.cjs +1 -1
  100. package/dist-lib/jump-to-source-native.cjs +1 -1
  101. package/dist-lib/menu.cjs +67 -31
  102. package/dist-lib/menu.mjs +64 -28
  103. package/dist-lib/metro-fingerprint-registry.cjs +8 -4
  104. package/dist-lib/metro-fingerprint-registry.mjs +6 -4
  105. package/dist-lib/metro-production-bundle.cjs +2 -2
  106. package/dist-lib/metro-production-bundle.mjs +2 -2
  107. package/dist-lib/metro.cjs +8 -4
  108. package/dist-lib/profiles.cjs +1 -1
  109. package/dist-lib/public-brand.cjs +7 -4
  110. package/dist-lib/react-native-host-modules.cjs +1 -1
  111. package/dist-lib/react-native-host-modules.mjs +1 -1
  112. package/dist-lib/render-mode.cjs +3 -13
  113. package/dist-lib/scripts/dev-server-scanner.cjs +5 -17
  114. package/dist-lib/sdk.cjs +129 -10
  115. package/dist-lib/sdk.mjs +127 -10
  116. package/dist-lib/skills.cjs +1635 -1121
  117. package/dist-lib/swift-compile.cjs +277 -70
  118. package/dist-lib/swift-compile.mjs +275 -70
  119. package/dist-lib/swift-package.cjs +17 -5
  120. package/dist-lib/swift-package.mjs +15 -4
  121. package/dist-lib/swift-submit.cjs +32 -8
  122. package/dist-lib/swift-submit.mjs +29 -7
  123. package/dist-lib/swift.cjs +30912 -577
  124. package/dist-lib/vite-kotlin.cjs +356 -0
  125. package/dist-lib/vite-swift.cjs +1743 -0
  126. package/dist-lib/vite.cjs +208 -1039
  127. package/kotlin/README.md +18 -0
  128. package/kotlin/Sources/androidx/annotation/Keep.kt +4 -0
  129. package/kotlin/Sources/dev/onejs/one/OneNativeSourceDispatch.kt +7 -0
  130. package/kotlin/Sources/dev/onejs/one/source/OneModule.kt +4 -0
  131. package/kotlin/Sources/org/json/JSON.kt +244 -0
  132. package/kotlin/toolchain.json +19 -0
  133. package/package.json +14 -2
  134. package/scripts/dev-server-scanner.ts +0 -18
  135. package/scripts/optional-demo-registry.ts +0 -17
  136. package/skills/contrast/SKILL.md +2 -2
  137. package/skills/peach-setup/SKILL.md +16 -0
  138. package/src/agent-daemon-client.ts +36 -22
  139. package/src/agent-identity.ts +0 -15
  140. package/src/agent-prompt.ts +12 -0
  141. package/src/attached-projects.ts +0 -72
  142. package/src/backend-origin.ts +10 -0
  143. package/src/bridge-contract-input.ts +2 -5
  144. package/src/bridge-contract.ts +2 -0
  145. package/src/capture-contract.ts +53 -11
  146. package/src/cloud.ts +4 -9
  147. package/src/dev-server-open.ts +0 -99
  148. package/src/home-paths.ts +2 -3
  149. package/src/host/agent-host.ts +82 -36
  150. package/src/host/bridge-host.ts +5 -11
  151. package/src/host/fetch-proxy-handler.ts +19 -43
  152. package/src/is-record.ts +4 -0
  153. package/src/menu.ts +82 -37
  154. package/src/metro-fingerprint-registry.ts +1 -4
  155. package/src/metro-production-bundle.ts +1 -1
  156. package/src/plist.ts +1 -4
  157. package/src/public-brand.ts +5 -3
  158. package/src/render-mode.ts +0 -12
  159. package/src/runtime-assets.ts +9 -2
  160. package/src/screenshots/compose.ts +7 -1
  161. package/src/screenshots/frame-compose.ts +21 -10
  162. package/src/screenshots/schema.ts +1 -4
  163. package/src/sim-client.ts +2 -4
  164. package/src/skills/builtin/maestro-test.ts +1 -4
  165. package/src/sleep.ts +4 -0
  166. package/src/swift-compile.ts +209 -47
  167. package/src/swift-icu.ts +140 -0
  168. package/src/swift-package.ts +26 -2
  169. package/src/swift-submit.ts +84 -4
  170. package/src/swift.ts +2 -0
  171. package/src/vite-plugin-kotlin.ts +376 -0
  172. package/src/vite-plugin-one.ts +224 -36
  173. package/src/vite-plugin-swift.ts +382 -96
  174. package/src/vite-plugin.ts +121 -134
  175. package/src/vite.d.ts +10 -0
  176. package/cli/resolve-assets.ts +0 -63
  177. package/src/rolldown-plugin-swift.ts +0 -244
  178. package/src/team-machine-prompt.ts +0 -73
@@ -0,0 +1,1743 @@
1
+ /*! rnx v0.1.579 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
+ "use strict";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // src/vite-plugin-swift.ts
33
+ var vite_plugin_swift_exports = {};
34
+ __export(vite_plugin_swift_exports, {
35
+ buildSwiftBundleFiles: () => buildSwiftBundleFiles,
36
+ buildSwiftResourceTable: () => buildSwiftResourceTable,
37
+ compileInput: () => compileInput,
38
+ entryRootId: () => entryRootId,
39
+ linkedModuleRoots: () => linkedModuleRoots,
40
+ serveSwiftPackageAsset: () => serveSwiftPackageAsset,
41
+ serveSwiftResource: () => serveSwiftResource,
42
+ sourceStamp: () => sourceStamp,
43
+ swiftPackageMetadata: () => swiftPackageMetadata,
44
+ swiftPackageWatchFiles: () => swiftPackageWatchFiles,
45
+ swiftPlugin: () => swiftPlugin
46
+ });
47
+ module.exports = __toCommonJS(vite_plugin_swift_exports);
48
+ var import_node_crypto3 = require("node:crypto");
49
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
50
+ var import_node_os3 = __toESM(require("node:os"), 1);
51
+ var import_node_path5 = __toESM(require("node:path"), 1);
52
+
53
+ // ../sootsim-swift/src/bundleAssets.ts
54
+ var import_svgpath = __toESM(require("svgpath"), 1);
55
+ var RASTER_EXTENSIONS = /* @__PURE__ */ new Set([
56
+ ".avif",
57
+ ".bmp",
58
+ ".gif",
59
+ ".ico",
60
+ ".jpeg",
61
+ ".jpg",
62
+ ".png",
63
+ ".webp"
64
+ ]);
65
+ function extOf(file) {
66
+ const dot = file.lastIndexOf(".");
67
+ return dot < 0 ? "" : file.slice(dot).toLowerCase();
68
+ }
69
+ function baseOf(file) {
70
+ const slash = file.lastIndexOf("/");
71
+ return slash < 0 ? file : file.slice(slash + 1);
72
+ }
73
+ function pickImagesetFile(images) {
74
+ const rasters = images.filter(
75
+ (image) => typeof image.filename === "string" && RASTER_EXTENSIONS.has(extOf(image.filename))
76
+ );
77
+ if (rasters.length === 0) return null;
78
+ const rank = (scale) => scale === "2x" ? 0 : scale === "3x" ? 1 : scale === "1x" ? 2 : 3;
79
+ const pick = [...rasters].sort((a, b) => rank(a.scale) - rank(b.scale))[0];
80
+ return {
81
+ filename: pick.filename,
82
+ scale: pick.scale === "2x" ? 2 : pick.scale === "3x" ? 3 : 1
83
+ };
84
+ }
85
+ function flatScale(path5) {
86
+ const match = /@([23])x\.[^./]+$/.exec(path5);
87
+ return match ? Number(match[1]) : 1;
88
+ }
89
+ function providesNamespace(text) {
90
+ if (text === null) return false;
91
+ try {
92
+ const parsed = JSON.parse(text);
93
+ return parsed?.properties?.["provides-namespace"] === true;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+ function catalogEntryName(contentsPath, kind, readText) {
99
+ const parts = contentsPath.split("/");
100
+ const leaf = parts[parts.length - 2] ?? "";
101
+ if (!leaf.endsWith(kind)) return null;
102
+ const short = leaf.slice(0, -kind.length) || null;
103
+ if (short === null) return null;
104
+ const segments = [short];
105
+ for (let depth = parts.length - 3; depth >= 0; depth -= 1) {
106
+ const dir = parts[depth] ?? "";
107
+ if (dir.endsWith(".xcassets") || dir === "" || dir === "." || dir === "..") break;
108
+ const folderContents = [...parts.slice(0, depth + 1), "Contents.json"].join("/");
109
+ if (providesNamespace(readText(folderContents))) segments.unshift(dir);
110
+ }
111
+ return segments.join("/");
112
+ }
113
+ function isFlatResourceImage(path5) {
114
+ return path5.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path5));
115
+ }
116
+ function enumerateBundleImages(paths, readText) {
117
+ const table = [];
118
+ const seen = /* @__PURE__ */ new Set();
119
+ const catalogs = paths.filter((path5) => baseOf(path5) === "Contents.json");
120
+ for (const contentsPath of catalogs) {
121
+ const name = catalogEntryName(contentsPath, ".imageset", readText);
122
+ if (name === null || seen.has(name)) continue;
123
+ let parsed;
124
+ try {
125
+ const text = readText(contentsPath);
126
+ if (text === null) continue;
127
+ parsed = JSON.parse(text);
128
+ } catch {
129
+ continue;
130
+ }
131
+ const images = parsed?.images;
132
+ if (!Array.isArray(images)) continue;
133
+ const dir = contentsPath.split("/").slice(0, -1).join("/");
134
+ const pick = pickImagesetFile(images);
135
+ if (pick === null || pick.filename.includes("/") || pick.filename.includes("\\"))
136
+ continue;
137
+ seen.add(name);
138
+ table.push({ name, file: `${dir}/${pick.filename}`, scale: pick.scale });
139
+ }
140
+ for (const path5 of paths) {
141
+ if (!isFlatResourceImage(path5)) continue;
142
+ const name = baseOf(path5).slice(0, -extOf(path5).length);
143
+ if (name === "" || seen.has(name)) continue;
144
+ seen.add(name);
145
+ table.push({ name, file: path5, scale: flatScale(path5) });
146
+ }
147
+ return table;
148
+ }
149
+ function colorComponent(value) {
150
+ if (typeof value !== "string") return null;
151
+ const parsed = value.startsWith("0x") ? Number.parseInt(value.slice(2), 16) / 255 : value.includes(".") ? Number.parseFloat(value) : Number.parseInt(value, 10) / 255;
152
+ return Number.isFinite(parsed) ? parsed : null;
153
+ }
154
+ function colorsetValue(color) {
155
+ if (typeof color !== "object" || color === null) return null;
156
+ const reference = Reflect.get(color, "reference");
157
+ if (typeof reference === "string") {
158
+ return { system: reference.replace(/Color$/, "") };
159
+ }
160
+ const space = Reflect.get(color, "color-space");
161
+ if (space !== "srgb" && space !== "extended-srgb") return null;
162
+ const components = Reflect.get(color, "components");
163
+ if (typeof components !== "object" || components === null) return null;
164
+ const [red, green, blue, alpha] = ["red", "green", "blue", "alpha"].map(
165
+ (key) => colorComponent(Reflect.get(components, key))
166
+ );
167
+ if (red === null || green === null || blue === null || alpha === null) return null;
168
+ if (![red, green, blue].every((value) => value >= 0 && value <= 1)) return null;
169
+ const byte = (value) => Math.round(value * 255);
170
+ return `rgba(${byte(red)}, ${byte(green)}, ${byte(blue)}, ${alpha})`;
171
+ }
172
+ function enumerateBundleColors(paths, readText) {
173
+ const table = {};
174
+ for (const contentsPath of paths.filter((path5) => baseOf(path5) === "Contents.json")) {
175
+ const name = catalogEntryName(contentsPath, ".colorset", readText);
176
+ if (name === null || name in table) continue;
177
+ let colors;
178
+ try {
179
+ const text = readText(contentsPath);
180
+ if (text === null) continue;
181
+ colors = Reflect.get(JSON.parse(text), "colors");
182
+ } catch {
183
+ continue;
184
+ }
185
+ if (!Array.isArray(colors)) continue;
186
+ let light = null;
187
+ let dark = null;
188
+ for (const entry of colors) {
189
+ if (typeof entry !== "object" || entry === null) continue;
190
+ const idiom = Reflect.get(entry, "idiom");
191
+ if (idiom !== "universal" && idiom !== "iphone") continue;
192
+ const appearances = Reflect.get(entry, "appearances");
193
+ const list = Array.isArray(appearances) ? appearances : [];
194
+ if (list.some((item) => Reflect.get(item, "appearance") === "contrast")) continue;
195
+ const isDark = list.some(
196
+ (item) => Reflect.get(item, "appearance") === "luminosity" && Reflect.get(item, "value") === "dark"
197
+ );
198
+ if (list.length > 0 && !isDark) continue;
199
+ const value = colorsetValue(Reflect.get(entry, "color"));
200
+ if (value === null) continue;
201
+ if (isDark) dark = value;
202
+ else light = value;
203
+ }
204
+ if (light === null) continue;
205
+ if (typeof light === "object") table[name] = light;
206
+ else table[name] = { light, dark: typeof dark === "string" ? dark : light };
207
+ }
208
+ return table;
209
+ }
210
+ var SYMBOL_CAP_HEIGHT_EM = 1443 / 2048;
211
+ function axisExtremes(points) {
212
+ const [a = 0, b = 0, c = 0, d] = points;
213
+ const out = [a, d ?? c];
214
+ const within = (t) => t > 0 && t < 1;
215
+ if (d === void 0) {
216
+ const denom = a - 2 * b + c;
217
+ const t = denom === 0 ? -1 : (a - b) / denom;
218
+ if (within(t)) out.push((1 - t) ** 2 * a + 2 * (1 - t) * t * b + t ** 2 * c);
219
+ return out;
220
+ }
221
+ const qa = 3 * (-a + 3 * b - 3 * c + d);
222
+ const qb = 6 * (a - 2 * b + c);
223
+ const qc = 3 * (b - a);
224
+ const roots = Math.abs(qa) < 1e-12 ? qb === 0 ? [] : [-qc / qb] : (() => {
225
+ const disc = qb * qb - 4 * qa * qc;
226
+ if (disc < 0) return [];
227
+ const root = Math.sqrt(disc);
228
+ return [(-qb + root) / (2 * qa), (-qb - root) / (2 * qa)];
229
+ })();
230
+ for (const t of roots.filter(within))
231
+ out.push(
232
+ (1 - t) ** 3 * a + 3 * (1 - t) ** 2 * t * b + 3 * (1 - t) * t ** 2 * c + t ** 3 * d
233
+ );
234
+ return out;
235
+ }
236
+ function pathBounds(d) {
237
+ const xs = [];
238
+ const ys = [];
239
+ for (const path5 of d)
240
+ (0, import_svgpath.default)(path5).abs().unshort().unarc().iterate((segment, _index, x, y) => {
241
+ const [command, ...args] = segment;
242
+ if (command === "M" || command === "L") {
243
+ xs.push(Number(args[0]));
244
+ ys.push(Number(args[1]));
245
+ } else if (command === "H") xs.push(Number(args[0]));
246
+ else if (command === "V") ys.push(Number(args[0]));
247
+ else if (command === "Q" || command === "C") {
248
+ const n = args.map(Number);
249
+ xs.push(...axisExtremes([x, ...n.filter((_, i) => i % 2 === 0)]));
250
+ ys.push(...axisExtremes([y, ...n.filter((_, i) => i % 2 === 1)]));
251
+ }
252
+ });
253
+ return {
254
+ minX: Math.min(...xs),
255
+ maxX: Math.max(...xs),
256
+ minY: Math.min(...ys),
257
+ maxY: Math.max(...ys)
258
+ };
259
+ }
260
+ function guide(svg, id, axis) {
261
+ const line = new RegExp(`<line[^>]*id="${id}"[^>]*>`).exec(svg)?.[0];
262
+ const value = line === void 0 ? void 0 : new RegExp(`${axis}="([-0-9.]+)"`).exec(line)?.[1];
263
+ return value === void 0 ? null : Number(value);
264
+ }
265
+ function enumerateBundleSymbols(paths, readText) {
266
+ const table = {};
267
+ for (const contentsPath of paths.filter((path5) => baseOf(path5) === "Contents.json")) {
268
+ const name = catalogEntryName(contentsPath, ".symbolset", readText);
269
+ if (name === null || name in table) continue;
270
+ let symbols;
271
+ try {
272
+ symbols = Reflect.get(JSON.parse(readText(contentsPath) ?? "null") ?? {}, "symbols");
273
+ } catch {
274
+ continue;
275
+ }
276
+ const file = Array.isArray(symbols) ? symbols.map((entry) => Reflect.get(entry ?? {}, "filename")).find((f) => typeof f === "string") : void 0;
277
+ if (typeof file !== "string") continue;
278
+ if (file.includes("/") || file.includes("\\")) continue;
279
+ const svg = readText(`${contentsPath.split("/").slice(0, -1).join("/")}/${file}`);
280
+ if (svg === null) continue;
281
+ const group = /<g id="Regular-M"[^>]*>([\s\S]*?)<\/g>/.exec(svg)?.[1];
282
+ const baseline = guide(svg, "Baseline-M", "y1");
283
+ const capline = guide(svg, "Capline-M", "y1");
284
+ const left = guide(svg, "left-margin-Regular-M", "x1") ?? guide(svg, "left-margin", "x1");
285
+ const right = guide(svg, "right-margin-Regular-M", "x1") ?? guide(svg, "right-margin", "x1");
286
+ if (group === void 0 || baseline === null || capline === null || left === null || right === null)
287
+ continue;
288
+ const d = [...group.matchAll(/ d="([^"]+)"/g)].map((match) => match[1] ?? "");
289
+ if (d.length === 0 || baseline <= capline) continue;
290
+ const scale = 24 * SYMBOL_CAP_HEIGHT_EM / (baseline - capline);
291
+ const box = pathBounds(d);
292
+ const translateX = 12 - (box.minX + box.maxX) * scale / 2;
293
+ const translateY = 12 - (box.minY + box.maxY) * scale / 2;
294
+ const advance = Math.round((right - left) * scale * 1e3) / 1e3;
295
+ const tall = (box.maxY - box.minY) * scale + 0.175 * 24;
296
+ table[name] = {
297
+ viewBox: "0 0 24 24",
298
+ advance,
299
+ box: [12 - advance / 2, 12 - tall / 2, advance, tall],
300
+ layers: d.map((path5) => ({
301
+ d: (0, import_svgpath.default)(path5).abs().scale(scale).translate(translateX, translateY).round(3).toString()
302
+ }))
303
+ };
304
+ }
305
+ return table;
306
+ }
307
+ function enumerateBundleJson(paths) {
308
+ const found = [];
309
+ const seen = /* @__PURE__ */ new Set();
310
+ const ordered = [...paths].filter(
311
+ (path5) => path5.split("/").includes("Resources") && path5.split("/").every((segment) => segment !== "" && segment !== "..") && (extOf(path5) === ".json" || extOf(path5) === ".xcstrings")
312
+ ).sort((a, b) => {
313
+ const aDepth = a.split("/").length;
314
+ const bDepth = b.split("/").length;
315
+ return aDepth - bDepth || (a < b ? -1 : a > b ? 1 : 0);
316
+ });
317
+ for (const file of ordered) {
318
+ const base = baseOf(file);
319
+ if (seen.has(base)) continue;
320
+ seen.add(base);
321
+ found.push({ base, file });
322
+ }
323
+ return found;
324
+ }
325
+
326
+ // ../sootsim-swift/src/entryModule.ts
327
+ function nativeExportLines(args, commonjs) {
328
+ const native = args.native;
329
+ if (!native) return [];
330
+ return Object.entries(native.modules).map(([name, methods]) => {
331
+ const calls = methods.map(
332
+ (method) => `${method}: (...args) => callSwiftModule(options, ${JSON.stringify(`${native.packageId}.${name}`)}, ${JSON.stringify(method)}, args, ${JSON.stringify(native.contractHash)})`
333
+ ).join(", ");
334
+ return commonjs ? `module.exports.${name} = Object.freeze({ ${calls} })` : `export const ${name} = Object.freeze({ ${calls} })`;
335
+ });
336
+ }
337
+ function swiftEntryModuleEsm(args) {
338
+ return [
339
+ `import { createSwiftEntry, callSwiftModule } from 'peachsim/swift'`,
340
+ `const options = ${JSON.stringify({ url: args.url, hash: args.hash, icuUrl: args.icuUrl, rootId: args.rootId, resources: args.resources, bundleFiles: args.bundleFiles, colors: args.colors, symbols: args.symbols })}`,
341
+ ...!args.native || args.native.defaultView ? ["export default createSwiftEntry(options)"] : [],
342
+ ...nativeExportLines(args, false),
343
+ ""
344
+ ].join("\n");
345
+ }
346
+
347
+ // src/auth/shared-session.ts
348
+ var import_node_fs = require("node:fs");
349
+ var import_node_path2 = require("node:path");
350
+
351
+ // src/home-paths.ts
352
+ var import_node_os = require("node:os");
353
+ var import_node_path = __toESM(require("node:path"), 1);
354
+ var PEACH_HOME_ENV = "PEACH_HOME";
355
+ function rnxHomeDir() {
356
+ const override = process.env[PEACH_HOME_ENV];
357
+ if (override && override.length > 0) return import_node_path.default.resolve(override);
358
+ return import_node_path.default.join((0, import_node_os.homedir)(), ".peach");
359
+ }
360
+ function electronDir() {
361
+ return import_node_path.default.join(rnxHomeDir(), "electron");
362
+ }
363
+ function electronUserDataDir() {
364
+ return import_node_path.default.join(electronDir(), "userData");
365
+ }
366
+ var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
367
+
368
+ // src/auth/shared-session.ts
369
+ function sharedDesktopAuthAccounts(user) {
370
+ return [
371
+ ...user?.personalAccountId ? [{ id: user.personalAccountId, name: "Personal" }] : [],
372
+ ...(user?.teams ?? []).map((team) => ({ id: team.id, name: team.name }))
373
+ ];
374
+ }
375
+ var SESSION_VERSION = 1;
376
+ var SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
377
+ var DEFAULT_ORIGIN = "https://contrast.dev";
378
+ function getBaseDir() {
379
+ const explicit = process.env[SESSION_FILE_ENV];
380
+ if (explicit?.trim()) {
381
+ return (0, import_node_path2.dirname)((0, import_node_path2.resolve)(explicit));
382
+ }
383
+ return electronUserDataDir();
384
+ }
385
+ function getSharedDesktopAuthFilePath() {
386
+ const explicit = process.env[SESSION_FILE_ENV];
387
+ if (explicit?.trim()) return (0, import_node_path2.resolve)(explicit);
388
+ return (0, import_node_path2.join)(getBaseDir(), "desktop-auth.json");
389
+ }
390
+ function normalizeUser(input) {
391
+ if (!input || typeof input !== "object") return null;
392
+ const value = input;
393
+ if (typeof value.id !== "string" || !value.id.trim()) return null;
394
+ return {
395
+ id: value.id.trim(),
396
+ name: typeof value.name === "string" ? value.name : void 0,
397
+ email: typeof value.email === "string" ? value.email : void 0,
398
+ image: typeof value.image === "string" ? value.image : void 0,
399
+ githubUsername: typeof value.githubUsername === "string" ? value.githubUsername : void 0,
400
+ personalAccountId: typeof value.personalAccountId === "string" && value.personalAccountId.trim() ? value.personalAccountId.trim() : void 0,
401
+ isAdmin: value.isAdmin === true,
402
+ teams: Array.isArray(value.teams) ? value.teams.map((team) => {
403
+ if (!team || typeof team !== "object") return null;
404
+ const t = team;
405
+ if (typeof t.id !== "string" || typeof t.name !== "string") return null;
406
+ return {
407
+ id: t.id,
408
+ name: t.name,
409
+ role: t.role === "owner" ? "owner" : "member",
410
+ githubOrg: typeof t.githubOrg === "string" ? t.githubOrg : null
411
+ };
412
+ }).filter((team) => !!team) : void 0
413
+ };
414
+ }
415
+ function normalizeSession(input) {
416
+ if (!input || typeof input !== "object") return null;
417
+ const value = input;
418
+ if (value.version !== SESSION_VERSION) return null;
419
+ if (typeof value.token !== "string" || !value.token.trim()) return null;
420
+ const origin = typeof value.origin === "string" && value.origin.trim() ? value.origin.trim() : DEFAULT_ORIGIN;
421
+ const source = value.source === "cli" || value.source === "electron" || value.source === "browser" || value.source === "unknown" ? value.source : "unknown";
422
+ const updatedAt = typeof value.updatedAt === "string" && value.updatedAt ? value.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
423
+ const validatedAt = typeof value.validatedAt === "string" && value.validatedAt ? value.validatedAt : void 0;
424
+ const accountId = typeof value.accountId === "string" && value.accountId.trim() ? value.accountId.trim() : void 0;
425
+ return {
426
+ version: SESSION_VERSION,
427
+ token: value.token.trim(),
428
+ user: normalizeUser(value.user),
429
+ origin,
430
+ source,
431
+ updatedAt,
432
+ validatedAt,
433
+ accountId
434
+ };
435
+ }
436
+ function readSharedDesktopAuthSession() {
437
+ const filepath = getSharedDesktopAuthFilePath();
438
+ if (!(0, import_node_fs.existsSync)(filepath)) return null;
439
+ try {
440
+ const parsed = JSON.parse((0, import_node_fs.readFileSync)(filepath, "utf8"));
441
+ const normalized = normalizeSession(parsed);
442
+ if (!normalized) {
443
+ (0, import_node_fs.rmSync)(filepath, { force: true });
444
+ return null;
445
+ }
446
+ return normalized;
447
+ } catch {
448
+ (0, import_node_fs.rmSync)(filepath, { force: true });
449
+ return null;
450
+ }
451
+ }
452
+
453
+ // cli/run-rnx.ts
454
+ var import_node_async_hooks = require("node:async_hooks");
455
+ var sinks = new import_node_async_hooks.AsyncLocalStorage();
456
+
457
+ // cli/auth.ts
458
+ var GITHUB_TOKEN_PREFIXES = ["ghs_", "ghp_", "gho_", "ghu_", "github_pat_"];
459
+ function looksLikeGitHubToken(token) {
460
+ return Boolean(
461
+ token && token.length >= 20 && GITHUB_TOKEN_PREFIXES.some((prefix) => token.startsWith(prefix))
462
+ );
463
+ }
464
+ function resolveGitHubActionsAuth() {
465
+ const contrastRunnerToken = process.env.CONTRAST_INSTALLATION_TOKEN?.trim();
466
+ const githubActionsToken = process.env.GITHUB_TOKEN?.trim();
467
+ const token = contrastRunnerToken || githubActionsToken;
468
+ if (!looksLikeGitHubToken(token)) return null;
469
+ const repoSlug = (process.env.CONTRAST_REPO || process.env.GITHUB_REPOSITORY || "").trim();
470
+ if (!repoSlug) return null;
471
+ const [owner, repo] = repoSlug.includes("/") ? repoSlug.split("/", 2) : [null, null];
472
+ const repositoryId = (process.env.CONTRAST_REPO_ID || process.env.GITHUB_REPOSITORY_ID || "").trim();
473
+ const installationId = (process.env.CONTRAST_INSTALLATION_ID || process.env.GITHUB_APP_INSTALLATION_ID || "").trim();
474
+ return {
475
+ kind: "github",
476
+ token,
477
+ repoId: repoSlug,
478
+ repositoryId: repositoryId || null,
479
+ owner: owner || null,
480
+ repo: repo || null,
481
+ installationId: installationId || null,
482
+ source: contrastRunnerToken ? "contrast-runner" : "github-actions"
483
+ };
484
+ }
485
+ function isCliApiKeySecret(secret) {
486
+ return secret.startsWith("sk_rnx_");
487
+ }
488
+ function readApiKeyFromEnv() {
489
+ const envName = "PEACH_API_KEY";
490
+ const secret = process.env[envName]?.trim();
491
+ return secret && isCliApiKeySecret(secret) ? { secret, envName } : null;
492
+ }
493
+ function resolveCliAuth() {
494
+ const envKey = readApiKeyFromEnv();
495
+ if (envKey) {
496
+ return {
497
+ kind: "api-key",
498
+ secret: envKey.secret,
499
+ source: "env",
500
+ envName: envKey.envName
501
+ };
502
+ }
503
+ const githubAuth = resolveGitHubActionsAuth();
504
+ if (githubAuth) return githubAuth;
505
+ const session = readSharedDesktopAuthSession();
506
+ if (session?.token) {
507
+ const accounts = sharedDesktopAuthAccounts(session.user);
508
+ return {
509
+ kind: "session",
510
+ token: session.token,
511
+ origin: session.origin,
512
+ accountId: session.accountId ?? (accounts.length === 1 ? accounts[0]?.id ?? null : null)
513
+ };
514
+ }
515
+ return null;
516
+ }
517
+ function authHeaderValue(auth) {
518
+ return auth.kind === "api-key" ? `Bearer ${auth.secret}` : `Bearer ${auth.token}`;
519
+ }
520
+
521
+ // src/backend-origin.ts
522
+ var DEFAULT_CONTRAST_BACKEND_ORIGIN = "https://contrast.dev";
523
+ function normalizeHostname(hostname) {
524
+ return hostname.replace(/^\[|\]$/g, "").toLowerCase();
525
+ }
526
+ function isLoopbackHost(hostname) {
527
+ const normalized = normalizeHostname(hostname);
528
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
529
+ }
530
+ function isContrastBackendHost(hostname) {
531
+ const normalized = normalizeHostname(hostname);
532
+ return isLoopbackHost(normalized) || normalized === "contrast.dev" || normalized.endsWith(".contrast.dev");
533
+ }
534
+ function resolveContrastBackendOrigin(currentOrigin) {
535
+ const origin = currentOrigin?.trim();
536
+ if (!origin) return DEFAULT_CONTRAST_BACKEND_ORIGIN;
537
+ try {
538
+ const parsed = new URL(origin);
539
+ if (isLoopbackHost(parsed.hostname) || isContrastBackendHost(parsed.hostname)) {
540
+ return parsed.origin;
541
+ }
542
+ } catch {
543
+ }
544
+ return DEFAULT_CONTRAST_BACKEND_ORIGIN;
545
+ }
546
+
547
+ // src/swift-compile.ts
548
+ var import_node_child_process2 = require("node:child_process");
549
+ var import_node_crypto2 = require("node:crypto");
550
+ var import_node_fs3 = require("node:fs");
551
+ var import_node_os2 = __toESM(require("node:os"), 1);
552
+ var import_node_path4 = __toESM(require("node:path"), 1);
553
+ var import_node_util2 = require("node:util");
554
+
555
+ // src/swift-icu.ts
556
+ var import_node_child_process = require("node:child_process");
557
+ var import_node_crypto = require("node:crypto");
558
+ var import_node_fs2 = require("node:fs");
559
+ var import_node_path3 = __toESM(require("node:path"), 1);
560
+ var import_node_util = require("node:util");
561
+ var import_node_zlib = require("node:zlib");
562
+ var run = (0, import_node_util.promisify)(import_node_child_process.execFile);
563
+ var deflate = (0, import_node_util.promisify)(import_node_zlib.gzip);
564
+ var ARCHIVE = "wasi/lib_FoundationICU.a";
565
+ var MEMBER = "icu_packaged_data.cpp.obj";
566
+ var raw = /* @__PURE__ */ new Map();
567
+ var assets = /* @__PURE__ */ new Map();
568
+ function memo(cache, key, load2) {
569
+ let pending = cache.get(key);
570
+ if (!pending) {
571
+ pending = load2();
572
+ pending.catch(() => cache.delete(key));
573
+ cache.set(key, pending);
574
+ }
575
+ return pending;
576
+ }
577
+ function swiftIcuData(toolchainBin, release) {
578
+ return memo(raw, `${toolchainBin}\0${release}`, () => load(toolchainBin, release));
579
+ }
580
+ function swiftIcuAsset(toolchainBin, release) {
581
+ return memo(assets, `${toolchainBin}\0${release}`, async () => {
582
+ const zipped = await deflate(await swiftIcuData(toolchainBin, release));
583
+ return { sha256: (0, import_node_crypto.createHash)("sha256").update(zipped).digest("hex"), gzip: zipped };
584
+ });
585
+ }
586
+ async function load(toolchainBin, release) {
587
+ const { stdout } = await run(import_node_path3.default.join(toolchainBin, "swift"), [
588
+ "sdk",
589
+ "configure",
590
+ "--show-configuration",
591
+ `${release}_wasm`,
592
+ "wasm32-unknown-wasip1"
593
+ ]);
594
+ const resources = /^swiftStaticResourcesPath: (.+)$/m.exec(stdout)?.[1]?.trim();
595
+ if (!resources) throw new Error(`swift-icu: ${release}_wasm names no static resources`);
596
+ return Buffer.from(
597
+ icuSegment(archiveMember((0, import_node_fs2.readFileSync)(import_node_path3.default.join(resources, ARCHIVE)), MEMBER))
598
+ );
599
+ }
600
+ function archiveMember(archive, name) {
601
+ if (archive.toString("latin1", 0, 8) !== "!<arch>\n")
602
+ throw new Error("swift-icu: not an ar archive");
603
+ let names = "";
604
+ for (let at = 8; at + 60 <= archive.length; ) {
605
+ const raw2 = archive.toString("latin1", at, at + 16).trimEnd();
606
+ const size = Number(archive.toString("latin1", at + 48, at + 58).trim());
607
+ const body = archive.subarray(at + 60, at + 60 + size);
608
+ if (raw2 === "//") names = body.toString("latin1");
609
+ const member = /^\/\d+$/.test(raw2) ? names.slice(Number(raw2.slice(1)), names.indexOf("/\n", Number(raw2.slice(1)))) : raw2.replace(/\/$/, "");
610
+ if (member === name) return body;
611
+ at += 60 + size + size % 2;
612
+ }
613
+ throw new Error(`swift-icu: ${name} is not in the sdk's icu archive`);
614
+ }
615
+ function icuSegment(object) {
616
+ let at = 8;
617
+ const leb = () => {
618
+ let result = 0;
619
+ let shift = 0;
620
+ let byte;
621
+ do {
622
+ byte = object[at++];
623
+ result += (byte & 127) * 2 ** shift;
624
+ shift += 7;
625
+ } while (byte & 128);
626
+ return result;
627
+ };
628
+ const found = [];
629
+ while (at < object.length) {
630
+ const id = object[at++];
631
+ const size = leb();
632
+ const end = at + size;
633
+ if (id === 11) {
634
+ for (let count = leb(); count > 0; count--) {
635
+ if (leb() !== 0) throw new Error("swift-icu: unexpected data segment kind");
636
+ at++;
637
+ leb();
638
+ at++;
639
+ const length = leb();
640
+ const bytes = object.subarray(at, at + length);
641
+ if (bytes[2] === 218 && bytes[3] === 39) found.push(bytes);
642
+ at += length;
643
+ }
644
+ }
645
+ at = end;
646
+ }
647
+ if (found.length !== 1)
648
+ throw new Error(`swift-icu: expected one icu data segment, found ${found.length}`);
649
+ return found[0];
650
+ }
651
+
652
+ // src/swift-package.ts
653
+ function swiftManifestTargets(manifest) {
654
+ const code = manifest.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
655
+ const targets = [];
656
+ const pattern = /\.(executableTarget|target)\s*\(/g;
657
+ let match;
658
+ while ((match = pattern.exec(code)) !== null) {
659
+ const executable = match[1] === "executableTarget";
660
+ let depth = 1;
661
+ const start = pattern.lastIndex;
662
+ let i = start;
663
+ let inString = false;
664
+ let escaped = false;
665
+ let flat = "";
666
+ while (i < code.length && depth > 0) {
667
+ const ch = code[i];
668
+ if (inString) {
669
+ if (escaped) escaped = false;
670
+ else if (ch === "\\") escaped = true;
671
+ else if (ch === '"') inString = false;
672
+ if (depth === 1) flat += ch;
673
+ } else if (ch === '"') {
674
+ inString = true;
675
+ if (depth === 1) flat += ch;
676
+ } else if (ch === "(") {
677
+ depth++;
678
+ if (depth === 1) flat += ch;
679
+ } else if (ch === ")") {
680
+ depth--;
681
+ if (depth >= 1) flat += ch;
682
+ } else if (depth === 1) {
683
+ flat += ch;
684
+ }
685
+ i++;
686
+ }
687
+ const name = /\bname\s*:\s*"([^"]+)"/.exec(flat)?.[1];
688
+ if (!name) continue;
689
+ const rawPath = /\bpath\s*:\s*"([^"]*)"/.exec(flat)?.[1];
690
+ const targetPath = rawPath == null || rawPath === "" ? `Sources/${name}` : rawPath.replace(/^\.\//, "").replace(/\/+$/, "");
691
+ const call = code.slice(start, i);
692
+ const languageMode = Number(
693
+ /\.swiftLanguageMode\(\s*\.v(\d+)\s*\)/.exec(call)?.[1] ?? "5"
694
+ );
695
+ const mainActorIsolation = /\.defaultIsolation\(\s*MainActor\.self\s*\)/.test(call);
696
+ targets.push({ name, path: targetPath, executable, languageMode, mainActorIsolation });
697
+ }
698
+ return targets;
699
+ }
700
+ function swiftPackagePartition(manifest) {
701
+ const targets = swiftManifestTargets(manifest);
702
+ return targets.length === 1 && targets[0].executable && targets[0].path === "." ? [] : targets;
703
+ }
704
+
705
+ // src/swift-compile.ts
706
+ var run2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
707
+ var WASM_TRIPLE = "wasm32-unknown-wasip1";
708
+ var cores = import_node_os2.default.availableParallelism();
709
+ var BUILD_ENV = { ...process.env, BINARYEN_CORES: String(Math.min(4, cores)) };
710
+ var BUILD_JOBS = String(Math.max(1, Math.floor(cores / 2)));
711
+ function swiftModuleInputFiles(root) {
712
+ const files = [
713
+ "Package.swift",
714
+ "scripts/contrast-app-Package.swift",
715
+ "toolchain.json"
716
+ ];
717
+ const walk = (dir) => {
718
+ for (const entry of (0, import_node_fs3.readdirSync)(import_node_path4.default.join(root, dir), { withFileTypes: true })) {
719
+ const relative = `${dir}/${entry.name}`;
720
+ if (entry.isDirectory()) walk(relative);
721
+ else if (/\.(swift|c|h)$/.test(entry.name)) files.push(relative);
722
+ }
723
+ };
724
+ walk("Sources");
725
+ return files.sort();
726
+ }
727
+ async function ensureMacroPlugins(moduleRoot, toolchainBin) {
728
+ const dir = import_node_path4.default.join(moduleRoot, ".build/host/RnxPreviewMacros");
729
+ const ext = process.platform === "darwin" ? "dylib" : "so";
730
+ const plugins = [
731
+ {
732
+ library: import_node_path4.default.join(dir, `libRnxPreviewMacros.${ext}`),
733
+ source: import_node_path4.default.join(moduleRoot, "Sources/RnxPreviewMacros/PreviewMacro.swift")
734
+ },
735
+ {
736
+ library: import_node_path4.default.join(dir, `libFoundationMacros.${ext}`),
737
+ source: import_node_path4.default.join(moduleRoot, "Sources/FoundationMacros/PredicateMacro.swift")
738
+ }
739
+ ];
740
+ const stale = plugins.some(
741
+ ({ library, source }) => !(0, import_node_fs3.existsSync)(library) || (0, import_node_fs3.statSync)(source).mtimeMs > (0, import_node_fs3.statSync)(library).mtimeMs
742
+ );
743
+ if (stale) {
744
+ await run2("sh", [
745
+ import_node_path4.default.join(moduleRoot, "scripts/build-preview-plugin.sh"),
746
+ toolchainBin,
747
+ plugins[0].library
748
+ ]);
749
+ }
750
+ return plugins.map(({ library }) => library);
751
+ }
752
+ var linkedModuleDigests = /* @__PURE__ */ new Map();
753
+ function linkedModuleDigest(root) {
754
+ const files = swiftModuleInputFiles(root);
755
+ const stampHash = (0, import_node_crypto2.createHash)("sha256");
756
+ for (const file of files) {
757
+ const stat = (0, import_node_fs3.statSync)(import_node_path4.default.join(root, file), { bigint: true });
758
+ stampHash.update(file);
759
+ stampHash.update(
760
+ `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}
761
+ `
762
+ );
763
+ }
764
+ const stamp = stampHash.digest("hex");
765
+ const cached = linkedModuleDigests.get(root);
766
+ if (cached?.stamp === stamp) return cached.digest;
767
+ const hash = (0, import_node_crypto2.createHash)("sha256");
768
+ for (const file of files) {
769
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
770
+ hash.update(
771
+ (0, import_node_crypto2.createHash)("sha256").update((0, import_node_fs3.readFileSync)(import_node_path4.default.join(root, file))).digest()
772
+ );
773
+ }
774
+ const digest = hash.digest();
775
+ linkedModuleDigests.set(root, { stamp, digest });
776
+ return digest;
777
+ }
778
+ function compileTargets(input) {
779
+ const manifest = input.files["Package.swift"];
780
+ return manifest === void 0 ? [] : swiftPackagePartition(manifest);
781
+ }
782
+ function withNativeSourceGlue(input, glue) {
783
+ const targets = compileTargets(input);
784
+ if (targets.length > 1) {
785
+ throw new Error("swift native modules need one SwiftPM target");
786
+ }
787
+ const targetPath = targets[0]?.path === "." ? "" : targets[0]?.path ?? "";
788
+ return {
789
+ ...input,
790
+ files: {
791
+ ...input.files,
792
+ [import_node_path4.default.posix.join(targetPath, "OneNativeSource.generated.swift")]: glue
793
+ },
794
+ oneModule: true
795
+ };
796
+ }
797
+ function swiftSiblingDependencies(files, targets) {
798
+ const names = new Set(targets.map((target) => target.name));
799
+ const siblings = /* @__PURE__ */ new Map();
800
+ for (const target of targets) {
801
+ const prefix = `${target.path}/`;
802
+ const deps = /* @__PURE__ */ new Set();
803
+ for (const [file, contents] of Object.entries(files)) {
804
+ if (file === "Package.swift" || !file.startsWith(prefix)) continue;
805
+ for (const match of contents.matchAll(
806
+ /^\s*(?:@\w+(?:\([^)]*\))?\s+)*import\s+(?:(?:typealias|struct|class|enum|protocol|let|var|func)\s+)?([A-Za-z_][A-Za-z0-9_]*)/gm
807
+ )) {
808
+ if (names.has(match[1]) && match[1] !== target.name) deps.add(match[1]);
809
+ }
810
+ }
811
+ siblings.set(target.name, [...deps].sort());
812
+ }
813
+ return siblings;
814
+ }
815
+ var ONE_PRODUCT = '.product(name: "One", package: "sootsim-swift")';
816
+ var ONE_IMPLICIT_FLAGS = ', "-Xfrontend", "-import-module", "-Xfrontend", "One"';
817
+ var MULTI_MANIFEST_VERSION = 5;
818
+ function swiftArtifactHash(input, release, moduleRoot) {
819
+ const hash = (0, import_node_crypto2.createHash)("sha256");
820
+ hash.update(`swift-compile/v1
821
+ ${release}
822
+ `);
823
+ if (input.oneModule) hash.update("one-module\n");
824
+ hash.update(linkedModuleDigest(moduleRoot));
825
+ for (const file of Object.keys(input.files).sort()) {
826
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
827
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(input.files[file]).digest());
828
+ }
829
+ for (const base of Object.keys(input.resources ?? {}).sort()) {
830
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(base).digest());
831
+ hash.update(
832
+ (0, import_node_crypto2.createHash)("sha256").update(input.resources?.[base] ?? "").digest()
833
+ );
834
+ }
835
+ for (const name of Object.keys(input.images ?? {}).sort()) {
836
+ const image = input.images?.[name];
837
+ if (!image) continue;
838
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(name).digest());
839
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(image.file).digest());
840
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(image.data).digest());
841
+ }
842
+ const targets = compileTargets(input);
843
+ if (targets.length > 0) {
844
+ hash.update(`swift-multi/v${MULTI_MANIFEST_VERSION}
845
+ `);
846
+ hash.update(
847
+ JSON.stringify({
848
+ targets,
849
+ siblings: [...swiftSiblingDependencies(input.files, targets)].sort(
850
+ (a, b) => String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0
851
+ )
852
+ })
853
+ );
854
+ }
855
+ return hash.digest("hex");
856
+ }
857
+ function emitSwiftPackageManifest(args) {
858
+ const { targets, siblings, moduleRoot, oneModule } = args;
859
+ const executable = targets.find((target) => target.executable);
860
+ if (!executable) throw new Error("swift-compile: no executable target to emit");
861
+ const stanza = (target) => {
862
+ const isolation = target.mainActorIsolation ? " .defaultIsolation(MainActor.self)," : "";
863
+ const swiftSettings = `swiftSettings: [.swiftLanguageMode(.v${target.languageMode}),${isolation} .unsafeFlags(["-Osize"${oneModule ? ONE_IMPLICIT_FLAGS : ""}])]`;
864
+ const deps = [
865
+ '.product(name: "SwiftUI", package: "sootsim-swift")',
866
+ ...oneModule ? [ONE_PRODUCT] : [],
867
+ ...(siblings.get(target.name) ?? []).map((sibling) => `"${sibling}"`)
868
+ ].join(", ");
869
+ const kind = target.executable ? ".executableTarget" : ".target";
870
+ const link = target.executable ? `,
871
+ linkerSettings: [
872
+ .unsafeFlags([
873
+ "-Xclang-linker", "-mexec-model=reactor",
874
+ "-Xlinker", "--export-if-defined=__main_argc_argv",
875
+ ]),
876
+ .unsafeFlags(["-Xlinker", "--strip-all"], .when(configuration: .release)),
877
+ ]` : "";
878
+ return [
879
+ ` ${kind}(`,
880
+ ` name: "${target.name}",`,
881
+ ` dependencies: [${deps}],`,
882
+ ` path: "${target.path}",`,
883
+ ` ${swiftSettings}${link}`,
884
+ ` ),`
885
+ ].join("\n");
886
+ };
887
+ return [
888
+ "// swift-tools-version: 6.2",
889
+ "import PackageDescription",
890
+ "",
891
+ "// generated by swift-compile from the project package partition.",
892
+ "let package = Package(",
893
+ ' name: "app",',
894
+ ` products: [.executable(name: "${executable.name}", targets: ["${executable.name}"])],`,
895
+ ` dependencies: [.package(path: "${moduleRoot}")],`,
896
+ " targets: [",
897
+ ...targets.map(stanza),
898
+ " ]",
899
+ ")",
900
+ ""
901
+ ].join("\n");
902
+ }
903
+ function syncSwiftTree(base, files) {
904
+ (0, import_node_fs3.mkdirSync)(base, { recursive: true });
905
+ const walk = (dir) => {
906
+ for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
907
+ const full = import_node_path4.default.join(dir, entry.name);
908
+ if (dir === base && entry.name === ".build") continue;
909
+ if (entry.isDirectory()) walk(full);
910
+ else if (!files.has(full)) (0, import_node_fs3.rmSync)(full, { force: true });
911
+ }
912
+ };
913
+ walk(base);
914
+ for (const [full, contents] of files) {
915
+ if ((0, import_node_fs3.existsSync)(full) && (0, import_node_fs3.readFileSync)(full, "utf8") === contents) continue;
916
+ (0, import_node_fs3.mkdirSync)(import_node_path4.default.dirname(full), { recursive: true });
917
+ (0, import_node_fs3.writeFileSync)(full, contents, "utf8");
918
+ }
919
+ }
920
+ function writeSwiftProject(layout, input, moduleRoot) {
921
+ const targets = compileTargets(input);
922
+ const files = /* @__PURE__ */ new Map();
923
+ if (targets.length === 0) {
924
+ for (const [file, contents] of Object.entries(input.files)) {
925
+ if (file === "Package.swift") continue;
926
+ files.set(import_node_path4.default.join(layout.sources, file), contents);
927
+ }
928
+ let manifest = (0, import_node_fs3.readFileSync)(
929
+ import_node_path4.default.join(moduleRoot, "scripts/contrast-app-Package.swift"),
930
+ "utf8"
931
+ ).replaceAll("__SOOTSIM_SWIFT_PACKAGE__", moduleRoot);
932
+ if (input.oneModule) {
933
+ const product = '.product(name: "CoreLocation", package: "sootsim-swift"),';
934
+ const flags = '"-Osize"';
935
+ if (!manifest.includes(product) || !manifest.includes(flags)) {
936
+ throw new Error(
937
+ "scripts/contrast-app-Package.swift lost the lines the One module links at"
938
+ );
939
+ }
940
+ manifest = manifest.replace(product, `${product}
941
+ ${ONE_PRODUCT},`).replace(flags, `${flags}${ONE_IMPLICIT_FLAGS}`);
942
+ }
943
+ files.set(import_node_path4.default.join(layout.root, "Package.swift"), manifest);
944
+ } else {
945
+ for (const [file, contents] of Object.entries(input.files)) {
946
+ if (file === "Package.swift") continue;
947
+ files.set(import_node_path4.default.join(layout.root, file), contents);
948
+ }
949
+ files.set(
950
+ import_node_path4.default.join(layout.root, "Package.swift"),
951
+ emitSwiftPackageManifest({
952
+ targets,
953
+ siblings: swiftSiblingDependencies(input.files, targets),
954
+ moduleRoot,
955
+ oneModule: input.oneModule
956
+ })
957
+ );
958
+ }
959
+ syncSwiftTree(layout.root, files);
960
+ }
961
+ function streamText(value) {
962
+ if (typeof value === "string") return value.trim();
963
+ if (Buffer.isBuffer(value)) return value.toString("utf8").trim();
964
+ return "";
965
+ }
966
+ function outputOf(error) {
967
+ if (error && typeof error === "object") {
968
+ const streams = [
969
+ streamText(Reflect.get(error, "stdout")),
970
+ streamText(Reflect.get(error, "stderr"))
971
+ ].filter(Boolean);
972
+ if (streams.length > 0) return streams.join("\n");
973
+ }
974
+ return error instanceof Error ? error.message : String(error);
975
+ }
976
+ function builtArtifact(layout) {
977
+ const binDir = import_node_path4.default.join(layout.root, ".build", WASM_TRIPLE, "release");
978
+ const manifest = (0, import_node_fs3.readFileSync)(import_node_path4.default.join(layout.root, "Package.swift"), "utf8");
979
+ const executable = swiftManifestTargets(manifest).find((target) => target.executable);
980
+ if (executable === void 0) {
981
+ throw new Error("swift-compile: the written manifest names no executable target");
982
+ }
983
+ const file = import_node_path4.default.join(binDir, `${executable.name}.wasm`);
984
+ if (!(0, import_node_fs3.existsSync)(file)) {
985
+ const found = (0, import_node_fs3.existsSync)(binDir) ? (0, import_node_fs3.readdirSync)(binDir).filter((name) => name.endsWith(".wasm")).sort() : [];
986
+ throw new Error(
987
+ `swift-compile: the build produced no ${executable.name}.wasm (found: ${found.join(", ") || "none"})`
988
+ );
989
+ }
990
+ return file;
991
+ }
992
+ function projectDiagnostic(output, layout) {
993
+ const base = layout.multi ? layout.root : layout.sources;
994
+ const prefix = `${base}${import_node_path4.default.sep}`;
995
+ return output.split("\n").map((line) => line.startsWith(prefix) ? line.slice(prefix.length) : line).join("\n").trim();
996
+ }
997
+ var ROOT_LOCKS = Symbol.for("peachsim.swiftCompile.rootLocks");
998
+ var host = globalThis;
999
+ var rootLocks = host[ROOT_LOCKS] ??= /* @__PURE__ */ new Map();
1000
+ function serialize(root, work) {
1001
+ const current = rootLocks.get(root) ?? Promise.resolve();
1002
+ const next = current.then(work, work);
1003
+ rootLocks.set(
1004
+ root,
1005
+ next.catch(() => {
1006
+ })
1007
+ );
1008
+ return next;
1009
+ }
1010
+ function compileSwiftProjectUnit(input, options) {
1011
+ (0, import_node_fs3.mkdirSync)(options.buildRoot, { recursive: true });
1012
+ const root = (0, import_node_fs3.realpathSync)(options.buildRoot);
1013
+ const moduleRoot = import_node_path4.default.resolve(options.moduleRoot);
1014
+ const layout = {
1015
+ root,
1016
+ sources: import_node_path4.default.join(root, "Sources", "App"),
1017
+ multi: compileTargets(input).length > 0
1018
+ };
1019
+ const hash = swiftArtifactHash(input, options.release, moduleRoot);
1020
+ return serialize(root, async () => {
1021
+ writeSwiftProject(layout, input, moduleRoot);
1022
+ const swift = import_node_path4.default.join(options.toolchainBin, "swift");
1023
+ if (!(0, import_node_fs3.existsSync)(swift)) {
1024
+ throw new Error(
1025
+ `swift-compile: no swift.org toolchain at ${options.toolchainBin}; install ${options.release} and its matching wasm sdk`
1026
+ );
1027
+ }
1028
+ const macroPlugins = await ensureMacroPlugins(moduleRoot, options.toolchainBin);
1029
+ const icu = swiftIcuAsset(options.toolchainBin, options.release);
1030
+ try {
1031
+ await run2(
1032
+ swift,
1033
+ [
1034
+ "build",
1035
+ "-j",
1036
+ BUILD_JOBS,
1037
+ "--package-path",
1038
+ root,
1039
+ "--swift-sdk",
1040
+ `${options.release}_wasm`,
1041
+ "-c",
1042
+ "release",
1043
+ "-Xswiftc",
1044
+ "-Osize",
1045
+ ...macroPlugins.flatMap((plugin) => [
1046
+ "-Xswiftc",
1047
+ "-load-plugin-library",
1048
+ "-Xswiftc",
1049
+ plugin
1050
+ ])
1051
+ ],
1052
+ { env: BUILD_ENV, maxBuffer: 64 * 1024 * 1024, timeout: options.timeoutMs }
1053
+ );
1054
+ } catch (error) {
1055
+ throw new Error(projectDiagnostic(outputOf(error), layout), { cause: error });
1056
+ }
1057
+ return {
1058
+ hash,
1059
+ bytes: (0, import_node_fs3.readFileSync)(builtArtifact(layout)),
1060
+ icu: await icu
1061
+ };
1062
+ });
1063
+ }
1064
+
1065
+ // src/swift-submit.ts
1066
+ var SWIFT_COMPILE_PATH = "/api/swift-compile";
1067
+ var SWIFT_HOST_ABI = 1;
1068
+ var POLL_INTERVAL_MS = 1500;
1069
+ function swiftNativeEntry(native, file) {
1070
+ return {
1071
+ packageId: native.packageId,
1072
+ contractHash: native.hash,
1073
+ modules: Object.fromEntries(
1074
+ (native.files[file] ?? []).map((name) => [name, native.modules[name] ?? []])
1075
+ ),
1076
+ defaultView: native.defaultViewFiles.includes(file)
1077
+ };
1078
+ }
1079
+ function httpSwiftCompileSubmit(context) {
1080
+ const { assetReadToken, authorization, endpoint } = context;
1081
+ if (!assetReadToken && !authorization) return void 0;
1082
+ const url = endpoint ? `${endpoint.replace(/\/+$/, "")}${SWIFT_COMPILE_PATH}` : SWIFT_COMPILE_PATH;
1083
+ return async (work) => {
1084
+ let response;
1085
+ try {
1086
+ response = await fetch(url, {
1087
+ method: "POST",
1088
+ headers: {
1089
+ "content-type": "application/json",
1090
+ ...authorization ? { authorization } : {}
1091
+ },
1092
+ body: JSON.stringify({
1093
+ ...work,
1094
+ hostAbi: SWIFT_HOST_ABI,
1095
+ ...assetReadToken ? { token: assetReadToken } : {}
1096
+ })
1097
+ });
1098
+ } catch (error) {
1099
+ throw new Error(
1100
+ `contrast-bundler: fetch failed for ${url} while compiling ${work.entry}: ${error instanceof Error ? error.message : String(error)}`,
1101
+ { cause: error }
1102
+ );
1103
+ }
1104
+ if (!response.ok) {
1105
+ const detail = (await response.text()).trim();
1106
+ throw new Error(
1107
+ `contrast-bundler: the swift compile for ${work.entry} answered ${response.status}${detail ? `: ${detail}` : ""}`
1108
+ );
1109
+ }
1110
+ return await response.json();
1111
+ };
1112
+ }
1113
+ function resolveImageUrls(urls, origin) {
1114
+ const resolved = {};
1115
+ for (const [name, url] of Object.entries(urls)) {
1116
+ resolved[name] = new URL(url, origin).href;
1117
+ }
1118
+ return resolved;
1119
+ }
1120
+ async function compileSwiftProject(submit, work, origin) {
1121
+ while (true) {
1122
+ const outcome = await submit(work);
1123
+ switch (outcome.status) {
1124
+ case "published":
1125
+ return {
1126
+ url: new URL(outcome.artifactUrl, origin).href,
1127
+ hash: outcome.hash,
1128
+ icuUrl: new URL(outcome.icuUrl, origin).href,
1129
+ ...outcome.imageUrls === void 0 ? {} : { imageUrls: resolveImageUrls(outcome.imageUrls, origin) },
1130
+ ...outcome.native === void 0 ? {} : { native: outcome.native }
1131
+ };
1132
+ case "compiling":
1133
+ await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL_MS));
1134
+ continue;
1135
+ case "refused":
1136
+ case "failed":
1137
+ throw new Error(
1138
+ `contrast-bundler: the swift sources for ${work.entry} cannot be compiled: ${outcome.detail}`
1139
+ );
1140
+ }
1141
+ }
1142
+ }
1143
+
1144
+ // src/vite-plugin-swift.ts
1145
+ var ARTIFACT_PATH = "/__peach/swift/";
1146
+ var RESOURCE_PATH = "/__peach/swift-res/";
1147
+ var ICU_PATH = "/__peach/swift-icu/";
1148
+ var VIRTUAL_PREFIX = "\0peach-swift:";
1149
+ var SKIP_SOURCE_DIRS = /* @__PURE__ */ new Set([
1150
+ "node_modules",
1151
+ "ios",
1152
+ "android",
1153
+ "dist",
1154
+ "types",
1155
+ "build"
1156
+ ]);
1157
+ var NATIVE_DESCRIPTOR = ".one-native-source.json";
1158
+ var NATIVE_GLUE = ".one-native-source.swift.txt";
1159
+ function swiftPackageId(packagePath) {
1160
+ return import_node_path5.default.basename(packagePath).replace(/[^A-Za-z0-9_]/g, "_").replace(/^(\d)/, "_$1");
1161
+ }
1162
+ function nativeSourceDescriptor(packagePath) {
1163
+ const file = import_node_path5.default.join(packagePath, NATIVE_DESCRIPTOR);
1164
+ if (!import_node_fs4.default.existsSync(file)) return null;
1165
+ const value = JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
1166
+ const invalid = () => new Error(`${file}: invalid One native source descriptor; run one typegen`);
1167
+ if (!value || typeof value !== "object" || Reflect.get(value, "generatedBy") !== "one typegen" || !import_node_fs4.default.existsSync(import_node_path5.default.join(packagePath, NATIVE_GLUE))) {
1168
+ throw invalid();
1169
+ }
1170
+ const packageId = Reflect.get(value, "packageId");
1171
+ const hash = Reflect.get(value, "hash");
1172
+ const rawFiles = Reflect.get(value, "files");
1173
+ const rawModules = Reflect.get(value, "modules");
1174
+ const rawDefaults = Reflect.get(value, "defaultViewFiles");
1175
+ if (typeof packageId !== "string" || typeof hash !== "string" || !rawFiles || typeof rawFiles !== "object" || !rawModules || typeof rawModules !== "object" || !Array.isArray(rawDefaults) || !rawDefaults.every((name) => typeof name === "string")) {
1176
+ throw invalid();
1177
+ }
1178
+ const files = {};
1179
+ const modules = {};
1180
+ for (const [key, names] of Object.entries(rawFiles)) {
1181
+ if (!Array.isArray(names) || !names.every((name) => typeof name === "string"))
1182
+ throw invalid();
1183
+ files[key] = names;
1184
+ }
1185
+ for (const [key, names] of Object.entries(rawModules)) {
1186
+ if (!Array.isArray(names) || !names.every((name) => typeof name === "string"))
1187
+ throw invalid();
1188
+ modules[key] = names;
1189
+ }
1190
+ if (packageId !== swiftPackageId(packagePath)) {
1191
+ throw new Error(`${file}: invalid One native source descriptor; run one typegen`);
1192
+ }
1193
+ return { packageId, hash, files, modules, defaultViewFiles: rawDefaults };
1194
+ }
1195
+ function nativeSourceFiles(packagePath) {
1196
+ return [NATIVE_DESCRIPTOR, NATIVE_GLUE].map((name) => import_node_path5.default.join(packagePath, name)).filter((file) => import_node_fs4.default.existsSync(file));
1197
+ }
1198
+ var projects = /* @__PURE__ */ new Map();
1199
+ var devServer;
1200
+ function projectFor(packagePath) {
1201
+ const existing = projects.get(packagePath);
1202
+ if (existing) return existing;
1203
+ const created = {
1204
+ packagePath,
1205
+ artifact: null,
1206
+ building: null,
1207
+ resourceFiles: /* @__PURE__ */ new Map()
1208
+ };
1209
+ projects.set(packagePath, created);
1210
+ return created;
1211
+ }
1212
+ function sourceFiles(root) {
1213
+ const found = [];
1214
+ const walk = (dir) => {
1215
+ for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
1216
+ if (entry.name.startsWith(".") || SKIP_SOURCE_DIRS.has(entry.name)) continue;
1217
+ const full = import_node_path5.default.join(dir, entry.name);
1218
+ if (entry.isDirectory()) {
1219
+ if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
1220
+ walk(full);
1221
+ } else if (entry.name.endsWith(".swift") && (entry.isFile() || entry.isSymbolicLink() && import_node_fs4.default.statSync(full).isFile()))
1222
+ found.push(full);
1223
+ }
1224
+ };
1225
+ walk(root);
1226
+ return found;
1227
+ }
1228
+ function resourceCandidateFiles(root) {
1229
+ const found = [];
1230
+ const walk = (dir) => {
1231
+ for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
1232
+ if (entry.name.startsWith(".") || SKIP_SOURCE_DIRS.has(entry.name) || entry.name === ".build")
1233
+ continue;
1234
+ const full = import_node_path5.default.join(dir, entry.name);
1235
+ if (entry.isDirectory()) {
1236
+ if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
1237
+ walk(full);
1238
+ } else {
1239
+ found.push(full);
1240
+ }
1241
+ }
1242
+ };
1243
+ walk(root);
1244
+ return found;
1245
+ }
1246
+ var RESOURCE_CONTENT_TYPES = {
1247
+ ".avif": "image/avif",
1248
+ ".bmp": "image/bmp",
1249
+ ".gif": "image/gif",
1250
+ ".ico": "image/x-icon",
1251
+ ".jpeg": "image/jpeg",
1252
+ ".jpg": "image/jpeg",
1253
+ ".png": "image/png",
1254
+ ".webp": "image/webp"
1255
+ };
1256
+ function buildSwiftResourceTable(project, baseUrl) {
1257
+ const absolute = resourceCandidateFiles(project.packagePath);
1258
+ const relative = absolute.map(
1259
+ (file) => import_node_path5.default.relative(project.packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep)
1260
+ );
1261
+ const byRelative = new Map(absolute.map((file, index) => [relative[index], file]));
1262
+ const readContents = (candidate) => {
1263
+ const file = byRelative.get(candidate);
1264
+ if (!file || !/(^|\/)Contents\.json$|\.symbolset\/[^/]+\.svg$/.test(candidate))
1265
+ return null;
1266
+ try {
1267
+ return import_node_fs4.default.readFileSync(file, "utf8");
1268
+ } catch {
1269
+ return null;
1270
+ }
1271
+ };
1272
+ const found = enumerateBundleImages(relative, readContents);
1273
+ const colors = enumerateBundleColors(relative, readContents);
1274
+ const symbols = enumerateBundleSymbols(relative, readContents);
1275
+ const table = {};
1276
+ const images = {};
1277
+ const watched = [];
1278
+ const registered = /* @__PURE__ */ new Map();
1279
+ for (const { name, file, scale } of found) {
1280
+ const disk = byRelative.get(file);
1281
+ if (!disk) continue;
1282
+ let bytes;
1283
+ try {
1284
+ bytes = import_node_fs4.default.readFileSync(disk);
1285
+ } catch {
1286
+ continue;
1287
+ }
1288
+ const hash = (0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex").slice(0, 16);
1289
+ const ext = import_node_path5.default.posix.extname(file).toLowerCase();
1290
+ table[name] = { uri: `${baseUrl}${RESOURCE_PATH}${hash}${ext}`, scale };
1291
+ images[name] = {
1292
+ file: import_node_path5.default.posix.basename(file),
1293
+ data: bytes.toString("base64")
1294
+ };
1295
+ watched.push(file);
1296
+ const contents = file.replace(/\/[^/]+$/, "/Contents.json");
1297
+ if (byRelative.has(contents)) watched.push(contents);
1298
+ registered.set(`${hash}${ext}`, disk);
1299
+ }
1300
+ for (const file of relative) {
1301
+ if (file.endsWith(".colorset/Contents.json") || file.includes(".symbolset/"))
1302
+ watched.push(file);
1303
+ }
1304
+ project.resourceFiles = registered;
1305
+ return { table, images, colors, symbols, watched };
1306
+ }
1307
+ var BUNDLE_MAX_FILES = 128;
1308
+ var BUNDLE_MAX_FILE_BYTES = 4 * 1024 * 1024;
1309
+ var BUNDLE_MAX_TOTAL_BYTES = 8 * 1024 * 1024;
1310
+ function buildSwiftBundleFiles(packagePath) {
1311
+ const relative = resourceCandidateFiles(packagePath).map(
1312
+ (file) => import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep)
1313
+ );
1314
+ const files = {};
1315
+ const watched = [];
1316
+ const skipped = [];
1317
+ let total = 0;
1318
+ for (const { base, file } of enumerateBundleJson(relative)) {
1319
+ watched.push(file);
1320
+ let bytes;
1321
+ try {
1322
+ bytes = import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, ...file.split("/")));
1323
+ } catch {
1324
+ continue;
1325
+ }
1326
+ if (bytes.length > BUNDLE_MAX_FILE_BYTES) {
1327
+ skipped.push(`${file} (${bytes.length} bytes past the per-file cap)`);
1328
+ continue;
1329
+ }
1330
+ if (Object.keys(files).length >= BUNDLE_MAX_FILES || total + bytes.length > BUNDLE_MAX_TOTAL_BYTES) {
1331
+ skipped.push(
1332
+ `${file} (pack caps: ${BUNDLE_MAX_FILES} files / ${BUNDLE_MAX_TOTAL_BYTES} bytes)`
1333
+ );
1334
+ continue;
1335
+ }
1336
+ files[base] = bytes.toString("base64");
1337
+ total += bytes.length;
1338
+ }
1339
+ return { files, watched, skipped };
1340
+ }
1341
+ function serveSwiftResource(project, key) {
1342
+ const disk = project.resourceFiles.get(key);
1343
+ if (!disk) return null;
1344
+ try {
1345
+ return {
1346
+ bytes: import_node_fs4.default.readFileSync(disk),
1347
+ contentType: RESOURCE_CONTENT_TYPES[import_node_path5.default.extname(disk).toLowerCase()] ?? "application/octet-stream"
1348
+ };
1349
+ } catch {
1350
+ return null;
1351
+ }
1352
+ }
1353
+ function linkedModuleRoots(packagePath) {
1354
+ let manifest;
1355
+ try {
1356
+ manifest = import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, "Package.swift"), "utf8");
1357
+ } catch {
1358
+ return [];
1359
+ }
1360
+ const roots = /* @__PURE__ */ new Set();
1361
+ const dependency = /\.package\(\s*path:\s*"([^"]+)"\s*\)/g;
1362
+ for (const match of manifest.replace(/^\s*\/\/.*$/gm, "").matchAll(dependency)) {
1363
+ const root = import_node_path5.default.resolve(packagePath, match[1]);
1364
+ if (import_node_fs4.default.existsSync(import_node_path5.default.join(root, "Package.swift"))) roots.add(root);
1365
+ }
1366
+ return [...roots].sort();
1367
+ }
1368
+ function sourceStamp(packagePath, moduleRootOverride) {
1369
+ const hash = (0, import_node_crypto3.createHash)("sha256");
1370
+ hash.update(packagePath);
1371
+ const projectInputs = /* @__PURE__ */ new Set([
1372
+ import_node_path5.default.join(packagePath, "Package.swift"),
1373
+ ...sourceFiles(packagePath),
1374
+ ...nativeSourceFiles(packagePath),
1375
+ ...buildSwiftBundleFiles(packagePath).watched.map(
1376
+ (file) => import_node_path5.default.join(packagePath, ...file.split(import_node_path5.default.posix.sep))
1377
+ ),
1378
+ ...buildSwiftResourceTable(projectFor(packagePath), "").watched.map(
1379
+ (file) => import_node_path5.default.join(packagePath, ...file.split(import_node_path5.default.posix.sep))
1380
+ )
1381
+ ]);
1382
+ for (const file of [...projectInputs].sort()) {
1383
+ hash.update(import_node_path5.default.relative(packagePath, file));
1384
+ hash.update(String(import_node_fs4.default.statSync(file).mtimeMs));
1385
+ }
1386
+ const moduleRoot = moduleRootOverride ?? linkedModuleRoots(packagePath).find(
1387
+ (root) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, "toolchain.json"))
1388
+ );
1389
+ if (moduleRoot) {
1390
+ hash.update(moduleRoot);
1391
+ for (const relative of swiftModuleInputFiles(moduleRoot)) {
1392
+ hash.update(relative);
1393
+ hash.update(String(import_node_fs4.default.statSync(import_node_path5.default.join(moduleRoot, relative)).mtimeMs));
1394
+ }
1395
+ }
1396
+ return hash.digest("hex").slice(0, 16);
1397
+ }
1398
+ function entryRootId(packagePath, file) {
1399
+ const relative = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1400
+ return `${import_node_path5.default.basename(packagePath)}${import_node_path5.default.posix.sep}${relative}`;
1401
+ }
1402
+ function serverOrigin() {
1403
+ const address = devServer?.httpServer?.address();
1404
+ const port = address && typeof address === "object" ? address.port : devServer?.config.server.port;
1405
+ if (!port) throw new Error("the swift urls need the dev server port");
1406
+ return `http://localhost:${port}`;
1407
+ }
1408
+ function artifactUrl(project, artifact) {
1409
+ return `${serverOrigin()}${ARTIFACT_PATH}${artifact.hash}.wasm`;
1410
+ }
1411
+ function icuUrl(origin, artifact) {
1412
+ return "url" in artifact.icu ? artifact.icu.url : `${origin}${ICU_PATH}${artifact.icu.sha256}`;
1413
+ }
1414
+ function packageSources(packagePath) {
1415
+ const files = {};
1416
+ for (const file of sourceFiles(packagePath)) {
1417
+ const relative = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1418
+ files[relative] = import_node_fs4.default.readFileSync(file, "utf8");
1419
+ }
1420
+ const manifest = import_node_path5.default.join(packagePath, "Package.swift");
1421
+ if (import_node_fs4.default.existsSync(manifest)) files["Package.swift"] = import_node_fs4.default.readFileSync(manifest, "utf8");
1422
+ return files;
1423
+ }
1424
+ function compileInput(packagePath, entry) {
1425
+ const input = {
1426
+ files: packageSources(packagePath),
1427
+ resources: buildSwiftBundleFiles(packagePath).files,
1428
+ images: buildSwiftResourceTable(projectFor(packagePath), "").images,
1429
+ entry,
1430
+ oneModule: true
1431
+ };
1432
+ return nativeSourceDescriptor(packagePath) ? withNativeSourceGlue(
1433
+ input,
1434
+ import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, NATIVE_GLUE), "utf8")
1435
+ ) : input;
1436
+ }
1437
+ async function compile(packagePath, entry, stamp, moduleRootOverride) {
1438
+ const moduleRoot = moduleRootOverride ?? linkedModuleRoots(packagePath).find(
1439
+ (root) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, "toolchain.json"))
1440
+ );
1441
+ if (!moduleRoot) return { ...await compileRemote(packagePath, entry), stamp };
1442
+ const toolchain = JSON.parse(
1443
+ import_node_fs4.default.readFileSync(import_node_path5.default.join(moduleRoot, "toolchain.json"), "utf8")
1444
+ );
1445
+ const release = toolchain && typeof toolchain === "object" ? Reflect.get(toolchain, "release") : void 0;
1446
+ if (typeof release !== "string") {
1447
+ throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`);
1448
+ }
1449
+ const artifact = await compileSwiftProjectUnit(compileInput(packagePath, entry), {
1450
+ // the compile rewrites Sources/App in its root before swiftpm builds, and
1451
+ // its lock orders one process only: two vites on one root (a lane's dev
1452
+ // server and a capture, or two checkouts) build each other's sources. so
1453
+ // each app and module checkout gets its own root, which keeps the same
1454
+ // app's incremental build across restarts.
1455
+ buildRoot: process.env.CONTRAST_SWIFT_BUILD_ROOT ?? import_node_path5.default.join(
1456
+ import_node_os3.default.tmpdir(),
1457
+ "contrast-swift-build",
1458
+ (0, import_node_crypto3.createHash)("sha256").update(`${moduleRoot}\0${import_node_path5.default.resolve(packagePath)}`).digest("hex").slice(0, 16)
1459
+ ),
1460
+ moduleRoot,
1461
+ release,
1462
+ toolchainBin: import_node_path5.default.join(
1463
+ import_node_os3.default.homedir(),
1464
+ `Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`
1465
+ )
1466
+ });
1467
+ return { ...artifact, stamp };
1468
+ }
1469
+ async function compileRemote(packagePath, entry) {
1470
+ const auth = resolveCliAuth();
1471
+ if (!auth || auth.kind === "github") {
1472
+ throw new Error(
1473
+ `compiling ${import_node_path5.default.basename(packagePath)} for Peach needs \`peach login\` or PEACH_API_KEY: this peachsim has no local SwiftUI module, so Contrast compiles it`
1474
+ );
1475
+ }
1476
+ const origin = resolveContrastBackendOrigin(
1477
+ auth.kind === "session" ? auth.origin : null
1478
+ );
1479
+ const submit = httpSwiftCompileSubmit({
1480
+ endpoint: origin,
1481
+ authorization: authHeaderValue(auth)
1482
+ });
1483
+ if (!submit) throw new Error("swift compile: no credential to submit with");
1484
+ const descriptor = nativeSourceDescriptor(packagePath);
1485
+ const address = await compileSwiftProject(
1486
+ submit,
1487
+ {
1488
+ files: packageSources(packagePath),
1489
+ entry,
1490
+ packageId: descriptor?.packageId ?? swiftPackageId(packagePath)
1491
+ },
1492
+ origin
1493
+ );
1494
+ if (descriptor && address.native?.hash !== descriptor.hash) {
1495
+ throw new Error(
1496
+ `${import_node_path5.default.basename(packagePath)}: Contrast compiled native contract ${address.native?.hash ?? "none"} but one typegen wrote ${descriptor.hash}; the app's One and the compile host's differ, or the compile host is older than this peachsim`
1497
+ );
1498
+ }
1499
+ const response = await fetch(address.url);
1500
+ if (!response.ok) {
1501
+ throw new Error(`swift compile: ${address.url} answered ${response.status}`);
1502
+ }
1503
+ return {
1504
+ hash: address.hash,
1505
+ bytes: Buffer.from(await response.arrayBuffer()),
1506
+ icu: { url: address.icuUrl }
1507
+ };
1508
+ }
1509
+ function artifactFor(project, entry, moduleRoot) {
1510
+ const stamp = sourceStamp(project.packagePath, moduleRoot);
1511
+ if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact);
1512
+ if (project.building) {
1513
+ const running = project.building;
1514
+ if (running.stamp === stamp) return running.promise;
1515
+ const again = () => artifactFor(project, entry, moduleRoot);
1516
+ return running.promise.then(again, again);
1517
+ }
1518
+ const promise = compile(project.packagePath, entry, stamp, moduleRoot).then(
1519
+ (artifact) => {
1520
+ project.artifact = artifact;
1521
+ project.building = null;
1522
+ return artifact;
1523
+ },
1524
+ (error) => {
1525
+ project.building = null;
1526
+ throw error;
1527
+ }
1528
+ );
1529
+ project.building = { stamp, promise };
1530
+ return promise;
1531
+ }
1532
+ function swiftPackageWatchFiles(packagePath, moduleRoot) {
1533
+ return [
1534
+ import_node_path5.default.join(packagePath, "Package.swift"),
1535
+ ...sourceFiles(packagePath),
1536
+ ...nativeSourceFiles(packagePath),
1537
+ ...resourceCandidateFiles(packagePath),
1538
+ ...moduleRoot ? swiftModuleInputFiles(moduleRoot).map((file) => import_node_path5.default.join(moduleRoot, file)) : []
1539
+ ];
1540
+ }
1541
+ async function swiftPackageMetadata(packagePath, packageName, moduleRoot, origin) {
1542
+ const mains = sourceFiles(packagePath).filter(
1543
+ (file) => /@main\s+(?:public\s+)?struct\s+\w+\s*:/.test(import_node_fs4.default.readFileSync(file, "utf8"))
1544
+ );
1545
+ const native = nativeSourceDescriptor(packagePath);
1546
+ if (mains.length > 1 || mains.length === 0 && !native) {
1547
+ throw new Error(
1548
+ `swift package ${packageName} needs exactly one @main source or generated native modules; found ${mains.length}`
1549
+ );
1550
+ }
1551
+ const project = projectFor(packagePath);
1552
+ const entry = mains.length === 1 ? import_node_path5.default.relative(packagePath, mains[0]).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep) : Object.entries(native?.files ?? {}).find(
1553
+ ([_file, names]) => names.length > 0
1554
+ )?.[0];
1555
+ if (!entry) throw new Error(`swift package ${packageName} has no native module source`);
1556
+ const artifact = await artifactFor(project, entry, moduleRoot);
1557
+ const resources = buildSwiftResourceTable(project, origin);
1558
+ const bundleFiles = buildSwiftBundleFiles(packagePath);
1559
+ return {
1560
+ url: `${origin}${ARTIFACT_PATH}${artifact.hash}.wasm`,
1561
+ hash: artifact.hash,
1562
+ icuUrl: icuUrl(origin, artifact),
1563
+ rootId: `${packageName}/${entry}`,
1564
+ resources: resources.table,
1565
+ bundleFiles: bundleFiles.files,
1566
+ colors: resources.colors,
1567
+ symbols: resources.symbols,
1568
+ native: native ? {
1569
+ packageId: native.packageId,
1570
+ contractHash: native.hash,
1571
+ modules: native.modules,
1572
+ defaultView: mains.length === 1
1573
+ } : void 0
1574
+ };
1575
+ }
1576
+ function serveSwiftPackageAsset(pathname, res) {
1577
+ if (pathname.startsWith(RESOURCE_PATH)) {
1578
+ for (const project of projects.values()) {
1579
+ const served = serveSwiftResource(project, pathname.slice(RESOURCE_PATH.length));
1580
+ if (!served) continue;
1581
+ res.setHeader("access-control-allow-origin", "*");
1582
+ res.setHeader("content-type", served.contentType);
1583
+ res.end(served.bytes);
1584
+ return true;
1585
+ }
1586
+ res.statusCode = 404;
1587
+ res.end("swift resource is stale");
1588
+ return true;
1589
+ }
1590
+ if (pathname.startsWith(ICU_PATH)) {
1591
+ for (const project of projects.values()) {
1592
+ const icu = project.artifact?.icu;
1593
+ if (!icu || "url" in icu || pathname !== `${ICU_PATH}${icu.sha256}`) continue;
1594
+ res.setHeader("access-control-allow-origin", "*");
1595
+ res.setHeader("content-type", "application/gzip");
1596
+ res.setHeader("cache-control", "public, max-age=31536000, immutable");
1597
+ res.end(icu.gzip);
1598
+ return true;
1599
+ }
1600
+ res.statusCode = 404;
1601
+ res.end("swift icu data is stale");
1602
+ return true;
1603
+ }
1604
+ if (!pathname.startsWith(ARTIFACT_PATH)) return false;
1605
+ for (const project of projects.values()) {
1606
+ const artifact = project.artifact;
1607
+ if (!artifact || pathname !== `${ARTIFACT_PATH}${artifact.hash}.wasm`) continue;
1608
+ res.setHeader("access-control-allow-origin", "*");
1609
+ res.setHeader("content-type", "application/wasm");
1610
+ res.end(artifact.bytes);
1611
+ return true;
1612
+ }
1613
+ res.statusCode = 404;
1614
+ res.end("swift artifact is stale");
1615
+ return true;
1616
+ }
1617
+ function packageFor(file) {
1618
+ let dir = import_node_path5.default.dirname(file);
1619
+ const stop = devServer?.config.root;
1620
+ for (; ; ) {
1621
+ if (import_node_fs4.default.existsSync(import_node_path5.default.join(dir, "Package.swift"))) return dir;
1622
+ if (stop !== void 0 && dir === stop || import_node_path5.default.dirname(dir) === dir) break;
1623
+ dir = import_node_path5.default.dirname(dir);
1624
+ }
1625
+ throw new Error(
1626
+ `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`
1627
+ );
1628
+ }
1629
+ function swiftPlugin(options = {}) {
1630
+ return {
1631
+ name: "rnx-swift",
1632
+ enforce: "pre",
1633
+ async resolveId(source, importer) {
1634
+ if (source.startsWith(VIRTUAL_PREFIX)) return source;
1635
+ if (source.startsWith("/@id/")) {
1636
+ const decoded = source.slice("/@id/".length).replace(/^__x00__/, "\0");
1637
+ return decoded.startsWith(VIRTUAL_PREFIX) ? decoded : null;
1638
+ }
1639
+ if (!source.endsWith(".swift")) return null;
1640
+ const resolved = await this.resolve(source, importer, { skipSelf: true });
1641
+ if (!resolved) throw new Error(`cannot resolve swift source ${source}`);
1642
+ let id = resolved.id;
1643
+ if (importer && (source.startsWith("./") || source.startsWith("../"))) {
1644
+ const raw2 = import_node_path5.default.resolve(import_node_path5.default.dirname(importer.split("?")[0] ?? importer), source);
1645
+ try {
1646
+ if (import_node_fs4.default.realpathSync(raw2) === import_node_fs4.default.realpathSync(resolved.id)) id = raw2;
1647
+ } catch {
1648
+ }
1649
+ }
1650
+ return VIRTUAL_PREFIX + id;
1651
+ },
1652
+ async load(id) {
1653
+ if (!id.startsWith(VIRTUAL_PREFIX)) return null;
1654
+ const file = id.slice(VIRTUAL_PREFIX.length);
1655
+ if (!import_node_fs4.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1656
+ const packagePath = options.packagePath ?? packageFor(file);
1657
+ const project = projectFor(packagePath);
1658
+ const entry = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1659
+ for (const source of [
1660
+ ...sourceFiles(packagePath),
1661
+ ...nativeSourceFiles(packagePath)
1662
+ ])
1663
+ this.addWatchFile(source);
1664
+ this.addWatchFile(import_node_path5.default.join(packagePath, "Package.swift"));
1665
+ for (const root of linkedModuleRoots(packagePath)) {
1666
+ for (const source of sourceFiles(root)) this.addWatchFile(source);
1667
+ }
1668
+ let artifact;
1669
+ try {
1670
+ artifact = await artifactFor(project, entry);
1671
+ } catch (error) {
1672
+ if (!project.artifact) throw error;
1673
+ this.warn(error instanceof Error ? error.message : String(error));
1674
+ artifact = project.artifact;
1675
+ }
1676
+ let resources = {};
1677
+ let colors = {};
1678
+ let symbols = {};
1679
+ try {
1680
+ const built = buildSwiftResourceTable(project, serverOrigin());
1681
+ resources = built.table;
1682
+ colors = built.colors;
1683
+ symbols = built.symbols;
1684
+ for (const watched of built.watched) {
1685
+ this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split(import_node_path5.default.posix.sep)));
1686
+ }
1687
+ } catch (error) {
1688
+ this.warn(
1689
+ `swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
1690
+ );
1691
+ }
1692
+ let bundleFiles = {};
1693
+ try {
1694
+ const packed = buildSwiftBundleFiles(packagePath);
1695
+ bundleFiles = packed.files;
1696
+ for (const watched of packed.watched) {
1697
+ this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split(import_node_path5.default.posix.sep)));
1698
+ }
1699
+ if (packed.skipped.length > 0) {
1700
+ this.warn(
1701
+ `swift bundle files skipped for ${packagePath}: ${packed.skipped.join("; ")}`
1702
+ );
1703
+ }
1704
+ } catch (error) {
1705
+ this.warn(
1706
+ `swift bundle files for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
1707
+ );
1708
+ }
1709
+ const native = nativeSourceDescriptor(packagePath);
1710
+ return swiftEntryModuleEsm({
1711
+ url: artifactUrl(project, artifact),
1712
+ hash: artifact.hash,
1713
+ icuUrl: icuUrl(serverOrigin(), artifact),
1714
+ rootId: entryRootId(packagePath, file),
1715
+ resources,
1716
+ bundleFiles,
1717
+ colors,
1718
+ symbols,
1719
+ native: native ? swiftNativeEntry(native, entry) : void 0
1720
+ });
1721
+ },
1722
+ configureServer(server) {
1723
+ devServer = server;
1724
+ server.middlewares.use((req, res, next) => {
1725
+ if (!serveSwiftPackageAsset((req.url ?? "").split("?")[0], res)) next();
1726
+ });
1727
+ }
1728
+ };
1729
+ }
1730
+ // Annotate the CommonJS export names for ESM import in node:
1731
+ 0 && (module.exports = {
1732
+ buildSwiftBundleFiles,
1733
+ buildSwiftResourceTable,
1734
+ compileInput,
1735
+ entryRootId,
1736
+ linkedModuleRoots,
1737
+ serveSwiftPackageAsset,
1738
+ serveSwiftResource,
1739
+ sourceStamp,
1740
+ swiftPackageMetadata,
1741
+ swiftPackageWatchFiles,
1742
+ swiftPlugin
1743
+ });