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.
- package/README.md +28 -22
- package/cli/app-catalog.ts +29 -14
- package/cli/app-fonts.ts +5 -3
- package/cli/bridge-flow-runner.ts +40 -29
- package/cli/cloud-client.ts +57 -21
- package/cli/cloud-test-tunnel.ts +12 -18
- package/cli/commands/agent.ts +1 -1
- package/cli/commands/app-fonts.ts +3 -3
- package/cli/commands/assert.ts +8 -8
- package/cli/commands/auth.ts +8 -3
- package/cli/commands/box/bundle-plane.ts +13 -1
- package/cli/commands/box.ts +16 -5
- package/cli/commands/compat.ts +1 -1
- package/cli/commands/config.ts +2 -2
- package/cli/commands/control.ts +125 -71
- package/cli/commands/cpu-profile.ts +45 -1
- package/cli/commands/daemon.ts +131 -33
- package/cli/commands/debug.ts +24 -17
- package/cli/commands/desktop.ts +7 -1
- package/cli/commands/detect.ts +101 -0
- package/cli/commands/detox.ts +6 -6
- package/cli/commands/device.ts +12 -12
- package/cli/commands/electron.ts +32 -14
- package/cli/commands/flow.ts +49 -41
- package/cli/commands/guest-host.ts +39 -1
- package/cli/commands/inspect/core.ts +14 -8
- package/cli/commands/inspect/list.ts +1 -1
- package/cli/commands/inspect/settle.ts +5 -3
- package/cli/commands/inspect/settling.ts +229 -44
- package/cli/commands/inspect/wait-idle.ts +3 -2
- package/cli/commands/inspect.ts +105 -93
- package/cli/commands/install-cli.ts +36 -15
- package/cli/commands/install-desktop.ts +68 -195
- package/cli/commands/kotlin.ts +6 -5
- package/cli/commands/login.ts +5 -5
- package/cli/commands/maestro-compare.ts +554 -0
- package/cli/commands/maestro-generate.ts +6 -3
- package/cli/commands/maestro.ts +22 -0
- package/cli/commands/mode.ts +2 -2
- package/cli/commands/no-bridge-hint.ts +6 -2
- package/cli/commands/platform.ts +35 -6
- package/cli/commands/preview.ts +6 -0
- package/cli/commands/project-report.ts +10 -12
- package/cli/commands/record.ts +37 -37
- package/cli/commands/runtime.ts +9 -9
- package/cli/commands/screenshot-layers.ts +4 -4
- package/cli/commands/screenshot-mode.ts +2 -2
- package/cli/commands/screenshot.ts +8 -8
- package/cli/commands/screenshots-capture.ts +2 -2
- package/cli/commands/serve.ts +9 -12
- package/cli/commands/setup.ts +29 -14
- package/cli/commands/shell-boolean-mode.ts +2 -2
- package/cli/commands/show-touches-mode.ts +2 -2
- package/cli/commands/skills.ts +11 -11
- package/cli/commands/swift.ts +29 -11
- package/cli/commands/telemetry.ts +2 -2
- package/cli/commands/test.ts +5 -0
- package/cli/commands/three-mode.ts +3 -3
- package/cli/commands/timeline.ts +4 -4
- package/cli/commands/upgrade.ts +2 -2
- package/cli/commands/upload.ts +15 -15
- package/cli/commands/what-happened.ts +2 -2
- package/cli/current-sim.ts +6 -3
- package/cli/desktop-companion.ts +55 -149
- package/cli/dev-build-freshness.ts +2 -2
- package/cli/drivers/desktop.ts +17 -9
- package/cli/drivers/electron.ts +1 -2
- package/cli/drivers/playwright-sim-host.ts +11 -2
- package/cli/drivers/playwright.ts +35 -2
- package/cli/drivers/types.ts +8 -0
- package/cli/maestro-compare.ts +670 -0
- package/cli/outbound-endpoints.ts +46 -8
- package/cli/recording-access.ts +6 -6
- package/cli/run-registry.ts +1 -1
- package/cli/runtime-compat.ts +5 -1
- package/cli/send-to-box.ts +3 -3
- package/cli/shell-init.ts +2 -1
- package/cli/ws-bridge.ts +9 -5
- package/detox/element-types.ts +1 -0
- package/detox/gestures.ts +3 -3
- package/detox/index.ts +67 -38
- package/detox/navigation-lifecycle.ts +9 -6
- package/detox/proof-frame.cjs +6 -4
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +42 -17
- package/dist-lib/bridge-contract-input.mjs +41 -16
- package/dist-lib/bridge-contract.cjs +1 -4
- package/dist-lib/bridge-contract.mjs +1 -3
- package/dist-lib/bundle-channel.cjs +2918 -550
- package/dist-lib/capture-contract.cjs +65 -24
- package/dist-lib/capture-contract.mjs +63 -24
- package/dist-lib/cloud-contract.cjs +2 -2
- package/dist-lib/cloud-contract.mjs +2 -2
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +3 -3
- package/dist-lib/cloud.mjs +3 -3
- package/dist-lib/config.cjs +15 -5
- package/dist-lib/config.mjs +154 -0
- package/dist-lib/detox/index.cjs +495 -139
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +1168 -238
- package/dist-lib/expo-swift-metadata.cjs +34 -23
- package/dist-lib/expo-swift-metadata.mjs +32 -23
- package/dist-lib/home-paths.cjs +2 -2
- package/dist-lib/host/bridge-host.cjs +2938 -654
- package/dist-lib/host/fetch-proxy-handler.cjs +14 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +7 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +5 -1
- package/dist-lib/host/replacement-module-handler.cjs +2 -2
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +999 -353
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +332 -19
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +7 -7
- package/dist-lib/metro-production-bundle.mjs +5 -5
- package/dist-lib/metro.cjs +688 -250
- package/dist-lib/native-app-identity-project.cjs +2647 -0
- package/dist-lib/native-app-identity-project.mjs +1257 -0
- package/dist-lib/native-app-identity.cjs +325 -25
- package/dist-lib/native-app-identity.mjs +323 -25
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +5 -1
- package/dist-lib/react-native-host-modules.mjs +5 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +57 -32
- package/dist-lib/sdk.cjs +231 -51
- package/dist-lib/sdk.mjs +231 -51
- package/dist-lib/skills.cjs +4107 -1374
- package/dist-lib/swift-compile.cjs +290 -30
- package/dist-lib/swift-compile.mjs +290 -30
- package/dist-lib/swift-package.cjs +143 -12
- package/dist-lib/swift-package.mjs +137 -11
- package/dist-lib/swift-submit.cjs +47 -5
- package/dist-lib/swift-submit.mjs +42 -5
- package/dist-lib/swift-toolchain.cjs +7 -2
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1392 -458
- package/dist-lib/vite.cjs +13 -17
- package/dist-lib/worklet-executable.cjs +248 -33
- package/dist-lib/worklet-executable.mjs +248 -33
- package/install.sh +10 -1
- package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Resources.kt +9 -9
- package/kotlin/AndroidViewSources/android/graphics/Canvas.kt +20 -0
- package/kotlin/AndroidViewSources/android/view/View.kt +211 -0
- package/kotlin/AndroidViewSources/android/widget/FrameLayout.kt +61 -0
- package/kotlin/AndroidViewSources/android/widget/LinearLayout.kt +49 -0
- package/kotlin/AppSources/android/view/Window.kt +36 -0
- package/kotlin/AppSources/android/view/WindowInsets.kt +9 -0
- package/kotlin/AppSources/android/view/WindowInsetsController.kt +5 -0
- package/kotlin/AppSources/android/window/OnBackInvokedDispatcher.kt +48 -0
- package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +20 -5
- package/kotlin/AppSources/androidx/compose/foundation/layout/WindowInsets.kt +15 -0
- package/kotlin/AppSources/androidx/compose/ui/platform/ComposeView.kt +2 -1
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +42 -50
- package/kotlin/ComposeSources/PeachComposeHost.kt +200 -84
- package/kotlin/ExpoComposeSources/PeachAndroidView.kt +87 -0
- package/kotlin/ExpoComposeSources/PeachExpoViewMount.kt +24 -0
- package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +28 -0
- package/kotlin/ExpoComposeSources/expo/modules/kotlin/views/ExpoComposeView.kt +70 -0
- package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +513 -0
- package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +173 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/AppContext.kt +20 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/Promise.kt +19 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +198 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/exception/CodedException.kt +126 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/functions/Functions.kt +132 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/jni/JavaScriptTypedArray.kt +81 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +98 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +236 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/records/Record.kt +12 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/runtime/Runtime.kt +9 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObject.kt +50 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +57 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/sharedobjects/SharedRef.kt +19 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/ConcreteTypedArrays.kt +158 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/GenericTypedArray.kt +8 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArray.kt +29 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/typedarray/TypedArrayIterator.kt +9 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/types/AnyType.kt +155 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/types/Either.kt +91 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/types/Enumerable.kt +6 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +54 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +23 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +71 -0
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +35 -0
- package/kotlin/README.md +13 -3
- package/kotlin/Sources/android/os/Build.kt +17 -0
- package/kotlin/Sources/android/os/Handler.kt +35 -0
- package/kotlin/Sources/android/os/Looper.kt +17 -0
- package/kotlin/Sources/android/os/MessageQueue.kt +56 -0
- package/kotlin/Sources/android/os/SystemClock.kt +14 -0
- package/kotlin/Sources/android/util/Log.kt +64 -0
- package/kotlin/Sources/java/security/MessageDigest.kt +114 -0
- package/kotlin/Sources/kotlin/text/StringFormat.kt +9 -0
- package/kotlin/Sources/org/json/JSON.kt +44 -0
- package/kotlin/fixtures/counter/Counter.d.kt.ts +1 -0
- package/kotlin/fixtures/counter/Counter.kt +10 -3
- package/kotlin/fixtures/counter/CounterApp.native.tsx +1 -1
- package/kotlin/fixtures/counter/vite.config.ts +2 -2
- package/kotlin/fixtures/headless-counter/Headless.kt +7 -6
- package/kotlin/fixtures/import-animation/Animation.d.kt.ts +7 -0
- package/kotlin/fixtures/import-animation/Animation.kt +80 -0
- package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +13 -0
- package/kotlin/fixtures/import-animation/README.md +24 -0
- package/kotlin/fixtures/import-animation/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-animation/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-animation/app/routes.d.ts +18 -0
- package/kotlin/fixtures/import-animation/package.json +23 -0
- package/kotlin/fixtures/import-animation/tsconfig.json +20 -0
- package/kotlin/fixtures/import-animation/vite.config.ts +20 -0
- package/kotlin/fixtures/import-editor/Editor.d.kt.ts +7 -0
- package/kotlin/fixtures/import-editor/Editor.kt +59 -0
- package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +23 -0
- package/kotlin/fixtures/import-editor/README.md +27 -0
- package/kotlin/fixtures/import-editor/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-editor/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-editor/package.json +23 -0
- package/kotlin/fixtures/import-editor/tsconfig.json +20 -0
- package/kotlin/fixtures/import-editor/vite.config.ts +20 -0
- package/kotlin/fixtures/import-effects/Effects.d.kt.ts +7 -0
- package/kotlin/fixtures/import-effects/Effects.kt +87 -0
- package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +13 -0
- package/kotlin/fixtures/import-effects/README.md +19 -0
- package/kotlin/fixtures/import-effects/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-effects/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-effects/package.json +23 -0
- package/kotlin/fixtures/import-effects/tsconfig.json +20 -0
- package/kotlin/fixtures/import-effects/vite.config.ts +20 -0
- package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +13 -0
- package/kotlin/fixtures/import-layout/Layout.d.kt.ts +7 -0
- package/kotlin/fixtures/import-layout/Layout.kt +72 -0
- package/kotlin/fixtures/import-layout/README.md +24 -0
- package/kotlin/fixtures/import-layout/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-layout/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-layout/package.json +23 -0
- package/kotlin/fixtures/import-layout/tsconfig.json +20 -0
- package/kotlin/fixtures/import-layout/vite.config.ts +20 -0
- package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +18 -0
- package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +7 -0
- package/kotlin/fixtures/import-navigation/Navigation.kt +104 -0
- package/kotlin/fixtures/import-navigation/README.md +31 -0
- package/kotlin/fixtures/import-navigation/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-navigation/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-navigation/package.json +23 -0
- package/kotlin/fixtures/import-navigation/tsconfig.json +20 -0
- package/kotlin/fixtures/import-navigation/vite.config.ts +20 -0
- package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +29 -0
- package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +7 -0
- package/kotlin/fixtures/import-overlays/Overlays.kt +93 -0
- package/kotlin/fixtures/import-overlays/README.md +34 -0
- package/kotlin/fixtures/import-overlays/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-overlays/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-overlays/package.json +23 -0
- package/kotlin/fixtures/import-overlays/tsconfig.json +20 -0
- package/kotlin/fixtures/import-overlays/vite.config.ts +20 -0
- package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +13 -0
- package/kotlin/fixtures/import-scroll/README.md +23 -0
- package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +7 -0
- package/kotlin/fixtures/import-scroll/Scroll.kt +91 -0
- package/kotlin/fixtures/import-scroll/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-scroll/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-scroll/package.json +23 -0
- package/kotlin/fixtures/import-scroll/tsconfig.json +20 -0
- package/kotlin/fixtures/import-scroll/vite.config.ts +20 -0
- package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +44 -0
- package/kotlin/fixtures/import-selection/README.md +32 -0
- package/kotlin/fixtures/import-selection/Selection.d.kt.ts +8 -0
- package/kotlin/fixtures/import-selection/Selection.kt +78 -0
- package/kotlin/fixtures/import-selection/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-selection/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-selection/package.json +23 -0
- package/kotlin/fixtures/import-selection/tsconfig.json +20 -0
- package/kotlin/fixtures/import-selection/vite.config.ts +20 -0
- package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +13 -0
- package/kotlin/fixtures/import-surfaces/README.md +22 -0
- package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +7 -0
- package/kotlin/fixtures/import-surfaces/Surfaces.kt +64 -0
- package/kotlin/fixtures/import-surfaces/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-surfaces/package.json +23 -0
- package/kotlin/fixtures/import-surfaces/tsconfig.json +20 -0
- package/kotlin/fixtures/import-surfaces/vite.config.ts +20 -0
- package/kotlin/fixtures/import-values/ImportValues.native.tsx +35 -0
- package/kotlin/fixtures/import-values/README.md +23 -0
- package/kotlin/fixtures/import-values/Values.d.kt.ts +11 -0
- package/kotlin/fixtures/import-values/Values.kt +66 -0
- package/kotlin/fixtures/import-values/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-values/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-values/package.json +23 -0
- package/kotlin/fixtures/import-values/tsconfig.json +20 -0
- package/kotlin/fixtures/import-values/vite.config.ts +20 -0
- package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +38 -0
- package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +36 -0
- package/kotlin/fixtures/kotlin-app-variants/Ime.kt +3 -1
- package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +50 -0
- package/kotlin/fork/NOTICE +6 -1
- package/kotlin/fork/README.md +15 -7
- package/kotlin/fork/build.sh +2 -0
- package/kotlin/fork/compose-worker-globals.patch +186 -4
- package/kotlin/fork/fork.json +2 -2
- package/kotlin/fork/skiko-worker-clock.patch +12 -0
- package/kotlin/toolchain.json +12 -12
- package/kotlin/tools/expo-types.main.kts +115 -0
- package/package.json +17 -23
- package/scripts/dev-server-scanner.ts +92 -66
- package/skills/contrast/SKILL.md +2 -2
- package/skills/peach-debug/SKILL.md +1 -1
- package/skills/peach-perf/SKILL.md +1 -1
- package/skills/peach-setup/SKILL.md +2 -1
- package/skills/peach-test/SKILL.md +8 -0
- package/src/assetCatalogContract.ts +43 -1
- package/src/bridge-contract-input.ts +46 -19
- package/src/bridge-contract.ts +0 -1
- package/src/bundle-channel.mjs +197 -84
- package/src/bundleGlobalFooter.ts +38 -0
- package/src/capture-contract.ts +107 -33
- package/src/cloud-contract.ts +5 -1
- package/src/cloud.ts +5 -2
- package/src/config.ts +22 -3
- package/src/configured-bundle-producer.ts +1 -1
- package/src/desktop-host-delivery.ts +95 -1
- package/src/expo-kotlin-bundle.ts +29 -0
- package/src/expo-kotlin-host.ts +201 -0
- package/src/expo-swift-host.ts +96 -76
- package/src/expoKotlinContract.ts +70 -0
- package/src/expoSwiftContract.ts +4 -25
- package/src/home-paths.ts +7 -4
- package/src/host/bridge-host.ts +23 -3
- package/src/host/fetch-proxy-handler.ts +13 -0
- package/src/host/fetch-proxy-overrides.ts +11 -0
- package/src/host/replacement-module-handler.ts +5 -2
- package/src/index.ts +3 -1
- package/src/kotlin-host.ts +463 -19
- package/src/metro-plugin.ts +56 -28
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-app-identity-project.ts +190 -0
- package/src/native-app-identity.ts +398 -34
- package/src/native-firebase-config.ts +69 -15
- package/src/native-seam-manifest.ts +6 -0
- package/src/one-widget-discovery.ts +2 -1
- package/src/openstep-plist.ts +107 -0
- package/src/react-native-host-modules.ts +4 -0
- package/src/runtime-assets.ts +2 -2
- package/src/sim-client.ts +2 -2
- package/src/swift-binary-modules.ts +3 -1
- package/src/swift-compile.ts +286 -38
- package/src/swift-distribution-contract.ts +4 -0
- package/src/swift-distribution.lock.json +123 -12
- package/src/swift-distribution.ts +7 -3
- package/src/swift-host.ts +298 -83
- package/src/swift-package-manifest.ts +29 -2
- package/src/swift-package.ts +180 -10
- package/src/swift-packages.ts +4 -0
- package/src/swift-standin-emit.ts +4 -1
- package/src/swift-standins.ts +274 -15
- package/src/swift-submit.ts +62 -7
- package/src/swift-toolchain.ts +6 -1
- package/src/swiftContract.ts +126 -3
- package/src/vite-plugin-one.ts +13 -16
- package/src/vite-plugin-swift.ts +4 -5
- package/src/vite-plugin.ts +360 -144
- package/src/vite.d.ts +1 -1
- package/src/worker-system-chunks.ts +221 -0
- package/src/worklet-executable.ts +348 -38
- package/src/worklets-babel.ts +1 -1
- package/src/xcode-project.ts +243 -127
- package/dist-lib/agent-identity.cjs +0 -39
- package/dist-lib/agent-sessions.cjs +0 -760
- package/dist-lib/cli-constants.cjs +0 -32
- package/dist-lib/render-mode.cjs +0 -44
- package/kotlin/AppSources/android/view/View.kt +0 -74
- package/kotlin/AppSources/android/widget/FrameLayout.kt +0 -6
- package/src/render-mode.ts +0 -15
- /package/kotlin/{AppSources → AndroidViewSources}/android/content/Context.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/android/content/res/Configuration.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/android/graphics/Color.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/android/util/DisplayMetrics.kt +0 -0
- /package/kotlin/{AppSources → AndroidViewSources}/dev/peach/app/JavaFormat.kt +0 -0
- /package/kotlin/{AppSources → Sources}/java/lang/Runnable.kt +0 -0
package/src/kotlin-host.ts
CHANGED
|
@@ -9,7 +9,9 @@ import { createHash } from 'node:crypto'
|
|
|
9
9
|
import fs from 'node:fs'
|
|
10
10
|
import { createRequire } from 'node:module'
|
|
11
11
|
import path from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
12
13
|
import { promisify } from 'node:util'
|
|
14
|
+
import { parse } from 'acorn'
|
|
13
15
|
import { rnxHomeDir } from './home-paths'
|
|
14
16
|
import { isRecord } from './is-record'
|
|
15
17
|
import {
|
|
@@ -18,6 +20,7 @@ import {
|
|
|
18
20
|
type ViewModelClass,
|
|
19
21
|
} from './kotlin-view-models'
|
|
20
22
|
import type { DayNight, DrawableFile } from './android-drawables'
|
|
23
|
+
import type { ExpoKotlinModules } from './expo-kotlin-host'
|
|
21
24
|
import type { KotlinAppProject, KotlinWindowTheme } from './kotlin-app'
|
|
22
25
|
import type { ServerResponse } from 'node:http'
|
|
23
26
|
|
|
@@ -226,9 +229,9 @@ export function kotlinSources(
|
|
|
226
229
|
// are a table here.
|
|
227
230
|
function entrySource(ids: string[]): string {
|
|
228
231
|
return [
|
|
232
|
+
'import android.os.Build',
|
|
229
233
|
'import kotlin.js.JsAny',
|
|
230
234
|
'import kotlin.js.Promise',
|
|
231
|
-
'import kotlinx.coroutines.CoroutineStart',
|
|
232
235
|
'import kotlinx.coroutines.Dispatchers',
|
|
233
236
|
'import kotlinx.coroutines.GlobalScope',
|
|
234
237
|
'import kotlinx.coroutines.promise',
|
|
@@ -242,10 +245,14 @@ function entrySource(ids: string[]): string {
|
|
|
242
245
|
' else -> null',
|
|
243
246
|
'}?.also { instances[packageName] = it }',
|
|
244
247
|
'',
|
|
248
|
+
'@OptIn(kotlin.js.ExperimentalJsExport::class)',
|
|
249
|
+
'@JsExport',
|
|
250
|
+
'fun peachSetAndroidBuildInfo(model: String, release: String) = Build.setDevice(model, release)',
|
|
251
|
+
'',
|
|
245
252
|
'@OptIn(kotlin.js.ExperimentalJsExport::class, kotlin.js.ExperimentalWasmJsInterop::class, kotlinx.coroutines.DelicateCoroutinesApi::class)',
|
|
246
253
|
'@JsExport',
|
|
247
254
|
'fun peachNativeCall(module: String, method: String, argsJson: String, contractHash: String): Promise<JsAny?> =',
|
|
248
|
-
' GlobalScope.promise(Dispatchers.Main
|
|
255
|
+
' GlobalScope.promise(Dispatchers.Main) {',
|
|
249
256
|
' val reply = JSONObject()',
|
|
250
257
|
' try {',
|
|
251
258
|
' val packageName = module.substringBefore(\'.\', "")',
|
|
@@ -268,7 +275,7 @@ function entrySource(ids: string[]): string {
|
|
|
268
275
|
].join('\n')
|
|
269
276
|
}
|
|
270
277
|
|
|
271
|
-
const BUILD_FORMAT =
|
|
278
|
+
const BUILD_FORMAT = 14
|
|
272
279
|
const builds = new Map<string, Promise<KotlinArtifact>>()
|
|
273
280
|
|
|
274
281
|
function buildsDir(): string {
|
|
@@ -304,7 +311,7 @@ export async function kotlinArtifact(
|
|
|
304
311
|
}
|
|
305
312
|
generated['PeachEntry.kt'] = entrySource(ids)
|
|
306
313
|
if (viewIds.length > 0) generated['PeachViews.kt'] = viewsSource(viewIds)
|
|
307
|
-
const shims = kotlinSources(path.join(moduleRoot, 'Sources')).filter(
|
|
314
|
+
const shims = kotlinSources(path.join(moduleRoot, 'Sources'), new Set()).filter(
|
|
308
315
|
(file) => usesCompose || !file.endsWith('/OneNativeSourceViewDispatch.kt'),
|
|
309
316
|
)
|
|
310
317
|
if (viewIds.length > 0)
|
|
@@ -315,6 +322,379 @@ export async function kotlinArtifact(
|
|
|
315
322
|
)
|
|
316
323
|
}
|
|
317
324
|
|
|
325
|
+
interface ExpoField {
|
|
326
|
+
name: string
|
|
327
|
+
key: string
|
|
328
|
+
required: boolean
|
|
329
|
+
mutable: boolean
|
|
330
|
+
hidden: boolean
|
|
331
|
+
initializer: [number, number] | null
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
interface ExpoTypesFile {
|
|
335
|
+
file: string
|
|
336
|
+
package: string
|
|
337
|
+
records: { name: string; hidden: boolean; fields: ExpoField[] }[]
|
|
338
|
+
enums: {
|
|
339
|
+
name: string
|
|
340
|
+
hidden: boolean
|
|
341
|
+
fields: number
|
|
342
|
+
parameter: string | null
|
|
343
|
+
hiddenParameter: boolean
|
|
344
|
+
}[]
|
|
345
|
+
// Expo views; compose views also name their props class
|
|
346
|
+
views: { name: string; hidden: boolean; props: string }[]
|
|
347
|
+
// ComposeProps classes with their public member properties
|
|
348
|
+
props: { name: string; hidden: boolean; states: string[] }[]
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function readExpoField(value: unknown): ExpoField {
|
|
352
|
+
if (
|
|
353
|
+
!isRecord(value) ||
|
|
354
|
+
typeof value.name !== 'string' ||
|
|
355
|
+
typeof value.key !== 'string' ||
|
|
356
|
+
typeof value.required !== 'boolean' ||
|
|
357
|
+
typeof value.mutable !== 'boolean' ||
|
|
358
|
+
typeof value.hidden !== 'boolean'
|
|
359
|
+
)
|
|
360
|
+
throw new Error('expo-types printed an invalid field')
|
|
361
|
+
const range = value.initializer
|
|
362
|
+
if (
|
|
363
|
+
range !== null &&
|
|
364
|
+
(!Array.isArray(range) ||
|
|
365
|
+
range.length !== 2 ||
|
|
366
|
+
typeof range[0] !== 'number' ||
|
|
367
|
+
typeof range[1] !== 'number')
|
|
368
|
+
)
|
|
369
|
+
throw new Error('expo-types printed an invalid initializer range')
|
|
370
|
+
return {
|
|
371
|
+
name: value.name,
|
|
372
|
+
key: value.key,
|
|
373
|
+
required: value.required,
|
|
374
|
+
mutable: value.mutable,
|
|
375
|
+
hidden: value.hidden,
|
|
376
|
+
initializer: range === null ? null : [range[0], range[1]],
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function readExpoTypes(output: string): ExpoTypesFile[] {
|
|
381
|
+
return output
|
|
382
|
+
.split('\n')
|
|
383
|
+
.filter((line) => line.startsWith('{'))
|
|
384
|
+
.map((line) => {
|
|
385
|
+
const row: unknown = JSON.parse(line)
|
|
386
|
+
if (
|
|
387
|
+
!isRecord(row) ||
|
|
388
|
+
typeof row.file !== 'string' ||
|
|
389
|
+
typeof row.package !== 'string' ||
|
|
390
|
+
!Array.isArray(row.records) ||
|
|
391
|
+
!Array.isArray(row.enums) ||
|
|
392
|
+
!Array.isArray(row.views) ||
|
|
393
|
+
!Array.isArray(row.props)
|
|
394
|
+
)
|
|
395
|
+
throw new Error('expo-types printed an invalid file')
|
|
396
|
+
return {
|
|
397
|
+
file: row.file,
|
|
398
|
+
package: row.package,
|
|
399
|
+
records: row.records.map((record: unknown) => {
|
|
400
|
+
if (
|
|
401
|
+
!isRecord(record) ||
|
|
402
|
+
typeof record.name !== 'string' ||
|
|
403
|
+
typeof record.hidden !== 'boolean' ||
|
|
404
|
+
!Array.isArray(record.fields)
|
|
405
|
+
)
|
|
406
|
+
throw new Error('expo-types printed an invalid record')
|
|
407
|
+
return {
|
|
408
|
+
name: record.name,
|
|
409
|
+
hidden: record.hidden,
|
|
410
|
+
fields: record.fields.map(readExpoField),
|
|
411
|
+
}
|
|
412
|
+
}),
|
|
413
|
+
enums: row.enums.map((entry: unknown) => {
|
|
414
|
+
if (
|
|
415
|
+
!isRecord(entry) ||
|
|
416
|
+
typeof entry.name !== 'string' ||
|
|
417
|
+
typeof entry.hidden !== 'boolean' ||
|
|
418
|
+
typeof entry.fields !== 'number' ||
|
|
419
|
+
(entry.parameter !== null && typeof entry.parameter !== 'string') ||
|
|
420
|
+
typeof entry.hiddenParameter !== 'boolean'
|
|
421
|
+
)
|
|
422
|
+
throw new Error('expo-types printed an invalid enum')
|
|
423
|
+
return {
|
|
424
|
+
name: entry.name,
|
|
425
|
+
hidden: entry.hidden,
|
|
426
|
+
fields: entry.fields,
|
|
427
|
+
parameter: entry.parameter,
|
|
428
|
+
hiddenParameter: entry.hiddenParameter,
|
|
429
|
+
}
|
|
430
|
+
}),
|
|
431
|
+
views: row.views.map((view: unknown) => {
|
|
432
|
+
if (
|
|
433
|
+
!isRecord(view) ||
|
|
434
|
+
typeof view.name !== 'string' ||
|
|
435
|
+
typeof view.hidden !== 'boolean' ||
|
|
436
|
+
typeof view.props !== 'string'
|
|
437
|
+
)
|
|
438
|
+
throw new Error('expo-types printed an invalid view')
|
|
439
|
+
return { name: view.name, hidden: view.hidden, props: view.props }
|
|
440
|
+
}),
|
|
441
|
+
props: row.props.map((entry: unknown) => {
|
|
442
|
+
if (
|
|
443
|
+
!isRecord(entry) ||
|
|
444
|
+
typeof entry.name !== 'string' ||
|
|
445
|
+
typeof entry.hidden !== 'boolean' ||
|
|
446
|
+
!Array.isArray(entry.states) ||
|
|
447
|
+
!entry.states.every((state: unknown) => typeof state === 'string')
|
|
448
|
+
)
|
|
449
|
+
throw new Error('expo-types printed invalid compose props')
|
|
450
|
+
return { name: entry.name, hidden: entry.hidden, states: entry.states }
|
|
451
|
+
}),
|
|
452
|
+
}
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const expoTypeScans = new Map<string, Promise<ExpoTypesFile[]>>()
|
|
457
|
+
|
|
458
|
+
// the records, enums, compose views and compose props the module sources
|
|
459
|
+
// declare, read by Kotlin's own parser; sources that name none skip the JVM start
|
|
460
|
+
function scanExpoTypes(
|
|
461
|
+
toolchain: KotlinToolchain,
|
|
462
|
+
script: string,
|
|
463
|
+
sources: string[],
|
|
464
|
+
): Promise<ExpoTypesFile[]> {
|
|
465
|
+
const candidates = sources.filter((file) =>
|
|
466
|
+
/\bRecord\b|\benum\s+class\b|\bExpoComposeView\b|\bExpoView\b|\bComposeProps\b/.test(
|
|
467
|
+
fs.readFileSync(file, 'utf8'),
|
|
468
|
+
),
|
|
469
|
+
)
|
|
470
|
+
if (candidates.length === 0) return Promise.resolve([])
|
|
471
|
+
const digest = createHash('sha256')
|
|
472
|
+
for (const file of [script, ...candidates])
|
|
473
|
+
digest.update(`${file}\0${fs.readFileSync(file, 'utf8')}\0`)
|
|
474
|
+
const key = digest.digest('hex')
|
|
475
|
+
let scan = expoTypeScans.get(key)
|
|
476
|
+
if (!scan) {
|
|
477
|
+
scan = (async () => {
|
|
478
|
+
const { kotlinc } = await ensureToolchain(toolchain)
|
|
479
|
+
const { stdout } = await run(
|
|
480
|
+
path.join(path.dirname(kotlinc), 'kotlinc'),
|
|
481
|
+
[
|
|
482
|
+
'-cp',
|
|
483
|
+
path.resolve(path.dirname(kotlinc), '../lib/kotlin-compiler.jar'),
|
|
484
|
+
'-script',
|
|
485
|
+
script,
|
|
486
|
+
...candidates,
|
|
487
|
+
],
|
|
488
|
+
{ maxBuffer: 16 * 1024 * 1024 },
|
|
489
|
+
)
|
|
490
|
+
return readExpoTypes(stdout)
|
|
491
|
+
})().catch((error: unknown) => {
|
|
492
|
+
expoTypeScans.delete(key)
|
|
493
|
+
throw error
|
|
494
|
+
})
|
|
495
|
+
expoTypeScans.set(key, scan)
|
|
496
|
+
}
|
|
497
|
+
return scan
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// upstream converts records and enums by reflection; Kotlin/Wasm has none, so
|
|
501
|
+
// each @Field initializer and constructor default reads the incoming value
|
|
502
|
+
// through PeachExpoTypes.initial, and each package registers its types
|
|
503
|
+
function expoTypeSources(files: ExpoTypesFile[]): {
|
|
504
|
+
generated: Record<string, string>
|
|
505
|
+
origins: Record<string, string>
|
|
506
|
+
registrations: string[]
|
|
507
|
+
} {
|
|
508
|
+
const generated: Record<string, string> = {}
|
|
509
|
+
const origins: Record<string, string> = {}
|
|
510
|
+
const registrations: string[] = []
|
|
511
|
+
files.forEach((file, index) => {
|
|
512
|
+
const edits: { range: [number, number]; owner: string; field: ExpoField }[] = []
|
|
513
|
+
const calls: string[] = []
|
|
514
|
+
for (const record of file.records) {
|
|
515
|
+
const fields: string[] = []
|
|
516
|
+
for (const field of record.fields) {
|
|
517
|
+
if (field.initializer)
|
|
518
|
+
edits.push({ range: field.initializer, owner: record.name, field })
|
|
519
|
+
if (field.hidden) {
|
|
520
|
+
if (!field.initializer)
|
|
521
|
+
throw new Error(
|
|
522
|
+
`${file.file}: @Field ${record.name}.${field.name} is private and has no initializer, which Peach cannot set`,
|
|
523
|
+
)
|
|
524
|
+
continue
|
|
525
|
+
}
|
|
526
|
+
const name = literal(field.key)
|
|
527
|
+
const read = `{ record: ${record.name} -> record.${field.name} }`
|
|
528
|
+
const write =
|
|
529
|
+
field.mutable && !field.initializer
|
|
530
|
+
? `{ record, value -> record.${field.name} = value }`
|
|
531
|
+
: 'null'
|
|
532
|
+
fields.push(
|
|
533
|
+
` dev.peach.expo.PeachExpoTypes.field(${name}, ${field.required}, ${read}, ${write}),`,
|
|
534
|
+
)
|
|
535
|
+
}
|
|
536
|
+
if (record.hidden) continue
|
|
537
|
+
calls.push(
|
|
538
|
+
[
|
|
539
|
+
` dev.peach.expo.PeachExpoTypes.record(${record.name}::class, ${literal(file.package ? `${file.package}.${record.name}` : record.name)}, { ${record.name}() }, listOf(`,
|
|
540
|
+
...fields,
|
|
541
|
+
' ))',
|
|
542
|
+
].join('\n'),
|
|
543
|
+
)
|
|
544
|
+
}
|
|
545
|
+
for (const entry of file.enums) {
|
|
546
|
+
if (entry.hidden) continue
|
|
547
|
+
if (entry.hiddenParameter)
|
|
548
|
+
throw new Error(
|
|
549
|
+
`${file.file}: enum ${entry.name} keeps its value in a private property, which Peach cannot read`,
|
|
550
|
+
)
|
|
551
|
+
const value = entry.fields === 1 ? `{ it.${entry.parameter} }` : 'null'
|
|
552
|
+
calls.push(
|
|
553
|
+
` dev.peach.expo.PeachExpoTypes.enum(${entry.name}::class, enumValues<${entry.name}>(), ${entry.fields}, ${entry.parameter === null ? 'null' : literal(entry.parameter)}, ${value})`,
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
// a view's constructor and its props' states are named where the source
|
|
557
|
+
// declares them, so they resolve through the source's own imports
|
|
558
|
+
const views = [
|
|
559
|
+
...file.views
|
|
560
|
+
.filter((view) => !view.hidden)
|
|
561
|
+
.map(
|
|
562
|
+
(view) =>
|
|
563
|
+
` dev.peach.expo.PeachExpoTypes.view(${view.name}::class, ${view.props ? `${view.props}::class` : 'null'}) { context, appContext -> ${view.name}(context, appContext) }`,
|
|
564
|
+
),
|
|
565
|
+
...file.props
|
|
566
|
+
.filter((entry) => !entry.hidden)
|
|
567
|
+
.map((entry) =>
|
|
568
|
+
[
|
|
569
|
+
` dev.peach.expo.PeachExpoTypes.composeProps(${entry.name}::class, listOf(`,
|
|
570
|
+
...entry.states.map(
|
|
571
|
+
(state) =>
|
|
572
|
+
` dev.peach.expo.PeachExpoCompose.prop(${literal(state)}) { props: ${entry.name} -> props.${state} },`,
|
|
573
|
+
),
|
|
574
|
+
' ))',
|
|
575
|
+
].join('\n'),
|
|
576
|
+
),
|
|
577
|
+
]
|
|
578
|
+
if (edits.length > 0 || views.length > 0) {
|
|
579
|
+
let text = fs.readFileSync(file.file, 'utf8').replaceAll('\r\n', '\n')
|
|
580
|
+
for (const { range, owner, field } of edits.sort((a, b) => b.range[0] - a.range[0]))
|
|
581
|
+
text = `${text.slice(0, range[0])}dev.peach.expo.PeachExpoTypes.initial(${owner}::class, ${literal(field.key)}, ${field.required}) { ${text.slice(range[0], range[1])} }${text.slice(range[1])}`
|
|
582
|
+
if (views.length > 0) {
|
|
583
|
+
text = [
|
|
584
|
+
text,
|
|
585
|
+
'',
|
|
586
|
+
`internal fun peachExpoViews${index}() {`,
|
|
587
|
+
...views,
|
|
588
|
+
'}',
|
|
589
|
+
'',
|
|
590
|
+
].join('\n')
|
|
591
|
+
registrations.push(
|
|
592
|
+
`${file.package ? `${file.package}.` : ''}peachExpoViews${index}()`,
|
|
593
|
+
)
|
|
594
|
+
}
|
|
595
|
+
const name = `PeachExpoSource_${index}_${path.basename(file.file)}`
|
|
596
|
+
generated[name] = text
|
|
597
|
+
origins[name] = file.file
|
|
598
|
+
}
|
|
599
|
+
if (calls.length === 0) return
|
|
600
|
+
generated[`PeachExpoTypes_${index}.kt`] = [
|
|
601
|
+
...(file.package ? [`package ${file.package}`, ''] : []),
|
|
602
|
+
`internal fun peachExpoTypes${index}() {`,
|
|
603
|
+
...calls,
|
|
604
|
+
'}',
|
|
605
|
+
'',
|
|
606
|
+
].join('\n')
|
|
607
|
+
registrations.push(
|
|
608
|
+
`${file.package ? `${file.package}.` : ''}peachExpoTypes${index}()`,
|
|
609
|
+
)
|
|
610
|
+
})
|
|
611
|
+
return { generated, origins, registrations }
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// local Expo modules: their android Kotlin, the shared actuals and the
|
|
615
|
+
// expo-modules-core DSL port, with an entry registering the classes Expo's
|
|
616
|
+
// autolinker names, as its generated ExpoModulesPackageList does
|
|
617
|
+
export async function expoKotlinArtifact(
|
|
618
|
+
root: string,
|
|
619
|
+
moduleRoot: string,
|
|
620
|
+
origin: string,
|
|
621
|
+
modules: ExpoKotlinModules,
|
|
622
|
+
): Promise<KotlinArtifact & { compose: boolean }> {
|
|
623
|
+
const toolchain = readToolchain(moduleRoot)
|
|
624
|
+
const language = appKotlinLanguage(root)
|
|
625
|
+
const scanned = await scanExpoTypes(
|
|
626
|
+
toolchain,
|
|
627
|
+
path.join(moduleRoot, 'tools/expo-types.main.kts'),
|
|
628
|
+
modules.sources,
|
|
629
|
+
)
|
|
630
|
+
// every exported view uses the existing native scene presenter. modules
|
|
631
|
+
// without views or compose imports do not link that renderer.
|
|
632
|
+
const usesCompose =
|
|
633
|
+
scanned.some((file) => file.views.length > 0) ||
|
|
634
|
+
modules.sources.some((file) =>
|
|
635
|
+
/\bandroidx\.compose\./.test(fs.readFileSync(file, 'utf8')),
|
|
636
|
+
)
|
|
637
|
+
const shims = [
|
|
638
|
+
...kotlinSources(path.join(moduleRoot, 'Sources'), new Set()).filter(
|
|
639
|
+
(file) => usesCompose || !file.endsWith('/OneNativeSourceViewDispatch.kt'),
|
|
640
|
+
),
|
|
641
|
+
...kotlinSources(path.join(moduleRoot, 'ExpoSources'), new Set()),
|
|
642
|
+
...kotlinSources(path.join(moduleRoot, 'AndroidViewSources'), new Set()),
|
|
643
|
+
...(usesCompose
|
|
644
|
+
? [
|
|
645
|
+
...kotlinSources(path.join(moduleRoot, 'ComposeSources')),
|
|
646
|
+
...kotlinSources(path.join(moduleRoot, 'ExpoComposeSources'), new Set()),
|
|
647
|
+
]
|
|
648
|
+
: []),
|
|
649
|
+
]
|
|
650
|
+
const types = expoTypeSources(scanned)
|
|
651
|
+
const rewritten = new Set(Object.values(types.origins))
|
|
652
|
+
const entry = [
|
|
653
|
+
'@file:OptIn(kotlin.js.ExperimentalJsExport::class)',
|
|
654
|
+
'',
|
|
655
|
+
'import dev.peach.expo.PeachExpoRuntime',
|
|
656
|
+
'',
|
|
657
|
+
'private val registered by lazy {',
|
|
658
|
+
...types.registrations.map((call) => ` ${call}`),
|
|
659
|
+
' PeachExpoRuntime.register(listOf(',
|
|
660
|
+
...modules.modules.map(
|
|
661
|
+
({ classifier, name }) =>
|
|
662
|
+
` { ${classifier}() } to ${name === null ? 'null' : JSON.stringify(name)},`,
|
|
663
|
+
),
|
|
664
|
+
' ))',
|
|
665
|
+
'}',
|
|
666
|
+
'',
|
|
667
|
+
'@JsExport fun peachExpoListen(notify: () -> Unit) {',
|
|
668
|
+
' registered',
|
|
669
|
+
' PeachExpoRuntime.listen(notify)',
|
|
670
|
+
'}',
|
|
671
|
+
'',
|
|
672
|
+
'@JsExport fun peachExpoCall(payload: String): String {',
|
|
673
|
+
' registered',
|
|
674
|
+
' return PeachExpoRuntime.dispatch(payload)',
|
|
675
|
+
'}',
|
|
676
|
+
'',
|
|
677
|
+
].join('\n')
|
|
678
|
+
const artifact = await compileKotlin(
|
|
679
|
+
{
|
|
680
|
+
root,
|
|
681
|
+
toolchain,
|
|
682
|
+
language,
|
|
683
|
+
sources: modules.sources.filter((file) => !rewritten.has(file)),
|
|
684
|
+
generated: {
|
|
685
|
+
...types.generated,
|
|
686
|
+
'PeachExpoEntry.kt': entry,
|
|
687
|
+
...(usesCompose ? { 'PeachViews.kt': viewsSource([]) } : {}),
|
|
688
|
+
},
|
|
689
|
+
origins: types.origins,
|
|
690
|
+
shims,
|
|
691
|
+
usesCompose,
|
|
692
|
+
},
|
|
693
|
+
origin,
|
|
694
|
+
)
|
|
695
|
+
return { ...artifact, compose: usesCompose }
|
|
696
|
+
}
|
|
697
|
+
|
|
318
698
|
function viewsSource(viewIds: string[]): string {
|
|
319
699
|
return [
|
|
320
700
|
'import dev.onejs.one.OneNativeSourceViewDispatch',
|
|
@@ -377,8 +757,8 @@ export async function kotlinAppArtifact(
|
|
|
377
757
|
'import androidx.lifecycle.createSavedStateHandle',
|
|
378
758
|
'',
|
|
379
759
|
'@JsExport',
|
|
380
|
-
'fun peachAppMount(density: Float, fontScale: Float, locale: String,
|
|
381
|
-
'
|
|
760
|
+
'fun peachAppMount(density: Float, fontScale: Float, locale: String,',
|
|
761
|
+
' font: String, insets: String, night: Boolean,',
|
|
382
762
|
' invalidate: (Boolean) -> Unit): String = dev.peach.app.peachStartApp(',
|
|
383
763
|
' dev.peach.app.PeachAppSpec(',
|
|
384
764
|
` android.content.ComponentName(${literal(app.packageName)}, ${literal(app.launcher.name)}),`,
|
|
@@ -392,8 +772,9 @@ export async function kotlinAppArtifact(
|
|
|
392
772
|
' androidx.lifecycle.viewmodel.viewModelFactory {',
|
|
393
773
|
...viewModels.map(viewModelInitializer),
|
|
394
774
|
' },',
|
|
775
|
+
` ${app.predictiveBack},`,
|
|
395
776
|
' ),',
|
|
396
|
-
' density, fontScale, locale,
|
|
777
|
+
' density, fontScale, locale, font, insets, night, invalidate,',
|
|
397
778
|
')',
|
|
398
779
|
'',
|
|
399
780
|
'@JsExport',
|
|
@@ -406,7 +787,10 @@ export async function kotlinAppArtifact(
|
|
|
406
787
|
'fun peachAppNight(night: Boolean) = dev.peach.app.peachAppNight(night)',
|
|
407
788
|
'',
|
|
408
789
|
'@JsExport',
|
|
409
|
-
'fun peachAppIme(bottom: Int) = dev.peach.app.peachAppIme(bottom)',
|
|
790
|
+
'fun peachAppIme(bottom: Int, visible: Boolean) = dev.peach.app.peachAppIme(bottom, visible)',
|
|
791
|
+
'',
|
|
792
|
+
'@JsExport',
|
|
793
|
+
'fun peachAppInsets(insets: String, visibleSystemBars: Int) = dev.peach.app.peachAppInsets(insets, visibleSystemBars)',
|
|
410
794
|
'',
|
|
411
795
|
].join('\n')
|
|
412
796
|
const generated: Record<string, string> = {
|
|
@@ -416,9 +800,10 @@ export async function kotlinAppArtifact(
|
|
|
416
800
|
...appResourceSources(app),
|
|
417
801
|
}
|
|
418
802
|
const shims = [
|
|
419
|
-
...kotlinSources(path.join(moduleRoot, 'Sources')),
|
|
803
|
+
...kotlinSources(path.join(moduleRoot, 'Sources'), new Set()),
|
|
420
804
|
...kotlinSources(path.join(moduleRoot, 'ComposeSources')),
|
|
421
805
|
...kotlinSources(path.join(moduleRoot, 'AppSources'), new Set()),
|
|
806
|
+
...kotlinSources(path.join(moduleRoot, 'AndroidViewSources'), new Set()),
|
|
422
807
|
]
|
|
423
808
|
return compileKotlin(
|
|
424
809
|
{
|
|
@@ -637,6 +1022,8 @@ async function compileKotlin(
|
|
|
637
1022
|
language: string
|
|
638
1023
|
sources: string[]
|
|
639
1024
|
generated: Record<string, string>
|
|
1025
|
+
// the app source a generated file rewrites, which its diagnostics name
|
|
1026
|
+
origins?: Record<string, string>
|
|
640
1027
|
shims: string[]
|
|
641
1028
|
usesCompose: boolean
|
|
642
1029
|
// a generated file that names the app's view models: rendered empty for a
|
|
@@ -655,6 +1042,7 @@ async function compileKotlin(
|
|
|
655
1042
|
language,
|
|
656
1043
|
sources,
|
|
657
1044
|
generated,
|
|
1045
|
+
origins = {},
|
|
658
1046
|
shims,
|
|
659
1047
|
usesCompose,
|
|
660
1048
|
viewModels,
|
|
@@ -701,6 +1089,7 @@ async function compileKotlin(
|
|
|
701
1089
|
const common = [
|
|
702
1090
|
// the full upstream Compose graph exceeds kotlinc's 512 mib default heap.
|
|
703
1091
|
'-J-Xmx2g',
|
|
1092
|
+
'-Xallow-kotlin-package',
|
|
704
1093
|
'-Xwasm-target=js',
|
|
705
1094
|
'-libraries',
|
|
706
1095
|
libraries.join(path.delimiter),
|
|
@@ -771,16 +1160,56 @@ async function compileKotlin(
|
|
|
771
1160
|
],
|
|
772
1161
|
{ maxBuffer: 16 * 1024 * 1024 },
|
|
773
1162
|
)
|
|
774
|
-
if (usesCompose)
|
|
1163
|
+
if (usesCompose) {
|
|
775
1164
|
await run('unzip', [
|
|
776
1165
|
'-q',
|
|
777
1166
|
'-j',
|
|
778
1167
|
composeRuntime,
|
|
779
1168
|
'skiko.mjs',
|
|
780
|
-
'skiko.wasm',
|
|
781
1169
|
'-d',
|
|
782
1170
|
path.join(work, 'js'),
|
|
783
1171
|
])
|
|
1172
|
+
// each artifact references the tenant's one upstream callback namespace.
|
|
1173
|
+
// the pinned loader's binding declarations define its public ABI.
|
|
1174
|
+
const skikoPath = path.join(work, 'js', 'skiko.mjs')
|
|
1175
|
+
const upstream = parse(fs.readFileSync(skikoPath, 'utf8'), {
|
|
1176
|
+
ecmaVersion: 'latest',
|
|
1177
|
+
sourceType: 'module',
|
|
1178
|
+
})
|
|
1179
|
+
const bindings: string[] = []
|
|
1180
|
+
for (const statement of upstream.body) {
|
|
1181
|
+
if (
|
|
1182
|
+
statement.type !== 'ExportNamedDeclaration' ||
|
|
1183
|
+
statement.declaration?.type !== 'VariableDeclaration' ||
|
|
1184
|
+
statement.declaration.kind !== 'let'
|
|
1185
|
+
)
|
|
1186
|
+
continue
|
|
1187
|
+
for (const binding of statement.declaration.declarations) {
|
|
1188
|
+
if (binding.id.type !== 'Identifier')
|
|
1189
|
+
throw new Error('Skiko binding must have one exported identifier')
|
|
1190
|
+
bindings.push(binding.id.name)
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (bindings.length !== 967) throw new Error('pinned Skiko binding count changed')
|
|
1194
|
+
const names = [
|
|
1195
|
+
'awaitSkiko',
|
|
1196
|
+
'loadedWasm',
|
|
1197
|
+
'registerSkikoWasmReadyCallback',
|
|
1198
|
+
'skikoApi',
|
|
1199
|
+
'GL',
|
|
1200
|
+
...bindings,
|
|
1201
|
+
]
|
|
1202
|
+
fs.writeFileSync(
|
|
1203
|
+
skikoPath,
|
|
1204
|
+
[
|
|
1205
|
+
'const runtime = globalThis.__peachKotlinSkiko;',
|
|
1206
|
+
"if (!runtime) throw Error('Kotlin tenant runtime was not installed before guest evaluation');",
|
|
1207
|
+
`for (const name of ${JSON.stringify(names)}) if (!(name in runtime)) throw Error('Kotlin tenant runtime has no ' + name);`,
|
|
1208
|
+
...names.map((name) => `export const ${name} = runtime.${name};`),
|
|
1209
|
+
'',
|
|
1210
|
+
].join('\n'),
|
|
1211
|
+
)
|
|
1212
|
+
}
|
|
784
1213
|
// kotlin's npm externals remain bare imports after linking. publish the
|
|
785
1214
|
// exact upstream ES modules beside the generated loader for workers.
|
|
786
1215
|
if (usesCompose) {
|
|
@@ -815,9 +1244,11 @@ async function compileKotlin(
|
|
|
815
1244
|
}
|
|
816
1245
|
} catch (error) {
|
|
817
1246
|
fs.rmSync(work, { recursive: true, force: true })
|
|
818
|
-
|
|
1247
|
+
let output = isRecord(error)
|
|
819
1248
|
? `${String(error.stderr ?? '')}${String(error.stdout ?? '')}`
|
|
820
1249
|
: ''
|
|
1250
|
+
for (const [name, source] of Object.entries(origins))
|
|
1251
|
+
output = output.replaceAll(path.join(generatedDir, name), source)
|
|
821
1252
|
throw new Error(
|
|
822
1253
|
`kotlin compile failed:\n${output.trim() || (error instanceof Error ? error.message : String(error))}`,
|
|
823
1254
|
)
|
|
@@ -882,6 +1313,22 @@ const CONTENT_TYPES: Record<string, string> = {
|
|
|
882
1313
|
'.wasm': 'application/wasm',
|
|
883
1314
|
}
|
|
884
1315
|
|
|
1316
|
+
// a compiled module's loader, import object and wasm, which a release bundle
|
|
1317
|
+
// carries beside its javascript
|
|
1318
|
+
export function kotlinArtifactFiles(
|
|
1319
|
+
hash: string,
|
|
1320
|
+
): { name: string; bytes: Buffer; contentType: string }[] {
|
|
1321
|
+
const dir = path.join(buildsDir(), hash, 'js')
|
|
1322
|
+
return fs
|
|
1323
|
+
.readdirSync(dir)
|
|
1324
|
+
.sort()
|
|
1325
|
+
.map((name) => {
|
|
1326
|
+
const contentType = CONTENT_TYPES[path.extname(name)]
|
|
1327
|
+
if (!contentType) throw new Error(`unexpected kotlin artifact file ${name}`)
|
|
1328
|
+
return { name, bytes: fs.readFileSync(path.join(dir, name)), contentType }
|
|
1329
|
+
})
|
|
1330
|
+
}
|
|
1331
|
+
|
|
885
1332
|
// serves a compiled module's loader, import object and wasm, cross-origin
|
|
886
1333
|
// to the simulator that imports them
|
|
887
1334
|
export function serveKotlinArtifact(pathname: string, res: ServerResponse): boolean {
|
|
@@ -915,13 +1362,10 @@ export function serveKotlinArtifact(pathname: string, res: ServerResponse): bool
|
|
|
915
1362
|
|
|
916
1363
|
const KOTLIN_PACKAGE_PATH = '/__peach/kotlin-package'
|
|
917
1364
|
|
|
918
|
-
// peachsim's own Kotlin module (toolchain pins and shims)
|
|
919
|
-
//
|
|
920
|
-
export function peachKotlinModuleRoot(
|
|
921
|
-
|
|
922
|
-
'peachsim/package.json',
|
|
923
|
-
)
|
|
924
|
-
return path.join(path.dirname(peachsimPackage), 'kotlin')
|
|
1365
|
+
// peachsim's own Kotlin module (toolchain pins and shims), found from this
|
|
1366
|
+
// file (src/ or dist-lib/ → ..) so apps need no direct peachsim dependency
|
|
1367
|
+
export function peachKotlinModuleRoot(): string {
|
|
1368
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'kotlin')
|
|
925
1369
|
}
|
|
926
1370
|
|
|
927
1371
|
// answers one /__peach/kotlin* request: the app's compiled module description,
|