peachsim 0.1.1640 → 0.1.1644
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 +5 -0
- package/cli/app-bundle-resources.ts +57 -0
- package/cli/app-stubs.ts +38 -13
- package/cli/cloud-session.ts +16 -0
- package/cli/commands/assert.ts +17 -4
- package/cli/commands/box.ts +33 -19
- package/cli/commands/control.ts +12 -0
- package/cli/commands/doctor.ts +24 -15
- package/cli/commands/inspect/actions.ts +3 -2
- package/cli/commands/inspect/core.ts +0 -8
- package/cli/commands/inspect.ts +0 -14
- package/cli/commands/platform.ts +2 -1
- package/cli/commands/record.ts +64 -28
- package/cli/commands/swift.ts +10 -2
- package/cli/commands/three-mode.ts +4 -38
- package/cli/commands/upload.ts +8 -8
- package/cli/drivers/playwright.ts +39 -0
- package/cli/flow-live-status.ts +0 -2
- package/cli/main.ts +1 -1
- package/cli/outbound-endpoints.ts +4 -2
- package/cli/ws-bridge.ts +0 -6
- package/detox/attempt-events.cjs +41 -5
- package/detox/index.ts +155 -109
- package/detox/jest-environment.cjs +5 -1
- package/detox/motion-change.cjs +7 -5
- package/detox/motion-change.d.cts +1 -0
- package/detox/motion-leg-starts.cjs +20 -0
- package/detox/motion-leg-starts.d.cts +8 -0
- 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 +12 -3
- 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 +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +2671 -1833
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +578 -130
- package/dist-lib/detox/jest-environment.cjs +5 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +2374 -1554
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +2968 -2141
- 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 +132 -57
- 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 +14 -6
- package/dist-lib/menu.mjs +14 -6
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +120 -53
- package/dist-lib/native-app-identity-project.cjs +96 -44
- package/dist-lib/native-app-identity-project.mjs +95 -44
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- 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 +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +5 -3
- package/dist-lib/sdk.mjs +5 -3
- package/dist-lib/skills.cjs +3932 -2868
- package/dist-lib/swift-compile.cjs +587 -374
- package/dist-lib/swift-compile.mjs +595 -369
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +8 -3
- package/dist-lib/swift-submit.mjs +8 -3
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +2263 -1467
- package/dist-lib/vite.cjs +1 -1
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/package.json +1 -1
- package/src/appBundleResourcesContract.ts +51 -0
- package/src/backend-origin.ts +11 -1
- package/src/host/bridge-host.ts +17 -11
- package/src/host/staged-host-files.ts +28 -4
- package/src/launchMetadataContract.ts +1 -0
- package/src/menu.ts +12 -4
- package/src/native-app-identity-project.ts +47 -38
- package/src/react-native-bridge-modules.ts +89 -76
- package/src/runtime-delivery.ts +6 -7
- package/src/swift-compile.ts +150 -112
- package/src/swift-compiler-digest.ts +5 -3
- package/src/swift-distribution.lock.json +9 -8
- package/src/swift-host.ts +6 -4
- package/src/swift-objc-compile.ts +416 -194
- package/src/swift-objc-metadata.ts +204 -0
- package/src/swift-objc-sdk.ts +195 -2
- package/src/swift-package-manifest.ts +6 -3
- package/src/swift-root-lock.ts +302 -0
- package/src/swift-submit.ts +15 -2
- package/src/swiftContract.ts +12 -0
- package/src/xcode-project.ts +77 -7
package/dist-lib/detox/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1644 | (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;
|
|
@@ -1247,7 +1247,7 @@ var require_motion_change = __commonJS({
|
|
|
1247
1247
|
var GESTURE_MOTION_MIN_TRANSITIONS = 120;
|
|
1248
1248
|
var GESTURE_TRAVEL_RATIO_RANGE = Object.freeze({ min: 0.8, max: 1.25 });
|
|
1249
1249
|
var GESTURE_QUARTER_TRAVEL_RATIO_RANGE = Object.freeze({ min: 0.5, max: 2 });
|
|
1250
|
-
function evaluateMotionTransitions(transitions, oracleTarget) {
|
|
1250
|
+
function evaluateMotionTransitions(transitions, oracleTarget, repeating) {
|
|
1251
1251
|
const key = (transition) => `${transition.fromLabel}\0${transition.toLabel}`;
|
|
1252
1252
|
const oracle = transitions.filter((transition) => transition.target === oracleTarget);
|
|
1253
1253
|
const sootsim = transitions.filter((transition) => transition.target === "sootsim");
|
|
@@ -1274,7 +1274,7 @@ var require_motion_change = __commonJS({
|
|
|
1274
1274
|
Math.floor(list.length * index / 4),
|
|
1275
1275
|
Math.floor(list.length * (index + 1) / 4)
|
|
1276
1276
|
);
|
|
1277
|
-
const follows = oracle.length >= GESTURE_MOTION_MIN_TRANSITIONS ? within(travel(sootsim), travel(oracle), GESTURE_TRAVEL_RATIO_RANGE) && [0, 1, 2, 3].every(
|
|
1277
|
+
const follows = !repeating && oracle.length >= GESTURE_MOTION_MIN_TRANSITIONS ? within(travel(sootsim), travel(oracle), GESTURE_TRAVEL_RATIO_RANGE) && [0, 1, 2, 3].every(
|
|
1278
1278
|
(index) => within(
|
|
1279
1279
|
travel(quarter(sootsim, index)),
|
|
1280
1280
|
travel(quarter(oracle, index)),
|
|
@@ -1502,7 +1502,7 @@ var require_motion_change = __commonJS({
|
|
|
1502
1502
|
const first = stages[0];
|
|
1503
1503
|
const last = stages[stages.length - 1];
|
|
1504
1504
|
return {
|
|
1505
|
-
passed: stages.length > 0 && first.passedThreshold && last.passedThreshold && (!needsIntermediates || movingPassed >= PUBLICATION_MIN_INTERMEDIATE_PASSES) && cyclePeriods.every((period) => period.passed) && evaluateMotionTransitions(transitions, oracleTarget).passed,
|
|
1505
|
+
passed: stages.length > 0 && first.passedThreshold && last.passedThreshold && (!needsIntermediates || movingPassed >= PUBLICATION_MIN_INTERMEDIATE_PASSES) && cyclePeriods.every((period) => period.passed) && evaluateMotionTransitions(transitions, oracleTarget, cyclePeriods.length > 0).passed,
|
|
1506
1506
|
cyclePeriods,
|
|
1507
1507
|
intermediateCount: intermediates.length,
|
|
1508
1508
|
intermediatePassed,
|
|
@@ -1524,6 +1524,26 @@ var require_motion_change = __commonJS({
|
|
|
1524
1524
|
}
|
|
1525
1525
|
});
|
|
1526
1526
|
|
|
1527
|
+
// ../peach-engine/src/capture/frame-offset.ts
|
|
1528
|
+
var frame_offset_exports = {};
|
|
1529
|
+
__export(frame_offset_exports, {
|
|
1530
|
+
frameIndexForOffset: () => frameIndexForOffset
|
|
1531
|
+
});
|
|
1532
|
+
function frameIndexForOffset(offsetMs, frameIntervalMs) {
|
|
1533
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0)
|
|
1534
|
+
throw new Error(`capture: invalid stage offset ${offsetMs}`);
|
|
1535
|
+
if (!Number.isFinite(frameIntervalMs) || frameIntervalMs <= 0)
|
|
1536
|
+
throw new Error(`capture: invalid frame interval ${frameIntervalMs}`);
|
|
1537
|
+
const quotient = offsetMs / frameIntervalMs;
|
|
1538
|
+
const nearest = Math.round(quotient);
|
|
1539
|
+
return Math.abs(quotient - nearest) <= Number.EPSILON * Math.abs(quotient) ? nearest : Math.ceil(quotient);
|
|
1540
|
+
}
|
|
1541
|
+
var init_frame_offset = __esm({
|
|
1542
|
+
"../peach-engine/src/capture/frame-offset.ts"() {
|
|
1543
|
+
"use strict";
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1527
1547
|
// ../../scripts/lib/remote-build-delivery.ts
|
|
1528
1548
|
var remote_build_delivery_exports = {};
|
|
1529
1549
|
__export(remote_build_delivery_exports, {
|
|
@@ -1655,6 +1675,7 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1655
1675
|
"../peach-conformance/e2e/utils/native-frame-receipt.cjs"(exports2, module2) {
|
|
1656
1676
|
"use strict";
|
|
1657
1677
|
var { createHash } = require("node:crypto");
|
|
1678
|
+
var { frameIndexForOffset: frameIndexForOffset2 } = (init_frame_offset(), __toCommonJS(frame_offset_exports));
|
|
1658
1679
|
function nativeVideoFrameReceipt(recordingSha256, sourceFrameIndex, ptsSeconds, inputClock) {
|
|
1659
1680
|
if (!/^[a-f0-9]{64}$/.test(recordingSha256)) throw new Error("invalid recording hash");
|
|
1660
1681
|
if (!Number.isSafeInteger(sourceFrameIndex) || sourceFrameIndex < 0)
|
|
@@ -1735,25 +1756,29 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1735
1756
|
if (output !== bytes.length) throw new Error("incomplete native framebuffer");
|
|
1736
1757
|
return bytes;
|
|
1737
1758
|
}
|
|
1738
|
-
function
|
|
1759
|
+
function nativeFramebufferPixels(receipt, bytes) {
|
|
1739
1760
|
requireNativeRawFrame(receipt, bytes);
|
|
1740
|
-
const { PNG } = require("pngjs");
|
|
1741
|
-
const fs3 = require("node:fs");
|
|
1742
1761
|
const { width, height, stride } = receipt.rawFramebuffer;
|
|
1743
|
-
|
|
1744
|
-
throw new Error("native framebuffer and screenshot dimensions differ");
|
|
1745
|
-
const png = new PNG({ width, height });
|
|
1762
|
+
const data = Buffer.allocUnsafe(width * height * 4);
|
|
1746
1763
|
for (let y = 0; y < height; y++) {
|
|
1747
1764
|
for (let x = 0; x < width; x++) {
|
|
1748
1765
|
const source = y * stride + x * 4;
|
|
1749
1766
|
const destination = (y * width + x) * 4;
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1767
|
+
data[destination] = bytes[source + 2];
|
|
1768
|
+
data[destination + 1] = bytes[source + 1];
|
|
1769
|
+
data[destination + 2] = bytes[source];
|
|
1770
|
+
data[destination + 3] = bytes[source + 3];
|
|
1754
1771
|
}
|
|
1755
1772
|
}
|
|
1756
|
-
|
|
1773
|
+
return { width, height, data };
|
|
1774
|
+
}
|
|
1775
|
+
function writeNativeFramebufferPng(receipt, bytes, outputPath, expectedSize) {
|
|
1776
|
+
const { PNG } = require("pngjs");
|
|
1777
|
+
const fs3 = require("node:fs");
|
|
1778
|
+
const pixels = nativeFramebufferPixels(receipt, bytes);
|
|
1779
|
+
if (pixels.width !== expectedSize.width || pixels.height !== expectedSize.height)
|
|
1780
|
+
throw new Error("native framebuffer and screenshot dimensions differ");
|
|
1781
|
+
fs3.writeFileSync(outputPath, PNG.sync.write(pixels));
|
|
1757
1782
|
return receipt;
|
|
1758
1783
|
}
|
|
1759
1784
|
module2.exports = {
|
|
@@ -1761,6 +1786,7 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1761
1786
|
requireNativeProducerFrame,
|
|
1762
1787
|
requireNativeRawFrame,
|
|
1763
1788
|
decodeNativeFramebuffer,
|
|
1789
|
+
nativeFramebufferPixels,
|
|
1764
1790
|
writeNativeFramebufferPng
|
|
1765
1791
|
};
|
|
1766
1792
|
async function buildNativeFrameConsumer({ root, cacheRoot, waitForAdmission = false }) {
|
|
@@ -1896,7 +1922,10 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1896
1922
|
ticks.push({
|
|
1897
1923
|
targetTimestampSeconds: row.targetTimestampSeconds,
|
|
1898
1924
|
timestampSeconds: row.timestampSeconds,
|
|
1899
|
-
durationSeconds: row.durationSeconds
|
|
1925
|
+
durationSeconds: row.durationSeconds,
|
|
1926
|
+
clockBridge: row.clockBridge,
|
|
1927
|
+
callbackThread: row.callbackThread,
|
|
1928
|
+
callbackWasMainThread: row.callbackWasMainThread
|
|
1900
1929
|
});
|
|
1901
1930
|
}
|
|
1902
1931
|
if (ticks.length < 2)
|
|
@@ -1910,7 +1939,7 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1910
1939
|
};
|
|
1911
1940
|
}
|
|
1912
1941
|
module2.exports.nativeUIFrameClockReceipt = nativeUIFrameClockReceipt;
|
|
1913
|
-
function pairedNativeWorkletFrameClock(root, lifecycle) {
|
|
1942
|
+
function pairedNativeWorkletFrameClock(root, lifecycle, seed = "ui-frame-after-delivered-touch-up") {
|
|
1914
1943
|
const { boundText } = require_attempt_events();
|
|
1915
1944
|
if (!lifecycle || typeof lifecycle !== "object" || Array.isArray(lifecycle))
|
|
1916
1945
|
throw new Error("paired worklets clock needs a retained native lifecycle");
|
|
@@ -1940,6 +1969,42 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1940
1969
|
)
|
|
1941
1970
|
)
|
|
1942
1971
|
);
|
|
1972
|
+
if (seed === "delivered-touch-drag") {
|
|
1973
|
+
const down = input.events[0];
|
|
1974
|
+
const up2 = input.events.at(-1);
|
|
1975
|
+
if (input.status !== "witnessed" || down?.phase !== 0 || up2?.phase !== 3 || !input.events.slice(1, -1).every((event) => event.phase === 1) || input.events.length < 3 || input.events.some(
|
|
1976
|
+
(event, index) => index > 0 && (event.deliveredAtSeconds < input.events[index - 1].deliveredAtSeconds || event.touchTimestampSeconds < input.events[index - 1].touchTimestampSeconds)
|
|
1977
|
+
))
|
|
1978
|
+
throw new Error(
|
|
1979
|
+
"paired drag needs one ordered delivered UIKit down/moves/up sequence"
|
|
1980
|
+
);
|
|
1981
|
+
if (!clock.ticks.some((tick2) => tick2.timestampSeconds < down.deliveredAtSeconds) || !clock.ticks.some((tick2) => tick2.timestampSeconds > up2.deliveredAtSeconds))
|
|
1982
|
+
throw new Error("native UI clock does not bracket the delivered drag");
|
|
1983
|
+
return {
|
|
1984
|
+
provenance: {
|
|
1985
|
+
sourceReceipt: receipt.sourceReceipt,
|
|
1986
|
+
sourceReceiptSha256: receipt.sourceReceiptSha256,
|
|
1987
|
+
inputReceipt: inputReceipt.sourceReceipt,
|
|
1988
|
+
inputReceiptSha256: inputReceipt.sourceReceiptSha256,
|
|
1989
|
+
alignment: "delivered-touch-drag",
|
|
1990
|
+
stageOffsetBasis: "declared-from-delivered-down",
|
|
1991
|
+
deliveredTouchDownSeconds: down.deliveredAtSeconds
|
|
1992
|
+
},
|
|
1993
|
+
hold: {
|
|
1994
|
+
x: down.x,
|
|
1995
|
+
y: down.y,
|
|
1996
|
+
release: { x: up2.x, y: up2.y },
|
|
1997
|
+
durationMs: (up2.deliveredAtSeconds - down.deliveredAtSeconds) * 1e3,
|
|
1998
|
+
eventDurationMs: (up2.touchTimestampSeconds - down.touchTimestampSeconds) * 1e3,
|
|
1999
|
+
moves: input.events.slice(1, -1).map((event) => ({
|
|
2000
|
+
x: event.x,
|
|
2001
|
+
y: event.y,
|
|
2002
|
+
afterMs: (event.deliveredAtSeconds - down.deliveredAtSeconds) * 1e3,
|
|
2003
|
+
eventAfterMs: (event.touchTimestampSeconds - down.touchTimestampSeconds) * 1e3
|
|
2004
|
+
}))
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
1943
2008
|
const up = input.events.find((event) => event.phase === 3);
|
|
1944
2009
|
if (input.status !== "witnessed" || !up)
|
|
1945
2010
|
throw new Error("paired worklets clock needs a delivered UIKit touch-up witness");
|
|
@@ -1965,6 +2030,323 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1965
2030
|
};
|
|
1966
2031
|
}
|
|
1967
2032
|
module2.exports.pairedNativeWorkletFrameClock = pairedNativeWorkletFrameClock;
|
|
2033
|
+
function nativeGradingPlan(sourceRoot, declaration) {
|
|
2034
|
+
const fs3 = require("node:fs"), path5 = require("node:path");
|
|
2035
|
+
const catalogs = [
|
|
2036
|
+
["packages/peach-conformance/src/cases/catalog.cjs", "CONFORMANCE_CASE_CATALOG"],
|
|
2037
|
+
[
|
|
2038
|
+
"packages/peach-library-conformance/src/cases/catalog.cjs",
|
|
2039
|
+
"LIBRARY_CONFORMANCE_CASE_CATALOG"
|
|
2040
|
+
]
|
|
2041
|
+
];
|
|
2042
|
+
let catalog, catalogPath;
|
|
2043
|
+
for (const [relative, key] of catalogs) {
|
|
2044
|
+
const entries = require(path5.join(sourceRoot, relative))[key];
|
|
2045
|
+
const entry = entries.find((item) => item.name === declaration.caseName);
|
|
2046
|
+
const checkpoint = entry?.checkpoints?.find(
|
|
2047
|
+
(item) => item.id === declaration.checkpoint
|
|
2048
|
+
);
|
|
2049
|
+
if (checkpoint) {
|
|
2050
|
+
catalog = checkpoint;
|
|
2051
|
+
catalogPath = relative;
|
|
2052
|
+
break;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
if (!catalog || !Array.isArray(catalog.stages) || JSON.stringify(catalog.stages) !== JSON.stringify(declaration.stages.map((stage) => stage.label)))
|
|
2056
|
+
throw new Error(
|
|
2057
|
+
"native sampled grading plan must include every catalog stage in order"
|
|
2058
|
+
);
|
|
2059
|
+
if (JSON.stringify(catalog.motionCadence ?? null) !== JSON.stringify(declaration.cadence))
|
|
2060
|
+
throw new Error("native sampled grading plan changed the catalog cadence");
|
|
2061
|
+
if (declaration.anchorPlan?.stages?.length !== declaration.stages.length || declaration.stages.some(
|
|
2062
|
+
(stage, index) => declaration.anchorPlan.stages[index].anchorId !== (stage.anchor?.id ?? "transition")
|
|
2063
|
+
))
|
|
2064
|
+
throw new Error("native sampled grading plan changed a declared stage anchor");
|
|
2065
|
+
if (declaration.stages.some(
|
|
2066
|
+
(stage) => !Number.isFinite(stage.offsetMs) || stage.offsetMs < 0
|
|
2067
|
+
))
|
|
2068
|
+
throw new Error("native sampled grading plan has invalid stage offsets");
|
|
2069
|
+
return {
|
|
2070
|
+
...declaration,
|
|
2071
|
+
contract: "continuous-catalog-stage-window-v1",
|
|
2072
|
+
catalogPath,
|
|
2073
|
+
catalogSha256: createHash("sha256").update(fs3.readFileSync(path5.join(sourceRoot, catalogPath))).digest("hex")
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
module2.exports.nativeGradingPlan = nativeGradingPlan;
|
|
2077
|
+
function tickValue(value, label = "native sampled recording") {
|
|
2078
|
+
if (typeof value !== "string" || !/^\d+$/.test(value))
|
|
2079
|
+
throw new Error(`${label} has invalid mach ticks`);
|
|
2080
|
+
return BigInt(value);
|
|
2081
|
+
}
|
|
2082
|
+
function nativeResolvedGradingWindow(plan, resolution, frames, markers) {
|
|
2083
|
+
const markerStart = tickValue(markers?.machStart, "native grade-start marker");
|
|
2084
|
+
const markerEnd = tickValue(markers?.machEnd, "native grade-end marker");
|
|
2085
|
+
if (!resolution || !Array.isArray(resolution.stages) || resolution.stages.length !== plan.stages.length || resolution.stages.some(
|
|
2086
|
+
(stage, index) => stage.label !== plan.stages[index].label || stage.offsetMs !== plan.stages[index].offsetMs || stage.anchorId !== plan.anchorPlan.stages[index].anchorId || !Number.isFinite(stage.atSec) || stage.atSec < 0
|
|
2087
|
+
) || !Number.isFinite(resolution.triggerInVideoSec) || resolution.triggerInVideoSec < 0)
|
|
2088
|
+
throw new Error("native sampled window resolution dropped or changed declared stages");
|
|
2089
|
+
for (const stage of resolution.stages) {
|
|
2090
|
+
const offset = stage.touchOffsetMs ?? stage.offsetMs;
|
|
2091
|
+
const expected = stage.basisSec + frameIndexForOffset2(offset, 1e3 / 60) * (1e3 / 60) / 1e3;
|
|
2092
|
+
if (!Number.isFinite(stage.basisSec) || stage.basisSec < 0 || !Number.isFinite(offset) || offset < 0 || !stage.initialBeforeTrigger && stage.atSec !== expected || stage.anchorId !== "transition" && offset !== stage.offsetMs)
|
|
2093
|
+
throw new Error("native sampled window changed the declared stage timing");
|
|
2094
|
+
}
|
|
2095
|
+
const origin = frames[0]?.consumerArrival;
|
|
2096
|
+
if (!origin || !Number.isSafeInteger(origin.timebaseNumer) || origin.timebaseNumer <= 0 || !Number.isSafeInteger(origin.timebaseDenom) || origin.timebaseDenom <= 0)
|
|
2097
|
+
throw new Error("native sampled window has no source clock");
|
|
2098
|
+
const atTicks = (seconds) => {
|
|
2099
|
+
const nanos = Math.ceil(seconds * 1e9);
|
|
2100
|
+
if (!Number.isSafeInteger(nanos) || nanos < 0)
|
|
2101
|
+
throw new Error("native sampled window exceeds exact source range");
|
|
2102
|
+
return BigInt(origin.machTicks) + (BigInt(nanos) * BigInt(origin.timebaseDenom) + BigInt(origin.timebaseNumer) - 1n) / BigInt(origin.timebaseNumer);
|
|
2103
|
+
};
|
|
2104
|
+
const endSeconds = Math.max(
|
|
2105
|
+
...resolution.stages.map((stage) => stage.atSec),
|
|
2106
|
+
...plan.cadence ? [resolution.triggerInVideoSec + 1.9] : []
|
|
2107
|
+
);
|
|
2108
|
+
const firstAnchor = atTicks(
|
|
2109
|
+
Math.min(
|
|
2110
|
+
resolution.triggerInVideoSec,
|
|
2111
|
+
...resolution.stages.map((stage) => Math.min(stage.atSec, stage.basisSec))
|
|
2112
|
+
)
|
|
2113
|
+
);
|
|
2114
|
+
const start = firstAnchor < markerStart ? firstAnchor : markerStart;
|
|
2115
|
+
const end = atTicks(endSeconds) + 1n;
|
|
2116
|
+
if (start < BigInt(origin.machTicks) || end > markerEnd || end <= start)
|
|
2117
|
+
throw new Error("native recorder does not bracket the complete catalog stage window");
|
|
2118
|
+
return { machStart: String(start), machEnd: String(end) };
|
|
2119
|
+
}
|
|
2120
|
+
module2.exports.nativeResolvedGradingWindow = nativeResolvedGradingWindow;
|
|
2121
|
+
function nativeRefreshBoundaryWindows(ticks, { startSeconds, endSeconds }) {
|
|
2122
|
+
if (!Number.isFinite(startSeconds) || !Number.isFinite(endSeconds) || !(endSeconds > startSeconds))
|
|
2123
|
+
throw new Error("native sampled grading window is empty");
|
|
2124
|
+
const intervals = [], gaps = [];
|
|
2125
|
+
for (const [refresh, tick2] of ticks.entries()) {
|
|
2126
|
+
const start = tick2.timestampSeconds, end = tick2.targetTimestampSeconds;
|
|
2127
|
+
if (!(end > start)) throw new Error("native UI clock has a reversed refresh interval");
|
|
2128
|
+
if (end > startSeconds && start < endSeconds) intervals.push({ refresh, start, end });
|
|
2129
|
+
const next = ticks[refresh + 1];
|
|
2130
|
+
if (!next) continue;
|
|
2131
|
+
const rounding = 8 * Number.EPSILON * Math.max(1, Math.abs(end), Math.abs(next.timestampSeconds));
|
|
2132
|
+
if (Math.abs(end - next.timestampSeconds) <= rounding) continue;
|
|
2133
|
+
const gapStart = Math.min(end, next.timestampSeconds);
|
|
2134
|
+
const gapEnd = Math.max(end, next.timestampSeconds);
|
|
2135
|
+
gaps.push({
|
|
2136
|
+
afterRefresh: refresh,
|
|
2137
|
+
start: gapStart,
|
|
2138
|
+
end: gapEnd,
|
|
2139
|
+
intersectsGrading: gapEnd > startSeconds && gapStart < endSeconds
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
return {
|
|
2143
|
+
intervals,
|
|
2144
|
+
gaps,
|
|
2145
|
+
bracketed: intervals.length > 0 && intervals[0].start <= startSeconds && intervals.at(-1).end >= endSeconds
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
module2.exports.nativeRefreshBoundaryWindows = nativeRefreshBoundaryWindows;
|
|
2149
|
+
function nativeRefreshSamples({
|
|
2150
|
+
frames,
|
|
2151
|
+
reads: observations,
|
|
2152
|
+
clockBytes,
|
|
2153
|
+
finalFrames,
|
|
2154
|
+
finalSurfaceStatus = "settled",
|
|
2155
|
+
grading
|
|
2156
|
+
}) {
|
|
2157
|
+
const clock = nativeUIFrameClockReceipt(clockBytes);
|
|
2158
|
+
const movingFinalSurface = finalSurfaceStatus === "moving" && finalFrames.length === 0;
|
|
2159
|
+
if (!frames.length || !observations.length || !movingFinalSurface && finalFrames.length < 3)
|
|
2160
|
+
throw new Error("native sampled recording has no observed stable final surface");
|
|
2161
|
+
const origin = frames[0].consumerArrival;
|
|
2162
|
+
if (origin?.domain !== "host-mach-absolute-time" || !/^[0-9a-f-]{36}$/i.test(origin.bootSessionUUID ?? "") || !Number.isSafeInteger(origin.timebaseNumer) || origin.timebaseNumer <= 0 || !Number.isSafeInteger(origin.timebaseDenom) || origin.timebaseDenom <= 0)
|
|
2163
|
+
throw new Error("native sampled recording has no authenticated host clock");
|
|
2164
|
+
const secondsPerTick = origin.timebaseNumer / origin.timebaseDenom / 1e9;
|
|
2165
|
+
const bridgeOrigin = tickValue(clock.ticks[0].clockBridge?.machBefore);
|
|
2166
|
+
const elapsed = (value) => {
|
|
2167
|
+
const ticks = tickValue(value) - bridgeOrigin;
|
|
2168
|
+
if (ticks < -BigInt(Number.MAX_SAFE_INTEGER) || ticks > BigInt(Number.MAX_SAFE_INTEGER))
|
|
2169
|
+
throw new Error("native clock bridge exceeds exact mach range");
|
|
2170
|
+
return Number(ticks) * secondsPerTick;
|
|
2171
|
+
};
|
|
2172
|
+
let lowerOffset = -Infinity;
|
|
2173
|
+
let upperOffset = Infinity;
|
|
2174
|
+
for (const tick2 of clock.ticks) {
|
|
2175
|
+
if (tick2.callbackThread !== "dedicated-user-interactive-run-loop" || tick2.callbackWasMainThread !== false)
|
|
2176
|
+
throw new Error(
|
|
2177
|
+
"native UI clock callback did not run on its independent recorder thread"
|
|
2178
|
+
);
|
|
2179
|
+
const bridge = tick2.clockBridge;
|
|
2180
|
+
if (bridge?.bootSessionUUID !== origin.bootSessionUUID || bridge.timebaseNumer !== origin.timebaseNumer || bridge.timebaseDenom !== origin.timebaseDenom || !Number.isFinite(bridge.mediaSeconds))
|
|
2181
|
+
throw new Error("native clock bridge kernel identity or timebase mismatch");
|
|
2182
|
+
const before = elapsed(bridge.machBefore), after = elapsed(bridge.machAfter);
|
|
2183
|
+
if (after < before) throw new Error("native clock bridge has reversed bracket");
|
|
2184
|
+
const rounding = 8 * Number.EPSILON * Math.max(1, Math.abs(bridge.mediaSeconds));
|
|
2185
|
+
lowerOffset = Math.max(lowerOffset, bridge.mediaSeconds - after - rounding);
|
|
2186
|
+
upperOffset = Math.min(upperOffset, bridge.mediaSeconds - before + rounding);
|
|
2187
|
+
}
|
|
2188
|
+
if (lowerOffset > upperOffset) throw new Error("native clock bridge brackets disagree");
|
|
2189
|
+
const sourceFrames = new Map(
|
|
2190
|
+
frames.map((frame, index) => [frame.callbackIndex, { frame, index }])
|
|
2191
|
+
);
|
|
2192
|
+
if (sourceFrames.size !== frames.length)
|
|
2193
|
+
throw new Error("native source callbacks repeat");
|
|
2194
|
+
const preservedEnds = frames.map((frame) => tickValue(frame.readWindow?.machEnd));
|
|
2195
|
+
if (preservedEnds.some((end, index) => index > 0 && end <= preservedEnds[index - 1]))
|
|
2196
|
+
throw new Error("native preserved source copy windows are not increasing");
|
|
2197
|
+
let latestPreserved = -1;
|
|
2198
|
+
let previousEnd = -Infinity;
|
|
2199
|
+
const reads = observations.map((frame) => {
|
|
2200
|
+
const readEnd = tickValue(frame.readWindow?.machEnd);
|
|
2201
|
+
while (latestPreserved + 1 < frames.length && preservedEnds[latestPreserved + 1] <= readEnd)
|
|
2202
|
+
latestPreserved++;
|
|
2203
|
+
const reference = sourceFrames.get(frame.sourceCallbackIndex);
|
|
2204
|
+
if (!reference || reference.index !== latestPreserved || reference.frame.rawPath !== frame.rawPath || reference.frame.rawFramebuffer?.sha256 !== frame.rawFramebuffer?.sha256 || reference.frame.rawFramebuffer?.width !== frame.rawFramebuffer?.width || reference.frame.rawFramebuffer?.height !== frame.rawFramebuffer?.height || reference.frame.rawFramebuffer?.stride !== frame.rawFramebuffer?.stride || reference.frame.rawFramebuffer?.pixelFormat !== frame.rawFramebuffer?.pixelFormat || reference.frame.source?.screenId !== frame.source?.screenId || tickValue(reference.frame.readWindow?.machEnd) > tickValue(frame.readWindow?.machEnd))
|
|
2205
|
+
throw new Error("native sampled read has no matching immutable source pixels");
|
|
2206
|
+
const host = frame.consumerArrival;
|
|
2207
|
+
if (host?.bootSessionUUID !== origin.bootSessionUUID || host.domain !== origin.domain || host.timebaseNumer !== origin.timebaseNumer || host.timebaseDenom !== origin.timebaseDenom || !Number.isSafeInteger(frame.source?.seedBeforeRead) || frame.source.seedBeforeRead !== frame.source.seedAfterRead || frame.source.surfaceSeed !== frame.source.seedBeforeRead || frame.rawFramebuffer?.status !== "preserved" || !/^[a-f0-9]{64}$/.test(frame.rawFramebuffer.sha256))
|
|
2208
|
+
throw new Error("native sampled read is superseded or has a different clock");
|
|
2209
|
+
const start = elapsed(frame.readWindow?.machStart), end = elapsed(frame.readWindow?.machEnd);
|
|
2210
|
+
if (end < start || start < previousEnd || tickValue(frame.readWindow.machStart) < tickValue(host.machTicks))
|
|
2211
|
+
throw new Error("native sampled read has an invalid copy window");
|
|
2212
|
+
previousEnd = end;
|
|
2213
|
+
return {
|
|
2214
|
+
start: start + lowerOffset,
|
|
2215
|
+
end: end + upperOffset,
|
|
2216
|
+
sourceFrameIndex: reference.index,
|
|
2217
|
+
callbackIndex: frame.callbackIndex
|
|
2218
|
+
};
|
|
2219
|
+
});
|
|
2220
|
+
const gradedStart = elapsed(grading?.machStart) + lowerOffset;
|
|
2221
|
+
const gradedEnd = elapsed(grading?.machEnd) + upperOffset;
|
|
2222
|
+
if (!(gradedEnd > gradedStart))
|
|
2223
|
+
throw new Error("native sampled grading window is empty");
|
|
2224
|
+
const boundaries = nativeRefreshBoundaryWindows(clock.ticks, {
|
|
2225
|
+
startSeconds: gradedStart,
|
|
2226
|
+
endSeconds: gradedEnd
|
|
2227
|
+
});
|
|
2228
|
+
const uncovered = boundaries.gaps.find((gap) => gap.intersectsGrading);
|
|
2229
|
+
if (uncovered)
|
|
2230
|
+
throw new Error(
|
|
2231
|
+
`native UI clock has an uncovered refresh after ${uncovered.afterRefresh}`
|
|
2232
|
+
);
|
|
2233
|
+
if (!boundaries.bracketed)
|
|
2234
|
+
throw new Error("native UI clock does not bracket every graded refresh");
|
|
2235
|
+
const samples = [];
|
|
2236
|
+
let cursor = 0;
|
|
2237
|
+
for (const { refresh, start, end } of boundaries.intervals) {
|
|
2238
|
+
while (cursor < reads.length && reads[cursor].start < start) cursor++;
|
|
2239
|
+
if (cursor === reads.length || reads[cursor].end >= end)
|
|
2240
|
+
throw new Error(`native recording has no surface sampled during refresh ${refresh}`);
|
|
2241
|
+
samples.push({
|
|
2242
|
+
refresh,
|
|
2243
|
+
sourceFrameIndex: reads[cursor].sourceFrameIndex,
|
|
2244
|
+
observationCallbackIndex: reads[cursor].callbackIndex,
|
|
2245
|
+
evidence: `surface sampled during refresh ${refresh}`
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
const final = observations.at(-1);
|
|
2249
|
+
let previousFinalEnd = tickValue(final.readWindow.machEnd);
|
|
2250
|
+
for (const settled of finalFrames) {
|
|
2251
|
+
const host = settled.consumerArrival;
|
|
2252
|
+
const source = settled.source, raw = settled.rawFramebuffer;
|
|
2253
|
+
if (host?.bootSessionUUID !== origin.bootSessionUUID || host.domain !== origin.domain || host.timebaseNumer !== origin.timebaseNumer || host.timebaseDenom !== origin.timebaseDenom || !Number.isSafeInteger(source?.seedBeforeRead) || source.surfaceSeed !== source.seedBeforeRead || source.seedBeforeRead !== source.seedAfterRead || raw?.status !== "preserved" || raw.sha256 !== final.rawFramebuffer.sha256 || raw.width !== final.rawFramebuffer.width || raw.height !== final.rawFramebuffer.height || raw.stride !== final.rawFramebuffer.stride || raw.pixelFormat !== final.rawFramebuffer.pixelFormat || source.screenId !== final.source.screenId || tickValue(settled.readWindow?.machStart) <= previousFinalEnd || tickValue(settled.readWindow?.machEnd) < tickValue(settled.readWindow?.machStart))
|
|
2254
|
+
throw new Error(
|
|
2255
|
+
"native recording final surface is unobserved or mismatches stable post-settle reads"
|
|
2256
|
+
);
|
|
2257
|
+
previousFinalEnd = tickValue(settled.readWindow.machEnd);
|
|
2258
|
+
}
|
|
2259
|
+
return {
|
|
2260
|
+
contract: "per-refresh-sampled-surface-v1",
|
|
2261
|
+
samples,
|
|
2262
|
+
displayedWrite: "unknown: may differ from sampled write within refresh",
|
|
2263
|
+
limits: {
|
|
2264
|
+
clockDiscontinuities: boundaries.gaps,
|
|
2265
|
+
...movingFinalSurface ? { finalSurface: "moving" } : {}
|
|
2266
|
+
},
|
|
2267
|
+
bridge: { bootSessionUUID: origin.bootSessionUUID, lowerOffset, upperOffset }
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
module2.exports.nativeRefreshSamples = nativeRefreshSamples;
|
|
2271
|
+
function authenticateNativeRefreshSamples(root, sourceRoot, lifecycle) {
|
|
2272
|
+
const fs3 = require("node:fs");
|
|
2273
|
+
const path5 = require("node:path");
|
|
2274
|
+
const { boundText } = require_attempt_events();
|
|
2275
|
+
const native = lifecycle.nativeFramebuffer;
|
|
2276
|
+
if (!native || native.exitCode !== 0 || native.failure || native.sourceSha256 !== createHash("sha256").update(
|
|
2277
|
+
fs3.readFileSync(
|
|
2278
|
+
path5.join(sourceRoot, "scripts/debug/native-ios-frame-consumer.m")
|
|
2279
|
+
)
|
|
2280
|
+
).digest("hex"))
|
|
2281
|
+
throw new Error("native sampled recording has no source-matched consumer");
|
|
2282
|
+
const parse2 = (text) => text.split("\n").filter(Boolean).map(JSON.parse);
|
|
2283
|
+
const frames = parse2(
|
|
2284
|
+
boundText(root, native.receipt, native.receiptSha256, "native sampled reads")
|
|
2285
|
+
);
|
|
2286
|
+
const observations = parse2(
|
|
2287
|
+
boundText(root, native.reads, native.readsSha256, "native sampled copy windows")
|
|
2288
|
+
);
|
|
2289
|
+
const clock = lifecycle.uiFrameClock;
|
|
2290
|
+
const clockBytes = Buffer.from(
|
|
2291
|
+
boundText(
|
|
2292
|
+
root,
|
|
2293
|
+
clock?.sourceReceipt,
|
|
2294
|
+
clock?.sourceReceiptSha256,
|
|
2295
|
+
"native sampled UI clock and bridge"
|
|
2296
|
+
)
|
|
2297
|
+
);
|
|
2298
|
+
const finalSurfaceStatus = lifecycle.finalFrameProof?.status;
|
|
2299
|
+
if (!Array.isArray(native.finalReads) || native.finalReads.length < 3 && !(finalSurfaceStatus === "moving" && native.finalReads.length === 0))
|
|
2300
|
+
throw new Error("native sampled recording has no stable post-settle reads");
|
|
2301
|
+
if (new Set(native.finalReads.map((receipt) => receipt.path)).size !== native.finalReads.length)
|
|
2302
|
+
throw new Error("native sampled end-state receipts repeat an observation");
|
|
2303
|
+
const finalFrames = native.finalReads.map((receipt) => {
|
|
2304
|
+
const rows = parse2(
|
|
2305
|
+
boundText(root, receipt.path, receipt.sha256, "native sampled end state")
|
|
2306
|
+
);
|
|
2307
|
+
if (!rows.length) throw new Error("native sampled end state is unobserved");
|
|
2308
|
+
return rows.at(-1);
|
|
2309
|
+
});
|
|
2310
|
+
const plan = JSON.parse(
|
|
2311
|
+
boundText(
|
|
2312
|
+
root,
|
|
2313
|
+
native.gradingPlan,
|
|
2314
|
+
native.gradingPlanSha256,
|
|
2315
|
+
"native sampled pre-capture stage plan"
|
|
2316
|
+
)
|
|
2317
|
+
);
|
|
2318
|
+
const verifiedPlan = nativeGradingPlan(sourceRoot, plan);
|
|
2319
|
+
const { catalogSha256: capturedCatalogSha256, ...capturedCheckpoint } = plan;
|
|
2320
|
+
const { catalogSha256: currentCatalogSha256, ...verifiedCheckpoint } = verifiedPlan;
|
|
2321
|
+
if (!/^[a-f0-9]{64}$/.test(capturedCatalogSha256 ?? "") || !/^[a-f0-9]{64}$/.test(currentCatalogSha256) || JSON.stringify(verifiedCheckpoint) !== JSON.stringify(capturedCheckpoint) || observations.some((row) => row.gradingPlanSha256 !== native.gradingPlanSha256) || frames.some((row) => row.gradingPlanSha256 !== native.gradingPlanSha256))
|
|
2322
|
+
throw new Error("native sampled stage plan was not bound before acquisition");
|
|
2323
|
+
const resolution = JSON.parse(
|
|
2324
|
+
boundText(
|
|
2325
|
+
root,
|
|
2326
|
+
native.windowResolution,
|
|
2327
|
+
native.windowResolutionSha256,
|
|
2328
|
+
"native sampled window resolution"
|
|
2329
|
+
)
|
|
2330
|
+
);
|
|
2331
|
+
const markers = JSON.parse(
|
|
2332
|
+
boundText(
|
|
2333
|
+
root,
|
|
2334
|
+
native.gradingMarkers,
|
|
2335
|
+
native.gradingMarkersSha256,
|
|
2336
|
+
"native sampled trigger markers"
|
|
2337
|
+
)
|
|
2338
|
+
);
|
|
2339
|
+
const grading = nativeResolvedGradingWindow(plan, resolution, frames, markers);
|
|
2340
|
+
return nativeRefreshSamples({
|
|
2341
|
+
frames,
|
|
2342
|
+
reads: observations,
|
|
2343
|
+
clockBytes,
|
|
2344
|
+
finalFrames,
|
|
2345
|
+
finalSurfaceStatus,
|
|
2346
|
+
grading
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
module2.exports.authenticateNativeRefreshSamples = authenticateNativeRefreshSamples;
|
|
1968
2350
|
async function encodeNativeFramebufferMovie(rawDir, videoPath) {
|
|
1969
2351
|
const fs3 = require("node:fs");
|
|
1970
2352
|
const path5 = require("node:path");
|
|
@@ -1991,27 +2373,6 @@ var require_native_frame_receipt = __commonJS({
|
|
|
1991
2373
|
});
|
|
1992
2374
|
if (times.some((value, index) => index > 0 && value <= times[index - 1]))
|
|
1993
2375
|
throw new Error("nonmonotonic native source arrivals");
|
|
1994
|
-
const reads = rows.map((row) => ({
|
|
1995
|
-
callbackIndex: row.callbackIndex,
|
|
1996
|
-
seedBeforeRead: row.source?.seedBeforeRead,
|
|
1997
|
-
seedAfterRead: row.source?.seedAfterRead,
|
|
1998
|
-
arrival: BigInt(row.consumerArrival.machTicks)
|
|
1999
|
-
}));
|
|
2000
|
-
for (const [index, read] of reads.entries()) {
|
|
2001
|
-
if (!Number.isSafeInteger(read.seedBeforeRead) || !Number.isSafeInteger(read.seedAfterRead))
|
|
2002
|
-
throw new Error(`native callback ${read.callbackIndex} has no surface seed`);
|
|
2003
|
-
const previous = reads[index - 1];
|
|
2004
|
-
if (!previous) continue;
|
|
2005
|
-
const lost = read.seedBeforeRead - previous.seedAfterRead - 1;
|
|
2006
|
-
if (lost > 1) {
|
|
2007
|
-
const gapMs = Number(
|
|
2008
|
-
(read.arrival - previous.arrival) * BigInt(firstClock.timebaseNumer) / BigInt(firstClock.timebaseDenom)
|
|
2009
|
-
) / 1e6;
|
|
2010
|
-
throw new Error(
|
|
2011
|
-
`native recording skipped ${lost} surface updates between callbacks ${previous.callbackIndex} and ${read.callbackIndex} (surface seed ${previous.seedAfterRead} to ${read.seedBeforeRead}, ${gapMs.toFixed(1)}ms between arrivals): no captured frame shows them`
|
|
2012
|
-
);
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
2376
|
function selectTime(start, end) {
|
|
2016
2377
|
if (end - start === 1) return String(times[start]);
|
|
2017
2378
|
const middle = Math.floor((start + end) / 2);
|
|
@@ -2415,6 +2776,10 @@ var require_attempt_events = __commonJS({
|
|
|
2415
2776
|
};
|
|
2416
2777
|
}
|
|
2417
2778
|
function authenticateNativeCadenceCensus(root, sourceRoot, life, decoded) {
|
|
2779
|
+
const {
|
|
2780
|
+
authenticateNativeRefreshSamples
|
|
2781
|
+
} = require_native_frame_receipt();
|
|
2782
|
+
authenticateNativeRefreshSamples(root, sourceRoot, life);
|
|
2418
2783
|
const native = life.nativeFramebuffer;
|
|
2419
2784
|
if (!native || native.exitCode !== 0 || native.failure || native.sourceSha256 !== proofByteHash(
|
|
2420
2785
|
fs3.readFileSync(
|
|
@@ -2432,10 +2797,10 @@ var require_attempt_events = __commonJS({
|
|
|
2432
2797
|
root,
|
|
2433
2798
|
native.mediaFrames,
|
|
2434
2799
|
native.mediaFramesSha256,
|
|
2435
|
-
"native cadence
|
|
2800
|
+
"native cadence sampled surface census"
|
|
2436
2801
|
).trim().split("\n").map(JSON.parse);
|
|
2437
2802
|
if (frames.length !== decoded.observations.length || media.length !== frames.length)
|
|
2438
|
-
throw new Error("native cadence acquisition and
|
|
2803
|
+
throw new Error("native cadence acquisition and sampled surface census differ");
|
|
2439
2804
|
const {
|
|
2440
2805
|
decodeNativeFramebuffer,
|
|
2441
2806
|
requireNativeRawFrame
|
|
@@ -2454,12 +2819,22 @@ var require_attempt_events = __commonJS({
|
|
|
2454
2819
|
return row.callbackIndex;
|
|
2455
2820
|
})
|
|
2456
2821
|
);
|
|
2457
|
-
const
|
|
2822
|
+
const reads = boundText(
|
|
2823
|
+
root,
|
|
2824
|
+
native.reads,
|
|
2825
|
+
native.readsSha256,
|
|
2826
|
+
"native cadence observations"
|
|
2827
|
+
).split("\n").filter(Boolean).map(JSON.parse);
|
|
2458
2828
|
let expectedCallback = 0;
|
|
2459
|
-
for (const
|
|
2829
|
+
for (const read of reads) {
|
|
2460
2830
|
while (superseded.delete(expectedCallback)) expectedCallback++;
|
|
2831
|
+
if (read.callbackIndex !== expectedCallback++)
|
|
2832
|
+
throw new Error("native cadence observations have invalid callback order");
|
|
2833
|
+
}
|
|
2834
|
+
const origin = frames[0].consumerArrival;
|
|
2835
|
+
for (const [index, frame] of frames.entries()) {
|
|
2461
2836
|
const clock = frame.consumerArrival;
|
|
2462
|
-
if (
|
|
2837
|
+
if (media[index].sourceFrameIndex !== index || !clock || clock.domain !== "host-mach-absolute-time" || clock.domain !== origin.domain || clock.timebaseNumer !== origin.timebaseNumer || clock.timebaseDenom !== origin.timebaseDenom || !Number.isSafeInteger(clock.timebaseNumer) || clock.timebaseNumer <= 0 || !Number.isSafeInteger(clock.timebaseDenom) || clock.timebaseDenom <= 0)
|
|
2463
2838
|
throw new Error("native cadence source census has invalid clock or order");
|
|
2464
2839
|
const pts = (BigInt(clock.machTicks) - BigInt(origin.machTicks)) * BigInt(clock.timebaseNumer) / BigInt(clock.timebaseDenom);
|
|
2465
2840
|
if (pts < 0n || pts !== BigInt(media[index].ptsNanoseconds) || Math.abs(Number(pts) / 1e6 - decoded.observations[index].atMs) > 1e-3)
|
|
@@ -2723,6 +3098,26 @@ var require_attempt_events = __commonJS({
|
|
|
2723
3098
|
paths.add(native.receipt);
|
|
2724
3099
|
paths.add(native.superseded);
|
|
2725
3100
|
paths.add(native.mediaFrames);
|
|
3101
|
+
paths.add(native.gradingMarkers);
|
|
3102
|
+
paths.add(native.gradingPlan);
|
|
3103
|
+
paths.add(native.windowResolution);
|
|
3104
|
+
paths.add(native.reads);
|
|
3105
|
+
for (const receipt2 of native.finalReads) {
|
|
3106
|
+
const text = boundText(
|
|
3107
|
+
root,
|
|
3108
|
+
receipt2.path,
|
|
3109
|
+
receipt2.sha256,
|
|
3110
|
+
"native sampled end state"
|
|
3111
|
+
);
|
|
3112
|
+
paths.add(receipt2.path);
|
|
3113
|
+
for (const row of text.split("\n").filter(Boolean).map(JSON.parse)) {
|
|
3114
|
+
const file = proofBoundPath(
|
|
3115
|
+
path5.dirname(proofBoundPath(root, receipt2.path)),
|
|
3116
|
+
row.rawPath
|
|
3117
|
+
);
|
|
3118
|
+
paths.add(path5.relative(root, file).split(path5.sep).join("/"));
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
2726
3121
|
const rows = fs3.readFileSync(proofBoundPath(root, native.receipt), "utf8").trim().split("\n").map(JSON.parse);
|
|
2727
3122
|
for (const row of rows) {
|
|
2728
3123
|
const file = proofBoundPath(
|
|
@@ -4996,7 +5391,7 @@ async function automateWebViewFrames(context, names, helpers, tenantWorkers) {
|
|
|
4996
5391
|
contentType += "; charset=windows-1252";
|
|
4997
5392
|
}
|
|
4998
5393
|
const headers = [{ name: "Content-Type", value: contentType }];
|
|
4999
|
-
if (document2
|
|
5394
|
+
if (document2) {
|
|
5000
5395
|
bound.load = null;
|
|
5001
5396
|
const config = JSON.parse(bound.config);
|
|
5002
5397
|
config.documentUrl = url;
|
|
@@ -5722,7 +6117,8 @@ ${WEBVIEW_FRAME_COLOR_SCHEME_SOURCE} var webKit = config.webKit || null;
|
|
|
5722
6117
|
crossOriginToSim = window.top.location.href === undefined;
|
|
5723
6118
|
} catch (_) {}
|
|
5724
6119
|
var cookieProperty = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
|
|
5725
|
-
|
|
6120
|
+
// file documents keep WebKit's native non-HTTP cookie behavior.
|
|
6121
|
+
if (cookieStore && /^https?:/.test(documentUrl)) {
|
|
5726
6122
|
var cookieOperations = ${webKitCookieOperationsSource};
|
|
5727
6123
|
var pageCookies = cookieOperations.restore(JSON.parse(cookieStore.cookies));
|
|
5728
6124
|
window.__peachUpdateWebKitCookies = function(cookies, policy) {
|
|
@@ -13139,6 +13535,7 @@ var by = {
|
|
|
13139
13535
|
// detox/index.ts
|
|
13140
13536
|
var import_motion_change = __toESM(require_motion_change(), 1);
|
|
13141
13537
|
var import_proof_frame = __toESM(require_proof_frame(), 1);
|
|
13538
|
+
var WORKLET_FRAME_TRACE_LIMIT = 16384;
|
|
13142
13539
|
var BASE_URL = process.env.PEACH_URL || "http://localhost:5173";
|
|
13143
13540
|
var SCREENSHOT_DIR = process.env.PEACH_SCREENSHOT_DIR || path4.join(process.cwd(), "test", "detox-driver", "screenshots", String(process.pid));
|
|
13144
13541
|
var _browser = null;
|
|
@@ -13415,8 +13812,12 @@ async function waitForSootsimWorkletsIdle(page, timeout = 3e3) {
|
|
|
13415
13812
|
);
|
|
13416
13813
|
if (!result?.settled) {
|
|
13417
13814
|
const pending = result?.pending ?? "no peer stats reply";
|
|
13418
|
-
throw
|
|
13419
|
-
|
|
13815
|
+
throw await describeSootsimBridgeFailure(
|
|
13816
|
+
page,
|
|
13817
|
+
new Error(
|
|
13818
|
+
`sootsim worklet runtime did not settle after ${result?.elapsed ?? timeout}ms (pending=${pending}; observation=${JSON.stringify(result?.observation)})`
|
|
13819
|
+
),
|
|
13820
|
+
"sootsim worklet runtime did not settle"
|
|
13420
13821
|
);
|
|
13421
13822
|
}
|
|
13422
13823
|
}
|
|
@@ -14629,6 +15030,7 @@ var device = {
|
|
|
14629
15030
|
async installApp() {
|
|
14630
15031
|
},
|
|
14631
15032
|
async uninstallApp() {
|
|
15033
|
+
await closeBrowser(false);
|
|
14632
15034
|
},
|
|
14633
15035
|
async openURL(url) {
|
|
14634
15036
|
const page = getPage();
|
|
@@ -15005,6 +15407,14 @@ var device = {
|
|
|
15005
15407
|
definition: import_motion_change.MOTION_CHANGE_DEFINITION,
|
|
15006
15408
|
installGlobal: true
|
|
15007
15409
|
});
|
|
15410
|
+
if (options.workletFrameTracePath) {
|
|
15411
|
+
await page.evaluate(async (limit) => {
|
|
15412
|
+
const enable = window.SootSim?.bridges?.test?.setShellReanimatedTraceEnabled;
|
|
15413
|
+
if (typeof enable !== "function")
|
|
15414
|
+
throw new Error("worklet trace bridge is absent");
|
|
15415
|
+
await enable(true, true, limit);
|
|
15416
|
+
}, WORKLET_FRAME_TRACE_LIMIT);
|
|
15417
|
+
}
|
|
15008
15418
|
const capturePromise = page.evaluate(
|
|
15009
15419
|
async (input) => {
|
|
15010
15420
|
const captureWindow = window;
|
|
@@ -15108,49 +15518,8 @@ var device = {
|
|
|
15108
15518
|
const controller = await lockstep.begin(60, {
|
|
15109
15519
|
workletFrameTimestampsSeconds: input.workletFrameTimestampsSeconds
|
|
15110
15520
|
});
|
|
15111
|
-
|
|
15112
|
-
const
|
|
15113
|
-
nextMove: 0,
|
|
15114
|
-
pressFrameIndex: null,
|
|
15115
|
-
releaseReceipt: null,
|
|
15116
|
-
released: false,
|
|
15117
|
-
x: input.hold?.x ?? 0,
|
|
15118
|
-
y: input.hold?.y ?? 0
|
|
15119
|
-
};
|
|
15120
|
-
const holdEventFrameIndex = (afterMs) => (holdState.pressFrameIndex ?? 0) + controller.framesForOffset(afterMs);
|
|
15121
|
-
const nextHoldEventFrameIndex = () => {
|
|
15122
|
-
if (!input.hold || holdState.released || holdState.pressFrameIndex === null) {
|
|
15123
|
-
return null;
|
|
15124
|
-
}
|
|
15125
|
-
const move = moves[holdState.nextMove];
|
|
15126
|
-
return holdEventFrameIndex(move ? move.afterMs : input.hold.durationMs);
|
|
15127
|
-
};
|
|
15128
|
-
const advanceHold = async (receipt) => {
|
|
15129
|
-
if (!input.hold || !interact || holdState.released) return;
|
|
15130
|
-
if (holdState.pressFrameIndex === null) {
|
|
15131
|
-
holdState.pressFrameIndex = receipt.frameIndex - 1;
|
|
15132
|
-
}
|
|
15133
|
-
for (let move = moves[holdState.nextMove]; move && receipt.frameIndex >= holdEventFrameIndex(move.afterMs); move = moves[holdState.nextMove]) {
|
|
15134
|
-
holdState.nextMove++;
|
|
15135
|
-
holdState.x = move.x;
|
|
15136
|
-
holdState.y = move.y;
|
|
15137
|
-
await interact.touchMove(
|
|
15138
|
-
move.x,
|
|
15139
|
-
move.y,
|
|
15140
|
-
999,
|
|
15141
|
-
controller.initialFrame.virtualTimestampMs + move.afterMs
|
|
15142
|
-
);
|
|
15143
|
-
}
|
|
15144
|
-
if (receipt.frameIndex < holdEventFrameIndex(input.hold.durationMs)) return;
|
|
15145
|
-
holdState.released = true;
|
|
15146
|
-
holdState.releaseReceipt = receipt;
|
|
15147
|
-
await interact.touchUp(
|
|
15148
|
-
holdState.x,
|
|
15149
|
-
holdState.y,
|
|
15150
|
-
999,
|
|
15151
|
-
controller.initialFrame.virtualTimestampMs + input.hold.durationMs
|
|
15152
|
-
);
|
|
15153
|
-
};
|
|
15521
|
+
let heldInput = null;
|
|
15522
|
+
const releaseReceipt = () => controller.inputReceipts.find((receipt) => receipt.phase === "up");
|
|
15154
15523
|
const keptFrames = /* @__PURE__ */ new Map();
|
|
15155
15524
|
const keepFrame = (receipt, bitmap) => {
|
|
15156
15525
|
keptFrames.get(receipt.frameIndex)?.bitmap.close();
|
|
@@ -15169,17 +15538,51 @@ var device = {
|
|
|
15169
15538
|
throw new Error("paired worklets callback preceded the Peach trigger");
|
|
15170
15539
|
state.ready = true;
|
|
15171
15540
|
if (input.hold && interact) {
|
|
15172
|
-
const
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15541
|
+
const hold = input.hold;
|
|
15542
|
+
const moves = hold.moves ?? [];
|
|
15543
|
+
const release = hold.release ?? moves.at(-1) ?? hold;
|
|
15544
|
+
const events = [
|
|
15545
|
+
{
|
|
15546
|
+
phase: "down",
|
|
15547
|
+
x: hold.x,
|
|
15548
|
+
y: hold.y,
|
|
15549
|
+
pointerId: 999,
|
|
15550
|
+
afterMs: 0,
|
|
15551
|
+
async deliver(timestamp2) {
|
|
15552
|
+
const hit = await interact.touchDown(hold.x, hold.y, 999, timestamp2);
|
|
15553
|
+
if (!hit)
|
|
15554
|
+
throw new Error(`peach motion hold missed at ${hold.x},${hold.y}`);
|
|
15555
|
+
}
|
|
15556
|
+
},
|
|
15557
|
+
...moves.map(
|
|
15558
|
+
(move) => ({
|
|
15559
|
+
phase: "move",
|
|
15560
|
+
x: move.x,
|
|
15561
|
+
y: move.y,
|
|
15562
|
+
pointerId: 999,
|
|
15563
|
+
afterMs: move.afterMs,
|
|
15564
|
+
eventAfterMs: move.eventAfterMs,
|
|
15565
|
+
async deliver(timestamp2) {
|
|
15566
|
+
await interact.touchMove(move.x, move.y, 999, timestamp2);
|
|
15567
|
+
}
|
|
15568
|
+
})
|
|
15569
|
+
),
|
|
15570
|
+
{
|
|
15571
|
+
phase: "up",
|
|
15572
|
+
x: release.x,
|
|
15573
|
+
y: release.y,
|
|
15574
|
+
pointerId: 999,
|
|
15575
|
+
afterMs: hold.durationMs,
|
|
15576
|
+
eventAfterMs: hold.eventDurationMs,
|
|
15577
|
+
async deliver(timestamp2) {
|
|
15578
|
+
await interact.touchUp(release.x, release.y, 999, timestamp2);
|
|
15579
|
+
}
|
|
15580
|
+
}
|
|
15581
|
+
];
|
|
15582
|
+
heldInput = controller.scheduleInput(events);
|
|
15583
|
+
heldInput.catch(() => {
|
|
15584
|
+
});
|
|
15585
|
+
await controller.flushFrame();
|
|
15183
15586
|
} else {
|
|
15184
15587
|
while (!state.triggerStarted) {
|
|
15185
15588
|
if (state.cancelled) throw new Error("peach motion capture was cancelled");
|
|
@@ -15213,9 +15616,8 @@ var device = {
|
|
|
15213
15616
|
for (let frame = 0; !visibleStart && frame < detectionFrameLimit; frame++) {
|
|
15214
15617
|
if (state.cancelled) throw new Error("peach motion capture was cancelled");
|
|
15215
15618
|
const receipt = await controller.stepFrame();
|
|
15216
|
-
await advanceHold(receipt);
|
|
15217
15619
|
const candidateBitmap = await captureBitmap();
|
|
15218
|
-
if (input.anchorAtTouch === "down" || input.anchorAtTouch === "up" &&
|
|
15620
|
+
if (input.anchorAtTouch === "down" || input.anchorAtTouch === "up" && releaseReceipt()?.frameIndex === receipt.frameIndex) {
|
|
15219
15621
|
keepFrame(receipt, await createImageBitmap(candidateBitmap));
|
|
15220
15622
|
}
|
|
15221
15623
|
const bounds = boundsFor(candidateBitmap);
|
|
@@ -15248,17 +15650,17 @@ var device = {
|
|
|
15248
15650
|
let currentReceipt = visibleStart;
|
|
15249
15651
|
if (input.anchorAtTouch === "down") driveStart = controller.initialFrame;
|
|
15250
15652
|
if (input.anchorAtTouch === "up") {
|
|
15251
|
-
while (!
|
|
15653
|
+
while (!releaseReceipt()) {
|
|
15252
15654
|
if (state.cancelled) throw new Error("peach motion capture was cancelled");
|
|
15253
15655
|
const receipt = await controller.stepFrame();
|
|
15254
|
-
await advanceHold(receipt);
|
|
15255
15656
|
currentReceipt = receipt;
|
|
15256
|
-
if (
|
|
15657
|
+
if (releaseReceipt()) keepFrame(receipt, await captureBitmap());
|
|
15257
15658
|
}
|
|
15258
|
-
|
|
15659
|
+
const released = releaseReceipt();
|
|
15660
|
+
if (!released) {
|
|
15259
15661
|
throw new Error("peach release anchoring found no release frame");
|
|
15260
15662
|
}
|
|
15261
|
-
driveStart =
|
|
15663
|
+
driveStart = { ...released, tenantFrame: null };
|
|
15262
15664
|
}
|
|
15263
15665
|
const resolvedAnchors = /* @__PURE__ */ new Map();
|
|
15264
15666
|
const anchorRecords = [];
|
|
@@ -15283,7 +15685,6 @@ var device = {
|
|
|
15283
15685
|
while (searchReceipt.virtualTimestampMs < searchStartVts) {
|
|
15284
15686
|
if (state.cancelled) throw new Error("peach motion capture was cancelled");
|
|
15285
15687
|
searchReceipt = await controller.stepFrame();
|
|
15286
|
-
await advanceHold(searchReceipt);
|
|
15287
15688
|
}
|
|
15288
15689
|
currentReceipt = searchReceipt;
|
|
15289
15690
|
let anchorBitmap = await captureBitmap();
|
|
@@ -15295,7 +15696,6 @@ var device = {
|
|
|
15295
15696
|
for (let frame = 0; frame < detectionFrameLimit; frame++) {
|
|
15296
15697
|
if (state.cancelled) throw new Error("peach motion capture was cancelled");
|
|
15297
15698
|
const receipt = await controller.stepFrame();
|
|
15298
|
-
await advanceHold(receipt);
|
|
15299
15699
|
const candidateBitmap = await captureBitmap();
|
|
15300
15700
|
const bounds = boundsFor(candidateBitmap);
|
|
15301
15701
|
const candidate = regionPixels(candidateBitmap);
|
|
@@ -15361,11 +15761,7 @@ var device = {
|
|
|
15361
15761
|
});
|
|
15362
15762
|
continue;
|
|
15363
15763
|
}
|
|
15364
|
-
for (let eventFrameIndex = nextHoldEventFrameIndex(); eventFrameIndex !== null && stageFrameIndex > eventFrameIndex; eventFrameIndex = nextHoldEventFrameIndex()) {
|
|
15365
|
-
await advanceHold(await controller.stepFrame());
|
|
15366
|
-
}
|
|
15367
15764
|
const receipt = await controller.stepToOffset(anchorReceipt, stage.offsetMs);
|
|
15368
|
-
await advanceHold(receipt);
|
|
15369
15765
|
currentReceipt = receipt;
|
|
15370
15766
|
snapshots.push({
|
|
15371
15767
|
actualMs: receipt.virtualTimestampMs - anchorReceipt.virtualTimestampMs,
|
|
@@ -15379,11 +15775,11 @@ var device = {
|
|
|
15379
15775
|
}
|
|
15380
15776
|
if (input.workletFrameTimestampsSeconds) controller.endWorkletFrameReceipt();
|
|
15381
15777
|
let releasedOffsetMs = 0;
|
|
15382
|
-
while (
|
|
15778
|
+
while (input.hold && !releaseReceipt()) {
|
|
15383
15779
|
const receipt = await controller.stepFrame();
|
|
15384
|
-
await advanceHold(receipt);
|
|
15385
15780
|
releasedOffsetMs = receipt.virtualTimestampMs - driveStart.virtualTimestampMs;
|
|
15386
15781
|
}
|
|
15782
|
+
await heldInput;
|
|
15387
15783
|
if (input.settleMs > 0) {
|
|
15388
15784
|
const maximumPrimaryMs = snapshots.reduce(
|
|
15389
15785
|
(maximum, snapshot2) => Math.max(
|
|
@@ -15449,22 +15845,24 @@ var device = {
|
|
|
15449
15845
|
token
|
|
15450
15846
|
}
|
|
15451
15847
|
);
|
|
15452
|
-
|
|
15453
|
-
|
|
15454
|
-
(captureToken) => {
|
|
15455
|
-
const registry = window.__sootsimVisibleChangeCaptures;
|
|
15456
|
-
return registry?.[captureToken]?.ready === true;
|
|
15457
|
-
},
|
|
15458
|
-
token,
|
|
15459
|
-
{ timeout: 5e3 }
|
|
15460
|
-
),
|
|
15461
|
-
capturePromise.then(() => {
|
|
15462
|
-
throw new Error("peach motion capture completed before its trigger");
|
|
15463
|
-
})
|
|
15464
|
-
]);
|
|
15465
|
-
const triggerStartMs = Date.now();
|
|
15848
|
+
let captureFailure;
|
|
15849
|
+
let triggerStartMs = 0;
|
|
15466
15850
|
let triggerCompletedMs = 0;
|
|
15467
15851
|
try {
|
|
15852
|
+
await Promise.race([
|
|
15853
|
+
page.waitForFunction(
|
|
15854
|
+
(captureToken) => {
|
|
15855
|
+
const registry = window.__sootsimVisibleChangeCaptures;
|
|
15856
|
+
return registry?.[captureToken]?.ready === true;
|
|
15857
|
+
},
|
|
15858
|
+
token,
|
|
15859
|
+
{ timeout: 5e3 }
|
|
15860
|
+
),
|
|
15861
|
+
capturePromise.then(() => {
|
|
15862
|
+
throw new Error("peach motion capture completed before its trigger");
|
|
15863
|
+
})
|
|
15864
|
+
]);
|
|
15865
|
+
triggerStartMs = Date.now();
|
|
15468
15866
|
const triggerPromise = (async () => {
|
|
15469
15867
|
if (!options.hold) {
|
|
15470
15868
|
if (typeof options.trigger !== "function") {
|
|
@@ -15500,6 +15898,7 @@ var device = {
|
|
|
15500
15898
|
}
|
|
15501
15899
|
};
|
|
15502
15900
|
} catch (error) {
|
|
15901
|
+
captureFailure = { error };
|
|
15503
15902
|
await page.evaluate((captureToken) => {
|
|
15504
15903
|
const registry = window.__sootsimVisibleChangeCaptures;
|
|
15505
15904
|
const capture = registry?.[captureToken];
|
|
@@ -15509,6 +15908,55 @@ var device = {
|
|
|
15509
15908
|
await capturePromise.catch(() => {
|
|
15510
15909
|
});
|
|
15511
15910
|
throw error;
|
|
15911
|
+
} finally {
|
|
15912
|
+
if (options.workletFrameTracePath) {
|
|
15913
|
+
try {
|
|
15914
|
+
const events = await page.evaluate(async () => {
|
|
15915
|
+
const test = window.SootSim?.bridges?.test;
|
|
15916
|
+
const read = test?.getShellReanimatedTrace;
|
|
15917
|
+
const enable = test?.setShellReanimatedTraceEnabled;
|
|
15918
|
+
if (typeof read !== "function" || typeof enable !== "function")
|
|
15919
|
+
throw new Error("worklet trace bridge is absent");
|
|
15920
|
+
try {
|
|
15921
|
+
return await read();
|
|
15922
|
+
} finally {
|
|
15923
|
+
await enable(false, false);
|
|
15924
|
+
}
|
|
15925
|
+
});
|
|
15926
|
+
fs2.writeFileSync(
|
|
15927
|
+
options.workletFrameTracePath,
|
|
15928
|
+
JSON.stringify(
|
|
15929
|
+
{
|
|
15930
|
+
source: "peach-native-frame-queue",
|
|
15931
|
+
limit: WORKLET_FRAME_TRACE_LIMIT,
|
|
15932
|
+
events
|
|
15933
|
+
},
|
|
15934
|
+
null,
|
|
15935
|
+
2
|
|
15936
|
+
)
|
|
15937
|
+
);
|
|
15938
|
+
} catch (traceError) {
|
|
15939
|
+
if (!captureFailure) throw traceError;
|
|
15940
|
+
const primary = captureFailure.error;
|
|
15941
|
+
if (primary && (typeof primary === "object" || typeof primary === "function") && Object.isExtensible(primary)) {
|
|
15942
|
+
try {
|
|
15943
|
+
Object.defineProperty(primary, "workletFrameTraceError", {
|
|
15944
|
+
value: traceError,
|
|
15945
|
+
configurable: true
|
|
15946
|
+
});
|
|
15947
|
+
} catch (attachmentError) {
|
|
15948
|
+
console.error(
|
|
15949
|
+
"[peach motion capture] could not attach trace failure:",
|
|
15950
|
+
attachmentError
|
|
15951
|
+
);
|
|
15952
|
+
}
|
|
15953
|
+
}
|
|
15954
|
+
console.error(
|
|
15955
|
+
"[peach motion capture] secondary worklet trace failure:",
|
|
15956
|
+
traceError
|
|
15957
|
+
);
|
|
15958
|
+
}
|
|
15959
|
+
}
|
|
15512
15960
|
}
|
|
15513
15961
|
},
|
|
15514
15962
|
// FAST multi-stage capture for sub-second animation diffs. each stage
|