peachsim 0.1.940 → 0.1.1400

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 (398) hide show
  1. package/README.md +28 -22
  2. package/cli/app-catalog.ts +29 -14
  3. package/cli/app-fonts.ts +17 -8
  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 +107 -95
  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 +19 -16
  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 +50 -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/screenshot-output.ts +17 -0
  77. package/cli/send-to-box.ts +3 -3
  78. package/cli/shell-init.ts +2 -1
  79. package/cli/ws-bridge.ts +9 -5
  80. package/detox/element-types.ts +1 -0
  81. package/detox/gestures.ts +3 -3
  82. package/detox/index.ts +67 -38
  83. package/detox/navigation-lifecycle.ts +9 -6
  84. package/detox/proof-frame.cjs +6 -4
  85. package/dist-lib/agent-daemon-client.cjs +1 -1
  86. package/dist-lib/agent-events.cjs +1 -1
  87. package/dist-lib/attached-projects.cjs +1 -1
  88. package/dist-lib/auth/shared-session.cjs +1 -1
  89. package/dist-lib/backend-origin.cjs +1 -1
  90. package/dist-lib/beta.cjs +1 -1
  91. package/dist-lib/beta.mjs +1 -1
  92. package/dist-lib/bridge-constants.cjs +1 -1
  93. package/dist-lib/bridge-contract-input.cjs +42 -17
  94. package/dist-lib/bridge-contract-input.mjs +41 -16
  95. package/dist-lib/bridge-contract.cjs +1 -4
  96. package/dist-lib/bridge-contract.mjs +1 -3
  97. package/dist-lib/bundle-channel.cjs +2918 -550
  98. package/dist-lib/capture-contract.cjs +65 -24
  99. package/dist-lib/capture-contract.mjs +63 -24
  100. package/dist-lib/cloud-contract.cjs +2 -2
  101. package/dist-lib/cloud-contract.mjs +2 -2
  102. package/dist-lib/cloud-test-args.cjs +1 -1
  103. package/dist-lib/cloud-test-args.mjs +1 -1
  104. package/dist-lib/cloud.cjs +3 -3
  105. package/dist-lib/cloud.mjs +3 -3
  106. package/dist-lib/config.cjs +15 -5
  107. package/dist-lib/config.mjs +154 -0
  108. package/dist-lib/detox/index.cjs +495 -139
  109. package/dist-lib/dev-bundle-resolution.cjs +3 -1
  110. package/dist-lib/expo-swift-bundle.cjs +1168 -238
  111. package/dist-lib/expo-swift-metadata.cjs +34 -23
  112. package/dist-lib/expo-swift-metadata.mjs +32 -23
  113. package/dist-lib/home-paths.cjs +2 -2
  114. package/dist-lib/host/bridge-host.cjs +2938 -654
  115. package/dist-lib/host/fetch-proxy-handler.cjs +14 -1
  116. package/dist-lib/host/fetch-proxy-overrides.cjs +7 -1
  117. package/dist-lib/host/fetch-proxy-overrides.mjs +5 -1
  118. package/dist-lib/host/replacement-module-handler.cjs +2 -2
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +981 -353
  121. package/dist-lib/jump-to-source-babel.cjs +1 -1
  122. package/dist-lib/jump-to-source-native.cjs +1 -1
  123. package/dist-lib/kotlin-host.cjs +332 -19
  124. package/dist-lib/menu.cjs +1 -1
  125. package/dist-lib/menu.mjs +1 -1
  126. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  127. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  128. package/dist-lib/metro-production-bundle.cjs +7 -7
  129. package/dist-lib/metro-production-bundle.mjs +5 -5
  130. package/dist-lib/metro.cjs +670 -250
  131. package/dist-lib/native-app-identity-project.cjs +2647 -0
  132. package/dist-lib/native-app-identity-project.mjs +1257 -0
  133. package/dist-lib/native-app-identity.cjs +304 -25
  134. package/dist-lib/native-app-identity.mjs +302 -25
  135. package/dist-lib/native-firebase-metadata.cjs +1 -1
  136. package/dist-lib/native-firebase-metadata.mjs +1 -1
  137. package/dist-lib/plist.cjs +1 -1
  138. package/dist-lib/plist.mjs +1 -1
  139. package/dist-lib/profiles.cjs +1 -1
  140. package/dist-lib/public-brand.cjs +1 -1
  141. package/dist-lib/react-native-host-modules.cjs +5 -1
  142. package/dist-lib/react-native-host-modules.mjs +5 -1
  143. package/dist-lib/scripts/dev-server-scanner.cjs +57 -32
  144. package/dist-lib/sdk.cjs +231 -51
  145. package/dist-lib/sdk.mjs +231 -51
  146. package/dist-lib/skills.cjs +4114 -1376
  147. package/dist-lib/swift-compile.cjs +290 -30
  148. package/dist-lib/swift-compile.mjs +290 -30
  149. package/dist-lib/swift-package.cjs +143 -12
  150. package/dist-lib/swift-package.mjs +137 -11
  151. package/dist-lib/swift-submit.cjs +47 -5
  152. package/dist-lib/swift-submit.mjs +42 -5
  153. package/dist-lib/swift-toolchain.cjs +7 -2
  154. package/dist-lib/swift.cjs +1 -1
  155. package/dist-lib/vite-swift.cjs +1392 -458
  156. package/dist-lib/vite.cjs +13 -17
  157. package/dist-lib/worklet-executable.cjs +248 -33
  158. package/dist-lib/worklet-executable.mjs +248 -33
  159. package/install.sh +10 -1
  160. package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Resources.kt +9 -9
  161. package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +20 -0
  162. package/kotlin/AndroidViewSources/android/view/View.kt +211 -0
  163. package/kotlin/AndroidViewSources/android/widget/FrameLayout.kt +61 -0
  164. package/kotlin/AndroidViewSources/android/widget/LinearLayout.kt +49 -0
  165. package/kotlin/AppSources/android/view/Window.kt +36 -0
  166. package/kotlin/AppSources/android/view/WindowInsets.kt +9 -0
  167. package/kotlin/AppSources/android/view/WindowInsetsController.kt +5 -0
  168. package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +48 -0
  169. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +20 -5
  170. package/kotlin/AppSources/androidx/compose/foundation/layout/WindowInsets.kt +15 -0
  171. package/kotlin/AppSources/androidx/compose/ui/platform/ComposeView.kt +2 -1
  172. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +42 -50
  173. package/kotlin/ComposeSources/PeachComposeHost.kt +200 -84
  174. package/kotlin/ExpoComposeSources/PeachAndroidView.kt +87 -0
  175. package/kotlin/ExpoComposeSources/PeachExpoViewMount.kt +24 -0
  176. package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +28 -0
  177. package/kotlin/ExpoComposeSources/expo/modules/kotlin/views/ExpoComposeView.kt +70 -0
  178. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +513 -0
  179. package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +173 -0
  180. package/kotlin/ExpoSources/expo/modules/kotlin/AppContext.kt +20 -0
  181. package/kotlin/ExpoSources/expo/modules/kotlin/Promise.kt +19 -0
  182. package/kotlin/ExpoSources/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +198 -0
  183. package/kotlin/ExpoSources/expo/modules/kotlin/exception/CodedException.kt +126 -0
  184. package/kotlin/ExpoSources/expo/modules/kotlin/functions/Functions.kt +132 -0
  185. package/kotlin/ExpoSources/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +81 -0
  186. package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +98 -0
  187. package/kotlin/ExpoSources/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +236 -0
  188. package/kotlin/ExpoSources/expo/modules/kotlin/records/Record.kt +12 -0
  189. package/kotlin/ExpoSources/expo/modules/kotlin/runtime/Runtime.kt +9 -0
  190. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObject.kt +50 -0
  191. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +57 -0
  192. package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedRef.kt +19 -0
  193. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/ConcreteTypedArrays.kt +158 -0
  194. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/GenericTypedArray.kt +8 -0
  195. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArray.kt +29 -0
  196. package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArrayIterator.kt +9 -0
  197. package/kotlin/ExpoSources/expo/modules/kotlin/types/AnyType.kt +155 -0
  198. package/kotlin/ExpoSources/expo/modules/kotlin/types/Either.kt +91 -0
  199. package/kotlin/ExpoSources/expo/modules/kotlin/types/Enumerable.kt +6 -0
  200. package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +54 -0
  201. package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +23 -0
  202. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +71 -0
  203. package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +35 -0
  204. package/kotlin/README.md +13 -3
  205. package/kotlin/Sources/android/os/Build.kt +17 -0
  206. package/kotlin/Sources/android/os/Handler.kt +35 -0
  207. package/kotlin/Sources/android/os/Looper.kt +17 -0
  208. package/kotlin/Sources/android/os/MessageQueue.kt +56 -0
  209. package/kotlin/Sources/android/os/SystemClock.kt +14 -0
  210. package/kotlin/Sources/android/util/Log.kt +64 -0
  211. package/kotlin/Sources/java/security/MessageDigest.kt +114 -0
  212. package/kotlin/Sources/kotlin/text/StringFormat.kt +9 -0
  213. package/kotlin/Sources/org/json/JSON.kt +44 -0
  214. package/kotlin/fixtures/counter/Counter.d.kt.ts +1 -0
  215. package/kotlin/fixtures/counter/Counter.kt +10 -3
  216. package/kotlin/fixtures/counter/CounterApp.native.tsx +1 -1
  217. package/kotlin/fixtures/counter/vite.config.ts +2 -2
  218. package/kotlin/fixtures/headless-counter/Headless.kt +7 -6
  219. package/kotlin/fixtures/import-animation/Animation.d.kt.ts +7 -0
  220. package/kotlin/fixtures/import-animation/Animation.kt +80 -0
  221. package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +13 -0
  222. package/kotlin/fixtures/import-animation/README.md +24 -0
  223. package/kotlin/fixtures/import-animation/app/_layout.tsx +4 -0
  224. package/kotlin/fixtures/import-animation/app/index+spa.tsx +1 -0
  225. package/kotlin/fixtures/import-animation/app/routes.d.ts +18 -0
  226. package/kotlin/fixtures/import-animation/package.json +23 -0
  227. package/kotlin/fixtures/import-animation/tsconfig.json +20 -0
  228. package/kotlin/fixtures/import-animation/vite.config.ts +20 -0
  229. package/kotlin/fixtures/import-editor/Editor.d.kt.ts +7 -0
  230. package/kotlin/fixtures/import-editor/Editor.kt +59 -0
  231. package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +23 -0
  232. package/kotlin/fixtures/import-editor/README.md +27 -0
  233. package/kotlin/fixtures/import-editor/app/_layout.tsx +4 -0
  234. package/kotlin/fixtures/import-editor/app/index+spa.tsx +1 -0
  235. package/kotlin/fixtures/import-editor/package.json +23 -0
  236. package/kotlin/fixtures/import-editor/tsconfig.json +20 -0
  237. package/kotlin/fixtures/import-editor/vite.config.ts +20 -0
  238. package/kotlin/fixtures/import-effects/Effects.d.kt.ts +7 -0
  239. package/kotlin/fixtures/import-effects/Effects.kt +87 -0
  240. package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +13 -0
  241. package/kotlin/fixtures/import-effects/README.md +19 -0
  242. package/kotlin/fixtures/import-effects/app/_layout.tsx +4 -0
  243. package/kotlin/fixtures/import-effects/app/index+spa.tsx +1 -0
  244. package/kotlin/fixtures/import-effects/package.json +23 -0
  245. package/kotlin/fixtures/import-effects/tsconfig.json +20 -0
  246. package/kotlin/fixtures/import-effects/vite.config.ts +20 -0
  247. package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +13 -0
  248. package/kotlin/fixtures/import-layout/Layout.d.kt.ts +7 -0
  249. package/kotlin/fixtures/import-layout/Layout.kt +72 -0
  250. package/kotlin/fixtures/import-layout/README.md +24 -0
  251. package/kotlin/fixtures/import-layout/app/_layout.tsx +4 -0
  252. package/kotlin/fixtures/import-layout/app/index+spa.tsx +1 -0
  253. package/kotlin/fixtures/import-layout/package.json +23 -0
  254. package/kotlin/fixtures/import-layout/tsconfig.json +20 -0
  255. package/kotlin/fixtures/import-layout/vite.config.ts +20 -0
  256. package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +18 -0
  257. package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +7 -0
  258. package/kotlin/fixtures/import-navigation/Navigation.kt +104 -0
  259. package/kotlin/fixtures/import-navigation/README.md +31 -0
  260. package/kotlin/fixtures/import-navigation/app/_layout.tsx +4 -0
  261. package/kotlin/fixtures/import-navigation/app/index+spa.tsx +1 -0
  262. package/kotlin/fixtures/import-navigation/package.json +23 -0
  263. package/kotlin/fixtures/import-navigation/tsconfig.json +20 -0
  264. package/kotlin/fixtures/import-navigation/vite.config.ts +20 -0
  265. package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +29 -0
  266. package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +7 -0
  267. package/kotlin/fixtures/import-overlays/Overlays.kt +93 -0
  268. package/kotlin/fixtures/import-overlays/README.md +34 -0
  269. package/kotlin/fixtures/import-overlays/app/_layout.tsx +4 -0
  270. package/kotlin/fixtures/import-overlays/app/index+spa.tsx +1 -0
  271. package/kotlin/fixtures/import-overlays/package.json +23 -0
  272. package/kotlin/fixtures/import-overlays/tsconfig.json +20 -0
  273. package/kotlin/fixtures/import-overlays/vite.config.ts +20 -0
  274. package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +13 -0
  275. package/kotlin/fixtures/import-scroll/README.md +23 -0
  276. package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +7 -0
  277. package/kotlin/fixtures/import-scroll/Scroll.kt +91 -0
  278. package/kotlin/fixtures/import-scroll/app/_layout.tsx +4 -0
  279. package/kotlin/fixtures/import-scroll/app/index+spa.tsx +1 -0
  280. package/kotlin/fixtures/import-scroll/package.json +23 -0
  281. package/kotlin/fixtures/import-scroll/tsconfig.json +20 -0
  282. package/kotlin/fixtures/import-scroll/vite.config.ts +20 -0
  283. package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +44 -0
  284. package/kotlin/fixtures/import-selection/README.md +32 -0
  285. package/kotlin/fixtures/import-selection/Selection.d.kt.ts +8 -0
  286. package/kotlin/fixtures/import-selection/Selection.kt +78 -0
  287. package/kotlin/fixtures/import-selection/app/_layout.tsx +4 -0
  288. package/kotlin/fixtures/import-selection/app/index+spa.tsx +1 -0
  289. package/kotlin/fixtures/import-selection/package.json +23 -0
  290. package/kotlin/fixtures/import-selection/tsconfig.json +20 -0
  291. package/kotlin/fixtures/import-selection/vite.config.ts +20 -0
  292. package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +13 -0
  293. package/kotlin/fixtures/import-surfaces/README.md +22 -0
  294. package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +7 -0
  295. package/kotlin/fixtures/import-surfaces/Surfaces.kt +64 -0
  296. package/kotlin/fixtures/import-surfaces/app/_layout.tsx +4 -0
  297. package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +1 -0
  298. package/kotlin/fixtures/import-surfaces/package.json +23 -0
  299. package/kotlin/fixtures/import-surfaces/tsconfig.json +20 -0
  300. package/kotlin/fixtures/import-surfaces/vite.config.ts +20 -0
  301. package/kotlin/fixtures/import-values/ImportValues.native.tsx +35 -0
  302. package/kotlin/fixtures/import-values/README.md +23 -0
  303. package/kotlin/fixtures/import-values/Values.d.kt.ts +11 -0
  304. package/kotlin/fixtures/import-values/Values.kt +66 -0
  305. package/kotlin/fixtures/import-values/app/_layout.tsx +4 -0
  306. package/kotlin/fixtures/import-values/app/index+spa.tsx +1 -0
  307. package/kotlin/fixtures/import-values/package.json +23 -0
  308. package/kotlin/fixtures/import-values/tsconfig.json +20 -0
  309. package/kotlin/fixtures/import-values/vite.config.ts +20 -0
  310. package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +38 -0
  311. package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +36 -0
  312. package/kotlin/fixtures/kotlin-app-variants/Ime.kt +3 -1
  313. package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +50 -0
  314. package/kotlin/fork/NOTICE +6 -1
  315. package/kotlin/fork/README.md +15 -7
  316. package/kotlin/fork/build.sh +2 -0
  317. package/kotlin/fork/compose-worker-globals.patch +186 -4
  318. package/kotlin/fork/fork.json +2 -2
  319. package/kotlin/fork/skiko-worker-clock.patch +12 -0
  320. package/kotlin/toolchain.json +12 -12
  321. package/kotlin/tools/expo-types.main.kts +115 -0
  322. package/package.json +17 -23
  323. package/scripts/dev-server-scanner.ts +92 -66
  324. package/skills/contrast/SKILL.md +2 -2
  325. package/skills/peach-debug/SKILL.md +1 -1
  326. package/skills/peach-perf/SKILL.md +1 -1
  327. package/skills/peach-setup/SKILL.md +2 -1
  328. package/skills/peach-test/SKILL.md +8 -0
  329. package/src/assetCatalogContract.ts +43 -1
  330. package/src/bridge-contract-input.ts +46 -19
  331. package/src/bridge-contract.ts +0 -1
  332. package/src/bundle-channel.mjs +197 -84
  333. package/src/bundleGlobalFooter.ts +38 -0
  334. package/src/capture-contract.ts +107 -33
  335. package/src/cloud-contract.ts +5 -1
  336. package/src/cloud.ts +5 -2
  337. package/src/config.ts +22 -3
  338. package/src/configured-bundle-producer.ts +1 -1
  339. package/src/desktop-host-delivery.ts +95 -1
  340. package/src/dev-bundle-resolution.ts +1 -1
  341. package/src/expo-kotlin-bundle.ts +29 -0
  342. package/src/expo-kotlin-host.ts +201 -0
  343. package/src/expo-swift-host.ts +96 -76
  344. package/src/expoKotlinContract.ts +70 -0
  345. package/src/expoSwiftContract.ts +4 -25
  346. package/src/home-paths.ts +7 -4
  347. package/src/host/bridge-host.ts +23 -3
  348. package/src/host/fetch-proxy-handler.ts +13 -0
  349. package/src/host/fetch-proxy-overrides.ts +11 -0
  350. package/src/host/replacement-module-handler.ts +5 -2
  351. package/src/index.ts +3 -1
  352. package/src/kotlin-host.ts +463 -19
  353. package/src/metro-plugin.ts +56 -28
  354. package/src/metro-production-bundle.ts +2 -2
  355. package/src/native-app-identity-project.ts +190 -0
  356. package/src/native-app-identity.ts +367 -34
  357. package/src/native-firebase-config.ts +69 -15
  358. package/src/native-seam-manifest.ts +6 -0
  359. package/src/one-widget-discovery.ts +2 -1
  360. package/src/openstep-plist.ts +107 -0
  361. package/src/react-native-host-modules.ts +4 -0
  362. package/src/runtime-assets.ts +2 -2
  363. package/src/sim-client.ts +2 -2
  364. package/src/swift-binary-modules.ts +3 -1
  365. package/src/swift-compile.ts +286 -38
  366. package/src/swift-distribution-contract.ts +4 -0
  367. package/src/swift-distribution.lock.json +123 -12
  368. package/src/swift-distribution.ts +7 -3
  369. package/src/swift-host.ts +298 -83
  370. package/src/swift-package-manifest.ts +29 -2
  371. package/src/swift-package.ts +180 -10
  372. package/src/swift-packages.ts +4 -0
  373. package/src/swift-standin-emit.ts +4 -1
  374. package/src/swift-standins.ts +274 -15
  375. package/src/swift-submit.ts +62 -7
  376. package/src/swift-toolchain.ts +6 -1
  377. package/src/swiftContract.ts +126 -3
  378. package/src/vite-plugin-one.ts +13 -16
  379. package/src/vite-plugin-swift.ts +4 -5
  380. package/src/vite-plugin.ts +360 -144
  381. package/src/vite.d.ts +1 -1
  382. package/src/worker-system-chunks.ts +221 -0
  383. package/src/worklet-executable.ts +348 -38
  384. package/src/worklets-babel.ts +1 -1
  385. package/src/xcode-project.ts +243 -127
  386. package/dist-lib/agent-identity.cjs +0 -39
  387. package/dist-lib/agent-sessions.cjs +0 -760
  388. package/dist-lib/cli-constants.cjs +0 -32
  389. package/dist-lib/render-mode.cjs +0 -44
  390. package/kotlin/AppSources/android/view/View.kt +0 -74
  391. package/kotlin/AppSources/android/widget/FrameLayout.kt +0 -6
  392. package/src/render-mode.ts +0 -15
  393. /package/kotlin/{AppSources → AndroidViewSources}/android/content/Context.kt +0 -0
  394. /package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Configuration.kt +0 -0
  395. /package/kotlin/{AppSources → AndroidViewSources}/android/graphics/Color.kt +0 -0
  396. /package/kotlin/{AppSources → AndroidViewSources}/android/util/DisplayMetrics.kt +0 -0
  397. /package/kotlin/{AppSources → AndroidViewSources}/dev/peach/app/JavaFormat.kt +0 -0
  398. /package/kotlin/{AppSources → Sources}/java/lang/Runnable.kt +0 -0
