@farmslot/adapter-rn 0.30.0 → 0.31.1
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/CHANGELOG.md +10 -0
- package/dist/android-gfxinfo-performance.d.ts +87 -0
- package/dist/android-gfxinfo-performance.d.ts.map +1 -0
- package/dist/android-gfxinfo-performance.js +451 -0
- package/dist/android-gfxinfo-performance.js.map +1 -0
- package/dist/performance-capture.d.ts +192 -0
- package/dist/performance-capture.d.ts.map +1 -0
- package/dist/performance-capture.js +751 -0
- package/dist/performance-capture.js.map +1 -0
- package/dist/performance-observer.d.ts +9 -0
- package/dist/performance-observer.d.ts.map +1 -0
- package/dist/performance-observer.js +24 -0
- package/dist/performance-observer.js.map +1 -0
- package/dist/runner.d.ts +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +38 -4
- package/dist/runner.js.map +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { type ActionHandler } from '@farmslot/recipe-runner';
|
|
2
|
+
import { type FrameMetricSummary, type PerformanceSourceStatus } from '@farmslot/recipe-runner/runtime/cdp-trace';
|
|
3
|
+
import { type AndroidDeviceDescription, type AndroidFrameSample, type AndroidGfxinfoCaptureResult, type AndroidGfxinfoPerformanceBackend, type AndroidShownMarker } from './android-gfxinfo-performance.js';
|
|
4
|
+
export declare const PERFORMANCE_CAPTURE_ACTION = "app.performance_capture";
|
|
5
|
+
export interface RecipeNodeSpan {
|
|
6
|
+
nodeId: string;
|
|
7
|
+
action: string;
|
|
8
|
+
/** What the node acted on (its test id, selector, text or key), named in failures. */
|
|
9
|
+
target?: string;
|
|
10
|
+
startedAtEpochMs: number;
|
|
11
|
+
endedAtEpochMs?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface PerformanceBudget {
|
|
14
|
+
/** Fail when the lowest sustained FPS of the window is below this. */
|
|
15
|
+
min_sustained_fps?: number;
|
|
16
|
+
/** Fail when the 95th percentile press latency is above this. */
|
|
17
|
+
max_press_p95_ms?: number;
|
|
18
|
+
/** Fail when any press latency is above this, or a press could not be timed. */
|
|
19
|
+
max_press_ms?: number;
|
|
20
|
+
/** Fail when the window recorded fewer native frames, e.g. a scroll that never rendered. */
|
|
21
|
+
min_frames?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The reference a failure is compared against, e.g. the default branch on a
|
|
25
|
+
* named build and refresh rate. `label` and the numbers appear in failure
|
|
26
|
+
* messages; the whole object is echoed in the summary.
|
|
27
|
+
*/
|
|
28
|
+
export interface PerformanceBaseline {
|
|
29
|
+
label: string;
|
|
30
|
+
sustained_fps?: number;
|
|
31
|
+
press_p95_ms?: number;
|
|
32
|
+
press_max_ms?: number;
|
|
33
|
+
/** Native frames the window recorded on the baseline. */
|
|
34
|
+
frames?: number;
|
|
35
|
+
/** Display refresh rate of the baseline; a different measured rate is noted. */
|
|
36
|
+
refresh_hz?: number;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface PerformanceTargets {
|
|
40
|
+
fps?: number;
|
|
41
|
+
press_ms?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface PressLatency {
|
|
44
|
+
nodeId: string;
|
|
45
|
+
action: string;
|
|
46
|
+
target?: string;
|
|
47
|
+
/** Input frame start to the end of the frame that answered it; absent when not timed. */
|
|
48
|
+
latencyMs?: number;
|
|
49
|
+
/**
|
|
50
|
+
* `shown`: the screen's `[farmslot-perf] shown` line, logged after React
|
|
51
|
+
* committed it, and the frames that started by two frame budgets after it.
|
|
52
|
+
* `mount`: the first frame after the touch that stood out is the answer.
|
|
53
|
+
* `first-frame`: none stood out, so the press is timed to the first frame
|
|
54
|
+
* after the touch; the new screen did no measurable UI-thread work.
|
|
55
|
+
*/
|
|
56
|
+
answer?: 'shown' | 'mount' | 'first-frame';
|
|
57
|
+
/** The screen whose shown line times the press (`shown_markers`). */
|
|
58
|
+
marker?: string;
|
|
59
|
+
/** Why the press has no latency. */
|
|
60
|
+
unmeasuredReason?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface PerformanceWindowSummary {
|
|
63
|
+
schemaVersion: 1;
|
|
64
|
+
id: string;
|
|
65
|
+
status: PerformanceSourceStatus;
|
|
66
|
+
platform: 'android';
|
|
67
|
+
source: string;
|
|
68
|
+
startedAtEpochMs: number;
|
|
69
|
+
endedAtEpochMs: number;
|
|
70
|
+
durationMs: number;
|
|
71
|
+
frames: FrameMetricSummary;
|
|
72
|
+
/** The lowest active FPS over any `sustainedWindowMs` of the window; absent with too few frames. */
|
|
73
|
+
sustainedFps?: number;
|
|
74
|
+
sustainedWindowMs: number;
|
|
75
|
+
presses: PressLatency[];
|
|
76
|
+
press: PressStats;
|
|
77
|
+
/** The same statistics per press target, e.g. per tab. */
|
|
78
|
+
pressByTarget: Record<string, PressStats>;
|
|
79
|
+
/**
|
|
80
|
+
* `inconclusive` when the capture has a coverage gap (failed polls, frames
|
|
81
|
+
* gfxinfo overwrote), whatever else failed: it never passes, and `failures`
|
|
82
|
+
* still lists what the partial capture showed. An idle window, no frames and
|
|
83
|
+
* no gaps, passes.
|
|
84
|
+
*/
|
|
85
|
+
budget?: {
|
|
86
|
+
verdict: 'pass' | 'fail' | 'inconclusive';
|
|
87
|
+
limits: PerformanceBudget;
|
|
88
|
+
failures: string[];
|
|
89
|
+
inconclusive: string[];
|
|
90
|
+
};
|
|
91
|
+
baseline?: PerformanceBaseline;
|
|
92
|
+
targets?: {
|
|
93
|
+
fps?: {
|
|
94
|
+
target: number;
|
|
95
|
+
sustainedFps?: number;
|
|
96
|
+
activeFps?: number;
|
|
97
|
+
met: boolean;
|
|
98
|
+
};
|
|
99
|
+
press?: {
|
|
100
|
+
target: number;
|
|
101
|
+
p95Ms?: number;
|
|
102
|
+
maxMs?: number;
|
|
103
|
+
met: boolean;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/** Frames the capture may have missed: poll failures, overwritten frames, an overrun window. */
|
|
107
|
+
coverageGapReasons: string[];
|
|
108
|
+
/** Why the source has no frames, e.g. an idle screen. Not a gap. */
|
|
109
|
+
unavailableReasons: string[];
|
|
110
|
+
/** Observations that don't change the verdict, e.g. a refresh rate unlike the baseline's. */
|
|
111
|
+
notes: string[];
|
|
112
|
+
}
|
|
113
|
+
export interface PressStats {
|
|
114
|
+
count: number;
|
|
115
|
+
timed: number;
|
|
116
|
+
/** Timed presses answered by their screen's shown line. */
|
|
117
|
+
shown: number;
|
|
118
|
+
/** Timed presses with no standout mount frame, answered by the first frame. */
|
|
119
|
+
firstFrame: number;
|
|
120
|
+
p50Ms?: number;
|
|
121
|
+
p95Ms?: number;
|
|
122
|
+
maxMs?: number;
|
|
123
|
+
}
|
|
124
|
+
export interface PerformanceCaptureSession {
|
|
125
|
+
handler: ActionHandler;
|
|
126
|
+
/** The runner's node boundaries, used to find the presses inside a window. */
|
|
127
|
+
recordNodeEvent(event: {
|
|
128
|
+
nodeId: string;
|
|
129
|
+
action: string;
|
|
130
|
+
target?: string;
|
|
131
|
+
status: 'running' | 'passed' | 'failed';
|
|
132
|
+
}): void;
|
|
133
|
+
close(): Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
export interface PerformanceCaptureSessionOptions {
|
|
136
|
+
env: Record<string, string | undefined>;
|
|
137
|
+
logger?: Pick<Console, 'warn'>;
|
|
138
|
+
/** Validate the nodes without a device: no window opens and nothing is measured. */
|
|
139
|
+
dryRun?: boolean;
|
|
140
|
+
createBackend?: (env: Record<string, string | undefined>) => AndroidGfxinfoPerformanceBackend;
|
|
141
|
+
now?: () => number;
|
|
142
|
+
}
|
|
143
|
+
export declare function createPerformanceCaptureSession({ env, logger, dryRun, createBackend, now, }: PerformanceCaptureSessionOptions): PerformanceCaptureSession;
|
|
144
|
+
export declare function summarizeWindow({ id, startedAtEpochMs, endedAtEpochMs, maxDurationMs, result, spans, budget, shownMarkers, baseline, targets, sustainedWindowMs, responseWindowMs, }: {
|
|
145
|
+
id: string;
|
|
146
|
+
startedAtEpochMs: number;
|
|
147
|
+
endedAtEpochMs: number;
|
|
148
|
+
maxDurationMs: number;
|
|
149
|
+
result: AndroidGfxinfoCaptureResult;
|
|
150
|
+
spans: readonly RecipeNodeSpan[];
|
|
151
|
+
budget?: PerformanceBudget;
|
|
152
|
+
/** Press target -> the screen whose `[farmslot-perf] shown` line answers it. */
|
|
153
|
+
shownMarkers?: Record<string, string>;
|
|
154
|
+
baseline?: PerformanceBaseline;
|
|
155
|
+
targets?: PerformanceTargets;
|
|
156
|
+
sustainedWindowMs: number;
|
|
157
|
+
responseWindowMs: number;
|
|
158
|
+
}): PerformanceWindowSummary;
|
|
159
|
+
/**
|
|
160
|
+
* The press's latency: from the first frame that handled input during the
|
|
161
|
+
* press node to the end of the frame that answered it. The answer is the
|
|
162
|
+
* first later frame that stands out (see MOUNT_MIN_MS) within
|
|
163
|
+
* `responseWindowMs`, or past it within `MOUNT_LOOKAHEAD` windows, after a
|
|
164
|
+
* pause too, a frame with a whole frame budget of UI-thread work: the frame
|
|
165
|
+
* that mounts and draws the new screen. Touch ripples run on the render
|
|
166
|
+
* thread. Only when nothing answers that way is the press timed to the first
|
|
167
|
+
* frame within `responseWindowMs` (`first-frame`). A press with no input
|
|
168
|
+
* frame, or no frame after it, is untimed.
|
|
169
|
+
*/
|
|
170
|
+
export declare function measurePress(samples: readonly AndroidFrameSample[], span: RecipeNodeSpan, responseWindowMs: number, windowEndEpochMs: number): PressLatency;
|
|
171
|
+
/**
|
|
172
|
+
* A press timed by the app's own word: from the first frame that handled input
|
|
173
|
+
* during the press node to the screen's next `[farmslot-perf] shown` line, or
|
|
174
|
+
* to the end of the last frame from the touch on that started by
|
|
175
|
+
* `SHOWN_MARKER_FRAMES` frame budgets after the line, if that is later. The
|
|
176
|
+
* app logs the line from the second animation frame after focus, once
|
|
177
|
+
* JavaScript is free, so the UI thread can still be finishing the mount frame
|
|
178
|
+
* then. A press can over-read by up to about two light frames, and could miss
|
|
179
|
+
* a mount frame that starts more than two budgets after the line; a live
|
|
180
|
+
* update that starts later doesn't count, and a stall that delays the mount
|
|
181
|
+
* delays the line too. No line before the next press leaves the press untimed.
|
|
182
|
+
*/
|
|
183
|
+
export declare function measureShownPress(samples: readonly AndroidFrameSample[], span: RecipeNodeSpan, screen: string, markers: readonly AndroidShownMarker[], searchEndEpochMs: number, frameBudgetMs: number): PressLatency;
|
|
184
|
+
export interface PerformanceReportEnvironment {
|
|
185
|
+
device?: AndroidDeviceDescription;
|
|
186
|
+
checkout?: {
|
|
187
|
+
head?: string;
|
|
188
|
+
dirty?: boolean;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export declare function renderPerformanceReport(title: string, summaries: readonly PerformanceWindowSummary[], environment?: PerformanceReportEnvironment): string;
|
|
192
|
+
//# sourceMappingURL=performance-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance-capture.d.ts","sourceRoot":"","sources":["../src/performance-capture.ts"],"names":[],"mappings":"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"}
|