browser-debugger-cli 0.8.0 → 0.9.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 +4 -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 +27 -5
- 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 +152 -113
- 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/query.d.ts +44 -17
- package/dist/commands/dom/helpers/query.js +244 -97
- package/dist/commands/dom/helpers/runElementCommand.d.ts +10 -2
- package/dist/commands/dom/helpers/runElementCommand.js +97 -30
- package/dist/commands/dom/helpers/screenshot.d.ts +4 -1
- package/dist/commands/dom/helpers/screenshot.js +164 -49
- package/dist/commands/dom/index.d.ts +3 -1
- package/dist/commands/dom/index.js +16 -6
- 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 +13 -3
- 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 +6 -13
- package/dist/commands/dom/semanticUtils.js +15 -19
- 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 +4 -11
- package/dist/commands/optionBehaviors.js +112 -21
- package/dist/commands/page.d.ts +2 -1
- package/dist/commands/page.js +41 -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/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 +40 -5
- package/dist/commands/shared/startHelpers.js +12 -3
- package/dist/commands/shared/validation.d.ts +3 -2
- package/dist/commands/shared/validation.js +4 -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 +2 -1
- package/dist/daemon/launcher.js +5 -6
- package/dist/daemon/server/SocketServer.js +1 -2
- package/dist/daemon/session/Session.d.ts +13 -0
- package/dist/daemon/session/Session.js +57 -8
- package/dist/daemon/session/chromeConnection.d.ts +9 -0
- package/dist/daemon/session/chromeConnection.js +45 -8
- package/dist/daemon/session/commandRegistry.js +52 -62
- package/dist/daemon/session/interactions.d.ts +35 -9
- package/dist/daemon/session/interactions.js +36 -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 +5393 -1600
- package/dist/errors/messages.d.ts +387 -24
- package/dist/errors/messages.js +761 -67
- package/dist/index.js +3976 -1558
- package/dist/ipc/client.d.ts +12 -1
- package/dist/ipc/client.js +22 -3
- package/dist/ipc/protocol/commands.d.ts +89 -4
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +258 -7
- 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 +106 -0
- package/dist/runtime/dom/actionEffects.js +256 -0
- package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -0
- package/dist/runtime/dom/actionEffectsScripts.js +234 -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 +77 -0
- package/dist/runtime/dom/elementInfo.js +191 -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 +174 -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 +10 -0
- package/dist/runtime/dom/formFillHelpers/fill.js +125 -10
- 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 +1 -1
- package/dist/runtime/dom/frameSelection.js +2 -2
- package/dist/runtime/dom/frames.d.ts +25 -2
- package/dist/runtime/dom/frames.js +202 -63
- package/dist/runtime/dom/layout.d.ts +67 -0
- package/dist/runtime/dom/layout.js +333 -0
- package/dist/runtime/dom/listenerPageScripts.d.ts +66 -0
- package/dist/runtime/dom/listenerPageScripts.js +279 -0
- package/dist/runtime/dom/listenerSummary.d.ts +132 -11
- package/dist/runtime/dom/listenerSummary.js +344 -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 +58 -2
- package/dist/runtime/dom/reactEventHelpers.js +212 -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 +51 -0
- package/dist/runtime/page/emulation.js +80 -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 +8 -3
- package/dist/telemetry/a11y.js +92 -28
- 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 +80 -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.js +2 -1
- package/dist/ui/formatters/dom.d.ts +26 -14
- package/dist/ui/formatters/dom.js +65 -52
- package/dist/ui/formatters/form.js +29 -18
- 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.js +13 -0
- 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 +10 -0
- package/dist/ui/formatting.js +28 -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 +388 -14
- package/dist/ui/messages/commands.js +664 -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/preview.js +5 -4
- package/dist/ui/messages/session.d.ts +16 -21
- package/dist/ui/messages/session.js +28 -26
- package/dist/ui/messages/sessionCommand.d.ts +43 -0
- package/dist/ui/messages/sessionCommand.js +52 -0
- package/dist/utils/async.d.ts +8 -0
- package/dist/utils/async.js +19 -0
- package/dist/utils/http.d.ts +22 -1
- package/dist/utils/http.js +28 -9
- package/dist/utils/selectorFilters.d.ts +36 -8
- package/dist/utils/selectorFilters.js +267 -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
|
@@ -3,29 +3,37 @@
|
|
|
3
3
|
* `:visible`.
|
|
4
4
|
*
|
|
5
5
|
* They are not CSS, so the selector is split here (in Node) into the CSS the
|
|
6
|
-
* browser runs and the filters the page applies to its matches.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* browser runs and the filters the page applies to its matches. A filter
|
|
7
|
+
* applies to the compound it is written in (`li.item:has-text("x"):visible`,
|
|
8
|
+
* `a:visible.active`). On the last compound it narrows down the matches; on an
|
|
9
|
+
* earlier one it scopes the rest of the selector, which is then matched under
|
|
10
|
+
* each element passing the filter: `li:has-text("Buy milk") .toggle` is the
|
|
11
|
+
* `.toggle` inside the row that says "Buy milk". Only descendant and child
|
|
12
|
+
* combinators can follow a filtered compound. Inside `:has()` filters test
|
|
13
|
+
* what an element contains (`li:has(label:text-is("x"))`); inside other
|
|
14
|
+
* pseudo-classes (`:not(:visible)`) they would have to be evaluated by the
|
|
15
|
+
* browser's selector matching, which bdg cannot do.
|
|
12
16
|
*/
|
|
13
17
|
import { CommandError } from '../errors/index.js';
|
|
14
|
-
import { invalidSelectorFilterError, misplacedSelectorFilterError } from '../errors/messages.js';
|
|
18
|
+
import { emptyTextFilterError, malformedSelectorError, invalidSelectorFilterError, misplacedSelectorFilterError, siblingAfterFilterError, siblingInHasError, } from '../errors/messages.js';
|
|
15
19
|
import { EXIT_CODES } from './exitCodes.js';
|
|
16
20
|
/** Cheap pre-check: selectors without any of the names are plain CSS. */
|
|
17
21
|
const FILTER_NAME_HINT = /:(?:has-text|text-is|visible)/i;
|
|
18
22
|
/** A filter name at a given position (sticky). */
|
|
19
23
|
const FILTER_NAME = /:(has-text|text-is|visible)(?![\w-])/iy;
|
|
20
|
-
/**
|
|
21
|
-
const
|
|
24
|
+
/** `:has(` at a given position (sticky). */
|
|
25
|
+
const HAS_OPEN = /:has\(/iy;
|
|
26
|
+
/** `:scope`, which a `:has()` with filters adds itself. */
|
|
27
|
+
const SCOPE_PSEUDO = /:scope(?![\w-])/i;
|
|
28
|
+
/** Characters that form combinators between compounds. */
|
|
29
|
+
const COMBINATOR_CHAR = /[\s>+~]/;
|
|
22
30
|
/**
|
|
23
31
|
* Split a selector into the CSS part and filters of each selector of its
|
|
24
32
|
* list.
|
|
25
33
|
*
|
|
26
34
|
* @param selector - Selector as the user gave it
|
|
27
35
|
* @returns The parts, or null for plain CSS (no filters anywhere), which runs unchanged
|
|
28
|
-
* @throws CommandError (81) for a
|
|
36
|
+
* @throws CommandError (81) for a misplaced or malformed filter
|
|
29
37
|
*/
|
|
30
38
|
export function parseSelectorFilters(selector) {
|
|
31
39
|
if (!FILTER_NAME_HINT.test(selector))
|
|
@@ -33,6 +41,21 @@ export function parseSelectorFilters(selector) {
|
|
|
33
41
|
const parts = splitSelectorList(selector).map((part) => parsePart(part, selector));
|
|
34
42
|
return parts.some((part) => part.filters.length > 0) ? parts : null;
|
|
35
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The parts with every `:visible` filter removed, to count what only
|
|
46
|
+
* visibility excluded.
|
|
47
|
+
*
|
|
48
|
+
* @param parts - Parsed selector parts
|
|
49
|
+
* @returns Parts without `:visible`, or null when they have none
|
|
50
|
+
*/
|
|
51
|
+
export function withoutVisibleFilters(parts) {
|
|
52
|
+
const json = JSON.stringify(parts);
|
|
53
|
+
if (!json.includes('"kind":"visible"'))
|
|
54
|
+
return null;
|
|
55
|
+
return JSON.parse(json, (key, value) => key === 'filters' && Array.isArray(value)
|
|
56
|
+
? value.filter((filter) => filter.kind !== 'visible')
|
|
57
|
+
: value);
|
|
58
|
+
}
|
|
36
59
|
/**
|
|
37
60
|
* Split a selector list at its top-level commas (not inside quotes,
|
|
38
61
|
* attribute brackets or parentheses).
|
|
@@ -54,35 +77,32 @@ export function splitSelectorList(selector) {
|
|
|
54
77
|
return parts;
|
|
55
78
|
}
|
|
56
79
|
/**
|
|
57
|
-
* Walk a selector, calling `visit`
|
|
58
|
-
* escapes and attribute brackets
|
|
80
|
+
* Walk a selector, calling `visit` at every token start outside quoted
|
|
81
|
+
* strings, escapes and attribute brackets (whose insides are skipped), with
|
|
82
|
+
* the parenthesis depth (for `)`, the depth inside it).
|
|
59
83
|
*
|
|
60
84
|
* @param text - Selector text
|
|
61
|
-
* @param visit - Returns the index to continue after (to
|
|
85
|
+
* @param visit - Returns the index to continue after (`index` to let scan handle the character)
|
|
62
86
|
*/
|
|
63
87
|
function scan(text, visit) {
|
|
64
88
|
let depth = 0;
|
|
65
|
-
let inBrackets = false;
|
|
66
89
|
for (let i = 0; i < text.length; i++) {
|
|
90
|
+
const next = visit(i, depth);
|
|
91
|
+
if (next !== i) {
|
|
92
|
+
i = next;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
67
95
|
const char = text[i];
|
|
68
|
-
if (char === '\\')
|
|
96
|
+
if (char === '\\')
|
|
69
97
|
i++;
|
|
70
|
-
|
|
71
|
-
else if (char === '"' || char === "'") {
|
|
98
|
+
else if (char === '"' || char === "'")
|
|
72
99
|
i = quotedEnd(text, i) - 1;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
else if (char === '
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
else if (char === '(' || char === ')') {
|
|
81
|
-
depth += char === '(' ? 1 : -1;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
i = visit(i, depth);
|
|
85
|
-
}
|
|
100
|
+
else if (char === '[')
|
|
101
|
+
i = bracketEnd(text, i) - 1;
|
|
102
|
+
else if (char === '(')
|
|
103
|
+
depth++;
|
|
104
|
+
else if (char === ')')
|
|
105
|
+
depth--;
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
/**
|
|
@@ -96,6 +116,25 @@ function quotedEnd(text, start) {
|
|
|
96
116
|
const end = closingQuote(text, start);
|
|
97
117
|
return end === -1 ? text.length : end + 1;
|
|
98
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* End of an attribute selector (quotes and escapes inside skipped).
|
|
121
|
+
*
|
|
122
|
+
* @param text - Text containing it
|
|
123
|
+
* @param start - Index of the `[`
|
|
124
|
+
* @returns Index after the `]`, or the text length when unterminated
|
|
125
|
+
*/
|
|
126
|
+
function bracketEnd(text, start) {
|
|
127
|
+
for (let i = start + 1; i < text.length; i++) {
|
|
128
|
+
const char = text[i];
|
|
129
|
+
if (char === '\\')
|
|
130
|
+
i++;
|
|
131
|
+
else if (char === '"' || char === "'")
|
|
132
|
+
i = quotedEnd(text, i) - 1;
|
|
133
|
+
else if (char === ']')
|
|
134
|
+
return i + 1;
|
|
135
|
+
}
|
|
136
|
+
return text.length;
|
|
137
|
+
}
|
|
99
138
|
/**
|
|
100
139
|
* Index of the quote closing the string opened at `start` (backslash escapes skipped).
|
|
101
140
|
*
|
|
@@ -113,35 +152,210 @@ function closingQuote(text, start) {
|
|
|
113
152
|
return -1;
|
|
114
153
|
}
|
|
115
154
|
/**
|
|
116
|
-
*
|
|
155
|
+
* Index of the parenthesis closing the one at `open` (quotes, escapes and
|
|
156
|
+
* attribute brackets skipped).
|
|
157
|
+
*
|
|
158
|
+
* @param text - Selector text
|
|
159
|
+
* @param open - Index of the `(`
|
|
160
|
+
* @returns Index of the `)`, or -1 when it is not closed
|
|
161
|
+
*/
|
|
162
|
+
function closingParenthesis(text, open) {
|
|
163
|
+
let close = -1;
|
|
164
|
+
scan(text.slice(open), (index, depth) => {
|
|
165
|
+
if (close !== -1 || text[open + index] !== ')' || depth !== 1)
|
|
166
|
+
return index;
|
|
167
|
+
close = open + index;
|
|
168
|
+
return text.length;
|
|
169
|
+
});
|
|
170
|
+
return close;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Throw an exit-81 error.
|
|
174
|
+
*
|
|
175
|
+
* @param err - Message and suggestion
|
|
176
|
+
* @throws CommandError (81)
|
|
177
|
+
*/
|
|
178
|
+
function reject(err) {
|
|
179
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Parse one selector of a list.
|
|
117
183
|
*
|
|
118
184
|
* @param part - One selector of the list
|
|
119
185
|
* @param selector - Whole selector, for error messages
|
|
120
|
-
* @returns CSS
|
|
121
|
-
* @throws CommandError (81)
|
|
186
|
+
* @returns CSS and filters of its first step, and the scoped steps after it
|
|
187
|
+
* @throws CommandError (81) for an empty selector or misplaced filters
|
|
122
188
|
*/
|
|
123
189
|
function parsePart(part, selector) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (!
|
|
190
|
+
if (part.trim() === '')
|
|
191
|
+
reject(malformedSelectorError(selector, 'empty-in-list'));
|
|
192
|
+
if (!FILTER_NAME_HINT.test(part))
|
|
127
193
|
return { css: part.trim(), filters: [] };
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
const css = part.slice(0, first.start);
|
|
134
|
-
const needsElement = css.trim() === '' || ENDS_IN_COMBINATOR.test(css);
|
|
194
|
+
const { leading, steps } = parseChain(part, selector);
|
|
195
|
+
if (leading)
|
|
196
|
+
reject(malformedSelectorError(selector, 'leading-combinator', leading));
|
|
197
|
+
const [first, ...rest] = steps;
|
|
135
198
|
return {
|
|
136
|
-
css:
|
|
137
|
-
filters:
|
|
199
|
+
css: first?.css ?? '*',
|
|
200
|
+
filters: first?.filters ?? [],
|
|
201
|
+
...(rest.length > 0 && { steps: rest }),
|
|
138
202
|
};
|
|
139
203
|
}
|
|
140
204
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
205
|
+
* Parse a complex selector into steps that end at each filtered compound.
|
|
206
|
+
*
|
|
207
|
+
* @param text - Selector (may start with a combinator inside `:has()`)
|
|
208
|
+
* @param selector - Whole selector, for error messages
|
|
209
|
+
* @returns The leading combinator (`''` for none) and the steps
|
|
210
|
+
* @throws CommandError (81) for a sibling combinator after a filter, or a trailing combinator
|
|
211
|
+
*/
|
|
212
|
+
function parseChain(text, selector) {
|
|
213
|
+
const compounds = splitCompounds(text.trim(), selector);
|
|
214
|
+
const leading = compounds[0]?.combinator ?? '';
|
|
215
|
+
const steps = [];
|
|
216
|
+
let combinator = ' ';
|
|
217
|
+
let css = '';
|
|
218
|
+
compounds.forEach((compound, i) => {
|
|
219
|
+
const parsed = parseCompound(compound.text, selector);
|
|
220
|
+
css += css ? joinCombinator(compound.combinator) + parsed.css : parsed.css;
|
|
221
|
+
const next = compounds[i + 1];
|
|
222
|
+
if (parsed.filters.length === 0 && next)
|
|
223
|
+
return;
|
|
224
|
+
steps.push({ combinator, css, filters: parsed.filters });
|
|
225
|
+
css = '';
|
|
226
|
+
if (!next)
|
|
227
|
+
return;
|
|
228
|
+
if (next.combinator !== ' ' && next.combinator !== '>') {
|
|
229
|
+
reject(siblingAfterFilterError(selector, next.combinator));
|
|
230
|
+
}
|
|
231
|
+
combinator = next.combinator;
|
|
232
|
+
});
|
|
233
|
+
return { leading, steps };
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Combinator as written between two compounds of the CSS.
|
|
237
|
+
*
|
|
238
|
+
* @param combinator - ` `, `>`, `+` or `~`
|
|
239
|
+
* @returns The combinator with surrounding spaces
|
|
240
|
+
*/
|
|
241
|
+
function joinCombinator(combinator) {
|
|
242
|
+
return combinator === ' ' ? ' ' : ` ${combinator} `;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Split a complex selector into its compounds at top-level combinators.
|
|
143
246
|
*
|
|
144
|
-
* @param
|
|
247
|
+
* @param text - Trimmed selector
|
|
248
|
+
* @param selector - Whole selector, for error messages
|
|
249
|
+
* @returns Compounds with the combinator before each (`''` for the first, unless it leads)
|
|
250
|
+
* @throws CommandError (81) when the selector ends with a combinator
|
|
251
|
+
*/
|
|
252
|
+
function splitCompounds(text, selector) {
|
|
253
|
+
const compounds = [];
|
|
254
|
+
let combinator = '';
|
|
255
|
+
let start = -1;
|
|
256
|
+
scan(text, (index, depth) => {
|
|
257
|
+
const char = text[index] ?? '';
|
|
258
|
+
if (depth > 0 || !COMBINATOR_CHAR.test(char)) {
|
|
259
|
+
if (start === -1)
|
|
260
|
+
start = index;
|
|
261
|
+
return index;
|
|
262
|
+
}
|
|
263
|
+
if (start !== -1) {
|
|
264
|
+
compounds.push({ combinator, text: text.slice(start, index) });
|
|
265
|
+
start = -1;
|
|
266
|
+
combinator = ' ';
|
|
267
|
+
}
|
|
268
|
+
if (char.trim() && !combinator.trim())
|
|
269
|
+
combinator = char;
|
|
270
|
+
return index;
|
|
271
|
+
});
|
|
272
|
+
if (start === -1)
|
|
273
|
+
reject(malformedSelectorError(selector, 'trailing-combinator'));
|
|
274
|
+
compounds.push({ combinator, text: text.slice(start) });
|
|
275
|
+
return compounds;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Take the filters (and `:has()` containing filters) out of a compound.
|
|
279
|
+
*
|
|
280
|
+
* @param text - Compound selector
|
|
281
|
+
* @param selector - Whole selector, for error messages
|
|
282
|
+
* @returns The compound's CSS (`*` when only filters are given) and its filters
|
|
283
|
+
* @throws CommandError (81) for a filter inside another pseudo-class
|
|
284
|
+
*/
|
|
285
|
+
function parseCompound(text, selector) {
|
|
286
|
+
if (!FILTER_NAME_HINT.test(text))
|
|
287
|
+
return { css: text, filters: [] };
|
|
288
|
+
const hasFilters = findHasFilters(text, selector);
|
|
289
|
+
const inHas = (match) => hasFilters.some((has) => match.start >= has.start && match.end <= has.end);
|
|
290
|
+
const filters = findFilters(text, selector).filter((match) => !inHas(match));
|
|
291
|
+
const misplaced = filters.find((match) => match.depth > 0);
|
|
292
|
+
if (misplaced) {
|
|
293
|
+
reject(misplacedSelectorFilterError(selector, text.slice(misplaced.start, misplaced.end)));
|
|
294
|
+
}
|
|
295
|
+
const removed = [...hasFilters, ...filters].sort((a, b) => a.start - b.start);
|
|
296
|
+
let css = '';
|
|
297
|
+
let position = 0;
|
|
298
|
+
for (const match of removed) {
|
|
299
|
+
css += text.slice(position, match.start);
|
|
300
|
+
position = match.end;
|
|
301
|
+
}
|
|
302
|
+
css += text.slice(position);
|
|
303
|
+
return { css: css || '*', filters: removed.map((match) => match.filter) };
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Find the top-level `:has()` of a compound whose argument uses filters.
|
|
307
|
+
*
|
|
308
|
+
* @param text - Compound selector
|
|
309
|
+
* @param selector - Whole selector, for error messages
|
|
310
|
+
* @returns The `:has()` filters with their positions
|
|
311
|
+
*/
|
|
312
|
+
function findHasFilters(text, selector) {
|
|
313
|
+
const matches = [];
|
|
314
|
+
scan(text, (index, depth) => {
|
|
315
|
+
HAS_OPEN.lastIndex = index;
|
|
316
|
+
if (depth > 0 || !HAS_OPEN.test(text))
|
|
317
|
+
return index;
|
|
318
|
+
const open = HAS_OPEN.lastIndex - 1;
|
|
319
|
+
const close = closingParenthesis(text, open);
|
|
320
|
+
if (close === -1)
|
|
321
|
+
return index;
|
|
322
|
+
const filter = parseHasArgument(text.slice(open + 1, close), selector);
|
|
323
|
+
if (filter)
|
|
324
|
+
matches.push({ start: index, end: close + 1, depth, filter });
|
|
325
|
+
return filter ? close : index;
|
|
326
|
+
});
|
|
327
|
+
return matches;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Parse the relative selector list of a `:has()` that may use filters.
|
|
331
|
+
*
|
|
332
|
+
* @param argument - Text between the parentheses
|
|
333
|
+
* @param selector - Whole selector, for error messages
|
|
334
|
+
* @returns The `has` filter, or null when the argument is plain CSS
|
|
335
|
+
* @throws CommandError (81) for a sibling combinator (`:has(+ x)`)
|
|
336
|
+
*/
|
|
337
|
+
function parseHasArgument(argument, selector) {
|
|
338
|
+
if (!FILTER_NAME_HINT.test(argument))
|
|
339
|
+
return null;
|
|
340
|
+
const selectors = splitSelectorList(argument).map((relative) => {
|
|
341
|
+
if (relative.trim() === '')
|
|
342
|
+
reject(malformedSelectorError(selector, 'empty-has'));
|
|
343
|
+
if (SCOPE_PSEUDO.test(relative))
|
|
344
|
+
reject(malformedSelectorError(selector, 'scope-in-has'));
|
|
345
|
+
const { leading, steps } = parseChain(relative, selector);
|
|
346
|
+
if (leading && leading !== '>')
|
|
347
|
+
reject(siblingInHasError(selector, leading));
|
|
348
|
+
const [first, ...rest] = steps;
|
|
349
|
+
return first ? [{ ...first, combinator: (leading || ' ') }, ...rest] : [];
|
|
350
|
+
});
|
|
351
|
+
const usesFilters = selectors.some((chain) => chain.some((step) => step.filters.length > 0));
|
|
352
|
+
return usesFilters ? { kind: 'has', selectors } : null;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Find every text and visibility filter in a compound (at any parenthesis
|
|
356
|
+
* depth, outside quotes and attribute brackets).
|
|
357
|
+
*
|
|
358
|
+
* @param part - Compound selector
|
|
145
359
|
* @param selector - Whole selector, for error messages
|
|
146
360
|
* @returns Filters in order
|
|
147
361
|
*/
|
|
@@ -169,15 +383,15 @@ function findFilters(part, selector) {
|
|
|
169
383
|
* @param name - `has-text` or `text-is`
|
|
170
384
|
* @param selector - Whole selector, for error messages
|
|
171
385
|
* @returns The filter (text whitespace-normalized) and the index after it
|
|
172
|
-
* @throws CommandError (81) when the argument is missing or not closed
|
|
386
|
+
* @throws CommandError (81) when the argument is missing or not closed, or `:has-text` is empty
|
|
173
387
|
*/
|
|
174
388
|
function readTextFilter(part, start, name, selector) {
|
|
175
389
|
const argument = part[start] === '(' ? readArgument(part, start + 1) : null;
|
|
176
|
-
if (!argument)
|
|
177
|
-
|
|
178
|
-
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
179
|
-
}
|
|
390
|
+
if (!argument)
|
|
391
|
+
reject(invalidSelectorFilterError(selector, `:${name}`));
|
|
180
392
|
const text = argument.text.replace(/\s+/g, ' ').trim();
|
|
393
|
+
if (name === 'has-text' && text === '')
|
|
394
|
+
reject(emptyTextFilterError(selector));
|
|
181
395
|
const filter = name === 'has-text'
|
|
182
396
|
? { kind: 'has-text', text: text.toLowerCase() }
|
|
183
397
|
: { kind: 'text-is', text };
|
|
@@ -25,10 +25,16 @@ export declare function hasAttributeSelector(selector: string): boolean;
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function detectSelectorQuoteDamage(selector: string): ShellDamageResult;
|
|
27
27
|
/**
|
|
28
|
-
* Detects shell quote damage in JavaScript expressions.
|
|
28
|
+
* Detects shell quote damage in JavaScript expressions. Conservative: only an
|
|
29
|
+
* error stripped quotes cause (`x is not defined`,
|
|
30
|
+
* `missing ) after argument list`, `Unexpected identifier`,
|
|
31
|
+
* `Unexpected token '.'`, `Invalid or unexpected token`) counts, about an
|
|
32
|
+
* argument of a DOM method that takes a string (`querySelector(input)`),
|
|
33
|
+
* which the script does not declare itself.
|
|
29
34
|
*
|
|
30
35
|
* @param script - The script as received by the command
|
|
36
|
+
* @param errorMessage - The error the script raised
|
|
31
37
|
* @returns Detection result with details and suggestions
|
|
32
38
|
*/
|
|
33
|
-
export declare function detectScriptQuoteDamage(script: string): ShellDamageResult;
|
|
39
|
+
export declare function detectScriptQuoteDamage(script: string, errorMessage: string): ShellDamageResult;
|
|
34
40
|
//# sourceMappingURL=shellDetection.d.ts.map
|
|
@@ -4,16 +4,52 @@
|
|
|
4
4
|
* Detects when shell quote handling has corrupted selectors or scripts,
|
|
5
5
|
* providing actionable suggestions for recovery.
|
|
6
6
|
*/
|
|
7
|
+
import { sessionCommand } from '../ui/messages/sessionCommand.js';
|
|
7
8
|
const ATTRIBUTE_SELECTOR_PATTERN = /\[[\w-]+=/.source;
|
|
8
9
|
const QUOTED_ATTRIBUTE_PATTERN = /\[[\w-]+=['"][^'"]*['"]\]/;
|
|
9
10
|
const UNQUOTED_ATTRIBUTE_PATTERN = /\[([\w-]+)=([\w-]+)\]/;
|
|
10
|
-
|
|
11
|
-
const
|
|
11
|
+
/** DOM methods whose one argument is a string (a selector, id, name or class) */
|
|
12
|
+
const STRING_ARGUMENT_METHODS = new Set([
|
|
13
|
+
'querySelector',
|
|
14
|
+
'querySelectorAll',
|
|
15
|
+
'getElementById',
|
|
16
|
+
'getElementsByClassName',
|
|
17
|
+
'getElementsByTagName',
|
|
18
|
+
'getElementsByName',
|
|
19
|
+
'closest',
|
|
20
|
+
'matches',
|
|
21
|
+
'getAttribute',
|
|
22
|
+
'hasAttribute',
|
|
23
|
+
'removeAttribute',
|
|
24
|
+
'toggleAttribute',
|
|
25
|
+
'createElement',
|
|
26
|
+
'add',
|
|
27
|
+
'remove',
|
|
28
|
+
'toggle',
|
|
29
|
+
]);
|
|
30
|
+
/** A call with one argument and no quotes, parentheses or commas in it */
|
|
31
|
+
const UNQUOTED_CALL_PATTERN = /([\w$]+)\(\s*([^()'"`,]+?)\s*\)/g;
|
|
32
|
+
/** `x is not defined`: the shell may have turned `"x"` into `x` */
|
|
33
|
+
const NOT_DEFINED_PATTERN = /^ReferenceError: ([\w$]+) is not defined/;
|
|
34
|
+
/**
|
|
35
|
+
* The syntax errors V8 raises for `fn(div p)`: the shell may have turned
|
|
36
|
+
* `"div p"` into `div p`
|
|
37
|
+
*/
|
|
38
|
+
const SEVERAL_WORDS_PATTERN = /^SyntaxError: (?:missing \) after argument list|Unexpected identifier '([\w$]+)')/;
|
|
39
|
+
/**
|
|
40
|
+
* The syntax errors V8 raises for a selector starting with punctuation:
|
|
41
|
+
* `fn(.btn)`, `fn(#main)`, `fn(> p)`
|
|
42
|
+
*/
|
|
43
|
+
const SELECTOR_SYNTAX_PATTERN = /^SyntaxError: (?:Unexpected token '[^']*'|Invalid or unexpected token|Private field '#[^']*' must be declared)/;
|
|
44
|
+
/** Start of a CSS selector that is not a JavaScript expression */
|
|
45
|
+
const SELECTOR_PUNCTUATION = /^[.#>~+*[:]/;
|
|
46
|
+
/** Words that may start a valid expression of several words */
|
|
47
|
+
const EXPRESSION_KEYWORDS = /^(?:new|typeof|void|delete|await|yield)\b/;
|
|
12
48
|
function noDamage() {
|
|
13
49
|
return { damaged: false };
|
|
14
50
|
}
|
|
15
51
|
function buildSelectorSuggestion(selector) {
|
|
16
|
-
return `Use the two-step pattern:\n 1. bdg dom query '${selector}'\n 2. bdg dom a11y describe 0`;
|
|
52
|
+
return `Use the two-step pattern:\n 1. ${sessionCommand(`bdg dom query '${selector}'`)}\n 2. ${sessionCommand('bdg dom a11y describe 0')}`;
|
|
17
53
|
}
|
|
18
54
|
function checkUnquotedAttribute(selector) {
|
|
19
55
|
if (QUOTED_ATTRIBUTE_PATTERN.test(selector)) {
|
|
@@ -31,31 +67,66 @@ function checkUnquotedAttribute(selector) {
|
|
|
31
67
|
suggestion: buildSelectorSuggestion(selector),
|
|
32
68
|
};
|
|
33
69
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
details: `${funcName}(${bareArg}) - quotes stripped by shell`,
|
|
46
|
-
suggestion: `Try: bdg dom eval '${fixedScript}'`,
|
|
47
|
-
};
|
|
70
|
+
/**
|
|
71
|
+
* Whether the script declares a name itself (then it is a variable, not a
|
|
72
|
+
* string that lost its quotes).
|
|
73
|
+
*
|
|
74
|
+
* @param script - The script
|
|
75
|
+
* @param name - Identifier
|
|
76
|
+
* @returns True for a `const`/`let`/`var`/`function`/`class` declaration of it
|
|
77
|
+
*/
|
|
78
|
+
function declares(script, name) {
|
|
79
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
80
|
+
return new RegExp(`\\b(?:const|let|var|function|class)\\s+${escaped}\\b`).test(script);
|
|
48
81
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Whether text starts with an identifier (and not with a longer one).
|
|
84
|
+
*
|
|
85
|
+
* @param text - Text
|
|
86
|
+
* @param name - Identifier
|
|
87
|
+
* @returns True when `text` is `name` or `name` followed by a non-identifier character
|
|
88
|
+
*/
|
|
89
|
+
function startsWithName(text, name) {
|
|
90
|
+
return text.startsWith(name) && !/^[\w$]/.test(text.slice(name.length));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Whether an unquoted argument is what the error complains about: for
|
|
94
|
+
* `ReferenceError` the argument starts with the undefined name (`input`,
|
|
95
|
+
* `my-id`, `button.primary`); for `SyntaxError` it is several bare words
|
|
96
|
+
* (`div p`), the unexpected one (when named) among the later ones; for a
|
|
97
|
+
* syntax error at punctuation it starts like a selector (`.btn`, `#main`).
|
|
98
|
+
*
|
|
99
|
+
* @param argument - Unquoted argument
|
|
100
|
+
* @param error - What the error says
|
|
101
|
+
* @returns True when quotes stripped from it explain the error
|
|
102
|
+
*/
|
|
103
|
+
function explainsError(argument, error) {
|
|
104
|
+
if (error.kind === 'undefined')
|
|
105
|
+
return startsWithName(argument, error.name);
|
|
106
|
+
if (error.kind === 'selector')
|
|
107
|
+
return SELECTOR_PUNCTUATION.test(argument);
|
|
108
|
+
const [, ...laterWords] = argument.split(/\s+/);
|
|
109
|
+
return (!EXPRESSION_KEYWORDS.test(argument) &&
|
|
110
|
+
laterWords.length > 0 &&
|
|
111
|
+
(error.name === undefined || laterWords.some((word) => startsWithName(word, error.name ?? ''))));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Read the identifier an error is about, when the error is one stripped
|
|
115
|
+
* quotes cause.
|
|
116
|
+
*
|
|
117
|
+
* @param errorMessage - The JavaScript error
|
|
118
|
+
* @returns The identifier and kind of error, or undefined for other errors
|
|
119
|
+
*/
|
|
120
|
+
function quoteRelatedError(errorMessage) {
|
|
121
|
+
const notDefined = NOT_DEFINED_PATTERN.exec(errorMessage);
|
|
122
|
+
if (notDefined?.[1])
|
|
123
|
+
return { kind: 'undefined', name: notDefined[1] };
|
|
124
|
+
if (SELECTOR_SYNTAX_PATTERN.test(errorMessage))
|
|
125
|
+
return { kind: 'selector' };
|
|
126
|
+
const syntax = SEVERAL_WORDS_PATTERN.exec(errorMessage);
|
|
127
|
+
if (!syntax)
|
|
128
|
+
return undefined;
|
|
129
|
+
return syntax[1] ? { kind: 'syntax', name: syntax[1] } : { kind: 'syntax' };
|
|
59
130
|
}
|
|
60
131
|
/**
|
|
61
132
|
* Checks if a selector contains attribute syntax.
|
|
@@ -79,16 +150,32 @@ export function detectSelectorQuoteDamage(selector) {
|
|
|
79
150
|
return checkUnquotedAttribute(selector);
|
|
80
151
|
}
|
|
81
152
|
/**
|
|
82
|
-
* Detects shell quote damage in JavaScript expressions.
|
|
153
|
+
* Detects shell quote damage in JavaScript expressions. Conservative: only an
|
|
154
|
+
* error stripped quotes cause (`x is not defined`,
|
|
155
|
+
* `missing ) after argument list`, `Unexpected identifier`,
|
|
156
|
+
* `Unexpected token '.'`, `Invalid or unexpected token`) counts, about an
|
|
157
|
+
* argument of a DOM method that takes a string (`querySelector(input)`),
|
|
158
|
+
* which the script does not declare itself.
|
|
83
159
|
*
|
|
84
160
|
* @param script - The script as received by the command
|
|
161
|
+
* @param errorMessage - The error the script raised
|
|
85
162
|
* @returns Detection result with details and suggestions
|
|
86
163
|
*/
|
|
87
|
-
export function detectScriptQuoteDamage(script) {
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
return
|
|
164
|
+
export function detectScriptQuoteDamage(script, errorMessage) {
|
|
165
|
+
const error = quoteRelatedError(errorMessage);
|
|
166
|
+
if (!error || (error.name && declares(script, error.name)))
|
|
167
|
+
return noDamage();
|
|
168
|
+
for (const [call, method = '', argument = ''] of script.matchAll(UNQUOTED_CALL_PATTERN)) {
|
|
169
|
+
if (!STRING_ARGUMENT_METHODS.has(method) || !explainsError(argument, error))
|
|
170
|
+
continue;
|
|
171
|
+
const fixedScript = script.replace(call, `${method}("${argument}")`);
|
|
172
|
+
return {
|
|
173
|
+
damaged: true,
|
|
174
|
+
type: 'unquoted-argument',
|
|
175
|
+
details: `${method}(${argument}) - quotes stripped by shell`,
|
|
176
|
+
suggestion: `Try: ${sessionCommand(`bdg dom eval '${fixedScript}'`)}`,
|
|
177
|
+
};
|
|
91
178
|
}
|
|
92
|
-
return
|
|
179
|
+
return noDamage();
|
|
93
180
|
}
|
|
94
181
|
//# sourceMappingURL=shellDetection.js.map
|
|
@@ -15,5 +15,31 @@ export declare function getSuggestion(input: string, candidates: readonly string
|
|
|
15
15
|
prefix?: string;
|
|
16
16
|
suffix?: string;
|
|
17
17
|
}): string;
|
|
18
|
+
/**
|
|
19
|
+
* Names (ids or classes) similar to one that matched nothing, best first, at
|
|
20
|
+
* most three: near-typos first (Levenshtein distance up to a fifth of the
|
|
21
|
+
* length, at least 2), then names sharing a long end, then names sharing a
|
|
22
|
+
* long start (at least a third of the name, at least 4 characters). The end
|
|
23
|
+
* ranks before the start because ids tend to name an action before the item
|
|
24
|
+
* (`add-to-cart-backpack` becomes `remove-backpack` once clicked).
|
|
25
|
+
*
|
|
26
|
+
* @param name - Id or class that matched nothing
|
|
27
|
+
* @param candidates - Ids or classes on the page
|
|
28
|
+
* @returns Up to three similar names
|
|
29
|
+
*/
|
|
30
|
+
export declare function findSimilarNames(name: string, candidates: readonly string[]): string[];
|
|
31
|
+
/** A selector that is just one id or one class, e.g. `#save` or `.btn-primary` */
|
|
32
|
+
export interface SingleNameSelector {
|
|
33
|
+
kind: 'id' | 'class';
|
|
34
|
+
name: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether a selector is a single id or class (the kind "did you mean" can
|
|
38
|
+
* suggest for).
|
|
39
|
+
*
|
|
40
|
+
* @param selector - CSS selector
|
|
41
|
+
* @returns The id or class, or null for any other selector
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseSingleNameSelector(selector: string): SingleNameSelector | null;
|
|
18
44
|
export {};
|
|
19
45
|
//# sourceMappingURL=suggestions.d.ts.map
|