browser-debugger-cli 0.10.0 → 0.12.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/.claude/skills/bdg/SKILL.md +268 -0
- package/README.md +148 -74
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/formInteraction.js +36 -6
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +158 -38
- package/dist/commands/dom/index.js +4 -1
- package/dist/commands/dom/screenshot.js +10 -6
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.js +1 -1
- package/dist/commands/installSkill.d.ts +20 -0
- package/dist/commands/installSkill.js +87 -0
- package/dist/commands/optionBehaviors.js +21 -6
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/optionTypes.d.ts +5 -1
- package/dist/commands/start.d.ts +13 -0
- package/dist/commands/start.js +19 -2
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +5 -0
- package/dist/daemon/session/commandRegistry.js +7 -1
- package/dist/daemon/session/plugins.js +4 -52
- package/dist/daemon.js +7986 -6848
- package/dist/errors/messages.d.ts +50 -4
- package/dist/errors/messages.js +94 -5
- package/dist/index.js +709 -190
- package/dist/ipc/client.d.ts +4 -0
- package/dist/ipc/client.js +8 -0
- package/dist/ipc/protocol/auditTypes.d.ts +129 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +23 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +4 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +71 -8
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +4 -1
- package/dist/runtime/dom/actionEffects.js +8 -4
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +36 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +215 -0
- package/dist/runtime/dom/auditScripts.d.ts +107 -0
- package/dist/runtime/dom/auditScripts.js +112 -0
- package/dist/runtime/dom/elementGeometry.d.ts +8 -2
- package/dist/runtime/dom/elementGeometry.js +24 -8
- package/dist/runtime/dom/elementInfo.d.ts +3 -2
- package/dist/runtime/dom/elementInfo.js +8 -2
- package/dist/runtime/dom/formFillHelpers/fill.js +2 -2
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +88 -23
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +89 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +3 -0
- package/dist/runtime/dom/inspectModel.js +30 -7
- package/dist/runtime/dom/inspectPaintModel.d.ts +48 -22
- package/dist/runtime/dom/inspectPaintModel.js +180 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +85 -12
- package/dist/runtime/dom/inspectScripts.js +314 -28
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +31 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +7 -0
- package/dist/runtime/dom/reactEventHelpers.js +27 -9
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/types.d.ts +12 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +106 -0
- package/dist/ui/formatters/dom.d.ts +1 -1
- package/dist/ui/formatters/dom.js +6 -3
- package/dist/ui/formatters/inspect.js +42 -15
- package/dist/ui/formatters/installSkill.d.ts +11 -0
- package/dist/ui/formatters/installSkill.js +31 -0
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/messages/commands.d.ts +44 -7
- package/dist/ui/messages/commands.js +83 -11
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +4 -2
package/dist/ipc/client.d.ts
CHANGED
|
@@ -231,6 +231,10 @@ export declare function domFormDiscover(): Promise<ClientResponse<'dom_form_disc
|
|
|
231
231
|
export declare function domListeners(params: NoType<(typeof COMMANDS)['dom_listeners']['requestSchema']>): Promise<ClientResponse<'dom_listeners'>>;
|
|
232
232
|
/** Positions, sizes and visibility of elements. */
|
|
233
233
|
export declare function domLayout(params: NoType<(typeof COMMANDS)['dom_layout']['requestSchema']>): Promise<ClientResponse<'dom_layout'>>;
|
|
234
|
+
/** Page-wide checks: contrast, overflow, layers, animations. */
|
|
235
|
+
export declare function domAudit(params: NoType<(typeof COMMANDS)['dom_audit']['requestSchema']>): Promise<ClientResponse<'dom_audit'>>;
|
|
236
|
+
/** Find text in the page's stylesheets. */
|
|
237
|
+
export declare function cssSearch(params: NoType<(typeof COMMANDS)['css_search']['requestSchema']>): Promise<ClientResponse<'css_search'>>;
|
|
234
238
|
/** What one element looks like: styles, box, layout and child tree. */
|
|
235
239
|
export declare function domInspect(params: NoType<(typeof COMMANDS)['dom_inspect']['requestSchema']>): Promise<ClientResponse<'dom_inspect'>>;
|
|
236
240
|
/**
|
package/dist/ipc/client.js
CHANGED
|
@@ -334,6 +334,14 @@ export async function domListeners(params) {
|
|
|
334
334
|
export async function domLayout(params) {
|
|
335
335
|
return sendCommand('dom_layout', params);
|
|
336
336
|
}
|
|
337
|
+
/** Page-wide checks: contrast, overflow, layers, animations. */
|
|
338
|
+
export async function domAudit(params) {
|
|
339
|
+
return sendCommand('dom_audit', params);
|
|
340
|
+
}
|
|
341
|
+
/** Find text in the page's stylesheets. */
|
|
342
|
+
export async function cssSearch(params) {
|
|
343
|
+
return sendCommand('css_search', params);
|
|
344
|
+
}
|
|
337
345
|
/** What one element looks like: styles, box, layout and child tree. */
|
|
338
346
|
export async function domInspect(params) {
|
|
339
347
|
return sendCommand('dom_inspect', params);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types of `bdg dom audit` (page-wide checks) and `bdg css search`.
|
|
3
|
+
*/
|
|
4
|
+
/** A check `dom audit` can run */
|
|
5
|
+
export type AuditCheck = 'contrast' | 'overflow' | 'layers' | 'animations';
|
|
6
|
+
/** Every check, in the order they are shown */
|
|
7
|
+
export declare const AUDIT_CHECKS: readonly AuditCheck[];
|
|
8
|
+
/** Text below the contrast level */
|
|
9
|
+
export interface AuditContrastItem {
|
|
10
|
+
/** `tag#id` or `tag.firstClass` */
|
|
11
|
+
element: string;
|
|
12
|
+
text: string;
|
|
13
|
+
/** WCAG ratio, rounded down to 2 decimals */
|
|
14
|
+
ratio: number;
|
|
15
|
+
color: string;
|
|
16
|
+
/** Background behind the text, composited */
|
|
17
|
+
background: string;
|
|
18
|
+
/** Font size (px) and weight: large text needs less contrast */
|
|
19
|
+
size: number;
|
|
20
|
+
weight: number;
|
|
21
|
+
inView: boolean;
|
|
22
|
+
/** Why the ratio is approximate (blend modes, filters) */
|
|
23
|
+
approximate?: string[];
|
|
24
|
+
}
|
|
25
|
+
/** An image drawn larger than its pixels, or with another aspect ratio */
|
|
26
|
+
export interface AuditImage {
|
|
27
|
+
element: string;
|
|
28
|
+
natural: {
|
|
29
|
+
w: number;
|
|
30
|
+
h: number;
|
|
31
|
+
};
|
|
32
|
+
rendered: {
|
|
33
|
+
w: number;
|
|
34
|
+
h: number;
|
|
35
|
+
};
|
|
36
|
+
/** Pixels needed (rendered size × pixel ratio) over the image's pixels, the larger of width and height */
|
|
37
|
+
scale: number;
|
|
38
|
+
/** Identical findings this one stands for (2 or more) */
|
|
39
|
+
count?: number;
|
|
40
|
+
upscaled?: true;
|
|
41
|
+
distorted?: true;
|
|
42
|
+
}
|
|
43
|
+
/** What `dom audit` found */
|
|
44
|
+
export interface AuditResult {
|
|
45
|
+
checks: AuditCheck[];
|
|
46
|
+
/** Elements walked */
|
|
47
|
+
walked: number;
|
|
48
|
+
/** The walk stopped at its cap: the page has more elements */
|
|
49
|
+
capped?: true;
|
|
50
|
+
contrast?: {
|
|
51
|
+
level: 'AA' | 'AAA';
|
|
52
|
+
/** Text holders checked */
|
|
53
|
+
checked: number;
|
|
54
|
+
/** How many are below the level */
|
|
55
|
+
failing: number;
|
|
56
|
+
/** The weakest ones, at most `--limit` */
|
|
57
|
+
items: AuditContrastItem[];
|
|
58
|
+
};
|
|
59
|
+
overflow?: {
|
|
60
|
+
pageWidth: number;
|
|
61
|
+
viewportWidth: number;
|
|
62
|
+
/** The page is wider than its viewport (it scrolls sideways) */
|
|
63
|
+
scrollsSideways: boolean;
|
|
64
|
+
/** Elements reaching past the viewport's right edge (not inside a scroller), farthest first */
|
|
65
|
+
wide: Array<{
|
|
66
|
+
element: string;
|
|
67
|
+
right: number;
|
|
68
|
+
width: number;
|
|
69
|
+
}>;
|
|
70
|
+
/** Text cut off: `ellipsis`, `clamp` or `clip` */
|
|
71
|
+
truncated: Array<{
|
|
72
|
+
element: string;
|
|
73
|
+
text: string;
|
|
74
|
+
kind: string;
|
|
75
|
+
count?: number;
|
|
76
|
+
}>;
|
|
77
|
+
images: AuditImage[];
|
|
78
|
+
/** Device pixel ratio the image scale counts in (an image needs that many pixels per CSS px) */
|
|
79
|
+
pixelRatio: number;
|
|
80
|
+
/** Elements whose content scrolls sideways inside them (carousels, tab strips): fine, but cut off at first sight */
|
|
81
|
+
scrollers: Array<{
|
|
82
|
+
element: string;
|
|
83
|
+
scrollWidth: number;
|
|
84
|
+
width: number;
|
|
85
|
+
}>;
|
|
86
|
+
};
|
|
87
|
+
layers?: Array<{
|
|
88
|
+
element: string;
|
|
89
|
+
position: string;
|
|
90
|
+
zIndex: string;
|
|
91
|
+
/** Viewport position and size */
|
|
92
|
+
rect: {
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
w: number;
|
|
96
|
+
h: number;
|
|
97
|
+
};
|
|
98
|
+
inView: boolean;
|
|
99
|
+
}>;
|
|
100
|
+
animations?: Array<{
|
|
101
|
+
element: string;
|
|
102
|
+
name: string;
|
|
103
|
+
type: string;
|
|
104
|
+
/** Duration (ms) */
|
|
105
|
+
duration: number | string;
|
|
106
|
+
iterations: number | string;
|
|
107
|
+
/** Driven by scrolling, not time */
|
|
108
|
+
scrollDriven?: true;
|
|
109
|
+
/** Identical animations this one stands for (2 or more) */
|
|
110
|
+
count?: number;
|
|
111
|
+
}>;
|
|
112
|
+
}
|
|
113
|
+
/** A stylesheet line where `css search` found the text */
|
|
114
|
+
export interface CssSearchMatch {
|
|
115
|
+
/** `app.css:12`, `bootstrap.min.css:5:52628`, `<style> in index.html:40` */
|
|
116
|
+
source: string;
|
|
117
|
+
/** The rule (or line) around the match, cut to a few hundred characters */
|
|
118
|
+
text: string;
|
|
119
|
+
}
|
|
120
|
+
/** What `css search` found */
|
|
121
|
+
export interface CssSearchResult {
|
|
122
|
+
query: string;
|
|
123
|
+
/** Stylesheets searched */
|
|
124
|
+
sheets: number;
|
|
125
|
+
/** Matches found (the list may be shorter: `--limit`) */
|
|
126
|
+
total: number;
|
|
127
|
+
matches: CssSearchMatch[];
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=auditTypes.d.ts.map
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Each command has a request schema (input) and response data schema (output).
|
|
6
6
|
*/
|
|
7
7
|
import type { HintDetails } from '../../errors/notices.js';
|
|
8
|
+
import type { AuditCheck, AuditResult, CssSearchResult } from './auditTypes.js';
|
|
8
9
|
import type { ClickResult, FillResult, LayoutResult, ListenersResult, PressKeyResult, RawFormData, ScrollResult, SubmitResult } from './domTypes.js';
|
|
9
10
|
import type { InspectResult } from './inspectTypes.js';
|
|
10
11
|
import type { PageState, SessionActivity } from '../session/types.js';
|
|
@@ -312,6 +313,26 @@ export interface DomLayoutCommand {
|
|
|
312
313
|
backendNodeId?: number;
|
|
313
314
|
}
|
|
314
315
|
export type DomLayoutData = LayoutResult;
|
|
316
|
+
/**
|
|
317
|
+
* dom_audit: page-wide checks (contrast, overflow, layers, animations).
|
|
318
|
+
*/
|
|
319
|
+
export interface DomAuditCommand {
|
|
320
|
+
checks: AuditCheck[];
|
|
321
|
+
/** WCAG level text must reach (default AA) */
|
|
322
|
+
level?: 'AA' | 'AAA';
|
|
323
|
+
/** Findings listed per check */
|
|
324
|
+
limit?: number;
|
|
325
|
+
}
|
|
326
|
+
export type DomAuditData = AuditResult;
|
|
327
|
+
/**
|
|
328
|
+
* css_search: find text in the page's stylesheets.
|
|
329
|
+
*/
|
|
330
|
+
export interface CssSearchCommand {
|
|
331
|
+
query: string;
|
|
332
|
+
/** Matches listed at most */
|
|
333
|
+
limit?: number;
|
|
334
|
+
}
|
|
335
|
+
export type CssSearchData = CssSearchResult;
|
|
315
336
|
/**
|
|
316
337
|
* dom_inspect: what one element looks like (styles, box, layout, child tree).
|
|
317
338
|
*/
|
|
@@ -369,6 +390,8 @@ export type RegistryShape = {
|
|
|
369
390
|
dom_form_discover: CommandDef<DomFormDiscoverCommand, DomFormDiscoverData>;
|
|
370
391
|
dom_listeners: CommandDef<DomListenersCommand, DomListenersData>;
|
|
371
392
|
dom_layout: CommandDef<DomLayoutCommand, DomLayoutData>;
|
|
393
|
+
dom_audit: CommandDef<DomAuditCommand, DomAuditData>;
|
|
394
|
+
css_search: CommandDef<CssSearchCommand, CssSearchData>;
|
|
372
395
|
dom_inspect: CommandDef<DomInspectCommand, DomInspectData>;
|
|
373
396
|
dom_wait: CommandDef<DomWaitCommand, DomWaitData>;
|
|
374
397
|
page_navigate: CommandDef<PageNavigateCommand, PageNavigationResult>;
|
|
@@ -35,6 +35,8 @@ export const COMMANDS = {
|
|
|
35
35
|
dom_form_discover: defineCommand(),
|
|
36
36
|
dom_listeners: defineCommand(),
|
|
37
37
|
dom_layout: defineCommand(),
|
|
38
|
+
dom_audit: defineCommand(),
|
|
39
|
+
css_search: defineCommand(),
|
|
38
40
|
dom_inspect: defineCommand(),
|
|
39
41
|
dom_wait: defineCommand(),
|
|
40
42
|
};
|
|
@@ -86,6 +86,8 @@ export interface ActionEffects {
|
|
|
86
86
|
navigation?: PageNavigation;
|
|
87
87
|
/** Messages that appeared or changed (at most 3; absent when none did) */
|
|
88
88
|
messages?: NewMessage[];
|
|
89
|
+
/** How many more new messages there were than `messages` lists */
|
|
90
|
+
moreMessages?: number;
|
|
89
91
|
/** Elements a hover or key press showed (at most 3, outermost first; absent when none) */
|
|
90
92
|
shown?: ShownElement[];
|
|
91
93
|
/** "none" when the action had no visible effect: no DOM change, request or navigation */
|
|
@@ -405,6 +407,8 @@ export interface ElementLayout {
|
|
|
405
407
|
scrollBy?: LayoutPoint;
|
|
406
408
|
/** Ancestor or iframe cutting it off (scroll that container instead of the page) */
|
|
407
409
|
clippedBy?: string;
|
|
410
|
+
/** It or a container is `position: fixed`: page scroll does not move it */
|
|
411
|
+
fixed?: true;
|
|
408
412
|
/** Why page scroll cannot bring it fully into view: it is fixed, or beyond the page's scroll range */
|
|
409
413
|
offScreenReason?: string;
|
|
410
414
|
/** Topmost element at the center of its visible part, when that is another element */
|
|
@@ -14,6 +14,13 @@ export interface InspectRect {
|
|
|
14
14
|
y: number;
|
|
15
15
|
w: number;
|
|
16
16
|
h: number;
|
|
17
|
+
/** `viewport`: x and y are in the viewport (a fixed element stays there however the page scrolls) */
|
|
18
|
+
in?: 'viewport';
|
|
19
|
+
/** Size of the box it covers on screen when a transform (rotation, skew) makes that differ; x and y are its corner */
|
|
20
|
+
screen?: {
|
|
21
|
+
w: number;
|
|
22
|
+
h: number;
|
|
23
|
+
};
|
|
17
24
|
}
|
|
18
25
|
/** Sides top, right, bottom, left */
|
|
19
26
|
export type Sides = [CssLength, CssLength, CssLength, CssLength];
|
|
@@ -104,17 +111,29 @@ export interface InspectContrast {
|
|
|
104
111
|
/** A background image or gradient is behind the text: the ratio uses the colors only */
|
|
105
112
|
overImage?: boolean;
|
|
106
113
|
/**
|
|
107
|
-
* Opacity of the element and its ancestors (below 1):
|
|
108
|
-
*
|
|
109
|
-
*
|
|
114
|
+
* Opacity of the element and its ancestors (below 1): each translucent
|
|
115
|
+
* element fades its background and the text over it before the ratio is
|
|
116
|
+
* taken
|
|
110
117
|
*/
|
|
111
118
|
opacity?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Why the ratio is approximate: `mix-blend-mode hard-light on h1`,
|
|
121
|
+
* `filter on div.skin-invert`, `canvas behind`, `div.overlay on top`
|
|
122
|
+
*/
|
|
123
|
+
approximate?: string[];
|
|
112
124
|
}
|
|
113
125
|
/** Typography (for containers without text of their own: only what differs from the parent) */
|
|
114
126
|
export interface InspectText {
|
|
127
|
+
/**
|
|
128
|
+
* Label of the descendant that draws most of the text when it is not the
|
|
129
|
+
* element (`abbr`, `slot.button__label`): the fields describe its text
|
|
130
|
+
*/
|
|
131
|
+
holder?: string;
|
|
115
132
|
family?: string;
|
|
116
|
-
/** Font Chrome rendered the text with, when it is
|
|
133
|
+
/** Font Chrome rendered the text with, when it is a fallback for the first family */
|
|
117
134
|
rendered?: string;
|
|
135
|
+
/** Font a generic first family (`sans-serif`, `system-ui`) resolved to */
|
|
136
|
+
resolved?: string;
|
|
118
137
|
/** The rendered font is a web font */
|
|
119
138
|
webfont?: boolean;
|
|
120
139
|
weight?: number;
|
|
@@ -132,19 +151,30 @@ export interface InspectText {
|
|
|
132
151
|
clamp?: string;
|
|
133
152
|
shadow?: string;
|
|
134
153
|
features?: string;
|
|
154
|
+
/** The text is cut off (clipped by overflow, with or without an ellipsis, or by a line clamp) */
|
|
155
|
+
truncated?: true;
|
|
156
|
+
/** The text is painted with its background (`background-clip: text`, transparent fill): no single color, so no contrast */
|
|
157
|
+
gradientFill?: true;
|
|
135
158
|
}
|
|
136
159
|
/** A background layer */
|
|
137
160
|
export type InspectFill = {
|
|
138
161
|
type: 'solid';
|
|
139
162
|
color: string;
|
|
140
163
|
} | {
|
|
141
|
-
type: 'gradient';
|
|
142
|
-
value: string;
|
|
143
|
-
} | {
|
|
144
|
-
type: 'image';
|
|
164
|
+
type: 'gradient' | 'image';
|
|
145
165
|
value: string;
|
|
146
166
|
size?: string;
|
|
167
|
+
position?: string;
|
|
147
168
|
};
|
|
169
|
+
/** How an SVG element is painted */
|
|
170
|
+
export interface InspectSvgPaint {
|
|
171
|
+
/** Fill color (hex), `none` or a paint server (`url(#grad)`) */
|
|
172
|
+
fill: string;
|
|
173
|
+
/** Stroke color, `none` or a paint server */
|
|
174
|
+
stroke: string;
|
|
175
|
+
/** Stroke width (px), when there is a stroke */
|
|
176
|
+
strokeWidth?: CssLength;
|
|
177
|
+
}
|
|
148
178
|
/** A border side (or all four) */
|
|
149
179
|
export interface InspectStroke {
|
|
150
180
|
side: 'all' | 'top' | 'right' | 'bottom' | 'left';
|
|
@@ -184,6 +214,8 @@ export interface InspectPseudo {
|
|
|
184
214
|
content?: string;
|
|
185
215
|
display?: string;
|
|
186
216
|
position?: string;
|
|
217
|
+
/** Offsets of a positioned one (top right bottom left) */
|
|
218
|
+
inset?: string;
|
|
187
219
|
size?: {
|
|
188
220
|
w: number;
|
|
189
221
|
h: number;
|
|
@@ -212,6 +244,12 @@ export interface InspectTreeNode {
|
|
|
212
244
|
/** `flex` or `grid` container */
|
|
213
245
|
layout?: 'flex' | 'grid';
|
|
214
246
|
text?: string;
|
|
247
|
+
/** `display: contents` (a text-only slot): no box of its own */
|
|
248
|
+
contents?: true;
|
|
249
|
+
/** Reached through a slot or a `display: contents` wrapper (`slot.label`, `div.row (contents)`) */
|
|
250
|
+
via?: string;
|
|
251
|
+
/** In the shadow root of its parent */
|
|
252
|
+
shadow?: true;
|
|
215
253
|
/** Identical siblings this row stands for (2 or more) */
|
|
216
254
|
count?: number;
|
|
217
255
|
children?: InspectTreeNode[];
|
|
@@ -251,6 +289,10 @@ export interface InspectHint {
|
|
|
251
289
|
reason: string;
|
|
252
290
|
/** e.g. `use display: flex or grid on this element` */
|
|
253
291
|
fix: string;
|
|
292
|
+
/** The longhands of a shorthand that have no effect, when the others do (`margin-top`, `margin-bottom`) */
|
|
293
|
+
only?: string[];
|
|
294
|
+
/** The custom properties that are not set (`unset-variable`) */
|
|
295
|
+
variables?: string[];
|
|
254
296
|
/** e.g. `.hero (app.css:12)` */
|
|
255
297
|
source: string;
|
|
256
298
|
}
|
|
@@ -264,6 +306,8 @@ export interface InspectRule {
|
|
|
264
306
|
computed?: string;
|
|
265
307
|
/** e.g. `.btn-primary (bootstrap.min.css:5:52628)`, `style attribute` */
|
|
266
308
|
source: string;
|
|
309
|
+
/** The rule as written (selector and declarations); a rule over 300 characters is cut to its selector and this declaration */
|
|
310
|
+
rule?: string;
|
|
267
311
|
/** Selectors of the declarations it beats */
|
|
268
312
|
overrides?: string[];
|
|
269
313
|
/** Set on an ancestor this many levels up (inherited) */
|
|
@@ -286,8 +330,15 @@ export interface InspectWhyEntry {
|
|
|
286
330
|
source: string;
|
|
287
331
|
/** Specificity of the rule's selector (ids, classes, types) */
|
|
288
332
|
specificity?: [number, number, number];
|
|
333
|
+
/** The rule as written (selector and declarations); a rule over 300 characters is cut to its selector and this declaration */
|
|
334
|
+
rule?: string;
|
|
289
335
|
/** `applied` (wins), `overridden`, or `inherited` (from an ancestor: the winner, or one it beat there) */
|
|
290
336
|
status: 'applied' | 'overridden' | 'inherited';
|
|
337
|
+
/**
|
|
338
|
+
* Why the winner changes nothing: `no effect: position is static`, or for
|
|
339
|
+
* an invalid `var()`, what applies instead (`falls back to the initial value`)
|
|
340
|
+
*/
|
|
341
|
+
note?: string;
|
|
291
342
|
important?: true;
|
|
292
343
|
layer?: string;
|
|
293
344
|
condition?: string;
|
|
@@ -300,6 +351,12 @@ export interface InspectWhy {
|
|
|
300
351
|
chain: InspectWhyEntry[];
|
|
301
352
|
/** Where the custom properties of the winning value are set */
|
|
302
353
|
variables?: InspectVariable[];
|
|
354
|
+
/** Rules for the element that set it under a `@media`/`@supports` condition that does not apply now */
|
|
355
|
+
inactive?: Array<{
|
|
356
|
+
value: string;
|
|
357
|
+
selector: string;
|
|
358
|
+
condition: string;
|
|
359
|
+
}>;
|
|
303
360
|
}
|
|
304
361
|
/** A custom property a winning value uses, and where it is set */
|
|
305
362
|
export interface InspectVariable {
|
|
@@ -324,6 +381,8 @@ export interface InspectResult {
|
|
|
324
381
|
* rendered) or the first
|
|
325
382
|
*/
|
|
326
383
|
picked?: 'first-visible' | 'first';
|
|
384
|
+
/** The selector named a pseudo-element (`a::after`): its element was inspected, the pseudo-element is under `pseudo` */
|
|
385
|
+
pseudoOf?: '::before' | '::after';
|
|
327
386
|
/** `tag#id.c1.c2(+N)` */
|
|
328
387
|
element: string;
|
|
329
388
|
/** Its text (innerText) or form value, at most 30 characters; not for containers */
|
|
@@ -341,6 +400,8 @@ export interface InspectResult {
|
|
|
341
400
|
* theme's
|
|
342
401
|
*/
|
|
343
402
|
theme?: 'dark';
|
|
403
|
+
/** The dark preference behind `theme` comes from `page emulate`, not the system */
|
|
404
|
+
themeFrom?: 'emulation';
|
|
344
405
|
/**
|
|
345
406
|
* Running CSS transitions (their property) and animations (their name):
|
|
346
407
|
* the values read are mid-way and will still change
|
|
@@ -352,6 +413,8 @@ export interface InspectResult {
|
|
|
352
413
|
layout?: InspectLayout;
|
|
353
414
|
text?: InspectText;
|
|
354
415
|
fills?: InspectFill[];
|
|
416
|
+
/** SVG paint: `fill` and `stroke` (with its width) of an SVG element */
|
|
417
|
+
paint?: InspectSvgPaint;
|
|
355
418
|
opacity?: number;
|
|
356
419
|
blend?: string;
|
|
357
420
|
strokes?: InspectStroke[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bdg css search <text>`: find text in the page's stylesheets, cross-origin
|
|
3
|
+
* ones included (CDP reads every stylesheet's text), and show the rule
|
|
4
|
+
* around each match with its `file:line`.
|
|
5
|
+
*/
|
|
6
|
+
import type { CDPConnection } from '../../connection/cdp.js';
|
|
7
|
+
import type { CssSearchResult } from '../../ipc/protocol/auditTypes.js';
|
|
8
|
+
import type { CssSearchCommand } from '../../ipc/protocol/commands.js';
|
|
9
|
+
/** Matches listed without `--limit` */
|
|
10
|
+
export declare const DEFAULT_CSS_SEARCH_LIMIT = 20;
|
|
11
|
+
/**
|
|
12
|
+
* Search the stylesheets for a text (case-insensitive).
|
|
13
|
+
*
|
|
14
|
+
* @param cdp - CDP connection
|
|
15
|
+
* @param params - Text and limit
|
|
16
|
+
* @returns Matches with their place
|
|
17
|
+
*/
|
|
18
|
+
export declare function searchStyleSheets(cdp: CDPConnection, params: CssSearchCommand): Promise<CssSearchResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Where a text occurs in a stylesheet (case-insensitive): how many times,
|
|
21
|
+
* and for the first `limit` matches the 0-based line and column and the
|
|
22
|
+
* rule around it (from the end of the previous rule to the end of this one,
|
|
23
|
+
* whitespace collapsed, at most {@link RULE_CONTEXT} characters each side).
|
|
24
|
+
* Lines are counted as the search moves on, so a big sheet is read once.
|
|
25
|
+
*
|
|
26
|
+
* @param text - Stylesheet text
|
|
27
|
+
* @param query - Text to find
|
|
28
|
+
* @param limit - Matches described at most
|
|
29
|
+
* @returns Match count and the described matches
|
|
30
|
+
*/
|
|
31
|
+
export declare function findInSheet(text: string, query: string, limit?: number): {
|
|
32
|
+
total: number;
|
|
33
|
+
matches: Array<{
|
|
34
|
+
line: number;
|
|
35
|
+
column: number;
|
|
36
|
+
rule: string;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bdg css search <text>`: find text in the page's stylesheets, cross-origin
|
|
3
|
+
* ones included (CDP reads every stylesheet's text), and show the rule
|
|
4
|
+
* around each match with its `file:line`.
|
|
5
|
+
*/
|
|
6
|
+
import { enableStyleDomains } from '../dom/inspect.js';
|
|
7
|
+
import { stylesheetPositionLabel, styleSheetHeaders } from '../dom/inspectRules.js';
|
|
8
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
9
|
+
import { getErrorMessage } from '../../utils/errors.js';
|
|
10
|
+
const log = createLogger('dom');
|
|
11
|
+
/** Matches listed without `--limit` */
|
|
12
|
+
export const DEFAULT_CSS_SEARCH_LIMIT = 20;
|
|
13
|
+
/** Characters of a rule shown around a match */
|
|
14
|
+
const RULE_CONTEXT = 240;
|
|
15
|
+
/**
|
|
16
|
+
* Search the stylesheets for a text (case-insensitive).
|
|
17
|
+
*
|
|
18
|
+
* @param cdp - CDP connection
|
|
19
|
+
* @param params - Text and limit
|
|
20
|
+
* @returns Matches with their place
|
|
21
|
+
*/
|
|
22
|
+
export async function searchStyleSheets(cdp, params) {
|
|
23
|
+
await enableStyleDomains(cdp);
|
|
24
|
+
const headers = [...styleSheetHeaders(cdp)].filter((header) => header.origin !== 'user-agent');
|
|
25
|
+
const limit = params.limit ?? DEFAULT_CSS_SEARCH_LIMIT;
|
|
26
|
+
const texts = await Promise.all(headers.map((header) => sheetText(cdp, header.styleSheetId)));
|
|
27
|
+
const matches = [];
|
|
28
|
+
let total = 0;
|
|
29
|
+
headers.forEach((header, i) => {
|
|
30
|
+
const found = findInSheet(texts[i] ?? '', params.query, Number.POSITIVE_INFINITY);
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
for (const match of found.matches) {
|
|
33
|
+
if (seen.has(match.rule))
|
|
34
|
+
continue;
|
|
35
|
+
seen.add(match.rule);
|
|
36
|
+
total++;
|
|
37
|
+
if (matches.length >= limit)
|
|
38
|
+
continue;
|
|
39
|
+
matches.push({
|
|
40
|
+
source: stylesheetPositionLabel(header, match.line, match.column),
|
|
41
|
+
text: match.rule,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return { query: params.query, sheets: headers.length, total, matches };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A stylesheet's text; empty when Chrome cannot give it.
|
|
49
|
+
*
|
|
50
|
+
* @param cdp - CDP connection
|
|
51
|
+
* @param styleSheetId - Stylesheet
|
|
52
|
+
* @returns Text
|
|
53
|
+
*/
|
|
54
|
+
async function sheetText(cdp, styleSheetId) {
|
|
55
|
+
try {
|
|
56
|
+
const response = (await cdp.send('CSS.getStyleSheetText', {
|
|
57
|
+
styleSheetId,
|
|
58
|
+
}));
|
|
59
|
+
return response.text;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
log.debug(`No text for stylesheet ${styleSheetId}: ${getErrorMessage(error)}`);
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Where a text occurs in a stylesheet (case-insensitive): how many times,
|
|
68
|
+
* and for the first `limit` matches the 0-based line and column and the
|
|
69
|
+
* rule around it (from the end of the previous rule to the end of this one,
|
|
70
|
+
* whitespace collapsed, at most {@link RULE_CONTEXT} characters each side).
|
|
71
|
+
* Lines are counted as the search moves on, so a big sheet is read once.
|
|
72
|
+
*
|
|
73
|
+
* @param text - Stylesheet text
|
|
74
|
+
* @param query - Text to find
|
|
75
|
+
* @param limit - Matches described at most
|
|
76
|
+
* @returns Match count and the described matches
|
|
77
|
+
*/
|
|
78
|
+
export function findInSheet(text, query, limit = Number.POSITIVE_INFINITY) {
|
|
79
|
+
const matches = [];
|
|
80
|
+
const haystack = text.toLowerCase();
|
|
81
|
+
const needle = query.toLowerCase();
|
|
82
|
+
if (needle === '')
|
|
83
|
+
return { total: 0, matches };
|
|
84
|
+
let total = 0;
|
|
85
|
+
let line = 0;
|
|
86
|
+
let lineStart = 0;
|
|
87
|
+
let scanned = 0;
|
|
88
|
+
for (let at = haystack.indexOf(needle); at >= 0; at = haystack.indexOf(needle, at + needle.length)) {
|
|
89
|
+
total++;
|
|
90
|
+
if (matches.length >= limit)
|
|
91
|
+
continue;
|
|
92
|
+
for (let i = text.indexOf('\n', scanned); i >= 0 && i < at; i = text.indexOf('\n', i + 1)) {
|
|
93
|
+
line++;
|
|
94
|
+
lineStart = i + 1;
|
|
95
|
+
}
|
|
96
|
+
scanned = at;
|
|
97
|
+
matches.push({ line, column: at - lineStart, rule: ruleAround(text, at) });
|
|
98
|
+
}
|
|
99
|
+
return { total, matches };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The rule around a position: whole when short, else its selector and the
|
|
103
|
+
* declarations near the position (`.btn { … color: var(--brand); … }`),
|
|
104
|
+
* whitespace collapsed.
|
|
105
|
+
*
|
|
106
|
+
* @param text - Stylesheet text
|
|
107
|
+
* @param at - Position of the match
|
|
108
|
+
* @returns Rule text
|
|
109
|
+
*/
|
|
110
|
+
function ruleAround(text, at) {
|
|
111
|
+
const ruleStart = text.lastIndexOf('}', at) + 1;
|
|
112
|
+
const open = text.indexOf('{', ruleStart);
|
|
113
|
+
const close = text.indexOf('}', at);
|
|
114
|
+
const ruleEnd = close < 0 ? text.length : close + 1;
|
|
115
|
+
const collapse = (part) => part.replace(/\s+/g, ' ').trim();
|
|
116
|
+
if (ruleEnd - ruleStart <= 2 * RULE_CONTEXT || open < 0 || open > at) {
|
|
117
|
+
return collapse(text.slice(Math.max(ruleStart, at - RULE_CONTEXT), Math.min(ruleEnd, at + RULE_CONTEXT)));
|
|
118
|
+
}
|
|
119
|
+
const near = collapse(text.slice(Math.max(open + 1, at - RULE_CONTEXT / 2), Math.min(ruleEnd - 1, at + RULE_CONTEXT / 2)));
|
|
120
|
+
return `${collapse(text.slice(ruleStart, open))} { … ${near} … }`;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -81,7 +81,10 @@ export interface OtherActivity {
|
|
|
81
81
|
* @param newDocument - Whether a new document loaded
|
|
82
82
|
* @returns New messages
|
|
83
83
|
*/
|
|
84
|
-
export declare function newMessages(before: SeenMessage[], after: SeenMessage[], newDocument: boolean):
|
|
84
|
+
export declare function newMessages(before: SeenMessage[], after: SeenMessage[], newDocument: boolean): {
|
|
85
|
+
messages: NewMessage[];
|
|
86
|
+
more: number;
|
|
87
|
+
};
|
|
85
88
|
/**
|
|
86
89
|
* Elements to report as shown: those whose text is not already reported as
|
|
87
90
|
* a new message, at most {@link MAX_SHOWN_ELEMENTS}, texts cut to
|
|
@@ -81,8 +81,6 @@ export function newMessages(before, after, newDocument) {
|
|
|
81
81
|
const reported = new Set();
|
|
82
82
|
const result = [];
|
|
83
83
|
for (const message of after) {
|
|
84
|
-
if (result.length >= MAX_NEW_MESSAGES)
|
|
85
|
-
break;
|
|
86
84
|
if (reported.has(message.text) || TICKING_TEXT.test(message.text))
|
|
87
85
|
continue;
|
|
88
86
|
if (!isNew(message))
|
|
@@ -90,7 +88,10 @@ export function newMessages(before, after, newDocument) {
|
|
|
90
88
|
reported.add(message.text);
|
|
91
89
|
result.push({ text: cutText(message.text), element: message.element });
|
|
92
90
|
}
|
|
93
|
-
return
|
|
91
|
+
return {
|
|
92
|
+
messages: result.slice(0, MAX_NEW_MESSAGES),
|
|
93
|
+
more: Math.max(0, result.length - MAX_NEW_MESSAGES),
|
|
94
|
+
};
|
|
94
95
|
}
|
|
95
96
|
/**
|
|
96
97
|
* Cut a text to {@link MAX_MESSAGE_LENGTH} characters, marking the cut.
|
|
@@ -333,11 +334,14 @@ function pageWork(watch, snapshot, domChanging = false) {
|
|
|
333
334
|
*/
|
|
334
335
|
function effectsOf(start, snapshot, events) {
|
|
335
336
|
const navigation = pageNavigation(start?.href, snapshot, events);
|
|
336
|
-
const messages = start && snapshot
|
|
337
|
+
const { messages, more } = start && snapshot
|
|
338
|
+
? newMessages(start.messages, snapshot.messages, snapshot.fresh)
|
|
339
|
+
: { messages: [], more: 0 };
|
|
337
340
|
const shown = shownElements(snapshot?.shown ?? [], messages);
|
|
338
341
|
return {
|
|
339
342
|
...(navigation && { navigation }),
|
|
340
343
|
...(messages.length > 0 && { messages }),
|
|
344
|
+
...(more > 0 && { moreMessages: more }),
|
|
341
345
|
...(shown.length > 0 && { shown }),
|
|
342
346
|
};
|
|
343
347
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bdg dom audit`: page-wide checks in one page walk
|
|
3
|
+
* ({@link AUDIT_PAGE_JS}), turned into findings by {@link buildAudit}.
|
|
4
|
+
*/
|
|
5
|
+
import type { CDPConnection } from '../../connection/cdp.js';
|
|
6
|
+
import type { AuditResult } from '../../ipc/protocol/auditTypes.js';
|
|
7
|
+
import type { DomAuditCommand } from '../../ipc/protocol/commands.js';
|
|
8
|
+
/** Findings listed per check without `--limit` */
|
|
9
|
+
export declare const DEFAULT_AUDIT_LIMIT = 20;
|
|
10
|
+
/**
|
|
11
|
+
* Run the page-wide checks.
|
|
12
|
+
*
|
|
13
|
+
* @param cdp - CDP connection
|
|
14
|
+
* @param params - Checks, WCAG level and limit
|
|
15
|
+
* @returns Findings
|
|
16
|
+
* @throws CommandError (91) when the page script fails
|
|
17
|
+
*/
|
|
18
|
+
export declare function auditPage(cdp: CDPConnection, params: DomAuditCommand): Promise<AuditResult>;
|
|
19
|
+
//# sourceMappingURL=audit.d.ts.map
|