browser-debugger-cli 0.9.0 → 0.11.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.
Files changed (139) hide show
  1. package/.claude/skills/bdg/SKILL.md +268 -0
  2. package/README.md +15 -1
  3. package/dist/commands/dom/a11y.js +2 -1
  4. package/dist/commands/dom/formInteraction.js +56 -25
  5. package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
  6. package/dist/commands/dom/helpers/keyAttributes.js +54 -0
  7. package/dist/commands/dom/helpers/query.d.ts +1 -1
  8. package/dist/commands/dom/helpers/query.js +66 -19
  9. package/dist/commands/dom/helpers/runElementCommand.js +4 -3
  10. package/dist/commands/dom/helpers/screenshot.js +85 -12
  11. package/dist/commands/dom/index.d.ts +1 -0
  12. package/dist/commands/dom/index.js +8 -3
  13. package/dist/commands/dom/inspect.d.ts +15 -0
  14. package/dist/commands/dom/inspect.js +82 -0
  15. package/dist/commands/dom/layout.js +2 -2
  16. package/dist/commands/dom/listeners.js +2 -2
  17. package/dist/commands/dom/semanticUtils.d.ts +14 -1
  18. package/dist/commands/dom/semanticUtils.js +44 -3
  19. package/dist/commands/installSkill.d.ts +20 -0
  20. package/dist/commands/installSkill.js +87 -0
  21. package/dist/commands/network/list.js +13 -2
  22. package/dist/commands/optionBehaviors.js +48 -6
  23. package/dist/commands/page.d.ts +1 -1
  24. package/dist/commands/page.js +62 -3
  25. package/dist/commands/shared/commonOptions.d.ts +4 -0
  26. package/dist/commands/shared/commonOptions.js +9 -0
  27. package/dist/commands/shared/optionTypes.d.ts +21 -0
  28. package/dist/commands/shared/startHelpers.d.ts +66 -0
  29. package/dist/commands/shared/startHelpers.js +91 -10
  30. package/dist/commands/shared/validation.d.ts +11 -0
  31. package/dist/commands/shared/validation.js +16 -0
  32. package/dist/commands.js +3 -0
  33. package/dist/daemon/launcher.d.ts +8 -1
  34. package/dist/daemon/launcher.js +3 -1
  35. package/dist/daemon/session/Session.d.ts +7 -0
  36. package/dist/daemon/session/Session.js +23 -1
  37. package/dist/daemon/session/commandRegistry.d.ts +14 -1
  38. package/dist/daemon/session/commandRegistry.js +65 -9
  39. package/dist/daemon/session/interactions.d.ts +18 -5
  40. package/dist/daemon/session/interactions.js +22 -12
  41. package/dist/daemon.js +3565 -329
  42. package/dist/errors/messages.d.ts +85 -0
  43. package/dist/errors/messages.js +128 -1
  44. package/dist/index.js +2151 -960
  45. package/dist/ipc/client.d.ts +9 -0
  46. package/dist/ipc/client.js +13 -0
  47. package/dist/ipc/protocol/commands.d.ts +56 -1
  48. package/dist/ipc/protocol/commands.js +2 -0
  49. package/dist/ipc/protocol/domTypes.d.ts +35 -2
  50. package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
  51. package/dist/ipc/protocol/inspectTypes.js +10 -0
  52. package/dist/runtime/dom/actionEffects.d.ts +94 -15
  53. package/dist/runtime/dom/actionEffects.js +173 -27
  54. package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -14
  55. package/dist/runtime/dom/actionEffectsScripts.js +224 -32
  56. package/dist/runtime/dom/elementInfo.d.ts +26 -0
  57. package/dist/runtime/dom/elementInfo.js +65 -0
  58. package/dist/runtime/dom/eventListeners.js +14 -4
  59. package/dist/runtime/dom/formFillHelpers/fill.d.ts +3 -4
  60. package/dist/runtime/dom/formFillHelpers/fill.js +77 -28
  61. package/dist/runtime/dom/frameSelection.d.ts +11 -0
  62. package/dist/runtime/dom/frameSelection.js +20 -1
  63. package/dist/runtime/dom/frames.d.ts +38 -5
  64. package/dist/runtime/dom/frames.js +136 -21
  65. package/dist/runtime/dom/inspect.d.ts +28 -0
  66. package/dist/runtime/dom/inspect.js +557 -0
  67. package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
  68. package/dist/runtime/dom/inspectAllStyles.js +385 -0
  69. package/dist/runtime/dom/inspectCascade.d.ts +94 -0
  70. package/dist/runtime/dom/inspectCascade.js +371 -0
  71. package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
  72. package/dist/runtime/dom/inspectCascadeModel.js +232 -0
  73. package/dist/runtime/dom/inspectHints.d.ts +62 -0
  74. package/dist/runtime/dom/inspectHints.js +305 -0
  75. package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
  76. package/dist/runtime/dom/inspectLayoutModel.js +346 -0
  77. package/dist/runtime/dom/inspectModel.d.ts +74 -0
  78. package/dist/runtime/dom/inspectModel.js +184 -0
  79. package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
  80. package/dist/runtime/dom/inspectPaintModel.js +461 -0
  81. package/dist/runtime/dom/inspectRules.d.ts +37 -0
  82. package/dist/runtime/dom/inspectRules.js +101 -0
  83. package/dist/runtime/dom/inspectScripts.d.ts +132 -0
  84. package/dist/runtime/dom/inspectScripts.js +263 -0
  85. package/dist/runtime/dom/inspectTree.d.ts +40 -0
  86. package/dist/runtime/dom/inspectTree.js +134 -0
  87. package/dist/runtime/dom/inspectVariables.d.ts +33 -0
  88. package/dist/runtime/dom/inspectVariables.js +94 -0
  89. package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
  90. package/dist/runtime/dom/inspectWhyModel.js +134 -0
  91. package/dist/runtime/dom/layout.d.ts +5 -1
  92. package/dist/runtime/dom/layout.js +10 -3
  93. package/dist/runtime/dom/listenerPageScripts.d.ts +11 -5
  94. package/dist/runtime/dom/listenerPageScripts.js +95 -9
  95. package/dist/runtime/dom/listenerSummary.d.ts +4 -0
  96. package/dist/runtime/dom/listenerSummary.js +26 -9
  97. package/dist/runtime/dom/reactEventHelpers.d.ts +5 -0
  98. package/dist/runtime/dom/reactEventHelpers.js +12 -4
  99. package/dist/runtime/page/emulation.d.ts +20 -0
  100. package/dist/runtime/page/emulation.js +37 -0
  101. package/dist/telemetry/a11y.d.ts +10 -0
  102. package/dist/telemetry/a11y.js +78 -1
  103. package/dist/telemetry/console.d.ts +1 -0
  104. package/dist/telemetry/console.js +100 -5
  105. package/dist/telemetry/network.js +3 -1
  106. package/dist/types.d.ts +40 -0
  107. package/dist/ui/formatters/details.d.ts +8 -0
  108. package/dist/ui/formatters/details.js +59 -3
  109. package/dist/ui/formatters/dom.d.ts +2 -1
  110. package/dist/ui/formatters/dom.js +25 -9
  111. package/dist/ui/formatters/inspect.d.ts +39 -0
  112. package/dist/ui/formatters/inspect.js +596 -0
  113. package/dist/ui/formatters/installSkill.d.ts +11 -0
  114. package/dist/ui/formatters/installSkill.js +31 -0
  115. package/dist/ui/formatters/keyAttributes.d.ts +19 -0
  116. package/dist/ui/formatters/keyAttributes.js +84 -0
  117. package/dist/ui/formatters/layout.js +2 -2
  118. package/dist/ui/formatters/networkHeaders.d.ts +13 -0
  119. package/dist/ui/formatters/networkHeaders.js +23 -3
  120. package/dist/ui/formatters/networkList.d.ts +29 -1
  121. package/dist/ui/formatters/networkList.js +86 -20
  122. package/dist/ui/formatters/status.js +1 -1
  123. package/dist/ui/formatting.d.ts +9 -0
  124. package/dist/ui/formatting.js +6 -3
  125. package/dist/ui/messages/commands.d.ts +123 -7
  126. package/dist/ui/messages/commands.js +181 -10
  127. package/dist/ui/messages/networkMessages.d.ts +14 -0
  128. package/dist/ui/messages/networkMessages.js +18 -0
  129. package/dist/ui/messages/session.d.ts +14 -0
  130. package/dist/ui/messages/session.js +20 -0
  131. package/dist/utils/async.d.ts +9 -0
  132. package/dist/utils/async.js +17 -0
  133. package/dist/utils/color.d.ts +84 -0
  134. package/dist/utils/color.js +376 -0
  135. package/dist/utils/cssValues.d.ts +109 -0
  136. package/dist/utils/cssValues.js +236 -0
  137. package/dist/utils/selectorFilters.d.ts +12 -0
  138. package/dist/utils/selectorFilters.js +29 -0
  139. package/package.json +2 -1
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Which declaration sets a property of an element: the CSS cascade computed
3
+ * from `CSS.getMatchedStylesForNode` (Chrome reports the matching rules, not
4
+ * which declaration wins).
5
+ *
6
+ * The rules come in ascending precedence (user-agent first, then layered
7
+ * rules in layer order, then unlayered rules by specificity and source
8
+ * order), between the presentational attributes and the style attribute.
9
+ * The last normal declaration wins unless an `!important` one exists; among
10
+ * important ones the style attribute wins, then layered ones (earlier layers
11
+ * first), then unlayered ones. Shorthands are expanded into their longhands
12
+ * and logical properties mapped to physical ones (horizontal, left-to-right
13
+ * writing). With no declaration of its own, an inherited property comes from
14
+ * the nearest ancestor that declares it.
15
+ */
16
+ /**
17
+ * The four side longhands of a pattern.
18
+ *
19
+ * @param pattern - e.g. `border-{side}-width`
20
+ * @returns Top, right, bottom, left
21
+ */
22
+ function sideLonghands(pattern) {
23
+ return ['top', 'right', 'bottom', 'left'].map((side) => pattern.replace('{side}', side));
24
+ }
25
+ /** Longhands of common shorthands, for values with `var()` (CDP leaves those unexpanded) */
26
+ const SHORTHANDS = {
27
+ margin: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'],
28
+ padding: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'],
29
+ inset: ['top', 'right', 'bottom', 'left'],
30
+ gap: ['row-gap', 'column-gap'],
31
+ overflow: ['overflow-x', 'overflow-y'],
32
+ 'border-width': [
33
+ 'border-top-width',
34
+ 'border-right-width',
35
+ 'border-bottom-width',
36
+ 'border-left-width',
37
+ ],
38
+ 'border-style': [
39
+ 'border-top-style',
40
+ 'border-right-style',
41
+ 'border-bottom-style',
42
+ 'border-left-style',
43
+ ],
44
+ 'border-color': [
45
+ 'border-top-color',
46
+ 'border-right-color',
47
+ 'border-bottom-color',
48
+ 'border-left-color',
49
+ ],
50
+ 'border-radius': [
51
+ 'border-top-left-radius',
52
+ 'border-top-right-radius',
53
+ 'border-bottom-right-radius',
54
+ 'border-bottom-left-radius',
55
+ ],
56
+ 'margin-inline': ['margin-left', 'margin-right'],
57
+ 'margin-block': ['margin-top', 'margin-bottom'],
58
+ 'padding-inline': ['padding-left', 'padding-right'],
59
+ 'padding-block': ['padding-top', 'padding-bottom'],
60
+ 'inset-inline': ['left', 'right'],
61
+ 'inset-block': ['top', 'bottom'],
62
+ border: ['width', 'style', 'color'].flatMap((part) => sideLonghands('border-{side}-' + part)),
63
+ ...Object.fromEntries(['top', 'right', 'bottom', 'left'].map((side) => [
64
+ `border-${side}`,
65
+ ['width', 'style', 'color'].map((part) => `border-${side}-${part}`),
66
+ ])),
67
+ background: ['background-color', 'background-image'],
68
+ font: ['font-style', 'font-weight', 'font-size', 'line-height', 'font-family'],
69
+ flex: ['flex-grow', 'flex-shrink', 'flex-basis'],
70
+ 'place-items': ['align-items', 'justify-items'],
71
+ 'place-content': ['align-content', 'justify-content'],
72
+ 'place-self': ['align-self', 'justify-self'],
73
+ };
74
+ /** Physical longhand of a logical one (horizontal-tb, ltr) */
75
+ const LOGICAL_TO_PHYSICAL = {
76
+ 'inline-size': 'width',
77
+ 'block-size': 'height',
78
+ 'min-inline-size': 'min-width',
79
+ 'min-block-size': 'min-height',
80
+ 'max-inline-size': 'max-width',
81
+ 'max-block-size': 'max-height',
82
+ 'inset-block-start': 'top',
83
+ 'inset-block-end': 'bottom',
84
+ 'inset-inline-start': 'left',
85
+ 'inset-inline-end': 'right',
86
+ };
87
+ /** Sides of the logical box properties (block-start → top, …) */
88
+ const LOGICAL_SIDES = {
89
+ 'block-start': 'top',
90
+ 'block-end': 'bottom',
91
+ 'inline-start': 'left',
92
+ 'inline-end': 'right',
93
+ };
94
+ /** Properties that inherit (those `dom inspect` reports) */
95
+ const INHERITED = new Set([
96
+ 'color',
97
+ 'font-family',
98
+ 'font-size',
99
+ 'font-style',
100
+ 'font-weight',
101
+ 'font-variant',
102
+ 'font-feature-settings',
103
+ 'font-variation-settings',
104
+ 'line-height',
105
+ 'letter-spacing',
106
+ 'word-spacing',
107
+ 'text-align',
108
+ 'text-indent',
109
+ 'text-transform',
110
+ 'text-shadow',
111
+ 'white-space-collapse',
112
+ 'text-wrap-mode',
113
+ 'visibility',
114
+ 'cursor',
115
+ 'direction',
116
+ 'list-style-type',
117
+ 'list-style-position',
118
+ 'tab-size',
119
+ 'pointer-events',
120
+ 'user-select',
121
+ '-webkit-text-security',
122
+ ]);
123
+ /**
124
+ * The longhands of a shorthand.
125
+ *
126
+ * @param name - Property name
127
+ * @returns Its longhands, or undefined when it is not a known shorthand
128
+ */
129
+ export function shorthandLonghands(name) {
130
+ return Object.hasOwn(SHORTHANDS, name) ? SHORTHANDS[name] : undefined;
131
+ }
132
+ /**
133
+ * Whether a property inherits.
134
+ *
135
+ * @param property - Longhand
136
+ * @returns True for inherited properties (custom properties inherit)
137
+ */
138
+ export function isInherited(property) {
139
+ return INHERITED.has(property) || property.startsWith('--');
140
+ }
141
+ /**
142
+ * The physical longhand a logical property sets.
143
+ *
144
+ * @param name - Property as written
145
+ * @returns Physical name, or the name itself
146
+ */
147
+ export function physicalName(name) {
148
+ const direct = LOGICAL_TO_PHYSICAL[name];
149
+ if (direct)
150
+ return direct;
151
+ const match = /^(margin|padding|border|scroll-margin|scroll-padding)-(block-start|block-end|inline-start|inline-end)(-(width|style|color))?$/.exec(name);
152
+ if (!match)
153
+ return name;
154
+ const side = LOGICAL_SIDES[match[2] ?? ''] ?? '';
155
+ return `${match[1]}-${side}${match[3] ?? ''}`;
156
+ }
157
+ /**
158
+ * The longhands one written property sets, with the value each gets.
159
+ *
160
+ * @param property - CDP property (with `longhandProperties` when Chrome expanded it)
161
+ * @returns Longhand names and values
162
+ */
163
+ function longhandsOf(property) {
164
+ if (property.longhandProperties && property.longhandProperties.length > 0) {
165
+ return property.longhandProperties.map((longhand) => ({
166
+ name: physicalName(longhand.name),
167
+ value: longhand.value,
168
+ }));
169
+ }
170
+ const expanded = SHORTHANDS[property.name];
171
+ if (expanded)
172
+ return expanded.map((name) => ({ name, value: property.value }));
173
+ return [{ name: physicalName(property.name), value: property.value }];
174
+ }
175
+ /**
176
+ * The declarations a CSS style block sets, longhand by longhand. Only
177
+ * declarations as written count (CDP adds unranged duplicates); disabled
178
+ * and invalid ones do not. A shorthand with `var()` gives each longhand the
179
+ * value as written: Chrome cannot expand it before substitution and reports
180
+ * the initial values (`currentcolor` for `border: 2px solid var(--c)`).
181
+ *
182
+ * @param style - CDP style
183
+ * @param source - Where the block comes from
184
+ * @returns Declarations
185
+ */
186
+ function declarationsOf(style, source) {
187
+ if (!style)
188
+ return [];
189
+ return style.cssProperties
190
+ .filter((property) => property.range !== undefined ||
191
+ source.origin === 'user-agent' ||
192
+ source.kind === 'attribute')
193
+ .filter((property) => property.disabled !== true && property.parsedOk !== false)
194
+ .flatMap((property) => {
195
+ const written = property.value.replace(/\s*!important\s*$/, '');
196
+ return longhandsOf(property).map(({ name, value }) => ({
197
+ property: name,
198
+ value: property.longhandProperties && value !== '' && !written.includes('var(')
199
+ ? value
200
+ : written,
201
+ ...(name !== property.name && { via: property.name, written }),
202
+ important: property.important === true,
203
+ source: {
204
+ ...source,
205
+ ...(property.range && {
206
+ line: property.range.startLine,
207
+ column: property.range.startColumn,
208
+ }),
209
+ },
210
+ }));
211
+ });
212
+ }
213
+ /**
214
+ * Source of a matched rule: its matching selector, origin, stylesheet,
215
+ * layer (nested ones as `outer.inner`), the media or container condition
216
+ * it is under (`not all and (…)`, as Chrome writes `not (…)`, shortened) and
217
+ * the selector's specificity.
218
+ *
219
+ * @param match - CDP rule match
220
+ * @returns Declaration source
221
+ */
222
+ function ruleSource(match) {
223
+ const { rule } = match;
224
+ const matching = rule.selectorList.selectors[match.matchingSelectors[0] ?? 0];
225
+ const selector = matching?.text ?? rule.selectorList.text;
226
+ const layer = rule.layers?.map((l) => l.text || '(anonymous)').join('.');
227
+ const condition = [...(rule.media ?? []), ...(rule.containerQueries ?? [])]
228
+ .map((c) => c.text.replace(/^not all and /, 'not '))
229
+ .join(' and ');
230
+ const specificity = matching?.specificity;
231
+ return {
232
+ kind: 'rule',
233
+ selector,
234
+ origin: rule.origin,
235
+ ...(specificity && { specificity: [specificity.a, specificity.b, specificity.c] }),
236
+ ...(rule.styleSheetId && { styleSheetId: rule.styleSheetId }),
237
+ ...(layer && { layer }),
238
+ ...(condition && { condition }),
239
+ };
240
+ }
241
+ /**
242
+ * The declarations of one element in ascending cascade order.
243
+ *
244
+ * @param entry - Inline style, presentational attributes and matched rules
245
+ * @returns Declarations, lowest precedence first
246
+ */
247
+ function orderedDeclarations(entry) {
248
+ return [
249
+ ...declarationsOf(entry.attributesStyle, { kind: 'attribute' }),
250
+ ...(entry.matchedCSSRules ?? []).flatMap((match) => declarationsOf(match.rule.style, ruleSource(match))),
251
+ ...declarationsOf(entry.inlineStyle, { kind: 'inline' }),
252
+ ];
253
+ }
254
+ /**
255
+ * Whether a declaration comes from the page's authors (not the browser's
256
+ * own stylesheet or an extension's).
257
+ *
258
+ * @param declaration - Declaration
259
+ * @returns True for author declarations
260
+ */
261
+ function isAuthor(declaration) {
262
+ const { origin } = declaration.source;
263
+ return origin === undefined || origin === 'regular' || origin === 'inspector';
264
+ }
265
+ /**
266
+ * Precedence of a declaration within one element's list. Normal: author over
267
+ * browser; then the style attribute; then by layer (presentational
268
+ * attributes below every layer, unlayered rules above); then by position.
269
+ * Important: browser over author; the style attribute; layers reversed
270
+ * (unlayered lowest); position.
271
+ *
272
+ * @param declaration - Declaration
273
+ * @param position - Its index in ascending order
274
+ * @param layers - Layer names in the order they first appear
275
+ * @returns Comparable tuple, higher wins
276
+ */
277
+ function precedence(declaration, position, layers) {
278
+ const { layer, kind } = declaration.source;
279
+ const layerIndex = layer === undefined ? -1 : layers.indexOf(layer);
280
+ const author = isAuthor(declaration) ? 1 : 0;
281
+ const inline = kind === 'inline' ? 1 : 0;
282
+ if (!declaration.important) {
283
+ const layerRank = kind === 'attribute' ? -1 : layer === undefined ? layers.length : layerIndex;
284
+ return [0, author, inline, layerRank, position];
285
+ }
286
+ return [1, 1 - author, inline, layer === undefined ? -1 : layers.length - layerIndex, position];
287
+ }
288
+ /**
289
+ * Compare two precedence tuples.
290
+ *
291
+ * @param a - Tuple
292
+ * @param b - Tuple
293
+ * @returns Positive when a wins
294
+ */
295
+ function compare(a, b) {
296
+ for (let i = 0; i < a.length; i++) {
297
+ const diff = (a[i] ?? 0) - (b[i] ?? 0);
298
+ if (diff !== 0)
299
+ return diff;
300
+ }
301
+ return 0;
302
+ }
303
+ /**
304
+ * Sort one element's declarations of a property, winner first.
305
+ *
306
+ * @param declarations - Declarations in ascending order
307
+ * @returns Highest precedence first
308
+ */
309
+ function byPrecedence(declarations) {
310
+ const layers = [
311
+ ...new Set(declarations.flatMap((d) => (d.source.layer ? [d.source.layer] : []))),
312
+ ];
313
+ return declarations
314
+ .map((declaration, position) => ({
315
+ declaration,
316
+ key: precedence(declaration, position, layers),
317
+ }))
318
+ .sort((a, b) => compare(b.key, a.key))
319
+ .map((entry) => entry.declaration);
320
+ }
321
+ /**
322
+ * The longhands the element's own declarations set (what hints check).
323
+ *
324
+ * @param matched - `CSS.getMatchedStylesForNode` response
325
+ * @returns Longhands
326
+ */
327
+ export function ownProperties(matched) {
328
+ return [...new Set(orderedDeclarations(matched).map((declaration) => declaration.property))];
329
+ }
330
+ /**
331
+ * Resolve the cascade of the given properties for an element.
332
+ *
333
+ * @param matched - `CSS.getMatchedStylesForNode` response
334
+ * @param properties - Longhands to resolve
335
+ * @returns Resolution per property
336
+ */
337
+ export function resolveCascade(matched, properties) {
338
+ const own = orderedDeclarations(matched);
339
+ const ancestors = (matched.inherited ?? []).map(orderedDeclarations);
340
+ const resolved = new Map();
341
+ for (const property of properties) {
342
+ const ranked = byPrecedence(own.filter((d) => d.property === property));
343
+ const [winner, ...overridden] = ranked;
344
+ if (winner) {
345
+ resolved.set(property, { winner, overridden });
346
+ continue;
347
+ }
348
+ resolved.set(property, inheritedWinner(property, ancestors));
349
+ }
350
+ return resolved;
351
+ }
352
+ /**
353
+ * The nearest ancestor's winning declaration of an inherited property.
354
+ *
355
+ * @param property - Longhand
356
+ * @param ancestors - Declarations of each ancestor, nearest first
357
+ * @returns The winner and the declarations it beat on that ancestor (none for
358
+ * non-inherited properties or when no ancestor sets it)
359
+ */
360
+ function inheritedWinner(property, ancestors) {
361
+ if (!isInherited(property))
362
+ return { overridden: [] };
363
+ for (const [depth, declarations] of ancestors.entries()) {
364
+ const ranked = byPrecedence(declarations.filter((d) => d.property === property)).map((declaration) => ({ ...declaration, ancestor: depth + 1 }));
365
+ const [winner, ...overridden] = ranked;
366
+ if (winner)
367
+ return { winner, overridden };
368
+ }
369
+ return { overridden: [] };
370
+ }
371
+ //# sourceMappingURL=inspectCascade.js.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `dom inspect`'s cascade fields from the matched rules: hints (declarations
3
+ * that have no effect), `--rules` (which declaration sets each shown
4
+ * property, sides set by one declaration merged into the shorthand) and
5
+ * `--why` (every declaration of one property).
6
+ */
7
+ import type { Protocol } from '../../connection/typed-cdp.js';
8
+ import type { InspectResult } from '../../ipc/protocol/inspectTypes.js';
9
+ import { type Declaration } from './inspectCascade.js';
10
+ import type { StyleMap } from './inspectLayoutModel.js';
11
+ /** Longhands reported together, so a shorthand can stand for its sides */
12
+ export interface PropertyGroup {
13
+ shorthand?: string;
14
+ longhands: string[];
15
+ }
16
+ /** What the cascade fields are built from */
17
+ export interface CascadeInput {
18
+ matched: Protocol.CSS.GetMatchedStylesForNodeResponse;
19
+ style: StyleMap;
20
+ parentStyle: StyleMap | undefined;
21
+ replaced: boolean;
22
+ /** Where a declaration comes from, for people */
23
+ label: (declaration: Declaration) => string;
24
+ rules?: boolean;
25
+ why?: string;
26
+ /** `--props` names: `--rules` then covers only these */
27
+ props?: string[];
28
+ /** Check for declarations that have no effect (default true) */
29
+ hints?: boolean;
30
+ formControl?: boolean;
31
+ }
32
+ /**
33
+ * The cascade fields of the result.
34
+ *
35
+ * @param input - Matched rules, computed styles, options
36
+ * @returns `hints` (empty when checked and nothing found), `rules` and `why`
37
+ */
38
+ export declare function buildCascadeFields(input: CascadeInput): Partial<InspectResult>;
39
+ //# sourceMappingURL=inspectCascadeModel.d.ts.map
@@ -0,0 +1,232 @@
1
+ /**
2
+ * `dom inspect`'s cascade fields from the matched rules: hints (declarations
3
+ * that have no effect), `--rules` (which declaration sets each shown
4
+ * property, sides set by one declaration merged into the shorthand) and
5
+ * `--why` (every declaration of one property).
6
+ */
7
+ import { normalizeProperty } from './inspectAllStyles.js';
8
+ import { physicalName, ownProperties, resolveCascade, shorthandLonghands, } from './inspectCascade.js';
9
+ import { formControlFontHints, inactiveHints, undefinedVariableHints, } from './inspectHints.js';
10
+ import { buildWhy } from './inspectWhyModel.js';
11
+ const SIDES = ['top', 'right', 'bottom', 'left'];
12
+ const sides = (pattern) => SIDES.map((side) => pattern.replace('{side}', side));
13
+ /** Properties `--rules` reports */
14
+ const RULE_GROUPS = [
15
+ { longhands: ['display'] },
16
+ { longhands: ['position'] },
17
+ { shorthand: 'inset', longhands: sides('{side}') },
18
+ { longhands: ['z-index'] },
19
+ { longhands: ['width'] },
20
+ { longhands: ['height'] },
21
+ { longhands: ['min-width'] },
22
+ { longhands: ['max-width'] },
23
+ { longhands: ['min-height'] },
24
+ { longhands: ['max-height'] },
25
+ { shorthand: 'margin', longhands: sides('margin-{side}') },
26
+ { shorthand: 'padding', longhands: sides('padding-{side}') },
27
+ { shorthand: 'border-width', longhands: sides('border-{side}-width') },
28
+ { shorthand: 'border-style', longhands: sides('border-{side}-style') },
29
+ { shorthand: 'border-color', longhands: sides('border-{side}-color') },
30
+ {
31
+ shorthand: 'border-radius',
32
+ longhands: ['top-left', 'top-right', 'bottom-right', 'bottom-left'].map((c) => `border-${c}-radius`),
33
+ },
34
+ { longhands: ['background-color'] },
35
+ { longhands: ['background-image'] },
36
+ { longhands: ['color'] },
37
+ { longhands: ['font-family'] },
38
+ { longhands: ['font-size'] },
39
+ { longhands: ['font-weight'] },
40
+ { longhands: ['line-height'] },
41
+ { longhands: ['letter-spacing'] },
42
+ { longhands: ['text-align'] },
43
+ { longhands: ['text-transform'] },
44
+ { longhands: ['opacity'] },
45
+ { longhands: ['box-shadow'] },
46
+ { longhands: ['transform'] },
47
+ { longhands: ['flex-direction'] },
48
+ { longhands: ['flex-wrap'] },
49
+ { longhands: ['justify-content'] },
50
+ { longhands: ['align-items'] },
51
+ { shorthand: 'gap', longhands: ['row-gap', 'column-gap'] },
52
+ { longhands: ['grid-template-columns'] },
53
+ { longhands: ['grid-template-rows'] },
54
+ { shorthand: 'flex', longhands: ['flex-grow', 'flex-shrink', 'flex-basis'] },
55
+ { longhands: ['align-self'] },
56
+ { shorthand: 'overflow', longhands: ['overflow-x', 'overflow-y'] },
57
+ { longhands: ['cursor'] },
58
+ ];
59
+ /**
60
+ * The cascade fields of the result.
61
+ *
62
+ * @param input - Matched rules, computed styles, options
63
+ * @returns `hints` (empty when checked and nothing found), `rules` and `why`
64
+ */
65
+ export function buildCascadeFields(input) {
66
+ const ruleGroups = input.rules ? (input.props?.map(propertyGroup) ?? RULE_GROUPS) : [];
67
+ const whyGroup = input.why ? propertyGroup(input.why) : undefined;
68
+ const wanted = [
69
+ ...new Set([
70
+ ...ownProperties(input.matched),
71
+ ...ruleGroups.flatMap((group) => group.longhands),
72
+ ...(whyGroup?.longhands ?? []),
73
+ ]),
74
+ ];
75
+ const cascade = resolveCascade(input.matched, wanted);
76
+ const hints = input.hints === false ? undefined : buildHints(cascade, input);
77
+ const rules = buildRules(ruleGroups, cascade, input);
78
+ return {
79
+ ...(hints && { hints }),
80
+ ...(rules.length > 0 && { rules }),
81
+ ...(whyGroup && { why: buildWhy(whyGroup, cascade, input) }),
82
+ };
83
+ }
84
+ /**
85
+ * The longhands a property name stands for.
86
+ *
87
+ * @param name - Shorthand, longhand, logical or custom property
88
+ * @returns Group (a shorthand with its longhands, or one physical longhand)
89
+ */
90
+ function propertyGroup(name) {
91
+ const longhands = shorthandLonghands(name);
92
+ return longhands
93
+ ? { shorthand: name, longhands: [...longhands] }
94
+ : { longhands: [physicalName(name)] };
95
+ }
96
+ /**
97
+ * Hints: declarations with no effect, and var() of unset custom properties;
98
+ * none without the computed style to check them against.
99
+ *
100
+ * @param cascade - Resolved properties
101
+ * @param input - Styles and the label function
102
+ * @returns Hints
103
+ */
104
+ function buildHints(cascade, input) {
105
+ if (input.style['display'] === undefined)
106
+ return [];
107
+ const ctx = {
108
+ style: input.style,
109
+ parentStyle: input.parentStyle,
110
+ replaced: input.replaced,
111
+ formControl: input.formControl === true,
112
+ };
113
+ return [
114
+ ...inactiveHints(cascade, ctx),
115
+ ...undefinedVariableHints(cascade, input.style),
116
+ ...formControlFontHints(cascade, ctx),
117
+ ].map((hint) => ({
118
+ kind: hint.kind,
119
+ property: hint.property,
120
+ value: hint.value,
121
+ reason: hint.reason,
122
+ fix: hint.fix,
123
+ source: input.label(hint.declaration),
124
+ }));
125
+ }
126
+ /**
127
+ * `--rules`: one row per property set by an author declaration; one row for
128
+ * a group (`padding`, `border-width`) when a single declaration sets all of
129
+ * it, and one for a shorthand Chrome could not expand (`var()`, such as
130
+ * `border: 2px solid var(--c)` or `background: var(--bg)`) however many
131
+ * properties it sets.
132
+ *
133
+ * @param groups - Properties to report
134
+ * @param cascade - Resolved properties
135
+ * @param input - Computed style and the label function
136
+ * @returns Rules
137
+ */
138
+ function buildRules(groups, cascade, input) {
139
+ const rows = groups.flatMap((group) => {
140
+ const resolutions = group.longhands.map((longhand) => cascade.get(longhand));
141
+ const winners = resolutions.map((resolution) => resolution?.winner);
142
+ const grouped = group.shorthand !== undefined ? groupedRow(group.shorthand, winners) : undefined;
143
+ if (grouped)
144
+ return [toRule(grouped.property, grouped, resolutions[0], input)];
145
+ return group.longhands.flatMap((longhand, i) => {
146
+ const winner = winners[i];
147
+ if (!winner || winner.source.origin === 'user-agent')
148
+ return [];
149
+ const unexpanded = winner.via !== undefined && winner.value === winner.written;
150
+ return unexpanded && winner.via
151
+ ? [toRule(winner.via, winner, resolutions[i], input)]
152
+ : [toRule(longhand, winner, resolutions[i], input)];
153
+ });
154
+ });
155
+ const seen = new Set();
156
+ return rows.filter((row) => {
157
+ const key = `${row.property}|${row.value}|${row.source}`;
158
+ if (seen.has(key))
159
+ return false;
160
+ seen.add(key);
161
+ return true;
162
+ });
163
+ }
164
+ /**
165
+ * One row for a group of longhands that a single author declaration sets:
166
+ * the shorthand as written, or (set by a wider shorthand, like `border`
167
+ * for `border-width`) the values it gives the sides; a wider shorthand
168
+ * Chrome could not expand (`var()`) as written.
169
+ *
170
+ * @param shorthand - The group's shorthand
171
+ * @param winners - Winning declaration of each longhand
172
+ * @returns The row's declaration, or undefined when the sides differ in source
173
+ */
174
+ function groupedRow(shorthand, winners) {
175
+ const [first] = winners;
176
+ if (!first?.via || first.source.origin === 'user-agent')
177
+ return undefined;
178
+ const same = winners.every((w) => w && w.via === first.via && sameSource(w, first));
179
+ if (!same)
180
+ return undefined;
181
+ const written = first.written ?? first.value;
182
+ if (first.via === shorthand || first.value === written) {
183
+ return { ...first, property: first.via, value: written };
184
+ }
185
+ const values = winners.map((w) => w?.value ?? '');
186
+ const value = values.every((v) => v === first.value) ? first.value : values.join(' ');
187
+ return { ...first, property: shorthand, value };
188
+ }
189
+ /**
190
+ * Whether two declarations come from the same place.
191
+ *
192
+ * @param a - Declaration
193
+ * @param b - Declaration
194
+ * @returns True for the same declaration (same rule and position)
195
+ */
196
+ function sameSource(a, b) {
197
+ return (a.source.kind === b.source.kind &&
198
+ a.source.selector === b.source.selector &&
199
+ a.source.styleSheetId === b.source.styleSheetId &&
200
+ a.source.line === b.source.line &&
201
+ a.source.column === b.source.column);
202
+ }
203
+ /**
204
+ * A `--rules` row.
205
+ *
206
+ * @param property - Property shown
207
+ * @param winner - Winning declaration
208
+ * @param resolution - Its resolution (for what it overrides)
209
+ * @param input - Computed style and the label function
210
+ * @returns Rule
211
+ */
212
+ function toRule(property, winner, resolution, input) {
213
+ const computed = input.style[property];
214
+ const overrides = [
215
+ ...new Set((resolution?.overridden ?? [])
216
+ .filter((d) => d.source.origin !== 'user-agent')
217
+ .map((d) => d.source.selector ?? d.source.kind)),
218
+ ];
219
+ return {
220
+ property,
221
+ value: winner.value,
222
+ ...(winner.value.includes('var(') &&
223
+ computed !== undefined && { computed: normalizeProperty(property, computed) }),
224
+ source: input.label(winner),
225
+ ...(overrides.length > 0 && { overrides }),
226
+ ...(winner.ancestor !== undefined && { inherited: winner.ancestor }),
227
+ ...(winner.important && { important: true }),
228
+ ...(winner.source.layer && { layer: winner.source.layer }),
229
+ ...(winner.source.condition && { condition: winner.source.condition }),
230
+ };
231
+ }
232
+ //# sourceMappingURL=inspectCascadeModel.js.map