@@ -3,15 +3,29 @@
3
3
  // against the Xcode Release build settings, or `android/app/build.gradle`), so
4
4
  // a simulator tenant reports the same identity the native Release build does.
5
5
  //
6
- // the values travel in a bundle footer or a response header bound to unchanged
7
- // artifact bytes. tenant boot validates both before evaluation and clears the
8
- // realm store when neither carries identity.
6
+ // identity travels in a bundle footer or response header. the one updates
7
+ // receipt travels only in the response header so publisher JavaScript stays
8
+ // unchanged. tenant boot validates both before evaluation and clears the realm
9
+ // store when neither carries identity.
10
+
11
+ import { parseOpenStepPlist } from './openstep-plist'
9
12
 
10
13
  export type NativeAppIdentityPlatform = 'ios' | 'android'
11
14
 
15
+ export interface NativeOneUpdatesReceipt {
16
+ enabled: boolean
17
+ url: string | null
18
+ runtimeVersion: string | null
19
+ embeddedManifestJson: string | null
20
+ }
21
+
12
22
  export interface NativeAppIdentity {
13
23
  // the user-facing name (CFBundleDisplayName, else CFBundleName).
14
24
  applicationName: string | null
25
+ // the literal CFBundleDisplayName, without a bundle-name fallback.
26
+ applicationDisplayName?: string
27
+ // the processed ARGB integer from RCTRootViewBackgroundColor.
28
+ rootViewBackgroundColor?: number
15
29
  // CFBundleName, which CFNetwork stamps into the default User-Agent and iOS's
16
30
  // own prompts use (the ASWebAuthenticationSession consent alert); distinct
17
31
  // from the display name. absent in footers built before it was carried.
@@ -23,7 +37,16 @@ export interface NativeAppIdentity {
23
37
  permissionUsageDescriptions?: Readonly<Record<string, unknown>>
24
38
  urlQuerySchemes?: readonly unknown[]
25
39
  appIntentIdentifiers?: readonly string[]
40
+ backgroundTaskIdentifiers?: readonly string[]
41
+ backgroundModes?: readonly string[]
42
+ supportedInterfaceOrientations?: readonly string[]
43
+ ipadSupportedInterfaceOrientations?: readonly string[]
44
+ defaultScreenOrientationMask?: string
45
+ shareIntoAppGroupId?: string
26
46
  alternateIconNames?: readonly string[]
47
+ // one updates configuration travels only in response metadata, never in guest
48
+ // code or inferred from JavaScript build flags.
49
+ oneUpdates?: NativeOneUpdatesReceipt
27
50
  }
28
51
 
29
52
  export type NativeAppIdentityFiles = Record<string, string>
@@ -47,6 +70,24 @@ function nonEmptyString(value: unknown): string | null {
47
70
 
48
71
  export function isNativeAppIdentity(value: unknown): value is NativeAppIdentity {
49
72
  if (!isRecord(value)) return false
73
+ const oneUpdates = value.oneUpdates
74
+ if (
75
+ oneUpdates !== undefined &&
76
+ (!isRecord(oneUpdates) ||
77
+ typeof oneUpdates.enabled !== 'boolean' ||
78
+ (oneUpdates.url !== null && typeof oneUpdates.url !== 'string') ||
79
+ (oneUpdates.runtimeVersion !== null &&
80
+ typeof oneUpdates.runtimeVersion !== 'string') ||
81
+ (oneUpdates.embeddedManifestJson !== null &&
82
+ typeof oneUpdates.embeddedManifestJson !== 'string') ||
83
+ (oneUpdates.enabled &&
84
+ (typeof oneUpdates.url !== 'string' ||
85
+ oneUpdates.url.length === 0 ||
86
+ typeof oneUpdates.runtimeVersion !== 'string' ||
87
+ oneUpdates.runtimeVersion.length === 0)))
88
+ ) {
89
+ return false
90
+ }
50
91
  const descriptions = value.permissionUsageDescriptions
51
92
  if (descriptions !== undefined && !isRecord(descriptions)) return false
52
93
  if (value.urlQuerySchemes !== undefined && !Array.isArray(value.urlQuerySchemes))
@@ -57,6 +98,52 @@ export function isNativeAppIdentity(value: unknown): value is NativeAppIdentity
57
98
  !value.appIntentIdentifiers.every((identifier) => typeof identifier === 'string'))
58
99
  )
59
100
  return false
101
+ if (
102
+ value.backgroundTaskIdentifiers !== undefined &&
103
+ (!Array.isArray(value.backgroundTaskIdentifiers) ||
104
+ !value.backgroundTaskIdentifiers.every(
105
+ (identifier) => typeof identifier === 'string',
106
+ ))
107
+ )
108
+ return false
109
+ if (
110
+ value.backgroundModes !== undefined &&
111
+ (!Array.isArray(value.backgroundModes) ||
112
+ !value.backgroundModes.every((mode) => typeof mode === 'string'))
113
+ )
114
+ return false
115
+ if (
116
+ value.shareIntoAppGroupId !== undefined &&
117
+ typeof value.shareIntoAppGroupId !== 'string'
118
+ )
119
+ return false
120
+ if (
121
+ value.applicationDisplayName !== undefined &&
122
+ typeof value.applicationDisplayName !== 'string'
123
+ )
124
+ return false
125
+ if (
126
+ value.rootViewBackgroundColor !== undefined &&
127
+ (typeof value.rootViewBackgroundColor !== 'number' ||
128
+ !Number.isInteger(value.rootViewBackgroundColor))
129
+ )
130
+ return false
131
+ for (const orientations of [
132
+ value.supportedInterfaceOrientations,
133
+ value.ipadSupportedInterfaceOrientations,
134
+ ]) {
135
+ if (
136
+ orientations !== undefined &&
137
+ (!Array.isArray(orientations) ||
138
+ !orientations.every((orientation) => typeof orientation === 'string'))
139
+ )
140
+ return false
141
+ }
142
+ if (
143
+ value.defaultScreenOrientationMask !== undefined &&
144
+ typeof value.defaultScreenOrientationMask !== 'string'
145
+ )
146
+ return false
60
147
  const platform = value.platform
61
148
  if (
62
149
  value.alternateIconNames !== undefined &&
@@ -86,6 +173,12 @@ export function readNativeAppIdentity(value: unknown): NativeAppIdentity | null
86
173
  }
87
174
  return {
88
175
  applicationName: value.applicationName,
176
+ ...(value.applicationDisplayName === undefined
177
+ ? {}
178
+ : { applicationDisplayName: value.applicationDisplayName }),
179
+ ...(value.rootViewBackgroundColor === undefined
180
+ ? {}
181
+ : { rootViewBackgroundColor: value.rootViewBackgroundColor }),
89
182
  ...(value.bundleName === undefined ? {} : { bundleName: value.bundleName }),
90
183
  appVersion: value.appVersion,
91
184
  buildVersion: value.buildVersion,
@@ -102,9 +195,43 @@ export function readNativeAppIdentity(value: unknown): NativeAppIdentity | null
102
195
  ...(value.appIntentIdentifiers === undefined
103
196
  ? {}
104
197
  : { appIntentIdentifiers: [...value.appIntentIdentifiers] }),
198
+ ...(value.backgroundTaskIdentifiers === undefined
199
+ ? {}
200
+ : { backgroundTaskIdentifiers: [...value.backgroundTaskIdentifiers] }),
201
+ ...(value.supportedInterfaceOrientations === undefined
202
+ ? {}
203
+ : {
204
+ supportedInterfaceOrientations: [...value.supportedInterfaceOrientations],
205
+ }),
206
+ ...(value.ipadSupportedInterfaceOrientations === undefined
207
+ ? {}
208
+ : {
209
+ ipadSupportedInterfaceOrientations: [
210
+ ...value.ipadSupportedInterfaceOrientations,
211
+ ],
212
+ }),
213
+ ...(value.defaultScreenOrientationMask === undefined
214
+ ? {}
215
+ : { defaultScreenOrientationMask: value.defaultScreenOrientationMask }),
216
+ ...(value.backgroundModes === undefined
217
+ ? {}
218
+ : { backgroundModes: [...value.backgroundModes] }),
219
+ ...(value.shareIntoAppGroupId === undefined
220
+ ? {}
221
+ : { shareIntoAppGroupId: value.shareIntoAppGroupId }),
105
222
  ...(value.urlQuerySchemes === undefined
106
223
  ? {}
107
224
  : { urlQuerySchemes: [...value.urlQuerySchemes] }),
225
+ ...(value.oneUpdates === undefined
226
+ ? {}
227
+ : {
228
+ oneUpdates: {
229
+ enabled: value.oneUpdates.enabled,
230
+ url: value.oneUpdates.url,
231
+ runtimeVersion: value.oneUpdates.runtimeVersion,
232
+ embeddedManifestJson: value.oneUpdates.embeddedManifestJson,
233
+ },
234
+ }),
108
235
  }
109
236
  }
