ultra11y 2.22.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 (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/docker/Dockerfile +7 -0
  4. package/docker/package.json +9 -0
  5. package/docker/runner.mjs +23 -0
  6. package/package.json +73 -0
  7. package/scripts/bench.mjs +151 -0
  8. package/scripts/build-act-corpus.mjs +143 -0
  9. package/scripts/build-adjudication.mjs +503 -0
  10. package/scripts/build-pack-rgaa.mjs +397 -0
  11. package/scripts/build-standards.mjs +548 -0
  12. package/scripts/copy-bundle.mjs +39 -0
  13. package/scripts/import-pack.mjs +107 -0
  14. package/scripts/sync-engine.mjs +84 -0
  15. package/scripts/sync-version.mjs +43 -0
  16. package/scripts/ultra11y.mjs +52739 -0
  17. package/scripts/vendor/act-testcases.json +12297 -0
  18. package/scripts/vendor/rgaa/SOURCE.json +8 -0
  19. package/scripts/vendor/rgaa/criteres.json +4144 -0
  20. package/scripts/vendor/rgaa/glossaire.json +480 -0
  21. package/scripts/vendor/wcag-2.2-fr.json +82 -0
  22. package/scripts/vendor/wcag-2.2-sc-universe.json +950 -0
  23. package/scripts/vendor/wcag-2.2-sc.json +574 -0
  24. package/scripts/verify-rgaa-source.mjs +126 -0
  25. package/scripts/verify-skill-bundle.mjs +91 -0
  26. package/skills/review-a11y/SKILL.md +123 -0
  27. package/skills/review-a11y/scripts/ultra11y.mjs +52739 -0
  28. package/skills/ultra11y/SKILL.md +292 -0
  29. package/skills/ultra11y/references/act.md +163 -0
  30. package/skills/ultra11y/references/adjudication.md +599 -0
  31. package/skills/ultra11y/references/audit.md +100 -0
  32. package/skills/ultra11y/references/authoring.md +37 -0
  33. package/skills/ultra11y/references/automation.md +113 -0
  34. package/skills/ultra11y/references/correction.md +41 -0
  35. package/skills/ultra11y/references/criteria.md +129 -0
  36. package/skills/ultra11y/references/cross-file.md +82 -0
  37. package/skills/ultra11y/references/dynamic.md +152 -0
  38. package/skills/ultra11y/references/false-positives.md +81 -0
  39. package/skills/ultra11y/references/fix.md +46 -0
  40. package/skills/ultra11y/references/focus-and-logic.md +64 -0
  41. package/skills/ultra11y/references/forbidden-patterns.md +61 -0
  42. package/skills/ultra11y/references/forms-and-errors.md +147 -0
  43. package/skills/ultra11y/references/guidance.md +55 -0
  44. package/skills/ultra11y/references/judgment.md +90 -0
  45. package/skills/ultra11y/references/media-and-motion.md +116 -0
  46. package/skills/ultra11y/references/methodology.md +72 -0
  47. package/skills/ultra11y/references/naming.md +117 -0
  48. package/skills/ultra11y/references/orchestration.md +44 -0
  49. package/skills/ultra11y/references/packs.md +193 -0
  50. package/skills/ultra11y/references/prd.md +104 -0
  51. package/skills/ultra11y/references/rendered.md +110 -0
  52. package/skills/ultra11y/references/scale.md +49 -0
  53. package/skills/ultra11y/references/standards.md +97 -0
  54. package/skills/ultra11y/references/structure.md +138 -0
  55. package/skills/ultra11y/references/verify.md +88 -0
  56. package/skills/ultra11y/references/widgets.md +106 -0
  57. package/skills/ultra11y/scripts/ultra11y.mjs +52739 -0
  58. package/src/act.ts +441 -0
  59. package/src/adjudicate.ts +630 -0
  60. package/src/aria.ts +230 -0
  61. package/src/audit.ts +396 -0
  62. package/src/auditor.ts +399 -0
  63. package/src/autofill.ts +102 -0
  64. package/src/axe-map.ts +294 -0
  65. package/src/baseline.ts +75 -0
  66. package/src/capture.ts +182 -0
  67. package/src/check.ts +243 -0
  68. package/src/cli.ts +1927 -0
  69. package/src/color.ts +102 -0
  70. package/src/config.ts +182 -0
  71. package/src/crawl.ts +75 -0
  72. package/src/criteria.ts +183 -0
  73. package/src/data/adjudication.json +770 -0
  74. package/src/data/guidance/rgaa.json +2395 -0
  75. package/src/data/standards/rgaa.glossary.json +478 -0
  76. package/src/data/standards/rgaa.json +3454 -0
  77. package/src/data/wcag-universe.json +530 -0
  78. package/src/data/wcag.json +1178 -0
  79. package/src/discover.ts +203 -0
  80. package/src/fix/codemods.ts +76 -0
  81. package/src/fix/diff.ts +64 -0
  82. package/src/fix/edits.ts +126 -0
  83. package/src/fix.ts +215 -0
  84. package/src/gh.ts +214 -0
  85. package/src/glob.ts +206 -0
  86. package/src/graph/build.ts +114 -0
  87. package/src/graph/engine-resolve.ts +0 -0
  88. package/src/graph/graph.ts +126 -0
  89. package/src/graph/imports.ts +359 -0
  90. package/src/graph/resolve.ts +73 -0
  91. package/src/graph/tsconfig.ts +113 -0
  92. package/src/grounding.ts +119 -0
  93. package/src/guidance/index.ts +45 -0
  94. package/src/guidance/types.ts +36 -0
  95. package/src/init.ts +113 -0
  96. package/src/mcp/handlers.ts +388 -0
  97. package/src/mcp/http.ts +260 -0
  98. package/src/mcp/prompts.ts +183 -0
  99. package/src/mcp/protocol.ts +187 -0
  100. package/src/mcp/resources.ts +136 -0
  101. package/src/mcp/server.ts +228 -0
  102. package/src/mcp/stdio.ts +119 -0
  103. package/src/mcp/tools.ts +347 -0
  104. package/src/messages.ts +1011 -0
  105. package/src/name.ts +174 -0
  106. package/src/orchestrate-templates.ts +231 -0
  107. package/src/orchestrate.ts +186 -0
  108. package/src/output.ts +108 -0
  109. package/src/pack.ts +187 -0
  110. package/src/parse/html.ts +258 -0
  111. package/src/parse/jsx-ast.ts +67 -0
  112. package/src/parse/jsx-bridge.ts +247 -0
  113. package/src/parse/jsx.ts +19 -0
  114. package/src/parse/source.ts +75 -0
  115. package/src/prd.ts +406 -0
  116. package/src/project-lock.ts +42 -0
  117. package/src/render.ts +307 -0
  118. package/src/report.ts +399 -0
  119. package/src/rules/colors.ts +125 -0
  120. package/src/rules/cross-registry.ts +264 -0
  121. package/src/rules/cross-rule.ts +83 -0
  122. package/src/rules/forms.ts +590 -0
  123. package/src/rules/frames.ts +33 -0
  124. package/src/rules/headings.ts +227 -0
  125. package/src/rules/images.ts +200 -0
  126. package/src/rules/links.ts +193 -0
  127. package/src/rules/mandatory.ts +173 -0
  128. package/src/rules/multimedia.ts +77 -0
  129. package/src/rules/navigation.ts +190 -0
  130. package/src/rules/presentation.ts +75 -0
  131. package/src/rules/registry.ts +57 -0
  132. package/src/rules/rule.ts +122 -0
  133. package/src/rules/scripts-aria.ts +636 -0
  134. package/src/rules/tables.ts +264 -0
  135. package/src/rules/timing.ts +55 -0
  136. package/src/sample.ts +176 -0
  137. package/src/scan-local.ts +866 -0
  138. package/src/scan.ts +538 -0
  139. package/src/standards/derive.ts +195 -0
  140. package/src/standards/index.ts +31 -0
  141. package/src/standards/pack-rules.ts +111 -0
  142. package/src/standards/pack.ts +81 -0
  143. package/src/standards/registry.ts +100 -0
  144. package/src/standards/types.ts +202 -0
  145. package/src/standards/validate.ts +544 -0
  146. package/src/standards/vocabulary.ts +75 -0
  147. package/src/types.ts +355 -0
  148. package/src/util.ts +32 -0
  149. package/src/vendor/codeindex-engine.d.mts +1009 -0
  150. package/src/vendor/codeindex-engine.mjs +13701 -0
  151. package/src/vendor/engine.meta.json +9 -0
  152. package/src/verify.ts +275 -0
  153. package/src/wcag.ts +128 -0
@@ -0,0 +1,574 @@
1
+ {
2
+ "wcagVersion": "2.2",
3
+ "source": "https://www.w3.org/TR/WCAG22/",
4
+ "criteriaSource": "https://github.com/w3c/wcag",
5
+ "principles": [
6
+ {
7
+ "number": 1,
8
+ "title": "Perceivable"
9
+ },
10
+ {
11
+ "number": 2,
12
+ "title": "Operable"
13
+ },
14
+ {
15
+ "number": 3,
16
+ "title": "Understandable"
17
+ },
18
+ {
19
+ "number": 4,
20
+ "title": "Robust"
21
+ }
22
+ ],
23
+ "guidelines": [
24
+ {
25
+ "number": "1.1",
26
+ "title": "Text Alternatives"
27
+ },
28
+ {
29
+ "number": "1.2",
30
+ "title": "Time-based Media"
31
+ },
32
+ {
33
+ "number": "1.3",
34
+ "title": "Adaptable"
35
+ },
36
+ {
37
+ "number": "1.4",
38
+ "title": "Distinguishable"
39
+ },
40
+ {
41
+ "number": "2.1",
42
+ "title": "Keyboard Accessible"
43
+ },
44
+ {
45
+ "number": "2.2",
46
+ "title": "Enough Time"
47
+ },
48
+ {
49
+ "number": "2.3",
50
+ "title": "Seizures and Physical Reactions"
51
+ },
52
+ {
53
+ "number": "2.4",
54
+ "title": "Navigable"
55
+ },
56
+ {
57
+ "number": "2.5",
58
+ "title": "Input Modalities"
59
+ },
60
+ {
61
+ "number": "3.1",
62
+ "title": "Readable"
63
+ },
64
+ {
65
+ "number": "3.2",
66
+ "title": "Predictable"
67
+ },
68
+ {
69
+ "number": "3.3",
70
+ "title": "Input Assistance"
71
+ },
72
+ {
73
+ "number": "4.1",
74
+ "title": "Compatible"
75
+ }
76
+ ],
77
+ "criteria": [
78
+ {
79
+ "sc": "1.1.1",
80
+ "slug": "non-text-content",
81
+ "title": "Non-text Content",
82
+ "level": "A",
83
+ "addedIn": "2.0",
84
+ "principle": 1,
85
+ "guideline": "1.1"
86
+ },
87
+ {
88
+ "sc": "1.2.1",
89
+ "slug": "audio-only-and-video-only-prerecorded",
90
+ "title": "Audio-only and Video-only (Prerecorded)",
91
+ "level": "A",
92
+ "addedIn": "2.0",
93
+ "principle": 1,
94
+ "guideline": "1.2"
95
+ },
96
+ {
97
+ "sc": "1.2.2",
98
+ "slug": "captions-prerecorded",
99
+ "title": "Captions (Prerecorded)",
100
+ "level": "A",
101
+ "addedIn": "2.0",
102
+ "principle": 1,
103
+ "guideline": "1.2"
104
+ },
105
+ {
106
+ "sc": "1.2.3",
107
+ "slug": "audio-description-or-media-alternative-prerecorded",
108
+ "title": "Audio Description or Media Alternative (Prerecorded)",
109
+ "level": "A",
110
+ "addedIn": "2.0",
111
+ "principle": 1,
112
+ "guideline": "1.2"
113
+ },
114
+ {
115
+ "sc": "1.2.4",
116
+ "slug": "captions-live",
117
+ "title": "Captions (Live)",
118
+ "level": "AA",
119
+ "addedIn": "2.0",
120
+ "principle": 1,
121
+ "guideline": "1.2"
122
+ },
123
+ {
124
+ "sc": "1.2.5",
125
+ "slug": "audio-description-prerecorded",
126
+ "title": "Audio Description (Prerecorded)",
127
+ "level": "AA",
128
+ "addedIn": "2.0",
129
+ "principle": 1,
130
+ "guideline": "1.2"
131
+ },
132
+ {
133
+ "sc": "1.3.1",
134
+ "slug": "info-and-relationships",
135
+ "title": "Info and Relationships",
136
+ "level": "A",
137
+ "addedIn": "2.0",
138
+ "principle": 1,
139
+ "guideline": "1.3"
140
+ },
141
+ {
142
+ "sc": "1.3.2",
143
+ "slug": "meaningful-sequence",
144
+ "title": "Meaningful Sequence",
145
+ "level": "A",
146
+ "addedIn": "2.0",
147
+ "principle": 1,
148
+ "guideline": "1.3"
149
+ },
150
+ {
151
+ "sc": "1.3.3",
152
+ "slug": "sensory-characteristics",
153
+ "title": "Sensory Characteristics",
154
+ "level": "A",
155
+ "addedIn": "2.0",
156
+ "principle": 1,
157
+ "guideline": "1.3"
158
+ },
159
+ {
160
+ "sc": "1.3.4",
161
+ "slug": "orientation",
162
+ "title": "Orientation",
163
+ "level": "AA",
164
+ "addedIn": "2.1",
165
+ "principle": 1,
166
+ "guideline": "1.3"
167
+ },
168
+ {
169
+ "sc": "1.3.5",
170
+ "slug": "identify-input-purpose",
171
+ "title": "Identify Input Purpose",
172
+ "level": "AA",
173
+ "addedIn": "2.1",
174
+ "principle": 1,
175
+ "guideline": "1.3"
176
+ },
177
+ {
178
+ "sc": "1.4.1",
179
+ "slug": "use-of-color",
180
+ "title": "Use of Color",
181
+ "level": "A",
182
+ "addedIn": "2.0",
183
+ "principle": 1,
184
+ "guideline": "1.4"
185
+ },
186
+ {
187
+ "sc": "1.4.2",
188
+ "slug": "audio-control",
189
+ "title": "Audio Control",
190
+ "level": "A",
191
+ "addedIn": "2.0",
192
+ "principle": 1,
193
+ "guideline": "1.4"
194
+ },
195
+ {
196
+ "sc": "1.4.3",
197
+ "slug": "contrast-minimum",
198
+ "title": "Contrast (Minimum)",
199
+ "level": "AA",
200
+ "addedIn": "2.0",
201
+ "principle": 1,
202
+ "guideline": "1.4"
203
+ },
204
+ {
205
+ "sc": "1.4.4",
206
+ "slug": "resize-text",
207
+ "title": "Resize Text",
208
+ "level": "AA",
209
+ "addedIn": "2.0",
210
+ "principle": 1,
211
+ "guideline": "1.4"
212
+ },
213
+ {
214
+ "sc": "1.4.5",
215
+ "slug": "images-of-text",
216
+ "title": "Images of Text",
217
+ "level": "AA",
218
+ "addedIn": "2.0",
219
+ "principle": 1,
220
+ "guideline": "1.4"
221
+ },
222
+ {
223
+ "sc": "1.4.10",
224
+ "slug": "reflow",
225
+ "title": "Reflow",
226
+ "level": "AA",
227
+ "addedIn": "2.1",
228
+ "principle": 1,
229
+ "guideline": "1.4"
230
+ },
231
+ {
232
+ "sc": "1.4.11",
233
+ "slug": "non-text-contrast",
234
+ "title": "Non-text Contrast",
235
+ "level": "AA",
236
+ "addedIn": "2.1",
237
+ "principle": 1,
238
+ "guideline": "1.4"
239
+ },
240
+ {
241
+ "sc": "1.4.12",
242
+ "slug": "text-spacing",
243
+ "title": "Text Spacing",
244
+ "level": "AA",
245
+ "addedIn": "2.1",
246
+ "principle": 1,
247
+ "guideline": "1.4"
248
+ },
249
+ {
250
+ "sc": "1.4.13",
251
+ "slug": "content-on-hover-or-focus",
252
+ "title": "Content on Hover or Focus",
253
+ "level": "AA",
254
+ "addedIn": "2.1",
255
+ "principle": 1,
256
+ "guideline": "1.4"
257
+ },
258
+ {
259
+ "sc": "2.1.1",
260
+ "slug": "keyboard",
261
+ "title": "Keyboard",
262
+ "level": "A",
263
+ "addedIn": "2.0",
264
+ "principle": 2,
265
+ "guideline": "2.1"
266
+ },
267
+ {
268
+ "sc": "2.1.2",
269
+ "slug": "no-keyboard-trap",
270
+ "title": "No Keyboard Trap",
271
+ "level": "A",
272
+ "addedIn": "2.0",
273
+ "principle": 2,
274
+ "guideline": "2.1"
275
+ },
276
+ {
277
+ "sc": "2.1.4",
278
+ "slug": "character-key-shortcuts",
279
+ "title": "Character Key Shortcuts",
280
+ "level": "A",
281
+ "addedIn": "2.1",
282
+ "principle": 2,
283
+ "guideline": "2.1"
284
+ },
285
+ {
286
+ "sc": "2.2.1",
287
+ "slug": "timing-adjustable",
288
+ "title": "Timing Adjustable",
289
+ "level": "A",
290
+ "addedIn": "2.0",
291
+ "principle": 2,
292
+ "guideline": "2.2"
293
+ },
294
+ {
295
+ "sc": "2.2.2",
296
+ "slug": "pause-stop-hide",
297
+ "title": "Pause, Stop, Hide",
298
+ "level": "A",
299
+ "addedIn": "2.0",
300
+ "principle": 2,
301
+ "guideline": "2.2"
302
+ },
303
+ {
304
+ "sc": "2.3.1",
305
+ "slug": "three-flashes-or-below-threshold",
306
+ "title": "Three Flashes or Below Threshold",
307
+ "level": "A",
308
+ "addedIn": "2.0",
309
+ "principle": 2,
310
+ "guideline": "2.3"
311
+ },
312
+ {
313
+ "sc": "2.4.1",
314
+ "slug": "bypass-blocks",
315
+ "title": "Bypass Blocks",
316
+ "level": "A",
317
+ "addedIn": "2.0",
318
+ "principle": 2,
319
+ "guideline": "2.4"
320
+ },
321
+ {
322
+ "sc": "2.4.2",
323
+ "slug": "page-titled",
324
+ "title": "Page Titled",
325
+ "level": "A",
326
+ "addedIn": "2.0",
327
+ "principle": 2,
328
+ "guideline": "2.4"
329
+ },
330
+ {
331
+ "sc": "2.4.3",
332
+ "slug": "focus-order",
333
+ "title": "Focus Order",
334
+ "level": "A",
335
+ "addedIn": "2.0",
336
+ "principle": 2,
337
+ "guideline": "2.4"
338
+ },
339
+ {
340
+ "sc": "2.4.4",
341
+ "slug": "link-purpose-in-context",
342
+ "title": "Link Purpose (In Context)",
343
+ "level": "A",
344
+ "addedIn": "2.0",
345
+ "principle": 2,
346
+ "guideline": "2.4"
347
+ },
348
+ {
349
+ "sc": "2.4.5",
350
+ "slug": "multiple-ways",
351
+ "title": "Multiple Ways",
352
+ "level": "AA",
353
+ "addedIn": "2.0",
354
+ "principle": 2,
355
+ "guideline": "2.4"
356
+ },
357
+ {
358
+ "sc": "2.4.6",
359
+ "slug": "headings-and-labels",
360
+ "title": "Headings and Labels",
361
+ "level": "AA",
362
+ "addedIn": "2.0",
363
+ "principle": 2,
364
+ "guideline": "2.4"
365
+ },
366
+ {
367
+ "sc": "2.4.7",
368
+ "slug": "focus-visible",
369
+ "title": "Focus Visible",
370
+ "level": "AA",
371
+ "addedIn": "2.0",
372
+ "principle": 2,
373
+ "guideline": "2.4"
374
+ },
375
+ {
376
+ "sc": "2.4.11",
377
+ "slug": "focus-not-obscured-minimum",
378
+ "title": "Focus Not Obscured (Minimum)",
379
+ "level": "AA",
380
+ "addedIn": "2.2",
381
+ "principle": 2,
382
+ "guideline": "2.4"
383
+ },
384
+ {
385
+ "sc": "2.5.1",
386
+ "slug": "pointer-gestures",
387
+ "title": "Pointer Gestures",
388
+ "level": "A",
389
+ "addedIn": "2.1",
390
+ "principle": 2,
391
+ "guideline": "2.5"
392
+ },
393
+ {
394
+ "sc": "2.5.2",
395
+ "slug": "pointer-cancellation",
396
+ "title": "Pointer Cancellation",
397
+ "level": "A",
398
+ "addedIn": "2.1",
399
+ "principle": 2,
400
+ "guideline": "2.5"
401
+ },
402
+ {
403
+ "sc": "2.5.3",
404
+ "slug": "label-in-name",
405
+ "title": "Label in Name",
406
+ "level": "A",
407
+ "addedIn": "2.1",
408
+ "principle": 2,
409
+ "guideline": "2.5"
410
+ },
411
+ {
412
+ "sc": "2.5.4",
413
+ "slug": "motion-actuation",
414
+ "title": "Motion Actuation",
415
+ "level": "A",
416
+ "addedIn": "2.1",
417
+ "principle": 2,
418
+ "guideline": "2.5"
419
+ },
420
+ {
421
+ "sc": "2.5.7",
422
+ "slug": "dragging-movements",
423
+ "title": "Dragging Movements",
424
+ "level": "AA",
425
+ "addedIn": "2.2",
426
+ "principle": 2,
427
+ "guideline": "2.5"
428
+ },
429
+ {
430
+ "sc": "2.5.8",
431
+ "slug": "target-size-minimum",
432
+ "title": "Target Size (Minimum)",
433
+ "level": "AA",
434
+ "addedIn": "2.2",
435
+ "principle": 2,
436
+ "guideline": "2.5"
437
+ },
438
+ {
439
+ "sc": "3.1.1",
440
+ "slug": "language-of-page",
441
+ "title": "Language of Page",
442
+ "level": "A",
443
+ "addedIn": "2.0",
444
+ "principle": 3,
445
+ "guideline": "3.1"
446
+ },
447
+ {
448
+ "sc": "3.1.2",
449
+ "slug": "language-of-parts",
450
+ "title": "Language of Parts",
451
+ "level": "AA",
452
+ "addedIn": "2.0",
453
+ "principle": 3,
454
+ "guideline": "3.1"
455
+ },
456
+ {
457
+ "sc": "3.2.1",
458
+ "slug": "on-focus",
459
+ "title": "On Focus",
460
+ "level": "A",
461
+ "addedIn": "2.0",
462
+ "principle": 3,
463
+ "guideline": "3.2"
464
+ },
465
+ {
466
+ "sc": "3.2.2",
467
+ "slug": "on-input",
468
+ "title": "On Input",
469
+ "level": "A",
470
+ "addedIn": "2.0",
471
+ "principle": 3,
472
+ "guideline": "3.2"
473
+ },
474
+ {
475
+ "sc": "3.2.3",
476
+ "slug": "consistent-navigation",
477
+ "title": "Consistent Navigation",
478
+ "level": "AA",
479
+ "addedIn": "2.0",
480
+ "principle": 3,
481
+ "guideline": "3.2"
482
+ },
483
+ {
484
+ "sc": "3.2.4",
485
+ "slug": "consistent-identification",
486
+ "title": "Consistent Identification",
487
+ "level": "AA",
488
+ "addedIn": "2.0",
489
+ "principle": 3,
490
+ "guideline": "3.2"
491
+ },
492
+ {
493
+ "sc": "3.2.6",
494
+ "slug": "consistent-help",
495
+ "title": "Consistent Help",
496
+ "level": "A",
497
+ "addedIn": "2.2",
498
+ "principle": 3,
499
+ "guideline": "3.2"
500
+ },
501
+ {
502
+ "sc": "3.3.1",
503
+ "slug": "error-identification",
504
+ "title": "Error Identification",
505
+ "level": "A",
506
+ "addedIn": "2.0",
507
+ "principle": 3,
508
+ "guideline": "3.3"
509
+ },
510
+ {
511
+ "sc": "3.3.2",
512
+ "slug": "labels-or-instructions",
513
+ "title": "Labels or Instructions",
514
+ "level": "A",
515
+ "addedIn": "2.0",
516
+ "principle": 3,
517
+ "guideline": "3.3"
518
+ },
519
+ {
520
+ "sc": "3.3.3",
521
+ "slug": "error-suggestion",
522
+ "title": "Error Suggestion",
523
+ "level": "AA",
524
+ "addedIn": "2.0",
525
+ "principle": 3,
526
+ "guideline": "3.3"
527
+ },
528
+ {
529
+ "sc": "3.3.4",
530
+ "slug": "error-prevention-legal-financial-data",
531
+ "title": "Error Prevention (Legal, Financial, Data)",
532
+ "level": "AA",
533
+ "addedIn": "2.0",
534
+ "principle": 3,
535
+ "guideline": "3.3"
536
+ },
537
+ {
538
+ "sc": "3.3.7",
539
+ "slug": "redundant-entry",
540
+ "title": "Redundant Entry",
541
+ "level": "A",
542
+ "addedIn": "2.2",
543
+ "principle": 3,
544
+ "guideline": "3.3"
545
+ },
546
+ {
547
+ "sc": "3.3.8",
548
+ "slug": "accessible-authentication-minimum",
549
+ "title": "Accessible Authentication (Minimum)",
550
+ "level": "AA",
551
+ "addedIn": "2.2",
552
+ "principle": 3,
553
+ "guideline": "3.3"
554
+ },
555
+ {
556
+ "sc": "4.1.2",
557
+ "slug": "name-role-value",
558
+ "title": "Name, Role, Value",
559
+ "level": "A",
560
+ "addedIn": "2.0",
561
+ "principle": 4,
562
+ "guideline": "4.1"
563
+ },
564
+ {
565
+ "sc": "4.1.3",
566
+ "slug": "status-messages",
567
+ "title": "Status Messages",
568
+ "level": "AA",
569
+ "addedIn": "2.1",
570
+ "principle": 4,
571
+ "guideline": "4.1"
572
+ }
573
+ ]
574
+ }
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ // DEV-ONLY (not in `bin`, not bundled, never run by tests — needs network). Proves that
3
+ // the vendored RGAA source (scripts/vendor/rgaa/criteres.json + glossaire.json), which
4
+ // scripts/build-pack-rgaa.mjs derives the shipped pack from, is still faithful to the
5
+ // official DINUM source (github.com/DISIC/accessibilite.numerique.gouv.fr, RGAA 4.1.2,
6
+ // same raw path as build-pack-rgaa.mjs's BASE). Does a DEEP, per-criterion / per-glossary-
7
+ // entry diff (not just a whole-file hash) so drift reads as "critère 4.2: tests differs"
8
+ // rather than an opaque "files differ". Refreshes scripts/vendor/rgaa/SOURCE.json with a
9
+ // provenance record on every run (clean or drifted) so the last verification is traceable.
10
+ // node scripts/verify-rgaa-source.mjs
11
+ import { writeFileSync, readFileSync } from "node:fs";
12
+ import { createHash } from "node:crypto";
13
+ import { dirname, join } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
17
+ const VENDOR = join(root, "scripts", "vendor", "rgaa");
18
+ const OWNER = "DISIC";
19
+ const REPO = "accessibilite.numerique.gouv.fr";
20
+ const BRANCH = "main";
21
+ const UPSTREAM_DIR = "RGAA"; // same path as build-pack-rgaa.mjs's BASE
22
+ const RAW_BASE = `https://raw.githubusercontent.com/${OWNER}/${REPO}/${BRANCH}/${UPSTREAM_DIR}`;
23
+ const FILES = ["criteres.json", "glossaire.json"];
24
+
25
+ const sha256 = (s) => createHash("sha256").update(s).digest("hex");
26
+
27
+ async function fetchText(name) {
28
+ const url = `${RAW_BASE}/${name}`;
29
+ const r = await fetch(url);
30
+ if (!r.ok) throw new Error(`verify-rgaa-source: GET ${url} → HTTP ${r.status}`);
31
+ return r.text();
32
+ }
33
+
34
+ async function fetchUpstreamCommit() {
35
+ const url = `https://api.github.com/repos/${OWNER}/${REPO}/commits/${BRANCH}`;
36
+ const r = await fetch(url, { headers: { Accept: "application/vnd.github+json" } });
37
+ if (!r.ok) throw new Error(`verify-rgaa-source: GET ${url} → HTTP ${r.status}`);
38
+ const json = await r.json();
39
+ return json.sha;
40
+ }
41
+
42
+ function criteriaById(criteres) {
43
+ const byId = new Map();
44
+ for (const topic of criteres.topics) {
45
+ for (const { criterium } of topic.criteria) byId.set(`${topic.number}.${criterium.number}`, criterium);
46
+ }
47
+ return byId;
48
+ }
49
+
50
+ // Deep, per-criterion diff over the fields the pack derives from: title, tests,
51
+ // WCAG/technique references, particular cases, technical notes.
52
+ function diffCriteria(upstream, vendored) {
53
+ const drift = [];
54
+ const up = criteriaById(upstream);
55
+ const ven = criteriaById(vendored);
56
+ const FIELDS = ["title", "tests", "references", "technicalNote", "particularCases"];
57
+ for (const id of new Set([...up.keys(), ...ven.keys()])) {
58
+ const u = up.get(id);
59
+ const v = ven.get(id);
60
+ if (!u) {
61
+ drift.push(`critère ${id}: présent dans le vendored, absent en amont`);
62
+ continue;
63
+ }
64
+ if (!v) {
65
+ drift.push(`critère ${id}: présent en amont, absent du vendored`);
66
+ continue;
67
+ }
68
+ for (const field of FIELDS) {
69
+ const a = JSON.stringify(u[field] ?? null);
70
+ const b = JSON.stringify(v[field] ?? null);
71
+ if (a !== b) drift.push(`critère ${id}: ${field} diffère`);
72
+ }
73
+ }
74
+ return drift.sort();
75
+ }
76
+
77
+ // Per-entry diff over the glossary (keyed by term title, the glossary's natural key).
78
+ function diffGlossary(upstream, vendored) {
79
+ const drift = [];
80
+ const up = new Map(upstream.glossary.map((e) => [e.title, e.body]));
81
+ const ven = new Map(vendored.glossary.map((e) => [e.title, e.body]));
82
+ for (const title of new Set([...up.keys(), ...ven.keys()])) {
83
+ const a = up.get(title);
84
+ const b = ven.get(title);
85
+ if (a === undefined) drift.push(`glossaire « ${title} » : présent dans le vendored, absent en amont`);
86
+ else if (b === undefined) drift.push(`glossaire « ${title} » : présent en amont, absent du vendored`);
87
+ else if (a !== b) drift.push(`glossaire « ${title} » : corps différent`);
88
+ }
89
+ return drift.sort();
90
+ }
91
+
92
+ async function main() {
93
+ const upstreamText = {};
94
+ for (const name of FILES) upstreamText[name] = await fetchText(name);
95
+
96
+ const vendoredText = Object.fromEntries(FILES.map((name) => [name, readFileSync(join(VENDOR, name), "utf8")]));
97
+
98
+ const drift = [
99
+ ...diffCriteria(JSON.parse(upstreamText["criteres.json"]), JSON.parse(vendoredText["criteres.json"])),
100
+ ...diffGlossary(JSON.parse(upstreamText["glossaire.json"]), JSON.parse(vendoredText["glossaire.json"])),
101
+ ];
102
+
103
+ const upstreamCommit = await fetchUpstreamCommit();
104
+ const files = {};
105
+ for (const name of FILES) files[name] = sha256(vendoredText[name]);
106
+ writeFileSync(
107
+ join(VENDOR, "SOURCE.json"),
108
+ `${JSON.stringify({ fetchedAt: new Date().toISOString(), upstreamCommit, files }, null, 2)}\n`,
109
+ );
110
+
111
+ if (drift.length === 0) {
112
+ console.log(
113
+ `verify-rgaa-source: scripts/vendor/rgaa/*.json is identical to ${OWNER}/${REPO}@${upstreamCommit} (${UPSTREAM_DIR}/).`,
114
+ );
115
+ return;
116
+ }
117
+
118
+ console.log(`verify-rgaa-source: ${drift.length} drift(s) vs ${OWNER}/${REPO}@${upstreamCommit} (${UPSTREAM_DIR}/):`);
119
+ for (const d of drift) console.log(` - ${d}`);
120
+ process.exit(1);
121
+ }
122
+
123
+ main().catch((e) => {
124
+ console.error(e);
125
+ process.exit(1);
126
+ });