@crouton-kit/capture 1.3.8 → 1.3.9
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/bin/capture +29841 -8539
- package/package.json +6 -3
- package/src/capture.ts +86 -173
- package/src/cdp/a11y.ts +113 -14
- package/src/cdp/args.ts +300 -205
- package/src/cdp/bridge/protocol.ts +93 -6
- package/src/cdp/bridge/server.ts +118 -53
- package/src/cdp/bridge/spawn.ts +53 -6
- package/src/cdp/client.ts +13 -3
- package/src/cdp/commands/bridge-serve.ts +20 -14
- package/src/cdp/commands/cdp.ts +259 -90
- package/src/cdp/commands/gate-guard.ts +43 -0
- package/src/cdp/commands/lib.ts +339 -213
- package/src/cdp/commands/measure/census.ts +76 -20
- package/src/cdp/commands/measure/check.ts +100 -26
- package/src/cdp/commands/measure/diff.ts +109 -21
- package/src/cdp/commands/measure/explain.ts +121 -19
- package/src/cdp/commands/measure/index.ts +31 -39
- package/src/cdp/commands/measure/map-ax.ts +52 -0
- package/src/cdp/commands/measure/map-focus.ts +41 -13
- package/src/cdp/commands/measure/map-layers.ts +49 -15
- package/src/cdp/commands/measure/map-scroll.ts +85 -15
- package/src/cdp/commands/measure/snap.ts +405 -25
- package/src/cdp/commands/measure/sweep.ts +217 -20
- package/src/cdp/commands/motion/index.ts +18 -17
- package/src/cdp/commands/motion/jank.ts +119 -9
- package/src/cdp/commands/motion/mask.ts +81 -12
- package/src/cdp/commands/motion/rec.ts +520 -19
- package/src/cdp/commands/motion/response.ts +65 -15
- package/src/cdp/commands/motion/timeline.ts +139 -14
- package/src/cdp/commands/page/click.ts +194 -0
- package/src/cdp/commands/page/elements.ts +154 -0
- package/src/cdp/commands/page/exec.ts +317 -0
- package/src/cdp/commands/page/index.ts +72 -0
- package/src/cdp/commands/page/navigate.ts +330 -0
- package/src/cdp/commands/page/scroll.ts +107 -0
- package/src/cdp/commands/page/shot.ts +208 -0
- package/src/cdp/commands/page/type.ts +104 -0
- package/src/cdp/commands/tab/index.ts +59 -0
- package/src/cdp/commands/tab/list.ts +167 -0
- package/src/cdp/commands/tab/network.ts +162 -0
- package/src/cdp/commands/tab/open.ts +90 -0
- package/src/cdp/commands/tab/reset.ts +160 -0
- package/src/cdp/connection.ts +368 -37
- package/src/cdp/detect.ts +42 -13
- package/src/cdp/dispatch.ts +74 -28
- package/src/cdp/har-recorder.ts +648 -115
- package/src/cdp/leaf-grammar.ts +99 -0
- package/src/cdp/measure/census.ts +205 -0
- package/src/cdp/measure/check.ts +318 -0
- package/src/cdp/measure/collectors/animation.ts +385 -0
- package/src/cdp/measure/collectors/ax.ts +278 -0
- package/src/cdp/measure/collectors/focus.ts +1041 -0
- package/src/cdp/measure/collectors/forms.ts +905 -0
- package/src/cdp/measure/collectors/geometry.ts +1038 -0
- package/src/cdp/measure/collectors/hittest.ts +835 -0
- package/src/cdp/measure/collectors/layers.ts +697 -0
- package/src/cdp/measure/collectors/media.ts +522 -0
- package/src/cdp/measure/collectors/pixels.ts +1584 -0
- package/src/cdp/measure/collectors/queries.ts +464 -0
- package/src/cdp/measure/collectors/scroll.ts +599 -0
- package/src/cdp/measure/collectors/states.ts +992 -0
- package/src/cdp/measure/collectors/style-provenance.ts +643 -0
- package/src/cdp/measure/collectors/styles.ts +355 -0
- package/src/cdp/measure/collectors/text.ts +713 -0
- package/src/cdp/measure/diff.ts +470 -0
- package/src/cdp/measure/explain.ts +498 -0
- package/src/cdp/measure/map-ax.ts +633 -0
- package/src/cdp/measure/map-focus.ts +188 -0
- package/src/cdp/measure/map-layers.ts +177 -0
- package/src/cdp/measure/map-scroll.ts +317 -0
- package/src/cdp/measure/redaction.ts +51 -0
- package/src/cdp/measure/settle.ts +1051 -0
- package/src/cdp/measure/snapshot.ts +390 -0
- package/src/cdp/measure/sweep.ts +330 -0
- package/src/cdp/measure/types.ts +220 -0
- package/src/cdp/measure/v2/collectors.ts +19 -0
- package/src/cdp/measure/v2/orchestrator.ts +46 -0
- package/src/cdp/measure/v2/snap-selection.ts +33 -0
- package/src/cdp/measure/v2/target-lock.ts +15 -0
- package/src/cdp/measure/v2/target.ts +16 -0
- package/src/cdp/motion/jank.ts +433 -0
- package/src/cdp/motion/mask.ts +421 -0
- package/src/cdp/motion/recorder.ts +764 -0
- package/src/cdp/motion/response.ts +356 -0
- package/src/cdp/motion/timeline.ts +215 -0
- package/src/cdp/record.ts +5 -212
- package/src/cdp/recorder-bridge.ts +1478 -105
- package/src/cdp/recorder-client.ts +326 -0
- package/src/cdp/scope-lock.ts +73 -0
- package/src/cdp/screenshot.ts +199 -66
- package/src/cdp/targets.ts +21 -1
- package/src/cdp/timing.ts +15 -10
- package/src/cdp/types.ts +22 -7
- package/src/cdp/viewport.ts +21 -0
- package/src/cdp.ts +12 -58
- package/src/command-help.ts +29 -0
- package/src/command-resolution.ts +22 -0
- package/src/command-walker.ts +35 -0
- package/src/contracts/a11y.ts +33 -0
- package/src/contracts/cursor.ts +35 -0
- package/src/contracts/index.ts +15 -0
- package/src/contracts/motion.ts +19 -0
- package/src/contracts/neutral.ts +56 -0
- package/src/contracts/primitives.ts +205 -0
- package/src/contracts/release.ts +17 -0
- package/src/contracts/results.ts +208 -0
- package/src/contracts/routes.ts +140 -0
- package/src/contracts/snapshot.ts +92 -0
- package/src/cursor/index.ts +13 -0
- package/src/cursor/service.ts +310 -0
- package/src/errors.ts +71 -0
- package/src/har-manager.ts +992 -41
- package/src/interact.ts +410 -98
- package/src/output/artifact-lifecycle.ts +371 -0
- package/src/output/artifact.ts +6 -0
- package/src/output/exact-raw.ts +114 -0
- package/src/output/render.ts +396 -2
- package/src/output/structured.ts +211 -0
- package/src/registry.ts +208 -0
- package/src/session/artifacts.ts +452 -200
- package/src/session/commands.ts +1162 -397
- package/src/session/coordinator.ts +251 -0
- package/src/session/har-filter.ts +57 -0
- package/src/session/log-tailer.ts +653 -0
- package/src/session-context.ts +279 -51
- package/src/vault/bundle.ts +32 -17
- package/src/cdp/commands/exec.ts +0 -105
- package/src/cdp/commands/har.ts +0 -128
- package/src/cdp/commands/tabs.ts +0 -183
- package/src/cdp/commands/traffic.ts +0 -121
- package/src/cdp/commands/ui.ts +0 -172
- package/src/cdp/execute.ts +0 -113
- package/src/cdp/har-output.ts +0 -47
- package/src/cdp/usage.ts +0 -79
- package/src/tab-session.ts +0 -91
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* command does — leaf commands never need to know they're nested under a
|
|
8
8
|
* branch.
|
|
9
9
|
*/
|
|
10
|
+
import { invalidInput } from '../../../errors.js';
|
|
10
11
|
import { type ParsedArgs } from '../../types.js';
|
|
11
12
|
import { cmdMeasureSnap } from './snap.js';
|
|
12
13
|
import { cmdMeasureCheck } from './check.js';
|
|
@@ -17,53 +18,44 @@ import { cmdMeasureSweep } from './sweep.js';
|
|
|
17
18
|
import { cmdMeasureMapFocus } from './map-focus.js';
|
|
18
19
|
import { cmdMeasureMapScroll } from './map-scroll.js';
|
|
19
20
|
import { cmdMeasureMapLayers } from './map-layers.js';
|
|
21
|
+
import { cmdMeasureMapAx } from './map-ax.js';
|
|
22
|
+
|
|
23
|
+
/** Root-help representation of this branch, assembled by `src/capture.ts`. */
|
|
24
|
+
export const COMMAND_BLOCK = `<command name="measure">
|
|
25
|
+
static facts over a settled snapshot — \`snap\` writes the substrate, every other leaf is a read-only query over it
|
|
26
|
+
use when measuring layout/content/targetability facts, diffing snapshots, or reading one facet (focus, scroll, layers, ax) of the substrate
|
|
27
|
+
snap · check · diff · census · explain · sweep · map — \`capture measure -h\`
|
|
28
|
+
</command>`;
|
|
20
29
|
|
|
21
30
|
export const MEASURE_USAGE = `capture measure — enriched snapshot substrate + read-only queries over it.
|
|
22
31
|
|
|
23
32
|
\`snap\` drives the page (or a base snapshot) and writes one settled artifact
|
|
24
33
|
directory; every other leaf below is a cheap read over that artifact and
|
|
25
|
-
never re-drives the browser unless
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
[--freeze-animations] [--settle-timeout <ms>] [--capture-unsettled]
|
|
30
|
-
[--pixels] [--state <state[:selector]>]...
|
|
31
|
-
|
|
32
|
-
check [url|snap] [--for <checks>] [--viewport <WxH>]... [--gate]
|
|
33
|
-
Threshold/fact checks over a snapshot
|
|
34
|
-
|
|
35
|
-
diff --before <snap> --after <snap> [--pixels] [--full] [--gate]
|
|
36
|
-
Structured before/after snapshot diff
|
|
37
|
-
|
|
38
|
-
census [--snap <id>]... [--url <url>]... [--set-file <path>] --axis <axis>
|
|
39
|
-
Value distributions across one or more snapshots
|
|
40
|
-
(--snap/--url repeatable)
|
|
34
|
+
never re-drives the browser unless its target is a URL (which snaps first).
|
|
35
|
+
Every leaf defaults to rendered prose; --json mirrors the same result.
|
|
36
|
+
Findings exit 0 — a report, not a failure.
|
|
37
|
+
\`--gate\` (exit 2 on findings/changes) is accepted only by check and diff.
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
<subcommand name="snap" args="[url|snap] [--freeze-animations] [--settle-timeout <ms>] [--capture-unsettled] [--pixels] [--state <state[:selector]>]... [--viewport <WxH>]..." whenToUse="drive + write the settled snapshot substrate every other leaf reads"/>
|
|
40
|
+
<subcommand name="check" args="[url|snap] [--for <checks>] [--gate]" whenToUse="read threshold/fact measurements from one snapshot"/>
|
|
41
|
+
<subcommand name="diff" args="--before <snap> --after <snap> [--pixels] [--full] [--gate]" whenToUse="structured before/after delta between two snapshots"/>
|
|
42
|
+
<subcommand name="census" args="[--snap <id>]... [--url <url>]... [--set-file <path>] --axis <axis>" whenToUse="value distributions across one or more snapshots"/>
|
|
43
|
+
<subcommand name="explain" args="<snap> --selector <sel> [--size] [--text] [--form]" whenToUse="per-element cascade/stacking/clipping/size/text/form explanation"/>
|
|
44
|
+
<subcommand name="sweep" args="[url] --axis <axis> [--from <val>] [--to <val>] [--viewport-height <val>]" whenToUse="responsive/environment sampling across an axis"/>
|
|
45
|
+
<subcommand name="map" args="focus|scroll|layers|ax [url|snap]" whenToUse="read one facet of a snapshot's substrate — capture measure map -h"/>
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
Responsive/environment sampling
|
|
47
|
-
|
|
48
|
-
map focus|scroll|layers [url|snap] Read one facet of a snapshot's substrate
|
|
49
|
-
(see \`capture measure map --help\`)
|
|
50
|
-
|
|
51
|
-
Every leaf defaults to rendered prose; --json mirrors the same result as JSON.
|
|
52
|
-
Exit codes: 0 by default — findings are a report, not a failure.
|
|
53
|
-
--gate exits 2 on findings/changes; only \`check\` and \`diff\` accept it.
|
|
54
|
-
|
|
55
|
-
capture measure <leaf> --help Per-leaf usage`;
|
|
47
|
+
capture measure <leaf> -h Per-leaf usage`;
|
|
56
48
|
|
|
57
49
|
export const MEASURE_MAP_USAGE = `capture measure map — read one facet of a snapshot's substrate (no browser re-drive).
|
|
58
50
|
|
|
59
|
-
Leaves:
|
|
60
|
-
focus [url|snap] Keyboard traversal order (focus.json)
|
|
61
|
-
scroll [url|snap] Scroll-container topology (scroll.json)
|
|
62
|
-
layers [url|snap] Paint/compositor layer map (layers.json)
|
|
63
|
-
|
|
64
51
|
A URL target first creates a snap; a snap target reads its existing artifact.
|
|
65
52
|
|
|
66
|
-
|
|
53
|
+
<subcommand name="focus" args="[url|snap]" whenToUse="keyboard traversal order (focus.json)"/>
|
|
54
|
+
<subcommand name="scroll" args="[url|snap]" whenToUse="scroll-container topology (scroll.json)"/>
|
|
55
|
+
<subcommand name="layers" args="[url|snap]" whenToUse="paint/compositor layer map (layers.json)"/>
|
|
56
|
+
<subcommand name="ax" args="[url|snap]" whenToUse="AX-tree ↔ layout map (ax.json + geometry.json)"/>
|
|
57
|
+
|
|
58
|
+
capture measure map <leaf> -h Per-leaf usage`;
|
|
67
59
|
|
|
68
60
|
export async function measureMain(parsed: ParsedArgs, args: string[]): Promise<void> {
|
|
69
61
|
const leaf = parsed.positional[0];
|
|
@@ -88,8 +80,7 @@ export async function measureMain(parsed: ParsedArgs, args: string[]): Promise<v
|
|
|
88
80
|
console.log(MEASURE_USAGE);
|
|
89
81
|
return;
|
|
90
82
|
default:
|
|
91
|
-
|
|
92
|
-
process.exit(1);
|
|
83
|
+
throw invalidInput(`Unknown measure leaf: ${leaf}.`, 'unknown_command');
|
|
93
84
|
}
|
|
94
85
|
}
|
|
95
86
|
|
|
@@ -104,11 +95,12 @@ async function measureMapMain(parsed: ParsedArgs, args: string[]): Promise<void>
|
|
|
104
95
|
return cmdMeasureMapScroll(rest, args);
|
|
105
96
|
case 'layers':
|
|
106
97
|
return cmdMeasureMapLayers(rest, args);
|
|
98
|
+
case 'ax':
|
|
99
|
+
return cmdMeasureMapAx(rest, args);
|
|
107
100
|
case undefined:
|
|
108
101
|
console.log(MEASURE_MAP_USAGE);
|
|
109
102
|
return;
|
|
110
103
|
default:
|
|
111
|
-
|
|
112
|
-
process.exit(1);
|
|
104
|
+
throw invalidInput(`Unknown measure map leaf: ${sub}.`, 'unknown_command');
|
|
113
105
|
}
|
|
114
106
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { captureMeasureSnap } from './snap.js';
|
|
3
|
+
import { buildMeasureMapAxResult } from '../../measure/map-ax.js';
|
|
4
|
+
import { ArtifactResolutionError, resolveSnapRef } from '../../../output/artifact.js';
|
|
5
|
+
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
6
|
+
|
|
7
|
+
const USAGE = `capture measure map ax [url|snap] — accessibility-tree facts recorded in a snapshot's ax.json joined against geometry.json
|
|
8
|
+
|
|
9
|
+
input:
|
|
10
|
+
[url|snap] required target: a URL creates a settled snapshot first; a snapshot id or absolute path is read without re-driving the browser
|
|
11
|
+
output: <ax-map …> — non-ignored AX nodes with role, name, states, backendNodeId, and top-viewport rect; ignored AX nodes with their ignored-reasons; DOM elements with rendered boxes but no non-ignored AX node; and AX nodes whose rect is offscreen, clipped, or zero-size; --json mirrors
|
|
12
|
+
effects: read-only over an existing snapshot artifact; a URL target writes one settled snapshot first`;
|
|
13
|
+
|
|
14
|
+
function recoveryResult(err: unknown): RenderableResult {
|
|
15
|
+
const detail = err instanceof ArtifactResolutionError || err instanceof Error
|
|
16
|
+
? err.message
|
|
17
|
+
: 'The snapshot artifacts could not be resolved.';
|
|
18
|
+
return {
|
|
19
|
+
tag: 'error',
|
|
20
|
+
attrs: { command: 'measure map ax', status: 'artifact_unavailable' },
|
|
21
|
+
summary: fact`AX-map facts could not be read: ${detail}`,
|
|
22
|
+
followUp: fact`Create a settled snapshot with \`capture measure snap <url>\`, then run \`capture measure map ax <snap>\`.`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function cmdMeasureMapAx(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
27
|
+
if (parsed.help) {
|
|
28
|
+
console.log(USAGE);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (parsed.positional.length !== 1) {
|
|
32
|
+
emitResult({
|
|
33
|
+
tag: 'error',
|
|
34
|
+
attrs: { command: 'measure map ax', status: 'invalid_target' },
|
|
35
|
+
summary: fact`Expected exactly one URL or snapshot target; received ${parsed.positional.length} positional target(s).`,
|
|
36
|
+
followUp: fact`Run \`capture measure map ax <snap>\` or \`capture measure map ax <url>\`.`,
|
|
37
|
+
}, { json: parsed.json });
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const target = parsed.positional[0]!;
|
|
44
|
+
const ref = await resolveSnapRef(target, {
|
|
45
|
+
onUrl: async (url) => captureMeasureSnap({ ...parsed, positional: [url] }, url),
|
|
46
|
+
});
|
|
47
|
+
emitResult(buildMeasureMapAxResult(ref), { json: parsed.json });
|
|
48
|
+
} catch (err) {
|
|
49
|
+
emitResult(recoveryResult(err), { json: parsed.json });
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,24 +1,52 @@
|
|
|
1
1
|
import { type ParsedArgs } from '../../types.js';
|
|
2
|
+
import { captureMeasureSnap } from './snap.js';
|
|
3
|
+
import { mapFocus } from '../../measure/map-focus.js';
|
|
4
|
+
import { ArtifactResolutionError, resolveSnapRef } from '../../../output/artifact.js';
|
|
2
5
|
import { emitResult, fact, type RenderableResult } from '../../../output/render.js';
|
|
3
6
|
|
|
4
|
-
const USAGE = `
|
|
7
|
+
const USAGE = `capture measure map focus [url|snap] — keyboard traversal facts recorded in a snapshot's focus.json
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
input:
|
|
10
|
+
[url|snap] required target: a URL creates a settled snapshot first; a snapshot id or absolute path is read without re-driving the browser
|
|
11
|
+
output: <focus-map …> — forward and reverse Tab sequences, top-viewport rects, scroll jumps, focus-visible style facts, and unreached focusable elements; --json mirrors
|
|
12
|
+
effects: read-only over an existing snapshot artifact; a URL target writes one settled snapshot first`;
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
function recoveryResult(err: unknown): RenderableResult {
|
|
15
|
+
const detail = err instanceof ArtifactResolutionError || err instanceof Error
|
|
16
|
+
? err.message
|
|
17
|
+
: 'The focus artifact could not be resolved.';
|
|
18
|
+
return {
|
|
19
|
+
tag: 'error',
|
|
20
|
+
attrs: { command: 'measure map focus', status: 'artifact_unavailable' },
|
|
21
|
+
summary: fact`Focus-map facts could not be read: ${detail}`,
|
|
22
|
+
followUp: fact`Create a settled snapshot with \`capture measure snap <url>\`, then run \`capture measure map focus <snap>\`.`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
11
25
|
|
|
12
26
|
export async function cmdMeasureMapFocus(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
13
27
|
if (parsed.help) {
|
|
14
28
|
console.log(USAGE);
|
|
15
|
-
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (parsed.positional.length !== 1) {
|
|
32
|
+
emitResult({
|
|
33
|
+
tag: 'error',
|
|
34
|
+
attrs: { command: 'measure map focus', status: 'invalid_target' },
|
|
35
|
+
summary: fact`Expected exactly one URL or snapshot target; received ${parsed.positional.length} positional target(s).`,
|
|
36
|
+
followUp: fact`Run \`capture measure map focus <snap>\` or \`capture measure map focus <url>\`.`,
|
|
37
|
+
}, { json: parsed.json });
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const target = parsed.positional[0]!;
|
|
44
|
+
const ref = await resolveSnapRef(target, {
|
|
45
|
+
onUrl: async (url) => captureMeasureSnap({ ...parsed, positional: [url] }, url),
|
|
46
|
+
});
|
|
47
|
+
emitResult(mapFocus(ref), { json: parsed.json });
|
|
48
|
+
} catch (err) {
|
|
49
|
+
emitResult(recoveryResult(err), { json: parsed.json });
|
|
50
|
+
process.exitCode = 1;
|
|
16
51
|
}
|
|
17
|
-
const result: RenderableResult = {
|
|
18
|
-
tag: 'error',
|
|
19
|
-
attrs: { command: 'measure map focus', status: 'not_implemented' },
|
|
20
|
-
summary: fact`\`measure map focus\` is not implemented yet.`,
|
|
21
|
-
};
|
|
22
|
-
emitResult(result, { json: parsed.json });
|
|
23
|
-
process.exit(1);
|
|
24
52
|
}
|
|
@@ -1,25 +1,59 @@
|
|
|
1
1
|
import { type ParsedArgs } from '../../types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { captureMeasureSnap } from './snap.js';
|
|
3
|
+
import { buildMeasureMapLayersResult } from '../../measure/map-layers.js';
|
|
4
|
+
import { resolveSnapRef } from '../../../output/artifact.js';
|
|
5
|
+
import { emitResult, fact, text, type RenderableResult } from '../../../output/render.js';
|
|
3
6
|
|
|
4
|
-
const USAGE = `
|
|
7
|
+
const USAGE = `capture measure map layers [url|snap] — paint/compositor facts recorded in a snapshot's layers.json
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
provenance for layer-affecting declarations
|
|
9
|
-
first
|
|
9
|
+
input:
|
|
10
|
+
[url|snap] required target: a URL creates a settled snapshot first; a snapshot id or absolute path is read without re-driving the browser
|
|
11
|
+
output: <layer-map …> — layer bounds, compositing reasons, DOMSnapshot paint order, per-node membership, and available source provenance for layer-affecting declarations; --json mirrors
|
|
12
|
+
effects: read-only over an existing snapshot artifact; a URL target writes one settled snapshot first`;
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
function errorResult(err: unknown): RenderableResult {
|
|
15
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
16
|
+
return {
|
|
17
|
+
tag: 'error',
|
|
18
|
+
attrs: { command: 'measure map layers', status: 'artifact_unavailable' },
|
|
19
|
+
summary: fact`Layer-map facts could not be read: ${detail}`,
|
|
20
|
+
followUp: text`Pass a settled snapshot id or its absolute artifact path, or pass a URL to create a snapshot first with capture measure snap.`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
export async function cmdMeasureMapLayers(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
14
25
|
if (parsed.help) {
|
|
15
26
|
console.log(USAGE);
|
|
16
|
-
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (parsed.positional.length > 1) {
|
|
30
|
+
emitResult({
|
|
31
|
+
tag: 'error',
|
|
32
|
+
attrs: { command: 'measure map layers', status: 'invalid_target' },
|
|
33
|
+
summary: text`measure map layers accepts at most one URL, snapshot id, or absolute snapshot artifact path.`,
|
|
34
|
+
}, { json: parsed.json });
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const target = parsed.positional[0];
|
|
39
|
+
if (!target) {
|
|
40
|
+
emitResult({
|
|
41
|
+
tag: 'error',
|
|
42
|
+
attrs: { command: 'measure map layers', status: 'missing_target' },
|
|
43
|
+
summary: text`A URL, snapshot id, or absolute snapshot artifact path is required to read layer-map facts.`,
|
|
44
|
+
followUp: text`Capture one with capture measure snap <url>, then pass its snapshot id or artifact path.`,
|
|
45
|
+
}, { json: parsed.json });
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const ref = await resolveSnapRef(target, {
|
|
52
|
+
onUrl: async (url) => captureMeasureSnap(parsed, url),
|
|
53
|
+
});
|
|
54
|
+
emitResult(buildMeasureMapLayersResult(ref), { json: parsed.json });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
emitResult(errorResult(err), { json: parsed.json });
|
|
57
|
+
process.exitCode = 1;
|
|
17
58
|
}
|
|
18
|
-
const result: RenderableResult = {
|
|
19
|
-
tag: 'error',
|
|
20
|
-
attrs: { command: 'measure map layers', status: 'not_implemented' },
|
|
21
|
-
summary: fact`\`measure map layers\` is not implemented yet.`,
|
|
22
|
-
};
|
|
23
|
-
emitResult(result, { json: parsed.json });
|
|
24
|
-
process.exit(1);
|
|
25
59
|
}
|
|
@@ -1,24 +1,94 @@
|
|
|
1
1
|
import { type ParsedArgs } from '../../types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { captureMeasureSnap } from './snap.js';
|
|
3
|
+
import { ArtifactResolutionError, resolveSnapRef, type SnapUrlResult } from '../../../output/artifact.js';
|
|
4
|
+
import { emitResult, fact, lineList, text, type FactLine, type RenderableResult } from '../../../output/render.js';
|
|
5
|
+
import { measureMapScroll } from '../../measure/map-scroll.js';
|
|
3
6
|
|
|
4
|
-
const USAGE = `
|
|
7
|
+
const USAGE = `capture measure map scroll [url|snap] — scroll-container topology recorded in a snapshot's scroll.json
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
visual/layout viewport facts
|
|
9
|
+
input:
|
|
10
|
+
[url|snap] required target: a URL creates a settled snapshot first; a snapshot id from the active session or an absolute snapshot artifact path is read without re-driving the browser
|
|
11
|
+
output: <scroll-map …> — containers, ranges, current/max offsets, sticky/fixed occupancy, snap points, visual/layout viewport facts, and reachable-content samples; --json mirrors
|
|
12
|
+
effects: read-only over an existing snapshot artifact; a URL target writes one settled snapshot first`;
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
type CaptureSnap = (parsed: ParsedArgs, target: string) => Promise<SnapUrlResult>;
|
|
15
|
+
type Emit = typeof emitResult;
|
|
16
|
+
|
|
17
|
+
interface MapScrollDeps {
|
|
18
|
+
readonly captureSnap?: CaptureSnap;
|
|
19
|
+
readonly emit?: Emit;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function artifactRecovery(err: ArtifactResolutionError): FactLine[] {
|
|
23
|
+
return [
|
|
24
|
+
fact`artifact resolution error: ${err.message}`,
|
|
25
|
+
fact`received ref: ${err.ref}`,
|
|
26
|
+
fact`searched path count: ${err.searched.length}`,
|
|
27
|
+
...err.searched.map((candidate) => fact`searched path: ${candidate}`),
|
|
28
|
+
fact`creating command: ${err.creatingCommand ?? 'capture measure snap'}`,
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function runMeasureMapScroll(parsed: ParsedArgs, deps: MapScrollDeps = {}): Promise<void> {
|
|
33
|
+
const captureSnap = deps.captureSnap ?? captureMeasureSnap;
|
|
34
|
+
const emit = deps.emit ?? emitResult;
|
|
11
35
|
|
|
12
|
-
export async function cmdMeasureMapScroll(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
13
36
|
if (parsed.help) {
|
|
14
37
|
console.log(USAGE);
|
|
15
|
-
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (parsed.positional.length > 1) {
|
|
42
|
+
const result: RenderableResult = {
|
|
43
|
+
tag: 'error',
|
|
44
|
+
attrs: { command: 'measure map scroll', status: 'invalid_target' },
|
|
45
|
+
summary: text`Measure map scroll accepts one URL, snapshot id, or absolute snapshot path.`,
|
|
46
|
+
followUp: text`Pass one target, for example: \`capture measure map scroll snap-a3f2\`.`,
|
|
47
|
+
};
|
|
48
|
+
emit(result, { json: parsed.json });
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return;
|
|
16
51
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
52
|
+
|
|
53
|
+
const target = parsed.positional[0] ?? parsed.url;
|
|
54
|
+
if (!target) {
|
|
55
|
+
const result: RenderableResult = {
|
|
56
|
+
tag: 'error',
|
|
57
|
+
attrs: { command: 'measure map scroll', status: 'missing_target' },
|
|
58
|
+
summary: text`No snapshot or URL target was received.`,
|
|
59
|
+
followUp: text`Create a snapshot with \`capture measure snap <url>\`, then pass its id or absolute path to \`capture measure map scroll\`.`,
|
|
60
|
+
};
|
|
61
|
+
emit(result, { json: parsed.json });
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const ref = await resolveSnapRef(target, {
|
|
68
|
+
onUrl: async (url) => captureSnap({ ...parsed, positional: [url] }, url),
|
|
69
|
+
});
|
|
70
|
+
emit(measureMapScroll(ref), { json: parsed.json });
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
73
|
+
const recovery = err instanceof ArtifactResolutionError ? artifactRecovery(err) : [];
|
|
74
|
+
const result: RenderableResult = {
|
|
75
|
+
tag: 'error',
|
|
76
|
+
attrs: {
|
|
77
|
+
command: 'measure map scroll',
|
|
78
|
+
status: 'artifact_unavailable',
|
|
79
|
+
recovery: err instanceof ArtifactResolutionError ? 'artifact-resolution-error' : undefined,
|
|
80
|
+
'searched-paths': err instanceof ArtifactResolutionError ? err.searched.length : undefined,
|
|
81
|
+
'creating-command': err instanceof ArtifactResolutionError ? (err.creatingCommand ?? 'capture measure snap') : undefined,
|
|
82
|
+
},
|
|
83
|
+
summary: fact`Scroll topology could not be read: ${detail}`,
|
|
84
|
+
sections: recovery.length ? [lineList(recovery)] : undefined,
|
|
85
|
+
followUp: text`Create a settled snapshot with \`capture measure snap <url>\`; it writes scroll.json for this query.`,
|
|
86
|
+
};
|
|
87
|
+
emit(result, { json: parsed.json });
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function cmdMeasureMapScroll(parsed: ParsedArgs, _args: string[]): Promise<void> {
|
|
93
|
+
await runMeasureMapScroll(parsed);
|
|
24
94
|
}
|