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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-side descriptions of elements for DOM command output (`dom query`,
|
|
3
|
+
* `dom layout`, DOM context of console messages): their text as a user sees
|
|
4
|
+
* it and where they live (iframes, shadow roots).
|
|
5
|
+
*/
|
|
6
|
+
/** Length of the text `dom get` shows for an element. */
|
|
7
|
+
export declare const ELEMENT_TEXT_LENGTH = 500;
|
|
8
|
+
/**
|
|
9
|
+
* Page-side removal of decorations from an element's text: close buttons
|
|
10
|
+
* (`.close`, `aria-label="Close"` or `"Dismiss"`, a button or link showing
|
|
11
|
+
* just `×`) and `aria-hidden` icons (text without letters or digits), so a
|
|
12
|
+
* flash message does not end in "×". `aria-hidden` text with words is kept:
|
|
13
|
+
* it is often the visible twin of a screen-reader text. Each decoration's
|
|
14
|
+
* text is removed once, from the end.
|
|
15
|
+
*/
|
|
16
|
+
export declare const WITHOUT_DECORATIONS_JS = "(el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n}";
|
|
17
|
+
/**
|
|
18
|
+
* Page-side text of an element as a user sees it: `innerText` for a rendered
|
|
19
|
+
* element (CSS-hidden parts left out, inline elements not split apart), none
|
|
20
|
+
* for an element that is not rendered, `textContent` for SVG and other
|
|
21
|
+
* elements without `innerText` and for `display: contents` wrappers (no box
|
|
22
|
+
* of their own, but their children are shown), and the label of an
|
|
23
|
+
* `<option>` (which its `<select>` renders). For large containers (more
|
|
24
|
+
* than 2000 characters of text) only the start is read, from the text nodes
|
|
25
|
+
* whose parent is rendered, so a preview never lays out a whole page's text,
|
|
26
|
+
* unless `full` is set. Decorations are left out ({@link WITHOUT_DECORATIONS_JS}).
|
|
27
|
+
*/
|
|
28
|
+
export declare const ELEMENT_TEXT_JS = "(el, full) => {\n const withoutDecorations = (el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n};\n const all = el.textContent || '';\n if (el.tagName === 'OPTION') return el.label;\n if (typeof el.innerText !== 'string') return full ? all : all.slice(0, 2000);\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n if (!rendered(el)) {\n const boxless = el.ownerDocument.defaultView.getComputedStyle(el).display === 'contents';\n return boxless ? withoutDecorations(el, full ? all : all.slice(0, 2000)) : '';\n }\n if (full || all.length <= 2000) return withoutDecorations(el, el.innerText);\n const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let start = '';\n while (start.length < 1000 && walker.nextNode()) {\n const parent = walker.currentNode.parentElement;\n if (!parent || rendered(parent)) start += walker.currentNode.data;\n }\n return withoutDecorations(el, start);\n}";
|
|
29
|
+
/** Shown instead of a secret field value (the same for every length) */
|
|
30
|
+
export declare const MASKED_VALUE = "\u2022\u2022\u2022\u2022";
|
|
31
|
+
/**
|
|
32
|
+
* Page-side check whether a form control holds a secret whose value must
|
|
33
|
+
* never leave the page: a password field (live type or type attribute), a
|
|
34
|
+
* field shown masked by CSS (`-webkit-text-security` other than `none`),
|
|
35
|
+
* one whose `autocomplete` names a password, a payment card (`cc-*`) or a
|
|
36
|
+
* one-time code, or a field named like a password, one-time code or card code
|
|
37
|
+
* (`password`, `passwd`, `pwd`, `passcode`, `otp`, `cvv`, `cvc`), which
|
|
38
|
+
* covers a password field switched to text by a "show password" button.
|
|
39
|
+
*/
|
|
40
|
+
export declare const SENSITIVE_FIELD_JS = "(el) => {\n const autocomplete = el.getAttribute('autocomplete') || '';\n if (/(^|\\s)(cc-[a-z-]+|one-time-code|current-password|new-password)(\\s|$)/i.test(autocomplete)) return true;\n if (el.type === 'password' || /^password$/i.test(el.getAttribute('type') || '')) return true;\n const names = [el.getAttribute('name'), el.id, autocomplete].join(' ');\n if (/passw|passwd|pwd|passcode|(^|[^a-z])otp([^a-z]|$)|cvv|cvc/i.test(names)) return true;\n try {\n const security = el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('-webkit-text-security');\n return Boolean(security) && security !== 'none';\n } catch (e) {\n return false;\n }\n}";
|
|
41
|
+
/**
|
|
42
|
+
* Page-side live state of a form control, which its attributes do not show:
|
|
43
|
+
* the type and current value of an `<input>` (for checkboxes and radios
|
|
44
|
+
* `checked` and their `value` attribute, not the default "on"), the value of
|
|
45
|
+
* a `<textarea>`, the labels of a `<select>`'s selected options and the type
|
|
46
|
+
* of a `<button>` in a form (`submit` when it has none; outside a form only
|
|
47
|
+
* a type attribute is shown). Empty for other elements.
|
|
48
|
+
*
|
|
49
|
+
* Secrets never leave the page: a hidden input's value is left out, and the
|
|
50
|
+
* value (or selected option) of a sensitive field ({@link SENSITIVE_FIELD_JS})
|
|
51
|
+
* is replaced by {@link MASKED_VALUE}, whatever its length, with
|
|
52
|
+
* `sensitive: true`.
|
|
53
|
+
*/
|
|
54
|
+
export declare const ELEMENT_STATE_JS = "(el) => {\n const isSensitive = (el) => {\n const autocomplete = el.getAttribute('autocomplete') || '';\n if (/(^|\\s)(cc-[a-z-]+|one-time-code|current-password|new-password)(\\s|$)/i.test(autocomplete)) return true;\n if (el.type === 'password' || /^password$/i.test(el.getAttribute('type') || '')) return true;\n const names = [el.getAttribute('name'), el.id, autocomplete].join(' ');\n if (/passw|passwd|pwd|passcode|(^|[^a-z])otp([^a-z]|$)|cvv|cvc/i.test(names)) return true;\n try {\n const security = el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('-webkit-text-security');\n return Boolean(security) && security !== 'none';\n } catch (e) {\n return false;\n }\n};\n const mask = (value) => (value ? '\u2022\u2022\u2022\u2022' : '');\n const guarded = (state) => {\n if (!isSensitive(el)) return state;\n const result = { ...state, sensitive: true };\n if ('value' in result) result.value = mask(result.value);\n if ('selected' in result) result.selected = mask(result.selected);\n return result;\n };\n switch (el.localName) {\n case 'input':\n if (el.type === 'hidden') return { type: 'hidden' };\n return guarded(\n /^(checkbox|radio)$/.test(el.type)\n ? { type: el.type, checked: el.checked, value: el.getAttribute('value') || '' }\n : { type: el.type, value: el.value }\n );\n case 'textarea':\n return guarded({ value: el.value });\n case 'select':\n return guarded({ selected: Array.from(el.selectedOptions || [], (option) => option.label).join(', ') });\n case 'button':\n return el.form ? { type: el.type } : {};\n default:\n return {};\n }\n}";
|
|
55
|
+
/**
|
|
56
|
+
* Page-side short description of an element: tag, id and up to two classes,
|
|
57
|
+
* e.g. `button#save.primary.large`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const ELEMENT_DESCRIPTION_JS = "(node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '')";
|
|
60
|
+
/**
|
|
61
|
+
* Page-side position of an element among its parent's children with the
|
|
62
|
+
* same short description ({@link ELEMENT_DESCRIPTION_JS}), e.g. `(2nd of 3)`;
|
|
63
|
+
* empty when it is the only one.
|
|
64
|
+
*/
|
|
65
|
+
export declare const SIBLING_POSITION_JS = "(el) => {\n const describe = (node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '');\n const parent = el.parentElement;\n if (!parent) return '';\n const own = describe(el);\n const same = Array.from(parent.children).filter((child) => describe(child) === own);\n if (same.length < 2) return '';\n const n = same.indexOf(el) + 1;\n const teen = n % 100 >= 11 && n % 100 <= 13;\n const suffix = teen ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' })[n % 10] || 'th';\n return '(' + n + suffix + ' of ' + same.length + ')';\n}";
|
|
66
|
+
/**
|
|
67
|
+
* Page-side identity of an element an action hit, so the output says which
|
|
68
|
+
* one it was, as one string: its short description
|
|
69
|
+
* ({@link ELEMENT_DESCRIPTION_JS}) and visible text (button value for button
|
|
70
|
+
* inputs, decorations left out), e.g. `button#add.btn "Add to cart"`. A
|
|
71
|
+
* `<select>` is named by its label, aria-label or name, else by its selected
|
|
72
|
+
* option, e.g. `select.sort "Sort products"`. An element without visible
|
|
73
|
+
* text is described by itself first: by its position among same-looking
|
|
74
|
+
* siblings ({@link SIBLING_POSITION_JS}), e.g. `div.figure (2nd of 3)`, with
|
|
75
|
+
* its aria-label, placeholder or title when it has one. Only an element
|
|
76
|
+
* without an id that is the only one of its kind is named by the nearest of
|
|
77
|
+
* three ancestors that has text, e.g.
|
|
78
|
+
* `input.toggle in div.view "Write report"` (rows of a list share their
|
|
79
|
+
* aria-label), leaving out the options
|
|
80
|
+
* of `<select>`s in it (read like `innerText`: CSS-hidden text is left out of
|
|
81
|
+
* a rendered ancestor; at most 500 text nodes; selects in shadow roots are
|
|
82
|
+
* not looked into), and never past an editable ancestor, whose text may be
|
|
83
|
+
* typed input; otherwise its aria-label, placeholder or title is used. Texts
|
|
84
|
+
* are cut at 40 characters. Contenteditable elements count as controls: what
|
|
85
|
+
* was typed into them is never echoed (nor their ancestors' text).
|
|
86
|
+
*/
|
|
87
|
+
export declare const ELEMENT_IDENTITY_JS = "(el) => {\n const describe = (node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '');\n const siblingPosition = (el) => {\n const describe = (node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '');\n const parent = el.parentElement;\n if (!parent) return '';\n const own = describe(el);\n const same = Array.from(parent.children).filter((child) => describe(child) === own);\n if (same.length < 2) return '';\n const n = same.indexOf(el) + 1;\n const teen = n % 100 >= 11 && n % 100 <= 13;\n const suffix = teen ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' })[n % 10] || 'th';\n return '(' + n + suffix + ' of ' + same.length + ')';\n};\n const withoutDecorations = (el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n};\n const clean = (text) => (text || '').replace(/\\s+/g, ' ').trim();\n const cut = (text) => {\n const characters = Array.from(text);\n return characters.length > 40 ? characters.slice(0, 40).join('') + '\u2026' : text;\n };\n const isControl = (node) => /^(input|select|textarea)$/.test(node.localName) || node.isContentEditable;\n const shownText = (node) =>\n isControl(node) ? '' : clean(withoutDecorations(node, typeof node.innerText === 'string' ? node.innerText : node.textContent));\n const buttonValue = (node) => (node.localName === 'input' && /^(submit|button|reset)$/i.test(node.type) ? clean(node.value) : '');\n const attributeText = (node) =>\n clean(node.getAttribute('aria-label')) || clean(node.getAttribute('placeholder')) || clean(node.getAttribute('title'));\n const selectName = (node) =>\n clean(node.labels && node.labels[0] && node.labels[0].innerText) ||\n clean(node.getAttribute('aria-label')) ||\n clean(node.getAttribute('name')) ||\n clean(node.selectedOptions && node.selectedOptions[0] && node.selectedOptions[0].label);\n const shown = (node, options) => !node.checkVisibility || node.checkVisibility(options);\n const textOutsideSelects = (node) => {\n if (!node.querySelector('select')) return shownText(node);\n const hiddenLeftOut = shown(node);\n const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_TEXT);\n let text = '';\n for (let visited = 0; visited < 500 && text.length < 200 && walker.nextNode(); visited++) {\n const parent = walker.currentNode.parentElement;\n if (!parent || parent.closest('select, textarea')) continue;\n if (!hiddenLeftOut || shown(parent, { visibilityProperty: true })) text += ' ' + walker.currentNode.data;\n }\n return clean(text);\n };\n const quoted = (text) => (text ? ' \"' + cut(text) + '\"' : '');\n const visible = shownText(el) || buttonValue(el) || (el.localName === 'select' ? selectName(el) : '');\n if (visible) return describe(el) + quoted(visible);\n const attribute = attributeText(el);\n const position = siblingPosition(el);\n if (position) return describe(el) + ' ' + position + quoted(attribute);\n let ancestor = el.id || el.isContentEditable ? null : el.parentElement;\n for (let depth = 0; ancestor && depth < 3; depth++, ancestor = ancestor.parentElement) {\n if (isControl(ancestor)) break;\n const text = textOutsideSelects(ancestor);\n if (text) return describe(el) + ' in ' + describe(ancestor) + quoted(text);\n }\n return describe(el) + quoted(attribute);\n}";
|
|
88
|
+
/**
|
|
89
|
+
* Page-side location of an element: the iframes it is in (outermost first)
|
|
90
|
+
* and the shadow root holding it, e.g. `iframe#pay > shadow root of <x-card>`;
|
|
91
|
+
* empty for the main document.
|
|
92
|
+
*/
|
|
93
|
+
export declare const ELEMENT_CONTEXT_JS = "(el) => {\n const describe = (node) => node.tagName.toLowerCase() + (node.id ? '#' + node.id : '');\n const parts = [];\n for (let doc = el.ownerDocument; doc && doc.defaultView && doc.defaultView.frameElement; ) {\n const frame = doc.defaultView.frameElement;\n parts.unshift(describe(frame));\n doc = frame.ownerDocument;\n }\n const root = el.getRootNode();\n if (root.host) parts.push('shadow root of <' + describe(root.host) + '>');\n return parts.join(' > ');\n}";
|
|
94
|
+
/**
|
|
95
|
+
* Short text preview of an element's text: whitespace collapsed, cut on a
|
|
96
|
+
* whole character (an emoji is never split, which would make JSON invalid).
|
|
97
|
+
*
|
|
98
|
+
* @param text - Element text as the page renders it
|
|
99
|
+
* @param length - Characters kept
|
|
100
|
+
* @returns Collapsed text, truncated to `length` characters (followed by `...`)
|
|
101
|
+
*/
|
|
102
|
+
export declare function textPreview(text: string, length?: number): string;
|
|
103
|
+
//# sourceMappingURL=elementInfo.d.ts.map
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-side descriptions of elements for DOM command output (`dom query`,
|
|
3
|
+
* `dom layout`, DOM context of console messages): their text as a user sees
|
|
4
|
+
* it and where they live (iframes, shadow roots).
|
|
5
|
+
*/
|
|
6
|
+
/** Length of the text preview shown for elements. */
|
|
7
|
+
const PREVIEW_LENGTH = 80;
|
|
8
|
+
/** Length of the text `dom get` shows for an element. */
|
|
9
|
+
export const ELEMENT_TEXT_LENGTH = 500;
|
|
10
|
+
/**
|
|
11
|
+
* Page-side removal of decorations from an element's text: close buttons
|
|
12
|
+
* (`.close`, `aria-label="Close"` or `"Dismiss"`, a button or link showing
|
|
13
|
+
* just `×`) and `aria-hidden` icons (text without letters or digits), so a
|
|
14
|
+
* flash message does not end in "×". `aria-hidden` text with words is kept:
|
|
15
|
+
* it is often the visible twin of a screen-reader text. Each decoration's
|
|
16
|
+
* text is removed once, from the end.
|
|
17
|
+
*/
|
|
18
|
+
export const WITHOUT_DECORATIONS_JS = `(el, text) => {
|
|
19
|
+
if (!text || typeof el.querySelectorAll !== 'function') return text;
|
|
20
|
+
const glyph = /^\\s*[×✕✖✗⨯]\\s*$/;
|
|
21
|
+
const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();
|
|
22
|
+
const closer = '.close, [aria-label="close" i], [aria-label="dismiss" i]';
|
|
23
|
+
const rendered = (node) => !node.checkVisibility || node.checkVisibility();
|
|
24
|
+
const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden="true"]'))
|
|
25
|
+
.filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));
|
|
26
|
+
if (/[×✕✖✗⨯]/.test(text)) {
|
|
27
|
+
found.push(...Array.from(el.querySelectorAll('button, a, [role="button"]')).filter((node) => glyph.test(node.textContent || '')));
|
|
28
|
+
}
|
|
29
|
+
const unique = Array.from(new Set(found));
|
|
30
|
+
const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));
|
|
31
|
+
let result = text;
|
|
32
|
+
for (const node of outermost) {
|
|
33
|
+
const part = textOf(node);
|
|
34
|
+
const at = part ? result.lastIndexOf(part) : -1;
|
|
35
|
+
if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}`;
|
|
39
|
+
/**
|
|
40
|
+
* Page-side text of an element as a user sees it: `innerText` for a rendered
|
|
41
|
+
* element (CSS-hidden parts left out, inline elements not split apart), none
|
|
42
|
+
* for an element that is not rendered, `textContent` for SVG and other
|
|
43
|
+
* elements without `innerText` and for `display: contents` wrappers (no box
|
|
44
|
+
* of their own, but their children are shown), and the label of an
|
|
45
|
+
* `<option>` (which its `<select>` renders). For large containers (more
|
|
46
|
+
* than 2000 characters of text) only the start is read, from the text nodes
|
|
47
|
+
* whose parent is rendered, so a preview never lays out a whole page's text,
|
|
48
|
+
* unless `full` is set. Decorations are left out ({@link WITHOUT_DECORATIONS_JS}).
|
|
49
|
+
*/
|
|
50
|
+
export const ELEMENT_TEXT_JS = `(el, full) => {
|
|
51
|
+
const withoutDecorations = ${WITHOUT_DECORATIONS_JS};
|
|
52
|
+
const all = el.textContent || '';
|
|
53
|
+
if (el.tagName === 'OPTION') return el.label;
|
|
54
|
+
if (typeof el.innerText !== 'string') return full ? all : all.slice(0, 2000);
|
|
55
|
+
const rendered = (node) => !node.checkVisibility || node.checkVisibility();
|
|
56
|
+
if (!rendered(el)) {
|
|
57
|
+
const boxless = el.ownerDocument.defaultView.getComputedStyle(el).display === 'contents';
|
|
58
|
+
return boxless ? withoutDecorations(el, full ? all : all.slice(0, 2000)) : '';
|
|
59
|
+
}
|
|
60
|
+
if (full || all.length <= 2000) return withoutDecorations(el, el.innerText);
|
|
61
|
+
const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
62
|
+
let start = '';
|
|
63
|
+
while (start.length < 1000 && walker.nextNode()) {
|
|
64
|
+
const parent = walker.currentNode.parentElement;
|
|
65
|
+
if (!parent || rendered(parent)) start += walker.currentNode.data;
|
|
66
|
+
}
|
|
67
|
+
return withoutDecorations(el, start);
|
|
68
|
+
}`;
|
|
69
|
+
/** Shown instead of a secret field value (the same for every length) */
|
|
70
|
+
export const MASKED_VALUE = '••••';
|
|
71
|
+
/**
|
|
72
|
+
* Page-side check whether a form control holds a secret whose value must
|
|
73
|
+
* never leave the page: a password field (live type or type attribute), a
|
|
74
|
+
* field shown masked by CSS (`-webkit-text-security` other than `none`),
|
|
75
|
+
* one whose `autocomplete` names a password, a payment card (`cc-*`) or a
|
|
76
|
+
* one-time code, or a field named like a password, one-time code or card code
|
|
77
|
+
* (`password`, `passwd`, `pwd`, `passcode`, `otp`, `cvv`, `cvc`), which
|
|
78
|
+
* covers a password field switched to text by a "show password" button.
|
|
79
|
+
*/
|
|
80
|
+
export const SENSITIVE_FIELD_JS = `(el) => {
|
|
81
|
+
const autocomplete = el.getAttribute('autocomplete') || '';
|
|
82
|
+
if (/(^|\\s)(cc-[a-z-]+|one-time-code|current-password|new-password)(\\s|$)/i.test(autocomplete)) return true;
|
|
83
|
+
if (el.type === 'password' || /^password$/i.test(el.getAttribute('type') || '')) return true;
|
|
84
|
+
const names = [el.getAttribute('name'), el.id, autocomplete].join(' ');
|
|
85
|
+
if (/passw|passwd|pwd|passcode|(^|[^a-z])otp([^a-z]|$)|cvv|cvc/i.test(names)) return true;
|
|
86
|
+
try {
|
|
87
|
+
const security = el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('-webkit-text-security');
|
|
88
|
+
return Boolean(security) && security !== 'none';
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}`;
|
|
93
|
+
/**
|
|
94
|
+
* Page-side live state of a form control, which its attributes do not show:
|
|
95
|
+
* the type and current value of an `<input>` (for checkboxes and radios
|
|
96
|
+
* `checked` and their `value` attribute, not the default "on"), the value of
|
|
97
|
+
* a `<textarea>`, the labels of a `<select>`'s selected options and the type
|
|
98
|
+
* of a `<button>` in a form (`submit` when it has none; outside a form only
|
|
99
|
+
* a type attribute is shown). Empty for other elements.
|
|
100
|
+
*
|
|
101
|
+
* Secrets never leave the page: a hidden input's value is left out, and the
|
|
102
|
+
* value (or selected option) of a sensitive field ({@link SENSITIVE_FIELD_JS})
|
|
103
|
+
* is replaced by {@link MASKED_VALUE}, whatever its length, with
|
|
104
|
+
* `sensitive: true`.
|
|
105
|
+
*/
|
|
106
|
+
export const ELEMENT_STATE_JS = `(el) => {
|
|
107
|
+
const isSensitive = ${SENSITIVE_FIELD_JS};
|
|
108
|
+
const mask = (value) => (value ? '${MASKED_VALUE}' : '');
|
|
109
|
+
const guarded = (state) => {
|
|
110
|
+
if (!isSensitive(el)) return state;
|
|
111
|
+
const result = { ...state, sensitive: true };
|
|
112
|
+
if ('value' in result) result.value = mask(result.value);
|
|
113
|
+
if ('selected' in result) result.selected = mask(result.selected);
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
switch (el.localName) {
|
|
117
|
+
case 'input':
|
|
118
|
+
if (el.type === 'hidden') return { type: 'hidden' };
|
|
119
|
+
return guarded(
|
|
120
|
+
/^(checkbox|radio)$/.test(el.type)
|
|
121
|
+
? { type: el.type, checked: el.checked, value: el.getAttribute('value') || '' }
|
|
122
|
+
: { type: el.type, value: el.value }
|
|
123
|
+
);
|
|
124
|
+
case 'textarea':
|
|
125
|
+
return guarded({ value: el.value });
|
|
126
|
+
case 'select':
|
|
127
|
+
return guarded({ selected: Array.from(el.selectedOptions || [], (option) => option.label).join(', ') });
|
|
128
|
+
case 'button':
|
|
129
|
+
return el.form ? { type: el.type } : {};
|
|
130
|
+
default:
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
}`;
|
|
134
|
+
/**
|
|
135
|
+
* Page-side short description of an element: tag, id and up to two classes,
|
|
136
|
+
* e.g. `button#save.primary.large`.
|
|
137
|
+
*/
|
|
138
|
+
export const ELEMENT_DESCRIPTION_JS = `(node) => node.tagName.toLowerCase() +
|
|
139
|
+
(node.id ? '#' + node.id : '') +
|
|
140
|
+
(node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '')`;
|
|
141
|
+
/**
|
|
142
|
+
* Page-side position of an element among its parent's children with the
|
|
143
|
+
* same short description ({@link ELEMENT_DESCRIPTION_JS}), e.g. `(2nd of 3)`;
|
|
144
|
+
* empty when it is the only one.
|
|
145
|
+
*/
|
|
146
|
+
export const SIBLING_POSITION_JS = `(el) => {
|
|
147
|
+
const describe = ${ELEMENT_DESCRIPTION_JS};
|
|
148
|
+
const parent = el.parentElement;
|
|
149
|
+
if (!parent) return '';
|
|
150
|
+
const own = describe(el);
|
|
151
|
+
const same = Array.from(parent.children).filter((child) => describe(child) === own);
|
|
152
|
+
if (same.length < 2) return '';
|
|
153
|
+
const n = same.indexOf(el) + 1;
|
|
154
|
+
const teen = n % 100 >= 11 && n % 100 <= 13;
|
|
155
|
+
const suffix = teen ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' })[n % 10] || 'th';
|
|
156
|
+
return '(' + n + suffix + ' of ' + same.length + ')';
|
|
157
|
+
}`;
|
|
158
|
+
/**
|
|
159
|
+
* Page-side identity of an element an action hit, so the output says which
|
|
160
|
+
* one it was, as one string: its short description
|
|
161
|
+
* ({@link ELEMENT_DESCRIPTION_JS}) and visible text (button value for button
|
|
162
|
+
* inputs, decorations left out), e.g. `button#add.btn "Add to cart"`. A
|
|
163
|
+
* `<select>` is named by its label, aria-label or name, else by its selected
|
|
164
|
+
* option, e.g. `select.sort "Sort products"`. An element without visible
|
|
165
|
+
* text is described by itself first: by its position among same-looking
|
|
166
|
+
* siblings ({@link SIBLING_POSITION_JS}), e.g. `div.figure (2nd of 3)`, with
|
|
167
|
+
* its aria-label, placeholder or title when it has one. Only an element
|
|
168
|
+
* without an id that is the only one of its kind is named by the nearest of
|
|
169
|
+
* three ancestors that has text, e.g.
|
|
170
|
+
* `input.toggle in div.view "Write report"` (rows of a list share their
|
|
171
|
+
* aria-label), leaving out the options
|
|
172
|
+
* of `<select>`s in it (read like `innerText`: CSS-hidden text is left out of
|
|
173
|
+
* a rendered ancestor; at most 500 text nodes; selects in shadow roots are
|
|
174
|
+
* not looked into), and never past an editable ancestor, whose text may be
|
|
175
|
+
* typed input; otherwise its aria-label, placeholder or title is used. Texts
|
|
176
|
+
* are cut at 40 characters. Contenteditable elements count as controls: what
|
|
177
|
+
* was typed into them is never echoed (nor their ancestors' text).
|
|
178
|
+
*/
|
|
179
|
+
export const ELEMENT_IDENTITY_JS = `(el) => {
|
|
180
|
+
const describe = ${ELEMENT_DESCRIPTION_JS};
|
|
181
|
+
const siblingPosition = ${SIBLING_POSITION_JS};
|
|
182
|
+
const withoutDecorations = ${WITHOUT_DECORATIONS_JS};
|
|
183
|
+
const clean = (text) => (text || '').replace(/\\s+/g, ' ').trim();
|
|
184
|
+
const cut = (text) => {
|
|
185
|
+
const characters = Array.from(text);
|
|
186
|
+
return characters.length > 40 ? characters.slice(0, 40).join('') + '…' : text;
|
|
187
|
+
};
|
|
188
|
+
const isControl = (node) => /^(input|select|textarea)$/.test(node.localName) || node.isContentEditable;
|
|
189
|
+
const shownText = (node) =>
|
|
190
|
+
isControl(node) ? '' : clean(withoutDecorations(node, typeof node.innerText === 'string' ? node.innerText : node.textContent));
|
|
191
|
+
const buttonValue = (node) => (node.localName === 'input' && /^(submit|button|reset)$/i.test(node.type) ? clean(node.value) : '');
|
|
192
|
+
const attributeText = (node) =>
|
|
193
|
+
clean(node.getAttribute('aria-label')) || clean(node.getAttribute('placeholder')) || clean(node.getAttribute('title'));
|
|
194
|
+
const selectName = (node) =>
|
|
195
|
+
clean(node.labels && node.labels[0] && node.labels[0].innerText) ||
|
|
196
|
+
clean(node.getAttribute('aria-label')) ||
|
|
197
|
+
clean(node.getAttribute('name')) ||
|
|
198
|
+
clean(node.selectedOptions && node.selectedOptions[0] && node.selectedOptions[0].label);
|
|
199
|
+
const shown = (node, options) => !node.checkVisibility || node.checkVisibility(options);
|
|
200
|
+
const textOutsideSelects = (node) => {
|
|
201
|
+
if (!node.querySelector('select')) return shownText(node);
|
|
202
|
+
const hiddenLeftOut = shown(node);
|
|
203
|
+
const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_TEXT);
|
|
204
|
+
let text = '';
|
|
205
|
+
for (let visited = 0; visited < 500 && text.length < 200 && walker.nextNode(); visited++) {
|
|
206
|
+
const parent = walker.currentNode.parentElement;
|
|
207
|
+
if (!parent || parent.closest('select, textarea')) continue;
|
|
208
|
+
if (!hiddenLeftOut || shown(parent, { visibilityProperty: true })) text += ' ' + walker.currentNode.data;
|
|
209
|
+
}
|
|
210
|
+
return clean(text);
|
|
211
|
+
};
|
|
212
|
+
const quoted = (text) => (text ? ' "' + cut(text) + '"' : '');
|
|
213
|
+
const visible = shownText(el) || buttonValue(el) || (el.localName === 'select' ? selectName(el) : '');
|
|
214
|
+
if (visible) return describe(el) + quoted(visible);
|
|
215
|
+
const attribute = attributeText(el);
|
|
216
|
+
const position = siblingPosition(el);
|
|
217
|
+
if (position) return describe(el) + ' ' + position + quoted(attribute);
|
|
218
|
+
let ancestor = el.id || el.isContentEditable ? null : el.parentElement;
|
|
219
|
+
for (let depth = 0; ancestor && depth < 3; depth++, ancestor = ancestor.parentElement) {
|
|
220
|
+
if (isControl(ancestor)) break;
|
|
221
|
+
const text = textOutsideSelects(ancestor);
|
|
222
|
+
if (text) return describe(el) + ' in ' + describe(ancestor) + quoted(text);
|
|
223
|
+
}
|
|
224
|
+
return describe(el) + quoted(attribute);
|
|
225
|
+
}`;
|
|
226
|
+
/**
|
|
227
|
+
* Page-side location of an element: the iframes it is in (outermost first)
|
|
228
|
+
* and the shadow root holding it, e.g. `iframe#pay > shadow root of <x-card>`;
|
|
229
|
+
* empty for the main document.
|
|
230
|
+
*/
|
|
231
|
+
export const ELEMENT_CONTEXT_JS = `(el) => {
|
|
232
|
+
const describe = (node) => node.tagName.toLowerCase() + (node.id ? '#' + node.id : '');
|
|
233
|
+
const parts = [];
|
|
234
|
+
for (let doc = el.ownerDocument; doc && doc.defaultView && doc.defaultView.frameElement; ) {
|
|
235
|
+
const frame = doc.defaultView.frameElement;
|
|
236
|
+
parts.unshift(describe(frame));
|
|
237
|
+
doc = frame.ownerDocument;
|
|
238
|
+
}
|
|
239
|
+
const root = el.getRootNode();
|
|
240
|
+
if (root.host) parts.push('shadow root of <' + describe(root.host) + '>');
|
|
241
|
+
return parts.join(' > ');
|
|
242
|
+
}`;
|
|
243
|
+
/**
|
|
244
|
+
* Short text preview of an element's text: whitespace collapsed, cut on a
|
|
245
|
+
* whole character (an emoji is never split, which would make JSON invalid).
|
|
246
|
+
*
|
|
247
|
+
* @param text - Element text as the page renders it
|
|
248
|
+
* @param length - Characters kept
|
|
249
|
+
* @returns Collapsed text, truncated to `length` characters (followed by `...`)
|
|
250
|
+
*/
|
|
251
|
+
export function textPreview(text, length = PREVIEW_LENGTH) {
|
|
252
|
+
const collapsed = text.replace(/\s+/g, ' ').trim();
|
|
253
|
+
const characters = Array.from(collapsed);
|
|
254
|
+
return characters.length > length ? characters.slice(0, length).join('') + '...' : collapsed;
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=elementInfo.js.map
|
|
@@ -31,6 +31,25 @@ export interface EvalResult {
|
|
|
31
31
|
* throwing getter becomes `[Error: …]`.
|
|
32
32
|
*/
|
|
33
33
|
export declare const JSON_SAFE_COPY_FUNCTION = "function () {\n const MAX_ITEMS = 1000;\n const ancestors = new Set();\n const kind = (value) => Object.prototype.toString.call(value).slice(8, -1);\n const isNode = (value) => typeof value.nodeType === 'number' && typeof value.nodeName === 'string';\n const describeNode = (node) => {\n if (node.nodeType !== 1) return node.nodeName.toLowerCase();\n return node.tagName.toLowerCase() + (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).join('.') : '');\n };\n const items = (list, next) => {\n const result = [];\n let count = 0;\n for (const item of list) {\n if (count++ === MAX_ITEMS) { result.push('\u2026'); break; }\n result.push(next(item));\n }\n return result;\n };\n const leaf = (value) => {\n if (value === undefined) return null;\n if (typeof value === 'number') {\n if (Number.isNaN(value) || !Number.isFinite(value)) return String(value);\n return Object.is(value, -0) ? '-0' : value;\n }\n if (typeof value === 'bigint') return value + 'n';\n if (typeof value === 'symbol') return value.toString();\n if (typeof value === 'function') return 'function ' + (value.name || '(anonymous)') + '()';\n return value;\n };\n const copy = (value, depth) => {\n if (value === null || typeof value !== 'object') return leaf(value);\n if (ancestors.has(value)) return '[Circular]';\n if (depth > 20) return '[\u2026]';\n const next = (item) => copy(item, depth + 1);\n const type = kind(value);\n if (isNode(value)) return describeNode(value);\n if (value.window === value) return 'Window';\n if (type === 'Date') return isNaN(value) ? 'Invalid Date' : value.toISOString();\n if (type === 'RegExp') return String(value);\n if (type === 'Error' || value instanceof Error) return value.name + ': ' + value.message;\n ancestors.add(value);\n try {\n if (type === 'Map') return items(value, ([k, v]) => [next(k), next(v)]);\n if (type === 'Set' || type === 'NodeList' || type === 'HTMLCollection') return items(value, next);\n if (ArrayBuffer.isView(value) && type !== 'DataView') return items(value, next);\n if (Array.isArray(value)) return items(value, next);\n const result = {};\n const keys = Object.keys(value);\n for (const key of keys.slice(0, MAX_ITEMS)) {\n try { result[key] = next(value[key]); } catch (e) { result[key] = '[Error: ' + (e && e.message) + ']'; }\n }\n if (keys.length > MAX_ITEMS) result['\u2026'] = (keys.length - MAX_ITEMS) + ' more keys';\n return result;\n } finally {\n ancestors.delete(value);\n }\n };\n return copy(this, 0);\n}";
|
|
34
|
+
/** What a busy target is called in messages: the page, or an iframe (`--frame`) */
|
|
35
|
+
export type BusyScope = 'page' | 'frame';
|
|
36
|
+
/** When {@link withBusyPageRecovery} checks the target, and what it calls it */
|
|
37
|
+
export interface BusyRecoveryOptions {
|
|
38
|
+
/** Time after which a command that has not answered gets the target checked */
|
|
39
|
+
busyAfterMs?: number;
|
|
40
|
+
/** How long the target gets to answer the check */
|
|
41
|
+
livenessMs?: number;
|
|
42
|
+
/** The page (default) or an iframe, for messages */
|
|
43
|
+
scope?: BusyScope;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether Chrome failed a command because the document it ran in went away
|
|
47
|
+
* (navigation, a removed iframe).
|
|
48
|
+
*
|
|
49
|
+
* @param error - Error thrown by a CDP command
|
|
50
|
+
* @returns True for a lost context
|
|
51
|
+
*/
|
|
52
|
+
export declare function isContextLostError(error: unknown): boolean;
|
|
34
53
|
/**
|
|
35
54
|
* Run a CDP command on the page, recovering a page kept busy by its own
|
|
36
55
|
* scripts. If the command has not answered when a script would have been
|
|
@@ -41,21 +60,45 @@ export declare const JSON_SAFE_COPY_FUNCTION = "function () {\n const MAX_ITEMS
|
|
|
41
60
|
*
|
|
42
61
|
* @param cdp - CDP connection
|
|
43
62
|
* @param command - The pending command
|
|
44
|
-
* @param
|
|
63
|
+
* @param options - When to check the page, how long it gets to answer, what to call it
|
|
45
64
|
* @returns The command's result
|
|
46
65
|
* @throws CommandError (102) when the page was busy
|
|
47
66
|
*/
|
|
48
|
-
export declare function withBusyPageRecovery<T>(cdp: CDPSender, command: Promise<T>,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
export declare function withBusyPageRecovery<T>(cdp: CDPSender, command: Promise<T>, options?: BusyRecoveryOptions): Promise<T>;
|
|
68
|
+
/**
|
|
69
|
+
* The work's value if it settles within `ms` (its rejection is passed on).
|
|
70
|
+
*
|
|
71
|
+
* @param work - Pending work
|
|
72
|
+
* @param ms - Time limit
|
|
73
|
+
* @returns `{ settled: true, value }`, or `{ settled: false }` when time ran out
|
|
74
|
+
*/
|
|
75
|
+
export declare function settledWithin<T>(work: Promise<T>, ms: number): Promise<{
|
|
76
|
+
settled: true;
|
|
77
|
+
value: T;
|
|
78
|
+
} | {
|
|
79
|
+
settled: false;
|
|
80
|
+
}>;
|
|
52
81
|
/** Where a `bdg dom eval` script runs (default: the page's main frame) */
|
|
53
82
|
export interface EvalTarget {
|
|
54
83
|
/** Session of an attached target (out-of-process iframe) */
|
|
55
84
|
sessionId?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Where to check and recover the target when it does not answer (default:
|
|
87
|
+
* the session itself); a session attached while the target was busy gets
|
|
88
|
+
* no answer at all
|
|
89
|
+
*/
|
|
90
|
+
recovery?: CDPSender;
|
|
56
91
|
/** Execution context of a frame (`ExecutionContextDescription.uniqueId`) */
|
|
57
92
|
uniqueContextId?: string;
|
|
58
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether the page still answers. A page that does not answer in time is
|
|
96
|
+
* taken as open: commands wait while a navigation is pending.
|
|
97
|
+
*
|
|
98
|
+
* @param cdp - CDP connection of the page
|
|
99
|
+
* @returns False when the page (tab) was closed
|
|
100
|
+
*/
|
|
101
|
+
export declare function pageStillOpen(cdp: CDPSender): Promise<boolean>;
|
|
59
102
|
/**
|
|
60
103
|
* Evaluate a `bdg dom eval` script.
|
|
61
104
|
*
|
|
@@ -70,7 +113,9 @@ export interface EvalTarget {
|
|
|
70
113
|
* @param script - JavaScript expression
|
|
71
114
|
* @param target - Session and execution context to run in (default: the page)
|
|
72
115
|
* @returns Value and type
|
|
73
|
-
* @throws CommandError (102) when the script was terminated
|
|
116
|
+
* @throws CommandError (102) when the script was terminated or a promise did
|
|
117
|
+
* not settle, (91) when it threw, (83) when the page navigated while it ran
|
|
118
|
+
* (in a frame, Chrome's context-lost error is passed on for the caller to explain)
|
|
74
119
|
*/
|
|
75
120
|
export declare function evaluateScript(cdp: CDPConnection, script: string, target?: EvalTarget): Promise<EvalResult>;
|
|
76
121
|
//# sourceMappingURL=evalHelpers.d.ts.map
|