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
package/dist/telemetry/a11y.d.ts
CHANGED
|
@@ -24,10 +24,22 @@ export declare function buildTreeFromRawNodes(rawNodes: Protocol.Accessibility.A
|
|
|
24
24
|
* @throws Error if CDP Accessibility domain fails
|
|
25
25
|
*/
|
|
26
26
|
export declare function collectA11yTree(): Promise<A11yTree>;
|
|
27
|
+
/**
|
|
28
|
+
* The tree with the values of secret fields masked ({@link MASKED_VALUE},
|
|
29
|
+
* whatever their length), and the names and values of the nodes inside them
|
|
30
|
+
* left out (the text Chrome lists under a text field is its value).
|
|
31
|
+
*
|
|
32
|
+
* @param tree - Accessibility tree
|
|
33
|
+
* @param secretIds - Node ids of secret fields
|
|
34
|
+
* @returns The same tree (nodes replaced in place)
|
|
35
|
+
*/
|
|
36
|
+
export declare function maskSecretValues(tree: A11yTree, secretIds: ReadonlySet<string>): A11yTree;
|
|
27
37
|
/**
|
|
28
38
|
* Queries accessibility tree by pattern (role, name, description).
|
|
29
39
|
*
|
|
30
40
|
* Performs case-insensitive matching with AND logic for multiple fields.
|
|
41
|
+
* An element reported more than once (by the page's tree and its frame's)
|
|
42
|
+
* is listed once.
|
|
31
43
|
*
|
|
32
44
|
* @param tree - Accessibility tree to search
|
|
33
45
|
* @param pattern - Query pattern with optional role, name, description
|
|
@@ -50,18 +62,21 @@ export declare function queryA11yTree(tree: A11yTree, pattern: A11yQueryPattern)
|
|
|
50
62
|
* Parses query pattern string into A11yQueryPattern object.
|
|
51
63
|
*
|
|
52
64
|
* Fields are `key:value` or `key=value`, separated by spaces or commas.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
65
|
+
* Keys: role, name, description (desc). Case-insensitive. A role ends at the
|
|
66
|
+
* next `key:`; a name or description runs to the next role/name/description
|
|
67
|
+
* field with a value, or to the end, so it may contain spaces and colons
|
|
68
|
+
* (`name=E-mail address:`). Quote a value to end it explicitly.
|
|
55
69
|
*
|
|
56
70
|
* @param patternString - Query pattern string
|
|
57
71
|
* @returns Parsed query pattern
|
|
58
|
-
* @throws CommandError (81) for unknown keys
|
|
72
|
+
* @throws CommandError (81) for unknown keys, also a misspelled one inside a name
|
|
59
73
|
*
|
|
60
74
|
* @example
|
|
61
75
|
* ```typescript
|
|
62
76
|
* parseQueryPattern('role:button name:Submit') // { role: 'button', name: 'Submit' }
|
|
63
77
|
* parseQueryPattern('role=link,name=Google Chrome') // { role: 'link', name: 'Google Chrome' }
|
|
64
78
|
* parseQueryPattern('name:"Sign in" role:button') // { name: 'Sign in', role: 'button' }
|
|
79
|
+
* parseQueryPattern('name=E-mail address:') // { name: 'E-mail address:' }
|
|
65
80
|
* ```
|
|
66
81
|
*/
|
|
67
82
|
export declare function parseQueryPattern(patternString: string): A11yQueryPattern;
|
package/dist/telemetry/a11y.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { CommandError } from '../errors/index.js';
|
|
2
2
|
import { unknownQueryFieldError } from '../errors/messages.js';
|
|
3
3
|
import { callCDP } from '../ipc/client.js';
|
|
4
|
+
import { MASKED_VALUE, SENSITIVE_FIELD_JS } from '../runtime/dom/elementInfo.js';
|
|
5
|
+
import { childFrameIds } from '../runtime/dom/frameLayout.js';
|
|
6
|
+
import { ConcurrencyLimiter } from '../utils/concurrency.js';
|
|
4
7
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
8
|
+
import { levenshteinDistance } from '../utils/levenshtein.js';
|
|
5
9
|
/**
|
|
6
10
|
* Builds accessibility tree from raw CDP nodes.
|
|
7
11
|
*
|
|
@@ -78,12 +82,87 @@ export async function collectA11yTree() {
|
|
|
78
82
|
if (!result?.nodes) {
|
|
79
83
|
throw new CommandError('Failed to get accessibility tree', { suggestion: 'CDP returned no nodes. The page may not be fully loaded.' }, EXIT_CODES.SOFTWARE_ERROR);
|
|
80
84
|
}
|
|
81
|
-
|
|
85
|
+
const tree = buildTreeFromRawNodes([
|
|
86
|
+
...result.nodes,
|
|
87
|
+
...(await collectFrameNodes(result.nodes)),
|
|
88
|
+
]);
|
|
89
|
+
return maskSecretValues(tree, await sensitiveNodeIds(tree));
|
|
82
90
|
}
|
|
83
91
|
finally {
|
|
84
92
|
await callCDP('Accessibility.disable', {});
|
|
85
93
|
}
|
|
86
94
|
}
|
|
95
|
+
/** Concurrent page checks for {@link sensitiveNodeIds} */
|
|
96
|
+
const SENSITIVE_CHECK_CONCURRENCY = 10;
|
|
97
|
+
/**
|
|
98
|
+
* The accessibility nodes whose value is a secret: nodes with a value whose
|
|
99
|
+
* element is a sensitive field ({@link SENSITIVE_FIELD_JS}: passwords, card
|
|
100
|
+
* fields, one-time codes). Only nodes with a value are checked, one page
|
|
101
|
+
* call each (a page has few).
|
|
102
|
+
*
|
|
103
|
+
* @param tree - Accessibility tree
|
|
104
|
+
* @returns Node ids of secret fields
|
|
105
|
+
*/
|
|
106
|
+
async function sensitiveNodeIds(tree) {
|
|
107
|
+
const candidates = [...tree.nodes.values()].filter((node) => node.value !== undefined && node.backendDOMNodeId !== undefined);
|
|
108
|
+
const limiter = new ConcurrencyLimiter(SENSITIVE_CHECK_CONCURRENCY);
|
|
109
|
+
const checked = await Promise.all(candidates.map((node) => limiter.run(async () => (await isSensitiveField(node.backendDOMNodeId ?? 0)) ? node.nodeId : null)));
|
|
110
|
+
return new Set(checked.filter((id) => id !== null));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Whether an element is a sensitive field. An element that cannot be read
|
|
114
|
+
* counts as one, so a value is never shown by mistake.
|
|
115
|
+
*
|
|
116
|
+
* @param backendNodeId - The element
|
|
117
|
+
* @returns True for secret fields (and unreadable elements)
|
|
118
|
+
*/
|
|
119
|
+
async function isSensitiveField(backendNodeId) {
|
|
120
|
+
const objectGroup = 'bdg-a11y-secret';
|
|
121
|
+
const resolved = await callCDP('DOM.resolveNode', { backendNodeId, objectGroup });
|
|
122
|
+
const objectId = resolved.data?.result?.object
|
|
123
|
+
?.objectId;
|
|
124
|
+
if (!objectId)
|
|
125
|
+
return true;
|
|
126
|
+
try {
|
|
127
|
+
const response = await callCDP('Runtime.callFunctionOn', {
|
|
128
|
+
objectId,
|
|
129
|
+
functionDeclaration: `function () { return this.nodeType !== 1 || (${SENSITIVE_FIELD_JS})(this); }`,
|
|
130
|
+
returnByValue: true,
|
|
131
|
+
});
|
|
132
|
+
const value = response.data?.result?.result
|
|
133
|
+
?.value;
|
|
134
|
+
return value !== false;
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await callCDP('Runtime.releaseObjectGroup', { objectGroup });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The tree with the values of secret fields masked ({@link MASKED_VALUE},
|
|
142
|
+
* whatever their length), and the names and values of the nodes inside them
|
|
143
|
+
* left out (the text Chrome lists under a text field is its value).
|
|
144
|
+
*
|
|
145
|
+
* @param tree - Accessibility tree
|
|
146
|
+
* @param secretIds - Node ids of secret fields
|
|
147
|
+
* @returns The same tree (nodes replaced in place)
|
|
148
|
+
*/
|
|
149
|
+
export function maskSecretValues(tree, secretIds) {
|
|
150
|
+
const hide = (nodeId, inside) => {
|
|
151
|
+
const node = tree.nodes.get(nodeId);
|
|
152
|
+
if (!node)
|
|
153
|
+
return;
|
|
154
|
+
const { name: _name, value: _value, ...rest } = node;
|
|
155
|
+
const masked = inside
|
|
156
|
+
? rest
|
|
157
|
+
: { ...node, ...(node.value !== undefined && { value: MASKED_VALUE }) };
|
|
158
|
+
tree.nodes.set(nodeId, masked);
|
|
159
|
+
if (tree.root.nodeId === nodeId)
|
|
160
|
+
tree.root = masked;
|
|
161
|
+
node.childIds?.forEach((childId) => hide(childId, true));
|
|
162
|
+
};
|
|
163
|
+
secretIds.forEach((nodeId) => hide(nodeId, false));
|
|
164
|
+
return tree;
|
|
165
|
+
}
|
|
87
166
|
/**
|
|
88
167
|
* Accessibility nodes of the page's iframes, attached under their `Iframe`
|
|
89
168
|
* node (the page's own tree stops there). Frames in another process
|
|
@@ -98,22 +177,13 @@ async function collectFrameNodes(pageNodes) {
|
|
|
98
177
|
const tree = (await callCDP('Page.getFrameTree', {})).data?.result;
|
|
99
178
|
const known = [...pageNodes];
|
|
100
179
|
const collected = [];
|
|
101
|
-
for (const [index, frameId] of
|
|
180
|
+
for (const [index, frameId] of childFrameIds(tree?.frameTree).entries()) {
|
|
102
181
|
const nodes = await frameNodes(frameId, `f${index}:`, known);
|
|
103
182
|
known.push(...nodes);
|
|
104
183
|
collected.push(...nodes);
|
|
105
184
|
}
|
|
106
185
|
return collected;
|
|
107
186
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Ids of all frames below a frame tree's root.
|
|
110
|
-
*
|
|
111
|
-
* @param tree - Frame tree
|
|
112
|
-
* @returns Frame ids, depth-first
|
|
113
|
-
*/
|
|
114
|
-
function childFrames(tree) {
|
|
115
|
-
return (tree?.childFrames ?? []).flatMap((child) => [child.frame.id, ...childFrames(child)]);
|
|
116
|
-
}
|
|
117
187
|
/**
|
|
118
188
|
* One frame's nodes, with ids prefixed, its root attached to the node of its
|
|
119
189
|
* `<iframe>` element.
|
|
@@ -234,6 +304,8 @@ const TEXT_ROLES = new Set(['statictext', 'inlinetextbox']);
|
|
|
234
304
|
* Queries accessibility tree by pattern (role, name, description).
|
|
235
305
|
*
|
|
236
306
|
* Performs case-insensitive matching with AND logic for multiple fields.
|
|
307
|
+
* An element reported more than once (by the page's tree and its frame's)
|
|
308
|
+
* is listed once.
|
|
237
309
|
*
|
|
238
310
|
* @param tree - Accessibility tree to search
|
|
239
311
|
* @param pattern - Query pattern with optional role, name, description
|
|
@@ -253,13 +325,19 @@ const TEXT_ROLES = new Set(['statictext', 'inlinetextbox']);
|
|
|
253
325
|
*/
|
|
254
326
|
export function queryA11yTree(tree, pattern) {
|
|
255
327
|
const matches = [];
|
|
328
|
+
const seenElements = new Set();
|
|
256
329
|
const wantsText = pattern.role !== undefined && TEXT_ROLES.has(pattern.role.toLowerCase());
|
|
257
330
|
for (const node of tree.nodes.values()) {
|
|
258
331
|
if (!wantsText && TEXT_ROLES.has(node.role.toLowerCase()))
|
|
259
332
|
continue;
|
|
260
|
-
if (matchesPattern(node, pattern))
|
|
261
|
-
|
|
333
|
+
if (!matchesPattern(node, pattern))
|
|
334
|
+
continue;
|
|
335
|
+
if (node.backendDOMNodeId !== undefined) {
|
|
336
|
+
if (seenElements.has(node.backendDOMNodeId))
|
|
337
|
+
continue;
|
|
338
|
+
seenElements.add(node.backendDOMNodeId);
|
|
262
339
|
}
|
|
340
|
+
matches.push(node);
|
|
263
341
|
}
|
|
264
342
|
return {
|
|
265
343
|
nodes: matches,
|
|
@@ -317,47 +395,110 @@ const QUERY_FIELDS = {
|
|
|
317
395
|
description: 'description',
|
|
318
396
|
desc: 'description',
|
|
319
397
|
};
|
|
398
|
+
/** The next `key:`/`key=` anywhere (fields may follow other text). */
|
|
399
|
+
const QUERY_KEY = /([a-z]+)\s*[:=]/i;
|
|
400
|
+
/** A quoted value (it may contain anything but its own quote). */
|
|
401
|
+
const QUOTED_QUERY_VALUE = /^\s*("[^"]*"|'[^']*')/;
|
|
402
|
+
/** Where a role value ends: at the next `key:` after a space or comma. */
|
|
403
|
+
const NEXT_QUERY_FIELD = /[\s,]+[a-z]+\s*[:=]/i;
|
|
320
404
|
/**
|
|
321
|
-
*
|
|
322
|
-
*
|
|
405
|
+
* Where a name or description ends: only at a known field that has a value,
|
|
406
|
+
* so names keep their spaces and colons (`name=E-mail address:`).
|
|
323
407
|
*/
|
|
324
|
-
const
|
|
408
|
+
const NEXT_KNOWN_QUERY_FIELD = /[\s,]+(?:role|name|description|desc)\s*[:=](?=\s*[^\s,])/i;
|
|
409
|
+
/**
|
|
410
|
+
* Read one field value.
|
|
411
|
+
*
|
|
412
|
+
* @param text - Text after `key:`
|
|
413
|
+
* @param field - Field the value is for
|
|
414
|
+
* @returns The value, where it ends in `text`, and whether it was quoted
|
|
415
|
+
*/
|
|
416
|
+
function readQueryValue(text, field) {
|
|
417
|
+
const quoted = QUOTED_QUERY_VALUE.exec(text);
|
|
418
|
+
if (quoted?.[1])
|
|
419
|
+
return { value: quoted[1].slice(1, -1), end: quoted[0].length, quoted: true };
|
|
420
|
+
const next = (field === 'role' ? NEXT_QUERY_FIELD : NEXT_KNOWN_QUERY_FIELD).exec(text);
|
|
421
|
+
const end = next ? next.index : text.length;
|
|
422
|
+
const value = text
|
|
423
|
+
.slice(0, end)
|
|
424
|
+
.trim()
|
|
425
|
+
.replace(/,+$/, '')
|
|
426
|
+
.replace(/^(["'])(.*)\1$/s, '$2');
|
|
427
|
+
return { value, end, quoted: false };
|
|
428
|
+
}
|
|
325
429
|
/**
|
|
326
430
|
* Parses query pattern string into A11yQueryPattern object.
|
|
327
431
|
*
|
|
328
432
|
* Fields are `key:value` or `key=value`, separated by spaces or commas.
|
|
329
|
-
*
|
|
330
|
-
*
|
|
433
|
+
* Keys: role, name, description (desc). Case-insensitive. A role ends at the
|
|
434
|
+
* next `key:`; a name or description runs to the next role/name/description
|
|
435
|
+
* field with a value, or to the end, so it may contain spaces and colons
|
|
436
|
+
* (`name=E-mail address:`). Quote a value to end it explicitly.
|
|
331
437
|
*
|
|
332
438
|
* @param patternString - Query pattern string
|
|
333
439
|
* @returns Parsed query pattern
|
|
334
|
-
* @throws CommandError (81) for unknown keys
|
|
440
|
+
* @throws CommandError (81) for unknown keys, also a misspelled one inside a name
|
|
335
441
|
*
|
|
336
442
|
* @example
|
|
337
443
|
* ```typescript
|
|
338
444
|
* parseQueryPattern('role:button name:Submit') // { role: 'button', name: 'Submit' }
|
|
339
445
|
* parseQueryPattern('role=link,name=Google Chrome') // { role: 'link', name: 'Google Chrome' }
|
|
340
446
|
* parseQueryPattern('name:"Sign in" role:button') // { name: 'Sign in', role: 'button' }
|
|
447
|
+
* parseQueryPattern('name=E-mail address:') // { name: 'E-mail address:' }
|
|
341
448
|
* ```
|
|
342
449
|
*/
|
|
343
450
|
export function parseQueryPattern(patternString) {
|
|
344
451
|
const pattern = {};
|
|
345
|
-
|
|
346
|
-
|
|
452
|
+
let rest = patternString;
|
|
453
|
+
for (let key = QUERY_KEY.exec(rest); key; key = QUERY_KEY.exec(rest)) {
|
|
454
|
+
const rawKey = key[1] ?? '';
|
|
347
455
|
const field = QUERY_FIELDS[rawKey.toLowerCase()];
|
|
348
|
-
if (!field)
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
.replace(/,+$/, '')
|
|
355
|
-
.replace(/^(["'])(.*)\1$/s, '$2');
|
|
456
|
+
if (!field)
|
|
457
|
+
throwUnknownField(rawKey);
|
|
458
|
+
const valueText = rest.slice(key.index + key[0].length);
|
|
459
|
+
const { value, end, quoted } = readQueryValue(valueText, field);
|
|
460
|
+
if (!quoted && field !== 'role')
|
|
461
|
+
checkMisspelledField(value, field);
|
|
356
462
|
if (value)
|
|
357
463
|
pattern[field] = value;
|
|
464
|
+
rest = valueText.slice(end);
|
|
358
465
|
}
|
|
359
466
|
return pattern;
|
|
360
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* Throw the exit-81 error for an unknown query field.
|
|
470
|
+
*
|
|
471
|
+
* @param field - The unrecognized key
|
|
472
|
+
* @param similar - A known field it looks like a typo of
|
|
473
|
+
* @param value - The name or description that absorbed it
|
|
474
|
+
* @throws CommandError (81)
|
|
475
|
+
*/
|
|
476
|
+
function throwUnknownField(field, similar, value) {
|
|
477
|
+
const err = unknownQueryFieldError(field, similar, value);
|
|
478
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
479
|
+
}
|
|
480
|
+
/** A `word:`/`word=` inside a name or description. */
|
|
481
|
+
const ABSORBED_KEY = /[\s,]([a-z]+)\s*[:=]/gi;
|
|
482
|
+
/**
|
|
483
|
+
* Reject a name or description that swallowed a misspelled field
|
|
484
|
+
* (`name:Save rol:button`): a `word:` within edit distance 1 of role, name or
|
|
485
|
+
* desc (2 of description). Wider distances would catch common label words
|
|
486
|
+
* (`Date:`, `Note:`, `Code:`).
|
|
487
|
+
*
|
|
488
|
+
* @param value - Unquoted name or description
|
|
489
|
+
* @param field - Field it is the value of
|
|
490
|
+
* @throws CommandError (81) with a "did you mean" suggestion
|
|
491
|
+
*/
|
|
492
|
+
function checkMisspelledField(value, field) {
|
|
493
|
+
for (const [, word = ''] of value.matchAll(ABSORBED_KEY)) {
|
|
494
|
+
const lower = word.toLowerCase();
|
|
495
|
+
if (QUERY_FIELDS[lower])
|
|
496
|
+
continue;
|
|
497
|
+
const similar = Object.keys(QUERY_FIELDS).find((name) => levenshteinDistance(lower, name) <= (name.length > 4 ? 2 : 1));
|
|
498
|
+
if (similar)
|
|
499
|
+
throwUnknownField(word, similar, `${field}=${value}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
361
502
|
/**
|
|
362
503
|
* Resolve accessibility properties for a DOM node via IPC.
|
|
363
504
|
*
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Console message collection via CDP Runtime and Log domains.
|
|
3
3
|
*
|
|
4
4
|
* Captures console.log, console.error, etc. and JavaScript exceptions
|
|
5
|
+
* (unhandled rejections removed again when a handler is attached later)
|
|
5
6
|
* with automatic nested object expansion, browser messages (failed loads,
|
|
6
7
|
* CORS, security, deprecations), and the same from cross-origin iframes and
|
|
7
8
|
* workers.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Console message collection via CDP Runtime and Log domains.
|
|
3
3
|
*
|
|
4
4
|
* Captures console.log, console.error, etc. and JavaScript exceptions
|
|
5
|
+
* (unhandled rejections removed again when a handler is attached later)
|
|
5
6
|
* with automatic nested object expansion, browser messages (failed loads,
|
|
6
7
|
* CORS, security, deprecations), and the same from cross-origin iframes and
|
|
7
8
|
* workers.
|
|
@@ -76,14 +77,17 @@ function createMessage(type, text, timestamp, args, context) {
|
|
|
76
77
|
/**
|
|
77
78
|
* Insert a message in timestamp order.
|
|
78
79
|
* Messages are kept sorted by timestamp to handle async expansion delays.
|
|
80
|
+
*
|
|
81
|
+
* @returns False when the message limit is reached (the message is dropped)
|
|
79
82
|
*/
|
|
80
83
|
function insertMessageByTimestamp(messages, message) {
|
|
81
84
|
if (messages.length >= MAX_CONSOLE_MESSAGES) {
|
|
82
85
|
log.debug(`Warning: Console message limit reached (${MAX_CONSOLE_MESSAGES})`);
|
|
83
|
-
return;
|
|
86
|
+
return false;
|
|
84
87
|
}
|
|
85
88
|
const insertIndex = findInsertIndex(messages, message.timestamp);
|
|
86
89
|
messages.splice(insertIndex, 0, message);
|
|
90
|
+
return true;
|
|
87
91
|
}
|
|
88
92
|
/**
|
|
89
93
|
* Find the correct insertion index to maintain timestamp order.
|
|
@@ -171,15 +175,93 @@ export function formatExceptionText(details) {
|
|
|
171
175
|
}
|
|
172
176
|
/**
|
|
173
177
|
* Handle an exception thrown event.
|
|
178
|
+
*
|
|
179
|
+
* @returns The message added, undefined when filtered out or over the limit
|
|
174
180
|
*/
|
|
175
181
|
function handleExceptionThrown(messages, params, context, includeAll) {
|
|
176
182
|
const exception = params.exceptionDetails;
|
|
177
183
|
const text = formatExceptionText(exception);
|
|
178
184
|
if (shouldExcludeConsoleMessage(text, 'error', includeAll)) {
|
|
179
|
-
return;
|
|
185
|
+
return undefined;
|
|
180
186
|
}
|
|
181
187
|
const message = createMessage('error', text, params.timestamp, undefined, context);
|
|
182
|
-
insertMessageByTimestamp(messages, message);
|
|
188
|
+
return insertMessageByTimestamp(messages, message) ? message : undefined;
|
|
189
|
+
}
|
|
190
|
+
/** CDP's text of an unhandled promise rejection (the only kind Chrome revokes) */
|
|
191
|
+
const UNHANDLED_REJECTION_TEXT = 'Uncaught (in promise)';
|
|
192
|
+
/**
|
|
193
|
+
* Unhandled promise rejections that may still be revoked: Chrome reports a
|
|
194
|
+
* rejection without a handler at the end of the task, and revokes the
|
|
195
|
+
* report (`Runtime.exceptionRevoked`) when a handler is attached later, as
|
|
196
|
+
* DevTools does by removing the message. `bdg dom eval` attaches its
|
|
197
|
+
* handler only after the evaluation returns, so this is what keeps
|
|
198
|
+
* `bdg dom eval 'Promise.reject(…)'` out of the console; a rejection
|
|
199
|
+
* nothing ever handles stays.
|
|
200
|
+
*/
|
|
201
|
+
class RevocableRejections {
|
|
202
|
+
/** Messages of rejections, by session and exception id, oldest first */
|
|
203
|
+
pending = new Map();
|
|
204
|
+
/**
|
|
205
|
+
* Remember a reported exception if it is an unhandled rejection. Past
|
|
206
|
+
* {@link MAX_CONSOLE_MESSAGES} rejections, the oldest is forgotten.
|
|
207
|
+
*
|
|
208
|
+
* @param details - Exception details
|
|
209
|
+
* @param message - Its console message
|
|
210
|
+
* @param sessionId - Session it was reported on (undefined: the page)
|
|
211
|
+
*/
|
|
212
|
+
track(details, message, sessionId) {
|
|
213
|
+
if (!details.text.startsWith(UNHANDLED_REJECTION_TEXT))
|
|
214
|
+
return;
|
|
215
|
+
this.pending.set(this.key(details.exceptionId, sessionId), message);
|
|
216
|
+
if (this.pending.size <= MAX_CONSOLE_MESSAGES)
|
|
217
|
+
return;
|
|
218
|
+
const [oldest] = this.pending.keys();
|
|
219
|
+
if (oldest !== undefined)
|
|
220
|
+
this.pending.delete(oldest);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Remove the message of a revoked rejection.
|
|
224
|
+
*
|
|
225
|
+
* @param messages - Console messages (updated)
|
|
226
|
+
* @param exceptionId - Revoked exception
|
|
227
|
+
* @param sessionId - Session it was revoked on (undefined: the page)
|
|
228
|
+
*/
|
|
229
|
+
revoke(messages, exceptionId, sessionId) {
|
|
230
|
+
const key = this.key(exceptionId, sessionId);
|
|
231
|
+
const message = this.pending.get(key);
|
|
232
|
+
this.pending.delete(key);
|
|
233
|
+
const index = message ? messages.indexOf(message) : -1;
|
|
234
|
+
if (index !== -1)
|
|
235
|
+
messages.splice(index, 1);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Forget the rejections of a session whose documents are gone (it
|
|
239
|
+
* detached, or its contexts were cleared by a navigation): they can no
|
|
240
|
+
* longer be revoked.
|
|
241
|
+
*
|
|
242
|
+
* @param sessionId - Session (undefined: the page)
|
|
243
|
+
*/
|
|
244
|
+
forgetSession(sessionId) {
|
|
245
|
+
const prefix = this.key('', sessionId);
|
|
246
|
+
for (const key of this.pending.keys()) {
|
|
247
|
+
if (key.startsWith(prefix))
|
|
248
|
+
this.pending.delete(key);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** Forget every rejection (the collection stopped). */
|
|
252
|
+
clear() {
|
|
253
|
+
this.pending.clear();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Map key of an exception: ids are counted per session.
|
|
257
|
+
*
|
|
258
|
+
* @param exceptionId - Exception id ('' for the session's prefix)
|
|
259
|
+
* @param sessionId - Session
|
|
260
|
+
* @returns Key
|
|
261
|
+
*/
|
|
262
|
+
key(exceptionId, sessionId) {
|
|
263
|
+
return `${sessionId ?? ''}:${exceptionId}`;
|
|
264
|
+
}
|
|
183
265
|
}
|
|
184
266
|
/**
|
|
185
267
|
* Handle a browser log entry (failed resource loads, CORS, security,
|
|
@@ -255,12 +337,24 @@ export async function startConsoleCollection(cdp, messages, includeAll = false,
|
|
|
255
337
|
};
|
|
256
338
|
handleConsoleAPICall(senderFor(cdp, sessionId), messages, params, context, includeAll);
|
|
257
339
|
});
|
|
258
|
-
|
|
340
|
+
const rejections = new RevocableRejections();
|
|
341
|
+
registry.registerTyped(typed, 'Runtime.exceptionThrown', (params, sessionId) => {
|
|
259
342
|
const context = {
|
|
260
343
|
navigationId: getCurrentNavigationId?.(),
|
|
261
344
|
stackTrace: convertStackTrace(params.exceptionDetails.stackTrace),
|
|
262
345
|
};
|
|
263
|
-
handleExceptionThrown(messages, params, context, includeAll);
|
|
346
|
+
const message = handleExceptionThrown(messages, params, context, includeAll);
|
|
347
|
+
if (message)
|
|
348
|
+
rejections.track(params.exceptionDetails, message, sessionId);
|
|
349
|
+
});
|
|
350
|
+
registry.registerTyped(typed, 'Runtime.exceptionRevoked', ({ exceptionId }, sessionId) => {
|
|
351
|
+
rejections.revoke(messages, exceptionId, sessionId);
|
|
352
|
+
});
|
|
353
|
+
registry.registerTyped(typed, 'Runtime.executionContextsCleared', (_params, sessionId) => {
|
|
354
|
+
rejections.forgetSession(sessionId);
|
|
355
|
+
});
|
|
356
|
+
registry.registerTyped(typed, 'Target.detachedFromTarget', ({ sessionId }) => {
|
|
357
|
+
rejections.forgetSession(sessionId);
|
|
264
358
|
});
|
|
265
359
|
registry.registerTyped(typed, 'Log.entryAdded', ({ entry }) => {
|
|
266
360
|
handleLogEntry(messages, entry, getCurrentNavigationId?.(), includeAll);
|
|
@@ -275,6 +369,7 @@ export async function startConsoleCollection(cdp, messages, includeAll = false,
|
|
|
275
369
|
const detachChildren = await startOptionalSources(cdp, typed);
|
|
276
370
|
return async () => {
|
|
277
371
|
registry.cleanup();
|
|
372
|
+
rejections.clear();
|
|
278
373
|
await detachChildren();
|
|
279
374
|
};
|
|
280
375
|
}
|
|
@@ -110,7 +110,7 @@ export function skippedBodyReason(body) {
|
|
|
110
110
|
* @param resourceType - Resource type reported with the response
|
|
111
111
|
*/
|
|
112
112
|
function applyResponse(request, response, resourceType) {
|
|
113
|
-
const { status, mimeType, headers, timing, remoteIPAddress, connectionId } = response;
|
|
113
|
+
const { status, mimeType, headers, timing, remoteIPAddress, remotePort, connectionId } = response;
|
|
114
114
|
request.status = status;
|
|
115
115
|
if (response.statusText)
|
|
116
116
|
request.statusText = response.statusText;
|
|
@@ -138,6 +138,8 @@ function applyResponse(request, response, resourceType) {
|
|
|
138
138
|
}
|
|
139
139
|
if (remoteIPAddress)
|
|
140
140
|
request.serverIPAddress = remoteIPAddress;
|
|
141
|
+
if (remotePort)
|
|
142
|
+
request.serverPort = remotePort;
|
|
141
143
|
if (connectionId !== undefined)
|
|
142
144
|
request.connection = String(connectionId);
|
|
143
145
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which requests an action is about (pages, API calls, sockets) and which
|
|
3
|
+
* are static assets loaded along the way (stylesheets, scripts, fonts,
|
|
4
|
+
* images), by CDP resource type.
|
|
5
|
+
*/
|
|
6
|
+
/** Fields a request needs to be classified */
|
|
7
|
+
interface ClassifiedRequest {
|
|
8
|
+
method?: string | undefined;
|
|
9
|
+
resourceType?: string | undefined;
|
|
10
|
+
status?: number | undefined;
|
|
11
|
+
failed?: true | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Whether a request is worth its own line after an action: one of the
|
|
15
|
+
* {@link ACTIVITY_RESOURCE_TYPES}, one of unknown type, any request that is
|
|
16
|
+
* not a GET (a `sendBeacon` POST typed `Other`), or an asset that failed (an
|
|
17
|
+
* HTTP error or no response).
|
|
18
|
+
*
|
|
19
|
+
* @param request - Request with its method and CDP resource type
|
|
20
|
+
* @returns False for assets fetched with GET that loaded normally
|
|
21
|
+
*/
|
|
22
|
+
export declare function isNotableRequest(request: ClassifiedRequest): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Short names of the asset types among requests, e.g. `["css", "js", "images"]`
|
|
25
|
+
* (types without a short name are lowercased, after the known ones).
|
|
26
|
+
*
|
|
27
|
+
* @param requests - Asset requests
|
|
28
|
+
* @returns Distinct names, known types first
|
|
29
|
+
*/
|
|
30
|
+
export declare function assetTypeNames(requests: ClassifiedRequest[]): string[];
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=requestKinds.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which requests an action is about (pages, API calls, sockets) and which
|
|
3
|
+
* are static assets loaded along the way (stylesheets, scripts, fonts,
|
|
4
|
+
* images), by CDP resource type.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Resource types listed one by one after an action: pages, API calls,
|
|
8
|
+
* sockets, and the requests a page sends about itself (pings, beacons,
|
|
9
|
+
* preflights, CSP reports)
|
|
10
|
+
*/
|
|
11
|
+
const ACTIVITY_RESOURCE_TYPES = new Set([
|
|
12
|
+
'Document',
|
|
13
|
+
'XHR',
|
|
14
|
+
'Fetch',
|
|
15
|
+
'WebSocket',
|
|
16
|
+
'EventSource',
|
|
17
|
+
'Ping',
|
|
18
|
+
'Preflight',
|
|
19
|
+
'CSPViolationReport',
|
|
20
|
+
]);
|
|
21
|
+
/** Short names of asset types, in the order summaries list them */
|
|
22
|
+
const ASSET_TYPE_NAMES = [
|
|
23
|
+
['Stylesheet', 'css'],
|
|
24
|
+
['Script', 'js'],
|
|
25
|
+
['Font', 'fonts'],
|
|
26
|
+
['Image', 'images'],
|
|
27
|
+
['Media', 'media'],
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Whether a request is worth its own line after an action: one of the
|
|
31
|
+
* {@link ACTIVITY_RESOURCE_TYPES}, one of unknown type, any request that is
|
|
32
|
+
* not a GET (a `sendBeacon` POST typed `Other`), or an asset that failed (an
|
|
33
|
+
* HTTP error or no response).
|
|
34
|
+
*
|
|
35
|
+
* @param request - Request with its method and CDP resource type
|
|
36
|
+
* @returns False for assets fetched with GET that loaded normally
|
|
37
|
+
*/
|
|
38
|
+
export function isNotableRequest(request) {
|
|
39
|
+
if (request.resourceType === undefined || ACTIVITY_RESOURCE_TYPES.has(request.resourceType)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (request.method !== undefined && request.method.toUpperCase() !== 'GET')
|
|
43
|
+
return true;
|
|
44
|
+
return request.failed === true || (request.status ?? 0) >= 400;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Short names of the asset types among requests, e.g. `["css", "js", "images"]`
|
|
48
|
+
* (types without a short name are lowercased, after the known ones).
|
|
49
|
+
*
|
|
50
|
+
* @param requests - Asset requests
|
|
51
|
+
* @returns Distinct names, known types first
|
|
52
|
+
*/
|
|
53
|
+
export function assetTypeNames(requests) {
|
|
54
|
+
const types = new Set(requests.map((request) => request.resourceType ?? 'Other'));
|
|
55
|
+
const known = ASSET_TYPE_NAMES.filter(([type]) => types.has(type)).map(([, name]) => name);
|
|
56
|
+
const others = [...types]
|
|
57
|
+
.filter((type) => !ASSET_TYPE_NAMES.some(([known]) => known === type))
|
|
58
|
+
.map((type) => type.toLowerCase());
|
|
59
|
+
return [...known, ...others];
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=requestKinds.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle state of captured network requests.
|
|
3
|
+
*
|
|
4
|
+
* A request is pending until it completes or fails. Status 0 means it failed
|
|
5
|
+
* without any response (DNS, refused, aborted, blocked); a request that got a
|
|
6
|
+
* response keeps its HTTP status even if loading failed afterwards.
|
|
7
|
+
*/
|
|
8
|
+
import type { NetworkRequest } from '../types.js';
|
|
9
|
+
/** Lifecycle state of a captured request. */
|
|
10
|
+
export type RequestState = 'pending' | 'failed' | 'complete';
|
|
11
|
+
/**
|
|
12
|
+
* Classify a request.
|
|
13
|
+
*
|
|
14
|
+
* @param request - Captured request
|
|
15
|
+
* @returns Its lifecycle state
|
|
16
|
+
*/
|
|
17
|
+
export declare function getRequestState(request: Pick<NetworkRequest, 'status'>): RequestState;
|
|
18
|
+
/**
|
|
19
|
+
* Why a request failed, leaving out Chrome stopping an unneeded body after a
|
|
20
|
+
* complete response (204/304 report `net::ERR_ABORTED` that way).
|
|
21
|
+
*
|
|
22
|
+
* @param request - Captured request
|
|
23
|
+
* @returns Reason, or undefined when there is none worth showing
|
|
24
|
+
*/
|
|
25
|
+
export declare function failureReason(request: Pick<NetworkRequest, 'status' | 'errorText'>): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Error Chrome reports when it stops reading a response body nobody needs
|
|
28
|
+
* (the response itself arrived; not a failure of the request).
|
|
29
|
+
*/
|
|
30
|
+
export declare const BODY_ABORTED = "net::ERR_ABORTED";
|
|
31
|
+
//# sourceMappingURL=requestState.d.ts.map
|