@setsquare/review-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +57 -0
  3. package/README.md +338 -0
  4. package/THIRD-PARTY-NOTICES.md +61 -0
  5. package/dist/autoscan.d.ts +25 -0
  6. package/dist/autoscan.d.ts.map +1 -0
  7. package/dist/autoscan.js +332 -0
  8. package/dist/autoscan.js.map +1 -0
  9. package/dist/axe.d.ts +96 -0
  10. package/dist/axe.d.ts.map +1 -0
  11. package/dist/axe.js +210 -0
  12. package/dist/axe.js.map +1 -0
  13. package/dist/bin/setsquare.d.ts +3 -0
  14. package/dist/bin/setsquare.d.ts.map +1 -0
  15. package/dist/bin/setsquare.js +21 -0
  16. package/dist/bin/setsquare.js.map +1 -0
  17. package/dist/brand.d.ts +84 -0
  18. package/dist/brand.d.ts.map +1 -0
  19. package/dist/brand.js +335 -0
  20. package/dist/brand.js.map +1 -0
  21. package/dist/checks/dynamic.d.ts +57 -0
  22. package/dist/checks/dynamic.d.ts.map +1 -0
  23. package/dist/checks/dynamic.js +184 -0
  24. package/dist/checks/dynamic.js.map +1 -0
  25. package/dist/checks/rule-pack.d.ts +34 -0
  26. package/dist/checks/rule-pack.d.ts.map +1 -0
  27. package/dist/checks/rule-pack.js +270 -0
  28. package/dist/checks/rule-pack.js.map +1 -0
  29. package/dist/checks/versions.d.ts +28 -0
  30. package/dist/checks/versions.d.ts.map +1 -0
  31. package/dist/checks/versions.js +45 -0
  32. package/dist/checks/versions.js.map +1 -0
  33. package/dist/ci.d.ts +37 -0
  34. package/dist/ci.d.ts.map +1 -0
  35. package/dist/ci.js +199 -0
  36. package/dist/ci.js.map +1 -0
  37. package/dist/contract.d.ts +334 -0
  38. package/dist/contract.d.ts.map +1 -0
  39. package/dist/contract.js +35 -0
  40. package/dist/contract.js.map +1 -0
  41. package/dist/dedupe.d.ts +79 -0
  42. package/dist/dedupe.d.ts.map +1 -0
  43. package/dist/dedupe.js +102 -0
  44. package/dist/dedupe.js.map +1 -0
  45. package/dist/document.d.ts +29 -0
  46. package/dist/document.d.ts.map +1 -0
  47. package/dist/document.js +94 -0
  48. package/dist/document.js.map +1 -0
  49. package/dist/envelope.d.ts +124 -0
  50. package/dist/envelope.d.ts.map +1 -0
  51. package/dist/envelope.js +337 -0
  52. package/dist/envelope.js.map +1 -0
  53. package/dist/escape.d.ts +14 -0
  54. package/dist/escape.d.ts.map +1 -0
  55. package/dist/escape.js +20 -0
  56. package/dist/escape.js.map +1 -0
  57. package/dist/findings.d.ts +47 -0
  58. package/dist/findings.d.ts.map +1 -0
  59. package/dist/findings.js +229 -0
  60. package/dist/findings.js.map +1 -0
  61. package/dist/fixture.d.ts +7 -0
  62. package/dist/fixture.d.ts.map +1 -0
  63. package/dist/fixture.js +237 -0
  64. package/dist/fixture.js.map +1 -0
  65. package/dist/group.d.ts +214 -0
  66. package/dist/group.d.ts.map +1 -0
  67. package/dist/group.js +709 -0
  68. package/dist/group.js.map +1 -0
  69. package/dist/html-report.d.ts +68 -0
  70. package/dist/html-report.d.ts.map +1 -0
  71. package/dist/html-report.js +649 -0
  72. package/dist/html-report.js.map +1 -0
  73. package/dist/human.d.ts +65 -0
  74. package/dist/human.d.ts.map +1 -0
  75. package/dist/human.js +111 -0
  76. package/dist/human.js.map +1 -0
  77. package/dist/index.d.ts +34 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +33 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/infer.d.ts +65 -0
  82. package/dist/infer.d.ts.map +1 -0
  83. package/dist/infer.js +67 -0
  84. package/dist/infer.js.map +1 -0
  85. package/dist/insights.d.ts +140 -0
  86. package/dist/insights.d.ts.map +1 -0
  87. package/dist/insights.js +483 -0
  88. package/dist/insights.js.map +1 -0
  89. package/dist/install.d.ts +8 -0
  90. package/dist/install.d.ts.map +1 -0
  91. package/dist/install.js +114 -0
  92. package/dist/install.js.map +1 -0
  93. package/dist/links.d.ts +45 -0
  94. package/dist/links.d.ts.map +1 -0
  95. package/dist/links.js +45 -0
  96. package/dist/links.js.map +1 -0
  97. package/dist/mask.d.ts +85 -0
  98. package/dist/mask.d.ts.map +1 -0
  99. package/dist/mask.js +98 -0
  100. package/dist/mask.js.map +1 -0
  101. package/dist/observe.d.ts +51 -0
  102. package/dist/observe.d.ts.map +1 -0
  103. package/dist/observe.js +233 -0
  104. package/dist/observe.js.map +1 -0
  105. package/dist/options.d.ts +249 -0
  106. package/dist/options.d.ts.map +1 -0
  107. package/dist/options.js +185 -0
  108. package/dist/options.js.map +1 -0
  109. package/dist/output.d.ts +113 -0
  110. package/dist/output.d.ts.map +1 -0
  111. package/dist/output.js +231 -0
  112. package/dist/output.js.map +1 -0
  113. package/dist/perf.d.ts +11 -0
  114. package/dist/perf.d.ts.map +1 -0
  115. package/dist/perf.js +150 -0
  116. package/dist/perf.js.map +1 -0
  117. package/dist/reading-level.d.ts +73 -0
  118. package/dist/reading-level.d.ts.map +1 -0
  119. package/dist/reading-level.js +189 -0
  120. package/dist/reading-level.js.map +1 -0
  121. package/dist/reporter.d.ts +94 -0
  122. package/dist/reporter.d.ts.map +1 -0
  123. package/dist/reporter.js +709 -0
  124. package/dist/reporter.js.map +1 -0
  125. package/dist/rules.d.ts +55 -0
  126. package/dist/rules.d.ts.map +1 -0
  127. package/dist/rules.js +77 -0
  128. package/dist/rules.js.map +1 -0
  129. package/dist/runtime.d.ts +32 -0
  130. package/dist/runtime.d.ts.map +1 -0
  131. package/dist/runtime.js +67 -0
  132. package/dist/runtime.js.map +1 -0
  133. package/dist/sanitise.d.ts +116 -0
  134. package/dist/sanitise.d.ts.map +1 -0
  135. package/dist/sanitise.js +247 -0
  136. package/dist/sanitise.js.map +1 -0
  137. package/dist/scan.d.ts +60 -0
  138. package/dist/scan.d.ts.map +1 -0
  139. package/dist/scan.js +246 -0
  140. package/dist/scan.js.map +1 -0
  141. package/dist/screens.d.ts +98 -0
  142. package/dist/screens.d.ts.map +1 -0
  143. package/dist/screens.js +301 -0
  144. package/dist/screens.js.map +1 -0
  145. package/dist/settle.d.ts +65 -0
  146. package/dist/settle.d.ts.map +1 -0
  147. package/dist/settle.js +162 -0
  148. package/dist/settle.js.map +1 -0
  149. package/dist/shots.d.ts +109 -0
  150. package/dist/shots.d.ts.map +1 -0
  151. package/dist/shots.js +113 -0
  152. package/dist/shots.js.map +1 -0
  153. package/dist/style.d.ts +47 -0
  154. package/dist/style.d.ts.map +1 -0
  155. package/dist/style.js +154 -0
  156. package/dist/style.js.map +1 -0
  157. package/dist/suite.d.ts +218 -0
  158. package/dist/suite.d.ts.map +1 -0
  159. package/dist/suite.js +219 -0
  160. package/dist/suite.js.map +1 -0
  161. package/dist/summary.d.ts +268 -0
  162. package/dist/summary.d.ts.map +1 -0
  163. package/dist/summary.js +1308 -0
  164. package/dist/summary.js.map +1 -0
  165. package/dist/unscannable.d.ts +75 -0
  166. package/dist/unscannable.d.ts.map +1 -0
  167. package/dist/unscannable.js +111 -0
  168. package/dist/unscannable.js.map +1 -0
  169. package/dist/versions.d.ts +13 -0
  170. package/dist/versions.d.ts.map +1 -0
  171. package/dist/versions.js +39 -0
  172. package/dist/versions.js.map +1 -0
  173. package/dist/wcag-map.d.ts +19 -0
  174. package/dist/wcag-map.d.ts.map +1 -0
  175. package/dist/wcag-map.js +52 -0
  176. package/dist/wcag-map.js.map +1 -0
  177. package/dist/with-setsquare.d.ts +26 -0
  178. package/dist/with-setsquare.d.ts.map +1 -0
  179. package/dist/with-setsquare.js +70 -0
  180. package/dist/with-setsquare.js.map +1 -0
  181. package/package.json +81 -0
