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
@@ -9,7 +9,9 @@ import { createHash } from 'node:crypto'
9
9
  import fs from 'node:fs'
10
10
  import { createRequire } from 'node:module'
11
11
  import path from 'node:path'
12
+ import { fileURLToPath } from 'node:url'
12
13
  import { promisify } from 'node:util'
14
+ import { parse } from 'acorn'
13
15
  import { rnxHomeDir } from './home-paths'
14
16
  import { isRecord } from './is-record'
15
17
  import {
@@ -18,6 +20,7 @@ import {
18
20
  type ViewModelClass,
19
21
  } from './kotlin-view-models'
20
22
  import type { DayNight, DrawableFile } from './android-drawables'
23
+ import type { ExpoKotlinModules } from './expo-kotlin-host'
21
24
  import type { KotlinAppProject, KotlinWindowTheme } from './kotlin-app'
22
25
  import type { ServerResponse } from 'node:http'
23
26
 
@@ -226,9 +229,9 @@ export function kotlinSources(
226
229
  // are a table here.
227
230
  function entrySource(ids: string[]): string {
228
231
  return [
232
+ 'import android.os.Build',
229
233
  'import kotlin.js.JsAny',
230
234
  'import kotlin.js.Promise',
231
- 'import kotlinx.coroutines.CoroutineStart',
232
235
  'import kotlinx.coroutines.Dispatchers',
233
236
  'import kotlinx.coroutines.GlobalScope',
234
237
  'import kotlinx.coroutines.promise',
@@ -242,10 +245,14 @@ function entrySource(ids: string[]): string {
242
245
  ' else -> null',
243
246
  '}?.also { instances[packageName] = it }',
244
247
  '',
248
+ '@OptIn(kotlin.js.ExperimentalJsExport::class)',
249
+ '@JsExport',
250
+ 'fun peachSetAndroidBuildInfo(model: String, release: String) = Build.setDevice(model, release)',
251
+ '',
245
252
  '@OptIn(kotlin.js.ExperimentalJsExport::class, kotlin.js.ExperimentalWasmJsInterop::class, kotlinx.coroutines.DelicateCoroutinesApi::class)',
246
253
  '@JsExport',
247
254
  'fun peachNativeCall(module: String, method: String, argsJson: String, contractHash: String): Promise<JsAny?> =',
248
- ' GlobalScope.promise(Dispatchers.Main, CoroutineStart.UNDISPATCHED) {',
255
+ ' GlobalScope.promise(Dispatchers.Main) {',
249
256
  ' val reply = JSONObject()',
250
257
  ' try {',
251
258
  ' val packageName = module.substringBefore(\'.\', "")',
@@ -268,7 +275,7 @@ function entrySource(ids: string[]): string {
268
275
  ].join('\n')
269
276
  }
270
277
 
271
- const BUILD_FORMAT = 8
278
+ const BUILD_FORMAT = 14
272
279
  const builds = new Map<string, Promise<KotlinArtifact>>()
273
280
 
274
281
  function buildsDir(): string {
@@ -304,7 +311,7 @@ export async function kotlinArtifact(
304
311
  }
305
312
  generated['PeachEntry.kt'] = entrySource(ids)
306
313
  if (viewIds.length > 0) generated['PeachViews.kt'] = viewsSource(viewIds)
307
- const shims = kotlinSources(path.join(moduleRoot, 'Sources')).filter(
314
+ const shims = kotlinSources(path.join(moduleRoot, 'Sources'), new Set()).filter(
308
315
  (file) => usesCompose || !file.endsWith('/OneNativeSourceViewDispatch.kt'),
309
316
  )
310
317
  if (viewIds.length > 0)
@@ -315,6 +322,379 @@ export async function kotlinArtifact(
315
322
  )
316
323
  }
317
324
 
325
+ interface ExpoField {
326
+ name: string
327
+ key: string
328
+ required: boolean
329
+ mutable: boolean
330
+ hidden: boolean
331
+ initializer: [number, number] | null
332
+ }
333
+
334
+ interface ExpoTypesFile {
335
+ file: string
336
+ package: string
337
+ records: { name: string; hidden: boolean; fields: ExpoField[] }[]
338
+ enums: {
339
+ name: string
340
+ hidden: boolean
341
+ fields: number
342
+ parameter: string | null
343
+ hiddenParameter: boolean
344
+ }[]
345
+ // Expo views; compose views also name their props class
346
+ views: { name: string; hidden: boolean; props: string }[]
347
+ // ComposeProps classes with their public member properties
348
+ props: { name: string; hidden: boolean; states: string[] }[]
349
+ }
350
+
351
+ function readExpoField(value: unknown): ExpoField {
352
+ if (
353
+ !isRecord(value) ||
354
+ typeof value.name !== 'string' ||
355
+ typeof value.key !== 'string' ||
356
+ typeof value.required !== 'boolean' ||
357
+ typeof value.mutable !== 'boolean' ||
358
+ typeof value.hidden !== 'boolean'
359
+ )
360
+ throw new Error('expo-types printed an invalid field')
361
+ const range = value.initializer
362
+ if (
363
+ range !== null &&
364
+ (!Array.isArray(range) ||
365
+ range.length !== 2 ||
366
+ typeof range[0] !== 'number' ||
367
+ typeof range[1] !== 'number')
368
+ )
369
+ throw new Error('expo-types printed an invalid initializer range')
370
+ return {
371
+ name: value.name,
372
+ key: value.key,
373
+ required: value.required,
374
+ mutable: value.mutable,
375
+ hidden: value.hidden,
376
+ initializer: range === null ? null : [range[0], range[1]],
377
+ }
378
+ }
379
+
380
+ function readExpoTypes(output: string): ExpoTypesFile[] {
381
+ return output
382
+ .split('\n')
383
+ .filter((line) => line.startsWith('{'))
384
+ .map((line) => {
385
+ const row: unknown = JSON.parse(line)
386
+ if (
387
+ !isRecord(row) ||
388
+ typeof row.file !== 'string' ||
389
+ typeof row.package !== 'string' ||
390
+ !Array.isArray(row.records) ||
391
+ !Array.isArray(row.enums) ||
392
+ !Array.isArray(row.views) ||
393
+ !Array.isArray(row.props)
394
+ )
395
+ throw new Error('expo-types printed an invalid file')
396
+ return {
397
+ file: row.file,
398
+ package: row.package,
399
+ records: row.records.map((record: unknown) => {
400
+ if (
401
+ !isRecord(record) ||
402
+ typeof record.name !== 'string' ||
403
+ typeof record.hidden !== 'boolean' ||
404
+ !Array.isArray(record.fields)
405
+ )
406
+ throw new Error('expo-types printed an invalid record')
407
+ return {
408
+ name: record.name,
409
+ hidden: record.hidden,
410
+ fields: record.fields.map(readExpoField),
411
+ }
412
+ }),
413
+ enums: row.enums.map((entry: unknown) => {
414
+ if (
415
+ !isRecord(entry) ||
416
+ typeof entry.name !== 'string' ||
417
+ typeof entry.hidden !== 'boolean' ||
418
+ typeof entry.fields !== 'number' ||
419
+ (entry.parameter !== null && typeof entry.parameter !== 'string') ||
420
+ typeof entry.hiddenParameter !== 'boolean'
421
+ )
422
+ throw new Error('expo-types printed an invalid enum')
423
+ return {
424
+ name: entry.name,
425
+ hidden: entry.hidden,
426
+ fields: entry.fields,
427
+ parameter: entry.parameter,
428
+ hiddenParameter: entry.hiddenParameter,
429
+ }
430
+ }),
431
+ views: row.views.map((view: unknown) => {
432
+ if (
433
+ !isRecord(view) ||
434
+ typeof view.name !== 'string' ||
435
+ typeof view.hidden !== 'boolean' ||
436
+ typeof view.props !== 'string'
437
+ )
438
+ throw new Error('expo-types printed an invalid view')
439
+ return { name: view.name, hidden: view.hidden, props: view.props }
440
+ }),
441
+ props: row.props.map((entry: unknown) => {
442
+ if (
443
+ !isRecord(entry) ||
444
+ typeof entry.name !== 'string' ||
445
+ typeof entry.hidden !== 'boolean' ||
446
+ !Array.isArray(entry.states) ||
447
+ !entry.states.every((state: unknown) => typeof state === 'string')
448
+ )
449
+ throw new Error('expo-types printed invalid compose props')
450
+ return { name: entry.name, hidden: entry.hidden, states: entry.states }
451
+ }),
452
+ }
453
+ })
454
+ }
455
+
456
+ const expoTypeScans = new Map<string, Promise<ExpoTypesFile[]>>()
457
+
458
+ // the records, enums, compose views and compose props the module sources
459
+ // declare, read by Kotlin's own parser; sources that name none skip the JVM start
460
+ function scanExpoTypes(
461
+ toolchain: KotlinToolchain,
462
+ script: string,
463
+ sources: string[],
464
+ ): Promise<ExpoTypesFile[]> {
465
+ const candidates = sources.filter((file) =>
466
+ /\bRecord\b|\benum\s+class\b|\bExpoComposeView\b|\bExpoView\b|\bComposeProps\b/.test(
467
+ fs.readFileSync(file, 'utf8'),
468
+ ),
469
+ )
470
+ if (candidates.length === 0) return Promise.resolve([])
471
+ const digest = createHash('sha256')
472
+ for (const file of [script, ...candidates])
473
+ digest.update(`${file}\0${fs.readFileSync(file, 'utf8')}\0`)
474
+ const key = digest.digest('hex')
475
+ let scan = expoTypeScans.get(key)
476
+ if (!scan) {
477
+ scan = (async () => {
478
+ const { kotlinc } = await ensureToolchain(toolchain)
479
+ const { stdout } = await run(
480
+ path.join(path.dirname(kotlinc), 'kotlinc'),
481
+ [
482
+ '-cp',
483
+ path.resolve(path.dirname(kotlinc), '../lib/kotlin-compiler.jar'),
484
+ '-script',
485
+ script,
486
+ ...candidates,
487
+ ],
488
+ { maxBuffer: 16 * 1024 * 1024 },
489
+ )
490
+ return readExpoTypes(stdout)
491
+ })().catch((error: unknown) => {
492
+ expoTypeScans.delete(key)
493
+ throw error
494
+ })
495
+ expoTypeScans.set(key, scan)
496
+ }
497
+ return scan
498
+ }
499
+
500
+ // upstream converts records and enums by reflection; Kotlin/Wasm has none, so
501
+ // each @Field initializer and constructor default reads the incoming value
502
+ // through PeachExpoTypes.initial, and each package registers its types
503
+ function expoTypeSources(files: ExpoTypesFile[]): {
504
+ generated: Record<string, string>
505
+ origins: Record<string, string>
506
+ registrations: string[]
507
+ } {
508
+ const generated: Record<string, string> = {}
509
+ const origins: Record<string, string> = {}
510
+ const registrations: string[] = []
511
+ files.forEach((file, index) => {
512
+ const edits: { range: [number, number]; owner: string; field: ExpoField }[] = []
513
+ const calls: string[] = []
514
+ for (const record of file.records) {
515
+ const fields: string[] = []
516
+ for (const field of record.fields) {
517
+ if (field.initializer)
518
+ edits.push({ range: field.initializer, owner: record.name, field })
519
+ if (field.hidden) {
520
+ if (!field.initializer)
521
+ throw new Error(
522
+ `${file.file}: @Field ${record.name}.${field.name} is private and has no initializer, which Peach cannot set`,
523
+ )
524
+ continue
525
+ }
526
+ const name = literal(field.key)
527
+ const read = `{ record: ${record.name} -> record.${field.name} }`
528
+ const write =
529
+ field.mutable && !field.initializer
530
+ ? `{ record, value -> record.${field.name} = value }`
531
+ : 'null'
532
+ fields.push(
533
+ ` dev.peach.expo.PeachExpoTypes.field(${name}, ${field.required}, ${read}, ${write}),`,
534
+ )
535
+ }
536
+ if (record.hidden) continue
537
+ calls.push(
538
+ [
539
+ ` dev.peach.expo.PeachExpoTypes.record(${record.name}::class, ${literal(file.package ? `${file.package}.${record.name}` : record.name)}, { ${record.name}() }, listOf(`,
540
+ ...fields,
541
+ ' ))',
542
+ ].join('\n'),
543
+ )
544
+ }
545
+ for (const entry of file.enums) {
546
+ if (entry.hidden) continue
547
+ if (entry.hiddenParameter)
548
+ throw new Error(
549
+ `${file.file}: enum ${entry.name} keeps its value in a private property, which Peach cannot read`,
550
+ )
551
+ const value = entry.fields === 1 ? `{ it.${entry.parameter} }` : 'null'
552
+ calls.push(
553
+ ` dev.peach.expo.PeachExpoTypes.enum(${entry.name}::class, enumValues<${entry.name}>(), ${entry.fields}, ${entry.parameter === null ? 'null' : literal(entry.parameter)}, ${value})`,
554
+ )
555
+ }
556
+ // a view's constructor and its props' states are named where the source
557
+ // declares them, so they resolve through the source's own imports
558
+ const views = [
559
+ ...file.views
560
+ .filter((view) => !view.hidden)
561
+ .map(
562
+ (view) =>
563
+ ` dev.peach.expo.PeachExpoTypes.view(${view.name}::class, ${view.props ? `${view.props}::class` : 'null'}) { context, appContext -> ${view.name}(context, appContext) }`,
564
+ ),
565
+ ...file.props
566
+ .filter((entry) => !entry.hidden)
567
+ .map((entry) =>
568
+ [
569
+ ` dev.peach.expo.PeachExpoTypes.composeProps(${entry.name}::class, listOf(`,
570
+ ...entry.states.map(
571
+ (state) =>
572
+ ` dev.peach.expo.PeachExpoCompose.prop(${literal(state)}) { props: ${entry.name} -> props.${state} },`,
573
+ ),
574
+ ' ))',
575
+ ].join('\n'),
576
+ ),
577
+ ]
578
+ if (edits.length > 0 || views.length > 0) {
579
+ let text = fs.readFileSync(file.file, 'utf8').replaceAll('\r\n', '\n')
580
+ for (const { range, owner, field } of edits.sort((a, b) => b.range[0] - a.range[0]))
581
+ text = `${text.slice(0, range[0])}dev.peach.expo.PeachExpoTypes.initial(${owner}::class, ${literal(field.key)}, ${field.required}) { ${text.slice(range[0], range[1])} }${text.slice(range[1])}`
582
+ if (views.length > 0) {
583
+ text = [
584
+ text,
585
+ '',
586
+ `internal fun peachExpoViews${index}() {`,
587
+ ...views,
588
+ '}',
589
+ '',
590
+ ].join('\n')
591
+ registrations.push(
592
+ `${file.package ? `${file.package}.` : ''}peachExpoViews${index}()`,
593
+ )
594
+ }
595
+ const name = `PeachExpoSource_${index}_${path.basename(file.file)}`
596
+ generated[name] = text
597
+ origins[name] = file.file
598
+ }
599
+ if (calls.length === 0) return
600
+ generated[`PeachExpoTypes_${index}.kt`] = [
601
+ ...(file.package ? [`package ${file.package}`, ''] : []),
602
+ `internal fun peachExpoTypes${index}() {`,
603
+ ...calls,
604
+ '}',
605
+ '',
606
+ ].join('\n')
607
+ registrations.push(
608
+ `${file.package ? `${file.package}.` : ''}peachExpoTypes${index}()`,
609
+ )
610
+ })
611
+ return { generated, origins, registrations }
612
+ }
613
+
614
+ // local Expo modules: their android Kotlin, the shared actuals and the
615
+ // expo-modules-core DSL port, with an entry registering the classes Expo's
616
+ // autolinker names, as its generated ExpoModulesPackageList does
617
+ export async function expoKotlinArtifact(
618
+ root: string,
619
+ moduleRoot: string,
620
+ origin: string,
621
+ modules: ExpoKotlinModules,
622
+ ): Promise<KotlinArtifact & { compose: boolean }> {
623
+ const toolchain = readToolchain(moduleRoot)
624
+ const language = appKotlinLanguage(root)
625
+ const scanned = await scanExpoTypes(
626
+ toolchain,
627
+ path.join(moduleRoot, 'tools/expo-types.main.kts'),
628
+ modules.sources,
629
+ )
630
+ // every exported view uses the existing native scene presenter. modules
631
+ // without views or compose imports do not link that renderer.
632
+ const usesCompose =
633
+ scanned.some((file) => file.views.length > 0) ||
634
+ modules.sources.some((file) =>
635
+ /\bandroidx\.compose\./.test(fs.readFileSync(file, 'utf8')),
636
+ )
637
+ const shims = [
638
+ ...kotlinSources(path.join(moduleRoot, 'Sources'), new Set()).filter(
639
+ (file) => usesCompose || !file.endsWith('/OneNativeSourceViewDispatch.kt'),
640
+ ),
641
+ ...kotlinSources(path.join(moduleRoot, 'ExpoSources'), new Set()),
642
+ ...kotlinSources(path.join(moduleRoot, 'AndroidViewSources'), new Set()),
643
+ ...(usesCompose
644
+ ? [
645
+ ...kotlinSources(path.join(moduleRoot, 'ComposeSources')),
646
+ ...kotlinSources(path.join(moduleRoot, 'ExpoComposeSources'), new Set()),
647
+ ]
648
+ : []),
649
+ ]
650
+ const types = expoTypeSources(scanned)
651
+ const rewritten = new Set(Object.values(types.origins))
652
+ const entry = [
653
+ '@file:OptIn(kotlin.js.ExperimentalJsExport::class)',
654
+ '',
655
+ 'import dev.peach.expo.PeachExpoRuntime',
656
+ '',
657
+ 'private val registered by lazy {',
658
+ ...types.registrations.map((call) => ` ${call}`),
659
+ ' PeachExpoRuntime.register(listOf(',
660
+ ...modules.modules.map(
661
+ ({ classifier, name }) =>
662
+ ` { ${classifier}() } to ${name === null ? 'null' : JSON.stringify(name)},`,
663
+ ),
664
+ ' ))',
665
+ '}',
666
+ '',
667
+ '@JsExport fun peachExpoListen(notify: () -> Unit) {',
668
+ ' registered',
669
+ ' PeachExpoRuntime.listen(notify)',
670
+ '}',
671
+ '',
672
+ '@JsExport fun peachExpoCall(payload: String): String {',
673
+ ' registered',
674
+ ' return PeachExpoRuntime.dispatch(payload)',
675
+ '}',
676
+ '',
677
+ ].join('\n')
678
+ const artifact = await compileKotlin(
679
+ {
680
+ root,
681
+ toolchain,
682
+ language,
683
+ sources: modules.sources.filter((file) => !rewritten.has(file)),
684
+ generated: {
685
+ ...types.generated,
686
+ 'PeachExpoEntry.kt': entry,
687
+ ...(usesCompose ? { 'PeachViews.kt': viewsSource([]) } : {}),
688
+ },
689
+ origins: types.origins,
690
+ shims,
691
+ usesCompose,
692
+ },
693
+ origin,
694
+ )
695
+ return { ...artifact, compose: usesCompose }
696
+ }
697
+
318
698
  function viewsSource(viewIds: string[]): string {
319
699
  return [
320
700
  'import dev.onejs.one.OneNativeSourceViewDispatch',
@@ -377,8 +757,8 @@ export async function kotlinAppArtifact(
377
757
  'import androidx.lifecycle.createSavedStateHandle',
378
758
  '',
379
759
  '@JsExport',
380
- 'fun peachAppMount(density: Float, fontScale: Float, locale: String, backend: String, stencilBits: Int,',
381
- ' regular: String, medium: String, bold: String, insets: String, night: Boolean,',
760
+ 'fun peachAppMount(density: Float, fontScale: Float, locale: String,',
761
+ ' font: String, insets: String, night: Boolean,',
382
762
  ' invalidate: (Boolean) -> Unit): String = dev.peach.app.peachStartApp(',
383
763
  ' dev.peach.app.PeachAppSpec(',
384
764
  ` android.content.ComponentName(${literal(app.packageName)}, ${literal(app.launcher.name)}),`,
@@ -392,8 +772,9 @@ export async function kotlinAppArtifact(
392
772
  ' androidx.lifecycle.viewmodel.viewModelFactory {',
393
773
  ...viewModels.map(viewModelInitializer),
394
774
  ' },',
775
+ ` ${app.predictiveBack},`,
395
776
  ' ),',
396
- ' density, fontScale, locale, backend, stencilBits, regular, medium, bold, insets, night, invalidate,',
777
+ ' density, fontScale, locale, font, insets, night, invalidate,',
397
778
  ')',
398
779
  '',
399
780
  '@JsExport',
@@ -406,7 +787,10 @@ export async function kotlinAppArtifact(
406
787
  'fun peachAppNight(night: Boolean) = dev.peach.app.peachAppNight(night)',
407
788
  '',
408
789
  '@JsExport',
409
- 'fun peachAppIme(bottom: Int) = dev.peach.app.peachAppIme(bottom)',
790
+ 'fun peachAppIme(bottom: Int, visible: Boolean) = dev.peach.app.peachAppIme(bottom, visible)',
791
+ '',
792
+ '@JsExport',
793
+ 'fun peachAppInsets(insets: String, visibleSystemBars: Int) = dev.peach.app.peachAppInsets(insets, visibleSystemBars)',
410
794
  '',
411
795
  ].join('\n')
412
796
  const generated: Record<string, string> = {
@@ -416,9 +800,10 @@ export async function kotlinAppArtifact(
416
800
  ...appResourceSources(app),
417
801
  }
418
802
  const shims = [
419
- ...kotlinSources(path.join(moduleRoot, 'Sources')),
803
+ ...kotlinSources(path.join(moduleRoot, 'Sources'), new Set()),
420
804
  ...kotlinSources(path.join(moduleRoot, 'ComposeSources')),
421
805
  ...kotlinSources(path.join(moduleRoot, 'AppSources'), new Set()),
806
+ ...kotlinSources(path.join(moduleRoot, 'AndroidViewSources'), new Set()),
422
807
  ]
423
808
  return compileKotlin(
424
809
  {
@@ -637,6 +1022,8 @@ async function compileKotlin(
637
1022
  language: string
638
1023
  sources: string[]
639
1024
  generated: Record<string, string>
1025
+ // the app source a generated file rewrites, which its diagnostics name
1026
+ origins?: Record<string, string>
640
1027
  shims: string[]
641
1028
  usesCompose: boolean
642
1029
  // a generated file that names the app's view models: rendered empty for a
@@ -655,6 +1042,7 @@ async function compileKotlin(
655
1042
  language,
656
1043
  sources,
657
1044
  generated,
1045
+ origins = {},
658
1046
  shims,
659
1047
  usesCompose,
660
1048
  viewModels,
@@ -701,6 +1089,7 @@ async function compileKotlin(
701
1089
  const common = [
702
1090
  // the full upstream Compose graph exceeds kotlinc's 512 mib default heap.
703
1091
  '-J-Xmx2g',
1092
+ '-Xallow-kotlin-package',
704
1093
  '-Xwasm-target=js',
705
1094
  '-libraries',
706
1095
  libraries.join(path.delimiter),
@@ -771,16 +1160,56 @@ async function compileKotlin(
771
1160
  ],
772
1161
  { maxBuffer: 16 * 1024 * 1024 },
773
1162
  )
774
- if (usesCompose)
1163
+ if (usesCompose) {
775
1164
  await run('unzip', [
776
1165
  '-q',
777
1166
  '-j',
778
1167
  composeRuntime,
779
1168
  'skiko.mjs',
780
- 'skiko.wasm',
781
1169
  '-d',
782
1170
  path.join(work, 'js'),
783
1171
  ])
1172
+ // each artifact references the tenant's one upstream callback namespace.
1173
+ // the pinned loader's binding declarations define its public ABI.
1174
+ const skikoPath = path.join(work, 'js', 'skiko.mjs')
1175
+ const upstream = parse(fs.readFileSync(skikoPath, 'utf8'), {
1176
+ ecmaVersion: 'latest',
1177
+ sourceType: 'module',
1178
+ })
1179
+ const bindings: string[] = []
1180
+ for (const statement of upstream.body) {
1181
+ if (
1182
+ statement.type !== 'ExportNamedDeclaration' ||
1183
+ statement.declaration?.type !== 'VariableDeclaration' ||
1184
+ statement.declaration.kind !== 'let'
1185
+ )
1186
+ continue
1187
+ for (const binding of statement.declaration.declarations) {
1188
+ if (binding.id.type !== 'Identifier')
1189
+ throw new Error('Skiko binding must have one exported identifier')
1190
+ bindings.push(binding.id.name)
1191
+ }
1192
+ }
1193
+ if (bindings.length !== 967) throw new Error('pinned Skiko binding count changed')
1194
+ const names = [
1195
+ 'awaitSkiko',
1196
+ 'loadedWasm',
1197
+ 'registerSkikoWasmReadyCallback',
1198
+ 'skikoApi',
1199
+ 'GL',
1200
+ ...bindings,
1201
+ ]
1202
+ fs.writeFileSync(
1203
+ skikoPath,
1204
+ [
1205
+ 'const runtime = globalThis.__peachKotlinSkiko;',
1206
+ "if (!runtime) throw Error('Kotlin tenant runtime was not installed before guest evaluation');",
1207
+ `for (const name of ${JSON.stringify(names)}) if (!(name in runtime)) throw Error('Kotlin tenant runtime has no ' + name);`,
1208
+ ...names.map((name) => `export const ${name} = runtime.${name};`),
1209
+ '',
1210
+ ].join('\n'),
1211
+ )
1212
+ }
784
1213
  // kotlin's npm externals remain bare imports after linking. publish the
785
1214
  // exact upstream ES modules beside the generated loader for workers.
786
1215
  if (usesCompose) {
@@ -815,9 +1244,11 @@ async function compileKotlin(
815
1244
  }
816
1245
  } catch (error) {
817
1246
  fs.rmSync(work, { recursive: true, force: true })
818
- const output = isRecord(error)
1247
+ let output = isRecord(error)
819
1248
  ? `${String(error.stderr ?? '')}${String(error.stdout ?? '')}`
820
1249
  : ''
1250
+ for (const [name, source] of Object.entries(origins))
1251
+ output = output.replaceAll(path.join(generatedDir, name), source)
821
1252
  throw new Error(
822
1253
  `kotlin compile failed:\n${output.trim() || (error instanceof Error ? error.message : String(error))}`,
823
1254
  )
@@ -882,6 +1313,22 @@ const CONTENT_TYPES: Record<string, string> = {
882
1313
  '.wasm': 'application/wasm',
883
1314
  }
884
1315
 
1316
+ // a compiled module's loader, import object and wasm, which a release bundle
1317
+ // carries beside its javascript
1318
+ export function kotlinArtifactFiles(
1319
+ hash: string,
1320
+ ): { name: string; bytes: Buffer; contentType: string }[] {
1321
+ const dir = path.join(buildsDir(), hash, 'js')
1322
+ return fs
1323
+ .readdirSync(dir)
1324
+ .sort()
1325
+ .map((name) => {
1326
+ const contentType = CONTENT_TYPES[path.extname(name)]
1327
+ if (!contentType) throw new Error(`unexpected kotlin artifact file ${name}`)
1328
+ return { name, bytes: fs.readFileSync(path.join(dir, name)), contentType }
1329
+ })
1330
+ }
1331
+
885
1332
  // serves a compiled module's loader, import object and wasm, cross-origin
886
1333
  // to the simulator that imports them
887
1334
  export function serveKotlinArtifact(pathname: string, res: ServerResponse): boolean {
@@ -915,13 +1362,10 @@ export function serveKotlinArtifact(pathname: string, res: ServerResponse): bool
915
1362
 
916
1363
  const KOTLIN_PACKAGE_PATH = '/__peach/kotlin-package'
917
1364
 
918
- // peachsim's own Kotlin module (toolchain pins and shims) beside the app's
919
- // installed peachsim
920
- export function peachKotlinModuleRoot(root: string): string {
921
- const peachsimPackage = createRequire(path.join(root, 'package.json')).resolve(
922
- 'peachsim/package.json',
923
- )
924
- return path.join(path.dirname(peachsimPackage), 'kotlin')
1365
+ // peachsim's own Kotlin module (toolchain pins and shims), found from this
1366
+ // file (src/ or dist-lib/ → ..) so apps need no direct peachsim dependency
1367
+ export function peachKotlinModuleRoot(): string {
1368
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'kotlin')
925
1369
  }
926
1370
 
927
1371
  // answers one /__peach/kotlin* request: the app's compiled module description,