peachsim 0.1.638 → 0.1.640

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 (81) hide show
  1. package/cli/command-registry.ts +1 -0
  2. package/cli/commands/control.ts +75 -6
  3. package/cli/commands/swift.ts +326 -0
  4. package/cli/main.ts +11 -0
  5. package/cli/outbound-endpoints.ts +5 -1
  6. package/dist-lib/agent-daemon-client.cjs +1 -1
  7. package/dist-lib/agent-events.cjs +1 -1
  8. package/dist-lib/agent-identity.cjs +1 -1
  9. package/dist-lib/agent-sessions.cjs +1 -1
  10. package/dist-lib/attached-projects.cjs +1 -1
  11. package/dist-lib/auth/shared-session.cjs +1 -1
  12. package/dist-lib/backend-origin.cjs +1 -1
  13. package/dist-lib/beta.cjs +1 -1
  14. package/dist-lib/beta.mjs +1 -1
  15. package/dist-lib/bridge-constants.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.cjs +1 -1
  17. package/dist-lib/bridge-contract-input.mjs +1 -1
  18. package/dist-lib/bridge-contract.cjs +1 -1
  19. package/dist-lib/bridge-contract.mjs +1 -1
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cli-constants.cjs +1 -1
  23. package/dist-lib/cloud-contract.cjs +1 -1
  24. package/dist-lib/cloud-contract.mjs +1 -1
  25. package/dist-lib/cloud-test-args.cjs +1 -1
  26. package/dist-lib/cloud-test-args.mjs +1 -1
  27. package/dist-lib/cloud.cjs +1 -1
  28. package/dist-lib/cloud.mjs +1 -1
  29. package/dist-lib/config.cjs +1 -1
  30. package/dist-lib/detox/index.cjs +1 -1
  31. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  32. package/dist-lib/home-paths.cjs +1 -1
  33. package/dist-lib/host/bridge-host.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  35. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  36. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  37. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  38. package/dist-lib/host/websocket-proxy.cjs +1 -1
  39. package/dist-lib/index.cjs +13 -4
  40. package/dist-lib/jump-to-source-babel.cjs +1 -1
  41. package/dist-lib/jump-to-source-native.cjs +1 -1
  42. package/dist-lib/kotlin-host.cjs +1 -1
  43. package/dist-lib/menu.cjs +1 -1
  44. package/dist-lib/menu.mjs +1 -1
  45. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  46. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  47. package/dist-lib/metro-production-bundle.cjs +1 -1
  48. package/dist-lib/metro-production-bundle.mjs +1 -1
  49. package/dist-lib/metro.cjs +1 -1
  50. package/dist-lib/native-app-identity.cjs +1 -1
  51. package/dist-lib/native-app-identity.mjs +1 -1
  52. package/dist-lib/profiles.cjs +1 -1
  53. package/dist-lib/public-brand.cjs +1 -1
  54. package/dist-lib/react-native-host-modules.cjs +1 -1
  55. package/dist-lib/react-native-host-modules.mjs +1 -1
  56. package/dist-lib/render-mode.cjs +1 -1
  57. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  58. package/dist-lib/sdk.cjs +1 -1
  59. package/dist-lib/sdk.mjs +1 -1
  60. package/dist-lib/skills.cjs +389 -232
  61. package/dist-lib/swift-compile.cjs +1 -1
  62. package/dist-lib/swift-compile.mjs +1 -1
  63. package/dist-lib/swift-package.cjs +1 -1
  64. package/dist-lib/swift-package.mjs +1 -1
  65. package/dist-lib/swift-submit.cjs +89 -8
  66. package/dist-lib/swift-submit.mjs +78 -8
  67. package/dist-lib/swift.cjs +16 -33308
  68. package/dist-lib/vite-swift.cjs +422 -247
  69. package/dist-lib/vite.cjs +13 -4
  70. package/package.json +1 -1
  71. package/skills/contrast/SKILL.md +1 -0
  72. package/src/assetCatalogContract.ts +16 -0
  73. package/src/dot-env.ts +148 -0
  74. package/src/metro-runtime-source.ts +15 -0
  75. package/src/swift-guest.ts +51 -0
  76. package/src/swift-host.ts +900 -0
  77. package/src/swift-submit.ts +97 -6
  78. package/src/swift.ts +36 -8
  79. package/src/swiftContract.ts +52 -0
  80. package/src/vite-plugin-one.ts +4 -3
  81. package/src/vite-plugin-swift.ts +43 -755
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.638 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.640 | (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;
@@ -45,6 +45,31 @@ __export(vite_plugin_swift_exports, {
45
45
  swiftPlugin: () => swiftPlugin
46
46
  });
47
47
  module.exports = __toCommonJS(vite_plugin_swift_exports);
48
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
49
+ var import_node_path6 = __toESM(require("node:path"), 1);
50
+
51
+ // ../sootsim-swift/src/entryModule.ts
52
+ function nativeExportLines(args, commonjs) {
53
+ const native = args.native;
54
+ if (!native) return [];
55
+ return Object.entries(native.modules).map(([name, methods]) => {
56
+ const calls = methods.map(
57
+ (method) => `${method}: (...args) => callSwiftModule(options, ${JSON.stringify(`${native.packageId}.${name}`)}, ${JSON.stringify(method)}, args, ${JSON.stringify(native.contractHash)})`
58
+ ).join(", ");
59
+ return commonjs ? `module.exports.${name} = Object.freeze({ ${calls} })` : `export const ${name} = Object.freeze({ ${calls} })`;
60
+ });
61
+ }
62
+ function swiftEntryModuleEsm(args) {
63
+ return [
64
+ `import { createSwiftEntry, callSwiftModule } from 'peachsim/swift'`,
65
+ `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 })}`,
66
+ ...!args.native || args.native.defaultView ? ["export default createSwiftEntry(options)"] : [],
67
+ ...nativeExportLines(args, false),
68
+ ""
69
+ ].join("\n");
70
+ }
71
+
72
+ // src/swift-host.ts
48
73
  var import_node_crypto3 = require("node:crypto");
49
74
  var import_node_fs4 = __toESM(require("node:fs"), 1);
50
75
  var import_node_os3 = __toESM(require("node:os"), 1);
@@ -82,8 +107,8 @@ function pickImagesetFile(images) {
82
107
  scale: pick.scale === "2x" ? 2 : pick.scale === "3x" ? 3 : 1
83
108
  };
84
109
  }
85
- function flatScale(path5) {
86
- const match = /@([23])x\.[^./]+$/.exec(path5);
110
+ function flatScale(path6) {
111
+ const match = /@([23])x\.[^./]+$/.exec(path6);
87
112
  return match ? Number(match[1]) : 1;
88
113
  }
89
114
  function providesNamespace(text) {
@@ -110,13 +135,13 @@ function catalogEntryName(contentsPath, kind, readText) {
110
135
  }
111
136
  return segments.join("/");
112
137
  }
113
- function isFlatResourceImage(path5) {
114
- return path5.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path5));
138
+ function isFlatResourceImage(path6) {
139
+ return path6.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path6));
115
140
  }
