peachsim 0.1.939 → 0.1.1399

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 (396) hide show
  1. package/README.md +28 -22
  2. package/cli/app-catalog.ts +29 -14
  3. package/cli/app-fonts.ts +5 -3
  4. package/cli/bridge-flow-runner.ts +40 -29
  5. package/cli/cloud-client.ts +57 -21
  6. package/cli/cloud-test-tunnel.ts +12 -18
  7. package/cli/commands/agent.ts +1 -1
  8. package/cli/commands/app-fonts.ts +3 -3
  9. package/cli/commands/assert.ts +8 -8
  10. package/cli/commands/auth.ts +8 -3
  11. package/cli/commands/box/bundle-plane.ts +13 -1
  12. package/cli/commands/box.ts +16 -5
  13. package/cli/commands/compat.ts +1 -1
  14. package/cli/commands/config.ts +2 -2
  15. package/cli/commands/control.ts +125 -71
  16. package/cli/commands/cpu-profile.ts +45 -1
  17. package/cli/commands/daemon.ts +131 -33
  18. package/cli/commands/debug.ts +24 -17
  19. package/cli/commands/desktop.ts +7 -1
  20. package/cli/commands/detect.ts +101 -0
  21. package/cli/commands/detox.ts +6 -6
  22. package/cli/commands/device.ts +12 -12
  23. package/cli/commands/electron.ts +32 -14
  24. package/cli/commands/flow.ts +49 -41
  25. package/cli/commands/guest-host.ts +39 -1
  26. package/cli/commands/inspect/core.ts +14 -8
  27. package/cli/commands/inspect/list.ts +1 -1
  28. package/cli/commands/inspect/settle.ts +5 -3
  29. package/cli/commands/inspect/settling.ts +229 -44
  30. package/cli/commands/inspect/wait-idle.ts +3 -2
  31. package/cli/commands/inspect.ts +105 -93
  32. package/cli/commands/install-cli.ts +36 -15
  33. package/cli/commands/install-desktop.ts +68 -195
  34. package/cli/commands/kotlin.ts +6 -5
  35. package/cli/commands/login.ts +5 -5
  36. package/cli/commands/maestro-compare.ts +554 -0
  37. package/cli/commands/maestro-generate.ts +6 -3
  38. package/cli/commands/maestro.ts +22 -0
  39. package/cli/commands/mode.ts +2 -2
  40. package/cli/commands/no-bridge-hint.ts +6 -2
  41. package/cli/commands/platform.ts +35 -6
  42. package/cli/commands/preview.ts +6 -0
  43. package/cli/commands/project-report.ts +10 -12
  44. package/cli/commands/record.ts +37 -37
  45. package/cli/commands/runtime.ts +9 -9
  46. package/cli/commands/screenshot-layers.ts +4 -4
  47. package/cli/commands/screenshot-mode.ts +2 -2
  48. package/cli/commands/screenshot.ts +8 -8
  49. package/cli/commands/screenshots-capture.ts +2 -2
  50. package/cli/commands/serve.ts +9 -12
  51. package/cli/commands/setup.ts +29 -14
  52. package/cli/commands/shell-boolean-mode.ts +2 -2
  53. package/cli/commands/show-touches-mode.ts +2 -2
  54. package/cli/commands/skills.ts +11 -11
  55. package/cli/commands/swift.ts +29 -11
  56. package/cli/commands/telemetry.ts +2 -2
  57. package/cli/commands/test.ts +5 -0
  58. package/cli/commands/three-mode.ts +3 -3
  59. package/cli/commands/timeline.ts +4 -4
  60. package/cli/commands/upgrade.ts +2 -2
  61. package/cli/commands/upload.ts +15 -15
  62. package/cli/commands/what-happened.ts +2 -2
  63. package/cli/current-sim.ts +6 -3
  64. package/cli/desktop-companion.ts +55 -149
  65. package/cli/dev-build-freshness.ts +2 -2
  66. package/cli/drivers/desktop.ts +17 -9
  67. package/cli/drivers/electron.ts +1 -2
  68. package/cli/drivers/playwright-sim-host.ts +11 -2
  69. package/cli/drivers/playwright.ts +35 -2
  70. package/cli/drivers/types.ts +8 -0
  71. package/cli/maestro-compare.ts +670 -0
  72. package/cli/outbound-endpoints.ts +46 -8
  73. package/cli/recording-access.ts +6 -6
  74. package/cli/run-registry.ts +1 -1
  75. package/cli/runtime-compat.ts +5 -1
  76. package/cli/send-to-box.ts +3 -3
  77. package/cli/shell-init.ts +2 -1
  78. package/cli/ws-bridge.ts +9 -5
  79. package/detox/element-types.ts +1 -0
  80. package/detox/gestures.ts +3 -3
  81. package/detox/index.ts +67 -38
  82. package/detox/navigation-lifecycle.ts +9 -6
  83. package/detox/proof-frame.cjs +6 -4
  84. package/dist-lib/agent-daemon-client.cjs +1 -1
  85. package/dist-lib/agent-events.cjs +1 -1
  86. package/dist-lib/attached-projects.cjs +1 -1
  87. package/dist-lib/auth/shared-session.cjs +1 -1
  88. package/dist-lib/backend-origin.cjs +1 -1
  89. package/dist-lib/beta.cjs +1 -1
  90. package/dist-lib/beta.mjs +1 -1
  91. package/dist-lib/bridge-constants.cjs +1 -1
  92. package/dist-lib/bridge-contract-input.cjs +42 -17
  93. package/dist-lib/bridge-contract-input.mjs +41 -16
  94. package/dist-lib/bridge-contract.cjs +1 -4
  95. package/dist-lib/bridge-contract.mjs +1 -3
  96. package/dist-lib/bundle-channel.cjs +2918 -550
  97. package/dist-lib/capture-contract.cjs +65 -24
  98. package/dist-lib/capture-contract.mjs +63 -24
  99. package/dist-lib/cloud-contract.cjs +2 -2
  100. package/dist-lib/cloud-contract.mjs +2 -2
  101. package/dist-lib/cloud-test-args.cjs +1 -1
  102. package/dist-lib/cloud-test-args.mjs +1 -1
  103. package/dist-lib/cloud.cjs +3 -3
  104. package/dist-lib/cloud.mjs +3 -3
  105. package/dist-lib/config.cjs +15 -5
  106. package/dist-lib/config.mjs +154 -0
  107. package/dist-lib/detox/index.cjs +495 -139
  108. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  109. package/dist-lib/expo-swift-bundle.cjs +1168 -238
  110. package/dist-lib/expo-swift-metadata.cjs +34 -23
  111. package/dist-lib/expo-swift-metadata.mjs +32 -23
  112. package/dist-lib/home-paths.cjs +2 -2
  113. package/dist-lib/host/bridge-host.cjs +2938 -654
  114. package/dist-lib/host/fetch-proxy-handler.cjs +14 -1
  115. package/dist-lib/host/fetch-proxy-overrides.cjs +7 -1
  116. package/dist-lib/host/fetch-proxy-overrides.mjs +5 -1
  117. package/dist-lib/host/replacement-module-handler.cjs +2 -2
  118. package/dist-lib/host/websocket-proxy.cjs +1 -1
  119. package/dist-lib/index.cjs +999 -353
  120. package/dist-lib/jump-to-source-babel.cjs +1 -1
  121. package/dist-lib/jump-to-source-native.cjs +1 -1
  122. package/dist-lib/kotlin-host.cjs +332 -19
  123. package/dist-lib/menu.cjs +1 -1
  124. package/dist-lib/menu.mjs +1 -1
  125. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  126. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  127. package/dist-lib/metro-production-bundle.cjs +7 -7
  128. package/dist-lib/metro-production-bundle.mjs +5 -5
  129. package/dist-lib/metro.cjs +688 -250
  130. package/dist-lib/native-app-identity-project.cjs +2647 -0
  131. package/dist-lib/native-app-identity-project.mjs +1257 -0
  132. package/dist-lib/native-app-identity.cjs +325 -25
  133. package/dist-lib/native-app-identity.mjs +323 -25
  134. package/dist-lib/native-firebase-metadata.cjs +1 -1
  135. package/dist-lib/native-firebase-metadata.mjs +1 -1
  136. package/dist-lib/plist.cjs +1 -1
  137. package/dist-lib/plist.mjs +1 -1
  138. package/dist-lib/profiles.cjs +1 -1
  139. package/dist-lib/public-brand.cjs +1 -1
  140. package/dist-lib/react-native-host-modules.cjs +5 -1
  141. package/dist-lib/react-native-host-modules.mjs +5 -1
  142. package/dist-lib/scripts/dev-server-scanner.cjs +57 -32
  143. package/dist-lib/sdk.cjs +231 -51
  144. package/dist-lib/sdk.mjs +231 -51
  145. package/dist-lib/skills.cjs +4107 -1374
  146. package/dist-lib/swift-compile.cjs +290 -30
  147. package/dist-lib/swift-compile.mjs +290 -30
  148. package/dist-lib/swift-package.cjs +143 -12
  149. package/dist-lib/swift-package.mjs +137 -11
  150. package/dist-lib/swift-submit.cjs +47 -5
  151. package/dist-lib/swift-submit.mjs +42 -5
  152. package/dist-lib/swift-toolchain.cjs +7 -2
  153. package/dist-lib/swift.cjs +1 -1
  154. package/dist-lib/vite-swift.cjs +1392 -458
  155. package/dist-lib/vite.cjs +13 -17
  156. package/dist-lib/worklet-executable.cjs +248 -33
  157. package/dist-lib/worklet-executable.mjs +248 -33
  158. package/install.sh +10 -1
  159. package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Resources.kt +9 -9
  160. package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +20 -0
  161. package/kotlin/AndroidViewSources/android/view/View.kt +211 -0
  162. package/kotlin/AndroidViewSources/android/widget/FrameLayout.kt +61 -0
  163. package/kotlin/AndroidViewSources/android/widget/LinearLayout.kt +49 -0
  164. package/kotlin/AppSources/android/view/Window.kt +36 -0
  165. package/kotlin/AppSources/android/view/WindowInsets.kt +9 -0
  166. package/kotlin/AppSources/android/view/WindowInsetsController.kt +5 -0
  167. package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +48 -0
  168. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +20 -5
  169. package/kotlin/AppSources/androidx/compose/foundation/layout/WindowInsets.kt +15 -0
  170. package/kotlin/AppSources/androidx/compose/ui/platform/ComposeView.kt +2 -1
  171. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +42 -50
  172. package/kotlin/ComposeSources/PeachComposeHost.kt +200 -84
  173. package/kotlin/ExpoComposeSources/PeachAndroidView.kt +87 -0
  174. package/kotlin/ExpoComposeSources/PeachExpoViewMount.kt +24 -0
  175. package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +28 -0
  176. package/kotlin/ExpoComposeSources/expo/modules/kotlin/views/ExpoComposeView.kt +70 -0
  177. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +513 -0
  178. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +173 -0
  179. package/kotlin/ExpoSources/expo/modules/kotlin/AppContext.kt +20 -0
  180. package/kotlin/ExpoSources/expo/modules/kotlin/Promise.kt +19 -0
  181. package/kotlin/ExpoSources/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +198 -0
  182. package/kotlin/ExpoSources/expo/modules/kotlin/exception/CodedException.kt +126 -0
  183. package/kotlin/ExpoSources/expo/modules/kotlin/functions/Functions.kt +132 -0
  184. package/kotlin/ExpoSources/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +81 -0
  185. package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +98 -0
  186. package/kotlin/ExpoSources/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +236 -0
  187. package/kotlin/ExpoSources/expo/modules/kotlin/records/Record.kt +12 -0
  188. package/kotlin/ExpoSources/expo/modules/kotlin/runtime/Runtime.kt +9 -0
  189. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObject.kt +50 -0
  190. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +57 -0
  191. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedRef.kt +19 -0
  192. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/ConcreteTypedArrays.kt +158 -0
  193. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/GenericTypedArray.kt +8 -0
  194. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArray.kt +29 -0
  195. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArrayIterator.kt +9 -0
  196. package/kotlin/ExpoSources/expo/modules/kotlin/types/AnyType.kt +155 -0
  197. package/kotlin/ExpoSources/expo/modules/kotlin/types/Either.kt +91 -0
  198. package/kotlin/ExpoSources/expo/modules/kotlin/types/Enumerable.kt +6 -0
  199. package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +54 -0
  200. package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +23 -0
  201. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +71 -0
  202. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +35 -0
  203. package/kotlin/README.md +13 -3
  204. package/kotlin/Sources/android/os/Build.kt +17 -0
  205. package/kotlin/Sources/android/os/Handler.kt +35 -0
  206. package/kotlin/Sources/android/os/Looper.kt +17 -0
  207. package/kotlin/Sources/android/os/MessageQueue.kt +56 -0
  208. package/kotlin/Sources/android/os/SystemClock.kt +14 -0
  209. package/kotlin/Sources/android/util/Log.kt +64 -0
  210. package/kotlin/Sources/java/security/MessageDigest.kt +114 -0
  211. package/kotlin/Sources/kotlin/text/StringFormat.kt +9 -0
  212. package/kotlin/Sources/org/json/JSON.kt +44 -0
  213. package/kotlin/fixtures/counter/Counter.d.kt.ts +1 -0
  214. package/kotlin/fixtures/counter/Counter.kt +10 -3
  215. package/kotlin/fixtures/counter/CounterApp.native.tsx +1 -1
  216. package/kotlin/fixtures/counter/vite.config.ts +2 -2
  217. package/kotlin/fixtures/headless-counter/Headless.kt +7 -6
  218. package/kotlin/fixtures/import-animation/Animation.d.kt.ts +7 -0
  219. package/kotlin/fixtures/import-animation/Animation.kt +80 -0
  220. package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +13 -0
  221. package/kotlin/fixtures/import-animation/README.md +24 -0
  222. package/kotlin/fixtures/import-animation/app/_layout.tsx +4 -0
  223. package/kotlin/fixtures/import-animation/app/index+spa.tsx +1 -0
  224. package/kotlin/fixtures/import-animation/app/routes.d.ts +18 -0
  225. package/kotlin/fixtures/import-animation/package.json +23 -0
  226. package/kotlin/fixtures/import-animation/tsconfig.json +20 -0
  227. package/kotlin/fixtures/import-animation/vite.config.ts +20 -0
  228. package/kotlin/fixtures/import-editor/Editor.d.kt.ts +7 -0
  229. package/kotlin/fixtures/import-editor/Editor.kt +59 -0
  230. package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +23 -0
  231. package/kotlin/fixtures/import-editor/README.md +27 -0
  232. package/kotlin/fixtures/import-editor/app/_layout.tsx +4 -0
  233. package/kotlin/fixtures/import-editor/app/index+spa.tsx +1 -0
  234. package/kotlin/fixtures/import-editor/package.json +23 -0
  235. package/kotlin/fixtures/import-editor/tsconfig.json +20 -0
  236. package/kotlin/fixtures/import-editor/vite.config.ts +20 -0
  237. package/kotlin/fixtures/import-effects/Effects.d.kt.ts +7 -0
  238. package/kotlin/fixtures/import-effects/Effects.kt +87 -0
  239. package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +13 -0
  240. package/kotlin/fixtures/import-effects/README.md +19 -0
  241. package/kotlin/fixtures/import-effects/app/_layout.tsx +4 -0
  242. package/kotlin/fixtures/import-effects/app/index+spa.tsx +1 -0
  243. package/kotlin/fixtures/import-effects/package.json +23 -0
  244. package/kotlin/fixtures/import-effects/tsconfig.json +20 -0
  245. package/kotlin/fixtures/import-effects/vite.config.ts +20 -0
  246. package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +13 -0
  247. package/kotlin/fixtures/import-layout/Layout.d.kt.ts +7 -0
  248. package/kotlin/fixtures/import-layout/Layout.kt +72 -0
  249. package/kotlin/fixtures/import-layout/README.md +24 -0
  250. package/kotlin/fixtures/import-layout/app/_layout.tsx +4 -0
  251. package/kotlin/fixtures/import-layout/app/index+spa.tsx +1 -0
  252. package/kotlin/fixtures/import-layout/package.json +23 -0
  253. package/kotlin/fixtures/import-layout/tsconfig.json +20 -0
  254. package/kotlin/fixtures/import-layout/vite.config.ts +20 -0
  255. package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +18 -0
  256. package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +7 -0
  257. package/kotlin/fixtures/import-navigation/Navigation.kt +104 -0
  258. package/kotlin/fixtures/import-navigation/README.md +31 -0
  259. package/kotlin/fixtures/import-navigation/app/_layout.tsx +4 -0
  260. package/kotlin/fixtures/import-navigation/app/index+spa.tsx +1 -0
  261. package/kotlin/fixtures/import-navigation/package.json +23 -0
  262. package/kotlin/fixtures/import-navigation/tsconfig.json +20 -0
  263. package/kotlin/fixtures/import-navigation/vite.config.ts +20 -0
  264. package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +29 -0
  265. package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +7 -0
  266. package/kotlin/fixtures/import-overlays/Overlays.kt +93 -0
  267. package/kotlin/fixtures/import-overlays/README.md +34 -0
  268. package/kotlin/fixtures/import-overlays/app/_layout.tsx +4 -0
  269. package/kotlin/fixtures/import-overlays/app/index+spa.tsx +1 -0
  270. package/kotlin/fixtures/import-overlays/package.json +23 -0
  271. package/kotlin/fixtures/import-overlays/tsconfig.json +20 -0
  272. package/kotlin/fixtures/import-overlays/vite.config.ts +20 -0
  273. package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +13 -0
  274. package/kotlin/fixtures/import-scroll/README.md +23 -0
  275. package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +7 -0
  276. package/kotlin/fixtures/import-scroll/Scroll.kt +91 -0
  277. package/kotlin/fixtures/import-scroll/app/_layout.tsx +4 -0
  278. package/kotlin/fixtures/import-scroll/app/index+spa.tsx +1 -0
  279. package/kotlin/fixtures/import-scroll/package.json +23 -0
  280. package/kotlin/fixtures/import-scroll/tsconfig.json +20 -0
  281. package/kotlin/fixtures/import-scroll/vite.config.ts +20 -0
  282. package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +44 -0
  283. package/kotlin/fixtures/import-selection/README.md +32 -0
  284. package/kotlin/fixtures/import-selection/Selection.d.kt.ts +8 -0
  285. package/kotlin/fixtures/import-selection/Selection.kt +78 -0
  286. package/kotlin/fixtures/import-selection/app/_layout.tsx +4 -0
  287. package/kotlin/fixtures/import-selection/app/index+spa.tsx +1 -0
  288. package/kotlin/fixtures/import-selection/package.json +23 -0
  289. package/kotlin/fixtures/import-selection/tsconfig.json +20 -0
  290. package/kotlin/fixtures/import-selection/vite.config.ts +20 -0
  291. package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +13 -0
  292. package/kotlin/fixtures/import-surfaces/README.md +22 -0
  293. package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +7 -0
  294. package/kotlin/fixtures/import-surfaces/Surfaces.kt +64 -0
  295. package/kotlin/fixtures/import-surfaces/app/_layout.tsx +4 -0
  296. package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +1 -0
  297. package/kotlin/fixtures/import-surfaces/package.json +23 -0
  298. package/kotlin/fixtures/import-surfaces/tsconfig.json +20 -0
  299. package/kotlin/fixtures/import-surfaces/vite.config.ts +20 -0
  300. package/kotlin/fixtures/import-values/ImportValues.native.tsx +35 -0
  301. package/kotlin/fixtures/import-values/README.md +23 -0
  302. package/kotlin/fixtures/import-values/Values.d.kt.ts +11 -0
  303. package/kotlin/fixtures/import-values/Values.kt +66 -0
  304. package/kotlin/fixtures/import-values/app/_layout.tsx +4 -0
  305. package/kotlin/fixtures/import-values/app/index+spa.tsx +1 -0
  306. package/kotlin/fixtures/import-values/package.json +23 -0
  307. package/kotlin/fixtures/import-values/tsconfig.json +20 -0
  308. package/kotlin/fixtures/import-values/vite.config.ts +20 -0
  309. package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +38 -0
  310. package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +36 -0
  311. package/kotlin/fixtures/kotlin-app-variants/Ime.kt +3 -1
  312. package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +50 -0
  313. package/kotlin/fork/NOTICE +6 -1
  314. package/kotlin/fork/README.md +15 -7
  315. package/kotlin/fork/build.sh +2 -0
  316. package/kotlin/fork/compose-worker-globals.patch +186 -4
  317. package/kotlin/fork/fork.json +2 -2
  318. package/kotlin/fork/skiko-worker-clock.patch +12 -0
  319. package/kotlin/toolchain.json +12 -12
  320. package/kotlin/tools/expo-types.main.kts +115 -0
  321. package/package.json +17 -23
  322. package/scripts/dev-server-scanner.ts +92 -66
  323. package/skills/contrast/SKILL.md +2 -2
  324. package/skills/peach-debug/SKILL.md +1 -1
  325. package/skills/peach-perf/SKILL.md +1 -1
  326. package/skills/peach-setup/SKILL.md +2 -1
  327. package/skills/peach-test/SKILL.md +8 -0
  328. package/src/assetCatalogContract.ts +43 -1
  329. package/src/bridge-contract-input.ts +46 -19
  330. package/src/bridge-contract.ts +0 -1
  331. package/src/bundle-channel.mjs +197 -84
  332. package/src/bundleGlobalFooter.ts +38 -0
  333. package/src/capture-contract.ts +107 -33
  334. package/src/cloud-contract.ts +5 -1
  335. package/src/cloud.ts +5 -2
  336. package/src/config.ts +22 -3
  337. package/src/configured-bundle-producer.ts +1 -1
  338. package/src/desktop-host-delivery.ts +95 -1
  339. package/src/expo-kotlin-bundle.ts +29 -0
  340. package/src/expo-kotlin-host.ts +201 -0
  341. package/src/expo-swift-host.ts +96 -76
  342. package/src/expoKotlinContract.ts +70 -0
  343. package/src/expoSwiftContract.ts +4 -25
  344. package/src/home-paths.ts +7 -4
  345. package/src/host/bridge-host.ts +23 -3
  346. package/src/host/fetch-proxy-handler.ts +13 -0
  347. package/src/host/fetch-proxy-overrides.ts +11 -0
  348. package/src/host/replacement-module-handler.ts +5 -2
  349. package/src/index.ts +3 -1
  350. package/src/kotlin-host.ts +463 -19
  351. package/src/metro-plugin.ts +56 -28
  352. package/src/metro-production-bundle.ts +2 -2
  353. package/src/native-app-identity-project.ts +190 -0
  354. package/src/native-app-identity.ts +398 -34
  355. package/src/native-firebase-config.ts +69 -15
  356. package/src/native-seam-manifest.ts +6 -0
  357. package/src/one-widget-discovery.ts +2 -1
  358. package/src/openstep-plist.ts +107 -0
  359. package/src/react-native-host-modules.ts +4 -0
  360. package/src/runtime-assets.ts +2 -2
  361. package/src/sim-client.ts +2 -2
  362. package/src/swift-binary-modules.ts +3 -1
  363. package/src/swift-compile.ts +286 -38
  364. package/src/swift-distribution-contract.ts +4 -0
  365. package/src/swift-distribution.lock.json +123 -12
  366. package/src/swift-distribution.ts +7 -3
  367. package/src/swift-host.ts +298 -83
  368. package/src/swift-package-manifest.ts +29 -2
  369. package/src/swift-package.ts +180 -10
  370. package/src/swift-packages.ts +4 -0
  371. package/src/swift-standin-emit.ts +4 -1
  372. package/src/swift-standins.ts +274 -15
  373. package/src/swift-submit.ts +62 -7
  374. package/src/swift-toolchain.ts +6 -1
  375. package/src/swiftContract.ts +126 -3
  376. package/src/vite-plugin-one.ts +13 -16
  377. package/src/vite-plugin-swift.ts +4 -5
  378. package/src/vite-plugin.ts +360 -144
  379. package/src/vite.d.ts +1 -1
  380. package/src/worker-system-chunks.ts +221 -0
  381. package/src/worklet-executable.ts +348 -38
  382. package/src/worklets-babel.ts +1 -1
  383. package/src/xcode-project.ts +243 -127
  384. package/dist-lib/agent-identity.cjs +0 -39
  385. package/dist-lib/agent-sessions.cjs +0 -760
  386. package/dist-lib/cli-constants.cjs +0 -32
  387. package/dist-lib/render-mode.cjs +0 -44
  388. package/kotlin/AppSources/android/view/View.kt +0 -74
  389. package/kotlin/AppSources/android/widget/FrameLayout.kt +0 -6
  390. package/src/render-mode.ts +0 -15
  391. /package/kotlin/{AppSources → AndroidViewSources}/android/content/Context.kt +0 -0
  392. /package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Configuration.kt +0 -0
  393. /package/kotlin/{AppSources → AndroidViewSources}/android/graphics/Color.kt +0 -0
  394. /package/kotlin/{AppSources → AndroidViewSources}/android/util/DisplayMetrics.kt +0 -0
  395. /package/kotlin/{AppSources → AndroidViewSources}/dev/peach/app/JavaFormat.kt +0 -0
  396. /package/kotlin/{AppSources → Sources}/java/lang/Runnable.kt +0 -0
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.939 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -35,15 +35,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
35
  ));
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
 
38
- // cli/internal-child.ts
39
- var RNX_FORKED_SCRIPT_ENV;
40
- var init_internal_child = __esm({
41
- "cli/internal-child.ts"() {
42
- "use strict";
43
- RNX_FORKED_SCRIPT_ENV = "PEACH_RUNS_FORKED_SCRIPTS";
44
- }
45
- });
46
-
47
38
  // src/backend-origin.ts
48
39
  function normalizeHostname(hostname) {
49
40
  return hostname.replace(/^\[|\]$/g, "").toLowerCase();
@@ -76,6 +67,15 @@ var init_backend_origin = __esm({
76
67
  }
77
68
  });
78
69
 
70
+ // cli/internal-child.ts
71
+ var RNX_FORKED_SCRIPT_ENV;
72
+ var init_internal_child = __esm({
73
+ "cli/internal-child.ts"() {
74
+ "use strict";
75
+ RNX_FORKED_SCRIPT_ENV = "PEACH_RUNS_FORKED_SCRIPTS";
76
+ }
77
+ });
78
+
79
79
  // src/is-record.ts
80
80
  function isRecord(value) {
81
81
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -114,7 +114,7 @@ function parseSwiftDistributionManifest(value) {
114
114
  };
115
115
  }
116
116
  function parseSwiftDistributionLock(value) {
117
- if (!isRecord(value) || typeof value.id !== "string" || !/^[a-f0-9]{16}$/.test(value.id) || typeof value.release !== "string" || typeof value.wasmSdkId !== "string" || typeof value.wasmSdkUrl !== "string" || typeof value.wasmSdkChecksum !== "string" || typeof value.source !== "string" || !isRecord(value.hosts)) {
117
+ if (!isRecord(value) || typeof value.id !== "string" || !/^[a-f0-9]{16}$/.test(value.id) || typeof value.release !== "string" || typeof value.wasmSdkId !== "string" || typeof value.wasmSdkUrl !== "string" || typeof value.wasmSdkChecksum !== "string" || typeof value.source !== "string" || !isStrings(value.targets) || !isRecord(value.hosts)) {
118
118
  throw new Error("invalid Swift distribution lock");
119
119
  }
120
120
  const hosts = value.hosts;
@@ -132,6 +132,7 @@ function parseSwiftDistributionLock(value) {
132
132
  wasmSdkUrl: value.wasmSdkUrl,
133
133
  wasmSdkChecksum: value.wasmSdkChecksum,
134
134
  source: value.source,
135
+ targets: value.targets,
135
136
  hosts: {
136
137
  "darwin-arm64": entry("darwin-arm64"),
137
138
  "linux-x64": entry("linux-x64"),
@@ -271,7 +272,10 @@ function swiftImplicitResourcePaths(paths) {
271
272
  function manifestCalls(manifest, kinds) {
272
273
  const code = manifest.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"])\/\/[^\n]*/g, "$1");
273
274
  const calls = [];
274
- const pattern = new RegExp(`\\.(${MANIFEST_DECLARATIONS.join("|")})\\s*\\(`, "g");
275
+ const pattern = new RegExp(
276
+ `(?:\\.(${MANIFEST_DECLARATIONS.join("|")})|\\b(Package))\\s*\\(`,
277
+ "g"
278
+ );
275
279
  let match;
276
280
  while ((match = pattern.exec(code)) !== null) {
277
281
  let depth = 1;
@@ -301,22 +305,59 @@ function manifestCalls(manifest, kinds) {
301
305
  }
302
306
  i++;
303
307
  }
304
- if (kinds.includes(match[1]))
305
- calls.push({ kind: match[1], flat, call: code.slice(start, i) });
308
+ const kind = match[1] ?? match[2];
309
+ if (kinds.includes(kind)) calls.push({ kind, flat, call: code.slice(start, i) });
310
+ else if (kind === "Package") {
311
+ calls.push(...manifestCalls(code.slice(start, i - 1), kinds));
312
+ }
306
313
  pattern.lastIndex = i;
307
314
  }
308
315
  return calls;
309
316
  }
317
+ function splitManifestArguments(source) {
318
+ const arguments_ = [];
319
+ let start = 0;
320
+ let parentheses = 0;
321
+ let brackets = 0;
322
+ let braces = 0;
323
+ let inString = false;
324
+ let escaped = false;
325
+ for (let index = 0; index < source.length; index++) {
326
+ const character = source[index];
327
+ if (inString) {
328
+ if (escaped) escaped = false;
329
+ else if (character === "\\") escaped = true;
330
+ else if (character === '"') inString = false;
331
+ } else if (character === '"') inString = true;
332
+ else if (character === "(") parentheses++;
333
+ else if (character === ")") parentheses--;
334
+ else if (character === "[") brackets++;
335
+ else if (character === "]") brackets--;
336
+ else if (character === "{") braces++;
337
+ else if (character === "}") braces--;
338
+ else if (character === "," && parentheses === 0 && brackets === 0 && braces === 0) {
339
+ arguments_.push(source.slice(start, index).trim());
340
+ start = index + 1;
341
+ }
342
+ }
343
+ const final = source.slice(start).trim();
344
+ if (final) arguments_.push(final);
345
+ return arguments_;
346
+ }
310
347
  function swiftPackageIdentity(url) {
311
348
  return (url.split("/").pop() ?? url).replace(/\.git$/, "").toLowerCase();
312
349
  }
313
350
  function swiftManifestTargets(manifest, resourcePaths = []) {
314
351
  const implicitResources = swiftImplicitResourcePaths(resourcePaths);
315
352
  const targets = [];
316
- for (const { kind, flat, call } of manifestCalls(manifest, [
317
- "executableTarget",
318
- "target"
319
- ])) {
353
+ const declarations = manifestCalls(manifest, ["executableTarget", "target"]);
354
+ const targetNames = new Set(
355
+ declarations.flatMap(({ flat }) => {
356
+ const name2 = /\bname\s*:\s*"([^"]+)"/.exec(flat)?.[1];
357
+ return name2 ? [name2] : [];
358
+ })
359
+ );
360
+ for (const { kind, flat, call } of declarations) {
320
361
  const executable = kind === "executableTarget";
321
362
  const name2 = /\bname\s*:\s*"([^"]+)"/.exec(flat)?.[1];
322
363
  if (!name2) continue;
@@ -326,6 +367,12 @@ function swiftManifestTargets(manifest, resourcePaths = []) {
326
367
  /\.swiftLanguageMode\(\s*\.v(\d+)\s*\)/.exec(call)?.[1] ?? "5"
327
368
  );
328
369
  const mainActorIsolation = /\.defaultIsolation\(\s*MainActor\.self\s*\)/.test(call);
370
+ const cxxInterop = /\.interoperabilityMode\(\s*\.Cxx\s*\)/.test(
371
+ call.replace(/"(?:\\.|[^"\\])*"/g, "")
372
+ );
373
+ const defines = [
374
+ ...call.matchAll(/\.define\(\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*\)/g)
375
+ ].map((match) => match[1]);
329
376
  const excluded = [
330
377
  ...(/\bexclude\s*:\s*\[([^\]]*)\]/.exec(flat)?.[1] ?? "").matchAll(/"([^"]+)"/g)
331
378
  ].map((match) => match[1].replace(/^\.\//, "").replace(/\/+$/, ""));
@@ -339,20 +386,95 @@ function swiftManifestTargets(manifest, resourcePaths = []) {
339
386
  return false;
340
387
  return true;
341
388
  });
389
+ const targetArguments = splitManifestArguments(call.slice(0, -1));
390
+ const dependencyArgument = targetArguments.find(
391
+ (argument) => /^dependencies\s*:/.test(argument)
392
+ );
393
+ const dependencyList = dependencyArgument?.replace(/^dependencies\s*:\s*/, "").trim();
394
+ const dependencyNames = /* @__PURE__ */ new Set();
395
+ if (dependencyList?.startsWith("[") && dependencyList.endsWith("]")) {
396
+ for (const dependency of splitManifestArguments(dependencyList.slice(1, -1))) {
397
+ const dependencyName = /^"([^"]+)"$/.exec(dependency)?.[1] ?? /^\.(?:target|byName)\s*\(\s*name\s*:\s*"([^"]+)"/.exec(dependency)?.[1];
398
+ if (dependencyName && targetNames.has(dependencyName))
399
+ dependencyNames.add(dependencyName);
400
+ }
401
+ }
402
+ const dependencies = [...dependencyNames].map((name3) => ({
403
+ kind: "target",
404
+ name: name3
405
+ }));
342
406
  targets.push({
343
407
  name: name2,
344
408
  path: targetPath,
345
409
  executable,
346
410
  languageMode,
347
411
  mainActorIsolation,
348
- resources
412
+ ...cxxInterop ? { cxxInterop: true } : {},
413
+ ...defines.length === 0 ? {} : { defines },
414
+ resources,
415
+ ...dependencies.length === 0 ? {} : { dependencies },
416
+ ...excluded.length === 0 ? {} : { exclude: excluded }
349
417
  });
350
418
  }
351
419
  return targets;
352
420
  }
421
+ function swiftManifestCxxLanguageStandard(manifest) {
422
+ const declaration = manifestCalls(manifest, ["Package"])[0];
423
+ if (!declaration) return void 0;
424
+ const code = declaration.flat.replace(/"(?:\\.|[^"\\])*"/g, "");
425
+ return /\bcxxLanguageStandard\s*:\s*\.(\w+)/.exec(code)?.[1];
426
+ }
427
+ function swiftResourceBundles(files2, resourcePaths = []) {
428
+ const manifest = files2["Package.swift"] ?? "";
429
+ const targets = swiftManifestTargets(manifest, resourcePaths).filter(
430
+ (target) => target.resources
431
+ );
432
+ if (targets.length === 0) return [];
433
+ const declaration = manifestCalls(manifest, ["Package"])[0];
434
+ const packageName = declaration && /\bname\s*:\s*"([^"/\\]+)"/.exec(declaration.flat)?.[1];
435
+ if (!packageName)
436
+ throw new Error("swift resource bundles require the original package name");
437
+ return targets.map((target) => ({
438
+ target: target.name,
439
+ sourcePath: target.path,
440
+ path: `${packageName}_${target.name}.bundle`
441
+ }));
442
+ }
443
+ function swiftResourceBundleForFile(bundles, file) {
444
+ let owner;
445
+ for (const bundle of bundles) {
446
+ const prefix = bundle.sourcePath === "." ? "" : `${bundle.sourcePath}/`;
447
+ if (file.startsWith(prefix) && (!owner || bundle.sourcePath.length > owner.sourcePath.length))
448
+ owner = bundle;
449
+ }
450
+ return owner;
451
+ }
452
+ function swiftResourceBundleInfoPlist(bundle) {
453
+ const name2 = bundle.path.split("/").pop().replace(/\.(bundle|framework)$/, "");
454
+ const escaped = name2.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
455
+ return `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>CFBundleName</key><string>${escaped}</string><key>CFBundlePackageType</key><string>${bundle.path.endsWith(".framework") ? "FMWK" : "BNDL"}</string></dict></plist>`;
456
+ }
457
+ function swiftMetalSourceFiles(files2) {
458
+ const sources = Object.entries(files2).filter(([file]) => file.endsWith(".metal"));
459
+ if (sources.length === 0) return {};
460
+ const targets = swiftManifestTargets(files2["Package.swift"] ?? "");
461
+ return Object.fromEntries(
462
+ sources.filter(([file]) => {
463
+ if (targets.length === 0) return true;
464
+ return targets.some((target) => {
465
+ const prefix = target.path === "." ? "" : `${target.path}/`;
466
+ if (!file.startsWith(prefix)) return false;
467
+ const relative = file.slice(prefix.length);
468
+ return !(target.exclude ?? []).some(
469
+ (excluded) => relative === excluded || relative.startsWith(`${excluded}/`)
470
+ );
471
+ });
472
+ })
473
+ );
474
+ }
353
475
  function swiftPackagePartition(manifest, resourcePaths = []) {
354
476
  const targets = swiftManifestTargets(manifest, resourcePaths);
355
- return targets.length === 1 && targets[0].executable && targets[0].path === "." ? [] : targets;
477
+ return targets.length === 1 && targets[0].executable && targets[0].path === "." && targets[0].languageMode === 5 && !targets[0].mainActorIsolation && !targets[0].defines?.length && !targets[0].cxxInterop && !swiftManifestCxxLanguageStandard(manifest) ? [] : targets;
356
478
  }
