@ploaness/governance 1.0.0 → 1.2.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 (222) hide show
  1. package/dist/a11y-hit-target.d.ts +35 -0
  2. package/dist/a11y-hit-target.d.ts.map +1 -0
  3. package/dist/a11y-hit-target.js +82 -0
  4. package/dist/a11y-hit-target.js.map +1 -0
  5. package/dist/a11y-incomplete.d.ts +18 -0
  6. package/dist/a11y-incomplete.d.ts.map +1 -0
  7. package/dist/a11y-incomplete.js +65 -0
  8. package/dist/a11y-incomplete.js.map +1 -0
  9. package/dist/access-boundary.d.ts +62 -6
  10. package/dist/access-boundary.d.ts.map +1 -1
  11. package/dist/access-boundary.js +201 -6
  12. package/dist/access-boundary.js.map +1 -1
  13. package/dist/admin-view-coverage.d.ts +25 -0
  14. package/dist/admin-view-coverage.d.ts.map +1 -0
  15. package/dist/admin-view-coverage.js +74 -0
  16. package/dist/admin-view-coverage.js.map +1 -0
  17. package/dist/asset-policy.d.ts.map +1 -1
  18. package/dist/asset-policy.js +1 -23
  19. package/dist/asset-policy.js.map +1 -1
  20. package/dist/axe-coverage.d.ts +40 -0
  21. package/dist/axe-coverage.d.ts.map +1 -0
  22. package/dist/axe-coverage.js +62 -0
  23. package/dist/axe-coverage.js.map +1 -0
  24. package/dist/banned-typography.d.ts.map +1 -1
  25. package/dist/banned-typography.js +26 -0
  26. package/dist/banned-typography.js.map +1 -1
  27. package/dist/blocklist-entries.d.ts +36 -0
  28. package/dist/blocklist-entries.d.ts.map +1 -0
  29. package/dist/blocklist-entries.js +338 -0
  30. package/dist/blocklist-entries.js.map +1 -0
  31. package/dist/blocklist.d.ts +91 -0
  32. package/dist/blocklist.d.ts.map +1 -0
  33. package/dist/blocklist.js +187 -0
  34. package/dist/blocklist.js.map +1 -0
  35. package/dist/build-diagnosis.d.ts +10 -0
  36. package/dist/build-diagnosis.d.ts.map +1 -0
  37. package/dist/build-diagnosis.js +38 -0
  38. package/dist/build-diagnosis.js.map +1 -0
  39. package/dist/container-failure.d.ts.map +1 -1
  40. package/dist/container-failure.js +1 -18
  41. package/dist/container-failure.js.map +1 -1
  42. package/dist/container-files.d.ts +16 -0
  43. package/dist/container-files.d.ts.map +1 -1
  44. package/dist/container-files.js +23 -8
  45. package/dist/container-files.js.map +1 -1
  46. package/dist/container-images.d.ts +43 -0
  47. package/dist/container-images.d.ts.map +1 -0
  48. package/dist/container-images.js +157 -0
  49. package/dist/container-images.js.map +1 -0
  50. package/dist/dependency-freshness.d.ts +8 -0
  51. package/dist/dependency-freshness.d.ts.map +1 -1
  52. package/dist/dependency-freshness.js +2 -18
  53. package/dist/dependency-freshness.js.map +1 -1
  54. package/dist/doc-blocks.d.ts.map +1 -1
  55. package/dist/doc-blocks.js +1 -13
  56. package/dist/doc-blocks.js.map +1 -1
  57. package/dist/document-references.d.ts.map +1 -1
  58. package/dist/document-references.js +7 -1
  59. package/dist/document-references.js.map +1 -1
  60. package/dist/editorconfig-policy.d.ts +7 -0
  61. package/dist/editorconfig-policy.d.ts.map +1 -1
  62. package/dist/editorconfig-policy.js +19 -12
  63. package/dist/editorconfig-policy.js.map +1 -1
  64. package/dist/environment-coherence.d.ts +87 -0
  65. package/dist/environment-coherence.d.ts.map +1 -0
  66. package/dist/environment-coherence.js +128 -0
  67. package/dist/environment-coherence.js.map +1 -0
  68. package/dist/environment-files.d.ts.map +1 -1
  69. package/dist/environment-files.js +1 -19
  70. package/dist/environment-files.js.map +1 -1
  71. package/dist/file-roles.d.ts +7 -2
  72. package/dist/file-roles.d.ts.map +1 -1
  73. package/dist/file-roles.js +10 -9
  74. package/dist/file-roles.js.map +1 -1
  75. package/dist/freshness-repair.d.ts +82 -0
  76. package/dist/freshness-repair.d.ts.map +1 -0
  77. package/dist/freshness-repair.js +91 -0
  78. package/dist/freshness-repair.js.map +1 -0
  79. package/dist/generated-denial.d.ts.map +1 -1
  80. package/dist/generated-denial.js +1 -9
  81. package/dist/generated-denial.js.map +1 -1
  82. package/dist/generated-drift.d.ts +18 -0
  83. package/dist/generated-drift.d.ts.map +1 -0
  84. package/dist/generated-drift.js +24 -0
  85. package/dist/generated-drift.js.map +1 -0
  86. package/dist/harness-package.d.ts +13 -0
  87. package/dist/harness-package.d.ts.map +1 -0
  88. package/dist/harness-package.js +20 -0
  89. package/dist/harness-package.js.map +1 -0
  90. package/dist/history-policy.d.ts.map +1 -1
  91. package/dist/history-policy.js +1 -7
  92. package/dist/history-policy.js.map +1 -1
  93. package/dist/image-assets.d.ts.map +1 -1
  94. package/dist/image-assets.js +1 -10
  95. package/dist/image-assets.js.map +1 -1
  96. package/dist/index.d.ts +22 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +22 -1
  99. package/dist/index.js.map +1 -1
  100. package/dist/install-policy.d.ts +21 -0
  101. package/dist/install-policy.d.ts.map +1 -1
  102. package/dist/install-policy.js +53 -1
  103. package/dist/install-policy.js.map +1 -1
  104. package/dist/jsx-accessibility-scope.d.ts +17 -0
  105. package/dist/jsx-accessibility-scope.d.ts.map +1 -0
  106. package/dist/jsx-accessibility-scope.js +36 -0
  107. package/dist/jsx-accessibility-scope.js.map +1 -0
  108. package/dist/jsx-accessibility.d.ts +13 -0
  109. package/dist/jsx-accessibility.d.ts.map +1 -0
  110. package/dist/jsx-accessibility.js +188 -0
  111. package/dist/jsx-accessibility.js.map +1 -0
  112. package/dist/network-policy.d.ts.map +1 -1
  113. package/dist/network-policy.js +1 -12
  114. package/dist/network-policy.js.map +1 -1
  115. package/dist/oxlint-policy.d.ts +16 -0
  116. package/dist/oxlint-policy.d.ts.map +1 -0
  117. package/dist/oxlint-policy.js +28 -0
  118. package/dist/oxlint-policy.js.map +1 -0
  119. package/dist/oxlint-report.d.ts +29 -0
  120. package/dist/oxlint-report.d.ts.map +1 -0
  121. package/dist/oxlint-report.js +84 -0
  122. package/dist/oxlint-report.js.map +1 -0
  123. package/dist/oxlint-scope.d.ts +13 -0
  124. package/dist/oxlint-scope.d.ts.map +1 -0
  125. package/dist/oxlint-scope.js +46 -0
  126. package/dist/oxlint-scope.js.map +1 -0
  127. package/dist/oxlint-suppressions.d.ts +17 -0
  128. package/dist/oxlint-suppressions.d.ts.map +1 -0
  129. package/dist/oxlint-suppressions.js +72 -0
  130. package/dist/oxlint-suppressions.js.map +1 -0
  131. package/dist/paired-assets.d.ts.map +1 -1
  132. package/dist/paired-assets.js.map +1 -1
  133. package/dist/payload-access.d.ts +6 -0
  134. package/dist/payload-access.d.ts.map +1 -1
  135. package/dist/payload-access.js +188 -57
  136. package/dist/payload-access.js.map +1 -1
  137. package/dist/payload-configs.d.ts +23 -0
  138. package/dist/payload-configs.d.ts.map +1 -0
  139. package/dist/payload-configs.js +96 -0
  140. package/dist/payload-configs.js.map +1 -0
  141. package/dist/payload-defaults.d.ts +128 -0
  142. package/dist/payload-defaults.d.ts.map +1 -0
  143. package/dist/payload-defaults.js +295 -0
  144. package/dist/payload-defaults.js.map +1 -0
  145. package/dist/payload-field-access.d.ts +4 -0
  146. package/dist/payload-field-access.d.ts.map +1 -0
  147. package/dist/payload-field-access.js +51 -0
  148. package/dist/payload-field-access.js.map +1 -0
  149. package/dist/payload-policy.d.ts +13 -0
  150. package/dist/payload-policy.d.ts.map +1 -1
  151. package/dist/payload-policy.js +171 -32
  152. package/dist/payload-policy.js.map +1 -1
  153. package/dist/payload-source.js +2 -2
  154. package/dist/payload-source.js.map +1 -1
  155. package/dist/relationship-cleanup.d.ts +18 -0
  156. package/dist/relationship-cleanup.d.ts.map +1 -0
  157. package/dist/relationship-cleanup.js +96 -0
  158. package/dist/relationship-cleanup.js.map +1 -0
  159. package/dist/release-age.d.ts.map +1 -1
  160. package/dist/release-age.js +1 -16
  161. package/dist/release-age.js.map +1 -1
  162. package/dist/request-limits.d.ts.map +1 -1
  163. package/dist/request-limits.js +1 -11
  164. package/dist/request-limits.js.map +1 -1
  165. package/dist/route-coverage.d.ts +27 -0
  166. package/dist/route-coverage.d.ts.map +1 -0
  167. package/dist/route-coverage.js +57 -0
  168. package/dist/route-coverage.js.map +1 -0
  169. package/dist/route-map.d.ts +65 -0
  170. package/dist/route-map.d.ts.map +1 -0
  171. package/dist/route-map.js +168 -0
  172. package/dist/route-map.js.map +1 -0
  173. package/dist/run-plan.d.ts.map +1 -1
  174. package/dist/run-plan.js +1 -19
  175. package/dist/run-plan.js.map +1 -1
  176. package/dist/secret-policy.d.ts.map +1 -1
  177. package/dist/secret-policy.js +5 -15
  178. package/dist/secret-policy.js.map +1 -1
  179. package/dist/settings-layering.d.ts +8 -30
  180. package/dist/settings-layering.d.ts.map +1 -1
  181. package/dist/settings-layering.js +19 -48
  182. package/dist/settings-layering.js.map +1 -1
  183. package/dist/settings.d.ts +19 -2
  184. package/dist/settings.d.ts.map +1 -1
  185. package/dist/settings.js +5 -1
  186. package/dist/settings.js.map +1 -1
  187. package/dist/source-security.d.ts +4 -0
  188. package/dist/source-security.d.ts.map +1 -0
  189. package/dist/source-security.js +56 -0
  190. package/dist/source-security.js.map +1 -0
  191. package/dist/source-text.d.ts +16 -0
  192. package/dist/source-text.d.ts.map +1 -1
  193. package/dist/source-text.js +46 -16
  194. package/dist/source-text.js.map +1 -1
  195. package/dist/toolchain-pins.d.ts.map +1 -1
  196. package/dist/toolchain-pins.js +2 -19
  197. package/dist/toolchain-pins.js.map +1 -1
  198. package/dist/version-policy.d.ts +7 -8
  199. package/dist/version-policy.d.ts.map +1 -1
  200. package/dist/version-policy.js +46 -26
  201. package/dist/version-policy.js.map +1 -1
  202. package/dist/vulnerability-policy.d.ts.map +1 -1
  203. package/dist/vulnerability-policy.js +1 -11
  204. package/dist/vulnerability-policy.js.map +1 -1
  205. package/dist/wiring-policy.d.ts.map +1 -1
  206. package/dist/wiring-policy.js +4 -1
  207. package/dist/wiring-policy.js.map +1 -1
  208. package/dist/wiring-violation.d.ts.map +1 -1
  209. package/dist/wiring-violation.js +1 -6
  210. package/dist/wiring-violation.js.map +1 -1
  211. package/dist/workspace-policy.d.ts.map +1 -1
  212. package/dist/workspace-policy.js +3 -9
  213. package/dist/workspace-policy.js.map +1 -1
  214. package/dist/yaml-blocks.d.ts +10 -0
  215. package/dist/yaml-blocks.d.ts.map +1 -1
  216. package/dist/yaml-blocks.js +20 -0
  217. package/dist/yaml-blocks.js.map +1 -1
  218. package/package.json +2 -2
  219. package/dist/url-safety.d.ts +0 -12
  220. package/dist/url-safety.d.ts.map +0 -1
  221. package/dist/url-safety.js +0 -58
  222. package/dist/url-safety.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ /** What a browser must report about a control before this module can classify it. */
