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/cli/commands/detect.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { basename, dirname, join } from 'node:path'
|
|
|
5
5
|
import JSON5 from 'json5'
|
|
6
6
|
import { parse as parseYaml } from 'yaml'
|
|
7
7
|
import { nodeRepoFileReader } from './nodeRepoFileReader'
|
|
8
|
+
import type { NativeRenderer } from '../../src/config'
|
|
8
9
|
import type { DependencyVersion, ProjectManifest } from './projectReportContract'
|
|
9
10
|
import type { RepoFileReader } from './repoFileReaderContract'
|
|
10
11
|
|
|
@@ -479,3 +480,103 @@ export function installedManifest(
|
|
|
479
480
|
devDependencies: mapped(manifest.devDependencies),
|
|
480
481
|
}
|
|
481
482
|
}
|
|
483
|
+
|
|
484
|
+
// read only explicit static native build settings for the selected platform.
|
|
485
|
+
export function readNativeProjectRenderer(
|
|
486
|
+
dir: string,
|
|
487
|
+
platform: 'ios' | 'android',
|
|
488
|
+
files: RepoFileReader = nodeRepoFileReader,
|
|
489
|
+
): NativeRenderer | undefined {
|
|
490
|
+
const declarations: Array<{ source: string; enabled: boolean }> = []
|
|
491
|
+
const add = (value: unknown, source: string, strings = false) => {
|
|
492
|
+
if (value === undefined) return
|
|
493
|
+
if (typeof value === 'boolean') declarations.push({ source, enabled: value })
|
|
494
|
+
else if (strings && (value === 'true' || value === 'false'))
|
|
495
|
+
declarations.push({ source, enabled: value === 'true' })
|
|
496
|
+
else throw new Error(`${source}: new architecture setting must be true or false`)
|
|
497
|
+
}
|
|
498
|
+
const json = (path: string): unknown => {
|
|
499
|
+
const text = readProjectMetadata(join(dir, path), files)
|
|
500
|
+
if (text === null) {
|
|
501
|
+
if (files.exists(join(dir, path)))
|
|
502
|
+
throw new Error(`${path}: unreadable native metadata`)
|
|
503
|
+
return undefined
|
|
504
|
+
}
|
|
505
|
+
try {
|
|
506
|
+
return JSON.parse(text)
|
|
507
|
+
} catch {
|
|
508
|
+
throw new Error(`${path}: malformed native metadata`)
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const expo = metadataValue(json('app.json'), 'expo')
|
|
512
|
+
add(metadataValue(expo, 'newArchEnabled'), 'app.json expo.newArchEnabled')
|
|
513
|
+
add(
|
|
514
|
+
metadataValue(metadataValue(expo, platform), 'newArchEnabled'),
|
|
515
|
+
`app.json expo.${platform}.newArchEnabled`,
|
|
516
|
+
)
|
|
517
|
+
if (platform === 'ios') {
|
|
518
|
+
add(
|
|
519
|
+
metadataValue(json('ios/Podfile.properties.json'), 'newArchEnabled'),
|
|
520
|
+
'ios/Podfile.properties.json newArchEnabled',
|
|
521
|
+
true,
|
|
522
|
+
)
|
|
523
|
+
const podfile = readProjectMetadata(join(dir, 'ios/Podfile'), files)
|
|
524
|
+
if (podfile === null && files.exists(join(dir, 'ios/Podfile')))
|
|
525
|
+
throw new Error('ios/Podfile: unreadable native metadata')
|
|
526
|
+
for (const line of (podfile ?? '').split(/\r?\n/)) {
|
|
527
|
+
// use_react_native!(new_arch_enabled: false) assigns RCT_NEW_ARCH_ENABLED
|
|
528
|
+
// itself; a non-literal argument defers to the declarations read here.
|
|
529
|
+
const argument =
|
|
530
|
+
/^\s*(?:use_react_native!\s*\(\s*)?:?new_arch_enabled\s*(?:=>|:)\s*(true|false)\b/.exec(
|
|
531
|
+
line,
|
|
532
|
+
)
|
|
533
|
+
if (argument) {
|
|
534
|
+
declarations.push({
|
|
535
|
+
source: 'ios/Podfile use_react_native! new_arch_enabled',
|
|
536
|
+
enabled: argument[1] === 'true',
|
|
537
|
+
})
|
|
538
|
+
continue
|
|
539
|
+
}
|
|
540
|
+
if (!/^\s*ENV\s*\[\s*['"]RCT_NEW_ARCH_ENABLED['"]\s*\]\s*(?:\|\|)?=/.test(line))
|
|
541
|
+
continue
|
|
542
|
+
// expo's generated conditional applies the parsed podfile property;
|
|
543
|
+
// it adds no independent renderer declaration.
|
|
544
|
+
if (
|
|
545
|
+
/^\s*ENV\s*\[\s*(['"])RCT_NEW_ARCH_ENABLED\1\s*\]\s*\|\|=\s*(['"])0\2\s+if\s+podfile_properties\s*\[\s*(['"])newArchEnabled\3\s*\]\s*==\s*(['"])false\4\s*;?\s*(?:#.*)?$/.test(
|
|
546
|
+
line,
|
|
547
|
+
)
|
|
548
|
+
)
|
|
549
|
+
continue
|
|
550
|
+
const match =
|
|
551
|
+
/^\s*ENV\s*\[\s*(['"])RCT_NEW_ARCH_ENABLED\1\s*\]\s*=\s*(['"])([01])\2\s*;?\s*(?:#.*)?$/.exec(
|
|
552
|
+
line,
|
|
553
|
+
)
|
|
554
|
+
if (!match)
|
|
555
|
+
throw new Error(
|
|
556
|
+
'ios/Podfile: RCT_NEW_ARCH_ENABLED must be an unconditional literal 0 or 1 assignment',
|
|
557
|
+
)
|
|
558
|
+
declarations.push({
|
|
559
|
+
source: 'ios/Podfile RCT_NEW_ARCH_ENABLED',
|
|
560
|
+
enabled: match[3] === '1',
|
|
561
|
+
})
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
const gradle = readProjectMetadata(join(dir, 'android/gradle.properties'), files)
|
|
565
|
+
if (gradle === null && files.exists(join(dir, 'android/gradle.properties')))
|
|
566
|
+
throw new Error('android/gradle.properties: unreadable native metadata')
|
|
567
|
+
for (const line of (gradle ?? '').split(/\r?\n/)) {
|
|
568
|
+
if (!/^\s*newArchEnabled\s*(?:[=:]|\s)/.test(line)) continue
|
|
569
|
+
const match = /^\s*newArchEnabled\s*[=:]\s*(true|false)\s*$/.exec(line)
|
|
570
|
+
if (!match)
|
|
571
|
+
throw new Error('android/gradle.properties: newArchEnabled must be true or false')
|
|
572
|
+
add(match[1], 'android/gradle.properties newArchEnabled', true)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
const first = declarations[0]
|
|
576
|
+
if (!first) return undefined
|
|
577
|
+
if (declarations.some((entry) => entry.enabled !== first.enabled))
|
|
578
|
+
throw new Error(
|
|
579
|
+
`contradictory ${platform} native renderer metadata: ${declarations.map((entry) => entry.source).join(', ')}`,
|
|
580
|
+
)
|
|
581
|
+
return first.enabled ? 'fabric' : 'paper'
|
|
582
|
+
}
|
package/cli/commands/detox.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { dirname, resolve, join, relative } from 'path'
|
|
|
18
18
|
import { rnxPublicBrand } from '../../src/public-brand'
|
|
19
19
|
import { resolveCliAuth } from '../auth'
|
|
20
20
|
import { registerRun, shouldRegisterRun } from '../run-registry'
|
|
21
|
-
import {
|
|
21
|
+
import { peachExit } from '../run-rnx'
|
|
22
22
|
import { buildShellUrl } from './control'
|
|
23
23
|
import { discoverSootsimUrl } from './flow'
|
|
24
24
|
import { resolveDefaultUploadOrigin } from './upload'
|
|
@@ -54,7 +54,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
54
54
|
|
|
55
55
|
if (args.includes('--help') || args.includes('-h')) {
|
|
56
56
|
console.log(HELP)
|
|
57
|
-
|
|
57
|
+
peachExit(0)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
if (args[0] === 'init') {
|
|
@@ -145,7 +145,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
145
145
|
` error: no detox tests found. expected one of: ${detoxDirs.join(', ')}\n` +
|
|
146
146
|
` run 'peach detox init' to scaffold a starter suite.`,
|
|
147
147
|
)
|
|
148
|
-
|
|
148
|
+
peachExit(1)
|
|
149
149
|
}
|
|
150
150
|
testDir ??= process.cwd()
|
|
151
151
|
|
|
@@ -162,7 +162,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
162
162
|
` error: ${rnxPublicBrand.commandName} detox runs Jest from this project, which needs its dev dependencies:\n` +
|
|
163
163
|
` npm i -D ${rnxPublicBrand.packageName} jest`,
|
|
164
164
|
)
|
|
165
|
-
|
|
165
|
+
peachExit(1)
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -378,7 +378,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
378
378
|
) {
|
|
379
379
|
unlinkSync(resultsFile)
|
|
380
380
|
}
|
|
381
|
-
|
|
381
|
+
peachExit(code)
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
export type JestRunSummary = {
|
|
@@ -515,5 +515,5 @@ describe('example', () => {
|
|
|
515
515
|
console.log(`\n next:`)
|
|
516
516
|
console.log(` peach detox --app <dev-server-port> # run the sample`)
|
|
517
517
|
console.log(` peach detox --watch`)
|
|
518
|
-
|
|
518
|
+
peachExit(0)
|
|
519
519
|
}
|
package/cli/commands/device.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from 'peach-engine/settings/devices'
|
|
7
7
|
import { DUO_REST_PROGRESS, type DuoFoldRest } from 'peach-engine/settings/duo-fold-bus'
|
|
8
8
|
import { SOOTSIM_SHELL_COMMAND_NAME } from '../../src/menu'
|
|
9
|
-
import {
|
|
9
|
+
import { peachExit } from '../run-rnx'
|
|
10
10
|
import {
|
|
11
11
|
callInBridge,
|
|
12
12
|
createBridgeFromParsed,
|
|
@@ -121,7 +121,7 @@ async function readCurrentPosture(
|
|
|
121
121
|
export async function runDeviceCommand(args: string[], opts: DeviceCommandOptions) {
|
|
122
122
|
if (args.includes('--help') || args.includes('-h')) {
|
|
123
123
|
usage()
|
|
124
|
-
|
|
124
|
+
peachExit(0)
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
const subcommand = args[0]
|
|
@@ -130,7 +130,7 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
130
130
|
const current = await readCurrentDevice(parsed)
|
|
131
131
|
if (!current) {
|
|
132
132
|
console.error(' error: could not read current device from the target sim')
|
|
133
|
-
|
|
133
|
+
peachExit(1)
|
|
134
134
|
}
|
|
135
135
|
console.log(
|
|
136
136
|
` ${current.model} ${current.width}x${current.height} pt ${current.width * current.scale}x${current.height * current.scale} px ${current.name}`,
|
|
@@ -148,12 +148,12 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
148
148
|
const model = args[1]
|
|
149
149
|
if (!model) {
|
|
150
150
|
console.error(' error: device set expects a model')
|
|
151
|
-
|
|
151
|
+
peachExit(1)
|
|
152
152
|
}
|
|
153
153
|
if (!(model in devices)) {
|
|
154
154
|
console.error(` error: unknown device "${model}"`)
|
|
155
155
|
console.error(' run `peach device list` to see valid models')
|
|
156
|
-
|
|
156
|
+
peachExit(1)
|
|
157
157
|
}
|
|
158
158
|
const parsed = parseBridgeCliArgs(args, { port: opts.port })
|
|
159
159
|
const bridge = createBridgeFromParsed(parsed)
|
|
@@ -182,25 +182,25 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
182
182
|
const want = raw === undefined || raw === 'toggle' || isDuoRest(raw) ? raw : null
|
|
183
183
|
if (want === null) {
|
|
184
184
|
console.error(` error: device posture expects ${DUO_RESTS.join(', ')}, or toggle`)
|
|
185
|
-
|
|
185
|
+
peachExit(1)
|
|
186
186
|
}
|
|
187
187
|
const current = await readCurrentPosture(parsed)
|
|
188
188
|
if (!current) {
|
|
189
189
|
console.error(' error: could not read current posture from the target sim')
|
|
190
|
-
|
|
190
|
+
peachExit(1)
|
|
191
191
|
}
|
|
192
192
|
if (!devices[current.model].supportedPostures) {
|
|
193
193
|
console.error(
|
|
194
194
|
` error: ${current.model} has a single posture — fold posture is only available on iphone-duo`,
|
|
195
195
|
)
|
|
196
|
-
|
|
196
|
+
peachExit(1)
|
|
197
197
|
}
|
|
198
198
|
if (!want) {
|
|
199
199
|
if (!current.rest) {
|
|
200
200
|
console.error(
|
|
201
201
|
' error: the Duo fold has no current rest; choose a posture explicitly',
|
|
202
202
|
)
|
|
203
|
-
|
|
203
|
+
peachExit(1)
|
|
204
204
|
}
|
|
205
205
|
console.log(` ${current.rest} (${current.model})`)
|
|
206
206
|
return
|
|
@@ -209,7 +209,7 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
209
209
|
console.error(
|
|
210
210
|
' error: the Duo fold has no current rest; choose a posture explicitly',
|
|
211
211
|
)
|
|
212
|
-
|
|
212
|
+
peachExit(1)
|
|
213
213
|
}
|
|
214
214
|
const target: DuoFoldRest =
|
|
215
215
|
want === 'toggle' ? (current.rest === 'closed' ? 'open' : 'closed') : want
|
|
@@ -246,7 +246,7 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
246
246
|
console.error(
|
|
247
247
|
` error: the fold did not come to rest at ${target} within 10s (${current.model})`,
|
|
248
248
|
)
|
|
249
|
-
|
|
249
|
+
peachExit(1)
|
|
250
250
|
}
|
|
251
251
|
console.log(` set posture: ${target} (${current.model})`)
|
|
252
252
|
} finally {
|
|
@@ -257,5 +257,5 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
257
257
|
|
|
258
258
|
usage()
|
|
259
259
|
console.error(` unknown subcommand: ${subcommand}`)
|
|
260
|
-
|
|
260
|
+
peachExit(1)
|
|
261
261
|
}
|
package/cli/commands/electron.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
//
|
|
2
|
-
// electron driver, or the native host via the desktop driver with --native
|
|
3
|
-
// (or PEACH_DESKTOP_APP). the drivers wrap findDesktopCompanion +
|
|
4
|
-
// launchDesktopCompanion, so this command is a thin CLI surface that
|
|
5
|
-
// parses options, prompts for install, and delegates everything else.
|
|
1
|
+
// desktop launches the native host on Mac and Linux, and Electron on Windows.
|
|
6
2
|
|
|
7
3
|
import { ensureProfile } from '../../src/profiles'
|
|
8
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
findDesktopCompanion,
|
|
6
|
+
wantsNativeDesktop,
|
|
7
|
+
notifyDesktopMigration,
|
|
8
|
+
} from '../desktop-companion'
|
|
9
9
|
import { desktopDriver, electronDriver } from '../drivers'
|
|
10
10
|
import { confirm } from '../prompt'
|
|
11
|
-
import {
|
|
11
|
+
import { peachExit } from '../run-rnx'
|
|
12
12
|
import { buildShellUrl } from './control'
|
|
13
13
|
import { printMissingCompanionHint } from './no-bridge-hint'
|
|
14
14
|
|
|
@@ -24,7 +24,7 @@ function parseInlinePort(args: string[]): number | undefined {
|
|
|
24
24
|
const port = raw ? Number(raw) : Number.NaN
|
|
25
25
|
if (!Number.isInteger(port) || port <= 0) {
|
|
26
26
|
console.error(` invalid --port value: ${raw || '(missing)'}`)
|
|
27
|
-
|
|
27
|
+
peachExit(1)
|
|
28
28
|
}
|
|
29
29
|
return port
|
|
30
30
|
}
|
|
@@ -33,6 +33,8 @@ export async function resolveElectronLaunchUrl(
|
|
|
33
33
|
args: string[],
|
|
34
34
|
opts: ElectronOptions,
|
|
35
35
|
): Promise<string | undefined> {
|
|
36
|
+
const deepLink = args.find((arg) => arg.startsWith('peach://'))
|
|
37
|
+
if (deepLink) return new URL(deepLink).href
|
|
36
38
|
const targetPort = parseInlinePort(args) ?? opts.port
|
|
37
39
|
return targetPort ? buildShellUrl(String(targetPort)) : undefined
|
|
38
40
|
}
|
|
@@ -49,6 +51,7 @@ options:
|
|
|
49
51
|
--port <number> connect to running dev server on this port
|
|
50
52
|
--profile <id> launch with an isolated persistent storage profile
|
|
51
53
|
--ephemeral launch with a temporary isolated storage profile
|
|
54
|
+
--electron launch Electron on Windows; native app on Mac and Linux
|
|
52
55
|
--native launch the native desktop host (peach desktop install --native)
|
|
53
56
|
|
|
54
57
|
examples:
|
|
@@ -57,17 +60,32 @@ examples:
|
|
|
57
60
|
peach desktop --profile qa
|
|
58
61
|
peach desktop --native
|
|
59
62
|
`)
|
|
60
|
-
|
|
63
|
+
peachExit(0)
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
const profileArg = args.find((_, i) => args[i - 1] === '--profile')
|
|
64
67
|
const ephemeralProfile = args.includes('--ephemeral')
|
|
65
68
|
if (profileArg && ephemeralProfile) {
|
|
66
69
|
console.error(' peach desktop: --profile cannot be combined with --ephemeral')
|
|
67
|
-
|
|
70
|
+
peachExit(1)
|
|
68
71
|
}
|
|
69
72
|
const profileId = profileArg ? ensureProfile(profileArg).id : undefined
|
|
70
|
-
|
|
73
|
+
if (args.includes('--native') && args.includes('--electron')) {
|
|
74
|
+
console.error(' --native cannot be combined with --electron')
|
|
75
|
+
peachExit(1)
|
|
76
|
+
}
|
|
77
|
+
const native = wantsNativeDesktop({
|
|
78
|
+
native: args.includes('--electron')
|
|
79
|
+
? false
|
|
80
|
+
: args.includes('--native')
|
|
81
|
+
? true
|
|
82
|
+
: undefined,
|
|
83
|
+
})
|
|
84
|
+
notifyDesktopMigration()
|
|
85
|
+
if (args.includes('--electron') && native)
|
|
86
|
+
console.error(
|
|
87
|
+
' Electron is Windows-only; using the native desktop app (peach install-desktop).',
|
|
88
|
+
)
|
|
71
89
|
let install = findDesktopCompanion({ native })
|
|
72
90
|
if (!install) {
|
|
73
91
|
printMissingCompanionHint(native)
|
|
@@ -76,10 +94,10 @@ examples:
|
|
|
76
94
|
if (canPrompt && (await confirm('run peach desktop install now?', true))) {
|
|
77
95
|
console.log()
|
|
78
96
|
const { runInstallDesktop } = await import('./install-desktop')
|
|
79
|
-
await runInstallDesktop(
|
|
97
|
+
await runInstallDesktop(['--yes'])
|
|
80
98
|
install = findDesktopCompanion({ native })
|
|
81
99
|
}
|
|
82
|
-
if (!install)
|
|
100
|
+
if (!install) peachExit(1)
|
|
83
101
|
}
|
|
84
102
|
console.log(` launching ${install.path}`)
|
|
85
103
|
|
|
@@ -92,7 +110,7 @@ examples:
|
|
|
92
110
|
})
|
|
93
111
|
if (!result.launched) {
|
|
94
112
|
console.error(` ${result.message}`)
|
|
95
|
-
|
|
113
|
+
peachExit(1)
|
|
96
114
|
}
|
|
97
115
|
console.log(` ${result.message}`)
|
|
98
116
|
}
|
package/cli/commands/flow.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { tmpdir } from 'os'
|
|
|
7
7
|
import * as path from 'path'
|
|
8
8
|
import { scanDevServers } from '../../scripts/dev-server-scanner'
|
|
9
9
|
import { loadOptionalDemoApps } from '../../scripts/optional-demo-registry'
|
|
10
|
-
import { applyPeachConfigToUrl, type PeachConfig } from '../../src/config'
|
|
11
10
|
import { printBridgeFailureDiagnostics } from '../bridge-diagnostics'
|
|
12
11
|
import {
|
|
13
12
|
SootSimBridgeFlowRunner,
|
|
@@ -15,14 +14,14 @@ import {
|
|
|
15
14
|
type SootSimFlowProfileResult,
|
|
16
15
|
type SootSimFlowTraceStep,
|
|
17
16
|
} from '../bridge-flow-runner'
|
|
18
|
-
import { readCurrentSimId, saveCurrentSimId } from '../current-sim'
|
|
19
|
-
import { findDesktopCompanion } from '../desktop-companion'
|
|
17
|
+
import { clearCurrentSimId, readCurrentSimId, saveCurrentSimId } from '../current-sim'
|
|
18
|
+
import { findDesktopCompanion, notifyDesktopMigration } from '../desktop-companion'
|
|
20
19
|
import { exportFlowSession } from '../flow-export'
|
|
21
20
|
import { parseFlowFile, validateFlowFile } from '../flow-file'
|
|
22
21
|
import { FlowLiveStatusReporter } from '../flow-live-status'
|
|
23
22
|
import { finalizeFlowSession, keepFlowCandidate, startFlowSession } from '../flow-session'
|
|
24
23
|
import { GLOBAL_FLAG_TAKES_VALUE } from '../parse-args'
|
|
25
|
-
import { PeachExit,
|
|
24
|
+
import { PeachExit, peachExit } from '../run-rnx'
|
|
26
25
|
import {
|
|
27
26
|
BRIDGE_VALUE_FLAGS,
|
|
28
27
|
createBridge,
|
|
@@ -32,15 +31,10 @@ import {
|
|
|
32
31
|
parseBridgeCliArgs,
|
|
33
32
|
resolveBridgePortForPin,
|
|
34
33
|
} from '../ws-bridge'
|
|
35
|
-
import {
|
|
36
|
-
buildShellUrl,
|
|
37
|
-
closeSimsBulk,
|
|
38
|
-
resolveShellBaseUrlForBridgePort,
|
|
39
|
-
runOpenCommand,
|
|
40
|
-
terminatePlaywrightHostsForSims,
|
|
41
|
-
} from './control'
|
|
34
|
+
import { closeSimsBulk, runOpenCommand, terminatePlaywrightHostsForSims } from './control'
|
|
42
35
|
import { printShellPerfReport } from './inspect'
|
|
43
36
|
import { resolveDefaultUploadOrigin } from './upload'
|
|
37
|
+
import type { PeachConfig } from '../../src/config'
|
|
44
38
|
import type { UploadResult } from './upload'
|
|
45
39
|
|
|
46
40
|
export { parseFlowFile, validateFlowFile } from '../flow-file'
|
|
@@ -59,6 +53,7 @@ const FLOW_BOOLEAN_FLAGS = [
|
|
|
59
53
|
'--headless',
|
|
60
54
|
'--headed',
|
|
61
55
|
'--proof',
|
|
56
|
+
'--load-env',
|
|
62
57
|
'--no-shell',
|
|
63
58
|
'--shell-only',
|
|
64
59
|
'--dry-run',
|
|
@@ -207,9 +202,9 @@ export async function resolveAppFrontmatterTarget(
|
|
|
207
202
|
export async function discoverSootsimUrl(): Promise<string | null> {
|
|
208
203
|
const servers = await scanDevServers()
|
|
209
204
|
const patched = servers.find((s) => s.patched)
|
|
210
|
-
if (patched) return `http://localhost:${patched.port}/
|
|
205
|
+
if (patched) return `http://localhost:${patched.port}/__peachsim/`
|
|
211
206
|
const any = servers[0]
|
|
212
|
-
if (any) return `http://localhost:${any.port}/
|
|
207
|
+
if (any) return `http://localhost:${any.port}/__peachsim/`
|
|
213
208
|
return null
|
|
214
209
|
}
|
|
215
210
|
|
|
@@ -221,6 +216,9 @@ export async function runFlowPlayback(
|
|
|
221
216
|
hooks: {
|
|
222
217
|
beforeSteps?: () => Promise<void>
|
|
223
218
|
runSteps?: (driver: SootSimBridgeFlowRunner, steps: MaestroStep[]) => Promise<void>
|
|
219
|
+
// afterSteps runs once the steps end, passed or failed, while the app is
|
|
220
|
+
// still on screen (maestro compare reads the app console here).
|
|
221
|
+
afterSteps?: (driver: SootSimBridgeFlowRunner) => Promise<void>
|
|
224
222
|
} = {},
|
|
225
223
|
): Promise<number> {
|
|
226
224
|
lastPreviewUploadResult = null
|
|
@@ -281,6 +279,8 @@ options:
|
|
|
281
279
|
frames without this, so its screenshot fails after 12
|
|
282
280
|
tries. use it for any run whose screenshots get
|
|
283
281
|
compared to anything
|
|
282
|
+
--load-env load local app environment through open for
|
|
283
|
+
react-native-config
|
|
284
284
|
--electron prefer opening the target in the desktop companion
|
|
285
285
|
--sim <id> target a specific current sim
|
|
286
286
|
--validate replay the drafted flow before clearing it
|
|
@@ -325,7 +325,7 @@ flow extension:
|
|
|
325
325
|
path: marketing/hero
|
|
326
326
|
withFrame: true
|
|
327
327
|
`)
|
|
328
|
-
|
|
328
|
+
peachExit(0)
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
const unexpected = findUnexpectedFlowArgs(args)
|
|
@@ -334,13 +334,13 @@ flow extension:
|
|
|
334
334
|
console.error(` error: ${problem.message}`)
|
|
335
335
|
}
|
|
336
336
|
console.error('\n run `peach maestro --help` for the flags this command accepts')
|
|
337
|
-
|
|
337
|
+
peachExit(1)
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
const flowPath = args[0]
|
|
341
341
|
if (!fs.existsSync(flowPath)) {
|
|
342
342
|
console.error(` error: ${flowPath} not found`)
|
|
343
|
-
|
|
343
|
+
peachExit(1)
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
const getFlag = (name: string) => args.find((_, i) => args[i - 1] === name)
|
|
@@ -408,7 +408,7 @@ flow extension:
|
|
|
408
408
|
: ''
|
|
409
409
|
if (previewBillingKind && previewBillingKind !== 'test_run') {
|
|
410
410
|
console.error(` error: invalid --billing-kind: ${previewBillingKind}`)
|
|
411
|
-
|
|
411
|
+
peachExit(1)
|
|
412
412
|
}
|
|
413
413
|
const replaceArgs = args.flatMap((arg, index) =>
|
|
414
414
|
args[index - 1] === '--replace' ? ['--replace', arg] : [],
|
|
@@ -439,7 +439,7 @@ flow extension:
|
|
|
439
439
|
const { frontmatter, steps } = parseFlowFile(flowContent)
|
|
440
440
|
if (steps.length === 0) {
|
|
441
441
|
console.error(' error: flow file must contain a YAML array of steps')
|
|
442
|
-
|
|
442
|
+
peachExit(1)
|
|
443
443
|
}
|
|
444
444
|
const targetDevice = deviceOverride || frontmatter.device || ''
|
|
445
445
|
const targetTheme = themeOverride || frontmatter.theme || ''
|
|
@@ -474,16 +474,17 @@ flow extension:
|
|
|
474
474
|
})
|
|
475
475
|
if (pinned.error !== undefined) {
|
|
476
476
|
console.error(` error: ${pinned.error}`)
|
|
477
|
-
|
|
477
|
+
peachExit(1)
|
|
478
478
|
} else {
|
|
479
479
|
parsedBridgeArgs.wsPort = pinned.port
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
|
-
if (wantElectron
|
|
482
|
+
if (wantElectron) notifyDesktopMigration()
|
|
483
|
+
if (wantElectron && !findDesktopCompanion({ native: false })) {
|
|
483
484
|
console.error(
|
|
484
|
-
' error: desktop
|
|
485
|
+
' error: desktop app not found. install it with `peach install-desktop`',
|
|
485
486
|
)
|
|
486
|
-
|
|
487
|
+
peachExit(1)
|
|
487
488
|
}
|
|
488
489
|
|
|
489
490
|
const effectiveOpenInNewSim =
|
|
@@ -551,26 +552,15 @@ flow extension:
|
|
|
551
552
|
const discovered = await discoverSootsimUrl()
|
|
552
553
|
if (!discovered) {
|
|
553
554
|
console.error(' error: no current sim and no peach target found')
|
|
554
|
-
|
|
555
|
+
peachExit(1)
|
|
555
556
|
}
|
|
556
557
|
openTarget = discovered
|
|
557
558
|
}
|
|
558
559
|
|
|
559
|
-
if (openTarget && targetDevice) {
|
|
560
|
-
const shellBaseUrl = applyPeachConfigToUrl(
|
|
561
|
-
pinnedShellBaseUrl || resolveShellBaseUrlForBridgePort(parsedBridgeArgs.wsPort),
|
|
562
|
-
targetAppResolution.runtimeConfig,
|
|
563
|
-
)
|
|
564
|
-
const url = new URL(
|
|
565
|
-
await buildShellUrl(openTarget, shellBaseUrl, { proof: proofMode }),
|
|
566
|
-
)
|
|
567
|
-
url.searchParams.set('device', targetDevice)
|
|
568
|
-
openTarget = url.toString()
|
|
569
|
-
}
|
|
570
|
-
|
|
571
560
|
if (openTarget) {
|
|
572
561
|
const openArgs = [openTarget]
|
|
573
562
|
if (pinnedShellBaseUrl) openArgs.push('--base-url', pinnedShellBaseUrl)
|
|
563
|
+
if (targetDevice) openArgs.push('--device', targetDevice)
|
|
574
564
|
openArgs.push(...replaceArgs)
|
|
575
565
|
openArgs.push(...remapArgs)
|
|
576
566
|
if (effectiveOpenInNewSim) {
|
|
@@ -587,6 +577,7 @@ flow extension:
|
|
|
587
577
|
openArgs.push('--headless')
|
|
588
578
|
}
|
|
589
579
|
if (proofMode) openArgs.push('--proof')
|
|
580
|
+
if (args.includes('--load-env')) openArgs.push('--load-env')
|
|
590
581
|
const opened = await runOpenCommand(openArgs, {
|
|
591
582
|
port: parsedBridgeArgs.wsPort,
|
|
592
583
|
timeoutMs: parsedBridgeArgs.commandTimeoutMs,
|
|
@@ -714,6 +705,16 @@ flow extension:
|
|
|
714
705
|
let exitCode = 0
|
|
715
706
|
const dryRun = args.includes('--dry-run') || args.includes('--no-run')
|
|
716
707
|
const startsWithLaunchApp = !!steps[0]?.launchApp
|
|
708
|
+
let afterStepsRan = false
|
|
709
|
+
const runAfterSteps = async () => {
|
|
710
|
+
if (!hooks.afterSteps || afterStepsRan) return
|
|
711
|
+
afterStepsRan = true
|
|
712
|
+
try {
|
|
713
|
+
await hooks.afterSteps(driver)
|
|
714
|
+
} catch (hookErr: any) {
|
|
715
|
+
console.warn(` after-steps hook failed: ${hookErr?.message || String(hookErr)}`)
|
|
716
|
+
}
|
|
717
|
+
}
|
|
717
718
|
try {
|
|
718
719
|
if (!startsWithLaunchApp) {
|
|
719
720
|
await driver.waitForTree(120000)
|
|
@@ -788,6 +789,7 @@ flow extension:
|
|
|
788
789
|
await hooks.beforeSteps?.()
|
|
789
790
|
if (hooks.runSteps) await hooks.runSteps(driver, steps)
|
|
790
791
|
else await driver.runFlow(steps, { skipPlan: dryRun })
|
|
792
|
+
await runAfterSteps()
|
|
791
793
|
if (profile) {
|
|
792
794
|
profileResult = await driver.stopProfile()
|
|
793
795
|
}
|
|
@@ -828,6 +830,7 @@ flow extension:
|
|
|
828
830
|
}
|
|
829
831
|
console.log()
|
|
830
832
|
} catch (err: any) {
|
|
833
|
+
await runAfterSteps()
|
|
831
834
|
if (profile && !profileResult) {
|
|
832
835
|
try {
|
|
833
836
|
profileResult = await driver.stopProfile()
|
|
@@ -1068,9 +1071,14 @@ async function closeFlowOwnedSims(
|
|
|
1068
1071
|
const openIds = ids.filter((id) =>
|
|
1069
1072
|
sims.some((sim) => sim.id === id && sim.readyState === 'open'),
|
|
1070
1073
|
)
|
|
1071
|
-
if (openIds.length === 0) return
|
|
1072
1074
|
const result = await closeSimsBulk(bridge, wsPort, commandTimeoutMs, openIds)
|
|
1073
1075
|
await terminatePlaywrightHostsForSims(sims, openIds)
|
|
1076
|
+
// retiring a flow-owned browser also retires its selection. a caller that
|
|
1077
|
+
// selected another sim during the flow keeps that selection.
|
|
1078
|
+
const selected = readCurrentSimId()
|
|
1079
|
+
if (selected && ids.includes(selected) && !result.remaining.includes(selected)) {
|
|
1080
|
+
clearCurrentSimId()
|
|
1081
|
+
}
|
|
1074
1082
|
if (result.closed.length > 0) {
|
|
1075
1083
|
console.log(` closed flow sim(s): ${result.closed.join(', ')}`)
|
|
1076
1084
|
}
|
|
@@ -1185,7 +1193,7 @@ async function runFlowSession(args: string[]) {
|
|
|
1185
1193
|
const result = keepFlowCandidate()
|
|
1186
1194
|
if (!result.active) {
|
|
1187
1195
|
console.error(' no active flow draft — run `peach maestro start` first')
|
|
1188
|
-
|
|
1196
|
+
peachExit(1)
|
|
1189
1197
|
}
|
|
1190
1198
|
if (!result.kept) {
|
|
1191
1199
|
console.log(' no pending action to keep')
|
|
@@ -1214,7 +1222,7 @@ async function runFlowSession(args: string[]) {
|
|
|
1214
1222
|
)
|
|
1215
1223
|
if (!result.active) {
|
|
1216
1224
|
console.error(' no active flow draft — run `peach maestro start` first')
|
|
1217
|
-
|
|
1225
|
+
peachExit(1)
|
|
1218
1226
|
}
|
|
1219
1227
|
if (!result.valid) {
|
|
1220
1228
|
console.error(' flow draft is not valid:')
|
|
@@ -1222,7 +1230,7 @@ async function runFlowSession(args: string[]) {
|
|
|
1222
1230
|
console.error(
|
|
1223
1231
|
' draft preserved — keep at least one real interaction or run `peach maestro start` to reset',
|
|
1224
1232
|
)
|
|
1225
|
-
|
|
1233
|
+
peachExit(1)
|
|
1226
1234
|
}
|
|
1227
1235
|
|
|
1228
1236
|
if (validate) {
|
|
@@ -1267,13 +1275,13 @@ async function runFlowSession(args: string[]) {
|
|
|
1267
1275
|
const flowPath = args[1]
|
|
1268
1276
|
if (!flowPath || flowPath.startsWith('-')) {
|
|
1269
1277
|
console.error(' usage: peach maestro validate <path>')
|
|
1270
|
-
|
|
1278
|
+
peachExit(1)
|
|
1271
1279
|
}
|
|
1272
1280
|
const issues = validateFlowFile(flowPath)
|
|
1273
1281
|
if (issues.length > 0) {
|
|
1274
1282
|
console.error(` x ${flowPath} failed validation:`)
|
|
1275
1283
|
for (const issue of issues) console.error(` - ${issue}`)
|
|
1276
|
-
|
|
1284
|
+
peachExit(1)
|
|
1277
1285
|
}
|
|
1278
1286
|
console.log(` + ${flowPath} looks valid`)
|
|
1279
1287
|
return
|
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
// what `peach swift` and `peach kotlin` share: a loopback-only guest host
|
|
2
2
|
// and the teardown of the simulator, driver and bridge their open started.
|
|
3
3
|
import { isLoopbackHost } from '../../src/backend-origin'
|
|
4
|
+
import { clearCurrentSimId, readCurrentSimId } from '../current-sim'
|
|
4
5
|
import { createBridge } from '../ws-bridge'
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
printDriverDiagnosticLogTail,
|
|
8
|
+
runCloseCommand,
|
|
9
|
+
type OpenCommandResources,
|
|
10
|
+
} from './control'
|
|
6
11
|
import type http from 'node:http'
|
|
7
12
|
|
|
13
|
+
export async function waitForGuestCompletion(
|
|
14
|
+
resources: OpenCommandResources,
|
|
15
|
+
signal: AbortSignal,
|
|
16
|
+
label: string,
|
|
17
|
+
): Promise<number> {
|
|
18
|
+
if (signal.aborted) return 0
|
|
19
|
+
let onAbort: () => void = () => {}
|
|
20
|
+
const aborted = new Promise<null>((resolve) => {
|
|
21
|
+
onAbort = () => resolve(null)
|
|
22
|
+
signal.addEventListener('abort', onAbort, { once: true })
|
|
23
|
+
})
|
|
24
|
+
try {
|
|
25
|
+
const result = await Promise.race([
|
|
26
|
+
aborted,
|
|
27
|
+
...(resources.driver?.completion ? [resources.driver.completion] : []),
|
|
28
|
+
])
|
|
29
|
+
if (result === null) return 0
|
|
30
|
+
// an exited driver has already closed its page and simulator.
|
|
31
|
+
if (result.signal === null && result.code !== null) {
|
|
32
|
+
if (resources.simId && readCurrentSimId() === resources.simId) clearCurrentSimId()
|
|
33
|
+
delete resources.simId
|
|
34
|
+
}
|
|
35
|
+
const code = result.code === 0 && result.signal === null ? 0 : 1
|
|
36
|
+
if (code && resources.driver) printDriverDiagnosticLogTail(resources.driver)
|
|
37
|
+
console.log(
|
|
38
|
+
` ${label} completed: ${result.signal ? `signal ${result.signal}` : `exit ${result.code ?? 'unknown'}`}${result.code !== null && resources.driver?.completionFile ? `; receipt ${resources.driver.completionFile}` : ''}`,
|
|
39
|
+
)
|
|
40
|
+
return code
|
|
41
|
+
} finally {
|
|
42
|
+
signal.removeEventListener('abort', onAbort)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
8
46
|
export function isLoopbackGuestRequest(
|
|
9
47
|
req: http.IncomingMessage,
|
|
10
48
|
origin: string,
|