cbrowser 18.70.0 → 18.72.0
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/dist/browser.d.ts +32 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +46 -2
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +1475 -24
- package/dist/cli.js.map +1 -1
- package/dist/cognitive/index.d.ts +10 -0
- package/dist/cognitive/index.d.ts.map +1 -1
- package/dist/cognitive/index.js +8 -0
- package/dist/cognitive/index.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +151 -3
- package/dist/daemon.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +67 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/base/capture-tools.d.ts +189 -0
- package/dist/mcp-tools/base/capture-tools.d.ts.map +1 -0
- package/dist/mcp-tools/base/capture-tools.js +667 -0
- package/dist/mcp-tools/base/capture-tools.js.map +1 -0
- package/dist/mcp-tools/base/gif-tools.d.ts +19 -0
- package/dist/mcp-tools/base/gif-tools.d.ts.map +1 -1
- package/dist/mcp-tools/base/gif-tools.js +45 -27
- package/dist/mcp-tools/base/gif-tools.js.map +1 -1
- package/dist/mcp-tools/base/index.d.ts +2 -1
- package/dist/mcp-tools/base/index.d.ts.map +1 -1
- package/dist/mcp-tools/base/index.js +5 -1
- package/dist/mcp-tools/base/index.js.map +1 -1
- package/dist/mcp-tools/tool-categories.d.ts.map +1 -1
- package/dist/mcp-tools/tool-categories.js +11 -0
- package/dist/mcp-tools/tool-categories.js.map +1 -1
- package/dist/recording/auto-capture.d.ts +72 -0
- package/dist/recording/auto-capture.d.ts.map +1 -0
- package/dist/recording/auto-capture.js +155 -0
- package/dist/recording/auto-capture.js.map +1 -0
- package/dist/recording/contact-sheet.d.ts +65 -0
- package/dist/recording/contact-sheet.d.ts.map +1 -0
- package/dist/recording/contact-sheet.js +204 -0
- package/dist/recording/contact-sheet.js.map +1 -0
- package/dist/recording/encoders.d.ts +101 -0
- package/dist/recording/encoders.d.ts.map +1 -0
- package/dist/recording/encoders.js +304 -0
- package/dist/recording/encoders.js.map +1 -0
- package/dist/recording/engine.d.ts +263 -0
- package/dist/recording/engine.d.ts.map +1 -0
- package/dist/recording/engine.js +896 -0
- package/dist/recording/engine.js.map +1 -0
- package/dist/recording/ssim.d.ts +78 -0
- package/dist/recording/ssim.d.ts.map +1 -0
- package/dist/recording/ssim.js +209 -0
- package/dist/recording/ssim.js.map +1 -0
- package/dist/recording/timing.d.ts +124 -0
- package/dist/recording/timing.d.ts.map +1 -0
- package/dist/recording/timing.js +251 -0
- package/dist/recording/timing.js.map +1 -0
- package/dist/recording/types.d.ts +316 -0
- package/dist/recording/types.d.ts.map +1 -0
- package/dist/recording/types.js +337 -0
- package/dist/recording/types.js.map +1 -0
- package/dist/testing/nl-test-suite.d.ts +10 -0
- package/dist/testing/nl-test-suite.d.ts.map +1 -1
- package/dist/testing/nl-test-suite.js +13 -1
- package/dist/testing/nl-test-suite.js.map +1 -1
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBrowser - Cognitive Browser Automation
|
|
3
|
+
* Copyright 2026 Alexandria Eden alexandria.shai.eden@gmail.com
|
|
4
|
+
* Learn more at https://cbrowser.ai - MIT License
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Minimum per-frame delay. GIF and WebP treat sub-20ms delays inconsistently
|
|
8
|
+
* (many decoders silently substitute 100ms), so delays are floored here.
|
|
9
|
+
*/
|
|
10
|
+
export const MIN_DELAY_MS = 20;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum per-frame delay.
|
|
13
|
+
*
|
|
14
|
+
Sharp validates each delay against 0..65535 and rejects the whole encode
|
|
15
|
+
* otherwise: "Expected integer or an array of integers between 0 and 65535 for
|
|
16
|
+
* delay". A capture left running while the page goes quiet produces exactly
|
|
17
|
+
* that: the last frame's delay stretches to cover the silent tail, and one
|
|
18
|
+
* out-of-range value destroys the artifact.
|
|
19
|
+
*
|
|
20
|
+
* Note this is sharp's API limit, not the format's. GIF stores delay as a
|
|
21
|
+
* uint16 of CENTIseconds, so a single frame can legally be held for up to
|
|
22
|
+
* 655_350ms — ten times what sharp will accept in one value. That gap is what
|
|
23
|
+
* {@link expandForPlayback} exploits: libvips merges identical consecutive
|
|
24
|
+
* frames and SUMS their delays, so N legal repeats collapse back into one page
|
|
25
|
+
* holding for their combined duration. Verified end-to-end in
|
|
26
|
+
* tests/recording-encoder-delays.test.ts.
|
|
27
|
+
*
|
|
28
|
+
* Emitting an illegal delay is therefore not a caller error to validate — it is
|
|
29
|
+
* this function's job to make impossible. See {@link expandForPlayback} for
|
|
30
|
+
* holding a frame longer than this ceiling without lying about the timing.
|
|
31
|
+
*/
|
|
32
|
+
export const MAX_DELAY_MS = 65535;
|
|
33
|
+
/** Tolerance for float comparisons on millisecond sums. */
|
|
34
|
+
const EPS = 1e-9;
|
|
35
|
+
/**
|
|
36
|
+
* Per-frame display delay in milliseconds, derived from consecutive timestamps.
|
|
37
|
+
*
|
|
38
|
+
* Frame `i` is displayed for `tMs[i + 1] - tMs[i]`; the last frame is displayed
|
|
39
|
+
* until `totalDurationMs`. Any lead-in before the first frame (`tMs[0] > 0`) is
|
|
40
|
+
* folded into frame 0's delay, so the delays describe the whole recording.
|
|
41
|
+
*
|
|
42
|
+
* Clamp behaviour, applied in this order:
|
|
43
|
+
*
|
|
44
|
+
* 1. FLOOR at {@link MIN_DELAY_MS}. When the floor inflates the total, the
|
|
45
|
+
* surplus is reclaimed from the frames that have headroom above it,
|
|
46
|
+
* proportionally to how much headroom each has, so the sum stays exactly
|
|
47
|
+
* `totalDurationMs` and no frame is singled out. If the recording is too
|
|
48
|
+
* short for every frame to hold the floor
|
|
49
|
+
* (`frames * MIN_DELAY_MS > totalDurationMs`), the floor wins and the sum is
|
|
50
|
+
* `frames * MIN_DELAY_MS` — the animation is then longer than the capture,
|
|
51
|
+
* which is the only way to keep all frames visible.
|
|
52
|
+
* 2. CEILING at {@link MAX_DELAY_MS}, last, so the reclaim cannot push a delay
|
|
53
|
+
* back over it. A capped delay means the returned timeline is SHORTER than
|
|
54
|
+
* `totalDurationMs`: a frame held past the ceiling cannot be expressed as a
|
|
55
|
+
* single delay. That truncation is the safe failure — the alternative is an
|
|
56
|
+
* encode that dies and takes the whole artifact with it. To keep real-time
|
|
57
|
+
* playback across a long hold, use {@link expandForPlayback}, which repeats
|
|
58
|
+
* the held frame instead of truncating it.
|
|
59
|
+
*
|
|
60
|
+
* Resulting guarantee: every returned delay is within
|
|
61
|
+
* `[MIN_DELAY_MS, MAX_DELAY_MS]`, for every input this function accepts.
|
|
62
|
+
*
|
|
63
|
+
* @throws if `tMs` is empty, contains negative or decreasing values, or if
|
|
64
|
+
* `totalDurationMs` is shorter than the last timestamp.
|
|
65
|
+
*/
|
|
66
|
+
export function delaysFromTimestamps(tMs, totalDurationMs, options = {}) {
|
|
67
|
+
if (tMs.length === 0) {
|
|
68
|
+
throw new Error("delaysFromTimestamps: timestamps must not be empty");
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isFinite(totalDurationMs) || totalDurationMs < 0) {
|
|
71
|
+
throw new Error(`delaysFromTimestamps: totalDurationMs must be a non-negative finite number, got ${totalDurationMs}`);
|
|
72
|
+
}
|
|
73
|
+
for (let i = 0; i < tMs.length; i++) {
|
|
74
|
+
const t = tMs[i];
|
|
75
|
+
if (!Number.isFinite(t) || t < 0) {
|
|
76
|
+
throw new Error(`delaysFromTimestamps: timestamp ${i} is not a non-negative finite number`);
|
|
77
|
+
}
|
|
78
|
+
if (i > 0 && t < tMs[i - 1]) {
|
|
79
|
+
throw new Error(`delaysFromTimestamps: timestamps must be non-decreasing (index ${i}: ${tMs[i - 1]} -> ${t})`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const last = tMs[tMs.length - 1];
|
|
83
|
+
if (totalDurationMs + EPS < last) {
|
|
84
|
+
throw new Error(`delaysFromTimestamps: totalDurationMs (${totalDurationMs}) is before the last timestamp (${last})`);
|
|
85
|
+
}
|
|
86
|
+
const n = tMs.length;
|
|
87
|
+
const delays = new Array(n);
|
|
88
|
+
for (let i = 0; i < n; i++) {
|
|
89
|
+
const end = i === n - 1 ? totalDurationMs : tMs[i + 1];
|
|
90
|
+
delays[i] = end - tMs[i];
|
|
91
|
+
}
|
|
92
|
+
// Fold the lead-in so the delays cover [0, totalDurationMs].
|
|
93
|
+
delays[0] = delays[0] + tMs[0];
|
|
94
|
+
let total = 0;
|
|
95
|
+
let headroom = 0;
|
|
96
|
+
for (let i = 0; i < n; i++) {
|
|
97
|
+
if (delays[i] < MIN_DELAY_MS)
|
|
98
|
+
delays[i] = MIN_DELAY_MS;
|
|
99
|
+
total += delays[i];
|
|
100
|
+
headroom += delays[i] - MIN_DELAY_MS;
|
|
101
|
+
}
|
|
102
|
+
const maxDelayMs = options.maxDelayMs ?? MAX_DELAY_MS;
|
|
103
|
+
if (maxDelayMs < MIN_DELAY_MS) {
|
|
104
|
+
throw new Error(`delaysFromTimestamps: maxDelayMs (${maxDelayMs}) is below MIN_DELAY_MS (${MIN_DELAY_MS})`);
|
|
105
|
+
}
|
|
106
|
+
let surplus = total - totalDurationMs;
|
|
107
|
+
if (surplus <= EPS || headroom <= 0)
|
|
108
|
+
return applyCeiling(delays, maxDelayMs);
|
|
109
|
+
// Proportional reclaim: cannot push any frame below the floor because each
|
|
110
|
+
// frame gives up at most its own share of the available headroom.
|
|
111
|
+
const take = Math.min(surplus, headroom);
|
|
112
|
+
for (let i = 0; i < n; i++) {
|
|
113
|
+
const room = delays[i] - MIN_DELAY_MS;
|
|
114
|
+
if (room <= 0)
|
|
115
|
+
continue;
|
|
116
|
+
delays[i] = delays[i] - (room / headroom) * take;
|
|
117
|
+
if (delays[i] < MIN_DELAY_MS)
|
|
118
|
+
delays[i] = MIN_DELAY_MS;
|
|
119
|
+
}
|
|
120
|
+
// Second pass absorbs float drift from the proportional split.
|
|
121
|
+
let achieved = 0;
|
|
122
|
+
for (let i = 0; i < n; i++)
|
|
123
|
+
achieved += delays[i];
|
|
124
|
+
surplus = achieved - totalDurationMs;
|
|
125
|
+
for (let i = 0; i < n && surplus > EPS; i++) {
|
|
126
|
+
const room = delays[i] - MIN_DELAY_MS;
|
|
127
|
+
if (room <= 0)
|
|
128
|
+
continue;
|
|
129
|
+
const shave = Math.min(room, surplus);
|
|
130
|
+
delays[i] = delays[i] - shave;
|
|
131
|
+
surplus -= shave;
|
|
132
|
+
}
|
|
133
|
+
return applyCeiling(delays, maxDelayMs);
|
|
134
|
+
}
|
|
135
|
+
/** Cap in place. Applied last so nothing can push a delay back over the limit. */
|
|
136
|
+
function applyCeiling(delays, maxDelayMs) {
|
|
137
|
+
for (let i = 0; i < delays.length; i++) {
|
|
138
|
+
if (delays[i] > maxDelayMs)
|
|
139
|
+
delays[i] = maxDelayMs;
|
|
140
|
+
}
|
|
141
|
+
return delays;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Pair items (frame paths, buffers) with legal delays, repeating any item held
|
|
145
|
+
* longer than the ceiling instead of truncating its delay.
|
|
146
|
+
*
|
|
147
|
+
* `delaysFromTimestamps` must cap a long hold, which shortens playback. This
|
|
148
|
+
* keeps real time: a frame held for 166_768ms becomes three copies of that same
|
|
149
|
+
* frame at ~55_589ms each. The result pairs 1:1 with its items, which is what
|
|
150
|
+
* the encoders require (`delays.length === frames.length`), and the delays sum
|
|
151
|
+
* to the same total as the uncapped timeline.
|
|
152
|
+
*
|
|
153
|
+
* Splitting evenly rather than emitting `[65535, 65535, 35698]` keeps the
|
|
154
|
+
* repeats visually indistinguishable in a scrubber and avoids a stray short
|
|
155
|
+
* frame at the end of a hold.
|
|
156
|
+
*
|
|
157
|
+
* @param items One entry per frame, in capture order.
|
|
158
|
+
* @throws if `items` and `tMs` differ in length, or on any input
|
|
159
|
+
* `delaysFromTimestamps` rejects.
|
|
160
|
+
*/
|
|
161
|
+
export function expandForPlayback(items, tMs, totalDurationMs, options = {}) {
|
|
162
|
+
if (items.length !== tMs.length) {
|
|
163
|
+
throw new Error(`expandForPlayback: ${items.length} items but ${tMs.length} timestamps - they must pair 1:1`);
|
|
164
|
+
}
|
|
165
|
+
const maxDelayMs = options.maxDelayMs ?? MAX_DELAY_MS;
|
|
166
|
+
// Uncapped, so the true duration of a long hold survives to be split.
|
|
167
|
+
const trueDelays = delaysFromTimestamps(tMs, totalDurationMs, {
|
|
168
|
+
maxDelayMs: Number.POSITIVE_INFINITY,
|
|
169
|
+
});
|
|
170
|
+
const outItems = [];
|
|
171
|
+
const outDelays = [];
|
|
172
|
+
for (let i = 0; i < items.length; i++) {
|
|
173
|
+
const delay = trueDelays[i];
|
|
174
|
+
if (delay <= maxDelayMs) {
|
|
175
|
+
outItems.push(items[i]);
|
|
176
|
+
outDelays.push(delay);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const repeats = Math.ceil(delay / maxDelayMs);
|
|
180
|
+
const each = delay / repeats;
|
|
181
|
+
for (let r = 0; r < repeats; r++) {
|
|
182
|
+
outItems.push(items[i]);
|
|
183
|
+
outDelays.push(each);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { items: outItems, delays: outDelays };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Intervals where capture stalled: a delta larger than
|
|
190
|
+
* `gapFactor * (1000 / targetFps)`.
|
|
191
|
+
*
|
|
192
|
+
* @throws if `targetFps` or `gapFactor` are not positive.
|
|
193
|
+
*/
|
|
194
|
+
export function findFrameGaps(tMs, targetFps, gapFactor = 3) {
|
|
195
|
+
if (!Number.isFinite(targetFps) || targetFps <= 0) {
|
|
196
|
+
throw new Error(`findFrameGaps: targetFps must be positive, got ${targetFps}`);
|
|
197
|
+
}
|
|
198
|
+
if (!Number.isFinite(gapFactor) || gapFactor <= 0) {
|
|
199
|
+
throw new Error(`findFrameGaps: gapFactor must be positive, got ${gapFactor}`);
|
|
200
|
+
}
|
|
201
|
+
const threshold = gapFactor * (1000 / targetFps);
|
|
202
|
+
const gaps = [];
|
|
203
|
+
for (let i = 0; i + 1 < tMs.length; i++) {
|
|
204
|
+
const delta = tMs[i + 1] - tMs[i];
|
|
205
|
+
if (delta > threshold)
|
|
206
|
+
gaps.push({ after_index: i, gap_ms: delta });
|
|
207
|
+
}
|
|
208
|
+
return gaps;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Clamp a capture rect into the viewport.
|
|
212
|
+
*
|
|
213
|
+
* Origin is clamped to the viewport first, then the extent is trimmed to what
|
|
214
|
+
* remains. `clamped` is true whenever any component changed, which the manifest
|
|
215
|
+
* surfaces as `region_clamped`.
|
|
216
|
+
*
|
|
217
|
+
* @throws if the input rect or the viewport has zero/negative area, or if the
|
|
218
|
+
* rect lies entirely outside the viewport (nothing left to capture).
|
|
219
|
+
*/
|
|
220
|
+
export function clampRect(rect, viewport) {
|
|
221
|
+
if (!Number.isFinite(viewport.width) || !Number.isFinite(viewport.height)) {
|
|
222
|
+
throw new Error("clampRect: viewport dimensions must be finite");
|
|
223
|
+
}
|
|
224
|
+
if (viewport.width <= 0 || viewport.height <= 0) {
|
|
225
|
+
throw new Error(`clampRect: viewport must have positive area, got ${viewport.width}x${viewport.height}`);
|
|
226
|
+
}
|
|
227
|
+
if (!Number.isFinite(rect.x) ||
|
|
228
|
+
!Number.isFinite(rect.y) ||
|
|
229
|
+
!Number.isFinite(rect.width) ||
|
|
230
|
+
!Number.isFinite(rect.height)) {
|
|
231
|
+
throw new Error("clampRect: rect components must be finite");
|
|
232
|
+
}
|
|
233
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
234
|
+
throw new Error(`clampRect: rect must have positive area, got ${rect.width}x${rect.height}`);
|
|
235
|
+
}
|
|
236
|
+
const x = Math.min(Math.max(rect.x, 0), viewport.width);
|
|
237
|
+
const y = Math.min(Math.max(rect.y, 0), viewport.height);
|
|
238
|
+
// Trim the far edge back inside the viewport, then re-measure from the
|
|
239
|
+
// clamped origin so the width can never go negative.
|
|
240
|
+
const right = Math.min(rect.x + rect.width, viewport.width);
|
|
241
|
+
const bottom = Math.min(rect.y + rect.height, viewport.height);
|
|
242
|
+
const width = Math.max(right - x, 0);
|
|
243
|
+
const height = Math.max(bottom - y, 0);
|
|
244
|
+
if (width <= 0 || height <= 0) {
|
|
245
|
+
throw new Error(`clampRect: rect ${rect.width}x${rect.height} at (${rect.x}, ${rect.y}) lies outside the ` +
|
|
246
|
+
`${viewport.width}x${viewport.height} viewport`);
|
|
247
|
+
}
|
|
248
|
+
const clamped = x !== rect.x || y !== rect.y || width !== rect.width || height !== rect.height;
|
|
249
|
+
return { rect: { x, y, width, height }, clamped };
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/recording/timing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBrowser - Cognitive Browser Automation
|
|
3
|
+
* Copyright 2026 Alexandria Eden alexandria.shai.eden@gmail.com
|
|
4
|
+
* Learn more at https://cbrowser.ai - MIT License
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Recording manifest contract.
|
|
8
|
+
*
|
|
9
|
+
* The manifest is the single artifact that describes a recording run: how it
|
|
10
|
+
* was captured, what was captured, and every frame that made it into the
|
|
11
|
+
* output. Encoders, the CLI, and any downstream consumer read this file rather
|
|
12
|
+
* than re-deriving state from the frame directory.
|
|
13
|
+
*
|
|
14
|
+
* Schemas are zod v4; every exported TS type is inferred from its schema so the
|
|
15
|
+
* runtime contract and the compile-time contract cannot drift.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
/**
|
|
19
|
+
* Manifest format version emitted by this build.
|
|
20
|
+
*
|
|
21
|
+
* v2 (2026-07-19) added the two-tier change signal: `key_frames`,
|
|
22
|
+
* `change_points_saturated` and `ssim_thresholds`, all OPTIONAL in the schema
|
|
23
|
+
* (expand-contract, so older files keep parsing) but always emitted, so
|
|
24
|
+
* `version >= 2` reliably signals their presence.
|
|
25
|
+
*
|
|
26
|
+
* v3 (2026-07-19) changed the DETECTOR behind those fields from global SSIM to
|
|
27
|
+
* {@link SSIM_THRESHOLDS} window-min ({@link changeScore}), and re-tuned the
|
|
28
|
+
* thresholds to the new scale (0.999/0.9 -> 0.95/0.6). The field shapes are
|
|
29
|
+
* unchanged, but a v3 `change_points` is not numerically comparable to a v2
|
|
30
|
+
* one — the version is the signal for that, since the values in
|
|
31
|
+
* `ssim_thresholds` alone cannot convey that the underlying metric changed.
|
|
32
|
+
* Readers wanting a uniform view across versions should use {@link changeSignal}.
|
|
33
|
+
*/
|
|
34
|
+
export declare const MANIFEST_VERSION = 3;
|
|
35
|
+
/**
|
|
36
|
+
* Change-detection thresholds for the two-tier signal.
|
|
37
|
+
*
|
|
38
|
+
* These are measured against the {@link changeScore} scale (minimum SSIM over an
|
|
39
|
+
* 8x8 window grid on a 128px signature), NOT global SSIM. The scale is entirely
|
|
40
|
+
* different from a whole-frame metric and the two are not interchangeable — a
|
|
41
|
+
* localized text change reads ~0.87 under changeScore versus ~0.9998 under
|
|
42
|
+
* global SSIM. The producer MUST feed these thresholds changeScore values;
|
|
43
|
+
* `ssim_thresholds` records them in every manifest so a reader never guesses.
|
|
44
|
+
*
|
|
45
|
+
* `CHANGE` (0.95) — "did anything change". Below the practical noise floor
|
|
46
|
+
* (an unchanging page scores exactly 1.0; window-min does not fire on codec
|
|
47
|
+
* noise) and above a 12px counter tick (~0.87), which is the localized change
|
|
48
|
+
* global SSIM could not see at any resolution.
|
|
49
|
+
*
|
|
50
|
+
* `KEY` (0.6) — "did something notable happen". The sparse tier, and the same
|
|
51
|
+
* signal that sets each frame's `anomaly` flag. It sits in the wide gap between
|
|
52
|
+
* a minor tick (~0.87, excluded) and a real interaction like a form fill
|
|
53
|
+
* (~0.21, included), so background chrome does not flood it. Because KEY <
|
|
54
|
+
* CHANGE, `key_frames` is necessarily a subset of `change_points`;
|
|
55
|
+
* {@link changeSignalIssues} checks a manifest honours it.
|
|
56
|
+
*
|
|
57
|
+
* Derived from measurement on five fixtures (static / ticking counter / two
|
|
58
|
+
* form fills / banner toggle / full-viewport animation), 2026-07-19. The
|
|
59
|
+
* counter fixture is why the two tiers matter: change_points fires on every
|
|
60
|
+
* tick and saturates, while key_frames stays empty — so key_frames is the
|
|
61
|
+
* usable signal precisely when change_points is not.
|
|
62
|
+
*/
|
|
63
|
+
export declare const SSIM_THRESHOLDS: {
|
|
64
|
+
readonly change: 0.95;
|
|
65
|
+
readonly key: 0.6;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Fraction of frames above which `change_points` is considered saturated —
|
|
69
|
+
* carrying no discriminating information for that capture.
|
|
70
|
+
*/
|
|
71
|
+
export declare const CHANGE_SATURATION_RATIO = 0.7;
|
|
72
|
+
export declare const EngineSchema: z.ZodEnum<{
|
|
73
|
+
chromium: "chromium";
|
|
74
|
+
firefox: "firefox";
|
|
75
|
+
webkit: "webkit";
|
|
76
|
+
}>;
|
|
77
|
+
export declare const CaptureMethodSchema: z.ZodEnum<{
|
|
78
|
+
"cdp-screencast": "cdp-screencast";
|
|
79
|
+
"screenshot-loop": "screenshot-loop";
|
|
80
|
+
}>;
|
|
81
|
+
export declare const ViewportSchema: z.ZodObject<{
|
|
82
|
+
width: z.ZodNumber;
|
|
83
|
+
height: z.ZodNumber;
|
|
84
|
+
deviceScaleFactor: z.ZodNumber;
|
|
85
|
+
}, z.core.$strip>;
|
|
86
|
+
export declare const RectSchema: z.ZodObject<{
|
|
87
|
+
x: z.ZodNumber;
|
|
88
|
+
y: z.ZodNumber;
|
|
89
|
+
width: z.ZodNumber;
|
|
90
|
+
height: z.ZodNumber;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
export declare const DimsSchema: z.ZodObject<{
|
|
93
|
+
width: z.ZodNumber;
|
|
94
|
+
height: z.ZodNumber;
|
|
95
|
+
}, z.core.$strip>;
|
|
96
|
+
/**
|
|
97
|
+
* What the recorder was pointed at. Discriminated on `kind` so encoders can
|
|
98
|
+
* switch exhaustively without guessing from the presence of optional fields.
|
|
99
|
+
*/
|
|
100
|
+
export declare const TargetSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
101
|
+
kind: z.ZodLiteral<"viewport">;
|
|
102
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
103
|
+
kind: z.ZodLiteral<"region">;
|
|
104
|
+
rect: z.ZodObject<{
|
|
105
|
+
x: z.ZodNumber;
|
|
106
|
+
y: z.ZodNumber;
|
|
107
|
+
width: z.ZodNumber;
|
|
108
|
+
height: z.ZodNumber;
|
|
109
|
+
}, z.core.$strip>;
|
|
110
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
111
|
+
kind: z.ZodLiteral<"element">;
|
|
112
|
+
selector: z.ZodString;
|
|
113
|
+
padding: z.ZodNumber;
|
|
114
|
+
}, z.core.$strip>], "kind">;
|
|
115
|
+
/**
|
|
116
|
+
* Console and network records are kept deliberately permissive: the recorder
|
|
117
|
+
* owns their exact shape and may attach engine-specific extras. Only the fields
|
|
118
|
+
* the encoders and reports actually read are required; unknown keys survive
|
|
119
|
+
* parsing untouched.
|
|
120
|
+
*/
|
|
121
|
+
export declare const ConsoleEntrySchema: z.ZodObject<{
|
|
122
|
+
t_ms: z.ZodNumber;
|
|
123
|
+
type: z.ZodString;
|
|
124
|
+
text: z.ZodString;
|
|
125
|
+
}, z.core.$loose>;
|
|
126
|
+
export declare const NetworkEntrySchema: z.ZodObject<{
|
|
127
|
+
t_ms: z.ZodNumber;
|
|
128
|
+
url: z.ZodString;
|
|
129
|
+
method: z.ZodOptional<z.ZodString>;
|
|
130
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
131
|
+
failed: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
+
}, z.core.$loose>;
|
|
133
|
+
export declare const FrameSchema: z.ZodObject<{
|
|
134
|
+
index: z.ZodNumber;
|
|
135
|
+
t_ms: z.ZodNumber;
|
|
136
|
+
path: z.ZodString;
|
|
137
|
+
crop: z.ZodObject<{
|
|
138
|
+
x: z.ZodNumber;
|
|
139
|
+
y: z.ZodNumber;
|
|
140
|
+
width: z.ZodNumber;
|
|
141
|
+
height: z.ZodNumber;
|
|
142
|
+
}, z.core.$strip>;
|
|
143
|
+
ssim_prev: z.ZodNullable<z.ZodNumber>;
|
|
144
|
+
anomaly: z.ZodBoolean;
|
|
145
|
+
tracking: z.ZodNullable<z.ZodEnum<{
|
|
146
|
+
ok: "ok";
|
|
147
|
+
stale: "stale";
|
|
148
|
+
}>>;
|
|
149
|
+
console: z.ZodArray<z.ZodObject<{
|
|
150
|
+
t_ms: z.ZodNumber;
|
|
151
|
+
type: z.ZodString;
|
|
152
|
+
text: z.ZodString;
|
|
153
|
+
}, z.core.$loose>>;
|
|
154
|
+
network: z.ZodArray<z.ZodObject<{
|
|
155
|
+
t_ms: z.ZodNumber;
|
|
156
|
+
url: z.ZodString;
|
|
157
|
+
method: z.ZodOptional<z.ZodString>;
|
|
158
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
159
|
+
failed: z.ZodOptional<z.ZodBoolean>;
|
|
160
|
+
}, z.core.$loose>>;
|
|
161
|
+
}, z.core.$strip>;
|
|
162
|
+
export declare const FrameGapSchema: z.ZodObject<{
|
|
163
|
+
after_index: z.ZodNumber;
|
|
164
|
+
gap_ms: z.ZodNumber;
|
|
165
|
+
}, z.core.$strip>;
|
|
166
|
+
export declare const RecordingManifestSchema: z.ZodObject<{
|
|
167
|
+
version: z.ZodNumber;
|
|
168
|
+
slug: z.ZodString;
|
|
169
|
+
engine: z.ZodEnum<{
|
|
170
|
+
chromium: "chromium";
|
|
171
|
+
firefox: "firefox";
|
|
172
|
+
webkit: "webkit";
|
|
173
|
+
}>;
|
|
174
|
+
capture_method: z.ZodEnum<{
|
|
175
|
+
"cdp-screencast": "cdp-screencast";
|
|
176
|
+
"screenshot-loop": "screenshot-loop";
|
|
177
|
+
}>;
|
|
178
|
+
target_fps: z.ZodNumber;
|
|
179
|
+
actual_fps: z.ZodNumber;
|
|
180
|
+
viewport: z.ZodObject<{
|
|
181
|
+
width: z.ZodNumber;
|
|
182
|
+
height: z.ZodNumber;
|
|
183
|
+
deviceScaleFactor: z.ZodNumber;
|
|
184
|
+
}, z.core.$strip>;
|
|
185
|
+
target: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
186
|
+
kind: z.ZodLiteral<"viewport">;
|
|
187
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
188
|
+
kind: z.ZodLiteral<"region">;
|
|
189
|
+
rect: z.ZodObject<{
|
|
190
|
+
x: z.ZodNumber;
|
|
191
|
+
y: z.ZodNumber;
|
|
192
|
+
width: z.ZodNumber;
|
|
193
|
+
height: z.ZodNumber;
|
|
194
|
+
}, z.core.$strip>;
|
|
195
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
196
|
+
kind: z.ZodLiteral<"element">;
|
|
197
|
+
selector: z.ZodString;
|
|
198
|
+
padding: z.ZodNumber;
|
|
199
|
+
}, z.core.$strip>], "kind">;
|
|
200
|
+
start_trigger: z.ZodNullable<z.ZodString>;
|
|
201
|
+
stop_trigger: z.ZodNullable<z.ZodString>;
|
|
202
|
+
trigger_timeout: z.ZodBoolean;
|
|
203
|
+
started_at: z.ZodString;
|
|
204
|
+
duration_ms: z.ZodNumber;
|
|
205
|
+
output_dims: z.ZodObject<{
|
|
206
|
+
width: z.ZodNumber;
|
|
207
|
+
height: z.ZodNumber;
|
|
208
|
+
}, z.core.$strip>;
|
|
209
|
+
frames: z.ZodArray<z.ZodObject<{
|
|
210
|
+
index: z.ZodNumber;
|
|
211
|
+
t_ms: z.ZodNumber;
|
|
212
|
+
path: z.ZodString;
|
|
213
|
+
crop: z.ZodObject<{
|
|
214
|
+
x: z.ZodNumber;
|
|
215
|
+
y: z.ZodNumber;
|
|
216
|
+
width: z.ZodNumber;
|
|
217
|
+
height: z.ZodNumber;
|
|
218
|
+
}, z.core.$strip>;
|
|
219
|
+
ssim_prev: z.ZodNullable<z.ZodNumber>;
|
|
220
|
+
anomaly: z.ZodBoolean;
|
|
221
|
+
tracking: z.ZodNullable<z.ZodEnum<{
|
|
222
|
+
ok: "ok";
|
|
223
|
+
stale: "stale";
|
|
224
|
+
}>>;
|
|
225
|
+
console: z.ZodArray<z.ZodObject<{
|
|
226
|
+
t_ms: z.ZodNumber;
|
|
227
|
+
type: z.ZodString;
|
|
228
|
+
text: z.ZodString;
|
|
229
|
+
}, z.core.$loose>>;
|
|
230
|
+
network: z.ZodArray<z.ZodObject<{
|
|
231
|
+
t_ms: z.ZodNumber;
|
|
232
|
+
url: z.ZodString;
|
|
233
|
+
method: z.ZodOptional<z.ZodString>;
|
|
234
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
235
|
+
failed: z.ZodOptional<z.ZodBoolean>;
|
|
236
|
+
}, z.core.$loose>>;
|
|
237
|
+
}, z.core.$strip>>;
|
|
238
|
+
change_points: z.ZodArray<z.ZodNumber>;
|
|
239
|
+
key_frames: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
240
|
+
change_points_saturated: z.ZodOptional<z.ZodBoolean>;
|
|
241
|
+
ssim_thresholds: z.ZodOptional<z.ZodObject<{
|
|
242
|
+
change: z.ZodNumber;
|
|
243
|
+
key: z.ZodNumber;
|
|
244
|
+
}, z.core.$strip>>;
|
|
245
|
+
method: z.ZodOptional<z.ZodString>;
|
|
246
|
+
frame_gaps: z.ZodArray<z.ZodObject<{
|
|
247
|
+
after_index: z.ZodNumber;
|
|
248
|
+
gap_ms: z.ZodNumber;
|
|
249
|
+
}, z.core.$strip>>;
|
|
250
|
+
region_clamped: z.ZodOptional<z.ZodBoolean>;
|
|
251
|
+
artifacts: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
252
|
+
}, z.core.$strip>;
|
|
253
|
+
export type Engine = z.infer<typeof EngineSchema>;
|
|
254
|
+
export type CaptureMethod = z.infer<typeof CaptureMethodSchema>;
|
|
255
|
+
export type Viewport = z.infer<typeof ViewportSchema>;
|
|
256
|
+
export type Rect = z.infer<typeof RectSchema>;
|
|
257
|
+
export type Dims = z.infer<typeof DimsSchema>;
|
|
258
|
+
export type RecordingTarget = z.infer<typeof TargetSchema>;
|
|
259
|
+
export type ConsoleEntry = z.infer<typeof ConsoleEntrySchema>;
|
|
260
|
+
export type NetworkEntry = z.infer<typeof NetworkEntrySchema>;
|
|
261
|
+
export type Frame = z.infer<typeof FrameSchema>;
|
|
262
|
+
export type FrameGap = z.infer<typeof FrameGapSchema>;
|
|
263
|
+
export type RecordingManifest = z.infer<typeof RecordingManifestSchema>;
|
|
264
|
+
/**
|
|
265
|
+
* Parse and validate a manifest.
|
|
266
|
+
*
|
|
267
|
+
* Accepts an already-parsed object or a raw JSON string. Throws an `Error`
|
|
268
|
+
* whose message lists every failing path, so a malformed manifest reports all
|
|
269
|
+
* of its problems at once instead of one per run.
|
|
270
|
+
*/
|
|
271
|
+
/**
|
|
272
|
+
* Uniform view of the change signal across manifest versions.
|
|
273
|
+
*
|
|
274
|
+
* A pre-v2 manifest has only `change_points`, computed with a whole-frame
|
|
275
|
+
* (global) SSIM detector — a DIFFERENT metric from the current window-min
|
|
276
|
+
* {@link SSIM_THRESHOLDS} scale, so the two are not numerically comparable. For
|
|
277
|
+
* such a file this returns the single list it has as both tiers rather than
|
|
278
|
+
* inventing a key/change split it never carried, and marks `legacy: true` so a
|
|
279
|
+
* reader knows the tiers are not really separated and the thresholds are not on
|
|
280
|
+
* the current scale. `saturated` is false because the old global detector could
|
|
281
|
+
* not saturate the way window-min can.
|
|
282
|
+
*
|
|
283
|
+
* This is NOT the exact identity an earlier version of this comment claimed:
|
|
284
|
+
* once the detector changed from global SSIM to window-min, an old file's
|
|
285
|
+
* change points stopped mapping onto today's `key_frames`. Reporting the one
|
|
286
|
+
* list for both tiers is the honest fallback, not a translation.
|
|
287
|
+
*
|
|
288
|
+
* Use this in readers instead of touching the optional fields directly, so a
|
|
289
|
+
* pre-v2 file does not read as "no key frames" when its change_points are the
|
|
290
|
+
* only signal it has.
|
|
291
|
+
*/
|
|
292
|
+
export declare function changeSignal(manifest: RecordingManifest): {
|
|
293
|
+
changePoints: number[];
|
|
294
|
+
keyFrames: number[];
|
|
295
|
+
saturated: boolean;
|
|
296
|
+
thresholds: {
|
|
297
|
+
change: number;
|
|
298
|
+
key: number;
|
|
299
|
+
};
|
|
300
|
+
legacy: boolean;
|
|
301
|
+
manifestVersion: number;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Consistency problems in a manifest's change signal, as human-readable
|
|
305
|
+
* strings. Empty means consistent.
|
|
306
|
+
*
|
|
307
|
+
* These are invariants the two-tier design guarantees by construction, so a
|
|
308
|
+
* violation means the producer is miscomputing rather than that the file is
|
|
309
|
+
* merely old. Deliberately NOT enforced inside {@link parseManifest}: readers
|
|
310
|
+
* such as `capture status` open whatever manifest is most recent, and failing
|
|
311
|
+
* the read outright would turn a reporting bug into an unreadable capture. Call
|
|
312
|
+
* this where you want the check — tests, or a diagnostic path.
|
|
313
|
+
*/
|
|
314
|
+
export declare function changeSignalIssues(manifest: RecordingManifest): string[];
|
|
315
|
+
export declare function parseManifest(input: unknown): RecordingManifest;
|
|
316
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/recording/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,eAAe;;;CAAsC,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAM3C,eAAO,MAAM,YAAY;;;;EAA4C,CAAC;AAEtE,eAAO,MAAM,mBAAmB;;;EAAgD,CAAC;AAEjF,eAAO,MAAM,cAAc;;;;iBAIzB,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;iBAKrB,CAAC;AAEH,eAAO,MAAM,UAAU;;;iBAGrB,CAAC;AAMH;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;2BAQvB,CAAC;AAMH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;iBAMrB,CAAC;AAEX,eAAO,MAAM,kBAAkB;;;;;;iBAQrB,CAAC;AAMX,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiBtB,CAAC;AAEH,eAAO,MAAM,cAAc;;;iBAIzB,CAAC;AAMH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgElC,CAAC;AAMH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAC9C,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAChD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAMxE;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG;IACzD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;CACzB,CAaA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,EAAE,CA2CxE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAsB/D"}
|