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;
@@ -1558,6 +1558,7 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
1558
1558
  } catch {
1559
1559
  return false;
1560
1560
  }
1561
+ if (url.pathname !== REPLACEMENT_MODULE_ROUTE) return false;
1561
1562
  if (options.requireLoopbackOrigin) {
1562
1563
  const origin = req.headers.origin;
1563
1564
  const remoteAddress = req.socket.remoteAddress?.replace(/^::ffff:/, "");
@@ -1575,7 +1576,6 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
1575
1576
  return true;
1576
1577
  }
1577
1578
  }
1578
- if (url.pathname !== REPLACEMENT_MODULE_ROUTE) return false;
1579
1579
  if (options.requestToken && url.searchParams.get("token") !== options.requestToken) {
1580
1580
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
1581
1581
  res.end("replacement module request token is invalid");
@@ -1983,10 +1983,16 @@ __export(config_exports, {
1983
1983
  hasPeachConfig: () => hasPeachConfig,
1984
1984
  isPeachConfig: () => isPeachConfig,
1985
1985
  mergePeachConfig: () => mergePeachConfig,
1986
+ readNativeRenderer: () => readNativeRenderer,
1986
1987
  readPeachBundleConfig: () => readPeachBundleConfig,
1987
1988
  readPeachBundleConfigFooter: () => readPeachBundleConfigFooter,
1988
1989
  readPeachConfigFromSearchParams: () => readPeachConfigFromSearchParams
1989
1990
  });
1991
+ function readNativeRenderer(value) {
1992
+ if (value === void 0) return void 0;
1993
+ if (value === "paper" || value === "fabric") return value;
1994
+ throw new Error("nativeRenderer must be paper or fabric");
1995
+ }
1990
1996
  function collectConfiguredFileModules(config) {
1991
1997
  return [
1992
1998
  ...new Set(
@@ -2002,7 +2008,7 @@ function collectConfiguredFileModules(config) {
2002
2008
  }
2003
2009
  function isPeachConfig(value) {
2004
2010
  if (!isRecord(value)) return false;
2005
- return (value.runtimeVersion === void 0 || typeof value.runtimeVersion === "string" && value.runtimeVersion === value.runtimeVersion.trim() && isValidRuntimeVersion(value.runtimeVersion)) && [
2011
+ return (value.nativeRenderer === void 0 || value.nativeRenderer === "paper" || value.nativeRenderer === "fabric") && (value.runtimeVersion === void 0 || typeof value.runtimeVersion === "string" && value.runtimeVersion === value.runtimeVersion.trim() && isValidRuntimeVersion(value.runtimeVersion)) && [
2006
2012
  "modules",
2007
2013
  "turboModules",
2008
2014
  "nativeModules",
@@ -2056,7 +2062,7 @@ function hasOwnKeys(value) {
2056
2062
  }
2057
2063
  function hasPeachConfig(config) {
2058
2064
  if (!config) return false;
2059
- return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
2065
+ return config.nativeRenderer !== void 0 || typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
2060
2066
  }
2061
2067
  function mergePeachConfig(base, override) {
2062
2068
  if (!hasPeachConfig(base) && !hasPeachConfig(override)) return void 0;
@@ -2064,6 +2070,7 @@ function mergePeachConfig(base, override) {
2064
2070
  if (!hasPeachConfig(override)) return base ? { ...base } : void 0;
2065
2071
  return {
2066
2072
  runtimeVersion: override.runtimeVersion ?? base.runtimeVersion,
2073
+ nativeRenderer: override.nativeRenderer ?? base.nativeRenderer,
2067
2074
  modules: { ...base.modules || {}, ...override.modules || {} },
2068
2075
  turboModules: { ...base.turboModules || {}, ...override.turboModules || {} },
2069
2076
  nativeModules: {
@@ -2085,14 +2092,16 @@ function mergePeachConfig(base, override) {
2085
2092
  function readPeachConfigFromSearchParams(searchParams) {
2086
2093
  const raw = searchParams.get(PEACH_CONFIG_QUERY_PARAM);
2087
2094
  if (!raw) return void 0;
2095
+ let parsed;
2088
2096
  try {
2089
- const parsed = JSON.parse(raw);
2090
- return hasPeachConfig(parsed) ? parsed : void 0;
2097
+ parsed = JSON.parse(raw);
2091
2098
  } catch (error) {
2092
2099
  const message = error instanceof Error ? error.message : String(error);
2093
2100
  console.warn(`[peach] invalid ${PEACH_CONFIG_QUERY_PARAM}: ${message}`);
2094
2101
  return void 0;
2095
2102
  }
2103
+ if (isRecord(parsed)) readNativeRenderer(parsed.nativeRenderer);
2104
+ return isPeachConfig(parsed) && hasPeachConfig(parsed) ? parsed : void 0;
2096
2105
  }
2097
2106
  function applyPeachConfigToUrl(url, config) {
2098
2107
  const parsed = new URL(url);
@@ -2126,12 +2135,12 @@ function resolveFileEntries(entries, configDir) {
2126
2135
  return Object.fromEntries(
2127
2136
  Object.entries(entries).map(([name2, resolution]) => [
2128
2137
  name2,
2129
- resolution && typeof resolution === "object" && "file" in resolution && typeof resolution.file === "string" ? { ...resolution, file: import_node_path12.default.resolve(configDir, resolution.file) } : resolution
2138
+ resolution && typeof resolution === "object" && "file" in resolution && typeof resolution.file === "string" ? { ...resolution, file: import_node_path13.default.resolve(configDir, resolution.file) } : resolution
2130
2139
  ])
2131
2140
  );
2132
2141
  }
2133
2142
  function resolveConfigReplacementFiles(config, configPath) {
2134
- const configDir = import_node_path12.default.dirname(configPath);
2143
+ const configDir = import_node_path13.default.dirname(configPath);
2135
2144
  return {
2136
2145
  ...config,
2137
2146
  modules: resolveFileEntries(config.modules, configDir),
@@ -2166,11 +2175,11 @@ async function resolveRNXAppConfig(startDir = process.cwd()) {
2166
2175
  const project = resolveAppProject(startDir);
2167
2176
  return { project, config: await loadRNXAppConfig(project) };
2168
2177
  }
2169
- var import_node_path12, import_jiti;
2178
+ var import_node_path13, import_jiti;
2170
2179
  var init_app_config = __esm({
2171
2180
  "cli/app-config.ts"() {
2172
2181
  "use strict";
2173
- import_node_path12 = __toESM(require("node:path"), 1);
2182
+ import_node_path13 = __toESM(require("node:path"), 1);
2174
2183
  import_jiti = require("jiti");
2175
2184
  init_config();
2176
2185
  init_config();
@@ -2200,14 +2209,14 @@ async function produceConfiguredBundle(source, config, runtimePackages) {
2200
2209
  const neededPackages = /* @__PURE__ */ new Set();
2201
2210
  const factories = [];
2202
2211
  const visit3 = (filePath) => {
2203
- const real = import_node_fs11.default.realpathSync(filePath);
2212
+ const real = import_node_fs12.default.realpathSync(filePath);
2204
2213
  const previous = files.get(real);
2205
2214
  if (previous !== void 0) return previous;
2206
2215
  const id = "peach-config-file:" + files.size;
2207
2216
  files.set(real, id);
2208
2217
  const compiled = compileReplacementModule({
2209
2218
  filePath: real,
2210
- source: import_node_fs11.default.readFileSync(real, "utf8"),
2219
+ source: import_node_fs12.default.readFileSync(real, "utf8"),
2211
2220
  runtimePackages,
2212
2221
  localModuleUrl: (specifier) => visit3(resolveReplacementModuleFile(real, specifier))
2213
2222
  });
@@ -2223,7 +2232,7 @@ async function produceConfiguredBundle(source, config, runtimePackages) {
2223
2232
  if (!entries || typeof entries !== "object") continue;
2224
2233
  for (const resolution of Object.values(entries)) {
2225
2234
  if (resolution && typeof resolution === "object" && "file" in resolution && typeof resolution.file === "string") {
2226
- resolution.file = visit3(import_node_path13.default.resolve(resolution.file));
2235
+ resolution.file = visit3(import_node_path14.default.resolve(resolution.file));
2227
2236
  }
2228
2237
  }
2229
2238
  }
@@ -2234,7 +2243,7 @@ async function produceConfiguredBundle(source, config, runtimePackages) {
2234
2243
  const compact = await (0, import_terser.minify)(code, {
2235
2244
  compress: false,
2236
2245
  mangle: false,
2237
- format: { comments: false, beautify: false }
2246
+ format: { comments: false }
2238
2247
  });
2239
2248
  if (!compact.code || /[\n\r]/.test(compact.code))
2240
2249
  throw new Error("configured bundle prefix must preserve the guest source lines");
@@ -2247,12 +2256,12 @@ async function produceConfiguredBundle(source, config, runtimePackages) {
2247
2256
  const identityStart = source.lastIndexOf("\nglobalThis.__sootsimModulePaths=");
2248
2257
  return prefix2 + (identityStart < 0 ? source + footer : source.slice(0, identityStart) + footer + source.slice(identityStart));
2249
2258
  }
2250
- var import_node_fs11, import_node_path13, import_sucrase, import_terser;
2259
+ var import_node_fs12, import_node_path14, import_sucrase, import_terser;
2251
2260
  var init_configured_bundle_producer = __esm({
2252
2261
  "src/configured-bundle-producer.ts"() {
2253
2262
  "use strict";
2254
- import_node_fs11 = __toESM(require("node:fs"), 1);
2255
- import_node_path13 = __toESM(require("node:path"), 1);
2263
+ import_node_fs12 = __toESM(require("node:fs"), 1);
2264
+ import_node_path14 = __toESM(require("node:path"), 1);
2256
2265
  import_sucrase = require("sucrase");
2257
2266
  import_terser = require("terser");
2258
2267
  init_replacementModule();
@@ -2282,9 +2291,10 @@ __export(src_exports, {
2282
2291
  jumpToSourceBabelPlugin: () => sootsimJumpToSourceBabelPlugin,
2283
2292
  metroPlugin: () => metro_plugin_default,
2284
2293
  packRnxCaptureRegions: () => packRnxCaptureRegions,
2294
+ peachPlugin: () => peachPlugin,
2285
2295
  rnxCaptureRasterRegions: () => rnxCaptureRasterRegions,
2286
- rnxPlugin: () => rnxPlugin,
2287
2296
  rnxRasterFrames: () => rnxRasterFrames,
2297
+ rnxRasterRegionRequests: () => rnxRasterRegionRequests,
2288
2298
  selectRnxRasterNodeIds: () => selectRnxRasterNodeIds
2289
2299
  });
2290
2300
  module.exports = __toCommonJS(src_exports);
@@ -2303,7 +2313,6 @@ var import_react_native_host_modules = require("peachsim/react-native-host-modul
2303
2313
  init_backend_origin();
2304
2314
 
2305
2315
  // src/cloud-contract.ts
2306
- var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
2307
2316
  var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
2308
2317
  var RNX_CLOUD_COMMAND_SCOPES = {
2309
2318
  state: "read",
@@ -2713,17 +2722,17 @@ function visit(node, visitor) {
2713
2722
  visit.BREAK = BREAK;
2714
2723
  visit.SKIP = SKIP;
2715
2724
  visit.REMOVE = REMOVE;
2716
- function visit_(key, node, visitor, path16) {
2717
- const ctrl = callVisitor(key, node, visitor, path16);
2725
+ function visit_(key, node, visitor, path17) {
2726
+ const ctrl = callVisitor(key, node, visitor, path17);
2718
2727
  if (isNode(ctrl) || isPair(ctrl)) {
2719
- replaceNode(key, path16, ctrl);
2720
- return visit_(key, ctrl, visitor, path16);
2728
+ replaceNode(key, path17, ctrl);
2729
+ return visit_(key, ctrl, visitor, path17);
2721
2730
  }
2722
2731
  if (typeof ctrl !== "symbol") {
2723
2732
  if (isCollection(node)) {
2724
- path16 = Object.freeze(path16.concat(node));
2733
+ path17 = Object.freeze(path17.concat(node));
2725
2734
  for (let i = 0; i < node.items.length; ++i) {
2726
- const ci = visit_(i, node.items[i], visitor, path16);
2735
+ const ci = visit_(i, node.items[i], visitor, path17);
2727
2736
  if (typeof ci === "number")
2728
2737
  i = ci - 1;
2729
2738
  else if (ci === BREAK)
@@ -2734,13 +2743,13 @@ function visit_(key, node, visitor, path16) {
2734
2743
  }
2735
2744
  }
2736
2745
  } else if (isPair(node)) {
2737
- path16 = Object.freeze(path16.concat(node));
2738
- const ck = visit_("key", node.key, visitor, path16);
2746
+ path17 = Object.freeze(path17.concat(node));
2747
+ const ck = visit_("key", node.key, visitor, path17);
2739
2748
  if (ck === BREAK)
2740
2749
  return BREAK;
2741
2750
  else if (ck === REMOVE)
2742
2751
  node.key = null;
2743
- const cv = visit_("value", node.value, visitor, path16);
2752
+ const cv = visit_("value", node.value, visitor, path17);
2744
2753
  if (cv === BREAK)
2745
2754
  return BREAK;
2746
2755
  else if (cv === REMOVE)
@@ -2761,17 +2770,17 @@ async function visitAsync(node, visitor) {
2761
2770
  visitAsync.BREAK = BREAK;
2762
2771
  visitAsync.SKIP = SKIP;
2763
2772
  visitAsync.REMOVE = REMOVE;
2764
- async function visitAsync_(key, node, visitor, path16) {
2765
- const ctrl = await callVisitor(key, node, visitor, path16);
2773
+ async function visitAsync_(key, node, visitor, path17) {
2774
+ const ctrl = await callVisitor(key, node, visitor, path17);
2766
2775
  if (isNode(ctrl) || isPair(ctrl)) {
2767
- replaceNode(key, path16, ctrl);
2768
- return visitAsync_(key, ctrl, visitor, path16);
2776
+ replaceNode(key, path17, ctrl);
2777
+ return visitAsync_(key, ctrl, visitor, path17);
2769
2778
  }
2770
2779
  if (typeof ctrl !== "symbol") {
2771
2780
  if (isCollection(node)) {
2772
- path16 = Object.freeze(path16.concat(node));
2781
+ path17 = Object.freeze(path17.concat(node));
2773
2782
  for (let i = 0; i < node.items.length; ++i) {
2774
- const ci = await visitAsync_(i, node.items[i], visitor, path16);
2783
+ const ci = await visitAsync_(i, node.items[i], visitor, path17);
2775
2784
  if (typeof ci === "number")
2776
2785
  i = ci - 1;
2777
2786
  else if (ci === BREAK)
@@ -2782,13 +2791,13 @@ async function visitAsync_(key, node, visitor, path16) {
2782
2791
  }
2783
2792
  }
2784
2793
  } else if (isPair(node)) {
2785
- path16 = Object.freeze(path16.concat(node));
2786
- const ck = await visitAsync_("key", node.key, visitor, path16);
2794
+ path17 = Object.freeze(path17.concat(node));
2795
+ const ck = await visitAsync_("key", node.key, visitor, path17);
2787
2796
  if (ck === BREAK)
2788
2797
  return BREAK;
2789
2798
  else if (ck === REMOVE)
2790
2799
  node.key = null;
2791
- const cv = await visitAsync_("value", node.value, visitor, path16);
2800
+ const cv = await visitAsync_("value", node.value, visitor, path17);
2792
2801
  if (cv === BREAK)
2793
2802
  return BREAK;
2794
2803
  else if (cv === REMOVE)
@@ -2815,23 +2824,23 @@ function initVisitor(visitor) {
2815
2824
  }
2816
2825
  return visitor;
2817
2826
  }
2818
- function callVisitor(key, node, visitor, path16) {
2827
+ function callVisitor(key, node, visitor, path17) {
2819
2828
  if (typeof visitor === "function")
2820
- return visitor(key, node, path16);
2829
+ return visitor(key, node, path17);
2821
2830
  if (isMap(node))
2822
- return visitor.Map?.(key, node, path16);
2831
+ return visitor.Map?.(key, node, path17);
2823
2832
  if (isSeq(node))
2824
- return visitor.Seq?.(key, node, path16);
2833
+ return visitor.Seq?.(key, node, path17);
2825
2834
  if (isPair(node))
2826
- return visitor.Pair?.(key, node, path16);
2835
+ return visitor.Pair?.(key, node, path17);
2827
2836
  if (isScalar(node))
2828
- return visitor.Scalar?.(key, node, path16);
2837
+ return visitor.Scalar?.(key, node, path17);
2829
2838
  if (isAlias(node))
2830
- return visitor.Alias?.(key, node, path16);
2839
+ return visitor.Alias?.(key, node, path17);
2831
2840
  return void 0;
2832
2841
  }
2833
- function replaceNode(key, path16, node) {
2834
- const parent = path16[path16.length - 1];
2842
+ function replaceNode(key, path17, node) {
2843
+ const parent = path17[path17.length - 1];
2835
2844
  if (isCollection(parent)) {
2836
2845
  parent.items[key] = node;
2837
2846
  } else if (isPair(parent)) {
@@ -3311,10 +3320,10 @@ function createNode(value, tagName, ctx) {
3311
3320
  }
3312
3321
 
3313
3322
  // ../../node_modules/yaml/browser/dist/nodes/Collection.js
3314
- function collectionFromPath(schema4, path16, value) {
3323
+ function collectionFromPath(schema4, path17, value) {
3315
3324
  let v = value;
3316
- for (let i = path16.length - 1; i >= 0; --i) {
3317
- const k = path16[i];
3325
+ for (let i = path17.length - 1; i >= 0; --i) {
3326
+ const k = path17[i];
3318
3327
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
3319
3328
  const a = [];
3320
3329
  a[k] = v;
@@ -3333,7 +3342,7 @@ function collectionFromPath(schema4, path16, value) {
3333
3342
  sourceObjects: /* @__PURE__ */ new Map()
3334
3343
  });
3335
3344
  }
3336
- var isEmptyPath = (path16) => path16 == null || typeof path16 === "object" && !!path16[Symbol.iterator]().next().done;
3345
+ var isEmptyPath = (path17) => path17 == null || typeof path17 === "object" && !!path17[Symbol.iterator]().next().done;
3337
3346
  var Collection = class extends NodeBase {
3338
3347
  constructor(type, schema4) {
3339
3348
  super(type);
@@ -3363,11 +3372,11 @@ var Collection = class extends NodeBase {
3363
3372
  * be a Pair instance or a `{ key, value }` object, which may not have a key
3364
3373
  * that already exists in the map.
3365
3374
  */
3366
- addIn(path16, value) {
3367
- if (isEmptyPath(path16))
3375
+ addIn(path17, value) {
3376
+ if (isEmptyPath(path17))
3368
3377
  this.add(value);
3369
3378
  else {
3370
- const [key, ...rest] = path16;
3379
+ const [key, ...rest] = path17;
3371
3380
  const node = this.get(key, true);
3372
3381
  if (isCollection(node))
3373
3382
  node.addIn(rest, value);
@@ -3381,8 +3390,8 @@ var Collection = class extends NodeBase {
3381
3390
  * Removes a value from the collection.
3382
3391
  * @returns `true` if the item was found and removed.
3383
3392
  */
3384
- deleteIn(path16) {
3385
- const [key, ...rest] = path16;
3393
+ deleteIn(path17) {
3394
+ const [key, ...rest] = path17;
3386
3395
  if (rest.length === 0)
3387
3396
  return this.delete(key);
3388
3397
  const node = this.get(key, true);
@@ -3396,8 +3405,8 @@ var Collection = class extends NodeBase {
3396
3405
  * scalar values from their surrounding node; to disable set `keepScalar` to
3397
3406
  * `true` (collections are always returned intact).
3398
3407
  */
3399
- getIn(path16, keepScalar) {
3400
- const [key, ...rest] = path16;
3408
+ getIn(path17, keepScalar) {
3409
+ const [key, ...rest] = path17;
3401
3410
  const node = this.get(key, true);
3402
3411
  if (rest.length === 0)
3403
3412
  return !keepScalar && isScalar(node) ? node.value : node;
@@ -3415,8 +3424,8 @@ var Collection = class extends NodeBase {
3415
3424
  /**
3416
3425
  * Checks if the collection includes a value with the key `key`.
3417
3426
  */
3418
- hasIn(path16) {
3419
- const [key, ...rest] = path16;
3427
+ hasIn(path17) {
3428
+ const [key, ...rest] = path17;
3420
3429
  if (rest.length === 0)
3421
3430
  return this.has(key);
3422
3431
  const node = this.get(key, true);
@@ -3426,8 +3435,8 @@ var Collection = class extends NodeBase {
3426
3435
  * Sets a value in this collection. For `!!set`, `value` needs to be a
3427
3436
  * boolean to add/remove the item from the set.
3428
3437
  */
3429
- setIn(path16, value) {
3430
- const [key, ...rest] = path16;
3438
+ setIn(path17, value) {
3439
+ const [key, ...rest] = path17;
3431
3440
  if (rest.length === 0) {
3432
3441
  this.set(key, value);
3433
3442
  } else {
@@ -4876,9 +4885,9 @@ function visit2(cst, visitor) {
4876
4885
  visit2.BREAK = BREAK2;
4877
4886
  visit2.SKIP = SKIP2;
4878
4887
  visit2.REMOVE = REMOVE2;
4879
- visit2.itemAtPath = (cst, path16) => {
4888
+ visit2.itemAtPath = (cst, path17) => {
4880
4889
  let item = cst;
4881
- for (const [field, index] of path16) {
4890
+ for (const [field, index] of path17) {
4882
4891
  const tok = item?.[field];
4883
4892
  if (tok && "items" in tok) {
4884
4893
  item = tok.items[index];
@@ -4887,23 +4896,23 @@ visit2.itemAtPath = (cst, path16) => {
4887
4896
  }
4888
4897
  return item;
4889
4898
  };
4890
- visit2.parentCollection = (cst, path16) => {
4891
- const parent = visit2.itemAtPath(cst, path16.slice(0, -1));
4892
- const field = path16[path16.length - 1][0];
4899
+ visit2.parentCollection = (cst, path17) => {
4900
+ const parent = visit2.itemAtPath(cst, path17.slice(0, -1));
4901
+ const field = path17[path17.length - 1][0];
4893
4902
  const coll = parent?.[field];
4894
4903
  if (coll && "items" in coll)
4895
4904
  return coll;
4896
4905
  throw new Error("Parent collection not found");
4897
4906
  };
4898
- function _visit(path16, item, visitor) {
4899
- let ctrl = visitor(item, path16);
4907
+ function _visit(path17, item, visitor) {
4908
+ let ctrl = visitor(item, path17);
4900
4909
  if (typeof ctrl === "symbol")
4901
4910
  return ctrl;
4902
4911
  for (const field of ["key", "value"]) {
4903
4912
  const token = item[field];
4904
4913
  if (token && "items" in token) {
4905
4914
  for (let i = 0; i < token.items.length; ++i) {
4906
- const ci = _visit(Object.freeze(path16.concat([[field, i]])), token.items[i], visitor);
4915
+ const ci = _visit(Object.freeze(path17.concat([[field, i]])), token.items[i], visitor);
4907
4916
  if (typeof ci === "number")
4908
4917
  i = ci - 1;
4909
4918
  else if (ci === BREAK2)
@@ -4914,10 +4923,10 @@ function _visit(path16, item, visitor) {
4914
4923
  }
4915
4924
  }
4916
4925
  if (typeof ctrl === "function" && field === "key")
4917
- ctrl = ctrl(item, path16);
4926
+ ctrl = ctrl(item, path17);
4918
4927
  }
4919
4928
  }
4920
- return typeof ctrl === "function" ? ctrl(item, path16) : ctrl;
4929
+ return typeof ctrl === "function" ? ctrl(item, path17) : ctrl;
4921
4930
  }
4922
4931
 
4923
4932
  // ../../node_modules/yaml/browser/dist/parse/lexer.js
@@ -5378,7 +5387,8 @@ function writeSecureJsonAtomic(filepath, value) {
5378
5387
 
5379
5388
  // cli/current-sim.ts
5380
5389
  var CURRENT_SIM_VERSION = 2;
5381
- var CURRENT_SIM_PATH_ENV = "SOOTSIM_CLI_CURRENT_SIM_PATH";
5390
+ var CURRENT_SIM_PATH_ENV = "PEACH_CLI_CURRENT_SIM_PATH";
5391
+ var LEGACY_CURRENT_SIM_PATH_ENV = "SOOTSIM_CLI_CURRENT_SIM_PATH";
5382
5392
  var IDENTITY_ENV_VARS = [
5383
5393
  "PEACH_CLI_IDENTITY",
5384
5394
  // explicit override — fastest path, skips all pid lookups
@@ -5457,8 +5467,9 @@ function getOwnerProcessGroupId() {
5457
5467
  return pgid !== null && pgid > 1 ? pgid : null;
5458
5468
  }
5459
5469
  function getCurrentSimPath() {
5460
- if (process.env[CURRENT_SIM_PATH_ENV]) {
5461
- return (0, import_path.resolve)(process.env[CURRENT_SIM_PATH_ENV]);
5470
+ const envPath = process.env[CURRENT_SIM_PATH_ENV] || process.env[LEGACY_CURRENT_SIM_PATH_ENV];
5471
+ if (envPath) {
5472
+ return (0, import_path.resolve)(envPath);
5462
5473
  }
5463
5474
  const key = getCliIdentityKey();
5464
5475
  return (0, import_path.join)((0, import_os.tmpdir)(), `peach-current-sim-${key}.json`);
@@ -5568,9 +5579,9 @@ function pathContains(parent, child) {
5568
5579
  function resolveDefaultDevBridgeLockfile() {
5569
5580
  const lockfiles = liveDevBridgeLockfiles();
5570
5581
  if (lockfiles.length === 0) return null;
5571
- if (process.env.SOOTSIM_BRIDGE_PORT !== void 0 || process.env.PORT_OFFSET !== void 0) {
5582
+ if (process.env.PEACH_BRIDGE_PORT !== void 0 || process.env.PORT_OFFSET !== void 0) {
5572
5583
  const requestedPort = resolveSootsimBridgePort({
5573
- explicitPort: process.env.SOOTSIM_BRIDGE_PORT,
5584
+ explicitPort: process.env.PEACH_BRIDGE_PORT,
5574
5585
  portOffset: process.env.PORT_OFFSET
5575
5586
  });
5576
5587
  return lockfiles.find((lockfile) => lockfile.bridgePort === requestedPort) ?? null;
@@ -5615,10 +5626,10 @@ function resolveBridgeWorldFresh() {
5615
5626
  }
5616
5627
  const lock = readDaemonLockfile();
5617
5628
  const requestedPort = resolveSootsimBridgePort({
5618
- explicitPort: process.env.SOOTSIM_BRIDGE_PORT,
5629
+ explicitPort: process.env.PEACH_BRIDGE_PORT,
5619
5630
  portOffset: process.env.PORT_OFFSET
5620
5631
  });
5621
- const explicitWorld = process.env.SOOTSIM_BRIDGE_PORT !== void 0 || process.env.PORT_OFFSET !== void 0;
5632
+ const explicitWorld = process.env.PEACH_BRIDGE_PORT !== void 0 || process.env.PORT_OFFSET !== void 0;
5622
5633
  if (lock && isDaemonLockfileFresh(lock) && (!explicitWorld || lock.bridgePort === requestedPort)) {
5623
5634
  return {
5624
5635
  source: "daemon",
@@ -5743,7 +5754,8 @@ function createBridge(wsPort, opts = {}) {
5743
5754
  }
5744
5755
  function maybeNoticeTarget(cmdType, simId) {
5745
5756
  if (noticedTarget) return;
5746
- if (process.env.SOOTSIM_QUIET_TARGET_NOTICE === "1") return;
5757
+ if (process.env.PEACH_QUIET_TARGET_NOTICE === "1" || process.env.SOOTSIM_QUIET_TARGET_NOTICE === "1")
5758
+ return;
5747
5759
  if (cmdType.startsWith("bridge:") || cmdType === "focus" || cmdType === "close")
5748
5760
  return;
5749
5761
  noticedTarget = true;
@@ -5926,7 +5938,6 @@ function createBridge(wsPort, opts = {}) {
5926
5938
  }
5927
5939
 
5928
5940
  // src/bridge-contract.ts
5929
- var PUBLIC_NO_NETWORK_MARKER = "public Peach Cloud simulators have no network";
5930
5941
  var HEADLESS_TENANT_FETCH_HUNG_MARKER = "headless tenant fetch hung";
5931
5942
 
5932
5943
  // cli/commands/inspect/core.ts
@@ -6493,7 +6504,7 @@ async function inspectFind(bridge, q) {
6493
6504
  }
6494
6505
  function waitReadyReason(status) {
6495
6506
  if (status.bridgeError) {
6496
- if (status.bridgeError.includes(HEADLESS_TENANT_FETCH_HUNG_MARKER) || status.bridgeError.includes(PUBLIC_NO_NETWORK_MARKER)) {
6507
+ if (status.bridgeError.includes(HEADLESS_TENANT_FETCH_HUNG_MARKER)) {
6497
6508
  return status.bridgeError;
6498
6509
  }
6499
6510
  return `sim unreachable: ${status.bridgeError} \u2014 the target tab is closed or no sim is connected (check \`peach list\`, reopen with \`peach open\`)`;
@@ -6743,6 +6754,17 @@ async function resolveTargetCoords(bridge, target) {
6743
6754
  }
6744
6755
 
6745
6756
  // cli/commands/inspect/settling.ts
6757
+ var RuntimeReadinessUnsupportedError = class extends Error {
6758
+ constructor(runtimeVersion, missingFields) {
6759
+ super(
6760
+ `runtime ${runtimeVersion} cannot report animation readiness (${missingFields.join(", ")} missing); run \`peach upgrade\` and reopen the simulator`
6761
+ );
6762
+ this.runtimeVersion = runtimeVersion;
6763
+ this.missingFields = missingFields;
6764
+ this.name = "RuntimeReadinessUnsupportedError";
6765
+ }
6766
+ code = "runtime_readiness_unsupported";
6767
+ };
6746
6768
  async function waitForSootsimIdle({
6747
6769
  bridge,
6748
6770
  simId,
@@ -6752,16 +6774,23 @@ async function waitForSootsimIdle({
6752
6774
  strict = false,
6753
6775
  paintStill = false
6754
6776
  }) {
6755
- const result = await bridge.send(
6756
- {
6757
- type: "evaluate",
6758
- simId,
6759
- code: `(async () => {
6777
+ let result;
6778
+ try {
6779
+ result = await bridge.send(
6780
+ {
6781
+ type: "evaluate",
6782
+ simId,
6783
+ code: `(async () => {
6760
6784
  const start = Date.now()
6761
6785
  const deadline = start + ${Math.max(0, Math.round(maxMs))}
6762
6786
  const pollMs = ${Math.max(1, Math.round(pollMs))}
6763
6787
  const strict = ${strict ? "true" : "false"}
6764
6788
  const paintStill = ${paintStill ? "true" : "false"}
6789
+ // full mounted-geometry observation needs at least two hierarchy reads
6790
+ // plus the stillness window, which a sub-second budget cannot afford:
6791
+ // tight budgets (auto-wait) settle on worker stillness plus root
6792
+ // membership, generous ones confirm the geometry too.
6793
+ const fullGeometry = ${maxMs >= 1e3 ? "true" : "false"}
6765
6794
  const requiredStillMs = paintStill ? 0 : ${Math.max(1, Math.round(stablePolls)) * Math.max(1, Math.round(pollMs))}
6766
6795
  const minimumStablePolls = paintStill ? 1 : ${Math.min(2, Math.max(1, Math.round(stablePolls)))}
6767
6796
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
@@ -6798,58 +6827,177 @@ async function waitForSootsimIdle({
6798
6827
  }
6799
6828
  } catch {}
6800
6829
 
6830
+ // a worker that cannot answer within the remaining budget is busy, not
6831
+ // idle: its poll reads as unsettled. bounding each read by the budget
6832
+ // keeps a busy worker from holding a poll past the deadline.
6833
+ const BUSY = { busy: true }
6834
+ const answer = (read) =>
6835
+ Promise.race([
6836
+ Promise.resolve().then(read),
6837
+ new Promise((resolve) =>
6838
+ setTimeout(() => resolve(BUSY), Math.max(deadline - Date.now(), 100)),
6839
+ ),
6840
+ ])
6841
+
6801
6842
  const readSnapshot = async () => {
6802
6843
  let animating = false
6803
6844
  let layoutDirty = false
6804
6845
  let pendingFetches = 0
6846
+ let workerBusy = false
6805
6847
  let requestTotal = 0
6806
6848
  let requestInFlight = 0
6849
+ let appCommits = null
6850
+ let nodeCount = null
6807
6851
  let renderStatsAvailable = false
6808
6852
  let shellAnimating = false
6853
+ let webViewStarting = false
6854
+ let webViewBlocker = ''
6809
6855
  // strict also waits out shell motion: a system alert, sheet or menu
6810
6856
  // animates in the shell worker while the app's own stats read still,
6811
6857
  // so its presentation is complete only once the shell's list empties.
6812
6858
  if (strict) {
6813
- try {
6814
- const motion = await window.__sootsimTest?.listAnimations?.()
6815
- shellAnimating = Array.isArray(motion) && motion.some((anim) => anim.realm === 'shell')
6816
- } catch {}
6859
+ const motion = await answer(() => window.__sootsimTest?.listAnimations?.()).catch(() => null)
6860
+ if (motion === BUSY) workerBusy = true
6861
+ else shellAnimating = Array.isArray(motion) && motion.some((anim) => anim.realm === 'shell' && anim.priority !== 'low')
6817
6862
  }
6818
- try {
6819
- const stats = await window.__sootsimRenderHost?.queryStats?.()
6820
- if (stats) {
6821
- renderStatsAvailable = true
6822
- animating =
6823
- stats.hasActiveAnims === true ||
6824
- stats.hasActiveNativeAnimations === true ||
6825
- stats.hasPendingAnimationFrames === true
6826
- layoutDirty =
6827
- stats.layoutDirty === true && stats.renderRequested === true
6828
- // a freshly-pushed screen showing a skeleton is layout-stable
6829
- // but not actually settled \u2014 its data/images are still in
6830
- // flight. treat bounded image-loader fetches as not-idle so
6831
- // settle waits for real content, capped by the budget.
6832
- const pf = stats.memory && stats.memory.imageLoader
6833
- ? stats.memory.imageLoader.pendingFetches
6834
- : 0
6835
- pendingFetches = typeof pf === 'number' ? pf : 0
6863
+ const stats = await answer(() => window.__sootsimRenderHost?.queryStats?.()).catch(() => null)
6864
+ if (stats === BUSY) {
6865
+ workerBusy = true
6866
+ } else if (stats) {
6867
+ renderStatsAvailable = true
6868
+ const missingFields = ['hasActiveBlockingNativeAnimations', 'hasPendingBlockingAnimationFrames']
6869
+ .filter((field) => typeof stats[field] !== 'boolean')
6870
+ if (strict && missingFields.length > 0) {
6871
+ return {
6872
+ runtimeUnsupported: {
6873
+ error: 'runtime_readiness_unsupported',
6874
+ runtimeVersion: window.__sootsimRuntimeVersion || 'unknown',
6875
+ missingFields,
6876
+ },
6877
+ }
6836
6878
  }
6837
- } catch {}
6879
+ // positive checks only: an older runtime without the blocking
6880
+ // fields reads as still, and strict rejects that runtime above
6881
+ // instead of ever settling on unknown motion.
6882
+ animating =
6883
+ stats.hasActiveAnims === true ||
6884
+ stats.hasActiveBlockingNativeAnimations === true ||
6885
+ stats.hasPendingBlockingAnimationFrames === true
6886
+ layoutDirty =
6887
+ stats.layoutDirty === true && stats.renderRequested === true
6888
+ // a freshly-pushed screen showing a skeleton is layout-stable
6889
+ // but not actually settled \u2014 its data/images are still in
6890
+ // flight. treat bounded image-loader fetches as not-idle so
6891
+ // settle waits for real content, capped by the budget.
6892
+ const pf = stats.memory && stats.memory.imageLoader
6893
+ ? stats.memory.imageLoader.pendingFetches
6894
+ : 0
6895
+ pendingFetches = typeof pf === 'number' ? pf : 0
6896
+ // tenant layout commits: a screen that keeps committing is still
6897
+ // mounting or remounting, even with no animation flag raised.
6898
+ appCommits = typeof stats.appCommitCount === 'number' ? stats.appCommitCount : null
6899
+ }
6900
+ // image preparation ends in the tenant before the compositor decodes
6901
+ // and realizes its pixels. both owners must finish before settling.
6902
+ // the compositor rejects a stats request it is too busy to answer.
6903
+ const compositor = await answer(() => window.__sootsimCompositor.getStats()).catch(() => BUSY)
6904
+ if (compositor === BUSY) {
6905
+ workerBusy = true
6906
+ } else {
6907
+ pendingFetches += compositor.images.pendingFetches
6908
+ pendingFetches += compositor.imageRealization.pending
6909
+ if (compositor.webViewCaptureSettled === false) {
6910
+ webViewStarting = true
6911
+ webViewBlocker = 'compositor webView capture not settled'
6912
+ }
6913
+ }
6838
6914
  try {
6839
6915
  const counts = await window.__sootsimTest?.getRequestCounts?.()
6840
6916
  requestTotal = typeof counts?.total === 'number' ? counts.total : 0
6841
6917
  requestInFlight = typeof counts?.inFlight === 'number' ? counts.inFlight : 0
6842
6918
  } catch {}
6843
- const root = window.__sootsimRoot
6919
+ // mounted-root membership: the live node count moves while the tree
6920
+ // mounts, unmounts or recycles, in either render mode.
6921
+ try {
6922
+ const total = await window.__sootsimTest?.getNodeCount?.()
6923
+ nodeCount = typeof total === 'number' ? total : null
6924
+ } catch {}
6925
+ // the mounted hierarchy in render-worker mode comes from the test
6926
+ // bridge: window.__sootsimRoot is an empty placeholder there, so a
6927
+ // walk over it hashes every screen to the same empty string while
6928
+ // it moves. listInspectable is the worker-backed read describe
6929
+ // uses; a worker too busy to answer it reads as unsettled. tight
6930
+ // budgets skip this read and settle on the cheaper signals above.
6931
+ let hierarchy = null
6932
+ if (fullGeometry && typeof window.__sootsimTest?.listInspectable === 'function') {
6933
+ try {
6934
+ hierarchy = await answer(() => window.__sootsimTest.listInspectable({}))
6935
+ } catch {
6936
+ hierarchy = null
6937
+ }
6938
+ }
6939
+ if (hierarchy === BUSY) {
6940
+ workerBusy = true
6941
+ hierarchy = null
6942
+ } else if (!Array.isArray(hierarchy)) {
6943
+ hierarchy = null
6944
+ }
6844
6945
  const nodes = []
6845
- if (root) {
6846
- const walk = (n) => {
6847
- if (n.layout && n.layout.width > 0) {
6848
- nodes.push(Math.round(n.layout.x) + ',' + Math.round(n.layout.y) + ',' + Math.round(n.layout.width))
6946
+ const PREVIEW_PROGRESS_REGEX = /^(creating webview|loading html|fetching bundle|waiting for first render|starting preview|loading simulator|fetching app code|waiting for native preview|starting design|loading canvas|fetching screens|painting the canvas|waiting for webview|waiting for preview|waiting for canvas|preview stalled)/i
6947
+ const notePreviewProgress = (tid, text) => {
6948
+ if (typeof tid === 'string' && (tid === 'web-preview-progress' || tid.endsWith('-preview-progress'))) {
6949
+ webViewStarting = true
6950
+ if (!webViewBlocker) webViewBlocker = 'preview progress active (' + tid + ')'
6951
+ }
6952
+ if (typeof text === 'string' && PREVIEW_PROGRESS_REGEX.test(text.trim())) {
6953
+ webViewStarting = true
6954
+ if (!webViewBlocker) webViewBlocker = 'webView startup in progress ("' + text.trim() + '")'
6955
+ }
6956
+ }
6957
+ if (hierarchy) {
6958
+ for (const n of hierarchy) {
6959
+ const layout = n && n.layout
6960
+ if (layout && layout.width > 0) {
6961
+ const abs = (n && (n.absolute || n.absolutePosition)) || layout
6962
+ nodes.push(Math.round(abs.x) + ',' + Math.round(abs.y) + ',' + Math.round(layout.width))
6963
+ }
6964
+ notePreviewProgress(
6965
+ n.testID || n.testId,
6966
+ typeof n.text === 'string' ? n.text : '',
6967
+ )
6968
+ }
6969
+ } else {
6970
+ const root = window.__sootsimRoot
6971
+ if (root) {
6972
+ const walk = (n) => {
6973
+ if (n.layout && n.layout.width > 0) {
6974
+ nodes.push(Math.round(n.layout.x) + ',' + Math.round(n.layout.y) + ',' + Math.round(n.layout.width))
6975
+ }
6976
+ notePreviewProgress(
6977
+ n.testID || n.props?.testID || n.props?.testId,
6978
+ n.textContent || (typeof n.props?.children === 'string' ? n.props.children : ''),
6979
+ )
6980
+ for (const c of n.children || []) walk(c)
6981
+ }
6982
+ walk(root)
6983
+ }
6984
+ }
6985
+ if (!webViewStarting && typeof document !== 'undefined') {
6986
+ const progressEl = document.querySelector(
6987
+ '[data-testid$="-preview-progress"], [data-testid="web-preview-progress"], [data-testid="ios-preview-progress"], [data-testid="design-preview-progress"]'
6988
+ )
6989
+ if (progressEl) {
6990
+ webViewStarting = true
6991
+ webViewBlocker = 'preview progress element displayed'
6992
+ } else {
6993
+ const unreadyWebView = document.querySelector(
6994
+ '[data-sootsim-webview-loading="true"], [data-sootsim-webview-mode="placeholder"]'
6995
+ )
6996
+ if (unreadyWebView) {
6997
+ webViewStarting = true
6998
+ webViewBlocker = 'webView document still loading'
6849
6999
  }
6850
- for (const c of n.children || []) walk(c)
6851
7000
  }
6852
- walk(root)
6853
7001
  }
6854
7002
  if (paintStill) {
6855
7003
  const canvas = composite.getCanvas()
@@ -6863,7 +7011,7 @@ async function waitForSootsimIdle({
6863
7011
  pixelFrameToken = token
6864
7012
  }
6865
7013
  }
6866
- return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, layoutDirty, pendingFetches, renderStatsAvailable, requestTotal, requestInFlight }
7014
+ return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, layoutDirty, pendingFetches, workerBusy, webViewStarting, webViewBlocker, renderStatsAvailable, requestTotal, requestInFlight, appCommits, nodeCount }
6867
7015
  }
6868
7016
 
6869
7017
  // how long generic background network is allowed to keep us waiting AFTER
@@ -6881,6 +7029,8 @@ async function waitForSootsimIdle({
6881
7029
  let lastScreenPixels = null
6882
7030
  let screenChangedPolls = 0
6883
7031
  let lastRequestTotal = -1
7032
+ let lastAppCommits = null
7033
+ let lastNodeCount = null
6884
7034
  let stable = 0
6885
7035
  let visuallyStillSince = 0
6886
7036
  // counts across the whole budget: a flag that is true on every poll is a
@@ -6890,6 +7040,7 @@ async function waitForSootsimIdle({
6890
7040
  let dirtyPolls = 0
6891
7041
  let animatingPolls = 0
6892
7042
  let layoutChangedPolls = 0
7043
+ let webViewStartingPolls = 0
6893
7044
  const screenUnchanged = (pixels) => {
6894
7045
  if (!pixels || !lastScreenPixels || pixels.width !== lastScreenPixels.width || pixels.height !== lastScreenPixels.height) return false
6895
7046
  if (pixels === lastScreenPixels) return true
@@ -6900,9 +7051,11 @@ async function waitForSootsimIdle({
6900
7051
  }
6901
7052
  while (Date.now() < deadline) {
6902
7053
  const snapshot = await readSnapshot()
7054
+ if (snapshot.runtimeUnsupported) return snapshot.runtimeUnsupported
6903
7055
  polls++
6904
7056
  if (snapshot.layoutDirty) dirtyPolls++
6905
7057
  if (snapshot.animating || snapshot.shellAnimating) animatingPolls++
7058
+ if (snapshot.webViewStarting) webViewStartingPolls++
6906
7059
  if (polls > 1 && snapshot.layout !== lastLayout) layoutChangedPolls++
6907
7060
  const screenChanged = paintStill && !screenUnchanged(snapshot.screenPixels)
6908
7061
  if (polls > 1 && screenChanged) screenChangedPolls++
@@ -6912,12 +7065,24 @@ async function waitForSootsimIdle({
6912
7065
  !snapshot.animating &&
6913
7066
  !snapshot.shellAnimating &&
6914
7067
  !snapshot.layoutDirty)
6915
- // visually settled: no animations, no image content loading, layout
6916
- // unchanged since the last poll.
7068
+ // visually settled: no worker motion or layout work, no new layout
7069
+ // commits, a stable mounted node count, no image content loading,
7070
+ // no webview startup in progress, layout unchanged since the last
7071
+ // poll. both modes wait out finite worker motion now; strict
7072
+ // additionally implies the flags through strictOk and adds shell
7073
+ // motion plus the blocking-stats floor. a null count or commit
7074
+ // reading means the runtime does not report it, which reads as
7075
+ // stable the way a missing stats block does.
6917
7076
  const visuallyStill =
6918
7077
  strictOk &&
7078
+ !snapshot.animating &&
7079
+ !snapshot.layoutDirty &&
7080
+ (snapshot.appCommits === null || lastAppCommits === null || snapshot.appCommits === lastAppCommits) &&
7081
+ (snapshot.nodeCount === null || lastNodeCount === null || snapshot.nodeCount === lastNodeCount) &&
6919
7082
  !screenChanged &&
6920
7083
  snapshot.pendingFetches === 0 &&
7084
+ !snapshot.workerBusy &&
7085
+ !snapshot.webViewStarting &&
6921
7086
  snapshot.layout === lastLayout
6922
7087
  // generic network quiet: nothing in flight and no new requests issued.
6923
7088
  const networkQuiet =
@@ -6942,15 +7107,22 @@ async function waitForSootsimIdle({
6942
7107
  lastLayout = snapshot.layout
6943
7108
  lastScreenPixels = snapshot.screenPixels
6944
7109
  lastRequestTotal = snapshot.requestTotal
7110
+ lastAppCommits = snapshot.appCommits
7111
+ lastNodeCount = snapshot.nodeCount
6945
7112
  await sleep(pollMs)
6946
7113
  }
6947
7114
  const last = await readSnapshot()
7115
+ if (last.runtimeUnsupported) return last.runtimeUnsupported
6948
7116
  const blockers = []
6949
7117
  if (strict && !last.renderStatsAvailable) blockers.push('render stats unavailable')
6950
7118
  if (last.animating) blockers.push('animations still running')
6951
7119
  if (last.shellAnimating) blockers.push('shell animations still running')
6952
7120
  if (last.layoutDirty) blockers.push('layout still dirty')
7121
+ if (last.appCommits !== null && lastAppCommits !== null && last.appCommits !== lastAppCommits) blockers.push('layout commits still landing')
7122
+ if (last.nodeCount !== null && lastNodeCount !== null && last.nodeCount !== lastNodeCount) blockers.push('node count still changing')
6953
7123
  if (last.pendingFetches > 0) blockers.push(last.pendingFetches + ' image fetches pending')
7124
+ if (last.workerBusy) blockers.push('a render worker too busy to report')
7125
+ if (last.webViewStarting) blockers.push(last.webViewBlocker || 'webView still starting')
6954
7126
  if (last.layout !== lastLayout) blockers.push('layout still changing')
6955
7127
  if (paintStill && !last.screenPixels) blockers.push('screen pixels unavailable')
6956
7128
  else if (paintStill && !screenUnchanged(last.screenPixels)) blockers.push('screen pixels still changing')
@@ -6963,17 +7135,31 @@ async function waitForSootsimIdle({
6963
7135
  blockedBy:
6964
7136
  (blockers.length ? blockers.join(', ') : 'unknown') +
6965
7137
  ' over ' + polls + ' polls (layout changed ' + layoutChangedPolls +
6966
- 'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x, screen changed ' + screenChangedPolls + 'x)',
7138
+ 'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x, webViewStarting ' + webViewStartingPolls + 'x, screen changed ' + screenChangedPolls + 'x)',
6967
7139
  }
6968
7140
  } finally {
6969
7141
  unsubscribe?.()
6970
7142
  }
6971
7143
  })()`
6972
- },
6973
- {
6974
- timeoutMs: maxMs + 1e3
6975
- }
6976
- );
7144
+ },
7145
+ {
7146
+ timeoutMs: Math.max(5e3, maxMs + 2e3)
7147
+ }
7148
+ );
7149
+ } catch (error) {
7150
+ const message = error instanceof Error ? error.message : String(error);
7151
+ return {
7152
+ settled: false,
7153
+ elapsed: maxMs,
7154
+ blockedBy: message
7155
+ };
7156
+ }
7157
+ if (result?.error === "runtime_readiness_unsupported" && typeof result.runtimeVersion === "string" && Array.isArray(result.missingFields) && result.missingFields.every((field) => typeof field === "string")) {
7158
+ throw new RuntimeReadinessUnsupportedError(
7159
+ result.runtimeVersion,
7160
+ result.missingFields
7161
+ );
7162
+ }
6977
7163
  const { elapsed, settled, blockedBy } = result ?? {};
6978
7164
  return {
6979
7165
  elapsed: typeof elapsed === "number" ? elapsed : maxMs,
@@ -7424,7 +7610,7 @@ function buildSimOpenUrl(currentUrl, target) {
7424
7610
  const url = new URL(currentUrl);
7425
7611
  for (const param of TARGET_SEARCH_PARAMS) url.searchParams.delete(param);
7426
7612
  const pathname = url.pathname.replace(/\/+$/, "") || "/";
7427
- const prefix2 = pathname === "/__soot" || pathname.startsWith("/__soot/") ? "/__soot" : "";
7613
+ const prefix2 = pathname === "/__peachsim" || pathname.startsWith("/__peachsim/") ? "/__peachsim" : "";
7428
7614
  const normalized = String(target).trim();
7429
7615
  if (/^\d+$/.test(normalized)) {
7430
7616
  url.pathname = `${prefix2}/rn/${normalized}`;
@@ -7870,26 +8056,44 @@ function isLiveRasterSource(node) {
7870
8056
  const source = node.props?.source;
7871
8057
  return node.type === "image" && !!source && typeof source === "object" && "uri" in source && typeof source.uri === "string" && source.uri.startsWith("live:");
7872
8058
  }
8059
+ function rasterLayerFrame(node) {
8060
+ const visible = node.visibleFrame;
8061
+ if (isLiveRasterSource(node) && visible && visible.width > 0 && visible.height > 0) {
8062
+ return { x: visible.x, y: visible.y, width: visible.width, height: visible.height };
8063
+ }
8064
+ return node.frame ?? {
8065
+ x: node.absolutePosition.x,
8066
+ y: node.absolutePosition.y,
8067
+ width: node.layout.width,
8068
+ height: node.layout.height
8069
+ };
8070
+ }
7873
8071
  function selectRnxRasterNodeIds(tree) {
7874
8072
  const nodesById = new Map(tree.nodes.map((node) => [node.nodeId, node]));
7875
8073
  const maxLayerArea = tree.width * tree.height * 0.25;
7876
8074
  const eligible = /* @__PURE__ */ new Map();
7877
8075
  for (const node of tree.nodes) {
7878
8076
  const liveVideo = isLiveRasterSource(node);
7879
- const visibleFrame = liveVideo && node.visibleFrame && (node.visibleFrame.width <= 0 || node.visibleFrame.height <= 0) ? node.frame : node.visibleFrame ?? node.frame;
8077
+ const visibleFrame = liveVideo ? rasterLayerFrame(node) : node.visibleFrame ?? node.frame;
7880
8078
  if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue;
8079
+ const glass = node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0 || node.style._liquidGlassMaterialTint !== void 0;
7881
8080
  const unsupported = node.type === "masked-view" || liveVideo || node.type === "text" && typeof node.text === "string" && (new RegExp("\\p{Emoji_Presentation}", "u").test(node.text) || new RegExp("\\uFE0F|\\u200D|\\u20E3|\\p{Emoji_Modifier}|\\p{Regional_Indicator}", "u").test(
7882
8081
  node.text
7883
- )) || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0 || node.style._liquidGlassMaterialTint !== void 0;
7884
- const area = visibleFrame.width * visibleFrame.height;
7885
- if (unsupported && (liveVideo || area <= maxLayerArea)) {
7886
- eligible.set(node.nodeId, { nodeId: node.nodeId, area, liveVideo });
8082
+ )) || glass;
8083
+ if (!unsupported) continue;
8084
+ const captured = rasterLayerFrame(node);
8085
+ const area = captured.width * captured.height;
8086
+ if (liveVideo || area <= maxLayerArea) {
8087
+ eligible.set(node.nodeId, { nodeId: node.nodeId, area, liveVideo, ownPaint: false });
8088
+ } else if (glass) {
8089
+ eligible.set(node.nodeId, { nodeId: node.nodeId, area, liveVideo, ownPaint: true });
7887
8090
  }
7888
8091
  }
7889
8092
  for (const nodeId of [...eligible.keys()]) {
7890
8093
  let parentNodeId = nodesById.get(nodeId)?.parentNodeId ?? null;
7891
8094
  while (parentNodeId !== null) {
7892
- if (eligible.has(parentNodeId)) {
8095
+ const parent = eligible.get(parentNodeId);
8096
+ if (parent && !parent.ownPaint) {
7893
8097
  eligible.delete(nodeId);
7894
8098
  break;
7895
8099
  }
@@ -7897,29 +8101,29 @@ function selectRnxRasterNodeIds(tree) {
7897
8101
  }
7898
8102
  }
7899
8103
  const candidates = [...eligible.values()].sort(
7900
- (left, right) => Number(right.liveVideo) - Number(left.liveVideo) || left.area - right.area || left.nodeId - right.nodeId
8104
+ (left, right) => Number(right.liveVideo) - Number(left.liveVideo) || Number(right.ownPaint) - Number(left.ownPaint) || left.area - right.area || left.nodeId - right.nodeId
7901
8105
  );
7902
8106
  const screenArea = tree.width * tree.height;
7903
- const selected = /* @__PURE__ */ new Set();
8107
+ const selection = { nodeIds: /* @__PURE__ */ new Set(), ownPaint: /* @__PURE__ */ new Set() };
7904
8108
  let atlasArea = 0;
8109
+ let layerArea = 0;
7905
8110
  for (const candidate of candidates) {
7906
- const maxAtlasArea = screenArea * (candidate.liveVideo ? 1 : 0.5);
7907
- if (atlasArea + candidate.area > maxAtlasArea) continue;
7908
- selected.add(candidate.nodeId);
8111
+ const leading = candidate.liveVideo || candidate.ownPaint;
8112
+ if (atlasArea + candidate.area > screenArea * (leading ? 1 : 1.25) || !leading && layerArea + candidate.area > screenArea * 0.5) {
8113
+ continue;
8114
+ }
8115
+ selection.nodeIds.add(candidate.nodeId);
8116
+ if (candidate.ownPaint) selection.ownPaint.add(candidate.nodeId);
7909
8117
  atlasArea += candidate.area;
8118
+ if (!leading) layerArea += candidate.area;
7910
8119
  }
7911
- return selected;
8120
+ return selection;
7912
8121
  }
7913
8122
  function rnxRasterFrames(tree, nodeIds) {
7914
8123
  const frames = /* @__PURE__ */ new Map();
7915
8124
  for (const node of tree.nodes) {
7916
8125
  if (!nodeIds.has(node.nodeId)) continue;
7917
- const frame = node.frame ?? {
7918
- x: node.absolutePosition.x,
7919
- y: node.absolutePosition.y,
7920
- width: node.layout.width,
7921
- height: node.layout.height
7922
- };
8126
+ const frame = rasterLayerFrame(node);
7923
8127
  const glassOutset = liquidGlassPaintOutset(node.style._liquidGlass, frame);
7924
8128
  const shadowRadius = typeof node.style.shadowRadius === "number" ? node.style.shadowRadius : 0;
7925
8129
  const shadowOffset = node.style.shadowOffset;
@@ -7993,11 +8197,22 @@ function rnxCaptureRasterRegions(capture) {
7993
8197
  visibleFrame: node.layout.visibleFrame
7994
8198
  }))
7995
8199
  };
7996
- const nodeIds = selectRnxRasterNodeIds(tree);
7997
- const frames = rnxRasterFrames(tree, nodeIds);
7998
- return [...nodeIds].flatMap((nodeId) => {
8200
+ const selection = selectRnxRasterNodeIds(tree);
8201
+ return rnxRasterRegionRequests(tree, selection);
8202
+ }
8203
+ function rnxRasterRegionRequests(tree, selection) {
8204
+ const frames = rnxRasterFrames(tree, selection.nodeIds);
8205
+ return [...selection.nodeIds].flatMap((nodeId) => {
7999
8206
  const frame = frames.get(nodeId);
8000
- return frame ? [{ key: String(nodeId), nodeId, frame }] : [];
8207
+ if (!frame) return [];
8208
+ return [
8209
+ {
8210
+ key: String(nodeId),
8211
+ nodeId,
8212
+ frame,
8213
+ ...selection.ownPaint.has(nodeId) ? { ownPaint: true } : {}
8214
+ }
8215
+ ];
8001
8216
  });
8002
8217
  }
8003
8218
  function packRnxCaptureRegions(regions, pixelRatio, surface) {
@@ -8018,6 +8233,7 @@ function packRnxCaptureRegions(regions, pixelRatio, surface) {
8018
8233
  return {
8019
8234
  key: region.key,
8020
8235
  nodeId: region.nodeId,
8236
+ ...region.ownPaint ? { ownPaint: true } : {},
8021
8237
  frame: {
8022
8238
  x: left / pixelRatio,
8023
8239
  y: top / pixelRatio,
@@ -8083,6 +8299,7 @@ function packRnxCaptureRegions(regions, pixelRatio, surface) {
8083
8299
  atlasRegions: best.regions.map((region) => ({
8084
8300
  key: region.key,
8085
8301
  nodeId: region.nodeId,
8302
+ ...region.ownPaint ? { ownPaint: true } : {},
8086
8303
  frame: region.frame,
8087
8304
  // a region flush with the far edge is anchored to the logical edge so
8088
8305
  // its source never runs past the atlas by a rounding hair
@@ -8159,7 +8376,7 @@ function isRnxCaptureRegionAtlas(value) {
8159
8376
  }
8160
8377
  const keys = /* @__PURE__ */ new Set();
8161
8378
  for (const region of value.regions) {
8162
- if (!captureRecord(region) || typeof region.key !== "string" || region.key.length === 0 || keys.has(region.key) || !captureInteger(region.nodeId) || region.nodeId <= 0 || !captureRect(region.frame) || region.frame.width <= 0 || region.frame.height <= 0 || !captureRect(region.source) || region.source.x < 0 || region.source.y < 0 || region.source.width !== region.frame.width || region.source.height !== region.frame.height || region.source.x + region.source.width > value.width || region.source.y + region.source.height > value.height) {
8379
+ if (!captureRecord(region) || typeof region.key !== "string" || region.key.length === 0 || keys.has(region.key) || !captureInteger(region.nodeId) || region.nodeId <= 0 || region.ownPaint !== void 0 && region.ownPaint !== true || !captureRect(region.frame) || region.frame.width <= 0 || region.frame.height <= 0 || !captureRect(region.source) || region.source.x < 0 || region.source.y < 0 || region.source.width !== region.frame.width || region.source.height !== region.frame.height || region.source.x + region.source.width > value.width || region.source.y + region.source.height > value.height) {
8163
8380
  return false;
8164
8381
  }
8165
8382
  keys.add(region.key);
@@ -8170,7 +8387,7 @@ function isRnxCaptureRegionAtlas(value) {
8170
8387
  // src/vite-plugin-one.ts
8171
8388
  var import_fs4 = __toESM(require("fs"), 1);
8172
8389
  var import_node_crypto5 = require("node:crypto");
8173
- var import_node_module2 = require("node:module");
8390
+ var import_node_url = require("node:url");
8174
8391
  var import_path3 = __toESM(require("path"), 1);
8175
8392
  var import_ws2 = require("ws");
8176
8393
 
@@ -8312,6 +8529,8 @@ var SWIFT_HMR_PATH = "/__peach/swift-hmr";
8312
8529
  var MAX_SWIFT_COMPILE_SOURCE_BYTES = 2 * 1024 * 1024;
8313
8530
  var MAX_SWIFT_COMPILE_RESOURCE_BYTES = 2 * 1024 * 1024;
8314
8531
  var MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES = 8 * 1024 * 1024;
8532
+ var MAX_SWIFT_COMPILE_METAL_BYTES = 2 * 1024 * 1024;
8533
+ var MAX_SWIFT_COMPILE_METAL_TOTAL_BYTES = 8 * 1024 * 1024;
8315
8534
  var MAX_SWIFT_COMPILE_IMAGE_BYTES = 4 * 1024 * 1024;
8316
8535
  var MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024;
8317
8536
 
@@ -8662,8 +8881,8 @@ var loadSwiftHost = () => swiftHost ??= import("peachsim/vite-swift");
8662
8881
  var KOTLIN_ROUTE_PREFIX = "/__peach/kotlin";
8663
8882
  var kotlinHost;
8664
8883
  var loadKotlinHost = () => kotlinHost ??= import("peachsim/kotlin-host");
8665
- function rnxPlugin(options = {}) {
8666
- const prefix2 = options.prefix || "/__soot";
8884
+ function peachPlugin(options = {}) {
8885
+ const prefix2 = options.prefix || "/__peachsim";
8667
8886
  return {
8668
8887
  name: "sootsim-one",
8669
8888
  // use-latest-callback ships a CJS main (module.exports, no default) with
@@ -8684,12 +8903,9 @@ function rnxPlugin(options = {}) {
8684
8903
  let swiftModuleRoot;
8685
8904
  const moduleRootFor = () => {
8686
8905
  if (swiftModuleRoot === void 0) {
8687
- const peachsimPackage = (0, import_node_module2.createRequire)(
8688
- import_path3.default.join(server.config.root, "package.json")
8689
- ).resolve("peachsim/package.json");
8690
8906
  const moduleRoot = import_path3.default.resolve(
8691
- import_path3.default.dirname(peachsimPackage),
8692
- "../sootsim-swift"
8907
+ import_path3.default.dirname((0, import_node_url.fileURLToPath)(__sootsim_import_meta_url)),
8908
+ "../../sootsim-swift"
8693
8909
  );
8694
8910
  swiftModuleRoot = import_fs4.default.existsSync(import_path3.default.join(moduleRoot, "toolchain.json")) ? moduleRoot : null;
8695
8911
  }
@@ -8776,7 +8992,7 @@ function rnxPlugin(options = {}) {
8776
8992
  if (!pathname.startsWith(KOTLIN_ROUTE_PREFIX)) return next();
8777
8993
  loadKotlinHost().then(async (host) => {
8778
8994
  if (!kotlinModuleRoot) {
8779
- kotlinModuleRoot = host.peachKotlinModuleRoot(server.config.root);
8995
+ kotlinModuleRoot = host.peachKotlinModuleRoot();
8780
8996
  server.watcher.add(import_path3.default.join(kotlinModuleRoot, "Sources"));
8781
8997
  }
8782
8998
  const served = await host.serveKotlinRequest(pathname, res, {
@@ -9009,6 +9225,101 @@ var import_path4 = __toESM(require("path"), 1);
9009
9225
  init_app_project();
9010
9226
  init_replacement_module_handler();
9011
9227
 
9228
+ // src/openstep-plist.ts
9229
+ var isDict = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9230
+ var str = (value) => typeof value === "string" ? value : void 0;
9231
+ var list = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
9232
+ function parseOpenStepPlist(text2) {
9233
+ let i = 0;
9234
+ const fail = (message) => {
9235
+ const line = text2.slice(0, i).split("\n").length;
9236
+ throw new Error(`project.pbxproj line ${line}: ${message}`);
9237
+ };
9238
+ const skip = () => {
9239
+ for (; ; ) {
9240
+ const ch = text2[i];
9241
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") i++;
9242
+ else if (ch === "/" && text2[i + 1] === "*") {
9243
+ const end = text2.indexOf("*/", i + 2);
9244
+ if (end < 0) fail("unterminated comment");
9245
+ i = end + 2;
9246
+ } else if (ch === "/" && text2[i + 1] === "/") {
9247
+ const end = text2.indexOf("\n", i);
9248
+ i = end < 0 ? text2.length : end + 1;
9249
+ } else return;
9250
+ }
9251
+ };
9252
+ const quoted = () => {
9253
+ i++;
9254
+ let out = "";
9255
+ for (; ; ) {
9256
+ const ch = text2[i++];
9257
+ if (ch === void 0) fail("unterminated string");
9258
+ if (ch === '"') return out;
9259
+ if (ch !== "\\") {
9260
+ out += ch;
9261
+ continue;
9262
+ }
9263
+ const next = text2[i++];
9264
+ if (next === "n") out += "\n";
9265
+ else if (next === "t") out += " ";
9266
+ else if (next === "r") out += "\r";
9267
+ else if (next === "U") {
9268
+ out += String.fromCharCode(parseInt(text2.slice(i, i + 4), 16));
9269
+ i += 4;
9270
+ } else out += next;
9271
+ }
9272
+ };
9273
+ const value = () => {
9274
+ skip();
9275
+ const ch = text2[i];
9276
+ if (ch === "{") {
9277
+ i++;
9278
+ const dict = {};
9279
+ for (; ; ) {
9280
+ skip();
9281
+ if (text2[i] === "}") {
9282
+ i++;
9283
+ return dict;
9284
+ }
9285
+ const key = value();
9286
+ if (typeof key !== "string") fail("dictionary key is not a string");
9287
+ skip();
9288
+ if (text2[i++] !== "=") fail("expected =");
9289
+ if (Object.hasOwn(dict, key)) fail(`duplicate key ${key}`);
9290
+ dict[key] = value();
9291
+ skip();
9292
+ if (text2[i++] !== ";") fail("expected ;");
9293
+ }
9294
+ }
9295
+ if (ch === "(") {
9296
+ i++;
9297
+ const list2 = [];
9298
+ for (; ; ) {
9299
+ skip();
9300
+ if (text2[i] === ")") {
9301
+ i++;
9302
+ return list2;
9303
+ }
9304
+ list2.push(value());
9305
+ skip();
9306
+ if (text2[i] === ",") i++;
9307
+ else if (text2[i] !== ")") fail("expected , or )");
9308
+ }
9309
+ }
9310
+ if (ch === '"') return quoted();
9311
+ const start = i;
9312
+ while (i < text2.length && /[A-Za-z0-9_$+/:.\-<>]/.test(text2[i])) i++;
9313
+ if (i === start) fail(`unexpected ${JSON.stringify(ch ?? "end of file")}`);
9314
+ return text2.slice(start, i);
9315
+ };
9316
+ if (text2.startsWith("// !$*UTF8*$!")) i = text2.indexOf("\n") + 1;
9317
+ const root = value();
9318
+ skip();
9319
+ if (i !== text2.length) fail("trailing content");
9320
+ return root;
9321
+ }
9322
+
9012
9323
  // src/native-app-identity.ts
9013
9324
  var NATIVE_APP_IDENTITY_PREFIX = "globalThis.__sootsimNativeAppIdentity=";
9014
9325
  var METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -9019,7 +9330,9 @@ function nonEmptyString(value) {
9019
9330
  return typeof value === "string" && value.length > 0 ? value : null;
9020
9331
  }
9021
9332
  function createNativeAppIdentityFooter(identity) {
9022
- const payload = JSON.stringify(identity).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
9333
+ const footerIdentity = { ...identity };
9334
+ delete footerIdentity.oneUpdates;
9335
+ const payload = JSON.stringify(footerIdentity).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
9023
9336
  return `
9024
9337
  ${NATIVE_APP_IDENTITY_PREFIX}${payload};`;
9025
9338
  }
@@ -9120,30 +9433,119 @@ function appTargetReleaseSettings(pbxproj, targetName, infoPlistRelativePath) {
9120
9433
  (candidate) => candidate.name === "Release" && named(candidate.settings)
9121
9434
  )?.settings ?? null;
9122
9435
  }
9123
- function nativeAppIdentityFromIos(files, parseInfoPlist) {
9436
+ function nativeAppIdentityFromIos(files, parseInfoPlist, iosTarget) {
9124
9437
  const pbxprojPath = Object.keys(files).find(
9125
- (path16) => /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path16)
9126
- );
9438
+ (path17) => /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path17)
9439
+ ) ?? Object.keys(files).find(
9440
+ (path17) => /(?:^|\/)[^/]+\.xcodeproj\/project\.pbxproj$/.test(path17)
9441
+ ) ?? Object.keys(files).find((path17) => /(?:^|\/)project\.pbxproj$/.test(path17));
9127
9442
  if (!pbxprojPath) return null;
9128
9443
  const pbxproj = files[pbxprojPath];
9129
- const applicationTargets = new Set(
9130
- [
9131
- ...pbxproj.matchAll(
9132
- /isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g
9133
- )
9134
- ].map((match) => match[1])
9444
+ let objects = {};
9445
+ try {
9446
+ const parsed = parseOpenStepPlist(pbxproj);
9447
+ if (isRecord2(parsed)) {
9448
+ objects = isRecord2(parsed.objects) ? parsed.objects : parsed;
9449
+ }
9450
+ } catch {
9451
+ }
9452
+ const targets = [];
9453
+ for (const [id, obj] of Object.entries(objects)) {
9454
+ if (isRecord2(obj) && obj.isa === "PBXNativeTarget" && typeof obj.name === "string") {
9455
+ targets.push({
9456
+ id,
9457
+ name: obj.name,
9458
+ productType: typeof obj.productType === "string" ? obj.productType : void 0,
9459
+ buildConfigurationList: typeof obj.buildConfigurationList === "string" ? obj.buildConfigurationList : void 0
9460
+ });
9461
+ }
9462
+ }
9463
+ const applicationTargetNames = new Set(
9464
+ targets.filter((t) => t.productType === "com.apple.product-type.application").map((t) => t.name)
9135
9465
  );
9136
- const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/;
9137
- const targetName = Object.keys(files).filter((path16) => !path16.includes("/Pods/")).map((path16) => path16.match(appDelegatePattern)?.[1]).find((name2) => name2 !== void 0 && applicationTargets.has(name2)) ?? null;
9138
- if (!targetName) return null;
9139
- const plist3 = files[`ios/${targetName}/Info.plist`];
9466
+ if (applicationTargetNames.size === 0) {
9467
+ for (const match of pbxproj.matchAll(
9468
+ /isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g
9469
+ )) {
9470
+ applicationTargetNames.add(match[1]);
9471
+ }
9472
+ }
9473
+ let targetName = null;
9474
+ if (iosTarget) {
9475
+ if (applicationTargetNames.has(iosTarget)) {
9476
+ targetName = iosTarget;
9477
+ } else {
9478
+ return null;
9479
+ }
9480
+ } else if (applicationTargetNames.size === 1) {
9481
+ targetName = [...applicationTargetNames][0];
9482
+ } else if (applicationTargetNames.size > 1) {
9483
+ const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/;
9484
+ const delegateTargets = Object.keys(files).filter((path17) => !path17.includes("/Pods/")).map((path17) => path17.match(appDelegatePattern)?.[1]).filter(
9485
+ (name2) => typeof name2 === "string" && applicationTargetNames.has(name2)
9486
+ );
9487
+ if (delegateTargets.length === 1) {
9488
+ targetName = delegateTargets[0];
9489
+ } else {
9490
+ return null;
9491
+ }
9492
+ } else {
9493
+ return null;
9494
+ }
9495
+ const selectedTarget = targets.find((t) => t.name === targetName);
9496
+ const settings = {
9497
+ TARGET_NAME: targetName,
9498
+ PRODUCT_NAME: targetName
9499
+ };
9500
+ for (const obj of Object.values(objects)) {
9501
+ if (isRecord2(obj) && obj.isa === "PBXProject" && typeof obj.buildConfigurationList === "string") {
9502
+ const listObj = objects[obj.buildConfigurationList];
9503
+ if (isRecord2(listObj) && Array.isArray(listObj.buildConfigurations)) {
9504
+ for (const configId of listObj.buildConfigurations) {
9505
+ if (typeof configId === "string") {
9506
+ const configObj = objects[configId];
9507
+ if (isRecord2(configObj) && configObj.name === "Release" && isRecord2(configObj.buildSettings)) {
9508
+ for (const [k, v] of Object.entries(configObj.buildSettings)) {
9509
+ if (typeof v === "string") settings[k] = v;
9510
+ }
9511
+ }
9512
+ }
9513
+ }
9514
+ }
9515
+ }
9516
+ }
9517
+ if (selectedTarget?.buildConfigurationList) {
9518
+ const listObj = objects[selectedTarget.buildConfigurationList];
9519
+ if (isRecord2(listObj) && Array.isArray(listObj.buildConfigurations)) {
9520
+ for (const configId of listObj.buildConfigurations) {
9521
+ if (typeof configId === "string") {
9522
+ const configObj = objects[configId];
9523
+ if (isRecord2(configObj) && configObj.name === "Release" && isRecord2(configObj.buildSettings)) {
9524
+ for (const [k, v] of Object.entries(configObj.buildSettings)) {
9525
+ if (typeof v === "string") settings[k] = v;
9526
+ }
9527
+ }
9528
+ }
9529
+ }
9530
+ }
9531
+ } else {
9532
+ const legacy = appTargetReleaseSettings(
9533
+ pbxproj,
9534
+ targetName,
9535
+ `${targetName}/Info.plist`
9536
+ );
9537
+ if (legacy) Object.assign(settings, legacy);
9538
+ }
9539
+ let plist3;
9540
+ const infoPlistPath = settings.INFOPLIST_FILE;
9541
+ if (infoPlistPath) {
9542
+ const normalized = infoPlistPath.replace(/^"|"$/g, "");
9543
+ plist3 = files[`ios/${normalized}`] ?? files[normalized] ?? Object.entries(files).find(([k]) => k.endsWith(`/${normalized}`))?.[1];
9544
+ }
9545
+ if (!plist3) {
9546
+ plist3 = files[`ios/${targetName}/Info.plist`] ?? files[`${targetName}/Info.plist`] ?? Object.entries(files).find(([k]) => k.endsWith(`/${targetName}/Info.plist`))?.[1];
9547
+ }
9140
9548
  if (!plist3) return null;
9141
- const settings = appTargetReleaseSettings(
9142
- pbxproj,
9143
- targetName,
9144
- `${targetName}/Info.plist`
9145
- );
9146
- if (!settings) return null;
9147
9549
  const bundleIdentifier = identityFieldFrom(
9148
9550
  settings,
9149
9551
  plistString(plist3, "CFBundleIdentifier"),
@@ -9170,8 +9572,57 @@ function nativeAppIdentityFromIos(files, parseInfoPlist) {
9170
9572
  const applicationName = identityFieldFrom(settings, plistString(plist3, "CFBundleDisplayName"), void 0) ?? bundleName;
9171
9573
  const dictionary = parseInfoPlist(plist3);
9172
9574
  if (!isRecord2(dictionary)) throw new Error("native app Info.plist is not a dictionary");
9575
+ const declaredDisplayName = dictionary.CFBundleDisplayName;
9576
+ const applicationDisplayName = typeof declaredDisplayName === "string" ? resolveBuildSetting(declaredDisplayName, settings) : void 0;
9577
+ if (applicationDisplayName === null)
9578
+ throw new Error("native app Info.plist CFBundleDisplayName is unresolved");
9579
+ const declaredBackgroundColor = dictionary.RCTRootViewBackgroundColor;
9580
+ const rootViewBackgroundColor = typeof declaredBackgroundColor === "number" && Number.isInteger(declaredBackgroundColor) ? declaredBackgroundColor : void 0;
9581
+ const icons = dictionary.CFBundleIcons;
9582
+ const alternates = isRecord2(icons) ? icons.CFBundleAlternateIcons : void 0;
9583
+ const alternateIconNames = /* @__PURE__ */ new Set();
9584
+ for (const name2 of isRecord2(alternates) ? Object.keys(alternates) : []) {
9585
+ const resolved = resolveBuildSetting(name2, settings);
9586
+ if (resolved === null) throw new Error("native alternate icon name is unresolved");
9587
+ alternateIconNames.add(resolved);
9588
+ }
9589
+ const alternateSetting = resolveBuildSetting(
9590
+ settings.ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES ?? "",
9591
+ settings
9592
+ );
9593
+ if (alternateSetting === null)
9594
+ throw new Error("native alternate icon setting is unresolved");
9595
+ for (const match of alternateSetting.replace(/\\"/g, '"').matchAll(/"([^"]*)"|([^\s(),]+)/g)) {
9596
+ alternateIconNames.add(match[1] ?? match[2]);
9597
+ }
9173
9598
  const intentIds = dictionary.OneAppIntentIdentifiers;
9174
9599
  const appIntentIdentifiers = Array.isArray(intentIds) && intentIds.every((value) => typeof value === "string") ? intentIds.map((value) => resolveBuildSetting(value, settings)) : [];
9600
+ const backgroundIds = dictionary.BGTaskSchedulerPermittedIdentifiers;
9601
+ const backgroundTaskIdentifiers = Array.isArray(backgroundIds) && backgroundIds.every((value) => typeof value === "string") ? backgroundIds.map((value) => resolveBuildSetting(value, settings)) : [];
9602
+ const declaredModes = dictionary.UIBackgroundModes;
9603
+ const backgroundModes = Array.isArray(declaredModes) && declaredModes.every((value) => typeof value === "string") ? declaredModes.map((value) => resolveBuildSetting(value, settings)) : [];
9604
+ const screenOrientationLists = [
9605
+ "UISupportedInterfaceOrientations",
9606
+ "UISupportedInterfaceOrientations~ipad"
9607
+ ].map((key) => {
9608
+ const list2 = dictionary[key];
9609
+ if (!Array.isArray(list2) || !list2.every((value) => typeof value === "string"))
9610
+ return [];
9611
+ return list2.map((value) => {
9612
+ const resolved = resolveBuildSetting(value, settings);
9613
+ if (resolved === null) throw new Error(`native app Info.plist ${key} is unresolved`);
9614
+ return resolved;
9615
+ });
9616
+ });
9617
+ const [supportedInterfaceOrientations, ipadSupportedInterfaceOrientations] = screenOrientationLists;
9618
+ const declaredDefaultOrientationMask = dictionary.EXDefaultScreenOrientationMask;
9619
+ const defaultScreenOrientationMask = typeof declaredDefaultOrientationMask === "string" ? resolveBuildSetting(declaredDefaultOrientationMask, settings) : void 0;
9620
+ if (defaultScreenOrientationMask === null)
9621
+ throw new Error("native app Info.plist EXDefaultScreenOrientationMask is unresolved");
9622
+ const declaredShareGroup = dictionary.ExpoShareIntoAppGroupId;
9623
+ const shareIntoAppGroupId = typeof declaredShareGroup === "string" ? resolveBuildSetting(declaredShareGroup, settings) : void 0;
9624
+ if (shareIntoAppGroupId === null)
9625
+ throw new Error("native app Info.plist ExpoShareIntoAppGroupId is unresolved");
9175
9626
  const querySchemes = dictionary.LSApplicationQueriesSchemes;
9176
9627
  if (querySchemes !== void 0 && !Array.isArray(querySchemes)) {
9177
9628
  throw new Error("native app Info.plist LSApplicationQueriesSchemes is not an array");
@@ -9185,7 +9636,8 @@ function nativeAppIdentityFromIos(files, parseInfoPlist) {
9185
9636
  });
9186
9637
  const permissionUsageDescriptions = {};
9187
9638
  for (const [key, value] of Object.entries(dictionary)) {
9188
- if (!/^NS.*UsageDescription$/.test(key)) continue;
9639
+ if (!/^NS.*UsageDescription$/.test(key) && key !== "NSLocationTemporaryUsageDescriptionDictionary")
9640
+ continue;
9189
9641
  if (typeof value !== "string") {
9190
9642
  permissionUsageDescriptions[key] = value;
9191
9643
  continue;
@@ -9206,28 +9658,35 @@ function nativeAppIdentityFromIos(files, parseInfoPlist) {
9206
9658
  }
9207
9659
  return {
9208
9660
  applicationName,
9661
+ ...applicationDisplayName === void 0 ? {} : { applicationDisplayName },
9662
+ ...rootViewBackgroundColor === void 0 ? {} : { rootViewBackgroundColor },
9209
9663
  bundleName,
9210
9664
  appVersion,
9211
9665
  buildVersion,
9212
9666
  bundleIdentifier,
9213
9667
  platform: "ios",
9668
+ supportedInterfaceOrientations,
9669
+ ipadSupportedInterfaceOrientations,
9670
+ ...defaultScreenOrientationMask === void 0 ? {} : { defaultScreenOrientationMask },
9671
+ alternateIconNames: [...alternateIconNames],
9214
9672
  permissionUsageDescriptions,
9215
9673
  urlQuerySchemes,
9674
+ ...shareIntoAppGroupId === void 0 ? {} : { shareIntoAppGroupId },
9675
+ backgroundTaskIdentifiers: backgroundTaskIdentifiers.every((value) => value !== null) ? backgroundTaskIdentifiers : [],
9676
+ backgroundModes: backgroundModes.every((value) => value !== null) ? backgroundModes : [],
9216
9677
  appIntentIdentifiers: appIntentIdentifiers.every((value) => value !== null) ? appIntentIdentifiers : []
9217
9678
  };
9218
9679
  }
9219
9680
  function canExtractNativeAppIdentity(platform) {
9220
9681
  return platform === "ios";
9221
9682
  }
9222
- function extractNativeAppIdentityFromFiles(files, platform, parseInfoPlist) {
9223
- return canExtractNativeAppIdentity(platform) ? nativeAppIdentityFromIos(files, parseInfoPlist) : null;
9683
+ function extractNativeAppIdentityFromFiles(files, platform, parseInfoPlist, iosTarget) {
9684
+ return canExtractNativeAppIdentity(platform) ? nativeAppIdentityFromIos(files, parseInfoPlist, iosTarget) : null;
9224
9685
  }
9225
9686
 
9226
- // src/native-firebase-config.ts
9687
+ // src/native-app-identity-project.ts
9227
9688
  var import_node_fs10 = __toESM(require("node:fs"), 1);
9228
- var import_node_module3 = require("node:module");
9229
9689
  var import_node_path11 = __toESM(require("node:path"), 1);
9230
- init_is_record();
9231
9690
 
9232
9691
  // src/plist.ts
9233
9692
  var import_plist = __toESM(require_plist(), 1);
@@ -9574,98 +10033,6 @@ function isJsonObject(value) {
9574
10033
  }
9575
10034
 
9576
10035
  // src/xcode-project.ts
9577
- function parseOpenStepPlist(text2) {
9578
- let i = 0;
9579
- const fail = (message) => {
9580
- const line = text2.slice(0, i).split("\n").length;
9581
- throw new Error(`project.pbxproj line ${line}: ${message}`);
9582
- };
9583
- const skip = () => {
9584
- for (; ; ) {
9585
- const ch = text2[i];
9586
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") i++;
9587
- else if (ch === "/" && text2[i + 1] === "*") {
9588
- const end = text2.indexOf("*/", i + 2);
9589
- if (end < 0) fail("unterminated comment");
9590
- i = end + 2;
9591
- } else if (ch === "/" && text2[i + 1] === "/") {
9592
- const end = text2.indexOf("\n", i);
9593
- i = end < 0 ? text2.length : end + 1;
9594
- } else return;
9595
- }
9596
- };
9597
- const quoted = () => {
9598
- i++;
9599
- let out = "";
9600
- for (; ; ) {
9601
- const ch = text2[i++];
9602
- if (ch === void 0) fail("unterminated string");
9603
- if (ch === '"') return out;
9604
- if (ch !== "\\") {
9605
- out += ch;
9606
- continue;
9607
- }
9608
- const next = text2[i++];
9609
- if (next === "n") out += "\n";
9610
- else if (next === "t") out += " ";
9611
- else if (next === "r") out += "\r";
9612
- else if (next === "U") {
9613
- out += String.fromCharCode(parseInt(text2.slice(i, i + 4), 16));
9614
- i += 4;
9615
- } else out += next;
9616
- }
9617
- };
9618
- const value = () => {
9619
- skip();
9620
- const ch = text2[i];
9621
- if (ch === "{") {
9622
- i++;
9623
- const dict = {};
9624
- for (; ; ) {
9625
- skip();
9626
- if (text2[i] === "}") {
9627
- i++;
9628
- return dict;
9629
- }
9630
- const key = value();
9631
- if (typeof key !== "string") fail("dictionary key is not a string");
9632
- skip();
9633
- if (text2[i++] !== "=") fail("expected =");
9634
- dict[key] = value();
9635
- skip();
9636
- if (text2[i++] !== ";") fail("expected ;");
9637
- }
9638
- }
9639
- if (ch === "(") {
9640
- i++;
9641
- const list2 = [];
9642
- for (; ; ) {
9643
- skip();
9644
- if (text2[i] === ")") {
9645
- i++;
9646
- return list2;
9647
- }
9648
- list2.push(value());
9649
- skip();
9650
- if (text2[i] === ",") i++;
9651
- else if (text2[i] !== ")") fail("expected , or )");
9652
- }
9653
- }
9654
- if (ch === '"') return quoted();
9655
- const start = i;
9656
- while (i < text2.length && /[A-Za-z0-9_$+/:.\-<>]/.test(text2[i])) i++;
9657
- if (i === start) fail(`unexpected ${JSON.stringify(ch ?? "end of file")}`);
9658
- return text2.slice(start, i);
9659
- };
9660
- if (text2.startsWith("// !$*UTF8*$!")) i = text2.indexOf("\n") + 1;
9661
- const root = value();
9662
- skip();
9663
- if (i !== text2.length) fail("trailing content");
9664
- return root;
9665
- }
9666
- var isDict = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9667
- var str = (value) => typeof value === "string" ? value : void 0;
9668
- var list = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
9669
10036
  var APPLICATION = "com.apple.product-type.application";
9670
10037
  var MODULE_PRODUCTS = /* @__PURE__ */ new Set([
9671
10038
  "com.apple.product-type.framework",
@@ -9692,9 +10059,9 @@ function walkFiles(dir) {
9692
10059
  return out;
9693
10060
  }
9694
10061
  var isDirectory = (dir) => import_node_fs9.default.existsSync(dir) && import_node_fs9.default.statSync(dir).isDirectory();
9695
- function readXcconfig(file, seen = /* @__PURE__ */ new Set()) {
9696
- if (seen.has(file) || !import_node_fs9.default.existsSync(file)) return {};
9697
- seen.add(file);
10062
+ function readXcconfig(file, read) {
10063
+ if (read.includes(file) || !import_node_fs9.default.existsSync(file)) return {};
10064
+ read.push(file);
9698
10065
  const settings = {};
9699
10066
  for (const raw of import_node_fs9.default.readFileSync(file, "utf8").split("\n")) {
9700
10067
  const line = raw.replace(/\/\/.*$/, "").trim();
@@ -9702,7 +10069,7 @@ function readXcconfig(file, seen = /* @__PURE__ */ new Set()) {
9702
10069
  if (include) {
9703
10070
  Object.assign(
9704
10071
  settings,
9705
- readXcconfig(import_node_path10.default.resolve(import_node_path10.default.dirname(file), include[1]), seen)
10072
+ readXcconfig(import_node_path10.default.resolve(import_node_path10.default.dirname(file), include[1]), read)
9706
10073
  );
9707
10074
  continue;
9708
10075
  }
@@ -9850,17 +10217,30 @@ function loadProject(xcodeproj) {
9850
10217
  loaded.set(projectFile, { mtimeMs, project: result });
9851
10218
  return result;
9852
10219
  }
9853
- function targetSettings(ref, builtins) {
10220
+ function defaultConfiguration(project) {
10221
+ const configList = project.object(str(project.project.buildConfigurationList));
10222
+ const names = list(configList?.buildConfigurations).flatMap(
10223
+ (id) => str(project.object(id)?.name) ?? []
10224
+ );
10225
+ return names.includes("Debug") ? "Debug" : str(configList?.defaultConfigurationName) ?? names[0] ?? "Debug";
10226
+ }
10227
+ function targetSettings(ref, configuration, builtins) {
9854
10228
  const { project, target } = ref;
10229
+ const xcconfigs = [];
9855
10230
  const level = (listId) => {
9856
- const configs2 = list(project.object(listId)?.buildConfigurations).map(project.object);
9857
- const config = configs2.find((entry) => entry?.name === "Debug") ?? configs2.find(Boolean);
10231
+ const configList = project.object(listId);
10232
+ const configs2 = list(configList?.buildConfigurations).map(project.object);
10233
+ const config = configs2.find((entry) => entry?.name === configuration) ?? configs2.find((entry) => entry?.name === str(configList?.defaultConfigurationName));
9858
10234
  if (!config) return [];
9859
- const name2 = str(config.name) ?? "Debug";
10235
+ const name2 = str(config.name) ?? configuration;
9860
10236
  const base = str(config.baseConfigurationReference);
9861
10237
  const own = isDict(config.buildSettings) ? config.buildSettings : {};
10238
+ const baseFile = base ? project.resolvePath(base) : null;
10239
+ const read = [];
10240
+ const fromFile = baseFile ? readXcconfig(baseFile, read) : {};
10241
+ xcconfigs.push(...read);
9862
10242
  return [
9863
- applyConditions(base ? readXcconfig(project.resolvePath(base) ?? "") : {}, name2),
10243
+ applyConditions(fromFile, name2),
9864
10244
  applyConditions(
9865
10245
  Object.fromEntries(
9866
10246
  Object.entries(own).flatMap(
@@ -9872,7 +10252,7 @@ function targetSettings(ref, builtins) {
9872
10252
  ];
9873
10253
  };
9874
10254
  const levels = [
9875
- builtins,
10255
+ { ...builtins, CONFIGURATION: configuration },
9876
10256
  ...level(str(project.project.buildConfigurationList)),
9877
10257
  ...level(str(target.buildConfigurationList))
9878
10258
  ];
@@ -9911,9 +10291,30 @@ function targetSettings(ref, builtins) {
9911
10291
  };
9912
10292
  return {
9913
10293
  get: (key) => valueAt(key, levels.length - 1),
9914
- keys: new Set(levels.flatMap((values) => Object.keys(values)))
10294
+ keys: new Set(levels.flatMap((values) => Object.keys(values))),
10295
+ xcconfigs
9915
10296
  };
9916
10297
  }
10298
+ function settingList(value) {
10299
+ const items2 = [];
10300
+ for (const [, quoted, plain] of value.matchAll(
10301
+ /"((?:\\.|[^"\\])*)"|((?:\\.|[^\s"])+)/g
10302
+ ))
10303
+ items2.push((quoted ?? plain).replace(/\\(.)/g, "$1"));
10304
+ return items2;
10305
+ }
10306
+ function compilationConditions(settings) {
10307
+ const flags = settingList(settings("OTHER_SWIFT_FLAGS"));
10308
+ const defined = flags.flatMap(
10309
+ (flag, index) => flag === "-D" ? flags[index + 1] ?? [] : flag.startsWith("-D") ? flag.slice(2) : []
10310
+ );
10311
+ return [
10312
+ .../* @__PURE__ */ new Set([
10313
+ ...settingList(settings("SWIFT_ACTIVE_COMPILATION_CONDITIONS")),
10314
+ ...defined
10315
+ ])
10316
+ ];
10317
+ }
9917
10318
  function expand(value, lookup) {
9918
10319
  return value.replace(
9919
10320
  /\$(?:\(([A-Za-z_][A-Za-z0-9_]*)((?::[^)]*)?)\)|\{([A-Za-z_][A-Za-z0-9_]*)((?::[^}]*)?)\}|([A-Za-z_][A-Za-z0-9_]*))/g,
@@ -9924,8 +10325,9 @@ function expand(value, lookup) {
9924
10325
  }
9925
10326
  );
9926
10327
  }
9927
- function targetMembers(ref) {
10328
+ function targetMembers(ref, settings, options = {}) {
9928
10329
  const { project, target, id: targetId } = ref;
10330
+ const targetName = str(target.name) ?? targetId;
9929
10331
  const sources = /* @__PURE__ */ new Set();
9930
10332
  const allSources = /* @__PURE__ */ new Set();
9931
10333
  const resources = /* @__PURE__ */ new Set();
@@ -9946,6 +10348,29 @@ function targetMembers(ref) {
9946
10348
  const resolved = project.resolvePath(refId);
9947
10349
  return resolved ? [resolved] : [];
9948
10350
  };
10351
+ const scriptForOutput = /* @__PURE__ */ new Map();
10352
+ const noteScriptOutput = (raw, script) => {
10353
+ const expanded = expand(raw, settings);
10354
+ if (!expanded.endsWith(".swift")) return;
10355
+ const absolute = import_node_path10.default.isAbsolute(expanded) ? import_node_path10.default.normalize(expanded) : import_node_path10.default.resolve(project.projectDir, expanded);
10356
+ if (!scriptForOutput.has(absolute)) scriptForOutput.set(absolute, script);
10357
+ };
10358
+ for (const phaseId of list(target.buildPhases)) {
10359
+ const phase = project.object(phaseId);
10360
+ if (phase?.isa !== "PBXShellScriptBuildPhase") continue;
10361
+ const script = str(phase.name) ?? phaseId;
10362
+ for (const raw of list(phase.outputPaths)) noteScriptOutput(raw, script);
10363
+ for (const listPath of list(phase.outputFileListPaths)) {
10364
+ const expandedList = expand(listPath, settings);
10365
+ const absoluteList = import_node_path10.default.isAbsolute(expandedList) ? expandedList : import_node_path10.default.resolve(project.projectDir, expandedList);
10366
+ if (!import_node_fs9.default.existsSync(absoluteList)) continue;
10367
+ for (const line of import_node_fs9.default.readFileSync(absoluteList, "utf8").split("\n")) {
10368
+ const trimmed = line.trim();
10369
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
10370
+ noteScriptOutput(trimmed, script);
10371
+ }
10372
+ }
10373
+ }
9949
10374
  for (const phaseId of list(target.buildPhases)) {
9950
10375
  const phase = project.object(phaseId);
9951
10376
  if (phase?.isa !== "PBXSourcesBuildPhase" && phase?.isa !== "PBXResourcesBuildPhase")
@@ -9953,13 +10378,30 @@ function targetMembers(ref) {
9953
10378
  for (const buildFileId of list(phase.files)) {
9954
10379
  for (const file of fileRefPaths(str(project.object(buildFileId)?.fileRef) ?? "")) {
9955
10380
  if (phase.isa === "PBXSourcesBuildPhase") {
9956
- if (!import_node_fs9.default.existsSync(file)) continue;
10381
+ if (!import_node_fs9.default.existsSync(file)) {
10382
+ if (options.requireSources === false) continue;
10383
+ const script = scriptForOutput.get(import_node_path10.default.normalize(file));
10384
+ throw new Error(
10385
+ 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}`
10386
+ );
10387
+ }
9957
10388
  allSources.add(file);
9958
10389
  if (file.endsWith(".swift")) sources.add(file);
9959
10390
  } else if (phase.isa === "PBXResourcesBuildPhase") addResource(file);
9960
10391
  }
9961
10392
  }
9962
10393
  }
10394
+ for (const [file, script] of scriptForOutput) {
10395
+ if (sources.has(file) || allSources.has(file)) continue;
10396
+ if (!import_node_fs9.default.existsSync(file)) {
10397
+ if (options.requireSources === false) continue;
10398
+ throw new Error(
10399
+ `${targetName}: missing generated source ${file} (Run Script "${script}" declares it in outputPaths; run the script or check in its output)`
10400
+ );
10401
+ }
10402
+ allSources.add(file);
10403
+ sources.add(file);
10404
+ }
9963
10405
  const addSynchronized = (folder, relative2) => {
9964
10406
  const file = import_node_path10.default.join(folder, relative2);
9965
10407
  if (NEVER_MEMBER.test(relative2)) return;
@@ -10062,7 +10504,7 @@ function nativeTargets(project) {
10062
10504
  function listXcodeAppTargets(xcodeprojOrWorkspace) {
10063
10505
  return workspaceProjects(xcodeprojOrWorkspace).projects.flatMap(nativeTargets).filter((ref) => ref.target.productType === APPLICATION).map((ref) => str(ref.target.name) ?? ref.id);
10064
10506
  }
10065
- function readXcodeAppTarget(input, targetName) {
10507
+ function readXcodeAppTarget(input, targetName, configurationName, options = {}) {
10066
10508
  const xcodeprojOrWorkspace = import_node_fs9.default.realpathSync(input);
10067
10509
  const workspace = xcodeprojOrWorkspace.endsWith(".xcworkspace") ? xcodeprojOrWorkspace : null;
10068
10510
  const { listed, projects, projectAt } = workspaceProjects(xcodeprojOrWorkspace);
@@ -10077,6 +10519,7 @@ function readXcodeAppTarget(input, targetName) {
10077
10519
  );
10078
10520
  }
10079
10521
  const app = named[0];
10522
+ const configuration = configurationName ?? defaultConfiguration(app.project);
10080
10523
  const byProduct = /* @__PURE__ */ new Map();
10081
10524
  for (const ref of allTargets) {
10082
10525
  const product = ref.project.object(str(ref.target.productReference));
@@ -10127,9 +10570,14 @@ function readXcodeAppTarget(input, targetName) {
10127
10570
  order.push(ref);
10128
10571
  };
10129
10572
  visit3(app);
10573
+ const xcconfigs = /* @__PURE__ */ new Set();
10130
10574
  const moduleTarget = (ref) => {
10131
10575
  const name2 = str(ref.target.name) ?? ref.id;
10132
- const { get: settings2, keys } = targetSettings(ref, {
10576
+ const {
10577
+ get: settings2,
10578
+ keys,
10579
+ xcconfigs: read
10580
+ } = targetSettings(ref, configuration, {
10133
10581
  TARGET_NAME: name2,
10134
10582
  PROJECT_NAME: import_node_path10.default.basename(ref.project.xcodeproj, ".xcodeproj"),
10135
10583
  SRCROOT: ref.project.projectDir,
@@ -10137,6 +10585,7 @@ function readXcodeAppTarget(input, targetName) {
10137
10585
  PROJECT_DIR: ref.project.projectDir,
10138
10586
  DEVELOPMENT_LANGUAGE: str(ref.project.project.developmentRegion) ?? "en"
10139
10587
  });
10588
+ for (const file of read) xcconfigs.add(file);
10140
10589
  const swiftVersion = Number.parseFloat(settings2("SWIFT_VERSION") || "5");
10141
10590
  return {
10142
10591
  settings: settings2,
@@ -10144,9 +10593,10 @@ function readXcodeAppTarget(input, targetName) {
10144
10593
  module: {
10145
10594
  name: name2,
10146
10595
  moduleName: applyOperator(settings2("PRODUCT_MODULE_NAME"), "c99extidentifier"),
10147
- ...targetMembers(ref),
10596
+ ...targetMembers(ref, settings2, options),
10148
10597
  languageMode: swiftVersion >= 6 ? 6 : 5,
10149
- mainActorIsolation: settings2("SWIFT_DEFAULT_ACTOR_ISOLATION") === "MainActor"
10598
+ mainActorIsolation: settings2("SWIFT_DEFAULT_ACTOR_ISOLATION") === "MainActor",
10599
+ defines: compilationConditions(settings2)
10150
10600
  }
10151
10601
  };
10152
10602
  };
@@ -10252,7 +10702,9 @@ function readXcodeAppTarget(input, targetName) {
10252
10702
  )
10253
10703
  ].map((dir) => import_node_fs9.default.realpathSync(dir))
10254
10704
  ),
10255
- projectFiles: [...new Set(used.map((project) => project.projectFile))].sort(),
10705
+ projectFiles: [
10706
+ .../* @__PURE__ */ new Set([...used.map((project) => project.projectFile), ...xcconfigs])
10707
+ ].sort(),
10256
10708
  appIconName: settings("ASSETCATALOG_COMPILER_APPICON_NAME"),
10257
10709
  infoPlist: import_plist2.default.build(info),
10258
10710
  infoPlistFile,
@@ -10263,16 +10715,138 @@ function readXcodeAppTarget(input, targetName) {
10263
10715
  ].sort(),
10264
10716
  binaryFrameworks: [...binaryFrameworks].map(([name2, file]) => ({ name: name2, path: file })).sort((left, right) => left.name.localeCompare(right.name)),
10265
10717
  packageResolved: import_node_fs9.default.existsSync(resolvedFile) ? resolvedFile : null,
10266
- appTargets: appTargetNames
10718
+ appTargets: appTargetNames,
10719
+ configuration,
10720
+ settings
10721
+ };
10722
+ }
10723
+
10724
+ // src/native-app-identity-project.ts
10725
+ function isRecord3(value) {
10726
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10727
+ }
10728
+ function nonEmptyString2(value) {
10729
+ return typeof value === "string" && value.length > 0 ? value : void 0;
10730
+ }
10731
+ function readNativeAppIdentityFromXcodeTarget(app, parseInfoPlist = parsePlistSource) {
10732
+ const dictionary = parseInfoPlist(app.infoPlist);
10733
+ if (!isRecord3(dictionary)) throw new Error("native app Info.plist is not a dictionary");
10734
+ const settings = app.settings;
10735
+ const bundleIdentifier = nonEmptyString2(dictionary.CFBundleIdentifier) ?? nonEmptyString2(settings?.("PRODUCT_BUNDLE_IDENTIFIER"));
10736
+ if (!bundleIdentifier) throw new Error("native app has no bundle identifier");
10737
+ const appVersion = nonEmptyString2(dictionary.CFBundleShortVersionString) ?? nonEmptyString2(settings?.("MARKETING_VERSION"));
10738
+ const buildVersion = nonEmptyString2(dictionary.CFBundleVersion) ?? nonEmptyString2(settings?.("CURRENT_PROJECT_VERSION"));
10739
+ if (!appVersion || !buildVersion) {
10740
+ throw new Error("native app has no version or build version");
10741
+ }
10742
+ const bundleName = nonEmptyString2(dictionary.CFBundleName) ?? nonEmptyString2(settings?.("PRODUCT_NAME")) ?? app.name;
10743
+ const applicationDisplayName = nonEmptyString2(dictionary.CFBundleDisplayName);
10744
+ const applicationName = applicationDisplayName ?? bundleName;
10745
+ const declaredBackgroundColor = dictionary.RCTRootViewBackgroundColor;
10746
+ const rootViewBackgroundColor = typeof declaredBackgroundColor === "number" && Number.isInteger(declaredBackgroundColor) ? declaredBackgroundColor : void 0;
10747
+ const alternateIconNames = /* @__PURE__ */ new Set();
10748
+ const icons = dictionary.CFBundleIcons;
10749
+ const alternates = isRecord3(icons) ? icons.CFBundleAlternateIcons : void 0;
10750
+ if (isRecord3(alternates)) {
10751
+ for (const name2 of Object.keys(alternates)) {
10752
+ if (name2) alternateIconNames.add(name2);
10753
+ }
10754
+ }
10755
+ const alternateSetting = settings?.("ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES") ?? "";
10756
+ for (const match of alternateSetting.replace(/\\"/g, '"').matchAll(/"([^"]*)"|([^\s(),]+)/g)) {
10757
+ const val = match[1] ?? match[2];
10758
+ if (val) alternateIconNames.add(val);
10759
+ }
10760
+ const permissionUsageDescriptions = {};
10761
+ for (const [key, value] of Object.entries(dictionary)) {
10762
+ if (!/^NS.*UsageDescription$/.test(key) && key !== "NSLocationTemporaryUsageDescriptionDictionary") {
10763
+ continue;
10764
+ }
10765
+ permissionUsageDescriptions[key] = value;
10766
+ }
10767
+ const querySchemes = dictionary.LSApplicationQueriesSchemes;
10768
+ const urlQuerySchemes = Array.isArray(querySchemes) ? querySchemes : [];
10769
+ const intentIds = dictionary.OneAppIntentIdentifiers;
10770
+ const appIntentIdentifiers = Array.isArray(intentIds) && intentIds.every((v) => typeof v === "string") ? intentIds : [];
10771
+ const backgroundIds = dictionary.BGTaskSchedulerPermittedIdentifiers;
10772
+ const backgroundTaskIdentifiers = Array.isArray(backgroundIds) && backgroundIds.every((v) => typeof v === "string") ? backgroundIds : [];
10773
+ const declaredModes = dictionary.UIBackgroundModes;
10774
+ const backgroundModes = Array.isArray(declaredModes) && declaredModes.every((v) => typeof v === "string") ? declaredModes : [];
10775
+ const supportedInterfaceOrientations = Array.isArray(dictionary.UISupportedInterfaceOrientations) && dictionary.UISupportedInterfaceOrientations.every((v) => typeof v === "string") ? dictionary.UISupportedInterfaceOrientations : [];
10776
+ const ipadSupportedInterfaceOrientations = Array.isArray(dictionary["UISupportedInterfaceOrientations~ipad"]) && dictionary["UISupportedInterfaceOrientations~ipad"].every(
10777
+ (v) => typeof v === "string"
10778
+ ) ? dictionary["UISupportedInterfaceOrientations~ipad"] : [];
10779
+ const defaultScreenOrientationMask = nonEmptyString2(
10780
+ dictionary.EXDefaultScreenOrientationMask
10781
+ );
10782
+ const shareIntoAppGroupId = nonEmptyString2(dictionary.ExpoShareIntoAppGroupId);
10783
+ return {
10784
+ applicationName,
10785
+ ...applicationDisplayName ? { applicationDisplayName } : {},
10786
+ ...rootViewBackgroundColor !== void 0 ? { rootViewBackgroundColor } : {},
10787
+ bundleName,
10788
+ appVersion,
10789
+ buildVersion,
10790
+ bundleIdentifier,
10791
+ platform: "ios",
10792
+ supportedInterfaceOrientations,
10793
+ ipadSupportedInterfaceOrientations,
10794
+ ...defaultScreenOrientationMask ? { defaultScreenOrientationMask } : {},
10795
+ alternateIconNames: [...alternateIconNames],
10796
+ permissionUsageDescriptions,
10797
+ urlQuerySchemes,
10798
+ ...shareIntoAppGroupId ? { shareIntoAppGroupId } : {},
10799
+ backgroundTaskIdentifiers,
10800
+ backgroundModes,
10801
+ appIntentIdentifiers
10267
10802
  };
10268
10803
  }
10804
+ function readNativeAppIdentityFromProject(projectRoot, platform, parseInfoPlist = parsePlistSource, iosTarget) {
10805
+ if (!canExtractNativeAppIdentity(platform)) return null;
10806
+ const iosRoot = import_node_path11.default.join(projectRoot, "ios");
10807
+ if (!import_node_fs10.default.existsSync(iosRoot)) return null;
10808
+ const projects = import_node_fs10.default.readdirSync(iosRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.endsWith(".xcodeproj"));
10809
+ if (projects.length === 0) return null;
10810
+ if (projects.length > 1 && !iosTarget) {
10811
+ throw new Error("native app identity requires a selected Xcode project");
10812
+ }
10813
+ let project = projects[0];
10814
+ if (iosTarget && projects.length > 1) {
10815
+ const matches = projects.filter(
10816
+ (entry) => listXcodeAppTargets(import_node_path11.default.join(iosRoot, entry.name)).includes(iosTarget)
10817
+ );
10818
+ if (matches.length === 0) {
10819
+ throw new Error(`native app identity has no application target ${iosTarget}`);
10820
+ }
10821
+ if (matches.length > 1) {
10822
+ throw new Error(
10823
+ `native app identity target ${iosTarget} is ambiguous across ${matches.map((entry) => entry.name).join(", ")}`
10824
+ );
10825
+ }
10826
+ project = matches[0];
10827
+ }
10828
+ const xcodeproj = import_node_path11.default.join(iosRoot, project.name);
10829
+ const appTargets = listXcodeAppTargets(xcodeproj);
10830
+ if (!iosTarget && appTargets.length > 1) {
10831
+ throw new Error("native app identity requires a selected app target");
10832
+ }
10833
+ const app = readXcodeAppTarget(xcodeproj, iosTarget ?? appTargets[0], "Release", {
10834
+ requireSources: false
10835
+ });
10836
+ return readNativeAppIdentityFromXcodeTarget(app, parseInfoPlist);
10837
+ }
10269
10838
 
10270
10839
  // src/native-firebase-config.ts
10271
- function readNativeFirebaseConfig(projectRoot, platforms = ["ios", "android"]) {
10840
+ var import_node_child_process3 = require("node:child_process");
10841
+ var import_node_fs11 = __toESM(require("node:fs"), 1);
10842
+ var import_node_os2 = __toESM(require("node:os"), 1);
10843
+ var import_node_path12 = __toESM(require("node:path"), 1);
10844
+ init_is_record();
10845
+ function readNativeFirebaseConfig(projectRoot, platforms = ["ios", "android"], iosTarget) {
10272
10846
  const result = {};
10273
10847
  let config = {};
10274
- const appJsonPath = import_node_path11.default.join(projectRoot, "app.json");
10275
- const appJson = import_node_fs10.default.existsSync(appJsonPath) ? JSON.parse(import_node_fs10.default.readFileSync(appJsonPath, "utf8")) : null;
10848
+ const appJsonPath = import_node_path12.default.join(projectRoot, "app.json");
10849
+ const appJson = import_node_fs11.default.existsSync(appJsonPath) ? JSON.parse(import_node_fs11.default.readFileSync(appJsonPath, "utf8")) : null;
10276
10850
  const staticExpo = isRecord(appJson) && (isRecord(appJson.expo) || [appJson.ios, appJson.android].some(
10277
10851
  (value) => isRecord(value) && typeof value.googleServicesFile === "string"
10278
10852
  ));
@@ -10281,41 +10855,76 @@ function readNativeFirebaseConfig(projectRoot, platforms = ["ios", "android"]) {
10281
10855
  "app.config.ts",
10282
10856
  "app.config.cjs",
10283
10857
  "app.config.mjs"
10284
- ].some((name2) => import_node_fs10.default.existsSync(import_node_path11.default.join(projectRoot, name2)));
10858
+ ].some((name2) => import_node_fs11.default.existsSync(import_node_path12.default.join(projectRoot, name2)));
10285
10859
  if (staticExpo || dynamicExpo) {
10286
- const require2 = (0, import_node_module3.createRequire)(import_node_path11.default.join(projectRoot, "package.json"));
10287
- const configModule = require2("@expo/config");
10288
- if (!isRecord(configModule) || typeof configModule.getConfig !== "function")
10289
- throw new Error("Expo native configuration is unavailable.");
10290
- const resolved = Reflect.apply(configModule.getConfig, configModule, [
10291
- projectRoot,
10292
- { skipSDKVersionRequirement: true }
10293
- ]);
10294
- config = isRecord(resolved) && isRecord(resolved.exp) ? resolved.exp : {};
10860
+ const directory = import_node_fs11.default.mkdtempSync(import_node_path12.default.join(import_node_os2.default.tmpdir(), "peach-expo-config-"));
10861
+ const resultPath = import_node_path12.default.join(directory, "config.json");
10862
+ try {
10863
+ const child = (0, import_node_child_process3.spawnSync)(
10864
+ "node",
10865
+ [
10866
+ "--input-type=commonjs",
10867
+ "-e",
10868
+ `const { createRequire } = require('node:module');
10869
+ const { writeFileSync } = require('node:fs');
10870
+ const { join } = require('node:path');
10871
+ const root = process.argv[1];
10872
+ const { getConfig } = createRequire(join(root, 'package.json'))('@expo/config');
10873
+ const result = JSON.stringify(getConfig(root, { skipSDKVersionRequirement: true }).exp);
10874
+ if (Buffer.byteLength(result) > 1024 * 1024) throw new Error('Expo native configuration exceeds 1 MiB.');
10875
+ writeFileSync(process.argv[2], result, { mode: 0o600 });`,
10876
+ import_node_path12.default.resolve(projectRoot),
10877
+ resultPath
10878
+ ],
10879
+ {
10880
+ cwd: projectRoot,
10881
+ encoding: "utf8",
10882
+ stdio: ["ignore", "ignore", "pipe"],
10883
+ timeout: 3e4,
10884
+ maxBuffer: 1024 * 1024
10885
+ }
10886
+ );
10887
+ if (child.error) throw child.error;
10888
+ if (child.status !== 0)
10889
+ throw new Error(child.stderr || "Expo native configuration evaluation failed.");
10890
+ const resolved = JSON.parse(import_node_fs11.default.readFileSync(resultPath, "utf8"));
10891
+ if (!isRecord(resolved))
10892
+ throw new Error("Expo native configuration is unavailable.");
10893
+ config = resolved;
10894
+ } finally {
10895
+ import_node_fs11.default.rmSync(directory, { recursive: true, force: true });
10896
+ }
10295
10897
  }
10296
10898
  const ios = config.ios;
10297
10899
  if (platforms.includes("ios") && (!isRecord(ios) || typeof ios.googleServicesFile !== "string")) {
10298
- const iosRoot = import_node_path11.default.join(projectRoot, "ios");
10299
- if (import_node_fs10.default.existsSync(iosRoot)) {
10300
- const projects = import_node_fs10.default.readdirSync(iosRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.endsWith(".xcodeproj"));
10900
+ const iosRoot = import_node_path12.default.join(projectRoot, "ios");
10901
+ if (import_node_fs11.default.existsSync(iosRoot)) {
10902
+ const projects = import_node_fs11.default.readdirSync(iosRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.endsWith(".xcodeproj"));
10301
10903
  if (projects.length > 1)
10302
10904
  throw new Error("native Firebase configuration requires a selected Xcode project");
10303
10905
  if (projects.length === 1) {
10304
- const xcodeproj = import_node_path11.default.join(iosRoot, projects[0].name);
10906
+ const xcodeproj = import_node_path12.default.join(iosRoot, projects[0].name);
10305
10907
  const appTargets = listXcodeAppTargets(xcodeproj);
10306
- if (appTargets.length > 1)
10908
+ if (!iosTarget && appTargets.length > 1)
10307
10909
  throw new Error("native Firebase configuration requires a selected app target");
10308
- if (appTargets.length === 1) {
10309
- const app = readXcodeAppTarget(xcodeproj, appTargets[0]);
10910
+ if (iosTarget || appTargets.length === 1) {
10911
+ const app = readXcodeAppTarget(
10912
+ xcodeproj,
10913
+ iosTarget ?? appTargets[0],
10914
+ void 0,
10915
+ {
10916
+ requireSources: false
10917
+ }
10918
+ );
10310
10919
  const delegates = app.allSources.filter(
10311
- (file) => /^AppDelegate\.(swift|mm|m)$/.test(import_node_path11.default.basename(file))
10920
+ (file) => /^AppDelegate\.(swift|mm|m)$/.test(import_node_path12.default.basename(file))
10312
10921
  );
10313
10922
  const defaultConfigured = delegates.some((file) => {
10314
- const source = import_node_fs10.default.readFileSync(file, "utf8").replace(
10923
+ const source = import_node_fs11.default.readFileSync(file, "utf8").replace(
10315
10924
  /\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:\\.|[^"\\])*"/g,
10316
10925
  (token) => token.startsWith('"') ? '""' : token.replace(/[^\n]/g, " ")
10317
10926
  );
10318
- const launch = /(?:\bfunc\s+application\s*\([\s\S]*?didFinishLaunchingWithOptions[\s\S]*?\)\s*->\s*Bool|^-\s*\(BOOL\)\s*application\s*:[\s\S]*?didFinishLaunchingWithOptions[^\{]*)\s*\{/m.exec(
10927
+ const launch = /(?:\bfunc\s+application\s*\([\s\S]*?didFinishLaunchingWithOptions[\s\S]*?\)\s*->\s*Bool|^-\s*\(BOOL\)\s*application\s*:[\s\S]*?didFinishLaunchingWithOptions[^{]*)\s*\{/m.exec(
10319
10928
  source
10320
10929
  );
10321
10930
  if (!launch) return false;
@@ -10331,7 +10940,21 @@ function readNativeFirebaseConfig(projectRoot, platforms = ["ios", "android"]) {
10331
10940
  if (token === "}") depth -= 1;
10332
10941
  if (depth === 0) return false;
10333
10942
  }
10334
- if (depth !== 1 || /\b(?:return|throw)\b|^\s*#(?:if|elseif|else)/m.test(before))
10943
+ let conditionalDepth = 0;
10944
+ for (const directive of before.matchAll(
10945
+ /^\s*#\s*(if|ifdef|ifndef|elseif|elif|else|endif)\b/gm
10946
+ )) {
10947
+ const kind = directive[1];
10948
+ if (kind === "if" || kind === "ifdef" || kind === "ifndef") {
10949
+ conditionalDepth += 1;
10950
+ } else if (conditionalDepth === 0) {
10951
+ conditionalDepth = -1;
10952
+ break;
10953
+ } else if (kind === "endif") {
10954
+ conditionalDepth -= 1;
10955
+ }
10956
+ }
10957
+ if (depth !== 1 || conditionalDepth !== 0 || /\b(?:return|throw)\b/.test(before))
10335
10958
  throw new Error(
10336
10959
  "native Firebase initialization must be unconditional in the app launch method"
10337
10960
  );
@@ -10339,15 +10962,15 @@ function readNativeFirebaseConfig(projectRoot, platforms = ["ios", "android"]) {
10339
10962
  });
10340
10963
  if (defaultConfigured) {
10341
10964
  const resources = app.resources.filter(
10342
- (file) => /^GoogleService-Info\.plist$/.test(import_node_path11.default.basename(file))
10965
+ (file) => /^GoogleService-Info\.plist$/.test(import_node_path12.default.basename(file))
10343
10966
  );
10344
10967
  if (resources.length !== 1)
10345
10968
  throw new Error(
10346
10969
  "native Firebase default initialization requires one GoogleService-Info.plist build resource"
10347
10970
  );
10348
10971
  config.ios = {
10349
- googleServicesFile: import_node_path11.default.relative(
10350
- import_node_fs10.default.realpathSync(projectRoot),
10972
+ googleServicesFile: import_node_path12.default.relative(
10973
+ import_node_fs11.default.realpathSync(projectRoot),
10351
10974
  resources[0]
10352
10975
  )
10353
10976
  };
@@ -10360,8 +10983,8 @@ function readNativeFirebaseConfig(projectRoot, platforms = ["ios", "android"]) {
10360
10983
  const native = config[platform];
10361
10984
  if (!isRecord(native) || typeof native.googleServicesFile !== "string") continue;
10362
10985
  try {
10363
- const source = import_node_fs10.default.readFileSync(
10364
- import_node_path11.default.resolve(projectRoot, native.googleServicesFile),
10986
+ const source = import_node_fs11.default.readFileSync(
10987
+ import_node_path12.default.resolve(projectRoot, native.googleServicesFile),
10365
10988
  "utf8"
10366
10989
  );
10367
10990
  const parsed = platform === "ios" ? parsePlistSource(source) : JSON.parse(source);
@@ -10493,7 +11116,7 @@ ${MODULE_PREFIX}`);
10493
11116
  }
10494
11117
 
10495
11118
  // src/metro-plugin.ts
10496
- var prefix = "/__soot";
11119
+ var prefix = "/__peachsim";
10497
11120
  function readCallableMetroExport(moduleValue, label) {
10498
11121
  const callable = typeof moduleValue === "function" ? moduleValue : typeof moduleValue === "object" && moduleValue !== null ? Reflect.get(moduleValue, "default") : void 0;
10499
11122
  if (typeof callable !== "function") {
@@ -10501,8 +11124,8 @@ function readCallableMetroExport(moduleValue, label) {
10501
11124
  }
10502
11125
  return (...args) => Reflect.apply(callable, void 0, args);
10503
11126
  }
10504
- function createMetroDefaultSerializer(projectRoot) {
10505
- const projectRequire = (0, import_module2.createRequire)(import_path4.default.join(projectRoot, "package.json"));
11127
+ function createMetroDefaultSerializer(asyncRequireModulePath) {
11128
+ const projectRequire = (0, import_module2.createRequire)(asyncRequireModulePath);
10506
11129
  const metroRoot = import_path4.default.dirname(projectRequire.resolve("metro/package.json"));
10507
11130
  const baseJSBundle = readCallableMetroExport(
10508
11131
  projectRequire(import_path4.default.join(metroRoot, "src/DeltaBundler/Serializers/baseJSBundle.js")),
@@ -10561,7 +11184,7 @@ function appendRNXModulePaths(result, graph, options, projectRoot) {
10561
11184
  };
10562
11185
  return typeof result === "string" ? appendRNXMetroModuleIdentityFooter(result, identity) : { ...result, code: appendRNXMetroModuleIdentityFooter(result.code, identity) };
10563
11186
  }
10564
- function withRNX(config) {
11187
+ function withPeach(config, options = {}) {
10565
11188
  const cfgProjectRoot = config.projectRoot || process.cwd();
10566
11189
  const configuredBundleUrl = "/index.bundle?platform=ios&dev=true&hot=true&minify=false";
10567
11190
  const cfgServer = config.server;
@@ -10581,7 +11204,9 @@ function withRNX(config) {
10581
11204
  serializer: {
10582
11205
  ...cfgSerializer,
10583
11206
  customSerializer: async (entryPoint, preModules, graph, serializerOptions) => {
10584
- const serializer = existingSerializer || (defaultSerializer ||= createMetroDefaultSerializer(cfgProjectRoot));
11207
+ const serializer = existingSerializer || (defaultSerializer ||= createMetroDefaultSerializer(
11208
+ serializerOptions.asyncRequireModulePath
11209
+ ));
10585
11210
  const result = await serializer(entryPoint, preModules, graph, serializerOptions);
10586
11211
  let annotated = appendRNXModulePaths(
10587
11212
  result,
@@ -10592,21 +11217,36 @@ function withRNX(config) {
10592
11217
  const iosRoot = import_path4.default.join(cfgProjectRoot, "ios");
10593
11218
  const bundlePlatform = graph.transformOptions?.platform;
10594
11219
  if (canExtractNativeAppIdentity(bundlePlatform) && import_fs5.default.existsSync(iosRoot)) {
10595
- const files = {};
10596
- for (const entry of import_fs5.default.readdirSync(iosRoot, { withFileTypes: true })) {
10597
- if (!entry.isDirectory() || entry.name === "Pods") continue;
10598
- const names = entry.name.endsWith(".xcodeproj") ? ["project.pbxproj"] : ["Info.plist", "AppDelegate.swift", "AppDelegate.mm", "AppDelegate.m"];
10599
- for (const name2 of names) {
10600
- const relative2 = `ios/${entry.name}/${name2}`;
10601
- const file = import_path4.default.join(cfgProjectRoot, relative2);
10602
- if (import_fs5.default.existsSync(file)) files[relative2] = import_fs5.default.readFileSync(file, "utf8");
10603
- }
10604
- }
10605
- const identity = extractNativeAppIdentityFromFiles(
10606
- files,
11220
+ let identity = readNativeAppIdentityFromProject(
11221
+ cfgProjectRoot,
10607
11222
  bundlePlatform,
10608
- parsePlistSource
11223
+ parsePlistSource,
11224
+ options.iosTarget
10609
11225
  );
11226
+ if (!identity) {
11227
+ const files = {};
11228
+ for (const entry of import_fs5.default.readdirSync(iosRoot, { withFileTypes: true })) {
11229
+ if (entry.name === "Pods") continue;
11230
+ if (entry.isDirectory()) {
11231
+ const names = entry.name.endsWith(".xcodeproj") ? ["project.pbxproj"] : ["Info.plist", "AppDelegate.swift", "AppDelegate.mm", "AppDelegate.m"];
11232
+ for (const name2 of names) {
11233
+ const relative2 = `ios/${entry.name}/${name2}`;
11234
+ const file = import_path4.default.join(cfgProjectRoot, relative2);
11235
+ if (import_fs5.default.existsSync(file)) files[relative2] = import_fs5.default.readFileSync(file, "utf8");
11236
+ }
11237
+ } else if (/AppDelegate\.(?:swift|mm|m)$/.test(entry.name)) {
11238
+ const relative2 = `ios/${entry.name}`;
11239
+ const file = import_path4.default.join(cfgProjectRoot, relative2);
11240
+ if (import_fs5.default.existsSync(file)) files[relative2] = import_fs5.default.readFileSync(file, "utf8");
11241
+ }
11242
+ }
11243
+ identity = extractNativeAppIdentityFromFiles(
11244
+ files,
11245
+ bundlePlatform,
11246
+ parsePlistSource,
11247
+ options.iosTarget
11248
+ );
11249
+ }
10610
11250
  if (identity) {
10611
11251
  annotated = typeof annotated === "string" ? insertNativeAppIdentityFooter(annotated, identity) : {
10612
11252
  ...annotated,
@@ -10620,7 +11260,8 @@ function withRNX(config) {
10620
11260
  const firebase = readNativeFirebaseMetadata(
10621
11261
  readNativeFirebaseConfig(
10622
11262
  cfgProjectRoot,
10623
- typeof bundlePlatform === "string" ? [bundlePlatform] : void 0
11263
+ typeof bundlePlatform === "string" ? [bundlePlatform] : void 0,
11264
+ options.iosTarget
10624
11265
  )
10625
11266
  );
10626
11267
  if (firebase)
@@ -10685,7 +11326,11 @@ function withRNX(config) {
10685
11326
  res.setHeader("Content-Type", "application/json");
10686
11327
  res.setHeader("Cache-Control", "no-store");
10687
11328
  res.setHeader("Access-Control-Allow-Origin", "*");
10688
- res.end(JSON.stringify(readNativeFirebaseConfig(cfgProjectRoot)));
11329
+ res.end(
11330
+ JSON.stringify(
11331
+ readNativeFirebaseConfig(cfgProjectRoot, void 0, options.iosTarget)
11332
+ )
11333
+ );
10689
11334
  } catch (error) {
10690
11335
  res.statusCode = 400;
10691
11336
  res.end(
@@ -10761,16 +11406,16 @@ function resolveMetroPort(server) {
10761
11406
  }
10762
11407
  return port;
10763
11408
  }
10764
- var metro_plugin_default = withRNX;
11409
+ var metro_plugin_default = withPeach;
10765
11410
 
10766
11411
  // src/jump-to-source-babel.ts
10767
- function normalizeSourcePath(path16) {
10768
- const queryStart = path16.indexOf("?");
10769
- const clean = queryStart >= 0 ? path16.slice(0, queryStart) : path16;
11412
+ function normalizeSourcePath(path17) {
11413
+ const queryStart = path17.indexOf("?");
11414
+ const clean = queryStart >= 0 ? path17.slice(0, queryStart) : path17;
10770
11415
  return clean.replace(/\\/g, "/");
10771
11416
  }
10772
- function matchesPath(path16, matcher) {
10773
- return typeof matcher === "string" ? path16.includes(matcher) : matcher.test(path16);
11417
+ function matchesPath(path17, matcher) {
11418
+ return typeof matcher === "string" ? path17.includes(matcher) : matcher.test(path17);
10774
11419
  }
10775
11420
  function shouldStampFile(filename, options) {
10776
11421
  const normalized = normalizeSourcePath(filename);
@@ -10803,11 +11448,11 @@ function sootsimJumpToSourceBabelPlugin(api, options = {}) {
10803
11448
  return {
10804
11449
  name: "sootsim-jump-to-source",
10805
11450
  visitor: {
10806
- JSXOpeningElement(path16, state) {
11451
+ JSXOpeningElement(path17, state) {
10807
11452
  if (options.enabled === false) return;
10808
11453
  const filename = state.file?.opts?.filename;
10809
11454
  if (!filename || !shouldStampFile(filename, options)) return;
10810
- const node = path16.node;
11455
+ const node = path17.node;
10811
11456
  const tagName = getJSXName(node.name);
10812
11457
  if (!tagName || tagName === "Fragment" || tagName.endsWith(".Fragment")) return;
10813
11458
  if (hasAttribute(node, attrName)) return;
@@ -10845,8 +11490,9 @@ function sootsimJumpToSourceBabelPlugin(api, options = {}) {
10845
11490
  jumpToSourceBabelPlugin,
10846
11491
  metroPlugin,
10847
11492
  packRnxCaptureRegions,
11493
+ peachPlugin,
10848
11494
  rnxCaptureRasterRegions,
10849
- rnxPlugin,
10850
11495
  rnxRasterFrames,
11496
+ rnxRasterRegionRequests,
10851
11497
  selectRnxRasterNodeIds
10852
11498
  });