browser-debugger-cli 0.6.9 → 0.6.11
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 +10 -0
- package/dist/commands/cdp.js +1 -1
- package/dist/commands/cdp.js.map +1 -1
- package/dist/commands/cleanup.js +1 -1
- package/dist/commands/cleanup.js.map +1 -1
- package/dist/commands/console.d.ts +4 -28
- package/dist/commands/console.d.ts.map +1 -1
- package/dist/commands/console.js +82 -192
- package/dist/commands/console.js.map +1 -1
- package/dist/commands/details.d.ts.map +1 -1
- package/dist/commands/details.js +27 -8
- package/dist/commands/details.js.map +1 -1
- package/dist/commands/dom/DomElementResolver.d.ts +25 -9
- package/dist/commands/dom/DomElementResolver.d.ts.map +1 -1
- package/dist/commands/dom/DomElementResolver.js +65 -17
- package/dist/commands/dom/DomElementResolver.js.map +1 -1
- package/dist/commands/dom/__tests__/screenshotResize.unit.test.d.ts +2 -0
- package/dist/commands/dom/__tests__/screenshotResize.unit.test.d.ts.map +1 -0
- package/dist/commands/dom/__tests__/screenshotResize.unit.test.js +184 -0
- package/dist/commands/dom/__tests__/screenshotResize.unit.test.js.map +1 -0
- package/dist/commands/dom/a11y.d.ts.map +1 -1
- package/dist/commands/dom/a11y.js +9 -9
- package/dist/commands/dom/a11y.js.map +1 -1
- package/dist/commands/dom/evalHelpers.d.ts.map +1 -1
- package/dist/commands/dom/evalHelpers.js +10 -7
- package/dist/commands/dom/evalHelpers.js.map +1 -1
- package/dist/commands/dom/formFillHelpers.d.ts +2 -1
- package/dist/commands/dom/formFillHelpers.d.ts.map +1 -1
- package/dist/commands/dom/formFillHelpers.js +22 -20
- package/dist/commands/dom/formFillHelpers.js.map +1 -1
- package/dist/commands/dom/formInteraction.d.ts.map +1 -1
- package/dist/commands/dom/formInteraction.js +26 -10
- package/dist/commands/dom/formInteraction.js.map +1 -1
- package/dist/commands/dom/formSubmitHelpers.d.ts +2 -1
- package/dist/commands/dom/formSubmitHelpers.d.ts.map +1 -1
- package/dist/commands/dom/formSubmitHelpers.js.map +1 -1
- package/dist/commands/dom/helpers.d.ts +47 -4
- package/dist/commands/dom/helpers.d.ts.map +1 -1
- package/dist/commands/dom/helpers.js +406 -21
- package/dist/commands/dom/helpers.js.map +1 -1
- package/dist/commands/dom/index.d.ts +1 -1
- package/dist/commands/dom/index.d.ts.map +1 -1
- package/dist/commands/dom/index.js +414 -151
- package/dist/commands/dom/index.js.map +1 -1
- package/dist/commands/dom/reactEventHelpers.d.ts +4 -4
- package/dist/commands/dom/reactEventHelpers.d.ts.map +1 -1
- package/dist/commands/dom/reactEventHelpers.js +11 -11
- package/dist/commands/dom/screenshotResize.d.ts +81 -0
- package/dist/commands/dom/screenshotResize.d.ts.map +1 -0
- package/dist/commands/dom/screenshotResize.js +103 -0
- package/dist/commands/dom/screenshotResize.js.map +1 -0
- package/dist/commands/helpJson.d.ts +20 -0
- package/dist/commands/helpJson.d.ts.map +1 -1
- package/dist/commands/helpJson.js +48 -39
- package/dist/commands/helpJson.js.map +1 -1
- package/dist/commands/network/har.d.ts.map +1 -1
- package/dist/commands/network/har.js +3 -2
- package/dist/commands/network/har.js.map +1 -1
- package/dist/commands/network/list.d.ts +0 -8
- package/dist/commands/network/list.d.ts.map +1 -1
- package/dist/commands/network/list.js +134 -208
- package/dist/commands/network/list.js.map +1 -1
- package/dist/commands/network/shared.d.ts.map +1 -1
- package/dist/commands/network/shared.js +6 -0
- package/dist/commands/network/shared.js.map +1 -1
- package/dist/commands/optionBehaviors.d.ts +26 -0
- package/dist/commands/optionBehaviors.d.ts.map +1 -0
- package/dist/commands/optionBehaviors.js +184 -0
- package/dist/commands/optionBehaviors.js.map +1 -0
- package/dist/commands/peek.d.ts +2 -4
- package/dist/commands/peek.d.ts.map +1 -1
- package/dist/commands/peek.js +109 -97
- package/dist/commands/peek.js.map +1 -1
- package/dist/commands/shared/CommandRunner.js +1 -1
- package/dist/commands/shared/CommandRunner.js.map +1 -1
- package/dist/commands/shared/dataFetcher.d.ts +58 -0
- package/dist/commands/shared/dataFetcher.d.ts.map +1 -0
- package/dist/commands/shared/dataFetcher.js +98 -0
- package/dist/commands/shared/dataFetcher.js.map +1 -0
- package/dist/commands/shared/followMode.d.ts +50 -0
- package/dist/commands/shared/followMode.d.ts.map +1 -0
- package/dist/commands/shared/followMode.js +58 -0
- package/dist/commands/shared/followMode.js.map +1 -0
- package/dist/commands/shared/optionTypes.d.ts +18 -4
- package/dist/commands/shared/optionTypes.d.ts.map +1 -1
- package/dist/commands/shared/startHelpers.d.ts.map +1 -1
- package/dist/commands/shared/startHelpers.js +6 -5
- package/dist/commands/shared/startHelpers.js.map +1 -1
- package/dist/commands/shared/validation.d.ts +0 -45
- package/dist/commands/shared/validation.d.ts.map +1 -1
- package/dist/commands/shared/validation.js +65 -107
- package/dist/commands/shared/validation.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +18 -9
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/stop.d.ts.map +1 -1
- package/dist/commands/stop.js +15 -4
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/tail.d.ts +2 -7
- package/dist/commands/tail.d.ts.map +1 -1
- package/dist/commands/tail.js +67 -79
- package/dist/commands/tail.js.map +1 -1
- package/dist/commands/types.d.ts +11 -5
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/connection/cdp.d.ts.map +1 -1
- package/dist/connection/cdp.js +5 -3
- package/dist/connection/cdp.js.map +1 -1
- package/dist/connection/diagnostics.d.ts +9 -0
- package/dist/connection/diagnostics.d.ts.map +1 -1
- package/dist/connection/diagnostics.js +14 -1
- package/dist/connection/diagnostics.js.map +1 -1
- package/dist/connection/errors.d.ts +0 -7
- package/dist/connection/errors.d.ts.map +1 -1
- package/dist/connection/errors.js +0 -7
- package/dist/connection/errors.js.map +1 -1
- package/dist/connection/handlers.d.ts +1 -8
- package/dist/connection/handlers.d.ts.map +1 -1
- package/dist/connection/handlers.js.map +1 -1
- package/dist/connection/launcher/binaryResolver.d.ts.map +1 -1
- package/dist/connection/launcher/binaryResolver.js +3 -15
- package/dist/connection/launcher/binaryResolver.js.map +1 -1
- package/dist/connection/launcher/preferencesLoader.d.ts.map +1 -1
- package/dist/connection/launcher/preferencesLoader.js +2 -1
- package/dist/connection/launcher/preferencesLoader.js.map +1 -1
- package/dist/connection/launcher.d.ts.map +1 -1
- package/dist/connection/launcher.js +5 -16
- package/dist/connection/launcher.js.map +1 -1
- package/dist/connection/pageReadiness.d.ts.map +1 -1
- package/dist/connection/pageReadiness.js +13 -16
- package/dist/connection/pageReadiness.js.map +1 -1
- package/dist/connection/typed-cdp.d.ts +3 -5
- package/dist/connection/typed-cdp.d.ts.map +1 -1
- package/dist/connection/typed-cdp.js +1 -1
- package/dist/connection/typed-cdp.js.map +1 -1
- package/dist/daemon/handlers/BaseHandler.d.ts.map +1 -1
- package/dist/daemon/handlers/BaseHandler.js +1 -1
- package/dist/daemon/handlers/BaseHandler.js.map +1 -1
- package/dist/daemon/handlers/QueryHandlers.d.ts.map +1 -1
- package/dist/daemon/handlers/QueryHandlers.js +1 -1
- package/dist/daemon/handlers/QueryHandlers.js.map +1 -1
- package/dist/daemon/handlers/SessionHandlers.d.ts.map +1 -1
- package/dist/daemon/handlers/SessionHandlers.js +1 -1
- package/dist/daemon/handlers/SessionHandlers.js.map +1 -1
- package/dist/daemon/handlers/responseHandler.d.ts.map +1 -1
- package/dist/daemon/handlers/responseHandler.js +10 -0
- package/dist/daemon/handlers/responseHandler.js.map +1 -1
- package/dist/daemon/ipcServer.js +1 -1
- package/dist/daemon/ipcServer.js.map +1 -1
- package/dist/daemon/launcher.d.ts.map +1 -1
- package/dist/daemon/launcher.js +3 -2
- package/dist/daemon/launcher.js.map +1 -1
- package/dist/daemon/lifecycle/signalHandlers.d.ts.map +1 -1
- package/dist/daemon/lifecycle/signalHandlers.js +1 -1
- package/dist/daemon/lifecycle/signalHandlers.js.map +1 -1
- package/dist/daemon/lifecycle/workerCleanup.d.ts.map +1 -1
- package/dist/daemon/lifecycle/workerCleanup.js +4 -3
- package/dist/daemon/lifecycle/workerCleanup.js.map +1 -1
- package/dist/daemon/lifecycle/workerConfig.d.ts.map +1 -1
- package/dist/daemon/lifecycle/workerConfig.js +1 -1
- package/dist/daemon/lifecycle/workerConfig.js.map +1 -1
- package/dist/daemon/lifecycle/workerIpc.d.ts.map +1 -1
- package/dist/daemon/lifecycle/workerIpc.js +1 -1
- package/dist/daemon/lifecycle/workerIpc.js.map +1 -1
- package/dist/daemon/server/JsonlParser.d.ts.map +1 -1
- package/dist/daemon/server/JsonlParser.js +1 -1
- package/dist/daemon/server/JsonlParser.js.map +1 -1
- package/dist/daemon/startSession.d.ts.map +1 -1
- package/dist/daemon/startSession.js +1 -1
- package/dist/daemon/startSession.js.map +1 -1
- package/dist/daemon/worker/TelemetryStore.d.ts +2 -1
- package/dist/daemon/worker/TelemetryStore.d.ts.map +1 -1
- package/dist/daemon/worker/TelemetryStore.js +4 -0
- package/dist/daemon/worker/TelemetryStore.js.map +1 -1
- package/dist/daemon/worker/plugins.d.ts.map +1 -1
- package/dist/daemon/worker/plugins.js +9 -2
- package/dist/daemon/worker/plugins.js.map +1 -1
- package/dist/index.js +23 -7
- package/dist/index.js.map +1 -1
- package/dist/ipc/utils/errors.js +1 -1
- package/dist/ipc/utils/errors.js.map +1 -1
- package/dist/session/QueryCacheManager.d.ts.map +1 -1
- package/dist/session/QueryCacheManager.js +1 -1
- package/dist/session/QueryCacheManager.js.map +1 -1
- package/dist/session/chrome.js +1 -1
- package/dist/session/chrome.js.map +1 -1
- package/dist/session/cleanup.js +5 -5
- package/dist/session/cleanup.js.map +1 -1
- package/dist/session/lock.js +1 -1
- package/dist/session/lock.js.map +1 -1
- package/dist/session/metadata.d.ts.map +1 -1
- package/dist/session/metadata.js +1 -1
- package/dist/session/metadata.js.map +1 -1
- package/dist/session/pid.js +1 -1
- package/dist/session/pid.js.map +1 -1
- package/dist/telemetry/dom.d.ts.map +1 -1
- package/dist/telemetry/dom.js +1 -1
- package/dist/telemetry/dom.js.map +1 -1
- package/dist/telemetry/filterPresets.d.ts.map +1 -1
- package/dist/telemetry/filterPresets.js +5 -2
- package/dist/telemetry/filterPresets.js.map +1 -1
- package/dist/telemetry/navigation.d.ts.map +1 -1
- package/dist/telemetry/navigation.js +7 -0
- package/dist/telemetry/navigation.js.map +1 -1
- package/dist/telemetry/network.d.ts +12 -1
- package/dist/telemetry/network.d.ts.map +1 -1
- package/dist/telemetry/network.js +117 -1
- package/dist/telemetry/network.js.map +1 -1
- package/dist/types.d.ts +148 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +33 -1
- package/dist/types.js.map +1 -1
- package/dist/ui/OutputBuilder.d.ts +2 -44
- package/dist/ui/OutputBuilder.d.ts.map +1 -1
- package/dist/ui/OutputBuilder.js +71 -103
- package/dist/ui/OutputBuilder.js.map +1 -1
- package/dist/ui/errors/CommandError.d.ts +0 -2
- package/dist/ui/errors/CommandError.d.ts.map +1 -1
- package/dist/ui/errors/CommandError.js.map +1 -1
- package/dist/ui/formatters/dom.d.ts +10 -30
- package/dist/ui/formatters/dom.d.ts.map +1 -1
- package/dist/ui/formatters/dom.js +13 -36
- package/dist/ui/formatters/dom.js.map +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/logging/logger.d.ts.map +1 -1
- package/dist/ui/logging/logger.js +1 -1
- package/dist/ui/logging/logger.js.map +1 -1
- package/dist/ui/messages/dom.d.ts +14 -0
- package/dist/ui/messages/dom.d.ts.map +1 -0
- package/dist/ui/messages/dom.js +28 -0
- package/dist/ui/messages/dom.js.map +1 -0
- package/dist/ui/messages/errors.d.ts +83 -0
- package/dist/ui/messages/errors.d.ts.map +1 -1
- package/dist/ui/messages/errors.js +171 -0
- package/dist/ui/messages/errors.js.map +1 -1
- package/dist/utils/async.d.ts +10 -0
- package/dist/utils/async.d.ts.map +1 -0
- package/dist/utils/async.js +12 -0
- package/dist/utils/async.js.map +1 -0
- package/dist/utils/atomicFile.js +1 -1
- package/dist/utils/atomicFile.js.map +1 -1
- package/dist/utils/errorMapping.d.ts.map +1 -1
- package/dist/utils/errorMapping.js +4 -1
- package/dist/utils/errorMapping.js.map +1 -1
- package/dist/utils/exitCodes.d.ts +17 -16
- package/dist/utils/exitCodes.d.ts.map +1 -1
- package/dist/utils/exitCodes.js +86 -16
- package/dist/utils/exitCodes.js.map +1 -1
- package/dist/utils/suggestions.d.ts +19 -0
- package/dist/utils/suggestions.d.ts.map +1 -0
- package/dist/utils/suggestions.js +34 -0
- package/dist/utils/suggestions.js.map +1 -0
- package/dist/utils/taskMappings.d.ts.map +1 -1
- package/dist/utils/taskMappings.js +35 -0
- package/dist/utils/taskMappings.js.map +1 -1
- package/dist/utils/typeGuards.d.ts +11 -4
- package/dist/utils/typeGuards.d.ts.map +1 -1
- package/dist/utils/typeGuards.js +1 -17
- package/dist/utils/typeGuards.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,175 +1,172 @@
|
|
|
1
1
|
import { DomElementResolver } from '../../commands/dom/DomElementResolver.js';
|
|
2
2
|
import { registerA11yCommands } from '../../commands/dom/a11y.js';
|
|
3
|
-
import { queryDOMElements, getDOMElements, capturePageScreenshot, getDomContext, } from '../../commands/dom/helpers.js';
|
|
3
|
+
import { queryDOMElements, getDOMElements, capturePageScreenshot, captureElementScreenshot, resolveSelector, getDomContext, } from '../../commands/dom/helpers.js';
|
|
4
4
|
import { runCommand } from '../../commands/shared/CommandRunner.js';
|
|
5
|
+
import { setupFollowMode } from '../../commands/shared/followMode.js';
|
|
5
6
|
import { positiveIntRule } from '../../commands/shared/validation.js';
|
|
6
7
|
import { QueryCacheManager } from '../../session/QueryCacheManager.js';
|
|
7
8
|
import { resolveA11yNode } from '../../telemetry/a11y.js';
|
|
8
9
|
import { synthesizeA11yNode } from '../../telemetry/roleInference.js';
|
|
9
10
|
import { CommandError } from '../../ui/errors/index.js';
|
|
10
11
|
import { formatDomQuery, formatDomGet, formatDomEval, formatDomScreenshot, } from '../../ui/formatters/dom.js';
|
|
11
|
-
import {
|
|
12
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
13
|
+
import { missingArgumentError, elementAtIndexNotFoundError, noNodesFoundError, } from '../../ui/messages/errors.js';
|
|
12
14
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
13
15
|
import { filterDefined } from '../../utils/objects.js';
|
|
16
|
+
const log = createLogger('dom');
|
|
14
17
|
/**
|
|
15
|
-
*
|
|
18
|
+
* Build filtered screenshot options from command options.
|
|
16
19
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
+
* @param options - Raw command options
|
|
21
|
+
* @returns Filtered options with undefined values removed
|
|
22
|
+
*/
|
|
23
|
+
function buildPageScreenshotOptions(options) {
|
|
24
|
+
return filterDefined({
|
|
25
|
+
format: options.format,
|
|
26
|
+
quality: options.quality,
|
|
27
|
+
fullPage: options.fullPage,
|
|
28
|
+
noResize: options.resize === false,
|
|
29
|
+
scroll: options.scroll,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build filtered element screenshot options from command options.
|
|
20
34
|
*
|
|
21
|
-
* @param
|
|
22
|
-
* @
|
|
35
|
+
* @param options - Raw command options
|
|
36
|
+
* @returns Filtered options with undefined values removed
|
|
23
37
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
...result,
|
|
31
|
-
...(navigationId !== null && { navigationId }),
|
|
32
|
-
};
|
|
33
|
-
await cacheManager.set(resultWithNavId);
|
|
34
|
-
return { success: true, data: result };
|
|
35
|
-
}, options, formatDomQuery);
|
|
38
|
+
function buildElementScreenshotOptions(options) {
|
|
39
|
+
return filterDefined({
|
|
40
|
+
format: options.format,
|
|
41
|
+
quality: options.quality,
|
|
42
|
+
noResize: options.resize === false,
|
|
43
|
+
});
|
|
36
44
|
}
|
|
37
45
|
/**
|
|
38
|
-
*
|
|
46
|
+
* Check if options specify an element target.
|
|
39
47
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
48
|
+
* @param options - Screenshot command options
|
|
49
|
+
* @returns True if selector or index is specified
|
|
50
|
+
*/
|
|
51
|
+
function hasElementTarget(options) {
|
|
52
|
+
return options.selector !== undefined || options.index !== undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve element nodeId from selector or cached index.
|
|
43
56
|
*
|
|
44
|
-
* @param
|
|
45
|
-
* @
|
|
57
|
+
* @param options - Options containing selector or index
|
|
58
|
+
* @returns CDP nodeId
|
|
59
|
+
* @throws CommandError if neither selector nor index provided
|
|
46
60
|
*/
|
|
47
|
-
async function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await
|
|
61
|
+
async function resolveElementNodeId(options) {
|
|
62
|
+
if (options.index !== undefined) {
|
|
63
|
+
const resolver = DomElementResolver.getInstance();
|
|
64
|
+
const node = await resolver.getNodeIdForIndex(options.index);
|
|
65
|
+
return node.nodeId;
|
|
51
66
|
}
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
if (options.selector !== undefined) {
|
|
68
|
+
return resolveSelector(options.selector);
|
|
54
69
|
}
|
|
70
|
+
const err = missingArgumentError('--selector "css-selector" or --index N from a previous query');
|
|
71
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
55
72
|
}
|
|
56
73
|
/**
|
|
57
|
-
*
|
|
74
|
+
* Add element metadata to screenshot result.
|
|
75
|
+
*
|
|
76
|
+
* @param result - Base screenshot result
|
|
77
|
+
* @param options - Options containing selector or index
|
|
78
|
+
* @returns Screenshot result with element info
|
|
58
79
|
*/
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const [a11yNode, domContext] = await Promise.all([
|
|
75
|
-
resolveA11yNode('', targetNode.nodeId),
|
|
76
|
-
getDomContext(targetNode.nodeId),
|
|
77
|
-
]);
|
|
78
|
-
// Graceful degradation: synthesize node from DOM context when a11y unavailable
|
|
79
|
-
const node = a11yNode ?? (domContext ? synthesizeA11yNode(domContext, targetNode.nodeId) : null);
|
|
80
|
-
if (!node) {
|
|
81
|
-
throw new CommandError(elementNotFoundError(`index ${index}`), {}, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
82
|
-
}
|
|
83
|
-
return { success: true, data: { node, domContext } };
|
|
84
|
-
}, options, formatSemanticNodeWithContext);
|
|
85
|
-
}
|
|
80
|
+
function addElementInfo(result, options) {
|
|
81
|
+
const bounds = {
|
|
82
|
+
x: 0,
|
|
83
|
+
y: 0,
|
|
84
|
+
width: result.width,
|
|
85
|
+
height: result.height,
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
...result,
|
|
89
|
+
element: {
|
|
90
|
+
...(options.selector !== undefined && { selector: options.selector }),
|
|
91
|
+
...(options.index !== undefined && { index: options.index }),
|
|
92
|
+
bounds,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
86
95
|
}
|
|
87
96
|
/**
|
|
88
|
-
*
|
|
97
|
+
* Ensure directory exists, creating it if necessary.
|
|
98
|
+
*
|
|
99
|
+
* @param dirPath - Directory path to ensure
|
|
100
|
+
* @param fs - File system module
|
|
89
101
|
*/
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
const getOptions = filterDefined({
|
|
94
|
-
selector,
|
|
95
|
-
all: options.all,
|
|
96
|
-
nth: options.nth,
|
|
97
|
-
nodeId: options.nodeId,
|
|
98
|
-
});
|
|
99
|
-
const result = await getDOMElements(getOptions);
|
|
100
|
-
return { success: true, data: result };
|
|
101
|
-
}, options, formatDomGet);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
await runCommand(async () => {
|
|
105
|
-
const a11yNode = await resolveA11yNode(selector);
|
|
106
|
-
// Fetch DOM context for enrichment or fallback
|
|
107
|
-
let domContext = null;
|
|
108
|
-
let nodeId;
|
|
109
|
-
if (a11yNode?.backendDOMNodeId) {
|
|
110
|
-
nodeId = a11yNode.backendDOMNodeId;
|
|
111
|
-
domContext = await getDomContext(nodeId);
|
|
112
|
-
}
|
|
113
|
-
else if (!a11yNode) {
|
|
114
|
-
// Try to get DOM context by querying the selector directly
|
|
115
|
-
const { callCDP } = await import('../../ipc/client.js');
|
|
116
|
-
const docResponse = await callCDP('DOM.getDocument', {});
|
|
117
|
-
const doc = docResponse.data?.result;
|
|
118
|
-
if (doc?.root?.nodeId) {
|
|
119
|
-
const queryResponse = await callCDP('DOM.querySelector', {
|
|
120
|
-
nodeId: doc.root.nodeId,
|
|
121
|
-
selector,
|
|
122
|
-
});
|
|
123
|
-
const queryResult = queryResponse.data?.result;
|
|
124
|
-
if (queryResult?.nodeId) {
|
|
125
|
-
nodeId = queryResult.nodeId;
|
|
126
|
-
domContext = await getDomContext(nodeId);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// Graceful degradation: synthesize node from DOM context when a11y unavailable
|
|
131
|
-
const node = a11yNode ?? (domContext && nodeId ? synthesizeA11yNode(domContext, nodeId) : null);
|
|
132
|
-
if (!node) {
|
|
133
|
-
throw new CommandError(elementNotFoundError(selector), {}, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
134
|
-
}
|
|
135
|
-
return { success: true, data: { node, domContext } };
|
|
136
|
-
}, options, formatSemanticNodeWithContext);
|
|
102
|
+
function ensureDirectory(dirPath, fs) {
|
|
103
|
+
if (!fs.existsSync(dirPath)) {
|
|
104
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
137
105
|
}
|
|
138
106
|
}
|
|
139
107
|
/**
|
|
140
|
-
*
|
|
108
|
+
* Format frame filename with zero-padded number.
|
|
141
109
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
110
|
+
* @param frameNumber - Frame number (1-based)
|
|
111
|
+
* @param format - Image format extension
|
|
112
|
+
* @returns Formatted filename
|
|
145
113
|
*/
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
114
|
+
function formatFrameFilename(frameNumber, format) {
|
|
115
|
+
return `${String(frameNumber).padStart(3, '0')}.${format}`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Capitalize first letter of string.
|
|
119
|
+
*
|
|
120
|
+
* @param str - Input string
|
|
121
|
+
* @returns String with first letter capitalized
|
|
122
|
+
*/
|
|
123
|
+
function capitalize(str) {
|
|
124
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Build role text for accessibility node display.
|
|
128
|
+
*
|
|
129
|
+
* @param node - Accessibility node
|
|
130
|
+
* @returns Formatted role text
|
|
131
|
+
*/
|
|
132
|
+
function buildRoleText(node) {
|
|
150
133
|
if (node.role.toLowerCase() === 'heading' && node.properties?.['level'] !== undefined) {
|
|
151
134
|
const level = node.properties['level'];
|
|
152
135
|
const levelNum = typeof level === 'number' ? level : Number(level);
|
|
153
136
|
if (!isNaN(levelNum)) {
|
|
154
|
-
|
|
137
|
+
return `[Heading L${levelNum}]`;
|
|
155
138
|
}
|
|
156
139
|
}
|
|
157
|
-
|
|
158
|
-
|
|
140
|
+
return `[${capitalize(node.role)}]`;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Build context text from a11y name or DOM context fallback.
|
|
144
|
+
*
|
|
145
|
+
* @param node - Accessibility node
|
|
146
|
+
* @param domContext - DOM context for fallback
|
|
147
|
+
* @returns Formatted context text
|
|
148
|
+
*/
|
|
149
|
+
function buildContextText(node, domContext) {
|
|
159
150
|
if (node.name) {
|
|
160
|
-
|
|
161
|
-
contextText = ` "${node.name}"`;
|
|
151
|
+
return ` "${node.name}"`;
|
|
162
152
|
}
|
|
163
|
-
|
|
164
|
-
// Fallback to DOM context when a11y name is missing
|
|
153
|
+
if (domContext) {
|
|
165
154
|
const tagPart = `<${domContext.tag}`;
|
|
166
155
|
const classPart = domContext.classes && domContext.classes.length > 0
|
|
167
156
|
? `.${domContext.classes.slice(0, 3).join('.')}`
|
|
168
157
|
: '';
|
|
169
158
|
const previewPart = domContext.preview ? ` "${domContext.preview}"` : '';
|
|
170
|
-
|
|
159
|
+
return ` ${tagPart}${classPart}>${previewPart}`;
|
|
171
160
|
}
|
|
172
|
-
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Build properties text from accessibility node state.
|
|
165
|
+
*
|
|
166
|
+
* @param node - Accessibility node
|
|
167
|
+
* @returns Formatted properties text
|
|
168
|
+
*/
|
|
169
|
+
function buildPropertiesText(node) {
|
|
173
170
|
const props = [];
|
|
174
171
|
if (node.focusable)
|
|
175
172
|
props.push('focusable');
|
|
@@ -179,47 +176,306 @@ function formatSemanticNodeWithContext(data) {
|
|
|
179
176
|
props.push('disabled');
|
|
180
177
|
if (node.required)
|
|
181
178
|
props.push('required');
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
return props.length > 0 ? ` (${props.join(', ')})` : '';
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Format semantic node with DOM context for display.
|
|
183
|
+
*
|
|
184
|
+
* @param data - Node and context data
|
|
185
|
+
* @returns Formatted string representation
|
|
186
|
+
*/
|
|
187
|
+
function formatSemanticNodeWithContext(data) {
|
|
188
|
+
const { node, domContext } = data;
|
|
189
|
+
const roleText = buildRoleText(node);
|
|
190
|
+
const contextText = buildContextText(node, domContext);
|
|
191
|
+
const propsText = buildPropertiesText(node);
|
|
184
192
|
const inferredText = node.inferred ? ' (inferred from DOM)' : '';
|
|
185
193
|
return `${roleText}${contextText}${propsText}${inferredText}`;
|
|
186
194
|
}
|
|
187
195
|
/**
|
|
188
|
-
*
|
|
196
|
+
* Resolve node with graceful degradation to synthesized node.
|
|
197
|
+
*
|
|
198
|
+
* @param a11yNode - Accessibility node or null
|
|
199
|
+
* @param domContext - DOM context for synthesis fallback
|
|
200
|
+
* @param nodeId - Node ID for synthesis
|
|
201
|
+
* @returns Resolved node or null
|
|
189
202
|
*/
|
|
190
|
-
function
|
|
191
|
-
|
|
203
|
+
function resolveNodeWithFallback(a11yNode, domContext, nodeId) {
|
|
204
|
+
if (a11yNode)
|
|
205
|
+
return a11yNode;
|
|
206
|
+
if (domContext && nodeId)
|
|
207
|
+
return synthesizeA11yNode(domContext, nodeId);
|
|
208
|
+
return null;
|
|
192
209
|
}
|
|
193
210
|
/**
|
|
194
|
-
*
|
|
211
|
+
* Query DOM context by selector when a11y node is unavailable.
|
|
195
212
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
|
|
213
|
+
* @param selector - CSS selector
|
|
214
|
+
* @returns Object with nodeId and domContext
|
|
215
|
+
*/
|
|
216
|
+
async function queryDomContextBySelector(selector) {
|
|
217
|
+
const { callCDP } = await import('../../ipc/client.js');
|
|
218
|
+
const docResponse = await callCDP('DOM.getDocument', {});
|
|
219
|
+
const doc = docResponse.data?.result;
|
|
220
|
+
if (!doc?.root?.nodeId) {
|
|
221
|
+
return { nodeId: undefined, domContext: null };
|
|
222
|
+
}
|
|
223
|
+
const queryResponse = await callCDP('DOM.querySelector', {
|
|
224
|
+
nodeId: doc.root.nodeId,
|
|
225
|
+
selector,
|
|
226
|
+
});
|
|
227
|
+
const queryResult = queryResponse.data?.result;
|
|
228
|
+
if (!queryResult?.nodeId) {
|
|
229
|
+
return { nodeId: undefined, domContext: null };
|
|
230
|
+
}
|
|
231
|
+
const domContext = await getDomContext(queryResult.nodeId);
|
|
232
|
+
return { nodeId: queryResult.nodeId, domContext };
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Handle bdg dom query command.
|
|
199
236
|
*
|
|
200
|
-
* @param
|
|
201
|
-
* @param options -
|
|
237
|
+
* @param selector - CSS selector to query
|
|
238
|
+
* @param options - Command options
|
|
202
239
|
*/
|
|
203
|
-
async function
|
|
240
|
+
async function handleDomQuery(selector, options) {
|
|
204
241
|
await runCommand(async () => {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
242
|
+
const result = await queryDOMElements(selector);
|
|
243
|
+
const cacheManager = QueryCacheManager.getInstance();
|
|
244
|
+
const navigationId = await cacheManager.getCurrentNavigationId();
|
|
245
|
+
const resultWithNavId = {
|
|
246
|
+
...result,
|
|
247
|
+
...(navigationId !== null && { navigationId }),
|
|
248
|
+
};
|
|
249
|
+
await cacheManager.set(resultWithNavId);
|
|
250
|
+
return { success: true, data: result };
|
|
251
|
+
}, options, formatDomQuery);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Handle get command with numeric index in raw mode.
|
|
255
|
+
*
|
|
256
|
+
* @param index - Element index
|
|
257
|
+
* @param options - Command options
|
|
258
|
+
*/
|
|
259
|
+
async function handleIndexGetRaw(index, options) {
|
|
260
|
+
const resolver = DomElementResolver.getInstance();
|
|
261
|
+
await runCommand(async () => {
|
|
262
|
+
const targetNode = await resolver.getNodeIdForIndex(index);
|
|
263
|
+
const getOptions = filterDefined({ nodeId: targetNode.nodeId });
|
|
264
|
+
const result = await getDOMElements(getOptions);
|
|
265
|
+
return { success: true, data: result };
|
|
266
|
+
}, options, formatDomGet);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Handle get command with numeric index in semantic mode.
|
|
270
|
+
*
|
|
271
|
+
* @param index - Element index
|
|
272
|
+
* @param options - Command options
|
|
273
|
+
*/
|
|
274
|
+
async function handleIndexGetSemantic(index, options) {
|
|
275
|
+
const resolver = DomElementResolver.getInstance();
|
|
276
|
+
await runCommand(async () => {
|
|
277
|
+
const targetNode = await resolver.getNodeIdForIndex(index);
|
|
278
|
+
const [a11yNode, domContext] = await Promise.all([
|
|
279
|
+
resolveA11yNode('', targetNode.nodeId),
|
|
280
|
+
getDomContext(targetNode.nodeId),
|
|
281
|
+
]);
|
|
282
|
+
const node = resolveNodeWithFallback(a11yNode, domContext, targetNode.nodeId);
|
|
283
|
+
if (!node) {
|
|
284
|
+
const err = elementAtIndexNotFoundError(index, 'cached query');
|
|
285
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
286
|
+
}
|
|
287
|
+
return { success: true, data: { node, domContext } };
|
|
288
|
+
}, options, formatSemanticNodeWithContext);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Handle get command with numeric index.
|
|
292
|
+
*
|
|
293
|
+
* @param index - Element index
|
|
294
|
+
* @param options - Command options
|
|
295
|
+
*/
|
|
296
|
+
async function handleIndexGet(index, options) {
|
|
297
|
+
if (options.raw) {
|
|
298
|
+
await handleIndexGetRaw(index, options);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
await handleIndexGetSemantic(index, options);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Handle get command with CSS selector in raw mode.
|
|
306
|
+
*
|
|
307
|
+
* @param selector - CSS selector
|
|
308
|
+
* @param options - Command options
|
|
309
|
+
*/
|
|
310
|
+
async function handleSelectorGetRaw(selector, options) {
|
|
311
|
+
await runCommand(async () => {
|
|
312
|
+
const getOptions = filterDefined({
|
|
313
|
+
selector,
|
|
314
|
+
all: options.all,
|
|
315
|
+
nth: options.nth,
|
|
316
|
+
nodeId: options.nodeId,
|
|
209
317
|
});
|
|
210
|
-
const result = await
|
|
318
|
+
const result = await getDOMElements(getOptions);
|
|
319
|
+
return { success: true, data: result };
|
|
320
|
+
}, options, formatDomGet);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Handle get command with CSS selector in semantic mode.
|
|
324
|
+
*
|
|
325
|
+
* @param selector - CSS selector
|
|
326
|
+
* @param options - Command options
|
|
327
|
+
*/
|
|
328
|
+
async function handleSelectorGetSemantic(selector, options) {
|
|
329
|
+
await runCommand(async () => {
|
|
330
|
+
const a11yNode = await resolveA11yNode(selector);
|
|
331
|
+
let domContext = null;
|
|
332
|
+
let nodeId;
|
|
333
|
+
if (a11yNode?.backendDOMNodeId) {
|
|
334
|
+
nodeId = a11yNode.backendDOMNodeId;
|
|
335
|
+
domContext = await getDomContext(nodeId);
|
|
336
|
+
}
|
|
337
|
+
else if (!a11yNode) {
|
|
338
|
+
const queryResult = await queryDomContextBySelector(selector);
|
|
339
|
+
nodeId = queryResult.nodeId;
|
|
340
|
+
domContext = queryResult.domContext;
|
|
341
|
+
}
|
|
342
|
+
const node = resolveNodeWithFallback(a11yNode, domContext, nodeId);
|
|
343
|
+
if (!node) {
|
|
344
|
+
const err = noNodesFoundError(selector);
|
|
345
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
346
|
+
}
|
|
347
|
+
return { success: true, data: { node, domContext } };
|
|
348
|
+
}, options, formatSemanticNodeWithContext);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Handle get command with CSS selector.
|
|
352
|
+
*
|
|
353
|
+
* @param selector - CSS selector
|
|
354
|
+
* @param options - Command options
|
|
355
|
+
*/
|
|
356
|
+
async function handleSelectorGet(selector, options) {
|
|
357
|
+
if (options.raw) {
|
|
358
|
+
await handleSelectorGetRaw(selector, options);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
await handleSelectorGetSemantic(selector, options);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Handle bdg dom get command.
|
|
366
|
+
*
|
|
367
|
+
* @param selectorOrIndex - CSS selector or numeric index
|
|
368
|
+
* @param options - Command options
|
|
369
|
+
*/
|
|
370
|
+
async function handleDomGet(selectorOrIndex, options) {
|
|
371
|
+
const isNumericIndex = /^\d+$/.test(selectorOrIndex);
|
|
372
|
+
if (isNumericIndex) {
|
|
373
|
+
await handleIndexGet(parseInt(selectorOrIndex, 10), options);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
await handleSelectorGet(selectorOrIndex, options);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Handle page-level screenshot capture.
|
|
381
|
+
*
|
|
382
|
+
* @param outputPath - Output file path
|
|
383
|
+
* @param options - Screenshot options
|
|
384
|
+
*/
|
|
385
|
+
async function handlePageScreenshot(outputPath, options) {
|
|
386
|
+
await runCommand(async () => {
|
|
387
|
+
const screenshotOptions = buildPageScreenshotOptions(options);
|
|
388
|
+
const result = await capturePageScreenshot(outputPath, screenshotOptions);
|
|
211
389
|
return { success: true, data: result };
|
|
212
390
|
}, options, formatDomScreenshot);
|
|
213
391
|
}
|
|
214
392
|
/**
|
|
215
|
-
* Handle
|
|
393
|
+
* Handle element-level screenshot capture.
|
|
216
394
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
|
|
395
|
+
* @param outputPath - Output file path
|
|
396
|
+
* @param options - Screenshot options with selector or index
|
|
397
|
+
*/
|
|
398
|
+
async function handleElementScreenshot(outputPath, options) {
|
|
399
|
+
await runCommand(async () => {
|
|
400
|
+
const nodeId = await resolveElementNodeId(options);
|
|
401
|
+
const screenshotOptions = buildElementScreenshotOptions(options);
|
|
402
|
+
const result = await captureElementScreenshot(outputPath, nodeId, screenshotOptions);
|
|
403
|
+
const elementResult = addElementInfo(result, options);
|
|
404
|
+
return { success: true, data: elementResult };
|
|
405
|
+
}, options, formatDomScreenshot);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Capture a single frame in sequence mode.
|
|
409
|
+
*
|
|
410
|
+
* @param outputPath - Full path for the frame file
|
|
411
|
+
* @param options - Screenshot options
|
|
412
|
+
*/
|
|
413
|
+
async function captureSequenceFrame(outputPath, options) {
|
|
414
|
+
if (hasElementTarget(options)) {
|
|
415
|
+
const nodeId = await resolveElementNodeId(options);
|
|
416
|
+
const elementOptions = buildElementScreenshotOptions(options);
|
|
417
|
+
await captureElementScreenshot(outputPath, nodeId, elementOptions);
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
const pageOptions = buildPageScreenshotOptions(options);
|
|
421
|
+
await capturePageScreenshot(outputPath, pageOptions);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Handle screenshot sequence capture to directory.
|
|
426
|
+
*
|
|
427
|
+
* @param outputDir - Output directory path
|
|
428
|
+
* @param options - Screenshot options with interval and limit
|
|
429
|
+
*/
|
|
430
|
+
async function handleSequenceCapture(outputDir, options) {
|
|
431
|
+
const fs = await import('fs');
|
|
432
|
+
const path = await import('path');
|
|
433
|
+
const absoluteDir = path.resolve(outputDir);
|
|
434
|
+
ensureDirectory(absoluteDir, fs);
|
|
435
|
+
const intervalRule = positiveIntRule({ min: 100, max: 60000, default: 1000 });
|
|
436
|
+
const limitRule = positiveIntRule({ min: 1, max: 10000, required: false });
|
|
437
|
+
const interval = intervalRule.validate(options.interval);
|
|
438
|
+
const limit = options.limit ? limitRule.validate(options.limit) : 0;
|
|
439
|
+
const format = options.format ?? 'png';
|
|
440
|
+
let frameCount = 0;
|
|
441
|
+
const captureFrame = async () => {
|
|
442
|
+
frameCount++;
|
|
443
|
+
const filename = formatFrameFilename(frameCount, format);
|
|
444
|
+
const outputPath = path.join(absoluteDir, filename);
|
|
445
|
+
await captureSequenceFrame(outputPath, options);
|
|
446
|
+
log.info(`Frame ${frameCount}: ${filename}`);
|
|
447
|
+
if (limit > 0 && frameCount >= limit) {
|
|
448
|
+
process.emit('SIGINT');
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
await setupFollowMode(captureFrame, {
|
|
452
|
+
startMessage: () => `Capturing to ${absoluteDir} every ${interval}ms...`,
|
|
453
|
+
stopMessage: () => `Captured ${frameCount} frames`,
|
|
454
|
+
intervalMs: interval,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Handle bdg dom screenshot command.
|
|
220
459
|
*
|
|
221
|
-
* @param
|
|
222
|
-
* @param options -
|
|
460
|
+
* @param outputPath - Output file path or directory
|
|
461
|
+
* @param options - Screenshot options
|
|
462
|
+
*/
|
|
463
|
+
async function handleDomScreenshot(outputPath, options) {
|
|
464
|
+
if (options.follow) {
|
|
465
|
+
await handleSequenceCapture(outputPath, options);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (hasElementTarget(options)) {
|
|
469
|
+
await handleElementScreenshot(outputPath, options);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
await handlePageScreenshot(outputPath, options);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Handle bdg dom eval command.
|
|
476
|
+
*
|
|
477
|
+
* @param script - JavaScript expression to evaluate
|
|
478
|
+
* @param options - Command options
|
|
223
479
|
*/
|
|
224
480
|
async function handleDomEval(script, options) {
|
|
225
481
|
await runCommand(async () => {
|
|
@@ -243,7 +499,7 @@ async function handleDomEval(script, options) {
|
|
|
243
499
|
}, options, formatDomEval);
|
|
244
500
|
}
|
|
245
501
|
/**
|
|
246
|
-
* Register DOM telemetry commands
|
|
502
|
+
* Register DOM telemetry commands.
|
|
247
503
|
*
|
|
248
504
|
* @param program - Commander.js Command instance
|
|
249
505
|
*/
|
|
@@ -284,11 +540,18 @@ export function registerDomCommands(program) {
|
|
|
284
540
|
});
|
|
285
541
|
dom
|
|
286
542
|
.command('screenshot')
|
|
287
|
-
.description('Capture page screenshot')
|
|
288
|
-
.argument('<path>', 'Output file path
|
|
543
|
+
.description('Capture page or element screenshot')
|
|
544
|
+
.argument('<path>', 'Output file path, or directory for --follow mode')
|
|
545
|
+
.option('--selector <selector>', 'CSS selector for element capture')
|
|
546
|
+
.option('--index <number>', 'Cached element index (0-based) from previous query', parseInt)
|
|
289
547
|
.option('--format <format>', 'Image format: png or jpeg (default: png)')
|
|
290
548
|
.option('--quality <number>', 'JPEG quality 0-100 (default: 90)', parseInt)
|
|
291
549
|
.option('--no-full-page', 'Capture viewport only (default: full page)')
|
|
550
|
+
.option('--no-resize', 'Disable auto-resize (full resolution)')
|
|
551
|
+
.option('--scroll <selector>', 'Scroll element into view before capture')
|
|
552
|
+
.option('-f, --follow', 'Continuous capture mode to directory')
|
|
553
|
+
.option('--interval <ms>', 'Capture interval for --follow (default: 1000)')
|
|
554
|
+
.option('--limit <count>', 'Max frames for --follow')
|
|
292
555
|
.option('-j, --json', 'Output as JSON')
|
|
293
556
|
.action(async (path, options) => {
|
|
294
557
|
await handleDomScreenshot(path, options);
|