2
+ export interface HitTargetProbe {
3
+ /** Border-box width in CSS pixels. */
4
+ readonly width: number;
5
+ /** Border-box height in CSS pixels. */
6
+ readonly height: number;
7
+ /** The computed `clip-path`, verbatim, or `none`. */
8
+ readonly clipPath: string;
9
+ /** The computed `clip`, verbatim, or `auto`. */
10
+ readonly clip: string;
11
+ /**
12
+ * Whether `elementFromPoint` at the box centre returned this element or a descendant of it.
13
+ *
14
+ * This is the same question Playwright's actionability check asks, and asking it here is what lets
15
+ * a clipped element and an obscured one be told apart rather than both reported as a timeout.
16
+ */
17
+ readonly ownsCentrePoint: boolean;
18
+ }
19
+ /** How a control can be reached, and therefore which passes of the sweep apply to it. */
20
+ export type HitTargetVerdict = 'hoverable' | 'visually-hidden' | 'obscured';
21
+ /** The verdict together with the sentence a report prints for it. */
22
+ export interface HitTargetClassification {
23
+ readonly verdict: HitTargetVerdict;
24
+ readonly reason: string;
25
+ }
26
+ /**
27
+ * Decide how a control can be reached, so the sweep hovers what it can and explains what it cannot.
28
+ *
29
+ * A control that owns its own centre point is hoverable whatever its styling, which is what keeps a
30
+ * decorative `clip-path` on a real button from being mistaken for a hiding technique.
31
+ * @param probe what the browser reports about the control.
32
+ * @returns the verdict and the sentence describing it.
33
+ */
34
+ export declare const classifyHitTarget: (probe: HitTargetProbe) => HitTargetClassification;
35
+ //# sourceMappingURL=a11y-hit-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-hit-target.d.ts","sourceRoot":"","sources":["../src/a11y-hit-target.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAClC;AAED,yFAAyF;AACzF,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,iBAAiB,GAAG,UAAU,CAAA;AAE3E,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAyED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,cAAc,KAAG,uBAkBzD,CAAA"}
@@ -0,0 +1,82 @@
1
+ // Classify whether a control has painted pixels and a pointer target before a hover contrast scan.
2
+ // The legacy visually-hidden pattern draws a 1px box and clips it away, so its box has area and its
3
+ // paint does not. Anything at or under this in either dimension is that pattern rather than a control.
4
+ const HIDDEN_BOX_LIMIT = 1;
5
+ // An `inset()` is empty when its opposing insets meet or cross, which is arithmetic rather than a
6
+ // spelling: `inset(50%)` collapses both axes, and so do `inset(50% 0)` and `inset(60% 10% 60% 10%)`.
7
+ // Matching the literal `inset(50%)` would have covered the one form every visually-hidden helper class
8
+ // ships and quietly missed the rest, so the shorthand is expanded and the pairs are added instead.
9
+ const FULL_EXTENT = 100;
10
+ const INSET_SHORTHAND = /^inset\(([^)]*)\)$/;
11
+ // `inset()` may carry a `round <radius>` clause, which describes corners rather than extent.
12
+ const ROUND_CLAUSE = /\sround\s[\s\S]*$/;
13
+ const PERCENT = '%';
14
+ // A percentage is the only unit that can be reasoned about without knowing the element's size, and it
15
+ // is the unit the pattern is always written in. A pixel inset is left undecided, which classifies the
16
+ // control as reachable and runs the check.
17
+ const asPercentage = (token) => token.endsWith(PERCENT) ? Number(token.slice(0, -PERCENT.length)) : Number.NaN;
18
+ // CSS box shorthand: one value applies to all four sides, two to the vertical and horizontal pairs,
19
+ // three add a distinct bottom, four are top/right/bottom/left in order.
20
+ const expandBoxShorthand = (values) => {
21
+ const [first, second, third, fourth] = values;
22
+ if (first === undefined) {
23
+ return [];
24
+ }
25
+ const right = second ?? first;
26
+ return [first, right, third ?? first, fourth ?? right];
27
+ };
28
+ const isEmptyInset = (argument) => {
29
+ const values = expandBoxShorthand(argument
30
+ .replace(ROUND_CLAUSE, '')
31
+ .split(/[\s,]+/)
32
+ .filter((token) => token.length > 0)
33
+ .map((token) => asPercentage(token)));
34
+ const [top, right, bottom, left] = values;
35
+ if (top === undefined || right === undefined || bottom === undefined || left === undefined) {
36
+ return false;
37
+ }
38
+ return top + bottom >= FULL_EXTENT || left + right >= FULL_EXTENT;
39
+ };
40
+ // A circle or ellipse of no radius clips everything away. Only an explicit zero counts: a keyword
41
+ // radius such as `closest-side` depends on the box and is left undecided.
42
+ const ZERO_RADIUS_SHAPE = /^(?:circle|ellipse)\(\s*0(?:px|%|em|rem)?[\s,)]/;
43
+ // `clip: rect(...)` with no extent, in the spellings browsers compute it to. The 1px form is the
44
+ // classic `.sr-only` rule, which pairs a 1px box with a rect that keeps none of it.
45
+ const EMPTY_CLIP = /^rect\(\s*[01](?:px)?[\s,]+[01](?:px)?[\s,]+[01](?:px)?[\s,]+[01](?:px)?\s*\)$/;
46
+ const isEmptyClipPath = (clipPath) => {
47
+ const normalised = clipPath.trim().toLowerCase();
48
+ const inset = INSET_SHORTHAND.exec(normalised);
49
+ return inset === null ? ZERO_RADIUS_SHAPE.test(normalised) : isEmptyInset(inset[1] ?? '');
50
+ };
51
+ const isEmptyClip = (clip) => EMPTY_CLIP.test(clip.trim().toLowerCase());
52
+ const hasNoPaintedArea = (probe) => probe.width <= 0 ||
53
+ probe.height <= 0 ||
54
+ (probe.width <= HIDDEN_BOX_LIMIT && probe.height <= HIDDEN_BOX_LIMIT) ||
55
+ isEmptyClipPath(probe.clipPath) ||
56
+ isEmptyClip(probe.clip);
57
+ /**
58
+ * Decide how a control can be reached, so the sweep hovers what it can and explains what it cannot.
59
+ *
60
+ * A control that owns its own centre point is hoverable whatever its styling, which is what keeps a
61
+ * decorative `clip-path` on a real button from being mistaken for a hiding technique.
62
+ * @param probe what the browser reports about the control.
63
+ * @returns the verdict and the sentence describing it.
64
+ */
65
+ export const classifyHitTarget = (probe) => {
66
+ if (probe.ownsCentrePoint) {
67
+ return { verdict: 'hoverable', reason: 'the control is reachable at its own centre point' };
68
+ }
69
+ if (hasNoPaintedArea(probe)) {
70
+ return {
71
+ verdict: 'visually-hidden',
72
+ reason: 'the control is visually hidden (a zero-area box or a clip that removes it), so it has no ' +
73
+ 'hit target and no painted pixels whose contrast could be measured under hover',
74
+ };
75
+ }
76
+ return {
77
+ verdict: 'obscured',
78
+ reason: 'the control paints but something else answers at its centre point, so a pointer cannot ' +
79
+ 'reach it; this is a defect in the page rather than in the sweep',
80
+ };
81
+ };
82
+ //# sourceMappingURL=a11y-hit-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-hit-target.js","sourceRoot":"","sources":["../src/a11y-hit-target.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The axe `incomplete` message keys that describe a defect rather than a question.
3
+ *
4
+ * Exported so a project reading a finding can see the whole of what is enforced, and so a spec can
5
+ * assert the enforced set rather than a single member of it.
6
+ */
7
+ export declare const DEFINITE_INCOMPLETE_KEYS: readonly string[];
8
+ /**
9
+ * Pick the entries of an axe `incomplete` bucket that are defects rather than open questions.
10
+ *
11
+ * Takes `unknown` rather than axe's own result type because this package declares no dependencies and
12
+ * must not acquire one to read a shape it only ever inspects. The caller passes `scan.incomplete`
13
+ * straight through.
14
+ * @param incomplete the `incomplete` array of an axe result.
15
+ * @returns one human-readable finding per definite defect, in the order axe reported them.
16
+ */
17
+ export declare const findDefiniteIncomplete: (incomplete: unknown) => readonly string[];
18
+ //# sourceMappingURL=a11y-incomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-incomplete.d.ts","sourceRoot":"","sources":["../src/a11y-incomplete.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAAmB,CAAA;AAuDzE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAAI,YAAY,OAAO,KAAG,SAAS,MAAM,EAGpE,CAAA"}
@@ -0,0 +1,65 @@
1
+ // Promote only mechanically decidable axe incomplete results to failures; retain cases requiring visual judgment.
2
+ import { asRecord, asText, isArray } from './json-shapes.js';
3
+ /**
4
+ * The axe `incomplete` message keys that describe a defect rather than a question.
5
+ *
6
+ * Exported so a project reading a finding can see the whole of what is enforced, and so a spec can
7
+ * assert the enforced set rather than a single member of it.
8
+ */
9
+ export const DEFINITE_INCOMPLETE_KEYS = ['equalRatio'];
10
+ // A node's checks live under three keys, and which one carries the result depends on how the rule was
11
+ // composed rather than on anything the reader chose. Reading only `any` would miss a rule expressed as
12
+ // `all`, which is a silence of exactly the kind this module exists to remove.
13
+ const CHECK_KEYS = ['any', 'all', 'none'];
14
+ // A node names itself by CSS selector, and axe nests the selector when the node is inside an iframe.
15
+ // Joining rather than taking the first keeps a finding pointing at the element a reader has to open.
16
+ const TARGET_SEPARATOR = ' >>> ';
17
+ const targetOf = (node) => {
18
+ const target = asRecord(node)['target'];
19
+ if (!isArray(target)) {
20
+ return '(unknown element)';
21
+ }
22
+ const parts = target.map((part) => asText(part));
23
+ return parts.length === 0 ? '(unknown element)' : parts.join(TARGET_SEPARATOR);
24
+ };
25
+ const checksOf = (node) => CHECK_KEYS.flatMap((key) => {
26
+ const checks = asRecord(node)[key];
27
+ return isArray(checks) ? checks : [];
28
+ });
29
+ // The message axe itself wrote, rather than one restated here. A locale-aware runner returns the
30
+ // translated sentence, and a finding that quoted an English copy would disagree with the report the
31
+ // same run printed.
32
+ const describeCheck = (ruleId, node, check) => {
33
+ const message = asText(asRecord(check)['message']);
34
+ const stated = message.length > 0 ? message : 'axe reported no message';
35
+ return `${ruleId} on ${targetOf(node)}: ${stated}`;
36
+ };
37
+ // axe names the reason it could not decide under `data.messageKey`, which is the only field here that
38
+ // is a stable identifier rather than prose: the message beside it is localised.
39
+ const messageKeyOf = (check) => {
40
+ const data = asRecord(asRecord(check)['data']);
41
+ return asText(data['messageKey']);
42
+ };
43
+ const definiteChecks = (ruleId, node) => checksOf(node)
44
+ .filter((check) => DEFINITE_INCOMPLETE_KEYS.includes(messageKeyOf(check)))
45
+ .map((check) => describeCheck(ruleId, node, check));
46
+ const definiteNodes = (result) => {
47
+ const nodes = asRecord(result)['nodes'];
48
+ const ruleId = asText(asRecord(result)['id']);
49
+ return isArray(nodes)
50
+ ? nodes.flatMap((node) => definiteChecks(ruleId, node))
51
+ : [];
52
+ };
53
+ /**
54
+ * Pick the entries of an axe `incomplete` bucket that are defects rather than open questions.
55
+ *
56
+ * Takes `unknown` rather than axe's own result type because this package declares no dependencies and
57
+ * must not acquire one to read a shape it only ever inspects. The caller passes `scan.incomplete`
58
+ * straight through.
59
+ * @param incomplete the `incomplete` array of an axe result.
60
+ * @returns one human-readable finding per definite defect, in the order axe reported them.
61
+ */
62
+ export const findDefiniteIncomplete = (incomplete) => isArray(incomplete)
63
+ ? incomplete.flatMap((result) => definiteNodes(result))
64
+ : [];
65
+ //# sourceMappingURL=a11y-incomplete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-incomplete.js","sourceRoot":"","sources":["../src/a11y-incomplete.ts"],"names":[],"mappings":"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"}
@@ -7,35 +7,91 @@ export type ReportedPermission = boolean | {
7
7
  readonly permission?: boolean;
8
8
  readonly where?: unknown;
9
9
  };
