browser-debugger-cli 0.12.0 → 0.14.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 (224) hide show
  1. package/.claude/skills/bdg/SKILL.md +100 -186
  2. package/README.md +5 -4
  3. package/dist/commands/cdp.d.ts +22 -1
  4. package/dist/commands/cdp.js +100 -43
  5. package/dist/commands/console.d.ts +12 -0
  6. package/dist/commands/console.js +67 -13
  7. package/dist/commands/dom/DomElementResolver.d.ts +3 -1
  8. package/dist/commands/dom/DomElementResolver.js +10 -3
  9. package/dist/commands/dom/a11y.d.ts +1 -1
  10. package/dist/commands/dom/a11y.js +23 -22
  11. package/dist/commands/dom/eval.d.ts +4 -2
  12. package/dist/commands/dom/eval.js +31 -7
  13. package/dist/commands/dom/form.js +10 -9
  14. package/dist/commands/dom/formInteraction.js +9 -8
  15. package/dist/commands/dom/get.js +32 -14
  16. package/dist/commands/dom/helpers/index.d.ts +1 -1
  17. package/dist/commands/dom/helpers/index.js +1 -1
  18. package/dist/commands/dom/helpers/query.d.ts +27 -3
  19. package/dist/commands/dom/helpers/query.js +152 -64
  20. package/dist/commands/dom/helpers/screenshot.js +13 -13
  21. package/dist/commands/dom/index.js +10 -3
  22. package/dist/commands/dom/query.d.ts +20 -2
  23. package/dist/commands/dom/query.js +39 -6
  24. package/dist/commands/dom/screenshot.js +3 -1
  25. package/dist/commands/dom/semanticUtils.d.ts +3 -2
  26. package/dist/commands/dom/semanticUtils.js +40 -9
  27. package/dist/commands/helpJson.d.ts +82 -19
  28. package/dist/commands/helpJson.js +112 -41
  29. package/dist/commands/helpTopic.d.ts +16 -1
  30. package/dist/commands/helpTopic.js +59 -1
  31. package/dist/commands/installSkill.d.ts +15 -5
  32. package/dist/commands/installSkill.js +86 -16
  33. package/dist/commands/network/list.js +65 -12
  34. package/dist/commands/optionBehaviors.d.ts +25 -2
  35. package/dist/commands/optionBehaviors.js +81 -46
  36. package/dist/commands/peek.js +3 -0
  37. package/dist/commands/shared/CommandRunner.js +13 -13
  38. package/dist/commands/shared/daemonErrorHandler.d.ts +5 -2
  39. package/dist/commands/shared/daemonErrorHandler.js +21 -10
  40. package/dist/commands/shared/dataFetcher.d.ts +14 -4
  41. package/dist/commands/shared/dataFetcher.js +20 -4
  42. package/dist/commands/shared/followMode.d.ts +9 -1
  43. package/dist/commands/shared/followMode.js +22 -4
  44. package/dist/commands/shared/handleValidationError.js +3 -3
  45. package/dist/commands/shared/optionTypes.d.ts +17 -3
  46. package/dist/commands/shared/outputFile.js +6 -1
  47. package/dist/commands/shared/startHelpers.js +3 -3
  48. package/dist/commands/start.d.ts +7 -5
  49. package/dist/commands/start.js +65 -21
  50. package/dist/commands/stop.d.ts +11 -0
  51. package/dist/commands/stop.js +24 -1
  52. package/dist/commands.js +1 -1
  53. package/dist/connection/cdp.d.ts +7 -0
  54. package/dist/connection/cdp.js +9 -0
  55. package/dist/connection/chromeIdentity.d.ts +8 -2
  56. package/dist/connection/chromeIdentity.js +85 -13
  57. package/dist/connection/launcher.js +3 -2
  58. package/dist/constants.d.ts +29 -1
  59. package/dist/constants.js +35 -1
  60. package/dist/daemon/SessionController.js +8 -1
  61. package/dist/daemon/launcher.d.ts +3 -2
  62. package/dist/daemon/launcher.js +47 -3
  63. package/dist/daemon/session/Session.d.ts +5 -1
  64. package/dist/daemon/session/Session.js +42 -3
  65. package/dist/daemon/session/TelemetryStore.d.ts +15 -1
  66. package/dist/daemon/session/TelemetryStore.js +19 -1
  67. package/dist/daemon/session/commandRegistry.js +52 -18
  68. package/dist/daemon/session/interactions.d.ts +2 -1
  69. package/dist/daemon/session/interactions.js +13 -1
  70. package/dist/daemon/session/matchedStylesReset.d.ts +26 -0
  71. package/dist/daemon/session/matchedStylesReset.js +46 -0
  72. package/dist/daemon/session/plugins.js +17 -2
  73. package/dist/daemon/session/teardown.js +1 -1
  74. package/dist/daemon/session/triggeredRequests.d.ts +0 -5
  75. package/dist/daemon/session/triggeredRequests.js +13 -7
  76. package/dist/daemon.js +2385 -1229
  77. package/dist/errors/messages.d.ts +62 -11
  78. package/dist/errors/messages.js +119 -22
  79. package/dist/index.js +14995 -9866
  80. package/dist/ipc/client.d.ts +18 -2
  81. package/dist/ipc/client.js +26 -5
  82. package/dist/ipc/protocol/auditTypes.d.ts +8 -2
  83. package/dist/ipc/protocol/commands.d.ts +16 -0
  84. package/dist/ipc/protocol/domTypes.d.ts +12 -0
  85. package/dist/ipc/protocol/inspectTypes.d.ts +7 -2
  86. package/dist/ipc/session/types.d.ts +7 -1
  87. package/dist/program.d.ts +14 -0
  88. package/dist/program.js +53 -0
  89. package/dist/runtime/dom/actionEffects.d.ts +5 -1
  90. package/dist/runtime/dom/actionEffects.js +26 -14
  91. package/dist/runtime/dom/audit.js +3 -2
  92. package/dist/runtime/dom/auditModel.js +6 -1
  93. package/dist/runtime/dom/auditScripts.d.ts +9 -3
  94. package/dist/runtime/dom/auditScripts.js +41 -5
  95. package/dist/runtime/dom/elementGeometry.d.ts +33 -3
  96. package/dist/runtime/dom/elementGeometry.js +44 -19
  97. package/dist/runtime/dom/elementInfo.d.ts +76 -18
  98. package/dist/runtime/dom/elementInfo.js +190 -40
  99. package/dist/runtime/dom/evalHelpers.d.ts +12 -2
  100. package/dist/runtime/dom/evalHelpers.js +67 -7
  101. package/dist/runtime/dom/formDiscovery.d.ts +6 -2
  102. package/dist/runtime/dom/formDiscovery.js +20 -3
  103. package/dist/runtime/dom/formFillHelpers/fill.js +7 -11
  104. package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
  105. package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
  106. package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
  107. package/dist/runtime/dom/formSubmitHelpers.js +4 -3
  108. package/dist/runtime/dom/frameLayout.js +1 -0
  109. package/dist/runtime/dom/frameScopedConnection.d.ts +7 -0
  110. package/dist/runtime/dom/frameScopedConnection.js +2 -2
  111. package/dist/runtime/dom/inspect.d.ts +17 -3
  112. package/dist/runtime/dom/inspect.js +45 -32
  113. package/dist/runtime/dom/inspectAllStyles.js +1 -0
  114. package/dist/runtime/dom/inspectHints.d.ts +1 -1
  115. package/dist/runtime/dom/inspectModel.d.ts +5 -4
  116. package/dist/runtime/dom/inspectModel.js +7 -3
  117. package/dist/runtime/dom/inspectPaintModel.d.ts +2 -0
  118. package/dist/runtime/dom/inspectPaintModel.js +3 -1
  119. package/dist/runtime/dom/inspectRules.d.ts +29 -3
  120. package/dist/runtime/dom/inspectRules.js +205 -11
  121. package/dist/runtime/dom/inspectScripts.d.ts +29 -2
  122. package/dist/runtime/dom/inspectScripts.js +49 -10
  123. package/dist/runtime/dom/layout.d.ts +0 -2
  124. package/dist/runtime/dom/layout.js +10 -9
  125. package/dist/runtime/dom/reactEventHelpers.d.ts +17 -4
  126. package/dist/runtime/dom/reactEventHelpers.js +71 -28
  127. package/dist/runtime/dom/targetNode.d.ts +27 -10
  128. package/dist/runtime/dom/targetNode.js +283 -16
  129. package/dist/runtime/dom/wait.js +2 -1
  130. package/dist/runtime/page/bdgWorld.d.ts +57 -0
  131. package/dist/runtime/page/bdgWorld.js +180 -0
  132. package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
  133. package/dist/runtime/page/replacedBuiltins.js +136 -0
  134. package/dist/session/QueryCacheManager.d.ts +4 -1
  135. package/dist/session/QueryCacheManager.js +5 -2
  136. package/dist/session/chrome.d.ts +4 -1
  137. package/dist/session/chrome.js +7 -1
  138. package/dist/session/cleanup/staleSession.d.ts +21 -4
  139. package/dist/session/cleanup/staleSession.js +79 -9
  140. package/dist/session/cleanup/userCommands.d.ts +4 -1
  141. package/dist/session/cleanup/userCommands.js +10 -5
  142. package/dist/session/daemonSocket.d.ts +10 -0
  143. package/dist/session/daemonSocket.js +22 -0
  144. package/dist/session/lastSession.d.ts +6 -3
  145. package/dist/session/lastSession.js +11 -5
  146. package/dist/session/paths.d.ts +3 -1
  147. package/dist/session/paths.js +5 -5
  148. package/dist/session/portClaims.js +4 -3
  149. package/dist/session/sessionList.d.ts +13 -5
  150. package/dist/session/sessionList.js +31 -7
  151. package/dist/telemetry/a11y.d.ts +15 -1
  152. package/dist/telemetry/a11y.js +85 -2
  153. package/dist/telemetry/console.d.ts +2 -1
  154. package/dist/telemetry/console.js +30 -21
  155. package/dist/telemetry/har/builder.js +1 -1
  156. package/dist/telemetry/network.d.ts +13 -16
  157. package/dist/telemetry/network.js +30 -52
  158. package/dist/telemetry/networkRetention.d.ts +83 -0
  159. package/dist/telemetry/networkRetention.js +117 -0
  160. package/dist/telemetry/pageCrash.d.ts +26 -0
  161. package/dist/telemetry/pageCrash.js +53 -0
  162. package/dist/types.d.ts +42 -0
  163. package/dist/ui/OutputBuilder.d.ts +10 -0
  164. package/dist/ui/OutputBuilder.js +12 -0
  165. package/dist/ui/formatters/a11y.d.ts +5 -7
  166. package/dist/ui/formatters/a11y.js +7 -61
  167. package/dist/ui/formatters/audit.js +14 -5
  168. package/dist/ui/formatters/cdp.d.ts +138 -0
  169. package/dist/ui/formatters/cdp.js +131 -0
  170. package/dist/ui/formatters/console/chronological.js +7 -5
  171. package/dist/ui/formatters/console/follow.d.ts +5 -2
  172. package/dist/ui/formatters/console/follow.js +7 -4
  173. package/dist/ui/formatters/console/json.d.ts +4 -7
  174. package/dist/ui/formatters/console/json.js +16 -14
  175. package/dist/ui/formatters/console/shared.d.ts +47 -2
  176. package/dist/ui/formatters/console/shared.js +33 -0
  177. package/dist/ui/formatters/console/summarize.d.ts +9 -2
  178. package/dist/ui/formatters/console/summarize.js +57 -11
  179. package/dist/ui/formatters/console.d.ts +3 -2
  180. package/dist/ui/formatters/console.js +8 -10
  181. package/dist/ui/formatters/details.js +4 -2
  182. package/dist/ui/formatters/dom.d.ts +14 -5
  183. package/dist/ui/formatters/dom.js +30 -13
  184. package/dist/ui/formatters/helpFormatters.js +1 -1
  185. package/dist/ui/formatters/inspect.js +9 -3
  186. package/dist/ui/formatters/installSkill.d.ts +9 -1
  187. package/dist/ui/formatters/installSkill.js +32 -6
  188. package/dist/ui/formatters/layout.js +4 -2
  189. package/dist/ui/formatters/longValues.d.ts +14 -0
  190. package/dist/ui/formatters/longValues.js +23 -0
  191. package/dist/ui/formatters/networkList.d.ts +8 -2
  192. package/dist/ui/formatters/networkList.js +11 -3
  193. package/dist/ui/formatters/preview.d.ts +6 -1
  194. package/dist/ui/formatters/preview.js +67 -15
  195. package/dist/ui/formatters/sessions.d.ts +2 -2
  196. package/dist/ui/formatters/sessions.js +9 -2
  197. package/dist/ui/formatters/status.js +7 -0
  198. package/dist/ui/formatters/triggeredRequests.js +2 -1
  199. package/dist/ui/logging/logger.d.ts +1 -1
  200. package/dist/ui/messages/chrome.d.ts +20 -1
  201. package/dist/ui/messages/chrome.js +29 -3
  202. package/dist/ui/messages/commands.d.ts +153 -12
  203. package/dist/ui/messages/commands.js +198 -15
  204. package/dist/ui/messages/consoleMessages.d.ts +24 -0
  205. package/dist/ui/messages/consoleMessages.js +32 -0
  206. package/dist/ui/messages/networkMessages.d.ts +24 -0
  207. package/dist/ui/messages/networkMessages.js +45 -0
  208. package/dist/ui/messages/preview.d.ts +6 -0
  209. package/dist/ui/messages/preview.js +9 -1
  210. package/dist/ui/messages/session.d.ts +13 -2
  211. package/dist/ui/messages/session.js +22 -3
  212. package/dist/utils/directories.d.ts +34 -0
  213. package/dist/utils/directories.js +88 -0
  214. package/dist/utils/display.d.ts +16 -0
  215. package/dist/utils/display.js +42 -0
  216. package/dist/utils/exitCodes.d.ts +1 -0
  217. package/dist/utils/exitCodes.js +6 -0
  218. package/dist/utils/http.d.ts +9 -2
  219. package/dist/utils/http.js +4 -3
  220. package/dist/utils/process.d.ts +12 -0
  221. package/dist/utils/process.js +25 -0
  222. package/dist/utils/strings.d.ts +19 -0
  223. package/dist/utils/strings.js +16 -0
  224. package/package.json +2 -2
