@a11y-pulse/tab-orchestrator 0.4.0 → 0.6.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/LICENSE.md CHANGED
@@ -1,166 +1,21 @@
1
- Copyright (c) 2026-present, A11y Pulse Limited (https://www.a11ypulse.com/). All rights reserved.
2
-
3
- # PolyForm Shield License 1.0.0
4
-
5
- <https://polyformproject.org/licenses/shield/1.0.0>
6
-
7
- ## Acceptance
8
-
9
- In order to get any license under these terms, you must agree
10
- to them as both strict obligations and conditions to all
11
- your licenses.
12
-
13
- ## Copyright License
14
-
15
- The licensor grants you a copyright license for the
16
- software to do everything you might do with the software
17
- that would otherwise infringe the licensor's copyright
18
- in it for any permitted purpose. However, you may
19
- only distribute the software according to [Distribution
20
- License](#distribution-license) and make changes or new works
21
- based on the software according to [Changes and New Works
22
- License](#changes-and-new-works-license).
23
-
24
- ## Distribution License
25
-
26
- The licensor grants you an additional copyright license
27
- to distribute copies of the software. Your license
28
- to distribute covers distributing the software with
29
- changes and new works permitted by [Changes and New Works
30
- License](#changes-and-new-works-license).
31
-
32
- ## Notices
33
-
34
- You must ensure that anyone who gets a copy of any part of
35
- the software from you also gets a copy of these terms or the
36
- URL for them above, as well as copies of any plain-text lines
37
- beginning with `Required Notice:` that the licensor provided
38
- with the software. For example:
39
-
40
- > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
41
-
42
- ## Changes and New Works License
43
-
44
- The licensor grants you an additional copyright license to
45
- make changes and new works based on the software for any
46
- permitted purpose.
47
-
48
- ## Patent License
49
-
50
- The licensor grants you a patent license for the software that
51
- covers patent claims the licensor can license, or becomes able
52
- to license, that you would infringe by using the software.
53
-
54
- ## Noncompete
55
-
56
- Any purpose is a permitted purpose, except for providing any
57
- product that competes with the software or any product the
58
- licensor or any of its affiliates provides using the software.
59
-
60
- ## Competition
61
-
62
- Goods and services compete even when they provide functionality
63
- through different kinds of interfaces or for different technical
64
- platforms. Applications can compete with services, libraries
65
- with plugins, frameworks with development tools, and so on,
66
- even if they're written in different programming languages
67
- or for different computer architectures. Goods and services
68
- compete even when provided free of charge. If you market a
69
- product as a practical substitute for the software or another
70
- product, it definitely competes.
71
-
72
- ## New Products
73
-
74
- If you are using the software to provide a product that does
75
- not compete, but the licensor or any of its affiliates brings
76
- your product into competition by providing a new version of
77
- the software or another product using the software, you may
78
- continue using versions of the software available under these
79
- terms beforehand to provide your competing product, but not
80
- any later versions.
81
-
82
- ## Discontinued Products
83
-
84
- You may begin using the software to compete with a product
85
- or service that the licensor or any of its affiliates has
86
- stopped providing, unless the licensor includes a plain-text
87
- line beginning with `Licensor Line of Business:` with the
88
- software that mentions that line of business. For example:
89
-
90
- > Licensor Line of Business: YoyodyneCMS Content Management
91
- System (http://example.com/cms)
92
-
93
- ## Sales of Business
94
-
95
- If the licensor or any of its affiliates sells a line of
96
- business developing the software or using the software
97
- to provide a product, the buyer can also enforce
98
- [Noncompete](#noncompete) for that product.
99
-
100
- ## Fair Use
101
-
102
- You may have "fair use" rights for the software under the
103
- law. These terms do not limit them.
104
-
105
- ## No Other Rights
106
-
107
- These terms do not allow you to sublicense or transfer any of
108
- your licenses to anyone else, or prevent the licensor from
109
- granting licenses to anyone else. These terms do not imply
110
- any other licenses.
111
-
112
- ## Patent Defense
113
-
114
- If you make any written claim that the software infringes or
115
- contributes to infringement of any patent, your patent license
116
- for the software granted under these terms ends immediately. If
117
- your company makes such a claim, your patent license ends
118
- immediately for work on behalf of your company.
119
-
120
- ## Violations
121
-
122
- The first time you are notified in writing that you have
123
- violated any of these terms, or done anything with the software
124
- not covered by your licenses, your licenses can nonetheless
125
- continue if you come into full compliance with these terms,
126
- and take practical steps to correct past violations, within
127
- 32 days of receiving notice. Otherwise, all your licenses
128
- end immediately.
129
-
130
- ## No Liability
131
-
132
- ***As far as the law allows, the software comes as is, without
133
- any warranty or condition, and the licensor will not be liable
134
- to you for any damages arising out of these terms or the use
135
- or nature of the software, under any kind of legal claim.***
136
-
137
- ## Definitions
138
-
139
- The **licensor** is the individual or entity offering these
140
- terms, and the **software** is the software the licensor makes
141
- available under these terms.
142
-
143
- A **product** can be a good or service, or a combination
144
- of them.
145
-
146
- **You** refers to the individual or entity agreeing to these
147
- terms.
148
-
149
- **Your company** is any legal entity, sole proprietorship,
150
- or other kind of organization that you work for, plus all
151
- its affiliates.
152
-
153
- **Affiliates** means the other organizations than an
154
- organization has control over, is under the control of, or is
155
- under common control with.
156
-
157
- **Control** means ownership of substantially all the assets of
158
- an entity, or the power to direct its management and policies
159
- by vote, contract, or otherwise. Control can be direct or
160
- indirect.
161
-
162
- **Your licenses** are all the licenses granted to you for the
163
- software under these terms.
164
-
165
- **Use** means anything you do with the software requiring one
166
- of your licenses.
1
+ # MIT License
2
+
3
+ Copyright (c) 2026-present, A11y Pulse (https://www.a11ypulse.com/)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@a11y-pulse/tab-orchestrator)](https://www.npmjs.com/package/@a11y-pulse/tab-orchestrator)
4
4
  [![CI](https://github.com/A11y-Pulse/audits/actions/workflows/ci.yml/badge.svg)](https://github.com/A11y-Pulse/audits/actions/workflows/ci.yml)
5
- [![License: PolyForm Shield 1.0.0](https://img.shields.io/badge/license-PolyForm%20Shield%201.0.0-blue)](./LICENSE.md)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](./LICENSE.md)
6
6
 
7
7
  A shared Tab-session runner for A11y Pulse's keyboard-driven accessibility audits. It drives a page through its focusable elements once (pressing Tab, tracking visited elements, capturing screenshots and styles on demand) while one or more **consumers** each score the tab stops for their own WCAG success criterion. This lets audits like [`@a11y-pulse/focus-appearance-audit`](../focus-appearance-audit) (WCAG 2.4.7) share a single tab loop with sibling audits instead of each re-tabbing the page from scratch.
8
8
 
9
9
  It is not an audit itself: it has no opinion on what "passing" means. It only owns the mechanics of moving focus, tracking cycle completion, and capturing the primitives (computed styles, clipped screenshots, unfocused/focused pairs) that consumers need.
10
10
 
11
- This package was developed by [A11y Pulse](https://www.a11ypulse.com/) for its accessibility monitoring service. It is released as source-available under the [PolyForm Shield License 1.0.0](#license).
11
+ This package was developed by [A11y Pulse](https://www.a11ypulse.com/) for its accessibility monitoring service. It is released under the [MIT License](#license).
12
12
 
13
13
  ## Install
14
14
 
@@ -16,7 +16,7 @@ This package was developed by [A11y Pulse](https://www.a11ypulse.com/) for its a
16
16
  npm install @a11y-pulse/tab-orchestrator
17
17
  ```
18
18
 
19
- Puppeteer is an optional peer dependency, needed only if you use the bundled [`PuppeteerAdaptor`](#puppeteeradaptor).
19
+ This package drives the page through a [`BrowserAdaptor`](#browseradaptor) you supply. It has no peer dependencies: install [`@a11y-pulse/browser-adaptor`](../browser-adaptor) alongside it for the bundled Puppeteer and Playwright implementations.
20
20
 
21
21
  ## `createTabOrchestrator`
22
22
 
@@ -41,29 +41,20 @@ Most callers won't build a `TabConsumer` by hand. Audit packages export a `creat
41
41
 
42
42
  ## `BrowserAdaptor`
43
43
 
44
- The orchestrator drives a page through an **adaptor**: a small interface of primitives (evaluate JS in the page, press Tab, take a clipped screenshot, etc.) so it never needs to know which browser automation library is behind it.
45
-
46
- | Method | Description |
47
- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
48
- | `evaluate(fn, ...args)` | Runs `fn` in the page context, passing in any serialisable `args`, and returns its result. |
49
- | `evaluateHandle(fn)` | Runs `fn` in the page context and returns an opaque `ElementRef` handle to the `Element` it returns, without serialising it. |
50
- | `disposeRef(ref)` | Releases a handle previously returned by `evaluateHandle`. |
51
- | `pressTab()` | Presses the Tab key, advancing focus to the next focusable element. |
52
- | `screenshotClip(clip)` | Screenshots a clipped region of the page (`{ x, y, width, height }`) and returns PNG bytes. |
53
- | `ensureFocusReporting()` | Ensures the page reports focus for its lifetime, in particular that `document.hasFocus()` works and `:focus` styles apply, even when the page is not the foreground tab/window. Must not throw. |
44
+ The orchestrator drives a page through an **adaptor**: a small interface of primitives (evaluate JS in the page, press Tab, take a clipped screenshot, etc.) so it never needs to know which browser automation library is behind it. The `BrowserAdaptor` interface and its method-by-method reference live in [`@a11y-pulse/browser-adaptor`](../browser-adaptor#browseradaptor); this package does not re-export them.
54
45
 
55
46
  The orchestrator is the only thing that mutates the page through this adaptor during a session; consumers score snapshots, they don't call `pressTab`, take screenshots, or install observers themselves.
56
47
 
57
- ### `PuppeteerAdaptor`
48
+ ### Supplying an adaptor
58
49
 
59
50
  ```js
60
51
  import { createTabOrchestrator } from "@a11y-pulse/tab-orchestrator";
61
- import { PuppeteerAdaptor } from "@a11y-pulse/tab-orchestrator/puppeteer";
52
+ import { PuppeteerAdaptor } from "@a11y-pulse/browser-adaptor/puppeteer";
62
53
 
63
54
  const orchestrator = createTabOrchestrator(new PuppeteerAdaptor(page));
64
55
  ```
65
56
 
66
- The package ships one implementation, `PuppeteerAdaptor`, backed by a Puppeteer `Page`, exported from the `./puppeteer` subpath so consumers that only need the type-level `BrowserAdaptor` interface aren't forced to import Puppeteer. Other environments (Playwright, Selenium, WebDriver) can be supported by implementing `BrowserAdaptor` against your automation library's page/session object; use [`src/adaptors/puppeteer.ts`](./src/adaptors/puppeteer.ts) as a reference implementation.
57
+ This package ships no adaptor of its own. [`@a11y-pulse/browser-adaptor`](../browser-adaptor) provides `BrowserAdaptor` along with two implementations, `PuppeteerAdaptor` and `PlaywrightAdaptor`, so consumers that only need the type-level interface aren't forced to install either automation library. Other environments (Selenium, WebDriver) can be supported by implementing `BrowserAdaptor` against your automation library's page/session object; use [`@a11y-pulse/browser-adaptor`'s `src/adaptors/puppeteer.ts`](../browser-adaptor/src/adaptors/puppeteer.ts) as a reference implementation.
67
58
 
68
59
  ## Releasing
69
60
 
@@ -84,10 +75,7 @@ Trusted Publisher on npm must stay configured for:
84
75
 
85
76
  ## License
86
77
 
87
- Released under the [PolyForm Shield License 1.0.0](./LICENSE.md), in plain language:
88
-
89
- - **Source-available.** The source is public and you can read, fork, and modify it.
90
- - **Permitted for non-competing use.** You can use this package freely in your own products and services, as long as they don't compete with A11y Pulse.
91
- - **Competing products are forbidden.** You may not use this software (or a modified version of it) to build a product or service that competes with A11y Pulse's accessibility monitoring offering.
78
+ [MIT](./LICENSE.md). Use it however you like, including in commercial and competing products.
92
79
 
93
- See [LICENSE.md](./LICENSE.md) for the full, binding terms.
80
+ The audit packages in this repository are licensed separately, under the
81
+ [PolyForm Shield License 1.0.0](../../LICENSE.md).
@@ -3,7 +3,7 @@
3
3
  * not reference any symbols outside their own scope. This also means they cannot share code, so any
4
4
  * common logic (like descending into shadow roots) must be duplicated.
5
5
  */
6
- import type { Rect } from "./adaptor";
6
+ import type { Rect } from "@a11y-pulse/browser-adaptor";
7
7
  import type { StyleSnapshot } from "./types";
8
8
  type BaselineElementEntry = {
9
9
  index: number;
@@ -1,4 +1,4 @@
1
- import type { BrowserAdaptor, ElementRef } from "./adaptor";
1
+ import type { BrowserAdaptor, ElementRef } from "@a11y-pulse/browser-adaptor";
2
2
  export type CaptureScreenshotOptions = {
3
3
  screenshotClipBuffer: number;
4
4
  };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,8 @@
1
- export type { BrowserAdaptor, ElementRef, Rect } from "./adaptor";
2
1
  export type { AuditSelfDisconnect, BaseAuditOptions, BaseAuditSummary, } from "./audit-scaffold";
3
2
  export { createAuditSelfDisconnect, DEFAULT_ELEMENT_LIMIT, DEFAULT_FAILED_ELEMENT_LIMIT, DEFAULT_SCREENSHOT_SETTLE_DELAY, DEFAULT_TIMEOUT, } from "./audit-scaffold";
4
3
  export type { ActiveElementBase, BaselinePayload } from "./browser-scripts";
5
4
  export { activeElementHandleScript, baselineScript, blurScript, clearMarkersScript, elementRectScript, elementStylesScript, focusScript, hasFocusScript, isCenterObscuredScript, pageDimensionsScript, probeActiveElementScript, scrollToCenterScript, } from "./browser-scripts";
6
- export { bufferedClip } from "./clip";
7
5
  export { FOCUS_STYLE_PROPERTIES } from "./focus-style";
8
- export { getSelector } from "./get-selector";
9
6
  export type { TabSessionOptions } from "./orchestrator";
10
7
  export { createTabOrchestrator } from "./orchestrator";
11
- export type { TruncateHtmlOptions } from "./truncate-html";
12
- export { truncateHtml } from "./truncate-html";
13
8
  export type { ActiveElementInfo, Capability, ContextChangeDrain, ContextChangeSignals, ObscuredMeasurement, SessionEndReason, StyleSnapshot, TabConsumer, TabSessionHandle, TabStopSnapshot, UnfocusedPair, } from "./types";
package/dist/index.js CHANGED
@@ -634,20 +634,6 @@ function hasFocusScript() {
634
634
  return document.hasFocus();
635
635
  }
636
636
 
637
- // src/clip.ts
638
- function bufferedClip(rect, pageWidth, pageHeight, buffer) {
639
- const left = Math.max(0, Math.min(buffer, rect.x));
640
- const top = Math.max(0, Math.min(buffer, rect.y));
641
- const right = Math.max(0, Math.min(buffer, pageWidth - rect.x - rect.width));
642
- const bottom = Math.max(0, Math.min(buffer, pageHeight - rect.y - rect.height));
643
- return {
644
- x: Math.max(0, rect.x - left),
645
- y: Math.max(0, rect.y - top),
646
- width: rect.width + left + right,
647
- height: rect.height + top + bottom
648
- };
649
- }
650
-
651
637
  // src/focus-style.ts
652
638
  var FOCUS_STYLE_PROPERTIES = [
653
639
  "background-color",
@@ -673,146 +659,11 @@ var FOCUS_STYLE_PROPERTIES = [
673
659
  "text-decoration"
674
660
  ];
675
661
 
676
- // src/get-selector.ts
677
- function getSelector(root) {
678
- const MAX_LENGTH = 128;
679
- const esc = (value) => typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(value) : value;
680
- const tagName = (node2) => node2.nodeType === 1 ? node2.nodeName.toLowerCase() : node2.nodeName.toUpperCase().replace(/^#/, "");
681
- const rootOf = (element) => {
682
- const node2 = element.getRootNode?.();
683
- return node2 && typeof node2.querySelectorAll === "function" ? node2 : null;
684
- };
685
- const matchCount = (queryRoot2, selector) => {
686
- try {
687
- return queryRoot2.querySelectorAll(selector).length;
688
- } catch {
689
- return Number.POSITIVE_INFINITY;
690
- }
691
- };
692
- const safeMatches = (element, selector) => {
693
- try {
694
- return typeof element.matches === "function" && element.matches(selector);
695
- } catch {
696
- return false;
697
- }
698
- };
699
- const idIsUnique = (element, id) => {
700
- const queryRoot2 = rootOf(element);
701
- return queryRoot2 === null || matchCount(queryRoot2, `#${esc(id)}`) === 1;
702
- };
703
- const classFeature = (cls) => ({
704
- feature: `.${esc(cls)}`,
705
- sharedBy: (sibling) => Array.from(sibling.classList ?? []).includes(cls)
706
- });
707
- const rarestFeature = (element, queryRoot2) => {
708
- const classes = Array.from(element.classList).slice(0, 8);
709
- if (!queryRoot2) {
710
- const cls = classes[0];
711
- return cls === void 0 ? null : classFeature(cls);
712
- }
713
- let best = null;
714
- let bestCount = Number.POSITIVE_INFINITY;
715
- for (const cls of classes) {
716
- const candidate = classFeature(cls);
717
- if (!safeMatches(element, candidate.feature)) {
718
- continue;
719
- }
720
- const count = matchCount(queryRoot2, candidate.feature);
721
- if (count < bestCount) {
722
- best = candidate;
723
- bestCount = count;
724
- }
725
- if (count === 1) {
726
- return best;
727
- }
728
- }
729
- const href = typeof element.getAttribute === "function" ? element.getAttribute("href") : null;
730
- if (href && href.length <= 100 && !/["\\?]/.test(href)) {
731
- const feature = `[href="${href}"]`;
732
- if (safeMatches(element, feature) && matchCount(queryRoot2, feature) < bestCount) {
733
- return {
734
- feature,
735
- sharedBy: (sibling) => typeof sibling.getAttribute === "function" && sibling.getAttribute("href") === href
736
- };
737
- }
738
- }
739
- return best;
740
- };
741
- const positionAmongSiblings = (element, sharedBy) => {
742
- const siblings = element.parentNode?.children;
743
- if (!siblings) {
744
- return { nth: 0, ambiguous: false };
745
- }
746
- let nth = 0;
747
- let count = 0;
748
- let shared = 0;
749
- for (const sibling of Array.from(siblings)) {
750
- if (sibling.nodeName !== element.nodeName) {
751
- continue;
752
- }
753
- count++;
754
- if (sibling === element) {
755
- nth = count;
756
- }
757
- if (sharedBy(sibling)) {
758
- shared++;
759
- }
760
- }
761
- return { nth, ambiguous: shared > 1 };
762
- };
763
- const segmentFor = (element, queryRoot2) => {
764
- if (element.id && idIsUnique(element, element.id)) {
765
- return { segment: `#${esc(element.id)}`, anchored: true };
766
- }
767
- const feature = rarestFeature(element, queryRoot2);
768
- let segment = tagName(element) + (feature === null ? "" : feature.feature);
769
- const { nth, ambiguous } = positionAmongSiblings(
770
- element,
771
- feature === null ? () => true : feature.sharedBy
772
- );
773
- if (ambiguous && nth > 0) {
774
- segment += `:nth-of-type(${nth})`;
775
- }
776
- return { segment, anchored: false };
777
- };
778
- const degradedSegmentFor = (element) => {
779
- let segment = tagName(element);
780
- const { nth, ambiguous } = positionAmongSiblings(element, () => true);
781
- if (ambiguous && nth > 0) {
782
- segment += `:nth-of-type(${nth})`;
783
- }
784
- return segment;
785
- };
786
- const segments = [];
787
- let node = root;
788
- const queryRoot = root && root.nodeType === 1 ? rootOf(root) : null;
789
- try {
790
- while (node && node.nodeType === 1) {
791
- const element = node;
792
- let { segment, anchored } = segmentFor(element, queryRoot);
793
- const joinedWith = (candidate) => segments.length ? candidate.length + 1 + segments.join(">").length : candidate.length;
794
- if (joinedWith(segment) > MAX_LENGTH - 1) {
795
- segment = degradedSegmentFor(element);
796
- anchored = false;
797
- if (joinedWith(segment) > MAX_LENGTH - 1) {
798
- break;
799
- }
800
- }
801
- segments.unshift(segment);
802
- if (anchored) {
803
- break;
804
- }
805
- if (queryRoot && matchCount(queryRoot, segments.join(">")) === 1) {
806
- break;
807
- }
808
- node = element.parentNode;
809
- }
810
- } catch {
811
- }
812
- return segments.join(">");
813
- }
662
+ // src/orchestrator.ts
663
+ import { getSelector, truncateHtml } from "@a11y-pulse/browser-adaptor/dom";
814
664
 
815
665
  // src/capture-screenshot.ts
666
+ import { bufferedClip } from "@a11y-pulse/browser-adaptor";
816
667
  async function captureScreenshot(adaptor, options, handle) {
817
668
  const { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);
818
669
  const rect = await adaptor.evaluate(elementRectScript, handle);
@@ -821,34 +672,8 @@ async function captureScreenshot(adaptor, options, handle) {
821
672
  return adaptor.screenshotClip(clip, scale);
822
673
  }
823
674
 
824
- // src/truncate-html.ts
825
- var DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH = 50;
826
- var DEFAULT_MAX_LENGTH = 300;
827
- var ELLIPSIS = "...";
828
- function truncateHtml(html, options = {}) {
829
- if (!html) {
830
- return "";
831
- }
832
- const maxAttributeValueLength = options.maxAttributeValueLength ?? DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH;
833
- const maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;
834
- const closingBracket = html.indexOf(">");
835
- const openingTag = closingBracket === -1 ? html : html.slice(0, closingBracket + 1);
836
- const trimmed = openingTag.replace(
837
- /=(["'])([\s\S]*?)\1/g,
838
- (match, quote, value) => {
839
- if (value.length <= maxAttributeValueLength) {
840
- return match;
841
- }
842
- return `=${quote}${value.slice(0, maxAttributeValueLength)}${ELLIPSIS}${quote}`;
843
- }
844
- );
845
- if (trimmed.length > maxLength) {
846
- return `${trimmed.slice(0, maxLength)}${ELLIPSIS}`;
847
- }
848
- return trimmed;
849
- }
850
-
851
675
  // src/unfocused-pair.ts
676
+ import { bufferedClip as bufferedClip2 } from "@a11y-pulse/browser-adaptor";
852
677
  async function captureUnfocusedPair(adaptor, options, handle) {
853
678
  if (await adaptor.evaluate(isCenterObscuredScript, handle)) {
854
679
  await adaptor.evaluate(scrollToCenterScript, handle);
@@ -856,7 +681,7 @@ async function captureUnfocusedPair(adaptor, options, handle) {
856
681
  }
857
682
  const { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);
858
683
  const focusedRect = await adaptor.evaluate(elementRectScript, handle);
859
- const focusedClip = bufferedClip(
684
+ const focusedClip = bufferedClip2(
860
685
  focusedRect,
861
686
  pageWidth,
862
687
  pageHeight,
@@ -866,7 +691,7 @@ async function captureUnfocusedPair(adaptor, options, handle) {
866
691
  const focusedScreenshot = await adaptor.screenshotClip(focusedClip, scale);
867
692
  await adaptor.evaluate(blurScript, handle);
868
693
  const unfocusedRect = await adaptor.evaluate(elementRectScript, handle);
869
- const unfocusedClip = bufferedClip(
694
+ const unfocusedClip = bufferedClip2(
870
695
  unfocusedRect,
871
696
  pageWidth,
872
697
  pageHeight,
@@ -892,7 +717,9 @@ async function captureUnfocusedPair(adaptor, options, handle) {
892
717
  // src/orchestrator.ts
893
718
  function isContextDestroyedError(error) {
894
719
  const message = error instanceof Error ? error.message : String(error);
895
- return /Execution context was destroyed|Target closed|frame was detached|navigat/i.test(message);
720
+ return /Execution context was destroyed|Target closed|has been closed|frame was detached|navigat/i.test(
721
+ message
722
+ );
896
723
  }
897
724
  var DEFAULT_SCREENSHOT_SETTLE_DELAY2 = 33;
898
725
  var DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;
@@ -1279,19 +1106,16 @@ export {
1279
1106
  activeElementHandleScript,
1280
1107
  baselineScript,
1281
1108
  blurScript,
1282
- bufferedClip,
1283
1109
  clearMarkersScript,
1284
1110
  createAuditSelfDisconnect,
1285
1111
  createTabOrchestrator,
1286
1112
  elementRectScript,
1287
1113
  elementStylesScript,
1288
1114
  focusScript,
1289
- getSelector,
1290
1115
  hasFocusScript,
1291
1116
  isCenterObscuredScript,
1292
1117
  pageDimensionsScript,
1293
1118
  probeActiveElementScript,
1294
- scrollToCenterScript,
1295
- truncateHtml
1119
+ scrollToCenterScript
1296
1120
  };
1297
1121
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/audit-scaffold.ts","../src/browser-scripts.ts","../src/clip.ts","../src/focus-style.ts","../src/get-selector.ts","../src/capture-screenshot.ts","../src/truncate-html.ts","../src/unfocused-pair.ts","../src/orchestrator.ts"],"sourcesContent":["import type { SessionEndReason, TabSessionHandle } from \"./types\";\n\nexport const DEFAULT_ELEMENT_LIMIT = 1024;\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33; // ~2 frames at 60fps\nexport const DEFAULT_FAILED_ELEMENT_LIMIT = 0; // 0 = never finish early\nexport const DEFAULT_TIMEOUT = 0; // 0 = no timeout\n\nexport type BaseAuditOptions = {\n\t/** Max focusable elements to tab through. */\n\telementLimit?: number;\n\t/** How long to wait (ms) after each Tab for the page to settle. */\n\tscreenshotSettleDelay?: number;\n\t/** Disconnect early after this many elements fail (0 = never). */\n\tfailedElementLimit?: number;\n\t/** Return whatever's gathered so far after this many ms (0 = no timeout). */\n\ttimeout?: number;\n};\n\nexport type BaseAuditSummary = {\n\tchecked: number;\n\tpassed: number;\n\tfailed: number;\n\t/** True if the element limit was hit before tabbing finished. */\n\treachedLimit: boolean;\n\t/** True if the audit stopped early after hitting `failedElementLimit`. */\n\treachedFailedElementLimit: boolean;\n\t/**\n\t * True if the audit returned early because `timeout` elapsed. The results are whatever had been\n\t * gathered when the deadline hit.\n\t */\n\ttimedOut: boolean;\n\t/**\n\t * Why the tab session ended, or `null` when this consumer disconnected itself (element limit,\n\t * failed-element limit, or timeout).\n\t */\n\tsessionEnd: SessionEndReason | null;\n};\n\nexport type AuditSelfDisconnect = {\n\t/** True once this consumer has disconnected itself from the session. */\n\treadonly disconnectedSelf: boolean;\n\t/** Arm a timeout that calls `onTimeout` then disconnects, if `timeout` is greater than 0. */\n\tarmTimeout(timeout: number, session: TabSessionHandle, onTimeout: () => void): void;\n\t/** Disconnect the session and record that this consumer initiated it. */\n\tdisconnect(session: TabSessionHandle): void;\n\t/** Clear any pending timeout without disconnecting. */\n\tclear(): void;\n};\n\n/**\n * Arms an optional self-disconnecting timeout and tracks whether this consumer initiated its own\n * disconnect, so `onSessionEnd` can tell that apart from an external end.\n */\nexport function createAuditSelfDisconnect(): AuditSelfDisconnect {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet disconnectedSelf = false;\n\n\tconst clear = (): void => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\n\tconst disconnect = (session: TabSessionHandle): void => {\n\t\tdisconnectedSelf = true;\n\t\tclear();\n\t\tsession.disconnect();\n\t};\n\n\treturn {\n\t\tget disconnectedSelf() {\n\t\t\treturn disconnectedSelf;\n\t\t},\n\t\tarmTimeout(timeout, session, onTimeout) {\n\t\t\tif (timeout > 0) {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t\tonTimeout();\n\t\t\t\t\tdisconnect(session);\n\t\t\t\t}, timeout);\n\t\t\t}\n\t\t},\n\t\tdisconnect,\n\t\tclear,\n\t};\n}\n","/**\n * IMPORTANT: Functions in this file are serialized and injected into the audited page, so they must\n * not reference any symbols outside their own scope. This also means they cannot share code, so any\n * common logic (like descending into shadow roots) must be duplicated.\n */\n\nimport type { Rect } from \"./adaptor\";\nimport type { StyleSnapshot } from \"./types\";\n\ntype BaselineElementEntry = {\n\tindex: number;\n\t/** Position of this element's snapshot in the payload's `styles` table. */\n\tstyleIndex: number;\n\t/** The element's page-relative rect in its unfocused (baseline) state. */\n\trect: Rect;\n};\n\n/**\n * The baseline as a table of unique style snapshots plus per-element entries referencing them.\n * Elements overwhelmingly share identical snapshots (same classes), so interning keeps the\n * serialised payload O(unique styles) instead of O(elements × style size).\n */\nexport type BaselinePayload = {\n\tstyles: StyleSnapshot[];\n\tentries: BaselineElementEntry[];\n};\n\n/**\n * Find up to `limit` focusable elements and mark them with `markerAttr` and a unique index. Then\n * return a snapshot of their styles to use as a baseline.\n */\nexport function baselineScript(\n\tprops: string[],\n\tmarkerAttr: string,\n\tlimit: number,\n): BaselinePayload {\n\t// Bound oversized computed values (data-URI background images can run to hundreds of KB) while\n\t// preserving inequality via the length and a fingerprint of the full value. Must be\n\t// byte-identical to the copy in probeActiveElementScript, or every oversized value would \"change\"\n\t// on focus and produce a false style-check pass.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst snapshot = (el: Element): StyleSnapshot => {\n\t\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\t\tconst cs = getComputedStyle(el, pseudo);\n\t\t\tconst out: Record<string, string> = {};\n\n\t\t\tfor (const prop of props) {\n\t\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t\t}\n\n\t\t\tif (pseudo) {\n\t\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t\t}\n\n\t\t\treturn out;\n\t\t};\n\n\t\treturn {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t};\n\t};\n\n\tconst candidates: Element[] = [];\n\tconst focusableSelector = [\n\t\t\"a[href]\",\n\t\t\"button\",\n\t\t\"input\",\n\t\t\"select\",\n\t\t\"textarea\",\n\t\t\"[tabindex]\",\n\t\t'[contenteditable=\"\"]',\n\t\t'[contenteditable=\"true\"]',\n\t].join(\", \");\n\n\t// Collect focusable candidates in document order, descending into open shadow roots.\n\tconst collect = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.matches(focusableSelector)) {\n\t\t\t\tcandidates.push(el);\n\t\t\t}\n\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tcollect(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tcollect(document);\n\n\tconst styles: StyleSnapshot[] = [];\n\tconst styleIndexByKey = new Map<string, number>();\n\tconst entries: BaselineElementEntry[] = [];\n\tlet idx = 0;\n\n\tfor (const el of candidates) {\n\t\tif (idx >= limit) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (el.hasAttribute(\"disabled\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((el as HTMLElement).tabIndex < 0) {\n\t\t\t// Skip elements that are focusable but not in the tab order (negative tabindex). The audit\n\t\t\t// tabs through the page, so it never lands on them, and counting them would waste the\n\t\t\t// element-limit budget.\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip non-visible elements so they don't consume the marker budget (e.g. links inside a closed\n\t\t// mega-menu). checkVisibility sees hiding applied by ancestors, which per-element computed\n\t\t// styles don't (`display` doesn't inherit); fall back to the element's own styles where it's\n\t\t// unavailable.\n\t\tconst visibilityCheck = el as Element & {\n\t\t\tcheckVisibility?: (options?: { checkVisibilityCSS?: boolean }) => boolean;\n\t\t};\n\n\t\tif (typeof visibilityCheck.checkVisibility === \"function\") {\n\t\t\tif (!visibilityCheck.checkVisibility({ checkVisibilityCSS: true })) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tconst cs = getComputedStyle(el);\n\n\t\t\tif (cs.display === \"none\" || cs.visibility === \"hidden\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Mark before measuring: the probe reads styles and rects while the marker is present, so the\n\t\t// baseline must be captured in the same DOM state.\n\t\tel.setAttribute(markerAttr, String(idx));\n\n\t\tconst rect = el.getBoundingClientRect();\n\n\t\t// Intern the snapshot: the JSON key is deterministic because every snapshot is built with the\n\t\t// same property insertion order.\n\t\tconst snap = snapshot(el);\n\t\tconst key = JSON.stringify(snap);\n\t\tlet styleIndex = styleIndexByKey.get(key);\n\n\t\tif (styleIndex === undefined) {\n\t\t\tstyleIndex = styles.length;\n\t\t\tstyles.push(snap);\n\t\t\tstyleIndexByKey.set(key, styleIndex);\n\t\t}\n\n\t\tentries.push({\n\t\t\tindex: idx,\n\t\t\tstyleIndex,\n\t\t\trect: {\n\t\t\t\tx: rect.left + window.scrollX,\n\t\t\t\ty: rect.top + window.scrollY,\n\t\t\t\twidth: rect.width,\n\t\t\t\theight: rect.height,\n\t\t\t},\n\t\t});\n\t\tidx++;\n\t}\n\n\treturn { styles, entries };\n}\n\nexport type ActiveElementBase = {\n\tindex: number | null;\n\tisBody: boolean;\n\t/**\n\t * The focused element is an <iframe>. Tabbing into a frame passes focus into its embedded\n\t * document, but the parent's document.activeElement is the frame element itself, so the loop uses\n\t * this to skip the frame as a tab stop.\n\t */\n\tisIframe: boolean;\n\thtml: string;\n\tstyles: StyleSnapshot;\n\trect: Rect;\n};\n\n/**\n * Read the currently focused element (descending open shadow roots), returning its marker index,\n * opening-tag HTML (marker attribute stripped), focus-relevant styles, and page-relative rect.\n */\nexport function probeActiveElementScript(props: string[], markerAttr: string): ActiveElementBase {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\tif (!el || el === document.body) {\n\t\treturn {\n\t\t\tindex: null,\n\t\t\tisBody: true,\n\t\t\tisIframe: false,\n\t\t\thtml: \"\",\n\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t};\n\t}\n\n\t// Must be byte-identical to the copy in baselineScript: snapshots from the two scripts are\n\t// compared for equality, so they have to truncate oversized values the same way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el as Element, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\tconst idxAttr = el.getAttribute(markerAttr);\n\tconst rect = el.getBoundingClientRect();\n\n\t// Shallow clone so only the opening tag is serialised, not the whole subtree; drop the internal\n\t// marker attribute so it never leaks into the snippet.\n\tconst openingTag = el.cloneNode(false) as Element;\n\topeningTag.removeAttribute(markerAttr);\n\n\treturn {\n\t\tindex: idxAttr === null ? null : Number(idxAttr),\n\t\tisBody: false,\n\t\tisIframe: el.tagName === \"IFRAME\",\n\t\thtml: openingTag.outerHTML,\n\t\tstyles: {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t},\n\t\trect: {\n\t\t\tx: rect.left + window.scrollX,\n\t\t\ty: rect.top + window.scrollY,\n\t\t\twidth: rect.width,\n\t\t\theight: rect.height,\n\t\t},\n\t};\n}\n\nexport function activeElementHandleScript(): Element | null {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\treturn el;\n}\n\nexport function pageDimensionsScript(): { width: number; height: number } {\n\treturn {\n\t\twidth: document.documentElement.scrollWidth,\n\t\theight: document.documentElement.scrollHeight,\n\t};\n}\n\nexport function elementRectScript(el: Element | null): Rect {\n\tif (!el) {\n\t\treturn { x: 0, y: 0, width: 0, height: 0 };\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\n\treturn {\n\t\tx: rect.left + window.scrollX,\n\t\ty: rect.top + window.scrollY,\n\t\twidth: rect.width,\n\t\theight: rect.height,\n\t};\n}\n\n/**\n * Whether something unrelated (e.g. a fixed cookie banner) covers the element's centre point, so a\n * screenshot of the element's box would show the covering element instead.\n */\nexport function isCenterObscuredScript(el: Element | null): boolean {\n\tif (!el) {\n\t\treturn false;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst x = Math.min(Math.max(rect.left + rect.width / 2, 0), window.innerWidth - 1);\n\tconst y = Math.min(Math.max(rect.top + rect.height / 2, 0), window.innerHeight - 1);\n\n\tlet hit = document.elementFromPoint(x, y);\n\n\t// Descend into shadow roots so a hit on a shadow host resolves to the inner element.\n\twhile (hit?.shadowRoot) {\n\t\tconst inner = hit.shadowRoot.elementFromPoint(x, y);\n\n\t\tif (!inner || inner === hit) {\n\t\t\tbreak;\n\t\t}\n\n\t\thit = inner;\n\t}\n\n\tif (!hit) {\n\t\treturn false;\n\t}\n\n\treturn hit !== el && !el.contains(hit) && !hit.contains(el);\n}\n\n/**\n * Scroll the element to the centre of the viewport, instantly so a screenshot taken immediately\n * afterwards isn't captured mid-animation.\n */\nexport function scrollToCenterScript(el: Element | null): void {\n\tel?.scrollIntoView({\n\t\tblock: \"center\",\n\t\tinline: \"nearest\",\n\t\tbehavior: \"instant\",\n\t});\n}\n\nexport function blurScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.blur();\n}\n\nexport function focusScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.focus({ preventScroll: true });\n}\n\n/**\n * Read focus-relevant computed styles from a given element. Unlike `probeActiveElementScript`, this\n * does not use `document.activeElement`, so it still works after the node has been blurred.\n */\nexport function elementStylesScript(el: Element | null, props: string[]): StyleSnapshot {\n\tif (!el) {\n\t\treturn { element: {}, before: {}, after: {} };\n\t}\n\n\t// Must be byte-identical to the copies in baselineScript and probeActiveElementScript: snapshots\n\t// from the scripts are compared for equality, so they have to truncate oversized values the same\n\t// way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\treturn {\n\t\telement: read(undefined),\n\t\tbefore: read(\"::before\"),\n\t\tafter: read(\"::after\"),\n\t};\n}\n\n/**\n * Remove the marker attribute from all elements, blur the active element, and scroll back to the\n * top. This is a best-effort attempt to reset state between audits: the tab loop scrolls later\n * elements into view as it walks the page (see scrollToCenterScript), and nothing else undoes that\n * once the session ends.\n */\nexport function clearMarkersScript(markerAttr: string): void {\n\tconst clear = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(`[${markerAttr}]`))) {\n\t\t\tel.removeAttribute(markerAttr);\n\t\t}\n\n\t\t// Descend into any shadow roots to clear markers within them.\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tclear(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tclear(document);\n\n\t(document.activeElement as HTMLElement | null)?.blur();\n\n\twindow.scrollTo(0, 0);\n}\n\nexport type MeasureObscuringResult = {\n\tcoveredFraction: number;\n\tfullyObscured: boolean;\n\toffscreen: boolean;\n\topacity: \"opaque\" | \"semi-transparent\" | \"unknown\";\n\tobscuredByHtml: string | null;\n\t/** True when a covering element was marked for selector resolution. */\n\thasObscurer: boolean;\n};\n\n/**\n * Hit-test whether author-created content entirely hides the focused element. Marks a single\n * covering element with `obscurerAttr` when containment confirms a full cover, so the host can\n * resolve its selector via getSelector.\n */\nexport function measureObscuringScript(\n\tel: Element | null,\n\tobscurerAttr: string,\n): MeasureObscuringResult {\n\tconst empty: MeasureObscuringResult = {\n\t\tcoveredFraction: 0,\n\t\tfullyObscured: false,\n\t\toffscreen: false,\n\t\topacity: \"opaque\",\n\t\tobscuredByHtml: null,\n\t\thasObscurer: false,\n\t};\n\n\tif (!el) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst left = Math.max(rect.left, 0);\n\tconst top = Math.max(rect.top, 0);\n\tconst right = Math.min(rect.right, window.innerWidth);\n\tconst bottom = Math.min(rect.bottom, window.innerHeight);\n\n\tif (right <= left || bottom <= top) {\n\t\treturn { ...empty, offscreen: true };\n\t}\n\n\tconst width = right - left;\n\tconst height = bottom - top;\n\tconst LARGE_ELEMENT_THRESHOLD_PX = 80;\n\n\tconst points: Array<{ x: number; y: number }> = [\n\t\t{ x: left, y: top },\n\t\t{ x: right - 0.5, y: top },\n\t\t{ x: left, y: bottom - 0.5 },\n\t\t{ x: right - 0.5, y: bottom - 0.5 },\n\t\t{ x: left + width / 2, y: top },\n\t\t{ x: left + width / 2, y: bottom - 0.5 },\n\t\t{ x: left, y: top + height / 2 },\n\t\t{ x: right - 0.5, y: top + height / 2 },\n\t\t{ x: left + width / 2, y: top + height / 2 },\n\t];\n\n\tif (width > LARGE_ELEMENT_THRESHOLD_PX || height > LARGE_ELEMENT_THRESHOLD_PX) {\n\t\tpoints.push(\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.75 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.75 },\n\t\t);\n\t}\n\n\tconst hitAt = (x: number, y: number): Element | null => {\n\t\tconst px = Math.min(Math.max(x, 0), window.innerWidth - 1);\n\t\tconst py = Math.min(Math.max(y, 0), window.innerHeight - 1);\n\t\tconst stack =\n\t\t\ttypeof document.elementsFromPoint === \"function\"\n\t\t\t\t? document.elementsFromPoint(px, py)\n\t\t\t\t: [document.elementFromPoint(px, py)].filter(Boolean);\n\n\t\tfor (let candidate of stack as Element[]) {\n\t\t\twhile (candidate?.shadowRoot) {\n\t\t\t\tconst inner = candidate.shadowRoot.elementFromPoint(px, py);\n\n\t\t\t\tif (!inner || inner === candidate) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcandidate = inner;\n\t\t\t}\n\n\t\t\tif (!candidate) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (candidate === el || el.contains(candidate) || candidate.contains(el)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn candidate;\n\t\t}\n\n\t\treturn null;\n\t};\n\n\tlet covered = 0;\n\tconst coverCounts = new Map<Element, number>();\n\n\tfor (const point of points) {\n\t\tconst cover = hitAt(point.x, point.y);\n\n\t\tif (cover) {\n\t\t\tcovered++;\n\t\t\tcoverCounts.set(cover, (coverCounts.get(cover) ?? 0) + 1);\n\t\t}\n\t}\n\n\tconst coveredFraction = points.length === 0 ? 0 : covered / points.length;\n\n\tif (coveredFraction < 1) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction,\n\t\t\tfullyObscured: false,\n\t\t};\n\t}\n\n\tlet topCover: Element | null = null;\n\tlet topCount = 0;\n\n\tfor (const [candidate, count] of coverCounts) {\n\t\tif (count > topCount) {\n\t\t\ttopCover = candidate;\n\t\t\ttopCount = count;\n\t\t}\n\t}\n\n\tif (!topCover) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: false,\n\t\t\topacity: \"unknown\",\n\t\t};\n\t}\n\n\tconst coverRect = topCover.getBoundingClientRect();\n\tconst containsFully =\n\t\tcoverRect.left <= rect.left &&\n\t\tcoverRect.top <= rect.top &&\n\t\tcoverRect.right >= rect.right &&\n\t\tcoverRect.bottom >= rect.bottom;\n\n\tif (!containsFully) {\n\t\t// Joint multi-element cover: cannot confirm a single opaque container.\n\t\treturn {\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: true,\n\t\t\toffscreen: false,\n\t\t\topacity: \"unknown\",\n\t\t\tobscuredByHtml: null,\n\t\t\thasObscurer: false,\n\t\t};\n\t}\n\n\tconst classifyOpacity = (node: Element): \"opaque\" | \"semi-transparent\" | \"unknown\" => {\n\t\tconst cs = getComputedStyle(node);\n\t\tconst opacity = Number.parseFloat(cs.opacity);\n\n\t\tif (!Number.isFinite(opacity)) {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\tif (opacity < 1) {\n\t\t\treturn \"semi-transparent\";\n\t\t}\n\n\t\tconst bg = cs.backgroundColor;\n\t\tconst rgba = bg.match(\n\t\t\t/rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/,\n\t\t);\n\n\t\tif (rgba) {\n\t\t\tconst alpha = rgba[4] === undefined ? 1 : Number.parseFloat(rgba[4]);\n\n\t\t\tif (alpha < 1) {\n\t\t\t\treturn alpha <= 0 ? \"semi-transparent\" : \"semi-transparent\";\n\t\t\t}\n\n\t\t\treturn \"opaque\";\n\t\t}\n\n\t\tif (cs.backgroundImage && cs.backgroundImage !== \"none\") {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\treturn \"unknown\";\n\t};\n\n\tconst opacity = classifyOpacity(topCover);\n\tconst openingTag = topCover.cloneNode(false) as Element;\n\topeningTag.removeAttribute(obscurerAttr);\n\ttopCover.setAttribute(obscurerAttr, \"1\");\n\n\treturn {\n\t\tcoveredFraction: 1,\n\t\tfullyObscured: true,\n\t\toffscreen: false,\n\t\topacity,\n\t\tobscuredByHtml: openingTag.outerHTML,\n\t\thasObscurer: true,\n\t};\n}\n\n/** Return the element marked as the obscurer, if any. */\nexport function obscurerHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-obscurer]\");\n}\n\nexport function clearObscurerScript(obscurerAttr: string): void {\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n}\n\ntype ContextObserverState = {\n\tbaselineHref: string;\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\t/** Elements that received focusin since the last drain. */\n\tfocusIns: Element[];\n\tsoftUrlChange: boolean;\n\tinstalled: boolean;\n};\n\ndeclare global {\n\tinterface Window {\n\t\t__a11yContextObserver?: ContextObserverState;\n\t}\n}\n\n/**\n * Install once: wrap window.open (record + inert stub), capture-phase submit (record +\n * preventDefault), track focusin and soft URL changes.\n */\nexport function installContextObserverScript(): void {\n\tif (window.__a11yContextObserver?.installed) {\n\t\treturn;\n\t}\n\n\tconst state: ContextObserverState = {\n\t\tbaselineHref: location.href,\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusIns: [],\n\t\tsoftUrlChange: false,\n\t\tinstalled: true,\n\t};\n\n\twindow.__a11yContextObserver = state;\n\n\tconst originalOpen = window.open.bind(window);\n\n\twindow.open = (..._args: Parameters<typeof window.open>) => {\n\t\tstate.openedWindow = true;\n\n\t\treturn {\n\t\t\tclosed: true,\n\t\t\tclose() {},\n\t\t\tfocus() {},\n\t\t\tblur() {},\n\t\t\topener: null,\n\t\t\tlocation: { href: \"\" },\n\t\t} as unknown as Window;\n\t};\n\n\t// Keep a reference so tooling does not tree-shake the bind as unused.\n\tvoid originalOpen;\n\n\tdocument.addEventListener(\n\t\t\"submit\",\n\t\t(event) => {\n\t\t\tstate.submittedForm = true;\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t},\n\t\ttrue,\n\t);\n\n\tdocument.addEventListener(\n\t\t\"focus\",\n\t\t(event) => {\n\t\t\tconst target = event.target;\n\n\t\t\t// Prefer nodeType: instanceof can be brittle across some embeddings.\n\t\t\tif (target && (target as Node).nodeType === 1) {\n\t\t\t\tstate.focusIns.push(target as Element);\n\t\t\t}\n\t\t},\n\t\ttrue,\n\t);\n\n\tconst noteSoftNav = (): void => {\n\t\tif (location.href !== state.baselineHref) {\n\t\t\tstate.softUrlChange = true;\n\t\t}\n\t};\n\n\twindow.addEventListener(\"hashchange\", noteSoftNav);\n\twindow.addEventListener(\"popstate\", noteSoftNav);\n\n\tconst wrapHistory = (method: \"pushState\" | \"replaceState\"): void => {\n\t\tconst original = history[method].bind(history);\n\n\t\thistory[method] = ((...args: Parameters<History[\"pushState\"]>) => {\n\t\t\tconst result = original(...args);\n\t\t\tnoteSoftNav();\n\n\t\t\treturn result;\n\t\t}) as History[\"pushState\"];\n\t};\n\n\twrapHistory(\"pushState\");\n\twrapHistory(\"replaceState\");\n}\n\nexport type DrainContextObserverResult = {\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\tfocusRemoved: boolean;\n\tredirect: \"outside\" | \"same-subtree\" | null;\n\tsoftUrlChange: boolean;\n\tnavigation: boolean;\n\tattributedHtml: string | null;\n\t/** True when an attributed element was marked for selector resolution. */\n\thasAttributed: boolean;\n};\n\n/**\n * Drain observer signals for the current tab stop and reset per-stop flags. Classification of focus\n * removal / redirection uses the post-Tab settle activeElement and the focusin history from this\n * stop only.\n */\nexport function drainContextObserverScript(markerAttr: string): DrainContextObserverResult {\n\t// Must be a string literal inside this function: it is serialized into the page.\n\tconst attributedAttr = \"data-a11y-ctx-attr\";\n\n\tconst empty: DrainContextObserverResult = {\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusRemoved: false,\n\t\tredirect: null,\n\t\tsoftUrlChange: false,\n\t\tnavigation: false,\n\t\tattributedHtml: null,\n\t\thasAttributed: false,\n\t};\n\n\tconst state = window.__a11yContextObserver;\n\n\tif (!state) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${attributedAttr}]`))) {\n\t\tmarked.removeAttribute(attributedAttr);\n\t}\n\n\tconst openedWindow = state.openedWindow;\n\tconst submittedForm = state.submittedForm;\n\tconst softUrlChange = state.softUrlChange;\n\tconst focusIns = state.focusIns.slice();\n\n\tstate.openedWindow = false;\n\tstate.submittedForm = false;\n\tstate.softUrlChange = false;\n\tstate.focusIns = [];\n\n\tlet active = document.activeElement;\n\n\twhile (active?.shadowRoot?.activeElement) {\n\t\tactive = active.shadowRoot.activeElement;\n\t}\n\n\tconst intended =\n\t\tfocusIns.find((el) => el !== document.body && el !== document.documentElement) ?? null;\n\n\tconst openingHtml = (node: Element): string => {\n\t\tconst clone = node.cloneNode(false) as Element;\n\t\tclone.removeAttribute(markerAttr);\n\t\tclone.removeAttribute(attributedAttr);\n\n\t\treturn clone.outerHTML;\n\t};\n\n\tlet focusRemoved = false;\n\tlet redirect: \"outside\" | \"same-subtree\" | null = null;\n\tlet attributedHtml: string | null = null;\n\tlet hasAttributed = false;\n\n\tconst bodyOrNone = !active || active === document.body || active === document.documentElement;\n\n\tif (intended && bodyOrNone) {\n\t\tfocusRemoved = true;\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t} else if (intended && active && intended !== active) {\n\t\tconst composedContains = (ancestor: Element, node: Element): boolean => {\n\t\t\tlet current: Node | null = node;\n\n\t\t\twhile (current) {\n\t\t\t\tif (current === ancestor) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tconst parent: Node | null = current.parentNode;\n\n\t\t\t\tif (parent) {\n\t\t\t\t\tcurrent = parent;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (current instanceof ShadowRoot) {\n\t\t\t\t\tcurrent = current.host;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t};\n\n\t\tconst same = composedContains(intended, active) || composedContains(active, intended);\n\n\t\tredirect = same ? \"same-subtree\" : \"outside\";\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t}\n\n\treturn {\n\t\topenedWindow,\n\t\tsubmittedForm,\n\t\tfocusRemoved,\n\t\tredirect,\n\t\tsoftUrlChange,\n\t\tnavigation: false,\n\t\tattributedHtml,\n\t\thasAttributed,\n\t};\n}\n\n/**\n * Return the element marked as attributed by drainContextObserverScript (the element that received\n * a focus event this stop and then lost it), if any.\n */\nexport function attributedHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-ctx-attr]\");\n}\n\nexport function clearAttributedScript(): void {\n\tfor (const marked of Array.from(document.querySelectorAll(\"[data-a11y-ctx-attr]\"))) {\n\t\tmarked.removeAttribute(\"data-a11y-ctx-attr\");\n\t}\n}\n\n/**\n * Drop focusin history accumulated after drain (e.g. pixel-diff blur/refocus) so the next tab stop\n * does not mis-attribute F55 or redirects.\n */\nexport function clearContextFocusInsScript(): void {\n\tconst state = window.__a11yContextObserver;\n\n\tif (state) {\n\t\tstate.focusIns = [];\n\t\tstate.openedWindow = false;\n\t\tstate.submittedForm = false;\n\t\tstate.softUrlChange = false;\n\t}\n}\n\n/** Current location.href for navigation comparison in the host. */\nexport function locationHrefScript(): string {\n\treturn location.href;\n}\n\nexport function hasFocusScript(): boolean {\n\treturn document.hasFocus();\n}\n","import type { Rect } from \"./adaptor\";\n\n/**\n * A screenshot clip framing `rect` with up to `buffer` pixels of padding on each side, shrunk where\n * the page edges leave less room. The padding allows for indicators drawn just outside the element\n * box (outlines, shadows) and for anti-aliasing.\n */\nexport function bufferedClip(\n\trect: Rect,\n\tpageWidth: number,\n\tpageHeight: number,\n\tbuffer: number,\n): Rect {\n\tconst left = Math.max(0, Math.min(buffer, rect.x));\n\tconst top = Math.max(0, Math.min(buffer, rect.y));\n\tconst right = Math.max(0, Math.min(buffer, pageWidth - rect.x - rect.width));\n\tconst bottom = Math.max(0, Math.min(buffer, pageHeight - rect.y - rect.height));\n\n\t// Clamp to non-negative origins: an element positioned partially off the top or left of the\n\t// document has a negative page-relative rect, and Puppeteer rejects a screenshot clip with\n\t// negative x/y.\n\treturn {\n\t\tx: Math.max(0, rect.x - left),\n\t\ty: Math.max(0, rect.y - top),\n\t\twidth: rect.width + left + right,\n\t\theight: rect.height + top + bottom,\n\t};\n}\n","/**\n * Computed-style properties that are likely to draw a visible focus indicator. Layout properties\n * like margin and padding are deliberately excluded.\n */\nexport const FOCUS_STYLE_PROPERTIES = [\n\t\"background-color\",\n\t\"background-image\",\n\t\"border-bottom-color\",\n\t\"border-bottom-style\",\n\t\"border-bottom-width\",\n\t\"border-left-color\",\n\t\"border-left-style\",\n\t\"border-left-width\",\n\t\"border-right-color\",\n\t\"border-right-style\",\n\t\"border-right-width\",\n\t\"border-top-color\",\n\t\"border-top-style\",\n\t\"border-top-width\",\n\t\"box-shadow\",\n\t\"color\",\n\t\"font-weight\",\n\t\"outline-color\",\n\t\"outline-style\",\n\t\"outline-width\",\n\t\"text-decoration\",\n] as const;\n","/**\n * Build a compact, hopefully-unique selector for any given element on the page. This function is\n * serialized and run in the page context, so it must not reference any symbols outside its own\n * scope.\n *\n * Mirrors axe-core's approach: each level contributes the fewest features that discriminate the\n * element (a unique id, the rarest single class, or an :nth-of-type position), and the walk stops\n * as soon as the joined selector matches exactly one element. Selectors stay short because of that\n * early stop, not because of truncation; the length budget is only a backstop.\n */\nexport function getSelector(root: Node | null): string {\n\tconst MAX_LENGTH = 128;\n\n\tconst esc = (value: string): string =>\n\t\ttypeof CSS !== \"undefined\" && typeof CSS.escape === \"function\" ? CSS.escape(value) : value;\n\n\tconst tagName = (node: Node): string =>\n\t\tnode.nodeType === 1\n\t\t\t? node.nodeName.toLowerCase()\n\t\t\t: node.nodeName.toUpperCase().replace(/^#/, \"\");\n\n\t// A querySelectorAll-capable root (Document or ShadowRoot) for the element, used to confirm\n\t// uniqueness and class rarity. Absent on the plain objects used in the serialisation unit tests,\n\t// where we trust ids and walk to the top instead of stopping early.\n\tconst rootOf = (element: Element): ParentNode | null => {\n\t\tconst node = (element as Element & { getRootNode?: () => Node }).getRootNode?.();\n\n\t\treturn node && typeof (node as ParentNode).querySelectorAll === \"function\"\n\t\t\t? (node as ParentNode)\n\t\t\t: null;\n\t};\n\n\tconst matchCount = (queryRoot: ParentNode, selector: string): number => {\n\t\ttry {\n\t\t\treturn queryRoot.querySelectorAll(selector).length;\n\t\t} catch {\n\t\t\t// A selector that fails to parse can't be relied on.\n\t\t\treturn Number.POSITIVE_INFINITY;\n\t\t}\n\t};\n\n\t// A candidate feature is only usable if it actually selects this element; an escaping or engine\n\t// mismatch (e.g. CSS string escapes in an attribute value) could otherwise produce a selector\n\t// that matches nothing. Or a different element entirely.\n\tconst safeMatches = (element: Element, selector: string): boolean => {\n\t\ttry {\n\t\t\treturn typeof element.matches === \"function\" && element.matches(selector);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst idIsUnique = (element: Element, id: string): boolean => {\n\t\tconst queryRoot = rootOf(element);\n\n\t\treturn queryRoot === null || matchCount(queryRoot, `#${esc(id)}`) === 1;\n\t};\n\n\t// A discriminating feature for one level of the selector: its CSS text plus a predicate telling\n\t// whether a sibling would match the same feature (used to decide if an :nth-of-type pin is still\n\t// needed).\n\ttype Feature = {\n\t\tfeature: string;\n\t\tsharedBy: (sibling: Element) => boolean;\n\t};\n\n\tconst classFeature = (cls: string): Feature => ({\n\t\tfeature: `.${esc(cls)}`,\n\t\tsharedBy: (sibling) => Array.from(sibling.classList ?? []).includes(cls),\n\t});\n\n\t// The single feature that narrows the element down the most: the class or href attribute with the\n\t// fewest matches in the root. Hrefs break ties between structurally-identical content blocks,\n\t// where classes and :nth-of-type (which only pins position within one parent) cannot. Without a\n\t// queryable root there is no rarity information, so the first class stands in.\n\tconst rarestFeature = (element: Element, queryRoot: ParentNode | null): Feature | null => {\n\t\tconst classes = Array.from(element.classList).slice(0, 8);\n\n\t\tif (!queryRoot) {\n\t\t\tconst cls = classes[0];\n\n\t\t\treturn cls === undefined ? null : classFeature(cls);\n\t\t}\n\n\t\tlet best: Feature | null = null;\n\t\tlet bestCount = Number.POSITIVE_INFINITY;\n\n\t\tfor (const cls of classes) {\n\t\t\tconst candidate = classFeature(cls);\n\n\t\t\tif (!safeMatches(element, candidate.feature)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst count = matchCount(queryRoot, candidate.feature);\n\n\t\t\tif (count < bestCount) {\n\t\t\t\tbest = candidate;\n\t\t\t\tbestCount = count;\n\t\t\t}\n\n\t\t\tif (count === 1) {\n\t\t\t\treturn best;\n\t\t\t}\n\t\t}\n\n\t\tconst href = typeof element.getAttribute === \"function\" ? element.getAttribute(\"href\") : null;\n\n\t\t// Skip hrefs with characters that change meaning inside a CSS string (quotes, backslashes), and\n\t\t// query strings, which are too volatile across runs to pin a selector to. Fragments (#main)\n\t\t// stay eligible. Skip links make stable discriminators.\n\t\tif (href && href.length <= 100 && !/[\"\\\\?]/.test(href)) {\n\t\t\tconst feature = `[href=\"${href}\"]`;\n\n\t\t\tif (safeMatches(element, feature) && matchCount(queryRoot, feature) < bestCount) {\n\t\t\t\treturn {\n\t\t\t\t\tfeature,\n\t\t\t\t\tsharedBy: (sibling) =>\n\t\t\t\t\t\ttypeof sibling.getAttribute === \"function\" && sibling.getAttribute(\"href\") === href,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn best;\n\t};\n\n\t// The element's 1-based position among same-tag siblings, and whether the chosen feature fails to\n\t// discriminate it from them (so the segment needs an :nth-of-type pin). Siblings are absent on\n\t// the serialisation test nodes. There we skip the pin.\n\tconst positionAmongSiblings = (\n\t\telement: Element,\n\t\tsharedBy: (sibling: Element) => boolean,\n\t): { nth: number; ambiguous: boolean } => {\n\t\tconst siblings = (element.parentNode as ParentNode | null)?.children;\n\n\t\tif (!siblings) {\n\t\t\treturn { nth: 0, ambiguous: false };\n\t\t}\n\n\t\tlet nth = 0;\n\t\tlet count = 0;\n\t\tlet shared = 0;\n\n\t\tfor (const sibling of Array.from(siblings)) {\n\t\t\tif (sibling.nodeName !== element.nodeName) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcount++;\n\n\t\t\tif (sibling === element) {\n\t\t\t\tnth = count;\n\t\t\t}\n\n\t\t\tif (sharedBy(sibling)) {\n\t\t\t\tshared++;\n\t\t\t}\n\t\t}\n\n\t\treturn { nth, ambiguous: shared > 1 };\n\t};\n\n\tconst segmentFor = (\n\t\telement: Element,\n\t\tqueryRoot: ParentNode | null,\n\t): { segment: string; anchored: boolean } => {\n\t\t// Only treat an id as an absolute anchor when it actually resolves to a single element: pages\n\t\t// with duplicate ids would otherwise yield a selector (e.g. \"#dup>a\") that matches several\n\t\t// elements.\n\t\tif (element.id && idIsUnique(element, element.id)) {\n\t\t\treturn { segment: `#${esc(element.id)}`, anchored: true };\n\t\t}\n\n\t\tconst feature = rarestFeature(element, queryRoot);\n\t\tlet segment = tagName(element) + (feature === null ? \"\" : feature.feature);\n\n\t\t// When same-tag siblings share the chosen feature, pin the position so the segment selects\n\t\t// exactly this element rather than all of them.\n\t\tconst { nth, ambiguous } = positionAmongSiblings(\n\t\t\telement,\n\t\t\tfeature === null ? () => true : feature.sharedBy,\n\t\t);\n\n\t\tif (ambiguous && nth > 0) {\n\t\t\tsegment += `:nth-of-type(${nth})`;\n\t\t}\n\n\t\treturn { segment, anchored: false };\n\t};\n\n\t// A minimal segment for when a level's full segment would blow the budget (e.g. an enormous class\n\t// name or a long href): just the tag, pinned by position when same-tag siblings would make the\n\t// bare tag ambiguous.\n\tconst degradedSegmentFor = (element: Element): string => {\n\t\tlet segment = tagName(element);\n\n\t\tconst { nth, ambiguous } = positionAmongSiblings(element, () => true);\n\n\t\tif (ambiguous && nth > 0) {\n\t\t\tsegment += `:nth-of-type(${nth})`;\n\t\t}\n\n\t\treturn segment;\n\t};\n\n\tconst segments: string[] = [];\n\tlet node: Node | null = root;\n\tconst queryRoot = root && root.nodeType === 1 ? rootOf(root as Element) : null;\n\n\ttry {\n\t\t// Walk only element nodes. Stopping at the first non-element parent (the document, or a\n\t\t// ShadowRoot when the target is inside an open shadow root) keeps the selector within its tree\n\t\t// and avoids reading `classList` off a node that has none.\n\t\twhile (node && node.nodeType === 1) {\n\t\t\tconst element = node as Element;\n\t\t\tlet { segment, anchored } = segmentFor(element, queryRoot);\n\n\t\t\t// Keep within the budget (the +1 accounts for the \">\" that would join this segment to the\n\t\t\t// rest). A level whose full segment doesn't fit is degraded to tag + position rather than\n\t\t\t// discarded, so ancestor context is never lost to one long feature.\n\t\t\tconst joinedWith = (candidate: string): number =>\n\t\t\t\tsegments.length ? candidate.length + 1 + segments.join(\">\").length : candidate.length;\n\n\t\t\tif (joinedWith(segment) > MAX_LENGTH - 1) {\n\t\t\t\tsegment = degradedSegmentFor(element);\n\t\t\t\tanchored = false;\n\n\t\t\t\tif (joinedWith(segment) > MAX_LENGTH - 1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsegments.unshift(segment);\n\n\t\t\tif (anchored) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Stop as soon as the selector already identifies a single element. This, not truncation, is\n\t\t\t// what keeps selectors short.\n\t\t\tif (queryRoot && matchCount(queryRoot, segments.join(\">\")) === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnode = element.parentNode;\n\t\t}\n\t} catch {\n\t\t// A malformed node tree must never crash the audit; return what we have.\n\t}\n\n\treturn segments.join(\">\");\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport { elementRectScript, pageDimensionsScript } from \"./browser-scripts\";\nimport { bufferedClip } from \"./clip\";\n\nexport type CaptureScreenshotOptions = {\n\tscreenshotClipBuffer: number;\n};\n\n/**\n * Screenshot the element `handle` currently refers to, clipped to its box plus\n * `screenshotClipBuffer` padding. The caller owns `handle` and must dispose it after this promise\n * settles.\n */\nexport async function captureScreenshot(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureScreenshotOptions,\n\thandle: ElementRef,\n): Promise<Uint8Array> {\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\tconst rect = await adaptor.evaluate(elementRectScript, handle);\n\tconst clip = bufferedClip(rect, pageWidth, pageHeight, options.screenshotClipBuffer);\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\treturn adaptor.screenshotClip(clip, scale);\n}\n","const DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH = 50;\nconst DEFAULT_MAX_LENGTH = 300;\nconst ELLIPSIS = \"...\";\n\nexport type TruncateHtmlOptions = {\n\tmaxAttributeValueLength?: number;\n\tmaxLength?: number;\n};\n\n/**\n * Given an element's outerHTML, produce a truncated version suitable for display in results. For\n * memory efficiency, pass a shallow outerHTML i.e. `Node.cloneNode(false).outerHTML`.\n */\nexport function truncateHtml(html: string, options: TruncateHtmlOptions = {}): string {\n\tif (!html) {\n\t\treturn \"\";\n\t}\n\n\tconst maxAttributeValueLength =\n\t\toptions.maxAttributeValueLength ?? DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH;\n\tconst maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;\n\n\t// Keep only the opening tag\n\tconst closingBracket = html.indexOf(\">\");\n\tconst openingTag = closingBracket === -1 ? html : html.slice(0, closingBracket + 1);\n\n\t// Shorten long quoted attribute values, preserving the quote style.\n\tconst trimmed = openingTag.replace(\n\t\t/=([\"'])([\\s\\S]*?)\\1/g,\n\t\t(match, quote: string, value: string) => {\n\t\t\tif (value.length <= maxAttributeValueLength) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\treturn `=${quote}${value.slice(0, maxAttributeValueLength)}${ELLIPSIS}${quote}`;\n\t\t},\n\t);\n\n\tif (trimmed.length > maxLength) {\n\t\treturn `${trimmed.slice(0, maxLength)}${ELLIPSIS}`;\n\t}\n\n\treturn trimmed;\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport {\n\tblurScript,\n\telementRectScript,\n\telementStylesScript,\n\tfocusScript,\n\tisCenterObscuredScript,\n\tpageDimensionsScript,\n\tscrollToCenterScript,\n} from \"./browser-scripts\";\nimport { bufferedClip } from \"./clip\";\nimport type { UnfocusedPair } from \"./types\";\n\nexport type CaptureUnfocusedPairOptions = {\n\tscreenshotSettleDelay: number;\n\tscreenshotClipBuffer: number;\n\tstyleProps: readonly string[];\n};\n\n/**\n * Capture focused and unfocused screenshots of the same element, plus the unfocused style snapshot\n * and clip anchors appearance needs to score later.\n *\n * The caller owns `handle` and must dispose it after this promise settles.\n */\nexport async function captureUnfocusedPair(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureUnfocusedPairOptions,\n\thandle: ElementRef,\n): Promise<UnfocusedPair> {\n\t// Tab scrolls an element just barely into view at the viewport edge, which is exactly where fixed\n\t// overlays (cookie banners, sticky footers) sit. If something covers the element there, both\n\t// screenshots would show the overlay and no indicator could ever be detected. Centre the element\n\t// in the viewport first, then give the scroll a moment to settle.\n\tif (await adaptor.evaluate(isCenterObscuredScript, handle)) {\n\t\tawait adaptor.evaluate(scrollToCenterScript, handle);\n\n\t\tawait new Promise((resolve) => setTimeout(resolve, options.screenshotSettleDelay));\n\t}\n\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\n\tconst focusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst focusedClip = bufferedClip(\n\t\tfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\t// Capture the focused state first, while focus is genuine: re-focusing the element afterwards\n\t// cannot always restore it (the host of a closed shadow root cannot push focus back inside), so\n\t// the focused frame must be taken before blurring.\n\tconst focusedScreenshot = await adaptor.screenshotClip(focusedClip, scale);\n\n\tawait adaptor.evaluate(blurScript, handle);\n\n\t// Measure the unfocused rect after blurring rather than reusing a stale baseline rect: a :focus\n\t// rule may have moved the element, and a fixed-position element's page-relative rect changes with\n\t// every scroll.\n\tconst unfocusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst unfocusedClip = bufferedClip(\n\t\tunfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst unfocusedScreenshot = await adaptor.screenshotClip(unfocusedClip, scale);\n\n\tconst unfocusedStyles = await adaptor.evaluate(elementStylesScript, handle, [\n\t\t...options.styleProps,\n\t]);\n\n\t// Restore focus so the next Tab advances from this element rather than restarting traversal. (A\n\t// closed-shadow host can't be re-focused into the root; the loop still progresses because Tab\n\t// re-enters from there.)\n\tawait adaptor.evaluate(focusScript, handle);\n\n\treturn {\n\t\tfocusedScreenshot,\n\t\tunfocusedScreenshot,\n\t\tunfocusedStyles,\n\t\tfocusedRect,\n\t\tunfocusedRect,\n\t\tfocusedClip,\n\t\tunfocusedClip,\n\t\tscale,\n\t};\n}\n","import type { BrowserAdaptor, ElementRef } from \"./adaptor\";\nimport {\n\ttype ActiveElementBase,\n\tactiveElementHandleScript,\n\tattributedHandleScript,\n\tbaselineScript,\n\tclearAttributedScript,\n\tclearContextFocusInsScript,\n\tclearMarkersScript,\n\tclearObscurerScript,\n\ttype DrainContextObserverResult,\n\tdrainContextObserverScript,\n\thasFocusScript,\n\tinstallContextObserverScript,\n\tlocationHrefScript,\n\tmeasureObscuringScript,\n\tobscurerHandleScript,\n\tprobeActiveElementScript,\n} from \"./browser-scripts\";\nimport { captureScreenshot } from \"./capture-screenshot\";\nimport { FOCUS_STYLE_PROPERTIES } from \"./focus-style\";\nimport { getSelector } from \"./get-selector\";\nimport { truncateHtml } from \"./truncate-html\";\nimport type {\n\tActiveElementInfo,\n\tCapability,\n\tContextChangeSignals,\n\tSessionEndReason,\n\tStyleSnapshot,\n\tTabConsumer,\n\tTabSessionHandle,\n\tTabStopSnapshot,\n\tUnfocusedPair,\n} from \"./types\";\nimport { captureUnfocusedPair } from \"./unfocused-pair\";\n\n/**\n * Whether an `evaluate()` rejection looks like the page navigated out from\n * under it (execution context destroyed) rather than an unexpected failure.\n * A full navigation is the one context-change signal the in-page observer\n * can never self-report: it destroys the JS context before it can log\n * anything, so the host has to infer it from how `evaluate()` failed.\n */\nfunction isContextDestroyedError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\n\treturn /Execution context was destroyed|Target closed|frame was detached|navigat/i.test(message);\n}\n\n/**\n * Outcome of the early, pre-isBody navigation check. `navigation: true`\n * means a full navigation was detected (via href diff or a destroyed-context\n * error) and the caller must end the session without proceeding any\n * further. Otherwise `raw` carries a drain payload already fetched while\n * resolving a soft URL change (so the later full drain can reuse it instead\n * of reading back state the first call already reset), or `null` when the\n * href was unchanged and nothing has been drained yet for this stop.\n */\ntype ContextNavigationOutcome =\n\t| { navigation: true }\n\t| { navigation: false; raw: DrainContextObserverResult | null };\n\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33;\nexport const DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;\nexport const DEFAULT_MARKER_LIMIT = 1024;\nexport const MARKER_ATTR = \"data-a11y-focus-idx\";\nexport const OBSCURER_ATTR = \"data-a11y-obscurer\";\nexport const OBSCURING_RECHECK_DELAY_MS = 250;\n\nexport type TabSessionOptions = {\n\tscreenshotSettleDelay?: number;\n\tscreenshotClipBuffer?: number;\n\tmarkerLimit?: number;\n\tbaselineElementLimit?: number;\n};\n\nexport type TabOrchestrator = {\n\tattach(consumer: TabConsumer): void;\n\trun(): Promise<void>;\n};\n\nexport function createTabOrchestrator(\n\tadaptor: BrowserAdaptor,\n\toptions: TabSessionOptions = {},\n): TabOrchestrator {\n\tconst consumers: TabConsumer[] = [];\n\tlet started = false;\n\tlet ran = false;\n\n\treturn {\n\t\tattach(consumer) {\n\t\t\tif (started) {\n\t\t\t\tthrow new Error(\"Cannot attach after run() has started\");\n\t\t\t}\n\n\t\t\tconsumers.push(consumer);\n\t\t},\n\t\tasync run() {\n\t\t\tif (ran) {\n\t\t\t\tthrow new Error(\"run() has already been called\");\n\t\t\t}\n\n\t\t\tran = true;\n\t\t\tstarted = true;\n\n\t\t\tif (consumers.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attached = new Set(consumers);\n\t\t\tconst screenshotSettleDelay =\n\t\t\t\toptions.screenshotSettleDelay ?? DEFAULT_SCREENSHOT_SETTLE_DELAY;\n\t\t\tconst screenshotClipBuffer = options.screenshotClipBuffer ?? DEFAULT_SCREENSHOT_CLIP_BUFFER;\n\t\t\tconst markerLimit = Math.max(\n\t\t\t\toptions.markerLimit ?? DEFAULT_MARKER_LIMIT,\n\t\t\t\toptions.baselineElementLimit ?? 0,\n\t\t\t);\n\t\t\tconst styleProps = [...FOCUS_STYLE_PROPERTIES];\n\t\t\tconst resolved = {\n\t\t\t\tscreenshotSettleDelay,\n\t\t\t\tscreenshotClipBuffer,\n\t\t\t\tstyleProps,\n\t\t\t};\n\n\t\t\tlet settleTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\tlet resolveSettle: (() => void) | undefined;\n\t\t\tconst pairByStop = { current: null as Promise<UnfocusedPair> | null };\n\t\t\tconst screenshotByStop = { current: null as Promise<Uint8Array> | null };\n\t\t\tlet activeHandle: ElementRef | undefined;\n\t\t\tlet lastHasAttributed = false;\n\t\t\t// Captured when observers are installed and re-armed after every\n\t\t\t// detected soft URL change, so repeated soft navigations across stops\n\t\t\t// compare against the most recent href rather than the original load.\n\t\t\tlet baselineHref: string | null = null;\n\n\t\t\tconst remainingHas = (capability: Capability): boolean =>\n\t\t\t\t[...attached].some((c) => c.capabilities.has(capability));\n\n\t\t\t// Decided once from who is attached at session start; never re-checked,\n\t\t\t// so the observer stays installed even after every declarer disconnects\n\t\t\t// (session-lifetime setup is never uninstalled, only skipped per-stop).\n\t\t\tconst contextSignalsWanted = remainingHas(\"contextSignals\");\n\n\t\t\tconst handleFor = (consumer: TabConsumer): TabSessionHandle => ({\n\t\t\t\tdisconnect() {\n\t\t\t\t\tattached.delete(consumer);\n\n\t\t\t\t\tif (attached.size === 0 && settleTimer !== undefined) {\n\t\t\t\t\t\tclearTimeout(settleTimer);\n\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\tresolveSettle?.();\n\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tensureUnfocusedPair: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"unfocusedPair capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tpairByStop.current ??= captureUnfocusedPair(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn pairByStop.current;\n\t\t\t\t},\n\t\t\t\tscreenshotClip: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"screenshot capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tscreenshotByStop.current ??= captureScreenshot(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn screenshotByStop.current;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst end = (reason: SessionEndReason): void => {\n\t\t\t\tfor (const consumer of attached) {\n\t\t\t\t\tconsumer.onSessionEnd?.(reason);\n\t\t\t\t}\n\n\t\t\t\tattached.clear();\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Primary navigation detection layer: compares `location.href` against\n\t\t\t * `baselineHref` on every stop, since a full navigation destroys the\n\t\t\t * in-page observer's execution context before it can self-report\n\t\t\t * anything (`drainContextObserverScript`'s own `navigation` field is\n\t\t\t * always `false`). A destroyed context can also simply throw instead\n\t\t\t * of returning a changed href (e.g. the navigation completes between\n\t\t\t * the href read and the drain read), which is treated the same way.\n\t\t\t *\n\t\t\t * Must run before the caller's `isBody`/\"done tabbing\" exit check: a\n\t\t\t * freshly-navigated page's `document.activeElement` defaults to\n\t\t\t * `document.body` unless the new page autofocuses something, so\n\t\t\t * without this proactive check a silent navigation would otherwise be\n\t\t\t * misclassified as `\"completed\"`.\n\t\t\t *\n\t\t\t * Deliberately cheap on the common (non-navigating) path: it only\n\t\t\t * reads `location.href` and does not call `drainContextObserverScript`\n\t\t\t * unless the href actually changed, so a stop that ends the session\n\t\t\t * via the isBody exit right after this check still drains at most\n\t\t\t * once overall (at the later, full-mapping call site).\n\t\t\t */\n\t\t\tasync function checkContextNavigation(): Promise<ContextNavigationOutcome> {\n\t\t\t\tlet href: string;\n\n\t\t\t\ttry {\n\t\t\t\t\thref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tif (baselineHref === null || href === baselineHref) {\n\t\t\t\t\treturn { navigation: false, raw: null };\n\t\t\t\t}\n\n\t\t\t\t// Document navigated (or soft URL change). Prefer the observer's\n\t\t\t\t// own soft-nav flag when the execution context survived;\n\t\t\t\t// otherwise treat it as a full navigation.\n\t\t\t\tlet raw: DrainContextObserverResult | null = null;\n\n\t\t\t\ttry {\n\t\t\t\t\traw = await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\traw = null;\n\t\t\t\t}\n\n\t\t\t\tif (raw === null || !raw.softUrlChange) {\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tbaselineHref = href;\n\n\t\t\t\treturn { navigation: false, raw };\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Whether the page reports focus, re-asserting focus reporting once\n\t\t\t * before answering no.\n\t\t\t */\n\t\t\tasync function documentHasFocus(): Promise<boolean> {\n\t\t\t\tif (await adaptor.evaluate(hasFocusScript)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Another target taking the foreground clears focus emulation.\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\treturn adaptor.evaluate(hasFocusScript);\n\t\t\t}\n\n\t\t\tlet failure: unknown;\n\n\t\t\ttry {\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\tconst payload = await adaptor.evaluate(\n\t\t\t\t\tbaselineScript,\n\t\t\t\t\tstyleProps,\n\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\tmarkerLimit,\n\t\t\t\t);\n\t\t\t\tconst interned = new Map<number, StyleSnapshot>();\n\n\t\t\t\tfor (const entry of payload.entries) {\n\t\t\t\t\tconst styles = payload.styles[entry.styleIndex];\n\n\t\t\t\t\tif (styles !== undefined) {\n\t\t\t\t\t\tinterned.set(entry.index, styles);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\tbaselineHref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t\tawait adaptor.evaluate(installContextObserverScript);\n\t\t\t\t}\n\n\t\t\t\tfor (const consumer of [...attached]) {\n\t\t\t\t\tawait consumer.onSessionStart?.(handleFor(consumer));\n\t\t\t\t}\n\n\t\t\t\tconst visited = new Set<number>();\n\t\t\t\tlet tabIndex = 0;\n\n\t\t\t\twhile (attached.size > 0) {\n\t\t\t\t\tpairByStop.current = null;\n\t\t\t\t\tscreenshotByStop.current = null;\n\t\t\t\t\tlastHasAttributed = false;\n\n\t\t\t\t\tif (!(await documentHasFocus())) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tawait adaptor.pressTab();\n\t\t\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t\t\tresolveSettle = resolve;\n\t\t\t\t\t\tsettleTimer = setTimeout(() => {\n\t\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}, screenshotSettleDelay);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (attached.size === 0) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Chromium only matches :focus while the document reports focus, so\n\t\t\t\t\t// an element measured after a loss reads as having no indicator\n\t\t\t\t\t// however correct its CSS is. Read here, at the point of\n\t\t\t\t\t// measurement, rather than relying on the pre-tab check. Acted on\n\t\t\t\t\t// below, once the probe has said whether there was anything to\n\t\t\t\t\t// measure.\n\t\t\t\t\tconst focusedAtMeasurement = await documentHasFocus();\n\n\t\t\t\t\tlet base: ActiveElementBase | null;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbase = await adaptor.evaluate(probeActiveElementScript, styleProps, MARKER_ATTR);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\") && isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t// Mid-flight navigation destroyed the execution context\n\t\t\t\t\t\t\t// before the probe could even run: there is no active\n\t\t\t\t\t\t\t// element to report, so end directly (mirrors the\n\t\t\t\t\t\t\t// isBody/\"done tabbing\" exit below, which also ends\n\t\t\t\t\t\t\t// without a snapshot when there is nothing to notify).\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\t// The press that leaves the last element takes focus out of the\n\t\t\t\t\t// document with it, so an unfocused page probing as <body> is the\n\t\t\t\t\t// tab order running out, not a stolen focus. Re-asserting focus\n\t\t\t\t\t// emulation cannot bring that back either. Leave it to the isBody\n\t\t\t\t\t// exit below and treat only a real element measured without focus\n\t\t\t\t\t// as a genuine loss.\n\t\t\t\t\tif (!focusedAtMeasurement && base !== null && !base.isBody) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Primary navigation detection layer, run before the isBody exit:\n\t\t\t\t\t// a silently-navigated page's evaluate() calls succeed against the\n\t\t\t\t\t// new document (Puppeteer/CDP targets whichever context is live at\n\t\t\t\t\t// call time), so a hard nav can surface as an ordinary `isBody: true`\n\t\t\t\t\t// probe result with no thrown error at all.\n\t\t\t\t\tlet contextNav: ContextNavigationOutcome | null = null;\n\n\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\tconst outcome = await checkContextNavigation();\n\n\t\t\t\t\t\tif (outcome.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontextNav = outcome;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base === null || base.isBody) {\n\t\t\t\t\t\t// Focus landed on <body>. This is normally \"done tabbing\", but it\n\t\t\t\t\t\t// is also what F55 focus-removal looks like (an element receives\n\t\t\t\t\t\t// focus and then blurs itself, e.g. `onfocus=\"this.blur()\"`): the\n\t\t\t\t\t\t// in-page observer's focusin history still attributes that stop to\n\t\t\t\t\t\t// the element that briefly held focus, even though nothing is\n\t\t\t\t\t\t// focused now. Drain before ending so that case gets one last\n\t\t\t\t\t\t// notification instead of vanishing silently.\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet raw: DrainContextObserverResult;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\traw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Mirrors the probe's own destroyed-context catch above:\n\t\t\t\t\t\t\t\t// there is no attributed element left to report, so end\n\t\t\t\t\t\t\t\t// directly with \"navigation\" rather than \"completed\" (a\n\t\t\t\t\t\t\t\t// navigation signal always ends the session that way).\n\t\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (raw.hasAttributed) {\n\t\t\t\t\t\t\t\tconst attributedRef = await adaptor.evaluateHandle(attributedHandleScript);\n\t\t\t\t\t\t\t\tlet selector: string;\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tselector = await adaptor.evaluate(getSelector, attributedRef);\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(attributedRef);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t\t\tindex: null,\n\t\t\t\t\t\t\t\t\tisBody: false,\n\t\t\t\t\t\t\t\t\tisIframe: false,\n\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.attributedHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\t\t\t\t\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t\t\t\tcontextSignals: {\n\t\t\t\t\t\t\t\t\t\tsignals: {\n\t\t\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t// This synthetic stop has no live focused element (it\n\t\t\t\t\t\t\t\t// already blurred itself before this notification is\n\t\t\t\t\t\t\t\t// built, by definition of F55), so capabilities that\n\t\t\t\t\t\t\t\t// need one (`unfocusedPair`, `baselineStyles`,\n\t\t\t\t\t\t\t\t// `obscuring`) have nothing to meaningfully act on.\n\t\t\t\t\t\t\t\t// Only consumers that declared `contextSignals` (the\n\t\t\t\t\t\t\t\t// capability this whole synthetic-stop mechanism exists\n\t\t\t\t\t\t\t\t// to serve) are notified; unlike every other stop in\n\t\t\t\t\t\t\t\t// this file, uniform notification is wrong here because\n\t\t\t\t\t\t\t\t// there is no element for the other capabilities to\n\t\t\t\t\t\t\t\t// read from.\n\t\t\t\t\t\t\t\tconst recipients = [...attached].filter((consumer) =>\n\t\t\t\t\t\t\t\t\tconsumer.capabilities.has(\"contextSignals\"),\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\t// Guaranteed even if a recipient's onTabStop throws,\n\t\t\t\t\t\t\t\t\t// so the `data-a11y-ctx-attr` DOM marker never survives\n\t\t\t\t\t\t\t\t\t// past this stop (mirrors how `disposeRef` is\n\t\t\t\t\t\t\t\t\t// guaranteed via `finally` in the main per-stop loop\n\t\t\t\t\t\t\t\t\t// below).\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.isIframe) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.index !== null) {\n\t\t\t\t\t\tif (visited.has(base.index)) {\n\t\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvisited.add(base.index);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst ref = await adaptor.evaluateHandle(activeElementHandleScript);\n\t\t\t\t\tactiveHandle = ref;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst selector = await adaptor.evaluate(getSelector, ref);\n\n\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t...base,\n\t\t\t\t\t\t\thtml: truncateHtml(base.html),\n\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (remainingHas(\"baselineStyles\") && activeElement.index !== null) {\n\t\t\t\t\t\t\tconst baselineStyles = interned.get(activeElement.index);\n\n\t\t\t\t\t\t\tif (baselineStyles !== undefined) {\n\t\t\t\t\t\t\t\tsnapshot.baselineStyles = baselineStyles;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet signals: ContextChangeSignals;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Reuse the drain already fetched above while resolving\n\t\t\t\t\t\t\t\t// a soft URL change (checkContextNavigation), if any;\n\t\t\t\t\t\t\t\t// re-draining here would read back state that call\n\t\t\t\t\t\t\t\t// already reset (focusIns, softUrlChange, etc.) and\n\t\t\t\t\t\t\t\t// silently lose whatever it was reporting. Otherwise\n\t\t\t\t\t\t\t\t// (the common case: href unchanged, nothing drained\n\t\t\t\t\t\t\t\t// yet) do the one drain this stop needs.\n\t\t\t\t\t\t\t\tconst raw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = raw.hasAttributed;\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// The page navigated out from under the drain call: the\n\t\t\t\t\t\t\t\t// in-page observer's own state (and any DOM markers it\n\t\t\t\t\t\t\t\t// set) went with it, so there is nothing left to report\n\t\t\t\t\t\t\t\t// besides the navigation itself.\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: false,\n\t\t\t\t\t\t\t\t\tsubmittedForm: false,\n\t\t\t\t\t\t\t\t\tfocusRemoved: false,\n\t\t\t\t\t\t\t\t\tredirect: null,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: false,\n\t\t\t\t\t\t\t\t\tnavigation: true,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.contextSignals = { signals };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"obscuring\")) {\n\t\t\t\t\t\t\tlet raw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\n\t\t\t\t\t\t\tif (raw.fullyObscured) {\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, OBSCURING_RECHECK_DELAY_MS));\n\t\t\t\t\t\t\t\traw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tlet obscuredBy: { selector: string; html: string } | null = null;\n\n\t\t\t\t\t\t\tif (raw.hasObscurer) {\n\t\t\t\t\t\t\t\tconst obscurer = await adaptor.evaluateHandle(obscurerHandleScript);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst obscurerSelector = await adaptor.evaluate(getSelector, obscurer);\n\t\t\t\t\t\t\t\t\tobscuredBy = {\n\t\t\t\t\t\t\t\t\t\tselector: obscurerSelector,\n\t\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.obscuredByHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(obscurer);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.obscuring = {\n\t\t\t\t\t\t\t\tcoveredFraction: raw.coveredFraction,\n\t\t\t\t\t\t\t\tfullyObscured: raw.fullyObscured,\n\t\t\t\t\t\t\t\toffscreen: raw.offscreen,\n\t\t\t\t\t\t\t\topacity: raw.opacity,\n\t\t\t\t\t\t\t\tobscuredBy,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tawait adaptor.evaluate(clearObscurerScript, OBSCURER_ATTR);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst recipients = [...attached];\n\n\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (snapshot.contextSignals?.signals.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (pairByStop.current !== null) {\n\t\t\t\t\t\t\t\tawait pairByStop.current;\n\n\t\t\t\t\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\t\t\t\t\t// ensureUnfocusedPair blurred and re-focused the element to\n\t\t\t\t\t\t\t\t\t// capture the unfocused screenshot; drop that noise so the\n\t\t\t\t\t\t\t\t\t// next drain doesn't misread it as a real focus-removal or\n\t\t\t\t\t\t\t\t\t// redirect.\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearContextFocusInsScript);\n\n\t\t\t\t\t\t\t\t\tif (lastHasAttributed) {\n\t\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Capture errors already surface through the consumer that\n\t\t\t\t\t\t\t// awaited; still dispose the handle below.\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tactiveHandle = undefined;\n\t\t\t\t\t\tawait adaptor.disposeRef(ref);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tfailure = error;\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait adaptor.evaluate(clearMarkersScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\t// Isolate teardown so a marker-clear throw cannot replace the loop error.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (failure !== undefined) {\n\t\t\t\tend(\"failed\");\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["opacity","node","queryRoot","DEFAULT_SCREENSHOT_SETTLE_DELAY"]}
1
+ {"version":3,"sources":["../src/audit-scaffold.ts","../src/browser-scripts.ts","../src/focus-style.ts","../src/orchestrator.ts","../src/capture-screenshot.ts","../src/unfocused-pair.ts"],"sourcesContent":["import type { SessionEndReason, TabSessionHandle } from \"./types\";\n\nexport const DEFAULT_ELEMENT_LIMIT = 1024;\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33; // ~2 frames at 60fps\nexport const DEFAULT_FAILED_ELEMENT_LIMIT = 0; // 0 = never finish early\nexport const DEFAULT_TIMEOUT = 0; // 0 = no timeout\n\nexport type BaseAuditOptions = {\n\t/** Max focusable elements to tab through. */\n\telementLimit?: number;\n\t/** How long to wait (ms) after each Tab for the page to settle. */\n\tscreenshotSettleDelay?: number;\n\t/** Disconnect early after this many elements fail (0 = never). */\n\tfailedElementLimit?: number;\n\t/** Return whatever's gathered so far after this many ms (0 = no timeout). */\n\ttimeout?: number;\n};\n\nexport type BaseAuditSummary = {\n\tchecked: number;\n\tpassed: number;\n\tfailed: number;\n\t/** True if the element limit was hit before tabbing finished. */\n\treachedLimit: boolean;\n\t/** True if the audit stopped early after hitting `failedElementLimit`. */\n\treachedFailedElementLimit: boolean;\n\t/**\n\t * True if the audit returned early because `timeout` elapsed. The results are whatever had been\n\t * gathered when the deadline hit.\n\t */\n\ttimedOut: boolean;\n\t/**\n\t * Why the tab session ended, or `null` when this consumer disconnected itself (element limit,\n\t * failed-element limit, or timeout).\n\t */\n\tsessionEnd: SessionEndReason | null;\n};\n\nexport type AuditSelfDisconnect = {\n\t/** True once this consumer has disconnected itself from the session. */\n\treadonly disconnectedSelf: boolean;\n\t/** Arm a timeout that calls `onTimeout` then disconnects, if `timeout` is greater than 0. */\n\tarmTimeout(timeout: number, session: TabSessionHandle, onTimeout: () => void): void;\n\t/** Disconnect the session and record that this consumer initiated it. */\n\tdisconnect(session: TabSessionHandle): void;\n\t/** Clear any pending timeout without disconnecting. */\n\tclear(): void;\n};\n\n/**\n * Arms an optional self-disconnecting timeout and tracks whether this consumer initiated its own\n * disconnect, so `onSessionEnd` can tell that apart from an external end.\n */\nexport function createAuditSelfDisconnect(): AuditSelfDisconnect {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet disconnectedSelf = false;\n\n\tconst clear = (): void => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\n\tconst disconnect = (session: TabSessionHandle): void => {\n\t\tdisconnectedSelf = true;\n\t\tclear();\n\t\tsession.disconnect();\n\t};\n\n\treturn {\n\t\tget disconnectedSelf() {\n\t\t\treturn disconnectedSelf;\n\t\t},\n\t\tarmTimeout(timeout, session, onTimeout) {\n\t\t\tif (timeout > 0) {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t\tonTimeout();\n\t\t\t\t\tdisconnect(session);\n\t\t\t\t}, timeout);\n\t\t\t}\n\t\t},\n\t\tdisconnect,\n\t\tclear,\n\t};\n}\n","/**\n * IMPORTANT: Functions in this file are serialized and injected into the audited page, so they must\n * not reference any symbols outside their own scope. This also means they cannot share code, so any\n * common logic (like descending into shadow roots) must be duplicated.\n */\n\nimport type { Rect } from \"@a11y-pulse/browser-adaptor\";\nimport type { StyleSnapshot } from \"./types\";\n\ntype BaselineElementEntry = {\n\tindex: number;\n\t/** Position of this element's snapshot in the payload's `styles` table. */\n\tstyleIndex: number;\n\t/** The element's page-relative rect in its unfocused (baseline) state. */\n\trect: Rect;\n};\n\n/**\n * The baseline as a table of unique style snapshots plus per-element entries referencing them.\n * Elements overwhelmingly share identical snapshots (same classes), so interning keeps the\n * serialised payload O(unique styles) instead of O(elements × style size).\n */\nexport type BaselinePayload = {\n\tstyles: StyleSnapshot[];\n\tentries: BaselineElementEntry[];\n};\n\n/**\n * Find up to `limit` focusable elements and mark them with `markerAttr` and a unique index. Then\n * return a snapshot of their styles to use as a baseline.\n */\nexport function baselineScript(\n\tprops: string[],\n\tmarkerAttr: string,\n\tlimit: number,\n): BaselinePayload {\n\t// Bound oversized computed values (data-URI background images can run to hundreds of KB) while\n\t// preserving inequality via the length and a fingerprint of the full value. Must be\n\t// byte-identical to the copy in probeActiveElementScript, or every oversized value would \"change\"\n\t// on focus and produce a false style-check pass.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst snapshot = (el: Element): StyleSnapshot => {\n\t\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\t\tconst cs = getComputedStyle(el, pseudo);\n\t\t\tconst out: Record<string, string> = {};\n\n\t\t\tfor (const prop of props) {\n\t\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t\t}\n\n\t\t\tif (pseudo) {\n\t\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t\t}\n\n\t\t\treturn out;\n\t\t};\n\n\t\treturn {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t};\n\t};\n\n\tconst candidates: Element[] = [];\n\tconst focusableSelector = [\n\t\t\"a[href]\",\n\t\t\"button\",\n\t\t\"input\",\n\t\t\"select\",\n\t\t\"textarea\",\n\t\t\"[tabindex]\",\n\t\t'[contenteditable=\"\"]',\n\t\t'[contenteditable=\"true\"]',\n\t].join(\", \");\n\n\t// Collect focusable candidates in document order, descending into open shadow roots.\n\tconst collect = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.matches(focusableSelector)) {\n\t\t\t\tcandidates.push(el);\n\t\t\t}\n\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tcollect(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tcollect(document);\n\n\tconst styles: StyleSnapshot[] = [];\n\tconst styleIndexByKey = new Map<string, number>();\n\tconst entries: BaselineElementEntry[] = [];\n\tlet idx = 0;\n\n\tfor (const el of candidates) {\n\t\tif (idx >= limit) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (el.hasAttribute(\"disabled\")) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((el as HTMLElement).tabIndex < 0) {\n\t\t\t// Skip elements that are focusable but not in the tab order (negative tabindex). The audit\n\t\t\t// tabs through the page, so it never lands on them, and counting them would waste the\n\t\t\t// element-limit budget.\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Skip non-visible elements so they don't consume the marker budget (e.g. links inside a closed\n\t\t// mega-menu). checkVisibility sees hiding applied by ancestors, which per-element computed\n\t\t// styles don't (`display` doesn't inherit); fall back to the element's own styles where it's\n\t\t// unavailable.\n\t\tconst visibilityCheck = el as Element & {\n\t\t\tcheckVisibility?: (options?: { checkVisibilityCSS?: boolean }) => boolean;\n\t\t};\n\n\t\tif (typeof visibilityCheck.checkVisibility === \"function\") {\n\t\t\tif (!visibilityCheck.checkVisibility({ checkVisibilityCSS: true })) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tconst cs = getComputedStyle(el);\n\n\t\t\tif (cs.display === \"none\" || cs.visibility === \"hidden\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Mark before measuring: the probe reads styles and rects while the marker is present, so the\n\t\t// baseline must be captured in the same DOM state.\n\t\tel.setAttribute(markerAttr, String(idx));\n\n\t\tconst rect = el.getBoundingClientRect();\n\n\t\t// Intern the snapshot: the JSON key is deterministic because every snapshot is built with the\n\t\t// same property insertion order.\n\t\tconst snap = snapshot(el);\n\t\tconst key = JSON.stringify(snap);\n\t\tlet styleIndex = styleIndexByKey.get(key);\n\n\t\tif (styleIndex === undefined) {\n\t\t\tstyleIndex = styles.length;\n\t\t\tstyles.push(snap);\n\t\t\tstyleIndexByKey.set(key, styleIndex);\n\t\t}\n\n\t\tentries.push({\n\t\t\tindex: idx,\n\t\t\tstyleIndex,\n\t\t\trect: {\n\t\t\t\tx: rect.left + window.scrollX,\n\t\t\t\ty: rect.top + window.scrollY,\n\t\t\t\twidth: rect.width,\n\t\t\t\theight: rect.height,\n\t\t\t},\n\t\t});\n\t\tidx++;\n\t}\n\n\treturn { styles, entries };\n}\n\nexport type ActiveElementBase = {\n\tindex: number | null;\n\tisBody: boolean;\n\t/**\n\t * The focused element is an <iframe>. Tabbing into a frame passes focus into its embedded\n\t * document, but the parent's document.activeElement is the frame element itself, so the loop uses\n\t * this to skip the frame as a tab stop.\n\t */\n\tisIframe: boolean;\n\thtml: string;\n\tstyles: StyleSnapshot;\n\trect: Rect;\n};\n\n/**\n * Read the currently focused element (descending open shadow roots), returning its marker index,\n * opening-tag HTML (marker attribute stripped), focus-relevant styles, and page-relative rect.\n */\nexport function probeActiveElementScript(props: string[], markerAttr: string): ActiveElementBase {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\tif (!el || el === document.body) {\n\t\treturn {\n\t\t\tindex: null,\n\t\t\tisBody: true,\n\t\t\tisIframe: false,\n\t\t\thtml: \"\",\n\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t};\n\t}\n\n\t// Must be byte-identical to the copy in baselineScript: snapshots from the two scripts are\n\t// compared for equality, so they have to truncate oversized values the same way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el as Element, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\tconst idxAttr = el.getAttribute(markerAttr);\n\tconst rect = el.getBoundingClientRect();\n\n\t// Shallow clone so only the opening tag is serialised, not the whole subtree; drop the internal\n\t// marker attribute so it never leaks into the snippet.\n\tconst openingTag = el.cloneNode(false) as Element;\n\topeningTag.removeAttribute(markerAttr);\n\n\treturn {\n\t\tindex: idxAttr === null ? null : Number(idxAttr),\n\t\tisBody: false,\n\t\tisIframe: el.tagName === \"IFRAME\",\n\t\thtml: openingTag.outerHTML,\n\t\tstyles: {\n\t\t\telement: read(undefined),\n\t\t\tbefore: read(\"::before\"),\n\t\t\tafter: read(\"::after\"),\n\t\t},\n\t\trect: {\n\t\t\tx: rect.left + window.scrollX,\n\t\t\ty: rect.top + window.scrollY,\n\t\t\twidth: rect.width,\n\t\t\theight: rect.height,\n\t\t},\n\t};\n}\n\nexport function activeElementHandleScript(): Element | null {\n\tlet el = document.activeElement;\n\n\twhile (el?.shadowRoot?.activeElement) {\n\t\tel = el.shadowRoot.activeElement;\n\t}\n\n\treturn el;\n}\n\nexport function pageDimensionsScript(): { width: number; height: number } {\n\treturn {\n\t\twidth: document.documentElement.scrollWidth,\n\t\theight: document.documentElement.scrollHeight,\n\t};\n}\n\nexport function elementRectScript(el: Element | null): Rect {\n\tif (!el) {\n\t\treturn { x: 0, y: 0, width: 0, height: 0 };\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\n\treturn {\n\t\tx: rect.left + window.scrollX,\n\t\ty: rect.top + window.scrollY,\n\t\twidth: rect.width,\n\t\theight: rect.height,\n\t};\n}\n\n/**\n * Whether something unrelated (e.g. a fixed cookie banner) covers the element's centre point, so a\n * screenshot of the element's box would show the covering element instead.\n */\nexport function isCenterObscuredScript(el: Element | null): boolean {\n\tif (!el) {\n\t\treturn false;\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst x = Math.min(Math.max(rect.left + rect.width / 2, 0), window.innerWidth - 1);\n\tconst y = Math.min(Math.max(rect.top + rect.height / 2, 0), window.innerHeight - 1);\n\n\tlet hit = document.elementFromPoint(x, y);\n\n\t// Descend into shadow roots so a hit on a shadow host resolves to the inner element.\n\twhile (hit?.shadowRoot) {\n\t\tconst inner = hit.shadowRoot.elementFromPoint(x, y);\n\n\t\tif (!inner || inner === hit) {\n\t\t\tbreak;\n\t\t}\n\n\t\thit = inner;\n\t}\n\n\tif (!hit) {\n\t\treturn false;\n\t}\n\n\treturn hit !== el && !el.contains(hit) && !hit.contains(el);\n}\n\n/**\n * Scroll the element to the centre of the viewport, instantly so a screenshot taken immediately\n * afterwards isn't captured mid-animation.\n */\nexport function scrollToCenterScript(el: Element | null): void {\n\tel?.scrollIntoView({\n\t\tblock: \"center\",\n\t\tinline: \"nearest\",\n\t\tbehavior: \"instant\",\n\t});\n}\n\nexport function blurScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.blur();\n}\n\nexport function focusScript(el: Element | null): void {\n\t(el as HTMLElement | null)?.focus({ preventScroll: true });\n}\n\n/**\n * Read focus-relevant computed styles from a given element. Unlike `probeActiveElementScript`, this\n * does not use `document.activeElement`, so it still works after the node has been blurred.\n */\nexport function elementStylesScript(el: Element | null, props: string[]): StyleSnapshot {\n\tif (!el) {\n\t\treturn { element: {}, before: {}, after: {} };\n\t}\n\n\t// Must be byte-identical to the copies in baselineScript and probeActiveElementScript: snapshots\n\t// from the scripts are compared for equality, so they have to truncate oversized values the same\n\t// way.\n\tconst truncateValue = (value: string): string => {\n\t\tconst MAX_STYLE_VALUE_LENGTH = 512;\n\n\t\tif (value.length <= MAX_STYLE_VALUE_LENGTH) {\n\t\t\treturn value;\n\t\t}\n\n\t\tlet hash = 5381;\n\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\thash = ((hash * 33) ^ value.charCodeAt(i)) >>> 0;\n\t\t}\n\n\t\treturn `${value.slice(0, MAX_STYLE_VALUE_LENGTH)}#len=${value.length}#h=${hash.toString(36)}`;\n\t};\n\n\tconst read = (pseudo: string | undefined): Record<string, string> => {\n\t\tconst cs = getComputedStyle(el, pseudo);\n\t\tconst out: Record<string, string> = {};\n\n\t\tfor (const prop of props) {\n\t\t\tout[prop] = truncateValue(cs.getPropertyValue(prop));\n\t\t}\n\n\t\tif (pseudo) {\n\t\t\tout.content = truncateValue(cs.getPropertyValue(\"content\"));\n\t\t}\n\n\t\treturn out;\n\t};\n\n\treturn {\n\t\telement: read(undefined),\n\t\tbefore: read(\"::before\"),\n\t\tafter: read(\"::after\"),\n\t};\n}\n\n/**\n * Remove the marker attribute from all elements, blur the active element, and scroll back to the\n * top. This is a best-effort attempt to reset state between audits: the tab loop scrolls later\n * elements into view as it walks the page (see scrollToCenterScript), and nothing else undoes that\n * once the session ends.\n */\nexport function clearMarkersScript(markerAttr: string): void {\n\tconst clear = (root: Document | ShadowRoot): void => {\n\t\tfor (const el of Array.from(root.querySelectorAll(`[${markerAttr}]`))) {\n\t\t\tel.removeAttribute(markerAttr);\n\t\t}\n\n\t\t// Descend into any shadow roots to clear markers within them.\n\t\tfor (const el of Array.from(root.querySelectorAll(\"*\"))) {\n\t\t\tif (el.shadowRoot) {\n\t\t\t\tclear(el.shadowRoot);\n\t\t\t}\n\t\t}\n\t};\n\n\tclear(document);\n\n\t(document.activeElement as HTMLElement | null)?.blur();\n\n\twindow.scrollTo(0, 0);\n}\n\nexport type MeasureObscuringResult = {\n\tcoveredFraction: number;\n\tfullyObscured: boolean;\n\toffscreen: boolean;\n\topacity: \"opaque\" | \"semi-transparent\" | \"unknown\";\n\tobscuredByHtml: string | null;\n\t/** True when a covering element was marked for selector resolution. */\n\thasObscurer: boolean;\n};\n\n/**\n * Hit-test whether author-created content entirely hides the focused element. Marks a single\n * covering element with `obscurerAttr` when containment confirms a full cover, so the host can\n * resolve its selector via getSelector.\n */\nexport function measureObscuringScript(\n\tel: Element | null,\n\tobscurerAttr: string,\n): MeasureObscuringResult {\n\tconst empty: MeasureObscuringResult = {\n\t\tcoveredFraction: 0,\n\t\tfullyObscured: false,\n\t\toffscreen: false,\n\t\topacity: \"opaque\",\n\t\tobscuredByHtml: null,\n\t\thasObscurer: false,\n\t};\n\n\tif (!el) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n\n\tconst rect = el.getBoundingClientRect();\n\tconst left = Math.max(rect.left, 0);\n\tconst top = Math.max(rect.top, 0);\n\tconst right = Math.min(rect.right, window.innerWidth);\n\tconst bottom = Math.min(rect.bottom, window.innerHeight);\n\n\tif (right <= left || bottom <= top) {\n\t\treturn { ...empty, offscreen: true };\n\t}\n\n\tconst width = right - left;\n\tconst height = bottom - top;\n\tconst LARGE_ELEMENT_THRESHOLD_PX = 80;\n\n\tconst points: Array<{ x: number; y: number }> = [\n\t\t{ x: left, y: top },\n\t\t{ x: right - 0.5, y: top },\n\t\t{ x: left, y: bottom - 0.5 },\n\t\t{ x: right - 0.5, y: bottom - 0.5 },\n\t\t{ x: left + width / 2, y: top },\n\t\t{ x: left + width / 2, y: bottom - 0.5 },\n\t\t{ x: left, y: top + height / 2 },\n\t\t{ x: right - 0.5, y: top + height / 2 },\n\t\t{ x: left + width / 2, y: top + height / 2 },\n\t];\n\n\tif (width > LARGE_ELEMENT_THRESHOLD_PX || height > LARGE_ELEMENT_THRESHOLD_PX) {\n\t\tpoints.push(\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.25 },\n\t\t\t{ x: left + width * 0.25, y: top + height * 0.75 },\n\t\t\t{ x: left + width * 0.75, y: top + height * 0.75 },\n\t\t);\n\t}\n\n\tconst hitAt = (x: number, y: number): Element | null => {\n\t\tconst px = Math.min(Math.max(x, 0), window.innerWidth - 1);\n\t\tconst py = Math.min(Math.max(y, 0), window.innerHeight - 1);\n\t\tconst stack =\n\t\t\ttypeof document.elementsFromPoint === \"function\"\n\t\t\t\t? document.elementsFromPoint(px, py)\n\t\t\t\t: [document.elementFromPoint(px, py)].filter(Boolean);\n\n\t\tfor (let candidate of stack as Element[]) {\n\t\t\twhile (candidate?.shadowRoot) {\n\t\t\t\tconst inner = candidate.shadowRoot.elementFromPoint(px, py);\n\n\t\t\t\tif (!inner || inner === candidate) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcandidate = inner;\n\t\t\t}\n\n\t\t\tif (!candidate) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (candidate === el || el.contains(candidate) || candidate.contains(el)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn candidate;\n\t\t}\n\n\t\treturn null;\n\t};\n\n\tlet covered = 0;\n\tconst coverCounts = new Map<Element, number>();\n\n\tfor (const point of points) {\n\t\tconst cover = hitAt(point.x, point.y);\n\n\t\tif (cover) {\n\t\t\tcovered++;\n\t\t\tcoverCounts.set(cover, (coverCounts.get(cover) ?? 0) + 1);\n\t\t}\n\t}\n\n\tconst coveredFraction = points.length === 0 ? 0 : covered / points.length;\n\n\tif (coveredFraction < 1) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction,\n\t\t\tfullyObscured: false,\n\t\t};\n\t}\n\n\tlet topCover: Element | null = null;\n\tlet topCount = 0;\n\n\tfor (const [candidate, count] of coverCounts) {\n\t\tif (count > topCount) {\n\t\t\ttopCover = candidate;\n\t\t\ttopCount = count;\n\t\t}\n\t}\n\n\tif (!topCover) {\n\t\treturn {\n\t\t\t...empty,\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: false,\n\t\t\topacity: \"unknown\",\n\t\t};\n\t}\n\n\tconst coverRect = topCover.getBoundingClientRect();\n\tconst containsFully =\n\t\tcoverRect.left <= rect.left &&\n\t\tcoverRect.top <= rect.top &&\n\t\tcoverRect.right >= rect.right &&\n\t\tcoverRect.bottom >= rect.bottom;\n\n\tif (!containsFully) {\n\t\t// Joint multi-element cover: cannot confirm a single opaque container.\n\t\treturn {\n\t\t\tcoveredFraction: 1,\n\t\t\tfullyObscured: true,\n\t\t\toffscreen: false,\n\t\t\topacity: \"unknown\",\n\t\t\tobscuredByHtml: null,\n\t\t\thasObscurer: false,\n\t\t};\n\t}\n\n\tconst classifyOpacity = (node: Element): \"opaque\" | \"semi-transparent\" | \"unknown\" => {\n\t\tconst cs = getComputedStyle(node);\n\t\tconst opacity = Number.parseFloat(cs.opacity);\n\n\t\tif (!Number.isFinite(opacity)) {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\tif (opacity < 1) {\n\t\t\treturn \"semi-transparent\";\n\t\t}\n\n\t\tconst bg = cs.backgroundColor;\n\t\tconst rgba = bg.match(\n\t\t\t/rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/,\n\t\t);\n\n\t\tif (rgba) {\n\t\t\tconst alpha = rgba[4] === undefined ? 1 : Number.parseFloat(rgba[4]);\n\n\t\t\tif (alpha < 1) {\n\t\t\t\treturn alpha <= 0 ? \"semi-transparent\" : \"semi-transparent\";\n\t\t\t}\n\n\t\t\treturn \"opaque\";\n\t\t}\n\n\t\tif (cs.backgroundImage && cs.backgroundImage !== \"none\") {\n\t\t\treturn \"unknown\";\n\t\t}\n\n\t\treturn \"unknown\";\n\t};\n\n\tconst opacity = classifyOpacity(topCover);\n\tconst openingTag = topCover.cloneNode(false) as Element;\n\topeningTag.removeAttribute(obscurerAttr);\n\ttopCover.setAttribute(obscurerAttr, \"1\");\n\n\treturn {\n\t\tcoveredFraction: 1,\n\t\tfullyObscured: true,\n\t\toffscreen: false,\n\t\topacity,\n\t\tobscuredByHtml: openingTag.outerHTML,\n\t\thasObscurer: true,\n\t};\n}\n\n/** Return the element marked as the obscurer, if any. */\nexport function obscurerHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-obscurer]\");\n}\n\nexport function clearObscurerScript(obscurerAttr: string): void {\n\tfor (const marked of Array.from(document.querySelectorAll(`[${obscurerAttr}]`))) {\n\t\tmarked.removeAttribute(obscurerAttr);\n\t}\n}\n\ntype ContextObserverState = {\n\tbaselineHref: string;\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\t/** Elements that received focusin since the last drain. */\n\tfocusIns: Element[];\n\tsoftUrlChange: boolean;\n\tinstalled: boolean;\n};\n\ndeclare global {\n\tinterface Window {\n\t\t__a11yContextObserver?: ContextObserverState;\n\t}\n}\n\n/**\n * Install once: wrap window.open (record + inert stub), capture-phase submit (record +\n * preventDefault), track focusin and soft URL changes.\n */\nexport function installContextObserverScript(): void {\n\tif (window.__a11yContextObserver?.installed) {\n\t\treturn;\n\t}\n\n\tconst state: ContextObserverState = {\n\t\tbaselineHref: location.href,\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusIns: [],\n\t\tsoftUrlChange: false,\n\t\tinstalled: true,\n\t};\n\n\twindow.__a11yContextObserver = state;\n\n\tconst originalOpen = window.open.bind(window);\n\n\twindow.open = (..._args: Parameters<typeof window.open>) => {\n\t\tstate.openedWindow = true;\n\n\t\treturn {\n\t\t\tclosed: true,\n\t\t\tclose() {},\n\t\t\tfocus() {},\n\t\t\tblur() {},\n\t\t\topener: null,\n\t\t\tlocation: { href: \"\" },\n\t\t} as unknown as Window;\n\t};\n\n\t// Keep a reference so tooling does not tree-shake the bind as unused.\n\tvoid originalOpen;\n\n\tdocument.addEventListener(\n\t\t\"submit\",\n\t\t(event) => {\n\t\t\tstate.submittedForm = true;\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t},\n\t\ttrue,\n\t);\n\n\tdocument.addEventListener(\n\t\t\"focus\",\n\t\t(event) => {\n\t\t\tconst target = event.target;\n\n\t\t\t// Prefer nodeType: instanceof can be brittle across some embeddings.\n\t\t\tif (target && (target as Node).nodeType === 1) {\n\t\t\t\tstate.focusIns.push(target as Element);\n\t\t\t}\n\t\t},\n\t\ttrue,\n\t);\n\n\tconst noteSoftNav = (): void => {\n\t\tif (location.href !== state.baselineHref) {\n\t\t\tstate.softUrlChange = true;\n\t\t}\n\t};\n\n\twindow.addEventListener(\"hashchange\", noteSoftNav);\n\twindow.addEventListener(\"popstate\", noteSoftNav);\n\n\tconst wrapHistory = (method: \"pushState\" | \"replaceState\"): void => {\n\t\tconst original = history[method].bind(history);\n\n\t\thistory[method] = ((...args: Parameters<History[\"pushState\"]>) => {\n\t\t\tconst result = original(...args);\n\t\t\tnoteSoftNav();\n\n\t\t\treturn result;\n\t\t}) as History[\"pushState\"];\n\t};\n\n\twrapHistory(\"pushState\");\n\twrapHistory(\"replaceState\");\n}\n\nexport type DrainContextObserverResult = {\n\topenedWindow: boolean;\n\tsubmittedForm: boolean;\n\tfocusRemoved: boolean;\n\tredirect: \"outside\" | \"same-subtree\" | null;\n\tsoftUrlChange: boolean;\n\tnavigation: boolean;\n\tattributedHtml: string | null;\n\t/** True when an attributed element was marked for selector resolution. */\n\thasAttributed: boolean;\n};\n\n/**\n * Drain observer signals for the current tab stop and reset per-stop flags. Classification of focus\n * removal / redirection uses the post-Tab settle activeElement and the focusin history from this\n * stop only.\n */\nexport function drainContextObserverScript(markerAttr: string): DrainContextObserverResult {\n\t// Must be a string literal inside this function: it is serialized into the page.\n\tconst attributedAttr = \"data-a11y-ctx-attr\";\n\n\tconst empty: DrainContextObserverResult = {\n\t\topenedWindow: false,\n\t\tsubmittedForm: false,\n\t\tfocusRemoved: false,\n\t\tredirect: null,\n\t\tsoftUrlChange: false,\n\t\tnavigation: false,\n\t\tattributedHtml: null,\n\t\thasAttributed: false,\n\t};\n\n\tconst state = window.__a11yContextObserver;\n\n\tif (!state) {\n\t\treturn empty;\n\t}\n\n\tfor (const marked of Array.from(document.querySelectorAll(`[${attributedAttr}]`))) {\n\t\tmarked.removeAttribute(attributedAttr);\n\t}\n\n\tconst openedWindow = state.openedWindow;\n\tconst submittedForm = state.submittedForm;\n\tconst softUrlChange = state.softUrlChange;\n\tconst focusIns = state.focusIns.slice();\n\n\tstate.openedWindow = false;\n\tstate.submittedForm = false;\n\tstate.softUrlChange = false;\n\tstate.focusIns = [];\n\n\tlet active = document.activeElement;\n\n\twhile (active?.shadowRoot?.activeElement) {\n\t\tactive = active.shadowRoot.activeElement;\n\t}\n\n\tconst intended =\n\t\tfocusIns.find((el) => el !== document.body && el !== document.documentElement) ?? null;\n\n\tconst openingHtml = (node: Element): string => {\n\t\tconst clone = node.cloneNode(false) as Element;\n\t\tclone.removeAttribute(markerAttr);\n\t\tclone.removeAttribute(attributedAttr);\n\n\t\treturn clone.outerHTML;\n\t};\n\n\tlet focusRemoved = false;\n\tlet redirect: \"outside\" | \"same-subtree\" | null = null;\n\tlet attributedHtml: string | null = null;\n\tlet hasAttributed = false;\n\n\tconst bodyOrNone = !active || active === document.body || active === document.documentElement;\n\n\tif (intended && bodyOrNone) {\n\t\tfocusRemoved = true;\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t} else if (intended && active && intended !== active) {\n\t\tconst composedContains = (ancestor: Element, node: Element): boolean => {\n\t\t\tlet current: Node | null = node;\n\n\t\t\twhile (current) {\n\t\t\t\tif (current === ancestor) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tconst parent: Node | null = current.parentNode;\n\n\t\t\t\tif (parent) {\n\t\t\t\t\tcurrent = parent;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (current instanceof ShadowRoot) {\n\t\t\t\t\tcurrent = current.host;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t};\n\n\t\tconst same = composedContains(intended, active) || composedContains(active, intended);\n\n\t\tredirect = same ? \"same-subtree\" : \"outside\";\n\t\tattributedHtml = openingHtml(intended);\n\t\tintended.setAttribute(attributedAttr, \"1\");\n\t\thasAttributed = true;\n\t}\n\n\treturn {\n\t\topenedWindow,\n\t\tsubmittedForm,\n\t\tfocusRemoved,\n\t\tredirect,\n\t\tsoftUrlChange,\n\t\tnavigation: false,\n\t\tattributedHtml,\n\t\thasAttributed,\n\t};\n}\n\n/**\n * Return the element marked as attributed by drainContextObserverScript (the element that received\n * a focus event this stop and then lost it), if any.\n */\nexport function attributedHandleScript(): Element | null {\n\treturn document.querySelector(\"[data-a11y-ctx-attr]\");\n}\n\nexport function clearAttributedScript(): void {\n\tfor (const marked of Array.from(document.querySelectorAll(\"[data-a11y-ctx-attr]\"))) {\n\t\tmarked.removeAttribute(\"data-a11y-ctx-attr\");\n\t}\n}\n\n/**\n * Drop focusin history accumulated after drain (e.g. pixel-diff blur/refocus) so the next tab stop\n * does not mis-attribute F55 or redirects.\n */\nexport function clearContextFocusInsScript(): void {\n\tconst state = window.__a11yContextObserver;\n\n\tif (state) {\n\t\tstate.focusIns = [];\n\t\tstate.openedWindow = false;\n\t\tstate.submittedForm = false;\n\t\tstate.softUrlChange = false;\n\t}\n}\n\n/** Current location.href for navigation comparison in the host. */\nexport function locationHrefScript(): string {\n\treturn location.href;\n}\n\nexport function hasFocusScript(): boolean {\n\treturn document.hasFocus();\n}\n","/**\n * Computed-style properties that are likely to draw a visible focus indicator. Layout properties\n * like margin and padding are deliberately excluded.\n */\nexport const FOCUS_STYLE_PROPERTIES = [\n\t\"background-color\",\n\t\"background-image\",\n\t\"border-bottom-color\",\n\t\"border-bottom-style\",\n\t\"border-bottom-width\",\n\t\"border-left-color\",\n\t\"border-left-style\",\n\t\"border-left-width\",\n\t\"border-right-color\",\n\t\"border-right-style\",\n\t\"border-right-width\",\n\t\"border-top-color\",\n\t\"border-top-style\",\n\t\"border-top-width\",\n\t\"box-shadow\",\n\t\"color\",\n\t\"font-weight\",\n\t\"outline-color\",\n\t\"outline-style\",\n\t\"outline-width\",\n\t\"text-decoration\",\n] as const;\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { getSelector, truncateHtml } from \"@a11y-pulse/browser-adaptor/dom\";\nimport {\n\ttype ActiveElementBase,\n\tactiveElementHandleScript,\n\tattributedHandleScript,\n\tbaselineScript,\n\tclearAttributedScript,\n\tclearContextFocusInsScript,\n\tclearMarkersScript,\n\tclearObscurerScript,\n\ttype DrainContextObserverResult,\n\tdrainContextObserverScript,\n\thasFocusScript,\n\tinstallContextObserverScript,\n\tlocationHrefScript,\n\tmeasureObscuringScript,\n\tobscurerHandleScript,\n\tprobeActiveElementScript,\n} from \"./browser-scripts\";\nimport { captureScreenshot } from \"./capture-screenshot\";\nimport { FOCUS_STYLE_PROPERTIES } from \"./focus-style\";\nimport type {\n\tActiveElementInfo,\n\tCapability,\n\tContextChangeSignals,\n\tSessionEndReason,\n\tStyleSnapshot,\n\tTabConsumer,\n\tTabSessionHandle,\n\tTabStopSnapshot,\n\tUnfocusedPair,\n} from \"./types\";\nimport { captureUnfocusedPair } from \"./unfocused-pair\";\n\n/**\n * Whether an `evaluate()` rejection looks like the page navigated out from\n * under it (execution context destroyed) rather than an unexpected failure.\n * A full navigation is the one context-change signal the in-page observer\n * can never self-report: it destroys the JS context before it can log\n * anything, so the host has to infer it from how `evaluate()` failed.\n */\nexport function isContextDestroyedError(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\n\t// Each adaptor's driver words this differently: \"Target closed\" is Puppeteer's, \"has been\n\t// closed\" is Playwright's.\n\treturn /Execution context was destroyed|Target closed|has been closed|frame was detached|navigat/i.test(\n\t\tmessage,\n\t);\n}\n\n/**\n * Outcome of the early, pre-isBody navigation check. `navigation: true`\n * means a full navigation was detected (via href diff or a destroyed-context\n * error) and the caller must end the session without proceeding any\n * further. Otherwise `raw` carries a drain payload already fetched while\n * resolving a soft URL change (so the later full drain can reuse it instead\n * of reading back state the first call already reset), or `null` when the\n * href was unchanged and nothing has been drained yet for this stop.\n */\ntype ContextNavigationOutcome =\n\t| { navigation: true }\n\t| { navigation: false; raw: DrainContextObserverResult | null };\n\nexport const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33;\nexport const DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;\nexport const DEFAULT_MARKER_LIMIT = 1024;\nexport const MARKER_ATTR = \"data-a11y-focus-idx\";\nexport const OBSCURER_ATTR = \"data-a11y-obscurer\";\nexport const OBSCURING_RECHECK_DELAY_MS = 250;\n\nexport type TabSessionOptions = {\n\tscreenshotSettleDelay?: number;\n\tscreenshotClipBuffer?: number;\n\tmarkerLimit?: number;\n\tbaselineElementLimit?: number;\n};\n\nexport type TabOrchestrator = {\n\tattach(consumer: TabConsumer): void;\n\trun(): Promise<void>;\n};\n\nexport function createTabOrchestrator(\n\tadaptor: BrowserAdaptor,\n\toptions: TabSessionOptions = {},\n): TabOrchestrator {\n\tconst consumers: TabConsumer[] = [];\n\tlet started = false;\n\tlet ran = false;\n\n\treturn {\n\t\tattach(consumer) {\n\t\t\tif (started) {\n\t\t\t\tthrow new Error(\"Cannot attach after run() has started\");\n\t\t\t}\n\n\t\t\tconsumers.push(consumer);\n\t\t},\n\t\tasync run() {\n\t\t\tif (ran) {\n\t\t\t\tthrow new Error(\"run() has already been called\");\n\t\t\t}\n\n\t\t\tran = true;\n\t\t\tstarted = true;\n\n\t\t\tif (consumers.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst attached = new Set(consumers);\n\t\t\tconst screenshotSettleDelay =\n\t\t\t\toptions.screenshotSettleDelay ?? DEFAULT_SCREENSHOT_SETTLE_DELAY;\n\t\t\tconst screenshotClipBuffer = options.screenshotClipBuffer ?? DEFAULT_SCREENSHOT_CLIP_BUFFER;\n\t\t\tconst markerLimit = Math.max(\n\t\t\t\toptions.markerLimit ?? DEFAULT_MARKER_LIMIT,\n\t\t\t\toptions.baselineElementLimit ?? 0,\n\t\t\t);\n\t\t\tconst styleProps = [...FOCUS_STYLE_PROPERTIES];\n\t\t\tconst resolved = {\n\t\t\t\tscreenshotSettleDelay,\n\t\t\t\tscreenshotClipBuffer,\n\t\t\t\tstyleProps,\n\t\t\t};\n\n\t\t\tlet settleTimer: ReturnType<typeof setTimeout> | undefined;\n\t\t\tlet resolveSettle: (() => void) | undefined;\n\t\t\tconst pairByStop = { current: null as Promise<UnfocusedPair> | null };\n\t\t\tconst screenshotByStop = { current: null as Promise<Uint8Array> | null };\n\t\t\tlet activeHandle: ElementRef | undefined;\n\t\t\tlet lastHasAttributed = false;\n\t\t\t// Captured when observers are installed and re-armed after every\n\t\t\t// detected soft URL change, so repeated soft navigations across stops\n\t\t\t// compare against the most recent href rather than the original load.\n\t\t\tlet baselineHref: string | null = null;\n\n\t\t\tconst remainingHas = (capability: Capability): boolean =>\n\t\t\t\t[...attached].some((c) => c.capabilities.has(capability));\n\n\t\t\t// Decided once from who is attached at session start; never re-checked,\n\t\t\t// so the observer stays installed even after every declarer disconnects\n\t\t\t// (session-lifetime setup is never uninstalled, only skipped per-stop).\n\t\t\tconst contextSignalsWanted = remainingHas(\"contextSignals\");\n\n\t\t\tconst handleFor = (consumer: TabConsumer): TabSessionHandle => ({\n\t\t\t\tdisconnect() {\n\t\t\t\t\tattached.delete(consumer);\n\n\t\t\t\t\tif (attached.size === 0 && settleTimer !== undefined) {\n\t\t\t\t\t\tclearTimeout(settleTimer);\n\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\tresolveSettle?.();\n\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tensureUnfocusedPair: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"unfocusedPair\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare unfocusedPair\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"unfocusedPair capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tpairByStop.current ??= captureUnfocusedPair(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn pairByStop.current;\n\t\t\t\t},\n\t\t\t\tscreenshotClip: () => {\n\t\t\t\t\tif (!consumer.capabilities.has(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!remainingHas(\"screenshot\")) {\n\t\t\t\t\t\tthrow new Error(\"Consumer did not declare screenshot\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (activeHandle === undefined) {\n\t\t\t\t\t\tthrow new Error(\"screenshot capture not implemented\");\n\t\t\t\t\t}\n\n\t\t\t\t\tscreenshotByStop.current ??= captureScreenshot(adaptor, resolved, activeHandle);\n\n\t\t\t\t\treturn screenshotByStop.current;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst end = (reason: SessionEndReason): void => {\n\t\t\t\tfor (const consumer of attached) {\n\t\t\t\t\tconsumer.onSessionEnd?.(reason);\n\t\t\t\t}\n\n\t\t\t\tattached.clear();\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Primary navigation detection layer: compares `location.href` against\n\t\t\t * `baselineHref` on every stop, since a full navigation destroys the\n\t\t\t * in-page observer's execution context before it can self-report\n\t\t\t * anything (`drainContextObserverScript`'s own `navigation` field is\n\t\t\t * always `false`). A destroyed context can also simply throw instead\n\t\t\t * of returning a changed href (e.g. the navigation completes between\n\t\t\t * the href read and the drain read), which is treated the same way.\n\t\t\t *\n\t\t\t * Must run before the caller's `isBody`/\"done tabbing\" exit check: a\n\t\t\t * freshly-navigated page's `document.activeElement` defaults to\n\t\t\t * `document.body` unless the new page autofocuses something, so\n\t\t\t * without this proactive check a silent navigation would otherwise be\n\t\t\t * misclassified as `\"completed\"`.\n\t\t\t *\n\t\t\t * Deliberately cheap on the common (non-navigating) path: it only\n\t\t\t * reads `location.href` and does not call `drainContextObserverScript`\n\t\t\t * unless the href actually changed, so a stop that ends the session\n\t\t\t * via the isBody exit right after this check still drains at most\n\t\t\t * once overall (at the later, full-mapping call site).\n\t\t\t */\n\t\t\tasync function checkContextNavigation(): Promise<ContextNavigationOutcome> {\n\t\t\t\tlet href: string;\n\n\t\t\t\ttry {\n\t\t\t\t\thref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tif (baselineHref === null || href === baselineHref) {\n\t\t\t\t\treturn { navigation: false, raw: null };\n\t\t\t\t}\n\n\t\t\t\t// Document navigated (or soft URL change). Prefer the observer's\n\t\t\t\t// own soft-nav flag when the execution context survived;\n\t\t\t\t// otherwise treat it as a full navigation.\n\t\t\t\tlet raw: DrainContextObserverResult | null = null;\n\n\t\t\t\ttry {\n\t\t\t\t\traw = await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\traw = null;\n\t\t\t\t}\n\n\t\t\t\tif (raw === null || !raw.softUrlChange) {\n\t\t\t\t\treturn { navigation: true };\n\t\t\t\t}\n\n\t\t\t\tbaselineHref = href;\n\n\t\t\t\treturn { navigation: false, raw };\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Whether the page reports focus, re-asserting focus reporting once\n\t\t\t * before answering no.\n\t\t\t */\n\t\t\tasync function documentHasFocus(): Promise<boolean> {\n\t\t\t\tif (await adaptor.evaluate(hasFocusScript)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Another target taking the foreground clears focus emulation.\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\treturn adaptor.evaluate(hasFocusScript);\n\t\t\t}\n\n\t\t\tlet failure: unknown;\n\n\t\t\ttry {\n\t\t\t\tawait adaptor.ensureFocusReporting();\n\n\t\t\t\tconst payload = await adaptor.evaluate(\n\t\t\t\t\tbaselineScript,\n\t\t\t\t\tstyleProps,\n\t\t\t\t\tMARKER_ATTR,\n\t\t\t\t\tmarkerLimit,\n\t\t\t\t);\n\t\t\t\tconst interned = new Map<number, StyleSnapshot>();\n\n\t\t\t\tfor (const entry of payload.entries) {\n\t\t\t\t\tconst styles = payload.styles[entry.styleIndex];\n\n\t\t\t\t\tif (styles !== undefined) {\n\t\t\t\t\t\tinterned.set(entry.index, styles);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\tbaselineHref = await adaptor.evaluate(locationHrefScript);\n\t\t\t\t\tawait adaptor.evaluate(installContextObserverScript);\n\t\t\t\t}\n\n\t\t\t\tfor (const consumer of [...attached]) {\n\t\t\t\t\tawait consumer.onSessionStart?.(handleFor(consumer));\n\t\t\t\t}\n\n\t\t\t\tconst visited = new Set<number>();\n\t\t\t\tlet tabIndex = 0;\n\n\t\t\t\twhile (attached.size > 0) {\n\t\t\t\t\tpairByStop.current = null;\n\t\t\t\t\tscreenshotByStop.current = null;\n\t\t\t\t\tlastHasAttributed = false;\n\n\t\t\t\t\tif (!(await documentHasFocus())) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tawait adaptor.pressTab();\n\t\t\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t\t\tresolveSettle = resolve;\n\t\t\t\t\t\tsettleTimer = setTimeout(() => {\n\t\t\t\t\t\t\tsettleTimer = undefined;\n\t\t\t\t\t\t\tresolveSettle = undefined;\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t}, screenshotSettleDelay);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (attached.size === 0) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Chromium only matches :focus while the document reports focus, so\n\t\t\t\t\t// an element measured after a loss reads as having no indicator\n\t\t\t\t\t// however correct its CSS is. Read here, at the point of\n\t\t\t\t\t// measurement, rather than relying on the pre-tab check. Acted on\n\t\t\t\t\t// below, once the probe has said whether there was anything to\n\t\t\t\t\t// measure.\n\t\t\t\t\tconst focusedAtMeasurement = await documentHasFocus();\n\n\t\t\t\t\tlet base: ActiveElementBase | null;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbase = await adaptor.evaluate(probeActiveElementScript, styleProps, MARKER_ATTR);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\") && isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t// Mid-flight navigation destroyed the execution context\n\t\t\t\t\t\t\t// before the probe could even run: there is no active\n\t\t\t\t\t\t\t// element to report, so end directly (mirrors the\n\t\t\t\t\t\t\t// isBody/\"done tabbing\" exit below, which also ends\n\t\t\t\t\t\t\t// without a snapshot when there is nothing to notify).\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\t// The press that leaves the last element takes focus out of the\n\t\t\t\t\t// document with it, so an unfocused page probing as <body> is the\n\t\t\t\t\t// tab order running out, not a stolen focus. Re-asserting focus\n\t\t\t\t\t// emulation cannot bring that back either. Leave it to the isBody\n\t\t\t\t\t// exit below and treat only a real element measured without focus\n\t\t\t\t\t// as a genuine loss.\n\t\t\t\t\tif (!focusedAtMeasurement && base !== null && !base.isBody) {\n\t\t\t\t\t\tend(\"lostFocus\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Primary navigation detection layer, run before the isBody exit:\n\t\t\t\t\t// a silently-navigated page's evaluate() calls succeed against the\n\t\t\t\t\t// new document (Puppeteer/CDP targets whichever context is live at\n\t\t\t\t\t// call time), so a hard nav can surface as an ordinary `isBody: true`\n\t\t\t\t\t// probe result with no thrown error at all.\n\t\t\t\t\tlet contextNav: ContextNavigationOutcome | null = null;\n\n\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\tconst outcome = await checkContextNavigation();\n\n\t\t\t\t\t\tif (outcome.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcontextNav = outcome;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base === null || base.isBody) {\n\t\t\t\t\t\t// Focus landed on <body>. This is normally \"done tabbing\", but it\n\t\t\t\t\t\t// is also what F55 focus-removal looks like (an element receives\n\t\t\t\t\t\t// focus and then blurs itself, e.g. `onfocus=\"this.blur()\"`): the\n\t\t\t\t\t\t// in-page observer's focusin history still attributes that stop to\n\t\t\t\t\t\t// the element that briefly held focus, even though nothing is\n\t\t\t\t\t\t// focused now. Drain before ending so that case gets one last\n\t\t\t\t\t\t// notification instead of vanishing silently.\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet raw: DrainContextObserverResult;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\traw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Mirrors the probe's own destroyed-context catch above:\n\t\t\t\t\t\t\t\t// there is no attributed element left to report, so end\n\t\t\t\t\t\t\t\t// directly with \"navigation\" rather than \"completed\" (a\n\t\t\t\t\t\t\t\t// navigation signal always ends the session that way).\n\t\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (raw.hasAttributed) {\n\t\t\t\t\t\t\t\tconst attributedRef = await adaptor.evaluateHandle(attributedHandleScript);\n\t\t\t\t\t\t\t\tlet selector: string;\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tselector = await adaptor.evaluate(getSelector, attributedRef);\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(attributedRef);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t\t\tindex: null,\n\t\t\t\t\t\t\t\t\tisBody: false,\n\t\t\t\t\t\t\t\t\tisIframe: false,\n\t\t\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.attributedHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\tstyles: { element: {}, before: {}, after: {} },\n\t\t\t\t\t\t\t\t\trect: { x: 0, y: 0, width: 0, height: 0 },\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t\t\t\tcontextSignals: {\n\t\t\t\t\t\t\t\t\t\tsignals: {\n\t\t\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t// This synthetic stop has no live focused element (it\n\t\t\t\t\t\t\t\t// already blurred itself before this notification is\n\t\t\t\t\t\t\t\t// built, by definition of F55), so capabilities that\n\t\t\t\t\t\t\t\t// need one (`unfocusedPair`, `baselineStyles`,\n\t\t\t\t\t\t\t\t// `obscuring`) have nothing to meaningfully act on.\n\t\t\t\t\t\t\t\t// Only consumers that declared `contextSignals` (the\n\t\t\t\t\t\t\t\t// capability this whole synthetic-stop mechanism exists\n\t\t\t\t\t\t\t\t// to serve) are notified; unlike every other stop in\n\t\t\t\t\t\t\t\t// this file, uniform notification is wrong here because\n\t\t\t\t\t\t\t\t// there is no element for the other capabilities to\n\t\t\t\t\t\t\t\t// read from.\n\t\t\t\t\t\t\t\tconst recipients = [...attached].filter((consumer) =>\n\t\t\t\t\t\t\t\t\tconsumer.capabilities.has(\"contextSignals\"),\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\t// Guaranteed even if a recipient's onTabStop throws,\n\t\t\t\t\t\t\t\t\t// so the `data-a11y-ctx-attr` DOM marker never survives\n\t\t\t\t\t\t\t\t\t// past this stop (mirrors how `disposeRef` is\n\t\t\t\t\t\t\t\t\t// guaranteed via `finally` in the main per-stop loop\n\t\t\t\t\t\t\t\t\t// below).\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.isIframe) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (base.index !== null) {\n\t\t\t\t\t\tif (visited.has(base.index)) {\n\t\t\t\t\t\t\tend(\"completed\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvisited.add(base.index);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst ref = await adaptor.evaluateHandle(activeElementHandleScript);\n\t\t\t\t\tactiveHandle = ref;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst selector = await adaptor.evaluate(getSelector, ref);\n\n\t\t\t\t\t\tconst activeElement: ActiveElementInfo = {\n\t\t\t\t\t\t\t...base,\n\t\t\t\t\t\t\thtml: truncateHtml(base.html),\n\t\t\t\t\t\t\tselector,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst snapshot: TabStopSnapshot = {\n\t\t\t\t\t\t\ttabIndex: ++tabIndex,\n\t\t\t\t\t\t\tactiveElement,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (remainingHas(\"baselineStyles\") && activeElement.index !== null) {\n\t\t\t\t\t\t\tconst baselineStyles = interned.get(activeElement.index);\n\n\t\t\t\t\t\t\tif (baselineStyles !== undefined) {\n\t\t\t\t\t\t\t\tsnapshot.baselineStyles = baselineStyles;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"contextSignals\")) {\n\t\t\t\t\t\t\tlet signals: ContextChangeSignals;\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t// Reuse the drain already fetched above while resolving\n\t\t\t\t\t\t\t\t// a soft URL change (checkContextNavigation), if any;\n\t\t\t\t\t\t\t\t// re-draining here would read back state that call\n\t\t\t\t\t\t\t\t// already reset (focusIns, softUrlChange, etc.) and\n\t\t\t\t\t\t\t\t// silently lose whatever it was reporting. Otherwise\n\t\t\t\t\t\t\t\t// (the common case: href unchanged, nothing drained\n\t\t\t\t\t\t\t\t// yet) do the one drain this stop needs.\n\t\t\t\t\t\t\t\tconst raw =\n\t\t\t\t\t\t\t\t\tcontextNav?.raw ??\n\t\t\t\t\t\t\t\t\t(await adaptor.evaluate(drainContextObserverScript, MARKER_ATTR));\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: raw.openedWindow,\n\t\t\t\t\t\t\t\t\tsubmittedForm: raw.submittedForm,\n\t\t\t\t\t\t\t\t\tfocusRemoved: raw.focusRemoved,\n\t\t\t\t\t\t\t\t\tredirect: raw.redirect,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: raw.softUrlChange,\n\t\t\t\t\t\t\t\t\tnavigation: raw.navigation,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = raw.hasAttributed;\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tif (!isContextDestroyedError(error)) {\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// The page navigated out from under the drain call: the\n\t\t\t\t\t\t\t\t// in-page observer's own state (and any DOM markers it\n\t\t\t\t\t\t\t\t// set) went with it, so there is nothing left to report\n\t\t\t\t\t\t\t\t// besides the navigation itself.\n\t\t\t\t\t\t\t\tsignals = {\n\t\t\t\t\t\t\t\t\topenedWindow: false,\n\t\t\t\t\t\t\t\t\tsubmittedForm: false,\n\t\t\t\t\t\t\t\t\tfocusRemoved: false,\n\t\t\t\t\t\t\t\t\tredirect: null,\n\t\t\t\t\t\t\t\t\tsoftUrlChange: false,\n\t\t\t\t\t\t\t\t\tnavigation: true,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tlastHasAttributed = false;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.contextSignals = { signals };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remainingHas(\"obscuring\")) {\n\t\t\t\t\t\t\tlet raw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\n\t\t\t\t\t\t\tif (raw.fullyObscured) {\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, OBSCURING_RECHECK_DELAY_MS));\n\t\t\t\t\t\t\t\traw = await adaptor.evaluate(measureObscuringScript, ref, OBSCURER_ATTR);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tlet obscuredBy: { selector: string; html: string } | null = null;\n\n\t\t\t\t\t\t\tif (raw.hasObscurer) {\n\t\t\t\t\t\t\t\tconst obscurer = await adaptor.evaluateHandle(obscurerHandleScript);\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst obscurerSelector = await adaptor.evaluate(getSelector, obscurer);\n\t\t\t\t\t\t\t\t\tobscuredBy = {\n\t\t\t\t\t\t\t\t\t\tselector: obscurerSelector,\n\t\t\t\t\t\t\t\t\t\thtml: truncateHtml(raw.obscuredByHtml ?? \"\"),\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tawait adaptor.disposeRef(obscurer);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tsnapshot.obscuring = {\n\t\t\t\t\t\t\t\tcoveredFraction: raw.coveredFraction,\n\t\t\t\t\t\t\t\tfullyObscured: raw.fullyObscured,\n\t\t\t\t\t\t\t\toffscreen: raw.offscreen,\n\t\t\t\t\t\t\t\topacity: raw.opacity,\n\t\t\t\t\t\t\t\tobscuredBy,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tawait adaptor.evaluate(clearObscurerScript, OBSCURER_ATTR);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst recipients = [...attached];\n\n\t\t\t\t\t\tfor (const consumer of recipients) {\n\t\t\t\t\t\t\tawait consumer.onTabStop(snapshot, handleFor(consumer));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (snapshot.contextSignals?.signals.navigation) {\n\t\t\t\t\t\t\tend(\"navigation\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (pairByStop.current !== null) {\n\t\t\t\t\t\t\t\tawait pairByStop.current;\n\n\t\t\t\t\t\t\t\tif (contextSignalsWanted) {\n\t\t\t\t\t\t\t\t\t// ensureUnfocusedPair blurred and re-focused the element to\n\t\t\t\t\t\t\t\t\t// capture the unfocused screenshot; drop that noise so the\n\t\t\t\t\t\t\t\t\t// next drain doesn't misread it as a real focus-removal or\n\t\t\t\t\t\t\t\t\t// redirect.\n\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearContextFocusInsScript);\n\n\t\t\t\t\t\t\t\t\tif (lastHasAttributed) {\n\t\t\t\t\t\t\t\t\t\tawait adaptor.evaluate(clearAttributedScript);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Capture errors already surface through the consumer that\n\t\t\t\t\t\t\t// awaited; still dispose the handle below.\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tactiveHandle = undefined;\n\t\t\t\t\t\tawait adaptor.disposeRef(ref);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tfailure = error;\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait adaptor.evaluate(clearMarkersScript, MARKER_ATTR);\n\t\t\t\t} catch {\n\t\t\t\t\t// Isolate teardown so a marker-clear throw cannot replace the loop error.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (failure !== undefined) {\n\t\t\t\tend(\"failed\");\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t},\n\t};\n}\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { bufferedClip } from \"@a11y-pulse/browser-adaptor\";\nimport { elementRectScript, pageDimensionsScript } from \"./browser-scripts\";\n\nexport type CaptureScreenshotOptions = {\n\tscreenshotClipBuffer: number;\n};\n\n/**\n * Screenshot the element `handle` currently refers to, clipped to its box plus\n * `screenshotClipBuffer` padding. The caller owns `handle` and must dispose it after this promise\n * settles.\n */\nexport async function captureScreenshot(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureScreenshotOptions,\n\thandle: ElementRef,\n): Promise<Uint8Array> {\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\tconst rect = await adaptor.evaluate(elementRectScript, handle);\n\tconst clip = bufferedClip(rect, pageWidth, pageHeight, options.screenshotClipBuffer);\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\treturn adaptor.screenshotClip(clip, scale);\n}\n","import type { BrowserAdaptor, ElementRef } from \"@a11y-pulse/browser-adaptor\";\nimport { bufferedClip } from \"@a11y-pulse/browser-adaptor\";\nimport {\n\tblurScript,\n\telementRectScript,\n\telementStylesScript,\n\tfocusScript,\n\tisCenterObscuredScript,\n\tpageDimensionsScript,\n\tscrollToCenterScript,\n} from \"./browser-scripts\";\nimport type { UnfocusedPair } from \"./types\";\n\nexport type CaptureUnfocusedPairOptions = {\n\tscreenshotSettleDelay: number;\n\tscreenshotClipBuffer: number;\n\tstyleProps: readonly string[];\n};\n\n/**\n * Capture focused and unfocused screenshots of the same element, plus the unfocused style snapshot\n * and clip anchors appearance needs to score later.\n *\n * The caller owns `handle` and must dispose it after this promise settles.\n */\nexport async function captureUnfocusedPair(\n\tadaptor: BrowserAdaptor,\n\toptions: CaptureUnfocusedPairOptions,\n\thandle: ElementRef,\n): Promise<UnfocusedPair> {\n\t// Tab scrolls an element just barely into view at the viewport edge, which is exactly where fixed\n\t// overlays (cookie banners, sticky footers) sit. If something covers the element there, both\n\t// screenshots would show the overlay and no indicator could ever be detected. Centre the element\n\t// in the viewport first, then give the scroll a moment to settle.\n\tif (await adaptor.evaluate(isCenterObscuredScript, handle)) {\n\t\tawait adaptor.evaluate(scrollToCenterScript, handle);\n\n\t\tawait new Promise((resolve) => setTimeout(resolve, options.screenshotSettleDelay));\n\t}\n\n\tconst { width: pageWidth, height: pageHeight } = await adaptor.evaluate(pageDimensionsScript);\n\n\tconst focusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst focusedClip = bufferedClip(\n\t\tfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst scale = adaptor.screenshotClipScale ?? 1;\n\n\t// Capture the focused state first, while focus is genuine: re-focusing the element afterwards\n\t// cannot always restore it (the host of a closed shadow root cannot push focus back inside), so\n\t// the focused frame must be taken before blurring.\n\tconst focusedScreenshot = await adaptor.screenshotClip(focusedClip, scale);\n\n\tawait adaptor.evaluate(blurScript, handle);\n\n\t// Measure the unfocused rect after blurring rather than reusing a stale baseline rect: a :focus\n\t// rule may have moved the element, and a fixed-position element's page-relative rect changes with\n\t// every scroll.\n\tconst unfocusedRect = await adaptor.evaluate(elementRectScript, handle);\n\tconst unfocusedClip = bufferedClip(\n\t\tunfocusedRect,\n\t\tpageWidth,\n\t\tpageHeight,\n\t\toptions.screenshotClipBuffer,\n\t);\n\n\tconst unfocusedScreenshot = await adaptor.screenshotClip(unfocusedClip, scale);\n\n\tconst unfocusedStyles = await adaptor.evaluate(elementStylesScript, handle, [\n\t\t...options.styleProps,\n\t]);\n\n\t// Restore focus so the next Tab advances from this element rather than restarting traversal. (A\n\t// closed-shadow host can't be re-focused into the root; the loop still progresses because Tab\n\t// re-enters from there.)\n\tawait adaptor.evaluate(focusScript, handle);\n\n\treturn {\n\t\tfocusedScreenshot,\n\t\tunfocusedScreenshot,\n\t\tunfocusedStyles,\n\t\tfocusedRect,\n\t\tunfocusedRect,\n\t\tfocusedClip,\n\t\tunfocusedClip,\n\t\tscale,\n\t};\n}\n"],"mappings":"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","names":["opacity","bufferedClip","bufferedClip","DEFAULT_SCREENSHOT_SETTLE_DELAY"]}
@@ -1,5 +1,13 @@
1
- import type { BrowserAdaptor } from "./adaptor";
1
+ import type { BrowserAdaptor } from "@a11y-pulse/browser-adaptor";
2
2
  import type { TabConsumer } from "./types";
3
+ /**
4
+ * Whether an `evaluate()` rejection looks like the page navigated out from
5
+ * under it (execution context destroyed) rather than an unexpected failure.
6
+ * A full navigation is the one context-change signal the in-page observer
7
+ * can never self-report: it destroys the JS context before it can log
8
+ * anything, so the host has to infer it from how `evaluate()` failed.
9
+ */
10
+ export declare function isContextDestroyedError(error: unknown): boolean;
3
11
  export declare const DEFAULT_SCREENSHOT_SETTLE_DELAY = 33;
4
12
  export declare const DEFAULT_SCREENSHOT_CLIP_BUFFER = 10;
5
13
  export declare const DEFAULT_MARKER_LIMIT = 1024;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Rect } from "./adaptor";
1
+ import type { Rect } from "@a11y-pulse/browser-adaptor";
2
2
  export type StyleSnapshot = {
3
3
  element: Record<string, string>;
4
4
  before: Record<string, string>;
@@ -1,4 +1,4 @@
1
- import type { BrowserAdaptor, ElementRef } from "./adaptor";
1
+ import type { BrowserAdaptor, ElementRef } from "@a11y-pulse/browser-adaptor";
2
2
  import type { UnfocusedPair } from "./types";
3
3
  export type CaptureUnfocusedPairOptions = {
4
4
  screenshotSettleDelay: number;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@a11y-pulse/tab-orchestrator",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Shared Tab-session runner for A11y Pulse keyboard audits.",
5
5
  "type": "module",
6
- "license": "SEE LICENSE IN LICENSE.md",
7
- "author": "A11y Pulse Limited",
6
+ "license": "MIT",
7
+ "author": "A11y Pulse",
8
8
  "homepage": "https://github.com/A11y-Pulse/audits/tree/main/packages/tab-orchestrator#readme",
9
9
  "repository": {
10
10
  "type": "git",
@@ -22,11 +22,6 @@
22
22
  "source": "./src/index.ts",
23
23
  "types": "./dist/index.d.ts",
24
24
  "import": "./dist/index.js"
25
- },
26
- "./puppeteer": {
27
- "source": "./src/adaptors/puppeteer.ts",
28
- "types": "./dist/adaptors/puppeteer.d.ts",
29
- "import": "./dist/adaptors/puppeteer.js"
30
25
  }
31
26
  },
32
27
  "files": [
@@ -45,19 +40,15 @@
45
40
  "publishConfig": {
46
41
  "access": "public"
47
42
  },
48
- "peerDependencies": {
49
- "puppeteer": "^25.7.0"
50
- },
51
- "peerDependenciesMeta": {
52
- "puppeteer": {
53
- "optional": true
54
- }
43
+ "dependencies": {
44
+ "@a11y-pulse/browser-adaptor": "*"
55
45
  },
56
46
  "devDependencies": {
57
47
  "@a11y-pulse/context-change-on-focus-audit": "*",
58
48
  "@a11y-pulse/focus-appearance-audit": "*",
59
49
  "@a11y-pulse/focus-not-obscured-audit": "*",
60
- "happy-dom": "^20.11.2",
61
- "puppeteer": "^25.7.0"
50
+ "happy-dom": "^20.12.0",
51
+ "playwright-core": "^1.63.0",
52
+ "puppeteer": "^25.10.0"
62
53
  }
63
54
  }
package/dist/adaptor.d.ts DELETED
@@ -1,48 +0,0 @@
1
- export type Rect = {
2
- x: number;
3
- y: number;
4
- width: number;
5
- height: number;
6
- };
7
- /**
8
- * An element handle in the page. This type is adaptor-specific and is opaque to the audit.
9
- */
10
- export type ElementRef = unknown;
11
- /**
12
- * A framework-specific adaptor that provides the primitives needed by the audit. An adaptor wraps a
13
- * single page in a web browser.
14
- */
15
- export interface BrowserAdaptor {
16
- /**
17
- * Run `fn` in the page context, passing in any serialisable `args`
18
- */
19
- evaluate<T>(fn: (...args: any[]) => T | Promise<T>, ...args: unknown[]): Promise<T>;
20
- /**
21
- * Return an opaque handle to an element in the page
22
- */
23
- evaluateHandle(fn: () => Element | null): Promise<ElementRef>;
24
- /**
25
- * Release a handle previously returned by evaluateHandle
26
- */
27
- disposeRef(ref: ElementRef): Promise<void>;
28
- /**
29
- * Press the tab key
30
- */
31
- pressTab(): Promise<void>;
32
- /**
33
- * Screenshot a clipped region of the page. `scale` is the device scale factor (bitmap pixels per
34
- * CSS pixel), multiplied against whatever deviceScaleFactor the page itself currently has
35
- * (Puppeteer does not replace it). The caller is responsible for the page being at a known
36
- * deviceScaleFactor before invoking this.
37
- */
38
- screenshotClip(clip: Rect, scale?: number): Promise<Uint8Array>;
39
- /** Device scale factor for evidence screenshots stored on failure. */
40
- readonly screenshotClipScale?: number;
41
- /**
42
- * Ensure the page reports focus for its lifetime. In particular, `document.hasFocus()` must
43
- * function correctly and :focus styles must be applied, even when the page is not the foreground.
44
- *
45
- * This function must not throw.
46
- */
47
- ensureFocusReporting(): Promise<void>;
48
- }
@@ -1,14 +0,0 @@
1
- import type { Page } from "puppeteer";
2
- import type { BrowserAdaptor, ElementRef, Rect } from "../adaptor";
3
- /** A BrowserAdaptor backed by a Puppeteer Page. */
4
- export declare class PuppeteerAdaptor implements BrowserAdaptor {
5
- private readonly page;
6
- readonly screenshotClipScale = 2;
7
- constructor(page: Page);
8
- evaluate<T>(fn: (...args: any[]) => T | Promise<T>, ...args: unknown[]): Promise<T>;
9
- evaluateHandle(fn: () => Element | null): Promise<ElementRef>;
10
- disposeRef(ref: ElementRef): Promise<void>;
11
- pressTab(): Promise<void>;
12
- screenshotClip(clip: Rect, scale?: number): Promise<Uint8Array>;
13
- ensureFocusReporting(): Promise<void>;
14
- }
@@ -1,56 +0,0 @@
1
- // src/adaptors/puppeteer.ts
2
- var focusEmulationSessions = /* @__PURE__ */ new WeakMap();
3
- var PuppeteerAdaptor = class {
4
- constructor(page) {
5
- this.page = page;
6
- }
7
- page;
8
- screenshotClipScale = 2;
9
- evaluate(fn, ...args) {
10
- return this.page.evaluate(fn, ...args);
11
- }
12
- evaluateHandle(fn) {
13
- return this.page.evaluateHandle(fn);
14
- }
15
- async disposeRef(ref) {
16
- await ref.dispose();
17
- }
18
- async pressTab() {
19
- await this.page.keyboard.press("Tab");
20
- }
21
- async screenshotClip(clip, scale = 1) {
22
- return await this.page.screenshot({
23
- type: "png",
24
- optimizeForSpeed: true,
25
- clip: {
26
- ...clip,
27
- scale
28
- }
29
- });
30
- }
31
- async ensureFocusReporting() {
32
- try {
33
- const existing = focusEmulationSessions.get(this.page);
34
- if (existing) {
35
- await existing.send("Emulation.setFocusEmulationEnabled", {
36
- enabled: false
37
- });
38
- await existing.send("Emulation.setFocusEmulationEnabled", {
39
- enabled: true
40
- });
41
- return;
42
- }
43
- const client = await this.page.createCDPSession();
44
- await client.send("Emulation.setFocusEmulationEnabled", {
45
- enabled: true
46
- });
47
- focusEmulationSessions.set(this.page, client);
48
- } catch (error) {
49
- console.warn("Could not enable focus emulation for the tab orchestrator", { error });
50
- }
51
- }
52
- };
53
- export {
54
- PuppeteerAdaptor
55
- };
56
- //# sourceMappingURL=puppeteer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adaptors/puppeteer.ts"],"sourcesContent":["import type { CDPSession, JSHandle, Page } from \"puppeteer\";\nimport type { BrowserAdaptor, ElementRef, Rect } from \"../adaptor\";\n\n// The CDP session holding focus emulation for each page. A fresh adaptor is created per audit run,\n// but a page often outlives one run, so without this each run would open another CDP session that\n// is never detached. The session is kept attached for the page's lifetime: Chrome clears the\n// emulation when the CDP client disconnects.\nconst focusEmulationSessions = new WeakMap<Page, CDPSession>();\n\n/** A BrowserAdaptor backed by a Puppeteer Page. */\nexport class PuppeteerAdaptor implements BrowserAdaptor {\n\treadonly screenshotClipScale = 2;\n\n\tconstructor(private readonly page: Page) {}\n\n\tevaluate<T>(\n\t\t// biome-ignore lint/suspicious/noExplicitAny: the page-evaluated fn accepts arbitrary serialised args and element handles.\n\t\tfn: (...args: any[]) => T | Promise<T>,\n\t\t...args: unknown[]\n\t): Promise<T> {\n\t\t// Puppeteer resolves any JSHandle passed as an arg to the live element.\n\t\treturn this.page.evaluate(fn as never, ...(args as never[])) as Promise<T>;\n\t}\n\n\tevaluateHandle(fn: () => Element | null): Promise<ElementRef> {\n\t\treturn this.page.evaluateHandle(fn);\n\t}\n\n\tasync disposeRef(ref: ElementRef): Promise<void> {\n\t\tawait (ref as JSHandle).dispose();\n\t}\n\n\tasync pressTab(): Promise<void> {\n\t\tawait this.page.keyboard.press(\"Tab\");\n\t}\n\n\tasync screenshotClip(clip: Rect, scale = 1): Promise<Uint8Array> {\n\t\treturn (await this.page.screenshot({\n\t\t\ttype: \"png\",\n\t\t\toptimizeForSpeed: true,\n\t\t\tclip: {\n\t\t\t\t...clip,\n\t\t\t\tscale,\n\t\t\t},\n\t\t})) as Uint8Array;\n\t}\n\n\tasync ensureFocusReporting(): Promise<void> {\n\t\t// Pages only report focus while they are the foreground tab. Since this browser instance is\n\t\t// likely running in the background, we need to enable focus emulation to get accurate results.\n\t\t//\n\t\t// Note this function does not throw if enabling emulation fails; it is a best-effort.\n\t\ttry {\n\t\t\tconst existing = focusEmulationSessions.get(this.page);\n\n\t\t\tif (existing) {\n\t\t\t\t// Chromium ignores a redundant enable, so re-asserting has to toggle.\n\t\t\t\tawait existing.send(\"Emulation.setFocusEmulationEnabled\", {\n\t\t\t\t\tenabled: false,\n\t\t\t\t});\n\t\t\t\tawait existing.send(\"Emulation.setFocusEmulationEnabled\", {\n\t\t\t\t\tenabled: true,\n\t\t\t\t});\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst client = await this.page.createCDPSession();\n\t\t\tawait client.send(\"Emulation.setFocusEmulationEnabled\", {\n\t\t\t\tenabled: true,\n\t\t\t});\n\t\t\tfocusEmulationSessions.set(this.page, client);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Could not enable focus emulation for the tab orchestrator\", { error });\n\t\t}\n\t}\n}\n"],"mappings":";AAOA,IAAM,yBAAyB,oBAAI,QAA0B;AAGtD,IAAM,mBAAN,MAAiD;AAAA,EAGvD,YAA6B,MAAY;AAAZ;AAAA,EAAa;AAAA,EAAb;AAAA,EAFpB,sBAAsB;AAAA,EAI/B,SAEC,OACG,MACU;AAEb,WAAO,KAAK,KAAK,SAAS,IAAa,GAAI,IAAgB;AAAA,EAC5D;AAAA,EAEA,eAAe,IAA+C;AAC7D,WAAO,KAAK,KAAK,eAAe,EAAE;AAAA,EACnC;AAAA,EAEA,MAAM,WAAW,KAAgC;AAChD,UAAO,IAAiB,QAAQ;AAAA,EACjC;AAAA,EAEA,MAAM,WAA0B;AAC/B,UAAM,KAAK,KAAK,SAAS,MAAM,KAAK;AAAA,EACrC;AAAA,EAEA,MAAM,eAAe,MAAY,QAAQ,GAAwB;AAChE,WAAQ,MAAM,KAAK,KAAK,WAAW;AAAA,MAClC,MAAM;AAAA,MACN,kBAAkB;AAAA,MAClB,MAAM;AAAA,QACL,GAAG;AAAA,QACH;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEA,MAAM,uBAAsC;AAK3C,QAAI;AACH,YAAM,WAAW,uBAAuB,IAAI,KAAK,IAAI;AAErD,UAAI,UAAU;AAEb,cAAM,SAAS,KAAK,sCAAsC;AAAA,UACzD,SAAS;AAAA,QACV,CAAC;AACD,cAAM,SAAS,KAAK,sCAAsC;AAAA,UACzD,SAAS;AAAA,QACV,CAAC;AAED;AAAA,MACD;AAEA,YAAM,SAAS,MAAM,KAAK,KAAK,iBAAiB;AAChD,YAAM,OAAO,KAAK,sCAAsC;AAAA,QACvD,SAAS;AAAA,MACV,CAAC;AACD,6BAAuB,IAAI,KAAK,MAAM,MAAM;AAAA,IAC7C,SAAS,OAAO;AACf,cAAQ,KAAK,6DAA6D,EAAE,MAAM,CAAC;AAAA,IACpF;AAAA,EACD;AACD;","names":[]}
package/dist/clip.d.ts DELETED
@@ -1,7 +0,0 @@
1
- import type { Rect } from "./adaptor";
2
- /**
3
- * A screenshot clip framing `rect` with up to `buffer` pixels of padding on each side, shrunk where
4
- * the page edges leave less room. The padding allows for indicators drawn just outside the element
5
- * box (outlines, shadows) and for anti-aliasing.
6
- */
7
- export declare function bufferedClip(rect: Rect, pageWidth: number, pageHeight: number, buffer: number): Rect;
@@ -1,11 +0,0 @@
1
- /**
2
- * Build a compact, hopefully-unique selector for any given element on the page. This function is
3
- * serialized and run in the page context, so it must not reference any symbols outside its own
4
- * scope.
5
- *
6
- * Mirrors axe-core's approach: each level contributes the fewest features that discriminate the
7
- * element (a unique id, the rarest single class, or an :nth-of-type position), and the walk stops
8
- * as soon as the joined selector matches exactly one element. Selectors stay short because of that
9
- * early stop, not because of truncation; the length budget is only a backstop.
10
- */
11
- export declare function getSelector(root: Node | null): string;
@@ -1,9 +0,0 @@
1
- export type TruncateHtmlOptions = {
2
- maxAttributeValueLength?: number;
3
- maxLength?: number;
4
- };
5
- /**
6
- * Given an element's outerHTML, produce a truncated version suitable for display in results. For
7
- * memory efficiency, pass a shallow outerHTML i.e. `Node.cloneNode(false).outerHTML`.
8
- */
9
- export declare function truncateHtml(html: string, options?: TruncateHtmlOptions): string;