10
- /** One entity's operations, keyed as Payload keys them. */
11
- export type ReportedEntity = Readonly<Record<string, ReportedPermission>>;
12
- /** The body of `/api/access`, read for the operations alone. */
10
+ /**
11
+ * A `fields` map as Payload sends it, or the bare `true` its sanitisation collapses a fully permitted
12
+ * one to - which says every field without naming one, and so cannot be enumerated.
13
+ */
14
+ export type ReportedFields = boolean | Readonly<Record<string, ReportedEntity>>;
15
+ /**
16
+ * One entity's operations and, beside them, the per-field map Payload reports for it.
17
+ *
18
+ * A field is reported in exactly the shape an entity is - its own operations, and its own `fields` when
19
+ * it is an array or a group - so one type describes both and the walk over them is the same walk.
20
+ */
21
+ export interface ReportedEntity {
22
+ readonly fields?: ReportedFields;
23
+ readonly [operation: string]: ReportedFields | ReportedPermission | undefined;
24
+ }
25
+ /** The body of `/api/access`, read for the operations and the fields beneath them. */
13
26
  export interface AccessReport {
14
27
  readonly canAccessAdmin?: boolean;
15
28
  readonly collections?: Readonly<Record<string, ReportedEntity>>;
16
29
  readonly globals?: Readonly<Record<string, ReportedEntity>>;
17
30
  }
