peachsim 0.1.1644 → 0.1.1653
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 +12 -0
- package/cli/app-catalog.ts +1 -1
- package/cli/app-fonts.ts +11 -80
- package/cli/app-splash.ts +18 -12
- package/cli/bridge-flow-runner.ts +11 -7
- package/cli/cloud-client.ts +10 -13
- package/cli/command-registry.ts +13 -4
- package/cli/commands/box.ts +4 -4
- package/cli/commands/control.ts +93 -53
- package/cli/commands/coverage-parser.ts +67 -0
- package/cli/commands/coverage.ts +550 -0
- package/cli/commands/debug.ts +9 -18
- package/cli/commands/desktop.ts +19 -7
- package/cli/commands/device.ts +14 -9
- package/cli/commands/guest-host.ts +6 -10
- package/cli/commands/inspect/core.ts +81 -17
- package/cli/commands/inspect.ts +13 -8
- package/cli/commands/kotlin.ts +2 -2
- package/cli/commands/maestro-compare.ts +1 -1
- package/cli/commands/platform.ts +20 -11
- package/cli/commands/preview.ts +16 -18
- package/cli/commands/runtime.ts +3 -2
- package/cli/commands/screenshot-layers.ts +1 -1
- package/cli/commands/screenshot.ts +1 -1
- package/cli/commands/serve.ts +5 -1
- package/cli/commands/support.ts +4 -4
- package/cli/commands/three-mode.ts +22 -15
- package/cli/commands/upload.ts +44 -45
- package/cli/current-sim.ts +38 -0
- package/cli/desktop-companion.ts +36 -8
- package/cli/device-option.ts +1 -1
- package/cli/drivers/playwright.ts +1 -0
- package/cli/drivers/types.ts +4 -0
- package/cli/help-core.ts +1 -1
- package/cli/internal-child.ts +2 -1
- package/cli/main.ts +9 -3
- package/cli/outbound-endpoints.ts +11 -3
- package/cli/output-redaction.ts +39 -14
- package/cli/remote-simulators.ts +4 -1
- package/cli/scene-video.ts +5 -2
- package/cli/send-to-box.ts +7 -10
- package/cli/telemetry.ts +1 -1
- package/cli/ws-bridge.ts +8 -1
- package/detox/attempt-events.cjs +3 -1
- package/detox/attempt-events.d.cts +1 -1
- package/detox/expectations.ts +108 -99
- package/detox/frame-offset.d.mts +6 -0
- package/detox/frame-offset.mjs +35 -0
- package/detox/gestures.ts +6 -18
- package/detox/index.ts +169 -101
- package/detox/jest-preset.cjs +1 -3
- package/detox/jest-transform.cjs +4 -0
- package/detox/matchers.ts +3 -0
- package/detox/motion-change.cjs +46 -114
- package/detox/motion-change.d.cts +5 -27
- 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 +65 -1
- package/dist-lib/bridge-contract-input.mjs +63 -1
- package/dist-lib/bridge-contract.cjs +19 -1
- package/dist-lib/bridge-contract.mjs +17 -1
- package/dist-lib/bundle-channel.cjs +38091 -14846
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +5 -3
- package/dist-lib/detox/index.cjs +1244 -4509
- package/dist-lib/detox/jest-preset.cjs +1 -3
- package/dist-lib/detox/jest-transform.cjs +4 -0
- package/dist-lib/dev-bundle-resolution.cjs +28 -24
- package/dist-lib/expo-swift-bundle.cjs +35049 -13125
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +45112 -63143
- package/dist-lib/host/fetch-proxy-handler.cjs +3544 -11
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1909 -2029
- 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 +2 -2
- 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 +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +380 -522
- package/dist-lib/native-app-identity-project.cjs +39 -252
- package/dist-lib/native-app-identity-project.mjs +1174 -11
- package/dist-lib/native-app-identity.cjs +8 -3
- package/dist-lib/native-app-identity.mjs +8 -3
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/plist.cjs +2 -244
- 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/public-origins-lib-esm.json +290 -0
- package/dist-lib/public-origins-lib-static.json +1 -0
- package/dist-lib/public-origins-lib.json +1852 -0
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +14 -253
- package/dist-lib/sdk.cjs +83 -16
- package/dist-lib/sdk.mjs +81 -15
- package/dist-lib/skills.cjs +50546 -75098
- package/dist-lib/swift-compile.cjs +25897 -1070
- package/dist-lib/swift-compile.mjs +25866 -1033
- package/dist-lib/swift-package.cjs +79 -1
- package/dist-lib/swift-package.mjs +75 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +34273 -12349
- package/dist-lib/vite.cjs +373 -298
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/dist-types/packages/peach-compat/src/engine-config.d.ts +2 -0
- package/dist-types/packages/peach-compat/src/stubs/form-sheet-spring.d.ts +9 -0
- package/dist-types/packages/peach-compat/src/stubs/native-evaluation-lease.d.ts +47 -0
- package/dist-types/packages/peach-compat/src/stubs/native-seams/react-native-cookies.d.ts +113 -0
- package/dist-types/packages/peach-compat/src/stubs/native-seams/react-native-gesture-handler.d.ts +154 -0
- package/dist-types/packages/peach-compat/src/stubs/screen-transition-math.d.ts +756 -0
- package/dist-types/packages/peach-compat/src/stubs/transition-scalar.d.ts +25 -0
- package/dist-types/packages/peach-engine/src/billing/plans.d.ts +64 -0
- package/dist-types/packages/peach-engine/src/browser-page-target.d.ts +2 -0
- package/dist-types/packages/peach-engine/src/bundle-platform-url.d.ts +3 -0
- package/dist-types/packages/peach-engine/src/capture/browser-accessibility-host.d.ts +3 -0
- package/dist-types/packages/peach-engine/src/capture/browser-composite.d.ts +55 -0
- package/dist-types/packages/peach-engine/src/capture/browserAccessibilityContract.d.ts +27 -0
- package/dist-types/packages/peach-engine/src/capture/nativeScreenCaptureContract.d.ts +22 -0
- package/dist-types/packages/peach-engine/src/cli-companion.d.ts +36 -0
- package/dist-types/packages/peach-engine/src/components/native-ui/webview-frame-bridge.d.ts +34 -0
- package/dist-types/packages/peach-engine/src/config.d.ts +5 -0
- package/dist-types/packages/peach-engine/src/engine/android-stretch-effect.d.ts +57 -0
- package/dist-types/packages/peach-engine/src/engine/animated-driven-style.d.ts +47 -0
- package/dist-types/packages/peach-engine/src/engine/animated-value.d.ts +5 -0
- package/dist-types/packages/peach-engine/src/engine/animated.d.ts +195 -0
- package/dist-types/packages/peach-engine/src/engine/animation-clockContract.d.ts +15 -0
- package/dist-types/packages/peach-engine/src/engine/animation-graph.d.ts +148 -0
- package/dist-types/packages/peach-engine/src/engine/app-catalog-wire.d.ts +10 -0
- package/dist-types/packages/peach-engine/src/engine/app-font-registry.d.ts +87 -0
- package/dist-types/packages/peach-engine/src/engine/attributed-paragraph-paint.d.ts +7 -0
- package/dist-types/packages/peach-engine/src/engine/attributed-paragraph.d.ts +123 -0
- package/dist-types/packages/peach-engine/src/engine/audioContract.d.ts +16 -0
- package/dist-types/packages/peach-engine/src/engine/canvaskit-api.d.ts +93 -0
- package/dist-types/packages/peach-engine/src/engine/canvaskit-renderer.d.ts +216 -0
- package/dist-types/packages/peach-engine/src/engine/config-plugin-fonts.d.ts +40 -0
- package/dist-types/packages/peach-engine/src/engine/config-plugin-splash.d.ts +25 -0
- package/dist-types/packages/peach-engine/src/engine/cull-visibility.d.ts +44 -0
- package/dist-types/packages/peach-engine/src/engine/damage-rect.d.ts +19 -0
- package/dist-types/packages/peach-engine/src/engine/engine-resource.d.ts +6 -0
- package/dist-types/packages/peach-engine/src/engine/expo-manifest.d.ts +5 -0
- package/dist-types/packages/peach-engine/src/engine/fiber-debug-metadata.d.ts +30 -0
- package/dist-types/packages/peach-engine/src/engine/font-config.d.ts +139 -0
- package/dist-types/packages/peach-engine/src/engine/generated/sf-pro-font.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/engine/glass-material.d.ts +31 -0
- package/dist-types/packages/peach-engine/src/engine/host-composite.d.ts +100 -0
- package/dist-types/packages/peach-engine/src/engine/hosted-semantics.d.ts +49 -0
- package/dist-types/packages/peach-engine/src/engine/image-audit.d.ts +70 -0
- package/dist-types/packages/peach-engine/src/engine/image-loader.d.ts +350 -0
- package/dist-types/packages/peach-engine/src/engine/inspect-info.d.ts +27 -0
- package/dist-types/packages/peach-engine/src/engine/inspect-outline.d.ts +14 -0
- package/dist-types/packages/peach-engine/src/engine/inspect-visibility.d.ts +33 -0
- package/dist-types/packages/peach-engine/src/engine/ios-fast-scroll.d.ts +16 -0
- package/dist-types/packages/peach-engine/src/engine/layout-sab.d.ts +43 -0
- package/dist-types/packages/peach-engine/src/engine/liquid-glass-rect.d.ts +23 -0
- package/dist-types/packages/peach-engine/src/engine/liquid-glass-shaders.d.ts +9 -0
- package/dist-types/packages/peach-engine/src/engine/liquid-glass.d.ts +283 -0
- package/dist-types/packages/peach-engine/src/engine/live-object-counts.d.ts +6 -0
- package/dist-types/packages/peach-engine/src/engine/lottie-runtime.d.ts +32 -0
- package/dist-types/packages/peach-engine/src/engine/lottie-types.d.ts +138 -0
- package/dist-types/packages/peach-engine/src/engine/memory-counts.d.ts +27 -0
- package/dist-types/packages/peach-engine/src/engine/metalDirectContract.d.ts +108 -0
- package/dist-types/packages/peach-engine/src/engine/metalShaderContract.d.ts +217 -0
- package/dist-types/packages/peach-engine/src/engine/native-animation-driver.d.ts +112 -0
- package/dist-types/packages/peach-engine/src/engine/native-animation-graph.d.ts +62 -0
- package/dist-types/packages/peach-engine/src/engine/native-layout.d.ts +133 -0
- package/dist-types/packages/peach-engine/src/engine/native-resources.d.ts +26 -0
- package/dist-types/packages/peach-engine/src/engine/nativeReanimatedContract.d.ts +25 -0
- package/dist-types/packages/peach-engine/src/engine/nativeReplayContract.d.ts +64 -0
- package/dist-types/packages/peach-engine/src/engine/node.d.ts +589 -0
- package/dist-types/packages/peach-engine/src/engine/paint-boundary-types.d.ts +112 -0
- package/dist-types/packages/peach-engine/src/engine/paint-boundary.d.ts +57 -0
- package/dist-types/packages/peach-engine/src/engine/portalContract.d.ts +21 -0
- package/dist-types/packages/peach-engine/src/engine/portals.d.ts +20 -0
- package/dist-types/packages/peach-engine/src/engine/render-damage.d.ts +107 -0
- package/dist-types/packages/peach-engine/src/engine/riveContract.d.ts +420 -0
- package/dist-types/packages/peach-engine/src/engine/rn-spring.d.ts +51 -0
- package/dist-types/packages/peach-engine/src/engine/scene-table.d.ts +379 -0
- package/dist-types/packages/peach-engine/src/engine/scroll-momentum-math.d.ts +152 -0
- package/dist-types/packages/peach-engine/src/engine/scroll-pan-contacts.d.ts +10 -0
- package/dist-types/packages/peach-engine/src/engine/scroll-platform.d.ts +75 -0
- package/dist-types/packages/peach-engine/src/engine/scroll-range.d.ts +14 -0
- package/dist-types/packages/peach-engine/src/engine/scroll-velocity.d.ts +14 -0
- package/dist-types/packages/peach-engine/src/engine/scroll.d.ts +320 -0
- package/dist-types/packages/peach-engine/src/engine/signal.d.ts +7 -0
- package/dist-types/packages/peach-engine/src/engine/snapshot-cache-policy.d.ts +6 -0
- package/dist-types/packages/peach-engine/src/engine/svg-geometry.d.ts +41 -0
- package/dist-types/packages/peach-engine/src/engine/text-attachment-paragraphs.d.ts +10 -0
- package/dist-types/packages/peach-engine/src/engine/text-decoration.d.ts +11 -0
- package/dist-types/packages/peach-engine/src/engine/text-line-break.d.ts +22 -0
- package/dist-types/packages/peach-engine/src/engine/text-paint-receipt.d.ts +98 -0
- package/dist-types/packages/peach-engine/src/engine/text-paragraph.d.ts +11 -0
- package/dist-types/packages/peach-engine/src/engine/text-paragraphContract.d.ts +34 -0
- package/dist-types/packages/peach-engine/src/engine/text-transform.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/engine/types.d.ts +657 -0
- package/dist-types/packages/peach-engine/src/engine/visible-frame.d.ts +54 -0
- package/dist-types/packages/peach-engine/src/engine/webrtcContract.d.ts +74 -0
- package/dist-types/packages/peach-engine/src/graphite-stats-fields.d.ts +184 -0
- package/dist-types/packages/peach-engine/src/guest-import-meta.d.ts +12 -0
- package/dist-types/packages/peach-engine/src/guest-module-overrides.d.ts +7 -0
- package/dist-types/packages/peach-engine/src/ios/app-card-types.d.ts +7 -0
- package/dist-types/packages/peach-engine/src/ios/apps/canvas-map-data.d.ts +177 -0
- package/dist-types/packages/peach-engine/src/ios/aux-home-scene.d.ts +54 -0
- package/dist-types/packages/peach-engine/src/ios/chrome-metrics.d.ts +47 -0
- package/dist-types/packages/peach-engine/src/ios/constants.d.ts +21 -0
- package/dist-types/packages/peach-engine/src/ios/duo-housing.d.ts +37 -0
- package/dist-types/packages/peach-engine/src/ios/home/types.d.ts +29 -0
- package/dist-types/packages/peach-engine/src/ios/home-app-catalog.d.ts +43 -0
- package/dist-types/packages/peach-engine/src/ios/photo-store.d.ts +73 -0
- package/dist-types/packages/peach-engine/src/ios/photoLibraryContract.d.ts +166 -0
- package/dist-types/packages/peach-engine/src/ios/shell-controller-types.d.ts +21 -0
- package/dist-types/packages/peach-engine/src/ios/shell-transition-debug.d.ts +18 -0
- package/dist-types/packages/peach-engine/src/metro-asset-path.d.ts +21 -0
- package/dist-types/packages/peach-engine/src/metro-fingerprint-registry-client.d.ts +11 -0
- package/dist-types/packages/peach-engine/src/metro-fingerprint.d.ts +38 -0
- package/dist-types/packages/peach-engine/src/native-preview-bundle.d.ts +44 -0
- package/dist-types/packages/peach-engine/src/observability/types.d.ts +74 -0
- package/dist-types/packages/peach-engine/src/peach-warm-manifest.d.ts +32 -0
- package/dist-types/packages/peach-engine/src/permissions/app-settings-client.d.ts +25 -0
- package/dist-types/packages/peach-engine/src/permissions/system-permission-types.d.ts +20 -0
- package/dist-types/packages/peach-engine/src/platform/android-back-panel.d.ts +131 -0
- package/dist-types/packages/peach-engine/src/platform/android-gesture-exclusion.d.ts +28 -0
- package/dist-types/packages/peach-engine/src/platform/android-predictive-back.d.ts +33 -0
- package/dist-types/packages/peach-engine/src/platform/system-bars-state.d.ts +14 -0
- package/dist-types/packages/peach-engine/src/playwright-sim-host.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/preview/bundle-layout.d.ts +8 -0
- package/dist-types/packages/peach-engine/src/preview/bundle-variantContract.d.ts +24 -0
- package/dist-types/packages/peach-engine/src/preview/capture-bundle.d.ts +26 -0
- package/dist-types/packages/peach-engine/src/preview/compress-node.d.ts +2 -0
- package/dist-types/packages/peach-engine/src/preview/compress.d.ts +2 -0
- package/dist-types/packages/peach-engine/src/preview/presigned-upload.d.ts +60 -0
- package/dist-types/packages/peach-engine/src/preview/trace.d.ts +144 -0
- package/dist-types/packages/peach-engine/src/preview/upload-protocol.d.ts +152 -0
- package/dist-types/packages/peach-engine/src/profiler/animation-recording-loader.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/profiler/profile-trace.d.ts +4 -0
- package/dist-types/packages/peach-engine/src/react-bridge.d.ts +3 -0
- package/dist-types/packages/peach-engine/src/react-native/AppState.d.ts +45 -0
- package/dist-types/packages/peach-engine/src/react-native/Appearance.d.ts +34 -0
- package/dist-types/packages/peach-engine/src/react-native/framework-colors-data.d.ts +2 -0
- package/dist-types/packages/peach-engine/src/react-native/host-services.d.ts +42 -0
- package/dist-types/packages/peach-engine/src/react-native/layout-animation.d.ts +22 -0
- package/dist-types/packages/peach-engine/src/react-native/platform-colors.d.ts +17 -0
- package/dist-types/packages/peach-engine/src/remote-stream/client-config.d.ts +10 -0
- package/dist-types/packages/peach-engine/src/remote-stream/cloud-display.d.ts +58 -0
- package/dist-types/packages/peach-engine/src/remote-stream/scene-recording.d.ts +45 -0
- package/dist-types/packages/peach-engine/src/render-worker/animated-drive-output.d.ts +35 -0
- package/dist-types/packages/peach-engine/src/render-worker/app-load.d.ts +16 -0
- package/dist-types/packages/peach-engine/src/render-worker/aux-surface-types.d.ts +2 -0
- package/dist-types/packages/peach-engine/src/render-worker/canvaskit-shared.d.ts +42 -0
- package/dist-types/packages/peach-engine/src/render-worker/canvaskit-webgpu.d.ts +434 -0
- package/dist-types/packages/peach-engine/src/render-worker/canvaskit-worker-init.d.ts +12 -0
- package/dist-types/packages/peach-engine/src/render-worker/compositor-backend.d.ts +31 -0
- package/dist-types/packages/peach-engine/src/render-worker/compositor-gpu.d.ts +202 -0
- package/dist-types/packages/peach-engine/src/render-worker/compositor-protocol.d.ts +1945 -0
- package/dist-types/packages/peach-engine/src/render-worker/compositor-runtime/scroll-drag-resample.d.ts +44 -0
- package/dist-types/packages/peach-engine/src/render-worker/css-keyframes-drive.d.ts +36 -0
- package/dist-types/packages/peach-engine/src/render-worker/edit-menu-geometry.d.ts +8 -0
- package/dist-types/packages/peach-engine/src/render-worker/engine-resource-relay.d.ts +21 -0
- package/dist-types/packages/peach-engine/src/render-worker/expo-widgetContract.d.ts +16 -0
- package/dist-types/packages/peach-engine/src/render-worker/gesture-seam-protocol.d.ts +152 -0
- package/dist-types/packages/peach-engine/src/render-worker/graph-linked-value.d.ts +29 -0
- package/dist-types/packages/peach-engine/src/render-worker/graphite-pipeline-compiler-contract.d.ts +49 -0
- package/dist-types/packages/peach-engine/src/render-worker/graphite-pipeline-compiler.d.ts +12 -0
- package/dist-types/packages/peach-engine/src/render-worker/host-service-protocol.d.ts +756 -0
- package/dist-types/packages/peach-engine/src/render-worker/image-lifecycle-event.d.ts +22 -0
- package/dist-types/packages/peach-engine/src/render-worker/image-pixel-contract.d.ts +48 -0
- package/dist-types/packages/peach-engine/src/render-worker/live-frame-mirror.d.ts +34 -0
- package/dist-types/packages/peach-engine/src/render-worker/map-command-channel.d.ts +189 -0
- package/dist-types/packages/peach-engine/src/render-worker/mapNativeContract.d.ts +101 -0
- package/dist-types/packages/peach-engine/src/render-worker/native-dynamic-synchronizable.d.ts +22 -0
- package/dist-types/packages/peach-engine/src/render-worker/native-menu-gesture.d.ts +9 -0
- package/dist-types/packages/peach-engine/src/render-worker/native-ui-commandContract.d.ts +23 -0
- package/dist-types/packages/peach-engine/src/render-worker/native-ui-protocol.d.ts +1087 -0
- package/dist-types/packages/peach-engine/src/render-worker/native-widgetContract.d.ts +100 -0
- package/dist-types/packages/peach-engine/src/render-worker/nativeAnimMirrorContract.d.ts +174 -0
- package/dist-types/packages/peach-engine/src/render-worker/nativeSyncReplyContract.d.ts +11 -0
- package/dist-types/packages/peach-engine/src/render-worker/one-native-contentContract.d.ts +54 -0
- package/dist-types/packages/peach-engine/src/render-worker/pointerSampleContract.d.ts +14 -0
- package/dist-types/packages/peach-engine/src/render-worker/protocol.d.ts +880 -0
- package/dist-types/packages/peach-engine/src/render-worker/reanimated-css-contract.d.ts +63 -0
- package/dist-types/packages/peach-engine/src/render-worker/reanimated-readiness.d.ts +29 -0
- package/dist-types/packages/peach-engine/src/render-worker/resilient-frame-clock.d.ts +22 -0
- package/dist-types/packages/peach-engine/src/render-worker/retained-app-load.d.ts +20 -0
- package/dist-types/packages/peach-engine/src/render-worker/screen-edge-swipe-commit-protocol.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/render-worker/screen-form-sheet-commit-protocol.d.ts +26 -0
- package/dist-types/packages/peach-engine/src/render-worker/scroll-mirror-tenant.d.ts +37 -0
- package/dist-types/packages/peach-engine/src/render-worker/scroll-mirror.d.ts +164 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-event-registry.d.ts +119 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-gesture-regions.d.ts +41 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-protocol.d.ts +603 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-scene-schema.d.ts +101 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-tree-inline-sources.d.ts +13 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-tree-wire.d.ts +87 -0
- package/dist-types/packages/peach-engine/src/render-worker/test-bridge-protocol.d.ts +47 -0
- package/dist-types/packages/peach-engine/src/render-worker/text-inputContract.d.ts +93 -0
- package/dist-types/packages/peach-engine/src/render-worker/tree-sync-port.d.ts +102 -0
- package/dist-types/packages/peach-engine/src/render-worker/webgpu-frame-transaction.d.ts +292 -0
- package/dist-types/packages/peach-engine/src/render-worker/webgpu-readback-scheduler.d.ts +107 -0
- package/dist-types/packages/peach-engine/src/render-worker/webview-capture-protocol.d.ts +16 -0
- package/dist-types/packages/peach-engine/src/render-worker/worker-input-runtime.d.ts +211 -0
- package/dist-types/packages/peach-engine/src/render-worker/worker-storage.d.ts +33 -0
- package/dist-types/packages/peach-engine/src/render-worker/worklet-runtime.d.ts +987 -0
- package/dist-types/packages/peach-engine/src/render-worker/worklet-sab.d.ts +24 -0
- package/dist-types/packages/peach-engine/src/rolldown-bundle-inventory.d.ts +84 -0
- package/dist-types/packages/peach-engine/src/rolldown-production-fingerprint.d.ts +31 -0
- package/dist-types/packages/peach-engine/src/screenshots/timeline-assets.d.ts +10 -0
- package/dist-types/packages/peach-engine/src/screenshots/tokens.d.ts +9 -0
- package/dist-types/packages/peach-engine/src/settings/devices.d.ts +70 -0
- package/dist-types/packages/peach-engine/src/settings/duo-fold-bus.d.ts +87 -0
- package/dist-types/packages/peach-engine/src/settings/interface-rotation.d.ts +30 -0
- package/dist-types/packages/peach-engine/src/settings/react-native-configuration.d.ts +12 -0
- package/dist-types/packages/peach-engine/src/settings/scene-geometryContract.d.ts +40 -0
- package/dist-types/packages/peach-engine/src/settings/store.d.ts +37 -0
- package/dist-types/packages/peach-engine/src/shell-events.d.ts +203 -0
- package/dist-types/packages/peach-engine/src/shell-intent.d.ts +68 -0
- package/dist-types/packages/peach-engine/src/sootsim-event.d.ts +35 -0
- package/dist-types/packages/peach-engine/src/top-overlay-state.d.ts +5 -0
- package/dist-types/packages/peach-engine/src/web-authentication-automation-host.d.ts +2 -0
- package/dist-types/packages/peach-engine/src/webAuthenticationContract.d.ts +34 -0
- package/dist-types/packages/peach-engine/src/webview-automation-host.d.ts +180 -0
- package/dist-types/packages/peach-engine/src/wireframe-mode.d.ts +16 -0
- package/dist-types/packages/peach-globals/src/frameBreakdownContract.d.ts +16 -0
- package/dist-types/packages/peach-globals/src/index.d.ts +3311 -0
- package/dist-types/packages/peach-globals/src/nativeSurfaceContract.d.ts +76 -0
- package/dist-types/packages/peach-globals/src/pointerSourceEventContract.d.ts +8 -0
- package/dist-types/packages/peach-globals/src/profileTrace.d.ts +11 -0
- package/dist-types/packages/peach-globals/src/profilerContract.d.ts +223 -0
- package/dist-types/packages/peach-kit/src/capabilities.d.ts +721 -0
- package/dist-types/packages/peach-kit/src/internal.d.ts +4 -0
- package/dist-types/packages/peach-kit/src/materials/types.d.ts +27 -0
- package/dist-types/packages/peach-kit/src/materials/uikit-blur-material.d.ts +14 -0
- package/dist-types/packages/peach-kit/src/navigation-types.d.ts +11 -0
- package/dist-types/packages/peach-kit/src/react-bridge.d.ts +53 -0
- package/dist-types/packages/peach-kit/src/runtime.d.ts +26 -0
- package/dist-types/packages/peach-kit/src/ui/hosting-safe-area.d.ts +24 -0
- package/dist-types/packages/peach-kit/src/ui/popover-placement.d.ts +53 -0
- package/dist-types/packages/peach-kit/src/ui/presentationContract.d.ts +17 -0
- package/kotlin/ComposeSources/PeachComposeHost.kt +1 -1
- package/kotlin/JniSources/com/facebook/jni/HybridData.kt +0 -2
- package/kotlin/JniSources/dev/peach/jni/NativeBindings.kt +0 -2
- package/kotlin/JniTransportSources/dev/peach/jni/integration/JniTransport.kt +0 -2
- package/kotlin/README.md +6 -0
- package/package.json +22 -2
- package/runtime-companion.json +1 -0
- package/skills/contrast/SKILL.md +1 -0
- package/src/assetCatalogContract.ts +21 -0
- package/src/bridge-contract-input.ts +84 -0
- package/src/bridge-contract.ts +35 -3
- package/src/browser-launch-policy.ts +1 -0
- package/src/bundle-channel.mjs +4 -2
- package/src/cli-runtime.ts +55 -0
- package/src/crash-fingerprint.ts +26 -0
- package/src/dev-bundle-resolution.ts +50 -23
- package/src/device-catalog.ts +1159 -0
- package/src/host/bridge-host.ts +3 -26
- package/src/host/browser-pointer.ts +71 -0
- package/src/host/dev-server-package-versions.ts +23 -0
- package/src/host/fetch-proxy-handler.ts +17 -4
- package/src/installed-package-versions.ts +142 -38
- package/src/kotlin-host.ts +1 -1
- package/src/metro-plugin.ts +4 -4
- package/src/metro-project-root.ts +68 -0
- package/src/nitro-adapter.lock.json +26 -0
- package/src/nitro-adapter.ts +103 -0
- package/src/nitro-bundle.ts +24 -10
- package/src/nitro-compile.ts +33 -51
- package/src/nitro-source.ts +177 -96
- package/src/nitroAdapterContract.ts +217 -0
- package/src/nitroContract.ts +24 -11
- package/src/process-group.ts +41 -34
- package/src/react-bridge-importers.ts +28 -0
- package/src/reactNativeConfigurationContract.ts +36 -0
- package/src/runtime-assets.ts +7 -2
- package/src/runtime-delivery.ts +2 -0
- package/src/runtimePlatformContract.ts +70 -0
- package/src/screenshots/frame-compose.ts +42 -43
- package/src/screenshots/org-deck.ts +9 -4
- package/src/screenshots/preset-names.ts +29 -0
- package/src/screenshots/registry.ts +5 -5
- package/src/screenshots/schema.ts +1 -1
- package/src/screenshots/screenshotContract.ts +971 -0
- package/src/settings-flags.ts +107 -0
- package/src/settingsContract.ts +609 -0
- package/src/storageSnapshotContract.ts +344 -0
- package/src/stub-pattern-matcher.ts +550 -0
- package/src/swift-compile.ts +908 -40
- package/src/swift-compiler-digest.ts +13 -1
- package/src/swift-distribution.lock.json +49 -22
- package/src/swift-host.ts +167 -52
- package/src/swift-objc-compile.ts +54 -24
- package/src/swift-objc-export.ts +532 -0
- package/src/swift-objc-import.ts +769 -0
- package/src/swift-objc-metadata.ts +285 -10
- package/src/swift-objc-sdk.ts +13970 -158
- package/src/swift-objc-standin.ts +137 -1
- package/src/swift-package.ts +94 -0
- package/src/swift-packages.ts +5 -5
- package/src/swift-submit.ts +6 -0
- package/src/swiftContract.ts +99 -0
- package/src/target-matcher.ts +6 -1
- package/src/vite-plugin.ts +14 -40
- package/src/worker-system-chunks.ts +1 -1
- package/src/workerChunkContract.ts +5 -0
- package/src/worklets-babel.ts +24 -5
- package/src/xcode-project.ts +36 -7
- package/src/xcode-target-members.ts +10 -3
- package/cli/drivers/playwright-sim-host.ts +0 -825
- package/kotlin/fixtures/counter/Counter.d.kt.ts +0 -17
- package/kotlin/fixtures/counter/Counter.kt +0 -51
- package/kotlin/fixtures/counter/CounterApp.native.tsx +0 -61
- package/kotlin/fixtures/counter/CounterApp.tsx +0 -3
- package/kotlin/fixtures/counter/CounterScreen.d.kt.ts +0 -8
- package/kotlin/fixtures/counter/CounterScreen.kt +0 -56
- package/kotlin/fixtures/counter/app/_layout.tsx +0 -4
- package/kotlin/fixtures/counter/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/counter/app/routes.d.ts +0 -18
- package/kotlin/fixtures/counter/package.json +0 -20
- package/kotlin/fixtures/counter/tsconfig.json +0 -19
- package/kotlin/fixtures/counter/vite.config.ts +0 -20
- package/kotlin/fixtures/headless-counter/Headless.kt +0 -234
- package/kotlin/fixtures/import-animation/Animation.d.kt.ts +0 -7
- package/kotlin/fixtures/import-animation/Animation.kt +0 -80
- package/kotlin/fixtures/import-animation/ImportAnimation.native.tsx +0 -13
- package/kotlin/fixtures/import-animation/README.md +0 -24
- package/kotlin/fixtures/import-animation/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-animation/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-animation/app/routes.d.ts +0 -18
- package/kotlin/fixtures/import-animation/package.json +0 -23
- package/kotlin/fixtures/import-animation/tsconfig.json +0 -20
- package/kotlin/fixtures/import-animation/vite.config.ts +0 -20
- package/kotlin/fixtures/import-drawing/Drawing.d.kt.ts +0 -7
- package/kotlin/fixtures/import-drawing/Drawing.kt +0 -101
- package/kotlin/fixtures/import-drawing/ImportDrawing.native.tsx +0 -18
- package/kotlin/fixtures/import-drawing/README.md +0 -34
- package/kotlin/fixtures/import-drawing/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-drawing/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-drawing/package.json +0 -23
- package/kotlin/fixtures/import-drawing/tsconfig.json +0 -20
- package/kotlin/fixtures/import-drawing/vite.config.ts +0 -20
- package/kotlin/fixtures/import-editor/Editor.d.kt.ts +0 -7
- package/kotlin/fixtures/import-editor/Editor.kt +0 -59
- package/kotlin/fixtures/import-editor/ImportEditor.native.tsx +0 -23
- package/kotlin/fixtures/import-editor/README.md +0 -27
- package/kotlin/fixtures/import-editor/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-editor/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-editor/package.json +0 -23
- package/kotlin/fixtures/import-editor/tsconfig.json +0 -20
- package/kotlin/fixtures/import-editor/vite.config.ts +0 -20
- package/kotlin/fixtures/import-effects/Effects.d.kt.ts +0 -7
- package/kotlin/fixtures/import-effects/Effects.kt +0 -87
- package/kotlin/fixtures/import-effects/ImportEffects.native.tsx +0 -13
- package/kotlin/fixtures/import-effects/README.md +0 -19
- package/kotlin/fixtures/import-effects/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-effects/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-effects/package.json +0 -23
- package/kotlin/fixtures/import-effects/tsconfig.json +0 -20
- package/kotlin/fixtures/import-effects/vite.config.ts +0 -20
- package/kotlin/fixtures/import-layout/ImportLayout.native.tsx +0 -13
- package/kotlin/fixtures/import-layout/Layout.d.kt.ts +0 -7
- package/kotlin/fixtures/import-layout/Layout.kt +0 -72
- package/kotlin/fixtures/import-layout/README.md +0 -24
- package/kotlin/fixtures/import-layout/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-layout/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-layout/package.json +0 -23
- package/kotlin/fixtures/import-layout/tsconfig.json +0 -20
- package/kotlin/fixtures/import-layout/vite.config.ts +0 -20
- package/kotlin/fixtures/import-navigation/ImportNavigation.native.tsx +0 -18
- package/kotlin/fixtures/import-navigation/Navigation.d.kt.ts +0 -7
- package/kotlin/fixtures/import-navigation/Navigation.kt +0 -104
- package/kotlin/fixtures/import-navigation/README.md +0 -31
- package/kotlin/fixtures/import-navigation/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-navigation/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-navigation/package.json +0 -23
- package/kotlin/fixtures/import-navigation/tsconfig.json +0 -20
- package/kotlin/fixtures/import-navigation/vite.config.ts +0 -20
- package/kotlin/fixtures/import-overlays/ImportOverlays.native.tsx +0 -29
- package/kotlin/fixtures/import-overlays/Overlays.d.kt.ts +0 -7
- package/kotlin/fixtures/import-overlays/Overlays.kt +0 -93
- package/kotlin/fixtures/import-overlays/README.md +0 -34
- package/kotlin/fixtures/import-overlays/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-overlays/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-overlays/package.json +0 -23
- package/kotlin/fixtures/import-overlays/tsconfig.json +0 -20
- package/kotlin/fixtures/import-overlays/vite.config.ts +0 -20
- package/kotlin/fixtures/import-resources/ImportResources.native.tsx +0 -18
- package/kotlin/fixtures/import-resources/README.md +0 -26
- package/kotlin/fixtures/import-resources/Resources.d.kt.ts +0 -6
- package/kotlin/fixtures/import-resources/Resources.kt +0 -72
- package/kotlin/fixtures/import-resources/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-resources/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-resources/package.json +0 -23
- package/kotlin/fixtures/import-resources/tsconfig.json +0 -20
- package/kotlin/fixtures/import-resources/vite.config.ts +0 -20
- package/kotlin/fixtures/import-scroll/ImportNested.native.tsx +0 -10
- package/kotlin/fixtures/import-scroll/ImportScroll.native.tsx +0 -13
- package/kotlin/fixtures/import-scroll/Nested.d.kt.ts +0 -4
- package/kotlin/fixtures/import-scroll/Nested.kt +0 -64
- package/kotlin/fixtures/import-scroll/README.md +0 -38
- package/kotlin/fixtures/import-scroll/Scroll.d.kt.ts +0 -7
- package/kotlin/fixtures/import-scroll/Scroll.kt +0 -91
- package/kotlin/fixtures/import-scroll/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-scroll/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-scroll/app/nested+spa.tsx +0 -1
- package/kotlin/fixtures/import-scroll/package.json +0 -23
- package/kotlin/fixtures/import-scroll/tsconfig.json +0 -20
- package/kotlin/fixtures/import-scroll/vite.config.ts +0 -20
- package/kotlin/fixtures/import-selection/ImportSelection.native.tsx +0 -44
- package/kotlin/fixtures/import-selection/README.md +0 -32
- package/kotlin/fixtures/import-selection/Selection.d.kt.ts +0 -8
- package/kotlin/fixtures/import-selection/Selection.kt +0 -78
- package/kotlin/fixtures/import-selection/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-selection/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-selection/package.json +0 -23
- package/kotlin/fixtures/import-selection/tsconfig.json +0 -20
- package/kotlin/fixtures/import-selection/vite.config.ts +0 -20
- package/kotlin/fixtures/import-surfaces/ImportSurfaces.native.tsx +0 -13
- package/kotlin/fixtures/import-surfaces/README.md +0 -22
- package/kotlin/fixtures/import-surfaces/Surfaces.d.kt.ts +0 -7
- package/kotlin/fixtures/import-surfaces/Surfaces.kt +0 -64
- package/kotlin/fixtures/import-surfaces/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-surfaces/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-surfaces/package.json +0 -23
- package/kotlin/fixtures/import-surfaces/tsconfig.json +0 -20
- package/kotlin/fixtures/import-surfaces/vite.config.ts +0 -20
- package/kotlin/fixtures/import-values/ImportValues.native.tsx +0 -61
- package/kotlin/fixtures/import-values/README.md +0 -28
- package/kotlin/fixtures/import-values/Values.d.kt.ts +0 -15
- package/kotlin/fixtures/import-values/Values.kt +0 -84
- package/kotlin/fixtures/import-values/app/_layout.tsx +0 -4
- package/kotlin/fixtures/import-values/app/index+spa.tsx +0 -1
- package/kotlin/fixtures/import-values/package.json +0 -23
- package/kotlin/fixtures/import-values/tsconfig.json +0 -20
- package/kotlin/fixtures/import-values/vite.config.ts +0 -20
- package/kotlin/fixtures/kotlin-app/app/build.gradle.kts +0 -30
- package/kotlin/fixtures/kotlin-app/app/src/main/AndroidManifest.xml +0 -13
- package/kotlin/fixtures/kotlin-app/app/src/main/java/dev/contrast/kotlinapp/MainActivity.kt +0 -60
- package/kotlin/fixtures/kotlin-app/app/src/main/java/dev/contrast/kotlinapp/ProbeApplication.kt +0 -18
- package/kotlin/fixtures/kotlin-app/build.gradle.kts +0 -5
- package/kotlin/fixtures/kotlin-app/gradle.properties +0 -4
- package/kotlin/fixtures/kotlin-app/settings.gradle.kts +0 -4
- package/kotlin/fixtures/kotlin-app-variants/Anim.kt +0 -62
- package/kotlin/fixtures/kotlin-app-variants/BarBehavior.kt +0 -38
- package/kotlin/fixtures/kotlin-app-variants/BarVisibility.kt +0 -36
- package/kotlin/fixtures/kotlin-app-variants/Bitmaps.kt +0 -50
- package/kotlin/fixtures/kotlin-app-variants/Browse.kt +0 -73
- package/kotlin/fixtures/kotlin-app-variants/Colors.kt +0 -41
- package/kotlin/fixtures/kotlin-app-variants/Controls.kt +0 -62
- package/kotlin/fixtures/kotlin-app-variants/Drag.kt +0 -56
- package/kotlin/fixtures/kotlin-app-variants/Draw.kt +0 -74
- package/kotlin/fixtures/kotlin-app-variants/Drawables.kt +0 -53
- package/kotlin/fixtures/kotlin-app-variants/Focus.kt +0 -62
- package/kotlin/fixtures/kotlin-app-variants/Form.kt +0 -47
- package/kotlin/fixtures/kotlin-app-variants/Icons.kt +0 -59
- package/kotlin/fixtures/kotlin-app-variants/Ime.kt +0 -53
- package/kotlin/fixtures/kotlin-app-variants/Lists.kt +0 -57
- package/kotlin/fixtures/kotlin-app-variants/Nav.kt +0 -47
- package/kotlin/fixtures/kotlin-app-variants/Note.kt +0 -40
- package/kotlin/fixtures/kotlin-app-variants/Overlays.kt +0 -56
- package/kotlin/fixtures/kotlin-app-variants/Pan.kt +0 -38
- package/kotlin/fixtures/kotlin-app-variants/Pickers.kt +0 -44
- package/kotlin/fixtures/kotlin-app-variants/Press.kt +0 -43
- package/kotlin/fixtures/kotlin-app-variants/Scroll.kt +0 -63
- package/kotlin/fixtures/kotlin-app-variants/Sheet.kt +0 -43
- package/kotlin/fixtures/kotlin-app-variants/Strings.kt +0 -49
- package/kotlin/fixtures/kotlin-app-variants/Tabs.kt +0 -47
- package/kotlin/fixtures/kotlin-app-variants/TextWidth.kt +0 -45
- package/kotlin/fixtures/kotlin-app-variants/TransientInsets.kt +0 -50
- package/kotlin/fixtures/kotlin-app-variants/Transition.kt +0 -69
- package/kotlin/fixtures/kotlin-app-variants/ViewConfig.kt +0 -22
- package/kotlin/fixtures/kotlin-app-variants/Widgets.kt +0 -67
- package/kotlin/fixtures/runtime-state/RuntimeState.kt +0 -200
- package/kotlin/fork/README.md +0 -97
- package/kotlin/fork/build.sh +0 -176
- package/kotlin/fork/compose-worker-globals.patch +0 -1247
- package/kotlin/fork/fork.json +0 -46
- package/kotlin/fork/publish.ts +0 -99
- package/kotlin/fork/skia-android-shaping.patch +0 -189
- package/kotlin/fork/skiko-local.init.gradle +0 -10
- package/kotlin/fork/skiko-worker-clock.patch +0 -122
|
@@ -1,825 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
acquireChromiumAdmission,
|
|
3
|
-
readChromiumQueue,
|
|
4
|
-
} from '../../../../scripts/lib/chromium-admission'
|
|
5
|
-
// the detached browser host, as CJS source.
|
|
6
|
-
//
|
|
7
|
-
// it resolves playwright from the absolute path the driver hands it, launches
|
|
8
|
-
// Chrome for Testing, opens the target url, then keeps itself (and therefore
|
|
9
|
-
// the browser) alive until the page asks it to close (`peach close`, or the
|
|
10
|
-
// bridge reclaiming an idle sim for a queued chromium launch),
|
|
11
|
-
// the browser disconnects, or its owning session exits. all inputs arrive
|
|
12
|
-
// through the environment.
|
|
13
|
-
//
|
|
14
|
-
// it stays a source string rather than a module of its own: playwright is
|
|
15
|
-
// resolved from the user's project at runtime and is not a dependency here, so
|
|
16
|
-
// there are no types for any of this, and a typed rewrite would be `unknown`
|
|
17
|
-
// casts end to end. cli/internal-child.ts runs it through node:vm with a real
|
|
18
|
-
// require, in a child process the CLI spawns as itself.
|
|
19
|
-
import {
|
|
20
|
-
chromeDisabledFeaturesSwitch,
|
|
21
|
-
LINUX_CHROME_SCOPE_ENV,
|
|
22
|
-
softwareChromeLaunchOptions,
|
|
23
|
-
} from '../../../../scripts/lib/reap-browser'
|
|
24
|
-
import { createBrowserPageTargetReader } from '../../../peach-engine/src/browser-page-target'
|
|
25
|
-
import { captureBrowserAccessibility } from '../../../peach-engine/src/capture/browser-accessibility-host'
|
|
26
|
-
import {
|
|
27
|
-
readBrowserCompositeCaptureRequest,
|
|
28
|
-
resolveBrowserCompositeCdpClip,
|
|
29
|
-
} from '../../../peach-engine/src/capture/browser-composite'
|
|
30
|
-
import { WEBVIEW_HOST_BOOTSTRAP_SOURCE } from '../../../peach-engine/src/components/native-ui/webview-frame-bridge'
|
|
31
|
-
import { createBrowserPointerDispatcher } from '../../../peach-engine/src/render-worker/browser-pointer'
|
|
32
|
-
import { automateWebAuthenticationSessions } from '../../../peach-engine/src/web-authentication-automation-host'
|
|
33
|
-
import {
|
|
34
|
-
automateWebViewFrames,
|
|
35
|
-
WEBVIEW_HOST_BINDING,
|
|
36
|
-
WEBVIEW_INSTANCE_ATTRIBUTE,
|
|
37
|
-
WEBVIEW_SCHEME_NAVIGATION_EVENT,
|
|
38
|
-
WEBVIEW_NETWORK_NAVIGATION_EVENT,
|
|
39
|
-
WEBVIEW_LOAD_EVENT,
|
|
40
|
-
webViewAutomationHelpersSource,
|
|
41
|
-
} from '../../../peach-engine/src/webview-automation-host'
|
|
42
|
-
import { DISPOSABLE_BROWSER_CACHE_DIR_NAMES } from '../../src/browser-cache'
|
|
43
|
-
import { listProcessChildren } from '../../src/process-group'
|
|
44
|
-
|
|
45
|
-
// a person's tap on the device canvas is a trusted input, which gives the page
|
|
46
|
-
// the user activation browsers require before media plays with sound, and an
|
|
47
|
-
// AVAudioPlayer started from that tap sounds. the driver's taps are dispatched
|
|
48
|
-
// inside the page and never activate it, so without this an app's play() is
|
|
49
|
-
// refused where the same tap by a person plays (RAN:
|
|
50
|
-
// scripts/debug/audiotap-probe.ts). ios needs no gesture for audio at all.
|
|
51
|
-
// every proof that drives media in chromium launches with this one policy.
|
|
52
|
-
export const DRIVER_AUTOPLAY_POLICY = '--autoplay-policy=no-user-gesture-required'
|
|
53
|
-
|
|
54
|
-
export const PLAYWRIGHT_SIM_HOST = `
|
|
55
|
-
const modulePath = process.env.SOOTSIM_PW_MODULE;
|
|
56
|
-
const executablePath = process.env.SOOTSIM_PW_EXECUTABLE;
|
|
57
|
-
// the shared libraries this host lacks, from peach's bundle. only the browser
|
|
58
|
-
// gets them on its library path: the bundled builds are older than the system's.
|
|
59
|
-
const libraryPath = process.env.SOOTSIM_PW_LIBRARY_PATH;
|
|
60
|
-
if (!executablePath) throw new Error('SOOTSIM_PW_EXECUTABLE is not set');
|
|
61
|
-
const url = process.env.SOOTSIM_PW_URL;
|
|
62
|
-
// the WS bridge port the sim page must register with, injected below as
|
|
63
|
-
// window.__sootsimBridgePort so the page skips its shell-port heuristic (which
|
|
64
|
-
// assumes the default offset and breaks when the dev bridge drifted).
|
|
65
|
-
const bridgePort = process.env.SOOTSIM_PW_BRIDGE_PORT || '';
|
|
66
|
-
const fetchProxySpki = process.env.PEACH_PW_FETCH_PROXY_SPKI || '';
|
|
67
|
-
const headless = process.env.SOOTSIM_PW_HEADLESS === '1';
|
|
68
|
-
const callerUserDataDir = process.env.SOOTSIM_PW_USERDATADIR || '';
|
|
69
|
-
// pid of the session that launched us (its topmost non-init ancestor). when
|
|
70
|
-
// that process exits we tear Chrome down — see startOwnerWatch below. it is a
|
|
71
|
-
// required lifetime bound: an invalid value aborts before Chrome launches.
|
|
72
|
-
// SOOTSIM_PW_OWNER_POLL_MS is a test-only override of the 5s poll interval;
|
|
73
|
-
// production never sets it.
|
|
74
|
-
const ownerPid = Number(process.env.SOOTSIM_PW_OWNER_PID || 0);
|
|
75
|
-
// the open command waiting for this host to connect; it may queue for the
|
|
76
|
-
// browser slot and then be killed by its own caller's budget.
|
|
77
|
-
const launcherPid = Number(process.env.SOOTSIM_PW_LAUNCHER_PID || 0);
|
|
78
|
-
const ownerPollMs = Number(process.env.SOOTSIM_PW_OWNER_POLL_MS || 5000);
|
|
79
|
-
// optional CDP port — exposes Chrome's remote-debugging endpoint so a
|
|
80
|
-
// bridge-driven sim can also be cpu-profiled (peach perf cpu --cdp-port).
|
|
81
|
-
// playwright keeps its own pipe connection; the port is an additional endpoint.
|
|
82
|
-
const cdpPort = process.env.SOOTSIM_PW_CDP_PORT || '';
|
|
83
|
-
// the native desktop host's standalone command (a JSON array). it attaches to
|
|
84
|
-
// this headless browser over CDP, composites the sim, and presents page frames
|
|
85
|
-
// only when a capture asks (specs/peach-desktop-host/headless.md).
|
|
86
|
-
const hostCommand = process.env.PEACH_PW_HOST_COMMAND ? JSON.parse(process.env.PEACH_PW_HOST_COMMAND) : null;
|
|
87
|
-
if (hostCommand && (!headless || !cdpPort)) {
|
|
88
|
-
throw new Error('[playwright-driver] the desktop host attaches only to a headless browser with a CDP port');
|
|
89
|
-
}
|
|
90
|
-
// extra chromium launch args, space-separated. a linux GPU runner injects
|
|
91
|
-
// '--use-angle=vulkan --enable-features=Vulkan' here so chrome binds the
|
|
92
|
-
// real GPU instead of silently falling back to swiftshader software
|
|
93
|
-
// rendering (which pegs cores and produces ~1fps canvas capture).
|
|
94
|
-
const extraChromeArgs = (process.env.SOOTSIM_PW_CHROME_ARGS || '')
|
|
95
|
-
.split(' ')
|
|
96
|
-
.filter(Boolean);
|
|
97
|
-
// A Mac always has a GPU, so a local macOS drive that lands on Playwright's
|
|
98
|
-
// software renderer is a misconfiguration that pegs every core: it fails
|
|
99
|
-
// closed unless the hardware-backed test browser is proven after launch. CI
|
|
100
|
-
// and Linux or Windows hosts, which are often GPU-less servers, use the
|
|
101
|
-
// bundled browser and render in software when no GPU is present.
|
|
102
|
-
const hardwareGpuRequired =
|
|
103
|
-
process.platform === 'darwin' && process.env.CI !== '1' && process.env.CI !== 'true';
|
|
104
|
-
const softwareRendererPattern = /swiftshader|software(?: rasterizer)?|llvmpipe|lavapipe|softpipe/i;
|
|
105
|
-
// page viewport as '<w>x<h>'. the caller sizes it from the device profile;
|
|
106
|
-
// the fallback is phone-shaped because the page hosts a phone sim — the
|
|
107
|
-
// playwright default (1280x720 desktop) letterboxes the device frame.
|
|
108
|
-
const viewportSpec = /^(\\d+)x(\\d+)$/.exec(process.env.SOOTSIM_PW_VIEWPORT || '');
|
|
109
|
-
const viewport = viewportSpec
|
|
110
|
-
? { width: Number(viewportSpec[1]), height: Number(viewportSpec[2]) }
|
|
111
|
-
: { width: 600, height: 900 };
|
|
112
|
-
// host display pixels per css pixel; default to a retina Mac browser's 2.
|
|
113
|
-
// medium renders at 2 and high at device native, independently of browser DPR.
|
|
114
|
-
// low follows measured on-screen density, capped at device native.
|
|
115
|
-
// SOOTSIM_PW_DSF changes browser DPR, not the selected render resolution.
|
|
116
|
-
const deviceScaleFactor = Number(process.env.SOOTSIM_PW_DSF || 2);
|
|
117
|
-
const closeTimeoutMs = Number(process.env.SOOTSIM_PW_CLOSE_TIMEOUT_MS || 2500);
|
|
118
|
-
const connectAckFile = process.env.SOOTSIM_PW_CONNECTED_ACK_FILE || '';
|
|
119
|
-
const admittedFile = process.env.SOOTSIM_PW_ADMITTED_FILE || '';
|
|
120
|
-
const completionFile = process.env.PEACH_PW_COMPLETION_FILE || '';
|
|
121
|
-
const disposableBrowserCacheDirs = new Set(${JSON.stringify(
|
|
122
|
-
DISPOSABLE_BROWSER_CACHE_DIR_NAMES,
|
|
123
|
-
)});
|
|
124
|
-
const { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } = require('fs');
|
|
125
|
-
const { execSync, spawn, spawnSync } = require('child_process');
|
|
126
|
-
const listProcessChildren = ${listProcessChildren.toString()};
|
|
127
|
-
const { tmpdir } = require('os');
|
|
128
|
-
const { join } = require('path');
|
|
129
|
-
const createBrowserPointerDispatcher = ${createBrowserPointerDispatcher.toString()};
|
|
130
|
-
const captureBrowserAccessibility = ${captureBrowserAccessibility.toString()};
|
|
131
|
-
const automateWebViewFrames = ${automateWebViewFrames.toString()};
|
|
132
|
-
const automateWebAuthenticationSessions = ${automateWebAuthenticationSessions.toString()};
|
|
133
|
-
const webViewAutomationHelpers = ${webViewAutomationHelpersSource()};
|
|
134
|
-
const readBrowserCompositeCaptureRequest = ${readBrowserCompositeCaptureRequest.toString()};
|
|
135
|
-
const resolveBrowserCompositeCdpClip = ${resolveBrowserCompositeCdpClip.toString()};
|
|
136
|
-
const acquireChromiumAdmission = ${acquireChromiumAdmission.toString()};
|
|
137
|
-
const readChromiumQueue = ${readChromiumQueue.toString()};
|
|
138
|
-
// chrome tree reaper — playwright's browser.close() and even SIGKILLing the
|
|
139
|
-
// root chrome process do NOT reliably take down the helper tree (gpu, renderer,
|
|
140
|
-
// network/storage utilities). they reparent to launchd/init and survive, each
|
|
141
|
-
// one busy enough to peg CPU at 100%+. close() only reaches the root.
|
|
142
|
-
//
|
|
143
|
-
// the prior approach was \`pgrep -f ms-playwright/[c]hromium\`. it never matched
|
|
144
|
-
// system Chrome, so the reaper was a no-op in the common case and every
|
|
145
|
-
// aborted/closed \`peach open\` leaked a renderer + gpu helper burning 100%+
|
|
146
|
-
// CPU until manually killed (multiple incidents 2026-06).
|
|
147
|
-
//
|
|
148
|
-
// a named profile can already belong to another host. its path finds browser
|
|
149
|
-
// candidates; the unique launch environment proves which ones we may reap,
|
|
150
|
-
// including helpers reparented after the browser root exits.
|
|
151
|
-
const reapEnabled = process.platform !== 'win32';
|
|
152
|
-
let profileDir = callerUserDataDir;
|
|
153
|
-
let ephemeralProfile = false;
|
|
154
|
-
if (!profileDir) {
|
|
155
|
-
profileDir = mkdtempSync(join(tmpdir(), 'peach-playwright-host-'));
|
|
156
|
-
ephemeralProfile = true;
|
|
157
|
-
}
|
|
158
|
-
// browser HTTP caches are acceleration data, not profile identity. give each
|
|
159
|
-
// host a bounded temporary cache so persistent named profiles retain cookies
|
|
160
|
-
// and storage without multiplying a private cache for every profile.
|
|
161
|
-
const diskCacheDir = mkdtempSync(join(tmpdir(), 'peach-playwright-cache-'));
|
|
162
|
-
let profileLaunched = false;
|
|
163
|
-
// [pid, command line] for every process on the profile. linux reads /proc,
|
|
164
|
-
// since minimal and rootless images ship no ps.
|
|
165
|
-
const profileProcesses = () => {
|
|
166
|
-
if (!reapEnabled) return [];
|
|
167
|
-
try {
|
|
168
|
-
// match the whole --user-data-dir argument: a pattern search also found
|
|
169
|
-
// browsers whose profile path merely starts with this one.
|
|
170
|
-
const flag = ' --user-data-dir=' + profileDir + ' ';
|
|
171
|
-
const rows = process.platform === 'linux'
|
|
172
|
-
? readdirSync('/proc').filter((name) => /^\\d+$/.test(name)).map((name) => {
|
|
173
|
-
let command = '';
|
|
174
|
-
// a process can exit between the listing and this read
|
|
175
|
-
try { command = readFileSync('/proc/' + name + '/cmdline', 'utf8').split('\\0').join(' ').trim(); } catch {}
|
|
176
|
-
return [Number(name), command];
|
|
177
|
-
})
|
|
178
|
-
: execSync('ps -axo pid=,command=', {
|
|
179
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
180
|
-
maxBuffer: Infinity,
|
|
181
|
-
})
|
|
182
|
-
.toString().split('\\n').map((line) => line.trim().match(/^(\\d+)\\s+(.*)$/))
|
|
183
|
-
.filter(Boolean).map((row) => [Number(row[1]), row[2]]);
|
|
184
|
-
return rows.filter(([pid, command]) => pid && pid !== process.pid && (' ' + command + ' ').includes(flag));
|
|
185
|
-
} catch (error) {
|
|
186
|
-
throw new Error('[playwright-driver] cannot establish browser profile ownership: ' + String(error));
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
const chromePidsByProfile = () => profileProcesses().map(([pid]) => pid);
|
|
190
|
-
const chromeCommandsByProfile = () => profileProcesses().map(([, command]) => command);
|
|
191
|
-
const assertNoUnsafeSwiftShaderFlag = () => {
|
|
192
|
-
if (!hardwareGpuRequired) return;
|
|
193
|
-
const unsafe = chromeCommandsByProfile().find((command) =>
|
|
194
|
-
command.includes('--enable-unsafe-swiftshader') ||
|
|
195
|
-
/--use-angle=(?:swiftshader|swiftshader-webgl)(?:\\s|$)/i.test(command)
|
|
196
|
-
);
|
|
197
|
-
if (unsafe) {
|
|
198
|
-
throw new Error(
|
|
199
|
-
'[playwright-driver] refused software-rendering Chrome: launched process contains a SwiftShader flag'
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
const killTree = (pid) => {
|
|
204
|
-
let kids = [];
|
|
205
|
-
try {
|
|
206
|
-
kids = listProcessChildren([pid]);
|
|
207
|
-
} catch {}
|
|
208
|
-
for (const k of kids) killTree(k);
|
|
209
|
-
try { process.kill(pid, 'SIGKILL'); } catch {}
|
|
210
|
-
};
|
|
211
|
-
const reapChrome = () => {
|
|
212
|
-
try {
|
|
213
|
-
for (const pid of chromePidsByProfile()) {
|
|
214
|
-
let owned = false;
|
|
215
|
-
if (process.platform === 'linux') {
|
|
216
|
-
try {
|
|
217
|
-
owned = readFileSync('/proc/' + pid + '/environ', 'utf8').split('\\0')
|
|
218
|
-
.includes('PEACH_PW_BROWSER_OWNER=' + diskCacheDir);
|
|
219
|
-
} catch {}
|
|
220
|
-
} else {
|
|
221
|
-
const plain = spawnSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8' });
|
|
222
|
-
const withEnv = spawnSync('ps', ['eww', '-o', 'command=', '-p', String(pid)], { encoding: 'utf8' });
|
|
223
|
-
const argv = (plain.stdout || '').trimEnd();
|
|
224
|
-
const full = (withEnv.stdout || '').trimEnd();
|
|
225
|
-
owned = plain.status === 0 && withEnv.status === 0 && full.startsWith(argv) &&
|
|
226
|
-
(full.slice(argv.length) + ' ').includes(' PEACH_PW_BROWSER_OWNER=' + diskCacheDir + ' ');
|
|
227
|
-
}
|
|
228
|
-
if (owned) killTree(pid);
|
|
229
|
-
}
|
|
230
|
-
} catch (error) {
|
|
231
|
-
process.stderr.write('[playwright-driver] browser teardown refused: ' + String(error) + '\\n');
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
const removeDisposableCaches = (root) => {
|
|
235
|
-
let children = [];
|
|
236
|
-
try { children = readdirSync(root, { withFileTypes: true }); } catch { return; }
|
|
237
|
-
for (const child of children) {
|
|
238
|
-
if (!child.isDirectory() || child.isSymbolicLink()) continue;
|
|
239
|
-
const childPath = join(root, child.name);
|
|
240
|
-
if (disposableBrowserCacheDirs.has(child.name)) {
|
|
241
|
-
try { rmSync(childPath, { recursive: true, force: true }); } catch {}
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
removeDisposableCaches(childPath);
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
// chrome treats a SingletonLock whose recorded hostname differs from the
|
|
248
|
-
// current one as held by another machine, and macOS renames the host with the
|
|
249
|
-
// network, so a lock left by a killed host blocks every relaunch of a named
|
|
250
|
-
// profile. no process runs this profile here, so its lock files are stale.
|
|
251
|
-
const clearStaleSingletonLock = () => {
|
|
252
|
-
if (chromePidsByProfile().length > 0) return;
|
|
253
|
-
for (const name of ['SingletonLock', 'SingletonSocket', 'SingletonCookie']) {
|
|
254
|
-
try { rmSync(join(profileDir, name), { force: true }); } catch {}
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
const cleanupProfile = () => {
|
|
258
|
-
try { rmSync(diskCacheDir, { recursive: true, force: true }); } catch {}
|
|
259
|
-
if (ephemeralProfile) {
|
|
260
|
-
try { rmSync(profileDir, { recursive: true, force: true }); } catch {}
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
if (profileLaunched) removeDisposableCaches(profileDir);
|
|
264
|
-
};
|
|
265
|
-
(async () => {
|
|
266
|
-
const pw = require(modulePath);
|
|
267
|
-
let context;
|
|
268
|
-
let keepAlive = null;
|
|
269
|
-
let connectTimer = null;
|
|
270
|
-
let connectAckPoll = null;
|
|
271
|
-
let ownerWatch = null;
|
|
272
|
-
let connected = false;
|
|
273
|
-
let shuttingDown = false;
|
|
274
|
-
let desktopHost = null;
|
|
275
|
-
// the host runs the tenant's workers in its realms, where the browser cannot see them; it
|
|
276
|
-
// evaluates there over node ipc and answers each realm's value as it settles, null where one
|
|
277
|
-
// throws. only that page's target is inspected; the first value that is not null answers,
|
|
278
|
-
// or null once no realm in that page is pending
|
|
279
|
-
const hostEvaluations = new Map();
|
|
280
|
-
let hostEvaluation = 0;
|
|
281
|
-
const readPageTarget = (${createBrowserPageTargetReader.toString()})();
|
|
282
|
-
const hostRealms = (page) => ({
|
|
283
|
-
evaluate: async (fn, args) => {
|
|
284
|
-
const targetId = await readPageTarget(page);
|
|
285
|
-
return new Promise((resolve) => {
|
|
286
|
-
const id = ++hostEvaluation;
|
|
287
|
-
hostEvaluations.set(id, resolve);
|
|
288
|
-
desktopHost.send({ t: 'evaluate', id, targetId, expression: '(' + fn + ')(' + JSON.stringify(args) + ')' });
|
|
289
|
-
});
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
const stopDesktopHost = async () => {
|
|
293
|
-
if (!desktopHost || desktopHost.exitCode !== null || desktopHost.signalCode !== null) return;
|
|
294
|
-
const exited = new Promise((resolve) => desktopHost.once('exit', resolve));
|
|
295
|
-
desktopHost.kill('SIGTERM');
|
|
296
|
-
await exited;
|
|
297
|
-
};
|
|
298
|
-
// the host's ready line follows its CDP auto-attach registration, so every
|
|
299
|
-
// page this browser opens afterwards is composited by it
|
|
300
|
-
const startDesktopHost = async () => {
|
|
301
|
-
// the persistent context's token changes on relaunch; its stores belong to the browser profile.
|
|
302
|
-
const profileSession = await context.newCDPSession(context.pages()[0]);
|
|
303
|
-
let profileContext;
|
|
304
|
-
try {
|
|
305
|
-
const { targetInfo } = await profileSession.send('Target.getTargetInfo');
|
|
306
|
-
profileContext = targetInfo.browserContextId;
|
|
307
|
-
if (!profileContext) throw new Error('[playwright-driver] persistent browser context has no identity');
|
|
308
|
-
} finally {
|
|
309
|
-
await profileSession.detach();
|
|
310
|
-
}
|
|
311
|
-
desktopHost = spawn(
|
|
312
|
-
hostCommand[0],
|
|
313
|
-
[...hostCommand.slice(1), '--port', '0', '--browser', 'http://127.0.0.1:' + cdpPort, '--headless',
|
|
314
|
-
'--profile', join(profileDir, 'peach-node'), '--profile-context', profileContext],
|
|
315
|
-
{ stdio: ['ignore', 'ignore', 'pipe', 'ipc'] },
|
|
316
|
-
);
|
|
317
|
-
desktopHost.on('message', (m) => {
|
|
318
|
-
if (m.value === null && m.pending > 0) return;
|
|
319
|
-
hostEvaluations.get(m.id)?.(m.value);
|
|
320
|
-
hostEvaluations.delete(m.id);
|
|
321
|
-
});
|
|
322
|
-
let log = '';
|
|
323
|
-
const hostUrl = await new Promise((resolve, reject) => {
|
|
324
|
-
const timer = setTimeout(() => reject(new Error('[playwright-driver] desktop host did not start within 30s: ' + log)), 30000);
|
|
325
|
-
desktopHost.once('error', (error) => { clearTimeout(timer); reject(error); });
|
|
326
|
-
desktopHost.once('exit', (code) => { clearTimeout(timer); reject(new Error('[playwright-driver] desktop host exited during startup (' + code + '): ' + log)); });
|
|
327
|
-
desktopHost.stderr.on('data', (data) => {
|
|
328
|
-
process.stderr.write(data);
|
|
329
|
-
if (log.length < 65536) log += String(data);
|
|
330
|
-
const ready = /peach host on (http:\\/\\/127\\.0\\.0\\.1:\\d+\\/)/.exec(log);
|
|
331
|
-
if (ready) { clearTimeout(timer); resolve(ready[1]); }
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
desktopHost.removeAllListeners('exit');
|
|
335
|
-
desktopHost.once('exit', (code) => {
|
|
336
|
-
process.stderr.write('[playwright-driver] desktop host exited (' + code + ')\\n');
|
|
337
|
-
void shutdown(3);
|
|
338
|
-
});
|
|
339
|
-
process.stderr.write('[playwright-driver] desktop host attached on ' + hostUrl + '\\n');
|
|
340
|
-
};
|
|
341
|
-
const clearConnectTimer = () => {
|
|
342
|
-
if (connectTimer) {
|
|
343
|
-
clearTimeout(connectTimer);
|
|
344
|
-
connectTimer = null;
|
|
345
|
-
}
|
|
346
|
-
if (connectAckPoll) {
|
|
347
|
-
clearInterval(connectAckPoll);
|
|
348
|
-
connectAckPoll = null;
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
const markConnected = () => {
|
|
352
|
-
connected = true;
|
|
353
|
-
clearConnectTimer();
|
|
354
|
-
};
|
|
355
|
-
const ackFileExists = () => {
|
|
356
|
-
if (!connectAckFile) return false;
|
|
357
|
-
try { return existsSync(connectAckFile); } catch { return false; }
|
|
358
|
-
};
|
|
359
|
-
const bounded = async (label, work, timeoutMs) => {
|
|
360
|
-
let timer = null;
|
|
361
|
-
try {
|
|
362
|
-
const timeout = new Promise((resolve) => {
|
|
363
|
-
timer = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
|
|
364
|
-
});
|
|
365
|
-
if (typeof timer.unref === 'function') timer.unref();
|
|
366
|
-
const result = await Promise.race([
|
|
367
|
-
Promise.resolve().then(work).then(
|
|
368
|
-
() => ({ timedOut: false }),
|
|
369
|
-
(error) => ({ timedOut: false, error }),
|
|
370
|
-
),
|
|
371
|
-
timeout,
|
|
372
|
-
]);
|
|
373
|
-
if (result && result.timedOut) {
|
|
374
|
-
process.stderr.write('[shutdown-timeout] ' + label + ' exceeded ' + timeoutMs + 'ms\\n');
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
if (result && result.error) {
|
|
378
|
-
process.stderr.write('[shutdown-error] ' + label + ': ' + String((result.error && result.error.message) || result.error) + '\\n');
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
return true;
|
|
382
|
-
} finally {
|
|
383
|
-
if (timer) clearTimeout(timer);
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
const browserProcess = () => {
|
|
387
|
-
if (context && typeof context.browser === 'function') {
|
|
388
|
-
try {
|
|
389
|
-
const b = context.browser();
|
|
390
|
-
if (b && typeof b.process === 'function') return b.process();
|
|
391
|
-
} catch {}
|
|
392
|
-
}
|
|
393
|
-
return null;
|
|
394
|
-
};
|
|
395
|
-
const processIsAlive = (proc) => {
|
|
396
|
-
if (!proc || !proc.pid) return false;
|
|
397
|
-
try {
|
|
398
|
-
process.kill(proc.pid, 0);
|
|
399
|
-
return true;
|
|
400
|
-
} catch (err) {
|
|
401
|
-
return err && err.code === 'EPERM';
|
|
402
|
-
}
|
|
403
|
-
};
|
|
404
|
-
const terminateBrowserProcess = async () => {
|
|
405
|
-
const proc = browserProcess();
|
|
406
|
-
if (!proc || !proc.pid || !processIsAlive(proc)) return;
|
|
407
|
-
try { proc.kill('SIGTERM'); } catch {}
|
|
408
|
-
await bounded('browser process exit after SIGTERM', async () => {
|
|
409
|
-
while (processIsAlive(proc)) {
|
|
410
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
411
|
-
}
|
|
412
|
-
}, 600);
|
|
413
|
-
if (!processIsAlive(proc)) return;
|
|
414
|
-
try { proc.kill('SIGKILL'); } catch {}
|
|
415
|
-
};
|
|
416
|
-
const shutdown = async (code = 0) => {
|
|
417
|
-
if (shuttingDown) return;
|
|
418
|
-
shuttingDown = true;
|
|
419
|
-
clearConnectTimer();
|
|
420
|
-
if (keepAlive) clearInterval(keepAlive);
|
|
421
|
-
if (ownerWatch) clearInterval(ownerWatch);
|
|
422
|
-
const timeoutMs = Number.isFinite(closeTimeoutMs) && closeTimeoutMs > 0 ? closeTimeoutMs : 2500;
|
|
423
|
-
if (desktopHost) desktopHost.removeAllListeners('exit');
|
|
424
|
-
await bounded('desktop host stop', stopDesktopHost, timeoutMs);
|
|
425
|
-
if (context) await bounded('context.close', () => context.close(), timeoutMs);
|
|
426
|
-
await terminateBrowserProcess();
|
|
427
|
-
// close() can leave reparented helpers; reap only this launch's browser
|
|
428
|
-
// and helpers, even if another host now uses the same named profile.
|
|
429
|
-
reapChrome();
|
|
430
|
-
cleanupProfile();
|
|
431
|
-
if (connectAckFile) {
|
|
432
|
-
try { unlinkSync(connectAckFile); } catch {}
|
|
433
|
-
}
|
|
434
|
-
if (admittedFile) {
|
|
435
|
-
try { unlinkSync(admittedFile); } catch {}
|
|
436
|
-
}
|
|
437
|
-
process.exit(code);
|
|
438
|
-
};
|
|
439
|
-
process.once('SIGTERM', () => { void shutdown(0); });
|
|
440
|
-
process.once('SIGINT', () => { void shutdown(0); });
|
|
441
|
-
// the launch-failure path at the bottom of this script exits without going
|
|
442
|
-
// through shutdown(); the exit hook (sync sweep) still reaps the tree.
|
|
443
|
-
process.on('exit', (code) => {
|
|
444
|
-
if (desktopHost && desktopHost.exitCode === null) desktopHost.kill('SIGKILL');
|
|
445
|
-
reapChrome();
|
|
446
|
-
cleanupProfile();
|
|
447
|
-
if (completionFile) {
|
|
448
|
-
try {
|
|
449
|
-
writeFileSync(completionFile, JSON.stringify({ code, pid: process.pid, completedAt: Date.now() }) + '\\n');
|
|
450
|
-
} catch (error) {
|
|
451
|
-
process.stderr.write('[completion-error] ' + String(error) + '\\n');
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
// owner-liveness watchdog — the load-bearing fix for leaked Chrome hosts.
|
|
456
|
-
// we are spawned detached + unref'd so \`peach open\` returns immediately,
|
|
457
|
-
// which means nothing in the OS process tree ties Chrome's lifetime to the
|
|
458
|
-
// session that asked for it. once the sim connects, the connect-timeout
|
|
459
|
-
// safety net is cleared, so absent an explicit \`peach close\` (or the
|
|
460
|
-
// daemon's idle reaper closing the sim socket) the host would hold Chrome
|
|
461
|
-
// open forever after its owner exits. poll the owning session's pid and tear
|
|
462
|
-
// down when it's gone. ESRCH (kill throws) = owner exited; EPERM = alive but
|
|
463
|
-
// not signalable by us, which still counts as alive.
|
|
464
|
-
const startOwnerWatch = () => {
|
|
465
|
-
if (!Number.isInteger(ownerPid) || ownerPid < 1) {
|
|
466
|
-
throw new Error('[playwright-driver] missing valid owning process; refusing unsupervised browser host');
|
|
467
|
-
}
|
|
468
|
-
const intervalMs = Number.isFinite(ownerPollMs) && ownerPollMs > 0 ? ownerPollMs : 5000;
|
|
469
|
-
const gone = (pid) => {
|
|
470
|
-
try {
|
|
471
|
-
process.kill(pid, 0);
|
|
472
|
-
return false;
|
|
473
|
-
} catch (err) {
|
|
474
|
-
return !(err && err.code === 'EPERM');
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
ownerWatch = setInterval(() => {
|
|
478
|
-
if (gone(ownerPid)) {
|
|
479
|
-
process.stderr.write('[owner-gone] owning process ' + ownerPid + ' exited; closing playwright host\\n');
|
|
480
|
-
void shutdown(0);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
// until the sim connects, the open command is the only one waiting for
|
|
484
|
-
// it; once that command is gone nobody receives the sim.
|
|
485
|
-
if (!connected && !ackFileExists() && Number.isInteger(launcherPid) && launcherPid > 1 && gone(launcherPid)) {
|
|
486
|
-
process.stderr.write('[launcher-gone] open command ' + launcherPid + ' exited before the sim connected; closing playwright host\\n');
|
|
487
|
-
void shutdown(2);
|
|
488
|
-
}
|
|
489
|
-
}, intervalMs);
|
|
490
|
-
if (typeof ownerWatch.unref === 'function') ownerWatch.unref();
|
|
491
|
-
};
|
|
492
|
-
startOwnerWatch();
|
|
493
|
-
// Local rendering uses Playwright's full Chrome for Testing app. On macOS its
|
|
494
|
-
// com.google.chrome.for.testing identity cannot intercept open/quit events for
|
|
495
|
-
// the user's com.google.Chrome browser. CI and non-Mac hosts use
|
|
496
|
-
// chromium_headless_shell; local Mac rendering fails closed because that
|
|
497
|
-
// shell has no GPU and silently chooses SwiftShader. NOTE: this is NOT enough to make \`peach perf cpu\`
|
|
498
|
-
// work — that profiles the tenant *worker*, and the JS Self-Profiler API
|
|
499
|
-
// cannot be constructed in a dedicated worker (see
|
|
500
|
-
// worker-handlers/cpu-profile.ts). cpu-profile needs a CDP-based profiler
|
|
501
|
-
// instead.
|
|
502
|
-
if (
|
|
503
|
-
hardwareGpuRequired &&
|
|
504
|
-
extraChromeArgs.some((arg) =>
|
|
505
|
-
arg === '--enable-unsafe-swiftshader' ||
|
|
506
|
-
/^--use-angle=(?:swiftshader|swiftshader-webgl)$/i.test(arg)
|
|
507
|
-
)
|
|
508
|
-
) {
|
|
509
|
-
throw new Error('[playwright-driver] refused SwiftShader launch arguments');
|
|
510
|
-
}
|
|
511
|
-
// one browser path per environment: the full, separately identified test app
|
|
512
|
-
// on a local Mac; the bundled chromium everywhere else. never launch a
|
|
513
|
-
// branded user browser.
|
|
514
|
-
const channelsToTry = hardwareGpuRequired ? ['chromium'] : [null];
|
|
515
|
-
const launchWith = async (channel) => {
|
|
516
|
-
const opts = channel
|
|
517
|
-
? { headless, channel, viewport, deviceScaleFactor }
|
|
518
|
-
: { headless, viewport, deviceScaleFactor };
|
|
519
|
-
opts.env = { ...process.env, PEACH_PW_BROWSER_OWNER: diskCacheDir };
|
|
520
|
-
opts.executablePath = executablePath;
|
|
521
|
-
if (libraryPath) {
|
|
522
|
-
opts.env = {
|
|
523
|
-
...opts.env,
|
|
524
|
-
LD_LIBRARY_PATH: [libraryPath, process.env.LD_LIBRARY_PATH].filter(Boolean).join(':'),
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
// keep the browser main process with its children in the caller's cgroup.
|
|
528
|
-
// otherwise chromium moves only main into a systemd app scope over dbus.
|
|
529
|
-
if (process.platform === 'linux') {
|
|
530
|
-
opts.env = { ...process.env, ...opts.env, ...${JSON.stringify(LINUX_CHROME_SCOPE_ENV)} };
|
|
531
|
-
}
|
|
532
|
-
// playwright injects --force-color-profile=srgb by default, which reads
|
|
533
|
-
// visibly washed-out next to a normal chrome window on a wide-gamut mac
|
|
534
|
-
// display. strip it so driver-launched sims color-match the user's own
|
|
535
|
-
// browser (real incident 2026-07-09: "low res and whitewashed" sim).
|
|
536
|
-
opts.ignoreDefaultArgs = ['--force-color-profile=srgb'];
|
|
537
|
-
// headless chromium backgrounds/occludes the renderer and throttles rAF +
|
|
538
|
-
// timers to ~1fps, which silently starves combined/video frame capture
|
|
539
|
-
// (was 47 frames over 63s -> frozen-looking mp4). keep the render loop at
|
|
540
|
-
// full rate so the recorder captures real motion. document.hidden stays
|
|
541
|
-
// false in headless, so assertTabVisibleForCapture never caught this — it
|
|
542
|
-
// has to be fixed at the launch flags.
|
|
543
|
-
opts.args = [
|
|
544
|
-
'--disk-cache-dir=' + diskCacheDir,
|
|
545
|
-
'--disk-cache-size=67108864',
|
|
546
|
-
'--disable-background-timer-throttling',
|
|
547
|
-
'--disable-backgrounding-occluded-windows',
|
|
548
|
-
'--disable-renderer-backgrounding',
|
|
549
|
-
${JSON.stringify(DRIVER_AUTOPLAY_POLICY)},
|
|
550
|
-
];
|
|
551
|
-
if (channel) {
|
|
552
|
-
// the full test browser can bind the real platform GPU — force the native
|
|
553
|
-
// ANGLE backend and strip playwright's --enable-unsafe-swiftshader
|
|
554
|
-
// default arg. without both, headless chrome silently falls back to
|
|
555
|
-
// swiftshader software rendering for the CanvasKit/WebGL canvas and
|
|
556
|
-
// pegs every core. SOOTSIM_PW_CHROME_ARGS is appended later, so an
|
|
557
|
-
// env-injected --use-angle=... still overrides (last flag wins).
|
|
558
|
-
const angle = process.platform === 'darwin' ? 'metal'
|
|
559
|
-
: process.platform === 'linux' ? 'vulkan' : 'default';
|
|
560
|
-
opts.args.unshift(
|
|
561
|
-
'--use-angle=' + angle,
|
|
562
|
-
'--enable-gpu',
|
|
563
|
-
'--enable-gpu-rasterization',
|
|
564
|
-
'--enable-zero-copy',
|
|
565
|
-
'--ignore-gpu-blocklist',
|
|
566
|
-
'--enable-unsafe-webgpu',
|
|
567
|
-
);
|
|
568
|
-
opts.ignoreDefaultArgs.push('--enable-unsafe-swiftshader');
|
|
569
|
-
} else if (hostCommand) {
|
|
570
|
-
// the attached desktop host draws every surface on its own device, so the
|
|
571
|
-
// page composites only dom and the frames a capture presents: chromium's
|
|
572
|
-
// software compositor does that without a gpu process emulated on the cpu
|
|
573
|
-
opts.args.push('--disable-gpu');
|
|
574
|
-
} else {
|
|
575
|
-
// the shared software launch owns the webgpu canvas backing and watchdog
|
|
576
|
-
// flags too; an adapter alone does not prove a canvas can present.
|
|
577
|
-
opts.args.push(...${JSON.stringify(softwareChromeLaunchOptions({}).args)});
|
|
578
|
-
}
|
|
579
|
-
opts.args.push(${JSON.stringify(chromeDisabledFeaturesSwitch())});
|
|
580
|
-
if (fetchProxySpki) opts.args.push('--ignore-certificate-errors-spki-list=' + fetchProxySpki);
|
|
581
|
-
if (cdpPort) opts.args.push('--remote-debugging-port=' + cdpPort);
|
|
582
|
-
if (extraChromeArgs.length) opts.args.push(...extraChromeArgs);
|
|
583
|
-
// headed: size the OS window to the (phone-shaped) viewport too, plus
|
|
584
|
-
// room for chrome's own toolbar, so the window isn't desktop-shaped
|
|
585
|
-
// around a letterboxed page.
|
|
586
|
-
if (!headless) opts.args.push('--window-size=' + viewport.width + ',' + (viewport.height + 88));
|
|
587
|
-
const launched = await pw.chromium.launchPersistentContext(profileDir, opts);
|
|
588
|
-
profileLaunched = true;
|
|
589
|
-
return launched;
|
|
590
|
-
};
|
|
591
|
-
// the host-wide chromium slot is held for this host's life; its exit frees it
|
|
592
|
-
await acquireChromiumAdmission({
|
|
593
|
-
fs: require('fs'),
|
|
594
|
-
os: require('os'),
|
|
595
|
-
path: require('path'),
|
|
596
|
-
childProcess: require('child_process'),
|
|
597
|
-
label: 'peach sim host ' + profileDir,
|
|
598
|
-
readQueue: readChromiumQueue,
|
|
599
|
-
});
|
|
600
|
-
// the launcher's connect wait starts here: time queued for the slot is not
|
|
601
|
-
// time the sim had to connect.
|
|
602
|
-
if (admittedFile) {
|
|
603
|
-
try { writeFileSync(admittedFile, JSON.stringify({ admittedAt: Date.now(), pid: process.pid }) + '\\n'); } catch {}
|
|
604
|
-
}
|
|
605
|
-
clearStaleSingletonLock();
|
|
606
|
-
let launchErr = null;
|
|
607
|
-
let launchedChannel = null;
|
|
608
|
-
for (const channel of channelsToTry) {
|
|
609
|
-
try {
|
|
610
|
-
context = await launchWith(channel);
|
|
611
|
-
launchedChannel = channel || 'chromium';
|
|
612
|
-
launchErr = null;
|
|
613
|
-
break;
|
|
614
|
-
} catch (err) {
|
|
615
|
-
launchErr = err;
|
|
616
|
-
const msg = err && err.message ? String(err.message) : '';
|
|
617
|
-
const isMissingChannel =
|
|
618
|
-
/not found at/i.test(msg) ||
|
|
619
|
-
/not installed/i.test(msg) ||
|
|
620
|
-
/Chromium distribution/i.test(msg);
|
|
621
|
-
if (!isMissingChannel) throw err; // unexpected failure: bubble up
|
|
622
|
-
if (channel) {
|
|
623
|
-
const next = hardwareGpuRequired
|
|
624
|
-
? '; bundled Chromium is disabled for local GPU work\\n'
|
|
625
|
-
: ', falling back to bundled chromium\\n';
|
|
626
|
-
process.stderr.write('[playwright-driver] channel "' + channel + '" not available' + next);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
if (launchErr) throw launchErr;
|
|
631
|
-
if (launchedChannel) {
|
|
632
|
-
process.stderr.write('[playwright-driver] launched ' + launchedChannel + '\\n');
|
|
633
|
-
}
|
|
634
|
-
if (hostCommand) await startDesktopHost();
|
|
635
|
-
// grant camera up front so the sim's Camera app reaches the host webcam
|
|
636
|
-
// through getUserMedia, mirroring a real iOS simulator where the camera just
|
|
637
|
-
// works. without this the unhandled chrome permission prompt auto-denies and
|
|
638
|
-
// CameraApp renders its "Camera Access Required" placeholder.
|
|
639
|
-
// location does NOT follow the same rule: a real iOS simulator never reads
|
|
640
|
-
// the host Mac's real position, it reports a simulated device location
|
|
641
|
-
// (simctl location / engine/geolocation-source.ts). granting chrome's
|
|
642
|
-
// geolocation here would leak the operator's real position into the tenant
|
|
643
|
-
// and its backend, so host-service-main.ts never reaches navigator.geolocation.
|
|
644
|
-
await context.grantPermissions(['camera']);
|
|
645
|
-
const hostMeta = {
|
|
646
|
-
sootsimHostDriver: 'playwright',
|
|
647
|
-
peachRenderer: hostCommand ? 'native' : 'chromium',
|
|
648
|
-
peachHostExecutable: hostCommand ? hostCommand[0] : null,
|
|
649
|
-
sootsimHostPid: process.pid,
|
|
650
|
-
sootsimHostOwnerPid: ownerPid,
|
|
651
|
-
sootsimHostStartedAt: Date.now(),
|
|
652
|
-
};
|
|
653
|
-
// inject the resolved bridge port (numeric literal) BEFORE the page runs, so
|
|
654
|
-
// the shell/engine talk to the daemon this driver owns. addInitScript runs
|
|
655
|
-
// ahead of every page script, and the vite dev plugin only fills the value in
|
|
656
|
-
// when it is still unset, so this one wins. plain assignment, NOT
|
|
657
|
-
// defineProperty: other injectors also assign to it, and a non-writable
|
|
658
|
-
// property would throw at that assignment and crash boot.
|
|
659
|
-
const bridgePortInject = bridgePort
|
|
660
|
-
? 'window.__sootsimBridgePort = ' + Number(bridgePort) + ';'
|
|
661
|
-
: '';
|
|
662
|
-
await context.addInitScript({
|
|
663
|
-
content: 'Object.defineProperty(window, "__sootsimHostMeta", { value: ' + JSON.stringify(hostMeta) + ', configurable: true });' + bridgePortInject,
|
|
664
|
-
});
|
|
665
|
-
// react-native-webview's frames: the bridge at document start of every
|
|
666
|
-
// document, CSP-proof injection, source.headers, no framing refusals and
|
|
667
|
-
// app-scheme navigations, as a WKWebView gives its page.
|
|
668
|
-
const webViewTimingName = 'peachwv' + require('crypto').randomBytes(12).toString('hex');
|
|
669
|
-
await automateWebViewFrames(
|
|
670
|
-
context,
|
|
671
|
-
{
|
|
672
|
-
hostBinding: ${JSON.stringify(WEBVIEW_HOST_BINDING)},
|
|
673
|
-
instanceAttribute: ${JSON.stringify(WEBVIEW_INSTANCE_ATTRIBUTE)},
|
|
674
|
-
schemeNavigationEvent: ${JSON.stringify(WEBVIEW_SCHEME_NAVIGATION_EVENT)},
|
|
675
|
-
networkNavigationEvent: ${JSON.stringify(WEBVIEW_NETWORK_NAVIGATION_EVENT)},
|
|
676
|
-
loadEvent: ${JSON.stringify(WEBVIEW_LOAD_EVENT)},
|
|
677
|
-
configTimingName: webViewTimingName,
|
|
678
|
-
bootstrapSource: '(' + ${JSON.stringify(WEBVIEW_HOST_BOOTSTRAP_SOURCE)} + ')(' + JSON.stringify(webViewTimingName) + ');',
|
|
679
|
-
},
|
|
680
|
-
webViewAutomationHelpers,
|
|
681
|
-
hostCommand ? (page) => [hostRealms(page)] : (page) => page.workers(),
|
|
682
|
-
);
|
|
683
|
-
await context.exposeBinding('__sootsimHostClose', async () => {
|
|
684
|
-
await shutdown(0);
|
|
685
|
-
});
|
|
686
|
-
const pointerHosts = new WeakMap();
|
|
687
|
-
await context.exposeBinding('__peachHostAccessibility', (source, instanceIds) =>
|
|
688
|
-
captureBrowserAccessibility(source.page, instanceIds));
|
|
689
|
-
await context.exposeBinding('__sootsimHostPointer', async (source, value) => {
|
|
690
|
-
let pending = pointerHosts.get(source.page);
|
|
691
|
-
if (!pending) {
|
|
692
|
-
pending = source.page.context().newCDPSession(source.page).then(session =>
|
|
693
|
-
createBrowserPointerDispatcher((method, params) => session.send(method, params)));
|
|
694
|
-
pointerHosts.set(source.page, pending);
|
|
695
|
-
}
|
|
696
|
-
await (await pending)(value);
|
|
697
|
-
});
|
|
698
|
-
await context.exposeBinding('__sootsimHostCapture', async (source, value) => {
|
|
699
|
-
const request = readBrowserCompositeCaptureRequest(value);
|
|
700
|
-
// a headless desktop host presents page frames only for a capture
|
|
701
|
-
if (desktopHost) await source.page.evaluate(() => window.__peachNative.prepareCapture());
|
|
702
|
-
const session = await source.page.context().newCDPSession(source.page);
|
|
703
|
-
try {
|
|
704
|
-
const { clip, originX, originY } = resolveBrowserCompositeCdpClip(
|
|
705
|
-
request,
|
|
706
|
-
await session.send('Page.getLayoutMetrics'),
|
|
707
|
-
);
|
|
708
|
-
const result = await session.send('Page.captureScreenshot', {
|
|
709
|
-
format: 'png',
|
|
710
|
-
fromSurface: true,
|
|
711
|
-
captureBeyondViewport: true,
|
|
712
|
-
clip,
|
|
713
|
-
});
|
|
714
|
-
return { dataUrl: 'data:image/png;base64,' + result.data, originX, originY };
|
|
715
|
-
} finally {
|
|
716
|
-
await session.detach();
|
|
717
|
-
}
|
|
718
|
-
});
|
|
719
|
-
const connectTimeoutMs = Number(process.env.SOOTSIM_PW_CONNECT_TIMEOUT_MS || 120000);
|
|
720
|
-
if (connectAckFile) {
|
|
721
|
-
connectAckPoll = setInterval(() => {
|
|
722
|
-
if (ackFileExists()) markConnected();
|
|
723
|
-
}, 500);
|
|
724
|
-
if (typeof connectAckPoll.unref === 'function') connectAckPoll.unref();
|
|
725
|
-
}
|
|
726
|
-
connectTimer = setTimeout(() => {
|
|
727
|
-
if (ackFileExists()) {
|
|
728
|
-
markConnected();
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
if (connected) return;
|
|
732
|
-
process.stderr.write('[connect-timeout] sim did not register with the bridge within ' + connectTimeoutMs + 'ms; closing playwright host\\n');
|
|
733
|
-
void shutdown(2);
|
|
734
|
-
}, Number.isFinite(connectTimeoutMs) && connectTimeoutMs > 0 ? connectTimeoutMs : 120000);
|
|
735
|
-
if (typeof connectTimer.unref === 'function') connectTimer.unref();
|
|
736
|
-
const page = await context.newPage();
|
|
737
|
-
await automateWebAuthenticationSessions(context, '__peachWebAuthenticationHost', page, !headless);
|
|
738
|
-
if (hardwareGpuRequired) {
|
|
739
|
-
assertNoUnsafeSwiftShaderFlag();
|
|
740
|
-
const gpu = await page.evaluate(() => {
|
|
741
|
-
const canvas = document.createElement('canvas');
|
|
742
|
-
const gl =
|
|
743
|
-
canvas.getContext('webgl2', { powerPreference: 'high-performance' }) ||
|
|
744
|
-
canvas.getContext('webgl', { powerPreference: 'high-performance' });
|
|
745
|
-
if (!gl) return { renderer: null, vendor: null };
|
|
746
|
-
const info = gl.getExtension('WEBGL_debug_renderer_info');
|
|
747
|
-
return {
|
|
748
|
-
renderer: info ? String(gl.getParameter(info.UNMASKED_RENDERER_WEBGL)) : null,
|
|
749
|
-
vendor: info ? String(gl.getParameter(info.UNMASKED_VENDOR_WEBGL)) : null,
|
|
750
|
-
};
|
|
751
|
-
});
|
|
752
|
-
if (!gpu.renderer) {
|
|
753
|
-
throw new Error(
|
|
754
|
-
'[playwright-driver] could not prove a hardware WebGL renderer; refusing local render-heavy launch'
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
if (softwareRendererPattern.test(gpu.renderer)) {
|
|
758
|
-
throw new Error('[playwright-driver] refused software WebGL renderer: ' + gpu.renderer);
|
|
759
|
-
}
|
|
760
|
-
assertNoUnsafeSwiftShaderFlag();
|
|
761
|
-
process.stderr.write(
|
|
762
|
-
'[playwright-driver] hardware WebGL renderer: ' + gpu.renderer +
|
|
763
|
-
(gpu.vendor ? ' (' + gpu.vendor + ')' : '') + '\\n'
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
// surface page-side failures (engine boot crash, failed WS registration)
|
|
767
|
-
// to stderr so a sim that never connects produces a real diagnostic
|
|
768
|
-
// instead of a silent "timed out waiting for opened sim to connect".
|
|
769
|
-
page.on('pageerror', (e) => {
|
|
770
|
-
process.stderr.write('[pageerror] ' + ((e && e.stack) || e) + '\\n');
|
|
771
|
-
});
|
|
772
|
-
page.on('console', (m) => {
|
|
773
|
-
if (m.type() === 'error') {
|
|
774
|
-
process.stderr.write('[console.error] ' + m.text() + '\\n');
|
|
775
|
-
if (hostCommand && m.text().includes('[compositor] worker lost:')) void shutdown(1);
|
|
776
|
-
}
|
|
777
|
-
});
|
|
778
|
-
// a crashed renderer never runs the page's bridge close path, so nothing
|
|
779
|
-
// would ever close this host.
|
|
780
|
-
page.on('crash', () => {
|
|
781
|
-
process.stderr.write('[page] renderer crashed; closing playwright host\\n');
|
|
782
|
-
void shutdown(1);
|
|
783
|
-
});
|
|
784
|
-
try {
|
|
785
|
-
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
786
|
-
} catch (err) {
|
|
787
|
-
const message = String((err && err.message) || err);
|
|
788
|
-
process.stderr.write('[goto] ' + message + '\\n');
|
|
789
|
-
// nothing serves this url: the page is a dead error document that can
|
|
790
|
-
// never register, and nothing re-navigates — waiting out the connect
|
|
791
|
-
// timeout burns 125s proving it (peach test on an unserved shell). exit
|
|
792
|
-
// now so the caller fails fast. a goto TIMEOUT keeps waiting (a
|
|
793
|
-
// slow-but-alive server may still finish loading the page); only
|
|
794
|
-
// connection-level failures, where no server exists at all, are fatal.
|
|
795
|
-
if (/ERR_CONNECTION_REFUSED|ERR_ADDRESS_UNREACHABLE|ERR_NAME_NOT_RESOLVED/.test(message)) {
|
|
796
|
-
process.stderr.write('[unserved] nothing serves ' + url + '; closing playwright host\\n');
|
|
797
|
-
void shutdown(3);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
keepAlive = setInterval(() => {}, 1 << 30);
|
|
801
|
-
// browser disconnect (e.g. user killed chrome from outside) → tear down.
|
|
802
|
-
// we are persistent-only now, so .browser() is the only handle to it.
|
|
803
|
-
try {
|
|
804
|
-
const b = context.browser && context.browser();
|
|
805
|
-
if (b && typeof b.on === 'function') b.on('disconnected', () => { void shutdown(0); });
|
|
806
|
-
} catch {}
|
|
807
|
-
context.on('close', () => { void shutdown(0); });
|
|
808
|
-
page.on('close', () => { void shutdown(0); });
|
|
809
|
-
})().catch((err) => {
|
|
810
|
-
const detail = String((err && err.stack) || err);
|
|
811
|
-
process.stderr.write(detail + '\\n');
|
|
812
|
-
// chrome's own error ("Missing X server") says what is wrong on a linux
|
|
813
|
-
// box with no display, not what to do about it.
|
|
814
|
-
if (
|
|
815
|
-
process.platform === 'linux' &&
|
|
816
|
-
!headless &&
|
|
817
|
-
!process.env.DISPLAY &&
|
|
818
|
-
!process.env.WAYLAND_DISPLAY &&
|
|
819
|
-
/missing x server|cannot open display|x server|x11/i.test(detail)
|
|
820
|
-
) {
|
|
821
|
-
process.stderr.write('hint: linux without DISPLAY/WAYLAND_DISPLAY — rerun without --headed (or add --headless) to run playwright headless.\\n');
|
|
822
|
-
}
|
|
823
|
-
process.exit(1);
|
|
824
|
-
});
|
|
825
|
-
`
|