@verndale/accessibility-standards 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OPERATING.md +2 -0
- package/README.md +4 -0
- package/bin/accessibility-standards.mjs +1 -1
- package/dist/manifest.json +39 -21
- package/dist/profiles/ai-orchestration/coverage-manifest.json +109 -2
- package/dist/profiles/ai-orchestration/implementation/accessibility.md +334 -0
- package/dist/profiles/ai-orchestration/pattern-contracts.json +125 -39
- package/dist/profiles/ai-orchestration/patterns/accordion.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/alert.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/carousel.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/combobox.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/consequential-submission.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/dialog.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/disclosure.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/dynamic-status.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/field.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/image.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/listbox.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/media-player.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/menu-button.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/pagination.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/popover.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/progress.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/tabs.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/tooltip.md +9 -0
- package/dist/profiles/ai-orchestration/patterns/validation.md +9 -0
- package/dist/profiles/ai-orchestration/review/accessibility.md +8 -1
- package/dist/profiles/ai-orchestration/semantics/accessible-name.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/alert.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/consequential-action.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/data-table.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/expanded-state.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/focus.initial.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/focus.return.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/focus.visible.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/form-error.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/form-label.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/form-required.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/headings.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/keyboard.escape.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/keyboard.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/landmarks.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/live-status.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/media-alternatives.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/motion-control.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/name-role-value.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/native-dialog.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/native-elements.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/non-text-alternative.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/roles-states-properties.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/selected-state.md +1 -0
- package/dist/profiles/ai-orchestration/semantics/target-size.md +1 -0
- package/dist/profiles/ai-orchestration/semantics.json +48 -1
- package/dist/profiles/ai-orchestration/testing/accessibility.md +1 -1
- package/dist/profiles/ai-orchestration/ui-design-brain-bindings.json +856 -0
- package/dist/profiles/conductor/a11y-semantics.md +22 -1
- package/dist/profiles/conductor/accessibility-quality.md +314 -0
- package/dist/profiles/conductor/applicability-matrix.json +111 -28
- package/dist/profiles/conductor/coverage-manifest.json +109 -2
- package/dist/profiles/conductor/pattern-contracts.json +125 -39
- package/dist/profiles/conductor/patterns/accordion.json +13 -3
- package/dist/profiles/conductor/patterns/alert.json +13 -3
- package/dist/profiles/conductor/patterns/carousel.json +13 -3
- package/dist/profiles/conductor/patterns/combobox.json +13 -3
- package/dist/profiles/conductor/patterns/consequential-submission.json +13 -3
- package/dist/profiles/conductor/patterns/dialog.json +13 -3
- package/dist/profiles/conductor/patterns/disclosure.json +13 -3
- package/dist/profiles/conductor/patterns/dynamic-status.json +13 -3
- package/dist/profiles/conductor/patterns/field.json +23 -3
- package/dist/profiles/conductor/patterns/image.json +23 -3
- package/dist/profiles/conductor/patterns/listbox.json +13 -3
- package/dist/profiles/conductor/patterns/media-player.json +23 -3
- package/dist/profiles/conductor/patterns/menu-button.json +13 -3
- package/dist/profiles/conductor/patterns/pagination.json +13 -3
- package/dist/profiles/conductor/patterns/popover.json +13 -3
- package/dist/profiles/conductor/patterns/progress.json +13 -3
- package/dist/profiles/conductor/patterns/tabs.json +13 -3
- package/dist/profiles/conductor/patterns/tooltip.json +13 -3
- package/dist/profiles/conductor/patterns/validation.json +23 -3
- package/dist/profiles/conductor/semantics.json +48 -1
- package/dist/profiles/conductor/ui-design-brain-bindings.json +856 -0
- package/dist/projection-manifest.json +103 -78
- package/dist/requirements-to-implementation.md +2 -0
- package/lib/build-manifest.mjs +12 -6
- package/lib/build.mjs +19 -5
- package/lib/compile-applicability.mjs +131 -10
- package/lib/coverage.mjs +20 -0
- package/lib/index.mjs +1 -1
- package/lib/load.mjs +31 -1
- package/lib/provenance.mjs +49 -0
- package/lib/render.mjs +69 -29
- package/lib/sync.mjs +240 -26
- package/lib/validate.mjs +255 -24
- package/package.json +24 -11
- package/profiles/ai-orchestration/profile.json +1 -1
- package/profiles/conductor/profile.json +1 -1
- package/schemas/consumer-config.schema.json +1 -1
- package/schemas/contract.schema.json +1 -0
- package/schemas/manifest.schema.json +1 -1
- package/schemas/projection-manifest.schema.json +84 -1
- package/schemas/ui-design-brain-bindings.schema.json +86 -0
- package/src/applicability/facts.yml +29 -1
- package/src/applicability/matrix.yml +3 -2
- package/src/applicability/ui-design-brain-bindings.yml +210 -0
- package/src/contract.yml +1 -0
- package/src/patterns/disclosure/accordion.yml +1 -1
- package/src/patterns/disclosure/disclosure.yml +1 -1
- package/src/patterns/feedback/alert.yml +1 -1
- package/src/patterns/feedback/dynamic-status.yml +1 -1
- package/src/patterns/feedback/progress.yml +1 -1
- package/src/patterns/forms/consequential-submission.yml +1 -1
- package/src/patterns/forms/field.yml +1 -1
- package/src/patterns/forms/validation.yml +1 -1
- package/src/patterns/media/carousel.yml +1 -1
- package/src/patterns/media/image.yml +1 -1
- package/src/patterns/media/media-player.yml +1 -1
- package/src/patterns/navigation/menu-button.yml +1 -1
- package/src/patterns/navigation/pagination.yml +1 -1
- package/src/patterns/navigation/tabs.yml +1 -1
- package/src/patterns/overlays/dialog.yml +1 -1
- package/src/patterns/overlays/popover.yml +1 -1
- package/src/patterns/overlays/tooltip.yml +1 -1
- package/src/patterns/selection/combobox.yml +1 -1
- package/src/patterns/selection/listbox.yml +1 -1
package/lib/validate.mjs
CHANGED
|
@@ -1,31 +1,262 @@
|
|
|
1
|
-
import { compileApplicability } from './compile-applicability.mjs';
|
|
1
|
+
import { compileApplicability, referencedFacts, validateExpression } from './compile-applicability.mjs';
|
|
2
|
+
|
|
3
|
+
const SEMVER = /^\d+\.\d+\.\d+$/;
|
|
4
|
+
const RECORD_ID = /^[a-z][a-z0-9.-]+$/;
|
|
5
|
+
const FACT_ID = /^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)+$/;
|
|
6
|
+
const RECORD_PREFIX = {
|
|
7
|
+
foundation: 'foundation.',
|
|
8
|
+
semantic: 'semantics.',
|
|
9
|
+
pattern: 'pattern.',
|
|
10
|
+
policy: 'policy.'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function isRecord(value) {
|
|
14
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isNonEmptyString(value) {
|
|
18
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function validateStringArray(label, value, errors, { required = true } = {}) {
|
|
22
|
+
if (!Array.isArray(value) || (required && value.length === 0) || value.some((item) => !isNonEmptyString(item))) {
|
|
23
|
+
errors.push(`${label} must be ${required ? 'a non-empty' : 'an'} array of non-empty strings`);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (new Set(value).size !== value.length) errors.push(`${label} must not contain duplicates`);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function validateAuthorityRecord(record, expectedKind, errors) {
|
|
31
|
+
const label = isRecord(record) && isNonEmptyString(record.id) ? record.id : `<malformed ${expectedKind}>`;
|
|
32
|
+
if (!isRecord(record)) {
|
|
33
|
+
errors.push(`Malformed ${expectedKind} record`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!isNonEmptyString(record.id) || !RECORD_ID.test(record.id) || !record.id.startsWith(RECORD_PREFIX[expectedKind])) errors.push(`${label} has invalid ${expectedKind} ID`);
|
|
37
|
+
if (!SEMVER.test(record.version ?? '')) errors.push(`${label} has invalid record version`);
|
|
38
|
+
if (!isNonEmptyString(record.title)) errors.push(`${label} has no title`);
|
|
39
|
+
if (record.kind !== expectedKind) errors.push(`${label} must declare kind ${expectedKind}`);
|
|
40
|
+
if (expectedKind !== 'pattern' && !isNonEmptyString(record.requirement)) errors.push(`${label} has no requirement`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function validateFacts(catalog, errors) {
|
|
44
|
+
if (!isRecord(catalog) || catalog.version !== 1 || !isRecord(catalog.facts) || Object.keys(catalog.facts).length === 0) {
|
|
45
|
+
errors.push('Malformed applicability fact catalog');
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
for (const [id, definition] of Object.entries(catalog.facts)) {
|
|
49
|
+
if (!FACT_ID.test(id)) errors.push(`Invalid applicability fact ID: ${id}`);
|
|
50
|
+
if (!isRecord(definition) || !['boolean', 'string', 'array'].includes(definition.type)) {
|
|
51
|
+
errors.push(`Fact ${id} has unsupported type ${definition?.type ?? '<missing>'}`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const extraKeys = Object.keys(definition).filter((key) => !['type', 'items', 'source', 'derived'].includes(key));
|
|
55
|
+
if (extraKeys.length) errors.push(`Fact ${id} has unsupported fields: ${extraKeys.join(', ')}`);
|
|
56
|
+
if (definition.type === 'array' && definition.items !== 'string') errors.push(`Fact ${id} must declare string array items`);
|
|
57
|
+
if (definition.type !== 'array' && Object.hasOwn(definition, 'items')) errors.push(`Fact ${id} cannot declare array items`);
|
|
58
|
+
if (Object.hasOwn(definition, 'source') && !isNonEmptyString(definition.source)) errors.push(`Fact ${id} has invalid source`);
|
|
59
|
+
if (Object.hasOwn(definition, 'derived') && definition.derived !== true) errors.push(`Fact ${id} has invalid derived marker`);
|
|
60
|
+
if (Object.hasOwn(definition, 'source') && Object.hasOwn(definition, 'derived')) errors.push(`Fact ${id} cannot be both sourced and derived`);
|
|
61
|
+
}
|
|
62
|
+
if (catalog.facts['component.ui_pattern_ids']?.source !== 'ui-design-brain') errors.push('component.ui_pattern_ids must be sourced from ui-design-brain');
|
|
63
|
+
if (catalog.facts['component.accessibility_pattern_ids']?.derived !== true) errors.push('component.accessibility_pattern_ids must be derived');
|
|
64
|
+
return catalog.facts;
|
|
65
|
+
}
|
|
2
66
|
|
|
3
67
|
export function validateStandards(data) {
|
|
4
68
|
const errors = [];
|
|
5
|
-
|
|
69
|
+
if (data.contract?.schema_version !== 2 || data.contract?.package_version !== '2.0.0') errors.push('Expected accessibility contract schema 2 targeting package 2.0.0');
|
|
70
|
+
if (data.package?.version !== data.contract?.package_version) errors.push(`Installed package version ${data.package?.version ?? '<missing>'} does not match contract target ${data.contract?.package_version ?? '<missing>'}`);
|
|
71
|
+
validateSources(data.sources, errors);
|
|
72
|
+
validateProfiles(data.profiles, data.contract?.package_version, errors);
|
|
73
|
+
validateProfileTemplates(data.profileTemplates, errors);
|
|
74
|
+
const foundations = Array.isArray(data.foundations) ? data.foundations : [];
|
|
75
|
+
const semantics = Array.isArray(data.semantics) ? data.semantics : [];
|
|
76
|
+
const patterns = Array.isArray(data.patterns) ? data.patterns : [];
|
|
77
|
+
const policies = Array.isArray(data.policies) ? data.policies : [];
|
|
78
|
+
if (!Array.isArray(data.foundations) || !foundations.length) errors.push('Foundations must be a non-empty array');
|
|
79
|
+
if (!Array.isArray(data.semantics) || !semantics.length) errors.push('Semantics must be a non-empty array');
|
|
80
|
+
if (!Array.isArray(data.patterns) || !patterns.length) errors.push('Patterns must be a non-empty array');
|
|
81
|
+
if (!Array.isArray(data.policies) || !policies.length) errors.push('Policies must be a non-empty array');
|
|
82
|
+
const records = [...foundations, ...semantics, ...patterns, ...policies];
|
|
6
83
|
const ids = new Set();
|
|
7
|
-
for (const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
const
|
|
19
|
-
for (const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
84
|
+
for (const [kind, group] of [['foundation', foundations], ['semantic', semantics], ['pattern', patterns], ['policy', policies]]) {
|
|
85
|
+
for (const record of group) {
|
|
86
|
+
validateAuthorityRecord(record, kind, errors);
|
|
87
|
+
if (!isRecord(record) || !isNonEmptyString(record.id)) continue;
|
|
88
|
+
if (ids.has(record.id)) errors.push(`Duplicate authority: ${record.id}`);
|
|
89
|
+
ids.add(record.id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const facts = validateFacts(data.facts, errors);
|
|
93
|
+
const semanticIds = new Set(semantics.map(({ id }) => id));
|
|
94
|
+
const patternIds = new Set(patterns.map(({ id }) => id));
|
|
95
|
+
for (const semantic of semantics) validateStringArray(`${semantic.id ?? '<malformed semantic>'} proof`, semantic.proof, errors);
|
|
96
|
+
for (const pattern of patterns) {
|
|
97
|
+
const id = pattern.id ?? '<malformed pattern>';
|
|
98
|
+
if (!validateStringArray(`${id} semantic dependencies`, pattern.requires, errors)) errors.push(`Orphan pattern: ${id}`);
|
|
99
|
+
for (const dependency of Array.isArray(pattern.requires) ? pattern.requires : []) if (!semanticIds.has(dependency)) errors.push(`${id} has missing semantic dependency ${dependency}`);
|
|
100
|
+
if (!isNonEmptyString(pattern.scope)) errors.push(`${id} has no scope`);
|
|
101
|
+
validateStringArray(`${id} behavior`, pattern.behavior, errors);
|
|
102
|
+
validateStringArray(`${id} product decisions`, pattern.product_decisions, errors);
|
|
103
|
+
validateStringArray(`${id} Functional Spec bindings`, pattern.functional_spec_bindings, errors);
|
|
104
|
+
validateStringArray(`${id} implementation outcomes`, pattern.implementation_outcomes, errors);
|
|
105
|
+
validateStringArray(`${id} evidence routes`, pattern.evidence_routes, errors);
|
|
106
|
+
try { validateExpression(pattern.activation, facts); } catch (error) { errors.push(`${id} activation: ${error.message}`); }
|
|
107
|
+
}
|
|
108
|
+
const proofKindObject = isRecord(data.evidence?.proof_kinds) ? data.evidence.proof_kinds : {};
|
|
109
|
+
if (data.evidence?.version !== 1 || !Object.keys(proofKindObject).length || !isNonEmptyString(data.evidence?.rule)) errors.push('Malformed evidence routing contract');
|
|
110
|
+
const expectedProofKinds = ['axe', 'e2e', 'human', 'unit'];
|
|
111
|
+
if (JSON.stringify(Object.keys(proofKindObject).sort()) !== JSON.stringify(expectedProofKinds)) errors.push('Evidence routes must be exactly unit, axe, e2e, and human');
|
|
112
|
+
const proofKinds = new Set(Object.keys(proofKindObject));
|
|
113
|
+
for (const [kind, route] of Object.entries(proofKindObject)) {
|
|
114
|
+
if (!isRecord(route) || !isNonEmptyString(route.purpose) || route.required_capability !== kind) errors.push(`Malformed evidence route ${kind}`);
|
|
115
|
+
}
|
|
116
|
+
for (const semantic of semantics) for (const route of Array.isArray(semantic.proof) ? semantic.proof : []) if (!proofKinds.has(route)) errors.push(`${semantic.id} uses unsupported proof route ${route}`);
|
|
117
|
+
for (const pattern of patterns) for (const route of Array.isArray(pattern.evidence_routes) ? pattern.evidence_routes : []) if (!proofKinds.has(route)) errors.push(`${pattern.id} uses unsupported proof route ${route}`);
|
|
118
|
+
const rows = Array.isArray(data.matrix?.rows) ? data.matrix.rows : [];
|
|
119
|
+
if (data.matrix?.version !== 1 || !rows.length) errors.push('Malformed applicability matrix');
|
|
120
|
+
const rowIds = new Set();
|
|
121
|
+
for (const row of rows) {
|
|
122
|
+
const id = row?.id ?? '<malformed applicability row>';
|
|
123
|
+
if (!isRecord(row) || !/^applicability\.[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(id)) errors.push(`Invalid applicability row ID: ${id}`);
|
|
124
|
+
if (rowIds.has(id)) errors.push(`Duplicate applicability row: ${id}`);
|
|
125
|
+
rowIds.add(id);
|
|
126
|
+
validateStringArray(`${id} outcomes`, row?.outcomes, errors);
|
|
127
|
+
validateStringArray(`${id} evidence`, row?.evidence, errors);
|
|
128
|
+
try { compileApplicability({ rows: [row] }, data.facts); } catch (error) { errors.push(`${id}: ${error.message}`); }
|
|
129
|
+
for (const outcome of Array.isArray(row?.outcomes) ? row.outcomes : []) if (!ids.has(outcome)) errors.push(`${id} references unknown outcome ${outcome}`);
|
|
130
|
+
for (const route of Array.isArray(row?.evidence) ? row.evidence : []) if (!proofKinds.has(route)) errors.push(`${id} uses unsupported proof route ${route}`);
|
|
131
|
+
}
|
|
132
|
+
const lanes = new Set([...semantics.flatMap((item) => Array.isArray(item.proof) ? item.proof : []), ...patterns.flatMap((item) => Array.isArray(item.evidence_routes) ? item.evidence_routes : []), ...rows.flatMap((item) => Array.isArray(item.evidence) ? item.evidence : [])]);
|
|
27
133
|
for (const lane of proofKinds) if (!lanes.has(lane)) errors.push(`Dead evidence lane: ${lane}`);
|
|
28
|
-
|
|
134
|
+
validateUiDesignBrainBindings(data.uiDesignBrainBindings, { facts }, semanticIds, patternIds, errors);
|
|
135
|
+
if (rows.length !== 19) errors.push(`Expected 19 applicability rows, found ${rows.length}`);
|
|
29
136
|
if (errors.length) throw new Error(errors.join('\n'));
|
|
30
|
-
return { records: records.length, semantics:
|
|
137
|
+
return { records: records.length, semantics: semantics.length, patterns: patterns.length, applicability: rows.length, uiPatternBindings: data.uiDesignBrainBindings.bindings.length };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function validateProfileTemplates(profileTemplates, errors) {
|
|
141
|
+
const expected = {
|
|
142
|
+
conductor: ['applicability/README.md', 'evidence/README.md', 'patterns/README.md', 'semantics/README.md'],
|
|
143
|
+
'ai-orchestration': ['implementation/README.md', 'patterns/README.md', 'review/README.md', 'semantics/README.md', 'testing/README.md']
|
|
144
|
+
};
|
|
145
|
+
if (!isRecord(profileTemplates) || JSON.stringify(Object.keys(profileTemplates).sort()) !== JSON.stringify(Object.keys(expected).sort())) {
|
|
146
|
+
errors.push('Profile templates must be exactly conductor and ai-orchestration');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
for (const [profile, paths] of Object.entries(expected)) {
|
|
150
|
+
const templates = profileTemplates[profile];
|
|
151
|
+
if (!isRecord(templates) || JSON.stringify(Object.keys(templates).sort()) !== JSON.stringify(paths)) {
|
|
152
|
+
errors.push(`Invalid profile template set: ${profile}`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
for (const [path, content] of Object.entries(templates)) {
|
|
156
|
+
if (!isNonEmptyString(content)) errors.push(`Empty profile template: ${profile}/${path}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function validateSources(sources, errors) {
|
|
162
|
+
if (!isRecord(sources) || sources.version !== 1 || !Array.isArray(sources.authorities) || !sources.authorities.length) {
|
|
163
|
+
errors.push('Malformed standards source hierarchy');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const authorityIds = new Set();
|
|
167
|
+
for (const authority of sources.authorities) {
|
|
168
|
+
if (!isRecord(authority) || !isNonEmptyString(authority.id) || !/^https:\/\//.test(authority.url ?? '') || typeof authority.normative !== 'boolean') {
|
|
169
|
+
errors.push(`Malformed standards authority ${authority?.id ?? '<missing>'}`);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (authorityIds.has(authority.id)) errors.push(`Duplicate standards authority ${authority.id}`);
|
|
173
|
+
authorityIds.add(authority.id);
|
|
174
|
+
}
|
|
175
|
+
if (!validateStringArray('Standards precedence', sources.precedence, errors)) return;
|
|
176
|
+
for (const id of authorityIds) if (!sources.precedence.includes(id)) errors.push(`Standards precedence omits authority ${id}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function validateProfiles(profiles, packageVersion, errors) {
|
|
180
|
+
const expected = {
|
|
181
|
+
conductor: ['ui-pattern-bindings', 'semantics', 'patterns', 'applicability', 'evidence'],
|
|
182
|
+
'ai-orchestration': ['ui-pattern-bindings', 'semantics', 'patterns', 'implementation', 'testing', 'review']
|
|
183
|
+
};
|
|
184
|
+
if (!isRecord(profiles) || JSON.stringify(Object.keys(profiles).sort()) !== JSON.stringify(Object.keys(expected).sort())) {
|
|
185
|
+
errors.push('Profile contracts must be exactly conductor and ai-orchestration');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const [name, channels] of Object.entries(expected)) {
|
|
189
|
+
const profile = profiles[name];
|
|
190
|
+
if (!isRecord(profile) || profile.name !== name || profile.version !== packageVersion || JSON.stringify(profile.channels) !== JSON.stringify(channels)) {
|
|
191
|
+
errors.push(`Invalid or incorrectly ordered profile contract: ${name}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function validateUiDesignBrainBindings(contract, facts, semanticIds, patternIds, errors) {
|
|
197
|
+
if (contract?.version !== 1 || !contract.source || !Array.isArray(contract.bindings)) {
|
|
198
|
+
errors.push('Malformed UI Design Brain binding contract');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (contract.source.package !== '@verndale/ui-design-brain') errors.push('UI Design Brain binding source package is invalid');
|
|
202
|
+
if (contract.source.repository !== 'https://github.com/verndale/ui-design-brain') errors.push('UI Design Brain binding repository is invalid');
|
|
203
|
+
if (contract.source.tracking_issue !== 'https://github.com/verndale/ui-design-brain/issues/47') errors.push('UI Design Brain binding tracking issue is invalid');
|
|
204
|
+
if (contract.source.package_version !== '1.16.0') errors.push('UI Design Brain projector package version is invalid');
|
|
205
|
+
if (contract.source.catalog_authority_version !== '1.15.1') errors.push('UI Design Brain catalog authority version is invalid');
|
|
206
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(contract.source.source_digest ?? '')) errors.push('UI Design Brain source digest is invalid');
|
|
207
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(contract.source.manifest_digest ?? '')) errors.push('UI Design Brain manifest digest is invalid');
|
|
208
|
+
if (contract.source.pattern_count !== contract.bindings.length) errors.push(`UI Design Brain binding count ${contract.bindings.length} does not match source count ${contract.source.pattern_count}`);
|
|
209
|
+
|
|
210
|
+
const expectedOrder = contract.bindings.map(({ ui_pattern_id }) => ui_pattern_id).sort();
|
|
211
|
+
const actualOrder = contract.bindings.map(({ ui_pattern_id }) => ui_pattern_id);
|
|
212
|
+
if (JSON.stringify(actualOrder) !== JSON.stringify(expectedOrder)) errors.push('UI Design Brain bindings must be sorted by canonical ui_pattern_id');
|
|
213
|
+
const seen = new Set();
|
|
214
|
+
for (const binding of contract.bindings) {
|
|
215
|
+
const prefix = `UI Design Brain binding ${binding.ui_pattern_id ?? '<missing>'}`;
|
|
216
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.ui_pattern_id ?? '')) errors.push(`${prefix} has invalid canonical ID`);
|
|
217
|
+
if (seen.has(binding.ui_pattern_id)) errors.push(`${prefix} is duplicated`);
|
|
218
|
+
seen.add(binding.ui_pattern_id);
|
|
219
|
+
if (!['direct', 'candidate', 'baseline-only'].includes(binding.classification)) errors.push(`${prefix} has invalid classification ${binding.classification}`);
|
|
220
|
+
validateSortedUnique(`${prefix} pattern_ids`, binding.pattern_ids ?? [], errors);
|
|
221
|
+
for (const patternId of binding.pattern_ids ?? []) if (!patternIds.has(patternId)) errors.push(`${prefix} references unknown pattern ${patternId}`);
|
|
222
|
+
validateSortedUnique(`${prefix} baseline_semantic_ids`, binding.baseline_semantic_ids ?? [], errors);
|
|
223
|
+
for (const semanticId of binding.baseline_semantic_ids ?? []) if (!semanticIds.has(semanticId)) errors.push(`${prefix} references unknown baseline semantic ${semanticId}`);
|
|
224
|
+
|
|
225
|
+
if (binding.classification === 'direct') {
|
|
226
|
+
if (!binding.pattern_ids?.length) errors.push(`${prefix} must declare at least one direct pattern`);
|
|
227
|
+
if (binding.candidates || binding.discriminator_facts) errors.push(`${prefix} direct mapping cannot declare candidates`);
|
|
228
|
+
} else if (binding.classification === 'baseline-only') {
|
|
229
|
+
if (binding.pattern_ids || binding.candidates || binding.discriminator_facts) errors.push(`${prefix} baseline-only mapping cannot declare specialized patterns`);
|
|
230
|
+
if (!binding.baseline_semantic_ids?.length) errors.push(`${prefix} baseline-only mapping must declare at least one baseline semantic`);
|
|
231
|
+
} else if (binding.classification === 'candidate') {
|
|
232
|
+
if (!binding.candidates?.length || !binding.discriminator_facts?.length) errors.push(`${prefix} candidate mapping requires candidates and discriminator facts`);
|
|
233
|
+
validateSortedUnique(`${prefix} discriminator_facts`, binding.discriminator_facts ?? [], errors);
|
|
234
|
+
for (const fact of binding.discriminator_facts ?? []) if (!facts.facts[fact]) errors.push(`${prefix} references unknown discriminator fact ${fact}`);
|
|
235
|
+
const candidatePatternIds = [];
|
|
236
|
+
const usedFacts = new Set();
|
|
237
|
+
for (const candidate of binding.candidates ?? []) {
|
|
238
|
+
candidatePatternIds.push(candidate.pattern_id);
|
|
239
|
+
if (!patternIds.has(candidate.pattern_id)) errors.push(`${prefix} references unknown candidate pattern ${candidate.pattern_id}`);
|
|
240
|
+
try {
|
|
241
|
+
validateExpression(candidate.when, facts.facts);
|
|
242
|
+
for (const fact of referencedFacts(candidate.when)) usedFacts.add(fact);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
errors.push(`${prefix} ${candidate.pattern_id}: ${error.message}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
validateSortedUnique(`${prefix} candidate pattern IDs`, candidatePatternIds, errors);
|
|
248
|
+
const declaredFacts = [...(binding.discriminator_facts ?? [])].sort();
|
|
249
|
+
const referenced = [...usedFacts].sort();
|
|
250
|
+
if (JSON.stringify(declaredFacts) !== JSON.stringify(referenced)) errors.push(`${prefix} discriminator facts do not exactly match candidate expressions`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function validateSortedUnique(label, values, errors) {
|
|
256
|
+
if (!Array.isArray(values) || values.some((value) => !isNonEmptyString(value))) {
|
|
257
|
+
errors.push(`${label} must be an array of non-empty strings`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const sorted = [...new Set(values)].sort();
|
|
261
|
+
if (JSON.stringify(values) !== JSON.stringify(sorted)) errors.push(`${label} must be sorted and unique`);
|
|
31
262
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verndale/accessibility-standards",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Deterministic accessibility semantics, patterns, applicability, and consumer projections",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/verndale/accessibility-standards.git"
|
|
11
|
+
},
|
|
12
|
+
"packageManager": "pnpm@11.1.1",
|
|
8
13
|
"engines": {
|
|
9
14
|
"node": ">=24.14.0"
|
|
10
15
|
},
|
|
@@ -21,26 +26,34 @@
|
|
|
21
26
|
"README.md",
|
|
22
27
|
"OPERATING.md"
|
|
23
28
|
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "node bin/accessibility-standards.mjs build",
|
|
31
|
+
"check": "node bin/accessibility-standards.mjs validate",
|
|
32
|
+
"test": "node --test tests/unit/*.test.mjs && pnpm release:preflight:selftest",
|
|
33
|
+
"contract:version:check": "node scripts/check-package-contract.cjs",
|
|
34
|
+
"verify:ci": "pnpm contract:version:check && pnpm check && pnpm test && pnpm build && git diff --exit-code -- . ':!dist'",
|
|
35
|
+
"release:preflight": "node scripts/check-release-commit.cjs",
|
|
36
|
+
"release:preflight:selftest": "node scripts/check-release-commit.selftest.cjs",
|
|
37
|
+
"prepack": "node scripts/check-package-contract.cjs",
|
|
38
|
+
"graph:build": "node scripts/wiki/build-graph.cjs",
|
|
39
|
+
"graph:check": "node scripts/wiki/build-graph.cjs --check",
|
|
40
|
+
"wiki:check": "node scripts/wiki/check.cjs",
|
|
41
|
+
"prepare": "node scripts/install-hooks.mjs"
|
|
42
|
+
},
|
|
24
43
|
"devDependencies": {
|
|
25
44
|
"@semantic-release/changelog": "6.0.3",
|
|
45
|
+
"@semantic-release/commit-analyzer": "13.0.1",
|
|
26
46
|
"@semantic-release/git": "10.0.1",
|
|
27
47
|
"@semantic-release/github": "12.0.8",
|
|
28
48
|
"@semantic-release/npm": "13.1.5",
|
|
29
49
|
"@verndale/ai-commit": "2.7.0",
|
|
30
50
|
"@verndale/ai-pr": "1.3.5",
|
|
51
|
+
"ajv": "8.20.0",
|
|
52
|
+
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
31
53
|
"semantic-release": "25.0.3"
|
|
32
54
|
},
|
|
33
55
|
"publishConfig": {
|
|
34
56
|
"access": "public",
|
|
35
57
|
"provenance": true
|
|
36
|
-
},
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "node bin/accessibility-standards.mjs build",
|
|
39
|
-
"check": "node bin/accessibility-standards.mjs validate",
|
|
40
|
-
"test": "node --test tests/unit/*.test.mjs",
|
|
41
|
-
"verify:ci": "pnpm check && pnpm test && pnpm build && git diff --exit-code -- . ':!dist'",
|
|
42
|
-
"graph:build": "node scripts/wiki/build-graph.cjs",
|
|
43
|
-
"graph:check": "node scripts/wiki/build-graph.cjs --check",
|
|
44
|
-
"wiki:check": "node scripts/wiki/check.cjs"
|
|
45
58
|
}
|
|
46
|
-
}
|
|
59
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"ai-orchestration","version":"
|
|
1
|
+
{"name":"ai-orchestration","version":"2.0.0","channels":["ui-pattern-bindings","semantics","patterns","implementation","testing","review"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"conductor","version":"
|
|
1
|
+
{"name":"conductor","version":"2.0.0","channels":["ui-pattern-bindings","semantics","patterns","applicability","evidence"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"consumer-config.schema.json","type":"object","required":["package","profile","routes"],"properties":{"package":{"const":"@verndale/accessibility-standards@
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"consumer-config.schema.json","type":"object","required":["package","profile","routes"],"properties":{"package":{"const":"@verndale/accessibility-standards@2.0.0"},"profile":{"enum":["conductor","ai-orchestration"]},"routes":{"type":"string"},"outputRoot":{"type":"string"}},"additionalProperties":false}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"contract.schema.json","type":"object","required":["schema_version","package_version"],"additionalProperties":false,"properties":{"schema_version":{"type":"integer","const":2},"package_version":{"type":"string","const":"2.0.0"}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"manifest.schema.json","type":"object","required":["package","schema_version","records"],"properties":{"package":{"type":"string"},"schema_version":{"type":"integer"},"records":{"type":"array","items":{"type":"object","required":["id","digest"]}}}}
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"manifest.schema.json","type":"object","required":["_generated","package","schema_version","source_digest","profile_digests","ui_design_brain","records"],"properties":{"_generated":{"type":"string"},"package":{"type":"string"},"schema_version":{"type":"integer"},"source_digest":{"type":"string","pattern":"^[a-f0-9]{64}$"},"profile_digests":{"type":"object"},"ui_design_brain":{"type":"object","required":["package","package_version","catalog_authority_version","source_digest","manifest_digest","binding_digest"]},"records":{"type":"array","items":{"type":"object","required":["id","digest"]}}}}
|
|
@@ -1 +1,84 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "projection-manifest.schema.json",
|
|
4
|
+
"$defs": {
|
|
5
|
+
"digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
6
|
+
"uiDesignBrain": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": ["package", "package_version", "catalog_authority_version", "source_digest", "manifest_digest", "binding_digest"]
|
|
9
|
+
},
|
|
10
|
+
"consumer": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"required": [
|
|
13
|
+
"_generated",
|
|
14
|
+
"package",
|
|
15
|
+
"profile",
|
|
16
|
+
"profile_version",
|
|
17
|
+
"schema_version",
|
|
18
|
+
"digests",
|
|
19
|
+
"ui_design_brain",
|
|
20
|
+
"ui_pattern_ids",
|
|
21
|
+
"projected_ids",
|
|
22
|
+
"lane_coverage",
|
|
23
|
+
"output_paths",
|
|
24
|
+
"output_digests",
|
|
25
|
+
"manifest_digest"
|
|
26
|
+
],
|
|
27
|
+
"properties": {
|
|
28
|
+
"_generated": { "type": "string" },
|
|
29
|
+
"package": { "type": "string" },
|
|
30
|
+
"profile": { "enum": ["conductor", "ai-orchestration"] },
|
|
31
|
+
"profile_version": { "type": "string" },
|
|
32
|
+
"schema_version": { "type": "integer" },
|
|
33
|
+
"digests": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": ["source", "profile", "ui_design_brain_bindings", "config", "routes"],
|
|
36
|
+
"additionalProperties": { "$ref": "#/$defs/digest" }
|
|
37
|
+
},
|
|
38
|
+
"ui_design_brain": { "$ref": "#/$defs/uiDesignBrain" },
|
|
39
|
+
"ui_pattern_ids": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
|
|
40
|
+
"projected_ids": { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
|
|
41
|
+
"lane_coverage": { "type": "object" },
|
|
42
|
+
"output_paths": { "type": "object", "additionalProperties": { "type": "string" } },
|
|
43
|
+
"output_digests": { "type": "object", "additionalProperties": { "$ref": "#/$defs/digest" } },
|
|
44
|
+
"manifest_digest": { "$ref": "#/$defs/digest" }
|
|
45
|
+
},
|
|
46
|
+
"additionalProperties": true
|
|
47
|
+
},
|
|
48
|
+
"distribution": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["_generated", "package", "schema_version", "source_digest", "profile_digests", "ui_design_brain", "profiles"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"_generated": { "type": "string" },
|
|
53
|
+
"package": { "type": "string" },
|
|
54
|
+
"schema_version": { "type": "integer" },
|
|
55
|
+
"source_digest": { "$ref": "#/$defs/digest" },
|
|
56
|
+
"profile_digests": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["conductor", "ai-orchestration"],
|
|
59
|
+
"additionalProperties": { "$ref": "#/$defs/digest" }
|
|
60
|
+
},
|
|
61
|
+
"ui_design_brain": { "$ref": "#/$defs/uiDesignBrain" },
|
|
62
|
+
"profiles": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"required": ["conductor", "ai-orchestration"],
|
|
65
|
+
"additionalProperties": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"required": ["version", "profile_digest", "source_digest", "outputs"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"version": { "type": "string" },
|
|
70
|
+
"profile_digest": { "$ref": "#/$defs/digest" },
|
|
71
|
+
"source_digest": { "$ref": "#/$defs/digest" },
|
|
72
|
+
"outputs": { "type": "object", "additionalProperties": { "$ref": "#/$defs/digest" } }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"additionalProperties": true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"oneOf": [
|
|
81
|
+
{ "$ref": "#/$defs/consumer" },
|
|
82
|
+
{ "$ref": "#/$defs/distribution" }
|
|
83
|
+
]
|
|
84
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "ui-design-brain-bindings.schema.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["version", "source", "bindings"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"version": { "type": "integer", "const": 1 },
|
|
9
|
+
"source": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["package", "package_version", "catalog_authority_version", "repository", "manifest_path", "source_digest", "manifest_digest", "pattern_count", "tracking_issue"],
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"package": { "const": "@verndale/ui-design-brain" },
|
|
15
|
+
"package_version": { "const": "1.16.0" },
|
|
16
|
+
"catalog_authority_version": { "const": "1.15.1" },
|
|
17
|
+
"repository": { "const": "https://github.com/verndale/ui-design-brain" },
|
|
18
|
+
"manifest_path": { "const": "skills/ui-design-brain/catalog-manifest.json" },
|
|
19
|
+
"source_digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
20
|
+
"manifest_digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
21
|
+
"pattern_count": { "type": "integer", "minimum": 1 },
|
|
22
|
+
"tracking_issue": { "const": "https://github.com/verndale/ui-design-brain/issues/47" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"bindings": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"minItems": 1,
|
|
28
|
+
"items": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["ui_pattern_id", "classification"],
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"properties": {
|
|
33
|
+
"ui_pattern_id": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
|
34
|
+
"classification": { "enum": ["direct", "candidate", "baseline-only"] },
|
|
35
|
+
"pattern_ids": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": { "type": "string", "pattern": "^pattern\\." },
|
|
38
|
+
"uniqueItems": true
|
|
39
|
+
},
|
|
40
|
+
"baseline_semantic_ids": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": { "type": "string", "pattern": "^semantics\\." },
|
|
43
|
+
"minItems": 1,
|
|
44
|
+
"uniqueItems": true
|
|
45
|
+
},
|
|
46
|
+
"discriminator_facts": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": { "type": "string" },
|
|
49
|
+
"minItems": 1,
|
|
50
|
+
"uniqueItems": true
|
|
51
|
+
},
|
|
52
|
+
"candidates": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"minItems": 1,
|
|
55
|
+
"items": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"required": ["pattern_id", "when"],
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"properties": {
|
|
60
|
+
"pattern_id": { "type": "string", "pattern": "^pattern\\." },
|
|
61
|
+
"when": { "type": "object", "minProperties": 1, "maxProperties": 1 }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"allOf": [
|
|
67
|
+
{
|
|
68
|
+
"if": { "properties": { "classification": { "const": "direct" } } },
|
|
69
|
+
"then": { "required": ["pattern_ids"], "not": { "anyOf": [{ "required": ["candidates"] }, { "required": ["discriminator_facts"] }] } }
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"if": { "properties": { "classification": { "const": "candidate" } } },
|
|
73
|
+
"then": { "required": ["discriminator_facts", "candidates"] }
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"if": { "properties": { "classification": { "const": "baseline-only" } } },
|
|
77
|
+
"then": {
|
|
78
|
+
"required": ["baseline_semantic_ids"],
|
|
79
|
+
"not": { "anyOf": [{ "required": ["pattern_ids"] }, { "required": ["candidates"] }, { "required": ["discriminator_facts"] }] }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1 +1,29 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"facts": {
|
|
4
|
+
"artifact.page_context": { "type": "boolean" },
|
|
5
|
+
"content.has_non_text": { "type": "boolean" },
|
|
6
|
+
"content.has_media": { "type": "boolean" },
|
|
7
|
+
"component.ui_pattern_ids": { "type": "array", "items": "string", "source": "ui-design-brain" },
|
|
8
|
+
"component.accessibility_pattern_ids": { "type": "array", "items": "string", "derived": true },
|
|
9
|
+
"component.command_menu_trigger": { "type": "boolean" },
|
|
10
|
+
"component.has_form_fields": { "type": "boolean" },
|
|
11
|
+
"component.has_validation": { "type": "boolean" },
|
|
12
|
+
"component.submission_consequence": { "type": "array", "items": "string" },
|
|
13
|
+
"component.has_dynamic_status": { "type": "boolean" },
|
|
14
|
+
"component.message_urgency": { "type": "string" },
|
|
15
|
+
"component.overlay_model": { "type": "string" },
|
|
16
|
+
"component.selection_model": { "type": "string" },
|
|
17
|
+
"interaction.has_motion_or_timing": { "type": "boolean" },
|
|
18
|
+
"interaction.has_complex_pointer": { "type": "boolean" },
|
|
19
|
+
"interaction.has_character_shortcuts": { "type": "boolean" },
|
|
20
|
+
"content.uses_orientation_or_sensory_cues": { "type": "boolean" },
|
|
21
|
+
"interaction.has_pointer_targets": { "type": "boolean" },
|
|
22
|
+
"interaction.can_obscure_focus": { "type": "boolean" },
|
|
23
|
+
"flow.has_authentication": { "type": "boolean" },
|
|
24
|
+
"flow.repeats_entry": { "type": "boolean" },
|
|
25
|
+
"artifact.has_help": { "type": "boolean" },
|
|
26
|
+
"content.has_locale_or_bidi": { "type": "boolean" },
|
|
27
|
+
"component.has_data_table_or_grid": { "type": "boolean" }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
{"id":"applicability.page-context","when":{"equals":{"fact":"artifact.page_context","value":true}},"outcomes":["semantics.headings","semantics.landmarks"],"evidence":["axe","human"]},
|
|
3
3
|
{"id":"applicability.non-text-content","when":{"equals":{"fact":"content.has_non_text","value":true}},"outcomes":["pattern.image"],"evidence":["axe","human"]},
|
|
4
4
|
{"id":"applicability.media","when":{"equals":{"fact":"content.has_media","value":true}},"outcomes":["pattern.media-player"],"evidence":["e2e","human"]},
|
|
5
|
-
{"id":"applicability.forms","when":{"equals":{"fact":"component.has_form_fields","value":true}},"outcomes":["pattern.field"
|
|
5
|
+
{"id":"applicability.forms","when":{"equals":{"fact":"component.has_form_fields","value":true}},"outcomes":["pattern.field"],"evidence":["unit","axe"]},
|
|
6
|
+
{"id":"applicability.validation","when":{"equals":{"fact":"component.has_validation","value":true}},"outcomes":["pattern.validation"],"evidence":["unit","axe","e2e","human"]},
|
|
6
7
|
{"id":"applicability.consequential-submission","when":{"contains":{"fact":"component.submission_consequence","value":"consequential"}},"outcomes":["pattern.consequential-submission"],"evidence":["e2e","human"]},
|
|
7
8
|
{"id":"applicability.dynamic-status","when":{"equals":{"fact":"component.has_dynamic_status","value":true}},"outcomes":["pattern.dynamic-status"],"evidence":["unit","e2e","human"]},
|
|
8
|
-
{"id":"applicability.overlays","when":{"contains":{"fact":"component.
|
|
9
|
+
{"id":"applicability.overlays","when":{"any":[{"contains":{"fact":"component.ui_pattern_ids","value":"modal"}},{"contains":{"fact":"component.ui_pattern_ids","value":"popover"}},{"contains":{"fact":"component.ui_pattern_ids","value":"tooltip"}}]},"outcomes":["semantics.keyboard.escape","semantics.focus.return"],"evidence":["e2e"]},
|
|
9
10
|
{"id":"applicability.motion-timing","when":{"equals":{"fact":"interaction.has_motion_or_timing","value":true}},"outcomes":["semantics.motion-control"],"evidence":["e2e","human"]},
|
|
10
11
|
{"id":"applicability.complex-pointer","when":{"equals":{"fact":"interaction.has_complex_pointer","value":true}},"outcomes":["semantics.keyboard"],"evidence":["e2e","human"]},
|
|
11
12
|
{"id":"applicability.character-shortcuts","when":{"equals":{"fact":"interaction.has_character_shortcuts","value":true}},"outcomes":["semantics.keyboard"],"evidence":["unit","e2e"]},
|