18
- /** One permission the running application actually grants. */
31
+ /** One permission the running application actually grants, on an entity or on one field of it. */
19
32
  export interface Granted {
20
33
  readonly entity: string;
21
34
  readonly operation: string;
35
+ readonly field?: string;
22
36
  }
23
37
  /**
24
38
  * A write is never a default a project should be able to reach by accident: Payload grants none of these
25
39
  * to an anonymous caller unless the project's own rule says so. `read` is judged too, but it is the one
26
40
  * a public site legitimately grants, which is what `publicAccess` exists to record.
41
+ *
42
+ * `readVersions` and `unlock` are judged for the reason `payload-defaults` demands they be decided at
43
+ * all. A version is the whole document, so a read narrowed to an audience is undone by asking for a
44
+ * version instead - and `readVersions: theSameHelperAsRead` reads as obviously right while handing a
45
+ * stranger everything the helper's filter was written to keep back. `unlock` clears the lockout an
46
+ * attempt cap just set. Payload reports both beside the four, and both were passing unlooked-at.
47
+ *
48
+ * Composed from the lists `payload-defaults.ts` already declares rather than restated here, so the two
49
+ * halves of the harness cannot drift into disagreeing about what an operation is called.
27
50
  */
28
51
  export declare const JUDGED_OPERATIONS: readonly string[];
