@ploaness/governance 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/a11y-focus-guard.d.ts +18 -0
  2. package/dist/a11y-focus-guard.d.ts.map +1 -0
  3. package/dist/a11y-focus-guard.js +17 -0
  4. package/dist/a11y-focus-guard.js.map +1 -0
  5. package/dist/a11y-hit-target.d.ts +7 -1
  6. package/dist/a11y-hit-target.d.ts.map +1 -1
  7. package/dist/a11y-hit-target.js +11 -0
  8. package/dist/a11y-hit-target.js.map +1 -1
  9. package/dist/asset-policy.d.ts +8 -4
  10. package/dist/asset-policy.d.ts.map +1 -1
  11. package/dist/asset-policy.js +28 -19
  12. package/dist/asset-policy.js.map +1 -1
  13. package/dist/browser-discovery.d.ts +116 -0
  14. package/dist/browser-discovery.d.ts.map +1 -0
  15. package/dist/browser-discovery.js +161 -0
  16. package/dist/browser-discovery.js.map +1 -0
  17. package/dist/browser-exemptions.d.ts +107 -0
  18. package/dist/browser-exemptions.d.ts.map +1 -0
  19. package/dist/browser-exemptions.js +163 -0
  20. package/dist/browser-exemptions.js.map +1 -0
  21. package/dist/browser-findings.d.ts +54 -0
  22. package/dist/browser-findings.d.ts.map +1 -0
  23. package/dist/browser-findings.js +52 -0
  24. package/dist/browser-findings.js.map +1 -0
  25. package/dist/browser-health.d.ts +45 -0
  26. package/dist/browser-health.d.ts.map +1 -0
  27. package/dist/browser-health.js +80 -0
  28. package/dist/browser-health.js.map +1 -0
  29. package/dist/browser-rules.d.ts +169 -0
  30. package/dist/browser-rules.d.ts.map +1 -0
  31. package/dist/browser-rules.js +345 -0
  32. package/dist/browser-rules.js.map +1 -0
  33. package/dist/browser-run.d.ts +110 -0
  34. package/dist/browser-run.d.ts.map +1 -0
  35. package/dist/browser-run.js +125 -0
  36. package/dist/browser-run.js.map +1 -0
  37. package/dist/browser-viewports.d.ts +77 -0
  38. package/dist/browser-viewports.d.ts.map +1 -0
  39. package/dist/browser-viewports.js +139 -0
  40. package/dist/browser-viewports.js.map +1 -0
  41. package/dist/change-scope.d.ts +158 -0
  42. package/dist/change-scope.d.ts.map +1 -0
  43. package/dist/change-scope.js +340 -0
  44. package/dist/change-scope.js.map +1 -0
  45. package/dist/cloudfront-functions.d.ts +34 -0
  46. package/dist/cloudfront-functions.d.ts.map +1 -0
  47. package/dist/cloudfront-functions.js +124 -0
  48. package/dist/cloudfront-functions.js.map +1 -0
  49. package/dist/generated-denial.d.ts +4 -1
  50. package/dist/generated-denial.d.ts.map +1 -1
  51. package/dist/generated-denial.js +10 -3
  52. package/dist/generated-denial.js.map +1 -1
  53. package/dist/index.d.ts +12 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +12 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/javascript-runtimes.d.ts +36 -0
  58. package/dist/javascript-runtimes.d.ts.map +1 -0
  59. package/dist/javascript-runtimes.js +40 -0
  60. package/dist/javascript-runtimes.js.map +1 -0
  61. package/dist/jsx-accessibility-scope.d.ts.map +1 -1
  62. package/dist/jsx-accessibility-scope.js +3 -0
  63. package/dist/jsx-accessibility-scope.js.map +1 -1
  64. package/dist/layout-boxes.d.ts.map +1 -1
  65. package/dist/layout-boxes.js +12 -14
  66. package/dist/layout-boxes.js.map +1 -1
  67. package/dist/layout-clip.d.ts +24 -0
  68. package/dist/layout-clip.d.ts.map +1 -0
  69. package/dist/layout-clip.js +82 -0
  70. package/dist/layout-clip.js.map +1 -0
  71. package/dist/layout-defects.d.ts +47 -4
  72. package/dist/layout-defects.d.ts.map +1 -1
  73. package/dist/layout-defects.js +94 -76
  74. package/dist/layout-defects.js.map +1 -1
  75. package/dist/layout-snapshot.d.ts +60 -2
  76. package/dist/layout-snapshot.d.ts.map +1 -1
  77. package/dist/layout-snapshot.js +65 -1
  78. package/dist/layout-snapshot.js.map +1 -1
  79. package/dist/layout-text.d.ts +4 -1
  80. package/dist/layout-text.d.ts.map +1 -1
  81. package/dist/layout-text.js +56 -7
  82. package/dist/layout-text.js.map +1 -1
  83. package/dist/route-map.d.ts +13 -0
  84. package/dist/route-map.d.ts.map +1 -1
  85. package/dist/route-map.js +13 -6
  86. package/dist/route-map.js.map +1 -1
  87. package/dist/run-plan.d.ts +27 -3
  88. package/dist/run-plan.d.ts.map +1 -1
  89. package/dist/run-plan.js +20 -3
  90. package/dist/run-plan.js.map +1 -1
  91. package/dist/settings-layering.d.ts.map +1 -1
  92. package/dist/settings-layering.js +6 -4
  93. package/dist/settings-layering.js.map +1 -1
  94. package/dist/settings.d.ts +32 -0
  95. package/dist/settings.d.ts.map +1 -1
  96. package/dist/settings.js +51 -12
  97. package/dist/settings.js.map +1 -1
  98. package/dist/terraform-blocks.d.ts +13 -0
  99. package/dist/terraform-blocks.d.ts.map +1 -0
  100. package/dist/terraform-blocks.js +27 -0
  101. package/dist/terraform-blocks.js.map +1 -0
  102. package/dist/terraform-policy.d.ts.map +1 -1
  103. package/dist/terraform-policy.js +3 -14
  104. package/dist/terraform-policy.js.map +1 -1
  105. package/dist/wiring-policy.d.ts +5 -2
  106. package/dist/wiring-policy.d.ts.map +1 -1
  107. package/dist/wiring-policy.js +16 -6
  108. package/dist/wiring-policy.js.map +1 -1
  109. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ import { type BrowserFinding, type FindingPlace } from './browser-findings.js';
