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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*!
|
|
1
|
+
/*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -280,7 +280,6 @@ __export(dev_server_scanner_exports, {
|
|
|
280
280
|
__shouldReuseScannerCacheEntry: () => __shouldReuseScannerCacheEntry,
|
|
281
281
|
discoverListeningPorts: () => discoverListeningPorts,
|
|
282
282
|
discoverListeningProcesses: () => discoverListeningProcesses,
|
|
283
|
-
pidFromNetstat: () => pidFromNetstat,
|
|
284
283
|
probePort: () => probePort,
|
|
285
284
|
resolveListeningPid: () => resolveListeningPid,
|
|
286
285
|
resolveProcessCwd: () => resolveProcessCwd,
|
|
@@ -470,7 +469,7 @@ function hasOwnKeys(value) {
|
|
|
470
469
|
}
|
|
471
470
|
function hasPeachConfig(config) {
|
|
472
471
|
if (!config) return false;
|
|
473
|
-
return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
|
|
472
|
+
return config.nativeRenderer !== void 0 || typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
|
|
474
473
|
}
|
|
475
474
|
function applyPeachConfigToUrl(url, config) {
|
|
476
475
|
const parsed = new URL(url);
|
|
@@ -590,9 +589,9 @@ function configuredWidgetFromScan(value, bundleUrl) {
|
|
|
590
589
|
}
|
|
591
590
|
|
|
592
591
|
// src/one-widget-discovery.ts
|
|
593
|
-
async function readConfiguredOneWidget(bundleUrl) {
|
|
592
|
+
async function readConfiguredOneWidget(bundleUrl, fetchScan = fetch) {
|
|
594
593
|
try {
|
|
595
|
-
const response = await
|
|
594
|
+
const response = await fetchScan(new URL("/__server-scan", bundleUrl).href, {
|
|
596
595
|
signal: AbortSignal.timeout(3e4)
|
|
597
596
|
});
|
|
598
597
|
if (!response.ok || !response.headers.get("content-type")?.includes("json"))
|
|
@@ -1215,30 +1214,19 @@ async function discoverListeningPorts(excludePorts = []) {
|
|
|
1215
1214
|
const processes = await discoverListeningProcesses(excludePorts);
|
|
1216
1215
|
return processes.map((p) => p.port);
|
|
1217
1216
|
}
|
|
1218
|
-
function pidFromNetstat(stdout, port) {
|
|
1219
|
-
for (const line of stdout.split("\n")) {
|
|
1220
|
-
if (!line.includes("LISTEN")) continue;
|
|
1221
|
-
const parts = line.trim().split(/\s+/);
|
|
1222
|
-
if (!parts[3]?.endsWith(`.${port}`)) continue;
|
|
1223
|
-
for (const token of parts) {
|
|
1224
|
-
const owner = token.match(/^\S+:(\d+)$/);
|
|
1225
|
-
if (owner) {
|
|
1226
|
-
const pid = Number(owner[1]);
|
|
1227
|
-
if (pid > 0) return pid;
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
return null;
|
|
1232
|
-
}
|
|
1233
1217
|
async function resolveListeningPid(port) {
|
|
1234
1218
|
if (!Number.isInteger(port) || port <= 0) return null;
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1219
|
+
if (process.platform === "darwin") {
|
|
1220
|
+
try {
|
|
1221
|
+
const { stdout } = await execGuarded(
|
|
1222
|
+
`lsof -nP -a -iTCP:${port} -sTCP:LISTEN -t 2>/dev/null`,
|
|
1223
|
+
8e3
|
|
1224
|
+
);
|
|
1225
|
+
const pids = [...new Set(stdout.trim().split(/\s+/).map(Number))];
|
|
1226
|
+
return pids.length === 1 && Number.isInteger(pids[0]) && pids[0] > 0 ? pids[0] : null;
|
|
1227
|
+
} catch {
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1242
1230
|
}
|
|
1243
1231
|
try {
|
|
1244
1232
|
const { stdout } = await execGuarded(`ss -tlnp 2>/dev/null`, 8e3, {
|
|
@@ -1482,15 +1470,53 @@ async function probePort(port, buildIconProxyUrl, listeningProcess) {
|
|
|
1482
1470
|
...ownerCwd ? { cwd: ownerCwd } : {}
|
|
1483
1471
|
};
|
|
1484
1472
|
const onePath = `/node_modules/one/metro-entry.bundle?platform=ios&dev=true`;
|
|
1485
|
-
const [sootsimRes, statusRes, manifestRes, expoRes, swiftRes] = await Promise.all([
|
|
1486
|
-
knownNonPatched.has(port) ? Promise.resolve(null) : httpGet(probeHostname, port, "/
|
|
1473
|
+
const [sootsimRes, statusRes, manifestRes, expoRes, swiftRes, oneScanRes] = await Promise.all([
|
|
1474
|
+
knownNonPatched.has(port) ? Promise.resolve(null) : httpGet(probeHostname, port, "/__peachsim/"),
|
|
1487
1475
|
httpGet(probeHostname, port, "/status"),
|
|
1488
1476
|
knownOne.has(port) ? Promise.resolve(null) : httpGet(probeHostname, port, "/", "GET", MANIFEST_TIMEOUT_MS, {
|
|
1489
1477
|
"expo-platform": "ios"
|
|
1490
1478
|
}),
|
|
1491
1479
|
knownNonExpo.has(port) ? Promise.resolve(null) : httpGet(probeHostname, port, "/_expo/status"),
|
|
1492
|
-
httpGet(probeHostname, port, SWIFT_DISCOVERY_PATH)
|
|
1480
|
+
httpGet(probeHostname, port, SWIFT_DISCOVERY_PATH),
|
|
1481
|
+
httpGet(probeHostname, port, "/__server-scan", "GET", MANIFEST_TIMEOUT_MS)
|
|
1493
1482
|
]);
|
|
1483
|
+
const oneScan = oneScanRes?.statusCode === 200 ? safeParseManifest(oneScanRes.body) : null;
|
|
1484
|
+
if (Array.isArray(oneScan)) {
|
|
1485
|
+
for (const entry of oneScan) {
|
|
1486
|
+
if (!entry || typeof entry !== "object" || entry.framework !== "one" || entry.port !== port || typeof entry.bundleUrl !== "string")
|
|
1487
|
+
continue;
|
|
1488
|
+
let bundle;
|
|
1489
|
+
try {
|
|
1490
|
+
bundle = new URL(entry.bundleUrl);
|
|
1491
|
+
} catch {
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
if (bundle.protocol !== "http:" || !isLoopbackHost(bundle.hostname) || bundle.port !== String(port) || !bundle.pathname.endsWith("/one/metro-entry.bundle"))
|
|
1495
|
+
continue;
|
|
1496
|
+
knownOne.add(port);
|
|
1497
|
+
return enrichWithManifest(
|
|
1498
|
+
applyManifest(
|
|
1499
|
+
{
|
|
1500
|
+
port,
|
|
1501
|
+
framework: "one",
|
|
1502
|
+
bundleUrl: withRuntimeConfig(
|
|
1503
|
+
port,
|
|
1504
|
+
alignLoopbackBundleUrlWithProbe(
|
|
1505
|
+
bundle.href,
|
|
1506
|
+
buildLoopbackProbeBaseUrl("http:", probeHostname, port)
|
|
1507
|
+
),
|
|
1508
|
+
owner.cwd
|
|
1509
|
+
),
|
|
1510
|
+
lastSeen: Date.now(),
|
|
1511
|
+
...owner
|
|
1512
|
+
},
|
|
1513
|
+
manifestRes,
|
|
1514
|
+
buildIconProxyUrl
|
|
1515
|
+
),
|
|
1516
|
+
buildIconProxyUrl
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1494
1520
|
const swift = swiftRes?.statusCode === 200 ? parseSwiftHostDiscovery(safeParseManifest(swiftRes.body)) : null;
|
|
1495
1521
|
if (swift) {
|
|
1496
1522
|
const base = buildLoopbackProbeBaseUrl("http:", probeHostname, port);
|
|
@@ -1560,7 +1586,7 @@ async function probePort(port, buildIconProxyUrl, listeningProcess) {
|
|
|
1560
1586
|
framework: "one",
|
|
1561
1587
|
bundleUrl: withRuntimeConfig(
|
|
1562
1588
|
port,
|
|
1563
|
-
`${buildLoopbackProbeBaseUrl("http:", probeHostname, port)}/
|
|
1589
|
+
`${buildLoopbackProbeBaseUrl("http:", probeHostname, port)}/__peachsim/bundle.js`,
|
|
1564
1590
|
owner.cwd
|
|
1565
1591
|
),
|
|
1566
1592
|
hmrUrl: `${buildLoopbackProbeBaseUrl("ws:", probeHostname, port)}/hot`,
|
|
@@ -1753,7 +1779,6 @@ async function scanDevServers(opts = {}) {
|
|
|
1753
1779
|
__shouldReuseScannerCacheEntry,
|
|
1754
1780
|
discoverListeningPorts,
|
|
1755
1781
|
discoverListeningProcesses,
|
|
1756
|
-
pidFromNetstat,
|
|
1757
1782
|
probePort,
|
|
1758
1783
|
resolveListeningPid,
|
|
1759
1784
|
resolveProcessCwd,
|
package/dist-lib/sdk.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*!
|
|
1
|
+
/*! peach v0.1.1399 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -109,7 +109,6 @@ module.exports = __toCommonJS(sdk_exports);
|
|
|
109
109
|
|
|
110
110
|
// src/bridge-contract.ts
|
|
111
111
|
var SEMANTIC_READY_CONTENT_NODE_FLOOR = 100;
|
|
112
|
-
var PUBLIC_NO_NETWORK_MARKER = "public Peach Cloud simulators have no network";
|
|
113
112
|
var HEADLESS_TENANT_FETCH_HUNG_MARKER = "headless tenant fetch hung";
|
|
114
113
|
function readyProbeHasContent(probe) {
|
|
115
114
|
return (probe.browserSurfaceCount ?? 0) > 0 || probe.liveFrameActive || probe.targets > 0 || probe.nodes >= SEMANTIC_READY_CONTENT_NODE_FLOOR;
|
|
@@ -850,7 +849,7 @@ function shellEscape(value) {
|
|
|
850
849
|
}
|
|
851
850
|
function waitReadyReason(status) {
|
|
852
851
|
if (status.bridgeError) {
|
|
853
|
-
if (status.bridgeError.includes(HEADLESS_TENANT_FETCH_HUNG_MARKER)
|
|
852
|
+
if (status.bridgeError.includes(HEADLESS_TENANT_FETCH_HUNG_MARKER)) {
|
|
854
853
|
return status.bridgeError;
|
|
855
854
|
}
|
|
856
855
|
return `sim unreachable: ${status.bridgeError} \u2014 the target tab is closed or no sim is connected (check \`peach list\`, reopen with \`peach open\`)`;
|
|
@@ -870,13 +869,18 @@ async function inspectCaptureReady(bridge, timeoutMs) {
|
|
|
870
869
|
if (snapshot.ready) return snapshot;
|
|
871
870
|
return inspectWaitReady(bridge, timeoutMs);
|
|
872
871
|
}
|
|
873
|
-
var READY_SNAPSHOT_SEND_TIMEOUT_MS =
|
|
872
|
+
var READY_SNAPSHOT_SEND_TIMEOUT_MS = 15e3;
|
|
874
873
|
async function inspectReadySnapshot(bridge, budgetMs, simId) {
|
|
875
874
|
const deadline = Date.now() + budgetMs;
|
|
876
875
|
let result;
|
|
877
876
|
do {
|
|
878
|
-
|
|
879
|
-
|
|
877
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
878
|
+
const sendTimeoutMs = Math.max(
|
|
879
|
+
READY_SNAPSHOT_SEND_TIMEOUT_MS,
|
|
880
|
+
Math.min(remaining, 6e4)
|
|
881
|
+
);
|
|
882
|
+
result = await sendReadyWait(bridge, 1, sendTimeoutMs, simId);
|
|
883
|
+
} while (result.polls !== 1 && !result.error?.startsWith("sim disconnected") && !result.error?.startsWith("bridge never reconnected") && Date.now() < deadline);
|
|
880
884
|
return waitReadyStatus(
|
|
881
885
|
result,
|
|
882
886
|
result.polls === 1 && result.probe ? readyProbeIsReady(result.probe) : false
|
|
@@ -1520,6 +1524,17 @@ async function resolveTargetCoords(bridge, target) {
|
|
|
1520
1524
|
}
|
|
1521
1525
|
|
|
1522
1526
|
// cli/commands/inspect/settling.ts
|
|
1527
|
+
var RuntimeReadinessUnsupportedError = class extends Error {
|
|
1528
|
+
constructor(runtimeVersion, missingFields) {
|
|
1529
|
+
super(
|
|
1530
|
+
`runtime ${runtimeVersion} cannot report animation readiness (${missingFields.join(", ")} missing); run \`peach upgrade\` and reopen the simulator`
|
|
1531
|
+
);
|
|
1532
|
+
this.runtimeVersion = runtimeVersion;
|
|
1533
|
+
this.missingFields = missingFields;
|
|
1534
|
+
this.name = "RuntimeReadinessUnsupportedError";
|
|
1535
|
+
}
|
|
1536
|
+
code = "runtime_readiness_unsupported";
|
|
1537
|
+
};
|
|
1523
1538
|
async function waitForSootsimIdle({
|
|
1524
1539
|
bridge,
|
|
1525
1540
|
simId,
|
|
@@ -1529,16 +1544,23 @@ async function waitForSootsimIdle({
|
|
|
1529
1544
|
strict = false,
|
|
1530
1545
|
paintStill = false
|
|
1531
1546
|
}) {
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1547
|
+
let result;
|
|
1548
|
+
try {
|
|
1549
|
+
result = await bridge.send(
|
|
1550
|
+
{
|
|
1551
|
+
type: "evaluate",
|
|
1552
|
+
simId,
|
|
1553
|
+
code: `(async () => {
|
|
1537
1554
|
const start = Date.now()
|
|
1538
1555
|
const deadline = start + ${Math.max(0, Math.round(maxMs))}
|
|
1539
1556
|
const pollMs = ${Math.max(1, Math.round(pollMs))}
|
|
1540
1557
|
const strict = ${strict ? "true" : "false"}
|
|
1541
1558
|
const paintStill = ${paintStill ? "true" : "false"}
|
|
1559
|
+
// full mounted-geometry observation needs at least two hierarchy reads
|
|
1560
|
+
// plus the stillness window, which a sub-second budget cannot afford:
|
|
1561
|
+
// tight budgets (auto-wait) settle on worker stillness plus root
|
|
1562
|
+
// membership, generous ones confirm the geometry too.
|
|
1563
|
+
const fullGeometry = ${maxMs >= 1e3 ? "true" : "false"}
|
|
1542
1564
|
const requiredStillMs = paintStill ? 0 : ${Math.max(1, Math.round(stablePolls)) * Math.max(1, Math.round(pollMs))}
|
|
1543
1565
|
const minimumStablePolls = paintStill ? 1 : ${Math.min(2, Math.max(1, Math.round(stablePolls)))}
|
|
1544
1566
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
@@ -1575,58 +1597,177 @@ async function waitForSootsimIdle({
|
|
|
1575
1597
|
}
|
|
1576
1598
|
} catch {}
|
|
1577
1599
|
|
|
1600
|
+
// a worker that cannot answer within the remaining budget is busy, not
|
|
1601
|
+
// idle: its poll reads as unsettled. bounding each read by the budget
|
|
1602
|
+
// keeps a busy worker from holding a poll past the deadline.
|
|
1603
|
+
const BUSY = { busy: true }
|
|
1604
|
+
const answer = (read) =>
|
|
1605
|
+
Promise.race([
|
|
1606
|
+
Promise.resolve().then(read),
|
|
1607
|
+
new Promise((resolve) =>
|
|
1608
|
+
setTimeout(() => resolve(BUSY), Math.max(deadline - Date.now(), 100)),
|
|
1609
|
+
),
|
|
1610
|
+
])
|
|
1611
|
+
|
|
1578
1612
|
const readSnapshot = async () => {
|
|
1579
1613
|
let animating = false
|
|
1580
1614
|
let layoutDirty = false
|
|
1581
1615
|
let pendingFetches = 0
|
|
1616
|
+
let workerBusy = false
|
|
1582
1617
|
let requestTotal = 0
|
|
1583
1618
|
let requestInFlight = 0
|
|
1619
|
+
let appCommits = null
|
|
1620
|
+
let nodeCount = null
|
|
1584
1621
|
let renderStatsAvailable = false
|
|
1585
1622
|
let shellAnimating = false
|
|
1623
|
+
let webViewStarting = false
|
|
1624
|
+
let webViewBlocker = ''
|
|
1586
1625
|
// strict also waits out shell motion: a system alert, sheet or menu
|
|
1587
1626
|
// animates in the shell worker while the app's own stats read still,
|
|
1588
1627
|
// so its presentation is complete only once the shell's list empties.
|
|
1589
1628
|
if (strict) {
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
} catch {}
|
|
1629
|
+
const motion = await answer(() => window.__sootsimTest?.listAnimations?.()).catch(() => null)
|
|
1630
|
+
if (motion === BUSY) workerBusy = true
|
|
1631
|
+
else shellAnimating = Array.isArray(motion) && motion.some((anim) => anim.realm === 'shell' && anim.priority !== 'low')
|
|
1594
1632
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
? stats.memory.imageLoader.pendingFetches
|
|
1611
|
-
: 0
|
|
1612
|
-
pendingFetches = typeof pf === 'number' ? pf : 0
|
|
1633
|
+
const stats = await answer(() => window.__sootsimRenderHost?.queryStats?.()).catch(() => null)
|
|
1634
|
+
if (stats === BUSY) {
|
|
1635
|
+
workerBusy = true
|
|
1636
|
+
} else if (stats) {
|
|
1637
|
+
renderStatsAvailable = true
|
|
1638
|
+
const missingFields = ['hasActiveBlockingNativeAnimations', 'hasPendingBlockingAnimationFrames']
|
|
1639
|
+
.filter((field) => typeof stats[field] !== 'boolean')
|
|
1640
|
+
if (strict && missingFields.length > 0) {
|
|
1641
|
+
return {
|
|
1642
|
+
runtimeUnsupported: {
|
|
1643
|
+
error: 'runtime_readiness_unsupported',
|
|
1644
|
+
runtimeVersion: window.__sootsimRuntimeVersion || 'unknown',
|
|
1645
|
+
missingFields,
|
|
1646
|
+
},
|
|
1647
|
+
}
|
|
1613
1648
|
}
|
|
1614
|
-
|
|
1649
|
+
// positive checks only: an older runtime without the blocking
|
|
1650
|
+
// fields reads as still, and strict rejects that runtime above
|
|
1651
|
+
// instead of ever settling on unknown motion.
|
|
1652
|
+
animating =
|
|
1653
|
+
stats.hasActiveAnims === true ||
|
|
1654
|
+
stats.hasActiveBlockingNativeAnimations === true ||
|
|
1655
|
+
stats.hasPendingBlockingAnimationFrames === true
|
|
1656
|
+
layoutDirty =
|
|
1657
|
+
stats.layoutDirty === true && stats.renderRequested === true
|
|
1658
|
+
// a freshly-pushed screen showing a skeleton is layout-stable
|
|
1659
|
+
// but not actually settled \u2014 its data/images are still in
|
|
1660
|
+
// flight. treat bounded image-loader fetches as not-idle so
|
|
1661
|
+
// settle waits for real content, capped by the budget.
|
|
1662
|
+
const pf = stats.memory && stats.memory.imageLoader
|
|
1663
|
+
? stats.memory.imageLoader.pendingFetches
|
|
1664
|
+
: 0
|
|
1665
|
+
pendingFetches = typeof pf === 'number' ? pf : 0
|
|
1666
|
+
// tenant layout commits: a screen that keeps committing is still
|
|
1667
|
+
// mounting or remounting, even with no animation flag raised.
|
|
1668
|
+
appCommits = typeof stats.appCommitCount === 'number' ? stats.appCommitCount : null
|
|
1669
|
+
}
|
|
1670
|
+
// image preparation ends in the tenant before the compositor decodes
|
|
1671
|
+
// and realizes its pixels. both owners must finish before settling.
|
|
1672
|
+
// the compositor rejects a stats request it is too busy to answer.
|
|
1673
|
+
const compositor = await answer(() => window.__sootsimCompositor.getStats()).catch(() => BUSY)
|
|
1674
|
+
if (compositor === BUSY) {
|
|
1675
|
+
workerBusy = true
|
|
1676
|
+
} else {
|
|
1677
|
+
pendingFetches += compositor.images.pendingFetches
|
|
1678
|
+
pendingFetches += compositor.imageRealization.pending
|
|
1679
|
+
if (compositor.webViewCaptureSettled === false) {
|
|
1680
|
+
webViewStarting = true
|
|
1681
|
+
webViewBlocker = 'compositor webView capture not settled'
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1615
1684
|
try {
|
|
1616
1685
|
const counts = await window.__sootsimTest?.getRequestCounts?.()
|
|
1617
1686
|
requestTotal = typeof counts?.total === 'number' ? counts.total : 0
|
|
1618
1687
|
requestInFlight = typeof counts?.inFlight === 'number' ? counts.inFlight : 0
|
|
1619
1688
|
} catch {}
|
|
1620
|
-
|
|
1689
|
+
// mounted-root membership: the live node count moves while the tree
|
|
1690
|
+
// mounts, unmounts or recycles, in either render mode.
|
|
1691
|
+
try {
|
|
1692
|
+
const total = await window.__sootsimTest?.getNodeCount?.()
|
|
1693
|
+
nodeCount = typeof total === 'number' ? total : null
|
|
1694
|
+
} catch {}
|
|
1695
|
+
// the mounted hierarchy in render-worker mode comes from the test
|
|
1696
|
+
// bridge: window.__sootsimRoot is an empty placeholder there, so a
|
|
1697
|
+
// walk over it hashes every screen to the same empty string while
|
|
1698
|
+
// it moves. listInspectable is the worker-backed read describe
|
|
1699
|
+
// uses; a worker too busy to answer it reads as unsettled. tight
|
|
1700
|
+
// budgets skip this read and settle on the cheaper signals above.
|
|
1701
|
+
let hierarchy = null
|
|
1702
|
+
if (fullGeometry && typeof window.__sootsimTest?.listInspectable === 'function') {
|
|
1703
|
+
try {
|
|
1704
|
+
hierarchy = await answer(() => window.__sootsimTest.listInspectable({}))
|
|
1705
|
+
} catch {
|
|
1706
|
+
hierarchy = null
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
if (hierarchy === BUSY) {
|
|
1710
|
+
workerBusy = true
|
|
1711
|
+
hierarchy = null
|
|
1712
|
+
} else if (!Array.isArray(hierarchy)) {
|
|
1713
|
+
hierarchy = null
|
|
1714
|
+
}
|
|
1621
1715
|
const nodes = []
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1716
|
+
const PREVIEW_PROGRESS_REGEX = /^(creating webview|loading html|fetching bundle|waiting for first render|starting preview|loading simulator|fetching app code|waiting for native preview|starting design|loading canvas|fetching screens|painting the canvas|waiting for webview|waiting for preview|waiting for canvas|preview stalled)/i
|
|
1717
|
+
const notePreviewProgress = (tid, text) => {
|
|
1718
|
+
if (typeof tid === 'string' && (tid === 'web-preview-progress' || tid.endsWith('-preview-progress'))) {
|
|
1719
|
+
webViewStarting = true
|
|
1720
|
+
if (!webViewBlocker) webViewBlocker = 'preview progress active (' + tid + ')'
|
|
1721
|
+
}
|
|
1722
|
+
if (typeof text === 'string' && PREVIEW_PROGRESS_REGEX.test(text.trim())) {
|
|
1723
|
+
webViewStarting = true
|
|
1724
|
+
if (!webViewBlocker) webViewBlocker = 'webView startup in progress ("' + text.trim() + '")'
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
if (hierarchy) {
|
|
1728
|
+
for (const n of hierarchy) {
|
|
1729
|
+
const layout = n && n.layout
|
|
1730
|
+
if (layout && layout.width > 0) {
|
|
1731
|
+
const abs = (n && (n.absolute || n.absolutePosition)) || layout
|
|
1732
|
+
nodes.push(Math.round(abs.x) + ',' + Math.round(abs.y) + ',' + Math.round(layout.width))
|
|
1733
|
+
}
|
|
1734
|
+
notePreviewProgress(
|
|
1735
|
+
n.testID || n.testId,
|
|
1736
|
+
typeof n.text === 'string' ? n.text : '',
|
|
1737
|
+
)
|
|
1738
|
+
}
|
|
1739
|
+
} else {
|
|
1740
|
+
const root = window.__sootsimRoot
|
|
1741
|
+
if (root) {
|
|
1742
|
+
const walk = (n) => {
|
|
1743
|
+
if (n.layout && n.layout.width > 0) {
|
|
1744
|
+
nodes.push(Math.round(n.layout.x) + ',' + Math.round(n.layout.y) + ',' + Math.round(n.layout.width))
|
|
1745
|
+
}
|
|
1746
|
+
notePreviewProgress(
|
|
1747
|
+
n.testID || n.props?.testID || n.props?.testId,
|
|
1748
|
+
n.textContent || (typeof n.props?.children === 'string' ? n.props.children : ''),
|
|
1749
|
+
)
|
|
1750
|
+
for (const c of n.children || []) walk(c)
|
|
1751
|
+
}
|
|
1752
|
+
walk(root)
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
if (!webViewStarting && typeof document !== 'undefined') {
|
|
1756
|
+
const progressEl = document.querySelector(
|
|
1757
|
+
'[data-testid$="-preview-progress"], [data-testid="web-preview-progress"], [data-testid="ios-preview-progress"], [data-testid="design-preview-progress"]'
|
|
1758
|
+
)
|
|
1759
|
+
if (progressEl) {
|
|
1760
|
+
webViewStarting = true
|
|
1761
|
+
webViewBlocker = 'preview progress element displayed'
|
|
1762
|
+
} else {
|
|
1763
|
+
const unreadyWebView = document.querySelector(
|
|
1764
|
+
'[data-sootsim-webview-loading="true"], [data-sootsim-webview-mode="placeholder"]'
|
|
1765
|
+
)
|
|
1766
|
+
if (unreadyWebView) {
|
|
1767
|
+
webViewStarting = true
|
|
1768
|
+
webViewBlocker = 'webView document still loading'
|
|
1626
1769
|
}
|
|
1627
|
-
for (const c of n.children || []) walk(c)
|
|
1628
1770
|
}
|
|
1629
|
-
walk(root)
|
|
1630
1771
|
}
|
|
1631
1772
|
if (paintStill) {
|
|
1632
1773
|
const canvas = composite.getCanvas()
|
|
@@ -1640,7 +1781,7 @@ async function waitForSootsimIdle({
|
|
|
1640
1781
|
pixelFrameToken = token
|
|
1641
1782
|
}
|
|
1642
1783
|
}
|
|
1643
|
-
return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, layoutDirty, pendingFetches, renderStatsAvailable, requestTotal, requestInFlight }
|
|
1784
|
+
return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, layoutDirty, pendingFetches, workerBusy, webViewStarting, webViewBlocker, renderStatsAvailable, requestTotal, requestInFlight, appCommits, nodeCount }
|
|
1644
1785
|
}
|
|
1645
1786
|
|
|
1646
1787
|
// how long generic background network is allowed to keep us waiting AFTER
|
|
@@ -1658,6 +1799,8 @@ async function waitForSootsimIdle({
|
|
|
1658
1799
|
let lastScreenPixels = null
|
|
1659
1800
|
let screenChangedPolls = 0
|
|
1660
1801
|
let lastRequestTotal = -1
|
|
1802
|
+
let lastAppCommits = null
|
|
1803
|
+
let lastNodeCount = null
|
|
1661
1804
|
let stable = 0
|
|
1662
1805
|
let visuallyStillSince = 0
|
|
1663
1806
|
// counts across the whole budget: a flag that is true on every poll is a
|
|
@@ -1667,6 +1810,7 @@ async function waitForSootsimIdle({
|
|
|
1667
1810
|
let dirtyPolls = 0
|
|
1668
1811
|
let animatingPolls = 0
|
|
1669
1812
|
let layoutChangedPolls = 0
|
|
1813
|
+
let webViewStartingPolls = 0
|
|
1670
1814
|
const screenUnchanged = (pixels) => {
|
|
1671
1815
|
if (!pixels || !lastScreenPixels || pixels.width !== lastScreenPixels.width || pixels.height !== lastScreenPixels.height) return false
|
|
1672
1816
|
if (pixels === lastScreenPixels) return true
|
|
@@ -1677,9 +1821,11 @@ async function waitForSootsimIdle({
|
|
|
1677
1821
|
}
|
|
1678
1822
|
while (Date.now() < deadline) {
|
|
1679
1823
|
const snapshot = await readSnapshot()
|
|
1824
|
+
if (snapshot.runtimeUnsupported) return snapshot.runtimeUnsupported
|
|
1680
1825
|
polls++
|
|
1681
1826
|
if (snapshot.layoutDirty) dirtyPolls++
|
|
1682
1827
|
if (snapshot.animating || snapshot.shellAnimating) animatingPolls++
|
|
1828
|
+
if (snapshot.webViewStarting) webViewStartingPolls++
|
|
1683
1829
|
if (polls > 1 && snapshot.layout !== lastLayout) layoutChangedPolls++
|
|
1684
1830
|
const screenChanged = paintStill && !screenUnchanged(snapshot.screenPixels)
|
|
1685
1831
|
if (polls > 1 && screenChanged) screenChangedPolls++
|
|
@@ -1689,12 +1835,24 @@ async function waitForSootsimIdle({
|
|
|
1689
1835
|
!snapshot.animating &&
|
|
1690
1836
|
!snapshot.shellAnimating &&
|
|
1691
1837
|
!snapshot.layoutDirty)
|
|
1692
|
-
// visually settled: no
|
|
1693
|
-
//
|
|
1838
|
+
// visually settled: no worker motion or layout work, no new layout
|
|
1839
|
+
// commits, a stable mounted node count, no image content loading,
|
|
1840
|
+
// no webview startup in progress, layout unchanged since the last
|
|
1841
|
+
// poll. both modes wait out finite worker motion now; strict
|
|
1842
|
+
// additionally implies the flags through strictOk and adds shell
|
|
1843
|
+
// motion plus the blocking-stats floor. a null count or commit
|
|
1844
|
+
// reading means the runtime does not report it, which reads as
|
|
1845
|
+
// stable the way a missing stats block does.
|
|
1694
1846
|
const visuallyStill =
|
|
1695
1847
|
strictOk &&
|
|
1848
|
+
!snapshot.animating &&
|
|
1849
|
+
!snapshot.layoutDirty &&
|
|
1850
|
+
(snapshot.appCommits === null || lastAppCommits === null || snapshot.appCommits === lastAppCommits) &&
|
|
1851
|
+
(snapshot.nodeCount === null || lastNodeCount === null || snapshot.nodeCount === lastNodeCount) &&
|
|
1696
1852
|
!screenChanged &&
|
|
1697
1853
|
snapshot.pendingFetches === 0 &&
|
|
1854
|
+
!snapshot.workerBusy &&
|
|
1855
|
+
!snapshot.webViewStarting &&
|
|
1698
1856
|
snapshot.layout === lastLayout
|
|
1699
1857
|
// generic network quiet: nothing in flight and no new requests issued.
|
|
1700
1858
|
const networkQuiet =
|
|
@@ -1719,15 +1877,22 @@ async function waitForSootsimIdle({
|
|
|
1719
1877
|
lastLayout = snapshot.layout
|
|
1720
1878
|
lastScreenPixels = snapshot.screenPixels
|
|
1721
1879
|
lastRequestTotal = snapshot.requestTotal
|
|
1880
|
+
lastAppCommits = snapshot.appCommits
|
|
1881
|
+
lastNodeCount = snapshot.nodeCount
|
|
1722
1882
|
await sleep(pollMs)
|
|
1723
1883
|
}
|
|
1724
1884
|
const last = await readSnapshot()
|
|
1885
|
+
if (last.runtimeUnsupported) return last.runtimeUnsupported
|
|
1725
1886
|
const blockers = []
|
|
1726
1887
|
if (strict && !last.renderStatsAvailable) blockers.push('render stats unavailable')
|
|
1727
1888
|
if (last.animating) blockers.push('animations still running')
|
|
1728
1889
|
if (last.shellAnimating) blockers.push('shell animations still running')
|
|
1729
1890
|
if (last.layoutDirty) blockers.push('layout still dirty')
|
|
1891
|
+
if (last.appCommits !== null && lastAppCommits !== null && last.appCommits !== lastAppCommits) blockers.push('layout commits still landing')
|
|
1892
|
+
if (last.nodeCount !== null && lastNodeCount !== null && last.nodeCount !== lastNodeCount) blockers.push('node count still changing')
|
|
1730
1893
|
if (last.pendingFetches > 0) blockers.push(last.pendingFetches + ' image fetches pending')
|
|
1894
|
+
if (last.workerBusy) blockers.push('a render worker too busy to report')
|
|
1895
|
+
if (last.webViewStarting) blockers.push(last.webViewBlocker || 'webView still starting')
|
|
1731
1896
|
if (last.layout !== lastLayout) blockers.push('layout still changing')
|
|
1732
1897
|
if (paintStill && !last.screenPixels) blockers.push('screen pixels unavailable')
|
|
1733
1898
|
else if (paintStill && !screenUnchanged(last.screenPixels)) blockers.push('screen pixels still changing')
|
|
@@ -1740,17 +1905,31 @@ async function waitForSootsimIdle({
|
|
|
1740
1905
|
blockedBy:
|
|
1741
1906
|
(blockers.length ? blockers.join(', ') : 'unknown') +
|
|
1742
1907
|
' over ' + polls + ' polls (layout changed ' + layoutChangedPolls +
|
|
1743
|
-
'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x, screen changed ' + screenChangedPolls + 'x)',
|
|
1908
|
+
'x, layoutDirty ' + dirtyPolls + 'x, animating ' + animatingPolls + 'x, webViewStarting ' + webViewStartingPolls + 'x, screen changed ' + screenChangedPolls + 'x)',
|
|
1744
1909
|
}
|
|
1745
1910
|
} finally {
|
|
1746
1911
|
unsubscribe?.()
|
|
1747
1912
|
}
|
|
1748
1913
|
})()`
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
timeoutMs: Math.max(5e3, maxMs + 2e3)
|
|
1917
|
+
}
|
|
1918
|
+
);
|
|
1919
|
+
} catch (error) {
|
|
1920
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1921
|
+
return {
|
|
1922
|
+
settled: false,
|
|
1923
|
+
elapsed: maxMs,
|
|
1924
|
+
blockedBy: message
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
if (result?.error === "runtime_readiness_unsupported" && typeof result.runtimeVersion === "string" && Array.isArray(result.missingFields) && result.missingFields.every((field) => typeof field === "string")) {
|
|
1928
|
+
throw new RuntimeReadinessUnsupportedError(
|
|
1929
|
+
result.runtimeVersion,
|
|
1930
|
+
result.missingFields
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1754
1933
|
const { elapsed, settled, blockedBy } = result ?? {};
|
|
1755
1934
|
return {
|
|
1756
1935
|
elapsed: typeof elapsed === "number" ? elapsed : maxMs,
|
|
@@ -1768,6 +1947,7 @@ async function waitReadyAndSettle(bridge, timeoutMs, options = {}) {
|
|
|
1768
1947
|
});
|
|
1769
1948
|
let idle = null;
|
|
1770
1949
|
for (; ; ) {
|
|
1950
|
+
idle = null;
|
|
1771
1951
|
const ready = await inspectWaitReady(bridge, remaining(), options);
|
|
1772
1952
|
if (!ready.ready) return result(ready, idle);
|
|
1773
1953
|
idle = await waitForSootsimIdle({
|
|
@@ -2268,7 +2448,7 @@ function buildSimOpenUrl(currentUrl, target) {
|
|
|
2268
2448
|
const url = new URL(currentUrl);
|
|
2269
2449
|
for (const param of TARGET_SEARCH_PARAMS) url.searchParams.delete(param);
|
|
2270
2450
|
const pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
2271
|
-
const prefix = pathname === "/
|
|
2451
|
+
const prefix = pathname === "/__peachsim" || pathname.startsWith("/__peachsim/") ? "/__peachsim" : "";
|
|
2272
2452
|
const normalized = String(target).trim();
|
|
2273
2453
|
if (/^\d+$/.test(normalized)) {
|
|
2274
2454
|
url.pathname = `${prefix}/rn/${normalized}`;
|