29
52
  /** Whether Payload reported this operation as permitted, in either of the two shapes it sends. */
30
- export declare const isPermitted: (permission: ReportedPermission | undefined) => boolean;
53
+ export declare const isPermitted: (permission: ReportedFields | ReportedPermission | undefined) => boolean;
31
54
  /** Every permission an access report grants, across collections and globals alike. */
32
55
  export declare const grantedPermissions: (report: AccessReport) => readonly Granted[];
33
56
  /** How one grant is named, both in a report and in the `publicAccess` entry that would declare it. */
34
57
  export declare const describeGrant: (granted: Granted) => string;
58
+ /** The field paths an entity reports but stores nothing behind, keyed by the entity's slug. */
59
+ export type DatalessFields = Readonly<Record<string, readonly string[]>>;
60
+ /**
61
+ * Every field path a built Payload configuration reports but keeps no data behind.
62
+ *
63
+ * Read through the untyped accessors rather than through Payload's types, because this package depends
64
+ * on nothing: what arrives is the configuration Payload booted, and what is read of it is the same four
65
+ * keys the response is built from.
66
+ * @param config the built Payload configuration, as its module's default export.
67
+ * @returns the dataless paths of each entity that has any, keyed by slug.
68
+ */
69
+ export declare const datalessFieldsIn: (config: unknown) => DatalessFields;
35
70
  /**
36
71
  * The grants the running application makes to a stranger that the project has not recorded. Empty is
37
72
  * the only passing answer: a project that declares nothing is judged most strictly, and a declaration
38
73
  * narrows nothing else.
74
+ * @param report the body of `/api/access` for an anonymous caller.
75
+ * @param declared the project's `publicAccess` entries.
76
+ * @param dataless the field paths the configuration reports but stores nothing behind, from
77
+ * `datalessFieldsIn`. Defaulted to none, which judges every reported path and so errs strictly.
78
+ * @returns one finding per undeclared grant, in report order.
79
+ */
80
+ export declare const undeclaredGrants: (report: AccessReport, declared: readonly PublicAccess[], dataless?: DatalessFields) => readonly string[];
81
+ /**
82
+ * Every `publicAccess` declaration the running application does not bear out: an entity and operation
83
+ * it grants nobody, or a field path under a granted operation that no reported field matches.
84
+ *
85
+ * The mirror of `undeclaredGrants`, and the access-boundary twin of an exclusion that excludes nothing:
86
+ * a declaration matching no grant records a decision with no effect, outlives the field it was written
87
+ * for, and covers that field the day it is exposed again without anyone being asked. A `.**` subtree
88
+ * counts as borne out while the group or anything beneath it is granted, which keeps the trade that
89
+ * form makes; a subtree under which nothing is granted is a misspelling like any other.
90
+ * @param report the body of `/api/access` for an anonymous caller.
91
+ * @param declared the project's `publicAccess` entries.
92
+ * @param dataless the field paths the configuration reports but stores nothing behind, from
93
+ * `datalessFieldsIn`. A declaration naming one is therefore reported stale and can be removed.
94
+ * @returns one finding per stale entry or field, in declaration order.
39
95
  */
40
- export declare const undeclaredGrants: (report: AccessReport, declared: readonly PublicAccess[]) => readonly string[];
96
+ export declare const staleDeclarations: (report: AccessReport, declared: readonly PublicAccess[], dataless?: DatalessFields) => readonly string[];
41
97
  //# sourceMappingURL=access-boundary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-boundary.d.ts","sourceRoot":"","sources":["../src/access-boundary.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE/D,2DAA2D;AAC3D,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAA;AAEzE,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;CAC5D;AAED,8DAA8D;AAC9D,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAA2C,CAAA;AAE1F,kGAAkG;AAClG,eAAO,MAAM,WAAW,GAAI,YAAY,kBAAkB,GAAG,SAAS,KAAG,OACO,CAAA;AAUhF,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,YAAY,KAAG,SAAS,OAAO,EAGzE,CAAA;AAED,sGAAsG;AACtG,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,KAAG,MAAkD,CAAA;AAQnG;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,YAAY,EACpB,UAAU,SAAS,YAAY,EAAE,KAChC,SAAS,MAAM,EAG4C,CAAA"}
