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
|
@@ -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;
|
|
@@ -319,7 +319,7 @@ function hasOwnKeys(value) {
|
|
|
319
319
|
}
|
|
320
320
|
function hasPeachConfig(config) {
|
|
321
321
|
if (!config) return false;
|
|
322
|
-
return config.nativeRenderer !== void 0 || typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
|
|
322
|
+
return config.nativeRenderer !== void 0 || config.mapKit !== void 0 || typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialProps) || hasOwnKeys(config.initialState);
|
|
323
323
|
}
|
|
324
324
|
function applyPeachConfigToUrl(url, config) {
|
|
325
325
|
const parsed = new URL(url);
|
|
@@ -331,18 +331,16 @@ function applyPeachConfigToUrl(url, config) {
|
|
|
331
331
|
return parsed.toString();
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
// src/
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
status: value.status
|
|
345
|
-
};
|
|
334
|
+
// src/expo-manifest-body.ts
|
|
335
|
+
function parseExpoManifestBody(body) {
|
|
336
|
+
const opening = /^(--[^\r\n]+)\r?\n/.exec(body);
|
|
337
|
+
if (!opening) return JSON.parse(body);
|
|
338
|
+
for (const part of body.split(opening[1])) {
|
|
339
|
+
const headerEnd = part.search(/\r?\n\r?\n/);
|
|
340
|
+
if (headerEnd < 0 || !/\bname="manifest"/.test(part.slice(0, headerEnd))) continue;
|
|
341
|
+
return JSON.parse(part.slice(headerEnd).trim());
|
|
342
|
+
}
|
|
343
|
+
throw new Error("Expo manifest response has no manifest part");
|
|
346
344
|
}
|
|
347
345
|
|
|
348
346
|
// src/native-dev-bundle-url.ts
|
|
@@ -389,6 +387,75 @@ function normalizeNativeDevBundleUrl(bundleUrl) {
|
|
|
389
387
|
}
|
|
390
388
|
}
|
|
391
389
|
|
|
390
|
+
// src/dev-bundle-resolution.ts
|
|
391
|
+
function shouldProxyLoopbackProbe(targetUrl) {
|
|
392
|
+
if (typeof window === "undefined") return false;
|
|
393
|
+
try {
|
|
394
|
+
const parsed = new URL(targetUrl);
|
|
395
|
+
return isLoopbackHost(parsed.hostname) && isLoopbackHost(window.location.hostname) && parsed.origin !== window.location.origin;
|
|
396
|
+
} catch {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async function fetchDevProbe(targetUrl, init) {
|
|
401
|
+
const requestInit = {
|
|
402
|
+
...init,
|
|
403
|
+
cache: init?.cache ?? "no-store"
|
|
404
|
+
};
|
|
405
|
+
if (!shouldProxyLoopbackProbe(targetUrl)) {
|
|
406
|
+
return fetch(targetUrl, requestInit);
|
|
407
|
+
}
|
|
408
|
+
return fetch(`/__fetch-proxy?url=${encodeURIComponent(targetUrl)}`, requestInit);
|
|
409
|
+
}
|
|
410
|
+
var MANIFEST_PROBE_TIMEOUT_MS = 3e4;
|
|
411
|
+
var manifestRequests = /* @__PURE__ */ new Map();
|
|
412
|
+
async function fetchDevManifest(baseUrl) {
|
|
413
|
+
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
414
|
+
const existing = manifestRequests.get(normalizedBaseUrl);
|
|
415
|
+
if (existing) return (await existing)?.clone();
|
|
416
|
+
const pending = fetchDevProbe(`${normalizedBaseUrl}/`, {
|
|
417
|
+
headers: { "expo-platform": "ios" }
|
|
418
|
+
}).then(async (res) => {
|
|
419
|
+
const body = await res.arrayBuffer();
|
|
420
|
+
return new Response(body.byteLength ? body : null, {
|
|
421
|
+
status: res.status,
|
|
422
|
+
headers: res.headers
|
|
423
|
+
});
|
|
424
|
+
}).catch(() => void 0);
|
|
425
|
+
let probeDeadline;
|
|
426
|
+
const request = Promise.race([
|
|
427
|
+
pending,
|
|
428
|
+
new Promise((resolve2) => {
|
|
429
|
+
probeDeadline = setTimeout(() => resolve2("timeout"), MANIFEST_PROBE_TIMEOUT_MS);
|
|
430
|
+
})
|
|
431
|
+
]).then((answered) => {
|
|
432
|
+
if (answered === "timeout")
|
|
433
|
+
throw new Error(
|
|
434
|
+
`Metro manifest request timed out after ${MANIFEST_PROBE_TIMEOUT_MS / 1e3}s: ${normalizedBaseUrl}/. Wait for the dev server to finish starting or check its manifest endpoint.`
|
|
435
|
+
);
|
|
436
|
+
return answered;
|
|
437
|
+
}).finally(() => {
|
|
438
|
+
clearTimeout(probeDeadline);
|
|
439
|
+
manifestRequests.delete(normalizedBaseUrl);
|
|
440
|
+
});
|
|
441
|
+
manifestRequests.set(normalizedBaseUrl, request);
|
|
442
|
+
return (await request)?.clone();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// src/kotlinDiscoveryContract.ts
|
|
446
|
+
var KOTLIN_DISCOVERY_PATH = "/__peach/kotlin-host";
|
|
447
|
+
var KOTLIN_GUEST_PATH = "/__peach/kotlin-guest.js";
|
|
448
|
+
function parseKotlinHostDiscovery(value) {
|
|
449
|
+
if (!value || typeof value !== "object") return null;
|
|
450
|
+
if (!("framework" in value) || value.framework !== "kotlin" || !("projectName" in value) || typeof value.projectName !== "string" || !("status" in value) || value.status !== "compiling" && value.status !== "ready" && value.status !== "error")
|
|
451
|
+
return null;
|
|
452
|
+
return {
|
|
453
|
+
framework: value.framework,
|
|
454
|
+
projectName: value.projectName,
|
|
455
|
+
status: value.status
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
392
459
|
// src/oneWidgetContract.ts
|
|
393
460
|
var HOME_WIDGET_FAMILIES = [
|
|
394
461
|
"systemSmall",
|
|
@@ -1138,6 +1205,28 @@ function httpGet(hostname, port, path, method = "GET", timeout = TIMEOUT_MS, hea
|
|
|
1138
1205
|
});
|
|
1139
1206
|
});
|
|
1140
1207
|
}
|
|
1208
|
+
async function probeManifest(hostname, port, timeout) {
|
|
1209
|
+
let deadline;
|
|
1210
|
+
try {
|
|
1211
|
+
return await Promise.race([
|
|
1212
|
+
fetchDevManifest(buildLoopbackProbeBaseUrl("http:", hostname, port)).then(
|
|
1213
|
+
async (res) => res ? {
|
|
1214
|
+
statusCode: res.status,
|
|
1215
|
+
body: await res.text(),
|
|
1216
|
+
contentType: res.headers.get("content-type") || "",
|
|
1217
|
+
hostname
|
|
1218
|
+
} : null
|
|
1219
|
+
),
|
|
1220
|
+
new Promise((resolve2) => {
|
|
1221
|
+
deadline = setTimeout(() => resolve2(null), timeout);
|
|
1222
|
+
})
|
|
1223
|
+
]);
|
|
1224
|
+
} catch {
|
|
1225
|
+
return null;
|
|
1226
|
+
} finally {
|
|
1227
|
+
clearTimeout(deadline);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1141
1230
|
var FALLBACK_PORTS = [
|
|
1142
1231
|
8081,
|
|
1143
1232
|
8082,
|
|
@@ -1485,7 +1574,7 @@ async function resolveLocalIconPath(hostname, port, rawIcon) {
|
|
|
1485
1574
|
function applyManifest(result, manifestRes, buildIconProxyUrl) {
|
|
1486
1575
|
if (!manifestRes) return result;
|
|
1487
1576
|
try {
|
|
1488
|
-
const manifest =
|
|
1577
|
+
const manifest = parseExpoManifestBody(manifestRes.body);
|
|
1489
1578
|
const client = manifest?.extra?.expoClient || manifest?.extra || {};
|
|
1490
1579
|
if (client.name) result.projectName = client.name;
|
|
1491
1580
|
if (typeof client.primaryColor === "string") result.primaryColor = client.primaryColor;
|
|
@@ -1579,16 +1668,7 @@ function enrichWithManifest(result, buildIconProxyUrl) {
|
|
|
1579
1668
|
}
|
|
1580
1669
|
async function fetchManifestEnrichment(result, buildIconProxyUrl) {
|
|
1581
1670
|
const hostname = normalizeHostname(new URL(result.bundleUrl).hostname);
|
|
1582
|
-
const manifestRes = await
|
|
1583
|
-
hostname,
|
|
1584
|
-
result.port,
|
|
1585
|
-
"/",
|
|
1586
|
-
"GET",
|
|
1587
|
-
ENRICH_TIMEOUT_MS,
|
|
1588
|
-
{
|
|
1589
|
-
"expo-platform": "ios"
|
|
1590
|
-
}
|
|
1591
|
-
);
|
|
1671
|
+
const manifestRes = await probeManifest(hostname, result.port, ENRICH_TIMEOUT_MS);
|
|
1592
1672
|
if (!manifestRes || manifestRes.statusCode !== 200) return;
|
|
1593
1673
|
const parsed = applyManifest(
|
|
1594
1674
|
{
|
|
@@ -1637,9 +1717,7 @@ async function probePort(port, buildIconProxyUrl, listeningProcess, verdict) {
|
|
|
1637
1717
|
Promise.all([
|
|
1638
1718
|
skipPatched ? Promise.resolve(null) : httpGet(probeHostname, port, "/__peachsim/"),
|
|
1639
1719
|
httpGet(probeHostname, port, "/status"),
|
|
1640
|
-
skipManifest ? Promise.resolve(null) :
|
|
1641
|
-
"expo-platform": "ios"
|
|
1642
|
-
}),
|
|
1720
|
+
skipManifest ? Promise.resolve(null) : probeManifest(probeHostname, port, MANIFEST_TIMEOUT_MS),
|
|
1643
1721
|
skipExpo ? Promise.resolve(null) : httpGet(probeHostname, port, "/_expo/status"),
|
|
1644
1722
|
httpGet(probeHostname, port, SWIFT_DISCOVERY_PATH),
|
|
1645
1723
|
httpGet(probeHostname, port, KOTLIN_DISCOVERY_PATH),
|
package/dist-lib/sdk.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 __defProp = Object.defineProperty;
|
|
@@ -87,7 +87,6 @@ __export(sdk_exports, {
|
|
|
87
87
|
layoutGetNativeEval: () => layoutGetNativeEval,
|
|
88
88
|
rankInteractive: () => rankInteractive,
|
|
89
89
|
readyProbeHasContent: () => readyProbeHasContent,
|
|
90
|
-
readyProbeHasTargetContent: () => readyProbeHasTargetContent,
|
|
91
90
|
readyProbeIsReady: () => readyProbeIsReady,
|
|
92
91
|
resolveFindMode: () => resolveFindMode,
|
|
93
92
|
resolveMaxMsFlag: () => resolveMaxMsFlag,
|
|
@@ -112,14 +111,20 @@ module.exports = __toCommonJS(sdk_exports);
|
|
|
112
111
|
// src/bridge-contract.ts
|
|
113
112
|
var SEMANTIC_READY_CONTENT_NODE_FLOOR = 100;
|
|
114
113
|
var HEADLESS_TENANT_FETCH_HUNG_MARKER = "headless tenant fetch hung";
|
|
115
|
-
function
|
|
116
|
-
|
|
114
|
+
function readyProbeContentBasis(probe) {
|
|
115
|
+
if (probe.paintedAppContent === true) return "paintedAppContent";
|
|
116
|
+
if ((probe.browserSurfaceCount ?? 0) > 0) return "browserSurface";
|
|
117
|
+
if (probe.liveFrameActive) return "liveFrame";
|
|
118
|
+
if (probe.targets > 0) return "targets";
|
|
119
|
+
if (probe.flag === true && probe.nodes >= SEMANTIC_READY_CONTENT_NODE_FLOOR)
|
|
120
|
+
return "nodeFloor";
|
|
121
|
+
return "";
|
|
117
122
|
}
|
|
118
|
-
function
|
|
119
|
-
return probe
|
|
123
|
+
function readyProbeHasContent(probe) {
|
|
124
|
+
return readyProbeContentBasis({ ...probe, flag: true }) !== "";
|
|
120
125
|
}
|
|
121
126
|
function readyProbeIsReady(probe) {
|
|
122
|
-
return (probe
|
|
127
|
+
return readyProbeContentBasis(probe) !== "" && probe.externalReady !== false && !probe.externalError && !probe.loadingText;
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
// src/bridge-contract-input.ts
|
|
@@ -969,7 +974,10 @@ function waitReadyStatus(result, ready) {
|
|
|
969
974
|
externalError: probe?.externalError ?? "",
|
|
970
975
|
suppressedEntryError: probe?.suppressedEntryError ?? "",
|
|
971
976
|
errors: probe?.errors ?? 0,
|
|
972
|
-
bridgeError: result.error ?? ""
|
|
977
|
+
bridgeError: result.error ?? "",
|
|
978
|
+
paintedAppContent: probe?.paintedAppContent ?? false,
|
|
979
|
+
treeQuietMs: probe?.treeQuietMs ?? 0,
|
|
980
|
+
readyBasis: probe ? readyProbeContentBasis(probe) : ""
|
|
973
981
|
};
|
|
974
982
|
}
|
|
975
983
|
async function inspectWaitSelector(bridge, testId, timeoutMs = 5e3, opts = {}) {
|
|
@@ -1585,6 +1593,28 @@ async function resolveTargetCoords(bridge, target) {
|
|
|
1585
1593
|
};
|
|
1586
1594
|
}
|
|
1587
1595
|
|
|
1596
|
+
// cli/maestro-accessibility.ts
|
|
1597
|
+
function accessibilityBounds(node) {
|
|
1598
|
+
const left = Math.trunc(node.frame.x);
|
|
1599
|
+
const top = Math.trunc(node.frame.y);
|
|
1600
|
+
return {
|
|
1601
|
+
x: left,
|
|
1602
|
+
y: top,
|
|
1603
|
+
width: Math.trunc(node.frame.x + node.frame.width) - left,
|
|
1604
|
+
height: Math.trunc(node.frame.y + node.frame.height) - top
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
function sameNativeAccessibilityShape(a, b, bounds) {
|
|
1608
|
+
if ((a.testID ?? "") !== (b.testID ?? "") || (a.label ?? "") !== (b.label ?? "") || (a.placeholder ?? "") !== (b.placeholder ?? "") || (a.value ?? "") !== (b.value ?? "") || a.enabled !== b.enabled || a.selected !== b.selected || a.checked !== b.checked || a.clickable !== b.clickable || (a.hasFocus ?? false) !== (b.hasFocus ?? false) || JSON.stringify(bounds(a)) !== JSON.stringify(bounds(b)) || a.children.length !== b.children.length) {
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
for (const [index, child] of a.children.entries()) {
|
|
1612
|
+
const other = b.children[index];
|
|
1613
|
+
if (!other || !sameNativeAccessibilityShape(child, other, bounds)) return false;
|
|
1614
|
+
}
|
|
1615
|
+
return true;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1588
1618
|
// cli/commands/inspect/settling.ts
|
|
1589
1619
|
var RuntimeReadinessUnsupportedError = class extends Error {
|
|
1590
1620
|
constructor(runtimeVersion, missingFields) {
|
|
@@ -1617,6 +1647,7 @@ async function waitForSootsimIdle({
|
|
|
1617
1647
|
stablePolls = 3,
|
|
1618
1648
|
strict = false,
|
|
1619
1649
|
paintStill = false,
|
|
1650
|
+
maestro = false,
|
|
1620
1651
|
motionOnly = false,
|
|
1621
1652
|
transitionStartWindowMs = 80,
|
|
1622
1653
|
transitionTimeoutMs = 4e3
|
|
@@ -1648,12 +1679,15 @@ async function waitForSootsimIdle({
|
|
|
1648
1679
|
const pollMs = ${Math.max(1, Math.round(pollMs))}
|
|
1649
1680
|
const strict = ${strict ? "true" : "false"}
|
|
1650
1681
|
const paintStill = ${paintStill ? "true" : "false"}
|
|
1682
|
+
const maestro = ${maestro ? "true" : "false"}
|
|
1683
|
+
const accessibilityBounds = ${accessibilityBounds.toString()}
|
|
1684
|
+
const sameNativeAccessibilityShape = ${sameNativeAccessibilityShape.toString()}
|
|
1651
1685
|
const motionOnly = ${motionOnly ? "true" : "false"}
|
|
1652
1686
|
// full mounted-geometry observation needs at least two hierarchy reads
|
|
1653
1687
|
// plus the stillness window, which a sub-second budget cannot afford:
|
|
1654
1688
|
// tight budgets (auto-wait) settle on worker stillness plus root
|
|
1655
1689
|
// membership, generous ones confirm the geometry too.
|
|
1656
|
-
const fullGeometry = ${!motionOnly && maxMs >= 1e3 ? "true" : "false"}
|
|
1690
|
+
const fullGeometry = ${!maestro && !motionOnly && maxMs >= 1e3 ? "true" : "false"}
|
|
1657
1691
|
const requiredStillMs = paintStill ? 0 : ${Math.max(1, Math.round(stablePolls)) * Math.max(1, Math.round(pollMs))}
|
|
1658
1692
|
const minimumStablePolls = paintStill ? 1 : ${Math.min(2, Math.max(1, Math.round(stablePolls)))}
|
|
1659
1693
|
const composite = paintStill ? window.SootSim?.bridges?.liveComposite : null
|
|
@@ -1775,6 +1809,9 @@ async function waitForSootsimIdle({
|
|
|
1775
1809
|
// strict also waits out shell motion: a system alert, sheet or menu
|
|
1776
1810
|
// animates in the shell worker while the app's own stats read still,
|
|
1777
1811
|
// so its presentation is complete only once the shell's list empties.
|
|
1812
|
+
const accessibilityRead = maestro
|
|
1813
|
+
? answer(() => window.__sootsimTest.captureAccessibilityTree()).catch(() => null)
|
|
1814
|
+
: null
|
|
1778
1815
|
const motionRead = strict
|
|
1779
1816
|
? answer(() => window.__sootsimTest?.listAnimations?.()).catch(() => null)
|
|
1780
1817
|
: null
|
|
@@ -1784,9 +1821,12 @@ async function waitForSootsimIdle({
|
|
|
1784
1821
|
// the compositor rejects a stats request it is too busy to answer.
|
|
1785
1822
|
const compositorRead = answer(() => window.__sootsimCompositor.getStats()).catch(() => BUSY)
|
|
1786
1823
|
const countsRead = motionOnly ? null : answer(() => window.__sootsimTest?.getRequestCounts?.()).catch(() => null)
|
|
1787
|
-
// mounted-root membership: the
|
|
1788
|
-
// mounts, unmounts or recycles, in
|
|
1789
|
-
|
|
1824
|
+
// mounted-root membership: the count of nodes that can reach the
|
|
1825
|
+
// window moves while the screen mounts, unmounts or recycles, in
|
|
1826
|
+
// either render mode. a screen a pushed one covers or an inactive
|
|
1827
|
+
// tab stays mounted out of the window, so what it mounts is not
|
|
1828
|
+
// screen motion.
|
|
1829
|
+
const nodeCountRead = answer(() => window.__sootsimTest?.getWindowNodeCount?.()).catch(() => null)
|
|
1790
1830
|
// the mounted hierarchy in render-worker mode comes from the test
|
|
1791
1831
|
// bridge: window.__sootsimRoot is an empty placeholder there, so a
|
|
1792
1832
|
// walk over it hashes every screen to the same empty string while
|
|
@@ -1816,7 +1856,7 @@ async function waitForSootsimIdle({
|
|
|
1816
1856
|
const motion = await motionRead
|
|
1817
1857
|
if (motion === BUSY) workerBusy = true
|
|
1818
1858
|
else if (Array.isArray(motion)) {
|
|
1819
|
-
const blocking = motion.filter((anim) => anim.realm === 'shell' && anim.priority !== 'low')
|
|
1859
|
+
const blocking = motion.filter((anim) => anim.realm === 'shell' && anim.priority !== 'low' && anim.visible !== false)
|
|
1820
1860
|
shellAnimating = blocking.length > 0
|
|
1821
1861
|
// name what holds the settle so a timeout points at its owner.
|
|
1822
1862
|
shellBlockers = blocking
|
|
@@ -1848,6 +1888,8 @@ async function waitForSootsimIdle({
|
|
|
1848
1888
|
stats.hasActiveAnims === true ||
|
|
1849
1889
|
stats.hasActiveBlockingNativeAnimations === true ||
|
|
1850
1890
|
(!paintStill && stats.hasPendingBlockingAnimationFrames === true)
|
|
1891
|
+
// the runtime reports only layout work that can reach the window:
|
|
1892
|
+
// a covered screen or inactive tab committing does not hold it.
|
|
1851
1893
|
layoutDirty =
|
|
1852
1894
|
stats.layoutDirty === true && stats.renderRequested === true
|
|
1853
1895
|
// a freshly-pushed screen showing a skeleton is layout-stable
|
|
@@ -1880,6 +1922,11 @@ async function waitForSootsimIdle({
|
|
|
1880
1922
|
const total = await nodeCountRead
|
|
1881
1923
|
if (total === BUSY) workerBusy = true
|
|
1882
1924
|
nodeCount = typeof total === 'number' ? total : null
|
|
1925
|
+
const accessibility = accessibilityRead ? await accessibilityRead : null
|
|
1926
|
+
const nativeHierarchy = Array.isArray(accessibility) && accessibility.length > 0
|
|
1927
|
+
? accessibility
|
|
1928
|
+
: null
|
|
1929
|
+
if (accessibility === BUSY) workerBusy = true
|
|
1883
1930
|
let hierarchy = hierarchyRead ? await hierarchyRead : null
|
|
1884
1931
|
if (hierarchy === BUSY) {
|
|
1885
1932
|
workerBusy = true
|
|
@@ -1939,9 +1986,15 @@ async function waitForSootsimIdle({
|
|
|
1939
1986
|
webViewStarting = true
|
|
1940
1987
|
webViewBlocker = 'preview progress element displayed'
|
|
1941
1988
|
} else {
|
|
1942
|
-
|
|
1989
|
+
// a loading document with no area (an app's hidden worker or
|
|
1990
|
+
// executor frame) paints nothing, and a screenshot on device
|
|
1991
|
+
// does not wait for it.
|
|
1992
|
+
const unreadyWebView = [...document.querySelectorAll(
|
|
1943
1993
|
'[data-sootsim-webview-loading="true"], [data-sootsim-webview-mode="placeholder"]'
|
|
1944
|
-
)
|
|
1994
|
+
)].find((view) => {
|
|
1995
|
+
const frame = view.querySelector('iframe')
|
|
1996
|
+
return !frame || frame.clientWidth > 0 || frame.clientHeight > 0
|
|
1997
|
+
})
|
|
1945
1998
|
if (unreadyWebView) {
|
|
1946
1999
|
webViewStarting = true
|
|
1947
2000
|
webViewBlocker = 'webView document still loading'
|
|
@@ -1960,7 +2013,7 @@ async function waitForSootsimIdle({
|
|
|
1960
2013
|
pixelFrameToken = token
|
|
1961
2014
|
}
|
|
1962
2015
|
}
|
|
1963
|
-
return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, shellBlockers, layoutDirty, pendingFetches, workerBusy, webViewStarting, webViewBlocker, renderStatsAvailable, requestTotal, requestInFlight, appCommits, nodeCount }
|
|
2016
|
+
return { layout: nodes.join(';'), nativeHierarchy, screenPixels, animating, shellAnimating, shellBlockers, layoutDirty, pendingFetches, workerBusy, webViewStarting, webViewBlocker, renderStatsAvailable, requestTotal, requestInFlight, appCommits, nodeCount }
|
|
1964
2017
|
}
|
|
1965
2018
|
|
|
1966
2019
|
// how long generic background network is allowed to keep us waiting AFTER
|
|
@@ -1974,6 +2027,7 @@ async function waitForSootsimIdle({
|
|
|
1974
2027
|
// so the generic request counters are a courtesy, not a gate: honor them
|
|
1975
2028
|
// only within this grace once the screen is visually still.
|
|
1976
2029
|
const networkQuietGraceMs = 1200
|
|
2030
|
+
let lastNativeHierarchy = null
|
|
1977
2031
|
let lastLayout = ''
|
|
1978
2032
|
let lastScreenPixels = null
|
|
1979
2033
|
let screenChangedPolls = 0
|
|
@@ -2001,6 +2055,9 @@ async function waitForSootsimIdle({
|
|
|
2001
2055
|
}
|
|
2002
2056
|
return true
|
|
2003
2057
|
}
|
|
2058
|
+
const hierarchyUnchanged = (current, previous) =>
|
|
2059
|
+
current !== null && previous !== null && current.length === previous.length &&
|
|
2060
|
+
current.every((node, index) => sameNativeAccessibilityShape(node, previous[index], accessibilityBounds))
|
|
2004
2061
|
const IDLE_HELD = { idleHeld: true }
|
|
2005
2062
|
while (true) {
|
|
2006
2063
|
if (clockNow() >= deadline) {
|
|
@@ -2046,7 +2103,7 @@ async function waitForSootsimIdle({
|
|
|
2046
2103
|
// the loop head drains a transition still in flight before it ends.
|
|
2047
2104
|
if (snapshot.workerBusy && clockNow() >= deadline && report) continue
|
|
2048
2105
|
polls++
|
|
2049
|
-
report = { snapshot, layout: lastLayout, screenPixels: lastScreenPixels, requestTotal: lastRequestTotal, appCommits: lastAppCommits, nodeCount: lastNodeCount }
|
|
2106
|
+
report = { snapshot, nativeHierarchy: lastNativeHierarchy, layout: lastLayout, screenPixels: lastScreenPixels, requestTotal: lastRequestTotal, appCommits: lastAppCommits, nodeCount: lastNodeCount }
|
|
2050
2107
|
if (snapshot.layoutDirty) dirtyPolls++
|
|
2051
2108
|
if (snapshot.animating || snapshot.shellAnimating) animatingPolls++
|
|
2052
2109
|
if (snapshot.webViewStarting) webViewStartingPolls++
|
|
@@ -2067,7 +2124,15 @@ async function waitForSootsimIdle({
|
|
|
2067
2124
|
// strictOk and adds shell motion plus the blocking-stats floor. a
|
|
2068
2125
|
// null count or commit reading means the runtime does not report
|
|
2069
2126
|
// it, which reads as stable the way a missing stats block does.
|
|
2070
|
-
|
|
2127
|
+
// native screen checks observe output, not animation ownership. after
|
|
2128
|
+
// its 3 s pixel phase, iOS accepts two equal native TreeNodes even
|
|
2129
|
+
// while paint-only playback continues. overlap hierarchy reads so
|
|
2130
|
+
// neither the gesture nor screenshot deadline grows.
|
|
2131
|
+
const maestroStill = snapshot.screenPixels !== null && !snapshot.workerBusy && !snapshot.webViewStarting && (
|
|
2132
|
+
!screenChanged || (clockNow() - start >= 3000 &&
|
|
2133
|
+
hierarchyUnchanged(snapshot.nativeHierarchy, lastNativeHierarchy))
|
|
2134
|
+
)
|
|
2135
|
+
const visuallyStill = maestro ? maestroStill :
|
|
2071
2136
|
strictOk &&
|
|
2072
2137
|
!snapshot.animating &&
|
|
2073
2138
|
!snapshot.layoutDirty &&
|
|
@@ -2095,7 +2160,7 @@ async function waitForSootsimIdle({
|
|
|
2095
2160
|
// grace (continuous background traffic must not block forever).
|
|
2096
2161
|
if (
|
|
2097
2162
|
stillProven() &&
|
|
2098
|
-
(motionOnly || networkQuiet || clockNow() - visuallyStillSince >= networkQuietGraceMs)
|
|
2163
|
+
(maestro || motionOnly || networkQuiet || clockNow() - visuallyStillSince >= networkQuietGraceMs)
|
|
2099
2164
|
) {
|
|
2100
2165
|
if (transitionWait) {
|
|
2101
2166
|
await transitionWait
|
|
@@ -2107,6 +2172,7 @@ async function waitForSootsimIdle({
|
|
|
2107
2172
|
stillPolls = []
|
|
2108
2173
|
visuallyStillSince = 0
|
|
2109
2174
|
}
|
|
2175
|
+
lastNativeHierarchy = snapshot.nativeHierarchy
|
|
2110
2176
|
lastLayout = snapshot.layout
|
|
2111
2177
|
lastScreenPixels = snapshot.screenPixels
|
|
2112
2178
|
lastRequestTotal = snapshot.requestTotal
|
|
@@ -2129,20 +2195,21 @@ async function waitForSootsimIdle({
|
|
|
2129
2195
|
}
|
|
2130
2196
|
const last = report.snapshot
|
|
2131
2197
|
const blockers = []
|
|
2132
|
-
if (strict && !last.renderStatsAvailable) blockers.push('render stats unavailable')
|
|
2133
|
-
if (last.animating) blockers.push('animations still running')
|
|
2134
|
-
if (last.shellAnimating) blockers.push('shell animations still running (' + last.shellBlockers + ')')
|
|
2135
|
-
if (last.layoutDirty) blockers.push('layout still dirty')
|
|
2136
|
-
if (last.appCommits !== null && report.appCommits !== null && last.appCommits !== report.appCommits) blockers.push('layout commits still landing')
|
|
2137
|
-
if (last.nodeCount !== null && report.nodeCount !== null && last.nodeCount !== report.nodeCount) blockers.push('node count still changing')
|
|
2138
|
-
if (!motionOnly && last.pendingFetches > 0) blockers.push(last.pendingFetches + ' image fetches pending')
|
|
2198
|
+
if (!maestro && strict && !last.renderStatsAvailable) blockers.push('render stats unavailable')
|
|
2199
|
+
if (!maestro && last.animating) blockers.push('animations still running')
|
|
2200
|
+
if (!maestro && last.shellAnimating) blockers.push('shell animations still running (' + last.shellBlockers + ')')
|
|
2201
|
+
if (!maestro && last.layoutDirty) blockers.push('layout still dirty')
|
|
2202
|
+
if (!maestro && last.appCommits !== null && report.appCommits !== null && last.appCommits !== report.appCommits) blockers.push('layout commits still landing')
|
|
2203
|
+
if (!maestro && last.nodeCount !== null && report.nodeCount !== null && last.nodeCount !== report.nodeCount) blockers.push('node count still changing')
|
|
2204
|
+
if (!maestro && !motionOnly && last.pendingFetches > 0) blockers.push(last.pendingFetches + ' image fetches pending')
|
|
2139
2205
|
if (last.workerBusy) blockers.push('a render worker too busy to report')
|
|
2140
2206
|
if (last.webViewStarting) blockers.push(last.webViewBlocker || 'webView still starting')
|
|
2141
|
-
if (last.layout !== report.layout) blockers.push('layout still changing')
|
|
2207
|
+
if (!maestro && last.layout !== report.layout) blockers.push('layout still changing')
|
|
2208
|
+
if (maestro && !hierarchyUnchanged(last.nativeHierarchy, report.nativeHierarchy)) blockers.push(last.nativeHierarchy === null ? 'native accessibility hierarchy unavailable' : 'native accessibility hierarchy still changing')
|
|
2142
2209
|
if (paintStill && !last.screenPixels) blockers.push('screen pixels unavailable')
|
|
2143
2210
|
else if (paintStill && !screenUnchanged(last.screenPixels, report.screenPixels)) blockers.push('screen pixels still changing')
|
|
2144
|
-
if (!motionOnly && last.requestInFlight > 0) blockers.push(last.requestInFlight + ' requests in flight')
|
|
2145
|
-
else if (!motionOnly && last.requestTotal !== report.requestTotal) blockers.push('new requests issued')
|
|
2211
|
+
if (!maestro && !motionOnly && last.requestInFlight > 0) blockers.push(last.requestInFlight + ' requests in flight')
|
|
2212
|
+
else if (!maestro && !motionOnly && last.requestTotal !== report.requestTotal) blockers.push('new requests issued')
|
|
2146
2213
|
if (polls < 2) blockers.push('sim too busy to poll twice')
|
|
2147
2214
|
// the last read is still: motion ended too near the deadline for the
|
|
2148
2215
|
// stillness window to hold before it.
|
|
@@ -2184,8 +2251,10 @@ async function waitForSootsimIdle({
|
|
|
2184
2251
|
async function waitReadyAndSettle(bridge, timeoutMs, options = {}) {
|
|
2185
2252
|
const start = Date.now();
|
|
2186
2253
|
const remaining = () => Math.max(0, start + timeoutMs - Date.now());
|
|
2254
|
+
let accepted = null;
|
|
2187
2255
|
const result = (status, idle2) => ({
|
|
2188
2256
|
status,
|
|
2257
|
+
accepted,
|
|
2189
2258
|
idle: idle2,
|
|
2190
2259
|
elapsedMs: Date.now() - start
|
|
2191
2260
|
});
|
|
@@ -2194,6 +2263,7 @@ async function waitReadyAndSettle(bridge, timeoutMs, options = {}) {
|
|
|
2194
2263
|
idle = null;
|
|
2195
2264
|
const ready = await inspectWaitReady(bridge, remaining(), options);
|
|
2196
2265
|
if (!ready.ready) return result(ready, idle);
|
|
2266
|
+
accepted = ready;
|
|
2197
2267
|
idle = await waitForSootsimIdle({
|
|
2198
2268
|
bridge,
|
|
2199
2269
|
simId: options.simId,
|
|
@@ -2617,6 +2687,20 @@ async function openAppUrl(bridge, target) {
|
|
|
2617
2687
|
return normalized;
|
|
2618
2688
|
}
|
|
2619
2689
|
|
|
2690
|
+
// src/launchMetadataContract.ts
|
|
2691
|
+
var LAUNCH_METADATA_QUERY_PARAM = "launchMetadata";
|
|
2692
|
+
var LAUNCH_METADATA_KEYS = [
|
|
2693
|
+
"appFonts",
|
|
2694
|
+
"appSplash",
|
|
2695
|
+
"appCatalog",
|
|
2696
|
+
"nativeAppIdentity",
|
|
2697
|
+
"nativeFirebase",
|
|
2698
|
+
"appManifest",
|
|
2699
|
+
"packageVersions",
|
|
2700
|
+
"dotEnv",
|
|
2701
|
+
"env"
|
|
2702
|
+
];
|
|
2703
|
+
|
|
2620
2704
|
// src/sleep.ts
|
|
2621
2705
|
function sleep(ms) {
|
|
2622
2706
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -2685,10 +2769,8 @@ var TARGET_SEARCH_PARAMS = [
|
|
|
2685
2769
|
"open",
|
|
2686
2770
|
"port",
|
|
2687
2771
|
"inspectOpen",
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
"appSplash",
|
|
2691
|
-
"appCatalog"
|
|
2772
|
+
...LAUNCH_METADATA_KEYS,
|
|
2773
|
+
LAUNCH_METADATA_QUERY_PARAM
|
|
2692
2774
|
];
|
|
2693
2775
|
function buildSimOpenUrl(currentUrl, target) {
|
|
2694
2776
|
const url = new URL(currentUrl);
|
|
@@ -3144,7 +3226,6 @@ var SimClient = class {
|
|
|
3144
3226
|
layoutGetNativeEval,
|
|
3145
3227
|
rankInteractive,
|
|
3146
3228
|
readyProbeHasContent,
|
|
3147
|
-
readyProbeHasTargetContent,
|
|
3148
3229
|
readyProbeIsReady,
|
|
3149
3230
|
resolveFindMode,
|
|
3150
3231
|
resolveMaxMsFlag,
|