357
479
  var MANIFEST_DECLARATIONS;
358
480
  var init_swift_package = __esm({
@@ -373,6 +495,42 @@ var init_swift_package = __esm({
373
495
  }
374
496
  });
375
497
 
498
+ // src/assetCatalogContract.ts
499
+ var init_assetCatalogContract = __esm({
500
+ "src/assetCatalogContract.ts"() {
501
+ "use strict";
502
+ }
503
+ });
504
+
505
+ // src/swiftContract.ts
506
+ function isSwiftResourcePath(value) {
507
+ return typeof value === "string" && value.length <= 4096 && !value.startsWith("/") && !value.includes("\\") && !value.includes("\0") && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
508
+ }
509
+ function isSwiftBundlePath(value) {
510
+ return typeof value === "string" && (value === "." || isSwiftResourcePath(value) && /\.(bundle|framework)$/.test(value));
511
+ }
512
+ function isSwiftResourceBundles(value) {
513
+ return Array.isArray(value) && value.length <= 128 && value.every(
514
+ (bundle) => typeof bundle === "object" && bundle !== null && typeof bundle.target === "string" && /^[A-Za-z_][A-Za-z0-9_-]*$/.test(bundle.target) && (bundle.sourcePath === "." || isSwiftResourcePath(bundle.sourcePath)) && isSwiftBundlePath(bundle.path)
515
+ ) && new Set(value.map((bundle) => bundle.target)).size === value.length && new Set(value.map((bundle) => bundle.path)).size === value.length;
516
+ }
517
+ function isSwiftMetalSources(value) {
518
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.entries(value).every(
519
+ ([file, source]) => file.endsWith(".metal") && isSwiftResourcePath(file) && typeof source === "object" && source !== null && !Array.isArray(source) && "url" in source && typeof source.url === "string" && "sha256" in source && typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256)
520
+ );
521
+ }
522
+ function isSwiftMetalLibrarySources(value) {
523
+ return isSwiftMetalSources(value) && Object.values(value).every(
524
+ (source) => "bundlePath" in source && isSwiftBundlePath(source.bundlePath)
525
+ );
526
+ }
527
+ var init_swiftContract = __esm({
528
+ "src/swiftContract.ts"() {
529
+ "use strict";
530
+ init_assetCatalogContract();
531
+ }
532
+ });
533
+
376
534
  // src/swift-compile.ts