1
+ {"version":3,"file":"access-boundary.d.ts","sourceRoot":"","sources":["../src/access-boundary.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE/D;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAE/E;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAA;IAChC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,kBAAkB,GAAG,SAAS,CAAA;CAC9E;AAED,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;CAC5D;AAED,kGAAkG;AAClG,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAI9C,CAAA;AAOD,kGAAkG;AAClG,eAAO,MAAM,WAAW,GACtB,YAAY,cAAc,GAAG,kBAAkB,GAAG,SAAS,KAC1D,OASF,CAAA;AA0DD,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,YAAY,KAAG,SAAS,OAAO,EAGzE,CAAA;AAED,sGAAsG;AACtG,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,KAAG,MAG8B,CAAA;AAE/E,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAA;AA4ExE;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,OAAO,KAAG,cAGjD,CAAA;AAyCF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,YAAY,EACpB,UAAU,SAAS,YAAY,EAAE,EACjC,WAAU,cAAmB,KAC5B,SAAS,MAAM,EAG4C,CAAA;AA0B9D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,YAAY,EACpB,UAAU,SAAS,YAAY,EAAE,EACjC,WAAU,cAAmB,KAC5B,SAAS,MAAM,EAiBjB,CAAA"}
@@ -1,26 +1,221 @@
1
+ // Compare Payload anonymous grants with explicit entity and field declarations, including stale exceptions.
2
+ import { isArray, readKey } from './json-shapes.js';
3
+ import { COLLECTION_OPERATIONS, READ_VERSIONS_OPERATION, UNLOCK_OPERATION, } from './payload-defaults.js';
1
4
  /**
2
5
  * A write is never a default a project should be able to reach by accident: Payload grants none of these
3
6
  * to an anonymous caller unless the project's own rule says so. `read` is judged too, but it is the one
4
7
  * a public site legitimately grants, which is what `publicAccess` exists to record.
8
+ *
9
+ * `readVersions` and `unlock` are judged for the reason `payload-defaults` demands they be decided at
10
+ * all. A version is the whole document, so a read narrowed to an audience is undone by asking for a
11
+ * version instead - and `readVersions: theSameHelperAsRead` reads as obviously right while handing a
12
+ * stranger everything the helper's filter was written to keep back. `unlock` clears the lockout an
13
+ * attempt cap just set. Payload reports both beside the four, and both were passing unlooked-at.
14
+ *
15
+ * Composed from the lists `payload-defaults.ts` already declares rather than restated here, so the two
16
+ * halves of the harness cannot drift into disagreeing about what an operation is called.
5
17
  */
6
- export const JUDGED_OPERATIONS = ['create', 'read', 'update', 'delete'];
18
+ export const JUDGED_OPERATIONS = [
19
+ ...COLLECTION_OPERATIONS,
20
+ READ_VERSIONS_OPERATION,
21
+ UNLOCK_OPERATION,
22
+ ];
23
+ /** The path standing in for a `fields` map Payload collapsed, which grants every field at once. */
24
+ const EVERY_FIELD = '*';
25
+ const PATH_SEPARATOR = '.';
7
26
  /** Whether Payload reported this operation as permitted, in either of the two shapes it sends. */
8
- export const isPermitted = (permission) => typeof permission === 'boolean' ? permission : permission?.permission === true;
9
- const grantsIn = (entities) => Object.entries(entities).flatMap(([entity, permissions]) => JUDGED_OPERATIONS.filter((operation) => isPermitted(permissions[operation])).map((operation) => ({ entity, operation })));
27
+ export const isPermitted = (permission) => {
28
+ if (permission === undefined || typeof permission === 'boolean') {
29
+ return permission === true;
30
+ }
31
+ // Both remaining shapes carry an index signature or an optional `permission`, so both are readable
32
+ // through one accessor rather than through a guard that would have to tell them apart by a key a
33
+ // field could legitimately be named.
34
+ const constrained = permission;
35
+ return constrained.permission === true;
36
+ };
37
+ /**
38
+ * Every field path one operation reaches, joined as Payload nests them.
39
+ *
40
+ * Recursive because an array or group field carries a `fields` map of its own, so a leaf is named by
41
+ * the path that reaches it (`playerFleet.shipKind`) rather than by its own name alone, which would
42
+ * collide between two arrays sharing a field name.
43
+ */
44
+ const fieldPathsFor = (fields, operation, prefix) => {
45
+ if (fields === undefined) {
46
+ return [];
47
+ }
48
+ if (typeof fields === 'boolean') {
49
+ return fields ? [`${prefix}${EVERY_FIELD}`] : [];
50
+ }
51
+ return Object.entries(fields).flatMap(([name, reported]) => [
52
+ ...(isPermitted(reported[operation]) ? [`${prefix}${name}`] : []),
53
+ ...fieldPathsFor(reported.fields, operation, `${prefix}${name}${PATH_SEPARATOR}`),
54
+ ]);
55
+ };
56
+ const grantsForOperation = (entity, permissions, operation) => [
57
+ { entity, operation },
58
+ ...fieldPathsFor(permissions.fields, operation, '').map((field) => ({ entity, operation, field })),
59
+ ];
60
+ // The field map is walked only for an operation the ENTITY grants, and that is a correctness rule
61
+ // rather than a saving. Payload reports a field's verdict whether or not the operation carrying it is
62
+ // reachable: an auth collection that denies `read` to a stranger still lists fields carrying
63
+ // `read: true`, because the field rule genuinely says yes and the collection rule is what stops the
64
+ // caller. Judging those would name permissions nobody holds, and a project cannot close a finding that
65
+ // describes no exposure - so it would close it by declaring it, which teaches exactly the wrong habit.
66
+ const grantsForEntity = (entity, permissions) => JUDGED_OPERATIONS.filter((operation) => isPermitted(permissions[operation])).flatMap((operation) => grantsForOperation(entity, permissions, operation));
67
+ const grantsIn = (entities) => Object.entries(entities).flatMap(([entity, permissions]) => grantsForEntity(entity, permissions));
10
68
  /** Every permission an access report grants, across collections and globals alike. */
11
69
  export const grantedPermissions = (report) => [
12
70
  ...grantsIn(report.collections ?? {}),
13
71
  ...grantsIn(report.globals ?? {}),
14
72
  ];
15
73
  /** How one grant is named, both in a report and in the `publicAccess` entry that would declare it. */
16
- export const describeGrant = (granted) => `${granted.entity}.${granted.operation}`;
17
- const isDeclared = (granted, declared) => declared.some((entry) => entry.entity === granted.entity && entry.operation === granted.operation);
74
+ export const describeGrant = (granted) => granted.field === undefined
75
+ ? `${granted.entity}.${granted.operation}`
76
+ : `${granted.entity}.${granted.operation}${PATH_SEPARATOR}${granted.field}`;
77
+ /** The field type Payload renders in the panel and keeps no column for. */
78
+ const UI_FIELD_TYPE = 'ui';
79
+ // A tab set is a container whose children live one level down; each tab hoists or adds a segment of its
80
+ // own, which is why it is walked apart from an ordinary named field.
81
+ const TABS_FIELD_TYPE = 'tabs';
82
+ // Blocks are reported as a map of their own, keyed by block slug, and `fieldPathsFor` never descends
83
+ // into one. Descending here would compose paths the report never sends, which match nothing.
84
+ const BLOCKS_FIELD_TYPE = 'blocks';
85
+ /** A field's or tab's own name, or nothing when it is presentational and hoists its children. */
86
+ const nameOf = (subject) => {
87
+ const name = readKey(subject, 'name');
88
+ return typeof name === 'string' && name.length > 0 ? name : undefined;
89
+ };
90
+ // A named container adds its segment; an unnamed one - a row, a collapsible, an unnamed group or tab -
91
+ // stores nothing of its own, and Payload reports its children at the parent's level.
92
+ const prefixUnder = (prefix, name) => name === undefined ? prefix : `${prefix}${name}${PATH_SEPARATOR}`;
93
+ // Stated as the lists to descend into rather than by recursing here, so the walk below recurses into
94
+ // itself alone. A tab set contributes one list per tab, each hoisting or adding its own segment; a UI
95
+ // field and a block set contribute none; everything else contributes its own `fields`, which is absent
96
+ // on a leaf and read as an empty list.
97
+ const childFieldsOf = (field, prefix) => {
98
+ const type = readKey(field, 'type');
99
+ if (type === UI_FIELD_TYPE || type === BLOCKS_FIELD_TYPE) {
100
+ return [];
101
+ }
102
+ if (type === TABS_FIELD_TYPE) {
103
+ const tabs = readKey(field, 'tabs');
104
+ return (isArray(tabs) ? tabs : []).map((tab) => ({
105
+ fields: readKey(tab, 'fields'),
106
+ prefix: prefixUnder(prefix, nameOf(tab)),
107
+ }));
108
+ }
109
+ return [{ fields: readKey(field, 'fields'), prefix: prefixUnder(prefix, nameOf(field)) }];
110
+ };
111
+ // An unnamed UI field is reported by nothing, so it names no path to drop.
112
+ const datalessPathOf = (field, prefix) => {
113
+ const name = nameOf(field);
114
+ return name !== undefined && readKey(field, 'type') === UI_FIELD_TYPE ? [`${prefix}${name}`] : [];
115
+ };
116
+ const datalessInFields = (fields, prefix) => (isArray(fields) ? fields : []).flatMap((field) => [
117
+ ...datalessPathOf(field, prefix),
118
+ ...childFieldsOf(field, prefix).flatMap((child) => datalessInFields(child.fields, child.prefix)),
119
+ ]);
120
+ const datalessInEntities = (entities) => Object.fromEntries((isArray(entities) ? entities : []).flatMap((entity) => {
121
+ const slug = readKey(entity, 'slug');
122
+ const paths = datalessInFields(readKey(entity, 'fields'), '');
123
+ return typeof slug !== 'string' || paths.length === 0 ? [] : [[slug, paths]];
124
+ }));
125
+ /**
126
+ * Every field path a built Payload configuration reports but keeps no data behind.
127
+ *
128
+ * Read through the untyped accessors rather than through Payload's types, because this package depends
129
+ * on nothing: what arrives is the configuration Payload booted, and what is read of it is the same four
130
+ * keys the response is built from.
131
+ * @param config the built Payload configuration, as its module's default export.
132
+ * @returns the dataless paths of each entity that has any, keyed by slug.
133
+ */
134
+ export const datalessFieldsIn = (config) => ({
135
+ ...datalessInEntities(readKey(config, 'collections')),
136
+ ...datalessInEntities(readKey(config, 'globals')),
137
+ });
138
+ const isDataless = (granted, dataless) => granted.field !== undefined && (dataless[granted.entity] ?? []).includes(granted.field);
139
+ /** Every permission the report grants over something the configuration actually stores. */
140
+ const dataBearingGrants = (report, dataless) => grantedPermissions(report).filter((granted) => !isDataless(granted, dataless));
141
+ // A field is matched by name and never by a wildcard the project could write for itself. An entry
142
+ // listing `*` covers only the grant Payload itself collapsed to `*`, where every field is open and the
143
+ // declaration is therefore exact rather than blanket.
144
+ //
145
+ // The ONE pattern a project may write is a trailing `.**` on a non-empty, star-free prefix: `sizes.**`
146
+ // names the group and everything beneath it - `sizes`, `sizes.thumbnail.url`, and a `sizes.*` Payload
147
+ // collapsed. An upload collection with eight renditions reports fifty-seven paths under `sizes`, and a
148
+ // list that long is a list nobody rereads, which defeats the declaration it exists to be. The trade is
149
+ // stated rather than hidden: a field added under that group later is covered by the same declaration.
150
+ // Every other spelling - `**` alone, `**` mid-path, `*` under a prefix - is a literal name, and a
151
+ // literal Payload never reports covers nothing, which fails in the safe direction. A top-level field
152
+ // therefore still has to be named one by one.
153
+ const SUBTREE_DECLARATION = /^([^*]+)\.\*\*$/;
154
+ const coversPath = (declared, field) => {
155
+ const prefix = SUBTREE_DECLARATION.exec(declared)?.[1];
156
+ return prefix === undefined
157
+ ? declared === field
158
+ : field === prefix || field.startsWith(`${prefix}${PATH_SEPARATOR}`);
159
+ };
160
+ const coversField = (entry, field) => (entry.fields ?? []).some((declared) => coversPath(declared, field));
161
+ const isDeclared = (granted, declared) => declared.some((entry) => {
162
+ if (entry.entity !== granted.entity || entry.operation !== granted.operation) {
163
+ return false;
164
+ }
165
+ return granted.field === undefined ? true : coversField(entry, granted.field);
166
+ });
18
167
  /**
19
168
  * The grants the running application makes to a stranger that the project has not recorded. Empty is
20
169
  * the only passing answer: a project that declares nothing is judged most strictly, and a declaration
21
170
  * narrows nothing else.
171
+ * @param report the body of `/api/access` for an anonymous caller.
172
+ * @param declared the project's `publicAccess` entries.
173
+ * @param dataless the field paths the configuration reports but stores nothing behind, from
174
+ * `datalessFieldsIn`. Defaulted to none, which judges every reported path and so errs strictly.
175
+ * @returns one finding per undeclared grant, in report order.
22
176
  */
23
- export const undeclaredGrants = (report, declared) => grantedPermissions(report)
177
+ export const undeclaredGrants = (report, declared, dataless = {}) => dataBearingGrants(report, dataless)
24
178
  .filter((granted) => !isDeclared(granted, declared))
25
179
  .map((granted) => describeGrant(granted));
180
+ const isGranted = (entry, granted) => granted.some((grant) => grant.entity === entry.entity && grant.operation === entry.operation);
181
+ const isCovering = (entry, declared, granted) => granted.some((grant) => grant.entity === entry.entity &&
182
+ grant.operation === entry.operation &&
183
+ grant.field !== undefined &&
184
+ coversPath(declared, grant.field));
185
+ const describeDeclaration = (entry, field) => describeGrant({
186
+ entity: entry.entity,
187
+ operation: entry.operation,
188
+ ...(field !== undefined && { field }),
189
+ });
190
+ const NO_MATCHING_GRANT = 'covers no grant the application makes';
191
+ /**
192
+ * Every `publicAccess` declaration the running application does not bear out: an entity and operation
193
+ * it grants nobody, or a field path under a granted operation that no reported field matches.
194
+ *
195
+ * The mirror of `undeclaredGrants`, and the access-boundary twin of an exclusion that excludes nothing:
196
+ * a declaration matching no grant records a decision with no effect, outlives the field it was written
197
+ * for, and covers that field the day it is exposed again without anyone being asked. A `.**` subtree
198
+ * counts as borne out while the group or anything beneath it is granted, which keeps the trade that
199
+ * form makes; a subtree under which nothing is granted is a misspelling like any other.
200
+ * @param report the body of `/api/access` for an anonymous caller.
201
+ * @param declared the project's `publicAccess` entries.
202
+ * @param dataless the field paths the configuration reports but stores nothing behind, from
203
+ * `datalessFieldsIn`. A declaration naming one is therefore reported stale and can be removed.
204
+ * @returns one finding per stale entry or field, in declaration order.
205
+ */
206
+ export const staleDeclarations = (report, declared, dataless = {}) => {
207
+ const granted = dataBearingGrants(report, dataless);
208
+ return declared.flatMap((entry) => {
209
+ if (!isGranted(entry, granted)) {
210
+ return [
211
+ `publicAccess entry "${describeDeclaration(entry)}" ${NO_MATCHING_GRANT}; the access rule ` +
212
+ 'was closed or the entity renamed, so remove the entry',
213
+ ];
214
+ }
215
+ return (entry.fields ?? [])
216
+ .filter((field) => !isCovering(entry, field, granted))
217
+ .map((field) => `publicAccess entry "${describeDeclaration(entry, field)}" ${NO_MATCHING_GRANT}; the field ` +
218
+ 'was renamed, hidden or misspelt, so correct or remove it');
219
+ });
220
+ };
26
221
  //# sourceMappingURL=access-boundary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-boundary.js","sourceRoot":"","sources":["../src/access-boundary.ts"],"names":[],"mappings":"AAyCA;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAE1F,kGAAkG;AAClG,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAA0C,EAAW,EAAE,CACjF,OAAO,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAA;AAEhF,MAAM,QAAQ,GAAG,CAAC,QAAkD,EAAsB,EAAE,CAC1F,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAC9B,CAAC,CAAC,MAAM,EAAE,WAAW,CAAoC,EAAsB,EAAE,CAC/E,iBAAiB,CAAC,MAAM,CAAC,CAAC,SAAiB,EAAW,EAAE,CACtD,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CACpC,CAAC,GAAG,CAAC,CAAC,SAAiB,EAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CACjE,CAAA;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAoB,EAAsB,EAAE,CAAC;IAC9E,GAAG,QAAQ,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IACrC,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;CAClC,CAAA;AAED,sGAAsG;AACtG,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAgB,EAAU,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAA;AAEnG,MAAM,UAAU,GAAG,CAAC,OAAgB,EAAE,QAAiC,EAAW,EAAE,CAClF,QAAQ,CAAC,IAAI,CACX,CAAC,KAAmB,EAAW,EAAE,CAC/B,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,CAC3E,CAAA;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,MAAoB,EACpB,QAAiC,EACd,EAAE,CACrB,kBAAkB,CAAC,MAAM,CAAC;KACvB,MAAM,CAAC,CAAC,OAAgB,EAAW,EAAE,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;KACrE,GAAG,CAAC,CAAC,OAAgB,EAAU,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"access-boundary.js","sourceRoot":"","sources":["../src/access-boundary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type SpecSource } from './axe-coverage.js';
2
+ import type { PayloadViolation } from './payload-source.js';
3
+ /** A custom admin view a Payload configuration declares, and where it declares it. */
4
+ export interface DeclaredAdminView {
5
+ readonly path: string;
6
+ readonly line: number;
7
+ }
8
+ /**
9
+ * The custom admin views a Payload configuration declares.
10
+ *
11
+ * Both `admin.components.views` and a collection's own are found, because both produce a route the
12
+ * pinned sweep will not reach and both are the project's markup.
13
+ * @param source the file's text.
14
+ * @returns one entry per declared view path, in the order the file declares them.
15
+ */
16
+ export declare const findDeclaredAdminViews: (source: string) => readonly DeclaredAdminView[];
17
+ /**
18
+ * The declared views that no specification scans for accessibility.
19
+ * @param views the views a configuration declares.
20
+ * @param specs the project's test files, which is where a scan would be written.
21
+ * @param everyFile every file a spec may import a scan from, the specs included.
22
+ * @returns one violation per view nothing looks at.
23
+ */
24
+ export declare const findUnscannedAdminViews: (views: readonly DeclaredAdminView[], specs: readonly SpecSource[], everyFile: readonly SpecSource[]) => readonly PayloadViolation[];
25
+ //# sourceMappingURL=admin-view-coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-view-coverage.d.ts","sourceRoot":"","sources":["../src/admin-view-coverage.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAG3D,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAsCD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,MAAM,KAAG,SAAS,iBAAiB,EAejF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,SAAS,iBAAiB,EAAE,EACnC,OAAO,SAAS,UAAU,EAAE,EAC5B,WAAW,SAAS,UAAU,EAAE,KAC/B,SAAS,gBAAgB,EA6BxB,CAAA"}