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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sessions across the default and named session directories (`bdg sessions`):
|
|
3
|
+
* running ones, and ones whose daemon died and left a Chrome or files behind.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import { getStatus } from '../ipc/client.js';
|
|
7
|
+
import { isSessionChrome, readLiveDaemonPid } from './cleanup/staleSession.js';
|
|
8
|
+
import { probeDaemonSocket } from './daemonSocket.js';
|
|
9
|
+
import { SESSION_STATE_FILES, getNamedSessionDir, listSessionDirs, sessionFilePathIn, } from './paths.js';
|
|
10
|
+
import { readPidFromFile } from './pid.js';
|
|
11
|
+
import { readPortFile } from './portClaims.js';
|
|
12
|
+
import { isValidSessionName, normalizeSessionName } from './sessionName.js';
|
|
13
|
+
import { createLogger, logDebugError } from '../ui/logging/index.js';
|
|
14
|
+
import { removeDirCommand, sessionCommand } from '../ui/messages/sessionCommand.js';
|
|
15
|
+
import { isProcessAlive } from '../utils/process.js';
|
|
16
|
+
const log = createLogger('session');
|
|
17
|
+
/**
|
|
18
|
+
* Summarize a daemon's status response.
|
|
19
|
+
*
|
|
20
|
+
* @param name - Session name
|
|
21
|
+
* @param data - Status data
|
|
22
|
+
* @returns Session info
|
|
23
|
+
*/
|
|
24
|
+
export function toRunningSession(name, data) {
|
|
25
|
+
const meta = data.sessionMetadata;
|
|
26
|
+
const state = meta ? 'active' : data.ending ? 'ending' : 'starting';
|
|
27
|
+
const url = data.pageState?.url ?? data.starting?.url;
|
|
28
|
+
return {
|
|
29
|
+
name,
|
|
30
|
+
state,
|
|
31
|
+
...(url !== undefined && { url }),
|
|
32
|
+
...(meta && { port: meta.port }),
|
|
33
|
+
daemonPid: data.daemonPid,
|
|
34
|
+
...(meta?.chromePid && { chromePid: meta.chromePid }),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Describe the session of a directory: a running daemon, or what a dead
|
|
39
|
+
* one left behind.
|
|
40
|
+
*
|
|
41
|
+
* @param entry - Session directory
|
|
42
|
+
* @returns Session info, or null when there is no session
|
|
43
|
+
*/
|
|
44
|
+
async function describeSession(entry) {
|
|
45
|
+
const { name, dir } = entry;
|
|
46
|
+
const socketPath = sessionFilePathIn(dir, 'DAEMON_SOCKET');
|
|
47
|
+
const probe = await probeDaemonSocket(socketPath);
|
|
48
|
+
if (probe !== 'alive')
|
|
49
|
+
return describeWithoutSocket(entry, probe);
|
|
50
|
+
try {
|
|
51
|
+
const response = await getStatus(socketPath);
|
|
52
|
+
if (response.status === 'ok' && response.data)
|
|
53
|
+
return toRunningSession(name, response.data);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
logDebugError(log, `get status of ${dir}`, error);
|
|
57
|
+
}
|
|
58
|
+
return { name, state: 'unresponsive' };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A session whose daemon socket does not answer: `starting` while its daemon
|
|
62
|
+
* (from daemon.pid, verified by command line) runs but has not bound the
|
|
63
|
+
* socket yet, otherwise see {@link describeLeftovers}.
|
|
64
|
+
*
|
|
65
|
+
* @param entry - Session directory
|
|
66
|
+
* @param probe - Result of probing its daemon socket
|
|
67
|
+
* @returns Session info, or null when the directory holds no session state
|
|
68
|
+
*/
|
|
69
|
+
function describeWithoutSocket(entry, probe) {
|
|
70
|
+
const daemonPid = readLiveDaemonPid(entry.dir);
|
|
71
|
+
if (daemonPid !== null)
|
|
72
|
+
return { name: entry.name, state: 'starting', daemonPid };
|
|
73
|
+
return describeLeftovers(entry, probe);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A session whose daemon is gone: `crashed` while the Chrome bdg launched for
|
|
77
|
+
* it still runs, `stale` when only its files are left. Nothing is changed on
|
|
78
|
+
* disk; `cleanup` names the command that removes them.
|
|
79
|
+
*
|
|
80
|
+
* @param entry - Session directory
|
|
81
|
+
* @param probe - Result of probing its daemon socket
|
|
82
|
+
* @returns Session info, or null when the directory holds no session state
|
|
83
|
+
*/
|
|
84
|
+
export function describeLeftovers({ name, dir }, probe) {
|
|
85
|
+
const chromePid = readPidFromFile(sessionFilePathIn(dir, 'CHROME_PID'));
|
|
86
|
+
const orphan = chromePid !== null && isProcessAlive(chromePid) && isSessionChrome(chromePid, dir);
|
|
87
|
+
const leftover = probe === 'stale' ||
|
|
88
|
+
SESSION_STATE_FILES.some((type) => fs.existsSync(sessionFilePathIn(dir, type)));
|
|
89
|
+
if (!orphan && !leftover)
|
|
90
|
+
return null;
|
|
91
|
+
const port = leftoverPort(dir);
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
state: orphan ? 'crashed' : 'stale',
|
|
95
|
+
...(port !== null && { port }),
|
|
96
|
+
...(orphan && { chromePid }),
|
|
97
|
+
cleanup: sessionCommand('bdg cleanup', name),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The port a dead session used: from its metadata, else its `port.txt`.
|
|
102
|
+
*
|
|
103
|
+
* @param dir - Session directory
|
|
104
|
+
* @returns Port, or null if unknown
|
|
105
|
+
*/
|
|
106
|
+
function leftoverPort(dir) {
|
|
107
|
+
try {
|
|
108
|
+
const meta = JSON.parse(fs.readFileSync(sessionFilePathIn(dir, 'METADATA'), 'utf8'));
|
|
109
|
+
if (typeof meta.port === 'number')
|
|
110
|
+
return meta.port;
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
logDebugError(log, `read the metadata in ${dir}`, error);
|
|
114
|
+
}
|
|
115
|
+
return readPortFile(sessionFilePathIn(dir, 'PORT'));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether a directory entry is a session `--session` can select: the
|
|
119
|
+
* default session, or a valid lower-case name.
|
|
120
|
+
*
|
|
121
|
+
* @param entry - Session directory
|
|
122
|
+
* @returns True if selectable
|
|
123
|
+
*/
|
|
124
|
+
function isSelectable({ name }) {
|
|
125
|
+
return name === null || (isValidSessionName(name) && name === normalizeSessionName(name));
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Whether two paths are the same directory (same inode and device), e.g.
|
|
129
|
+
* `sessions/ALPHA` and `sessions/alpha` on a case-insensitive file system.
|
|
130
|
+
*
|
|
131
|
+
* @param a - First path
|
|
132
|
+
* @param b - Second path
|
|
133
|
+
* @returns True if both exist and are the same directory
|
|
134
|
+
*/
|
|
135
|
+
function isSameDir(a, b) {
|
|
136
|
+
try {
|
|
137
|
+
const first = fs.statSync(a);
|
|
138
|
+
const second = fs.statSync(b);
|
|
139
|
+
return first.ino === second.ino && first.dev === second.dev;
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
logDebugError(log, `compare ${a} with ${b}`, error);
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The session `--session` reaches in a directory: the entry itself when its
|
|
148
|
+
* name is selectable, the lower-cased session when the name differs only in
|
|
149
|
+
* case and `--session <lower-case>` resolves to this very directory (a
|
|
150
|
+
* case-insensitive file system), else null.
|
|
151
|
+
*
|
|
152
|
+
* @param entry - Session directory
|
|
153
|
+
* @returns Selectable entry, or null
|
|
154
|
+
*/
|
|
155
|
+
function selectableEntry(entry) {
|
|
156
|
+
if (isSelectable(entry) || entry.name === null)
|
|
157
|
+
return entry;
|
|
158
|
+
const lower = normalizeSessionName(entry.name);
|
|
159
|
+
if (!isValidSessionName(lower))
|
|
160
|
+
return null;
|
|
161
|
+
return isSameDir(entry.dir, getNamedSessionDir(lower)) ? { name: lower, dir: entry.dir } : null;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A directory `--session` cannot reach (e.g. `--json`, or `ALPHA` on a
|
|
165
|
+
* case-sensitive file system, made by an earlier build): described like any
|
|
166
|
+
* session while its daemon answers, otherwise `stale` with the command that
|
|
167
|
+
* removes it by hand.
|
|
168
|
+
*
|
|
169
|
+
* @param entry - Session directory
|
|
170
|
+
* @returns Session info
|
|
171
|
+
*/
|
|
172
|
+
async function describeUnselectable(entry) {
|
|
173
|
+
const socketPath = sessionFilePathIn(entry.dir, 'DAEMON_SOCKET');
|
|
174
|
+
if ((await probeDaemonSocket(socketPath)) === 'alive')
|
|
175
|
+
return describeSession(entry);
|
|
176
|
+
return { name: entry.name, state: 'stale', cleanup: removeDirCommand(entry.dir) };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Every session: the default one first, then named sessions by name. Includes
|
|
180
|
+
* crashed and stale sessions, and directories `--session` cannot reach, so
|
|
181
|
+
* their leftovers can be cleaned up. A directory differing only in case
|
|
182
|
+
* (`ALPHA`) that `--session alpha` reaches is listed once, as `alpha`.
|
|
183
|
+
*
|
|
184
|
+
* @returns Sessions
|
|
185
|
+
*/
|
|
186
|
+
export async function listRunningSessions() {
|
|
187
|
+
const listed = new Set();
|
|
188
|
+
const sessions = await Promise.all(listSessionDirs().map((entry) => {
|
|
189
|
+
const selectable = selectableEntry(entry);
|
|
190
|
+
if (selectable === null)
|
|
191
|
+
return describeUnselectable(entry);
|
|
192
|
+
if (listed.has(selectable.name))
|
|
193
|
+
return Promise.resolve(null);
|
|
194
|
+
listed.add(selectable.name);
|
|
195
|
+
return describeSession(selectable);
|
|
196
|
+
}));
|
|
197
|
+
return sessions.filter((session) => session !== null);
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=sessionList.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named sessions (`--session <name>` / `BDG_SESSION`).
|
|
3
|
+
*
|
|
4
|
+
* A named session lives in `<base>/sessions/<name>/` with its own daemon,
|
|
5
|
+
* socket, Chrome and port, so several agents can run bdg side by side.
|
|
6
|
+
* Names are case-insensitive: they are lower-cased when selected, so `ALPHA`
|
|
7
|
+
* and `alpha` are the same session (and directory) on every file system.
|
|
8
|
+
*/
|
|
9
|
+
/** Longest session name */
|
|
10
|
+
export declare const MAX_SESSION_NAME_LENGTH = 40;
|
|
11
|
+
/**
|
|
12
|
+
* Whether a name has the allowed characters and length (case-insensitive).
|
|
13
|
+
*
|
|
14
|
+
* @param name - Session name
|
|
15
|
+
* @returns True for a valid name
|
|
16
|
+
*/
|
|
17
|
+
export declare function isValidSessionName(name: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The canonical form of a session name: lower case.
|
|
20
|
+
*
|
|
21
|
+
* @param name - Session name as given
|
|
22
|
+
* @returns Lower-cased name
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeSessionName(name: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Check a session name: allowed characters, length, and a daemon socket path
|
|
27
|
+
* the OS accepts. When even the shortest name would make the socket path too
|
|
28
|
+
* long, the base directory is blamed instead of the name.
|
|
29
|
+
*
|
|
30
|
+
* @param name - Session name
|
|
31
|
+
* @throws CommandError (81) for an invalid name or a too-long socket path
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateSessionName(name: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Select the session the command acts on.
|
|
36
|
+
*
|
|
37
|
+
* `--session` wins over `BDG_SESSION`; the choice is stored in `BDG_SESSION`
|
|
38
|
+
* so the rest of the process (and a daemon it spawns) resolves the same
|
|
39
|
+
* session directory. The name is stored lower-cased. An empty `BDG_SESSION`
|
|
40
|
+
* means the default session.
|
|
41
|
+
*
|
|
42
|
+
* @param optionValue - Value of `--session`, if given
|
|
43
|
+
* @throws CommandError (81) for an invalid name
|
|
44
|
+
*/
|
|
45
|
+
export declare function selectSession(optionValue: string | undefined): void;
|
|
46
|
+
//# sourceMappingURL=sessionName.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named sessions (`--session <name>` / `BDG_SESSION`).
|
|
3
|
+
*
|
|
4
|
+
* A named session lives in `<base>/sessions/<name>/` with its own daemon,
|
|
5
|
+
* socket, Chrome and port, so several agents can run bdg side by side.
|
|
6
|
+
* Names are case-insensitive: they are lower-cased when selected, so `ALPHA`
|
|
7
|
+
* and `alpha` are the same session (and directory) on every file system.
|
|
8
|
+
*/
|
|
9
|
+
import { CommandError } from '../errors/index.js';
|
|
10
|
+
import { invalidSessionNameError, sessionNameSocketTooLongError, socketPathTooLongError, } from '../errors/messages.js';
|
|
11
|
+
import { MAX_DAEMON_SOCKET_PATH_BYTES, SESSION_NAME_ENV, getNamedSessionDir, sessionFilePathIn, } from './paths.js';
|
|
12
|
+
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
13
|
+
/** Longest session name */
|
|
14
|
+
export const MAX_SESSION_NAME_LENGTH = 40;
|
|
15
|
+
/**
|
|
16
|
+
* Characters allowed in a session name: a letter or digit first (so a name
|
|
17
|
+
* never looks like an option), then letters, digits, `-` or `_`.
|
|
18
|
+
*/
|
|
19
|
+
const SESSION_NAME_PATTERN = new RegExp(`^[A-Za-z0-9][A-Za-z0-9_-]{0,${MAX_SESSION_NAME_LENGTH - 1}}$`);
|
|
20
|
+
/**
|
|
21
|
+
* Whether a name has the allowed characters and length (case-insensitive).
|
|
22
|
+
*
|
|
23
|
+
* @param name - Session name
|
|
24
|
+
* @returns True for a valid name
|
|
25
|
+
*/
|
|
26
|
+
export function isValidSessionName(name) {
|
|
27
|
+
return SESSION_NAME_PATTERN.test(name);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The canonical form of a session name: lower case.
|
|
31
|
+
*
|
|
32
|
+
* @param name - Session name as given
|
|
33
|
+
* @returns Lower-cased name
|
|
34
|
+
*/
|
|
35
|
+
export function normalizeSessionName(name) {
|
|
36
|
+
return name.toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check a session name: allowed characters, length, and a daemon socket path
|
|
40
|
+
* the OS accepts. When even the shortest name would make the socket path too
|
|
41
|
+
* long, the base directory is blamed instead of the name.
|
|
42
|
+
*
|
|
43
|
+
* @param name - Session name
|
|
44
|
+
* @throws CommandError (81) for an invalid name or a too-long socket path
|
|
45
|
+
*/
|
|
46
|
+
export function validateSessionName(name) {
|
|
47
|
+
if (!isValidSessionName(name)) {
|
|
48
|
+
throwInvalid(invalidSessionNameError(name, MAX_SESSION_NAME_LENGTH));
|
|
49
|
+
}
|
|
50
|
+
const shortest = namedSocketPath('a');
|
|
51
|
+
if (Buffer.byteLength(shortest) > MAX_DAEMON_SOCKET_PATH_BYTES) {
|
|
52
|
+
throwInvalid(socketPathTooLongError(shortest, MAX_DAEMON_SOCKET_PATH_BYTES));
|
|
53
|
+
}
|
|
54
|
+
const socketPath = namedSocketPath(name);
|
|
55
|
+
if (Buffer.byteLength(socketPath) > MAX_DAEMON_SOCKET_PATH_BYTES) {
|
|
56
|
+
throwInvalid(sessionNameSocketTooLongError(name, socketPath, MAX_DAEMON_SOCKET_PATH_BYTES));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Daemon socket path of a named session.
|
|
61
|
+
*
|
|
62
|
+
* @param name - Session name
|
|
63
|
+
* @returns Socket path
|
|
64
|
+
*/
|
|
65
|
+
function namedSocketPath(name) {
|
|
66
|
+
return sessionFilePathIn(getNamedSessionDir(name), 'DAEMON_SOCKET');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Throw an invalid-arguments error (81).
|
|
70
|
+
*
|
|
71
|
+
* @param err - Message and suggestion
|
|
72
|
+
* @throws CommandError always
|
|
73
|
+
*/
|
|
74
|
+
function throwInvalid(err) {
|
|
75
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Select the session the command acts on.
|
|
79
|
+
*
|
|
80
|
+
* `--session` wins over `BDG_SESSION`; the choice is stored in `BDG_SESSION`
|
|
81
|
+
* so the rest of the process (and a daemon it spawns) resolves the same
|
|
82
|
+
* session directory. The name is stored lower-cased. An empty `BDG_SESSION`
|
|
83
|
+
* means the default session.
|
|
84
|
+
*
|
|
85
|
+
* @param optionValue - Value of `--session`, if given
|
|
86
|
+
* @throws CommandError (81) for an invalid name
|
|
87
|
+
*/
|
|
88
|
+
export function selectSession(optionValue) {
|
|
89
|
+
const name = optionValue ?? process.env[SESSION_NAME_ENV]?.trim();
|
|
90
|
+
if (name === undefined || (optionValue === undefined && name === '')) {
|
|
91
|
+
delete process.env[SESSION_NAME_ENV];
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
validateSessionName(name);
|
|
95
|
+
process.env[SESSION_NAME_ENV] = normalizeSessionName(name);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=sessionName.js.map
|
package/dist/telemetry/a11y.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export declare function collectA11yTree(): Promise<A11yTree>;
|
|
|
28
28
|
* Queries accessibility tree by pattern (role, name, description).
|
|
29
29
|
*
|
|
30
30
|
* Performs case-insensitive matching with AND logic for multiple fields.
|
|
31
|
+
* An element reported more than once (by the page's tree and its frame's)
|
|
32
|
+
* is listed once.
|
|
31
33
|
*
|
|
32
34
|
* @param tree - Accessibility tree to search
|
|
33
35
|
* @param pattern - Query pattern with optional role, name, description
|
|
@@ -50,18 +52,21 @@ export declare function queryA11yTree(tree: A11yTree, pattern: A11yQueryPattern)
|
|
|
50
52
|
* Parses query pattern string into A11yQueryPattern object.
|
|
51
53
|
*
|
|
52
54
|
* Fields are `key:value` or `key=value`, separated by spaces or commas.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
+
* Keys: role, name, description (desc). Case-insensitive. A role ends at the
|
|
56
|
+
* next `key:`; a name or description runs to the next role/name/description
|
|
57
|
+
* field with a value, or to the end, so it may contain spaces and colons
|
|
58
|
+
* (`name=E-mail address:`). Quote a value to end it explicitly.
|
|
55
59
|
*
|
|
56
60
|
* @param patternString - Query pattern string
|
|
57
61
|
* @returns Parsed query pattern
|
|
58
|
-
* @throws CommandError (81) for unknown keys
|
|
62
|
+
* @throws CommandError (81) for unknown keys, also a misspelled one inside a name
|
|
59
63
|
*
|
|
60
64
|
* @example
|
|
61
65
|
* ```typescript
|
|
62
66
|
* parseQueryPattern('role:button name:Submit') // { role: 'button', name: 'Submit' }
|
|
63
67
|
* parseQueryPattern('role=link,name=Google Chrome') // { role: 'link', name: 'Google Chrome' }
|
|
64
68
|
* parseQueryPattern('name:"Sign in" role:button') // { name: 'Sign in', role: 'button' }
|
|
69
|
+
* parseQueryPattern('name=E-mail address:') // { name: 'E-mail address:' }
|
|
65
70
|
* ```
|
|
66
71
|
*/
|
|
67
72
|
export declare function parseQueryPattern(patternString: string): A11yQueryPattern;
|
package/dist/telemetry/a11y.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
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 { childFrameIds } from '../runtime/dom/frameLayout.js';
|
|
4
5
|
import { EXIT_CODES } from '../utils/exitCodes.js';
|
|
6
|
+
import { levenshteinDistance } from '../utils/levenshtein.js';
|
|
5
7
|
/**
|
|
6
8
|
* Builds accessibility tree from raw CDP nodes.
|
|
7
9
|
*
|
|
@@ -98,22 +100,13 @@ async function collectFrameNodes(pageNodes) {
|
|
|
98
100
|
const tree = (await callCDP('Page.getFrameTree', {})).data?.result;
|
|
99
101
|
const known = [...pageNodes];
|
|
100
102
|
const collected = [];
|
|
101
|
-
for (const [index, frameId] of
|
|
103
|
+
for (const [index, frameId] of childFrameIds(tree?.frameTree).entries()) {
|
|
102
104
|
const nodes = await frameNodes(frameId, `f${index}:`, known);
|
|
103
105
|
known.push(...nodes);
|
|
104
106
|
collected.push(...nodes);
|
|
105
107
|
}
|
|
106
108
|
return collected;
|
|
107
109
|
}
|
|
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
110
|
/**
|
|
118
111
|
* One frame's nodes, with ids prefixed, its root attached to the node of its
|
|
119
112
|
* `<iframe>` element.
|
|
@@ -234,6 +227,8 @@ const TEXT_ROLES = new Set(['statictext', 'inlinetextbox']);
|
|
|
234
227
|
* Queries accessibility tree by pattern (role, name, description).
|
|
235
228
|
*
|
|
236
229
|
* Performs case-insensitive matching with AND logic for multiple fields.
|
|
230
|
+
* An element reported more than once (by the page's tree and its frame's)
|
|
231
|
+
* is listed once.
|
|
237
232
|
*
|
|
238
233
|
* @param tree - Accessibility tree to search
|
|
239
234
|
* @param pattern - Query pattern with optional role, name, description
|
|
@@ -253,13 +248,19 @@ const TEXT_ROLES = new Set(['statictext', 'inlinetextbox']);
|
|
|
253
248
|
*/
|
|
254
249
|
export function queryA11yTree(tree, pattern) {
|
|
255
250
|
const matches = [];
|
|
251
|
+
const seenElements = new Set();
|
|
256
252
|
const wantsText = pattern.role !== undefined && TEXT_ROLES.has(pattern.role.toLowerCase());
|
|
257
253
|
for (const node of tree.nodes.values()) {
|
|
258
254
|
if (!wantsText && TEXT_ROLES.has(node.role.toLowerCase()))
|
|
259
255
|
continue;
|
|
260
|
-
if (matchesPattern(node, pattern))
|
|
261
|
-
|
|
256
|
+
if (!matchesPattern(node, pattern))
|
|
257
|
+
continue;
|
|
258
|
+
if (node.backendDOMNodeId !== undefined) {
|
|
259
|
+
if (seenElements.has(node.backendDOMNodeId))
|
|
260
|
+
continue;
|
|
261
|
+
seenElements.add(node.backendDOMNodeId);
|
|
262
262
|
}
|
|
263
|
+
matches.push(node);
|
|
263
264
|
}
|
|
264
265
|
return {
|
|
265
266
|
nodes: matches,
|
|
@@ -317,47 +318,110 @@ const QUERY_FIELDS = {
|
|
|
317
318
|
description: 'description',
|
|
318
319
|
desc: 'description',
|
|
319
320
|
};
|
|
321
|
+
/** The next `key:`/`key=` anywhere (fields may follow other text). */
|
|
322
|
+
const QUERY_KEY = /([a-z]+)\s*[:=]/i;
|
|
323
|
+
/** A quoted value (it may contain anything but its own quote). */
|
|
324
|
+
const QUOTED_QUERY_VALUE = /^\s*("[^"]*"|'[^']*')/;
|
|
325
|
+
/** Where a role value ends: at the next `key:` after a space or comma. */
|
|
326
|
+
const NEXT_QUERY_FIELD = /[\s,]+[a-z]+\s*[:=]/i;
|
|
320
327
|
/**
|
|
321
|
-
*
|
|
322
|
-
*
|
|
328
|
+
* Where a name or description ends: only at a known field that has a value,
|
|
329
|
+
* so names keep their spaces and colons (`name=E-mail address:`).
|
|
323
330
|
*/
|
|
324
|
-
const
|
|
331
|
+
const NEXT_KNOWN_QUERY_FIELD = /[\s,]+(?:role|name|description|desc)\s*[:=](?=\s*[^\s,])/i;
|
|
332
|
+
/**
|
|
333
|
+
* Read one field value.
|
|
334
|
+
*
|
|
335
|
+
* @param text - Text after `key:`
|
|
336
|
+
* @param field - Field the value is for
|
|
337
|
+
* @returns The value, where it ends in `text`, and whether it was quoted
|
|
338
|
+
*/
|
|
339
|
+
function readQueryValue(text, field) {
|
|
340
|
+
const quoted = QUOTED_QUERY_VALUE.exec(text);
|
|
341
|
+
if (quoted?.[1])
|
|
342
|
+
return { value: quoted[1].slice(1, -1), end: quoted[0].length, quoted: true };
|
|
343
|
+
const next = (field === 'role' ? NEXT_QUERY_FIELD : NEXT_KNOWN_QUERY_FIELD).exec(text);
|
|
344
|
+
const end = next ? next.index : text.length;
|
|
345
|
+
const value = text
|
|
346
|
+
.slice(0, end)
|
|
347
|
+
.trim()
|
|
348
|
+
.replace(/,+$/, '')
|
|
349
|
+
.replace(/^(["'])(.*)\1$/s, '$2');
|
|
350
|
+
return { value, end, quoted: false };
|
|
351
|
+
}
|
|
325
352
|
/**
|
|
326
353
|
* Parses query pattern string into A11yQueryPattern object.
|
|
327
354
|
*
|
|
328
355
|
* Fields are `key:value` or `key=value`, separated by spaces or commas.
|
|
329
|
-
*
|
|
330
|
-
*
|
|
356
|
+
* Keys: role, name, description (desc). Case-insensitive. A role ends at the
|
|
357
|
+
* next `key:`; a name or description runs to the next role/name/description
|
|
358
|
+
* field with a value, or to the end, so it may contain spaces and colons
|
|
359
|
+
* (`name=E-mail address:`). Quote a value to end it explicitly.
|
|
331
360
|
*
|
|
332
361
|
* @param patternString - Query pattern string
|
|
333
362
|
* @returns Parsed query pattern
|
|
334
|
-
* @throws CommandError (81) for unknown keys
|
|
363
|
+
* @throws CommandError (81) for unknown keys, also a misspelled one inside a name
|
|
335
364
|
*
|
|
336
365
|
* @example
|
|
337
366
|
* ```typescript
|
|
338
367
|
* parseQueryPattern('role:button name:Submit') // { role: 'button', name: 'Submit' }
|
|
339
368
|
* parseQueryPattern('role=link,name=Google Chrome') // { role: 'link', name: 'Google Chrome' }
|
|
340
369
|
* parseQueryPattern('name:"Sign in" role:button') // { name: 'Sign in', role: 'button' }
|
|
370
|
+
* parseQueryPattern('name=E-mail address:') // { name: 'E-mail address:' }
|
|
341
371
|
* ```
|
|
342
372
|
*/
|
|
343
373
|
export function parseQueryPattern(patternString) {
|
|
344
374
|
const pattern = {};
|
|
345
|
-
|
|
346
|
-
|
|
375
|
+
let rest = patternString;
|
|
376
|
+
for (let key = QUERY_KEY.exec(rest); key; key = QUERY_KEY.exec(rest)) {
|
|
377
|
+
const rawKey = key[1] ?? '';
|
|
347
378
|
const field = QUERY_FIELDS[rawKey.toLowerCase()];
|
|
348
|
-
if (!field)
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
.replace(/,+$/, '')
|
|
355
|
-
.replace(/^(["'])(.*)\1$/s, '$2');
|
|
379
|
+
if (!field)
|
|
380
|
+
throwUnknownField(rawKey);
|
|
381
|
+
const valueText = rest.slice(key.index + key[0].length);
|
|
382
|
+
const { value, end, quoted } = readQueryValue(valueText, field);
|
|
383
|
+
if (!quoted && field !== 'role')
|
|
384
|
+
checkMisspelledField(value, field);
|
|
356
385
|
if (value)
|
|
357
386
|
pattern[field] = value;
|
|
387
|
+
rest = valueText.slice(end);
|
|
358
388
|
}
|
|
359
389
|
return pattern;
|
|
360
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Throw the exit-81 error for an unknown query field.
|
|
393
|
+
*
|
|
394
|
+
* @param field - The unrecognized key
|
|
395
|
+
* @param similar - A known field it looks like a typo of
|
|
396
|
+
* @param value - The name or description that absorbed it
|
|
397
|
+
* @throws CommandError (81)
|
|
398
|
+
*/
|
|
399
|
+
function throwUnknownField(field, similar, value) {
|
|
400
|
+
const err = unknownQueryFieldError(field, similar, value);
|
|
401
|
+
throw new CommandError(err.message, { suggestion: err.suggestion }, EXIT_CODES.INVALID_ARGUMENTS);
|
|
402
|
+
}
|
|
403
|
+
/** A `word:`/`word=` inside a name or description. */
|
|
404
|
+
const ABSORBED_KEY = /[\s,]([a-z]+)\s*[:=]/gi;
|
|
405
|
+
/**
|
|
406
|
+
* Reject a name or description that swallowed a misspelled field
|
|
407
|
+
* (`name:Save rol:button`): a `word:` within edit distance 1 of role, name or
|
|
408
|
+
* desc (2 of description). Wider distances would catch common label words
|
|
409
|
+
* (`Date:`, `Note:`, `Code:`).
|
|
410
|
+
*
|
|
411
|
+
* @param value - Unquoted name or description
|
|
412
|
+
* @param field - Field it is the value of
|
|
413
|
+
* @throws CommandError (81) with a "did you mean" suggestion
|
|
414
|
+
*/
|
|
415
|
+
function checkMisspelledField(value, field) {
|
|
416
|
+
for (const [, word = ''] of value.matchAll(ABSORBED_KEY)) {
|
|
417
|
+
const lower = word.toLowerCase();
|
|
418
|
+
if (QUERY_FIELDS[lower])
|
|
419
|
+
continue;
|
|
420
|
+
const similar = Object.keys(QUERY_FIELDS).find((name) => levenshteinDistance(lower, name) <= (name.length > 4 ? 2 : 1));
|
|
421
|
+
if (similar)
|
|
422
|
+
throwUnknownField(word, similar, `${field}=${value}`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
361
425
|
/**
|
|
362
426
|
* Resolve accessibility properties for a DOM node via IPC.
|
|
363
427
|
*
|
|
@@ -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
|