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