browser-debugger-cli 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/cdp/schema.d.ts +4 -1
- package/dist/cdp/schema.js +48 -7
- package/dist/commands/cdp.js +3 -2
- package/dist/commands/cleanup.d.ts +11 -0
- package/dist/commands/cleanup.js +161 -57
- package/dist/commands/console.d.ts +20 -1
- package/dist/commands/console.js +57 -17
- package/dist/commands/details.js +3 -2
- package/dist/commands/dom/DomElementResolver.d.ts +10 -3
- package/dist/commands/dom/DomElementResolver.js +35 -17
- package/dist/commands/dom/a11y.d.ts +10 -0
- package/dist/commands/dom/a11y.js +29 -6
- package/dist/commands/dom/eval.d.ts +3 -1
- package/dist/commands/dom/eval.js +29 -4
- package/dist/commands/dom/form.js +16 -62
- package/dist/commands/dom/formInteraction.js +189 -119
- package/dist/commands/dom/formSummary.d.ts +49 -0
- package/dist/commands/dom/formSummary.js +180 -0
- package/dist/commands/dom/frames.d.ts +2 -1
- package/dist/commands/dom/frames.js +17 -2
- package/dist/commands/dom/get.d.ts +6 -5
- package/dist/commands/dom/get.js +92 -82
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
- package/dist/commands/dom/helpers/keyAttributes.js +54 -0
- package/dist/commands/dom/helpers/query.d.ts +44 -17
- package/dist/commands/dom/helpers/query.js +300 -106
- package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
- package/dist/commands/dom/helpers/runElementCommand.js +98 -30
- package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
- package/dist/commands/dom/helpers/screenshot.js +239 -51
- package/dist/commands/dom/index.d.ts +4 -1
- package/dist/commands/dom/index.js +22 -7
- package/dist/commands/dom/inspect.d.ts +15 -0
- package/dist/commands/dom/inspect.js +82 -0
- package/dist/commands/dom/layout.d.ts +14 -0
- package/dist/commands/dom/layout.js +54 -0
- package/dist/commands/dom/listeners.d.ts +5 -1
- package/dist/commands/dom/listeners.js +15 -5
- package/dist/commands/dom/query.js +2 -3
- package/dist/commands/dom/screenshot.d.ts +12 -2
- package/dist/commands/dom/screenshot.js +27 -3
- package/dist/commands/dom/semanticUtils.d.ts +16 -10
- package/dist/commands/dom/semanticUtils.js +53 -16
- package/dist/commands/dom/wait.d.ts +13 -0
- package/dist/commands/dom/wait.js +83 -0
- package/dist/commands/helpJson.js +2 -2
- package/dist/commands/network/list.js +17 -13
- package/dist/commands/optionBehaviors.js +154 -21
- package/dist/commands/page.d.ts +3 -2
- package/dist/commands/page.js +100 -5
- package/dist/commands/peek.js +4 -11
- package/dist/commands/sessions.d.ts +8 -0
- package/dist/commands/sessions.js +19 -0
- package/dist/commands/shared/CommandRunner.js +4 -4
- package/dist/commands/shared/commonOptions.d.ts +4 -0
- package/dist/commands/shared/commonOptions.js +9 -0
- package/dist/commands/shared/dataFetcher.js +2 -2
- package/dist/commands/shared/followMode.d.ts +21 -1
- package/dist/commands/shared/followMode.js +29 -2
- package/dist/commands/shared/handleValidationError.d.ts +2 -2
- package/dist/commands/shared/handleValidationError.js +12 -3
- package/dist/commands/shared/optionTypes.d.ts +61 -5
- package/dist/commands/shared/startHelpers.d.ts +66 -0
- package/dist/commands/shared/startHelpers.js +103 -13
- package/dist/commands/shared/validation.d.ts +14 -2
- package/dist/commands/shared/validation.js +20 -3
- package/dist/commands/start.d.ts +63 -0
- package/dist/commands/start.js +115 -15
- package/dist/commands/status.js +29 -7
- package/dist/commands/stop.js +7 -6
- package/dist/commands/tail.js +4 -11
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands.js +2 -0
- package/dist/connection/chromeIdentity.d.ts +65 -0
- package/dist/connection/chromeIdentity.js +143 -0
- package/dist/connection/launcher/profilePreferences.d.ts +47 -0
- package/dist/connection/launcher/profilePreferences.js +151 -0
- package/dist/connection/launcher.d.ts +21 -2
- package/dist/connection/launcher.js +42 -16
- package/dist/connection/portReservation.d.ts +14 -4
- package/dist/connection/portReservation.js +21 -6
- package/dist/connection/startupExit.d.ts +8 -0
- package/dist/connection/startupExit.js +15 -6
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +9 -2
- package/dist/daemon/SessionController.js +23 -7
- package/dist/daemon/errors.d.ts +1 -1
- package/dist/daemon/errors.js +1 -1
- package/dist/daemon/launcher.d.ts +10 -2
- package/dist/daemon/launcher.js +8 -7
- package/dist/daemon/server/SocketServer.js +1 -2
- package/dist/daemon/session/Session.d.ts +20 -0
- package/dist/daemon/session/Session.js +80 -9
- package/dist/daemon/session/chromeConnection.d.ts +9 -0
- package/dist/daemon/session/chromeConnection.js +45 -8
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +113 -67
- package/dist/daemon/session/interactions.d.ts +48 -9
- package/dist/daemon/session/interactions.js +46 -9
- package/dist/daemon/session/triggeredRequests.d.ts +67 -0
- package/dist/daemon/session/triggeredRequests.js +157 -0
- package/dist/daemon/session/types.d.ts +5 -1
- package/dist/daemon.js +10630 -3601
- package/dist/errors/messages.d.ts +456 -24
- package/dist/errors/messages.js +862 -67
- package/dist/index.js +6915 -3401
- package/dist/ipc/client.d.ts +21 -1
- package/dist/ipc/client.js +35 -3
- package/dist/ipc/protocol/commands.d.ts +145 -5
- package/dist/ipc/protocol/commands.js +4 -0
- package/dist/ipc/protocol/domTypes.d.ts +291 -7
- package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
- package/dist/ipc/protocol/inspectTypes.js +10 -0
- package/dist/ipc/session/lifecycle.d.ts +8 -1
- package/dist/ipc/session/queries.d.ts +5 -1
- package/dist/ipc/session/types.d.ts +5 -0
- package/dist/ipc/transport/index.d.ts +2 -1
- package/dist/ipc/transport/index.js +2 -2
- package/dist/runtime/dom/actionEffects.d.ts +185 -0
- package/dist/runtime/dom/actionEffects.js +402 -0
- package/dist/runtime/dom/actionEffectsScripts.d.ts +90 -0
- package/dist/runtime/dom/actionEffectsScripts.js +426 -0
- package/dist/runtime/dom/elementGeometry.d.ts +170 -0
- package/dist/runtime/dom/elementGeometry.js +553 -0
- package/dist/runtime/dom/elementInfo.d.ts +103 -0
- package/dist/runtime/dom/elementInfo.js +256 -0
- package/dist/runtime/dom/evalHelpers.d.ts +51 -6
- package/dist/runtime/dom/evalHelpers.js +136 -26
- package/dist/runtime/dom/eventListeners.d.ts +2 -1
- package/dist/runtime/dom/eventListeners.js +184 -47
- package/dist/runtime/dom/formDiscovery.d.ts +1 -1
- package/dist/runtime/dom/formDiscovery.js +116 -16
- package/dist/runtime/dom/formFillHelpers/fill.d.ts +9 -0
- package/dist/runtime/dom/formFillHelpers/fill.js +178 -14
- package/dist/runtime/dom/formFillHelpers/index.d.ts +2 -2
- package/dist/runtime/dom/formFillHelpers/index.js +2 -2
- package/dist/runtime/dom/formFillHelpers/pressKey.js +14 -3
- package/dist/runtime/dom/formFillHelpers/scroll.d.ts +3 -0
- package/dist/runtime/dom/formFillHelpers/scroll.js +60 -18
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +17 -0
- package/dist/runtime/dom/formFillHelpers/shared.js +25 -1
- package/dist/runtime/dom/formFillHelpers/stability.d.ts +20 -6
- package/dist/runtime/dom/formFillHelpers/stability.js +50 -19
- package/dist/runtime/dom/formSubmitHelpers.d.ts +3 -0
- package/dist/runtime/dom/formSubmitHelpers.js +89 -15
- package/dist/runtime/dom/frameLayout.d.ts +60 -0
- package/dist/runtime/dom/frameLayout.js +140 -0
- package/dist/runtime/dom/frameOrigin.d.ts +50 -0
- package/dist/runtime/dom/frameOrigin.js +62 -0
- package/dist/runtime/dom/frameScopedConnection.d.ts +92 -0
- package/dist/runtime/dom/frameScopedConnection.js +252 -0
- package/dist/runtime/dom/frameSelection.d.ts +12 -1
- package/dist/runtime/dom/frameSelection.js +22 -3
- package/dist/runtime/dom/frames.d.ts +61 -5
- package/dist/runtime/dom/frames.js +329 -75
- package/dist/runtime/dom/inspect.d.ts +28 -0
- package/dist/runtime/dom/inspect.js +557 -0
- package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
- package/dist/runtime/dom/inspectAllStyles.js +385 -0
- package/dist/runtime/dom/inspectCascade.d.ts +94 -0
- package/dist/runtime/dom/inspectCascade.js +371 -0
- package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
- package/dist/runtime/dom/inspectCascadeModel.js +232 -0
- package/dist/runtime/dom/inspectHints.d.ts +62 -0
- package/dist/runtime/dom/inspectHints.js +305 -0
- package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
- package/dist/runtime/dom/inspectLayoutModel.js +346 -0
- package/dist/runtime/dom/inspectModel.d.ts +74 -0
- package/dist/runtime/dom/inspectModel.js +184 -0
- package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
- package/dist/runtime/dom/inspectPaintModel.js +461 -0
- package/dist/runtime/dom/inspectRules.d.ts +37 -0
- package/dist/runtime/dom/inspectRules.js +101 -0
- package/dist/runtime/dom/inspectScripts.d.ts +132 -0
- package/dist/runtime/dom/inspectScripts.js +263 -0
- package/dist/runtime/dom/inspectTree.d.ts +40 -0
- package/dist/runtime/dom/inspectTree.js +134 -0
- package/dist/runtime/dom/inspectVariables.d.ts +33 -0
- package/dist/runtime/dom/inspectVariables.js +94 -0
- package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
- package/dist/runtime/dom/inspectWhyModel.js +134 -0
- package/dist/runtime/dom/layout.d.ts +71 -0
- package/dist/runtime/dom/layout.js +340 -0
- package/dist/runtime/dom/listenerPageScripts.d.ts +72 -0
- package/dist/runtime/dom/listenerPageScripts.js +365 -0
- package/dist/runtime/dom/listenerSummary.d.ts +136 -11
- package/dist/runtime/dom/listenerSummary.js +361 -22
- package/dist/runtime/dom/pageActivity.d.ts +41 -0
- package/dist/runtime/dom/pageActivity.js +123 -0
- package/dist/runtime/dom/reactEventHelpers.d.ts +63 -2
- package/dist/runtime/dom/reactEventHelpers.js +220 -41
- package/dist/runtime/dom/targetNode.d.ts +80 -27
- package/dist/runtime/dom/targetNode.js +249 -33
- package/dist/runtime/dom/wait.d.ts +25 -0
- package/dist/runtime/dom/wait.js +199 -0
- package/dist/runtime/dom/waitCondition.d.ts +71 -0
- package/dist/runtime/dom/waitCondition.js +75 -0
- package/dist/runtime/page/emulation.d.ts +71 -0
- package/dist/runtime/page/emulation.js +117 -0
- package/dist/runtime/page/loadingState.d.ts +36 -0
- package/dist/runtime/page/loadingState.js +86 -0
- package/dist/runtime/page/navigation.d.ts +46 -2
- package/dist/runtime/page/navigation.js +69 -33
- package/dist/session/QueryCacheManager.d.ts +11 -1
- package/dist/session/QueryCacheManager.js +25 -3
- package/dist/session/chromeOwners.d.ts +34 -0
- package/dist/session/chromeOwners.js +51 -0
- package/dist/session/cleanup/staleSession.d.ts +11 -1
- package/dist/session/cleanup/staleSession.js +17 -6
- package/dist/session/cleanup/userCommands.js +2 -4
- package/dist/session/metadata.d.ts +5 -1
- package/dist/session/metadata.js +2 -1
- package/dist/session/paths.d.ts +77 -3
- package/dist/session/paths.js +111 -5
- package/dist/session/port.d.ts +31 -7
- package/dist/session/port.js +50 -43
- package/dist/session/portClaims.d.ts +66 -0
- package/dist/session/portClaims.js +284 -0
- package/dist/session/sessionList.d.ts +58 -0
- package/dist/session/sessionList.js +199 -0
- package/dist/session/sessionName.d.ts +46 -0
- package/dist/session/sessionName.js +97 -0
- package/dist/telemetry/a11y.d.ts +18 -3
- package/dist/telemetry/a11y.js +170 -29
- package/dist/telemetry/console.d.ts +1 -0
- package/dist/telemetry/console.js +100 -5
- package/dist/telemetry/network.js +3 -1
- package/dist/telemetry/requestKinds.d.ts +32 -0
- package/dist/telemetry/requestKinds.js +61 -0
- package/dist/telemetry/requestState.d.ts +31 -0
- package/dist/telemetry/requestState.js +38 -0
- package/dist/types.d.ts +112 -3
- package/dist/ui/formatters/a11y.js +3 -0
- package/dist/ui/formatters/console/chronological.d.ts +8 -0
- package/dist/ui/formatters/console/chronological.js +17 -4
- package/dist/ui/formatters/console/json.js +3 -4
- package/dist/ui/formatters/console/shared.d.ts +12 -0
- package/dist/ui/formatters/console.d.ts +2 -2
- package/dist/ui/formatters/console.js +1 -1
- package/dist/ui/formatters/details.d.ts +8 -0
- package/dist/ui/formatters/details.js +61 -4
- package/dist/ui/formatters/dom.d.ts +27 -14
- package/dist/ui/formatters/dom.js +88 -59
- package/dist/ui/formatters/form.js +29 -18
- package/dist/ui/formatters/inspect.d.ts +39 -0
- package/dist/ui/formatters/inspect.js +596 -0
- package/dist/ui/formatters/keyAttributes.d.ts +19 -0
- package/dist/ui/formatters/keyAttributes.js +84 -0
- package/dist/ui/formatters/layout.d.ts +31 -0
- package/dist/ui/formatters/layout.js +53 -0
- package/dist/ui/formatters/listeners.d.ts +3 -2
- package/dist/ui/formatters/listeners.js +73 -9
- package/dist/ui/formatters/networkHeaders.d.ts +13 -0
- package/dist/ui/formatters/networkHeaders.js +36 -3
- package/dist/ui/formatters/networkList.d.ts +29 -1
- package/dist/ui/formatters/networkList.js +86 -20
- package/dist/ui/formatters/preview.js +2 -1
- package/dist/ui/formatters/requestStatus.d.ts +1 -17
- package/dist/ui/formatters/requestStatus.js +2 -30
- package/dist/ui/formatters/sessions.d.ts +12 -0
- package/dist/ui/formatters/sessions.js +40 -0
- package/dist/ui/formatters/status.d.ts +21 -2
- package/dist/ui/formatters/status.js +47 -10
- package/dist/ui/formatters/triggeredRequests.d.ts +36 -0
- package/dist/ui/formatters/triggeredRequests.js +65 -0
- package/dist/ui/formatting.d.ts +19 -0
- package/dist/ui/formatting.js +31 -36
- package/dist/ui/messages/chrome.d.ts +9 -0
- package/dist/ui/messages/chrome.js +17 -5
- package/dist/ui/messages/commands.d.ts +504 -14
- package/dist/ui/messages/commands.js +835 -21
- package/dist/ui/messages/consoleMessages.d.ts +10 -0
- package/dist/ui/messages/consoleMessages.js +17 -0
- package/dist/ui/messages/hints.js +2 -1
- package/dist/ui/messages/networkMessages.d.ts +14 -0
- package/dist/ui/messages/networkMessages.js +18 -0
- package/dist/ui/messages/preview.js +5 -4
- package/dist/ui/messages/session.d.ts +30 -21
- package/dist/ui/messages/session.js +48 -26
- package/dist/ui/messages/sessionCommand.d.ts +43 -0
- package/dist/ui/messages/sessionCommand.js +52 -0
- package/dist/utils/async.d.ts +17 -0
- package/dist/utils/async.js +36 -0
- package/dist/utils/color.d.ts +84 -0
- package/dist/utils/color.js +376 -0
- package/dist/utils/cssValues.d.ts +109 -0
- package/dist/utils/cssValues.js +236 -0
- package/dist/utils/http.d.ts +22 -1
- package/dist/utils/http.js +28 -9
- package/dist/utils/selectorFilters.d.ts +48 -8
- package/dist/utils/selectorFilters.js +296 -53
- package/dist/utils/shellDetection.d.ts +8 -2
- package/dist/utils/shellDetection.js +120 -33
- package/dist/utils/suggestions.d.ts +26 -0
- package/dist/utils/suggestions.js +73 -0
- package/dist/utils/taskMappings.js +10 -0
- package/dist/utils/url.d.ts +12 -2
- package/dist/utils/url.js +69 -7
- package/package.json +1 -1
- package/dist/ui/formatters/sessionFormatters.d.ts +0 -58
- package/dist/ui/formatters/sessionFormatters.js +0 -121
|
@@ -9,19 +9,23 @@
|
|
|
9
9
|
* bdg invocations. Selectors are matched in the page, including open shadow
|
|
10
10
|
* roots and same-origin iframes, like a user sees the page.
|
|
11
11
|
*/
|
|
12
|
+
import { keyAttributes } from './keyAttributes.js';
|
|
12
13
|
import { CommandError } from '../../../errors/index.js';
|
|
13
|
-
import { noNodesFoundError, indexOutOfRangeError, eitherArgumentRequiredError, invalidSelectorError, nodeIdNotFoundError, staleNodeError, } from '../../../errors/messages.js';
|
|
14
|
+
import { noNodesFoundError, indexOutOfRangeError, eitherArgumentRequiredError, invalidSelectorError, nodeIdNotFoundError, operationFailedError, similarSelectorsLine, staleNodeError, } from '../../../errors/messages.js';
|
|
14
15
|
import { callCDP } from '../../../ipc/client.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
16
|
+
import { ELEMENT_GEOMETRY_JS, VIEWPORT_SIZE_JS, classifyViewportPosition, } from '../../../runtime/dom/elementGeometry.js';
|
|
17
|
+
import { ELEMENT_CONTEXT_JS, ELEMENT_STATE_JS, ELEMENT_TEXT_JS, ELEMENT_TEXT_LENGTH, textPreview, } from '../../../runtime/dom/elementInfo.js';
|
|
18
|
+
import { DEEP_QUERY_JS, UNSEARCHED_CONTENT_JS, pageNamesJS, selectorArgsJS, } from '../../../runtime/dom/targetNode.js';
|
|
17
19
|
import { createLogger } from '../../../ui/logging/index.js';
|
|
20
|
+
import { sessionCommand } from '../../../ui/messages/sessionCommand.js';
|
|
18
21
|
import { ConcurrencyLimiter } from '../../../utils/concurrency.js';
|
|
22
|
+
import { getErrorMessage } from '../../../utils/errors.js';
|
|
19
23
|
import { EXIT_CODES } from '../../../utils/exitCodes.js';
|
|
24
|
+
import { leadingCompounds, parseSelectorFilters, withoutVisibleFilters, } from '../../../utils/selectorFilters.js';
|
|
25
|
+
import { findSimilarNames, parseSingleNameSelector } from '../../../utils/suggestions.js';
|
|
20
26
|
const log = createLogger('dom');
|
|
21
27
|
/** Maximum concurrent CDP calls to avoid overwhelming the connection. */
|
|
22
28
|
const CDP_CONCURRENCY_LIMIT = 10;
|
|
23
|
-
/** Length of the text preview shown for queried elements. */
|
|
24
|
-
const PREVIEW_LENGTH = 80;
|
|
25
29
|
/**
|
|
26
30
|
* Convert CDP's flat attribute list to a record.
|
|
27
31
|
*
|
|
@@ -63,33 +67,6 @@ async function getOuterHTML(ref) {
|
|
|
63
67
|
const response = await callCDP('DOM.getOuterHTML', ref);
|
|
64
68
|
return response.data?.result?.outerHTML;
|
|
65
69
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Page-side text of an element as a user sees it: `innerText` for a rendered
|
|
68
|
-
* element (CSS-hidden parts left out, inline elements not split apart), none
|
|
69
|
-
* for an element that is not rendered, `textContent` for SVG and other
|
|
70
|
-
* elements without `innerText` and for `display: contents` wrappers (no box
|
|
71
|
-
* of their own, but their children are shown).
|
|
72
|
-
*/
|
|
73
|
-
const ELEMENT_TEXT_JS = `(el) => {
|
|
74
|
-
if (typeof el.innerText !== 'string') return el.textContent || '';
|
|
75
|
-
if (!el.checkVisibility || el.checkVisibility()) return el.innerText;
|
|
76
|
-
const boxless = el.ownerDocument.defaultView.getComputedStyle(el).display === 'contents';
|
|
77
|
-
return boxless ? el.textContent || '' : '';
|
|
78
|
-
}`;
|
|
79
|
-
/**
|
|
80
|
-
* Short text preview of an element's text: whitespace collapsed, cut on a
|
|
81
|
-
* whole character (an emoji is never split, which would make JSON invalid).
|
|
82
|
-
*
|
|
83
|
-
* @param text - Element text as the page renders it
|
|
84
|
-
* @returns Collapsed text, truncated to {@link PREVIEW_LENGTH} characters
|
|
85
|
-
*/
|
|
86
|
-
export function textPreview(text) {
|
|
87
|
-
const collapsed = text.replace(/\s+/g, ' ').trim();
|
|
88
|
-
const characters = Array.from(collapsed);
|
|
89
|
-
return characters.length > PREVIEW_LENGTH
|
|
90
|
-
? characters.slice(0, PREVIEW_LENGTH).join('') + '...'
|
|
91
|
-
: collapsed;
|
|
92
|
-
}
|
|
93
70
|
/** Counter giving each query its own object group (queries may run concurrently) */
|
|
94
71
|
let queryCount = 0;
|
|
95
72
|
/**
|
|
@@ -126,53 +103,203 @@ async function withSelection(selector, use) {
|
|
|
126
103
|
await callCDP('Runtime.releaseObjectGroup', { objectGroup });
|
|
127
104
|
}
|
|
128
105
|
}
|
|
129
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* The "no nodes" error for a selector that matched nothing. One more page
|
|
108
|
+
* evaluation, on this failure path only, tells what the page says about it
|
|
109
|
+
* ({@link noMatchContext}).
|
|
110
|
+
*
|
|
111
|
+
* @param selector - Selector as given
|
|
112
|
+
* @returns Message and suggestion
|
|
113
|
+
*/
|
|
114
|
+
export async function noMatchesError(selector) {
|
|
115
|
+
return noNodesFoundError(selector, await noMatchContext(selector));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Page-side index of the first leading compound that matches a shadow host
|
|
119
|
+
* (an element with an open shadow root, anywhere a selector reaches) while
|
|
120
|
+
* the selector after it finds something: then the selector only failed by
|
|
121
|
+
* crossing into the shadow root.
|
|
122
|
+
*
|
|
123
|
+
* @param compounds - Leading compounds of the selector
|
|
124
|
+
* @returns Expression evaluating to the index, or -1
|
|
125
|
+
*/
|
|
126
|
+
function shadowHostJS(compounds) {
|
|
127
|
+
const plain = compounds
|
|
128
|
+
.slice(0, MAX_HOST_COMPOUNDS)
|
|
129
|
+
.map((entry) => [withoutFilters(entry.compound), withoutFilters(entry.rest)]);
|
|
130
|
+
return `${JSON.stringify(plain)}.findIndex(([host, rest]) => { try { const deep = ${DEEP_QUERY_JS}; return deep(host, null).some((el) => el.shadowRoot) && deep(rest, null).length > 0; } catch (e) { return false; } })`;
|
|
131
|
+
}
|
|
132
|
+
/** Leading compounds checked for a shadow host (each check walks the page) */
|
|
133
|
+
const MAX_HOST_COMPOUNDS = 4;
|
|
134
|
+
/**
|
|
135
|
+
* A selector without bdg's filters (`:visible`, `:has-text()`, `:text-is()`),
|
|
136
|
+
* which plain CSS does not know.
|
|
137
|
+
*
|
|
138
|
+
* @param selector - Selector
|
|
139
|
+
* @returns Plain CSS
|
|
140
|
+
*/
|
|
141
|
+
function withoutFilters(selector) {
|
|
142
|
+
return (selector.replace(/:visible\b|:(has-text|text-is)\((?:"[^"]*"|'[^']*'|[^)"'])*\)/g, '').trim() ||
|
|
143
|
+
'*');
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* What the page says about a selector that matched nothing, in one
|
|
147
|
+
* evaluation: whether it is still loading, how many elements match with the
|
|
148
|
+
* selector's `:visible` filters removed, whether it has cross-origin iframes
|
|
149
|
+
* or embeds (which selectors do not search), and for a selector that is a
|
|
150
|
+
* single id or class, the similar ids or classes on the page.
|
|
151
|
+
*
|
|
152
|
+
* @param selector - Selector as given
|
|
153
|
+
* @returns Context for {@link noNodesFoundError} (empty when the page did not answer)
|
|
154
|
+
*/
|
|
155
|
+
export async function noMatchContext(selector) {
|
|
156
|
+
const parts = parseSelectorFilters(selector);
|
|
157
|
+
const unfiltered = parts && withoutVisibleFilters(parts);
|
|
158
|
+
const single = parseSingleNameSelector(selector);
|
|
159
|
+
const hidden = unfiltered
|
|
160
|
+
? `(() => { try { return (${DEEP_QUERY_JS})(${JSON.stringify(selector)}, ${JSON.stringify(unfiltered)}).length; } catch (e) { return 0; } })()`
|
|
161
|
+
: '0';
|
|
162
|
+
const names = single ? pageNamesJS(single.kind) : '[]';
|
|
163
|
+
const compounds = leadingCompounds(selector);
|
|
164
|
+
const shadowHost = compounds.length > 0 ? shadowHostJS(compounds) : '-1';
|
|
165
|
+
try {
|
|
166
|
+
const evaluated = await callCDP('Runtime.evaluate', {
|
|
167
|
+
expression: `({ hidden: ${hidden}, readyState: document.readyState, unsearched: ${UNSEARCHED_CONTENT_JS}, names: ${names}, shadowHost: ${shadowHost} })`,
|
|
168
|
+
returnByValue: true,
|
|
169
|
+
});
|
|
170
|
+
const { result } = (evaluated.data?.result ?? {});
|
|
171
|
+
const value = (result?.value ?? {});
|
|
172
|
+
const similar = single && Array.isArray(value.names)
|
|
173
|
+
? similarSelectorsLine(single.kind, findSimilarNames(single.name, value.names.filter((n) => typeof n === 'string')))
|
|
174
|
+
: '';
|
|
175
|
+
return {
|
|
176
|
+
hidden: typeof value.hidden === 'number' ? value.hidden : 0,
|
|
177
|
+
...(typeof value.readyState === 'string' && { readyState: value.readyState }),
|
|
178
|
+
...(value.unsearched && {
|
|
179
|
+
unsearched: {
|
|
180
|
+
crossOriginFrames: value.unsearched.crossOriginFrames === true,
|
|
181
|
+
embeds: value.unsearched.embeds === true,
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
184
|
+
...(similar && { similar }),
|
|
185
|
+
...(typeof value.shadowHost === 'number' &&
|
|
186
|
+
compounds[value.shadowHost] && { shadowHost: compounds[value.shadowHost] }),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
log.debug(`Could not read the page after no match: ${getErrorMessage(error)}`);
|
|
191
|
+
return {};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The page's `document.readyState`, read for a failure that may come from a
|
|
196
|
+
* page still loading.
|
|
197
|
+
*
|
|
198
|
+
* @returns The state, or undefined when the page did not answer
|
|
199
|
+
*/
|
|
200
|
+
export async function documentReadyState() {
|
|
201
|
+
try {
|
|
202
|
+
const evaluated = await callCDP('Runtime.evaluate', {
|
|
203
|
+
expression: 'document.readyState',
|
|
204
|
+
returnByValue: true,
|
|
205
|
+
});
|
|
206
|
+
const { result } = (evaluated.data?.result ?? {});
|
|
207
|
+
return typeof result?.value === 'string' ? result.value : undefined;
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
log.debug(`Could not read document.readyState: ${getErrorMessage(error)}`);
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** Matches whose viewport position `dom query` reports (measuring is not free) */
|
|
215
|
+
const VIEWPORT_HINT_LIMIT = 100;
|
|
216
|
+
/**
|
|
217
|
+
* Where each element of a page-side array lives (an iframe and/or a shadow
|
|
218
|
+
* root), its text, its form control state ({@link ELEMENT_STATE_JS}) and, for
|
|
219
|
+
* the first {@link VIEWPORT_HINT_LIMIT}, its position
|
|
220
|
+
* relative to the viewport, plus the viewport size. An element that cannot be
|
|
221
|
+
* read gets empty details instead of failing the whole query.
|
|
222
|
+
*/
|
|
130
223
|
const ELEMENT_DETAILS_FUNCTION = `function () {
|
|
131
|
-
const
|
|
224
|
+
const contextOf = ${ELEMENT_CONTEXT_JS};
|
|
132
225
|
const textOf = ${ELEMENT_TEXT_JS};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
226
|
+
const geometryOf = ${ELEMENT_GEOMETRY_JS};
|
|
227
|
+
const stateOf = ${ELEMENT_STATE_JS};
|
|
228
|
+
const read = (el, index) => {
|
|
229
|
+
try {
|
|
230
|
+
return { context: contextOf(el), text: textOf(el), state: stateOf(el), geometry: index < ${VIEWPORT_HINT_LIMIT} ? geometryOf(el) : null };
|
|
231
|
+
} catch (e) {
|
|
232
|
+
return {};
|
|
139
233
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return { context: parts.join(' > '), text: textOf(el) };
|
|
143
|
-
});
|
|
234
|
+
};
|
|
235
|
+
return { viewport: (${VIEWPORT_SIZE_JS})(window), elements: Array.from(this, read) };
|
|
144
236
|
}`;
|
|
145
237
|
/**
|
|
146
238
|
* Backend node ids of the elements in a page-side array, each with where it
|
|
147
|
-
* lives (empty for the main document) and its
|
|
239
|
+
* lives (empty for the main document), its text and its viewport position.
|
|
148
240
|
*
|
|
149
241
|
* @param arrayObjectId - Remote object id of the array
|
|
150
242
|
* @returns Elements in array order (ones that cannot be described are left out)
|
|
243
|
+
* @throws CommandError (91) when the page could not describe the matches
|
|
151
244
|
*/
|
|
152
245
|
async function elementsWithDetails(arrayObjectId) {
|
|
246
|
+
const { viewport, elements = [] } = await readPageDetails(arrayObjectId);
|
|
247
|
+
const ids = await elementBackendNodeIds(arrayObjectId);
|
|
248
|
+
return ids.flatMap((backendNodeId, index) => {
|
|
249
|
+
if (backendNodeId === undefined)
|
|
250
|
+
return [];
|
|
251
|
+
const details = elements[index];
|
|
252
|
+
return [
|
|
253
|
+
{
|
|
254
|
+
backendNodeId,
|
|
255
|
+
context: details?.context ?? '',
|
|
256
|
+
text: details?.text ?? '',
|
|
257
|
+
state: details?.state ?? {},
|
|
258
|
+
...viewportHint(details?.geometry, viewport),
|
|
259
|
+
},
|
|
260
|
+
];
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Run {@link ELEMENT_DETAILS_FUNCTION} on a page-side array.
|
|
265
|
+
*
|
|
266
|
+
* @param arrayObjectId - Remote object id of the array
|
|
267
|
+
* @returns Details of each element and the viewport size
|
|
268
|
+
* @throws CommandError (91) when the script failed
|
|
269
|
+
*/
|
|
270
|
+
async function readPageDetails(arrayObjectId) {
|
|
153
271
|
const response = await callCDP('Runtime.callFunctionOn', {
|
|
154
272
|
objectId: arrayObjectId,
|
|
155
273
|
functionDeclaration: ELEMENT_DETAILS_FUNCTION,
|
|
156
274
|
returnByValue: true,
|
|
157
275
|
});
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
276
|
+
const result = response.data?.result;
|
|
277
|
+
if (response.status === 'error' || result?.exceptionDetails) {
|
|
278
|
+
const detail = result?.exceptionDetails?.exception?.description ?? response.error ?? 'no result';
|
|
279
|
+
const err = operationFailedError('describe the matches', detail.split('\n')[0] ?? detail);
|
|
280
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.SCRIPT_ERROR);
|
|
281
|
+
}
|
|
282
|
+
return (result?.result?.value ?? {});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Viewport position of a measured element for `dom query`.
|
|
286
|
+
*
|
|
287
|
+
* @param geometry - Page-side measurements (none beyond the hint limit)
|
|
288
|
+
* @param viewport - Viewport size
|
|
289
|
+
* @returns `inViewport` (and `clippedBy`), or nothing when not measured
|
|
290
|
+
*/
|
|
291
|
+
function viewportHint(geometry, viewport) {
|
|
292
|
+
if (!geometry || !viewport)
|
|
293
|
+
return {};
|
|
294
|
+
const { inViewport, clippedBy } = classifyViewportPosition(geometry, viewport);
|
|
295
|
+
return { inViewport, ...(clippedBy && { clippedBy }) };
|
|
171
296
|
}
|
|
172
297
|
/**
|
|
173
298
|
* The page-side array of a selector query, or the error explaining why there
|
|
174
299
|
* is none: a selector the browser rejects (a `SyntaxError` DOMException) is the
|
|
175
|
-
* user's (81
|
|
300
|
+
* user's (81; the browser's message is left out for selectors with filters,
|
|
301
|
+
* as it quotes the CSS bdg rewrote them to); anything else (no session, a
|
|
302
|
+
* page navigating away) is not.
|
|
176
303
|
*
|
|
177
304
|
* @param selector - CSS selector
|
|
178
305
|
* @param evaluated - `Runtime.evaluate` response
|
|
@@ -184,7 +311,7 @@ function selectionObjectId(selector, evaluated) {
|
|
|
184
311
|
{});
|
|
185
312
|
const description = exceptionDetails?.exception?.description;
|
|
186
313
|
if (description?.startsWith('SyntaxError')) {
|
|
187
|
-
const detail = description.split('\n')[0];
|
|
314
|
+
const detail = parseSelectorFilters(selector) ? undefined : description.split('\n')[0];
|
|
188
315
|
const err = invalidSelectorError(selector, detail);
|
|
189
316
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
190
317
|
}
|
|
@@ -242,21 +369,27 @@ export async function queryDOMElements(selector) {
|
|
|
242
369
|
if (elements.length > 20) {
|
|
243
370
|
log.debug(`Querying ${elements.length} elements with selector: ${selector}`);
|
|
244
371
|
}
|
|
245
|
-
const nodes = await mapConcurrently(elements, async (
|
|
372
|
+
const nodes = await mapConcurrently(elements, async (element, index) => {
|
|
373
|
+
const { backendNodeId, context, text, state, inViewport, clippedBy } = element;
|
|
246
374
|
const desc = await describeNode({ backendNodeId });
|
|
247
375
|
if (!desc)
|
|
248
376
|
return { index, nodeId: 0 };
|
|
249
377
|
const attributes = unpackAttributes(desc.attributes);
|
|
250
378
|
const classes = attributes['class']?.split(/\s+/).filter(Boolean);
|
|
251
379
|
const preview = textPreview(text);
|
|
380
|
+
const tag = desc.nodeName.toLowerCase();
|
|
381
|
+
const keys = keyAttributes(tag, attributes, state);
|
|
252
382
|
return {
|
|
253
383
|
index,
|
|
254
384
|
nodeId: desc.backendNodeId,
|
|
255
|
-
tag
|
|
256
|
-
...identifyingAttributes(attributes),
|
|
385
|
+
tag,
|
|
386
|
+
...identifyingAttributes(attributes, desc.nodeName),
|
|
387
|
+
...(keys && { attributes: keys }),
|
|
257
388
|
...(classes && { classes }),
|
|
258
389
|
...(preview && { preview }),
|
|
259
390
|
...(context && { context }),
|
|
391
|
+
...(inViewport && { inViewport }),
|
|
392
|
+
...(clippedBy && { clippedBy }),
|
|
260
393
|
};
|
|
261
394
|
});
|
|
262
395
|
return { selector, count: nodes.length, nodes };
|
|
@@ -265,63 +398,144 @@ export async function queryDOMElements(selector) {
|
|
|
265
398
|
* The attributes that tell similar elements apart (form fields especially).
|
|
266
399
|
*
|
|
267
400
|
* @param attributes - Element attributes
|
|
268
|
-
* @
|
|
401
|
+
* @param nodeName - Element name (`OPTION`s also report their `value`)
|
|
402
|
+
* @returns id, name and type (and an option's value) when present
|
|
269
403
|
*/
|
|
270
|
-
function identifyingAttributes(attributes) {
|
|
404
|
+
function identifyingAttributes(attributes, nodeName) {
|
|
405
|
+
const value = nodeName === 'OPTION' ? attributes['value'] : undefined;
|
|
271
406
|
return {
|
|
272
407
|
...(attributes['id'] && { id: attributes['id'] }),
|
|
273
408
|
...(attributes['name'] && { name: attributes['name'] }),
|
|
274
409
|
...(attributes['type'] && { type: attributes['type'] }),
|
|
410
|
+
...(value !== undefined && { value }),
|
|
275
411
|
};
|
|
276
412
|
}
|
|
277
413
|
/**
|
|
278
|
-
* The text of one element as the page renders it ({@link ELEMENT_TEXT_JS})
|
|
414
|
+
* The text of one element as the page renders it ({@link ELEMENT_TEXT_JS})
|
|
415
|
+
* and its form control state ({@link ELEMENT_STATE_JS}).
|
|
279
416
|
*
|
|
280
417
|
* @param ref - Node reference
|
|
281
|
-
* @
|
|
418
|
+
* @param full - Read all of a large container's text, not just its start
|
|
419
|
+
* @returns Element text and state, empty when the node cannot be read
|
|
282
420
|
*/
|
|
283
|
-
async function
|
|
421
|
+
async function elementTextAndState(ref, full) {
|
|
284
422
|
const objectGroup = `bdg-text-${process.pid}-${++queryCount}`;
|
|
285
423
|
const resolved = await callCDP('DOM.resolveNode', { ...ref, objectGroup });
|
|
286
424
|
const objectId = resolved.data?.result?.object
|
|
287
425
|
.objectId;
|
|
288
426
|
if (!objectId)
|
|
289
|
-
return '';
|
|
427
|
+
return { text: '', state: {} };
|
|
290
428
|
try {
|
|
291
429
|
const response = await callCDP('Runtime.callFunctionOn', {
|
|
292
430
|
objectId,
|
|
293
|
-
functionDeclaration: `function () { return (${ELEMENT_TEXT_JS})(this); }`,
|
|
431
|
+
functionDeclaration: `function (full) { return { text: (${ELEMENT_TEXT_JS})(this, full), state: (${ELEMENT_STATE_JS})(this) }; }`,
|
|
432
|
+
arguments: [{ value: full }],
|
|
294
433
|
returnByValue: true,
|
|
295
434
|
});
|
|
296
|
-
const value = response.data?.result?.result
|
|
297
|
-
|
|
298
|
-
|
|
435
|
+
const value = response.data?.result?.result?.value;
|
|
436
|
+
return {
|
|
437
|
+
text: typeof value?.text === 'string' ? value.text : '',
|
|
438
|
+
state: value?.state ?? {},
|
|
439
|
+
};
|
|
299
440
|
}
|
|
300
441
|
finally {
|
|
301
442
|
await callCDP('Runtime.releaseObjectGroup', { objectGroup });
|
|
302
443
|
}
|
|
303
444
|
}
|
|
304
445
|
/**
|
|
305
|
-
* Get DOM context (tag, classes, text preview) for a node
|
|
446
|
+
* Get DOM context (tag, classes, key attributes, text preview) for a node: a one-line
|
|
447
|
+
* preview, and up to {@link ELEMENT_TEXT_LENGTH} characters of text when it
|
|
448
|
+
* is longer (all of it with `full`).
|
|
306
449
|
*
|
|
307
450
|
* @param ref - Node reference
|
|
451
|
+
* @param options - `full`: the whole text instead of its first 500 characters
|
|
308
452
|
* @returns DOM context, or null if the node does not exist
|
|
309
453
|
*/
|
|
310
|
-
export async function getDomContext(ref) {
|
|
454
|
+
export async function getDomContext(ref, options = {}) {
|
|
311
455
|
await callCDP('DOM.enable', {});
|
|
312
456
|
const desc = await describeNode(ref);
|
|
313
457
|
if (!desc) {
|
|
314
458
|
log.debug(`No DOM context for ${JSON.stringify(ref)}`);
|
|
315
459
|
return null;
|
|
316
460
|
}
|
|
317
|
-
const
|
|
318
|
-
const
|
|
461
|
+
const full = options.full === true;
|
|
462
|
+
const attributes = unpackAttributes(desc.attributes);
|
|
463
|
+
const classes = attributes['class']?.split(/\s+/).filter(Boolean);
|
|
464
|
+
const { text, state } = await elementTextAndState(ref, full);
|
|
465
|
+
const preview = textPreview(text);
|
|
466
|
+
const longer = textPreview(text, full ? Number.POSITIVE_INFINITY : ELEMENT_TEXT_LENGTH);
|
|
467
|
+
const tag = desc.nodeName.toLowerCase();
|
|
468
|
+
const keys = keyAttributes(tag, attributes, state);
|
|
319
469
|
return {
|
|
320
|
-
tag
|
|
470
|
+
tag,
|
|
321
471
|
...(classes && classes.length > 0 && { classes }),
|
|
472
|
+
...(keys && { attributes: keys }),
|
|
473
|
+
...(state.sensitive && { sensitive: true }),
|
|
322
474
|
...(preview && { preview }),
|
|
475
|
+
...(longer !== preview && { text: longer }),
|
|
476
|
+
...(!preview && (await childElements(ref))),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/** Child elements named for an element without text */
|
|
480
|
+
const CHILDREN_LISTED = 5;
|
|
481
|
+
/**
|
|
482
|
+
* The child elements of an element (for one without text: a body holding
|
|
483
|
+
* only an iframe, an empty app root).
|
|
484
|
+
*
|
|
485
|
+
* @param ref - Node reference
|
|
486
|
+
* @returns The first {@link CHILDREN_LISTED} as `tag#id.class` and how many there are
|
|
487
|
+
*/
|
|
488
|
+
async function childElements(ref) {
|
|
489
|
+
const response = await callCDP('DOM.describeNode', { ...ref, depth: 1 });
|
|
490
|
+
const node = response.data?.result?.node;
|
|
491
|
+
const elements = (node?.children ?? []).filter((child) => child.nodeType === 1);
|
|
492
|
+
return {
|
|
493
|
+
children: elements.slice(0, CHILDREN_LISTED).map(childLabel),
|
|
494
|
+
childCount: elements.length,
|
|
323
495
|
};
|
|
324
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* A child element in a few characters.
|
|
499
|
+
*
|
|
500
|
+
* @param node - Child node
|
|
501
|
+
* @returns e.g. `iframe#app.full`
|
|
502
|
+
*/
|
|
503
|
+
function childLabel(node) {
|
|
504
|
+
const attributes = unpackAttributes(node.attributes);
|
|
505
|
+
const id = attributes['id'] ? `#${attributes['id']}` : '';
|
|
506
|
+
const classes = (attributes['class'] ?? '').split(/\s+/).filter(Boolean).slice(0, 2);
|
|
507
|
+
return `${node.nodeName.toLowerCase()}${id}${classes.map((name) => `.${name}`).join('')}`;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* All matches of a selector.
|
|
511
|
+
*
|
|
512
|
+
* @param selector - CSS selector
|
|
513
|
+
* @returns Backend node ids of the matches (at least one)
|
|
514
|
+
* @throws CommandError (83) when nothing matches
|
|
515
|
+
*/
|
|
516
|
+
async function selectMatches(selector) {
|
|
517
|
+
const backendNodeIds = await selectAll(selector);
|
|
518
|
+
if (backendNodeIds.length > 0)
|
|
519
|
+
return backendNodeIds;
|
|
520
|
+
const err = await noMatchesError(selector);
|
|
521
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* One match of a selector.
|
|
525
|
+
*
|
|
526
|
+
* @param selector - CSS selector
|
|
527
|
+
* @param index - Which match (0-based)
|
|
528
|
+
* @returns Its backend node id
|
|
529
|
+
* @throws CommandError (83) when nothing matches, (81) for an index beyond the matches
|
|
530
|
+
*/
|
|
531
|
+
export async function selectMatch(selector, index = 0) {
|
|
532
|
+
const backendNodeIds = await selectMatches(selector);
|
|
533
|
+
const backendNodeId = backendNodeIds[index];
|
|
534
|
+
if (backendNodeId !== undefined)
|
|
535
|
+
return backendNodeId;
|
|
536
|
+
const err = indexOutOfRangeError(index, backendNodeIds.length - 1);
|
|
537
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
538
|
+
}
|
|
325
539
|
/**
|
|
326
540
|
* Pick the nodes `dom get --raw` should report for a selector.
|
|
327
541
|
*
|
|
@@ -330,20 +544,10 @@ export async function getDomContext(ref) {
|
|
|
330
544
|
* @returns Node references to describe
|
|
331
545
|
*/
|
|
332
546
|
async function selectForGet(selector, options) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const err = noNodesFoundError(selector);
|
|
336
|
-
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
337
|
-
}
|
|
338
|
-
if (options.all)
|
|
339
|
-
return backendNodeIds.map((backendNodeId) => ({ backendNodeId }));
|
|
340
|
-
const position = options.nth ?? 0;
|
|
341
|
-
const backendNodeId = backendNodeIds[position];
|
|
342
|
-
if (backendNodeId === undefined) {
|
|
343
|
-
const err = indexOutOfRangeError(position, backendNodeIds.length - 1);
|
|
344
|
-
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
547
|
+
if (options.all) {
|
|
548
|
+
return (await selectMatches(selector)).map((backendNodeId) => ({ backendNodeId }));
|
|
345
549
|
}
|
|
346
|
-
return [{ backendNodeId }];
|
|
550
|
+
return [{ backendNodeId: await selectMatch(selector, options.nth ?? 0) }];
|
|
347
551
|
}
|
|
348
552
|
/**
|
|
349
553
|
* Get full details (attributes, outer HTML) for `bdg dom get --raw`.
|
|
@@ -366,7 +570,7 @@ export async function getDOMElements(options) {
|
|
|
366
570
|
refs = await selectForGet(options.selector, options);
|
|
367
571
|
}
|
|
368
572
|
else {
|
|
369
|
-
const err = eitherArgumentRequiredError('selector', 'nodeId', 'bdg dom get <selector> or bdg dom get --node-id <id>');
|
|
573
|
+
const err = eitherArgumentRequiredError('selector', 'nodeId', `${sessionCommand('bdg dom get <selector>')} or ${sessionCommand('bdg dom get --node-id <id>')}`);
|
|
370
574
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
371
575
|
}
|
|
372
576
|
const nodes = await mapConcurrently(refs, async (ref) => {
|
|
@@ -398,7 +602,7 @@ export async function getDOMElements(options) {
|
|
|
398
602
|
export async function resolveSelector(selector) {
|
|
399
603
|
const backendNodeId = (await selectAll(selector))[0];
|
|
400
604
|
if (backendNodeId === undefined) {
|
|
401
|
-
const err =
|
|
605
|
+
const err = await noMatchesError(selector);
|
|
402
606
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.RESOURCE_NOT_FOUND);
|
|
403
607
|
}
|
|
404
608
|
return { backendNodeId };
|
|
@@ -412,16 +616,6 @@ export async function resolveSelector(selector) {
|
|
|
412
616
|
export async function resolveBackendNodeIds(selectors) {
|
|
413
617
|
return mapConcurrently(selectors, async (selector) => (await selectAll(selector))[0]);
|
|
414
618
|
}
|
|
415
|
-
/**
|
|
416
|
-
* Accessibility node of the first element matching a selector.
|
|
417
|
-
*
|
|
418
|
-
* @param selector - CSS selector
|
|
419
|
-
* @returns A11y node, or null when nothing matches or the node is not exposed
|
|
420
|
-
*/
|
|
421
|
-
export async function resolveA11yNodeForSelector(selector) {
|
|
422
|
-
const [backendNodeId] = await resolveBackendNodeIds([selector]);
|
|
423
|
-
return backendNodeId === undefined ? null : resolveA11yNode({ backendNodeId });
|
|
424
|
-
}
|
|
425
619
|
/**
|
|
426
620
|
* Check that a cached element is still part of the current page.
|
|
427
621
|
*
|
|
@@ -431,10 +625,10 @@ export async function resolveA11yNodeForSelector(selector) {
|
|
|
431
625
|
* false for removed elements.
|
|
432
626
|
*
|
|
433
627
|
* @param backendNodeId - Backend node id from the query cache
|
|
434
|
-
* @param
|
|
628
|
+
* @param source - Index the user gave and the list it refers to, for the error message
|
|
435
629
|
* @throws CommandError (87) when the element is gone
|
|
436
630
|
*/
|
|
437
|
-
export async function assertNodeAttached(backendNodeId,
|
|
631
|
+
export async function assertNodeAttached(backendNodeId, source) {
|
|
438
632
|
const resolved = await callCDP('DOM.resolveNode', { backendNodeId, objectGroup: 'bdg-check' });
|
|
439
633
|
const objectId = resolved.data?.result?.object
|
|
440
634
|
.objectId;
|
|
@@ -450,7 +644,7 @@ export async function assertNodeAttached(backendNodeId, index) {
|
|
|
450
644
|
await callCDP('Runtime.releaseObjectGroup', { objectGroup: 'bdg-check' });
|
|
451
645
|
}
|
|
452
646
|
if (!attached) {
|
|
453
|
-
const err = staleNodeError(index);
|
|
647
|
+
const err = staleNodeError(source?.index, source);
|
|
454
648
|
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.STALE_CACHE);
|
|
455
649
|
}
|
|
456
650
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Captures the common flow: resolve selector/index → call IPC → normalize errors.
|
|
5
5
|
* Keeps individual command handlers focused on option wiring and output formatting.
|
|
6
6
|
*/
|
|
7
|
+
import type { IndexSource } from '../../../types.js';
|
|
7
8
|
interface IpcResponse<T> {
|
|
8
9
|
status: string;
|
|
9
10
|
data?: T;
|
|
@@ -16,6 +17,8 @@ interface ResultPayload {
|
|
|
16
17
|
error?: string | undefined;
|
|
17
18
|
suggestion?: string | undefined;
|
|
18
19
|
exitCode?: number | undefined;
|
|
20
|
+
/** The element is not one the command acts on (fill, submit) */
|
|
21
|
+
unsuitableElement?: boolean | undefined;
|
|
19
22
|
}
|
|
20
23
|
interface CommandResult<T> {
|
|
21
24
|
success: boolean;
|
|
@@ -42,6 +45,8 @@ export interface ElementCommandOptions<Req, Res extends ResultPayload> {
|
|
|
42
45
|
}) => Req;
|
|
43
46
|
/** Invoke the IPC client function. */
|
|
44
47
|
call: (req: Req) => Promise<IpcResponse<Res>>;
|
|
48
|
+
/** `bdg dom` subcommand being run, e.g. "fill" (for suggestions). */
|
|
49
|
+
command: string;
|
|
45
50
|
/** Operation label used in fallback error messages (e.g. "fill element"). */
|
|
46
51
|
action: string;
|
|
47
52
|
/** Fallback suggestion when the result reports failure without one. */
|
|
@@ -49,8 +54,11 @@ export interface ElementCommandOptions<Req, Res extends ResultPayload> {
|
|
|
49
54
|
}
|
|
50
55
|
/**
|
|
51
56
|
* Resolve an element target, invoke the IPC call, and normalize failures
|
|
52
|
-
* into the structured `CommandRunner` result shape.
|
|
57
|
+
* into the structured `CommandRunner` result shape. A numeric index names the
|
|
58
|
+
* list it refers to (`indexSource` in the data, and in errors).
|
|
53
59
|
*/
|
|
54
|
-
export declare function runElementCommand<Req, Res extends ResultPayload>(options: ElementCommandOptions<Req, Res>): Promise<CommandResult<Omit<Res, 'success'
|
|
60
|
+
export declare function runElementCommand<Req, Res extends ResultPayload>(options: ElementCommandOptions<Req, Res>): Promise<CommandResult<Omit<Res, 'success'> & {
|
|
61
|
+
indexSource?: IndexSource;
|
|
62
|
+
}>>;
|
|
55
63
|
export {};
|
|
56
64
|
//# sourceMappingURL=runElementCommand.d.ts.map
|