peachsim 0.1.940 → 0.1.1400
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -22
- package/cli/app-catalog.ts +29 -14
- package/cli/app-fonts.ts +17 -8
- 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 +107 -95
- 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 +19 -16
- 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 +50 -8
- package/cli/recording-access.ts +6 -6
- package/cli/run-registry.ts +1 -1
- package/cli/runtime-compat.ts +5 -1
- package/cli/screenshot-output.ts +17 -0
- 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 +3 -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 +981 -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 +670 -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 +304 -25
- package/dist-lib/native-app-identity.mjs +302 -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 +4114 -1376
- 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/dev-bundle-resolution.ts +1 -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 +367 -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
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
@file:OptIn(kotlin.io.encoding.ExperimentalEncodingApi::class)
|
|
2
|
+
|
|
3
|
+
package dev.peach.expo
|
|
4
|
+
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.content.res.Configuration
|
|
7
|
+
import android.content.res.Resources
|
|
8
|
+
import android.util.DisplayMetrics
|
|
9
|
+
import kotlin.math.roundToInt
|
|
10
|
+
import android.util.Log
|
|
11
|
+
import expo.modules.kotlin.AppContext
|
|
12
|
+
import expo.modules.kotlin.Promise
|
|
13
|
+
import expo.modules.kotlin.classcomponent.ClassDefinitionData
|
|
14
|
+
import expo.modules.kotlin.exception.CodedException
|
|
15
|
+
import expo.modules.kotlin.exception.FunctionCallException
|
|
16
|
+
import expo.modules.kotlin.exception.UnexpectedException
|
|
17
|
+
import expo.modules.kotlin.exception.exceptionDecorator
|
|
18
|
+
import expo.modules.kotlin.exception.toCodedException
|
|
19
|
+
import expo.modules.kotlin.functions.AnyFunction
|
|
20
|
+
import expo.modules.kotlin.functions.AsyncFunctionWithPromiseComponent
|
|
21
|
+
import expo.modules.kotlin.functions.SuspendFunctionComponent
|
|
22
|
+
import expo.modules.kotlin.functions.SyncFunctionComponent
|
|
23
|
+
import expo.modules.kotlin.functions.UntypedAsyncFunctionComponent
|
|
24
|
+
import expo.modules.kotlin.jni.JavaScriptTypedArray
|
|
25
|
+
import expo.modules.kotlin.jni.TypedArrayKind
|
|
26
|
+
import expo.modules.kotlin.modules.Module
|
|
27
|
+
import expo.modules.kotlin.modules.ModuleDefinitionData
|
|
28
|
+
import expo.modules.kotlin.objects.ObjectDefinitionData
|
|
29
|
+
import expo.modules.kotlin.sharedobjects.SharedObject
|
|
30
|
+
import expo.modules.kotlin.sharedobjects.SharedObjectClass
|
|
31
|
+
import expo.modules.kotlin.sharedobjects.SharedObjectId
|
|
32
|
+
import expo.modules.kotlin.sharedobjects.SharedRef
|
|
33
|
+
import expo.modules.kotlin.typedarray.RawTypedArrayHolder
|
|
34
|
+
import expo.modules.kotlin.views.ExpoView
|
|
35
|
+
import expo.modules.kotlin.views.ViewManagerDefinition
|
|
36
|
+
import kotlin.io.encoding.Base64
|
|
37
|
+
import kotlinx.coroutines.launch
|
|
38
|
+
import org.json.JSONArray
|
|
39
|
+
import org.json.JSONObject
|
|
40
|
+
|
|
41
|
+
// the module registry of one Kotlin/Wasm process (ModuleRegistry and
|
|
42
|
+
// ModuleHolder), answering the tenant's Expo requests over the Expo wire that
|
|
43
|
+
// ExpoWire in sootsim-swift's ExpoModulesCore owns. The tenant installer is
|
|
44
|
+
// shared with the Swift process; only this side differs.
|
|
45
|
+
object PeachExpoRuntime {
|
|
46
|
+
private class Holder(val name: String, val module: Module, val definition: ModuleDefinitionData) {
|
|
47
|
+
val functions: Map<String, AnyFunction> = functionsOf(definition.objectDefinition)
|
|
48
|
+
val classes: Map<String, ClassDefinitionData> = definition.classData.associateBy { it.name }
|
|
49
|
+
val views: Map<String, ViewManagerDefinition> = definition.views.associateBy { it.name }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// a view React Native created, with the props it last received
|
|
53
|
+
private class MountedView(val module: String, val definition: ViewManagerDefinition, val view: ExpoView) {
|
|
54
|
+
var props: Map<*, *>? = null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private val viewResources = Resources(Configuration(), DisplayMetrics(), emptyMap(), emptyMap(), emptyMap(), emptyMap())
|
|
58
|
+
private object PeachContext : Context() {
|
|
59
|
+
override val resources: Resources get() = viewResources
|
|
60
|
+
override val theme = Resources.Theme()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
internal fun configureViewDevice(density: Float, locale: String, night: Boolean) {
|
|
64
|
+
val metrics = viewResources.displayMetrics.apply {
|
|
65
|
+
densityDpi = (density * DisplayMetrics.DENSITY_DEFAULT).roundToInt()
|
|
66
|
+
this.density = densityDpi / DisplayMetrics.DENSITY_DEFAULT.toFloat()
|
|
67
|
+
}
|
|
68
|
+
val configuration = Configuration().apply {
|
|
69
|
+
localeTag = locale
|
|
70
|
+
uiMode = Configuration.UI_MODE_TYPE_NORMAL or
|
|
71
|
+
if (night) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO
|
|
72
|
+
}
|
|
73
|
+
viewResources.configuration.setTo(configuration)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private fun functionsOf(definition: ObjectDefinitionData): Map<String, AnyFunction> =
|
|
77
|
+
definition.syncFunctions + definition.asyncFunctions
|
|
78
|
+
|
|
79
|
+
private sealed class Settled {
|
|
80
|
+
class Value(val value: Any?) : Settled()
|
|
81
|
+
class Failure(val error: CodedException) : Settled()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private val appContext = AppContext()
|
|
85
|
+
private val sharedObjects = appContext.runtime.sharedObjectRegistry
|
|
86
|
+
// the typed arrays the current request decoded, in wire order
|
|
87
|
+
private val requestArrays = mutableListOf<JavaScriptTypedArray>()
|
|
88
|
+
private val holders = LinkedHashMap<String, Holder>()
|
|
89
|
+
private val mountedViews = HashMap<String, MountedView>()
|
|
90
|
+
private val events = mutableListOf<String>()
|
|
91
|
+
private val settled = mutableMapOf<Int, Settled>()
|
|
92
|
+
private val pending = mutableSetOf<Int>()
|
|
93
|
+
private var nextCall = 1
|
|
94
|
+
private var notify: (() -> Unit)? = null
|
|
95
|
+
|
|
96
|
+
fun register(modules: List<Pair<() -> Module, String?>>) {
|
|
97
|
+
for ((create, override) in modules) {
|
|
98
|
+
val module = create()
|
|
99
|
+
module.context = appContext
|
|
100
|
+
val definition = module.definition()
|
|
101
|
+
val name = override ?: definition.name
|
|
102
|
+
check(name !in holders) { "duplicate Expo module: $name" }
|
|
103
|
+
val holder = Holder(name, module, definition)
|
|
104
|
+
module.emitter = { event, body ->
|
|
105
|
+
events.add(wireObject(listOf("module" to name, "name" to event, "body" to body)))
|
|
106
|
+
notify?.invoke()
|
|
107
|
+
}
|
|
108
|
+
holders[name] = holder
|
|
109
|
+
for (data in definition.classData) {
|
|
110
|
+
if (data.ownerClass != Unit::class) sharedObjects.addClass(data.ownerClass, SharedObjectClass(name, data.name))
|
|
111
|
+
}
|
|
112
|
+
for (body in definition.onCreate) body()
|
|
113
|
+
}
|
|
114
|
+
sharedObjects.emitter = { native, event, payload ->
|
|
115
|
+
events.add(wireObject(listOf("object" to native, "name" to event, "args" to payload)))
|
|
116
|
+
notify?.invoke()
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
internal fun sharedObject(id: SharedObjectId): SharedObject = sharedObjects.toNativeObject(id)
|
|
121
|
+
|
|
122
|
+
internal fun mountedView(id: String): ExpoView =
|
|
123
|
+
mountedViews[id]?.view ?: throw CodedException("Unable to find the Expo view $id")
|
|
124
|
+
|
|
125
|
+
// the tenant's wake-up: an event or a settled call is waiting
|
|
126
|
+
fun listen(listener: () -> Unit) {
|
|
127
|
+
notify = listener
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fun dispatch(payload: String): String {
|
|
131
|
+
requestArrays.clear()
|
|
132
|
+
val reply = try {
|
|
133
|
+
"\"ok\":true,\"value\":${request(decode(JSONArray("[$payload]").opt(0)))}"
|
|
134
|
+
} catch (error: Throwable) {
|
|
135
|
+
val coded = error.toCodedException()
|
|
136
|
+
"\"ok\":false,\"error\":{\"message\":${quote(coded.message ?: "")},\"code\":${quote(coded.code)}}"
|
|
137
|
+
}
|
|
138
|
+
// JSI typed arrays share the caller's memory: what a synchronous body
|
|
139
|
+
// wrote reaches the caller's array before the call returns
|
|
140
|
+
val writes = requestArrays.filter { it.dirty }
|
|
141
|
+
requestArrays.clear()
|
|
142
|
+
if (writes.isEmpty()) return "{$reply}"
|
|
143
|
+
return "{$reply,\"writes\":${writes.joinToString(",", "[", "]") { "[${it.ordinal},${quote(Base64.encode(it.bytes))}]" }}}"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fun request(value: Any?): String {
|
|
147
|
+
val fields = value as? Map<*, *> ?: throw UnexpectedException("invalid Expo request")
|
|
148
|
+
val operation = fields["operation"] as? String
|
|
149
|
+
when (operation) {
|
|
150
|
+
"metadata" -> return metadata()
|
|
151
|
+
"events" -> {
|
|
152
|
+
val drained = events.joinToString(",", "[", "]")
|
|
153
|
+
events.clear()
|
|
154
|
+
return drained
|
|
155
|
+
}
|
|
156
|
+
"dropView" -> {
|
|
157
|
+
val mounted = mountedViews.remove(fields["id"] as? String ?: throw UnexpectedException("invalid Expo view"))
|
|
158
|
+
mounted?.definition?.onViewDestroys?.invoke(mounted.view)
|
|
159
|
+
return "null"
|
|
160
|
+
}
|
|
161
|
+
"poll" -> {
|
|
162
|
+
val id = (fields["id"] as? Double)?.toInt() ?: throw UnexpectedException("invalid Expo poll")
|
|
163
|
+
if (id in pending) return wireObject(listOf("pending" to id.toDouble()))
|
|
164
|
+
return reply(settled.remove(id) ?: throw UnexpectedException("unknown Expo call $id"))
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (operation == "release") {
|
|
168
|
+
val id = (fields["id"] as? Double)?.toInt() ?: throw UnexpectedException("invalid Expo release")
|
|
169
|
+
sharedObjects.delete(SharedObjectId(id))
|
|
170
|
+
return "null"
|
|
171
|
+
}
|
|
172
|
+
val name = fields["module"] as? String ?: throw UnexpectedException("invalid Expo request")
|
|
173
|
+
val holder = holders[name] ?: throw CodedException("ERR_MODULE_NOT_FOUND", "Module '$name' not found", null)
|
|
174
|
+
if ("view" in fields) return viewRequest(holder, operation, fields)
|
|
175
|
+
val className = fields["class"] as? String
|
|
176
|
+
if (className != null) return classRequest(holder, className, operation, fields)
|
|
177
|
+
val member = fields["member"] as? String ?: throw UnexpectedException("invalid Expo request")
|
|
178
|
+
val definition = holder.definition.objectDefinition
|
|
179
|
+
return when (operation) {
|
|
180
|
+
"get" -> {
|
|
181
|
+
definition.constants[member]?.let { constant ->
|
|
182
|
+
if (!constant.evaluated) {
|
|
183
|
+
constant.value = constant.getter?.body?.invoke(emptyArray())
|
|
184
|
+
constant.evaluated = true
|
|
185
|
+
}
|
|
186
|
+
return wireObject(listOf("value" to constant.value))
|
|
187
|
+
}
|
|
188
|
+
val getter = definition.properties[member]?.getter ?: return wireObject(listOf("value" to Undefined))
|
|
189
|
+
wireObject(listOf("value" to getter.body(emptyArray())))
|
|
190
|
+
}
|
|
191
|
+
"set" -> {
|
|
192
|
+
val setter = definition.properties[member]?.setter ?: return wireObject(listOf("value" to Undefined))
|
|
193
|
+
setter.body(setter.convertArgs(listOf(fields["value"])))
|
|
194
|
+
wireObject(listOf("value" to Undefined))
|
|
195
|
+
}
|
|
196
|
+
"call" -> {
|
|
197
|
+
val function = holder.functions[member]
|
|
198
|
+
?: throw CodedException("ERR_FUNCTION_NOT_FOUND", "Function '$member' not found in module '$name'", null)
|
|
199
|
+
val args = fields["args"] as? List<*> ?: throw UnexpectedException("invalid Expo arguments")
|
|
200
|
+
call(name, function, args)
|
|
201
|
+
}
|
|
202
|
+
else -> throw UnexpectedException("invalid Expo operation $operation")
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// a view's props, which create the view the first time, or one of its
|
|
207
|
+
// async functions, whose first argument is the view when it takes the owner
|
|
208
|
+
private fun viewRequest(holder: Holder, operation: String?, fields: Map<*, *>): String {
|
|
209
|
+
val id = fields["id"] as? String ?: throw UnexpectedException("invalid Expo view")
|
|
210
|
+
val viewName = fields["view"] as? String ?: throw UnexpectedException("invalid Expo view")
|
|
211
|
+
if (operation == "viewProps") {
|
|
212
|
+
configureViewDevice(
|
|
213
|
+
(fields["density"] as? Double)?.toFloat() ?: throw UnexpectedException("missing Expo view device density"),
|
|
214
|
+
fields["locale"] as? String ?: throw UnexpectedException("missing Expo view device locale"),
|
|
215
|
+
fields["night"] as? Boolean ?: throw UnexpectedException("missing Expo view device theme"),
|
|
216
|
+
)
|
|
217
|
+
val props = fields["props"] as? Map<*, *> ?: throw UnexpectedException("invalid Expo view props")
|
|
218
|
+
val mounted = mountedViews.getOrPut(id) {
|
|
219
|
+
val definition = holder.views[viewName] ?: throw UnexpectedException("View '$viewName' not found in module '${holder.name}'")
|
|
220
|
+
val view = definition.createView(PeachContext, appContext)
|
|
221
|
+
view.callbacks = definition.callbacks
|
|
222
|
+
view.emitter = { event, body ->
|
|
223
|
+
events.add(wireObject(listOf("view" to id, "module" to holder.name, "name" to event, "body" to body)))
|
|
224
|
+
notify?.invoke()
|
|
225
|
+
}
|
|
226
|
+
MountedView(holder.name, definition, view)
|
|
227
|
+
}
|
|
228
|
+
updateView(mounted, props)
|
|
229
|
+
return "null"
|
|
230
|
+
}
|
|
231
|
+
val mounted = mountedViews[id]?.takeIf { it.module == holder.name && it.definition.name == viewName }
|
|
232
|
+
?: throw CodedException("Unable to find the $viewName view $id")
|
|
233
|
+
val member = fields["member"] as? String ?: throw UnexpectedException("invalid Expo request")
|
|
234
|
+
if (operation != "call") throw UnexpectedException("invalid Expo operation $operation")
|
|
235
|
+
val function = mounted.definition.asyncFunctions[member]
|
|
236
|
+
?: throw CodedException("ERR_FUNCTION_NOT_FOUND", "Function '$member' not found in view '$viewName'", null)
|
|
237
|
+
val args = fields["args"] as? List<*> ?: throw UnexpectedException("invalid Expo arguments")
|
|
238
|
+
return call("${holder.name}.$viewName", function, if (function.takesOwner) listOf(mounted.view) + args else args)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Fabric's first props hold only the props React set; later ones each
|
|
242
|
+
// prop that changed, a removed one as null. a failed prop is logged and
|
|
243
|
+
// the rest still apply, then the view hears that its props updated
|
|
244
|
+
private fun updateView(mounted: MountedView, props: Map<*, *>) {
|
|
245
|
+
val last = mounted.props
|
|
246
|
+
val changed = mounted.definition.props.values.filter { prop ->
|
|
247
|
+
if (last == null) prop.name in props else last[prop.name] != props[prop.name]
|
|
248
|
+
}
|
|
249
|
+
mounted.props = props
|
|
250
|
+
if (last != null && changed.isEmpty()) return
|
|
251
|
+
for (prop in changed) {
|
|
252
|
+
try {
|
|
253
|
+
prop.set(props[prop.name], mounted.view)
|
|
254
|
+
} catch (error: Throwable) {
|
|
255
|
+
Log.e("ExpoModulesCore", error.message ?: error.toString())
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
mounted.definition.onViewDidUpdateProps?.invoke(mounted.view)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// a Class member: the constructor, a static function, or an instance's
|
|
262
|
+
// function or property, whose first argument is `this` when it takes the owner
|
|
263
|
+
private fun classRequest(holder: Holder, className: String, operation: String?, fields: Map<*, *>): String {
|
|
264
|
+
val data = holder.classes[className] ?: throw UnexpectedException("Class '$className' not found in module '${holder.name}'")
|
|
265
|
+
val args = fields["args"] as? List<*> ?: emptyList<Any?>()
|
|
266
|
+
val owner = fields["this"]
|
|
267
|
+
if (operation == "construct") {
|
|
268
|
+
val constructor = data.constructor ?: return "[0,null]"
|
|
269
|
+
val native = exceptionDecorator({ FunctionCallException("constructor", "${holder.name}.$className", it) }) {
|
|
270
|
+
constructor.body(constructor.convertArgs(args))
|
|
271
|
+
}
|
|
272
|
+
if (native !is SharedObject) return "[0,null]"
|
|
273
|
+
if (native.runtime == null) native.runtime = appContext.runtime
|
|
274
|
+
val id = sharedObjects.add(native).value
|
|
275
|
+
return "[$id,${encode((native as? SharedRef<*>)?.nativeRefType)}]"
|
|
276
|
+
}
|
|
277
|
+
val member = fields["member"] as? String ?: throw UnexpectedException("invalid Expo request")
|
|
278
|
+
val withOwner = { function: AnyFunction, rest: List<Any?> -> if (function.takesOwner) listOf(owner) + rest else rest }
|
|
279
|
+
return when (operation) {
|
|
280
|
+
"get" -> {
|
|
281
|
+
val getter = data.objectDefinition.properties[member]?.getter ?: return wireObject(listOf("value" to Undefined))
|
|
282
|
+
wireObject(listOf("value" to getter.body(getter.convertArgs(withOwner(getter, emptyList())))))
|
|
283
|
+
}
|
|
284
|
+
"set" -> {
|
|
285
|
+
val setter = data.objectDefinition.properties[member]?.setter ?: return wireObject(listOf("value" to Undefined))
|
|
286
|
+
setter.body(setter.convertArgs(withOwner(setter, listOf(fields["value"]))))
|
|
287
|
+
wireObject(listOf("value" to Undefined))
|
|
288
|
+
}
|
|
289
|
+
"call" -> {
|
|
290
|
+
val function = if ("this" in fields) functionsOf(data.objectDefinition)[member]
|
|
291
|
+
else data.staticSyncFunctions[member] ?: data.staticAsyncFunctions[member]
|
|
292
|
+
function ?: throw CodedException("ERR_FUNCTION_NOT_FOUND", "Function '$member' not found in class '$className'", null)
|
|
293
|
+
call("${holder.name}.$className", function, withOwner(function, args))
|
|
294
|
+
}
|
|
295
|
+
else -> throw UnexpectedException("invalid Expo operation $operation")
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private fun call(module: String, function: AnyFunction, args: List<Any?>): String {
|
|
300
|
+
if (function is SyncFunctionComponent) {
|
|
301
|
+
val value = exceptionDecorator({ FunctionCallException(function.name, module, it) }) {
|
|
302
|
+
function.body(function.convertArgs(args))
|
|
303
|
+
}
|
|
304
|
+
return wireObject(listOf("value" to value))
|
|
305
|
+
}
|
|
306
|
+
val id = nextCall++
|
|
307
|
+
pending.add(id)
|
|
308
|
+
val settle = { result: Settled ->
|
|
309
|
+
if (pending.remove(id)) {
|
|
310
|
+
settled[id] = result
|
|
311
|
+
notify?.invoke()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
val promise = object : Promise {
|
|
315
|
+
override fun resolve(value: Any?) = settle(Settled.Value(value))
|
|
316
|
+
override fun reject(code: String?, message: String?, cause: Throwable?) =
|
|
317
|
+
settle(Settled.Failure(CodedException(code ?: "UnknownCode", message, cause)))
|
|
318
|
+
}
|
|
319
|
+
val fail = { error: Throwable ->
|
|
320
|
+
settle(Settled.Failure(FunctionCallException(function.name, module, error.toCodedException())))
|
|
321
|
+
}
|
|
322
|
+
// android runs async bodies on its modules queue, after the call returns
|
|
323
|
+
appContext.modulesQueue.launch {
|
|
324
|
+
try {
|
|
325
|
+
val converted = function.convertArgs(args)
|
|
326
|
+
when (function) {
|
|
327
|
+
is UntypedAsyncFunctionComponent -> promise.resolve(function.body(converted))
|
|
328
|
+
is AsyncFunctionWithPromiseComponent -> function.body(converted, promise)
|
|
329
|
+
is SuspendFunctionComponent -> promise.resolve(function.body(this, converted))
|
|
330
|
+
}
|
|
331
|
+
} catch (error: Throwable) {
|
|
332
|
+
fail(error)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return wireObject(listOf("pending" to id.toDouble()))
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private fun reply(result: Settled): String = when (result) {
|
|
339
|
+
is Settled.Value -> wireObject(listOf("value" to result.value))
|
|
340
|
+
is Settled.Failure -> throw result.error
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private fun functionMetadata(functions: Map<String, AnyFunction>): List<Any?> =
|
|
344
|
+
functions.keys.sorted().map { name ->
|
|
345
|
+
val function = functions.getValue(name)
|
|
346
|
+
Wire(wireObject(listOf("name" to name, "async" to (function !is SyncFunctionComponent), "enumerable" to function.isEnumerable)))
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private fun metadata(): String = holders.keys.sorted().map { holders.getValue(it) }.map { holder ->
|
|
350
|
+
val definition = holder.definition.objectDefinition
|
|
351
|
+
val properties = definition.constants.keys.sorted().map { Wire(wireObject(listOf("name" to it, "get" to true, "set" to false))) } +
|
|
352
|
+
definition.properties.keys.sorted().map { name ->
|
|
353
|
+
val property = definition.properties.getValue(name)
|
|
354
|
+
Wire(wireObject(listOf("name" to name, "get" to (property.getter != null), "set" to (property.setter != null))))
|
|
355
|
+
}
|
|
356
|
+
Wire(wireObject(listOf(
|
|
357
|
+
"name" to holder.name,
|
|
358
|
+
"definitionName" to holder.definition.name,
|
|
359
|
+
"constants" to Wire(wireObject(definition.legacyConstantsProvider().entries.sortedBy { it.key }.map { it.key to it.value })),
|
|
360
|
+
"functions" to functionMetadata(holder.functions),
|
|
361
|
+
"properties" to properties,
|
|
362
|
+
"events" to definition.eventNames,
|
|
363
|
+
"views" to holder.definition.views.map { view ->
|
|
364
|
+
Wire(wireObject(listOf(
|
|
365
|
+
"name" to view.name,
|
|
366
|
+
"props" to view.props.keys.sorted(),
|
|
367
|
+
"events" to view.callbacks,
|
|
368
|
+
"functions" to functionMetadata(view.asyncFunctions),
|
|
369
|
+
)))
|
|
370
|
+
},
|
|
371
|
+
"classes" to holder.definition.classData.sortedBy { it.name }.map { data ->
|
|
372
|
+
val instance = data.objectDefinition
|
|
373
|
+
Wire(wireObject(listOf(
|
|
374
|
+
"name" to data.name,
|
|
375
|
+
"functions" to functionMetadata(functionsOf(instance)),
|
|
376
|
+
"staticFunctions" to functionMetadata(data.staticSyncFunctions + data.staticAsyncFunctions),
|
|
377
|
+
"properties" to instance.properties.keys.sorted().map { name ->
|
|
378
|
+
val property = instance.properties.getValue(name)
|
|
379
|
+
Wire(wireObject(listOf("name" to name, "get" to (property.getter != null), "set" to (property.setter != null))))
|
|
380
|
+
},
|
|
381
|
+
)))
|
|
382
|
+
},
|
|
383
|
+
)))
|
|
384
|
+
}.let { encode(it) }
|
|
385
|
+
|
|
386
|
+
// javascript undefined, and an already encoded wire value
|
|
387
|
+
private object Undefined
|
|
388
|
+
private class Wire(val json: String)
|
|
389
|
+
|
|
390
|
+
private fun decode(value: Any?): Any? = when (value) {
|
|
391
|
+
null, JSONObject.NULL -> null
|
|
392
|
+
is Boolean, is String -> value
|
|
393
|
+
is Number -> value.toDouble()
|
|
394
|
+
is JSONArray -> List(value.length()) { decode(value.opt(it)) }
|
|
395
|
+
is JSONObject -> {
|
|
396
|
+
val keys = value.keys().asSequence().toList()
|
|
397
|
+
if (keys.size != 1) throw UnexpectedException("invalid Expo wire value")
|
|
398
|
+
val raw = value.opt(keys[0])
|
|
399
|
+
when (keys[0]) {
|
|
400
|
+
"k" -> {
|
|
401
|
+
val pairs = raw as? JSONArray ?: throw UnexpectedException("invalid Expo wire object")
|
|
402
|
+
val fields = LinkedHashMap<String, Any?>()
|
|
403
|
+
for (index in 0 until pairs.length()) {
|
|
404
|
+
val pair = pairs.opt(index) as? JSONArray ?: throw UnexpectedException("invalid Expo wire object")
|
|
405
|
+
fields[pair.opt(0) as? String ?: throw UnexpectedException("invalid Expo wire object")] = decode(pair.opt(1))
|
|
406
|
+
}
|
|
407
|
+
fields
|
|
408
|
+
}
|
|
409
|
+
// a missing argument reaches the converters as null
|
|
410
|
+
"u" -> null
|
|
411
|
+
"n" -> when (raw) {
|
|
412
|
+
"-0" -> -0.0
|
|
413
|
+
"NaN" -> Double.NaN
|
|
414
|
+
"Infinity" -> Double.POSITIVE_INFINITY
|
|
415
|
+
"-Infinity" -> Double.NEGATIVE_INFINITY
|
|
416
|
+
else -> throw UnexpectedException("invalid Expo wire number")
|
|
417
|
+
}
|
|
418
|
+
"ab" -> Base64.decode(raw as? String ?: throw UnexpectedException("invalid Expo wire bytes"))
|
|
419
|
+
"t" -> {
|
|
420
|
+
val pair = raw as? JSONArray ?: throw UnexpectedException("invalid Expo wire typed array")
|
|
421
|
+
val kind = TypedArrayKind.entries.firstOrNull { it.name == pair.opt(0) }
|
|
422
|
+
?: throw UnexpectedException("invalid Expo wire typed array")
|
|
423
|
+
val bytes = Base64.decode(pair.opt(1) as? String ?: throw UnexpectedException("invalid Expo wire bytes"))
|
|
424
|
+
val offset = pair.opt(2) as? Number ?: throw UnexpectedException("invalid Expo wire typed array")
|
|
425
|
+
val byteOffset = offset.toInt()
|
|
426
|
+
val width = JavaScriptTypedArray.elementSize(kind)
|
|
427
|
+
if (pair.length() != 3 || offset.toDouble() != byteOffset.toDouble() || byteOffset < 0 || byteOffset % width != 0 || bytes.size % width != 0)
|
|
428
|
+
throw UnexpectedException("invalid Expo wire typed array")
|
|
429
|
+
JavaScriptTypedArray(kind, bytes, requestArrays.size, byteOffset).also { requestArrays.add(it) }
|
|
430
|
+
}
|
|
431
|
+
"so" -> SharedObjectId((raw as? Number)?.toInt() ?: throw UnexpectedException("invalid Expo shared object"))
|
|
432
|
+
else -> throw UnexpectedException("Expo wire value '${keys[0]}' is not supported in Kotlin yet")
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else -> throw UnexpectedException("invalid Expo wire value")
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private fun wireObject(fields: List<Pair<String, Any?>>): String =
|
|
439
|
+
fields.joinToString(",", "{\"k\":[", "]}") { (key, value) -> "[${quote(key)},${encode(value)}]" }
|
|
440
|
+
|
|
441
|
+
// JSTypeConverterProvider's conversions to javascript
|
|
442
|
+
private fun encode(value: Any?): String = when (value) {
|
|
443
|
+
null, Unit -> "null"
|
|
444
|
+
Undefined -> "{\"u\":0}"
|
|
445
|
+
is Wire -> value.json
|
|
446
|
+
is String -> quote(value)
|
|
447
|
+
is Boolean -> value.toString()
|
|
448
|
+
is Char -> quote(value.toString())
|
|
449
|
+
is Number -> number(value.toDouble())
|
|
450
|
+
is Map<*, *> -> wireObject(value.entries.map { (key, item) -> key.toString() to item })
|
|
451
|
+
is Collection<*> -> value.joinToString(",", "[", "]") { encode(it) }
|
|
452
|
+
is Array<*> -> value.joinToString(",", "[", "]") { encode(it) }
|
|
453
|
+
is Pair<*, *> -> "[${encode(value.first)},${encode(value.second)}]"
|
|
454
|
+
is IntArray -> value.joinToString(",", "[", "]") { number(it.toDouble()) }
|
|
455
|
+
is LongArray -> value.joinToString(",", "[", "]") { number(it.toDouble()) }
|
|
456
|
+
is DoubleArray -> value.joinToString(",", "[", "]") { number(it) }
|
|
457
|
+
is FloatArray -> value.joinToString(",", "[", "]") { number(it.toDouble()) }
|
|
458
|
+
is BooleanArray -> value.joinToString(",", "[", "]") { it.toString() }
|
|
459
|
+
// a ByteArray reaches JavaScript as a Uint8Array, as on Android
|
|
460
|
+
is ByteArray -> typedArray(TypedArrayKind.Uint8Array, value, 0)
|
|
461
|
+
is JavaScriptTypedArray -> typedArray(value.kind, value.bytes, value.byteOffset)
|
|
462
|
+
is RawTypedArrayHolder -> typedArray(value.rawArray.kind, value.rawArray.bytes, value.rawArray.byteOffset)
|
|
463
|
+
is SharedObject -> sharedObjectWire(value)
|
|
464
|
+
else -> {
|
|
465
|
+
val converted = PeachExpoTypes.toJS(value)
|
|
466
|
+
if (converted === PeachExpoTypes.Unconverted)
|
|
467
|
+
throw UnexpectedException("Cannot convert '${value::class.simpleName}' to a JavaScript value")
|
|
468
|
+
encode(converted)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private fun typedArray(kind: TypedArrayKind, bytes: ByteArray, byteOffset: Int): String =
|
|
473
|
+
"{\"t\":[${quote(kind.name)},${quote(Base64.encode(bytes))},$byteOffset]}"
|
|
474
|
+
|
|
475
|
+
// a native object JavaScript already holds is that object; a new one gets an
|
|
476
|
+
// instance of its exported class, or a plain object for an unexported
|
|
477
|
+
// SharedRef, as convertSharedObject does
|
|
478
|
+
private fun sharedObjectWire(native: SharedObject): String {
|
|
479
|
+
val exported = sharedObjects.classOf(native)
|
|
480
|
+
if (native.sharedObjectId.value == 0) {
|
|
481
|
+
if (exported == null && native !is SharedRef<*>)
|
|
482
|
+
throw UnexpectedException("Could not find JavaScript class for shared object: $native")
|
|
483
|
+
if (native.runtime == null) native.runtime = appContext.runtime
|
|
484
|
+
sharedObjects.add(native)
|
|
485
|
+
}
|
|
486
|
+
val refType = (native as? SharedRef<*>)?.nativeRefType
|
|
487
|
+
return "{\"so\":[${native.sharedObjectId.value},${encode(exported?.module)},${encode(exported?.name)},${encode(refType)}]}"
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private fun number(value: Double): String = when {
|
|
491
|
+
value.isNaN() -> "{\"n\":\"NaN\"}"
|
|
492
|
+
value == Double.POSITIVE_INFINITY -> "{\"n\":\"Infinity\"}"
|
|
493
|
+
value == Double.NEGATIVE_INFINITY -> "{\"n\":\"-Infinity\"}"
|
|
494
|
+
value == 0.0 && 1.0 / value < 0 -> "{\"n\":\"-0\"}"
|
|
495
|
+
value == value.toLong().toDouble() && kotlin.math.abs(value) < 1e15 -> value.toLong().toString()
|
|
496
|
+
else -> value.toString()
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private fun quote(value: String): String {
|
|
500
|
+
val out = StringBuilder("\"")
|
|
501
|
+
for (c in value) {
|
|
502
|
+
when (c) {
|
|
503
|
+
'"' -> out.append("\\\"")
|
|
504
|
+
'\\' -> out.append("\\\\")
|
|
505
|
+
'\n' -> out.append("\\n")
|
|
506
|
+
'\r' -> out.append("\\r")
|
|
507
|
+
'\t' -> out.append("\\t")
|
|
508
|
+
else -> if (c.code < 0x20) out.append("\\u").append(c.code.toString(16).padStart(4, '0')) else out.append(c)
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return out.append('"').toString()
|
|
512
|
+
}
|
|
513
|
+
}
|