peachsim 0.1.1620 → 0.1.1635
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/LICENSE +1 -1
- package/README.md +45 -6
- package/cli/app-config.ts +4 -1
- package/cli/app-fonts.ts +39 -18
- package/cli/app-stubs.ts +224 -9
- package/cli/bridge-flow-runner.ts +248 -156
- package/cli/cloud-client.ts +68 -57
- package/cli/command-registry.ts +1 -0
- package/cli/commands/box/search-command.ts +3 -2
- package/cli/commands/box.ts +10 -29
- package/cli/commands/control.ts +391 -92
- package/cli/commands/doctor.ts +17 -184
- package/cli/commands/flow.ts +79 -11
- package/cli/commands/inspect/core.ts +10 -5
- package/cli/commands/inspect/settling.ts +71 -22
- package/cli/commands/inspect/wait-ready.ts +12 -0
- package/cli/commands/inspect.ts +26 -3
- package/cli/commands/install-desktop.ts +1 -0
- package/cli/commands/maestro-compare.ts +105 -67
- package/cli/commands/maestro.ts +9 -1
- package/cli/commands/platform.ts +71 -36
- package/cli/commands/record.ts +21 -1
- package/cli/commands/screenshot.ts +23 -1
- package/cli/commands/serve.ts +7 -2
- package/cli/commands/setup.ts +19 -42
- package/cli/commands/swift.ts +5 -0
- package/cli/commands/upload.ts +51 -8
- package/cli/desktop-companion.ts +102 -17
- package/cli/dev-build-freshness.ts +37 -2
- package/cli/drivers/desktop.ts +14 -1
- package/cli/drivers/playwright-sim-host.ts +44 -8
- package/cli/drivers/playwright.ts +9 -8
- package/cli/drivers/types.ts +5 -0
- package/cli/maestro-accessibility.ts +53 -0
- package/cli/maestro-compare.ts +108 -12
- package/cli/main.ts +2 -1
- package/cli/outbound-endpoints.ts +35 -6
- package/cli/parent-pid.ts +0 -9
- package/cli/pause-aware-timeout.ts +102 -0
- package/cli/remote-simulators.ts +112 -0
- package/cli/remoteSimulatorContract.ts +21 -0
- package/cli/scene-video.ts +89 -0
- package/cli/ws-bridge.ts +23 -14
- package/detox/attempt-events.cjs +4 -0
- package/detox/expectations.ts +5 -1
- package/detox/index.ts +241 -181
- package/detox/motion-change.cjs +73 -0
- package/detox/motion-change.d.cts +21 -0
- package/detox/navigation-lifecycle.ts +22 -4
- package/dist-lib/agent-daemon-client.cjs +3 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +3 -2
- 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 +13 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +14 -8
- package/dist-lib/bridge-contract.mjs +13 -7
- package/dist-lib/bundle-channel.cjs +10480 -7962
- 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 +3 -2
- package/dist-lib/cloud.mjs +3 -2
- package/dist-lib/config.cjs +10 -6
- package/dist-lib/config.mjs +10 -6
- package/dist-lib/detox/index.cjs +1854 -966
- package/dist-lib/dev-bundle-resolution.cjs +64 -24
- package/dist-lib/expo-swift-bundle.cjs +8715 -6271
- package/dist-lib/expo-swift-metadata.cjs +7 -4
- package/dist-lib/expo-swift-metadata.mjs +7 -4
- package/dist-lib/home-paths.cjs +32 -3
- package/dist-lib/host/bridge-host.cjs +10135 -7060
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- 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 +1843 -408
- 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 +1 -1
- package/dist-lib/menu.cjs +3 -2
- package/dist-lib/menu.mjs +3 -2
- 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 +5 -4
- package/dist-lib/metro-production-bundle.mjs +5 -4
- package/dist-lib/metro.cjs +1182 -418
- package/dist-lib/native-app-identity-project.cjs +782 -23
- package/dist-lib/native-app-identity-project.mjs +781 -23
- package/dist-lib/native-app-identity.cjs +276 -9
- package/dist-lib/native-app-identity.mjs +273 -8
- package/dist-lib/native-firebase-metadata.cjs +5 -4
- package/dist-lib/native-firebase-metadata.mjs +5 -4
- 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 +3 -2
- package/dist-lib/react-native-host-modules.cjs +8 -1
- package/dist-lib/react-native-host-modules.mjs +8 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +106 -28
- package/dist-lib/sdk.cjs +115 -34
- package/dist-lib/sdk.mjs +115 -33
- package/dist-lib/skills.cjs +15274 -9904
- package/dist-lib/swift-compile.cjs +1165 -173
- package/dist-lib/swift-compile.mjs +1212 -203
- package/dist-lib/swift-package.cjs +12 -3
- package/dist-lib/swift-package.mjs +12 -3
- package/dist-lib/swift-submit.cjs +7 -1
- package/dist-lib/swift-submit.mjs +5 -1
- package/dist-lib/swift-toolchain.cjs +133 -18
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +2648 -862
- package/dist-lib/vite.cjs +70 -14
- package/dist-lib/worklet-executable.cjs +25 -2
- package/dist-lib/worklet-executable.mjs +25 -2
- package/kotlin/AppSources/dev/peach/app/PeachDrawable.kt +21 -14
- package/kotlin/Sources/androidx/annotation/DrawableRes.kt +14 -0
- package/package.json +2 -2
- package/scripts/dev-server-scanner.ts +35 -14
- package/skills/peach-perf/SKILL.md +11 -5
- package/skills/peach-setup/SKILL.md +16 -19
- package/src/admitted-mac-build.ts +34 -8
- package/src/android-app-identity.ts +146 -0
- package/src/android-autolinking.ts +75 -0
- package/src/android-string-value.ts +36 -0
- package/src/android-strings.ts +5 -36
- package/src/androidAutolinkingContract.ts +40 -0
- package/src/bridge-constants.ts +11 -0
- package/src/bridge-contract.ts +22 -34
- package/src/bundleGlobalFooter.ts +26 -7
- package/src/codex-client.ts +3 -2
- package/src/config.ts +15 -0
- package/src/desktop-host-delivery.ts +5 -1
- package/src/dev-bundle-resolution.ts +60 -26
- package/src/expo-kotlin-host.ts +6 -36
- package/src/expo-manifest-body.ts +13 -0
- package/src/expo-swift-bundle.ts +17 -12
- package/src/expo-swift-host.ts +640 -192
- package/src/expo-swift-ready.ts +78 -0
- package/src/expoSwiftContract.ts +19 -0
- package/src/home-paths.ts +48 -2
- package/src/host/bridge-host.ts +12 -57
- package/src/host/staged-host-files.ts +29 -2
- package/src/installed-package-versions.ts +54 -14
- package/src/launchMetadataContract.ts +24 -0
- package/src/metro-plugin.ts +37 -42
- package/src/native-app-identity-project.ts +142 -5
- package/src/native-app-identity.ts +176 -11
- package/src/native-firebase-config.ts +209 -13
- package/src/nitro-compile.ts +2 -5
- package/src/nitro-source.ts +147 -63
- package/src/process-group.ts +114 -61
- package/src/process-output.ts +0 -9
- package/src/public-brand.ts +1 -0
- package/src/react-native-bridge-modules.ts +416 -0
- package/src/react-native-host-modules.ts +7 -0
- package/src/sim-client.ts +6 -4
- package/src/swift-binary-modules.ts +2 -2
- package/src/swift-compile.ts +1007 -108
- package/src/swift-compiler-digest.ts +14 -0
- package/src/swift-distribution.lock.json +40 -12
- package/src/swift-distribution.ts +61 -9
- package/src/swift-host.ts +149 -56
- package/src/swift-icu.ts +14 -6
- package/src/swift-objc-sdk.ts +133 -10
- package/src/swift-objc-standin.ts +38 -3
- package/src/swift-package-manifest.ts +21 -6
- package/src/swift-package.ts +15 -1
- package/src/swift-packages.ts +119 -60
- package/src/swift-standin-emit.ts +57 -9
- package/src/swift-standins.ts +185 -25
- package/src/swift-submit.ts +10 -0
- package/src/swift-toolchain.ts +149 -23
- package/src/vite-plugin.ts +20 -20
- package/src/worklet-executable.ts +30 -3
- package/src/worklets-babel.ts +35 -14
- package/src/xcode-project.ts +10 -0
package/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1635 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -7,6 +7,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
7
7
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
8
|
var __getProtoOf = Object.getPrototypeOf;
|
|
9
9
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
10
13
|
var __export = (target, all) => {
|
|
11
14
|
for (var name in all)
|
|
12
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -29,6 +32,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
32
|
));
|
|
30
33
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
34
|
|
|
35
|
+
// src/process-group.ts
|
|
36
|
+
var init_process_group = __esm({
|
|
37
|
+
"src/process-group.ts"() {
|
|
38
|
+
"use strict";
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
32
42
|
// src/vite-plugin-one.ts
|
|
33
43
|
var vite_plugin_one_exports = {};
|
|
34
44
|
__export(vite_plugin_one_exports, {
|
|
@@ -657,6 +667,7 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
|
|
|
657
667
|
var import_fs = __toESM(require("fs"), 1);
|
|
658
668
|
var import_path = __toESM(require("path"), 1);
|
|
659
669
|
var import_lazy_stub_patterns = require("@peach/compat/lazy-stub-patterns");
|
|
670
|
+
var import_native_package_names = require("@peach/compat/native-package-names");
|
|
660
671
|
var import_stub_manifest = require("@peach/compat/stub-manifest");
|
|
661
672
|
|
|
662
673
|
// ../peach-engine/src/stub-pattern-matcher.ts
|
|
@@ -677,7 +688,7 @@ function extractPackageFromPath(modulePath) {
|
|
|
677
688
|
}
|
|
678
689
|
|
|
679
690
|
// src/installed-package-versions.ts
|
|
680
|
-
function
|
|
691
|
+
function readInstalledPackage(projectRoot, name) {
|
|
681
692
|
let dir = projectRoot;
|
|
682
693
|
for (; ; ) {
|
|
683
694
|
let manifest;
|
|
@@ -694,8 +705,7 @@ function readInstalledVersion(projectRoot, name) {
|
|
|
694
705
|
dir = parent;
|
|
695
706
|
continue;
|
|
696
707
|
}
|
|
697
|
-
|
|
698
|
-
return typeof version === "string" && version.length > 0 ? version : null;
|
|
708
|
+
return typeof manifest === "object" && manifest !== null ? { root: import_path.default.join(dir, "node_modules", name), manifest } : null;
|
|
699
709
|
}
|
|
700
710
|
}
|
|
701
711
|
function readInstalledPackageVersions(projectRoot, modulePaths) {
|
|
@@ -706,19 +716,59 @@ function readInstalledPackageVersions(projectRoot, modulePaths) {
|
|
|
706
716
|
}
|
|
707
717
|
const versions = {};
|
|
708
718
|
for (const name of [...names].sort()) {
|
|
709
|
-
const
|
|
710
|
-
|
|
719
|
+
const installed = readInstalledPackage(projectRoot, name);
|
|
720
|
+
const version = installed && Reflect.get(installed.manifest, "version");
|
|
721
|
+
if (typeof version === "string" && version.length > 0) versions[name] = version;
|
|
711
722
|
}
|
|
712
723
|
return versions;
|
|
713
724
|
}
|
|
714
725
|
function readInstalledNativePackageVersions(projectRoot) {
|
|
726
|
+
const appRoot = import_fs.default.realpathSync(projectRoot);
|
|
727
|
+
const nativeNames = /* @__PURE__ */ new Set();
|
|
728
|
+
for (const specifier of [
|
|
729
|
+
"react-native",
|
|
730
|
+
...import_native_package_names.NATIVE_RUNTIME_PACKAGE_NAMES,
|
|
731
|
+
...import_lazy_stub_patterns.LAZY_STUB_PATTERNS,
|
|
732
|
+
...Object.values(import_stub_manifest.COMPAT_STUBS).map((entry) => entry.specifier)
|
|
733
|
+
]) {
|
|
734
|
+
const name = extractPackageFromPath(`node_modules/${specifier}`);
|
|
735
|
+
if (name) nativeNames.add(name);
|
|
736
|
+
}
|
|
737
|
+
const manifest = JSON.parse(
|
|
738
|
+
import_fs.default.readFileSync(import_path.default.join(appRoot, "package.json"), "utf8")
|
|
739
|
+
);
|
|
740
|
+
if (typeof manifest !== "object" || manifest === null) {
|
|
741
|
+
throw new Error("app package.json must contain an object");
|
|
742
|
+
}
|
|
743
|
+
const pending = [{ root: appRoot, manifest }];
|
|
744
|
+
const visited = /* @__PURE__ */ new Set();
|
|
745
|
+
const needed = /* @__PURE__ */ new Set();
|
|
746
|
+
for (let next = pending.pop(); next; next = pending.pop()) {
|
|
747
|
+
const root = import_fs.default.realpathSync(next.root);
|
|
748
|
+
if (visited.has(root)) continue;
|
|
749
|
+
visited.add(root);
|
|
750
|
+
const fields = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
751
|
+
if (root === appRoot) fields.push("devDependencies");
|
|
752
|
+
const peers = Reflect.get(next.manifest, "peerDependenciesMeta");
|
|
753
|
+
for (const field of fields) {
|
|
754
|
+
const dependencies = Reflect.get(next.manifest, field);
|
|
755
|
+
if (typeof dependencies !== "object" || dependencies === null) continue;
|
|
756
|
+
for (const name of Object.keys(dependencies)) {
|
|
757
|
+
if (field === "peerDependencies" && typeof peers === "object" && peers !== null) {
|
|
758
|
+
const peer = Reflect.get(peers, name);
|
|
759
|
+
if (typeof peer === "object" && peer !== null && Reflect.get(peer, "optional"))
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const installed = readInstalledPackage(root, name);
|
|
763
|
+
if (!installed) continue;
|
|
764
|
+
if (nativeNames.has(name)) needed.add(name);
|
|
765
|
+
pending.push(installed);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
715
769
|
return readInstalledPackageVersions(
|
|
716
770
|
projectRoot,
|
|
717
|
-
[
|
|
718
|
-
"react-native",
|
|
719
|
-
...import_lazy_stub_patterns.LAZY_STUB_PATTERNS,
|
|
720
|
-
...Object.values(import_stub_manifest.COMPAT_STUBS).map((entry) => entry.specifier)
|
|
721
|
-
].map((specifier) => `node_modules/${specifier}`)
|
|
771
|
+
[...needed].map((specifier) => `node_modules/${specifier}`)
|
|
722
772
|
);
|
|
723
773
|
}
|
|
724
774
|
|
|
@@ -797,6 +847,8 @@ function readPackageFile(root, file, maxBytes) {
|
|
|
797
847
|
// src/swift-submit.ts
|
|
798
848
|
var SWIFT_PACKAGE_PATH = "/__peach/swift-package/";
|
|
799
849
|
var SWIFT_HMR_PATH = "/__peach/swift-hmr";
|
|
850
|
+
var DEFAULT_SWIFT_COMPILE_BUILD_MS = 10 * 6e4;
|
|
851
|
+
var MAX_SWIFT_COMPILE_BUILD_MS = 60 * 6e4;
|
|
800
852
|
var MAX_SWIFT_COMPILE_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
801
853
|
var MAX_SWIFT_COMPILE_RESOURCE_BYTES = 2 * 1024 * 1024;
|
|
802
854
|
var MAX_SWIFT_COMPILE_RESOURCE_TOTAL_BYTES = 8 * 1024 * 1024;
|
|
@@ -971,13 +1023,14 @@ function findBundleFooter(source, prefix) {
|
|
|
971
1023
|
if (newline < 0) return -1;
|
|
972
1024
|
const line = newline + 1;
|
|
973
1025
|
if (source.startsWith(prefix, line)) return newline;
|
|
974
|
-
if (
|
|
975
|
-
return -1;
|
|
976
|
-
}
|
|
1026
|
+
if (!isFooterBlockLine(source, line, end)) return -1;
|
|
977
1027
|
end = newline;
|
|
978
1028
|
}
|
|
979
1029
|
return -1;
|
|
980
1030
|
}
|
|
1031
|
+
function isFooterBlockLine(source, line, end) {
|
|
1032
|
+
return line === end || source.startsWith("globalThis.", line) || source.startsWith("//", line);
|
|
1033
|
+
}
|
|
981
1034
|
function readFooter(source, prefix, suffix, name) {
|
|
982
1035
|
const start = findBundleFooter(source, prefix);
|
|
983
1036
|
if (start < 0) return null;
|
|
@@ -1034,6 +1087,9 @@ var import_fs2 = __toESM(require("fs"), 1);
|
|
|
1034
1087
|
var import_path2 = __toESM(require("path"), 1);
|
|
1035
1088
|
var import_url = require("url");
|
|
1036
1089
|
|
|
1090
|
+
// ../contrast-runtime-delivery/src/index.ts
|
|
1091
|
+
init_process_group();
|
|
1092
|
+
|
|
1037
1093
|
// ../contrast-runtime-delivery/src/version.ts
|
|
1038
1094
|
function isValidRuntimeVersion(value) {
|
|
1039
1095
|
return value.length <= 100 && /^[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(value);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1635 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -119,6 +119,17 @@ function declareParam(inner, fn, index, param) {
|
|
|
119
119
|
declare(inner, name, { kind: "param", fn, index });
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
|
+
if (isNode(param) && param.type === "ArrayPattern") {
|
|
123
|
+
const elements = Reflect.get(param, "elements");
|
|
124
|
+
if (!Array.isArray(elements)) return;
|
|
125
|
+
elements.forEach((element, position) => {
|
|
126
|
+
const name2 = identifierName(element);
|
|
127
|
+
if (name2 !== void 0)
|
|
128
|
+
declare(inner, name2, { kind: "param", fn, index, key: String(position) });
|
|
129
|
+
else declareOpaque(inner, patternNames(element));
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
122
133
|
if (!isNode(param) || param.type !== "ObjectPattern") {
|
|
123
134
|
declareOpaque(inner, patternNames(param));
|
|
124
135
|
return;
|
|
@@ -267,6 +278,17 @@ function immediateArgument(fn, index, collection) {
|
|
|
267
278
|
return { argument: args[index], scope: immediate.scope };
|
|
268
279
|
}
|
|
269
280
|
function propertyValue(object, name) {
|
|
281
|
+
if (object.type === "ArrayExpression") {
|
|
282
|
+
const index = Number(name);
|
|
283
|
+
const elements = Reflect.get(object, "elements");
|
|
284
|
+
if (!Number.isSafeInteger(index) || index < 0 || String(index) !== name)
|
|
285
|
+
return void 0;
|
|
286
|
+
if (!Array.isArray(elements)) return void 0;
|
|
287
|
+
if (elements.slice(0, index + 1).some((element) => isNode(element) && element.type === "SpreadElement"))
|
|
288
|
+
return void 0;
|
|
289
|
+
const value2 = elements[index];
|
|
290
|
+
return isNode(value2) ? value2 : void 0;
|
|
291
|
+
}
|
|
270
292
|
let value;
|
|
271
293
|
for (const property of childNodes(Reflect.get(object, "properties"))) {
|
|
272
294
|
if (property.type === "SpreadElement") return void 0;
|
|
@@ -284,7 +306,8 @@ function resolveProperty(owner, name, collection, depth) {
|
|
|
284
306
|
}
|
|
285
307
|
function resolveObject(expression, scope, collection, depth) {
|
|
286
308
|
if (depth > MAX_RESOLUTION_DEPTH) return void 0;
|
|
287
|
-
if (expression.type === "ObjectExpression"
|
|
309
|
+
if (expression.type === "ObjectExpression" || expression.type === "ArrayExpression")
|
|
310
|
+
return { object: expression, scope };
|
|
288
311
|
if (expression.type === "SequenceExpression") {
|
|
289
312
|
const last = childNodes(Reflect.get(expression, "expressions")).at(-1);
|
|
290
313
|
return last && resolveObject(last, scope, collection, depth + 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1635 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
|
|
3
3
|
// src/worklet-executable.ts
|
|
4
4
|
import { parse } from "acorn";
|
|
@@ -94,6 +94,17 @@ function declareParam(inner, fn, index, param) {
|
|
|
94
94
|
declare(inner, name, { kind: "param", fn, index });
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
|
+
if (isNode(param) && param.type === "ArrayPattern") {
|
|
98
|
+
const elements = Reflect.get(param, "elements");
|
|
99
|
+
if (!Array.isArray(elements)) return;
|
|
100
|
+
elements.forEach((element, position) => {
|
|
101
|
+
const name2 = identifierName(element);
|
|
102
|
+
if (name2 !== void 0)
|
|
103
|
+
declare(inner, name2, { kind: "param", fn, index, key: String(position) });
|
|
104
|
+
else declareOpaque(inner, patternNames(element));
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
97
108
|
if (!isNode(param) || param.type !== "ObjectPattern") {
|
|
98
109
|
declareOpaque(inner, patternNames(param));
|
|
99
110
|
return;
|
|
@@ -242,6 +253,17 @@ function immediateArgument(fn, index, collection) {
|
|
|
242
253
|
return { argument: args[index], scope: immediate.scope };
|
|
243
254
|
}
|
|
244
255
|
function propertyValue(object, name) {
|
|
256
|
+
if (object.type === "ArrayExpression") {
|
|
257
|
+
const index = Number(name);
|
|
258
|
+
const elements = Reflect.get(object, "elements");
|
|
259
|
+
if (!Number.isSafeInteger(index) || index < 0 || String(index) !== name)
|
|
260
|
+
return void 0;
|
|
261
|
+
if (!Array.isArray(elements)) return void 0;
|
|
262
|
+
if (elements.slice(0, index + 1).some((element) => isNode(element) && element.type === "SpreadElement"))
|
|
263
|
+
return void 0;
|
|
264
|
+
const value2 = elements[index];
|
|
265
|
+
return isNode(value2) ? value2 : void 0;
|
|
266
|
+
}
|
|
245
267
|
let value;
|
|
246
268
|
for (const property of childNodes(Reflect.get(object, "properties"))) {
|
|
247
269
|
if (property.type === "SpreadElement") return void 0;
|
|
@@ -259,7 +281,8 @@ function resolveProperty(owner, name, collection, depth) {
|
|
|
259
281
|
}
|
|
260
282
|
function resolveObject(expression, scope, collection, depth) {
|
|
261
283
|
if (depth > MAX_RESOLUTION_DEPTH) return void 0;
|
|
262
|
-
if (expression.type === "ObjectExpression"
|
|
284
|
+
if (expression.type === "ObjectExpression" || expression.type === "ArrayExpression")
|
|
285
|
+
return { object: expression, scope };
|
|
263
286
|
if (expression.type === "SequenceExpression") {
|
|
264
287
|
const last = childNodes(Reflect.get(expression, "expressions")).at(-1);
|
|
265
288
|
return last && resolveObject(last, scope, collection, depth + 1);
|
|
@@ -4,18 +4,21 @@ import androidx.compose.ui.graphics.Brush
|
|
|
4
4
|
import androidx.compose.ui.graphics.Color
|
|
5
5
|
import androidx.compose.ui.graphics.ImageBitmap
|
|
6
6
|
import androidx.compose.ui.graphics.SolidColor
|
|
7
|
-
import androidx.compose.ui.graphics.
|
|
7
|
+
import androidx.compose.ui.graphics.asComposeImageBitmap
|
|
8
8
|
import androidx.compose.ui.graphics.vector.ImageVector
|
|
9
9
|
import kotlin.io.encoding.Base64
|
|
10
10
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
|
11
|
+
import org.jetbrains.skia.Bitmap as SkiaBitmap
|
|
11
12
|
import org.jetbrains.skia.BlendMode
|
|
13
|
+
import org.jetbrains.skia.Canvas
|
|
14
|
+
import org.jetbrains.skia.Codec
|
|
15
|
+
import org.jetbrains.skia.Data
|
|
12
16
|
import org.jetbrains.skia.FilterMipmap
|
|
13
17
|
import org.jetbrains.skia.FilterMode
|
|
14
18
|
import org.jetbrains.skia.Image
|
|
15
19
|
import org.jetbrains.skia.MipmapMode
|
|
16
20
|
import org.jetbrains.skia.Paint
|
|
17
21
|
import org.jetbrains.skia.Rect
|
|
18
|
-
import org.jetbrains.skia.Surface
|
|
19
22
|
|
|
20
23
|
// one file a drawable resource resolves to under a density (ResTable_config's
|
|
21
24
|
// value, 0 for an unqualified directory) and a platform version: a vector the
|
|
@@ -67,28 +70,32 @@ private fun isBetter(a: PeachDrawable, b: PeachDrawable, requested: Int): Boolea
|
|
|
67
70
|
// bitmap drawn for a higher density than the display's is scaled down, its
|
|
68
71
|
// size rounded, through a linear filtered draw with the source blend; one
|
|
69
72
|
// drawn for a lower density, or nodpi, keeps its pixels, as from API 28 the
|
|
70
|
-
// upscale is left to draw time, which BitmapPainter does not do
|
|
73
|
+
// upscale is left to draw time, which BitmapPainter does not do. as
|
|
74
|
+
// ImageDecoder does, the codec decodes into a bitmap and the scale draws into
|
|
75
|
+
// another
|
|
71
76
|
@OptIn(ExperimentalEncodingApi::class)
|
|
72
77
|
internal fun peachDecodeBitmap(drawable: PeachDrawable.Bitmap, targetDensity: Int): ImageBitmap {
|
|
73
78
|
if (drawable.ninePatch)
|
|
74
79
|
throw UnsupportedOperationException("peach kotlin: the nine-patch drawable ${drawable.path} is not drawn yet")
|
|
75
|
-
val
|
|
80
|
+
val decoded = Codec.makeFromData(Data.makeFromBytes(Base64.decode(drawable.data))).readPixels()
|
|
76
81
|
val density = if (drawable.density == DENSITY_DEFAULT) DENSITY_MEDIUM else drawable.density
|
|
77
|
-
if (drawable.density == DENSITY_NONE || density <= targetDensity) return
|
|
82
|
+
if (drawable.density == DENSITY_NONE || density <= targetDensity) return decoded.asComposeImageBitmap()
|
|
78
83
|
val scale = targetDensity.toFloat() / density
|
|
79
|
-
val width = maxOf((
|
|
80
|
-
val height = maxOf((
|
|
81
|
-
val
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Rect.makeWH(
|
|
84
|
+
val width = maxOf((decoded.width * scale + 0.5f).toInt(), 1)
|
|
85
|
+
val height = maxOf((decoded.height * scale + 0.5f).toInt(), 1)
|
|
86
|
+
val scaled = SkiaBitmap().apply { allocN32Pixels(width, height) }
|
|
87
|
+
val canvas = Canvas(scaled)
|
|
88
|
+
canvas.scale(width.toFloat() / decoded.width, height.toFloat() / decoded.height)
|
|
89
|
+
canvas.drawImageRect(
|
|
90
|
+
Image.makeFromBitmap(decoded),
|
|
91
|
+
Rect.makeWH(decoded.width.toFloat(), decoded.height.toFloat()),
|
|
92
|
+
Rect.makeWH(decoded.width.toFloat(), decoded.height.toFloat()),
|
|
87
93
|
FilterMipmap(FilterMode.LINEAR, MipmapMode.NONE),
|
|
88
94
|
Paint().apply { blendMode = BlendMode.SRC },
|
|
89
95
|
false,
|
|
90
96
|
)
|
|
91
|
-
|
|
97
|
+
canvas.close()
|
|
98
|
+
return scaled.asComposeImageBitmap()
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
// ComplexColorCompat.willDraw: a transparent solid color draws nothing
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package androidx.annotation
|
|
2
|
+
|
|
3
|
+
// androidx.annotation's DrawableRes, which only its android artifact declares
|
|
4
|
+
@MustBeDocumented
|
|
5
|
+
@Retention(AnnotationRetention.BINARY)
|
|
6
|
+
@Target(
|
|
7
|
+
AnnotationTarget.FUNCTION,
|
|
8
|
+
AnnotationTarget.PROPERTY_GETTER,
|
|
9
|
+
AnnotationTarget.PROPERTY_SETTER,
|
|
10
|
+
AnnotationTarget.VALUE_PARAMETER,
|
|
11
|
+
AnnotationTarget.FIELD,
|
|
12
|
+
AnnotationTarget.LOCAL_VARIABLE,
|
|
13
|
+
)
|
|
14
|
+
annotation class DrawableRes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peachsim",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1635",
|
|
4
4
|
"description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
|
|
5
5
|
"author": "Tamagui LLC",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -371,7 +371,7 @@
|
|
|
371
371
|
"rollup": "4.62.2",
|
|
372
372
|
"systemjs": "6.15.1",
|
|
373
373
|
"tsconfck": "3.1.6",
|
|
374
|
-
"typescript": "7.1.0-
|
|
374
|
+
"typescript": "7.1.0-beta",
|
|
375
375
|
"vite": "8.3.3",
|
|
376
376
|
"yaml": "^2.8.3"
|
|
377
377
|
}
|
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
normalizeHostname,
|
|
25
25
|
} from '../src/backend-origin.ts'
|
|
26
26
|
import { applyPeachConfigToUrl } from '../src/config.ts'
|
|
27
|
+
import { fetchDevManifest } from '../src/dev-bundle-resolution'
|
|
28
|
+
import { parseExpoManifestBody } from '../src/expo-manifest-body'
|
|
27
29
|
import {
|
|
28
30
|
KOTLIN_DISCOVERY_PATH,
|
|
29
31
|
KOTLIN_GUEST_PATH,
|
|
@@ -448,6 +450,36 @@ function httpGet(
|
|
|
448
450
|
})
|
|
449
451
|
}
|
|
450
452
|
|
|
453
|
+
async function probeManifest(
|
|
454
|
+
hostname: string,
|
|
455
|
+
port: number,
|
|
456
|
+
timeout: number,
|
|
457
|
+
): Promise<HttpResult | null> {
|
|
458
|
+
let deadline: ReturnType<typeof setTimeout> | undefined
|
|
459
|
+
try {
|
|
460
|
+
return await Promise.race([
|
|
461
|
+
fetchDevManifest(buildLoopbackProbeBaseUrl('http:', hostname, port)).then(
|
|
462
|
+
async (res) =>
|
|
463
|
+
res
|
|
464
|
+
? {
|
|
465
|
+
statusCode: res.status,
|
|
466
|
+
body: await res.text(),
|
|
467
|
+
contentType: res.headers.get('content-type') || '',
|
|
468
|
+
hostname,
|
|
469
|
+
}
|
|
470
|
+
: null,
|
|
471
|
+
),
|
|
472
|
+
new Promise<null>((resolve) => {
|
|
473
|
+
deadline = setTimeout(() => resolve(null), timeout)
|
|
474
|
+
}),
|
|
475
|
+
])
|
|
476
|
+
} catch {
|
|
477
|
+
return null
|
|
478
|
+
} finally {
|
|
479
|
+
clearTimeout(deadline)
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
451
483
|
// ── discovery ───────────────────────────────────────────────────────────────
|
|
452
484
|
|
|
453
485
|
export interface ListeningProcess {
|
|
@@ -938,7 +970,7 @@ function applyManifest(
|
|
|
938
970
|
): DiscoveredServer {
|
|
939
971
|
if (!manifestRes) return result
|
|
940
972
|
try {
|
|
941
|
-
const manifest =
|
|
973
|
+
const manifest = parseExpoManifestBody(manifestRes.body)
|
|
942
974
|
const client = manifest?.extra?.expoClient || manifest?.extra || {}
|
|
943
975
|
if (client.name) result.projectName = client.name
|
|
944
976
|
if (typeof client.primaryColor === 'string') result.primaryColor = client.primaryColor
|
|
@@ -1089,16 +1121,7 @@ async function fetchManifestEnrichment(
|
|
|
1089
1121
|
buildIconProxyUrl?: (externalUrl: string) => string,
|
|
1090
1122
|
): Promise<void> {
|
|
1091
1123
|
const hostname = normalizeHostname(new URL(result.bundleUrl).hostname)
|
|
1092
|
-
const manifestRes = await
|
|
1093
|
-
hostname,
|
|
1094
|
-
result.port,
|
|
1095
|
-
'/',
|
|
1096
|
-
'GET',
|
|
1097
|
-
ENRICH_TIMEOUT_MS,
|
|
1098
|
-
{
|
|
1099
|
-
'expo-platform': 'ios',
|
|
1100
|
-
},
|
|
1101
|
-
)
|
|
1124
|
+
const manifestRes = await probeManifest(hostname, result.port, ENRICH_TIMEOUT_MS)
|
|
1102
1125
|
if (!manifestRes || manifestRes.statusCode !== 200) return
|
|
1103
1126
|
// reuse applyManifest's exact field extraction against a throwaway result.
|
|
1104
1127
|
const parsed = applyManifest(
|
|
@@ -1205,9 +1228,7 @@ export async function probePort(
|
|
|
1205
1228
|
httpGet(probeHostname, port, '/status'),
|
|
1206
1229
|
skipManifest
|
|
1207
1230
|
? Promise.resolve(null)
|
|
1208
|
-
:
|
|
1209
|
-
'expo-platform': 'ios',
|
|
1210
|
-
}),
|
|
1231
|
+
: probeManifest(probeHostname, port, MANIFEST_TIMEOUT_MS),
|
|
1211
1232
|
skipExpo ? Promise.resolve(null) : httpGet(probeHostname, port, '/_expo/status'),
|
|
1212
1233
|
httpGet(probeHostname, port, SWIFT_DISCOVERY_PATH),
|
|
1213
1234
|
httpGet(probeHostname, port, KOTLIN_DISCOVERY_PATH),
|
|
@@ -310,9 +310,14 @@ attributes a missing frame to its hop:
|
|
|
310
310
|
- `delivery lag` — vsync postMessage receipt latency above the run's
|
|
311
311
|
best case; jitter here means the shell worker was descheduled or its
|
|
312
312
|
queue was busy.
|
|
313
|
-
- `
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
- `present interval` — intervals between admitted compositor presentations,
|
|
314
|
+
including native frames, while progress remains pending after the preceding
|
|
315
|
+
presentation (`motionPending` in `compositorCadence.frameSeries`). Settled
|
|
316
|
+
admitted frames end a sequence; deferred frames retain the pending gap.
|
|
317
|
+
`paintIntervals` reports the sample count, so zero samples do not prove
|
|
318
|
+
smoothness. Shell work and JavaScript flushes can park while native frames
|
|
319
|
+
continue presenting.
|
|
320
|
+
- `idle breaks` — host gaps >250ms, excluded from host interval statistics.
|
|
316
321
|
|
|
317
322
|
the compositor `compositorHostFrameClock` keeps its historical name and reports
|
|
318
323
|
display/timer ticks consumed by the compositor worker's resilient clock during
|
|
@@ -320,8 +325,9 @@ the acknowledged profile window. `published` is null because renderer-main no
|
|
|
320
325
|
longer publishes compositor frames. sparse consumed ticks with low production
|
|
321
326
|
time identifies worker scheduling starvation. many consumed ticks with sparse
|
|
322
327
|
paints can be legitimate dirty-gating; correlate the frame series before
|
|
323
|
-
treating it as a regression.
|
|
324
|
-
|
|
328
|
+
treating it as a regression. Host and shell delivery statistics come from the
|
|
329
|
+
shell worker; `cadence.paintInterval*` comes from compositor presentation times
|
|
330
|
+
during motion.
|
|
325
331
|
|
|
326
332
|
compare baseline vs contended captures of the same scripted window; also
|
|
327
333
|
compare **painted-frame counts**, the crudest and most robust signal.
|
|
@@ -42,7 +42,8 @@ debugging or `/peach-test` for writing tests.
|
|
|
42
42
|
daemon is down, the dev server port is wrong, or the runtime is missing.
|
|
43
43
|
walk the recovery checklist before reinstalling.
|
|
44
44
|
- **keep one sim for your whole session.** the first `peach open <port>`
|
|
45
|
-
launches
|
|
45
|
+
launches the simulator window: the native desktop app on Apple silicon macOS
|
|
46
|
+
and Linux x64, otherwise Chrome for Testing with an isolated profile. later opens reuse the
|
|
46
47
|
saved sim and navigate it in place. use `peach do reload` when the target
|
|
47
48
|
has not changed:
|
|
48
49
|
|
|
@@ -74,27 +75,23 @@ debugging or `/peach-test` for writing tests.
|
|
|
74
75
|
saved sim to reuse. verify the development server and Peach runtime, then
|
|
75
76
|
retry once after fixing the cause.
|
|
76
77
|
|
|
77
|
-
##
|
|
78
|
+
## the simulator window
|
|
78
79
|
|
|
79
|
-
on Apple silicon macOS and Linux x64,
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
on Apple silicon macOS and Linux x64, `peach open` shows the app in the native
|
|
81
|
+
desktop app, which is faster than the browser. setup installs it, and on a Mac
|
|
82
|
+
the first `peach open` installs it if it is missing. do not pass a driver flag
|
|
83
|
+
for the user's own viewing.
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
peach desktop install
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
desktop
|
|
90
|
-
`peach desktop install --native` on Linux installs the standalone headless host
|
|
91
|
-
instead of the desktop app. Intel Mac and Linux arm64 use the browser.
|
|
92
|
-
`peach open 8081 --driver playwright --headed` explicitly opens a browser window.
|
|
93
|
-
pass the selected driver when opening the app in the steps below; installing
|
|
94
|
-
the desktop app alone does not change the saved window preference.
|
|
85
|
+
the Linux desktop app needs a desktop session; its `.deb` installer uses
|
|
86
|
+
`sudo apt-get install`. `peach desktop install --native` on Linux installs the
|
|
87
|
+
standalone headless host instead of the desktop app. Intel Mac and Linux arm64
|
|
88
|
+
use the browser. `peach open 8081 --driver playwright --headed` explicitly opens
|
|
89
|
+
a browser window. a yellow "GPU disabled" item in the browser simulator's menu
|
|
90
|
+
bar means it is drawing without a GPU and will be slow; move the user to the
|
|
91
|
+
desktop app.
|
|
95
92
|
|
|
96
|
-
the
|
|
97
|
-
|
|
93
|
+
the background service below keeps the bridge ready for either window and is a
|
|
94
|
+
separate optional choice.
|
|
98
95
|
|
|
99
96
|
## first move
|
|
100
97
|
|
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
export type WindowAdmission =
|
|
4
|
+
| { kind: 'heavy' | 'quiet'; cores: number | null; scopes: string[] }
|
|
5
|
+
| { kind: 'external'; name: string; cores: null }
|
|
6
|
+
// do not cache authority: release returns capacity while this process stays live.
|
|
7
|
+
export function windowAdmission(): WindowAdmission | null {
|
|
8
|
+
const result = spawnSync('tm', ['window', 'ancestor', '--json'], {
|
|
9
|
+
encoding: 'utf8',
|
|
10
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
11
|
+
})
|
|
12
|
+
if (result.error) throw result.error
|
|
13
|
+
if (result.status === 1) return null
|
|
14
|
+
if (result.status !== 0) throw new Error(`tm window ancestor exited ${result.status}`)
|
|
15
|
+
const entry = JSON.parse(result.stdout) as {
|
|
16
|
+
kind: string
|
|
17
|
+
name?: string
|
|
18
|
+
cores?: number
|
|
19
|
+
scopes?: string[]
|
|
20
|
+
}
|
|
21
|
+
if (entry.kind === 'external')
|
|
22
|
+
return { kind: 'external', name: entry.name ?? '', cores: null }
|
|
23
|
+
if (entry.kind !== 'heavy' && entry.kind !== 'quiet')
|
|
24
|
+
throw new Error(`tm window ancestor returned kind ${entry.kind}`)
|
|
25
|
+
return {
|
|
26
|
+
kind: entry.kind,
|
|
27
|
+
cores: entry.cores && entry.cores > 0 ? entry.cores : null,
|
|
28
|
+
scopes: entry.scopes ?? [],
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// a held destination slot and verified native grant authorize local compilation.
|
|
4
33
|
export function admittedMacBuilderSlot(
|
|
5
34
|
env: { readonly [key: string]: string | undefined } = process.env,
|
|
6
35
|
): boolean {
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
env.REMOTE_MAC_SLOT_HELD === '1' &&
|
|
10
|
-
env.CONTRAST_HEAVY_ADMITTED === '1'
|
|
11
|
-
)
|
|
36
|
+
if (process.platform !== 'darwin' || env.REMOTE_MAC_SLOT_HELD !== '1') return false
|
|
37
|
+
return windowAdmission() !== null
|
|
12
38
|
}
|