377
535
  function swiftDistributionAt(root) {
378
536
  const file = import_node_path2.default.join(root, SWIFT_DISTRIBUTION_MANIFEST);
@@ -395,6 +553,7 @@ function swiftModuleInputFiles(root) {
395
553
  "Package.swift",
396
554
  "scripts/contrast-app-Package.swift",
397
555
  "scripts/build-distribution.ts",
556
+ "scripts/distribution-clang.ts",
398
557
  "scripts/build-distribution-plugins.sh",
399
558
  "scripts/build-preview-plugin.sh",
400
559
  "toolchain.json"
@@ -419,6 +578,10 @@ function swiftMacroPlugins(moduleRoot) {
419
578
  stale: false
420
579
  };
421
580
  const dir = import_node_path2.default.join(moduleRoot, ".build/host/RnxPreviewMacros");
581
+ const release = JSON.parse(
582
+ (0, import_node_fs2.readFileSync)(import_node_path2.default.join(moduleRoot, "toolchain.json"), "utf8")
583
+ ).release;
584
+ const compilerStamp = import_node_path2.default.join(dir, "compiler-release");
422
585
  const ext = process.platform === "darwin" ? "dylib" : "so";
423
586
  const plugins = [
424
587
  {
@@ -432,12 +595,12 @@ function swiftMacroPlugins(moduleRoot) {
432
595
  ];
433
596
  return {
434
597
  libraries: plugins.map(({ library }) => library),
435
- stale: plugins.some(
598
+ stale: !(0, import_node_fs2.existsSync)(compilerStamp) || (0, import_node_fs2.readFileSync)(compilerStamp, "utf8").trim() !== release || plugins.some(
436
599
  ({ library, source }) => !(0, import_node_fs2.existsSync)(library) || (0, import_node_fs2.statSync)(source).mtimeMs > (0, import_node_fs2.statSync)(library).mtimeMs
437
600
  )
438
601
  };
439
602
  }
440
- async function ensureMacroPlugins(moduleRoot, toolchainBin) {
603
+ async function ensureMacroPlugins(moduleRoot, toolchainBin, release) {
441
604
  const { libraries, stale } = swiftMacroPlugins(moduleRoot);
442
605
  if (stale) {
443
606
  await run2("sh", [
@@ -445,6 +608,11 @@ async function ensureMacroPlugins(moduleRoot, toolchainBin) {
445
608
  toolchainBin,
446
609
  libraries[0]
447
610
  ]);
611
+ (0, import_node_fs2.writeFileSync)(
612
+ import_node_path2.default.join(import_node_path2.default.dirname(libraries[0]), "compiler-release"),
613
+ `${release}
614
+ `
615
+ );
448
616
  }
449
617
  return libraries;
450
618
  }
@@ -629,7 +797,11 @@ function swiftTargetDependencies(files2, targets) {
629
797
  const siblings = /* @__PURE__ */ new Map();
630
798
  for (const target of targets) {
631
799
  const prefix = target.path === "." ? "" : `${target.path}/`;
632
- const deps = /* @__PURE__ */ new Set();
800
+ const deps = new Set(
801
+ (target.dependencies ?? []).flatMap(
802
+ (dependency) => dependency.kind === "target" && dependency.name !== target.name && names.has(dependency.name) ? [dependency.name] : []
803
+ )
804
+ );
633
805
  for (const [file, contents] of Object.entries(files2)) {
634
806
  if (file === "Package.swift" || !file.startsWith(prefix)) continue;
635
807
  if (targets.some(
@@ -646,7 +818,7 @@ function swiftTargetDependencies(files2, targets) {
646
818
  }
647
819
  function swiftArtifactHash(input, release, moduleRoot) {
648
820
  const hash = (0, import_node_crypto2.createHash)("sha256");
649
- hash.update(`swift-compile/v20
821
+ hash.update(`swift-compile/v23
650
822
  ${release}
651
823
  `);
652
824
  if (input.oneModule) hash.update("one-module\n");
@@ -655,6 +827,13 @@ ${release}
655
827
  hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
656
828
  hash.update((0, import_node_crypto2.createHash)("sha256").update(input.files[file]).digest());
657
829
  }
830
+ for (const file of Object.keys(input.metalSources ?? {}).sort()) {
831
+ hash.update("metal-source\n");
832
+ hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
833
+ hash.update(
834
+ (0, import_node_crypto2.createHash)("sha256").update(input.metalSources?.[file] ?? "").digest()
835
+ );
836
+ }
658
837
  for (const base of Object.keys(input.resources ?? {}).sort()) {
659
838
  hash.update((0, import_node_crypto2.createHash)("sha256").update(base).digest());
660
839
  hash.update(
@@ -672,6 +851,19 @@ ${release}
672
851
  hash.update("resource-path\n");
673
852
  hash.update((0, import_node_crypto2.createHash)("sha256").update(file).digest());
674
853
  }
854
+ hash.update(
855
+ JSON.stringify(
856
+ [...input.resourceBundles ?? []].sort((a, b) => a.target.localeCompare(b.target))
857
+ )
858
+ );
859
+ for (const target of Object.keys(input.libraries ?? {}).sort()) {
860
+ hash.update(`library
861
+ ${target}
862
+ `);
863
+ hash.update(
864
+ (0, import_node_crypto2.createHash)("sha256").update(input.libraries?.[target] ?? "").digest()
865
+ );
866
+ }
675
867
  const targets = compileTargets(input);
676
868
  if (targets.length > 0) {
677
869
  hash.update(`swift-multi/v${MULTI_MANIFEST_VERSION}
@@ -707,10 +899,22 @@ function emitSwiftPackageManifest(args) {
707
899
  if (!executable) throw new Error("swift-compile: no executable target to emit");
708
900
  const stanza = (target) => {
709
901
  const isolation = target.mainActorIsolation ? " .defaultIsolation(MainActor.self)," : "";
902
+ const interoperability = target.cxxInterop ? " .interoperabilityMode(.Cxx)," : "";
903
+ const defines = (target.defines ?? []).map((name2) => ` .define(${JSON.stringify(name2)}),`).join("");
710
904
  const imports = distribution ? `, "-I", ${JSON.stringify(import_node_path2.default.join(moduleRoot, "modules"))}` : "";
711
- const swiftSettings = `swiftSettings: [.swiftLanguageMode(.v${target.languageMode}),${isolation} .unsafeFlags(["-Osize"${FOUNDATION_IMPLICIT_FLAGS}${oneModule ? ONE_IMPLICIT_FLAGS : ""}${imports}])]`;
905
+ const native = args.native?.get(target.name);
906
+ const nativeImports = (siblings.get(target.name) ?? []).filter((name2) => args.native?.has(name2)).map(
907
+ (name2) => `, "-Xfrontend", "-import-module", "-Xfrontend", ${JSON.stringify(name2)}`
908
+ ).join("");
909
+ const header = args.headerDirectory && [...args.native?.values() ?? []].some(
910
+ (value) => value.late.producers.includes(target.name)
911
+ ) ? `, "-emit-objc-header-path", ${JSON.stringify(import_node_path2.default.join(args.headerDirectory, `${target.name}-Swift.h`))}` : "";
912
+ const swiftSettings = `swiftSettings: [.swiftLanguageMode(.v${target.languageMode}),${isolation}${interoperability}${defines} .unsafeFlags(["-Osize"${FOUNDATION_IMPLICIT_FLAGS}${oneModule ? ONE_IMPLICIT_FLAGS : ""}${imports}${nativeImports}${header}])]`;
712
913
  const deps = [
713
- ...distribution ? [] : [
914
+ ...target.executable ? [...args.native?.values() ?? []].map(
915
+ (value) => JSON.stringify(value.late.name)
916
+ ) : [],
917
+ ...distribution || native ? [] : [
714
918
  '.product(name: "SwiftUI", package: "sootsim-swift")',
715
919
  ...oneModule ? [ONE_PRODUCT] : [],
716
920
  ...spriteKit ? [SPRITEKIT_PRODUCT] : []
@@ -731,13 +935,15 @@ function emitSwiftPackageManifest(args) {
731
935
  "-Xlinker", "--export-if-defined=__main_argc_argv"${library},
732
936
  ]),
733
937
  .unsafeFlags(["-Xlinker", "--strip-all"], .when(configuration: .release)),
734
- ]` : "";
938
+ ]` : args.libraries?.has(target.name) ? `,
939
+ linkerSettings: [.unsafeFlags([${JSON.stringify(args.libraries.get(target.name))}])]` : "";
735
940
  return [
736
941
  ` ${kind}(`,
737
942
  ` name: "${target.name}",`,
738
943
  ` dependencies: [${deps}],`,
739
944
  ` path: "${target.path}",`,
740
- ` ${swiftSettings}${link}`,
945
+ ...native ? [` exclude: ${JSON.stringify(native.lateSources)},`] : [],
946
+ ` ${native ? "cxxSettings: []" : swiftSettings}${link}`,
741
947
  ` ),`
742
948
  ].join("\n");
743
949
  };
@@ -752,7 +958,16 @@ function emitSwiftPackageManifest(args) {
752
958
  ` dependencies: [${distribution ? "" : `.package(path: "${moduleRoot}")`}],`,
753
959
  " targets: [",
754
960
  ...targets.map(stanza),
755
- " ]",
961
+ ...[...args.native?.entries() ?? []].flatMap(([name2, native]) => [
962
+ ` .target(name: ${JSON.stringify(native.late.name)},`,
963
+ ` dependencies: ${JSON.stringify([name2, ...native.late.producers])},`,
964
+ ` path: ${JSON.stringify(native.path)},`,
965
+ ` sources: ${JSON.stringify(native.lateSources)},`,
966
+ ` publicHeadersPath: "include",`,
967
+ ` cxxSettings: [.unsafeFlags(["-fno-cxx-modules", "-I", ${JSON.stringify(native.includeDirectory)}, "-I", ${JSON.stringify(args.headerDirectory)}])]),`
968
+ ]),
969
+ args.cxxLanguageStandard ? ` ],
970
+ cxxLanguageStandard: .${args.cxxLanguageStandard}` : " ]",
756
971
  ")",
757
972
  ""
758
973
  ].join("\n");
@@ -769,9 +984,10 @@ function syncSwiftTree(base, files2) {
769
984
  };
770
985
  walk2(base);
771
986
  for (const [full, contents] of files2) {
772
- if ((0, import_node_fs2.existsSync)(full) && (0, import_node_fs2.readFileSync)(full, "utf8") === contents) continue;
987
+ if ((0, import_node_fs2.existsSync)(full) && (typeof contents === "string" ? (0, import_node_fs2.readFileSync)(full, "utf8") === contents : (0, import_node_fs2.readFileSync)(full).equals(contents)))
988
+ continue;
773
989
  (0, import_node_fs2.mkdirSync)(import_node_path2.default.dirname(full), { recursive: true });
774
- (0, import_node_fs2.writeFileSync)(full, contents, "utf8");
990
+ (0, import_node_fs2.writeFileSync)(full, contents);
775
991
  }
776
992
  }
777
993
  function splitTopLevel(text3) {
@@ -815,21 +1031,21 @@ function scanObjCExtensionMembers(source, tokens, records) {
815
1031
  const masked = swiftCodeMask(source);
816
1032
  const found = /* @__PURE__ */ new Map();
817
1033
  const member = /\b(func|var)\s+([A-Za-z_]\w*)/g;
818
- for (const record3 of records) {
819
- const close = closingBrace(source, tokens, record3.open);
820
- const body = masked.slice(record3.open, close);
1034
+ for (const record4 of records) {
1035
+ const close = closingBrace(source, tokens, record4.open);
1036
+ const body = masked.slice(record4.open, close);
821
1037
  member.lastIndex = 0;
822
1038
  for (let match = member.exec(body); match; match = member.exec(body)) {
823
- const at = record3.open + match.index + match[0].indexOf(match[1]);
1039
+ const at = record4.open + match.index + match[0].indexOf(match[1]);
824
1040
  let depth = 0;
825
1041
  for (const token of tokens) {
826
- if (token <= record3.open) continue;
1042
+ if (token <= record4.open) continue;
827
1043
  if (token >= at) break;
828
1044
  if (source[token] === "{") depth++;
829
1045
  else if (source[token] === "}") depth--;
830
1046
  }
831
1047
  if (depth > 0) continue;
832
- const head = masked.slice(record3.open, at);
1048
+ const head = masked.slice(record4.open, at);
833
1049
  const bound = Math.max(
834
1050
  head.lastIndexOf(";"),
835
1051
  head.lastIndexOf("{"),
@@ -840,11 +1056,11 @@ function scanObjCExtensionMembers(source, tokens, records) {
840
1056
  prefix
841
1057
  ))
842
1058
  continue;
843
- if (!/@objc\b/.test(prefix) && !/@IBAction\b/.test(prefix) && !record3.objcMembers)
1059
+ if (!/@objc\b/.test(prefix) && !/@IBAction\b/.test(prefix) && !record4.objcMembers)
844
1060
  continue;
845
- const members = found.get(record3.type) ?? /* @__PURE__ */ new Set();
1061
+ const members = found.get(record4.type) ?? /* @__PURE__ */ new Set();
846
1062
  members.add(match[2]);
847
- found.set(record3.type, members);
1063
+ found.set(record4.type, members);
848
1064
  }
849
1065
  }
850
1066
  return found;
@@ -1816,6 +2032,8 @@ function swiftCodeScan(source) {
1816
2032
  return { lineStarts: starts, tokens };
1817
2033
  }
1818
2034
  function writeSwiftProject(layout, input, moduleRoot) {
2035
+ if (input.resourceBundles !== void 0 && !isSwiftResourceBundles(input.resourceBundles))
2036
+ throw new Error("swift compile input has invalid target bundle assignments");
1819
2037
  const resourceTargets = swiftManifestTargets(
1820
2038
  input.files["Package.swift"] ?? "",
1821
2039
  input.resourcePaths
@@ -1832,6 +2050,80 @@ function writeSwiftProject(layout, input, moduleRoot) {
1832
2050
  }
1833
2051
  ] : compileTargets(input);
1834
2052
  const files2 = /* @__PURE__ */ new Map();
2053
+ const native = /* @__PURE__ */ new Map();
2054
+ const headerDirectory = import_node_path2.default.join(layout.root, ".build", "peach-cxx-headers");
2055
+ for (const target of targets) {
2056
+ const prefix = target.path === "." ? "" : `${target.path}/`;
2057
+ const entries = Object.entries(input.files).filter(
2058
+ ([file]) => file.startsWith(prefix) && !targets.some(
2059
+ (candidate) => candidate.name !== target.name && candidate.path !== "." && candidate.path.startsWith(prefix) && file.startsWith(`${candidate.path}/`)
2060
+ )
2061
+ );
2062
+ if (entries.some(([file]) => file.endsWith(".swift"))) continue;
2063
+ const units = entries.filter(([file]) => /\.(?:c|cc|cpp|cxx|m|mm)$/.test(file));
2064
+ if (units.length === 0) continue;
2065
+ const includes = `${prefix}include/`;
2066
+ const consumers = /* @__PURE__ */ new Map();
2067
+ const reverse = /* @__PURE__ */ new Map();
2068
+ for (const [file, contents] of entries) {
2069
+ const producers = /* @__PURE__ */ new Set();
2070
+ const source = contents.replace(/\/\*[\s\S]*?\*\//g, "");
2071
+ for (const match of source.matchAll(/^\s*#\s*include\s*[<"]([^>"]+)[>"]/gm)) {
2072
+ const include = match[1];
2073
+ const producer = /(?:^|\/)([^/]+)-Swift\.h$/.exec(include)?.[1];
2074
+ if (producer && targets.some((value) => value.name === producer))
2075
+ producers.add(producer);
2076
+ const local = [
2077
+ import_node_path2.default.posix.join(import_node_path2.default.posix.dirname(file), include),
2078
+ `${includes}${include}`
2079
+ ].find((candidate) => input.files[candidate] !== void 0);
2080
+ if (local) {
2081
+ reverse.set(local, [...reverse.get(local) ?? [], file]);
2082
+ }
2083
+ }
2084
+ consumers.set(file, producers);
2085
+ }
2086
+ const queue = [...consumers.keys()].filter((file) => consumers.get(file).size > 0);
2087
+ for (let index = 0; index < queue.length; index++) {
2088
+ const file = queue[index];
2089
+ for (const parent of reverse.get(file) ?? []) {
2090
+ const inherited = consumers.get(parent);
2091
+ const before = inherited.size;
2092
+ for (const producer of consumers.get(file)) inherited.add(producer);
2093
+ if (before !== inherited.size) queue.push(parent);
2094
+ }
2095
+ }
2096
+ const late = units.filter(([file]) => consumers.get(file).size > 0);
2097
+ if (late.length === 0) continue;
2098
+ native.set(target.name, {
2099
+ path: target.path,
2100
+ includeDirectory: import_node_path2.default.join(layout.root, includes),
2101
+ lateSources: late.map(([file]) => file.slice(prefix.length)),
2102
+ late: {
2103
+ name: `${target.name}SwiftImplementation`,
2104
+ producers: [
2105
+ ...new Set(late.flatMap(([file]) => [...consumers.get(file)]))
2106
+ ].sort()
2107
+ }
2108
+ });
2109
+ if (input.files[`${includes}module.modulemap`] !== void 0) continue;
2110
+ const roots = entries.filter(
2111
+ ([file]) => file.startsWith(includes) && /\.(?:h|hpp|hh|hxx)$/.test(file) && consumers.get(file).size === 0
2112
+ );
2113
+ files2.set(
2114
+ import_node_path2.default.join(layout.root, includes, "module.modulemap"),
2115
+ [
2116
+ `module ${target.name} {`,
2117
+ ...roots.map(
2118
+ ([file]) => ` header ${JSON.stringify(file.slice(includes.length))}`
2119
+ ),
2120
+ " export *",
2121
+ "}",
2122
+ ""
2123
+ ].join("\n")
2124
+ );
2125
+ }
2126
+ if (native.size > 0) (0, import_node_fs2.mkdirSync)(headerDirectory, { recursive: true });
1835
2127
  const modulePlan = (0, import_node_crypto2.createHash)("sha256").update(
1836
2128
  swiftModuleInputFiles(moduleRoot).join("\n")
1837
2129
  );
@@ -1840,7 +2132,11 @@ function writeSwiftProject(layout, input, moduleRoot) {
1840
2132
  }
1841
2133
  const resourcePlan = (0, import_node_crypto2.createHash)("sha256").update(
1842
2134
  JSON.stringify(
1843
- resourceTargets.filter((target) => target.resources).map((target) => [target.name, target.path])
2135
+ resourceTargets.filter((target) => target.resources).map((target) => [
2136
+ target.name,
2137
+ target.path,
2138
+ input.resourceBundles?.find((bundle) => bundle.target === target.name)?.path
2139
+ ])
1844
2140
  )
1845
2141
  ).digest("hex").slice(0, 16);
1846
2142
  const moduleFiles = `
@@ -1900,6 +2196,12 @@ function writeSwiftProject(layout, input, moduleRoot) {
1900
2196
  files2.set(import_node_path2.default.join(layout.root, "Package.swift"), manifest + moduleFiles);
1901
2197
  } else {
1902
2198
  const base = layout.multi ? layout.root : layout.sources;
2199
+ const libraries = /* @__PURE__ */ new Map();
2200
+ for (const [target, data] of Object.entries(input.libraries ?? {})) {
2201
+ const file = import_node_path2.default.join(layout.root, ".peach-libraries", `lib${target}.a`);
2202
+ files2.set(file, Buffer.from(data, "base64"));
2203
+ libraries.set(target, file);
2204
+ }
1903
2205
  for (const [file, contents] of Object.entries(input.files)) {
1904
2206
  if (file === "Package.swift") continue;
1905
2207
  files2.set(
@@ -1911,6 +2213,11 @@ function writeSwiftProject(layout, input, moduleRoot) {
1911
2213
  import_node_path2.default.join(layout.root, "Package.swift"),
1912
2214
  emitSwiftPackageManifest({
1913
2215
  targets,
2216
+ native,
2217
+ headerDirectory: native.size > 0 ? headerDirectory : void 0,
2218
+ cxxLanguageStandard: swiftManifestCxxLanguageStandard(
2219
+ input.files["Package.swift"] ?? ""
2220
+ ),
1914
2221
  siblings: swiftTargetDependencies(
1915
2222
  input.files,
1916
2223
  layout.multi ? targets : targets.map((target) => ({ ...target, path: "." }))
@@ -1918,12 +2225,20 @@ function writeSwiftProject(layout, input, moduleRoot) {
1918
2225
  moduleRoot,
1919
2226
  oneModule: input.oneModule,
1920
2227
  spriteKit: importsSpriteKit(input.files),
1921
- distribution: distribution ?? void 0
2228
+ distribution: distribution ?? void 0,
2229
+ libraries
1922
2230
  }) + moduleFiles
1923
2231
  );
1924
2232
  }
1925
2233
  for (const target of resourceTargets) {
1926
2234
  if (!target.resources) continue;
2235
+ const bundle = input.resourceBundles?.find(
2236
+ (candidate) => candidate.target === target.name
2237
+ );
2238
+ if (!bundle || bundle.sourcePath !== target.path)
2239
+ throw new Error(
2240
+ `swift resource target ${target.name} has no matching bundle assignment`
2241
+ );
1927
2242
  const directory = layout.multi ? import_node_path2.default.join(layout.root, target.path) : layout.sources;
1928
2243
  files2.set(
1929
2244
  import_node_path2.default.join(directory, "ResourceBundleAccessor.generated.swift"),
@@ -1931,7 +2246,11 @@ function writeSwiftProject(layout, input, moduleRoot) {
1931
2246
  "import Foundation",
1932
2247
  "",
1933
2248
  "extension Foundation.Bundle {",
1934
- " static let module: Bundle = Bundle.main",
2249
+ bundle.path === "." ? " static let module: Bundle = Bundle.main" : ` static let module: Bundle = {
2250
+ let url = Bundle.main.bundleURL.appendingPathComponent(${JSON.stringify(bundle.path)})
2251
+ guard let bundle = Bundle(url: url) else { fatalError("resource bundle is missing: ${bundle.target}") }
2252
+ return bundle
2253
+ }()`,
1935
2254
  "}",
1936
2255
  ""
1937
2256
  ].join("\n")
@@ -1996,7 +2315,7 @@ function compileSwiftProjectUnit(input, options) {
1996
2315
  };
1997
2316
  const hash = swiftArtifactHash(input, options.release, moduleRoot);
1998
2317
  return serialize(root, async () => {
1999
- if (process.platform === "darwin" && process.env.TM_SESSION && process.env.CI !== "true" && process.env.CONTRAST_HEAVY_ADMITTED !== "1" && moduleRoot === import_node_path2.default.resolve(__sootsim_import_meta_dirname, "../../sootsim-swift") && !swiftDistributionAt(moduleRoot)) {
2318
+ if (process.platform === "darwin" && process.env.TM_SESSION && process.env.CI !== "true" && moduleRoot === import_node_path2.default.resolve(__sootsim_import_meta_dirname, "../../sootsim-swift") && !swiftDistributionAt(moduleRoot)) {
2000
2319
  const requestRoot = (0, import_node_fs2.mkdtempSync)(import_node_path2.default.join(root, ".builder-request-"));
2001
2320
  const output = import_node_path2.default.join(requestRoot, "output");
2002
2321
  const request = import_node_path2.default.join(requestRoot, "request.json");
@@ -2021,7 +2340,11 @@ function compileSwiftProjectUnit(input, options) {
2021
2340
  "--output",
2022
2341
  output
2023
2342
  ],
2024
- { cwd: repo, timeout: options.timeoutMs, maxBuffer: 64 * 1024 * 1024 }
2343
+ {
2344
+ cwd: repo,
2345
+ timeout: options.timeoutMs,
2346
+ maxBuffer: 64 * 1024 * 1024
2347
+ }
2025
2348
  );
2026
2349
  pending.child.stdout?.on("data", (bytes2) => process.stdout.write(bytes2));
2027
2350
  pending.child.stderr?.on("data", (bytes2) => process.stderr.write(bytes2));
@@ -2053,7 +2376,11 @@ function compileSwiftProjectUnit(input, options) {
2053
2376
  `swift-compile: no swift.org toolchain at ${options.toolchainBin}; install ${options.release} and its matching wasm sdk`
2054
2377
  );
2055
2378
  }
2056
- const macroPlugins = await ensureMacroPlugins(moduleRoot, options.toolchainBin);
2379
+ const macroPlugins = await ensureMacroPlugins(
2380
+ moduleRoot,
2381
+ options.toolchainBin,
2382
+ options.release
2383
+ );
2057
2384
  const sdk = swiftModuleSdk(moduleRoot).wasmSdkId;
2058
2385
  const icu = swiftIcuAsset(options.toolchainBin, sdk);
2059
2386
  try {
@@ -2061,6 +2388,8 @@ function compileSwiftProjectUnit(input, options) {
2061
2388
  swift,
2062
2389
  [
2063
2390
  "build",
2391
+ "--build-system",
2392
+ "native",
2064
2393
  "-j",
2065
2394
  BUILD_JOBS,
2066
2395
  "--package-path",
@@ -2119,6 +2448,7 @@ var init_swift_compile = __esm({
2119
2448
  init_swift_distribution_contract();
2120
2449
  init_swift_icu();
2121
2450
  init_swift_package();
2451
+ init_swiftContract();
2122
2452
  run2 = (0, import_node_util2.promisify)(import_node_child_process3.execFile);
2123
2453
  WASM_TRIPLE = "wasm32-unknown-wasip1";
2124
2454
  cores = import_node_os.default.availableParallelism();
@@ -2141,6 +2471,7 @@ var init_swift_compile = __esm({
2141
2471
  TikTokBusinessSDK: [],
2142
2472
  nSure: [],
2143
2473
  Statsig: [],
2474
+ TOCropViewController: [],
2144
2475
  RiveRuntime: ["MetalKit"],
2145
2476
  MetalKit: [],
2146
2477
  DatadogCore: [],
@@ -2155,6 +2486,7 @@ var init_swift_compile = __esm({
2155
2486
  Intercom: [],
2156
2487
  FirebaseCore: [],
2157
2488
  FirebaseSharedSwift: ["FirebaseCore"],
2489
+ FirebaseRemoteConfig: ["FirebaseCore", "FirebaseSharedSwift"],
2158
2490
  FirebaseFirestoreInternal: ["FirebaseCore"],
2159
2491
  FirebaseAnalytics: ["FirebaseCore"],
2160
2492
  FirebaseAppCheck: ["FirebaseCore"],
@@ -2162,6 +2494,7 @@ var init_swift_compile = __esm({
2162
2494
  FirebaseCrashlytics: ["FirebaseCore"],
2163
2495
  FirebaseFirestore: ["FirebaseCore", "FirebaseSharedSwift", "FirebaseFirestoreInternal"],
2164
2496
  FirebaseStorage: ["FirebaseCore"],
2497
+ GoogleMobileAds: [],
2165
2498
  FBAudienceNetwork: []
2166
2499
  };
2167
2500
  OPTIONAL_FRAMEWORKS = new Set(Object.keys(OPTIONAL_FRAMEWORK_DEPENDENCIES));
@@ -2177,14 +2510,14 @@ var init_swift_compile = __esm({
2177
2510
  "PeachObjC",
2178
2511
  "Dispatch",
2179
2512
  "FoundationNetworking",
2180
- "WASILibc",
2181
- "wasi_pthread"
2513
+ "PeachFoundationCombine",
2514
+ "Darwin"
2182
2515
  ];
2183
2516
  FOUNDATION_IMPLICIT_FLAGS = FOUNDATION_IMPLICIT_MODULES.map(
2184
2517
  (module2) => `, "-Xfrontend", "-import-module", "-Xfrontend", "${module2}"`
2185
2518
  ).join("");
2186
2519
  ONE_IMPLICIT_FLAGS = ', "-Xfrontend", "-import-module", "-Xfrontend", "One"';
2187
- MULTI_MANIFEST_VERSION = 12;
2520
+ MULTI_MANIFEST_VERSION = 18;
2188
2521
  DIRECTIVE = /^[ \t]*#(?:if|elseif)\b/;
2189
2522
  OS_IOS = /\bos\(\s*iOS\s*\)/g;
2190
2523
  OBJC_ATTRIBUTE = /^@(?:objcMembers\b|objc\b(?:(\s*)\(([^()]*)\))?|IBAction\b)/;
@@ -2372,24 +2705,135 @@ var swift_distribution_lock_default;
2372
2705
  var init_swift_distribution_lock = __esm({
2373
2706
  "src/swift-distribution.lock.json"() {
2374
2707
  swift_distribution_lock_default = {
2375
- id: "dd4939815d74c469",
2376
- release: "swift-6.3-RELEASE",
2377
- wasmSdkId: "swift-6.3-RELEASE_wasm-peach-41921598-peach-edd95e37",
2378
- wasmSdkUrl: "https://r2.rnxsim.com/swift-wasm-sdk/swift-6.3-RELEASE_wasm-peach-41921598-peach-edd95e37.artifactbundle.tar.gz",
2379
- wasmSdkChecksum: "e8b76a6b78c21e9e16a6943a691ea001aeb0a18518d775a26130520303eb5c6d",
2380
- source: "65037f7f51d461fcf993549d206b3a5dfaa1f58f",
2708
+ id: "3246f7e5ccb24952",
2709
+ release: "swift-6.4.0-RELEASE",
2710
+ wasmSdkId: "swift-6.4.0-RELEASE_wasm-peach-e4629014",
2711
+ wasmSdkUrl: "https://r2.sootsim.com/swift-wasm-sdk/swift-6.4.0-RELEASE_wasm-peach-e4629014.artifactbundle.tar.gz",
2712
+ wasmSdkChecksum: "522f3ff98e951872d64751376d45e36394e572ec2aa4e0a52fb0027824bf41f6",
2713
+ source: "a1e87668019272a329f6d9fada213573e3c5107f",
2714
+ targets: [
2715
+ "ExpoModulesCore",
2716
+ "PeachJSON",
2717
+ "PeachFoundation",
2718
+ "FoundationNetworking",
2719
+ "DeveloperToolsSupport",
2720
+ "SwiftUI",
2721
+ "Dispatch",
2722
+ "Darwin",
2723
+ "MachO",
2724
+ "ObjectiveC",
2725
+ "PeachObjC",
2726
+ "UIKitCore",
2727
+ "PeachICU",
2728
+ "CoreImage",
2729
+ "QuartzCore",
2730
+ "CoreGraphics",
2731
+ "AppTrackingTransparency",
2732
+ "StoreKit",
2733
+ "WeatherKit",
2734
+ "DeviceCheck",
2735
+ "PassKit",
2736
+ "Translation",
2737
+ "CoreSpotlight",
2738
+ "CoreVideo",
2739
+ "CoreMedia",
2740
+ "VideoToolbox",
2741
+ "AdSupport",
2742
+ "BackgroundTasks",
2743
+ "LocalAuthentication",
2744
+ "CoreMotion",
2745
+ "MessageUI",
2746
+ "GameController",
2747
+ "MetricKit",
2748
+ "Metal",
2749
+ "AuthenticationServices",
2750
+ "os",
2751
+ "Charts",
2752
+ "Symbols",
2753
+ "UniformTypeIdentifiers",
2754
+ "WidgetKit",
2755
+ "ActivityKit",
2756
+ "SwiftData",
2757
+ "UIKit",
2758
+ "Combine",
2759
+ "PeachFoundationCombine",
2760
+ "AppIntents",
2761
+ "PhotosUI",
2762
+ "AVFoundation",
2763
+ "AVFAudio",
2764
+ "UserNotifications",
2765
+ "Network",
2766
+ "OSLog",
2767
+ "Security",
2768
+ "Accelerate",
2769
+ "CoreText",
2770
+ "CommonCrypto",
2771
+ "PeachCodec",
2772
+ "Compression",
2773
+ "zlib",
2774
+ "CryptoKit",
2775
+ "TikTokBusinessSDK",
2776
+ "nSure",
2777
+ "Statsig",
2778
+ "TOCropViewController",
2779
+ "RiveRuntime",
2780
+ "MetalKit",
2781
+ "DatadogCore",
2782
+ "DatadogLogs",
2783
+ "DatadogRUM",
2784
+ "DatadogTrace",
2785
+ "AppLovinSDK",
2786
+ "GoogleMobileAds",
2787
+ "AppsFlyerLib",
2788
+ "FBSDKCoreKit",
2789
+ "FBAEMKit",
2790
+ "FBSDKCoreKit_Basics",
2791
+ "FirebaseCore",
2792
+ "FirebaseSharedSwift",
2793
+ "FirebaseRemoteConfig",
2794
+ "FirebaseFirestoreInternal",
2795
+ "FirebaseAnalytics",
2796
+ "FirebaseAppCheck",
2797
+ "FirebaseAuth",
2798
+ "FirebaseCrashlytics",
2799
+ "FirebaseFirestore",
2800
+ "FirebaseStorage",
2801
+ "Intercom",
2802
+ "FBAudienceNetwork",
2803
+ "NaturalLanguage",
2804
+ "AVKit",
2805
+ "SafariServices",
2806
+ "QuickLook",
2807
+ "CoreHaptics",
2808
+ "WebKit",
2809
+ "Photos",
2810
+ "LinkPresentation",
2811
+ "FoundationModels",
2812
+ "CoreTransferable",
2813
+ "AudioToolbox",
2814
+ "Intents",
2815
+ "MobileCoreServices",
2816
+ "ImageIO",
2817
+ "CoreLocationEnums",
2818
+ "CoreLocation",
2819
+ "MapKit",
2820
+ "One",
2821
+ "GameplayKit",
2822
+ "SpriteKit",
2823
+ "PeachBox2D"
2824
+ ],
2381
2825
  hosts: {
2382
2826
  "darwin-arm64": {
2383
- sha256: "d15d78a731a2613ab34d199f45aec8d4a6046ce54155f321ea67f9f7476e5dfd",
2384
- size: 47319022
2827
+ sha256: "fb5a3e9db1eae670b5ba84432345e52de8b55b5fdf0e468ff95dfa25a62a6968",
2828
+ size: 55050821
2385
2829
  },
2386
2830
  "linux-x64": {
2387
- sha256: "0bcedbd713a5682041638fb99204b20b62437636302359f9a60fa2cb103acbf2",
2388
- size: 47324525
2831
+ sha256: "46a0eae68905751ee7c6536e79f626bc71d9d5a22cf9a76ce12b8d6c3aa34293",
2832
+ size: 55056198
2389
2833
  },
2390
2834
  "linux-arm64": {
2391
- sha256: "dfb7d0abf23a5c006ad81b06d4263b01f903220d5b15717d5c224f959c0e422f",
2392
- size: 47323454
2835
+ sha256: "ea6453b55ff428d4d152d829731504400d49969ab0750264a931eaf0d7659122",
2836
+ size: 55055213
2393
2837
  }
2394
2838
  }
2395
2839
  };
@@ -2397,6 +2841,9 @@ var init_swift_distribution_lock = __esm({
2397
2841
  });
2398
2842
 
2399
2843
  // src/swift-toolchain.ts
2844
+ function swiftCompilerRelease(release) {
2845
+ return release.replace(/^swift-(\d+\.\d+)\.0-RELEASE$/, "swift-$1-RELEASE");
2846
+ }
2400
2847
  function swiftToolchainBin(release) {
2401
2848
  if (process.platform === "darwin") {
2402
2849
  const dirs = [import_node_os4.default.homedir(), "/"].map(
@@ -2420,7 +2867,7 @@ function swiftToolchain(release, sdk) {
2420
2867
  encoding: "utf8",
2421
2868
  stdio: ["ignore", "pipe", "pipe"]
2422
2869
  }) : "";
2423
- if (!version.includes(`(${release})`)) {
2870
+ if (!version.includes(`(${swiftCompilerRelease(release)})`)) {
2424
2871
  const found = version ? `found ${version.split("\n")[0]?.trim()} at ${swift}` : process.platform === "darwin" ? `found no ${release}.xctoolchain in ~/Library/Developer/Toolchains or /Library/Developer/Toolchains` : "found no swift on PATH";
2425
2872
  throw new Error(
2426
2873
  `peach compiles Swift with the swift.org toolchain ${release} and its wasm SDK; ${found}.
@@ -2518,12 +2965,15 @@ function swiftDistributionRoot() {
2518
2965
  }
2519
2966
  return pending;
2520
2967
  }
2521
- async function resolveSwiftModule() {
2968
+ async function resolveSwiftModule({
2969
+ required = false
2970
+ } = {}) {
2522
2971
  const checkout2 = import_node_path6.default.resolve(__sootsim_import_meta_dirname, "../../sootsim-swift");
2523
2972
  if (import_node_fs5.default.existsSync(import_node_path6.default.join(checkout2, "toolchain.json"))) {
2524
2973
  try {
2525
2974
  return { root: checkout2, ...swiftModuleToolchain(checkout2) };
2526
- } catch {
2975
+ } catch (error) {
2976
+ if (required) throw error;
2527
2977
  return null;
2528
2978
  }
2529
2979
  }
@@ -2563,20 +3013,20 @@ function baseOf(file) {
2563
3013
  const slash = file.lastIndexOf("/");
2564
3014
  return slash < 0 ? file : file.slice(slash + 1);
2565
3015
  }
2566
- function pickImagesetFile(images) {
2567
- const rasters = images.filter(
2568
- (image) => typeof image.filename === "string" && RASTER_EXTENSIONS.has(extOf(image.filename))
2569
- );
2570
- if (rasters.length === 0) return null;
2571
- const rank = (scale) => scale === "2x" ? 0 : scale === "3x" ? 1 : scale === "1x" ? 2 : 3;
2572
- const pick = [...rasters].sort((a, b) => rank(a.scale) - rank(b.scale))[0];
2573
- return {
2574
- filename: pick.filename,
2575
- scale: pick.scale === "2x" ? 2 : pick.scale === "3x" ? 3 : 1
2576
- };
3016
+ function imagesetFiles(images) {
3017
+ return images.flatMap((image) => {
3018
+ if (typeof image.filename !== "string" || !RASTER_EXTENSIONS.has(extOf(image.filename)) || image.filename.includes("/") || image.filename.includes("\\"))
3019
+ return [];
3020
+ return [
3021
+ {
3022
+ filename: image.filename,
3023
+ scale: image.scale === "2x" ? 2 : image.scale === "3x" ? 3 : 1
3024
+ }
3025
+ ];
3026
+ }).sort((a, b) => a.scale - b.scale);
2577
3027
  }
2578
- function flatScale(path16) {
2579
- const match = /@([23])x\.[^./]+$/.exec(path16);
3028
+ function flatScale(path18) {
3029
+ const match = /@([23])x\.[^./]+$/.exec(path18);
2580
3030
  return match ? Number(match[1]) : 1;
2581
3031
  }
2582
3032
  function providesNamespace(text3) {
@@ -2603,16 +3053,26 @@ function catalogEntryName(contentsPath, kind, readText) {
2603
3053
  }
2604
3054
  return segments.join("/");
2605
3055
  }
2606
- function isFlatResourceImage(path16) {
2607
- return path16.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path16));
3056
+ function isFlatResourceImage(path18) {
3057
+ return path18.split("/").includes("Resources") && RASTER_EXTENSIONS.has(extOf(path18));
2608
3058
  }
2609
- function enumerateBundleImages(paths, readText) {
3059
+ function enumerateBundleImages(paths, readText, bundleForFile = () => ".") {
2610
3060
  const table = [];
2611
- const seen = /* @__PURE__ */ new Set();
2612
- const catalogs = paths.filter((path16) => baseOf(path16) === "Contents.json");
3061
+ const seen = /* @__PURE__ */ new Map();
3062
+ const namesFor = (file) => {
3063
+ const bundlePath = bundleForFile(file);
3064
+ if (!isSwiftBundlePath(bundlePath))
3065
+ throw new Error(`invalid image bundle ${bundlePath}`);
3066
+ const names = seen.get(bundlePath) ?? /* @__PURE__ */ new Set();
3067
+ seen.set(bundlePath, names);
3068
+ return { bundlePath, names };
3069
+ };
3070
+ const catalogs = paths.filter((path18) => baseOf(path18) === "Contents.json");
2613
3071
  for (const contentsPath of catalogs) {
2614
3072
  const name2 = catalogEntryName(contentsPath, ".imageset", readText);
2615
- if (name2 === null || seen.has(name2)) continue;
3073
+ if (name2 === null) continue;
3074
+ const { bundlePath, names } = namesFor(contentsPath);
3075
+ if (names.has(name2)) continue;
2616
3076
  let parsed;
2617
3077
  try {
2618
3078
  const text3 = readText(contentsPath);
@@ -2624,18 +3084,24 @@ function enumerateBundleImages(paths, readText) {
2624
3084
  const images = parsed?.images;
2625
3085
  if (!Array.isArray(images)) continue;
2626
3086
  const dir = contentsPath.split("/").slice(0, -1).join("/");
2627
- const pick = pickImagesetFile(images);
2628
- if (pick === null || pick.filename.includes("/") || pick.filename.includes("\\"))
2629
- continue;
2630
- seen.add(name2);
2631
- table.push({ name: name2, file: `${dir}/${pick.filename}`, scale: pick.scale });
3087
+ const variants = imagesetFiles(images);
3088
+ if (variants.length === 0) continue;
3089
+ names.add(name2);
3090
+ for (const variant of variants)
3091
+ table.push({
3092
+ name: name2,
3093
+ file: `${dir}/${variant.filename}`,
3094
+ scale: variant.scale,
3095
+ bundlePath
3096
+ });
2632
3097
  }
2633
- for (const path16 of paths) {
2634
- if (!isFlatResourceImage(path16)) continue;
2635
- const name2 = baseOf(path16).slice(0, -extOf(path16).length);
2636
- if (name2 === "" || seen.has(name2)) continue;
2637
- seen.add(name2);
2638
- table.push({ name: name2, file: path16, scale: flatScale(path16) });
3098
+ for (const path18 of paths) {
3099
+ if (!isFlatResourceImage(path18)) continue;
3100
+ const name2 = baseOf(path18).slice(0, -extOf(path18).length);
3101
+ const { bundlePath, names } = namesFor(path18);
3102
+ if (name2 === "" || names.has(name2)) continue;
3103
+ names.add(name2);
3104
+ table.push({ name: name2, file: path18, scale: flatScale(path18), bundlePath });
2639
3105
  }
2640
3106
  return table;
2641
3107
  }
@@ -2664,7 +3130,7 @@ function colorsetValue(color) {
2664
3130
  }
2665
3131
  function enumerateBundleColors(paths, readText) {
2666
3132
  const table = {};
2667
- for (const contentsPath of paths.filter((path16) => baseOf(path16) === "Contents.json")) {
3133
+ for (const contentsPath of paths.filter((path18) => baseOf(path18) === "Contents.json")) {
2668
3134
  const name2 = catalogEntryName(contentsPath, ".colorset", readText);
2669
3135
  if (name2 === null || name2 in table) continue;
2670
3136
  let colors;
@@ -2728,8 +3194,8 @@ function axisExtremes(points) {
2728
3194
  function pathBounds(d) {
2729
3195
  const xs = [];
2730
3196
  const ys = [];
2731
- for (const path16 of d)
2732
- (0, import_svgpath.default)(path16).abs().unshort().unarc().iterate((segment2, _index, x, y) => {
3197
+ for (const path18 of d)
3198
+ (0, import_svgpath.default)(path18).abs().unshort().unarc().iterate((segment2, _index, x, y) => {
2733
3199
  const [command, ...args] = segment2;
2734
3200
  if (command === "M" || command === "L") {
2735
3201
  xs.push(Number(args[0]));
@@ -2754,11 +3220,14 @@ function guide(svg, id, axis) {
2754
3220
  const value = line === void 0 ? void 0 : new RegExp(`${axis}="([-0-9.]+)"`).exec(line)?.[1];
2755
3221
  return value === void 0 ? null : Number(value);
2756
3222
  }
2757
- function enumerateBundleSymbols(paths, readText) {
3223
+ function enumerateBundleSymbols(paths, readText, bundleForFile) {
2758
3224
  const table = {};
2759
- for (const contentsPath of paths.filter((path16) => baseOf(path16) === "Contents.json")) {
3225
+ for (const contentsPath of paths.filter((path18) => baseOf(path18) === "Contents.json")) {
2760
3226
  const name2 = catalogEntryName(contentsPath, ".symbolset", readText);
2761
- if (name2 === null || name2 in table) continue;
3227
+ if (name2 === null) continue;
3228
+ const bundle = bundleForFile(contentsPath);
3229
+ const key2 = bundle === "." ? name2 : `${bundle}/${name2}`;
3230
+ if (Object.hasOwn(table, key2)) continue;
2762
3231
  let symbols;
2763
3232
  try {
2764
3233
  symbols = Reflect.get(JSON.parse(readText(contentsPath) ?? "null") ?? {}, "symbols");
@@ -2785,22 +3254,27 @@ function enumerateBundleSymbols(paths, readText) {
2785
3254
  const translateY = 12 - (box.minY + box.maxY) * scale / 2;
2786
3255
  const advance = Math.round((right - left) * scale * 1e3) / 1e3;
2787
3256
  const tall = (box.maxY - box.minY) * scale + 0.175 * 24;
2788
- table[name2] = {
3257
+ table[key2] = {
2789
3258
  viewBox: "0 0 24 24",
2790
3259
  advance,
2791
3260
  box: [12 - advance / 2, 12 - tall / 2, advance, tall],
2792
- layers: d.map((path16) => ({
2793
- d: (0, import_svgpath.default)(path16).abs().scale(scale).translate(translateX, translateY).round(3).toString()
3261
+ layers: d.map((path18) => ({
3262
+ d: (0, import_svgpath.default)(path18).abs().scale(scale).translate(translateX, translateY).round(3).toString()
2794
3263
  }))
2795
3264
  };
2796
3265
  }
2797
3266
  return table;
2798
3267
  }
2799
- function enumerateBundleJson(paths) {
3268
+ function isCopiedResource(path18) {
3269
+ const ext = extOf(path18);
3270
+ if (ext === ".json" || ext === ".xcstrings") return true;
3271
+ return !RASTER_EXTENSIONS.has(ext) && !COMPILED_EXTENSIONS.has(ext) && !path18.split("/").some((segment2) => NESTED_BUNDLE_DIR.test(segment2));
3272
+ }
3273
+ function enumerateBundleResources(paths) {
2800
3274
  const found = [];
2801
3275
  const seen = /* @__PURE__ */ new Set();
2802
3276
  const ordered = [...paths].filter(
2803
- (path16) => path16.split("/").includes("Resources") && path16.split("/").every((segment2) => segment2 !== "" && segment2 !== "..") && (extOf(path16) === ".json" || extOf(path16) === ".xcstrings")
3277
+ (path18) => path18.split("/").includes("Resources") && path18.split("/").every((segment2) => segment2 !== "" && segment2 !== "..") && isCopiedResource(path18)
2804
3278
  ).sort((a, b) => {
2805
3279
  const aDepth = a.split("/").length;
2806
3280
  const bDepth = b.split("/").length;
@@ -2814,11 +3288,13 @@ function enumerateBundleJson(paths) {
2814
3288
  }
2815
3289
  return found;
2816
3290
  }
2817
- var import_svgpath, RASTER_EXTENSIONS, SYMBOL_CAP_HEIGHT_EM;
3291
+ var import_svgpath, RASTER_EXTENSIONS, SYMBOL_CAP_HEIGHT_EM, COMPILED_EXTENSIONS, NESTED_BUNDLE_DIR;
2818
3292
  var init_bundleAssets = __esm({
2819
3293
  "../sootsim-swift/src/bundleAssets.ts"() {
2820
3294
  "use strict";
2821
3295
  import_svgpath = __toESM(require("svgpath"), 1);
3296
+ init_assetCatalogContract();
3297
+ init_swiftContract();
2822
3298
  RASTER_EXTENSIONS = /* @__PURE__ */ new Set([
2823
3299
  ".avif",
2824
3300
  ".bmp",
@@ -2830,6 +3306,8 @@ var init_bundleAssets = __esm({
2830
3306
  ".webp"
2831
3307
  ]);
2832
3308
  SYMBOL_CAP_HEIGHT_EM = 1443 / 2048;
3309
+ COMPILED_EXTENSIONS = /* @__PURE__ */ new Set([".swift", ".metal", ".storyboard", ".xib"]);
3310
+ NESTED_BUNDLE_DIR = /\.(?:xcassets|lproj)$/;
2833
3311
  }
2834
3312
  });
2835
3313
 
@@ -3316,7 +3794,7 @@ function frameworkModules(paths) {
3316
3794
  }
3317
3795
  async function range(url, spec) {
3318
3796
  const response = await fetch(url, {
3319
- headers: { range: `bytes=${spec}` },
3797
+ headers: { range: `bytes=${spec}`, "accept-encoding": "identity" },
3320
3798
  signal: AbortSignal.timeout(2e4)
3321
3799
  });
3322
3800
  const match = /^bytes (\d+)-\d+\/(\d+)$/.exec(
@@ -3345,9 +3823,9 @@ async function zipEntries(url) {
3345
3823
  if (locator < 0 || tail.bytes.readUInt32LE(locator) !== 117853008)
3346
3824
  throw new Error(`${url} has no zip64 locator`);
3347
3825
  const recordAt = Number(tail.bytes.readBigUInt64LE(locator + 8));
3348
- const record3 = recordAt >= tail.start ? tail.bytes.subarray(recordAt - tail.start) : (await range(url, `${recordAt}-${recordAt + 55}`)).bytes;
3349
- size = Number(record3.readBigUInt64LE(40));
3350
- offset = Number(record3.readBigUInt64LE(48));
3826
+ const record4 = recordAt >= tail.start ? tail.bytes.subarray(recordAt - tail.start) : (await range(url, `${recordAt}-${recordAt + 55}`)).bytes;
3827
+ size = Number(record4.readBigUInt64LE(40));
3828
+ offset = Number(record4.readBigUInt64LE(48));
3351
3829
  }
3352
3830
  const directory = offset >= tail.start ? tail.bytes.subarray(offset - tail.start, offset - tail.start + size) : (await range(url, `${offset}-${offset + size - 1}`)).bytes;
3353
3831
  const names = [];
@@ -3405,7 +3883,7 @@ function dump(dir) {
3405
3883
  ).digest("hex").slice(0, 32);
3406
3884
  const cached2 = import_node_path9.default.join(cacheDir2, `${key2}.json`);
3407
3885
  if (import_node_fs8.default.existsSync(cached2))
3408
- return { key: key2, raw: record2(JSON.parse(import_node_fs8.default.readFileSync(cached2, "utf8"))) };
3886
+ return { key: key2, raw: record3(JSON.parse(import_node_fs8.default.readFileSync(cached2, "utf8"))) };
3409
3887
  let output;
3410
3888
  try {
3411
3889
  output = (0, import_node_child_process6.execFileSync)(
@@ -3434,13 +3912,13 @@ function dump(dir) {
3434
3912
  }
3435
3913
  import_node_fs8.default.mkdirSync(cacheDir2, { recursive: true });
3436
3914
  import_node_fs8.default.writeFileSync(cached2, output);
3437
- return { key: key2, raw: record2(JSON.parse(output)) };
3915
+ return { key: key2, raw: record3(JSON.parse(output)) };
3438
3916
  }
3439
3917
  function defaultTraits(manifest) {
3440
3918
  const traits = new Map(
3441
3919
  array(manifest.traits).map((trait) => [
3442
- string(record2(trait).name) ?? "",
3443
- array(record2(trait).enabledTraits).flatMap((name2) => string(name2) ?? [])
3920
+ string(record3(trait).name) ?? "",
3921
+ array(record3(trait).enabledTraits).flatMap((name2) => string(name2) ?? [])
3444
3922
  ])
3445
3923
  );
3446
3924
  const enabled = /* @__PURE__ */ new Set();
@@ -3454,7 +3932,7 @@ function defaultTraits(manifest) {
3454
3932
  return enabled;
3455
3933
  }
3456
3934
  function linksOnIOS(condition, traits) {
3457
- const { platformNames, traits: needed } = record2(condition);
3935
+ const { platformNames, traits: needed } = record3(condition);
3458
3936
  const platforms = array(platformNames);
3459
3937
  if (platforms.length > 0 && !platforms.includes("ios")) return false;
3460
3938
  const gates = array(needed);
@@ -3467,7 +3945,7 @@ function swiftPackageManifest(dir) {
3467
3945
  if (known) return known;
3468
3946
  const traits = defaultTraits(raw2);
3469
3947
  const tools = Number(
3470
- /^(\d+)/.exec(string(record2(raw2.toolsVersion)._version) ?? "5")?.[1] ?? 5
3948
+ /^(\d+)/.exec(string(record3(raw2.toolsVersion)._version) ?? "5")?.[1] ?? 5
3471
3949
  );
3472
3950
  const declaredModes = array(raw2.swiftLanguageVersions).flatMap((mode) => {
3473
3951
  const major = Number(/^(\d+)/.exec(string(mode) ?? "")?.[1]);
@@ -3479,11 +3957,11 @@ function swiftPackageManifest(dir) {
3479
3957
  );
3480
3958
  const packages = /* @__PURE__ */ new Map();
3481
3959
  for (const entry of array(raw2.dependencies)) {
3482
- const { sourceControl, fileSystem } = record2(entry);
3483
- const source = record2(array(sourceControl ?? fileSystem)[0]);
3960
+ const { sourceControl, fileSystem } = record3(entry);
3961
+ const source = record3(array(sourceControl ?? fileSystem)[0]);
3484
3962
  const identity = string(source.identity);
3485
3963
  if (!identity) continue;
3486
- const url = string(record2(array(record2(source.location).remote)[0]).urlString);
3964
+ const url = string(record3(array(record3(source.location).remote)[0]).urlString);
3487
3965
  const local = string(source.path);
3488
3966
  const value = url ? { url } : local ? { dir: local } : null;
3489
3967
  if (!value) continue;
@@ -3493,26 +3971,26 @@ function swiftPackageManifest(dir) {
3493
3971
  }
3494
3972
  const products = /* @__PURE__ */ new Map();
3495
3973
  for (const product of array(raw2.products)) {
3496
- const { name: name2, targets: targets2, type } = record2(product);
3497
- if (!string(name2) || !("library" in record2(type))) continue;
3974
+ const { name: name3, targets: targets2, type } = record3(product);
3975
+ if (!string(name3) || !("library" in record3(type))) continue;
3498
3976
  products.set(
3499
- string(name2),
3977
+ string(name3),
3500
3978
  array(targets2).flatMap((target) => string(target) ?? [])
3501
3979
  );
3502
3980
  }
3503
3981
  const targets = /* @__PURE__ */ new Map();
3504
- const names = new Set(array(raw2.targets).map((target) => string(record2(target).name)));
3982
+ const names = new Set(array(raw2.targets).map((target) => string(record3(target).name)));
3505
3983
  for (const entry of array(raw2.targets)) {
3506
- const target = record2(entry);
3507
- const name2 = string(target.name);
3984
+ const target = record3(entry);
3985
+ const name3 = string(target.name);
3508
3986
  const kind = string(target.type);
3509
- if (!name2 || !isTargetType(kind)) continue;
3987
+ if (!name3 || !isTargetType(kind)) continue;
3510
3988
  const declared = string(target.path);
3511
3989
  const roots = kind === "test" ? ["Tests"] : ["Sources", "Source", "src", "srcs"];
3512
- const targetDir = declared ? import_node_path9.default.resolve(dir, declared) : roots.map((root) => import_node_path9.default.join(dir, root, name2)).find((candidate) => import_node_fs8.default.existsSync(candidate)) ?? import_node_path9.default.join(dir, roots[0], name2);
3990
+ const targetDir = declared ? import_node_path9.default.resolve(dir, declared) : roots.map((root) => import_node_path9.default.join(dir, root, name3)).find((candidate) => import_node_fs8.default.existsSync(candidate)) ?? import_node_path9.default.join(dir, roots[0], name3);
3513
3991
  const dependencies = [];
3514
3992
  for (const dependency of array(target.dependencies)) {
3515
- const { target: local, product, byName } = record2(dependency);
3993
+ const { target: local, product, byName } = record3(dependency);
3516
3994
  const payload = array(local ?? product ?? byName);
3517
3995
  if (!linksOnIOS(payload[payload.length - 1], traits)) continue;
3518
3996
  const dependencyName = string(payload[0]);
@@ -3534,16 +4012,26 @@ function swiftPackageManifest(dir) {
3534
4012
  }
3535
4013
  let languageMode = packageMode;
3536
4014
  let mainActorIsolation = false;
4015
+ const defines = [];
3537
4016
  for (const setting of array(target.settings)) {
3538
- const settingKind = record2(record2(setting).kind);
3539
- const mode = string(record2(settingKind.swiftLanguageMode)._0);
4017
+ const { kind: rawKind, tool, condition } = record3(setting);
4018
+ const settingKind = record3(rawKind);
4019
+ const mode = string(record3(settingKind.swiftLanguageMode)._0);
3540
4020
  if (mode)
3541
4021
  languageMode = Math.max(5, Number(/^(\d+)/.exec(mode)?.[1] ?? languageMode));
3542
- if (string(record2(settingKind.defaultIsolation)._0) === "MainActor")
4022
+ if (string(record3(settingKind.defaultIsolation)._0) === "MainActor")
3543
4023
  mainActorIsolation = true;
4024
+ const define = string(record3(settingKind.define)._0);
4025
+ if (define && string(tool) === "swift" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(define) && linksOnIOS(condition, traits)) {
4026
+ const config = string(record3(condition).config);
4027
+ defines.push({
4028
+ name: define,
4029
+ configuration: config === "debug" || config === "release" ? config : null
4030
+ });
4031
+ }
3544
4032
  }
3545
- targets.set(name2, {
3546
- name: name2,
4033
+ targets.set(name3, {
4034
+ name: name3,
3547
4035
  type: kind,
3548
4036
  dir: targetDir,
3549
4037
  sources: target.sources == null ? null : array(target.sources).flatMap((file) => string(file) ?? []),
@@ -3551,12 +4039,15 @@ function swiftPackageManifest(dir) {
3551
4039
  dependencies,
3552
4040
  languageMode,
3553
4041
  mainActorIsolation,
4042
+ defines,
3554
4043
  declaresResources: array(target.resources).length > 0,
3555
4044
  url: string(target.url) ?? null,
3556
4045
  checksum: string(target.checksum) ?? null
3557
4046
  });
3558
4047
  }
3559
- const manifest = { dir, products, targets, packages };
4048
+ const name2 = string(raw2.name);
4049
+ if (!name2) throw new Error(`swiftpm returned no package name for ${dir}`);
4050
+ const manifest = { name: name2, dir, products, targets, packages };
3560
4051
  evaluated.set(key2, manifest);
3561
4052
  return manifest;
3562
4053
  }
@@ -3582,7 +4073,7 @@ function swiftTargetFiles(manifest, target) {
3582
4073
  ];
3583
4074
  return included.flatMap(walk).filter((file) => !within(file, excluded)).sort();
3584
4075
  }
3585
- var import_node_child_process6, import_node_crypto5, import_node_fs8, import_node_os6, import_node_path9, TARGET_TYPES, isTargetType, record2, array, string, cacheDir2, evaluated, within;
4076
+ var import_node_child_process6, import_node_crypto5, import_node_fs8, import_node_os6, import_node_path9, TARGET_TYPES, isTargetType, record3, array, string, cacheDir2, evaluated, within;
3586
4077
  var init_swift_package_manifest = __esm({
3587
4078
  "src/swift-package-manifest.ts"() {
3588
4079
  "use strict";
@@ -3603,7 +4094,7 @@ var init_swift_package_manifest = __esm({
3603
4094
  "plugin"
3604
4095
  ];
3605
4096
  isTargetType = (value) => TARGET_TYPES.some((type) => type === value);
3606
- record2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
4097
+ record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
3607
4098
  array = (value) => Array.isArray(value) ? value : [];
3608
4099
  string = (value) => typeof value === "string" ? value : void 0;
3609
4100
  cacheDir2 = import_node_path9.default.join(import_node_os6.default.tmpdir(), "peach-swift-manifests");
@@ -3726,6 +4217,7 @@ function resolveSwiftPackages(options) {
3726
4217
  files: files2,
3727
4218
  packageDir: pkg.manifest.dir,
3728
4219
  remote: pkg.remote,
4220
+ packageDefines: target.defines,
3729
4221
  executable: false,
3730
4222
  languageMode: target.languageMode,
3731
4223
  mainActorIsolation: target.mainActorIsolation,
@@ -4092,10 +4584,10 @@ function emitSwiftStandin(graphs, options) {
4092
4584
  owners.push(...physical.map(() => current));
4093
4585
  };
4094
4586
  const scoped = (body) => (symbol, ...rest) => {
4095
- const path16 = pathOf(symbol);
4096
- if (omit.has(path16)) return void skipped.push({ path: path16, reason: omit.get(path16) });
4587
+ const path18 = pathOf(symbol);
4588
+ if (omit.has(path18)) return void skipped.push({ path: path18, reason: omit.get(path18) });
4097
4589
  const previous = current;
4098
- current = path16;
4590
+ current = path18;
4099
4591
  try {
4100
4592
  body(symbol, ...rest);
4101
4593
  } finally {
@@ -4115,14 +4607,14 @@ function emitSwiftStandin(graphs, options) {
4115
4607
  const access = (symbol, openOwner) => symbol.accessLevel === "open" && openOwner ? "open" : "public";
4116
4608
  const emitMember = scoped(
4117
4609
  (member, indent, context) => {
4118
- const path16 = pathOf(member);
4610
+ const path18 = pathOf(member);
4119
4611
  if (TYPE_KINDS.has(member.kind.identifier)) {
4120
4612
  emitType(member, indent);
4121
4613
  return;
4122
4614
  }
4123
4615
  if (member.kind.identifier === "swift.enum.case") return;
4124
4616
  if (!MEMBER_KINDS.has(member.kind.identifier)) {
4125
- skipped.push({ path: path16, reason: `unhandled ${member.kind.identifier}` });
4617
+ skipped.push({ path: path18, reason: `unhandled ${member.kind.identifier}` });
4126
4618
  return;
4127
4619
  }
4128
4620
  const { 1: attrs, 2: rest } = LEADING_ATTRS.exec(text(member));
@@ -4136,12 +4628,12 @@ function emitSwiftStandin(graphs, options) {
4136
4628
  const final = context.inClass && flags.has("final") ? "final " : "";
4137
4629
  const mutating = flags.has("mutating") ? "mutating " : flags.has("nonmutating") ? "nonmutating " : "";
4138
4630
  let core = `${flags.has("convenience") ? "convenience " : ""}${modifiers[2]}`;
4139
- const override = context.inClass && !options.plain?.has(path16) && (flags.has("override") || overrides.has(member.identifier.precise) || options.forcedOverride?.has(path16) || isObjC(member) && path16.endsWith(".init()")) ? "override " : "";
4631
+ const override = context.inClass && !options.plain?.has(path18) && (flags.has("override") || overrides.has(member.identifier.precise) || options.forcedOverride?.has(path18) || isObjC(member) && path18.endsWith(".init()")) ? "override " : "";
4140
4632
  const head = `${indent}${attrs}${access(member, context.openOwner)} ${required ? "required " : ""}${final}${override}${mutating}`;
4141
- const measured = behavior.get(path16);
4142
- if (measured) used.add(path16);
4633
+ const measured = behavior.get(path18);
4634
+ if (measured) used.add(path18);
4143
4635
  const overloadBody = /\basync\b/.test(core) ? measured?.asyncBody : /\bResult</.test(core) ? measured?.resultBody : void 0;
4144
- const body = overloadBody ?? measured?.body ?? trap(path16);
4636
+ const body = overloadBody ?? measured?.body ?? trap(path18);
4145
4637
  switch (member.kind.identifier) {
4146
4638
  case "swift.property":
4147
4639
  case "swift.type.property":
@@ -4162,7 +4654,7 @@ function emitSwiftStandin(graphs, options) {
4162
4654
  return;
4163
4655
  }
4164
4656
  emit(
4165
- `${head}${computed} { get${effects} { ${body} }${settable && !effects ? ` ${parsed.nonmutating ? "nonmutating " : ""}set { ${measured?.set ?? trap(path16)} }` : ""} }`
4657
+ `${head}${computed} { get${effects} { ${body} }${settable && !effects ? ` ${parsed.nonmutating ? "nonmutating " : ""}set { ${measured?.set ?? trap(path18)} }` : ""} }`
4166
4658
  );
4167
4659
  return;
4168
4660
  }
@@ -4215,7 +4707,7 @@ function emitSwiftStandin(graphs, options) {
4215
4707
  };
4216
4708
  const emitType = scoped(
4217
4709
  (symbol, indent) => {
4218
- const path16 = pathOf(symbol);
4710
+ const path18 = pathOf(symbol);
4219
4711
  const id = symbol.identifier.precise;
4220
4712
  const inner = `${indent} `;
4221
4713
  switch (symbol.kind.identifier) {
@@ -4227,10 +4719,10 @@ function emitSwiftStandin(graphs, options) {
4227
4719
  case "swift.class": {
4228
4720
  const shape = header(symbol, "class", indent);
4229
4721
  if (!shape)
4230
- return void skipped.push({ path: path16, reason: `unparsed class ${text(symbol)}` });
4231
- const parent = options.superclasses?.get(path16) ?? superclass.get(id);
4722
+ return void skipped.push({ path: path18, reason: `unparsed class ${text(symbol)}` });
4723
+ const parent = options.superclasses?.get(path18) ?? superclass.get(id);
4232
4724
  const blocked = classBlocked(id);
4233
- if (blocked) return void skipped.push({ path: path16, reason: blocked });
4725
+ if (blocked) return void skipped.push({ path: path18, reason: blocked });
4234
4726
  const ancestors = /* @__PURE__ */ new Set();
4235
4727
  for (let at = superId.get(id); at; at = superId.get(at))
4236
4728
  for (const name2 of conforms.get(at) ?? []) ancestors.add(name2);
@@ -4246,9 +4738,9 @@ function emitSwiftStandin(graphs, options) {
4246
4738
  `${indent}${shape.attrs}${open ? "open" : "public"} class ${shape.name}${shape.generics}${list2.length ? `: ${list2.join(", ")}` : ""}${selfConforming(shape.where)} {`
4247
4739
  );
4248
4740
  const members = membersOf(id);
4249
- const extra = options.behavior?.get(path16)?.members;
4741
+ const extra = options.behavior?.get(path18)?.members;
4250
4742
  if (extra) {
4251
- used.add(path16);
4743
+ used.add(path18);
4252
4744
  emit(`${inner}${extra}`);
4253
4745
  }
4254
4746
  for (const member of members)
@@ -4300,13 +4792,13 @@ function emitSwiftStandin(graphs, options) {
4300
4792
  continue;
4301
4793
  have.add(witnessKey(member));
4302
4794
  have.add(sideLeaf(member));
4303
- emitWitness(member, inner, path16);
4795
+ emitWitness(member, inner, path18);
4304
4796
  }
4305
4797
  }
4306
4798
  const coder = members.some((member) => leaf(member) === "init(coder:)");
4307
4799
  if (designated && !coder && externalRoot(id) !== "NSObject" && externalRoot(id) !== null)
4308
4800
  emit(
4309
- `${inner}public required init?(coder: NSCoder) { ${trap(`${path16}.init(coder:)`)} }`
4801
+ `${inner}public required init?(coder: NSCoder) { ${trap(`${path18}.init(coder:)`)} }`
4310
4802
  );
4311
4803
  emit(`${indent}}`);
4312
4804
  return;
@@ -4314,7 +4806,7 @@ function emitSwiftStandin(graphs, options) {
4314
4806
  case "swift.struct": {
4315
4807
  const shape = header(symbol, "struct", indent);
4316
4808
  if (!shape)
4317
- return void skipped.push({ path: path16, reason: `unparsed struct ${text(symbol)}` });
4809
+ return void skipped.push({ path: path18, reason: `unparsed struct ${text(symbol)}` });
4318
4810
  const members = membersOf(id);
4319
4811
  const raw2 = rawType(members);
4320
4812
  const list2 = inheritance(id);
@@ -4346,7 +4838,7 @@ function emitSwiftStandin(graphs, options) {
4346
4838
  case "swift.enum": {
4347
4839
  const shape = header(symbol, "enum", indent);
4348
4840
  if (!shape)
4349
- return void skipped.push({ path: path16, reason: `unparsed enum ${text(symbol)}` });
4841
+ return void skipped.push({ path: path18, reason: `unparsed enum ${text(symbol)}` });
4350
4842
  const members = membersOf(id);
4351
4843
  const raw2 = rawType(members);
4352
4844
  const cases = members.filter(
@@ -4355,7 +4847,7 @@ function emitSwiftStandin(graphs, options) {
4355
4847
  const missing = raw2 ? cases.filter((member) => !values.has(member.identifier.precise)) : [];
4356
4848
  if (raw2 && isObjC(symbol) && missing.length > 0)
4357
4849
  return void skipped.push({
4358
- path: path16,
4850
+ path: path18,
4359
4851
  reason: `no header value for ${missing.map(leaf).join(", ")}`
4360
4852
  });
4361
4853
  const list2 = inheritance(id, raw2 ?? void 0).filter(
@@ -4395,7 +4887,7 @@ function emitSwiftStandin(graphs, options) {
4395
4887
  const shape = header(symbol, "protocol", indent);
4396
4888
  if (!shape)
4397
4889
  return void skipped.push({
4398
- path: path16,
4890
+ path: path18,
4399
4891
  reason: `unparsed protocol ${text(symbol)}`
4400
4892
  });
4401
4893
  const list2 = [...isObjC(symbol) ? ["AnyObject"] : [], ...inheritance(id)];
@@ -4420,7 +4912,7 @@ function emitSwiftStandin(graphs, options) {
4420
4912
  }
4421
4913
  emit(`${indent}}`);
4422
4914
  if (defaults.length > 0) {
4423
- emit(`${indent}extension ${path16} {`);
4915
+ emit(`${indent}extension ${path18} {`);
4424
4916
  for (const member of defaults) {
4425
4917
  const decl = text(member);
4426
4918
  if (optional.has(member.identifier.precise) && member.kind.identifier === "swift.method" && !decl.replace(/\([^)]*\)/g, "").includes("->") && !/ async\b/.test(decl))
@@ -4466,7 +4958,7 @@ function emitSwiftStandin(graphs, options) {
4466
4958
  return init ? /rawValue:\s*([^)]+)\)/.exec(text(init))?.[1]?.trim() ?? null : null;
4467
4959
  };
4468
4960
  const emitGlobal = scoped((symbol) => {
4469
- const path16 = pathOf(symbol);
4961
+ const path18 = pathOf(symbol);
4470
4962
  const kind = symbol.kind.identifier;
4471
4963
  if (kind === "swift.func" || kind === "swift.func.op" || kind === "swift.method" || kind === "swift.type.method") {
4472
4964
  const { 1: attrs, 2: rest } = LEADING_ATTRS.exec(text(symbol));
@@ -4477,17 +4969,17 @@ function emitSwiftStandin(graphs, options) {
4477
4969
  if (fix && !/^(?:prefix|infix|postfix)\b/.test(spelled))
4478
4970
  spelled = `${fix} ${spelled}`;
4479
4971
  }
4480
- emit(`${attrs}public ${spelled} { ${trap(path16)} }`);
4972
+ emit(`${attrs}public ${spelled} { ${trap(path18)} }`);
4481
4973
  } else if (kind === "swift.var") {
4482
4974
  const parsed = splitAccessors(text(symbol).replace(/^let /, "var "));
4483
- const measured = behavior.get(path16);
4484
- if (measured) used.add(path16);
4485
- emit(`public ${parsed.decl} { ${measured?.body ?? trap(path16)} }`);
4486
- } else skipped.push({ path: path16, reason: `unhandled top-level ${kind}` });
4975
+ const measured = behavior.get(path18);
4976
+ if (measured) used.add(path18);
4977
+ emit(`public ${parsed.decl} { ${measured?.body ?? trap(path18)} }`);
4978
+ } else skipped.push({ path: path18, reason: `unhandled top-level ${kind}` });
4487
4979
  });
4488
4980
  const roots = [...byId.values()].filter((symbol) => !owner.has(symbol.identifier.precise) && !unavailable(symbol)).sort((a, b) => pathOf(a).localeCompare(pathOf(b)));
4489
4981
  for (const symbol of roots) {
4490
- const path16 = pathOf(symbol);
4982
+ const path18 = pathOf(symbol);
4491
4983
  if (TYPE_KINDS.has(symbol.kind.identifier)) emitType(symbol, "");
4492
4984
  else emitGlobal(symbol);
4493
4985
  }
@@ -4519,7 +5011,7 @@ function emitSwiftStandin(graphs, options) {
4519
5011
  else emitMember(member, " ", { openOwner: false, inClass: false });
4520
5012
  emit(`}`);
4521
5013
  }
4522
- const imports = ["Foundation", "UIKit", ...options.imports ?? []];
5014
+ const imports = [...STANDIN_IMPORTS, ...options.imports ?? []];
4523
5015
  const banner = [
4524
5016
  `// stand-in for the binary ${options.module} module, generated from ${options.source}.`,
4525
5017
  `// do not hand-edit: every body traps because the vendor binary cannot run`,
@@ -4528,9 +5020,9 @@ function emitSwiftStandin(graphs, options) {
4528
5020
  ...imports.map((name2) => `import ${name2}`),
4529
5021
  ""
4530
5022
  ];
4531
- for (const path16 of behavior.keys())
4532
- if (!used.has(path16))
4533
- skipped.push({ path: path16, reason: "behavior names no emitted declaration" });
5023
+ for (const path18 of behavior.keys())
5024
+ if (!used.has(path18))
5025
+ skipped.push({ path: path18, reason: "behavior names no emitted declaration" });
4534
5026
  return {
4535
5027
  source: `${[...banner, ...lines].join("\n")}
4536
5028
  `,
@@ -4566,11 +5058,12 @@ function stripDefaults(decl) {
4566
5058
  }
4567
5059
  return out;
4568
5060
  }
4569
- var STANDIN_EMIT_VERSION, TYPE_KINDS, MEMBER_KINDS, ACCESSOR_WORD, KEEP_EXTERNAL, LEADING_ATTRS, OBJC_ONLY, text, leaf, pathOf, unavailable, witnessKey, sideLeaf, isObjC, synthesizedEquality, trap, KEYWORDS, tick;
5061
+ var STANDIN_EMIT_VERSION, STANDIN_IMPORTS, TYPE_KINDS, MEMBER_KINDS, ACCESSOR_WORD, KEEP_EXTERNAL, LEADING_ATTRS, OBJC_ONLY, text, leaf, pathOf, unavailable, witnessKey, sideLeaf, isObjC, synthesizedEquality, trap, KEYWORDS, tick;
4570
5062
  var init_swift_standin_emit = __esm({
4571
5063
  "src/swift-standin-emit.ts"() {
4572
5064
  "use strict";
4573
5065
  STANDIN_EMIT_VERSION = "5";
5066
+ STANDIN_IMPORTS = ["Foundation", "UIKit"];
4574
5067
  TYPE_KINDS = /* @__PURE__ */ new Set([
4575
5068
  "swift.class",
4576
5069
  "swift.struct",
@@ -4616,7 +5109,7 @@ var init_swift_standin_emit = __esm({
4616
5109
  sideLeaf = (symbol) => `${symbol.kind.identifier.startsWith("swift.type.") ? "type" : "instance"}|leaf|${leaf(symbol)}`;
4617
5110
  isObjC = (symbol) => symbol.identifier.precise.startsWith("c:");
4618
5111
  synthesizedEquality = (symbol, conforms) => conforms.includes("Hashable") ? ["==(_:_:)", "hash(into:)", "hashValue"].includes(leaf(symbol)) : conforms.includes("Equatable") && leaf(symbol) === "==(_:_:)";
4619
- trap = (path16) => `fatalError("${path16} is a binary framework peach cannot run")`;
5112
+ trap = (path18) => `fatalError("${path18} is a binary framework peach cannot run")`;
4620
5113
  KEYWORDS = /* @__PURE__ */ new Set([
4621
5114
  "Type",
4622
5115
  "Protocol",
@@ -4768,6 +5261,8 @@ function xcframework(binary) {
4768
5261
  }
4769
5262
  function surfaces(xcframeworkDir) {
4770
5263
  const slice = import_node_fs12.default.readdirSync(xcframeworkDir).filter((name2) => /^ios-.+-simulator$/.test(name2)).sort()[0];
5264
+ if (!slice && xcframeworkDir.endsWith(".framework"))
5265
+ throw new Error("a plain .framework has no .xcframework simulator slice");
4771
5266
  if (!slice) throw new Error("it has no iOS simulator slice");
4772
5267
  const root = import_node_path12.default.join(xcframeworkDir, slice);
4773
5268
  const found = [];
@@ -4810,6 +5305,9 @@ function peachModuleDirs(toolchain, imports) {
4810
5305
  const { moduleRoot, toolchainBin } = toolchain;
4811
5306
  if (swiftDistributionAt(moduleRoot)) return [import_node_path12.default.join(moduleRoot, "modules")];
4812
5307
  const peach = new Set(swiftPeachModules(moduleRoot));
5308
+ const targets = [.../* @__PURE__ */ new Set([...STANDIN_IMPORTS, ...imports])].filter(
5309
+ (name2) => peach.has(name2)
5310
+ );
4813
5311
  const plugins = swiftMacroPlugins(moduleRoot);
4814
5312
  if (plugins.stale)
4815
5313
  exec("sh", [
@@ -4817,11 +5315,15 @@ function peachModuleDirs(toolchain, imports) {
4817
5315
  toolchainBin,
4818
5316
  plugins.libraries[0]
4819
5317
  ]);
4820
- for (const target of imports.filter((name2) => peach.has(name2)))
5318
+ for (const target of targets)
4821
5319
  exec(
4822
5320
  import_node_path12.default.join(toolchainBin, "swift"),
4823
5321
  [
4824
5322
  "build",
5323
+ "--build-system",
5324
+ "native",
5325
+ "-j",
5326
+ "4",
4825
5327
  "--swift-sdk",
4826
5328
  swiftModuleSdk(moduleRoot).wasmSdkId,
4827
5329
  "-c",
@@ -4848,7 +5350,12 @@ function peachModuleDirs(toolchain, imports) {
4848
5350
  ];
4849
5351
  }
4850
5352
  function swiftInterfaceForWasm(text3) {
4851
- return text3.replace(
5353
+ const rooted = text3.replace(
5354
+ /\bObjectiveC(\.|::)(NSObject|NSObjectProtocol)\b/g,
5355
+ "Foundation$1$2"
5356
+ );
5357
+ const foundation = rooted !== text3 && !/^import[ \t]+Foundation\b/m.test(rooted) ? rooted.replace(/^(?=import[ \t])/m, "import Foundation\n") : rooted;
5358
+ return foundation.replace(
4852
5359
  /^(\/\/ swift-module-flags:.*)$/m,
4853
5360
  (flags) => flags.replace(/-target \S+/, `-target ${TRIPLE}`).replace(/\s-enable-objc-interop\b/, "").replace(/\s-target-variant \S+/, "")
4854
5361
  ).replace(
@@ -5165,6 +5672,158 @@ ${roots.map((file) => ` header "${file}"
5165
5672
  }
5166
5673
  };
5167
5674
  }
5675
+ function crateFor(root, name2) {
5676
+ const walk2 = (dir, depth) => {
5677
+ const manifest = import_node_path12.default.join(dir, "Cargo.toml");
5678
+ if (import_node_fs12.default.existsSync(manifest)) {
5679
+ const text3 = import_node_fs12.default.readFileSync(manifest, "utf8");
5680
+ const section = (title) => new RegExp(`^\\[${title}\\]\\s*$([\\s\\S]*?)(?=^\\[|(?![\\s\\S]))`, "m").exec(
5681
+ text3
5682
+ )?.[1] ?? "";
5683
+ const field = (body, key2) => new RegExp(`^${key2}\\s*=\\s*"([^"]+)"`, "m").exec(body)?.[1];
5684
+ const lib = section("lib");
5685
+ const crate = field(lib, "name") ?? field(section("package"), "name")?.replaceAll("-", "_");
5686
+ if (crate === name2) {
5687
+ if (!/crate-type\s*=\s*\[[^\]]*"staticlib"/.test(lib))
5688
+ throw new Error(
5689
+ `${import_node_path12.default.relative(root, manifest)} builds lib${name2} without crate-type "staticlib"`
5690
+ );
5691
+ return manifest;
5692
+ }
5693
+ }
5694
+ if (depth === 0) return null;
5695
+ for (const entry of import_node_fs12.default.readdirSync(dir, { withFileTypes: true })) {
5696
+ if (!entry.isDirectory() || UNSEARCHED.has(entry.name) || entry.name.endsWith(".xcframework"))
5697
+ continue;
5698
+ const found = walk2(import_node_path12.default.join(dir, entry.name), depth - 1);
5699
+ if (found) return found;
5700
+ }
5701
+ return null;
5702
+ };
5703
+ return walk2(root, 4);
5704
+ }
5705
+ function standinLibrary(binary, searchRoot) {
5706
+ const dir = xcframework(binary).dir;
5707
+ const slice = import_node_fs12.default.readdirSync(dir).filter((name2) => /^ios-.+-simulator$/.test(name2)).sort()[0];
5708
+ const library = slice ? import_node_fs12.default.readdirSync(import_node_path12.default.join(dir, slice)).find((name2) => /^lib.+\.a$/.test(name2)) : void 0;
5709
+ const supplied = import_node_path12.default.join(dir, RUST_TARGET);
5710
+ if (import_node_fs12.default.existsSync(supplied)) {
5711
+ const found = import_node_fs12.default.readdirSync(supplied).filter((name2) => name2.endsWith(".a"));
5712
+ const chosen = library && found.includes(library) ? library : found.length === 1 ? found[0] : null;
5713
+ if (!chosen)
5714
+ throw new Error(
5715
+ `${import_node_path12.default.relative(searchRoot, supplied)} holds no single static library${library ? ` named ${library}` : ""}`
5716
+ );
5717
+ return import_node_path12.default.join(supplied, chosen);
5718
+ }
5719
+ if (!library) return null;
5720
+ const manifest = crateFor(searchRoot, library.slice("lib".length, -".a".length));
5721
+ if (!manifest) return null;
5722
+ const result = (0, import_node_child_process8.spawnSync)(
5723
+ "cargo",
5724
+ [
5725
+ "build",
5726
+ "--release",
5727
+ "--target",
5728
+ RUST_TARGET,
5729
+ "--message-format=json-render-diagnostics",
5730
+ "--manifest-path",
5731
+ manifest
5732
+ ],
5733
+ {
5734
+ encoding: "utf8",
5735
+ maxBuffer: 256 * 1024 * 1024,
5736
+ stdio: ["ignore", "pipe", "pipe"]
5737
+ }
5738
+ );
5739
+ const crate = import_node_path12.default.relative(searchRoot, import_node_path12.default.dirname(manifest));
5740
+ if (result.error)
5741
+ throw new Error(
5742
+ `${crate} is a rust crate and cargo is not installed; install rust (https://rustup.rs), then run again`
5743
+ );
5744
+ if (result.status !== 0) {
5745
+ if (/target may not be installed|can't find crate for `(core|std)`/.test(result.stderr))
5746
+ throw new Error(
5747
+ `${crate} builds for ${RUST_TARGET}, which this rust lacks; run \`rustup target add ${RUST_TARGET}\`, then run again`
5748
+ );
5749
+ throw new Error(
5750
+ `cargo could not build ${crate} for ${RUST_TARGET}: ${failure(result)}`
5751
+ );
5752
+ }
5753
+ for (const line of result.stdout.split("\n")) {
5754
+ if (!line.startsWith("{")) continue;
5755
+ const message = JSON.parse(line);
5756
+ if (!message || typeof message !== "object" || !("filenames" in message) || !Array.isArray(message.filenames))
5757
+ continue;
5758
+ const archive = message.filenames.find(
5759
+ (file) => typeof file === "string" && import_node_path12.default.basename(file) === library
5760
+ );
5761
+ if (archive) return archive;
5762
+ }
5763
+ throw new Error(`cargo built ${crate} for ${RUST_TARGET} but reported no ${library}`);
5764
+ }
5765
+ function sourceBinaryStandins(binary, toolchain, provided) {
5766
+ const repo = import_node_path12.default.resolve(toolchain.moduleRoot, "../..");
5767
+ const work = import_node_fs12.default.mkdtempSync(import_node_path12.default.join(import_node_os7.default.tmpdir(), "peach-standin-request-"));
5768
+ try {
5769
+ const artifact = xcframework(binary);
5770
+ const local = { ...binary, dir: artifact.dir, url: null, checksum: null };
5771
+ const generation = standinGenerationInputs([local], toolchain);
5772
+ const request = import_node_path12.default.join(work, "request.json");
5773
+ const delivery = import_node_path12.default.join(work, "delivery");
5774
+ import_node_fs12.default.writeFileSync(
5775
+ request,
5776
+ JSON.stringify({
5777
+ binary: local,
5778
+ provided: [...provided],
5779
+ generation,
5780
+ release: toolchain.release,
5781
+ callerToolchainBin: toolchain.toolchainBin
5782
+ })
5783
+ );
5784
+ (0, import_node_child_process8.execFileSync)(
5785
+ "bash",
5786
+ [
5787
+ import_node_path12.default.join(repo, "scripts/build/remote.sh"),
5788
+ // a deadline makes a busy pool wait for a slot; without one it refuses
5789
+ // at once and the app reads as missing a module
5790
+ "--timeout",
5791
+ "1800",
5792
+ "--delivery-dir",
5793
+ delivery,
5794
+ "--toolchain",
5795
+ "packages/sootsim-swift/toolchain.json",
5796
+ "--input",
5797
+ request,
5798
+ "--input",
5799
+ artifact.dir,
5800
+ "bun",
5801
+ "scripts/build/swift-standins.ts",
5802
+ "--request",
5803
+ ".remote-inputs/request.json",
5804
+ "--artifact",
5805
+ `.remote-inputs/${import_node_path12.default.basename(artifact.dir)}`,
5806
+ "--output",
5807
+ ".remote-out/standins.json"
5808
+ ],
5809
+ { cwd: repo, stdio: "inherit" }
5810
+ );
5811
+ const receipt = JSON.parse(
5812
+ import_node_fs12.default.readFileSync(import_node_path12.default.join(delivery, "standins.json"), "utf8")
5813
+ );
5814
+ if (!receipt || typeof receipt !== "object" || !("generation" in receipt) || receipt.generation !== generation || !("result" in receipt))
5815
+ throw new Error("Swift stand-in builder source differs from the caller");
5816
+ const result = receipt.result;
5817
+ if (typeof result === "string") return result;
5818
+ if (!Array.isArray(result) || !result.every(
5819
+ (item) => item && typeof item === "object" && typeof item.module === "string" && typeof item.target === "string" && item.target === binary.target && item.files && typeof item.files === "object" && !Array.isArray(item.files) && Object.values(item.files).every((text3) => typeof text3 === "string")
5820
+ ))
5821
+ throw new Error("invalid Swift stand-in builder result");
5822
+ return result;
5823
+ } finally {
5824
+ import_node_fs12.default.rmSync(work, { recursive: true, force: true });
5825
+ }
5826
+ }
5168
5827
  function standinGenerationInputs(binaries, toolchain) {
5169
5828
  const { moduleRoot, toolchainBin, release } = toolchain;
5170
5829
  const parts = [
@@ -5240,7 +5899,7 @@ function binaryStandins(binary, toolchain, provided) {
5240
5899
  const work = import_node_fs12.default.mkdtempSync(import_node_path12.default.join(import_node_os7.default.tmpdir(), "peach-standin-"));
5241
5900
  let result;
5242
5901
  try {
5243
- result = found.map((surface) => {
5902
+ result = process.platform === "darwin" && process.env.TM_SESSION && process.env.CI !== "true" && toolchain.moduleRoot === import_node_path12.default.resolve(__sootsim_import_meta_dirname, "../../sootsim-swift") && !swiftDistributionAt(toolchain.moduleRoot) ? sourceBinaryStandins(binary, toolchain, provided) : found.map((surface) => {
5244
5903
  const dir = import_node_path12.default.join(work, surface.module);
5245
5904
  try {
5246
5905
  return surface.kind === "swift" ? swiftStandin(surface, binary, toolchain, dir) : cStandin(surface, binary, toolchain, dir);
@@ -5250,6 +5909,7 @@ function binaryStandins(binary, toolchain, provided) {
5250
5909
  );
5251
5910
  }
5252
5911
  });
5912
+ if (typeof result === "string") return result;
5253
5913
  import_node_fs12.default.mkdirSync(import_node_path12.default.dirname(cached2), { recursive: true });
5254
5914
  import_node_fs12.default.writeFileSync(cached2, JSON.stringify(result));
5255
5915
  built.set(id, result);
@@ -5270,7 +5930,7 @@ function swiftStandins(binaries, toolchain, provided = /* @__PURE__ */ new Set()
5270
5930
  }
5271
5931
  return { standins, gaps };
5272
5932
  }
5273
- var import_node_child_process8, import_node_crypto6, import_node_fs12, import_node_os7, import_node_path12, TRIPLE, cacheDir3, key, digests, files, OBJC, identChar, built;
5933
+ var import_node_child_process8, import_node_crypto6, import_node_fs12, import_node_os7, import_node_path12, TRIPLE, cacheDir3, key, digests, files, OBJC, identChar, RUST_TARGET, UNSEARCHED, built;
5274
5934
  var init_swift_standins = __esm({
5275
5935
  "src/swift-standins.ts"() {
5276
5936
  "use strict";
@@ -5289,6 +5949,15 @@ var init_swift_standins = __esm({
5289
5949
  files = (dir, test) => import_node_fs12.default.existsSync(dir) ? import_node_fs12.default.readdirSync(dir, { recursive: true, encoding: "utf8" }).filter((file) => test(import_node_path12.default.basename(file))).sort() : [];
5290
5950
  OBJC = /^\s*(?:@interface|@protocol|@class|@import|#import)\b/m;
5291
5951
  identChar = (char) => char !== void 0 && /[\w$]/.test(char);
5952
+ RUST_TARGET = "wasm32-wasip1";
5953
+ UNSEARCHED = /* @__PURE__ */ new Set([
5954
+ "node_modules",
5955
+ "Pods",
5956
+ "target",
5957
+ ".build",
5958
+ ".git",
5959
+ "DerivedData"
5960
+ ]);
5292
5961
  built = /* @__PURE__ */ new Map();
5293
5962
  }
5294
5963
  });
@@ -5337,7 +6006,11 @@ function isRecord2(value) {
5337
6006
  return typeof value === "object" && value !== null && !Array.isArray(value);
5338
6007
  }
5339
6008
  function isSwiftNativeModules(value) {
5340
- return isRecord2(value) && typeof value.packageId === "string" && typeof value.hash === "string" && isRecord2(value.files) && isRecord2(value.modules) && [...Object.values(value.files), ...Object.values(value.modules)].every(
6009
+ return isRecord2(value) && typeof value.packageId === "string" && typeof value.hash === "string" && isRecord2(value.files) && isRecord2(value.modules) && isRecord2(value.views) && [
6010
+ ...Object.values(value.files),
6011
+ ...Object.values(value.modules),
6012
+ ...Object.values(value.views)
6013
+ ].every(
5341
6014
  (names) => Array.isArray(names) && names.every((name2) => typeof name2 === "string")
5342
6015
  ) && Array.isArray(value.defaultViewFiles) && value.defaultViewFiles.every((file) => typeof file === "string");
5343
6016
  }
@@ -5345,7 +6018,7 @@ function isSwiftCompileOutcome(value) {
5345
6018
  if (!isRecord2(value)) return false;
5346
6019
  switch (value.status) {
5347
6020
  case "published":
5348
- return typeof value.artifactUrl === "string" && typeof value.icuUrl === "string" && typeof value.hash === "string" && /^[a-f0-9]{64}$/.test(value.hash) && (value.imageUrls === void 0 || isRecord2(value.imageUrls) && Object.values(value.imageUrls).every((url) => typeof url === "string")) && (value.native === void 0 || isSwiftNativeModules(value.native));
6021
+ return typeof value.artifactUrl === "string" && typeof value.icuUrl === "string" && typeof value.hash === "string" && /^[a-f0-9]{64}$/.test(value.hash) && (value.imageUrls === void 0 || isRecord2(value.imageUrls) && Object.values(value.imageUrls).every((url) => typeof url === "string")) && (value.metalSources === void 0 || isSwiftMetalLibrarySources(value.metalSources)) && (value.native === void 0 || isSwiftNativeModules(value.native));
5349
6022
  case "compiling":
5350
6023
  return typeof value.hash === "string" && /^[a-f0-9]{64}$/.test(value.hash);
5351
6024
  case "refused":
@@ -5380,6 +6053,14 @@ async function compileSwiftProject(submit, work, origin, signal) {
5380
6053
  hash: outcome.hash,
5381
6054
  icuUrl: compileAssetUrl(outcome.icuUrl, origin),
5382
6055
  ...outcome.imageUrls === void 0 ? {} : { imageUrls: resolveImageUrls(outcome.imageUrls, origin) },
6056
+ ...outcome.metalSources === void 0 ? {} : {
6057
+ metalSources: Object.fromEntries(
6058
+ Object.entries(outcome.metalSources).map(([file, source]) => [
6059
+ file,
6060
+ { ...source, url: compileAssetUrl(source.url, origin) }
6061
+ ])
6062
+ )
6063
+ },
5383
6064
  ...outcome.native === void 0 ? {} : { native: outcome.native }
5384
6065
  };
5385
6066
  case "compiling":
@@ -5407,20 +6088,24 @@ async function compileSwiftProject(submit, work, origin, signal) {
5407
6088
  }
5408
6089
  }
5409
6090
  }
5410
- var SWIFT_COMPILE_PATH, MAX_SWIFT_COMPILE_FILES, MAX_SWIFT_COMPILE_SOURCE_BYTES, MAX_SWIFT_COMPILE_RESOURCES, MAX_SWIFT_COMPILE_RESOURCE_BYTES, MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES, MAX_SWIFT_COMPILE_IMAGES, MAX_SWIFT_COMPILE_IMAGE_BYTES, MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES, SWIFT_HOST_ABI, POLL_INTERVAL_MS;
6091
+ var SWIFT_COMPILE_PATH, MAX_SWIFT_COMPILE_FILES, MAX_SWIFT_COMPILE_SOURCE_BYTES, MAX_SWIFT_COMPILE_RESOURCES, MAX_SWIFT_COMPILE_RESOURCE_BYTES, MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES, MAX_SWIFT_COMPILE_METAL_FILES, MAX_SWIFT_COMPILE_METAL_BYTES, MAX_SWIFT_COMPILE_METAL_TOTAL_BYTES, MAX_SWIFT_COMPILE_IMAGES, MAX_SWIFT_COMPILE_IMAGE_BYTES, MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES, SWIFT_HOST_ABI, POLL_INTERVAL_MS;
5411
6092
  var init_swift_submit = __esm({
5412
6093
  "src/swift-submit.ts"() {
5413
6094
  "use strict";
6095
+ init_swiftContract();
5414
6096
  SWIFT_COMPILE_PATH = "/api/swift-compile";
5415
6097
  MAX_SWIFT_COMPILE_FILES = 256;
5416
6098
  MAX_SWIFT_COMPILE_SOURCE_BYTES = 2 * 1024 * 1024;
5417
6099
  MAX_SWIFT_COMPILE_RESOURCES = 128;
5418
6100
  MAX_SWIFT_COMPILE_RESOURCE_BYTES = 2 * 1024 * 1024;
5419
6101
  MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES = 8 * 1024 * 1024;
6102
+ MAX_SWIFT_COMPILE_METAL_FILES = 128;
6103
+ MAX_SWIFT_COMPILE_METAL_BYTES = 2 * 1024 * 1024;
6104
+ MAX_SWIFT_COMPILE_METAL_TOTAL_BYTES = 8 * 1024 * 1024;
5420
6105
  MAX_SWIFT_COMPILE_IMAGES = 128;
5421
6106
  MAX_SWIFT_COMPILE_IMAGE_BYTES = 4 * 1024 * 1024;
5422
6107
  MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024;
5423
- SWIFT_HOST_ABI = 24;
6108
+ SWIFT_HOST_ABI = 41;
5424
6109
  POLL_INTERVAL_MS = 1500;
5425
6110
  }
5426
6111
  });
@@ -5429,7 +6114,7 @@ var init_swift_submit = __esm({
5429
6114
  var require_parse = __commonJS({
5430
6115
  "../../node_modules/plist/lib/parse.js"(exports2) {
5431
6116
  var { DOMParser } = require("@xmldom/xmldom");
5432
- exports2.parse = parse;
6117
+ exports2.parse = parse2;
5433
6118
  var TEXT_NODE = 3;
5434
6119
  var CDATA_NODE = 4;
5435
6120
  var COMMENT_NODE = 8;
@@ -5448,7 +6133,7 @@ var require_parse = __commonJS({
5448
6133
  throw new Error(message);
5449
6134
  }
5450
6135
  }
5451
- function parse(xml) {
6136
+ function parse2(xml) {
5452
6137
  var doc = new DOMParser().parseFromString(xml, "text/xml");
5453
6138
  invariant(
5454
6139
  doc.documentElement.nodeName === "plist",
@@ -5710,7 +6395,7 @@ var require_parse2 = __commonJS({
5710
6395
  var token;
5711
6396
  var key2;
5712
6397
  var root;
5713
- module2.exports = function parse(text3, reviver) {
6398
+ module2.exports = function parse2(text3, reviver) {
5714
6399
  source = String(text3);
5715
6400
  parseState = "start";
5716
6401
  stack = [];
@@ -5893,15 +6578,15 @@ var require_parse2 = __commonJS({
5893
6578
  return newToken("punctuator", read());
5894
6579
  case "n":
5895
6580
  read();
5896
- literal("ull");
6581
+ literal2("ull");
5897
6582
  return newToken("null", null);
5898
6583
  case "t":
5899
6584
  read();
5900
- literal("rue");
6585
+ literal2("rue");
5901
6586
  return newToken("boolean", true);
5902
6587
  case "f":
5903
6588
  read();
5904
- literal("alse");
6589
+ literal2("alse");
5905
6590
  return newToken("boolean", false);
5906
6591
  case "-":
5907
6592
  case "+":
@@ -5932,11 +6617,11 @@ var require_parse2 = __commonJS({
5932
6617
  return;
5933
6618
  case "I":
5934
6619
  read();
5935
- literal("nfinity");
6620
+ literal2("nfinity");
5936
6621
  return newToken("numeric", Infinity);
5937
6622
  case "N":
5938
6623
  read();
5939
- literal("aN");
6624
+ literal2("aN");
5940
6625
  return newToken("numeric", NaN);
5941
6626
  case '"':
5942
6627
  case "'":
@@ -6030,11 +6715,11 @@ var require_parse2 = __commonJS({
6030
6715
  return;
6031
6716
  case "I":
6032
6717
  read();
6033
- literal("nfinity");
6718
+ literal2("nfinity");
6034
6719
  return newToken("numeric", sign * Infinity);
6035
6720
  case "N":
6036
6721
  read();
6037
- literal("aN");
6722
+ literal2("aN");
6038
6723
  return newToken("numeric", NaN);
6039
6724
  }
6040
6725
  throw invalidChar(read());
@@ -6267,7 +6952,7 @@ var require_parse2 = __commonJS({
6267
6952
  column
6268
6953
  };
6269
6954
  }
6270
- function literal(s) {
6955
+ function literal2(s) {
6271
6956
  for (const c2 of s) {
6272
6957
  const p = peek();
6273
6958
  if (p !== c2) {
@@ -6766,10 +7451,10 @@ var require_stringify = __commonJS({
6766
7451
  // ../../node_modules/json5/lib/index.js
6767
7452
  var require_lib = __commonJS({
6768
7453
  "../../node_modules/json5/lib/index.js"(exports2, module2) {
6769
- var parse = require_parse2();
7454
+ var parse2 = require_parse2();
6770
7455
  var stringify = require_stringify();
6771
7456
  var JSON52 = {
6772
- parse,
7457
+ parse: parse2,
6773
7458
  stringify
6774
7459
  };
6775
7460
  module2.exports = JSON52;
@@ -7115,7 +7800,7 @@ var init_xcode_json_project = __esm({
7115
7800
  }
7116
7801
  });
7117
7802
 
7118
- // src/xcode-project.ts
7803
+ // src/openstep-plist.ts
7119
7804
  function parseOpenStepPlist(text3) {
7120
7805
  let i = 0;
7121
7806
  const fail = (message) => {
@@ -7173,6 +7858,7 @@ function parseOpenStepPlist(text3) {
7173
7858
  if (typeof key2 !== "string") fail("dictionary key is not a string");
7174
7859
  skip();
7175
7860
  if (text3[i++] !== "=") fail("expected =");
7861
+ if (Object.hasOwn(dict, key2)) fail(`duplicate key ${key2}`);
7176
7862
  dict[key2] = value();
7177
7863
  skip();
7178
7864
  if (text3[i++] !== ";") fail("expected ;");
@@ -7205,6 +7891,17 @@ function parseOpenStepPlist(text3) {
7205
7891
  if (i !== text3.length) fail("trailing content");
7206
7892
  return root;
7207
7893
  }
7894
+ var isDict, str, list;
7895
+ var init_openstep_plist = __esm({
7896
+ "src/openstep-plist.ts"() {
7897
+ "use strict";
7898
+ isDict = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7899
+ str = (value) => typeof value === "string" ? value : void 0;
7900
+ list = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
7901
+ }
7902
+ });
7903
+
7904
+ // src/xcode-project.ts
7208
7905
  function walkFiles(dir) {
7209
7906
  if (!import_node_fs13.default.existsSync(dir)) return [];
7210
7907
  const out = [];
@@ -7216,9 +7913,9 @@ function walkFiles(dir) {
7216
7913
  }
7217
7914
  return out;
7218
7915
  }
7219
- function readXcconfig(file, seen = /* @__PURE__ */ new Set()) {
7220
- if (seen.has(file) || !import_node_fs13.default.existsSync(file)) return {};
7221
- seen.add(file);
7916
+ function readXcconfig(file, read) {
7917
+ if (read.includes(file) || !import_node_fs13.default.existsSync(file)) return {};
7918
+ read.push(file);
7222
7919
  const settings = {};
7223
7920
  for (const raw2 of import_node_fs13.default.readFileSync(file, "utf8").split("\n")) {
7224
7921
  const line = raw2.replace(/\/\/.*$/, "").trim();
@@ -7226,7 +7923,7 @@ function readXcconfig(file, seen = /* @__PURE__ */ new Set()) {
7226
7923
  if (include) {
7227
7924
  Object.assign(
7228
7925
  settings,
7229
- readXcconfig(import_node_path14.default.resolve(import_node_path14.default.dirname(file), include[1]), seen)
7926
+ readXcconfig(import_node_path14.default.resolve(import_node_path14.default.dirname(file), include[1]), read)
7230
7927
  );
7231
7928
  continue;
7232
7929
  }
@@ -7307,7 +8004,7 @@ function workspaceReferences(workspace) {
7307
8004
  function enclosingXcodeWorkspace(xcodeproj) {
7308
8005
  const project = import_node_fs13.default.realpathSync(xcodeproj);
7309
8006
  for (let dir = import_node_path14.default.dirname(project); ; dir = import_node_path14.default.dirname(dir)) {
7310
- for (const name2 of import_node_fs13.default.readdirSync(dir)) {
8007
+ for (const name2 of import_node_fs13.default.readdirSync(dir).sort()) {
7311
8008
  if (!name2.endsWith(".xcworkspace")) continue;
7312
8009
  const workspace = import_node_path14.default.join(dir, name2);
7313
8010
  if (!import_node_fs13.default.existsSync(import_node_path14.default.join(workspace, "contents.xcworkspacedata"))) continue;
@@ -7388,17 +8085,30 @@ function loadProject(xcodeproj) {
7388
8085
  loaded.set(projectFile, { mtimeMs, project: result });
7389
8086
  return result;
7390
8087
  }
7391
- function targetSettings(ref, builtins) {
8088
+ function defaultConfiguration(project) {
8089
+ const configList = project.object(str(project.project.buildConfigurationList));
8090
+ const names = list(configList?.buildConfigurations).flatMap(
8091
+ (id) => str(project.object(id)?.name) ?? []
8092
+ );
8093
+ return names.includes("Debug") ? "Debug" : str(configList?.defaultConfigurationName) ?? names[0] ?? "Debug";
8094
+ }
8095
+ function targetSettings(ref, configuration, builtins) {
7392
8096
  const { project, target } = ref;
8097
+ const xcconfigs = [];
7393
8098
  const level = (listId) => {
7394
- const configs = list(project.object(listId)?.buildConfigurations).map(project.object);
7395
- const config = configs.find((entry) => entry?.name === "Debug") ?? configs.find(Boolean);
8099
+ const configList = project.object(listId);
8100
+ const configs = list(configList?.buildConfigurations).map(project.object);
8101
+ const config = configs.find((entry) => entry?.name === configuration) ?? configs.find((entry) => entry?.name === str(configList?.defaultConfigurationName));
7396
8102
  if (!config) return [];
7397
- const name2 = str(config.name) ?? "Debug";
8103
+ const name2 = str(config.name) ?? configuration;
7398
8104
  const base = str(config.baseConfigurationReference);
7399
8105
  const own = isDict(config.buildSettings) ? config.buildSettings : {};
8106
+ const baseFile = base ? project.resolvePath(base) : null;
8107
+ const read = [];
8108
+ const fromFile = baseFile ? readXcconfig(baseFile, read) : {};
8109
+ xcconfigs.push(...read);
7400
8110
  return [
7401
- applyConditions(base ? readXcconfig(project.resolvePath(base) ?? "") : {}, name2),
8111
+ applyConditions(fromFile, name2),
7402
8112
  applyConditions(
7403
8113
  Object.fromEntries(
7404
8114
  Object.entries(own).flatMap(
@@ -7410,7 +8120,7 @@ function targetSettings(ref, builtins) {
7410
8120
  ];
7411
8121
  };
7412
8122
  const levels = [
7413
- builtins,
8123
+ { ...builtins, CONFIGURATION: configuration },
7414
8124
  ...level(str(project.project.buildConfigurationList)),
7415
8125
  ...level(str(target.buildConfigurationList))
7416
8126
  ];
@@ -7449,9 +8159,30 @@ function targetSettings(ref, builtins) {
7449
8159
  };
7450
8160
  return {
7451
8161
  get: (key2) => valueAt(key2, levels.length - 1),
7452
- keys: new Set(levels.flatMap((values) => Object.keys(values)))
8162
+ keys: new Set(levels.flatMap((values) => Object.keys(values))),
8163
+ xcconfigs
7453
8164
  };
7454
8165
  }
8166
+ function settingList(value) {
8167
+ const items2 = [];
8168
+ for (const [, quoted, plain] of value.matchAll(
8169
+ /"((?:\\.|[^"\\])*)"|((?:\\.|[^\s"])+)/g
8170
+ ))
8171
+ items2.push((quoted ?? plain).replace(/\\(.)/g, "$1"));
8172
+ return items2;
8173
+ }
8174
+ function compilationConditions(settings) {
8175
+ const flags = settingList(settings("OTHER_SWIFT_FLAGS"));
8176
+ const defined = flags.flatMap(
8177
+ (flag, index) => flag === "-D" ? flags[index + 1] ?? [] : flag.startsWith("-D") ? flag.slice(2) : []
8178
+ );
8179
+ return [
8180
+ .../* @__PURE__ */ new Set([
8181
+ ...settingList(settings("SWIFT_ACTIVE_COMPILATION_CONDITIONS")),
8182
+ ...defined
8183
+ ])
8184
+ ];
8185
+ }
7455
8186
  function expand(value, lookup) {
7456
8187
  return value.replace(
7457
8188
  /\$(?:\(([A-Za-z_][A-Za-z0-9_]*)((?::[^)]*)?)\)|\{([A-Za-z_][A-Za-z0-9_]*)((?::[^}]*)?)\}|([A-Za-z_][A-Za-z0-9_]*))/g,
@@ -7462,8 +8193,9 @@ function expand(value, lookup) {
7462
8193
  }
7463
8194
  );
7464
8195
  }
7465
- function targetMembers(ref) {
8196
+ function targetMembers(ref, settings, options = {}) {
7466
8197
  const { project, target, id: targetId } = ref;
8198
+ const targetName = str(target.name) ?? targetId;
7467
8199
  const sources = /* @__PURE__ */ new Set();
7468
8200
  const allSources = /* @__PURE__ */ new Set();
7469
8201
  const resources = /* @__PURE__ */ new Set();
@@ -7484,20 +8216,60 @@ function targetMembers(ref) {
7484
8216
  const resolved = project.resolvePath(refId);
7485
8217
  return resolved ? [resolved] : [];
7486
8218
  };
8219
+ const scriptForOutput = /* @__PURE__ */ new Map();
8220
+ const noteScriptOutput = (raw2, script) => {
8221
+ const expanded = expand(raw2, settings);
8222
+ if (!expanded.endsWith(".swift")) return;
8223
+ const absolute = import_node_path14.default.isAbsolute(expanded) ? import_node_path14.default.normalize(expanded) : import_node_path14.default.resolve(project.projectDir, expanded);
8224
+ if (!scriptForOutput.has(absolute)) scriptForOutput.set(absolute, script);
8225
+ };
7487
8226
  for (const phaseId of list(target.buildPhases)) {
7488
8227
  const phase = project.object(phaseId);
7489
- if (phase?.isa !== "PBXSourcesBuildPhase" && phase?.isa !== "PBXResourcesBuildPhase")
7490
- continue;
7491
- for (const buildFileId of list(phase.files)) {
8228
+ if (phase?.isa !== "PBXShellScriptBuildPhase") continue;
8229
+ const script = str(phase.name) ?? phaseId;
8230
+ for (const raw2 of list(phase.outputPaths)) noteScriptOutput(raw2, script);
8231
+ for (const listPath of list(phase.outputFileListPaths)) {
8232
+ const expandedList = expand(listPath, settings);
8233
+ const absoluteList = import_node_path14.default.isAbsolute(expandedList) ? expandedList : import_node_path14.default.resolve(project.projectDir, expandedList);
8234
+ if (!import_node_fs13.default.existsSync(absoluteList)) continue;
8235
+ for (const line of import_node_fs13.default.readFileSync(absoluteList, "utf8").split("\n")) {
8236
+ const trimmed = line.trim();
8237
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
8238
+ noteScriptOutput(trimmed, script);
8239
+ }
8240
+ }
8241
+ }
8242
+ for (const phaseId of list(target.buildPhases)) {
8243
+ const phase = project.object(phaseId);
8244
+ if (phase?.isa !== "PBXSourcesBuildPhase" && phase?.isa !== "PBXResourcesBuildPhase")
8245
+ continue;
8246
+ for (const buildFileId of list(phase.files)) {
7492
8247
  for (const file of fileRefPaths(str(project.object(buildFileId)?.fileRef) ?? "")) {
7493
8248
  if (phase.isa === "PBXSourcesBuildPhase") {
7494
- if (!import_node_fs13.default.existsSync(file)) continue;
8249
+ if (!import_node_fs13.default.existsSync(file)) {
8250
+ if (options.requireSources === false) continue;
8251
+ const script = scriptForOutput.get(import_node_path14.default.normalize(file));
8252
+ throw new Error(
8253
+ script ? `${targetName}: missing generated source ${file} (Run Script "${script}" declares it in outputPaths; run the script or check in its output)` : `${targetName}: missing source ${file}`
8254
+ );
8255
+ }
7495
8256
  allSources.add(file);
7496
8257
  if (file.endsWith(".swift")) sources.add(file);
7497
8258
  } else if (phase.isa === "PBXResourcesBuildPhase") addResource(file);
7498
8259
  }
7499
8260
  }
7500
8261
  }
8262
+ for (const [file, script] of scriptForOutput) {
8263
+ if (sources.has(file) || allSources.has(file)) continue;
8264
+ if (!import_node_fs13.default.existsSync(file)) {
8265
+ if (options.requireSources === false) continue;
8266
+ throw new Error(
8267
+ `${targetName}: missing generated source ${file} (Run Script "${script}" declares it in outputPaths; run the script or check in its output)`
8268
+ );
8269
+ }
8270
+ allSources.add(file);
8271
+ sources.add(file);
8272
+ }
7501
8273
  const addSynchronized = (folder, relative) => {
7502
8274
  const file = import_node_path14.default.join(folder, relative);
7503
8275
  if (NEVER_MEMBER.test(relative)) return;
@@ -7596,7 +8368,7 @@ function nativeTargets(project) {
7596
8368
  }
7597
8369
  return refs;
7598
8370
  }
7599
- function readXcodeAppTarget(input, targetName) {
8371
+ function readXcodeAppTarget(input, targetName, configurationName, options = {}) {
7600
8372
  const xcodeprojOrWorkspace = import_node_fs13.default.realpathSync(input);
7601
8373
  const workspace = xcodeprojOrWorkspace.endsWith(".xcworkspace") ? xcodeprojOrWorkspace : null;
7602
8374
  const { listed, projects: projects2, projectAt } = workspaceProjects(xcodeprojOrWorkspace);
@@ -7611,6 +8383,7 @@ function readXcodeAppTarget(input, targetName) {
7611
8383
  );
7612
8384
  }
7613
8385
  const app = named[0];
8386
+ const configuration = configurationName ?? defaultConfiguration(app.project);
7614
8387
  const byProduct = /* @__PURE__ */ new Map();
7615
8388
  for (const ref of allTargets) {
7616
8389
  const product = ref.project.object(str(ref.target.productReference));
@@ -7661,9 +8434,14 @@ function readXcodeAppTarget(input, targetName) {
7661
8434
  order.push(ref);
7662
8435
  };
7663
8436
  visit(app);
8437
+ const xcconfigs = /* @__PURE__ */ new Set();
7664
8438
  const moduleTarget = (ref) => {
7665
8439
  const name2 = str(ref.target.name) ?? ref.id;
7666
- const { get: settings2, keys } = targetSettings(ref, {
8440
+ const {
8441
+ get: settings2,
8442
+ keys,
8443
+ xcconfigs: read
8444
+ } = targetSettings(ref, configuration, {
7667
8445
  TARGET_NAME: name2,
7668
8446
  PROJECT_NAME: import_node_path14.default.basename(ref.project.xcodeproj, ".xcodeproj"),
7669
8447
  SRCROOT: ref.project.projectDir,
@@ -7671,6 +8449,7 @@ function readXcodeAppTarget(input, targetName) {
7671
8449
  PROJECT_DIR: ref.project.projectDir,
7672
8450
  DEVELOPMENT_LANGUAGE: str(ref.project.project.developmentRegion) ?? "en"
7673
8451
  });
8452
+ for (const file of read) xcconfigs.add(file);
7674
8453
  const swiftVersion = Number.parseFloat(settings2("SWIFT_VERSION") || "5");
7675
8454
  return {
7676
8455
  settings: settings2,
@@ -7678,9 +8457,10 @@ function readXcodeAppTarget(input, targetName) {
7678
8457
  module: {
7679
8458
  name: name2,
7680
8459
  moduleName: applyOperator(settings2("PRODUCT_MODULE_NAME"), "c99extidentifier"),
7681
- ...targetMembers(ref),
8460
+ ...targetMembers(ref, settings2, options),
7682
8461
  languageMode: swiftVersion >= 6 ? 6 : 5,
7683
- mainActorIsolation: settings2("SWIFT_DEFAULT_ACTOR_ISOLATION") === "MainActor"
8462
+ mainActorIsolation: settings2("SWIFT_DEFAULT_ACTOR_ISOLATION") === "MainActor",
8463
+ defines: compilationConditions(settings2)
7684
8464
  }
7685
8465
  };
7686
8466
  };
@@ -7786,7 +8566,9 @@ function readXcodeAppTarget(input, targetName) {
7786
8566
  )
7787
8567
  ].map((dir) => import_node_fs13.default.realpathSync(dir))
7788
8568
  ),
7789
- projectFiles: [...new Set(used.map((project) => project.projectFile))].sort(),
8569
+ projectFiles: [
8570
+ .../* @__PURE__ */ new Set([...used.map((project) => project.projectFile), ...xcconfigs])
8571
+ ].sort(),
7790
8572
  appIconName: settings("ASSETCATALOG_COMPILER_APPICON_NAME"),
7791
8573
  infoPlist: import_plist.default.build(info),
7792
8574
  infoPlistFile,
@@ -7797,10 +8579,12 @@ function readXcodeAppTarget(input, targetName) {
7797
8579
  ].sort(),
7798
8580
  binaryFrameworks: [...binaryFrameworks].map(([name2, file]) => ({ name: name2, path: file })).sort((left, right) => left.name.localeCompare(right.name)),
7799
8581
  packageResolved: import_node_fs13.default.existsSync(resolvedFile) ? resolvedFile : null,
7800
- appTargets: appTargetNames
8582
+ appTargets: appTargetNames,
8583
+ configuration,
8584
+ settings
7801
8585
  };
7802
8586
  }
7803
- var import_node_fs13, import_node_path14, import_plist, isDict, str, list, APPLICATION, MODULE_PRODUCTS, NEVER_MEMBER, BUNDLE_DIR, CONDITIONS, isDirectory, loaded, targetRefs;
8587
+ var import_node_fs13, import_node_path14, import_plist, APPLICATION, MODULE_PRODUCTS, NEVER_MEMBER, BUNDLE_DIR, CONDITIONS, isDirectory, loaded, targetRefs;
7804
8588
  var init_xcode_project = __esm({
7805
8589
  "src/xcode-project.ts"() {
7806
8590
  "use strict";
@@ -7808,9 +8592,8 @@ var init_xcode_project = __esm({
7808
8592
  import_node_path14 = __toESM(require("node:path"), 1);
7809
8593
  import_plist = __toESM(require_plist(), 1);
7810
8594
  init_xcode_json_project();
7811
- isDict = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7812
- str = (value) => typeof value === "string" ? value : void 0;
7813
- list = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
8595
+ init_openstep_plist();
8596
+ init_openstep_plist();
7814
8597
  APPLICATION = "com.apple.product-type.application";
7815
8598
  MODULE_PRODUCTS = /* @__PURE__ */ new Set([
7816
8599
  "com.apple.product-type.framework",
@@ -7947,7 +8730,7 @@ function swiftPackages(entry, app) {
7947
8730
  cached2.standinKey = generation;
7948
8731
  }
7949
8732
  }
7950
- return cached2;
8733
+ return { ...cached2, binaries: reached };
7951
8734
  }
7952
8735
  function xcodeInputKey(layout, file, relative) {
7953
8736
  const owner = layout.targetDirs.get(file);
@@ -7957,14 +8740,37 @@ function xcodeInputKey(layout, file, relative) {
7957
8740
  function xcodeLayout(root) {
7958
8741
  const registered = xcodeProjects.get(root);
7959
8742
  if (!registered) return null;
7960
- const app = readXcodeAppTarget(registered.xcodeproj, registered.target);
7961
- const files2 = /* @__PURE__ */ new Set([...app.projectFiles, ...app.sources, ...app.resources]);
8743
+ const app = readXcodeAppTarget(
8744
+ registered.xcodeproj,
8745
+ registered.target,
8746
+ registered.configuration
8747
+ );
8748
+ const appSources = app.allSources.filter(
8749
+ (file) => file.endsWith(".swift") || file.endsWith(".metal")
8750
+ );
8751
+ const files2 = /* @__PURE__ */ new Set([...app.projectFiles, ...appSources, ...app.resources]);
7962
8752
  if (app.infoPlistFile) files2.add(app.infoPlistFile);
7963
- const targetDirs = new Map(app.sources.map((file) => [file, XCODE_APP_DIR]));
8753
+ const targetDirs = new Map(
8754
+ [...appSources, ...app.resources].map((file) => [file, XCODE_APP_DIR])
8755
+ );
8756
+ const resourceBundles = [
8757
+ { target: app.moduleName, sourcePath: XCODE_APP_DIR, path: "." }
8758
+ ];
7964
8759
  const targets = [];
7965
8760
  for (const framework of app.frameworks) {
7966
8761
  const dir = import_node_path15.default.posix.join(XCODE_FRAMEWORKS_DIR, framework.moduleName);
7967
- for (const file of framework.sources) {
8762
+ resourceBundles.push({
8763
+ target: framework.moduleName,
8764
+ sourcePath: dir,
8765
+ path: `Frameworks/${framework.moduleName}.framework`
8766
+ });
8767
+ for (const file of framework.allSources) {
8768
+ if (!file.endsWith(".swift") && !file.endsWith(".metal")) continue;
8769
+ if (targetDirs.has(file)) continue;
8770
+ targetDirs.set(file, dir);
8771
+ files2.add(file);
8772
+ }
8773
+ for (const file of framework.resources) {
7968
8774
  if (targetDirs.has(file)) continue;
7969
8775
  targetDirs.set(file, dir);
7970
8776
  files2.add(file);
@@ -7976,10 +8782,11 @@ function xcodeLayout(root) {
7976
8782
  resources: false,
7977
8783
  exclude: [],
7978
8784
  languageMode: framework.languageMode,
7979
- mainActorIsolation: framework.mainActorIsolation
8785
+ mainActorIsolation: framework.mainActorIsolation,
8786
+ defines: framework.defines
7980
8787
  });
7981
8788
  }
7982
- const { packages, standins, projectGaps } = swiftPackages(registered, app);
8789
+ const { packages, standins, projectGaps, binaries } = swiftPackages(registered, app);
7983
8790
  for (const standin of standins)
7984
8791
  targets.push({
7985
8792
  name: standin.module,
@@ -7990,6 +8797,7 @@ function xcodeLayout(root) {
7990
8797
  languageMode: 5,
7991
8798
  mainActorIsolation: false
7992
8799
  });
8800
+ const packageConfiguration = app.configuration === "Debug" ? "debug" : "release";
7993
8801
  for (const target of packages.targets) {
7994
8802
  const relative = import_node_path15.default.relative(
7995
8803
  target.remote ? import_node_path15.default.join(root, REMOTE_CHECKOUTS) : root,
@@ -8001,8 +8809,21 @@ function xcodeLayout(root) {
8001
8809
  targets.push({
8002
8810
  ...target,
8003
8811
  path: target.remote ? import_node_path15.default.posix.join(XCODE_PACKAGES_DIR, posix) : posix,
8004
- exclude: []
8812
+ exclude: [],
8813
+ defines: [
8814
+ ...packageConfiguration === "debug" ? ["DEBUG"] : [],
8815
+ ...target.packageDefines.filter(
8816
+ (define) => define.configuration === null || define.configuration === packageConfiguration
8817
+ ).map((define) => define.name)
8818
+ ]
8005
8819
  });
8820
+ if (target.resources) {
8821
+ resourceBundles.push({
8822
+ target: target.name,
8823
+ sourcePath: target.remote ? import_node_path15.default.posix.join(XCODE_PACKAGES_DIR, posix) : posix,
8824
+ path: `${swiftPackageManifest(target.packageDir).name}_${target.name}.bundle`
8825
+ });
8826
+ }
8006
8827
  if (!target.remote) files2.add(import_node_path15.default.join(target.packageDir, "Package.swift"));
8007
8828
  for (const file of target.files) files2.add(file);
8008
8829
  }
@@ -8014,20 +8835,26 @@ function xcodeLayout(root) {
8014
8835
  targets,
8015
8836
  packages,
8016
8837
  standins,
8838
+ binaries,
8017
8839
  projectGaps,
8018
8840
  targetDirs,
8841
+ resourceBundles,
8019
8842
  files: [...files2].sort()
8020
8843
  };
8021
8844
  }
8022
8845
  function xcodeManifest(layout) {
8023
- const settings = (mode, mainActor) => `swiftSettings: [.swiftLanguageMode(.v${mode})${mainActor ? ", .defaultIsolation(MainActor.self)" : ""}]`;
8846
+ const settings = (target) => `swiftSettings: [${[
8847
+ `.swiftLanguageMode(.v${target.languageMode})`,
8848
+ ...target.mainActorIsolation ? [".defaultIsolation(MainActor.self)"] : [],
8849
+ ...(target.defines ?? []).map((name2) => `.define(${JSON.stringify(name2)})`)
8850
+ ].join(", ")}]`;
8024
8851
  return [
8025
8852
  "// swift-tools-version: 6.2",
8026
8853
  "import PackageDescription",
8027
8854
  'let package = Package(name: "app", targets: [',
8028
- ` .executableTarget(name: "${layout.app.moduleName}", path: "${XCODE_APP_DIR}", ${settings(layout.app.languageMode, layout.app.mainActorIsolation)}),`,
8855
+ ` .executableTarget(name: "${layout.app.moduleName}", path: "${XCODE_APP_DIR}", ${settings(layout.app)}),`,
8029
8856
  ...layout.targets.map(
8030
- (target) => ` .target(name: "${target.name}", path: "${target.path}", ${target.exclude.length > 0 ? `exclude: ${JSON.stringify(target.exclude)}, ` : ""}${target.resources ? "resources: [], " : ""}${settings(target.languageMode, target.mainActorIsolation)}),`
8857
+ (target) => ` .target(name: "${target.name}", path: "${target.path}", ${target.exclude.length > 0 ? `exclude: ${JSON.stringify(target.exclude)}, ` : ""}${target.resources ? "resources: [], " : ""}${settings(target)}),`
8031
8858
  ),
8032
8859
  "])",
8033
8860
  ""
@@ -8068,10 +8895,10 @@ function sourceFiles(root) {
8068
8895
  }
8069
8896
  function resourceCandidateFiles(root) {
8070
8897
  return packageFiles(root).filter(
8071
- (file) => !file.endsWith(".swift") && !["project.pbxproj", "project.xcproj"].includes(import_node_path15.default.basename(file))
8898
+ (file) => !file.endsWith(".swift") && !file.endsWith(".xcconfig") && !["project.pbxproj", "project.xcproj"].includes(import_node_path15.default.basename(file))
8072
8899
  );
8073
8900
  }
8074
- function buildSwiftResourceTable(project, baseUrl) {
8901
+ function buildSwiftResourceTable(project, baseUrl, inputs) {
8075
8902
  const limits = inputLimits(Boolean(project.hosted));
8076
8903
  const absolute = resourceCandidateFiles(project.packagePath);
8077
8904
  const relative = absolute.map(
@@ -8086,15 +8913,77 @@ function buildSwiftResourceTable(project, baseUrl) {
8086
8913
  "utf8"
8087
8914
  );
8088
8915
  };
8089
- const found = enumerateBundleImages(relative, readContents);
8090
- const colors = enumerateBundleColors(relative, readContents);
8091
- const symbols = enumerateBundleSymbols(relative, readContents);
8092
- const table = {};
8916
+ const layout = xcodeLayout(project.packagePath);
8917
+ const bundles = layout?.resourceBundles ?? swiftResourceBundles(inputs, relative);
8918
+ const catalogFiles = relative.filter((file) => {
8919
+ const disk = byRelative.get(file);
8920
+ return !disk || !layout?.targetDirs.get(disk)?.startsWith(`${XCODE_FRAMEWORKS_DIR}/`);
8921
+ });
8922
+ const found = enumerateBundleImages(relative, readContents, (file) => {
8923
+ const disk = byRelative.get(file);
8924
+ if (!disk) throw new Error(`image source ${file} has no input file`);
8925
+ const key2 = layout ? xcodeInputKey(layout, disk, file) : file;
8926
+ return swiftResourceBundleForFile(bundles, key2)?.path ?? ".";
8927
+ });
8928
+ const colors = enumerateBundleColors(catalogFiles, readContents);
8929
+ const symbols = enumerateBundleSymbols(catalogFiles, readContents, (file) => {
8930
+ const disk = byRelative.get(file);
8931
+ if (!disk) throw new Error(`symbol source ${file} has no input file`);
8932
+ const key2 = layout ? xcodeInputKey(layout, disk, file) : file;
8933
+ return swiftResourceBundleForFile(bundles, key2)?.path ?? ".";
8934
+ });
8935
+ const table = /* @__PURE__ */ Object.create(null);
8093
8936
  const images = {};
8937
+ const metalSources = {};
8938
+ const metalUploads = {};
8094
8939
  const watched = [];
8095
8940
  const registered = /* @__PURE__ */ new Map();
8941
+ const shaderFiles = relative.filter((file) => file.endsWith(".metal"));
8942
+ const metalCandidates = {};
8943
+ if (inputs["Package.swift"] !== void 0)
8944
+ metalCandidates["Package.swift"] = inputs["Package.swift"];
8945
+ const metalInputs = /* @__PURE__ */ new Map();
8946
+ for (const file of shaderFiles) {
8947
+ const disk = byRelative.get(file);
8948
+ if (!disk) throw new Error(`metal source ${file} has no input file`);
8949
+ const key2 = layout ? xcodeInputKey(layout, disk, file) : file;
8950
+ metalCandidates[key2] = "";
8951
+ metalInputs.set(key2, { file, disk });
8952
+ }
8953
+ const metalFiles = Object.keys(swiftMetalSourceFiles(metalCandidates));
8954
+ if (metalFiles.length > MAX_SWIFT_COMPILE_METAL_FILES)
8955
+ throw new Error("metal source pack exceeds the compile service file cap");
8956
+ let metalBytes = 0;
8957
+ for (const key2 of metalFiles) {
8958
+ const input = metalInputs.get(key2);
8959
+ if (!input) throw new Error(`metal source ${key2} has no input file`);
8960
+ const { file, disk } = input;
8961
+ const bytes = readPackageFile(
8962
+ project.packagePath,
8963
+ disk,
8964
+ MAX_SWIFT_COMPILE_METAL_BYTES
8965
+ );
8966
+ metalBytes += bytes.length;
8967
+ if (metalBytes > MAX_SWIFT_COMPILE_METAL_TOTAL_BYTES)
8968
+ throw new Error("metal source pack exceeds the compile service caps");
8969
+ const source = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
8970
+ bytes
8971
+ );
8972
+ const sha256 = (0, import_node_crypto7.createHash)("sha256").update(bytes).digest("hex");
8973
+ const bundle = swiftResourceBundleForFile(bundles, key2);
8974
+ if (!bundle && inputs["Package.swift"])
8975
+ throw new Error(`metal source ${key2} has no resource target bundle`);
8976
+ metalSources[key2] = {
8977
+ sha256,
8978
+ url: `${baseUrl}${RESOURCE_PATH}${sha256}.metal`,
8979
+ bundlePath: bundle?.path ?? "."
8980
+ };
8981
+ metalUploads[key2] = source;
8982
+ watched.push(file);
8983
+ registered.set(`${sha256}.metal`, { bytes, contentType: "text/plain; charset=utf-8" });
8984
+ }
8096
8985
  let imageBytes = 0;
8097
- for (const { name: name2, file, scale } of found) {
8986
+ for (const { name: name2, file, scale, bundlePath } of found) {
8098
8987
  const disk = byRelative.get(file);
8099
8988
  if (!disk) continue;
8100
8989
  if (Object.keys(images).length >= limits.images || imageBytes + import_node_fs14.default.statSync(disk).size > limits.imageTotalBytes) {
@@ -8104,8 +8993,12 @@ function buildSwiftResourceTable(project, baseUrl) {
8104
8993
  imageBytes += bytes.length;
8105
8994
  const hash = (0, import_node_crypto7.createHash)("sha256").update(bytes).digest("hex").slice(0, 16);
8106
8995
  const ext = import_node_path15.default.posix.extname(file).toLowerCase();
8107
- table[name2] = { uri: `${baseUrl}${RESOURCE_PATH}${hash}${ext}`, scale };
8108
- images[name2] = {
8996
+ const bundle = table[bundlePath] ?? /* @__PURE__ */ Object.create(null);
8997
+ const variants = bundle[name2] ??= [];
8998
+ variants.push({ uri: `${baseUrl}${RESOURCE_PATH}${hash}${ext}`, scale });
8999
+ table[bundlePath] = bundle;
9000
+ const key2 = layout ? xcodeInputKey(layout, disk, file) : file;
9001
+ images[key2] = {
8109
9002
  file: import_node_path15.default.posix.basename(file),
8110
9003
  data: bytes.toString("base64")
8111
9004
  };
@@ -8127,9 +9020,9 @@ function buildSwiftResourceTable(project, baseUrl) {
8127
9020
  project.resourcesPrevious = project.resourceFiles;
8128
9021
  project.resourceFiles = registered;
8129
9022
  }
8130
- return { table, images, colors, symbols, watched };
9023
+ return { table, images, metalSources, metalUploads, colors, symbols, watched };
8131
9024
  }
8132
- function buildSwiftBundleFiles(packagePath, hosted) {
9025
+ function buildSwiftBundleFiles(packagePath, hosted, inputs) {
8133
9026
  packagePath = import_node_fs14.default.realpathSync(packagePath);
8134
9027
  const limits = inputLimits(hosted);
8135
9028
  const relative = resourceCandidateFiles(packagePath).map(
@@ -8139,30 +9032,51 @@ function buildSwiftBundleFiles(packagePath, hosted) {
8139
9032
  const watched = [];
8140
9033
  let total = 0;
8141
9034
  const xcode = xcodeLayout(packagePath);
9035
+ const bundles = xcode?.resourceBundles ?? swiftResourceBundles(inputs ?? packageSources(packagePath), relative);
9036
+ const addFile = (key2, bytes) => {
9037
+ if (Object.keys(files2).length >= limits.resources || bytes.length > limits.resourceBytes || total + bytes.length > limits.resourceTotalBytes)
9038
+ throw new Error(
9039
+ `swift bundle pack exceeds ${limits.resources} files / ${limits.resourceTotalBytes} bytes`
9040
+ );
9041
+ files2[key2] = bytes.toString("base64");
9042
+ total += bytes.length;
9043
+ };
8142
9044
  if (xcode) {
8143
9045
  const bytes = Buffer.from(xcode.app.infoPlist);
8144
9046
  if (bytes.length > limits.resourceBytes || bytes.length > limits.resourceTotalBytes)
8145
9047
  throw new Error("swift launch Info.plist exceeds bundle resource limits");
8146
- files2["Info.plist"] = bytes.toString("base64");
8147
- total += bytes.length;
9048
+ addFile("Info.plist", bytes);
8148
9049
  if (xcode.app.infoPlistFile)
8149
9050
  watched.push(
8150
9051
  import_node_path15.default.relative(packagePath, xcode.app.infoPlistFile).split(import_node_path15.default.sep).join("/")
8151
9052
  );
8152
9053
  }
8153
- for (const { base, file } of enumerateBundleJson(relative)) {
8154
- watched.push(file);
9054
+ const groups = /* @__PURE__ */ new Map();
9055
+ for (const file of relative) {
8155
9056
  const disk = import_node_path15.default.join(packagePath, ...file.split("/"));
8156
- if (Object.keys(files2).length >= limits.resources || total + import_node_fs14.default.statSync(disk).size > limits.resourceTotalBytes) {
8157
- throw new Error(
8158
- `swift bundle pack exceeds ${limits.resources} files / ${limits.resourceTotalBytes} bytes`
8159
- );
9057
+ const key2 = xcode ? xcodeInputKey(xcode, disk, file) : file;
9058
+ const bundle = swiftResourceBundleForFile(bundles, key2);
9059
+ const bundlePath = bundle?.path ?? ".";
9060
+ const group = groups.get(bundlePath) ?? [];
9061
+ group.push(file);
9062
+ groups.set(bundlePath, group);
9063
+ }
9064
+ for (const bundle of bundles) {
9065
+ if (bundle.path === ".") continue;
9066
+ addFile(
9067
+ `${bundle.path}/Info.plist`,
9068
+ Buffer.from(swiftResourceBundleInfoPlist(bundle))
9069
+ );
9070
+ }
9071
+ for (const [bundlePath, paths] of groups) {
9072
+ for (const { base, file } of enumerateBundleResources(paths)) {
9073
+ watched.push(file);
9074
+ const disk = import_node_path15.default.join(packagePath, ...file.split("/"));
9075
+ const bytes = readPackageFile(packagePath, disk, limits.resourceBytes);
9076
+ addFile(bundlePath === "." ? base : `${bundlePath}/${base}`, bytes);
8160
9077
  }
8161
- const bytes = readPackageFile(packagePath, disk, limits.resourceBytes);
8162
- files2[base] = bytes.toString("base64");
8163
- total += bytes.length;
8164
9078
  }
8165
- return { files: files2, watched };
9079
+ return { files: files2, bundles, watched };
8166
9080
  }
8167
9081
  function linkedModuleRoots(packagePath) {
8168
9082
  packagePath = import_node_fs14.default.realpathSync(packagePath);
@@ -8208,7 +9122,20 @@ function sourceStamp(packagePath, moduleRootOverride, hosted = false) {
8208
9122
  function icuUrl(origin, artifact) {
8209
9123
  return "url" in artifact.icu ? artifact.icu.url : `${origin}${ICU_PATH}${artifact.icu.sha256}`;
8210
9124
  }
8211
- function packageSources(packagePath) {
9125
+ function standinLibraries(layout) {
9126
+ const linked = /* @__PURE__ */ new Map();
9127
+ for (const binary of layout.binaries) {
9128
+ const modules = layout.standins.filter(
9129
+ (standin) => standin.target === binary.target && `${standin.module}.c` in standin.files
9130
+ );
9131
+ if (modules.length === 0) continue;
9132
+ const library = standinLibrary(binary, layout.app.rootDir);
9133
+ if (!library) continue;
9134
+ linked.set(library, modules);
9135
+ }
9136
+ return linked;
9137
+ }
9138
+ function packageSources(packagePath, linked = /* @__PURE__ */ new Set()) {
8212
9139
  const xcode = xcodeLayout(packagePath);
8213
9140
  const files2 = {};
8214
9141
  for (const file of sourceFiles(packagePath)) {
@@ -8224,25 +9151,40 @@ function packageSources(packagePath) {
8224
9151
  files2["Package.swift"] = xcodeManifest(xcode);
8225
9152
  for (const standin of xcode.standins)
8226
9153
  for (const [file, text3] of Object.entries(standin.files))
8227
- files2[import_node_path15.default.posix.join(XCODE_STANDINS_DIR, standin.module, file)] = text3;
9154
+ files2[import_node_path15.default.posix.join(XCODE_STANDINS_DIR, standin.module, file)] = linked.has(standin) && file === `${standin.module}.c` ? `// ${standin.target}'s WebAssembly library defines ${standin.module}'s functions.
9155
+ ` : text3;
8228
9156
  }
8229
9157
  return files2;
8230
9158
  }
8231
9159
  function packageSnapshot(project, entry) {
8232
- const files2 = packageSources(project.packagePath);
8233
9160
  const layout = xcodeLayout(project.packagePath);
9161
+ const linked = layout ? standinLibraries(layout) : /* @__PURE__ */ new Map();
9162
+ const files2 = packageSources(project.packagePath, new Set([...linked.values()].flat()));
8234
9163
  const native = nativeSourceDescriptor(project.packagePath);
8235
9164
  return {
8236
9165
  files: files2,
8237
9166
  entry: layout ? xcodeInputKey(layout, import_node_path15.default.resolve(project.packagePath, entry), entry) : entry,
8238
9167
  resourcePaths: swiftImplicitResourcePaths(
8239
- resourceCandidateFiles(project.packagePath).map(
8240
- (file) => import_node_path15.default.relative(project.packagePath, file).split(import_node_path15.default.sep).join(import_node_path15.default.posix.sep)
8241
- )
9168
+ resourceCandidateFiles(project.packagePath).map((file) => {
9169
+ const relative = import_node_path15.default.relative(project.packagePath, file).split(import_node_path15.default.sep).join(import_node_path15.default.posix.sep);
9170
+ return layout && file.endsWith(".metal") ? xcodeInputKey(layout, file, relative) : relative;
9171
+ })
9172
+ ),
9173
+ resources: buildSwiftResourceTable(project, "", files2),
9174
+ bundleFiles: buildSwiftBundleFiles(
9175
+ project.packagePath,
9176
+ Boolean(project.hosted),
9177
+ files2
8242
9178
  ),
8243
- resources: buildSwiftResourceTable(project, ""),
8244
- bundleFiles: buildSwiftBundleFiles(project.packagePath, Boolean(project.hosted)),
8245
9179
  native,
9180
+ ...linked.size > 0 ? {
9181
+ libraries: Object.fromEntries(
9182
+ [...linked].map(([library, [first]]) => [
9183
+ first.module,
9184
+ import_node_fs14.default.readFileSync(library).toString("base64")
9185
+ ])
9186
+ )
9187
+ } : {},
8246
9188
  ...native ? {
8247
9189
  glue: readPackageFile(
8248
9190
  project.packagePath,
@@ -8255,11 +9197,14 @@ function packageSnapshot(project, entry) {
8255
9197
  function snapshotCompileInput(snapshot) {
8256
9198
  const input = {
8257
9199
  files: snapshot.files,
9200
+ metalSources: snapshot.resources.metalUploads,
8258
9201
  resourcePaths: snapshot.resourcePaths,
9202
+ resourceBundles: snapshot.bundleFiles.bundles,
8259
9203
  resources: snapshot.bundleFiles.files,
8260
9204
  images: snapshot.resources.images,
8261
9205
  entry: snapshot.entry,
8262
- oneModule: true
9206
+ oneModule: true,
9207
+ ...snapshot.libraries ? { libraries: snapshot.libraries } : {}
8263
9208
  };
8264
9209
  return snapshot.glue ? withNativeSourceGlue(input, snapshot.glue) : input;
8265
9210
  }
@@ -8268,6 +9213,7 @@ async function compile(packagePath, stamp, moduleRootOverride, hosted, snapshot)
8268
9213
  table: snapshot.resources.table,
8269
9214
  colors: snapshot.resources.colors,
8270
9215
  symbols: snapshot.resources.symbols,
9216
+ metalSources: snapshot.resources.metalSources,
8271
9217
  watched: snapshot.resources.watched
8272
9218
  };
8273
9219
  const content = {
@@ -8375,12 +9321,7 @@ async function missingModulesMessage(packagePath, missing) {
8375
9321
  ];
8376
9322
  }),
8377
9323
  ...[...missing.keys()].flatMap(
8378
- (name2) => UNPROVIDED_PLATFORM_MODULES[name2] ? [
8379
- [
8380
- name2,
8381
- { source: "the iOS SDK", reason: UNPROVIDED_PLATFORM_MODULES[name2] }
8382
- ]
8383
- ] : IOS_SDK_FRAMEWORKS.has(name2) ? [
9324
+ (name2) => IOS_SDK_FRAMEWORKS.has(name2) ? [
8384
9325
  [
8385
9326
  name2,
8386
9327
  { source: "the iOS SDK", reason: "a framework Peach does not provide" }
@@ -8391,9 +9332,7 @@ async function missingModulesMessage(packagePath, missing) {
8391
9332
  ...layout?.packages.unavailable ?? []
8392
9333
  ]);
8393
9334
  const packaged = new Set(
8394
- [...missing.keys()].filter(
8395
- (name2) => !UNPROVIDED_PLATFORM_MODULES[name2] && !IOS_SDK_FRAMEWORKS.has(name2)
8396
- )
9335
+ [...missing.keys()].filter((name2) => !IOS_SDK_FRAMEWORKS.has(name2))
8397
9336
  );
8398
9337
  if (packaged.size > 0)
8399
9338
  await Promise.all(
@@ -8452,7 +9391,9 @@ async function compileRemote(packagePath, snapshot, hosted) {
8452
9391
  submit,
8453
9392
  {
8454
9393
  files: snapshot.files,
9394
+ metalSources: snapshot.resources.metalUploads,
8455
9395
  resourcePaths: snapshot.resourcePaths,
9396
+ resourceBundles: snapshot.bundleFiles.bundles,
8456
9397
  resources: snapshot.bundleFiles.files,
8457
9398
  images: snapshot.resources.images,
8458
9399
  entry: snapshot.entry,
@@ -8534,7 +9475,7 @@ function artifactFor(project, entry, moduleRoot) {
8534
9475
  project.building = { stamp, promise: fresh };
8535
9476
  return fresh;
8536
9477
  }
8537
- var import_node_crypto7, import_node_fs14, import_node_os8, import_node_path15, RESOURCE_PATH, ICU_PATH, SKIP_SOURCE_DIRS, NATIVE_DESCRIPTOR, NATIVE_GLUE, SwiftMissingModulesError, projects, xcodeProjects, XCODE_APP_DIR, XCODE_FRAMEWORKS_DIR, XCODE_PACKAGES_DIR, XCODE_STANDINS_DIR, CHECKOUTS_PREFIX, HOSTED_LIMITS, LOCAL_LIMITS, inputLimits, RESOURCE_CONTENT_TYPES, API_GAP_PATTERNS, UNPROVIDED_PLATFORM_MODULES, IOS_SDK_FRAMEWORKS;
9478
+ var import_node_crypto7, import_node_fs14, import_node_os8, import_node_path15, RESOURCE_PATH, ICU_PATH, SKIP_SOURCE_DIRS, NATIVE_DESCRIPTOR, NATIVE_GLUE, SwiftMissingModulesError, projects, xcodeProjects, XCODE_APP_DIR, XCODE_FRAMEWORKS_DIR, XCODE_PACKAGES_DIR, XCODE_STANDINS_DIR, CHECKOUTS_PREFIX, HOSTED_LIMITS, LOCAL_LIMITS, inputLimits, RESOURCE_CONTENT_TYPES, API_GAP_PATTERNS, IOS_SDK_FRAMEWORKS;
8538
9479
  var init_swift_host = __esm({
8539
9480
  "src/swift-host.ts"() {
8540
9481
  "use strict";
@@ -8550,6 +9491,7 @@ var init_swift_host = __esm({
8550
9491
  init_swift_compile();
8551
9492
  init_swift_distribution();
8552
9493
  init_swift_package();
9494
+ init_swift_package_manifest();
8553
9495
  init_swift_packages();
8554
9496
  init_swift_source_file();
8555
9497
  init_swift_standins();
@@ -8617,10 +9559,6 @@ var init_swift_host = __esm({
8617
9559
  [/^'([^']+)' is unavailable/, (match) => match[1]],
8618
9560
  [/^no such module '([^']+)'/, (match) => `module ${match[1]}`]
8619
9561
  ];
8620
- UNPROVIDED_PLATFORM_MODULES = {
8621
- Darwin: "Peach compiles for WebAssembly, which has no Darwin; C library names come through Foundation",
8622
- MachO: "Peach compiles for WebAssembly, which has no Mach-O images"
8623
- };
8624
9562
  IOS_SDK_FRAMEWORKS = new Set(ios_sdk_frameworks_default.frameworks);
8625
9563
  }
8626
9564
  });
@@ -8784,7 +9722,7 @@ function expoEntry(units) {
8784
9722
  import ExpoModulesCore
8785
9723
  ${[...new Set(units.map((unit) => unit.namespace))].map((namespace) => `import ${namespace}`).join("\n")}
8786
9724
 
8787
- @main struct PeachExpoApp: RNXPackage {
9725
+ @main struct PeachExpoApp: PeachPackage {
8788
9726
  init() { MainActor.assumeIsolated { PeachExpoRuntime.register([${units.flatMap((unit) => unit.classes).join(", ")}]) } }
8789
9727
  func view(props: JSON) -> PeachExpoNativeView { PeachExpoNativeView(props: props) }
8790
9728
  }
@@ -8807,72 +9745,45 @@ async function compileExpoSwiftModules(root, base, publishArtifact) {
8807
9745
  modules: [],
8808
9746
  failures: []
8809
9747
  };
8810
- const key2 = (0, import_node_crypto8.createHash)("sha256").update(import_node_fs15.default.realpathSync(root)).digest("hex");
8811
- const linked = [];
8812
- for (const unit of units) {
8813
- if (!module2) {
9748
+ if (!module2) {
9749
+ for (const unit of units)
8814
9750
  result.failures.push({
8815
9751
  namespace: unit.moduleId,
8816
9752
  diagnostic: "No local Swift toolchain or distribution is available"
8817
9753
  });
8818
- continue;
8819
- }
9754
+ return result;
9755
+ }
9756
+ const key2 = (0, import_node_crypto8.createHash)("sha256").update(import_node_fs15.default.realpathSync(root)).digest("hex");
9757
+ const failures = /* @__PURE__ */ new Map();
9758
+ const candidates = [];
9759
+ const stamps = /* @__PURE__ */ new Map();
9760
+ for (const unit of units) {
8820
9761
  const packagePath = import_node_path16.default.join(import_node_os9.default.tmpdir(), "peach-expo-swift", key2, unit.moduleId);
8821
- let stamp;
8822
- try {
8823
- syncSwiftTree(
8824
- packagePath,
8825
- new Map(
8826
- Object.entries(unit.files).map(([name2, source]) => [
8827
- import_node_path16.default.join(packagePath, name2),
8828
- source
8829
- ])
8830
- )
8831
- );
8832
- stamp = sourceStamp(packagePath, module2.root);
8833
- const failed = failedCompilations.get(packagePath);
8834
- if (failed?.stamp === stamp) {
8835
- result.failures.push({
8836
- namespace: unit.moduleId,
8837
- diagnostic: failed.diagnostic
8838
- });
8839
- continue;
8840
- }
8841
- failedCompilations.delete(packagePath);
8842
- await artifactFor(projectFor(packagePath), EXPO_SWIFT_ENTRY, module2.root);
8843
- linked.push(unit);
8844
- } catch (error) {
8845
- const diagnostic = error instanceof Error ? error.message : String(error);
8846
- const appPaths = Object.values(unit.files).flatMap((source) => {
8847
- const match = /^#sourceLocation\(file: (".*"), line: 1\)$/m.exec(source);
8848
- if (!match) return [];
8849
- const file = JSON.parse(match[1]);
8850
- return typeof file === "string" ? [file] : [];
8851
- });
8852
- const errors = diagnostic.match(/^.+:\d+(?::\d+)?: error:.*$/gm) ?? [];
8853
- const sourceErrors = errors.filter(
8854
- (line) => appPaths.some((file) => line.startsWith(`${file}:`)) && !/external macro implementation type|failed to load library plugin/.test(line)
8855
- );
8856
- if (publishArtifact && (sourceErrors.length === 0 || sourceErrors.length !== errors.length) && !(error instanceof SwiftMissingModulesError))
8857
- throw error;
8858
- if (sourceErrors.length && sourceErrors.length === errors.length && module2 && stamp && sourceStamp(packagePath, module2.root) === stamp) {
8859
- failedCompilations.set(packagePath, {
8860
- stamp,
8861
- diagnostic: sourceErrors.join("\n")
8862
- });
8863
- }
8864
- result.failures.push({
8865
- namespace: unit.moduleId,
8866
- diagnostic: sourceErrors.length ? sourceErrors.join("\n") : diagnostic
8867
- });
9762
+ syncSwiftTree(
9763
+ packagePath,
9764
+ new Map(
9765
+ Object.entries(unit.files).map(([name2, source]) => [
9766
+ import_node_path16.default.join(packagePath, name2),
9767
+ source
9768
+ ])
9769
+ )
9770
+ );
9771
+ const stamp = sourceStamp(packagePath, module2.root);
9772
+ const failed = failedCompilations.get(packagePath);
9773
+ if (failed?.stamp === stamp) {
9774
+ failures.set(unit.moduleId, failed.diagnostic);
9775
+ continue;
8868
9776
  }
9777
+ failedCompilations.delete(packagePath);
9778
+ stamps.set(unit, stamp);
9779
+ candidates.push(unit);
8869
9780
  }
8870
- if (module2 && linked.length) {
9781
+ const link = async (linked2) => {
8871
9782
  const files2 = Object.assign(
8872
9783
  {},
8873
- ...linked.map((unit) => unit.files)
9784
+ ...linked2.map((unit) => unit.files)
8874
9785
  );
8875
- files2[EXPO_SWIFT_ENTRY] = expoEntry(linked);
9786
+ files2[EXPO_SWIFT_ENTRY] = expoEntry(linked2);
8876
9787
  const targets = [
8877
9788
  ...new Set(
8878
9789
  Object.keys(files2).flatMap((name2) => {
@@ -8895,11 +9806,58 @@ let package = Package(name: "PeachExpo", targets: [${targets.map((name2) => `.ta
8895
9806
  ])
8896
9807
  )
8897
9808
  );
8898
- const artifact = await artifactFor(
8899
- projectFor(packagePath),
8900
- EXPO_SWIFT_ENTRY,
8901
- module2.root
8902
- );
9809
+ return artifactFor(projectFor(packagePath), EXPO_SWIFT_ENTRY, module2.root);
9810
+ };
9811
+ let linked = candidates;
9812
+ let artifact;
9813
+ if (linked.length) {
9814
+ try {
9815
+ artifact = await link(linked);
9816
+ } catch (linkError) {
9817
+ linked = [];
9818
+ for (const unit of candidates) {
9819
+ const packagePath = import_node_path16.default.join(import_node_os9.default.tmpdir(), "peach-expo-swift", key2, unit.moduleId);
9820
+ try {
9821
+ await artifactFor(projectFor(packagePath), EXPO_SWIFT_ENTRY, module2.root);
9822
+ linked.push(unit);
9823
+ } catch (error) {
9824
+ const diagnostic = error instanceof Error ? error.message : String(error);
9825
+ const appPaths = Object.values(unit.files).flatMap((source) => {
9826
+ const match = /^#sourceLocation\(file: (".*"), line: 1\)$/m.exec(source);
9827
+ if (!match) return [];
9828
+ const file = JSON.parse(match[1]);
9829
+ return typeof file === "string" ? [file] : [];
9830
+ });
9831
+ const errors = diagnostic.match(/^.+:\d+(?::\d+)?: error:.*$/gm) ?? [];
9832
+ const sourceErrors = errors.filter(
9833
+ (line) => appPaths.some((file) => line.startsWith(`${file}:`)) && !/external macro implementation type|failed to load library plugin/.test(
9834
+ line
9835
+ )
9836
+ );
9837
+ if (publishArtifact && (sourceErrors.length === 0 || sourceErrors.length !== errors.length) && !(error instanceof SwiftMissingModulesError))
9838
+ throw error;
9839
+ if (sourceErrors.length && sourceErrors.length === errors.length && sourceStamp(packagePath, module2.root) === stamps.get(unit)) {
9840
+ failedCompilations.set(packagePath, {
9841
+ stamp: stamps.get(unit),
9842
+ diagnostic: sourceErrors.join("\n")
9843
+ });
9844
+ }
9845
+ failures.set(
9846
+ unit.moduleId,
9847
+ sourceErrors.length ? sourceErrors.join("\n") : diagnostic
9848
+ );
9849
+ }
9850
+ }
9851
+ if (linked.length === candidates.length) throw linkError;
9852
+ if (linked.length) artifact = await link(linked);
9853
+ }
9854
+ }
9855
+ for (const unit of units) {
9856
+ const diagnostic = failures.get(unit.moduleId);
9857
+ if (diagnostic !== void 0)
9858
+ result.failures.push({ namespace: unit.moduleId, diagnostic });
9859
+ }
9860
+ if (artifact) {
8903
9861
  await publishArtifact?.(artifact);
8904
9862
  result.modules.push({
8905
9863
  namespaces: linked.map((unit) => unit.moduleId),
@@ -8966,114 +9924,1387 @@ var init_expo_swift_bundle = __esm({
8966
9924
  }
8967
9925
  });
8968
9926
 
8969
- // src/bundle-channel.mjs
8970
- var bundle_channel_exports = {};
8971
- __export(bundle_channel_exports, {
8972
- prepareBundleChannel: () => prepareBundleChannel,
8973
- publishBundleChannel: () => publishBundleChannel,
8974
- snapshotOneDevBundle: () => snapshotOneDevBundle
8975
- });
8976
- module.exports = __toCommonJS(bundle_channel_exports);
8977
- var import_node_crypto9 = require("node:crypto");
8978
- var import_node_vm = require("node:vm");
8979
-
8980
- // ../contrast/src/runtime/process-group.ts
8981
- var import_node_child_process = require("node:child_process");
8982
-
8983
- // ../contrast/src/sleep.ts
8984
- function sleep(ms) {
8985
- return new Promise((resolve2) => setTimeout(resolve2, ms));
9927
+ // src/kotlin-view-models.ts
9928
+ function strings2(value) {
9929
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
9930
+ throw new Error("abi-classes printed an invalid list");
9931
+ return value;
8986
9932
  }
8987
-
8988
- // ../contrast/src/runtime/process-group.ts
8989
- var GRACEFUL_TIMEOUT_MS = 3e3;
8990
- var FORCED_EXIT_TIMEOUT_MS = 1e3;
8991
- function signalProcessGroup(child, signal) {
8992
- if (!child.pid) return;
8993
- try {
8994
- process.kill(-child.pid, signal);
8995
- } catch {
8996
- try {
8997
- process.kill(child.pid, signal);
8998
- } catch {
8999
- }
9933
+ function readAbiClasses(output) {
9934
+ return output.split("\n").filter((line) => line.startsWith("{")).map((line) => {
9935
+ const row = JSON.parse(line);
9936
+ if (!isRecord(row) || typeof row.name !== "string" || typeof row.kind !== "string" || typeof row.modality !== "string" || typeof row.inner !== "boolean" || typeof row.generic !== "boolean" || !Array.isArray(row.constructors))
9937
+ throw new Error(`abi-classes printed an invalid class: ${line}`);
9938
+ return {
9939
+ name: row.name,
9940
+ kind: row.kind,
9941
+ modality: row.modality,
9942
+ inner: row.inner,
9943
+ generic: row.generic,
9944
+ supers: strings2(row.supers),
9945
+ constructors: row.constructors.map((constructor) => {
9946
+ if (!isRecord(constructor) || typeof constructor.defaulted !== "boolean")
9947
+ throw new Error(`abi-classes printed an invalid constructor: ${line}`);
9948
+ return {
9949
+ parameters: strings2(constructor.parameters),
9950
+ defaulted: constructor.defaulted
9951
+ };
9952
+ })
9953
+ };
9954
+ });
9955
+ }
9956
+ function matching(row, signature) {
9957
+ for (const { parameters } of row.constructors) {
9958
+ if (parameters.join() === signature.join()) return "match";
9959
+ if (parameters.length === signature.length && signature.every((type) => parameters.includes(type)))
9960
+ return "misordered";
9000
9961
  }
9962
+ return null;
9001
9963
  }
9002
- function runningPids(pids) {
9003
- return pids.filter((pid) => {
9004
- try {
9005
- process.kill(pid, 0);
9006
- return true;
9007
- } catch (error) {
9008
- return error instanceof Error && "code" in error && error.code === "EPERM";
9964
+ function signatureOf(row, android) {
9965
+ const handled = matching(row, android ? [APPLICATION2, HANDLE] : [HANDLE]);
9966
+ if (handled === "misordered") return "misordered";
9967
+ if (handled) return android ? "application-handle" : "handle";
9968
+ if (android)
9969
+ return row.constructors.some(({ parameters }) => parameters.join() === APPLICATION2) ? "application" : null;
9970
+ return row.constructors.some(
9971
+ ({ parameters, defaulted }) => parameters.length === 0 || defaulted
9972
+ ) ? "none" : null;
9973
+ }
9974
+ function viewModelClasses(rows) {
9975
+ const byName = new Map(rows.map((row) => [row.name, row]));
9976
+ const base = (row, seen) => {
9977
+ for (const name2 of row.supers) {
9978
+ if (name2 === VIEW_MODEL || name2 === ANDROID_VIEW_MODEL) return name2;
9979
+ const parent = byName.get(name2);
9980
+ if (parent && !seen.has(name2)) {
9981
+ seen.add(name2);
9982
+ const found = base(parent, seen);
9983
+ if (found) return found;
9984
+ }
9009
9985
  }
9986
+ return null;
9987
+ };
9988
+ return rows.flatMap((row) => {
9989
+ if (row.kind !== "CLASS" || row.inner || row.generic) return [];
9990
+ if (row.modality === "ABSTRACT" || row.modality === "SEALED") return [];
9991
+ const root = base(row, /* @__PURE__ */ new Set());
9992
+ if (!root) return [];
9993
+ const signature = signatureOf(row, root === ANDROID_VIEW_MODEL);
9994
+ return signature ? [{ name: row.name, signature }] : [];
9010
9995
  });
9011
9996
  }
9012
- function groupIsEmpty(pgid) {
9013
- try {
9014
- process.kill(-pgid, 0);
9015
- } catch (error) {
9016
- if (!(error instanceof Error && "code" in error && error.code === "EPERM"))
9017
- return true;
9997
+ var VIEW_MODEL, ANDROID_VIEW_MODEL, APPLICATION2, HANDLE;
9998
+ var init_kotlin_view_models = __esm({
9999
+ "src/kotlin-view-models.ts"() {
10000
+ "use strict";
10001
+ init_is_record();
10002
+ VIEW_MODEL = "androidx.lifecycle.ViewModel";
10003
+ ANDROID_VIEW_MODEL = "androidx.lifecycle.AndroidViewModel";
10004
+ APPLICATION2 = "android.app.Application";
10005
+ HANDLE = "androidx.lifecycle.SavedStateHandle";
9018
10006
  }
9019
- const result = (0, import_node_child_process.spawnSync)("ps", ["-A", "-o", "pgid=,stat="], {
9020
- encoding: "utf8",
9021
- stdio: ["ignore", "pipe", "pipe"]
10007
+ });
10008
+
10009
+ // src/kotlin-host.ts
10010
+ var kotlin_host_exports = {};
10011
+ __export(kotlin_host_exports, {
10012
+ expoKotlinArtifact: () => expoKotlinArtifact,
10013
+ kotlinAppArtifact: () => kotlinAppArtifact,
10014
+ kotlinArtifact: () => kotlinArtifact,
10015
+ kotlinArtifactFiles: () => kotlinArtifactFiles,
10016
+ kotlinSources: () => kotlinSources,
10017
+ peachKotlinModuleRoot: () => peachKotlinModuleRoot,
10018
+ serveKotlinArtifact: () => serveKotlinArtifact,
10019
+ serveKotlinRequest: () => serveKotlinRequest
10020
+ });
10021
+ function readToolchain(moduleRoot) {
10022
+ const parsed = JSON.parse(
10023
+ import_node_fs16.default.readFileSync(import_node_path17.default.join(moduleRoot, "toolchain.json"), "utf8")
10024
+ );
10025
+ const download = (value) => {
10026
+ if (!isRecord(value) || typeof value.url !== "string" || typeof value.sha256 !== "string")
10027
+ throw new Error(
10028
+ "peachsim kotlin/toolchain.json names a download without url and sha256"
10029
+ );
10030
+ return { url: value.url, sha256: value.sha256 };
10031
+ };
10032
+ if (!isRecord(parsed) || !isRecord(parsed.compiler) || !Array.isArray(parsed.libraries) || !Array.isArray(parsed.composeLibraries) || !Array.isArray(parsed.composeJsLibraries) || !isRecord(parsed.composeRuntime) || typeof parsed.composeRuntime.name !== "string")
10033
+ throw new Error("peachsim kotlin/toolchain.json has no compiler and libraries");
10034
+ const version = parsed.compiler.version;
10035
+ if (typeof version !== "string")
10036
+ throw new Error("peachsim kotlin/toolchain.json has no compiler version");
10037
+ const libraries = (values) => values.map((library) => {
10038
+ if (!isRecord(library) || typeof library.name !== "string")
10039
+ throw new Error("peachsim kotlin/toolchain.json names a library without a name");
10040
+ return { ...download(library), name: library.name };
9022
10041
  });
9023
- if (result.error || result.status !== 0) return false;
9024
- return !result.stdout.split("\n").some((line) => {
9025
- const [group, state] = line.trim().split(/\s+/);
9026
- return Number(group) === pgid && state !== void 0 && !state.startsWith("Z");
10042
+ return {
10043
+ compiler: { ...download(parsed.compiler), version },
10044
+ libraries: libraries(parsed.libraries),
10045
+ composeLibraries: libraries(parsed.composeLibraries),
10046
+ composeRuntime: {
10047
+ ...download(parsed.composeRuntime),
10048
+ name: parsed.composeRuntime.name
10049
+ },
10050
+ composeJsLibraries: parsed.composeJsLibraries.map((library) => {
10051
+ if (!isRecord(library) || typeof library.name !== "string" || typeof library.module !== "string" || typeof library.file !== "string" || typeof library.archiveEntry !== "string")
10052
+ throw new Error("peachsim kotlin/toolchain.json has an incomplete JS library");
10053
+ return {
10054
+ ...download(library),
10055
+ name: library.name,
10056
+ module: library.module,
10057
+ file: library.file,
10058
+ archiveEntry: library.archiveEntry
10059
+ };
10060
+ })
10061
+ };
10062
+ }
10063
+ function kotlinHome() {
10064
+ return import_node_path17.default.join(rnxHomeDir(), "kotlin");
10065
+ }
10066
+ async function fetchVerified(download, target) {
10067
+ if (import_node_fs16.default.existsSync(target)) return;
10068
+ const response = await fetch(download.url);
10069
+ if (!response.ok) throw new Error(`${download.url} answered ${response.status}`);
10070
+ const bytes = Buffer.from(await response.arrayBuffer());
10071
+ const digest2 = (0, import_node_crypto9.createHash)("sha256").update(bytes).digest("hex");
10072
+ if (digest2 !== download.sha256)
10073
+ throw new Error(`${download.url} has sha256 ${digest2}, expected ${download.sha256}`);
10074
+ import_node_fs16.default.mkdirSync(import_node_path17.default.dirname(target), { recursive: true });
10075
+ const partial = `${target}.${process.pid}.partial`;
10076
+ import_node_fs16.default.writeFileSync(partial, bytes);
10077
+ import_node_fs16.default.renameSync(partial, target);
10078
+ }
10079
+ function ensureToolchain(toolchain) {
10080
+ toolchainReady ??= (async () => {
10081
+ const dir = import_node_path17.default.join(kotlinHome(), toolchain.compiler.version);
10082
+ const kotlinc = import_node_path17.default.join(dir, "kotlinc", "bin", "kotlinc-wasm");
10083
+ if (!import_node_fs16.default.existsSync(kotlinc)) {
10084
+ const zip = import_node_path17.default.join(dir, "kotlin-compiler.zip");
10085
+ await fetchVerified(toolchain.compiler, zip);
10086
+ await run4("unzip", ["-q", "-o", zip, "-d", dir]);
10087
+ import_node_fs16.default.rmSync(zip);
10088
+ }
10089
+ const java = process.env.JAVA_HOME ? import_node_path17.default.join(process.env.JAVA_HOME, "bin", "java") : "java";
10090
+ await run4(java, ["-version"]).catch(() => {
10091
+ throw new Error(
10092
+ `Kotlin in Peach compiles with kotlinc, which needs a Java runtime (17 or newer, as android builds use); ${process.env.JAVA_HOME ? `JAVA_HOME ${process.env.JAVA_HOME} has none` : "set JAVA_HOME or put java on PATH"}`
10093
+ );
10094
+ });
10095
+ const libraries = [import_node_path17.default.join(dir, "kotlinc", "lib", "kotlin-stdlib-wasm-js.klib")];
10096
+ for (const library of toolchain.libraries) {
10097
+ const target = import_node_path17.default.join(dir, library.name);
10098
+ await fetchVerified(library, target);
10099
+ libraries.push(target);
10100
+ }
10101
+ return { kotlinc, libraries };
10102
+ })().catch((error) => {
10103
+ toolchainReady = void 0;
10104
+ throw error;
9027
10105
  });
10106
+ return toolchainReady;
9028
10107
  }
9029
- async function waitForExit(pids, pgid, timeoutMs) {
9030
- const deadline = Date.now() + timeoutMs;
9031
- const gone = () => runningPids(pids).length === 0 && groupIsEmpty(pgid);
9032
- while (Date.now() < deadline) {
9033
- if (gone()) return true;
9034
- await sleep(50);
9035
- }
9036
- return gone();
10108
+ function appKotlinLanguage(root) {
10109
+ const reactNative = import_node_path17.default.dirname(
10110
+ (0, import_node_module2.createRequire)(import_node_path17.default.join(root, "package.json")).resolve("react-native/package.json")
10111
+ );
10112
+ const versions = import_node_fs16.default.readFileSync(
10113
+ import_node_path17.default.join(reactNative, "gradle", "libs.versions.toml"),
10114
+ "utf8"
10115
+ );
10116
+ const match = /^kotlin\s*=\s*"(\d+)\.(\d+)/m.exec(versions);
10117
+ if (!match)
10118
+ throw new Error(`${reactNative}/gradle/libs.versions.toml names no kotlin version`);
10119
+ return `${match[1]}.${match[2]}`;
9037
10120
  }
9038
- async function terminateProcessGroup(child, signal = "SIGTERM", gracefulTimeoutMs = GRACEFUL_TIMEOUT_MS) {
9039
- if (!child.pid) {
9040
- try {
9041
- child.kill(signal);
9042
- } catch {
9043
- }
9044
- return;
9045
- }
9046
- signalProcessGroup(child, "SIGSTOP");
9047
- const descendants = /* @__PURE__ */ new Set();
9048
- let parents = [child.pid];
9049
- let inspectionError = null;
9050
- while (parents.length > 0) {
9051
- const result = (0, import_node_child_process.spawnSync)("pgrep", ["-P", parents.join(",")], {
9052
- encoding: "utf8",
9053
- stdio: ["ignore", "pipe", "pipe"]
9054
- });
9055
- if (result.error || result.status !== 0 && result.status !== 1) {
9056
- inspectionError = new Error(
9057
- `could not inspect descendants of processes ${parents.join(",")}: ${result.error?.message || result.stderr.trim() || `pgrep exited ${result.status}`}`
9058
- );
9059
- break;
9060
- }
9061
- const next = [];
9062
- for (const line of result.stdout.split("\n")) {
9063
- const descendantPid = Number(line.trim());
9064
- if (Number.isInteger(descendantPid) && descendantPid > 1 && !descendants.has(descendantPid)) {
9065
- descendants.add(descendantPid);
9066
- next.push(descendantPid);
9067
- try {
9068
- process.kill(descendantPid, "SIGSTOP");
9069
- } catch {
9070
- }
9071
- }
10121
+ function kotlinSources(root, skip = SKIP_DIRS) {
10122
+ const sources = [];
10123
+ const walk2 = (dir) => {
10124
+ for (const entry of import_node_fs16.default.readdirSync(dir, { withFileTypes: true })) {
10125
+ if (entry.name.startsWith(".") || skip.has(entry.name)) continue;
10126
+ const full = import_node_path17.default.join(dir, entry.name);
10127
+ if (entry.isDirectory()) walk2(full);
10128
+ else if (entry.isFile() && entry.name.endsWith(".kt")) sources.push(full);
9072
10129
  }
9073
- parents = next;
9074
- }
9075
- const pids = [child.pid, ...descendants];
9076
- const gracefulDeadline = Date.now() + gracefulTimeoutMs;
10130
+ };
10131
+ walk2(root);
10132
+ return sources.sort();
10133
+ }
10134
+ function entrySource(ids) {
10135
+ return [
10136
+ "import android.os.Build",
10137
+ "import kotlin.js.JsAny",
10138
+ "import kotlin.js.Promise",
10139
+ "import kotlinx.coroutines.Dispatchers",
10140
+ "import kotlinx.coroutines.GlobalScope",
10141
+ "import kotlinx.coroutines.promise",
10142
+ "import dev.onejs.one.OneNativeSourceDispatch",
10143
+ "import org.json.JSONObject",
10144
+ "",
10145
+ "private val instances = mutableMapOf<String, OneNativeSourceDispatch>()",
10146
+ "",
10147
+ "private fun dispatcher(packageName: String): OneNativeSourceDispatch? = instances[packageName] ?: when (packageName) {",
10148
+ ...ids.map((id) => ` ${JSON.stringify(id)} -> OneNativeSource_${id}()`),
10149
+ " else -> null",
10150
+ "}?.also { instances[packageName] = it }",
10151
+ "",
10152
+ "@OptIn(kotlin.js.ExperimentalJsExport::class)",
10153
+ "@JsExport",
10154
+ "fun peachSetAndroidBuildInfo(model: String, release: String) = Build.setDevice(model, release)",
10155
+ "",
10156
+ "@OptIn(kotlin.js.ExperimentalJsExport::class, kotlin.js.ExperimentalWasmJsInterop::class, kotlinx.coroutines.DelicateCoroutinesApi::class)",
10157
+ "@JsExport",
10158
+ "fun peachNativeCall(module: String, method: String, argsJson: String, contractHash: String): Promise<JsAny?> =",
10159
+ " GlobalScope.promise(Dispatchers.Main) {",
10160
+ " val reply = JSONObject()",
10161
+ " try {",
10162
+ ` val packageName = module.substringBefore('.', "")`,
10163
+ " val dispatch = dispatcher(packageName)",
10164
+ " if (packageName.isEmpty() || !module.contains('.')) {",
10165
+ ' reply.put("ok", false).put("code", "E_NATIVE_SOURCE").put("error", "invalid module $module")',
10166
+ " } else if (dispatch == null) {",
10167
+ ' reply.put("ok", false).put("code", "E_NATIVE_SOURCE").put("error", "$module.$method: OneNativeSource_$packageName")',
10168
+ " } else if (dispatch.contractHash != contractHash) {",
10169
+ ' reply.put("ok", false).put("code", "E_NATIVE_SOURCE_REBUILD").put("error", "native module $module changed; rebuild the app")',
10170
+ " } else {",
10171
+ ' reply.put("ok", true).put("value", dispatch.call(module, method, argsJson))',
10172
+ " }",
10173
+ " } catch (error: Throwable) {",
10174
+ ' reply.put("ok", false).put("code", "E_NATIVE_SOURCE").put("error", "$module.$method: ${error.message}")',
10175
+ " }",
10176
+ " reply.toString().toJsString()",
10177
+ " }",
10178
+ ""
10179
+ ].join("\n");
10180
+ }
10181
+ function buildsDir() {
10182
+ return import_node_path17.default.join(kotlinHome(), "builds");
10183
+ }
10184
+ async function kotlinArtifact(root, moduleRoot, origin) {
10185
+ const { kotlinSourceId, nativeSourceContract, renderKotlinSourceGlue } = await import("vxrn/native-source-contract");
10186
+ const toolchain = readToolchain(moduleRoot);
10187
+ const language = appKotlinLanguage(root);
10188
+ const sources = kotlinSources(root);
10189
+ if (sources.length === 0) throw new Error(`${root} holds no Kotlin sources`);
10190
+ const generated = {};
10191
+ const ids = [];
10192
+ const viewIds = [];
10193
+ let usesCompose = false;
10194
+ for (const file of sources) {
10195
+ const source = import_node_fs16.default.readFileSync(file, "utf8");
10196
+ if (/\bandroidx\.compose\./.test(source)) usesCompose = true;
10197
+ const contract = nativeSourceContract(file, source);
10198
+ if (contract.modules.length === 0 && contract.views.length === 0) continue;
10199
+ const id = kotlinSourceId(root, file);
10200
+ if (contract.modules.length > 0) ids.push(id);
10201
+ if (contract.views.length > 0) viewIds.push(id);
10202
+ generated[`OneNativeSource_${id}.kt`] = renderKotlinSourceGlue(id, contract).source;
10203
+ }
10204
+ generated["PeachEntry.kt"] = entrySource(ids);
10205
+ if (viewIds.length > 0) generated["PeachViews.kt"] = viewsSource(viewIds);
10206
+ const shims = kotlinSources(import_node_path17.default.join(moduleRoot, "Sources"), /* @__PURE__ */ new Set()).filter(
10207
+ (file) => usesCompose || !file.endsWith("/OneNativeSourceViewDispatch.kt")
10208
+ );
10209
+ if (viewIds.length > 0)
10210
+ shims.push(...kotlinSources(import_node_path17.default.join(moduleRoot, "ComposeSources")));
10211
+ return compileKotlin(
10212
+ { root, toolchain, language, sources, generated, shims, usesCompose },
10213
+ origin
10214
+ );
10215
+ }
10216
+ function readExpoField(value) {
10217
+ if (!isRecord(value) || typeof value.name !== "string" || typeof value.key !== "string" || typeof value.required !== "boolean" || typeof value.mutable !== "boolean" || typeof value.hidden !== "boolean")
10218
+ throw new Error("expo-types printed an invalid field");
10219
+ const range2 = value.initializer;
10220
+ if (range2 !== null && (!Array.isArray(range2) || range2.length !== 2 || typeof range2[0] !== "number" || typeof range2[1] !== "number"))
10221
+ throw new Error("expo-types printed an invalid initializer range");
10222
+ return {
10223
+ name: value.name,
10224
+ key: value.key,
10225
+ required: value.required,
10226
+ mutable: value.mutable,
10227
+ hidden: value.hidden,
10228
+ initializer: range2 === null ? null : [range2[0], range2[1]]
10229
+ };
10230
+ }
10231
+ function readExpoTypes(output) {
10232
+ return output.split("\n").filter((line) => line.startsWith("{")).map((line) => {
10233
+ const row = JSON.parse(line);
10234
+ if (!isRecord(row) || typeof row.file !== "string" || typeof row.package !== "string" || !Array.isArray(row.records) || !Array.isArray(row.enums) || !Array.isArray(row.views) || !Array.isArray(row.props))
10235
+ throw new Error("expo-types printed an invalid file");
10236
+ return {
10237
+ file: row.file,
10238
+ package: row.package,
10239
+ records: row.records.map((record4) => {
10240
+ if (!isRecord(record4) || typeof record4.name !== "string" || typeof record4.hidden !== "boolean" || !Array.isArray(record4.fields))
10241
+ throw new Error("expo-types printed an invalid record");
10242
+ return {
10243
+ name: record4.name,
10244
+ hidden: record4.hidden,
10245
+ fields: record4.fields.map(readExpoField)
10246
+ };
10247
+ }),
10248
+ enums: row.enums.map((entry) => {
10249
+ if (!isRecord(entry) || typeof entry.name !== "string" || typeof entry.hidden !== "boolean" || typeof entry.fields !== "number" || entry.parameter !== null && typeof entry.parameter !== "string" || typeof entry.hiddenParameter !== "boolean")
10250
+ throw new Error("expo-types printed an invalid enum");
10251
+ return {
10252
+ name: entry.name,
10253
+ hidden: entry.hidden,
10254
+ fields: entry.fields,
10255
+ parameter: entry.parameter,
10256
+ hiddenParameter: entry.hiddenParameter
10257
+ };
10258
+ }),
10259
+ views: row.views.map((view) => {
10260
+ if (!isRecord(view) || typeof view.name !== "string" || typeof view.hidden !== "boolean" || typeof view.props !== "string")
10261
+ throw new Error("expo-types printed an invalid view");
10262
+ return { name: view.name, hidden: view.hidden, props: view.props };
10263
+ }),
10264
+ props: row.props.map((entry) => {
10265
+ if (!isRecord(entry) || typeof entry.name !== "string" || typeof entry.hidden !== "boolean" || !Array.isArray(entry.states) || !entry.states.every((state) => typeof state === "string"))
10266
+ throw new Error("expo-types printed invalid compose props");
10267
+ return { name: entry.name, hidden: entry.hidden, states: entry.states };
10268
+ })
10269
+ };
10270
+ });
10271
+ }
10272
+ function scanExpoTypes(toolchain, script, sources) {
10273
+ const candidates = sources.filter(
10274
+ (file) => /\bRecord\b|\benum\s+class\b|\bExpoComposeView\b|\bExpoView\b|\bComposeProps\b/.test(
10275
+ import_node_fs16.default.readFileSync(file, "utf8")
10276
+ )
10277
+ );
10278
+ if (candidates.length === 0) return Promise.resolve([]);
10279
+ const digest2 = (0, import_node_crypto9.createHash)("sha256");
10280
+ for (const file of [script, ...candidates])
10281
+ digest2.update(`${file}\0${import_node_fs16.default.readFileSync(file, "utf8")}\0`);
10282
+ const key2 = digest2.digest("hex");
10283
+ let scan = expoTypeScans.get(key2);
10284
+ if (!scan) {
10285
+ scan = (async () => {
10286
+ const { kotlinc } = await ensureToolchain(toolchain);
10287
+ const { stdout } = await run4(
10288
+ import_node_path17.default.join(import_node_path17.default.dirname(kotlinc), "kotlinc"),
10289
+ [
10290
+ "-cp",
10291
+ import_node_path17.default.resolve(import_node_path17.default.dirname(kotlinc), "../lib/kotlin-compiler.jar"),
10292
+ "-script",
10293
+ script,
10294
+ ...candidates
10295
+ ],
10296
+ { maxBuffer: 16 * 1024 * 1024 }
10297
+ );
10298
+ return readExpoTypes(stdout);
10299
+ })().catch((error) => {
10300
+ expoTypeScans.delete(key2);
10301
+ throw error;
10302
+ });
10303
+ expoTypeScans.set(key2, scan);
10304
+ }
10305
+ return scan;
10306
+ }
10307
+ function expoTypeSources(files2) {
10308
+ const generated = {};
10309
+ const origins = {};
10310
+ const registrations = [];
10311
+ files2.forEach((file, index) => {
10312
+ const edits = [];
10313
+ const calls = [];
10314
+ for (const record4 of file.records) {
10315
+ const fields = [];
10316
+ for (const field of record4.fields) {
10317
+ if (field.initializer)
10318
+ edits.push({ range: field.initializer, owner: record4.name, field });
10319
+ if (field.hidden) {
10320
+ if (!field.initializer)
10321
+ throw new Error(
10322
+ `${file.file}: @Field ${record4.name}.${field.name} is private and has no initializer, which Peach cannot set`
10323
+ );
10324
+ continue;
10325
+ }
10326
+ const name2 = literal(field.key);
10327
+ const read = `{ record: ${record4.name} -> record.${field.name} }`;
10328
+ const write = field.mutable && !field.initializer ? `{ record, value -> record.${field.name} = value }` : "null";
10329
+ fields.push(
10330
+ ` dev.peach.expo.PeachExpoTypes.field(${name2}, ${field.required}, ${read}, ${write}),`
10331
+ );
10332
+ }
10333
+ if (record4.hidden) continue;
10334
+ calls.push(
10335
+ [
10336
+ ` dev.peach.expo.PeachExpoTypes.record(${record4.name}::class, ${literal(file.package ? `${file.package}.${record4.name}` : record4.name)}, { ${record4.name}() }, listOf(`,
10337
+ ...fields,
10338
+ " ))"
10339
+ ].join("\n")
10340
+ );
10341
+ }
10342
+ for (const entry of file.enums) {
10343
+ if (entry.hidden) continue;
10344
+ if (entry.hiddenParameter)
10345
+ throw new Error(
10346
+ `${file.file}: enum ${entry.name} keeps its value in a private property, which Peach cannot read`
10347
+ );
10348
+ const value = entry.fields === 1 ? `{ it.${entry.parameter} }` : "null";
10349
+ calls.push(
10350
+ ` dev.peach.expo.PeachExpoTypes.enum(${entry.name}::class, enumValues<${entry.name}>(), ${entry.fields}, ${entry.parameter === null ? "null" : literal(entry.parameter)}, ${value})`
10351
+ );
10352
+ }
10353
+ const views = [
10354
+ ...file.views.filter((view) => !view.hidden).map(
10355
+ (view) => ` dev.peach.expo.PeachExpoTypes.view(${view.name}::class, ${view.props ? `${view.props}::class` : "null"}) { context, appContext -> ${view.name}(context, appContext) }`
10356
+ ),
10357
+ ...file.props.filter((entry) => !entry.hidden).map(
10358
+ (entry) => [
10359
+ ` dev.peach.expo.PeachExpoTypes.composeProps(${entry.name}::class, listOf(`,
10360
+ ...entry.states.map(
10361
+ (state) => ` dev.peach.expo.PeachExpoCompose.prop(${literal(state)}) { props: ${entry.name} -> props.${state} },`
10362
+ ),
10363
+ " ))"
10364
+ ].join("\n")
10365
+ )
10366
+ ];
10367
+ if (edits.length > 0 || views.length > 0) {
10368
+ let text3 = import_node_fs16.default.readFileSync(file.file, "utf8").replaceAll("\r\n", "\n");
10369
+ for (const { range: range2, owner, field } of edits.sort((a, b) => b.range[0] - a.range[0]))
10370
+ text3 = `${text3.slice(0, range2[0])}dev.peach.expo.PeachExpoTypes.initial(${owner}::class, ${literal(field.key)}, ${field.required}) { ${text3.slice(range2[0], range2[1])} }${text3.slice(range2[1])}`;
10371
+ if (views.length > 0) {
10372
+ text3 = [
10373
+ text3,
10374
+ "",
10375
+ `internal fun peachExpoViews${index}() {`,
10376
+ ...views,
10377
+ "}",
10378
+ ""
10379
+ ].join("\n");
10380
+ registrations.push(
10381
+ `${file.package ? `${file.package}.` : ""}peachExpoViews${index}()`
10382
+ );
10383
+ }
10384
+ const name2 = `PeachExpoSource_${index}_${import_node_path17.default.basename(file.file)}`;
10385
+ generated[name2] = text3;
10386
+ origins[name2] = file.file;
10387
+ }
10388
+ if (calls.length === 0) return;
10389
+ generated[`PeachExpoTypes_${index}.kt`] = [
10390
+ ...file.package ? [`package ${file.package}`, ""] : [],
10391
+ `internal fun peachExpoTypes${index}() {`,
10392
+ ...calls,
10393
+ "}",
10394
+ ""
10395
+ ].join("\n");
10396
+ registrations.push(
10397
+ `${file.package ? `${file.package}.` : ""}peachExpoTypes${index}()`
10398
+ );
10399
+ });
10400
+ return { generated, origins, registrations };
10401
+ }
10402
+ async function expoKotlinArtifact(root, moduleRoot, origin, modules) {
10403
+ const toolchain = readToolchain(moduleRoot);
10404
+ const language = appKotlinLanguage(root);
10405
+ const scanned = await scanExpoTypes(
10406
+ toolchain,
10407
+ import_node_path17.default.join(moduleRoot, "tools/expo-types.main.kts"),
10408
+ modules.sources
10409
+ );
10410
+ const usesCompose = scanned.some((file) => file.views.length > 0) || modules.sources.some(
10411
+ (file) => /\bandroidx\.compose\./.test(import_node_fs16.default.readFileSync(file, "utf8"))
10412
+ );
10413
+ const shims = [
10414
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "Sources"), /* @__PURE__ */ new Set()).filter(
10415
+ (file) => usesCompose || !file.endsWith("/OneNativeSourceViewDispatch.kt")
10416
+ ),
10417
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "ExpoSources"), /* @__PURE__ */ new Set()),
10418
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "AndroidViewSources"), /* @__PURE__ */ new Set()),
10419
+ ...usesCompose ? [
10420
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "ComposeSources")),
10421
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "ExpoComposeSources"), /* @__PURE__ */ new Set())
10422
+ ] : []
10423
+ ];
10424
+ const types = expoTypeSources(scanned);
10425
+ const rewritten = new Set(Object.values(types.origins));
10426
+ const entry = [
10427
+ "@file:OptIn(kotlin.js.ExperimentalJsExport::class)",
10428
+ "",
10429
+ "import dev.peach.expo.PeachExpoRuntime",
10430
+ "",
10431
+ "private val registered by lazy {",
10432
+ ...types.registrations.map((call) => ` ${call}`),
10433
+ " PeachExpoRuntime.register(listOf(",
10434
+ ...modules.modules.map(
10435
+ ({ classifier, name: name2 }) => ` { ${classifier}() } to ${name2 === null ? "null" : JSON.stringify(name2)},`
10436
+ ),
10437
+ " ))",
10438
+ "}",
10439
+ "",
10440
+ "@JsExport fun peachExpoListen(notify: () -> Unit) {",
10441
+ " registered",
10442
+ " PeachExpoRuntime.listen(notify)",
10443
+ "}",
10444
+ "",
10445
+ "@JsExport fun peachExpoCall(payload: String): String {",
10446
+ " registered",
10447
+ " return PeachExpoRuntime.dispatch(payload)",
10448
+ "}",
10449
+ ""
10450
+ ].join("\n");
10451
+ const artifact = await compileKotlin(
10452
+ {
10453
+ root,
10454
+ toolchain,
10455
+ language,
10456
+ sources: modules.sources.filter((file) => !rewritten.has(file)),
10457
+ generated: {
10458
+ ...types.generated,
10459
+ "PeachExpoEntry.kt": entry,
10460
+ ...usesCompose ? { "PeachViews.kt": viewsSource([]) } : {}
10461
+ },
10462
+ origins: types.origins,
10463
+ shims,
10464
+ usesCompose
10465
+ },
10466
+ origin
10467
+ );
10468
+ return { ...artifact, compose: usesCompose };
10469
+ }
10470
+ function viewsSource(viewIds) {
10471
+ return [
10472
+ "import dev.onejs.one.OneNativeSourceViewDispatch",
10473
+ "",
10474
+ "fun peachViewDispatcher(source: String): OneNativeSourceViewDispatch = when (source) {",
10475
+ ...viewIds.map((id) => ` ${JSON.stringify(id)} -> OneNativeSourceViews_${id}()`),
10476
+ ' else -> error("composable source $source is not linked; rebuild the app")',
10477
+ "}"
10478
+ ].join("\n");
10479
+ }
10480
+ function viewModelInitializer({ name: name2, signature }) {
10481
+ const call = (args) => ` addInitializer(${name2}::class) { ${name2}(${args}) }`;
10482
+ switch (signature) {
10483
+ case "none":
10484
+ return call("");
10485
+ case "handle":
10486
+ return call("createSavedStateHandle()");
10487
+ case "application":
10488
+ return call("dev.peach.app.peachApplication()");
10489
+ case "application-handle":
10490
+ return call("dev.peach.app.peachApplication(), createSavedStateHandle()");
10491
+ case "misordered":
10492
+ return ` addInitializer(${name2}::class) { throw UnsupportedOperationException(${JSON.stringify(
10493
+ `Class ${name2.split(".").pop()} must have parameters in the proper order: [class android.app.Application, class androidx.lifecycle.SavedStateHandle]`
10494
+ )}) }`;
10495
+ }
10496
+ }
10497
+ function windowTheme(window) {
10498
+ return [
10499
+ " android.view.WindowTheme(",
10500
+ ` ${window.edgeToEdgeEnforced}, ${argb(window.statusBarColor)}, ${argb(window.navigationBarColor)},`,
10501
+ ` ${window.statusBarContrastEnforced}, ${window.navigationBarContrastEnforced},`,
10502
+ ` ${window.lightStatusBar}, ${window.lightNavigationBar},`,
10503
+ " ),"
10504
+ ];
10505
+ }
10506
+ async function kotlinAppArtifact(app, moduleRoot, origin) {
10507
+ const toolchain = readToolchain(moduleRoot);
10508
+ const appEntry = (viewModels) => [
10509
+ "@file:OptIn(kotlin.js.ExperimentalJsExport::class)",
10510
+ "",
10511
+ "import androidx.lifecycle.createSavedStateHandle",
10512
+ "",
10513
+ "@JsExport",
10514
+ "fun peachAppMount(density: Float, fontScale: Float, locale: String,",
10515
+ " font: String, insets: String, night: Boolean,",
10516
+ " invalidate: (Boolean) -> Unit): String = dev.peach.app.peachStartApp(",
10517
+ " dev.peach.app.PeachAppSpec(",
10518
+ ` android.content.ComponentName(${literal(app.packageName)}, ${literal(app.launcher.name)}),`,
10519
+ ` android.content.ComponentName(${literal(app.packageName)}, ${literal(app.launcher.targetClass)}),`,
10520
+ ...windowTheme(app.window.day),
10521
+ ...windowTheme(app.window.night),
10522
+ ` { ${app.applicationClass ?? "android.app.Application"}() },`,
10523
+ ` { ${app.launcher.targetClass}() },`,
10524
+ ` ${app.handlesUiMode},`,
10525
+ ` ${literal(app.softInputAdjust)},`,
10526
+ " androidx.lifecycle.viewmodel.viewModelFactory {",
10527
+ ...viewModels.map(viewModelInitializer),
10528
+ " },",
10529
+ ` ${app.predictiveBack},`,
10530
+ " ),",
10531
+ " density, fontScale, locale, font, insets, night, invalidate,",
10532
+ ")",
10533
+ "",
10534
+ "@JsExport",
10535
+ `fun peachAppBack(): Boolean = dev.peach.app.peachAppBack(${app.predictiveBack})`,
10536
+ "",
10537
+ "@JsExport",
10538
+ "fun peachAppTask(foreground: Boolean) = dev.peach.app.peachAppTask(foreground)",
10539
+ "",
10540
+ "@JsExport",
10541
+ "fun peachAppNight(night: Boolean) = dev.peach.app.peachAppNight(night)",
10542
+ "",
10543
+ "@JsExport",
10544
+ "fun peachAppIme(bottom: Int, visible: Boolean) = dev.peach.app.peachAppIme(bottom, visible)",
10545
+ "",
10546
+ "@JsExport",
10547
+ "fun peachAppInsets(insets: String, visibleSystemBars: Int) = dev.peach.app.peachAppInsets(insets, visibleSystemBars)",
10548
+ ""
10549
+ ].join("\n");
10550
+ const generated = {
10551
+ "PeachEntry.kt": entrySource([]),
10552
+ "PeachViews.kt": viewsSource([]),
10553
+ "PeachAppEntry.kt": appEntry([]),
10554
+ ...appResourceSources(app)
10555
+ };
10556
+ const shims = [
10557
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "Sources"), /* @__PURE__ */ new Set()),
10558
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "ComposeSources")),
10559
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "AppSources"), /* @__PURE__ */ new Set()),
10560
+ ...kotlinSources(import_node_path17.default.join(moduleRoot, "AndroidViewSources"), /* @__PURE__ */ new Set())
10561
+ ];
10562
+ return compileKotlin(
10563
+ {
10564
+ root: app.projectDir,
10565
+ toolchain,
10566
+ language: app.language,
10567
+ sources: app.sources,
10568
+ generated,
10569
+ shims,
10570
+ usesCompose: true,
10571
+ viewModels: {
10572
+ file: "PeachAppEntry.kt",
10573
+ render: appEntry,
10574
+ script: import_node_path17.default.join(moduleRoot, "tools/abi-classes.main.kts")
10575
+ }
10576
+ },
10577
+ origin
10578
+ );
10579
+ }
10580
+ function appResourceSources(app) {
10581
+ const field = (name2) => `\`${name2.replace(/[.-]/g, "_")}\``;
10582
+ const id = (type, entry) => `0x7f${type.toString(16).padStart(2, "0")}${entry.toString(16).padStart(4, "0")}`;
10583
+ const localized = (resources, render) => resources.map((resource) => ({
10584
+ name: resource.name,
10585
+ init: `mapOf(${Object.entries(resource.values).map(([locale, value]) => `${literal(locale)} to ${render(value)}`).join(", ")})`
10586
+ }));
10587
+ const kinds = [
10588
+ {
10589
+ type: "string",
10590
+ table: "peachAppStrings",
10591
+ value: "Map<String, String>",
10592
+ entries: localized(app.strings.strings, literal)
10593
+ },
10594
+ {
10595
+ type: "plurals",
10596
+ table: "peachAppPlurals",
10597
+ value: "Map<String, Map<String, String>>",
10598
+ entries: localized(
10599
+ app.strings.plurals,
10600
+ (items2) => `mapOf(${Object.entries(items2).map(([quantity, text3]) => `${literal(quantity)} to ${literal(text3 ?? "")}`).join(", ")})`
10601
+ )
10602
+ },
10603
+ {
10604
+ type: "array",
10605
+ table: "peachAppArrays",
10606
+ value: "Map<String, List<String>>",
10607
+ entries: localized(
10608
+ app.strings.arrays,
10609
+ (items2) => `listOf(${items2.map(literal).join(", ")})`
10610
+ )
10611
+ },
10612
+ {
10613
+ type: "drawable",
10614
+ table: "peachAppDrawables",
10615
+ value: "List<PeachDrawable>",
10616
+ entries: app.drawables.map((drawable) => ({
10617
+ name: drawable.name,
10618
+ init: `listOf(
10619
+ ${drawable.files.map((file) => drawableInit(file, import_node_path17.default.relative(app.moduleDir, file.file))).join(",\n")}
10620
+ )`
10621
+ }))
10622
+ },
10623
+ // the day and the night ARGB
10624
+ {
10625
+ type: "color",
10626
+ table: "peachAppColors",
10627
+ value: "IntArray",
10628
+ entries: app.colors.map((color) => ({
10629
+ name: color.name,
10630
+ init: `intArrayOf(${argb(color.day)}, ${argb(color.night)})`
10631
+ }))
10632
+ }
10633
+ ].map((kind, index) => ({
10634
+ ...kind,
10635
+ entries: kind.entries.map((entry, number) => ({
10636
+ ...entry,
10637
+ id: id(index + 1, number)
10638
+ }))
10639
+ }));
10640
+ return {
10641
+ "R.kt": [
10642
+ `package ${app.namespace}`,
10643
+ "",
10644
+ "object R {",
10645
+ ...kinds.flatMap((kind) => [
10646
+ ` object ${kind.type} {`,
10647
+ ...kind.entries.map(
10648
+ (entry) => ` const val ${field(entry.name)}: Int = ${entry.id}`
10649
+ ),
10650
+ " }"
10651
+ ]),
10652
+ "}",
10653
+ ""
10654
+ ].join("\n"),
10655
+ "PeachAppResources.kt": [
10656
+ "package dev.peach.app",
10657
+ "",
10658
+ ...[
10659
+ "androidx.compose.ui.graphics.BlendMode",
10660
+ "androidx.compose.ui.graphics.Color",
10661
+ "androidx.compose.ui.graphics.PathFillType",
10662
+ "androidx.compose.ui.graphics.StrokeCap",
10663
+ "androidx.compose.ui.graphics.StrokeJoin",
10664
+ "androidx.compose.ui.graphics.vector.ImageVector",
10665
+ "androidx.compose.ui.graphics.vector.addPathNodes",
10666
+ "androidx.compose.ui.unit.Dp"
10667
+ ].map((name2) => `import ${name2}`),
10668
+ "",
10669
+ ...kinds.flatMap((kind) => [
10670
+ `internal val ${kind.table}: Map<Int, ${kind.value}> = mapOf(`,
10671
+ ...kind.entries.map((entry) => ` ${entry.id} to ${entry.init},`),
10672
+ ")",
10673
+ ""
10674
+ ])
10675
+ ].join("\n")
10676
+ };
10677
+ }
10678
+ function drawableInit(drawable, file) {
10679
+ const where = `${literal(file)}, ${drawable.density}, ${drawable.version}`;
10680
+ if (drawable.kind === "xml") return `PeachDrawable.Xml(${where})`;
10681
+ if (drawable.kind === "bitmap")
10682
+ return `PeachDrawable.Bitmap(${where}, ${drawable.ninePatch}, ${literal(
10683
+ import_node_fs16.default.readFileSync(drawable.file).toString("base64")
10684
+ )})`;
10685
+ const vector = drawable.vector;
10686
+ const float = (value) => `${value}f`;
10687
+ const pick = (value) => value.day === value.night ? argb(value.day) : `if (night) ${argb(value.night)} else ${argb(value.day)}`;
10688
+ const nodes = (pathData) => pathData ? `addPathNodes(${literal(pathData)})` : "emptyList()";
10689
+ const calls = vector.calls.map((call) => {
10690
+ if (call.call === "clear") return ".clearGroup()";
10691
+ if (call.call === "clip")
10692
+ return `.addGroup(name = ${literal(call.name)}, clipPathData = ${nodes(call.pathData)})`;
10693
+ if (call.call === "group")
10694
+ return `.addGroup(${[
10695
+ literal(call.name),
10696
+ ...[
10697
+ call.rotate,
10698
+ call.pivotX,
10699
+ call.pivotY,
10700
+ call.scaleX,
10701
+ call.scaleY,
10702
+ call.translateX,
10703
+ call.translateY
10704
+ ].map(float)
10705
+ ].join(", ")})`;
10706
+ return `.addPath(${[
10707
+ nodes(call.pathData),
10708
+ `PathFillType.${call.fillType}`,
10709
+ literal(call.name),
10710
+ `peachBrush(${pick(call.fill)})`,
10711
+ float(call.fillAlpha),
10712
+ `peachBrush(${pick(call.stroke)})`,
10713
+ float(call.strokeAlpha),
10714
+ float(call.strokeWidth),
10715
+ `StrokeCap.${call.cap}`,
10716
+ `StrokeJoin.${call.join}`,
10717
+ ...[call.miter, call.trimStart, call.trimEnd, call.trimOffset].map(float)
10718
+ ].join(", ")})`;
10719
+ });
10720
+ return [
10721
+ `PeachDrawable.Vector(${where}) { night -> ImageVector.Builder(`,
10722
+ ` defaultWidth = Dp(${float(vector.width)}), defaultHeight = Dp(${float(vector.height)}),`,
10723
+ ` viewportWidth = ${float(vector.viewportWidth)}, viewportHeight = ${float(vector.viewportHeight)},`,
10724
+ ` tintColor = ${vector.tint ? `Color(${pick(vector.tint)})` : "Color.Unspecified"},`,
10725
+ ` tintBlendMode = BlendMode.${vector.tintMode}, autoMirror = ${vector.autoMirror},`,
10726
+ ` )`,
10727
+ ...calls.map((call) => ` ${call}`),
10728
+ " .build() }"
10729
+ ].join("\n");
10730
+ }
10731
+ async function compileKotlin(input, origin) {
10732
+ const {
10733
+ root,
10734
+ toolchain,
10735
+ language,
10736
+ sources,
10737
+ generated,
10738
+ origins = {},
10739
+ shims,
10740
+ usesCompose,
10741
+ viewModels
10742
+ } = input;
10743
+ const digest2 = (0, import_node_crypto9.createHash)("sha256");
10744
+ digest2.update(JSON.stringify({ format: BUILD_FORMAT, toolchain, language }));
10745
+ for (const file of [...shims, ...sources, ...viewModels ? [viewModels.script] : []])
10746
+ digest2.update(`${import_node_path17.default.relative(root, file)}\0${import_node_fs16.default.readFileSync(file, "utf8")}\0`);
10747
+ for (const [name2, text3] of Object.entries(generated))
10748
+ digest2.update(`${name2}\0${text3}\0`);
10749
+ const hash = digest2.digest("hex").slice(0, 24);
10750
+ const artifact = {
10751
+ url: `${origin}${KOTLIN_ARTIFACT_PREFIX}${hash}/app.mjs`,
10752
+ hash
10753
+ };
10754
+ const out = import_node_path17.default.join(buildsDir(), hash);
10755
+ if (import_node_fs16.default.existsSync(import_node_path17.default.join(out, "js", "app.wasm"))) return artifact;
10756
+ const pending = builds.get(hash);
10757
+ if (pending) return pending;
10758
+ const build = (async () => {
10759
+ const ready = await ensureToolchain(toolchain);
10760
+ const { kotlinc } = ready;
10761
+ const libraries = [...ready.libraries];
10762
+ const composeRuntime = import_node_path17.default.join(
10763
+ kotlinHome(),
10764
+ toolchain.compiler.version,
10765
+ toolchain.composeRuntime.name
10766
+ );
10767
+ if (usesCompose) {
10768
+ for (const library of toolchain.composeLibraries) {
10769
+ const target = import_node_path17.default.join(kotlinHome(), toolchain.compiler.version, library.name);
10770
+ await fetchVerified(library, target);
10771
+ libraries.push(target);
10772
+ }
10773
+ await fetchVerified(toolchain.composeRuntime, composeRuntime);
10774
+ }
10775
+ import_node_fs16.default.mkdirSync(buildsDir(), { recursive: true });
10776
+ const work = import_node_fs16.default.mkdtempSync(import_node_path17.default.join(buildsDir(), `${hash}-`));
10777
+ const generatedDir = import_node_path17.default.join(work, "generated");
10778
+ import_node_fs16.default.mkdirSync(generatedDir);
10779
+ for (const [name2, text3] of Object.entries(generated))
10780
+ import_node_fs16.default.writeFileSync(import_node_path17.default.join(generatedDir, name2), text3);
10781
+ const common = [
10782
+ // the full upstream Compose graph exceeds kotlinc's 512 mib default heap.
10783
+ "-J-Xmx2g",
10784
+ "-Xallow-kotlin-package",
10785
+ "-Xwasm-target=js",
10786
+ "-libraries",
10787
+ libraries.join(import_node_path17.default.delimiter)
10788
+ ];
10789
+ const compileKlib = (outputDir) => run4(
10790
+ kotlinc,
10791
+ [
10792
+ ...common,
10793
+ ...usesCompose ? [
10794
+ `-Xplugin=${import_node_path17.default.resolve(import_node_path17.default.dirname(kotlinc), "../lib/compose-compiler-plugin.jar")}`
10795
+ ] : [],
10796
+ "-language-version",
10797
+ language,
10798
+ "-api-version",
10799
+ language,
10800
+ "-nowarn",
10801
+ "-nopack",
10802
+ "-ir-output-dir",
10803
+ outputDir,
10804
+ "-ir-output-name",
10805
+ "app",
10806
+ ...shims,
10807
+ ...sources,
10808
+ ...Object.keys(generated).map((name2) => import_node_path17.default.join(generatedDir, name2))
10809
+ ],
10810
+ { maxBuffer: 16 * 1024 * 1024 }
10811
+ );
10812
+ try {
10813
+ if (viewModels && sources.some((file) => import_node_fs16.default.readFileSync(file, "utf8").includes("ViewModel"))) {
10814
+ await compileKlib(import_node_path17.default.join(work, "abi"));
10815
+ const { stdout } = await run4(
10816
+ import_node_path17.default.join(import_node_path17.default.dirname(kotlinc), "kotlinc"),
10817
+ [
10818
+ "-cp",
10819
+ import_node_path17.default.resolve(import_node_path17.default.dirname(kotlinc), "../lib/kotlin-compiler.jar"),
10820
+ "-script",
10821
+ viewModels.script,
10822
+ import_node_path17.default.join(work, "abi")
10823
+ ],
10824
+ { maxBuffer: 64 * 1024 * 1024 }
10825
+ );
10826
+ import_node_fs16.default.writeFileSync(
10827
+ import_node_path17.default.join(generatedDir, viewModels.file),
10828
+ viewModels.render(viewModelClasses(readAbiClasses(stdout)))
10829
+ );
10830
+ }
10831
+ await compileKlib(import_node_path17.default.join(work, "klib"));
10832
+ await run4(
10833
+ kotlinc,
10834
+ [
10835
+ ...common,
10836
+ "-nowarn",
10837
+ `-Xinclude=${import_node_path17.default.join(work, "klib")}`,
10838
+ "-Xir-produce-js",
10839
+ "-ir-output-dir",
10840
+ import_node_path17.default.join(work, "js"),
10841
+ "-ir-output-name",
10842
+ "app"
10843
+ ],
10844
+ { maxBuffer: 16 * 1024 * 1024 }
10845
+ );
10846
+ if (usesCompose) {
10847
+ await run4("unzip", [
10848
+ "-q",
10849
+ "-j",
10850
+ composeRuntime,
10851
+ "skiko.mjs",
10852
+ "-d",
10853
+ import_node_path17.default.join(work, "js")
10854
+ ]);
10855
+ const skikoPath = import_node_path17.default.join(work, "js", "skiko.mjs");
10856
+ const upstream = (0, import_acorn.parse)(import_node_fs16.default.readFileSync(skikoPath, "utf8"), {
10857
+ ecmaVersion: "latest",
10858
+ sourceType: "module"
10859
+ });
10860
+ const bindings = [];
10861
+ for (const statement of upstream.body) {
10862
+ if (statement.type !== "ExportNamedDeclaration" || statement.declaration?.type !== "VariableDeclaration" || statement.declaration.kind !== "let")
10863
+ continue;
10864
+ for (const binding of statement.declaration.declarations) {
10865
+ if (binding.id.type !== "Identifier")
10866
+ throw new Error("Skiko binding must have one exported identifier");
10867
+ bindings.push(binding.id.name);
10868
+ }
10869
+ }
10870
+ if (bindings.length !== 967) throw new Error("pinned Skiko binding count changed");
10871
+ const names = [
10872
+ "awaitSkiko",
10873
+ "loadedWasm",
10874
+ "registerSkikoWasmReadyCallback",
10875
+ "skikoApi",
10876
+ "GL",
10877
+ ...bindings
10878
+ ];
10879
+ import_node_fs16.default.writeFileSync(
10880
+ skikoPath,
10881
+ [
10882
+ "const runtime = globalThis.__peachKotlinSkiko;",
10883
+ "if (!runtime) throw Error('Kotlin tenant runtime was not installed before guest evaluation');",
10884
+ `for (const name of ${JSON.stringify(names)}) if (!(name in runtime)) throw Error('Kotlin tenant runtime has no ' + name);`,
10885
+ ...names.map((name2) => `export const ${name2} = runtime.${name2};`),
10886
+ ""
10887
+ ].join("\n")
10888
+ );
10889
+ }
10890
+ if (usesCompose) {
10891
+ for (const library of toolchain.composeJsLibraries) {
10892
+ let imported = false;
10893
+ for (const file of import_node_fs16.default.readdirSync(import_node_path17.default.join(work, "js"))) {
10894
+ if (!file.endsWith(".mjs")) continue;
10895
+ const target = import_node_path17.default.join(work, "js", file);
10896
+ const source = import_node_fs16.default.readFileSync(target, "utf8");
10897
+ let next = source;
10898
+ for (const quote of ["'", '"'])
10899
+ next = next.replaceAll(
10900
+ `from ${quote}${library.module}${quote}`,
10901
+ `from ${quote}./${library.file}${quote}`
10902
+ );
10903
+ if (next === source) continue;
10904
+ import_node_fs16.default.writeFileSync(target, next);
10905
+ imported = true;
10906
+ }
10907
+ if (!imported) continue;
10908
+ const archive = import_node_path17.default.join(
10909
+ kotlinHome(),
10910
+ toolchain.compiler.version,
10911
+ library.name
10912
+ );
10913
+ await fetchVerified(library, archive);
10914
+ const { stdout } = await run4("tar", ["-xOf", archive, library.archiveEntry], {
10915
+ maxBuffer: 16 * 1024 * 1024
10916
+ });
10917
+ import_node_fs16.default.writeFileSync(import_node_path17.default.join(work, "js", library.file), stdout);
10918
+ }
10919
+ }
10920
+ } catch (error) {
10921
+ import_node_fs16.default.rmSync(work, { recursive: true, force: true });
10922
+ let output = isRecord(error) ? `${String(error.stderr ?? "")}${String(error.stdout ?? "")}` : "";
10923
+ for (const [name2, source] of Object.entries(origins))
10924
+ output = output.replaceAll(import_node_path17.default.join(generatedDir, name2), source);
10925
+ throw new Error(
10926
+ `kotlin compile failed:
10927
+ ${output.trim() || (error instanceof Error ? error.message : String(error))}`
10928
+ );
10929
+ }
10930
+ const seams = [
10931
+ ["process.release.name", "process.release?.name"]
10932
+ ];
10933
+ const js = import_node_fs16.default.readdirSync(import_node_path17.default.join(work, "js")).filter((file) => file.endsWith(".mjs"));
10934
+ if (js.some(
10935
+ (file) => import_node_fs16.default.readFileSync(import_node_path17.default.join(work, "js", file), "utf8").includes("'kotlinx.coroutines.")
10936
+ ))
10937
+ seams.push([
10938
+ "(typeof(window) !== 'undefined' && window != null && typeof(window.addEventListener) === 'function') ? window : null",
10939
+ "null"
10940
+ ]);
10941
+ for (const [from, to] of seams) {
10942
+ let patched = 0;
10943
+ for (const file of js) {
10944
+ const target = import_node_path17.default.join(work, "js", file);
10945
+ const text3 = import_node_fs16.default.readFileSync(target, "utf8");
10946
+ const next = text3.replaceAll(from, to);
10947
+ if (next !== text3) {
10948
+ patched++;
10949
+ import_node_fs16.default.writeFileSync(target, next);
10950
+ }
10951
+ }
10952
+ if (patched === 0)
10953
+ throw new Error(
10954
+ `kotlin output no longer contains ${from}; recheck how it detects its host in Peach`
10955
+ );
10956
+ }
10957
+ try {
10958
+ import_node_fs16.default.renameSync(work, out);
10959
+ } catch (error) {
10960
+ if (!import_node_fs16.default.existsSync(import_node_path17.default.join(out, "js", "app.wasm"))) throw error;
10961
+ import_node_fs16.default.rmSync(work, { recursive: true, force: true });
10962
+ }
10963
+ return artifact;
10964
+ })().finally(() => builds.delete(hash));
10965
+ builds.set(hash, build);
10966
+ return build;
10967
+ }
10968
+ function kotlinArtifactFiles(hash) {
10969
+ const dir = import_node_path17.default.join(buildsDir(), hash, "js");
10970
+ return import_node_fs16.default.readdirSync(dir).sort().map((name2) => {
10971
+ const contentType = CONTENT_TYPES[import_node_path17.default.extname(name2)];
10972
+ if (!contentType) throw new Error(`unexpected kotlin artifact file ${name2}`);
10973
+ return { name: name2, bytes: import_node_fs16.default.readFileSync(import_node_path17.default.join(dir, name2)), contentType };
10974
+ });
10975
+ }
10976
+ function serveKotlinArtifact(pathname, res) {
10977
+ if (!pathname.startsWith(KOTLIN_ARTIFACT_PREFIX)) return false;
10978
+ const [hash, file, ...rest] = pathname.slice(KOTLIN_ARTIFACT_PREFIX.length).split("/");
10979
+ const type = file ? CONTENT_TYPES[import_node_path17.default.extname(file)] : void 0;
10980
+ if (!hash || !/^[0-9a-f]+$/.test(hash) || !file || rest.length > 0 || !type || file.includes("..")) {
10981
+ res.statusCode = 404;
10982
+ res.end("not a kotlin artifact");
10983
+ return true;
10984
+ }
10985
+ const target = import_node_path17.default.join(buildsDir(), hash, "js", file);
10986
+ if (!import_node_fs16.default.existsSync(target)) {
10987
+ res.statusCode = 404;
10988
+ res.end(`kotlin artifact ${hash}/${file} was not built`);
10989
+ return true;
10990
+ }
10991
+ res.setHeader("access-control-allow-origin", "*");
10992
+ res.setHeader("content-type", type);
10993
+ res.setHeader("cache-control", "public, max-age=31536000, immutable");
10994
+ res.end(import_node_fs16.default.readFileSync(target));
10995
+ return true;
10996
+ }
10997
+ function peachKotlinModuleRoot() {
10998
+ return import_node_path17.default.resolve(import_node_path17.default.dirname((0, import_node_url2.fileURLToPath)(__sootsim_import_meta_url)), "..", "kotlin");
10999
+ }
11000
+ async function serveKotlinRequest(pathname, res, app) {
11001
+ if (serveKotlinArtifact(pathname, res)) return true;
11002
+ if (pathname !== KOTLIN_PACKAGE_PATH) return false;
11003
+ res.setHeader("access-control-allow-origin", "*");
11004
+ try {
11005
+ const artifact = app.project ? await kotlinAppArtifact(app.project, app.moduleRoot, app.origin) : await kotlinArtifact(app.root, app.moduleRoot, app.origin);
11006
+ res.setHeader("content-type", "application/json");
11007
+ res.end(JSON.stringify({ ...artifact, hmrPath: app.hmrPath }));
11008
+ } catch (error) {
11009
+ res.statusCode = 500;
11010
+ res.end(error instanceof Error ? error.message : String(error));
11011
+ }
11012
+ return true;
11013
+ }
11014
+ var import_node_child_process10, import_node_crypto9, import_node_fs16, import_node_module2, import_node_path17, import_node_url2, import_node_util4, import_acorn, run4, KOTLIN_ARTIFACT_PREFIX, toolchainReady, SKIP_DIRS, BUILD_FORMAT, builds, expoTypeScans, argb, escapes, literal, CONTENT_TYPES, KOTLIN_PACKAGE_PATH;
11015
+ var init_kotlin_host = __esm({
11016
+ "src/kotlin-host.ts"() {
11017
+ "use strict";
11018
+ import_node_child_process10 = require("node:child_process");
11019
+ import_node_crypto9 = require("node:crypto");
11020
+ import_node_fs16 = __toESM(require("node:fs"), 1);
11021
+ import_node_module2 = require("node:module");
11022
+ import_node_path17 = __toESM(require("node:path"), 1);
11023
+ import_node_url2 = require("node:url");
11024
+ import_node_util4 = require("node:util");
11025
+ import_acorn = require("acorn");
11026
+ init_home_paths();
11027
+ init_is_record();
11028
+ init_kotlin_view_models();
11029
+ run4 = (0, import_node_util4.promisify)(import_node_child_process10.execFile);
11030
+ KOTLIN_ARTIFACT_PREFIX = "/__peach/kotlin-artifact/";
11031
+ SKIP_DIRS = /* @__PURE__ */ new Set([
11032
+ "node_modules",
11033
+ "ios",
11034
+ "android",
11035
+ "dist",
11036
+ "types",
11037
+ "build",
11038
+ "tests",
11039
+ "__tests__",
11040
+ "scripts"
11041
+ ]);
11042
+ BUILD_FORMAT = 14;
11043
+ builds = /* @__PURE__ */ new Map();
11044
+ expoTypeScans = /* @__PURE__ */ new Map();
11045
+ argb = (color) => `0x${color.toString(16).padStart(8, "0").toUpperCase()}.toInt()`;
11046
+ escapes = {
11047
+ "\\": "\\\\",
11048
+ '"': '\\"',
11049
+ $: "\\$",
11050
+ "\n": "\\n",
11051
+ "\r": "\\r",
11052
+ " ": "\\t"
11053
+ };
11054
+ literal = (value) => `"${value.replace(
11055
+ /[\\"$\u0000-\u001f]/g,
11056
+ (char) => escapes[char] ?? `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`
11057
+ )}"`;
11058
+ CONTENT_TYPES = {
11059
+ ".mjs": "text/javascript",
11060
+ ".wasm": "application/wasm"
11061
+ };
11062
+ KOTLIN_PACKAGE_PATH = "/__peach/kotlin-package";
11063
+ }
11064
+ });
11065
+
11066
+ // src/expo-kotlin-host.ts
11067
+ async function collectExpoKotlinModules(appRoot) {
11068
+ appRoot = import_node_fs17.default.realpathSync(appRoot);
11069
+ let autolinker;
11070
+ try {
11071
+ autolinker = (0, import_node_module3.createRequire)(import_node_path18.default.join(appRoot, "package.json")).resolve(
11072
+ "expo-modules-autolinking/package.json"
11073
+ );
11074
+ } catch (error) {
11075
+ if (isRecord(error) && error.code === "MODULE_NOT_FOUND") return null;
11076
+ throw error;
11077
+ }
11078
+ const { stdout } = await run5(
11079
+ process.execPath,
11080
+ [
11081
+ import_node_path18.default.join(import_node_path18.default.dirname(autolinker), "bin/expo-modules-autolinking.js"),
11082
+ "resolve",
11083
+ "--platform",
11084
+ "android",
11085
+ "--project-root",
11086
+ appRoot,
11087
+ "--json"
11088
+ ],
11089
+ { timeout: 3e4, maxBuffer: 8 * 1024 * 1024, env: scriptEnv2 }
11090
+ );
11091
+ const resolved = JSON.parse(stdout);
11092
+ if (!isRecord(resolved) || !Array.isArray(resolved.modules))
11093
+ throw new Error("invalid Expo Android resolution");
11094
+ const result = { modules: [], sources: [] };
11095
+ for (const entry of resolved.modules) {
11096
+ if (!isRecord(entry) || !Array.isArray(entry.projects))
11097
+ throw new Error("invalid Expo Android module entry");
11098
+ for (const project of entry.projects) {
11099
+ if (!isRecord(project) || typeof project.sourceDir !== "string" || !Array.isArray(project.modules))
11100
+ throw new Error("invalid Expo Android project");
11101
+ const sourceDir = import_node_fs17.default.realpathSync(project.sourceDir);
11102
+ if (sourceDir.split(import_node_path18.default.sep).includes("node_modules")) continue;
11103
+ if (project.modules.length === 0) continue;
11104
+ for (const module2 of project.modules) {
11105
+ if (!isRecord(module2) || typeof module2.classifier !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+$/.test(
11106
+ module2.classifier
11107
+ ) || module2.name !== null && typeof module2.name !== "string")
11108
+ throw new Error(`${sourceDir}: invalid Expo Android module descriptor`);
11109
+ result.modules.push({ classifier: module2.classifier, name: module2.name });
11110
+ }
11111
+ const walk2 = (dir) => {
11112
+ if (!import_node_fs17.default.existsSync(dir)) return;
11113
+ for (const child of import_node_fs17.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
11114
+ const file = import_node_path18.default.join(dir, child.name);
11115
+ if (child.isSymbolicLink())
11116
+ throw new Error(`Expo Kotlin source is a symlink: ${file}`);
11117
+ if (child.isDirectory()) walk2(file);
11118
+ else if (child.name.endsWith(".java"))
11119
+ throw new Error(`${file}: Java sources in Expo modules are not supported yet`);
11120
+ else if (child.name.endsWith(".kt")) result.sources.push(file);
11121
+ }
11122
+ };
11123
+ walk2(import_node_path18.default.join(sourceDir, "src", "main", "java"));
11124
+ walk2(import_node_path18.default.join(sourceDir, "src", "main", "kotlin"));
11125
+ }
11126
+ }
11127
+ return result.modules.length === 0 ? null : result;
11128
+ }
11129
+ async function compileExpoKotlinModules(root, origin) {
11130
+ const found = await collectExpoKotlinModules(root);
11131
+ if (!found) return null;
11132
+ const namespaces = found.modules.map(
11133
+ (module2) => module2.name ?? module2.classifier.split(".").pop()
11134
+ );
11135
+ const { expoKotlinArtifact: expoKotlinArtifact2 } = await Promise.resolve().then(() => (init_kotlin_host(), kotlin_host_exports));
11136
+ try {
11137
+ const artifact = await expoKotlinArtifact2(root, MODULE_ROOT, origin, found);
11138
+ return { namespaces, module: artifact, failures: [] };
11139
+ } catch (error) {
11140
+ const message = error instanceof Error ? error.message : String(error);
11141
+ const errors = message.match(/^.+\.kt:\d+:\d+: error:.*$/gm);
11142
+ if (!errors) throw error;
11143
+ return {
11144
+ namespaces,
11145
+ module: null,
11146
+ failures: namespaces.map((namespace) => ({
11147
+ namespace,
11148
+ diagnostic: errors.join("\n")
11149
+ }))
11150
+ };
11151
+ }
11152
+ }
11153
+ var import_node_child_process11, import_node_fs17, import_node_module3, import_node_path18, import_node_util5, run5, scriptEnv2, MODULE_ROOT;
11154
+ var init_expo_kotlin_host = __esm({
11155
+ "src/expo-kotlin-host.ts"() {
11156
+ "use strict";
11157
+ import_node_child_process11 = require("node:child_process");
11158
+ import_node_fs17 = __toESM(require("node:fs"), 1);
11159
+ import_node_module3 = require("node:module");
11160
+ import_node_path18 = __toESM(require("node:path"), 1);
11161
+ import_node_util5 = require("node:util");
11162
+ init_internal_child();
11163
+ init_backend_origin();
11164
+ init_is_record();
11165
+ run5 = (0, import_node_util5.promisify)(import_node_child_process11.execFile);
11166
+ scriptEnv2 = { ...process.env, [RNX_FORKED_SCRIPT_ENV]: "1" };
11167
+ MODULE_ROOT = import_node_path18.default.resolve(__sootsim_import_meta_dirname, "../kotlin");
11168
+ }
11169
+ });
11170
+
11171
+ // src/expo-kotlin-bundle.ts
11172
+ var expo_kotlin_bundle_exports = {};
11173
+ __export(expo_kotlin_bundle_exports, {
11174
+ buildExpoKotlinBundle: () => buildExpoKotlinBundle
11175
+ });
11176
+ async function buildExpoKotlinBundle(appRoot) {
11177
+ const metadata = await compileExpoKotlinModules(appRoot, "");
11178
+ if (!metadata) return null;
11179
+ const { module: module2 } = metadata;
11180
+ if (!module2) return { metadata, assets: [] };
11181
+ const { kotlinArtifactFiles: kotlinArtifactFiles2 } = await Promise.resolve().then(() => (init_kotlin_host(), kotlin_host_exports));
11182
+ const dir = `__peach/kotlin/${module2.hash}`;
11183
+ return {
11184
+ metadata: { ...metadata, module: { ...module2, url: `${dir}/app.mjs` } },
11185
+ assets: kotlinArtifactFiles2(module2.hash).map((file) => ({
11186
+ path: `${dir}/${file.name}`,
11187
+ bytes: file.bytes,
11188
+ contentType: file.contentType
11189
+ }))
11190
+ };
11191
+ }
11192
+ var init_expo_kotlin_bundle = __esm({
11193
+ "src/expo-kotlin-bundle.ts"() {
11194
+ "use strict";
11195
+ init_expo_kotlin_host();
11196
+ }
11197
+ });
11198
+
11199
+ // src/bundle-channel.mjs
11200
+ var bundle_channel_exports = {};
11201
+ __export(bundle_channel_exports, {
11202
+ prepareBundleChannel: () => prepareBundleChannel,
11203
+ publishBundleChannel: () => publishBundleChannel,
11204
+ snapshotDevServerBundles: () => snapshotDevServerBundles,
11205
+ snapshotOneDevBundle: () => snapshotOneDevBundle
11206
+ });
11207
+ module.exports = __toCommonJS(bundle_channel_exports);
11208
+ var import_node_crypto10 = require("node:crypto");
11209
+ var import_node_vm = require("node:vm");
11210
+
11211
+ // ../contrast/src/runtime/process-group.ts
11212
+ var import_node_child_process = require("node:child_process");
11213
+
11214
+ // ../contrast/src/sleep.ts
11215
+ function sleep(ms) {
11216
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
11217
+ }
11218
+
11219
+ // ../contrast/src/runtime/process-group.ts
11220
+ var GRACEFUL_TIMEOUT_MS = 3e3;
11221
+ var FORCED_EXIT_TIMEOUT_MS = 1e3;
11222
+ function signalProcessGroup(child, signal) {
11223
+ if (!child.pid) return;
11224
+ try {
11225
+ process.kill(-child.pid, signal);
11226
+ } catch {
11227
+ try {
11228
+ process.kill(child.pid, signal);
11229
+ } catch {
11230
+ }
11231
+ }
11232
+ }
11233
+ function runningPids(pids) {
11234
+ return pids.filter((pid) => {
11235
+ try {
11236
+ process.kill(pid, 0);
11237
+ return true;
11238
+ } catch (error) {
11239
+ return error instanceof Error && "code" in error && error.code === "EPERM";
11240
+ }
11241
+ });
11242
+ }
11243
+ function groupIsEmpty(pgid) {
11244
+ try {
11245
+ process.kill(-pgid, 0);
11246
+ } catch (error) {
11247
+ if (!(error instanceof Error && "code" in error && error.code === "EPERM"))
11248
+ return true;
11249
+ }
11250
+ const result = (0, import_node_child_process.spawnSync)("ps", ["-A", "-o", "pgid=,stat="], {
11251
+ encoding: "utf8",
11252
+ stdio: ["ignore", "pipe", "pipe"]
11253
+ });
11254
+ if (result.error || result.status !== 0) return false;
11255
+ return !result.stdout.split("\n").some((line) => {
11256
+ const [group, state] = line.trim().split(/\s+/);
11257
+ return Number(group) === pgid && state !== void 0 && !state.startsWith("Z");
11258
+ });
11259
+ }
11260
+ async function waitForExit(pids, pgid, timeoutMs) {
11261
+ const deadline = Date.now() + timeoutMs;
11262
+ const gone = () => runningPids(pids).length === 0 && groupIsEmpty(pgid);
11263
+ while (Date.now() < deadline) {
11264
+ if (gone()) return true;
11265
+ await sleep(50);
11266
+ }
11267
+ return gone();
11268
+ }
11269
+ async function terminateProcessGroup(child, signal = "SIGTERM", gracefulTimeoutMs = GRACEFUL_TIMEOUT_MS) {
11270
+ if (!child.pid) {
11271
+ try {
11272
+ child.kill(signal);
11273
+ } catch {
11274
+ }
11275
+ return;
11276
+ }
11277
+ signalProcessGroup(child, "SIGSTOP");
11278
+ const descendants = /* @__PURE__ */ new Set();
11279
+ let parents = [child.pid];
11280
+ let inspectionError = null;
11281
+ while (parents.length > 0) {
11282
+ const result = (0, import_node_child_process.spawnSync)("pgrep", ["-P", parents.join(",")], {
11283
+ encoding: "utf8",
11284
+ stdio: ["ignore", "pipe", "pipe"]
11285
+ });
11286
+ if (result.error || result.status !== 0 && result.status !== 1) {
11287
+ inspectionError = new Error(
11288
+ `could not inspect descendants of processes ${parents.join(",")}: ${result.error?.message || result.stderr.trim() || `pgrep exited ${result.status}`}`
11289
+ );
11290
+ break;
11291
+ }
11292
+ const next = [];
11293
+ for (const line of result.stdout.split("\n")) {
11294
+ const descendantPid = Number(line.trim());
11295
+ if (Number.isInteger(descendantPid) && descendantPid > 1 && !descendants.has(descendantPid)) {
11296
+ descendants.add(descendantPid);
11297
+ next.push(descendantPid);
11298
+ try {
11299
+ process.kill(descendantPid, "SIGSTOP");
11300
+ } catch {
11301
+ }
11302
+ }
11303
+ }
11304
+ parents = next;
11305
+ }
11306
+ const pids = [child.pid, ...descendants];
11307
+ const gracefulDeadline = Date.now() + gracefulTimeoutMs;
9077
11308
  try {
9078
11309
  process.kill(child.pid, signal);
9079
11310
  } catch {
@@ -9130,65 +11361,124 @@ async function terminateProcessGroup(child, signal = "SIGTERM", gracefulTimeoutM
9130
11361
  if (inspectionError) throw inspectionError;
9131
11362
  }
9132
11363
 
9133
- // src/expoSwiftContract.ts
11364
+ // src/bundle-channel.mjs
11365
+ init_backend_origin();
11366
+
11367
+ // src/bundleGlobalFooter.ts
11368
+ function readBundleGlobalFooter(source, name2) {
11369
+ const prefix = `globalThis.${name2}=`;
11370
+ const start = source.lastIndexOf(`
11371
+ ${prefix}`);
11372
+ if (start < 0) return null;
11373
+ const end = source.indexOf("\n", start + 1);
11374
+ const line = source.slice(start + 1, end < 0 ? void 0 : end);
11375
+ if (!line.endsWith(";")) throw new Error(`${name2} footer is truncated`);
11376
+ const value = JSON.parse(line.slice(prefix.length, -1));
11377
+ if (value === null) throw new Error(`${name2} footer has no metadata`);
11378
+ return value;
11379
+ }
11380
+ function insertBundleGlobalFooter(source, name2, value) {
11381
+ const prefix = `globalThis.${name2}=`;
11382
+ const start = source.lastIndexOf(`
11383
+ ${prefix}`);
11384
+ if (start >= 0) {
11385
+ readBundleGlobalFooter(source, name2);
11386
+ const end = source.indexOf("\n", start + 1);
11387
+ source = source.slice(0, start) + (end < 0 ? "" : source.slice(end));
11388
+ }
11389
+ const json = JSON.stringify(value).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
11390
+ const footer = `
11391
+ ${prefix}${json};`;
11392
+ const identity = source.lastIndexOf("\nglobalThis.__sootsimModulePaths=");
11393
+ return identity < 0 ? source + footer : source.slice(0, identity) + footer + source.slice(identity);
11394
+ }
11395
+
11396
+ // src/expoKotlinContract.ts
9134
11397
  function record(value) {
9135
11398
  return value !== null && typeof value === "object" && !Array.isArray(value);
9136
11399
  }
11400
+ function readExpoKotlinMetadata(value) {
11401
+ const invalid = () => new Error("invalid Expo Kotlin compilation metadata");
11402
+ if (!record(value) || !Array.isArray(value.namespaces) || !Array.isArray(value.failures))
11403
+ throw invalid();
11404
+ const namespaces = [];
11405
+ for (const namespace of value.namespaces) {
11406
+ if (typeof namespace !== "string") throw invalid();
11407
+ namespaces.push(namespace);
11408
+ }
11409
+ const failures = [];
11410
+ for (const failure2 of value.failures) {
11411
+ if (!record(failure2) || typeof failure2.namespace !== "string" || typeof failure2.diagnostic !== "string")
11412
+ throw invalid();
11413
+ failures.push({ namespace: failure2.namespace, diagnostic: failure2.diagnostic });
11414
+ }
11415
+ const module2 = value.module;
11416
+ if (module2 === null) return { namespaces, module: null, failures };
11417
+ if (!record(module2) || typeof module2.url !== "string" || typeof module2.hash !== "string" || typeof module2.compose !== "boolean")
11418
+ throw invalid();
11419
+ return {
11420
+ namespaces,
11421
+ module: { url: module2.url, hash: module2.hash, compose: module2.compose },
11422
+ failures
11423
+ };
11424
+ }
11425
+ function insertExpoKotlinFooter(source, metadata) {
11426
+ return insertBundleGlobalFooter(
11427
+ source,
11428
+ "__peachExpoKotlin",
11429
+ readExpoKotlinMetadata(metadata)
11430
+ );
11431
+ }
11432
+
11433
+ // src/expoSwiftContract.ts
11434
+ function record2(value) {
11435
+ return value !== null && typeof value === "object" && !Array.isArray(value);
11436
+ }
9137
11437
  function strings(value) {
9138
11438
  return Array.isArray(value) && value.every((item) => typeof item === "string");
9139
11439
  }
9140
11440
  function isExpoSwiftCompilationResult(value) {
9141
- if (!record(value) || !strings(value.namespaces) || !Array.isArray(value.modules) || value.modules.length > 1 || !Array.isArray(value.failures))
11441
+ if (!record2(value) || !strings(value.namespaces) || !Array.isArray(value.modules) || value.modules.length > 1 || !Array.isArray(value.failures))
9142
11442
  return false;
9143
11443
  const remaining = new Set(value.namespaces);
9144
11444
  if (remaining.size !== value.namespaces.length) return false;
9145
11445
  for (const artifact of value.modules) {
9146
- if (!record(artifact) || !strings(artifact.namespaces) || artifact.namespaces.length === 0 || !artifact.namespaces.every((namespace) => remaining.delete(namespace)) || typeof artifact.url !== "string" || typeof artifact.hash !== "string" || typeof artifact.icuUrl !== "string" || typeof artifact.rootId !== "string")
11446
+ if (!record2(artifact) || !strings(artifact.namespaces) || artifact.namespaces.length === 0 || !artifact.namespaces.every((namespace) => remaining.delete(namespace)) || typeof artifact.url !== "string" || typeof artifact.hash !== "string" || typeof artifact.icuUrl !== "string" || typeof artifact.rootId !== "string")
9147
11447
  return false;
9148
11448
  }
9149
11449
  for (const failure2 of value.failures) {
9150
- if (!record(failure2) || typeof failure2.namespace !== "string" || !remaining.delete(failure2.namespace) || typeof failure2.diagnostic !== "string")
11450
+ if (!record2(failure2) || typeof failure2.namespace !== "string" || !remaining.delete(failure2.namespace) || typeof failure2.diagnostic !== "string")
9151
11451
  return false;
9152
11452
  }
9153
11453
  return remaining.size === 0;
9154
11454
  }
9155
- var FOOTER_PREFIX = "globalThis.__peachExpoSwift=";
9156
11455
  function readExpoSwiftMetadata(value) {
9157
11456
  if (value === null || value === void 0) return null;
9158
11457
  if (!isExpoSwiftCompilationResult(value))
9159
11458
  throw new Error("invalid Expo Swift compilation metadata");
9160
11459
  return value;
9161
11460
  }
9162
- function readExpoSwiftFooter(source) {
9163
- const start = source.lastIndexOf(`
9164
- ${FOOTER_PREFIX}`);
9165
- if (start < 0) return null;
9166
- const end = source.indexOf("\n", start + 1);
9167
- const line = source.slice(start + 1, end < 0 ? void 0 : end);
9168
- if (!line.endsWith(";")) throw new Error("Expo Swift footer is truncated");
9169
- const metadata = readExpoSwiftMetadata(JSON.parse(line.slice(FOOTER_PREFIX.length, -1)));
9170
- if (!metadata) throw new Error("Expo Swift footer has no metadata");
9171
- return metadata;
9172
- }
9173
11461
  function insertExpoSwiftFooter(source, metadata) {
9174
11462
  readExpoSwiftMetadata(metadata);
9175
- const start = source.lastIndexOf(`
9176
- ${FOOTER_PREFIX}`);
9177
- if (start >= 0) {
9178
- readExpoSwiftFooter(source);
9179
- const end = source.indexOf("\n", start + 1);
9180
- source = source.slice(0, start) + (end < 0 ? "" : source.slice(end));
9181
- }
9182
- const json = JSON.stringify(metadata).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
9183
- const footer = `
9184
- ${FOOTER_PREFIX}${json};`;
9185
- const identity = source.lastIndexOf("\nglobalThis.__sootsimModulePaths=");
9186
- return identity < 0 ? source + footer : source.slice(0, identity) + footer + source.slice(identity);
11463
+ return insertBundleGlobalFooter(source, "__peachExpoSwift", metadata);
9187
11464
  }
9188
11465
 
9189
11466
  // src/bundle-channel.mjs
9190
- var digest = (bytes) => (0, import_node_crypto9.createHash)("sha256").update(bytes).digest("hex");
11467
+ var digest = (bytes) => (0, import_node_crypto10.createHash)("sha256").update(bytes).digest("hex");
9191
11468
  var segment = /^[a-zA-Z0-9._-]+$/;
11469
+ function packagedAssetDirectory(location) {
11470
+ const parsed = new URL(location, "https://packaged.invalid");
11471
+ if (parsed.origin !== "https://packaged.invalid")
11472
+ throw new Error(`non-packaged asset location: ${location}`);
11473
+ const outside = parsed.searchParams.get("unstable_path");
11474
+ if (outside === null) return parsed.pathname.replace(/^\/+/, "");
11475
+ return `assets/${outside.replace(/^\.\//, "").replace(/\.\.\//g, "_")}`;
11476
+ }
11477
+ var channelKey = (platform) => platform === "ios" ? "peach/channels/prod" : `peach/channels/${platform}/prod`;
11478
+ var nativeAssetPath = {
11479
+ ios: /^__peach\/(swift|swift-icu)\/[a-f0-9]{64}(\.wasm)?$/,
11480
+ android: /^__peach\/kotlin\/[a-f0-9]+\/[A-Za-z0-9._-]+\.(mjs|wasm)$/
11481
+ };
9192
11482
  function prepareBundleChannel({
9193
11483
  prefix,
9194
11484
  publicOrigin,
@@ -9196,15 +11486,16 @@ function prepareBundleChannel({
9196
11486
  runtimeVersion,
9197
11487
  name: name2,
9198
11488
  icon,
9199
- bundle,
9200
- assets: assets2,
9201
- expoSwift = null
11489
+ platforms
9202
11490
  }) {
9203
11491
  if (!/^[a-f0-9]{40}$/.test(sourceSha) || !/^\d+$/.test(runtimeVersion)) {
9204
11492
  throw new Error("invalid bundle channel source identity");
9205
11493
  }
9206
11494
  const origin = new URL(publicOrigin);
9207
- if (origin.protocol !== "https:" || origin.pathname !== "/" || origin.search || origin.hash || origin.username || origin.password) {
11495
+ if (
11496
+ // a loopback origin serves local release proofs
11497
+ !(origin.protocol === "https:" || origin.protocol === "http:" && isLoopbackHost(origin.hostname)) || origin.pathname !== "/" || origin.search || origin.hash || origin.username || origin.password
11498
+ ) {
9208
11499
  throw new Error("bundle channels require a public HTTPS origin");
9209
11500
  }
9210
11501
  if (!prefix.split("/").every((s) => segment.test(s) && s !== "." && s !== "..")) {
@@ -9212,6 +11503,39 @@ function prepareBundleChannel({
9212
11503
  }
9213
11504
  if (!icon.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex")))
9214
11505
  throw new Error("bundle channel icon must be PNG");
11506
+ if (!platforms.length) throw new Error("bundle channel has no platforms");
11507
+ if (new Set(platforms.map((p) => p.platform)).size !== platforms.length)
11508
+ throw new Error("bundle channel repeats a platform");
11509
+ const channels = platforms.map(
11510
+ (snapshot) => prepareChannel({
11511
+ prefix,
11512
+ origin,
11513
+ sourceSha,
11514
+ runtimeVersion,
11515
+ name: name2,
11516
+ icon,
11517
+ ...snapshot
11518
+ })
11519
+ );
11520
+ return { prefix, origin: origin.origin, channels };
11521
+ }
11522
+ function prepareChannel({
11523
+ prefix,
11524
+ origin,
11525
+ sourceSha,
11526
+ runtimeVersion,
11527
+ name: name2,
11528
+ icon,
11529
+ platform,
11530
+ bundle,
11531
+ assets: assets2,
11532
+ expoSwift = null,
11533
+ expoKotlin = null
11534
+ }) {
11535
+ if (!(platform in nativeAssetPath)) throw new Error(`unknown platform: ${platform}`);
11536
+ const native = platform === "ios" ? expoSwift : expoKotlin;
11537
+ if ((platform === "ios" ? expoKotlin : expoSwift) !== null)
11538
+ throw new Error(`${platform} bundle carries another platform's native modules`);
9215
11539
  const files2 = /* @__PURE__ */ new Map();
9216
11540
  for (const asset of assets2) {
9217
11541
  if (!asset.path.startsWith("assets/") || !asset.path.split("/").every((s) => s && s !== "." && s !== ".." && !/[\\?#%]/.test(s))) {
@@ -9220,23 +11544,23 @@ function prepareBundleChannel({
9220
11544
  if (files2.has(asset.path)) throw new Error(`duplicate packaged asset: ${asset.path}`);
9221
11545
  files2.set(asset.path, asset.bytes);
9222
11546
  }
9223
- for (const asset of expoSwift?.assets ?? []) {
9224
- if (!/^__peach\/(swift|swift-icu)\/[a-f0-9]{64}(\.wasm)?$/.test(asset.path))
9225
- throw new Error(`invalid Swift artifact path: ${asset.path}`);
11547
+ for (const asset of native?.assets ?? []) {
11548
+ if (!nativeAssetPath[platform].test(asset.path))
11549
+ throw new Error(`invalid native artifact path: ${asset.path}`);
9226
11550
  if (files2.has(asset.path)) throw new Error(`duplicate packaged asset: ${asset.path}`);
9227
11551
  files2.set(asset.path, asset.bytes);
9228
11552
  }
9229
- const identity = (0, import_node_crypto9.createHash)("sha256").update(bundle).update(icon);
9230
- for (const [path16, bytes] of [...files2].sort(
11553
+ const identity = (0, import_node_crypto10.createHash)("sha256").update(bundle).update(icon);
11554
+ for (const [path18, bytes] of [...files2].sort(
9231
11555
  ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
9232
11556
  )) {
9233
- identity.update(JSON.stringify([path16, digest(bytes)]));
11557
+ identity.update(JSON.stringify([path18, digest(bytes)]));
9234
11558
  }
9235
- if (expoSwift) identity.update(JSON.stringify(expoSwift.metadata));
11559
+ if (native) identity.update(JSON.stringify(native.metadata));
9236
11560
  const releaseKey = `${prefix}/peach/releases/${identity.digest("hex")}`;
9237
11561
  const assetBaseUrl = `${origin.origin}/${releaseKey}/`;
9238
11562
  const directories = new Set(
9239
- [...files2.keys()].map((path16) => path16.slice(0, path16.lastIndexOf("/")))
11563
+ [...files2.keys()].map((path18) => path18.slice(0, path18.lastIndexOf("/")))
9240
11564
  );
9241
11565
  let locations = 0;
9242
11566
  let source = new TextDecoder("utf-8", { fatal: true }).decode(bundle).replace(
@@ -9244,36 +11568,46 @@ function prepareBundleChannel({
9244
11568
  /("?httpServerLocation"?\s*:\s*)(?:"((?:[^"\\]|\\.)*)"|`([^`\\$]*)`)/g,
9245
11569
  (match, property, quoted, template) => {
9246
11570
  const location = quoted === void 0 ? template : JSON.parse(`"${quoted}"`);
9247
- const parsed = new URL(location, "https://packaged.invalid");
9248
- if (parsed.origin !== "https://packaged.invalid")
9249
- throw new Error(`non-packaged asset location: ${location}`);
9250
- const path16 = (parsed.searchParams.get("unstable_path") ?? parsed.pathname).replace(/^\/+/, "").replace(/\.\.\//g, "_");
9251
- if (!directories.has(path16)) {
11571
+ const path18 = packagedAssetDirectory(location);
11572
+ if (!directories.has(path18)) {
9252
11573
  throw new Error(`bundle asset location has no packaged files: ${location}`);
9253
11574
  }
9254
11575
  locations++;
9255
- return `${property}${JSON.stringify(new URL(path16, assetBaseUrl).href)}`;
11576
+ return `${property}${JSON.stringify(new URL(path18, assetBaseUrl).href)}`;
9256
11577
  }
9257
11578
  );
9258
11579
  if (assets2.length && !locations)
9259
11580
  throw new Error("bundle has packaged files but no asset metadata");
11581
+ const release = (url) => new URL(url.replace(/^\//, ""), assetBaseUrl).href;
9260
11582
  let nativeMetadata = null;
9261
11583
  if (expoSwift) {
9262
11584
  nativeMetadata = {
9263
- ...expoSwift.metadata,
9264
- modules: expoSwift.metadata.modules.map((artifact) => ({
9265
- ...artifact,
9266
- url: new URL(artifact.url.replace(/^\//, ""), assetBaseUrl).href,
9267
- icuUrl: artifact.icuUrl.startsWith("/") ? new URL(artifact.icuUrl.slice(1), assetBaseUrl).href : artifact.icuUrl
9268
- }))
11585
+ expoSwift: {
11586
+ ...expoSwift.metadata,
11587
+ modules: expoSwift.metadata.modules.map((artifact) => ({
11588
+ ...artifact,
11589
+ url: release(artifact.url),
11590
+ icuUrl: artifact.icuUrl.startsWith("/") ? release(artifact.icuUrl) : artifact.icuUrl
11591
+ }))
11592
+ }
11593
+ };
11594
+ source = insertExpoSwiftFooter(source, nativeMetadata.expoSwift);
11595
+ }
11596
+ if (expoKotlin) {
11597
+ const { module: module2 } = expoKotlin.metadata;
11598
+ nativeMetadata = {
11599
+ expoKotlin: {
11600
+ ...expoKotlin.metadata,
11601
+ module: module2 && { ...module2, url: release(module2.url) }
11602
+ }
9269
11603
  };
9270
- source = insertExpoSwiftFooter(source, nativeMetadata);
11604
+ source = insertExpoKotlinFooter(source, nativeMetadata.expoKotlin);
9271
11605
  }
9272
11606
  new import_node_vm.Script(source, { filename: "peach-release-bundle.js" });
9273
11607
  const bundleBytes = Buffer.from(source);
9274
11608
  const metadata = {
9275
11609
  schemaVersion: 1,
9276
- ...nativeMetadata ? { expoSwift: nativeMetadata } : {},
11610
+ ...nativeMetadata,
9277
11611
  channel: "prod",
9278
11612
  name: name2,
9279
11613
  iconUrl: `${assetBaseUrl}icon.png`,
@@ -9281,7 +11615,7 @@ function prepareBundleChannel({
9281
11615
  assetBaseUrl,
9282
11616
  sourceSha,
9283
11617
  runtimeVersion,
9284
- platform: "ios",
11618
+ platform,
9285
11619
  bundleHash: digest(bundleBytes)
9286
11620
  };
9287
11621
  const objects = [
@@ -9291,10 +11625,10 @@ function prepareBundleChannel({
9291
11625
  contentType: "application/javascript"
9292
11626
  },
9293
11627
  { key: `${releaseKey}/icon.png`, bytes: icon, contentType: "image/png" },
9294
- ...[...files2].map(([path16, bytes]) => ({
9295
- key: `${releaseKey}/${path16}`,
11628
+ ...[...files2].map(([path18, bytes]) => ({
11629
+ key: `${releaseKey}/${path18}`,
9296
11630
  bytes,
9297
- contentType: expoSwift?.assets.find((asset) => asset.path === path16)?.contentType ?? {
11631
+ contentType: native?.assets.find((asset) => asset.path === path18)?.contentType ?? {
9298
11632
  png: "image/png",
9299
11633
  jpg: "image/jpeg",
9300
11634
  jpeg: "image/jpeg",
@@ -9303,51 +11637,104 @@ function prepareBundleChannel({
9303
11637
  ttf: "font/ttf",
9304
11638
  otf: "font/otf",
9305
11639
  wav: "audio/wav"
9306
- }[path16.split(".").pop()] ?? "application/octet-stream"
11640
+ }[path18.split(".").pop()] ?? "application/octet-stream"
9307
11641
  }))
9308
11642
  ];
9309
- return { prefix, origin: origin.origin, metadata, objects };
11643
+ return { platform, key: `${prefix}/${channelKey(platform)}`, metadata, objects };
9310
11644
  }
9311
11645
  async function publishBundleChannel(release, { put, fetchObject = fetch }) {
9312
11646
  async function uploadVerified(object, cacheControl) {
9313
11647
  await put(object.key, object.bytes, object.contentType, cacheControl);
9314
- const response2 = await fetchObject(`${release.origin}/${object.key}`);
9315
- if (!response2.ok)
9316
- throw new Error(`bundle channel download failed: ${response2.status} ${object.key}`);
9317
- const bytes = Buffer.from(await response2.arrayBuffer());
11648
+ const response = await fetchObject(`${release.origin}/${object.key}`);
11649
+ if (!response.ok)
11650
+ throw new Error(`bundle channel download failed: ${response.status} ${object.key}`);
11651
+ const bytes = Buffer.from(await response.arrayBuffer());
9318
11652
  if (digest(bytes) !== digest(object.bytes))
9319
11653
  throw new Error(`bundle channel download mismatch: ${object.key}`);
9320
11654
  }
9321
- for (const object of release.objects) await uploadVerified(object);
9322
- const bundle = release.objects[0];
9323
- await uploadVerified(
9324
- { ...bundle, key: `${release.prefix}/peach/channels/prod.js` },
9325
- "no-store"
9326
- );
9327
- await put(
9328
- `${release.prefix}/peach/channels/prod.json`,
9329
- Buffer.from(JSON.stringify(release.metadata)),
9330
- "application/json",
9331
- "no-store"
9332
- );
9333
- const response = await fetchObject(
9334
- `${release.origin}/${release.prefix}/peach/channels/prod.json`,
9335
- { cache: "no-store" }
9336
- );
9337
- if (!response.ok || JSON.stringify(await response.json()) !== JSON.stringify(release.metadata)) {
9338
- throw new Error("published bundle channel metadata did not match");
11655
+ for (const channel of release.channels)
11656
+ for (const object of channel.objects) await uploadVerified(object);
11657
+ for (const channel of release.channels) {
11658
+ await uploadVerified({ ...channel.objects[0], key: `${channel.key}.js` }, "no-store");
11659
+ await put(
11660
+ `${channel.key}.json`,
11661
+ Buffer.from(JSON.stringify(channel.metadata)),
11662
+ "application/json",
11663
+ "no-store"
11664
+ );
11665
+ const response = await fetchObject(`${release.origin}/${channel.key}.json`, {
11666
+ cache: "no-store"
11667
+ });
11668
+ if (!response.ok || JSON.stringify(await response.json()) !== JSON.stringify(channel.metadata)) {
11669
+ throw new Error(
11670
+ `published ${channel.platform} bundle channel metadata did not match`
11671
+ );
11672
+ }
11673
+ console.log(
11674
+ `[ota] Peach ${channel.platform} prod channel: ${release.origin}/${channel.key}.json`
11675
+ );
9339
11676
  }
9340
- console.log(
9341
- `[ota] Peach prod channel: ${release.origin}/${release.prefix}/peach/channels/prod.json`
9342
- );
11677
+ }
11678
+ async function snapshotDevServerBundles(origin, projectRoot, { platforms = ["ios", "android"], assertRunning = () => {
11679
+ } } = {}) {
11680
+ const { readFileSync: readFileSync5 } = await import("node:fs");
11681
+ const { basename: basename2 } = await import("node:path");
11682
+ const snapshots = [];
11683
+ for (const platform of platforms) {
11684
+ const url = `${origin}/index.bundle?platform=${platform}&dev=true&minify=false`;
11685
+ const deadline = Date.now() + 10 * 6e4;
11686
+ let bundle;
11687
+ while (!bundle) {
11688
+ assertRunning();
11689
+ if (Date.now() > deadline)
11690
+ throw new Error(`dev server ${platform} bundle timed out`);
11691
+ const response = await fetch(url).catch(() => null);
11692
+ if (response?.ok) bundle = Buffer.from(await response.arrayBuffer());
11693
+ else await new Promise((resolve2) => setTimeout(resolve2, 2e3));
11694
+ }
11695
+ const assets2 = /* @__PURE__ */ new Map();
11696
+ for (const [registration] of bundle.toString("utf8").matchAll(/(?<=registerAsset\()\{[^{}]*"__packager_asset": true[^{}]*\}/g)) {
11697
+ const asset = JSON.parse(registration);
11698
+ const directory = packagedAssetDirectory(asset.httpServerLocation);
11699
+ if (asset.files) {
11700
+ for (const file of asset.files) {
11701
+ const path18 = `${directory}/${basename2(file)}`;
11702
+ if (!assets2.has(path18)) assets2.set(path18, readFileSync5(file));
11703
+ }
11704
+ continue;
11705
+ }
11706
+ for (const scale of asset.scales) {
11707
+ const file = `${asset.name}${scale === 1 ? "" : `@${scale}x`}.${asset.type}`;
11708
+ const path18 = `${directory}/${file}`;
11709
+ if (assets2.has(path18)) continue;
11710
+ const response = await fetch(
11711
+ `${origin}${asset.httpServerLocation}/${file}?platform=${platform}&hash=${asset.hash}`
11712
+ );
11713
+ if (!response.ok)
11714
+ throw new Error(`dev server asset ${path18} failed: ${response.status}`);
11715
+ assets2.set(path18, Buffer.from(await response.arrayBuffer()));
11716
+ }
11717
+ }
11718
+ snapshots.push({
11719
+ platform,
11720
+ bundle,
11721
+ assets: [...assets2].map(([path18, bytes]) => ({ path: path18, bytes })),
11722
+ ...platform === "ios" ? {
11723
+ expoSwift: await (await Promise.resolve().then(() => (init_expo_swift_bundle(), expo_swift_bundle_exports))).buildExpoSwiftBundle(projectRoot)
11724
+ } : {
11725
+ expoKotlin: await (await Promise.resolve().then(() => (init_expo_kotlin_bundle(), expo_kotlin_bundle_exports))).buildExpoKotlinBundle(projectRoot)
11726
+ }
11727
+ });
11728
+ }
11729
+ return { platforms: snapshots };
9343
11730
  }
9344
11731
  async function snapshotOneDevBundle(projectRoot, env = {}) {
9345
11732
  const { spawn: spawn2 } = await import("node:child_process");
9346
11733
  const { readFileSync: readFileSync5 } = await import("node:fs");
9347
11734
  const { createServer } = await import("node:net");
9348
- const { basename: basename2, dirname: dirname2, join: join2 } = await import("node:path");
9349
- const { createRequire: createRequire2 } = await import("node:module");
9350
- const onePackage = createRequire2(join2(projectRoot, "package.json")).resolve(
11735
+ const { dirname: dirname2, join: join2 } = await import("node:path");
11736
+ const { createRequire: createRequire4 } = await import("node:module");
11737
+ const onePackage = createRequire4(join2(projectRoot, "package.json")).resolve(
9351
11738
  "one/package.json"
9352
11739
  );
9353
11740
  const oneBin = join2(
@@ -9367,32 +11754,12 @@ async function snapshotOneDevBundle(projectRoot, env = {}) {
9367
11754
  detached: true
9368
11755
  });
9369
11756
  try {
9370
- const url = `http://127.0.0.1:${port}/index.bundle?platform=ios&dev=true&minify=false`;
9371
- const deadline = Date.now() + 10 * 6e4;
9372
- let bundle;
9373
- while (!bundle) {
9374
- if (child.exitCode !== null)
9375
- throw new Error(`one dev exited with ${child.exitCode}`);
9376
- if (Date.now() > deadline) throw new Error("one dev bundle timed out");
9377
- const response = await fetch(url).catch(() => null);
9378
- if (response?.ok) bundle = Buffer.from(await response.arrayBuffer());
9379
- else await new Promise((resolve2) => setTimeout(resolve2, 2e3));
9380
- }
9381
- const assets2 = /* @__PURE__ */ new Map();
9382
- for (const [registration] of bundle.toString("utf8").matchAll(/(?<=registerAsset\()\{[^{}]*"__packager_asset": true[^{}]*\}/g)) {
9383
- const asset = JSON.parse(registration);
9384
- for (const file of asset.files) {
9385
- const path16 = `${asset.httpServerLocation.replace(/^\/+/, "")}/${basename2(file)}`;
9386
- if (!assets2.has(path16)) assets2.set(path16, readFileSync5(file));
11757
+ return await snapshotDevServerBundles(`http://127.0.0.1:${port}`, projectRoot, {
11758
+ assertRunning: () => {
11759
+ if (child.exitCode !== null)
11760
+ throw new Error(`one dev exited with ${child.exitCode}`);
9387
11761
  }
9388
- }
9389
- const { buildExpoSwiftBundle: buildExpoSwiftBundle2 } = await Promise.resolve().then(() => (init_expo_swift_bundle(), expo_swift_bundle_exports));
9390
- const expoSwift = await buildExpoSwiftBundle2(projectRoot);
9391
- return {
9392
- bundle,
9393
- assets: [...assets2].map(([path16, bytes]) => ({ path: path16, bytes })),
9394
- expoSwift
9395
- };
11762
+ });
9396
11763
  } finally {
9397
11764
  await terminateProcessGroup(child);
9398
11765
  }
@@ -9401,5 +11768,6 @@ async function snapshotOneDevBundle(projectRoot, env = {}) {
9401
11768
  0 && (module.exports = {
9402
11769
  prepareBundleChannel,
9403
11770
  publishBundleChannel,
11771
+ snapshotDevServerBundles,
9404
11772
  snapshotOneDevBundle
9405
11773
  });