110
237
 
@@ -145,7 +272,10 @@ export function readNativeAppIdentityFromBundle(
145
272
  }
146
273
  const identity = readNativeAppIdentity(value)
147
274
  if (!identity) throw new Error('native app identity response header is malformed')
148
- if (footer && !equalIdentityValues(identity, readNativeAppIdentity(footer))) {
275
+ const footerIdentity = footer ? readNativeAppIdentity(footer) : null
276
+ const headerBase = { ...identity, oneUpdates: undefined }
277
+ const footerBase = footerIdentity && { ...footerIdentity, oneUpdates: undefined }
278
+ if (footerIdentity && !equalIdentityValues(headerBase, footerBase)) {
149
279
  throw new Error('native app identity response header conflicts with bundle footer')
150
280
  }
151
281
  return identity
@@ -168,7 +298,10 @@ export function getAppliedNativeAppIdentity(): NativeAppIdentity | null {
168
298
  // one line, so the footer survives a bundle whose last statement is the Metro
169
299
  // module identity footer and can be found again by reading the text.
170
300
  export function createNativeAppIdentityFooter(identity: NativeAppIdentity): string {
171
- const payload = JSON.stringify(identity)
301
+ const footerIdentity = { ...identity }
302
+ // native update configuration belongs to response metadata, never guest code.
303
+ delete footerIdentity.oneUpdates
304
+ const payload = JSON.stringify(footerIdentity)
172
305
  .replace(/\u2028/g, '\\u2028')
173
306
  .replace(/\u2029/g, '\\u2029')
174
307
  .replace(/<\//g, '<\\/')
@@ -196,6 +329,9 @@ export function readNativeAppIdentityFooter(source: string): NativeAppIdentity |
196
329
  if (!isNativeAppIdentity(parsed)) {
197
330
  throw new Error('native app identity footer is malformed')
198
331
  }
332
+ if (parsed.oneUpdates !== undefined) {
333
+ throw new Error('native app identity footer cannot contain one updates receipt')
334
+ }
199
335
  return parsed
200
336
  }
201
337
 
@@ -372,39 +508,162 @@ function appTargetReleaseSettings(
372
508
  function nativeAppIdentityFromIos(
373
509
  files: NativeAppIdentityFiles,
374
510
  parseInfoPlist: (source: string) => unknown,
511
+ iosTarget?: string,
375
512
  ): NativeAppIdentity | null {
376
- const pbxprojPath = Object.keys(files).find((path) =>
377
- /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path),
378
- )
513
+ const pbxprojPath =
514
+ Object.keys(files).find((path) =>
515
+ /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path),
516
+ ) ??
517
+ Object.keys(files).find((path) =>
518
+ /(?:^|\/)[^/]+\.xcodeproj\/project\.pbxproj$/.test(path),
519
+ ) ??
520
+ Object.keys(files).find((path) => /(?:^|\/)project\.pbxproj$/.test(path))
379
521
  if (!pbxprojPath) return null
380
522
  const pbxproj = files[pbxprojPath]
381
- // the installed app is the target Xcode builds as an application. an App
382
- // Clip or extension carries its own AppDelegate and Info.plist, so a target
383
- // directory alone cannot say which binary is the app.
384
- const applicationTargets = new Set(
385
- [
386
- ...pbxproj.matchAll(
387
- /isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g,
388
- ),
389
- ].map((match) => match[1]),
523
+
524
+ let objects: Record<string, unknown> = {}
525
+ try {
526
+ const parsed = parseOpenStepPlist(pbxproj)
527
+ if (isRecord(parsed)) {
528
+ objects = isRecord(parsed.objects) ? parsed.objects : parsed
529
+ }
530
+ } catch {
531
+ // fallback if unparseable
532
+ }
533
+
534
+ interface NativeTargetSummary {
535
+ id: string
536
+ name: string
537
+ productType?: string
538
+ buildConfigurationList?: string
539
+ }
540
+ const targets: NativeTargetSummary[] = []
541
+ for (const [id, obj] of Object.entries(objects)) {
542
+ if (isRecord(obj) && obj.isa === 'PBXNativeTarget' && typeof obj.name === 'string') {
543
+ targets.push({
544
+ id,
545
+ name: obj.name,
546
+ productType: typeof obj.productType === 'string' ? obj.productType : undefined,
547
+ buildConfigurationList:
548
+ typeof obj.buildConfigurationList === 'string'
549
+ ? obj.buildConfigurationList
550
+ : undefined,
551
+ })
552
+ }
553
+ }
554
+ const applicationTargetNames = new Set(
555
+ targets
556
+ .filter((t) => t.productType === 'com.apple.product-type.application')
557
+ .map((t) => t.name),
390
558
  )
391
- const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/
392
- const targetName =
393
- Object.keys(files)
559
+ if (applicationTargetNames.size === 0) {
560
+ for (const match of pbxproj.matchAll(
561
+ /isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g,
562
+ )) {
563
+ applicationTargetNames.add(match[1])
564
+ }
565
+ }
566
+
567
+ let targetName: string | null = null
568
+ if (iosTarget) {
569
+ if (applicationTargetNames.has(iosTarget)) {
570
+ targetName = iosTarget
571
+ } else {
572
+ return null
573
+ }
574
+ } else if (applicationTargetNames.size === 1) {
575
+ targetName = [...applicationTargetNames][0]!
576
+ } else if (applicationTargetNames.size > 1) {
577
+ const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/
578
+ const delegateTargets = Object.keys(files)
394
579
  .filter((path) => !path.includes('/Pods/'))
395
580
  .map((path) => path.match(appDelegatePattern)?.[1])
396
- .find((name) => name !== undefined && applicationTargets.has(name)) ?? null
397
- if (!targetName) return null
581
+ .filter(
582
+ (name): name is string =>
583
+ typeof name === 'string' && applicationTargetNames.has(name),
584
+ )
585
+ if (delegateTargets.length === 1) {
586
+ targetName = delegateTargets[0]!
587
+ } else {
588
+ return null
589
+ }
590
+ } else {
591
+ return null
592
+ }
398
593
 
399
- const plist = files[`ios/${targetName}/Info.plist`]
400
- if (!plist) return null
594
+ const selectedTarget = targets.find((t) => t.name === targetName)
595
+ const settings: Record<string, string> = {
596
+ TARGET_NAME: targetName,
597
+ PRODUCT_NAME: targetName,
598
+ }
599
+ for (const obj of Object.values(objects)) {
600
+ if (
601
+ isRecord(obj) &&
602
+ obj.isa === 'PBXProject' &&
603
+ typeof obj.buildConfigurationList === 'string'
604
+ ) {
605
+ const listObj = objects[obj.buildConfigurationList]
606
+ if (isRecord(listObj) && Array.isArray(listObj.buildConfigurations)) {
607
+ for (const configId of listObj.buildConfigurations) {
608
+ if (typeof configId === 'string') {
609
+ const configObj = objects[configId]
610
+ if (
611
+ isRecord(configObj) &&
612
+ configObj.name === 'Release' &&
613
+ isRecord(configObj.buildSettings)
614
+ ) {
615
+ for (const [k, v] of Object.entries(configObj.buildSettings)) {
616
+ if (typeof v === 'string') settings[k] = v
617
+ }
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ }
624
+ if (selectedTarget?.buildConfigurationList) {
625
+ const listObj = objects[selectedTarget.buildConfigurationList]
626
+ if (isRecord(listObj) && Array.isArray(listObj.buildConfigurations)) {
627
+ for (const configId of listObj.buildConfigurations) {
628
+ if (typeof configId === 'string') {
629
+ const configObj = objects[configId]
630
+ if (
631
+ isRecord(configObj) &&
632
+ configObj.name === 'Release' &&
633
+ isRecord(configObj.buildSettings)
634
+ ) {
635
+ for (const [k, v] of Object.entries(configObj.buildSettings)) {
636
+ if (typeof v === 'string') settings[k] = v
637
+ }
638
+ }
639
+ }
640
+ }
641
+ }
642
+ } else {
643
+ const legacy = appTargetReleaseSettings(
644
+ pbxproj,
645
+ targetName,
646
+ `${targetName}/Info.plist`,
647
+ )
648
+ if (legacy) Object.assign(settings, legacy)
649
+ }
401
650
 
402
- const settings = appTargetReleaseSettings(
403
- pbxproj,
404
- targetName,
405
- `${targetName}/Info.plist`,
406
- )
407
- if (!settings) return null
651
+ let plist: string | undefined
652
+ const infoPlistPath = settings.INFOPLIST_FILE
653
+ if (infoPlistPath) {
654
+ const normalized = infoPlistPath.replace(/^"|"$/g, '')
655
+ plist =
656
+ files[`ios/${normalized}`] ??
657
+ files[normalized] ??
658
+ Object.entries(files).find(([k]) => k.endsWith(`/${normalized}`))?.[1]
659
+ }
660
+ if (!plist) {
661
+ plist =
662
+ files[`ios/${targetName}/Info.plist`] ??
663
+ files[`${targetName}/Info.plist`] ??
664
+ Object.entries(files).find(([k]) => k.endsWith(`/${targetName}/Info.plist`))?.[1]
665
+ }
666
+ if (!plist) return null
408
667
 
409
668
  const bundleIdentifier = identityFieldFrom(
410
669
  settings,
@@ -444,6 +703,19 @@ function nativeAppIdentityFromIos(
444
703
  // with the build-side parser; the browser transport has no XML dependency.
445
704
  const dictionary = parseInfoPlist(plist)
446
705
  if (!isRecord(dictionary)) throw new Error('native app Info.plist is not a dictionary')
706
+ const declaredDisplayName = dictionary.CFBundleDisplayName
707
+ const applicationDisplayName =
708
+ typeof declaredDisplayName === 'string'
709
+ ? resolveBuildSetting(declaredDisplayName, settings)
710
+ : undefined
711
+ if (applicationDisplayName === null)
712
+ throw new Error('native app Info.plist CFBundleDisplayName is unresolved')
713
+ const declaredBackgroundColor = dictionary.RCTRootViewBackgroundColor
714
+ const rootViewBackgroundColor =
715
+ typeof declaredBackgroundColor === 'number' &&
716
+ Number.isInteger(declaredBackgroundColor)
717
+ ? declaredBackgroundColor
718
+ : undefined
447
719
  const icons = dictionary.CFBundleIcons
448
720
  const alternates = isRecord(icons) ? icons.CFBundleAlternateIcons : undefined
449
721
  const alternateIconNames = new Set<string>()
@@ -470,6 +742,48 @@ function nativeAppIdentityFromIos(
470
742
  Array.isArray(intentIds) && intentIds.every((value) => typeof value === 'string')
471
743
  ? intentIds.map((value: string) => resolveBuildSetting(value, settings))
472
744
  : []
745
+ const backgroundIds = dictionary.BGTaskSchedulerPermittedIdentifiers
746
+ const backgroundTaskIdentifiers =
747
+ Array.isArray(backgroundIds) &&
748
+ backgroundIds.every((value) => typeof value === 'string')
749
+ ? backgroundIds.map((value: string) => resolveBuildSetting(value, settings))
750
+ : []
751
+ const declaredModes = dictionary.UIBackgroundModes
752
+ const backgroundModes =
753
+ Array.isArray(declaredModes) &&
754
+ declaredModes.every((value) => typeof value === 'string')
755
+ ? declaredModes.map((value: string) => resolveBuildSetting(value, settings))
756
+ : []
757
+ // the root orientation controller casts each plist list as a whole [String].
758
+ const screenOrientationLists = [
759
+ 'UISupportedInterfaceOrientations',
760
+ 'UISupportedInterfaceOrientations~ipad',
761
+ ].map((key) => {
762
+ const list = dictionary[key]
763
+ if (!Array.isArray(list) || !list.every((value) => typeof value === 'string'))
764
+ return []
765
+ return list.map((value: string) => {
766
+ const resolved = resolveBuildSetting(value, settings)
767
+ if (resolved === null) throw new Error(`native app Info.plist ${key} is unresolved`)
768
+ return resolved
769
+ })
770
+ })
771
+ const [supportedInterfaceOrientations, ipadSupportedInterfaceOrientations] =
772
+ screenOrientationLists
773
+ const declaredDefaultOrientationMask = dictionary.EXDefaultScreenOrientationMask
774
+ const defaultScreenOrientationMask =
775
+ typeof declaredDefaultOrientationMask === 'string'
776
+ ? resolveBuildSetting(declaredDefaultOrientationMask, settings)
777
+ : undefined
778
+ if (defaultScreenOrientationMask === null)
779
+ throw new Error('native app Info.plist EXDefaultScreenOrientationMask is unresolved')
780
+ const declaredShareGroup = dictionary.ExpoShareIntoAppGroupId
781
+ const shareIntoAppGroupId =
782
+ typeof declaredShareGroup === 'string'
783
+ ? resolveBuildSetting(declaredShareGroup, settings)
784
+ : undefined
785
+ if (shareIntoAppGroupId === null)
786
+ throw new Error('native app Info.plist ExpoShareIntoAppGroupId is unresolved')
473
787
  const querySchemes = dictionary.LSApplicationQueriesSchemes
474
788
  if (querySchemes !== undefined && !Array.isArray(querySchemes)) {
475
789
  throw new Error('native app Info.plist LSApplicationQueriesSchemes is not an array')
@@ -483,7 +797,11 @@ function nativeAppIdentityFromIos(
483
797
  })
484
798
  const permissionUsageDescriptions: Record<string, unknown> = {}
485
799
  for (const [key, value] of Object.entries(dictionary)) {
486
- if (!/^NS.*UsageDescription$/.test(key)) continue
800
+ if (
801
+ !/^NS.*UsageDescription$/.test(key) &&
802
+ key !== 'NSLocationTemporaryUsageDescriptionDictionary'
803
+ )
804
+ continue
487
805
  // retain non-string plist values: the native factory checks key presence.
488
806
  if (typeof value !== 'string') {
489
807
  permissionUsageDescriptions[key] = value
@@ -509,14 +827,28 @@ function nativeAppIdentityFromIos(
509
827
 
510
828
  return {
511
829
  applicationName,
830
+ ...(applicationDisplayName === undefined ? {} : { applicationDisplayName }),
831
+ ...(rootViewBackgroundColor === undefined ? {} : { rootViewBackgroundColor }),
512
832
  bundleName,
513
833
  appVersion,
514
834
  buildVersion,
515
835
  bundleIdentifier,
516
836
  platform: 'ios',
837
+ supportedInterfaceOrientations,
838
+ ipadSupportedInterfaceOrientations,
839
+ ...(defaultScreenOrientationMask === undefined
840
+ ? {}
841
+ : { defaultScreenOrientationMask }),
517
842
  alternateIconNames: [...alternateIconNames],
518
843
  permissionUsageDescriptions,
519
844
  urlQuerySchemes,
845
+ ...(shareIntoAppGroupId === undefined ? {} : { shareIntoAppGroupId }),
846
+ backgroundTaskIdentifiers: backgroundTaskIdentifiers.every((value) => value !== null)
847
+ ? backgroundTaskIdentifiers
848
+ : [],
849
+ backgroundModes: backgroundModes.every((value) => value !== null)
850
+ ? backgroundModes
851
+ : [],
520
852
  appIntentIdentifiers: appIntentIdentifiers.every((value) => value !== null)
521
853
  ? appIntentIdentifiers
522
854
  : [],
@@ -535,13 +867,14 @@ export function canExtractNativeAppIdentity(platform: unknown): platform is 'ios
535
867
  // read the identity a bundle builder transports. files are project-relative
536
868
  // paths to their text, so both a checked-out app directory and a stored
537
869
  // project file map resolve identically. null when the project carries no
538
- // resolvable native config — the builder then ships no footer.
870
+ // resolvable native config.
539
871
  export function extractNativeAppIdentityFromFiles(
540
872
  files: NativeAppIdentityFiles,
541
- platform: NativeAppIdentityPlatform,
873
+ platform: unknown,
542
874
  parseInfoPlist: (source: string) => unknown,
875
+ iosTarget?: string,
543
876
  ): NativeAppIdentity | null {
544
877
  return canExtractNativeAppIdentity(platform)
545
- ? nativeAppIdentityFromIos(files, parseInfoPlist)
878
+ ? nativeAppIdentityFromIos(files, parseInfoPlist, iosTarget)
546
879
  : null
547
880
  }
@@ -1,7 +1,8 @@
1
1
  // read native default initialization inputs before the guest Firebase SDK starts.
2
2
  // expose only FirebaseOptions, never a partner-specific extra configuration.
3
+ import { spawnSync } from 'node:child_process'
3
4
  import fs from 'node:fs'
4
- import { createRequire } from 'node:module'
5
+ import os from 'node:os'
5
6
  import path from 'node:path'
6
7
  import { isRecord } from './is-record'
7
8
  import { parsePlistSource } from './plist'
@@ -10,6 +11,7 @@ import { listXcodeAppTargets, readXcodeAppTarget } from './xcode-project'
10
11
  export function readNativeFirebaseConfig(
11
12
  projectRoot: string,
12
13
  platforms: readonly string[] = ['ios', 'android'],
14
+ iosTarget?: string,
13
15
  ) {
14
16
  const result: Record<string, unknown> = {}
15
17
  let config: Record<string, unknown> = {}
@@ -30,15 +32,45 @@ export function readNativeFirebaseConfig(
30
32
  'app.config.mjs',
31
33
  ].some((name) => fs.existsSync(path.join(projectRoot, name)))
32
34
  if (staticExpo || dynamicExpo) {
33
- const require = createRequire(path.join(projectRoot, 'package.json'))
34
- const configModule: unknown = require('@expo/config')
35
- if (!isRecord(configModule) || typeof configModule.getConfig !== 'function')
36
- throw new Error('Expo native configuration is unavailable.')
37
- const resolved: unknown = Reflect.apply(configModule.getConfig, configModule, [
38
- projectRoot,
39
- { skipSDKVersionRequirement: true },
40
- ])
41
- config = isRecord(resolved) && isRecord(resolved.exp) ? resolved.exp : {}
35
+ // expo's synchronous ESM/TypeScript loader requires Node's module evaluator.
36
+ // a private result file keeps authored console output off the configuration wire.
37
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'peach-expo-config-'))
38
+ const resultPath = path.join(directory, 'config.json')
39
+ try {
40
+ const child = spawnSync(
41
+ 'node',
42
+ [
43
+ '--input-type=commonjs',
44
+ '-e',
45
+ `const { createRequire } = require('node:module');
46
+ const { writeFileSync } = require('node:fs');
47
+ const { join } = require('node:path');
48
+ const root = process.argv[1];
49
+ const { getConfig } = createRequire(join(root, 'package.json'))('@expo/config');
50
+ const result = JSON.stringify(getConfig(root, { skipSDKVersionRequirement: true }).exp);
51
+ if (Buffer.byteLength(result) > 1024 * 1024) throw new Error('Expo native configuration exceeds 1 MiB.');
52
+ writeFileSync(process.argv[2], result, { mode: 0o600 });`,
53
+ path.resolve(projectRoot),
54
+ resultPath,
55
+ ],
56
+ {
57
+ cwd: projectRoot,
58
+ encoding: 'utf8',
59
+ stdio: ['ignore', 'ignore', 'pipe'],
60
+ timeout: 30_000,
61
+ maxBuffer: 1024 * 1024,
62
+ },
63
+ )
64
+ if (child.error) throw child.error
65
+ if (child.status !== 0)
66
+ throw new Error(child.stderr || 'Expo native configuration evaluation failed.')
67
+ const resolved: unknown = JSON.parse(fs.readFileSync(resultPath, 'utf8'))
68
+ if (!isRecord(resolved))
69
+ throw new Error('Expo native configuration is unavailable.')
70
+ config = resolved
71
+ } finally {
72
+ fs.rmSync(directory, { recursive: true, force: true })
73
+ }
42
74
  }
43
75
  const ios = config.ios
44
76
  if (
@@ -55,10 +87,17 @@ export function readNativeFirebaseConfig(
55
87
  if (projects.length === 1) {
56
88
  const xcodeproj = path.join(iosRoot, projects[0].name)
57
89
  const appTargets = listXcodeAppTargets(xcodeproj)
58
- if (appTargets.length > 1)
90
+ if (!iosTarget && appTargets.length > 1)
59
91
  throw new Error('native Firebase configuration requires a selected app target')
60
- if (appTargets.length === 1) {
61
- const app = readXcodeAppTarget(xcodeproj, appTargets[0])
92
+ if (iosTarget || appTargets.length === 1) {
93
+ const app = readXcodeAppTarget(
94
+ xcodeproj,
95
+ iosTarget ?? appTargets[0],
96
+ undefined,
97
+ {
98
+ requireSources: false,
99
+ },
100
+ )
62
101
  const delegates = app.allSources.filter((file) =>
63
102
  /^AppDelegate\.(swift|mm|m)$/.test(path.basename(file)),
64
103
  )
@@ -69,7 +108,7 @@ export function readNativeFirebaseConfig(
69
108
  token.startsWith('"') ? '""' : token.replace(/[^\n]/g, ' '),
70
109
  )
71
110
  const launch =
72
- /(?:\bfunc\s+application\s*\([\s\S]*?didFinishLaunchingWithOptions[\s\S]*?\)\s*->\s*Bool|^-\s*\(BOOL\)\s*application\s*:[\s\S]*?didFinishLaunchingWithOptions[^\{]*)\s*\{/m.exec(
111
+ /(?:\bfunc\s+application\s*\([\s\S]*?didFinishLaunchingWithOptions[\s\S]*?\)\s*->\s*Bool|^-\s*\(BOOL\)\s*application\s*:[\s\S]*?didFinishLaunchingWithOptions[^{]*)\s*\{/m.exec(
73
112
  source,
74
113
  )
75
114
  if (!launch) return false
@@ -86,9 +125,24 @@ export function readNativeFirebaseConfig(
86
125
  if (token === '}') depth -= 1
87
126
  if (depth === 0) return false
88
127
  }
128
+ let conditionalDepth = 0
129
+ for (const directive of before.matchAll(
130
+ /^\s*#\s*(if|ifdef|ifndef|elseif|elif|else|endif)\b/gm,
131
+ )) {
132
+ const kind = directive[1]
133
+ if (kind === 'if' || kind === 'ifdef' || kind === 'ifndef') {
134
+ conditionalDepth += 1
135
+ } else if (conditionalDepth === 0) {
136
+ conditionalDepth = -1
137
+ break
138
+ } else if (kind === 'endif') {
139
+ conditionalDepth -= 1
140
+ }
141
+ }
89
142
  if (
90
143
  depth !== 1 ||
91
- /\b(?:return|throw)\b|^\s*#(?:if|elseif|else)/m.test(before)
144
+ conditionalDepth !== 0 ||
145
+ /\b(?:return|throw)\b/.test(before)
92
146
  )
93
147
  throw new Error(
94
148
  'native Firebase initialization must be unconditional in the app launch method',
@@ -148,6 +148,12 @@ export const NATIVE_SEAM_MANIFEST: NativeSeamEntry[] = [
148
148
  notes:
149
149
  'ASAuthorizationAppleIDButton native view — render the system button surface while upstream AppleAuthenticationButton.tsx remains unchanged',
150
150
  },
151
+ {
152
+ pkg: 'expo-asset',
153
+ seamBasenames: ['resolveAssetSource'],
154
+ target: path.resolve(NATIVE_SEAMS_DIR, '../rn-libraries/resolveAssetSource.ts'),
155
+ notes: 'the native Image resolver and transformer registration share one owner',
156
+ },
151
157
  {
152
158
  pkg: '@bam.tech/react-native-image-resizer',
153
159
  // a Metro bundle reaches the same seam through the ImageResizer