@@ -16,8 +16,9 @@ export interface ElementGeometry {
16
16
  rect: LayoutBox;
17
17
  /**
18
18
  * For a zero-size element whose children show (an inline list around
19
- * floated items): the box around those children, which is what can be
20
- * seen of it
19
+ * floated items, a `display: contents` web component whose shadow root
20
+ * shows a dialog): the box around those children in the flat tree, which
21
+ * is what can be seen of it
21
22
  */
22
23
  content?: LayoutBox | null;
23
24
  /** Area its iframes and overflow-clipping ancestors leave visible, in the same coordinates */
@@ -33,6 +34,11 @@ export interface ElementGeometry {
33
34
  * e.g. `opacity: 0 on div#menu`
34
35
  */
35
36
  invisible: string | null;
37
+ /**
38
+ * Masked by `mask-image` on it or an ancestor ({@link MASKED_BY_JS}), e.g.
39
+ * `mask-image on div.hero`: part or all of it may not show
40
+ */
41
+ masked?: string | null;
36
42
  /** Inside an `inert` element: shown, but not interactive */
37
43
  inert: boolean;
38
44
  /** Fixed to the top-level viewport (it or a container is `position: fixed`): page scroll does not move it */
@@ -113,6 +119,29 @@ export declare const FRAME_OFFSET_JS = "(frame) => {\n const rect = frame.getBo
113
119
  * its bounding box is the box itself, scaled.
114
120
  */
115
121
  export declare const SCALES_ONLY_JS = "(style) => {\n if (style.rotate && style.rotate !== 'none') return false;\n if (!style.transform || style.transform === 'none') return true;\n const matrix = /^matrix\\(([^)]*)\\)$/.exec(style.transform);\n if (!matrix) return false;\n const values = matrix[1].split(',').map(parseFloat);\n return Math.abs(values[1]) < 1e-6 && Math.abs(values[2]) < 1e-6;\n}";
122
+ /**
123
+ * Page-side clip of a node by its ancestors in the flat tree (through the
124
+ * slots it is shown in and open shadow roots): the padding boxes
125
+ * ({@link CLIP_BOX_JS}) of those that cut off overflowing content and hold
126
+ * the node in their containing-block chain. An absolutely positioned node
127
+ * skips static ancestors (that are not transformed) up to its containing
128
+ * block, a fixed one is not clipped at all unless a transformed (or
129
+ * filtered, contained, …) ancestor holds it like an absolute one, and
130
+ * inline ancestors and `display: contents` ones have no box to clip with.
131
+ * The root element is left out (its overflow belongs to the viewport), and
132
+ * so is the body's overflow unless the root element's overflow is not
133
+ * `visible` (then the body keeps its own overflow and, e.g. as the page's
134
+ * scroller, clips like any container).
135
+ * Returns the clip, whether overlay scrollbars of the containers show along
136
+ * its right and bottom edges ({@link OVERLAY_SCROLLBARS_JS}), the innermost
137
+ * ancestor cutting off part of `rect` (null when none does), why the
138
+ * innermost clipping ancestor with no area (a collapsed
139
+ * `height: 0; overflow: hidden` accordion) hides it, e.g.
140
+ * `clipped by div#acc: zero height` (null when none has), and whether the
141
+ * node is fixed to the viewport (it or a container in its containing-block
142
+ * chain is `position: fixed`; `fixedBy` is that node).
143
+ */
144
+ export declare const ANCESTOR_CLIP_JS: string;
116
145
  /** Page-side test of a `clip-path` that cuts everything away: `inset()` with percentages leaving no area. */
117
146
  export declare const CLIP_PATH_CUTS_ALL_JS = "(clipPath) => {\n const inset = /^inset\\(([^)]*)\\)/.exec(clipPath || '');\n const values = inset ? inset[1].split(' round ')[0].trim().split(/\\s+/) : [];\n if (values.length === 0 || values.some((v) => !/%$/.test(v))) return false;\n const [top, right = top, bottom = top, left = right] = values.map(parseFloat);\n return top + bottom >= 100 || left + right >= 100;\n}";
118
147
  /**
@@ -126,7 +155,8 @@ export declare const CLIP_RECT_CUTS_ALL_JS = "(position, clip) => {\n if ((posi
126
155
  * top-level viewport coordinates, its clip by ancestors
127
156
  * ({@link ANCESTOR_CLIP_JS}) and by the viewports of its iframes, why it
128
157
  * cannot be seen at all (not rendered, or inside a clipping container
129
- * collapsed to zero size), why a rendered one is still invisible
158
+ * collapsed to zero size; a `display: contents` element is measured by
159
+ * its children in the flat tree and is hidden only when none shows), why a rendered one is still invisible
130
160
  * ({@link INVISIBLE_REASON_JS}), whether it is inert (an `inert` element
131
161
  * around it, through shadow roots) or fixed to the top-level viewport, and how
132
162
  * far the top-level page can scroll ({@link SCROLL_RANGE_JS}) or what locks
@@ -152,28 +152,30 @@ const HOLDS_FIXED_JS = `(style) =>
152
152
  /transform|filter|perspective/.test(style.willChange) || /paint|layout|strict|content/.test(style.contain)`;
153
153
  /**
154
154
  * Page-side test of whether a `position: fixed` node is fixed to its
155
- * document's viewport (no ancestor holds it, {@link HOLDS_FIXED_JS}).
155
+ * document's viewport (no ancestor in the flat tree holds it,
156
+ * {@link HOLDS_FIXED_JS}).
156
157
  */
157
158
  const FIXED_TO_VIEWPORT_JS = `(n) => {
158
159
  const holdsFixed = ${HOLDS_FIXED_JS};
159
- const parentOf = (node) => node.parentElement || (node.parentNode && node.parentNode.host) || null;
160
+ const parentOf = ${FLAT_PARENT_JS};
160
161
  for (let p = parentOf(n); p && p !== n.ownerDocument.documentElement; p = parentOf(p)) {
161
162
  if (holdsFixed(p.ownerDocument.defaultView.getComputedStyle(p))) return false;
162
163
  }
163
164
  return true;
164
165
  }`;
165
166
  /**
166
- * Page-side clip of a node by its ancestors (looked up through open shadow
167
- * roots): the padding boxes ({@link CLIP_BOX_JS}) of those that cut off
168
- * overflowing content and hold the node in their containing-block chain. An
169
- * absolutely positioned node skips static ancestors (that are not
170
- * transformed) up to its containing block, a fixed one is not clipped at all
171
- * unless a transformed (or filtered, contained, …) ancestor holds it like an
172
- * absolute one, and inline ancestors and `display: contents` ones have no box
173
- * to clip with. The root element is left out (its overflow belongs to the
174
- * viewport), and so is the body's overflow unless the root element's
175
- * overflow is not `visible` (then the body keeps its own overflow and, e.g.
176
- * as the page's scroller, clips like any container).
167
+ * Page-side clip of a node by its ancestors in the flat tree (through the
168
+ * slots it is shown in and open shadow roots): the padding boxes
169
+ * ({@link CLIP_BOX_JS}) of those that cut off overflowing content and hold
170
+ * the node in their containing-block chain. An absolutely positioned node
171
+ * skips static ancestors (that are not transformed) up to its containing
172
+ * block, a fixed one is not clipped at all unless a transformed (or
173
+ * filtered, contained, …) ancestor holds it like an absolute one, and
174
+ * inline ancestors and `display: contents` ones have no box to clip with.
175
+ * The root element is left out (its overflow belongs to the viewport), and
176
+ * so is the body's overflow unless the root element's overflow is not
177
+ * `visible` (then the body keeps its own overflow and, e.g. as the page's
178
+ * scroller, clips like any container).
177
179
  * Returns the clip, whether overlay scrollbars of the containers show along
178
180
  * its right and bottom edges ({@link OVERLAY_SCROLLBARS_JS}), the innermost
179
181
  * ancestor cutting off part of `rect` (null when none does), why the
@@ -183,14 +185,14 @@ const FIXED_TO_VIEWPORT_JS = `(n) => {
183
185
  * node is fixed to the viewport (it or a container in its containing-block
184
186
  * chain is `position: fixed`; `fixedBy` is that node).
185
187
  */
186
- const ANCESTOR_CLIP_JS = `(node, rect, describe) => {
188
+ export const ANCESTOR_CLIP_JS = `(node, rect, describe) => {
187
189
  const clipBox = ${CLIP_BOX_JS};
188
190
  const overlayScrollbars = ${OVERLAY_SCROLLBARS_JS};
189
191
  const holdsFixed = ${HOLDS_FIXED_JS};
190
192
  const fixedToViewport = ${FIXED_TO_VIEWPORT_JS};
191
193
  const reasons = ${JSON.stringify(LAYOUT_REASONS)};
192
194
  const styleOf = (n) => n.ownerDocument.defaultView.getComputedStyle(n);
193
- const parentOf = (n) => n.parentElement || (n.parentNode && n.parentNode.host) || null;
195
+ const parentOf = ${FLAT_PARENT_JS};
194
196
  const doc = node.ownerDocument;
195
197
  const rootStyle = styleOf(doc.documentElement);
196
198
  const bodyClips = rootStyle.overflowX !== 'visible' || rootStyle.overflowY !== 'visible';
@@ -268,12 +270,28 @@ const INVISIBLE_REASON_JS = `(el, describe) => {
268
270
  }
269
271
  return null;
270
272
  }`;
273
+ /**
274
+ * Page-side mask over an element: the nearest of it and its ancestors in the
275
+ * flat tree (slots, shadow hosts, iframes) with a `mask-image`. How much the
276
+ * mask hides is not evaluated. Null without one.
277
+ */
278
+ const MASKED_BY_JS = `(el, describe) => {
279
+ const flatParent = ${FLAT_PARENT_JS};
280
+ const parentOf = (n) => flatParent(n) || n.ownerDocument.defaultView.frameElement || null;
281
+ for (let n = el; n; n = parentOf(n)) {
282
+ const style = n.ownerDocument.defaultView.getComputedStyle(n);
283
+ const mask = style.maskImage || style.webkitMaskImage;
284
+ if (mask && mask !== 'none') return n === el ? 'mask-image' : 'mask-image on ' + describe(n);
285
+ }
286
+ return null;
287
+ }`;
271
288
  /**
272
289
  * Page-side measurement of an element ({@link ElementGeometry}): its box in
273
290
  * top-level viewport coordinates, its clip by ancestors
274
291
  * ({@link ANCESTOR_CLIP_JS}) and by the viewports of its iframes, why it
275
292
  * cannot be seen at all (not rendered, or inside a clipping container
276
- * collapsed to zero size), why a rendered one is still invisible
293
+ * collapsed to zero size; a `display: contents` element is measured by
294
+ * its children in the flat tree and is hidden only when none shows), why a rendered one is still invisible
277
295
  * ({@link INVISIBLE_REASON_JS}), whether it is inert (an `inert` element
278
296
  * around it, through shadow roots) or fixed to the top-level viewport, and how
279
297
  * far the top-level page can scroll ({@link SCROLL_RANGE_JS}) or what locks
@@ -292,6 +310,7 @@ export const ELEMENT_GEOMETRY_JS = `(el) => {
292
310
  const scrollRange = ${SCROLL_RANGE_JS};
293
311
  const scrollLock = ${SCROLL_LOCK_JS};
294
312
  const invisibleReason = ${INVISIBLE_REASON_JS};
313
+ const maskedBy = ${MASKED_BY_JS};
295
314
  const describe = ${ELEMENT_DESCRIPTION_JS};
296
315
  const reasons = ${JSON.stringify(LAYOUT_REASONS)};
297
316
  const styleOf = (node) => node.ownerDocument.defaultView.getComputedStyle(node);
@@ -311,11 +330,16 @@ export const ELEMENT_GEOMETRY_JS = `(el) => {
311
330
  }
312
331
  return 'not rendered';
313
332
  };
333
+ const flatChildren = (node) =>
334
+ Array.from((node.shadowRoot || node).children).flatMap((c) => {
335
+ const shown = c.localName === 'slot' ? c.assignedElements({ flatten: true }) : [c];
336
+ return shown.flatMap((s) => (styleOf(s).display === 'contents' ? flatChildren(s) : [s]));
337
+ });
314
338
  const shownChildren = (node) => {
315
339
  const s = styleOf(node);
316
340
  if (s.overflowX !== 'visible' || s.overflowY !== 'visible') return null;
317
341
  let union = null;
318
- for (const c of Array.from(node.children).slice(0, 50)) {
342
+ for (const c of flatChildren(node).slice(0, 50)) {
319
343
  const r = c.getBoundingClientRect();
320
344
  if (r.width <= 0 || r.height <= 0 || styleOf(c).visibility !== 'visible') continue;
321
345
  union = union
@@ -326,7 +350,7 @@ export const ELEMENT_GEOMETRY_JS = `(el) => {
326
350
  };
327
351
  const hiddenReason = (style, box, content) => {
328
352
  if (style.display === 'none') return 'display: none';
329
- if (style.display === 'contents') return 'display: contents (no box of its own)';
353
+ if (style.display === 'contents') return content ? null : 'display: contents (no box of its own)';
330
354
  if (el.getClientRects().length === 0) {
331
355
  return el.tagName === 'OPTION' ? reasons.option : 'not rendered (an ancestor has display: none)';
332
356
  }
@@ -386,9 +410,10 @@ export const ELEMENT_GEOMETRY_JS = `(el) => {
386
410
  while (top.frameElement) top = top.parent;
387
411
  const toBox = (r) => r && { x: r.left, y: r.top, width: r.right - r.left, height: r.bottom - r.top };
388
412
  const invisible = hidden ? null : invisibleReason(el, describe);
413
+ const masked = hidden || invisible ? null : maskedBy(el, describe);
389
414
  const lock = scrollLock(top, describe);
390
415
  if (lock && fixedBy === top.document.body) fixed = false;
391
- return { rect: toBox(rect), content: toBox(content), clip: toBox(clip), clipOverlay: overlay, clipper: clipper, hidden: hidden, invisible: invisible, inert: isInert(), fixed: fixed, sticky: !fixed && isSticky(), pageScroll: scrollRange(top), scrollLock: lock, offset: { x: x, y: y } };
416
+ return { rect: toBox(rect), content: toBox(content), clip: toBox(clip), clipOverlay: overlay, clipper: clipper, hidden: hidden, invisible: invisible, masked: masked, inert: isInert(), fixed: fixed, sticky: !fixed && isSticky(), pageScroll: scrollRange(top), scrollLock: lock, offset: { x: x, y: y } };
392
417
  }`;
393
418
  /**
394
419
  * Edges of a box.
@@ -3,6 +3,19 @@
3
3
  * `dom layout`, DOM context of console messages): their text as a user sees
4
4
  * it and where they live (iframes, shadow roots).
5
5
  */
6
+ /**
7
+ * Whether a class name is shown in element labels: not empty and not a
8
+ * fragment of a declaration-like class attribute ({@link CLASS_FRAGMENT}).
9
+ *
10
+ * @param name - Class name
11
+ * @returns True to show it
12
+ */
13
+ export declare function isLabelClass(name: string): boolean;
14
+ /**
15
+ * Page-side `(node) => string[]`: the element's classes shown in labels
16
+ * ({@link isLabelClass}), read without array helpers a page may replace
17
+ */
18
+ export declare const LABEL_CLASSES_JS: string;
6
19
  /** Length of the text `dom get` shows for an element. */
7
20
  export declare const ELEMENT_TEXT_LENGTH = 500;
8
21
  /**
@@ -14,18 +27,47 @@ export declare const ELEMENT_TEXT_LENGTH = 500;
14
27
  * text is removed once, from the end.
15
28
  */
16
29
  export declare const WITHOUT_DECORATIONS_JS = "(el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n}";
30
+ /**
31
+ * Page-side check whether `innerText` misses part of what an element shows:
32
+ * it is a `<slot>` or hosts an open shadow root, or one of its descendants
33
+ * is or does. `innerText` follows neither shadow roots nor slots.
34
+ */
35
+ export declare const COMPOSED_JS = "(node) => {\n const composes = (n) => Boolean(n.shadowRoot) || n.localName === 'slot';\n if (composes(node)) return true;\n const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT);\n while (walker.nextNode()) if (composes(walker.currentNode)) return true;\n return false;\n}";
36
+ /**
37
+ * Page-side text of an element as the flat tree renders it, for elements
38
+ * `innerText` cannot read ({@link COMPOSED_JS}): a shadow host is read from
39
+ * its open shadow root (its own labels and fallback content; light children
40
+ * only where a slot shows them), and each slot is replaced by what it
41
+ * shows, its assigned nodes flattened through nested slots (or its fallback
42
+ * content when nothing is assigned). Parts without slots or shadow roots
43
+ * are read with `innerText`; elements that are not rendered and text under
44
+ * `visibility: hidden` are left out, elements that are not inline are set
45
+ * apart by line breaks, as is a `<br>`. Fields and editable regions inside
46
+ * it (inputs, textareas, selects, a contenteditable editor) are skipped, so
47
+ * what a user typed is never read, unless `fields` is set: then selects
48
+ * (all their options) and editable regions are read as `innerText` reads
49
+ * them (inputs and textareas it leaves out too). Raw text keeps
50
+ * `innerText`'s collapsed whitespace. The text is cut at `limit` characters, and a
51
+ * part whose text alone passes the limit is read from its text nodes
52
+ * (`textContent`) instead of `innerText`, which would lay out all of it.
53
+ */
54
+ export declare const FLAT_TEXT_JS = "(el, limit, fields) => {\n const composed = (node) => {\n const composes = (n) => Boolean(n.shadowRoot) || n.localName === 'slot';\n if (composes(node)) return true;\n const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT);\n while (walker.nextNode()) if (composes(walker.currentNode)) return true;\n return false;\n};\n const view = el.ownerDocument.defaultView;\n let text = '';\n const nodesOf = (node) =>\n node.localName === 'slot' ? node.assignedNodes({ flatten: true }) : Array.from((node.shadowRoot || node).childNodes);\n const read = (node, visible) => {\n if (text.length >= limit) return;\n if (node.nodeType === 3 && visible) text += node.data.replace(/\\s+/g, ' ');\n if (node.nodeType !== 1) return;\n if (/^(input|textarea)$/.test(node.localName)) return;\n if (!fields && (node.localName === 'select' || node.isContentEditable)) return;\n const style = view.getComputedStyle(node);\n if (node.checkVisibility && !node.checkVisibility() && style.display !== 'contents') return;\n if (node.localName === 'br') text += '\\n';\n const apart = style.display.startsWith('inline') || style.display === 'contents' ? '' : '\\n';\n text += apart;\n const own = node.textContent || '';\n if (composed(node)) nodesOf(node).forEach((child) => read(child, style.visibility === 'visible'));\n else if (own.length > limit - text.length) text += own.slice(0, limit - text.length);\n else text += typeof node.innerText === 'string' ? node.innerText : own;\n text += apart;\n };\n const visible = view.getComputedStyle(el).visibility === 'visible';\n nodesOf(el).forEach((child) => read(child, visible));\n return text.slice(0, limit);\n}";
17
55
  /**
18
56
  * Page-side text of an element as a user sees it: `innerText` for a rendered
19
57
  * element (CSS-hidden parts left out, inline elements not split apart), none
20
58
  * for an element that is not rendered, `textContent` for SVG and other
21
59
  * elements without `innerText` and for `display: contents` wrappers (no box
22
60
  * of their own, but their children are shown), and the label of an
23
- * `<option>` (which its `<select>` renders). For large containers (more
24
- * than 2000 characters of text) only the start is read, from the text nodes
25
- * whose parent is rendered, so a preview never lays out a whole page's text,
26
- * unless `full` is set. Decorations are left out ({@link WITHOUT_DECORATIONS_JS}).
61
+ * `<option>` (which its `<select>` renders). A web component, a slot, and
62
+ * an element holding either are read through the flat tree
63
+ * ({@link FLAT_TEXT_JS}): the text a component renders from its shadow
64
+ * root, slotted content in place of its slots, blocks set apart. For large
65
+ * containers (more than 2000 characters of text) only the start is read,
66
+ * from the text nodes whose parent is rendered, so a preview never lays out
67
+ * a whole page's text, unless `full` is set. Decorations are left out
68
+ * ({@link WITHOUT_DECORATIONS_JS}).
27
69
  */
28
- export declare const ELEMENT_TEXT_JS = "(el, full) => {\n const withoutDecorations = (el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n};\n const all = el.textContent || '';\n if (el.tagName === 'OPTION') return el.label;\n if (typeof el.innerText !== 'string') return full ? all : all.slice(0, 2000);\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n if (!rendered(el)) {\n const boxless = el.ownerDocument.defaultView.getComputedStyle(el).display === 'contents';\n return boxless ? withoutDecorations(el, full ? all : all.slice(0, 2000)) : '';\n }\n if (full || all.length <= 2000) return withoutDecorations(el, el.innerText);\n const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let start = '';\n while (start.length < 1000 && walker.nextNode()) {\n const parent = walker.currentNode.parentElement;\n if (!parent || rendered(parent)) start += walker.currentNode.data;\n }\n return withoutDecorations(el, start);\n}";
70
+ export declare const ELEMENT_TEXT_JS = "(el, full) => {\n const withoutDecorations = (el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n};\n const composed = (node) => {\n const composes = (n) => Boolean(n.shadowRoot) || n.localName === 'slot';\n if (composes(node)) return true;\n const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT);\n while (walker.nextNode()) if (composes(walker.currentNode)) return true;\n return false;\n};\n const flatText = (el, limit, fields) => {\n const composed = (node) => {\n const composes = (n) => Boolean(n.shadowRoot) || n.localName === 'slot';\n if (composes(node)) return true;\n const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT);\n while (walker.nextNode()) if (composes(walker.currentNode)) return true;\n return false;\n};\n const view = el.ownerDocument.defaultView;\n let text = '';\n const nodesOf = (node) =>\n node.localName === 'slot' ? node.assignedNodes({ flatten: true }) : Array.from((node.shadowRoot || node).childNodes);\n const read = (node, visible) => {\n if (text.length >= limit) return;\n if (node.nodeType === 3 && visible) text += node.data.replace(/\\s+/g, ' ');\n if (node.nodeType !== 1) return;\n if (/^(input|textarea)$/.test(node.localName)) return;\n if (!fields && (node.localName === 'select' || node.isContentEditable)) return;\n const style = view.getComputedStyle(node);\n if (node.checkVisibility && !node.checkVisibility() && style.display !== 'contents') return;\n if (node.localName === 'br') text += '\\n';\n const apart = style.display.startsWith('inline') || style.display === 'contents' ? '' : '\\n';\n text += apart;\n const own = node.textContent || '';\n if (composed(node)) nodesOf(node).forEach((child) => read(child, style.visibility === 'visible'));\n else if (own.length > limit - text.length) text += own.slice(0, limit - text.length);\n else text += typeof node.innerText === 'string' ? node.innerText : own;\n text += apart;\n };\n const visible = view.getComputedStyle(el).visibility === 'visible';\n nodesOf(el).forEach((child) => read(child, visible));\n return text.slice(0, limit);\n};\n const all = el.textContent || '';\n if (el.tagName === 'OPTION') return el.label;\n if (typeof el.innerText !== 'string') return full ? all : all.slice(0, 2000);\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const shown = rendered(el);\n const boxless = !shown && el.ownerDocument.defaultView.getComputedStyle(el).display === 'contents';\n if (!shown && !boxless) return '';\n if (composed(el)) return withoutDecorations(el, flatText(el, full ? Infinity : 2000));\n if (boxless) return withoutDecorations(el, full ? all : all.slice(0, 2000));\n if (full || all.length <= 2000) return withoutDecorations(el, el.innerText);\n const walker = el.ownerDocument.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let start = '';\n while (start.length < 1000 && walker.nextNode()) {\n const parent = walker.currentNode.parentElement;\n if (!parent || rendered(parent)) start += walker.currentNode.data;\n }\n return withoutDecorations(el, start);\n}";
29
71
  /** Shown instead of a secret field value (the same for every length) */
30
72
  export declare const MASKED_VALUE = "\u2022\u2022\u2022\u2022";
31
73
  /**
@@ -53,26 +95,40 @@ export declare const SENSITIVE_FIELD_JS = "(el) => {\n const autocomplete = el.
53
95
  */
54
96
  export declare const ELEMENT_STATE_JS = "(el) => {\n const isSensitive = (el) => {\n const autocomplete = el.getAttribute('autocomplete') || '';\n if (/(^|\\s)(cc-[a-z-]+|one-time-code|current-password|new-password)(\\s|$)/i.test(autocomplete)) return true;\n if (el.type === 'password' || /^password$/i.test(el.getAttribute('type') || '')) return true;\n const names = [el.getAttribute('name'), el.id, autocomplete].join(' ');\n if (/passw|passwd|pwd|passcode|(^|[^a-z])otp([^a-z]|$)|cvv|cvc/i.test(names)) return true;\n try {\n const security = el.ownerDocument.defaultView.getComputedStyle(el).getPropertyValue('-webkit-text-security');\n return Boolean(security) && security !== 'none';\n } catch (e) {\n return false;\n }\n};\n const mask = (value) => (value ? '\u2022\u2022\u2022\u2022' : '');\n const guarded = (state) => {\n if (!isSensitive(el)) return state;\n const result = { ...state, sensitive: true };\n if ('value' in result) result.value = mask(result.value);\n if ('selected' in result) result.selected = mask(result.selected);\n return result;\n };\n switch (el.localName) {\n case 'input':\n if (el.type === 'hidden') return { type: 'hidden' };\n return guarded(\n /^(checkbox|radio)$/.test(el.type)\n ? { type: el.type, checked: el.checked, value: el.getAttribute('value') || '' }\n : { type: el.type, value: el.value }\n );\n case 'textarea':\n return guarded({ value: el.value });\n case 'select':\n return guarded({ selected: Array.from(el.selectedOptions || [], (option) => option.label).join(', ') });\n case 'button':\n return el.form ? { type: el.type } : {};\n default:\n return {};\n }\n}";
55
97
  /**
56
- * Page-side short description of an element: tag, id and up to two classes,
57
- * e.g. `button#save.primary.large`.
98
+ * Page-side short description of an element: tag, id and up to two classes
99
+ * ({@link LABEL_CLASSES_JS}), e.g. `button#save.primary.large`.
100
+ */
101
+ export declare const ELEMENT_DESCRIPTION_JS: string;
102
+ /**
103
+ * Page-side reason an element is disabled, or null when it is not. A native
104
+ * control (`button`, `input`, `select`, `textarea`, `fieldset`, `optgroup`,
105
+ * `option`) is disabled by its `disabled attribute` or by being `inside a
106
+ * disabled <fieldset>` (not in its first `<legend>`), an `<option>` by
107
+ * being `inside a disabled <optgroup>`; any other element
108
+ * (e.g. a custom element) when its `disabled` property is set (empty
109
+ * reason). Reads the element's own attributes rather than
110
+ * `matches(':disabled')`, which pages replace.
58
111
  */
59
- export declare const ELEMENT_DESCRIPTION_JS = "(node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '')";
112
+ export declare const DISABLED_CAUSE_JS = "(el) => {\n if (!/^(button|input|select|textarea|fieldset|optgroup|option)$/.test(el.localName)) return el.disabled ? '' : null;\n if (el.hasAttribute('disabled')) return 'disabled attribute';\n if (el.localName === 'option') {\n const group = el.parentElement;\n return group && group.localName === 'optgroup' && group.hasAttribute('disabled') ? 'inside a disabled <optgroup>' : null;\n }\n for (let node = el.parentElement; node; node = node.parentElement) {\n if (node.localName !== 'fieldset' || !node.hasAttribute('disabled')) continue;\n let legend = node.firstElementChild;\n while (legend && legend.localName !== 'legend') legend = legend.nextElementSibling;\n if (!legend || !legend.contains(el)) return 'inside a disabled <fieldset>';\n }\n return null;\n}";
60
113
  /**
61
114
  * Page-side position of an element among its parent's children with the
62
115
  * same short description ({@link ELEMENT_DESCRIPTION_JS}), e.g. `(2nd of 3)`;
63
116
  * empty when it is the only one.
64
117
  */
65
- export declare const SIBLING_POSITION_JS = "(el) => {\n const describe = (node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '');\n const parent = el.parentElement;\n if (!parent) return '';\n const own = describe(el);\n const same = Array.from(parent.children).filter((child) => describe(child) === own);\n if (same.length < 2) return '';\n const n = same.indexOf(el) + 1;\n const teen = n % 100 >= 11 && n % 100 <= 13;\n const suffix = teen ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' })[n % 10] || 'th';\n return '(' + n + suffix + ' of ' + same.length + ')';\n}";
118
+ export declare const SIBLING_POSITION_JS: string;
66
119
  /**
67
120
  * Page-side identity of an element an action hit, so the output says which
68
121
  * one it was, as one string: its short description
69
122
  * ({@link ELEMENT_DESCRIPTION_JS}) and visible text (button value for button
70
- * inputs, decorations left out), e.g. `button#add.btn "Add to cart"`. A
71
- * `<select>` is named by its label, aria-label or name, else by its selected
123
+ * inputs, decorations left out; read through the flat tree for web
124
+ * components and slots, {@link FLAT_TEXT_JS}), e.g.
125
+ * `button#add.btn "Add to cart"`. A `<select>` is named by its label
126
+ * (`aria-labelledby` or `<label>`), aria-label or name, else by its selected
72
127
  * option, e.g. `select.sort "Sort products"`. An element without visible
73
128
  * text is described by itself first: by its position among same-looking
74
129
  * siblings ({@link SIBLING_POSITION_JS}), e.g. `div.figure (2nd of 3)`, with
75
- * its aria-label, placeholder or title when it has one. Only an element
130
+ * its name when it has one: aria-label, label, placeholder, title, or the
131
+ * alt text of an image (in it). Only an element
76
132
  * without an id that is the only one of its kind is named by the nearest of
77
133
  * three ancestors that has text, e.g.
78
134
  * `input.toggle in div.view "Write report"` (rows of a list share their
@@ -80,18 +136,20 @@ export declare const SIBLING_POSITION_JS = "(el) => {\n const describe = (node)
80
136
  * of `<select>`s in it (read like `innerText`: CSS-hidden text is left out of
81
137
  * a rendered ancestor; at most 500 text nodes; selects in shadow roots are
82
138
  * not looked into), and never past an editable ancestor, whose text may be
83
- * typed input; otherwise its aria-label, placeholder or title is used. Texts
139
+ * typed input; otherwise its name is used. Texts
84
140
  * are cut at 40 characters. Contenteditable elements count as controls: what
85
141
  * was typed into them is never echoed (nor their ancestors' text).
86
142
  */
87
- export declare const ELEMENT_IDENTITY_JS = "(el) => {\n const describe = (node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '');\n const siblingPosition = (el) => {\n const describe = (node) => node.tagName.toLowerCase() +\n (node.id ? '#' + node.id : '') +\n (node.classList && node.classList.length ? '.' + Array.from(node.classList).slice(0, 2).join('.') : '');\n const parent = el.parentElement;\n if (!parent) return '';\n const own = describe(el);\n const same = Array.from(parent.children).filter((child) => describe(child) === own);\n if (same.length < 2) return '';\n const n = same.indexOf(el) + 1;\n const teen = n % 100 >= 11 && n % 100 <= 13;\n const suffix = teen ? 'th' : ({ 1: 'st', 2: 'nd', 3: 'rd' })[n % 10] || 'th';\n return '(' + n + suffix + ' of ' + same.length + ')';\n};\n const withoutDecorations = (el, text) => {\n if (!text || typeof el.querySelectorAll !== 'function') return text;\n const glyph = /^\\s*[\u00D7\u2715\u2716\u2717\u2A2F]\\s*$/;\n const textOf = (node) => (typeof node.innerText === 'string' ? node.innerText : node.textContent || '').trim();\n const closer = '.close, [aria-label=\"close\" i], [aria-label=\"dismiss\" i]';\n const rendered = (node) => !node.checkVisibility || node.checkVisibility();\n const found = Array.from(el.querySelectorAll(closer + ', [aria-hidden=\"true\"]'))\n .filter((node) => rendered(node) && (node.matches(closer) || !/[\\p{L}\\p{N}]/u.test(textOf(node))));\n if (/[\u00D7\u2715\u2716\u2717\u2A2F]/.test(text)) {\n found.push(...Array.from(el.querySelectorAll('button, a, [role=\"button\"]')).filter((node) => glyph.test(node.textContent || '')));\n }\n const unique = Array.from(new Set(found));\n const outermost = unique.filter((node) => !unique.some((other) => other !== node && other.contains(node)));\n let result = text;\n for (const node of outermost) {\n const part = textOf(node);\n const at = part ? result.lastIndexOf(part) : -1;\n if (at >= 0) result = result.slice(0, at) + result.slice(at + part.length);\n }\n return result;\n};\n const clean = (text) => (text || '').replace(/\\s+/g, ' ').trim();\n const cut = (text) => {\n const characters = Array.from(text);\n return characters.length > 40 ? characters.slice(0, 40).join('') + '\u2026' : text;\n };\n const isControl = (node) => /^(input|select|textarea)$/.test(node.localName) || node.isContentEditable;\n const shownText = (node) =>\n isControl(node) ? '' : clean(withoutDecorations(node, typeof node.innerText === 'string' ? node.innerText : node.textContent));\n const buttonValue = (node) => (node.localName === 'input' && /^(submit|button|reset)$/i.test(node.type) ? clean(node.value) : '');\n const attributeText = (node) =>\n clean(node.getAttribute('aria-label')) || clean(node.getAttribute('placeholder')) || clean(node.getAttribute('title'));\n const selectName = (node) =>\n clean(node.labels && node.labels[0] && node.labels[0].innerText) ||\n clean(node.getAttribute('aria-label')) ||\n clean(node.getAttribute('name')) ||\n clean(node.selectedOptions && node.selectedOptions[0] && node.selectedOptions[0].label);\n const shown = (node, options) => !node.checkVisibility || node.checkVisibility(options);\n const textOutsideSelects = (node) => {\n if (!node.querySelector('select')) return shownText(node);\n const hiddenLeftOut = shown(node);\n const walker = node.ownerDocument.createTreeWalker(node, NodeFilter.SHOW_TEXT);\n let text = '';\n for (let visited = 0; visited < 500 && text.length < 200 && walker.nextNode(); visited++) {\n const parent = walker.currentNode.parentElement;\n if (!parent || parent.closest('select, textarea')) continue;\n if (!hiddenLeftOut || shown(parent, { visibilityProperty: true })) text += ' ' + walker.currentNode.data;\n }\n return clean(text);\n };\n const quoted = (text) => (text ? ' \"' + cut(text) + '\"' : '');\n const visible = shownText(el) || buttonValue(el) || (el.localName === 'select' ? selectName(el) : '');\n if (visible) return describe(el) + quoted(visible);\n const attribute = attributeText(el);\n const position = siblingPosition(el);\n if (position) return describe(el) + ' ' + position + quoted(attribute);\n let ancestor = el.id || el.isContentEditable ? null : el.parentElement;\n for (let depth = 0; ancestor && depth < 3; depth++, ancestor = ancestor.parentElement) {\n if (isControl(ancestor)) break;\n const text = textOutsideSelects(ancestor);\n if (text) return describe(el) + ' in ' + describe(ancestor) + quoted(text);\n }\n return describe(el) + quoted(attribute);\n}";
143
+ export declare const ELEMENT_IDENTITY_JS: string;
88
144
  /**
89
145
  * Page-side location of an element: the iframes it is in (outermost first)
90
- * and the shadow root holding it, with the host's first class and light-DOM
91
- * text, e.g. `iframe#pay > shadow root of <sl-button.primary "Save">`;
92
- * empty for the main document.
146
+ * and the shadow root holding it, with the host's first class and the text
147
+ * a user sees of it ({@link ELEMENT_TEXT_JS}, cut at 30 characters with
148
+ * `…`), e.g. `iframe#pay > shadow root of <sl-button.primary "Save">`;
149
+ * empty for the main document. Each host's text is read once per script
150
+ * run, as many matches share a host.
93
151
  */
94
- export declare const ELEMENT_CONTEXT_JS = "(el) => {\n const describe = (node) => node.tagName.toLowerCase() + (node.id ? '#' + node.id : '');\n const parts = [];\n for (let doc = el.ownerDocument; doc && doc.defaultView && doc.defaultView.frameElement; ) {\n const frame = doc.defaultView.frameElement;\n parts.unshift(describe(frame));\n doc = frame.ownerDocument;\n }\n const root = el.getRootNode();\n if (root.host) {\n const host = root.host;\n const label = describe(host) + (!host.id && host.classList.length ? '.' + host.classList[0] : '');\n const text = (host.textContent || '').replace(/\\s+/g, ' ').trim().slice(0, 30);\n parts.push('shadow root of <' + label + (text ? ' \"' + text + '\"' : '') + '>');\n }\n return parts.join(' > ');\n}";
152
+ export declare const ELEMENT_CONTEXT_JS: string;
95
153
  /**
96
154
  * Short text preview of an element's text: whitespace collapsed, cut on a
97
155
  * whole character (an emoji is never split, which would make JSON invalid).