116
141
  function enumerateBundleImages(paths, readText) {
117
142
  const table = [];
118
143
  const seen = /* @__PURE__ */ new Set();
119
- const catalogs = paths.filter((path5) => baseOf(path5) === "Contents.json");
144
+ const catalogs = paths.filter((path6) => baseOf(path6) === "Contents.json");
120
145
  for (const contentsPath of catalogs) {
121
146
  const name = catalogEntryName(contentsPath, ".imageset", readText);
122
147
  if (name === null || seen.has(name)) continue;
@@ -137,12 +162,12 @@ function enumerateBundleImages(paths, readText) {
137
162
  seen.add(name);
138
163
  table.push({ name, file: `${dir}/${pick.filename}`, scale: pick.scale });
139
164
  }
140
- for (const path5 of paths) {
141
- if (!isFlatResourceImage(path5)) continue;
142
- const name = baseOf(path5).slice(0, -extOf(path5).length);
165
+ for (const path6 of paths) {
166
+ if (!isFlatResourceImage(path6)) continue;
167
+ const name = baseOf(path6).slice(0, -extOf(path6).length);
143
168
  if (name === "" || seen.has(name)) continue;
144
169
  seen.add(name);
145
- table.push({ name, file: path5, scale: flatScale(path5) });
170
+ table.push({ name, file: path6, scale: flatScale(path6) });
146
171
  }
147
172
  return table;
148
173
  }
@@ -171,7 +196,7 @@ function colorsetValue(color) {
171
196
  }
172
197
  function enumerateBundleColors(paths, readText) {
173
198
  const table = {};
174
- for (const contentsPath of paths.filter((path5) => baseOf(path5) === "Contents.json")) {
199
+ for (const contentsPath of paths.filter((path6) => baseOf(path6) === "Contents.json")) {
175
200
  const name = catalogEntryName(contentsPath, ".colorset", readText);
176
201
  if (name === null || name in table) continue;
177
202
  let colors;
@@ -236,8 +261,8 @@ function axisExtremes(points) {
236
261
  function pathBounds(d) {
237
262
  const xs = [];
238
263
  const ys = [];
239
- for (const path5 of d)
240
- (0, import_svgpath.default)(path5).abs().unshort().unarc().iterate((segment, _index, x, y) => {
264
+ for (const path6 of d)
265
+ (0, import_svgpath.default)(path6).abs().unshort().unarc().iterate((segment, _index, x, y) => {
241
266
  const [command, ...args] = segment;
242
267
  if (command === "M" || command === "L") {
243
268
  xs.push(Number(args[0]));
@@ -264,7 +289,7 @@ function guide(svg, id, axis) {
264
289
  }
265
290
  function enumerateBundleSymbols(paths, readText) {
266
291
  const table = {};
267
- for (const contentsPath of paths.filter((path5) => baseOf(path5) === "Contents.json")) {
292
+ for (const contentsPath of paths.filter((path6) => baseOf(path6) === "Contents.json")) {
268
293
  const name = catalogEntryName(contentsPath, ".symbolset", readText);
269
294
  if (name === null || name in table) continue;
270
295
  let symbols;
@@ -297,8 +322,8 @@ function enumerateBundleSymbols(paths, readText) {
297
322
  viewBox: "0 0 24 24",
298
323
  advance,
299
324
  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()
325
+ layers: d.map((path6) => ({
326
+ d: (0, import_svgpath.default)(path6).abs().scale(scale).translate(translateX, translateY).round(3).toString()
302
327
  }))
303
328
  };
304
329
  }
@@ -308,7 +333,7 @@ function enumerateBundleJson(paths) {
308
333
  const found = [];
309
334
  const seen = /* @__PURE__ */ new Set();
310
335
  const ordered = [...paths].filter(
311
- (path5) => path5.split("/").includes("Resources") && path5.split("/").every((segment) => segment !== "" && segment !== "..") && (extOf(path5) === ".json" || extOf(path5) === ".xcstrings")
336
+ (path6) => path6.split("/").includes("Resources") && path6.split("/").every((segment) => segment !== "" && segment !== "..") && (extOf(path6) === ".json" || extOf(path6) === ".xcstrings")
312
337
  ).sort((a, b) => {
313
338
  const aDepth = a.split("/").length;
314
339
  const bDepth = b.split("/").length;
@@ -323,27 +348,6 @@ function enumerateBundleJson(paths) {
323
348
  return found;
324
349
  }
325
350
 
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
351
  // src/auth/shared-session.ts
348
352
  var import_node_fs = require("node:fs");
349
353
  var import_node_path2 = require("node:path");
@@ -1155,7 +1159,15 @@ function compileSwiftProjectUnit(input, options) {
1155
1159
 
1156
1160
  // src/swift-submit.ts
1157
1161
  var SWIFT_COMPILE_PATH = "/api/swift-compile";
1158
- var SWIFT_HOST_ABI = 1;
1162
+ var MAX_SWIFT_COMPILE_FILES = 256;
1163
+ var MAX_SWIFT_COMPILE_SOURCE_BYTES = 2 * 1024 * 1024;
1164
+ var MAX_SWIFT_COMPILE_RESOURCES = 128;
1165
+ var MAX_SWIFT_COMPILE_RESOURCE_BYTES = 2 * 1024 * 1024;
1166
+ var MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES = 8 * 1024 * 1024;
1167
+ var MAX_SWIFT_COMPILE_IMAGES = 128;
1168
+ var MAX_SWIFT_COMPILE_IMAGE_BYTES = 4 * 1024 * 1024;
1169
+ var MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024;
1170
+ var SWIFT_HOST_ABI = 2;
1159
1171
  var POLL_INTERVAL_MS = 1500;
1160
1172
  function swiftNativeEntry(native, file) {
1161
1173
  return {
@@ -1176,6 +1188,7 @@ function httpSwiftCompileSubmit(context) {
1176
1188
  try {
1177
1189
  response = await fetch(url, {
1178
1190
  method: "POST",
1191
+ signal: context.signal,
1179
1192
  headers: {
1180
1193
  "content-type": "application/json",
1181
1194
  ...authorization ? { authorization } : {}
@@ -1198,30 +1211,78 @@ function httpSwiftCompileSubmit(context) {
1198
1211
  `contrast-bundler: the swift compile for ${work.entry} answered ${response.status}${detail ? `: ${detail}` : ""}`
1199
1212
  );
1200
1213
  }
1201
- return await response.json();
1214
+ const outcome = await response.json();
1215
+ if (!isSwiftCompileOutcome(outcome)) {
1216
+ throw new Error("swift compile: the service returned invalid artifact metadata");
1217
+ }
1218
+ return outcome;
1202
1219
  };
1203
1220
  }
1221
+ function isRecord(value) {
1222
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1223
+ }
1224
+ function isSwiftNativeModules(value) {
1225
+ return isRecord(value) && typeof value.packageId === "string" && typeof value.hash === "string" && isRecord(value.files) && isRecord(value.modules) && [...Object.values(value.files), ...Object.values(value.modules)].every(
1226
+ (names) => Array.isArray(names) && names.every((name) => typeof name === "string")
1227
+ ) && Array.isArray(value.defaultViewFiles) && value.defaultViewFiles.every((file) => typeof file === "string");
1228
+ }
1229
+ function isSwiftCompileOutcome(value) {
1230
+ if (!isRecord(value)) return false;
1231
+ switch (value.status) {
1232
+ case "published":
1233
+ return typeof value.artifactUrl === "string" && typeof value.icuUrl === "string" && typeof value.hash === "string" && /^[a-f0-9]{64}$/.test(value.hash) && (value.imageUrls === void 0 || isRecord(value.imageUrls) && Object.values(value.imageUrls).every((url) => typeof url === "string")) && (value.native === void 0 || isSwiftNativeModules(value.native));
1234
+ case "compiling":
1235
+ return typeof value.hash === "string" && /^[a-f0-9]{64}$/.test(value.hash);
1236
+ case "refused":
1237
+ case "failed":
1238
+ return typeof value.detail === "string";
1239
+ default:
1240
+ return false;
1241
+ }
1242
+ }
1243
+ function compileAssetUrl(value, origin) {
1244
+ const url = new URL(value, origin);
1245
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password || url.origin !== new URL(origin).origin) {
1246
+ throw new Error("swift compile: artifact address escapes the compile origin");
1247
+ }
1248
+ return url.href;
1249
+ }
1204
1250
  function resolveImageUrls(urls, origin) {
1205
1251
  const resolved = {};
1206
1252
  for (const [name, url] of Object.entries(urls)) {
1207
- resolved[name] = new URL(url, origin).href;
1253
+ resolved[name] = compileAssetUrl(url, origin);
1208
1254
  }
1209
1255
  return resolved;
1210
1256
  }
1211
- async function compileSwiftProject(submit, work, origin) {
1257
+ async function compileSwiftProject(submit, work, origin, signal) {
1212
1258
  while (true) {
1259
+ signal?.throwIfAborted();
1213
1260
  const outcome = await submit(work);
1214
1261
  switch (outcome.status) {
1215
1262
  case "published":
1216
1263
  return {
1217
- url: new URL(outcome.artifactUrl, origin).href,
1264
+ url: compileAssetUrl(outcome.artifactUrl, origin),
1218
1265
  hash: outcome.hash,
1219
- icuUrl: new URL(outcome.icuUrl, origin).href,
1266
+ icuUrl: compileAssetUrl(outcome.icuUrl, origin),
1220
1267
  ...outcome.imageUrls === void 0 ? {} : { imageUrls: resolveImageUrls(outcome.imageUrls, origin) },
1221
1268
  ...outcome.native === void 0 ? {} : { native: outcome.native }
1222
1269
  };
1223
1270
  case "compiling":
1224
- await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL_MS));
1271
+ await new Promise((resolve2, reject) => {
1272
+ const finish = () => {
1273
+ signal?.removeEventListener("abort", abort);
1274
+ resolve2();
1275
+ };
1276
+ const timer = setTimeout(finish, POLL_INTERVAL_MS);
1277
+ const abort = () => {
1278
+ clearTimeout(timer);
1279
+ signal?.removeEventListener("abort", abort);
1280
+ const reason = signal?.reason;
1281
+ reject(reason instanceof Error ? reason : new Error(String(reason)));
1282
+ };
1283
+ signal?.addEventListener("abort", abort, { once: true });
1284
+ if (signal?.aborted) abort();
1285
+ });
1225
1286
  continue;
1226
1287
  case "refused":
1227
1288
  case "failed":
@@ -1232,11 +1293,10 @@ async function compileSwiftProject(submit, work, origin) {
1232
1293
  }
1233
1294
  }
1234
1295
 
1235
- // src/vite-plugin-swift.ts
1296
+ // src/swift-host.ts
1236
1297
  var ARTIFACT_PATH = "/__peach/swift/";
1237
1298
  var RESOURCE_PATH = "/__peach/swift-res/";
1238
1299
  var ICU_PATH = "/__peach/swift-icu/";
1239
- var VIRTUAL_PREFIX = "\0peach-swift:";
1240
1300
  var SKIP_SOURCE_DIRS = /* @__PURE__ */ new Set([
1241
1301
  "node_modules",
1242
1302
  "ios",
@@ -1251,89 +1311,124 @@ function swiftPackageId(packagePath) {
1251
1311
  return import_node_path5.default.basename(packagePath).replace(/[^A-Za-z0-9_]/g, "_").replace(/^(\d)/, "_$1");
1252
1312
  }
1253
1313
  function nativeSourceDescriptor(packagePath) {
1314
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1254
1315
  const file = import_node_path5.default.join(packagePath, NATIVE_DESCRIPTOR);
1255
1316
  if (!import_node_fs4.default.existsSync(file)) return null;
1256
- const value = JSON.parse(import_node_fs4.default.readFileSync(file, "utf8"));
1317
+ const value = JSON.parse(
1318
+ readPackageFile(packagePath, file, MAX_SWIFT_COMPILE_SOURCE_BYTES).toString("utf8")
1319
+ );
1257
1320
  const invalid = () => new Error(`${file}: invalid One native source descriptor; run one typegen`);
1258
1321
  if (!value || typeof value !== "object" || Reflect.get(value, "generatedBy") !== "one typegen" || !import_node_fs4.default.existsSync(import_node_path5.default.join(packagePath, NATIVE_GLUE))) {
1259
1322
  throw invalid();
1260
1323
  }
1261
- const packageId = Reflect.get(value, "packageId");
1262
- const hash = Reflect.get(value, "hash");
1263
- const rawFiles = Reflect.get(value, "files");
1264
- const rawModules = Reflect.get(value, "modules");
1265
- const rawDefaults = Reflect.get(value, "defaultViewFiles");
1266
- if (typeof packageId !== "string" || typeof hash !== "string" || !rawFiles || typeof rawFiles !== "object" || !rawModules || typeof rawModules !== "object" || !Array.isArray(rawDefaults) || !rawDefaults.every((name) => typeof name === "string")) {
1324
+ if (!isSwiftNativeModules(value) || value.packageId !== swiftPackageId(packagePath)) {
1267
1325
  throw invalid();
1268
1326
  }
1269
- const files = {};
1270
- const modules = {};
1271
- for (const [key, names] of Object.entries(rawFiles)) {
1272
- if (!Array.isArray(names) || !names.every((name) => typeof name === "string"))
1273
- throw invalid();
1274
- files[key] = names;
1275
- }
1276
- for (const [key, names] of Object.entries(rawModules)) {
1277
- if (!Array.isArray(names) || !names.every((name) => typeof name === "string"))
1278
- throw invalid();
1279
- modules[key] = names;
1280
- }
1281
- if (packageId !== swiftPackageId(packagePath)) {
1282
- throw new Error(`${file}: invalid One native source descriptor; run one typegen`);
1283
- }
1284
- return { packageId, hash, files, modules, defaultViewFiles: rawDefaults };
1327
+ return value;
1285
1328
  }
1286
1329
  function nativeSourceFiles(packagePath) {
1330
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1287
1331
  return [NATIVE_DESCRIPTOR, NATIVE_GLUE].map((name) => import_node_path5.default.join(packagePath, name)).filter((file) => import_node_fs4.default.existsSync(file));
1288
1332
  }
1289
1333
  var projects = /* @__PURE__ */ new Map();
1290
- var devServer;
1291
- function projectFor(packagePath) {
1334
+ function projectFor(packagePath, hosted) {
1335
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1292
1336
  const existing = projects.get(packagePath);
1293
- if (existing) return existing;
1337
+ if (existing) {
1338
+ if (hosted && !existing.hosted) {
1339
+ throw new Error("a Swift package cannot share local and hosted compile ownership");
1340
+ }
1341
+ return existing;
1342
+ }
1294
1343
  const created = {
1295
1344
  packagePath,
1296
1345
  artifact: null,
1297
1346
  building: null,
1298
- resourceFiles: /* @__PURE__ */ new Map()
1347
+ resourceFiles: /* @__PURE__ */ new Map(),
1348
+ resourcesPrevious: /* @__PURE__ */ new Map(),
1349
+ artifacts: [],
1350
+ hosted
1299
1351
  };
1300
1352
  projects.set(packagePath, created);
1301
1353
  return created;
1302
1354
  }
1303
- function sourceFiles(root) {
1355
+ function packageFiles(root) {
1356
+ const canonicalRoot = import_node_fs4.default.realpathSync(root);
1304
1357
  const found = [];
1305
1358
  const walk = (dir) => {
1306
- for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
1307
- if (entry.name.startsWith(".") || SKIP_SOURCE_DIRS.has(entry.name)) continue;
1308
- const full = import_node_path5.default.join(dir, entry.name);
1309
- if (entry.isDirectory()) {
1310
- if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
1311
- walk(full);
1312
- } else if (entry.name.endsWith(".swift") && (entry.isFile() || entry.isSymbolicLink() && import_node_fs4.default.statSync(full).isFile()))
1313
- found.push(full);
1314
- }
1315
- };
1316
- walk(root);
1317
- return found;
1318
- }
1319
- function resourceCandidateFiles(root) {
1320
- const found = [];
1321
- const walk = (dir) => {
1322
- for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true })) {
1323
- if (entry.name.startsWith(".") || SKIP_SOURCE_DIRS.has(entry.name) || entry.name === ".build")
1359
+ for (const entry of import_node_fs4.default.readdirSync(dir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
1360
+ if (entry.name.startsWith(".") && entry.name !== NATIVE_DESCRIPTOR && entry.name !== NATIVE_GLUE || SKIP_SOURCE_DIRS.has(entry.name))
1324
1361
  continue;
1325
1362
  const full = import_node_path5.default.join(dir, entry.name);
1363
+ if (entry.isSymbolicLink()) throw new Error(`swift input is a symlink: ${full}`);
1364
+ const real = import_node_fs4.default.realpathSync(full);
1365
+ const relative = import_node_path5.default.relative(canonicalRoot, real);
1366
+ if (relative.startsWith(`..${import_node_path5.default.sep}`) || relative === ".." || import_node_path5.default.isAbsolute(relative)) {
1367
+ throw new Error(`swift input escapes the selected package: ${full}`);
1368
+ }
1326
1369
  if (entry.isDirectory()) {
1327
- if (full !== root && import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
1370
+ if (import_node_fs4.default.existsSync(import_node_path5.default.join(full, "Package.swift"))) continue;
1328
1371
  walk(full);
1329
- } else {
1372
+ } else if (entry.isFile()) {
1330
1373
  found.push(full);
1374
+ if (found.length > 4096) throw new Error("swift package exceeds 4096 input files");
1331
1375
  }
1332
1376
  }
1333
1377
  };
1334
- walk(root);
1378
+ walk(canonicalRoot);
1335
1379
  return found;
1336
1380
  }
1381
+ function readPackageFile(root, file, maxBytes) {
1382
+ const canonicalRoot = import_node_fs4.default.realpathSync(root);
1383
+ const relative = import_node_path5.default.relative(canonicalRoot, import_node_fs4.default.realpathSync(file));
1384
+ if (relative === ".." || relative.startsWith(`..${import_node_path5.default.sep}`) || import_node_path5.default.isAbsolute(relative)) {
1385
+ throw new Error(`swift input escapes the selected package: ${file}`);
1386
+ }
1387
+ let ancestor = import_node_path5.default.resolve(file);
1388
+ while (ancestor !== canonicalRoot) {
1389
+ if (import_node_fs4.default.lstatSync(ancestor).isSymbolicLink()) {
1390
+ throw new Error(`swift input is a symlink: ${ancestor}`);
1391
+ }
1392
+ const parent = import_node_path5.default.dirname(ancestor);
1393
+ if (parent === ancestor)
1394
+ throw new Error(`swift input escapes the selected package: ${file}`);
1395
+ ancestor = parent;
1396
+ }
1397
+ const expected = import_node_fs4.default.statSync(file);
1398
+ const fd = import_node_fs4.default.openSync(file, import_node_fs4.default.constants.O_RDONLY | import_node_fs4.default.constants.O_NOFOLLOW);
1399
+ try {
1400
+ const stat = import_node_fs4.default.fstatSync(fd);
1401
+ if (stat.dev !== expected.dev || stat.ino !== expected.ino) {
1402
+ throw new Error(`swift input changed while opening: ${file}`);
1403
+ }
1404
+ if (!stat.isFile() || stat.size > maxBytes) {
1405
+ throw new Error(`swift input exceeds ${maxBytes} bytes: ${file}`);
1406
+ }
1407
+ const bytes = Buffer.alloc(stat.size + 1);
1408
+ let offset = 0;
1409
+ while (offset < bytes.length) {
1410
+ const count = import_node_fs4.default.readSync(fd, bytes, offset, bytes.length - offset, null);
1411
+ if (count === 0) break;
1412
+ offset += count;
1413
+ }
1414
+ if (offset !== stat.size || import_node_fs4.default.fstatSync(fd).size !== stat.size) {
1415
+ throw new Error(`swift input changed while reading: ${file}`);
1416
+ }
1417
+ return bytes.subarray(0, offset);
1418
+ } finally {
1419
+ import_node_fs4.default.closeSync(fd);
1420
+ }
1421
+ }
1422
+ function sourceFiles(root) {
1423
+ const files = packageFiles(root).filter((file) => file.endsWith(".swift"));
1424
+ if (files.length > MAX_SWIFT_COMPILE_FILES || files.reduce((total, file) => total + import_node_fs4.default.statSync(file).size, 0) > MAX_SWIFT_COMPILE_SOURCE_BYTES) {
1425
+ throw new Error("swift sources exceed the compile service caps");
1426
+ }
1427
+ return files;
1428
+ }
1429
+ function resourceCandidateFiles(root) {
1430
+ return packageFiles(root).filter((file) => !file.endsWith(".swift"));
1431
+ }
1337
1432
  var RESOURCE_CONTENT_TYPES = {
1338
1433
  ".avif": "image/avif",
1339
1434
  ".bmp": "image/bmp",
@@ -1354,11 +1449,11 @@ function buildSwiftResourceTable(project, baseUrl) {
1354
1449
  const file = byRelative.get(candidate);
1355
1450
  if (!file || !/(^|\/)Contents\.json$|\.symbolset\/[^/]+\.svg$/.test(candidate))
1356
1451
  return null;
1357
- try {
1358
- return import_node_fs4.default.readFileSync(file, "utf8");
1359
- } catch {
1360
- return null;
1361
- }
1452
+ return readPackageFile(
1453
+ project.packagePath,
1454
+ file,
1455
+ MAX_SWIFT_COMPILE_RESOURCE_BYTES
1456
+ ).toString("utf8");
1362
1457
  };
1363
1458
  const found = enumerateBundleImages(relative, readContents);
1364
1459
  const colors = enumerateBundleColors(relative, readContents);
@@ -1367,15 +1462,19 @@ function buildSwiftResourceTable(project, baseUrl) {
1367
1462
  const images = {};
1368
1463
  const watched = [];
1369
1464
  const registered = /* @__PURE__ */ new Map();
1465
+ let imageBytes = 0;
1370
1466
  for (const { name, file, scale } of found) {
1371
1467
  const disk = byRelative.get(file);
1372
1468
  if (!disk) continue;
1373
- let bytes;
1374
- try {
1375
- bytes = import_node_fs4.default.readFileSync(disk);
1376
- } catch {
1377
- continue;
1469
+ if (Object.keys(images).length >= MAX_SWIFT_COMPILE_IMAGES || imageBytes + import_node_fs4.default.statSync(disk).size > MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES) {
1470
+ throw new Error("swift image pack exceeds the compile service caps");
1378
1471
  }
1472
+ const bytes = readPackageFile(
1473
+ project.packagePath,
1474
+ disk,
1475
+ MAX_SWIFT_COMPILE_IMAGE_BYTES
1476
+ );
1477
+ imageBytes += bytes.length;
1379
1478
  const hash = (0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex").slice(0, 16);
1380
1479
  const ext = import_node_path5.default.posix.extname(file).toLowerCase();
1381
1480
  table[name] = { uri: `${baseUrl}${RESOURCE_PATH}${hash}${ext}`, scale };
@@ -1386,65 +1485,60 @@ function buildSwiftResourceTable(project, baseUrl) {
1386
1485
  watched.push(file);
1387
1486
  const contents = file.replace(/\/[^/]+$/, "/Contents.json");
1388
1487
  if (byRelative.has(contents)) watched.push(contents);
1389
- registered.set(`${hash}${ext}`, disk);
1488
+ registered.set(`${hash}${ext}`, {
1489
+ bytes,
1490
+ contentType: RESOURCE_CONTENT_TYPES[ext] ?? "application/octet-stream"
1491
+ });
1390
1492
  }
1391
1493
  for (const file of relative) {
1392
1494
  if (file.endsWith(".colorset/Contents.json") || file.includes(".symbolset/"))
1393
1495
  watched.push(file);
1394
1496
  }
1395
- project.resourceFiles = registered;
1497
+ if (registered.size !== project.resourceFiles.size || [...registered].some(
1498
+ ([key, value]) => !project.resourceFiles.get(key)?.bytes.equals(value.bytes)
1499
+ )) {
1500
+ project.resourcesPrevious = project.resourceFiles;
1501
+ project.resourceFiles = registered;
1502
+ }
1396
1503
  return { table, images, colors, symbols, watched };
1397
1504
  }
1398
- var BUNDLE_MAX_FILES = 128;
1399
- var BUNDLE_MAX_FILE_BYTES = 4 * 1024 * 1024;
1400
- var BUNDLE_MAX_TOTAL_BYTES = 8 * 1024 * 1024;
1505
+ var BUNDLE_MAX_FILES = MAX_SWIFT_COMPILE_RESOURCES;
1506
+ var BUNDLE_MAX_FILE_BYTES = MAX_SWIFT_COMPILE_RESOURCE_BYTES;
1507
+ var BUNDLE_MAX_TOTAL_BYTES = MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES;
1401
1508
  function buildSwiftBundleFiles(packagePath) {
1509
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1402
1510
  const relative = resourceCandidateFiles(packagePath).map(
1403
1511
  (file) => import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep)
1404
1512
  );
1405
1513
  const files = {};
1406
1514
  const watched = [];
1407
- const skipped = [];
1408
1515
  let total = 0;
1409
1516
  for (const { base, file } of enumerateBundleJson(relative)) {
1410
1517
  watched.push(file);
1411
- let bytes;
1412
- try {
1413
- bytes = import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, ...file.split("/")));
1414
- } catch {
1415
- continue;
1416
- }
1417
- if (bytes.length > BUNDLE_MAX_FILE_BYTES) {
1418
- skipped.push(`${file} (${bytes.length} bytes past the per-file cap)`);
1419
- continue;
1420
- }
1421
- if (Object.keys(files).length >= BUNDLE_MAX_FILES || total + bytes.length > BUNDLE_MAX_TOTAL_BYTES) {
1422
- skipped.push(
1423
- `${file} (pack caps: ${BUNDLE_MAX_FILES} files / ${BUNDLE_MAX_TOTAL_BYTES} bytes)`
1518
+ const disk = import_node_path5.default.join(packagePath, ...file.split("/"));
1519
+ if (Object.keys(files).length >= BUNDLE_MAX_FILES || total + import_node_fs4.default.statSync(disk).size > BUNDLE_MAX_TOTAL_BYTES) {
1520
+ throw new Error(
1521
+ `swift bundle pack exceeds ${BUNDLE_MAX_FILES} files / ${BUNDLE_MAX_TOTAL_BYTES} bytes`
1424
1522
  );
1425
- continue;
1426
1523
  }
1524
+ const bytes = readPackageFile(packagePath, disk, BUNDLE_MAX_FILE_BYTES);
1427
1525
  files[base] = bytes.toString("base64");
1428
1526
  total += bytes.length;
1429
1527
  }
1430
- return { files, watched, skipped };
1528
+ return { files, watched };
1431
1529
  }
1432
1530
  function serveSwiftResource(project, key) {
1433
- const disk = project.resourceFiles.get(key);
1434
- if (!disk) return null;
1435
- try {
1436
- return {
1437
- bytes: import_node_fs4.default.readFileSync(disk),
1438
- contentType: RESOURCE_CONTENT_TYPES[import_node_path5.default.extname(disk).toLowerCase()] ?? "application/octet-stream"
1439
- };
1440
- } catch {
1441
- return null;
1442
- }
1531
+ return project.resourceFiles.get(key) ?? project.resourcesPrevious.get(key) ?? null;
1443
1532
  }
1444
1533
  function linkedModuleRoots(packagePath) {
1534
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1445
1535
  let manifest;
1446
1536
  try {
1447
- manifest = import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, "Package.swift"), "utf8");
1537
+ manifest = readPackageFile(
1538
+ packagePath,
1539
+ import_node_path5.default.join(packagePath, "Package.swift"),
1540
+ MAX_SWIFT_COMPILE_SOURCE_BYTES
1541
+ ).toString("utf8");
1448
1542
  } catch {
1449
1543
  return [];
1450
1544
  }
@@ -1456,25 +1550,16 @@ function linkedModuleRoots(packagePath) {
1456
1550
  }
1457
1551
  return [...roots].sort();
1458
1552
  }
1459
- function sourceStamp(packagePath, moduleRootOverride) {
1553
+ function sourceStamp(packagePath, moduleRootOverride, hosted = false) {
1460
1554
  const hash = (0, import_node_crypto3.createHash)("sha256");
1461
1555
  hash.update(packagePath);
1462
- const projectInputs = /* @__PURE__ */ new Set([
1463
- import_node_path5.default.join(packagePath, "Package.swift"),
1464
- ...sourceFiles(packagePath),
1465
- ...nativeSourceFiles(packagePath),
1466
- ...buildSwiftBundleFiles(packagePath).watched.map(
1467
- (file) => import_node_path5.default.join(packagePath, ...file.split(import_node_path5.default.posix.sep))
1468
- ),
1469
- ...buildSwiftResourceTable(projectFor(packagePath), "").watched.map(
1470
- (file) => import_node_path5.default.join(packagePath, ...file.split(import_node_path5.default.posix.sep))
1471
- )
1472
- ]);
1556
+ const projectInputs = new Set(packageFiles(packagePath));
1473
1557
  for (const file of [...projectInputs].sort()) {
1474
1558
  hash.update(import_node_path5.default.relative(packagePath, file));
1475
- hash.update(String(import_node_fs4.default.statSync(file).mtimeMs));
1559
+ const stat = import_node_fs4.default.statSync(file);
1560
+ hash.update(`${stat.mtimeMs}/${stat.ctimeMs}/${stat.size}`);
1476
1561
  }
1477
- const moduleRoot = moduleRootOverride ?? linkedModuleRoots(packagePath).find(
1562
+ const moduleRoot = hosted ? void 0 : moduleRootOverride ?? linkedModuleRoots(packagePath).find(
1478
1563
  (root) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, "toolchain.json"))
1479
1564
  );
1480
1565
  if (moduleRoot) {
@@ -1490,46 +1575,85 @@ function entryRootId(packagePath, file) {
1490
1575
  const relative = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1491
1576
  return `${import_node_path5.default.basename(packagePath)}${import_node_path5.default.posix.sep}${relative}`;
1492
1577
  }
1493
- function serverOrigin() {
1494
- const address = devServer?.httpServer?.address();
1495
- const port = address && typeof address === "object" ? address.port : devServer?.config.server.port;
1496
- if (!port) throw new Error("the swift urls need the dev server port");
1497
- return `http://localhost:${port}`;
1498
- }
1499
- function artifactUrl(project, artifact) {
1500
- return `${serverOrigin()}${ARTIFACT_PATH}${artifact.hash}.wasm`;
1501
- }
1502
1578
  function icuUrl(origin, artifact) {
1503
1579
  return "url" in artifact.icu ? artifact.icu.url : `${origin}${ICU_PATH}${artifact.icu.sha256}`;
1504
1580
  }
1505
1581
  function packageSources(packagePath) {
1582
+ const sources = sourceFiles(packagePath);
1583
+ if (sources.length > MAX_SWIFT_COMPILE_FILES)
1584
+ throw new Error("swift source file cap exceeded");
1585
+ let total = 0;
1586
+ for (const file of sources) total += import_node_fs4.default.statSync(file).size;
1587
+ if (total > MAX_SWIFT_COMPILE_SOURCE_BYTES)
1588
+ throw new Error("swift source byte cap exceeded");
1506
1589
  const files = {};
1507
- for (const file of sourceFiles(packagePath)) {
1590
+ for (const file of sources) {
1508
1591
  const relative = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1509
- files[relative] = import_node_fs4.default.readFileSync(file, "utf8");
1592
+ files[relative] = readPackageFile(
1593
+ packagePath,
1594
+ file,
1595
+ MAX_SWIFT_COMPILE_SOURCE_BYTES
1596
+ ).toString("utf8");
1510
1597
  }
1511
- const manifest = import_node_path5.default.join(packagePath, "Package.swift");
1512
- if (import_node_fs4.default.existsSync(manifest)) files["Package.swift"] = import_node_fs4.default.readFileSync(manifest, "utf8");
1513
1598
  return files;
1514
1599
  }
1515
- function compileInput(packagePath, entry) {
1600
+ function packageSnapshot(project) {
1601
+ const files = packageSources(project.packagePath);
1602
+ const native = nativeSourceDescriptor(project.packagePath);
1603
+ return {
1604
+ files,
1605
+ resources: buildSwiftResourceTable(project, ""),
1606
+ bundleFiles: buildSwiftBundleFiles(project.packagePath),
1607
+ native,
1608
+ ...native ? {
1609
+ glue: readPackageFile(
1610
+ project.packagePath,
1611
+ import_node_path5.default.join(project.packagePath, NATIVE_GLUE),
1612
+ MAX_SWIFT_COMPILE_SOURCE_BYTES
1613
+ ).toString("utf8")
1614
+ } : {}
1615
+ };
1616
+ }
1617
+ function snapshotCompileInput(snapshot, entry) {
1516
1618
  const input = {
1517
- files: packageSources(packagePath),
1518
- resources: buildSwiftBundleFiles(packagePath).files,
1519
- images: buildSwiftResourceTable(projectFor(packagePath), "").images,
1619
+ files: snapshot.files,
1620
+ resources: snapshot.bundleFiles.files,
1621
+ images: snapshot.resources.images,
1520
1622
  entry,
1521
1623
  oneModule: true
1522
1624
  };
1523
- return nativeSourceDescriptor(packagePath) ? withNativeSourceGlue(
1524
- input,
1525
- import_node_fs4.default.readFileSync(import_node_path5.default.join(packagePath, NATIVE_GLUE), "utf8")
1526
- ) : input;
1625
+ return snapshot.glue ? withNativeSourceGlue(input, snapshot.glue) : input;
1527
1626
  }
1528
- async function compile(packagePath, entry, stamp, moduleRootOverride) {
1627
+ function compileInput(packagePath, entry) {
1628
+ return snapshotCompileInput(packageSnapshot(projectFor(packagePath)), entry);
1629
+ }
1630
+ async function compile(packagePath, entry, stamp, moduleRootOverride, hosted, snapshot) {
1631
+ const resources = {
1632
+ table: snapshot.resources.table,
1633
+ colors: snapshot.resources.colors,
1634
+ symbols: snapshot.resources.symbols,
1635
+ watched: snapshot.resources.watched
1636
+ };
1637
+ const content = {
1638
+ resources,
1639
+ bundleFiles: snapshot.bundleFiles,
1640
+ native: snapshot.native
1641
+ };
1642
+ if (hosted)
1643
+ return {
1644
+ ...await compileRemote(packagePath, entry, snapshot, hosted),
1645
+ stamp,
1646
+ package: content
1647
+ };
1529
1648
  const moduleRoot = moduleRootOverride ?? linkedModuleRoots(packagePath).find(
1530
1649
  (root) => import_node_fs4.default.existsSync(import_node_path5.default.join(root, "toolchain.json"))
1531
1650
  );
1532
- if (!moduleRoot) return { ...await compileRemote(packagePath, entry), stamp };
1651
+ if (!moduleRoot)
1652
+ return {
1653
+ ...await compileRemote(packagePath, entry, snapshot),
1654
+ stamp,
1655
+ package: content
1656
+ };
1533
1657
  const toolchain = JSON.parse(
1534
1658
  import_node_fs4.default.readFileSync(import_node_path5.default.join(moduleRoot, "toolchain.json"), "utf8")
1535
1659
  );
@@ -1537,7 +1661,7 @@ async function compile(packagePath, entry, stamp, moduleRootOverride) {
1537
1661
  if (typeof release !== "string") {
1538
1662
  throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`);
1539
1663
  }
1540
- const artifact = await compileSwiftProjectUnit(compileInput(packagePath, entry), {
1664
+ const artifact = await compileSwiftProjectUnit(snapshotCompileInput(snapshot, entry), {
1541
1665
  // the compile rewrites Sources/App in its root before swiftpm builds, and
1542
1666
  // its lock orders one process only: two vites on one root (a lane's dev
1543
1667
  // server and a capture, or two checkouts) build each other's sources. so
@@ -1555,50 +1679,75 @@ async function compile(packagePath, entry, stamp, moduleRootOverride) {
1555
1679
  `Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`
1556
1680
  )
1557
1681
  });
1558
- return { ...artifact, stamp };
1682
+ return { ...artifact, stamp, package: content };
1559
1683
  }
1560
- async function compileRemote(packagePath, entry) {
1561
- const auth = resolveCliAuth();
1562
- if (!auth || auth.kind === "github") {
1684
+ async function compileRemote(packagePath, entry, snapshot, hosted) {
1685
+ const auth = hosted ? null : resolveCliAuth();
1686
+ if (!hosted && (!auth || auth.kind === "github")) {
1563
1687
  throw new Error(
1564
1688
  `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`
1565
1689
  );
1566
1690
  }
1567
- const origin = resolveContrastBackendOrigin(
1568
- auth.kind === "session" ? auth.origin : null
1569
- );
1691
+ const origin = hosted?.origin ?? resolveContrastBackendOrigin(auth?.kind === "session" ? auth.origin : null);
1692
+ const authorization = hosted?.authorization ?? (auth ? authHeaderValue(auth) : void 0);
1693
+ const signal = AbortSignal.any([
1694
+ ...hosted ? [hosted.signal] : [],
1695
+ AbortSignal.timeout(5 * 6e4)
1696
+ ]);
1570
1697
  const submit = httpSwiftCompileSubmit({
1571
1698
  endpoint: origin,
1572
- authorization: authHeaderValue(auth)
1699
+ authorization,
1700
+ signal
1573
1701
  });
1574
1702
  if (!submit) throw new Error("swift compile: no credential to submit with");
1575
- const descriptor = nativeSourceDescriptor(packagePath);
1703
+ const descriptor = snapshot.native;
1576
1704
  const address = await compileSwiftProject(
1577
1705
  submit,
1578
1706
  {
1579
- files: packageSources(packagePath),
1707
+ files: snapshot.files,
1708
+ resources: snapshot.bundleFiles.files,
1709
+ images: snapshot.resources.images,
1580
1710
  entry,
1581
1711
  packageId: descriptor?.packageId ?? swiftPackageId(packagePath)
1582
1712
  },
1583
- origin
1713
+ origin,
1714
+ signal
1584
1715
  );
1585
1716
  if (descriptor && address.native?.hash !== descriptor.hash) {
1586
1717
  throw new Error(
1587
1718
  `${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`
1588
1719
  );
1589
1720
  }
1590
- const response = await fetch(address.url);
1721
+ const response = await fetch(address.url, { signal });
1591
1722
  if (!response.ok) {
1592
1723
  throw new Error(`swift compile: ${address.url} answered ${response.status}`);
1593
1724
  }
1725
+ const maxArtifactBytes = 64 * 1024 * 1024;
1726
+ if (Number(response.headers.get("content-length")) > maxArtifactBytes || !response.body) {
1727
+ throw new Error("swift compile: artifact exceeds 64 MiB or has no body");
1728
+ }
1729
+ const reader = response.body.getReader();
1730
+ const chunks = [];
1731
+ let total = 0;
1732
+ for (; ; ) {
1733
+ signal.throwIfAborted();
1734
+ const next = await reader.read();
1735
+ if (next.done) break;
1736
+ total += next.value.byteLength;
1737
+ if (total > maxArtifactBytes) {
1738
+ await reader.cancel();
1739
+ throw new Error("swift compile: artifact exceeds 64 MiB");
1740
+ }
1741
+ chunks.push(next.value);
1742
+ }
1594
1743
  return {
1595
1744
  hash: address.hash,
1596
- bytes: Buffer.from(await response.arrayBuffer()),
1745
+ bytes: Buffer.concat(chunks),
1597
1746
  icu: { url: address.icuUrl }
1598
1747
  };
1599
1748
  }
1600
1749
  function artifactFor(project, entry, moduleRoot) {
1601
- const stamp = sourceStamp(project.packagePath, moduleRoot);
1750
+ const stamp = sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted));
1602
1751
  if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact);
1603
1752
  if (project.building) {
1604
1753
  const running = project.building;
@@ -1606,9 +1755,24 @@ function artifactFor(project, entry, moduleRoot) {
1606
1755
  const again = () => artifactFor(project, entry, moduleRoot);
1607
1756
  return running.promise.then(again, again);
1608
1757
  }
1609
- const promise = compile(project.packagePath, entry, stamp, moduleRoot).then(
1758
+ const snapshot = packageSnapshot(project);
1759
+ if (sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted)) !== stamp) {
1760
+ return artifactFor(project, entry, moduleRoot);
1761
+ }
1762
+ const promise = compile(
1763
+ project.packagePath,
1764
+ entry,
1765
+ stamp,
1766
+ moduleRoot,
1767
+ project.hosted,
1768
+ snapshot
1769
+ ).then(
1610
1770
  (artifact) => {
1611
1771
  project.artifact = artifact;
1772
+ project.artifacts = [
1773
+ artifact,
1774
+ ...project.artifacts.filter((old) => old.hash !== artifact.hash)
1775
+ ].slice(0, 2);
1612
1776
  project.building = null;
1613
1777
  return artifact;
1614
1778
  },
@@ -1617,10 +1781,14 @@ function artifactFor(project, entry, moduleRoot) {
1617
1781
  throw error;
1618
1782
  }
1619
1783
  );
1620
- project.building = { stamp, promise };
1621
- return promise;
1784
+ const fresh = promise.then(
1785
+ (artifact) => sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted)) === artifact.stamp ? artifact : artifactFor(project, entry, moduleRoot)
1786
+ );
1787
+ project.building = { stamp, promise: fresh };
1788
+ return fresh;
1622
1789
  }
1623
1790
  function swiftPackageWatchFiles(packagePath, moduleRoot) {
1791
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1624
1792
  return [
1625
1793
  import_node_path5.default.join(packagePath, "Package.swift"),
1626
1794
  ...sourceFiles(packagePath),
@@ -1629,9 +1797,12 @@ function swiftPackageWatchFiles(packagePath, moduleRoot) {
1629
1797
  ...moduleRoot ? swiftModuleInputFiles(moduleRoot).map((file) => import_node_path5.default.join(moduleRoot, file)) : []
1630
1798
  ];
1631
1799
  }
1632
- async function swiftPackageMetadata(packagePath, packageName, moduleRoot, origin) {
1800
+ async function swiftPackageMetadata(packagePath, packageName, moduleRoot, origin, hosted) {
1801
+ packagePath = import_node_fs4.default.realpathSync(packagePath);
1633
1802
  const mains = sourceFiles(packagePath).filter(
1634
- (file) => /@main\s+(?:public\s+)?struct\s+\w+\s*:/.test(import_node_fs4.default.readFileSync(file, "utf8"))
1803
+ (file) => /@main\s+(?:public\s+)?struct\s+\w+\s*:/.test(
1804
+ readPackageFile(packagePath, file, MAX_SWIFT_COMPILE_SOURCE_BYTES).toString("utf8")
1805
+ )
1635
1806
  );
1636
1807
  const native = nativeSourceDescriptor(packagePath);
1637
1808
  if (mains.length > 1 || mains.length === 0 && !native) {
@@ -1639,27 +1810,32 @@ async function swiftPackageMetadata(packagePath, packageName, moduleRoot, origin
1639
1810
  `swift package ${packageName} needs exactly one @main source or generated native modules; found ${mains.length}`
1640
1811
  );
1641
1812
  }
1642
- const project = projectFor(packagePath);
1813
+ const project = projectFor(packagePath, hosted);
1643
1814
  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(
1644
1815
  ([_file, names]) => names.length > 0
1645
1816
  )?.[0];
1646
1817
  if (!entry) throw new Error(`swift package ${packageName} has no native module source`);
1647
1818
  const artifact = await artifactFor(project, entry, moduleRoot);
1648
- const resources = buildSwiftResourceTable(project, origin);
1649
- const bundleFiles = buildSwiftBundleFiles(packagePath);
1819
+ const resources = artifact.package.resources;
1820
+ const bundleFiles = artifact.package.bundleFiles;
1650
1821
  return {
1651
1822
  url: `${origin}${ARTIFACT_PATH}${artifact.hash}.wasm`,
1652
1823
  hash: artifact.hash,
1653
1824
  icuUrl: icuUrl(origin, artifact),
1654
1825
  rootId: `${packageName}/${entry}`,
1655
- resources: resources.table,
1826
+ resources: Object.fromEntries(
1827
+ Object.entries(resources.table).map(([name, image]) => [
1828
+ name,
1829
+ { ...image, uri: `${origin}${image.uri}` }
1830
+ ])
1831
+ ),
1656
1832
  bundleFiles: bundleFiles.files,
1657
1833
  colors: resources.colors,
1658
1834
  symbols: resources.symbols,
1659
- native: native ? {
1660
- packageId: native.packageId,
1661
- contractHash: native.hash,
1662
- modules: native.modules,
1835
+ native: artifact.package.native ? {
1836
+ packageId: artifact.package.native.packageId,
1837
+ contractHash: artifact.package.native.hash,
1838
+ modules: artifact.package.native.modules,
1663
1839
  defaultView: mains.length === 1
1664
1840
  } : void 0
1665
1841
  };
@@ -1680,7 +1856,9 @@ function serveSwiftPackageAsset(pathname, res) {
1680
1856
  }
1681
1857
  if (pathname.startsWith(ICU_PATH)) {
1682
1858
  for (const project of projects.values()) {
1683
- const icu = project.artifact?.icu;
1859
+ const icu = project.artifacts.find(
1860
+ (artifact) => "sha256" in artifact.icu && pathname === `${ICU_PATH}${artifact.icu.sha256}`
1861
+ )?.icu;
1684
1862
  if (!icu || "url" in icu || pathname !== `${ICU_PATH}${icu.sha256}`) continue;
1685
1863
  res.setHeader("access-control-allow-origin", "*");
1686
1864
  res.setHeader("content-type", "application/gzip");
@@ -1694,7 +1872,9 @@ function serveSwiftPackageAsset(pathname, res) {
1694
1872
  }
1695
1873
  if (!pathname.startsWith(ARTIFACT_PATH)) return false;
1696
1874
  for (const project of projects.values()) {
1697
- const artifact = project.artifact;
1875
+ const artifact = project.artifacts.find(
1876
+ (artifact2) => pathname === `${ARTIFACT_PATH}${artifact2.hash}.wasm`
1877
+ );
1698
1878
  if (!artifact || pathname !== `${ARTIFACT_PATH}${artifact.hash}.wasm`) continue;
1699
1879
  res.setHeader("access-control-allow-origin", "*");
1700
1880
  res.setHeader("content-type", "application/wasm");
@@ -1705,13 +1885,27 @@ function serveSwiftPackageAsset(pathname, res) {
1705
1885
  res.end("swift artifact is stale");
1706
1886
  return true;
1707
1887
  }
1888
+
1889
+ // src/vite-plugin-swift.ts
1890
+ var ARTIFACT_PATH2 = "/__peach/swift/";
1891
+ var VIRTUAL_PREFIX = "\0peach-swift:";
1892
+ var devServer;
1893
+ function serverOrigin() {
1894
+ const address = devServer?.httpServer?.address();
1895
+ const port = address && typeof address === "object" ? address.port : devServer?.config.server.port;
1896
+ if (!port) throw new Error("the swift urls need the dev server port");
1897
+ return `http://localhost:${port}`;
1898
+ }
1899
+ function artifactUrl(project, artifact) {
1900
+ return `${serverOrigin()}${ARTIFACT_PATH2}${artifact.hash}.wasm`;
1901
+ }
1708
1902
  function packageFor(file) {
1709
- let dir = import_node_path5.default.dirname(file);
1903
+ let dir = import_node_path6.default.dirname(file);
1710
1904
  const stop = devServer?.config.root;
1711
1905
  for (; ; ) {
1712
- if (import_node_fs4.default.existsSync(import_node_path5.default.join(dir, "Package.swift"))) return dir;
1713
- if (stop !== void 0 && dir === stop || import_node_path5.default.dirname(dir) === dir) break;
1714
- dir = import_node_path5.default.dirname(dir);
1906
+ if (import_node_fs5.default.existsSync(import_node_path6.default.join(dir, "Package.swift"))) return dir;
1907
+ if (stop !== void 0 && dir === stop || import_node_path6.default.dirname(dir) === dir) break;
1908
+ dir = import_node_path6.default.dirname(dir);
1715
1909
  }
1716
1910
  throw new Error(
1717
1911
  `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`
@@ -1732,9 +1926,9 @@ function swiftPlugin(options = {}) {
1732
1926
  if (!resolved) throw new Error(`cannot resolve swift source ${source}`);
1733
1927
  let id = resolved.id;
1734
1928
  if (importer && (source.startsWith("./") || source.startsWith("../"))) {
1735
- const raw2 = import_node_path5.default.resolve(import_node_path5.default.dirname(importer.split("?")[0] ?? importer), source);
1929
+ const raw2 = import_node_path6.default.resolve(import_node_path6.default.dirname(importer.split("?")[0] ?? importer), source);
1736
1930
  try {
1737
- if (import_node_fs4.default.realpathSync(raw2) === import_node_fs4.default.realpathSync(resolved.id)) id = raw2;
1931
+ if (import_node_fs5.default.realpathSync(raw2) === import_node_fs5.default.realpathSync(resolved.id)) id = raw2;
1738
1932
  } catch {
1739
1933
  }
1740
1934
  }
@@ -1743,16 +1937,16 @@ function swiftPlugin(options = {}) {
1743
1937
  async load(id) {
1744
1938
  if (!id.startsWith(VIRTUAL_PREFIX)) return null;
1745
1939
  const file = id.slice(VIRTUAL_PREFIX.length);
1746
- if (!import_node_fs4.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1940
+ if (!import_node_fs5.default.existsSync(file)) throw new Error(`swift source ${file} no longer exists`);
1747
1941
  const packagePath = options.packagePath ?? packageFor(file);
1748
1942
  const project = projectFor(packagePath);
1749
- const entry = import_node_path5.default.relative(packagePath, file).split(import_node_path5.default.sep).join(import_node_path5.default.posix.sep);
1943
+ const entry = import_node_path6.default.relative(packagePath, file).split(import_node_path6.default.sep).join(import_node_path6.default.posix.sep);
1750
1944
  for (const source of [
1751
1945
  ...sourceFiles(packagePath),
1752
1946
  ...nativeSourceFiles(packagePath)
1753
1947
  ])
1754
1948
  this.addWatchFile(source);
1755
- this.addWatchFile(import_node_path5.default.join(packagePath, "Package.swift"));
1949
+ this.addWatchFile(import_node_path6.default.join(packagePath, "Package.swift"));
1756
1950
  for (const root of linkedModuleRoots(packagePath)) {
1757
1951
  for (const source of sourceFiles(root)) this.addWatchFile(source);
1758
1952
  }
@@ -1764,39 +1958,20 @@ function swiftPlugin(options = {}) {
1764
1958
  this.warn(error instanceof Error ? error.message : String(error));
1765
1959
  artifact = project.artifact;
1766
1960
  }
1767
- let resources = {};
1768
- let colors = {};
1769
- let symbols = {};
1770
- try {
1771
- const built = buildSwiftResourceTable(project, serverOrigin());
1772
- resources = built.table;
1773
- colors = built.colors;
1774
- symbols = built.symbols;
1775
- for (const watched of built.watched) {
1776
- this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split(import_node_path5.default.posix.sep)));
1777
- }
1778
- } catch (error) {
1779
- this.warn(
1780
- `swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
1781
- );
1782
- }
1783
- let bundleFiles = {};
1784
- try {
1785
- const packed = buildSwiftBundleFiles(packagePath);
1786
- bundleFiles = packed.files;
1787
- for (const watched of packed.watched) {
1788
- this.addWatchFile(import_node_path5.default.join(packagePath, ...watched.split(import_node_path5.default.posix.sep)));
1789
- }
1790
- if (packed.skipped.length > 0) {
1791
- this.warn(
1792
- `swift bundle files skipped for ${packagePath}: ${packed.skipped.join("; ")}`
1793
- );
1794
- }
1795
- } catch (error) {
1796
- this.warn(
1797
- `swift bundle files for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`
1798
- );
1799
- }
1961
+ const resources = Object.fromEntries(
1962
+ Object.entries(artifact.package.resources.table).map(([name, image]) => [
1963
+ name,
1964
+ { ...image, uri: `${serverOrigin()}${image.uri}` }
1965
+ ])
1966
+ );
1967
+ for (const watched of [
1968
+ ...artifact.package.resources.watched,
1969
+ ...artifact.package.bundleFiles.watched
1970
+ ])
1971
+ this.addWatchFile(import_node_path6.default.join(packagePath, ...watched.split(import_node_path6.default.posix.sep)));
1972
+ const colors = artifact.package.resources.colors;
1973
+ const symbols = artifact.package.resources.symbols;
1974
+ const bundleFiles = artifact.package.bundleFiles.files;
1800
1975
  const native = nativeSourceDescriptor(packagePath);
1801
1976
  return swiftEntryModuleEsm({
1802
1977
  url: artifactUrl(project, artifact),