2
+ import { type LayoutNode, type LayoutSnapshot } from './layout-snapshot.js';
3
+ /** The attribute naming the rules an element is exempt from. */
4
+ export declare const EXEMPTION_ATTRIBUTE: string;
5
+ /** The attribute stating why. */
6
+ export declare const EXEMPTION_REASON_ATTRIBUTE: string;
7
+ /** The rule name of a malformed or stale exemption. */
8
+ export declare const EXEMPTION_RULE: string;
9
+ /**
10
+ * The rules an element can be exempted from: those that judge one element. A rule that judges the
11
+ * whole page - an error, a failed request, a font, stability - has no element to carry an exemption.
12
+ */
13
+ export declare const EXEMPTIBLE_RULES: readonly string[];
14
+ /**
15
+ * The rule names an exemption attribute carries.
16
+ * @param value the attribute value, or undefined when it is absent.
17
+ * @returns each name once, in the order written.
18
+ */
19
+ export declare const exemptionRules: (value: string | undefined) => readonly string[];
20
+ /** An exemption an element declares. */
21
+ export interface DeclaredExemption {
22
+ readonly location: string;
23
+ /** The element's position in its document, the same across viewports, states and engines. */
24
+ readonly key: string;
25
+ readonly label: string;
26
+ readonly rules: readonly string[];
27
+ }
28
+ /** One rule an exemption excused at least once. */
29
+ export interface UsedExemption {
30
+ readonly location: string;
31
+ readonly key: string;
32
+ readonly rule: string;
33
+ }
34
+ /**
35
+ * The exemption that excuses a rule for an element: its own, or the nearest ancestor's.
36
+ * @param snapshot the measured page.
37
+ * @param node the element the rule judged.
38
+ * @param rule the rule.
39
+ * @returns the element carrying the exemption, or undefined when nothing excuses it.
40
+ */
41
+ export declare const exemptingNode: (snapshot: LayoutSnapshot, node: LayoutNode, rule: string) => LayoutNode | undefined;
42
+ /** A finding a rule would report, before exemptions are applied. */
43
+ export interface CandidateFinding {
44
+ readonly rule: string;
45
+ /** The elements the finding is about. Any of them, or an ancestor of any, can excuse it. */
46
+ readonly subjects: readonly LayoutNode[];
47
+ readonly message: string;
48
+ }
49
+ /** What applying exemptions leaves: the findings still reported, and the exemptions that excused one. */
50
+ export interface ExemptedFindings {
51
+ readonly findings: readonly BrowserFinding[];
52
+ readonly used: readonly UsedExemption[];
53
+ }
54
+ /**
55
+ * Drop every candidate an exemption excuses, recording which exemption did.
56
+ * @param snapshot the measured page.
57
+ * @param place where it was measured.
58
+ * @param candidates what the rules found.
59
+ * @returns the findings still reported and the exemptions used.
60
+ */
61
+ export declare const applyExemptions: (snapshot: LayoutSnapshot, place: FindingPlace, candidates: readonly CandidateFinding[]) => ExemptedFindings;
62
+ /**
63
+ * The exemptions a measured page declares.
64
+ * @param snapshot the measured page.
65
+ * @param location the route or story the page is.
66
+ * @returns one entry per element carrying the attribute.
67
+ */
68
+ export declare const declaredExemptions: (snapshot: LayoutSnapshot, location: string) => readonly DeclaredExemption[];
69
+ /**
70
+ * The malformed exemptions on a measured page: an unknown rule name, or no reason.
71
+ * @param snapshot the measured page.
72
+ * @param place where it was measured.
73
+ * @returns one finding per malformed exemption.
74
+ */
75
+ export declare const malformedExemptions: (snapshot: LayoutSnapshot, place: FindingPlace) => readonly BrowserFinding[];
76
+ /**
77
+ * The exemption rules that excused nothing anywhere they were judged.
78
+ * @param declared every exemption the run saw.
79
+ * @param used every exemption that excused a finding.
80
+ * @param judgedRules the rules the run actually applied; a rule that never ran cannot prove anything stale.
81
+ * @param engine the engine to report the finding under.
82
+ * @returns one finding per stale rule name on one element.
83
+ */
84
+ export declare const staleExemptions: (declared: readonly DeclaredExemption[], used: readonly UsedExemption[], judgedRules: readonly string[], engine: string) => readonly BrowserFinding[];
85
+ /** An element carrying an exemption, as the page describes it for a finding on it or a descendant. */
86
+ export interface ExemptionCarrier {
87
+ readonly key: string;
88
+ readonly label: string;
89
+ /** The raw attribute value. */
90
+ readonly rules: string;
91
+ readonly reason: string;
92
+ }
93
+ /** What judging one element-level finding against the exemptions above it yields. */
94
+ export interface CarriedJudgement {
95
+ /** The finding, or undefined when an exemption excused it. */
96
+ readonly finding: BrowserFinding | undefined;
97
+ readonly used: readonly UsedExemption[];
98
+ readonly declared: readonly DeclaredExemption[];
99
+ }
100
+ /**
101
+ * Judge a finding on one element against the exemptions on it and its ancestors.
102
+ * @param finding the finding, as the rule reported it.
103
+ * @param carriers the exemptions on the element or an ancestor, nearest first.
104
+ * @returns the finding unless excused, the exemption that excused it, and every exemption seen.
105
+ */
106
+ export declare const judgeCarried: (finding: BrowserFinding, carriers: readonly ExemptionCarrier[]) => CarriedJudgement;
107
+ //# sourceMappingURL=browser-exemptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-exemptions.d.ts","sourceRoot":"","sources":["../src/browser-exemptions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAa,MAAM,uBAAuB,CAAA;AACzF,OAAO,EAAS,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAElF,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,EAAE,MAA+B,CAAA;AAEjE,iCAAiC;AACjC,eAAO,MAAM,0BAA0B,EAAE,MAAsC,CAAA;AAE/E,uDAAuD;AACvD,eAAO,MAAM,cAAc,EAAE,MAAoB,CAAA;AAEjD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAkB7C,CAAA;AAID;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,SAAS,MAAM,EAEzE,CAAA;AAED,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,6FAA6F;IAC7F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,UAAU,cAAc,EAAE,MAAM,UAAU,EAAE,MAAM,MAAM,KAAG,UAAU,GAAG,SAMrG,CAAA;AAED,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,yGAAyG;AACzG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAA;IAC5C,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAA;CACxC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAU,cAAc,EACxB,OAAO,YAAY,EACnB,YAAY,SAAS,gBAAgB,EAAE,KACtC,gBA+BF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,UAAU,cAAc,EAAE,UAAU,MAAM,KAAG,SAAS,iBAAiB,EAUrG,CAAA;AAEL;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,cAAc,EAAE,OAAO,YAAY,KAAG,SAAS,cAAc,EAgCvG,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAU,SAAS,iBAAiB,EAAE,EACtC,MAAM,SAAS,aAAa,EAAE,EAC9B,aAAa,SAAS,MAAM,EAAE,EAC9B,QAAQ,MAAM,KACb,SAAS,cAAc,EA4BzB,CAAA;AAED,sGAAsG;AACtG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAA;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAChD;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,cAAc,EAAE,UAAU,SAAS,gBAAgB,EAAE,KAAG,gBAgB7F,CAAA"}
@@ -0,0 +1,163 @@
1
+ // Per-element exemptions from the browser rules, and the checks that keep them honest.
2
+ //
3
+ // An exemption names the rules it excuses, space separated, in `data-ploaness-layout`, and says why in
4
+ // `data-ploaness-layout-reason`. It is judged across everything one run measured: an exemption that
5
+ // excused nothing in any pass, viewport, engine or state is stale, because the design it was written
6
+ // for is gone and it now only hides whatever defect appears there next.
7
+ import { findingAt } from './browser-findings.js';
8
+ import { keyOf } from './layout-snapshot.js';
9
+ /** The attribute naming the rules an element is exempt from. */
10
+ export const EXEMPTION_ATTRIBUTE = 'data-ploaness-layout';
11
+ /** The attribute stating why. */
12
+ export const EXEMPTION_REASON_ATTRIBUTE = 'data-ploaness-layout-reason';
13
+ /** The rule name of a malformed or stale exemption. */
14
+ export const EXEMPTION_RULE = 'exemption';
15
+ /**
16
+ * The rules an element can be exempted from: those that judge one element. A rule that judges the
17
+ * whole page - an error, a failed request, a font, stability - has no element to carry an exemption.
18
+ */
19
+ export const EXEMPTIBLE_RULES = [
20
+ 'attached',
21
+ 'overflow',
22
+ 'text-overlap',
23
+ 'clipped-text',
24
+ 'obscured',
25
+ 'focus-obscured',
26
+ 'text-spacing',
27
+ 'text-resize',
28
+ 'small-text',
29
+ 'image-load',
30
+ 'image-distorted',
31
+ 'image-upscaled',
32
+ 'media-space',
33
+ 'focus-visible',
34
+ 'focus-contrast',
35
+ 'focus-order',
36
+ 'disclosure-keyboard',
37
+ ];
38
+ const WHITESPACE = /\s+/u;
39
+ /**
40
+ * The rule names an exemption attribute carries.
41
+ * @param value the attribute value, or undefined when it is absent.
42
+ * @returns each name once, in the order written.
43
+ */
44
+ export const exemptionRules = (value) => [
45
+ ...new Set((value ?? '').split(WHITESPACE).filter((name) => name.length > 0)),
46
+ ];
47
+ /**
48
+ * The exemption that excuses a rule for an element: its own, or the nearest ancestor's.
49
+ * @param snapshot the measured page.
50
+ * @param node the element the rule judged.
51
+ * @param rule the rule.
52
+ * @returns the element carrying the exemption, or undefined when nothing excuses it.
53
+ */
54
+ export const exemptingNode = (snapshot, node, rule) => {
55
+ if (exemptionRules(node.exemption).includes(rule)) {
56
+ return node;
57
+ }
58
+ const parent = snapshot.nodes[node.parent];
59
+ return parent === undefined ? undefined : exemptingNode(snapshot, parent, rule);
60
+ };
61
+ /**
62
+ * Drop every candidate an exemption excuses, recording which exemption did.
63
+ * @param snapshot the measured page.
64
+ * @param place where it was measured.
65
+ * @param candidates what the rules found.
66
+ * @returns the findings still reported and the exemptions used.
67
+ */
68
+ export const applyExemptions = (snapshot, place, candidates) => {
69
+ const judged = candidates.map((candidate) => ({
70
+ candidate,
71
+ by: candidate.subjects.flatMap((subject) => {
72
+ const node = exemptingNode(snapshot, subject, candidate.rule);
73
+ return node === undefined ? [] : [node];
74
+ }),
75
+ }));
76
+ return {
77
+ findings: judged
78
+ .filter((entry) => entry.by.length === 0)
79
+ .map((entry) => findingAt(place, entry.candidate.rule, entry.candidate.message, entry.candidate.subjects.map((subject) => subject.index))),
80
+ used: judged.flatMap((entry) => entry.by.map((node) => ({
81
+ location: place.location,
82
+ key: keyOf(snapshot, node),
83
+ rule: entry.candidate.rule,
84
+ }))),
85
+ };
86
+ };
87
+ /**
88
+ * The exemptions a measured page declares.
89
+ * @param snapshot the measured page.
90
+ * @param location the route or story the page is.
91
+ * @returns one entry per element carrying the attribute.
92
+ */
93
+ export const declaredExemptions = (snapshot, location) => snapshot.nodes
94
+ .filter((node) => node.exemption !== undefined)
95
+ .map((node) => ({
96
+ location,
97
+ key: keyOf(snapshot, node),
98
+ label: node.label,
99
+ rules: exemptionRules(node.exemption),
100
+ }));
101
+ /**
102
+ * The malformed exemptions on a measured page: an unknown rule name, or no reason.
103
+ * @param snapshot the measured page.
104
+ * @param place where it was measured.
105
+ * @returns one finding per malformed exemption.
106
+ */
107
+ export const malformedExemptions = (snapshot, place) => snapshot.nodes.flatMap((node) => {
108
+ if (node.exemption === undefined) {
109
+ return [];
110
+ }
111
+ const rules = exemptionRules(node.exemption);
112
+ const unknown = rules.filter((rule) => !EXEMPTIBLE_RULES.includes(rule));
113
+ return [
114
+ ...(rules.length === 0
115
+ ? [findingAt(place, EXEMPTION_RULE, `${EXEMPTION_ATTRIBUTE} names no rule on ${node.label}`, [node.index])]
116
+ : []),
117
+ ...unknown.map((rule) => findingAt(place, EXEMPTION_RULE, `unknown rule "${rule}" in ${EXEMPTION_ATTRIBUTE} on ${node.label}; ` +
118
+ `the exemptible rules are ${EXEMPTIBLE_RULES.join(', ')}`, [node.index])),
119
+ ...(node.exemptionReason.length === 0
120
+ ? [
121
+ findingAt(place, EXEMPTION_RULE, `layout exemption without a reason on ${node.label}; state it in ${EXEMPTION_REASON_ATTRIBUTE}`, [node.index]),
122
+ ]
123
+ : []),
124
+ ];
125
+ });
126
+ /**
127
+ * The exemption rules that excused nothing anywhere they were judged.
128
+ * @param declared every exemption the run saw.
129
+ * @param used every exemption that excused a finding.
130
+ * @param judgedRules the rules the run actually applied; a rule that never ran cannot prove anything stale.
131
+ * @param engine the engine to report the finding under.
132
+ * @returns one finding per stale rule name on one element.
133
+ */
134
+ export const staleExemptions = (declared, used, judgedRules, engine) => {
135
+ const usedKeys = new Set(used.map((entry) => `${entry.location}|${entry.key}|${entry.rule}`));
136
+ const distinct = declared.filter((entry, index) => declared.findIndex((other) => other.location === entry.location && other.key === entry.key) === index);
137
+ return distinct.flatMap((entry) => entry.rules
138
+ .filter((rule) => EXEMPTIBLE_RULES.includes(rule) &&
139
+ judgedRules.includes(rule) &&
140
+ !usedKeys.has(`${entry.location}|${entry.key}|${rule}`))
141
+ .map((rule) => findingAt({ location: entry.location, state: 'every', engine, viewport: undefined }, EXEMPTION_RULE, `stale layout exemption "${rule}" on ${entry.label}: it excuses nothing in any pass, viewport, ` +
142
+ `engine or state; remove it from ${EXEMPTION_ATTRIBUTE}`)));
143
+ };
144
+ /**
145
+ * Judge a finding on one element against the exemptions on it and its ancestors.
146
+ * @param finding the finding, as the rule reported it.
147
+ * @param carriers the exemptions on the element or an ancestor, nearest first.
148
+ * @returns the finding unless excused, the exemption that excused it, and every exemption seen.
149
+ */
150
+ export const judgeCarried = (finding, carriers) => {
151
+ const excusing = carriers.find((carrier) => exemptionRules(carrier.rules).includes(finding.rule));
152
+ return {
153
+ finding: excusing === undefined ? finding : undefined,
154
+ used: excusing === undefined ? [] : [{ location: finding.location, key: excusing.key, rule: finding.rule }],
155
+ declared: carriers.map((carrier) => ({
156
+ location: finding.location,
157
+ key: carrier.key,
158
+ label: carrier.label,
159
+ rules: exemptionRules(carrier.rules),
160
+ })),
161
+ };
162
+ };
163
+ //# sourceMappingURL=browser-exemptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-exemptions.js","sourceRoot":"","sources":["../src/browser-exemptions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /** A viewport a finding was measured at. */
2
+ export interface FindingViewport {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ }
6
+ /** One defect a browser rule found. */
7
+ export interface BrowserFinding {
8
+ /** The rule's name, as an exemption names it. */
9
+ readonly rule: string;
10
+ /** The route path, or `story:<id>` for a Storybook story. */
11
+ readonly location: string;
12
+ /** What the page was doing: `resting`, `hover`, `focus`, `dark`, `text-spacing`, and so on. */
13
+ readonly state: string;
14
+ /** The browser engine: `chromium`, `firefox` or `webkit`. */
15
+ readonly engine: string;
16
+ /** The viewport, or undefined for a finding that holds at every viewport. */
17
+ readonly viewport: FindingViewport | undefined;
18
+ /** What is wrong, with the element it is wrong on and the values measured. */
19
+ readonly message: string;
20
+ /** The elements to outline in the failure screenshot, as measured-node indices. */
21
+ readonly elements: readonly number[];
22
+ }
23
+ /** Where a finding was measured, without its rule and message. */
24
+ export type FindingPlace = Pick<BrowserFinding, 'location' | 'state' | 'engine' | 'viewport'>;
25
+ /**
26
+ * The text line a finding is reported as.
27
+ * @param finding the finding.
28
+ * @returns `[rule] location @WxH (engine, state): message`.
29
+ */
30
+ export declare const findingLine: (finding: BrowserFinding) => string;
31
+ /**
32
+ * A finding at a place.
33
+ * @param place where it was measured.
34
+ * @param rule the rule's name.
35
+ * @param message what is wrong.
36
+ * @param elements the measured-node indices to outline.
37
+ * @returns the finding.
38
+ */
39
+ export declare const findingAt: (place: FindingPlace, rule: string, message: string, elements?: readonly number[]) => BrowserFinding;
40
+ /**
41
+ * The findings with their duplicate lines removed, in the order they were found.
42
+ * @param findings every finding, possibly repeated across measurements.
43
+ * @returns one finding per distinct line.
44
+ */
45
+ export declare const distinctFindings: (findings: readonly BrowserFinding[]) => readonly BrowserFinding[];
46
+ /**
47
+ * The distinct places among a set of findings, which is where a failure screenshot is taken.
48
+ * @param findings the findings.
49
+ * @returns one entry per location, state, engine and viewport, with the elements to outline there.
50
+ */
51
+ export declare const failingPlaces: (findings: readonly BrowserFinding[]) => readonly (FindingPlace & {
52
+ readonly elements: readonly number[];
53
+ })[];
54
+ //# sourceMappingURL=browser-findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-findings.d.ts","sourceRoot":"","sources":["../src/browser-findings.ts"],"names":[],"mappings":"AAMA,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,CAAA;IAC9C,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,kEAAkE;AAClE,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAA;AAK7F;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,cAAc,KAAG,MAEO,CAAA;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GACpB,OAAO,YAAY,EACnB,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,WAAU,SAAS,MAAM,EAAO,KAC/B,cAAyD,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,UAAU,SAAS,cAAc,EAAE,KAAG,SAAS,cAAc,EAK7F,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACxB,UAAU,SAAS,cAAc,EAAE,KAClC,SAAS,CAAC,YAAY,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,EAoBpE,CAAA"}
@@ -0,0 +1,52 @@
1
+ // The one shape every browser rule reports in, and the one line it is printed as.
2
+ //
3
+ // The sweep runs many passes over many pages, states, viewports and engines, and an agent reading a
4
+ // failure has to know which of them produced it before it can reproduce it. Every rule therefore
5
+ // reports the same fields, and the line printed for it names each of them in the same order.
6
+ const viewportText = (viewport) => viewport === undefined ? '' : ` @${String(viewport.width)}x${String(viewport.height)}`;
7
+ /**
8
+ * The text line a finding is reported as.
9
+ * @param finding the finding.
10
+ * @returns `[rule] location @WxH (engine, state): message`.
11
+ */
12
+ export const findingLine = (finding) => `[${finding.rule}] ${finding.location}${viewportText(finding.viewport)} ` +
13
+ `(${finding.engine}, ${finding.state}): ${finding.message}`;
14
+ /**
15
+ * A finding at a place.
16
+ * @param place where it was measured.
17
+ * @param rule the rule's name.
18
+ * @param message what is wrong.
19
+ * @param elements the measured-node indices to outline.
20
+ * @returns the finding.
21
+ */
22
+ export const findingAt = (place, rule, message, elements = []) => ({ ...place, rule, message, elements });
23
+ /**
24
+ * The findings with their duplicate lines removed, in the order they were found.
25
+ * @param findings every finding, possibly repeated across measurements.
26
+ * @returns one finding per distinct line.
27
+ */
28
+ export const distinctFindings = (findings) => {
29
+ const lines = findings.map((finding) => findingLine(finding));
30
+ return findings.filter((finding, index) => lines.indexOf(findingLine(finding)) === index);
31
+ };
32
+ /**
33
+ * The distinct places among a set of findings, which is where a failure screenshot is taken.
34
+ * @param findings the findings.
35
+ * @returns one entry per location, state, engine and viewport, with the elements to outline there.
36
+ */
37
+ export const failingPlaces = (findings) => {
38
+ const keyOf = (finding) => `${finding.location}|${finding.state}|${finding.engine}|${viewportText(finding.viewport)}`;
39
+ const firsts = findings.filter((finding, index) => findings.findIndex((other) => keyOf(other) === keyOf(finding)) === index);
40
+ return firsts.map((first) => ({
41
+ location: first.location,
42
+ state: first.state,
43
+ engine: first.engine,
44
+ viewport: first.viewport,
45
+ elements: [
46
+ ...new Set(findings
47
+ .filter((finding) => keyOf(finding) === keyOf(first))
48
+ .flatMap((finding) => finding.elements)),
49
+ ],
50
+ }));
51
+ };
52
+ //# sourceMappingURL=browser-findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-findings.js","sourceRoot":"","sources":["../src/browser-findings.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,EAAE;AACF,oGAAoG;AACpG,iGAAiG;AACjG,6FAA6F;AA6B7F,MAAM,YAAY,GAAG,CAAC,QAAqC,EAAU,EAAE,CACrE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAA;AAExF;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAuB,EAAU,EAAE,CAC7D,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACzE,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;AAE7D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,KAAmB,EACnB,IAAY,EACZ,OAAe,EACf,WAA8B,EAAE,EAChB,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAmC,EAA6B,EAAE;IACjG,MAAM,KAAK,GAAsB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAuB,EAAU,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;IACxG,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,OAAuB,EAAE,KAAa,EAAW,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,CACnG,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,QAAmC,EACmC,EAAE;IACxE,MAAM,KAAK,GAAG,CAAC,OAAqB,EAAU,EAAE,CAC9C,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC5F,MAAM,MAAM,GAA8B,QAAQ,CAAC,MAAM,CACvD,CAAC,OAAuB,EAAE,KAAa,EAAW,EAAE,CAClD,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAqB,EAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,CACpG,CAAA;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAqB,EAA2D,EAAE,CAAC,CAAC;QACrG,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE;YACR,GAAG,IAAI,GAAG,CACR,QAAQ;iBACL,MAAM,CAAC,CAAC,OAAuB,EAAW,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC;iBAC7E,OAAO,CAAC,CAAC,OAAuB,EAAqB,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAC7E;SACF;KACF,CAAC,CAAC,CAAA;AACL,CAAC,CAAA"}
@@ -0,0 +1,45 @@
1
+ /** No uncaught error, `console.error` or hydration warning while the page loads or is driven. */
2
+ export declare const PAGE_ERROR_RULE: string;
3
+ /** No subresource answering 4xx or 5xx, and no request the network guard blocked. */
4
+ export declare const FAILED_REQUEST_RULE: string;
5
+ /** Every crawled internal link answers. */
6
+ export declare const BROKEN_LINK_RULE: string;
7
+ /** The page settled and its geometry held still between two measurements. */
8
+ export declare const PAGE_NOT_STABLE_RULE: string;
9
+ /**
10
+ * The origins a page may load from: the application and every declared auxiliary server.
11
+ * @param urls the server URLs.
12
+ * @returns each origin once.
13
+ */
14
+ export declare const allowedOrigins: (urls: readonly string[]) => readonly string[];
15
+ /**
16
+ * Whether the network guard lets a request through.
17
+ * @param url the requested URL.
18
+ * @param origins the allowed origins.
19
+ * @returns true for an allowed origin and for a URL that never leaves the browser.
20
+ */
21
+ export declare const isAllowedRequest: (url: string, origins: readonly string[]) => boolean;
22
+ /** A console message as the browser reports it. */
23
+ export interface ConsoleEntry {
24
+ readonly type: string;
25
+ readonly text: string;
26
+ }
27
+ /**
28
+ * The console messages that are page errors: every `console.error`, and any warning about hydration.
29
+ * @param entry the console message.
30
+ * @returns the message to report, or undefined when it is not a page error.
31
+ */
32
+ export declare const consolePageError: (entry: ConsoleEntry) => string | undefined;
33
+ /**
34
+ * A browser message cut to a length a report line can carry.
35
+ * @param text the message.
36
+ * @returns its first line, at most 300 characters.
37
+ */
38
+ export declare const reportedText: (text: string) => string;
39
+ /**
40
+ * Whether a response status is a failure.
41
+ * @param status the HTTP status.
42
+ * @returns true for 4xx and 5xx.
43
+ */
44
+ export declare const isFailedStatus: (status: number) => boolean;
45
+ //# sourceMappingURL=browser-health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-health.d.ts","sourceRoot":"","sources":["../src/browser-health.ts"],"names":[],"mappings":"AAQA,iGAAiG;AACjG,eAAO,MAAM,eAAe,EAAE,MAAqB,CAAA;AAEnD,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,EAAE,MAAyB,CAAA;AAE3D,2CAA2C;AAC3C,eAAO,MAAM,gBAAgB,EAAE,MAAsB,CAAA;AAErD,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,EAAE,MAA0B,CAAA;AAY7D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,SAAS,MAAM,EAEvE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,EAAE,SAAS,SAAS,MAAM,EAAE,KAAG,OAO1E,CAAA;AAED,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAQD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,YAAY,KAAG,MAAM,GAAG,SAK/D,CAAA;AAKD;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MAG3C,CAAA;AAID;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,OAA+B,CAAA"}
@@ -0,0 +1,80 @@
1
+ // Runtime health: what the browser itself reports while a page loads and is driven.
2
+ //
3
+ // A page can pass every geometric and accessibility rule while it throws, fails to load a stylesheet,
4
+ // or quietly depends on a third-party host. The browser reports each of these as an event, and these
5
+ // rules turn each event into a finding. The guard rule is the reason the suite needs no internet: any
6
+ // request outside the application and its declared auxiliary servers is blocked and reported, so a page
7
+ // cannot pass on one machine and fail on another for a reason outside the repository.
8
+ /** No uncaught error, `console.error` or hydration warning while the page loads or is driven. */
9
+ export const PAGE_ERROR_RULE = 'page-error';
10
+ /** No subresource answering 4xx or 5xx, and no request the network guard blocked. */
11
+ export const FAILED_REQUEST_RULE = 'failed-request';
12
+ /** Every crawled internal link answers. */
13
+ export const BROKEN_LINK_RULE = 'broken-link';
14
+ /** The page settled and its geometry held still between two measurements. */
15
+ export const PAGE_NOT_STABLE_RULE = 'page-not-stable';
16
+ const LOCAL_SCHEMES = new Set(['data:', 'blob:', 'about:']);
17
+ const originOf = (url) => {
18
+ try {
19
+ return new URL(url).origin;
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ };
25
+ /**
26
+ * The origins a page may load from: the application and every declared auxiliary server.
27
+ * @param urls the server URLs.
28
+ * @returns each origin once.
29
+ */
30
+ export const allowedOrigins = (urls) => [
31
+ ...new Set(urls.flatMap((url) => [originOf(url) ?? ''].filter(Boolean))),
32
+ ];
33
+ /**
34
+ * Whether the network guard lets a request through.
35
+ * @param url the requested URL.
36
+ * @param origins the allowed origins.
37
+ * @returns true for an allowed origin and for a URL that never leaves the browser.
38
+ */
39
+ export const isAllowedRequest = (url, origins) => {
40
+ const scheme = /^[a-z][a-z\d+.-]*:/iu.exec(url)?.[0]?.toLowerCase() ?? '';
41
+ if (LOCAL_SCHEMES.has(scheme)) {
42
+ return true;
43
+ }
44
+ const origin = originOf(url);
45
+ return origin !== undefined && origins.includes(origin);
46
+ };
47
+ // The browser logs a failed subresource as a console error too. It is reported once, as the failed
48
+ // request it describes, with its URL and status.
49
+ const RESOURCE_FAILURE = /^Failed to load resource\b/u;
50
+ const HYDRATION = /hydrat/iu;
51
+ /**
52
+ * The console messages that are page errors: every `console.error`, and any warning about hydration.
53
+ * @param entry the console message.
54
+ * @returns the message to report, or undefined when it is not a page error.
55
+ */
56
+ export const consolePageError = (entry) => {
57
+ if (entry.type === 'error' && !RESOURCE_FAILURE.test(entry.text)) {
58
+ return `console.error: ${entry.text}`;
59
+ }
60
+ return entry.type === 'warning' && HYDRATION.test(entry.text) ? `hydration warning: ${entry.text}` : undefined;
61
+ };
62
+ /** The longest stretch of a browser message a finding repeats. */
63
+ const MAX_MESSAGE = 300;
64
+ /**
65
+ * A browser message cut to a length a report line can carry.
66
+ * @param text the message.
67
+ * @returns its first line, at most 300 characters.
68
+ */
69
+ export const reportedText = (text) => {
70
+ const [firstLine = ''] = text.trim().split('\n', 1);
71
+ return firstLine.length > MAX_MESSAGE ? `${firstLine.slice(0, MAX_MESSAGE)}...` : firstLine;
72
+ };
73
+ const HTTP_ERROR = 400;
74
+ /**
75
+ * Whether a response status is a failure.
76
+ * @param status the HTTP status.
77
+ * @returns true for 4xx and 5xx.
78
+ */
79
+ export const isFailedStatus = (status) => status >= HTTP_ERROR;
80
+ //# sourceMappingURL=browser-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-health.js","sourceRoot":"","sources":["../src/browser-health.ts"],"names":[],"mappings":"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"}