@@ -0,0 +1,247 @@
1
+ import { MAX_CONTEXT_LABEL_LENGTH, MAX_FAILURE_SUMMARY_LENGTH, MAX_SELECTORS_PER_FINDING, MAX_SELECTOR_LENGTH, MAX_URL_PATTERN_LENGTH, hostileCharacters, } from '@setsquare/schema/limits';
2
+ /**
3
+ * Sanitisation at source.
4
+ *
5
+ * The trust posture the platform sells rests on this file: "PII-safe by
6
+ * construction" has to be a property of the SDK, not a promise about the
7
+ * platform, because the platform can only be careful with what it receives.
8
+ * Everything here runs in the customer's own process, before a byte crosses any
9
+ * boundary — including the boundary into Playwright's own HTML report and trace,
10
+ * which is why this is the one thing the fixture does rather than the reporter.
11
+ *
12
+ * Every function is pure. No I/O, no page access, no state. That is what lets the
13
+ * fixture call them without becoming stateful, and what lets them be tested
14
+ * against genuinely awful inputs rather than mocked ones.
15
+ *
16
+ * The limits come from `packages/schema` rather than being restated here. A cap
17
+ * that drifted from the contract would surface as rejected payloads in a
18
+ * customer's CI, which is the worst possible place to discover it.
19
+ *
20
+ * What this file deliberately does NOT do: neutralise injection. `<script>` and
21
+ * `](evil)` survive intact, because they are legitimate content in a CSS
22
+ * selector and because destroying evidence to feel safer is a bad trade. Output
23
+ * encoding belongs to each sink — the platform UI, PR-comment markdown, exports,
24
+ * MCP payloads — and each has to do its own.
25
+ */
26
+ /**
27
+ * Remove characters that exist only to deceive.
28
+ *
29
+ * Zero-width characters make two visually identical selectors unequal, which
30
+ * breaks dedupe and clustering silently. Bidi overrides make a rendered line read
31
+ * backwards. Control characters break logs and terminals. None has any business
32
+ * in a CSS selector, so removal loses nothing real.
33
+ */
34
+ export function stripHostileCharacters(value) {
35
+ return value.replace(hostileCharacters(), '');
36
+ }
37
+ /**
38
+ * Cap a string's length, marking the cut.
39
+ *
40
+ * The ellipsis matters: a silently shortened selector looks like a selector that
41
+ * was always that short, and someone will eventually try to use it to find the
42
+ * element. One character of honesty prevents that.
43
+ */
44
+ export function capLength(value, max) {
45
+ if (value.length <= max)
46
+ return value;
47
+ return `${value.slice(0, max - 1)}…`;
48
+ }
49
+ /** Strip, then cap. The order matters — stripping can only shorten. */
50
+ function clean(value, max) {
51
+ return capLength(stripHostileCharacters(value), max);
52
+ }
53
+ /**
54
+ * Reduce a finding's selectors to a representative, bounded set.
55
+ *
56
+ * Deduplicated before capping, deliberately. The hostile fixture emits 617
57
+ * selectors of which 600 are near-identical `span:nth-child(N)`; taking the first
58
+ * ten raw would return ten variations of the same thing and describe nothing.
59
+ * Deduplicating first means the cap spends its budget on distinct shapes.
60
+ *
61
+ * Note that dedupe happens AFTER stripping, which is the only order that works:
62
+ * two selectors differing solely by a zero-width character are the same selector
63
+ * wearing a disguise, and comparing them before stripping would keep both.
64
+ */
65
+ export function sanitiseSelectors(raw, max = MAX_SELECTORS_PER_FINDING) {
66
+ const seen = new Set();
67
+ const selectors = [];
68
+ let shortened = false;
69
+ for (const candidate of raw) {
70
+ const stripped = stripHostileCharacters(candidate);
71
+ if (stripped.length === 0)
72
+ continue;
73
+ const capped = capLength(stripped, MAX_SELECTOR_LENGTH);
74
+ if (capped !== stripped)
75
+ shortened = true;
76
+ if (seen.has(capped))
77
+ continue;
78
+ seen.add(capped);
79
+ if (selectors.length < max)
80
+ selectors.push(capped);
81
+ }
82
+ return {
83
+ selectors,
84
+ selectorsTruncated: shortened || seen.size > selectors.length,
85
+ };
86
+ }
87
+ /** Axe's prose, bounded. */
88
+ export function sanitiseFailureSummary(value) {
89
+ if (!value)
90
+ return undefined;
91
+ const cleaned = clean(value, MAX_FAILURE_SUMMARY_LENGTH);
92
+ return cleaned.length > 0 ? cleaned : undefined;
93
+ }
94
+ /** Theme, persona and state labels — short, and page-derived often enough to matter. */
95
+ export function sanitiseLabel(value) {
96
+ if (!value)
97
+ return undefined;
98
+ const cleaned = clean(value, MAX_CONTEXT_LABEL_LENGTH);
99
+ return cleaned.length > 0 ? cleaned : undefined;
100
+ }
101
+ /**
102
+ * Test identifiers and titles.
103
+ *
104
+ * A deliberately different treatment from page-derived strings, and the
105
+ * difference is worth stating because it looks inconsistent otherwise.
106
+ *
107
+ * Test titles are AUTHORED BY THE CUSTOMER'S DEVELOPERS, not derived from a
108
+ * scanned page. They are not attacker-influenceable in the way a class name is,
109
+ * and they are the only human-readable handle the runs list has — so identifiers
110
+ * inside them are left alone. `the item editor loads for 8fa1c0de` stays exactly
111
+ * that, because redacting it would produce a run list of indistinguishable rows.
112
+ *
113
+ * They can still carry personal data: a data-driven test titled with a candidate's
114
+ * email is entirely ordinary. That is accepted rather than solved, and it is
115
+ * consistent with the posture already recorded in the dev plan — run envelopes
116
+ * structurally carry personal data of customer staff (commit author names and
117
+ * emails, annotation identities), which is exactly why Setsquare designs as a
118
+ * processor rather than pretending the risk away.
119
+ *
120
+ * What IS applied is character sanitisation, because a bidi override in a test
121
+ * title breaks a terminal, a log and a grid cell just as thoroughly as one in a
122
+ * selector, whoever wrote it.
123
+ */
124
+ export function sanitiseTestText(value, max) {
125
+ return clean(value, max);
126
+ }
127
+ /**
128
+ * Turn a URL into a route pattern, dropping everything that identifies a visit.
129
+ *
130
+ * This is sanitisation, not inference, and the distinction is worth being precise
131
+ * about because it decides which stage owns it. Replacing `/items/42/edit` with
132
+ * `/items/:id/edit` removes a record identifier; replacing
133
+ * `/reset-password/8f3a…` removes a live credential. Naming that pattern
134
+ * `item-editor` is inference, and belongs with surface resolution.
135
+ *
136
+ * Three things are removed unconditionally:
137
+ *
138
+ * - the origin, because the schema has no `url` field and the surface is the
139
+ * path, not the host
140
+ * - the query string, which is where tokens, emails and search terms live
141
+ * - the fragment, for the same reason
142
+ *
143
+ * And then any segment that looks like an identifier rather than a name is
144
+ * replaced. The test is deliberately conservative in the SAFE direction: a
145
+ * word-like segment is kept, and anything else becomes `:id`. Over-redacting
146
+ * costs a slightly coarser grid row; under-redacting ships someone's password
147
+ * reset token to a server.
148
+ */
149
+ export function toUrlPattern(url) {
150
+ let path;
151
+ try {
152
+ // A relative URL still parses against a base we then discard, so a caller
153
+ // does not have to know which kind it holds.
154
+ path = new URL(url, 'http://localhost').pathname;
155
+ }
156
+ catch {
157
+ return { pattern: '/', redacted: false };
158
+ }
159
+ if (path === '' || path === '/')
160
+ return { pattern: '/', redacted: false };
161
+ let redacted = false;
162
+ const segments = path.split('/').map((segment) => {
163
+ if (segment === '')
164
+ return segment;
165
+ const decoded = decodeSegment(segment);
166
+ if (looksLikeIdentifier(decoded)) {
167
+ redacted = true;
168
+ return ':id';
169
+ }
170
+ return stripHostileCharacters(decoded);
171
+ });
172
+ return {
173
+ pattern: capLength(segments.join('/'), MAX_URL_PATTERN_LENGTH) || '/',
174
+ redacted,
175
+ };
176
+ }
177
+ /** Percent-encoding hides the shape of a segment; decode before judging it. */
178
+ function decodeSegment(segment) {
179
+ try {
180
+ return decodeURIComponent(segment);
181
+ }
182
+ catch {
183
+ return segment;
184
+ }
185
+ }
186
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
187
+ const ULID = /^[0-9A-HJKMNP-TV-Z]{26}$/;
188
+ const ALL_DIGITS = /^\d+$/;
189
+ const LONG_HEX = /^[0-9a-f]{8,}$/i;
190
+ /** `job`, `v1`, `2fa` — lowercase words, optionally with digits. */
191
+ const LOWER_WORD = /^[a-z0-9]+$/;
192
+ /** `API`, `EU` — acronyms do appear in routes. */
193
+ const UPPER_WORD = /^[A-Z0-9]+$/;
194
+ /** `Settings` — capitalised, as some frameworks route. */
195
+ const CAPITALISED_WORD = /^[A-Z][a-z0-9]*$/;
196
+ /** Longer than any plausible route word, before separators are considered. */
197
+ const MAX_NAME_PART = 24;
198
+ /** A letters-and-digits run this long with no separator is a token, not a name. */
199
+ const TOKENISH_MIXED_LENGTH = 12;
200
+ /**
201
+ * Is one separator-delimited part of a segment a name rather than a token?
202
+ *
203
+ * The load-bearing signal is CASE. Route segments are lowercase, kebab-case,
204
+ * occasionally capitalised or an acronym — they do not mix cases inside a single
205
+ * unseparated run. Tokens do, constantly: `aGVsbG8gd29ybGQK` is alphanumeric and
206
+ * starts with a letter, which an earlier version of this function accepted as a
207
+ * word and passed straight through. Base64 is the common case and it was the
208
+ * case that broke it.
209
+ */
210
+ function partIsNameLike(part) {
211
+ if (part.length === 0 || part.length > MAX_NAME_PART)
212
+ return false;
213
+ if (!(LOWER_WORD.test(part) || UPPER_WORD.test(part) || CAPITALISED_WORD.test(part))) {
214
+ return false;
215
+ }
216
+ // `2fa` is fine; `abc123def456ghi` is a token wearing lowercase.
217
+ const mixesLettersAndDigits = /[a-zA-Z]/.test(part) && /\d/.test(part);
218
+ return !(mixesLettersAndDigits && part.length >= TOKENISH_MIXED_LENGTH);
219
+ }
220
+ /**
221
+ * Is this path segment an identifier rather than a name?
222
+ *
223
+ * Ordered so the cheap certain cases decide first, and biased toward redaction:
224
+ * the final rule KEEPS only what looks like a name, so novel token formats are
225
+ * redacted by default rather than by enumeration. A redaction list that has to
226
+ * predict every id format is a redaction list that eventually misses one — and
227
+ * the thing it misses is a live credential in someone's URL.
228
+ */
229
+ function looksLikeIdentifier(segment) {
230
+ if (segment.length === 0)
231
+ return false;
232
+ if (ALL_DIGITS.test(segment))
233
+ return true;
234
+ if (UUID.test(segment))
235
+ return true;
236
+ if (ULID.test(segment))
237
+ return true;
238
+ if (LONG_HEX.test(segment))
239
+ return true;
240
+ // An email address in a path. Rare, and unmistakably personal data.
241
+ if (segment.includes('@'))
242
+ return true;
243
+ // Every separator-delimited part must look like a name. One token-shaped part
244
+ // is enough to redact the whole segment: `Xj9__aB4-zQ` is not a route.
245
+ return !segment.split(/[-_.]/).every(partIsNameLike);
246
+ }
247
+ //# sourceMappingURL=sanitise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitise.js","sourceRoot":"","sources":["../src/sanitise.ts"],"names":[],"mappings":"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"}
package/dist/scan.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { type Page } from '@playwright/test';
2
+ import { type WcagTarget } from './rules.js';
3
+ import { toUrlPattern } from './sanitise.js';
4
+ /**
5
+ * Level 2: the explicit scan API.
6
+ *
7
+ * Auto-scan gets a grid for free from a suite nobody edited. This is for the
8
+ * states auto-scan cannot reach on its own — a modal that is open, a form showing
9
+ * validation errors, a theme the suite toggles deliberately — where the customer
10
+ * knows something we could never infer and says so.
11
+ *
12
+ * The signature takes `page` explicitly rather than arriving as a Playwright
13
+ * fixture, and that is a deliberate trade. A fixture would read marginally better
14
+ * (`async ({ page, setsquare }) => …`) but would need TypeScript declaration merging
15
+ * to type at all, since the customer imports `test` from `@playwright/test` and
16
+ * not from us. Passing `page` costs one argument, types perfectly, matches what
17
+ * `@axe-core/playwright` already trained everyone to expect — and calling this at
18
+ * all is a test edit by definition, so the "zero test rewrites" promise (which is
19
+ * about auto-scan) is untouched.
20
+ */
21
+ export interface ScanOptions {
22
+ /** The named state being evidenced: `validation-errors`, `menu-open`. */
23
+ state?: string;
24
+ /** Overrides whatever the DOM says. A customer's statement outranks our sniffing. */
25
+ theme?: string;
26
+ /** Overrides the project-name mapping for this scan. */
27
+ persona?: string;
28
+ }
29
+ /**
30
+ * What a scan found, returned to the caller.
31
+ *
32
+ * SDK-computed facts only, and no comparison of any kind — there is nothing here
33
+ * to compare against, locally, ever. Returned rather than kept private so a
34
+ * customer can assert on their own conformance if they want to; P5's `setsquare
35
+ * verify` is the supported way to do that, but nothing here should stop someone
36
+ * writing `expect(result.violations).toBe(0)` today.
37
+ */
38
+ export interface ScanSummary {
39
+ surface: string;
40
+ /** The conformance level this scan was assessed against. */
41
+ wcagTarget: WcagTarget;
42
+ violations: number;
43
+ incomplete: number;
44
+ passes: number;
45
+ /** False when the scan could not run. The SDK never throws into a customer's test. */
46
+ scanned: boolean;
47
+ }
48
+ declare function scan(page: Page, surface: string, options?: ScanOptions): Promise<ScanSummary>;
49
+ /**
50
+ * The explicit API surface.
51
+ *
52
+ * An object rather than a bare function so the shape has room to grow — P5 adds
53
+ * `setsquare.verify()` — without the import line changing under anyone.
54
+ */
55
+ export declare const setsquare: {
56
+ scan: typeof scan;
57
+ };
58
+ /** Exported for the reporter's surface normalisation; the same function scans use. */
59
+ export { toUrlPattern };
60
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAmBnD,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,OAAO,EAAE,OAAO,CAAC;CAClB;AAuCD,iBAAe,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAoMhG;AAQD;;;;;GAKG;AACH,eAAO,MAAM,SAAS;;CAAW,CAAC;AAElC,sFAAsF;AACtF,OAAO,EAAE,YAAY,EAAE,CAAC"}
package/dist/scan.js ADDED
@@ -0,0 +1,246 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { test } from '@playwright/test';
3
+ import { engineVersion, rulesetDigest, runAxe } from './axe.js';
4
+ import { SCAN_RECORD_ATTACHMENT, SCAN_RECORD_VERSION, } from './contract.js';
5
+ import { toFindings, toInapplicable, toPasses } from './findings.js';
6
+ import { capturePerf } from './perf.js';
7
+ import { readDocumentFacts } from './document.js';
8
+ import { pageRuntime } from './observe.js';
9
+ import { recordUnscannable } from './unscannable.js';
10
+ import { isDisabled } from './options.js';
11
+ import { runStatusMessages, runTextSpacing } from './checks/dynamic.js';
12
+ import { setsquareCheckVersions } from './checks/versions.js';
13
+ import { runReadingLevel } from './reading-level.js';
14
+ import { readingLevelEnabled, rulePolicy, scanTimeoutMs } from './runtime.js';
15
+ import { tagsFor } from './rules.js';
16
+ import { sanitiseLabel } from './sanitise.js';
17
+ import { toUrlPattern } from './sanitise.js';
18
+ /**
19
+ * Page loads, tracked so scans of one load share a `visitRef`.
20
+ *
21
+ * A `WeakMap` because the key is a Playwright `Page` and we must not keep it
22
+ * alive: a suite with thousands of pages would otherwise leak every one of them
23
+ * for the lifetime of the worker.
24
+ *
25
+ * The heuristic is "same URL means same visit", which is right for the case that
26
+ * matters — scanning one page repeatedly as its state changes — and wrong only if
27
+ * a test navigates away and back to the identical URL, where it merges two visits
28
+ * that produced identical perf numbers anyway.
29
+ */
30
+ const visits = new WeakMap();
31
+ function visitFor(page, url) {
32
+ const existing = visits.get(page);
33
+ if (existing && existing.url === url)
34
+ return { ref: existing.ref, firstOfVisit: false };
35
+ const ref = createHash('sha256')
36
+ .update(`${url}#${Date.now()}#${Math.random()}`, 'utf8')
37
+ .digest('hex')
38
+ .slice(0, 16);
39
+ visits.set(page, { url, ref, perfCaptured: false });
40
+ return { ref, firstOfVisit: true };
41
+ }
42
+ /** Marks this visit's perf as taken, so later scans of the same load skip it. */
43
+ function markPerfCaptured(page) {
44
+ const existing = visits.get(page);
45
+ if (existing)
46
+ existing.perfCaptured = true;
47
+ }
48
+ function perfAlreadyCaptured(page) {
49
+ return visits.get(page)?.perfCaptured ?? false;
50
+ }
51
+ async function scan(page, surface, options = {}) {
52
+ const declared = { surface };
53
+ const state = sanitiseLabel(options.state);
54
+ if (state)
55
+ declared.state = state;
56
+ const theme = sanitiseLabel(options.theme);
57
+ if (theme)
58
+ declared.theme = theme;
59
+ const persona = sanitiseLabel(options.persona);
60
+ if (persona)
61
+ declared.persona = persona;
62
+ const policy = rulePolicy();
63
+ /*
64
+ * `SETSQUARE_DISABLED` means off, including here.
65
+ *
66
+ * An explicit `setsquare.scan()` is the customer's own call, so there is an argument
67
+ * for honouring it regardless — but the switch exists so somebody can answer
68
+ * "is it you or is it me" in one command, and a baseline with half the SDK
69
+ * still running answers nothing. Off means off.
70
+ *
71
+ * Returns the same shape a refused scan returns, so a spec that asserts on the
72
+ * result keeps working rather than throwing on `undefined` the moment the
73
+ * switch is set.
74
+ */
75
+ if (isDisabled()) {
76
+ return {
77
+ surface,
78
+ wcagTarget: policy.target,
79
+ violations: 0,
80
+ incomplete: 0,
81
+ passes: 0,
82
+ scanned: false,
83
+ };
84
+ }
85
+ try {
86
+ const scanStartedAt = Date.now();
87
+ const results = await runAxe(page, policy, scanTimeoutMs());
88
+ const axeMs = Date.now() - scanStartedAt;
89
+ if (!results) {
90
+ // Explicit scans get the same ceiling as automatic ones. A customer who
91
+ // called `setsquare.scan()` still did not agree to lend us their whole test
92
+ // budget, and the honest return is "no scan" rather than an empty one.
93
+ recordUnscannable(page, toUrlPattern(page.url()).pattern, 'scan-timed-out');
94
+ return {
95
+ surface,
96
+ wcagTarget: policy.target,
97
+ violations: 0,
98
+ incomplete: 0,
99
+ passes: 0,
100
+ scanned: false,
101
+ };
102
+ }
103
+ const findings = toFindings(results);
104
+ const passes = toPasses(results);
105
+ const inapplicable = toInapplicable(results);
106
+ // Setsquare's own checks ride alongside axe's, in the same findings array
107
+ // and under the same never-fail discipline. The digest names the run's
108
+ // configured check set, never which scans happened to execute one — see
109
+ // setsquareCheckVersions for why.
110
+ const setsquareChecks = setsquareCheckVersions(policy);
111
+ if (readingLevelEnabled()) {
112
+ const readingLevel = await runReadingLevel(page);
113
+ if (readingLevel)
114
+ findings.push(readingLevel);
115
+ }
116
+ /*
117
+ * Dynamic checks run on explicit scans because the moment is safe by
118
+ * construction: the customer's test is awaiting this call, the page is
119
+ * single-threaded, and the text-spacing probe injects, measures and
120
+ * removes its stylesheet before we return. An explicit scan of an error
121
+ * state (`{ state: 'validation-errors' }`) is also exactly when the
122
+ * status-message layer has something real to look at.
123
+ */
124
+ for (const check of [await runTextSpacing(page), await runStatusMessages(page)]) {
125
+ if (check.finding)
126
+ findings.push(check.finding);
127
+ if (check.pass)
128
+ passes.push(check.pass);
129
+ if (check.inapplicable)
130
+ inapplicable.push(check.inapplicable);
131
+ }
132
+ const url = page.url();
133
+ const visit = visitFor(page, url);
134
+ const record = {
135
+ recordVersion: SCAN_RECORD_VERSION,
136
+ kind: 'scan',
137
+ page: {
138
+ project: test.info().project.name,
139
+ engine: engineOf(page),
140
+ url,
141
+ },
142
+ declared,
143
+ visitRef: visit.ref,
144
+ axeVersion: engineVersion(results) ?? 'unknown',
145
+ rulesetDigest: rulesetDigest(engineVersion(results) ?? 'unknown', results, tagsFor(policy), setsquareChecks),
146
+ wcagTarget: policy.target,
147
+ findings,
148
+ passes,
149
+ inapplicable,
150
+ };
151
+ const viewport = page.viewportSize();
152
+ if (viewport)
153
+ record.page.viewport = viewport;
154
+ const probesStartedAt = Date.now();
155
+ // One round-trip for theme and document facts, as the auto-scan path does.
156
+ const facts = await readDocumentFacts(page);
157
+ if (facts.theme)
158
+ record.page.observedTheme = facts.theme;
159
+ if (facts.document)
160
+ record.document = facts.document;
161
+ /*
162
+ * Performance is a property of the PAGE LOAD, not of the scan.
163
+ *
164
+ * Scanning one page repeatedly as its state changes — modal closed, then
165
+ * modal open — produces several scans from a single navigation, and every one
166
+ * of them would report identical timings. Emitting it once per visit is what
167
+ * stops pillar 3's averages being weighted by how many states a surface
168
+ * happens to have, which would skew the trend line in a way nobody could see
169
+ * from the chart.
170
+ *
171
+ * Emitted once rather than emitted repeatedly and de-duplicated server-side:
172
+ * the same outcome, minus a whole class of bug.
173
+ */
174
+ if (!perfAlreadyCaptured(page)) {
175
+ const perf = await capturePerf(page);
176
+ if (perf)
177
+ record.perf = perf;
178
+ // Page-runtime rides the same gate for the same reason: it describes the
179
+ // LOAD, so scanning one page in four states must not report the same 500
180
+ // four times.
181
+ const runtime = pageRuntime(page);
182
+ if (runtime)
183
+ record.runtime = runtime;
184
+ // Marked regardless of whether either block materialised — the gate means
185
+ // "this visit's page-load facts have been taken", not "perf existed".
186
+ markPerfCaptured(page);
187
+ }
188
+ const probesMs = Date.now() - probesStartedAt;
189
+ record.cost = {
190
+ totalMs: Date.now() - scanStartedAt,
191
+ axeMs,
192
+ ...(probesMs > 0 ? { probesMs } : {}),
193
+ };
194
+ await test.info().attach(SCAN_RECORD_ATTACHMENT, {
195
+ body: Buffer.from(JSON.stringify(record), 'utf8'),
196
+ contentType: 'application/json',
197
+ });
198
+ return {
199
+ surface,
200
+ wcagTarget: policy.target,
201
+ violations: findings.filter((finding) => finding.status === 'violation').length,
202
+ incomplete: findings.filter((finding) => finding.status === 'incomplete').length,
203
+ passes: passes.length,
204
+ scanned: true,
205
+ };
206
+ }
207
+ catch (error) {
208
+ /*
209
+ * A scan failure is never the customer's build failing.
210
+ *
211
+ * The suite's verdict belongs to the suite; our evidence-gathering has no
212
+ * standing to overturn it. This warns through Playwright's own annotation
213
+ * channel — so it appears in their report next to the test it happened in —
214
+ * and returns a summary that says plainly that nothing was scanned, rather
215
+ * than zeroes that would read as a clean page.
216
+ */
217
+ recordUnscannable(page, toUrlPattern(page.url()).pattern, 'scan-failed');
218
+ test.info().annotations.push({
219
+ type: 'setsquare-warning',
220
+ description: `Setsquare could not scan "${surface}": ${error instanceof Error ? error.message : String(error)}. Your test is unaffected.`,
221
+ });
222
+ return {
223
+ surface,
224
+ wcagTarget: policy.target,
225
+ violations: 0,
226
+ incomplete: 0,
227
+ passes: 0,
228
+ scanned: false,
229
+ };
230
+ }
231
+ }
232
+ /** Playwright exposes the browser type on the page's context. */
233
+ function engineOf(page) {
234
+ const name = page.context().browser()?.browserType().name();
235
+ return name === 'firefox' || name === 'webkit' ? name : 'chromium';
236
+ }
237
+ /**
238
+ * The explicit API surface.
239
+ *
240
+ * An object rather than a bare function so the shape has room to grow — P5 adds
241
+ * `setsquare.verify()` — without the import line changing under anyone.
242
+ */
243
+ export const setsquare = { scan };
244
+ /** Exported for the reporter's surface normalisation; the same function scans use. */
245
+ export { toUrlPattern };
246
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}