design-constraint-validator 2.0.1 → 2.1.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/README.md +76 -21
- package/cli/commands/build.js +1 -1
- package/cli/commands/build.ts +1 -1
- package/cli/commands/graph.js +2 -2
- package/cli/commands/graph.ts +2 -2
- package/cli/commands/validate.d.ts.map +1 -1
- package/cli/commands/validate.js +40 -6
- package/cli/commands/validate.ts +37 -8
- package/cli/config-schema.d.ts +39 -0
- package/cli/config-schema.d.ts.map +1 -1
- package/cli/config-schema.js +4 -2
- package/cli/config-schema.ts +4 -2
- package/cli/config.d.ts.map +1 -1
- package/cli/config.js +8 -2
- package/cli/config.ts +8 -2
- package/cli/constraint-registry.d.ts +16 -0
- package/cli/constraint-registry.d.ts.map +1 -1
- package/cli/constraint-registry.js +64 -31
- package/cli/constraint-registry.ts +67 -31
- package/cli/dcv.js +8 -24
- package/cli/dcv.ts +8 -20
- package/cli/json-output.d.ts +3 -1
- package/cli/json-output.d.ts.map +1 -1
- package/cli/json-output.js +11 -4
- package/cli/json-output.ts +13 -4
- package/cli/types.d.ts +2 -0
- package/cli/types.d.ts.map +1 -1
- package/cli/types.ts +2 -0
- package/cli/validate-api.d.ts +40 -0
- package/cli/validate-api.d.ts.map +1 -0
- package/cli/validate-api.js +85 -0
- package/cli/validate-api.ts +126 -0
- package/core/breakpoints.d.ts +8 -2
- package/core/breakpoints.d.ts.map +1 -1
- package/core/breakpoints.js +24 -3
- package/core/breakpoints.ts +22 -3
- package/core/color.js +4 -4
- package/core/color.ts +4 -4
- package/core/constraints/monotonic-lightness.d.ts.map +1 -1
- package/core/constraints/monotonic-lightness.js +9 -5
- package/core/constraints/monotonic-lightness.ts +9 -4
- package/core/constraints/wcag.d.ts.map +1 -1
- package/core/constraints/wcag.js +6 -0
- package/core/constraints/wcag.ts +6 -0
- package/core/dtcg.d.ts +38 -0
- package/core/dtcg.d.ts.map +1 -0
- package/core/dtcg.js +88 -0
- package/core/dtcg.ts +102 -0
- package/core/engine.d.ts +6 -0
- package/core/engine.d.ts.map +1 -1
- package/core/engine.ts +7 -0
- package/core/flatten.d.ts +5 -3
- package/core/flatten.d.ts.map +1 -1
- package/core/flatten.js +24 -10
- package/core/flatten.ts +39 -16
- package/core/image-export.d.ts.map +1 -1
- package/core/image-export.js +10 -7
- package/core/image-export.ts +9 -6
- package/core/index.d.ts +2 -0
- package/core/index.d.ts.map +1 -1
- package/core/index.js +4 -0
- package/core/index.ts +6 -0
- package/core/why.d.ts +1 -1
- package/core/why.d.ts.map +1 -1
- package/core/why.ts +1 -1
- package/mcp/contracts.d.ts +118 -0
- package/mcp/contracts.d.ts.map +1 -0
- package/mcp/contracts.js +30 -0
- package/mcp/contracts.ts +51 -0
- package/mcp/index.d.ts +9 -0
- package/mcp/index.d.ts.map +1 -0
- package/mcp/index.js +32 -0
- package/mcp/index.ts +70 -0
- package/mcp/tools.d.ts +52 -0
- package/mcp/tools.d.ts.map +1 -0
- package/mcp/tools.js +172 -0
- package/mcp/tools.ts +254 -0
- package/package.json +41 -26
- package/server.json +21 -0
- package/cli/constraints-loader.d.ts.map +0 -1
- package/cli/engine-helpers.d.ts.map +0 -1
- package/core/cross-axis-config.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,cAAc
|
|
1
|
+
{"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAMzB,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAEd,CAAC;AAEjB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAGZ,CAAC;AAEjB,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,KAAK,CAAC,EAAE,eAAe,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAO3F"}
|
package/cli/config-schema.js
CHANGED
|
@@ -3,7 +3,8 @@ export const WcagRuleSchema = z.object({
|
|
|
3
3
|
foreground: z.string(),
|
|
4
4
|
background: z.string(),
|
|
5
5
|
ratio: z.number().positive().optional(),
|
|
6
|
-
description: z.string().optional()
|
|
6
|
+
description: z.string().optional(),
|
|
7
|
+
backdrop: z.string().optional()
|
|
7
8
|
});
|
|
8
9
|
export const ConstraintsSchema = z.object({
|
|
9
10
|
wcag: z.array(WcagRuleSchema).optional()
|
|
@@ -15,7 +16,8 @@ export const DcvConfigSchema = z.object({
|
|
|
15
16
|
export function validateConfig(raw) {
|
|
16
17
|
const res = DcvConfigSchema.safeParse(raw);
|
|
17
18
|
if (!res.success) {
|
|
18
|
-
|
|
19
|
+
// zod v4 renamed ZodError.errors -> .issues (the .errors getter was removed).
|
|
20
|
+
return { errors: res.error.issues.map((e) => `${e.path.join('.') || '<root>'}: ${e.message}`) };
|
|
19
21
|
}
|
|
20
22
|
return { value: res.data };
|
|
21
23
|
}
|
package/cli/config-schema.ts
CHANGED
|
@@ -4,7 +4,8 @@ export const WcagRuleSchema = z.object({
|
|
|
4
4
|
foreground: z.string(),
|
|
5
5
|
background: z.string(),
|
|
6
6
|
ratio: z.number().positive().optional(),
|
|
7
|
-
description: z.string().optional()
|
|
7
|
+
description: z.string().optional(),
|
|
8
|
+
backdrop: z.string().optional()
|
|
8
9
|
});
|
|
9
10
|
|
|
10
11
|
export const ConstraintsSchema = z.object({
|
|
@@ -21,7 +22,8 @@ export type DcvConfigParsed = z.infer<typeof DcvConfigSchema>;
|
|
|
21
22
|
export function validateConfig(raw: unknown): { value?: DcvConfigParsed; errors?: string[] } {
|
|
22
23
|
const res = DcvConfigSchema.safeParse(raw);
|
|
23
24
|
if (!res.success) {
|
|
24
|
-
|
|
25
|
+
// zod v4 renamed ZodError.errors -> .issues (the .errors getter was removed).
|
|
26
|
+
return { errors: res.error.issues.map((e) => `${e.path.join('.') || '<root>'}: ${e.message}`) };
|
|
25
27
|
}
|
|
26
28
|
return { value: res.data };
|
|
27
29
|
}
|
package/cli/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAkCzE"}
|
package/cli/config.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { basename, extname } from 'node:path';
|
|
2
3
|
import { validateConfig } from './config-schema.js';
|
|
3
4
|
import { ok, err } from './result.js';
|
|
4
5
|
export function loadConfig(configPath) {
|
|
6
|
+
if (configPath && !existsSync(configPath)) {
|
|
7
|
+
return err(`Config file not found: ${configPath}`);
|
|
8
|
+
}
|
|
5
9
|
const candidates = configPath ? [configPath] : [
|
|
6
10
|
'dcv.config.json',
|
|
7
|
-
'dcv.config.js',
|
|
8
11
|
'.dcvrc.json',
|
|
9
12
|
'package.json'
|
|
10
13
|
];
|
|
11
14
|
for (const p of candidates) {
|
|
12
15
|
if (!existsSync(p))
|
|
13
16
|
continue;
|
|
17
|
+
if (extname(p) === '.js') {
|
|
18
|
+
return err(`Unsupported config file ${p}: use JSON config (dcv.config.json, .dcvrc.json, or package.json "dcv").`);
|
|
19
|
+
}
|
|
14
20
|
try {
|
|
15
21
|
const rawTxt = readFileSync(p, 'utf8');
|
|
16
22
|
let raw = JSON.parse(rawTxt);
|
|
17
|
-
if (p === 'package.json' && raw && typeof raw === 'object') {
|
|
23
|
+
if (basename(p) === 'package.json' && raw && typeof raw === 'object') {
|
|
18
24
|
const pkg = raw;
|
|
19
25
|
if ('dcv' in pkg) {
|
|
20
26
|
raw = pkg.dcv;
|
package/cli/config.ts
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { basename, extname } from 'node:path';
|
|
2
3
|
import { validateConfig } from './config-schema.js';
|
|
3
4
|
import { ok, err, type Result } from './result.js';
|
|
4
5
|
import type { DcvConfig } from './types.js';
|
|
5
6
|
|
|
6
7
|
export function loadConfig(configPath?: string): Result<DcvConfig, string> {
|
|
8
|
+
if (configPath && !existsSync(configPath)) {
|
|
9
|
+
return err(`Config file not found: ${configPath}`);
|
|
10
|
+
}
|
|
7
11
|
const candidates = configPath ? [configPath] : [
|
|
8
12
|
'dcv.config.json',
|
|
9
|
-
'dcv.config.js',
|
|
10
13
|
'.dcvrc.json',
|
|
11
14
|
'package.json'
|
|
12
15
|
];
|
|
13
16
|
for (const p of candidates) {
|
|
14
17
|
if (!existsSync(p)) continue;
|
|
18
|
+
if (extname(p) === '.js') {
|
|
19
|
+
return err(`Unsupported config file ${p}: use JSON config (dcv.config.json, .dcvrc.json, or package.json "dcv").`);
|
|
20
|
+
}
|
|
15
21
|
try {
|
|
16
22
|
const rawTxt = readFileSync(p, 'utf8');
|
|
17
23
|
let raw: unknown = JSON.parse(rawTxt);
|
|
18
|
-
if (p === 'package.json' && raw && typeof raw === 'object') {
|
|
24
|
+
if (basename(p) === 'package.json' && raw && typeof raw === 'object') {
|
|
19
25
|
const pkg = raw as Record<string, unknown>;
|
|
20
26
|
if ('dcv' in pkg) {
|
|
21
27
|
raw = pkg.dcv;
|
|
@@ -67,6 +67,8 @@ export type AttachOptions = {
|
|
|
67
67
|
knownIds: Set<string>;
|
|
68
68
|
crossAxisDebug?: boolean;
|
|
69
69
|
};
|
|
70
|
+
export declare const DEFAULT_WCAG_PAIRS: WcagRule[];
|
|
71
|
+
export declare const DEFAULT_THRESHOLDS: ThresholdRule[];
|
|
70
72
|
/**
|
|
71
73
|
* Discover all constraint sources for a given configuration and breakpoint.
|
|
72
74
|
*
|
|
@@ -98,4 +100,18 @@ export declare function attachConstraints(engine: Engine, sources: ConstraintSou
|
|
|
98
100
|
* @param attachOpts Attachment options
|
|
99
101
|
*/
|
|
100
102
|
export declare function setupConstraints(engine: Engine, discoveryOpts: DiscoveryOptions, attachOpts: AttachOptions): ConstraintSource[];
|
|
103
|
+
/**
|
|
104
|
+
* Collect the token ids referenced by the active constraint sources, plus
|
|
105
|
+
* whether that coverage is fully enumerable.
|
|
106
|
+
*
|
|
107
|
+
* Used to detect the silent-pass case: a token file that validates with zero
|
|
108
|
+
* errors only because no active constraint references any of its tokens. Cross-
|
|
109
|
+
* axis rule ids are not enumerated here, so when any cross-axis source is present
|
|
110
|
+
* `coverageKnown` is false and callers must stay conservative (never claim
|
|
111
|
+
* "nothing was checked" when they cannot be sure).
|
|
112
|
+
*/
|
|
113
|
+
export declare function collectReferencedIds(sources: ConstraintSource[]): {
|
|
114
|
+
ids: Set<string>;
|
|
115
|
+
coverageKnown: boolean;
|
|
116
|
+
};
|
|
101
117
|
//# sourceMappingURL=constraint-registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraint-registry.d.ts","sourceRoot":"","sources":["constraint-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAY5C,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAEtD,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,UAAU,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAMF;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CA8F9E;AAMD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"constraint-registry.d.ts","sourceRoot":"","sources":["constraint-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,16 @@ import { WcagContrastPlugin } from '../core/constraints/wcag.js';
|
|
|
18
18
|
import { ThresholdPlugin } from '../core/constraints/threshold.js';
|
|
19
19
|
import { CrossAxisPlugin } from '../core/constraints/cross-axis.js';
|
|
20
20
|
import { loadCrossAxisRules } from './cross-axis-loader.js';
|
|
21
|
+
// Built-in default constraint rules. Shared by attachConstraints (to register
|
|
22
|
+
// plugins) and collectReferencedIds (to compute coverage) so the two never drift.
|
|
23
|
+
export const DEFAULT_WCAG_PAIRS = [
|
|
24
|
+
{ fg: 'color.role.text.default', bg: 'color.role.bg.surface', min: 4.5, where: 'Body text on surface' },
|
|
25
|
+
{ fg: 'color.role.accent.default', bg: 'color.role.bg.surface', min: 3.0, where: 'Accent on surface' },
|
|
26
|
+
{ fg: 'color.role.focus.ring', bg: 'color.role.bg.surface', min: 3.0, where: 'Focus ring on surface', backdrop: '#ffffff' },
|
|
27
|
+
];
|
|
28
|
+
export const DEFAULT_THRESHOLDS = [
|
|
29
|
+
{ id: 'control.size.min', op: '>=', valuePx: 44, where: 'Touch target (WCAG / Apple HIG)' },
|
|
30
|
+
];
|
|
21
31
|
// ============================================================================
|
|
22
32
|
// Discovery
|
|
23
33
|
// ============================================================================
|
|
@@ -132,42 +142,13 @@ export function attachConstraints(engine, sources, opts) {
|
|
|
132
142
|
switch (source.type) {
|
|
133
143
|
case 'builtin-wcag': {
|
|
134
144
|
if (source.enabled) {
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
fg: 'color.role.text.default',
|
|
138
|
-
bg: 'color.role.bg.surface',
|
|
139
|
-
min: 4.5,
|
|
140
|
-
where: 'Body text on surface',
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
fg: 'color.role.accent.default',
|
|
144
|
-
bg: 'color.role.bg.surface',
|
|
145
|
-
min: 3.0,
|
|
146
|
-
where: 'Accent on surface',
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
fg: 'color.role.focus.ring',
|
|
150
|
-
bg: 'color.role.bg.surface',
|
|
151
|
-
min: 3.0,
|
|
152
|
-
where: 'Focus ring on surface',
|
|
153
|
-
backdrop: '#ffffff',
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
engine.use(WcagContrastPlugin(defaultWcagPairs));
|
|
145
|
+
engine.use(WcagContrastPlugin(DEFAULT_WCAG_PAIRS));
|
|
157
146
|
}
|
|
158
147
|
break;
|
|
159
148
|
}
|
|
160
149
|
case 'builtin-threshold': {
|
|
161
150
|
if (source.enabled) {
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
id: 'control.size.min',
|
|
165
|
-
op: '>=',
|
|
166
|
-
valuePx: 44,
|
|
167
|
-
where: 'Touch target (WCAG / Apple HIG)',
|
|
168
|
-
},
|
|
169
|
-
];
|
|
170
|
-
engine.use(ThresholdPlugin(defaultThresholds, 'threshold'));
|
|
151
|
+
engine.use(ThresholdPlugin(DEFAULT_THRESHOLDS, 'threshold'));
|
|
171
152
|
}
|
|
172
153
|
break;
|
|
173
154
|
}
|
|
@@ -223,3 +204,55 @@ export function setupConstraints(engine, discoveryOpts, attachOpts) {
|
|
|
223
204
|
attachConstraints(engine, sources, attachOpts);
|
|
224
205
|
return sources;
|
|
225
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Collect the token ids referenced by the active constraint sources, plus
|
|
209
|
+
* whether that coverage is fully enumerable.
|
|
210
|
+
*
|
|
211
|
+
* Used to detect the silent-pass case: a token file that validates with zero
|
|
212
|
+
* errors only because no active constraint references any of its tokens. Cross-
|
|
213
|
+
* axis rule ids are not enumerated here, so when any cross-axis source is present
|
|
214
|
+
* `coverageKnown` is false and callers must stay conservative (never claim
|
|
215
|
+
* "nothing was checked" when they cannot be sure).
|
|
216
|
+
*/
|
|
217
|
+
export function collectReferencedIds(sources) {
|
|
218
|
+
const ids = new Set();
|
|
219
|
+
let coverageKnown = true;
|
|
220
|
+
const addOrders = (orders) => {
|
|
221
|
+
for (const [a, , b] of orders) {
|
|
222
|
+
ids.add(a);
|
|
223
|
+
ids.add(b);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
for (const source of sources) {
|
|
227
|
+
switch (source.type) {
|
|
228
|
+
case 'builtin-wcag':
|
|
229
|
+
for (const p of DEFAULT_WCAG_PAIRS) {
|
|
230
|
+
ids.add(p.fg);
|
|
231
|
+
ids.add(p.bg);
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case 'builtin-threshold':
|
|
235
|
+
for (const t of DEFAULT_THRESHOLDS)
|
|
236
|
+
ids.add(t.id);
|
|
237
|
+
break;
|
|
238
|
+
case 'config-wcag':
|
|
239
|
+
for (const r of source.rules) {
|
|
240
|
+
ids.add(r.fg);
|
|
241
|
+
ids.add(r.bg);
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
case 'custom-threshold':
|
|
245
|
+
for (const r of source.rules)
|
|
246
|
+
ids.add(r.id);
|
|
247
|
+
break;
|
|
248
|
+
case 'order-file':
|
|
249
|
+
case 'lightness-file':
|
|
250
|
+
addOrders(source.orders);
|
|
251
|
+
break;
|
|
252
|
+
case 'cross-axis-file':
|
|
253
|
+
coverageKnown = false;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return { ids, coverageKnown };
|
|
258
|
+
}
|
|
@@ -69,6 +69,18 @@ export type AttachOptions = {
|
|
|
69
69
|
crossAxisDebug?: boolean;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
// Built-in default constraint rules. Shared by attachConstraints (to register
|
|
73
|
+
// plugins) and collectReferencedIds (to compute coverage) so the two never drift.
|
|
74
|
+
export const DEFAULT_WCAG_PAIRS: WcagRule[] = [
|
|
75
|
+
{ fg: 'color.role.text.default', bg: 'color.role.bg.surface', min: 4.5, where: 'Body text on surface' },
|
|
76
|
+
{ fg: 'color.role.accent.default', bg: 'color.role.bg.surface', min: 3.0, where: 'Accent on surface' },
|
|
77
|
+
{ fg: 'color.role.focus.ring', bg: 'color.role.bg.surface', min: 3.0, where: 'Focus ring on surface', backdrop: '#ffffff' },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
export const DEFAULT_THRESHOLDS: ThresholdRule[] = [
|
|
81
|
+
{ id: 'control.size.min', op: '>=', valuePx: 44, where: 'Touch target (WCAG / Apple HIG)' },
|
|
82
|
+
];
|
|
83
|
+
|
|
72
84
|
// ============================================================================
|
|
73
85
|
// Discovery
|
|
74
86
|
// ============================================================================
|
|
@@ -200,43 +212,14 @@ export function attachConstraints(engine: Engine, sources: ConstraintSource[], o
|
|
|
200
212
|
switch (source.type) {
|
|
201
213
|
case 'builtin-wcag': {
|
|
202
214
|
if (source.enabled) {
|
|
203
|
-
|
|
204
|
-
{
|
|
205
|
-
fg: 'color.role.text.default',
|
|
206
|
-
bg: 'color.role.bg.surface',
|
|
207
|
-
min: 4.5,
|
|
208
|
-
where: 'Body text on surface',
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
fg: 'color.role.accent.default',
|
|
212
|
-
bg: 'color.role.bg.surface',
|
|
213
|
-
min: 3.0,
|
|
214
|
-
where: 'Accent on surface',
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
fg: 'color.role.focus.ring',
|
|
218
|
-
bg: 'color.role.bg.surface',
|
|
219
|
-
min: 3.0,
|
|
220
|
-
where: 'Focus ring on surface',
|
|
221
|
-
backdrop: '#ffffff',
|
|
222
|
-
},
|
|
223
|
-
];
|
|
224
|
-
engine.use(WcagContrastPlugin(defaultWcagPairs));
|
|
215
|
+
engine.use(WcagContrastPlugin(DEFAULT_WCAG_PAIRS));
|
|
225
216
|
}
|
|
226
217
|
break;
|
|
227
218
|
}
|
|
228
219
|
|
|
229
220
|
case 'builtin-threshold': {
|
|
230
221
|
if (source.enabled) {
|
|
231
|
-
|
|
232
|
-
{
|
|
233
|
-
id: 'control.size.min',
|
|
234
|
-
op: '>=',
|
|
235
|
-
valuePx: 44,
|
|
236
|
-
where: 'Touch target (WCAG / Apple HIG)',
|
|
237
|
-
},
|
|
238
|
-
];
|
|
239
|
-
engine.use(ThresholdPlugin(defaultThresholds, 'threshold'));
|
|
222
|
+
engine.use(ThresholdPlugin(DEFAULT_THRESHOLDS, 'threshold'));
|
|
240
223
|
}
|
|
241
224
|
break;
|
|
242
225
|
}
|
|
@@ -302,3 +285,56 @@ export function setupConstraints(
|
|
|
302
285
|
attachConstraints(engine, sources, attachOpts);
|
|
303
286
|
return sources;
|
|
304
287
|
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Collect the token ids referenced by the active constraint sources, plus
|
|
291
|
+
* whether that coverage is fully enumerable.
|
|
292
|
+
*
|
|
293
|
+
* Used to detect the silent-pass case: a token file that validates with zero
|
|
294
|
+
* errors only because no active constraint references any of its tokens. Cross-
|
|
295
|
+
* axis rule ids are not enumerated here, so when any cross-axis source is present
|
|
296
|
+
* `coverageKnown` is false and callers must stay conservative (never claim
|
|
297
|
+
* "nothing was checked" when they cannot be sure).
|
|
298
|
+
*/
|
|
299
|
+
export function collectReferencedIds(sources: ConstraintSource[]): { ids: Set<string>; coverageKnown: boolean } {
|
|
300
|
+
const ids = new Set<string>();
|
|
301
|
+
let coverageKnown = true;
|
|
302
|
+
const addOrders = (orders: OrderRule[]) => {
|
|
303
|
+
for (const [a, , b] of orders) {
|
|
304
|
+
ids.add(a);
|
|
305
|
+
ids.add(b);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
for (const source of sources) {
|
|
310
|
+
switch (source.type) {
|
|
311
|
+
case 'builtin-wcag':
|
|
312
|
+
for (const p of DEFAULT_WCAG_PAIRS) {
|
|
313
|
+
ids.add(p.fg);
|
|
314
|
+
ids.add(p.bg);
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
case 'builtin-threshold':
|
|
318
|
+
for (const t of DEFAULT_THRESHOLDS) ids.add(t.id);
|
|
319
|
+
break;
|
|
320
|
+
case 'config-wcag':
|
|
321
|
+
for (const r of source.rules) {
|
|
322
|
+
ids.add(r.fg);
|
|
323
|
+
ids.add(r.bg);
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
326
|
+
case 'custom-threshold':
|
|
327
|
+
for (const r of source.rules) ids.add(r.id);
|
|
328
|
+
break;
|
|
329
|
+
case 'order-file':
|
|
330
|
+
case 'lightness-file':
|
|
331
|
+
addOrders(source.orders);
|
|
332
|
+
break;
|
|
333
|
+
case 'cross-axis-file':
|
|
334
|
+
coverageKnown = false;
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return { ids, coverageKnown };
|
|
340
|
+
}
|
package/cli/dcv.js
CHANGED
|
@@ -2,30 +2,12 @@
|
|
|
2
2
|
// Clean minimal DCV CLI entrypoint
|
|
3
3
|
import yargs from 'yargs/yargs';
|
|
4
4
|
import { hideBin } from 'yargs/helpers';
|
|
5
|
-
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import { createRequire } from 'module';
|
|
7
|
-
import path from 'node:path';
|
|
8
5
|
import { setCommand, buildCommand, validateCommand, graphCommand, whyCommand, patchCommand, patchApplyCommand } from './commands/index.js';
|
|
9
|
-
// Early intercept: experimental graph diff helper
|
|
10
|
-
(() => {
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
if (args[0] === 'graph' && args[1] === 'diff') {
|
|
13
|
-
const pass = args.slice(2);
|
|
14
|
-
const require = createRequire(import.meta.url);
|
|
15
|
-
const tsx = (() => { try {
|
|
16
|
-
return require.resolve('tsx/dist/cli.mjs');
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return path.resolve('node_modules/tsx/dist/cli.mjs');
|
|
20
|
-
} })();
|
|
21
|
-
const r = spawnSync(process.execPath, [tsx, path.resolve('scripts/graph-diff.ts'), ...pass], { stdio: 'inherit', env: process.env });
|
|
22
|
-
process.exit(r.status ?? 0);
|
|
23
|
-
}
|
|
24
|
-
})();
|
|
25
6
|
const cli = yargs(hideBin(process.argv))
|
|
26
7
|
.scriptName('dcv')
|
|
27
8
|
.parserConfiguration({ 'camel-case-expansion': false })
|
|
28
|
-
.option('quiet', { type: 'boolean' })
|
|
9
|
+
.option('quiet', { type: 'boolean' })
|
|
10
|
+
.option('config', { type: 'string', describe: 'Path to JSON config file' });
|
|
29
11
|
cli.command('set <expressions..>', 'Set token values', y => y
|
|
30
12
|
.positional('expressions', { type: 'string', array: true })
|
|
31
13
|
.option('dry-run', { type: 'boolean', default: false })
|
|
@@ -43,14 +25,16 @@ cli.command('build', 'Build token outputs', y => y
|
|
|
43
25
|
.option('mapper', { type: 'string' })
|
|
44
26
|
.option('theme', { type: 'string' })
|
|
45
27
|
.option('dry-run', { type: 'boolean', default: false })
|
|
46
|
-
.option('tokens', { type: 'string',
|
|
47
|
-
cli.command('validate', 'Validate constraints', y => y
|
|
28
|
+
.option('tokens', { type: 'string', describe: 'Path to a tokens file (defaults to tokens/tokens.example.json)' }), a => buildCommand(a));
|
|
29
|
+
cli.command('validate [tokens-path]', 'Validate constraints', y => y
|
|
30
|
+
.positional('tokens-path', { type: 'string', describe: 'Path to a tokens file (positional alias for --tokens)' })
|
|
31
|
+
.option('constraints-dir', { type: 'string', describe: 'Directory holding order / cross-axis constraint files (default: themes)' })
|
|
48
32
|
.option('fail-on', { type: 'string', choices: ['off', 'warn', 'error'], default: 'error' })
|
|
49
33
|
.option('summary', { type: 'string', choices: ['none', 'table', 'json'], default: 'none' })
|
|
50
34
|
.option('format', { type: 'string', choices: ['text', 'json'], default: 'text', describe: 'Output format' })
|
|
51
35
|
.option('output', { type: 'string', describe: 'Write JSON output to file' })
|
|
52
36
|
.option('receipt', { type: 'string', describe: 'Generate validation receipt with audit trail' })
|
|
53
|
-
.option('tokens', { type: 'string',
|
|
37
|
+
.option('tokens', { type: 'string', describe: 'Path to a tokens file (defaults to tokens/tokens.example.json)' })
|
|
54
38
|
.option('theme', { type: 'string', describe: 'Apply named theme tokens before validation' })
|
|
55
39
|
.option('breakpoint', { type: 'string' })
|
|
56
40
|
.option('all-breakpoints', { type: 'boolean' })
|
|
@@ -70,7 +54,7 @@ cli.command('graph', 'Generate dependency / constraint graph', y => y
|
|
|
70
54
|
.option('min-severity', { type: 'string', choices: ['warn', 'error'], default: 'warn' })
|
|
71
55
|
.option('focus', { type: 'string' })
|
|
72
56
|
.option('radius', { type: 'number', default: 1 })
|
|
73
|
-
.option('tokens', { type: 'string',
|
|
57
|
+
.option('tokens', { type: 'string', describe: 'Path to a tokens file (defaults to tokens/tokens.example.json)' }), a => graphCommand(a));
|
|
74
58
|
cli.command('why <tokenId>', 'Explain token provenance', y => y
|
|
75
59
|
.positional('tokenId', { type: 'string', demandOption: true })
|
|
76
60
|
.option('format', { type: 'string', choices: ['json', 'table'], default: 'json' })
|
package/cli/dcv.ts
CHANGED
|
@@ -2,28 +2,14 @@
|
|
|
2
2
|
// Clean minimal DCV CLI entrypoint
|
|
3
3
|
import yargs from 'yargs/yargs';
|
|
4
4
|
import { hideBin } from 'yargs/helpers';
|
|
5
|
-
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import { createRequire } from 'module';
|
|
7
|
-
import path from 'node:path';
|
|
8
5
|
import { type SetOptions, type BuildOptions, type ValidateOptions, type GraphOptions, type WhyOptions, type PatchOptions, type PatchApplyOptions } from './types.js';
|
|
9
6
|
import { setCommand, buildCommand, validateCommand, graphCommand, whyCommand, patchCommand, patchApplyCommand } from './commands/index.js';
|
|
10
7
|
|
|
11
|
-
// Early intercept: experimental graph diff helper
|
|
12
|
-
(() => {
|
|
13
|
-
const args = process.argv.slice(2);
|
|
14
|
-
if (args[0] === 'graph' && args[1] === 'diff') {
|
|
15
|
-
const pass = args.slice(2);
|
|
16
|
-
const require = createRequire(import.meta.url);
|
|
17
|
-
const tsx = (() => { try { return require.resolve('tsx/dist/cli.mjs'); } catch { return path.resolve('node_modules/tsx/dist/cli.mjs'); }})();
|
|
18
|
-
const r = spawnSync(process.execPath, [tsx, path.resolve('scripts/graph-diff.ts'), ...pass], { stdio: 'inherit', env: process.env });
|
|
19
|
-
process.exit(r.status ?? 0);
|
|
20
|
-
}
|
|
21
|
-
})();
|
|
22
|
-
|
|
23
8
|
const cli = yargs(hideBin(process.argv))
|
|
24
9
|
.scriptName('dcv')
|
|
25
10
|
.parserConfiguration({ 'camel-case-expansion': false })
|
|
26
|
-
.option('quiet', { type: 'boolean' })
|
|
11
|
+
.option('quiet', { type: 'boolean' })
|
|
12
|
+
.option('config', { type: 'string', describe: 'Path to JSON config file' });
|
|
27
13
|
|
|
28
14
|
cli.command<SetOptions>('set <expressions..>', 'Set token values', y => y
|
|
29
15
|
.positional('expressions', { type: 'string', array: true })
|
|
@@ -45,17 +31,19 @@ cli.command<BuildOptions>('build', 'Build token outputs', y => y
|
|
|
45
31
|
.option('mapper', { type: 'string' })
|
|
46
32
|
.option('theme', { type: 'string' })
|
|
47
33
|
.option('dry-run', { type: 'boolean', default: false })
|
|
48
|
-
.option('tokens', { type: 'string',
|
|
34
|
+
.option('tokens', { type: 'string', describe: 'Path to a tokens file (defaults to tokens/tokens.example.json)' }),
|
|
49
35
|
a => buildCommand(a)
|
|
50
36
|
);
|
|
51
37
|
|
|
52
|
-
cli.command<ValidateOptions>('validate', 'Validate constraints', y => y
|
|
38
|
+
cli.command<ValidateOptions>('validate [tokens-path]', 'Validate constraints', y => y
|
|
39
|
+
.positional('tokens-path', { type: 'string', describe: 'Path to a tokens file (positional alias for --tokens)' })
|
|
40
|
+
.option('constraints-dir', { type: 'string', describe: 'Directory holding order / cross-axis constraint files (default: themes)' })
|
|
53
41
|
.option('fail-on', { type: 'string', choices: ['off','warn','error'], default: 'error' })
|
|
54
42
|
.option('summary', { type: 'string', choices: ['none','table','json'], default: 'none' })
|
|
55
43
|
.option('format', { type: 'string', choices: ['text','json'], default: 'text', describe: 'Output format' })
|
|
56
44
|
.option('output', { type: 'string', describe: 'Write JSON output to file' })
|
|
57
45
|
.option('receipt', { type: 'string', describe: 'Generate validation receipt with audit trail' })
|
|
58
|
-
.option('tokens', { type: 'string',
|
|
46
|
+
.option('tokens', { type: 'string', describe: 'Path to a tokens file (defaults to tokens/tokens.example.json)' })
|
|
59
47
|
.option('theme', { type: 'string', describe: 'Apply named theme tokens before validation' })
|
|
60
48
|
.option('breakpoint', { type: 'string' })
|
|
61
49
|
.option('all-breakpoints', { type: 'boolean' })
|
|
@@ -78,7 +66,7 @@ cli.command<GraphOptions>('graph', 'Generate dependency / constraint graph', y =
|
|
|
78
66
|
.option('min-severity', { type: 'string', choices: ['warn','error'], default: 'warn' })
|
|
79
67
|
.option('focus', { type: 'string' })
|
|
80
68
|
.option('radius', { type: 'number', default: 1 })
|
|
81
|
-
.option('tokens', { type: 'string',
|
|
69
|
+
.option('tokens', { type: 'string', describe: 'Path to a tokens file (defaults to tokens/tokens.example.json)' }),
|
|
82
70
|
a => graphCommand(a)
|
|
83
71
|
);
|
|
84
72
|
|
package/cli/json-output.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface ValidationResult {
|
|
|
21
21
|
};
|
|
22
22
|
violations: ConstraintViolation[];
|
|
23
23
|
warnings?: ConstraintViolation[];
|
|
24
|
+
/** Set when tokens were validated but no active constraint referenced any of them. */
|
|
25
|
+
note?: string;
|
|
24
26
|
stats: {
|
|
25
27
|
durationMs: number;
|
|
26
28
|
engineVersion: string;
|
|
@@ -57,7 +59,7 @@ export declare function formatViolation(issue: ConstraintIssue): ConstraintViola
|
|
|
57
59
|
/**
|
|
58
60
|
* Generate a ValidationResult from collected issues
|
|
59
61
|
*/
|
|
60
|
-
export declare function createValidationResult(errors: ConstraintIssue[], warnings: ConstraintIssue[], durationMs: number, engineVersion: string): ValidationResult;
|
|
62
|
+
export declare function createValidationResult(errors: ConstraintIssue[], warnings: ConstraintIssue[], durationMs: number, engineVersion: string, note?: string): ValidationResult;
|
|
61
63
|
/**
|
|
62
64
|
* Generate a ValidationReceipt with full audit trail
|
|
63
65
|
*/
|
package/cli/json-output.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-output.d.ts","sourceRoot":"","sources":["json-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMzD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,WAAW,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzC,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;QACjC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,mBAAmB,
|
|
1
|
+
{"version":3,"file":"json-output.d.ts","sourceRoot":"","sources":["json-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMzD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,WAAW,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzC,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;QACjC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,mBAAmB,CAsB3E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,EAAE,EACzB,QAAQ,EAAE,eAAe,EAAE,EAC3B,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,gBAAgB,CAqBlB;AAcD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAC/B,iBAAiB,CAoCnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CASxE"}
|
package/cli/json-output.js
CHANGED
|
@@ -11,19 +11,25 @@ export function formatViolation(issue) {
|
|
|
11
11
|
level: issue.level === 'error' ? 'error' : 'warn',
|
|
12
12
|
message: issue.message,
|
|
13
13
|
};
|
|
14
|
-
if (issue.
|
|
14
|
+
if (issue.involvedTokens?.length) {
|
|
15
|
+
violation.nodes = issue.involvedTokens;
|
|
16
|
+
}
|
|
17
|
+
else if (issue.id) {
|
|
15
18
|
violation.nodes = [issue.id];
|
|
16
19
|
}
|
|
17
20
|
// Add context from where field or other metadata
|
|
18
|
-
if (issue.where) {
|
|
19
|
-
violation.context = {
|
|
21
|
+
if (issue.where || issue.metadata) {
|
|
22
|
+
violation.context = {
|
|
23
|
+
...(issue.where ? { where: issue.where } : {}),
|
|
24
|
+
...(issue.metadata ?? {}),
|
|
25
|
+
};
|
|
20
26
|
}
|
|
21
27
|
return violation;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* Generate a ValidationResult from collected issues
|
|
25
31
|
*/
|
|
26
|
-
export function createValidationResult(errors, warnings, durationMs, engineVersion) {
|
|
32
|
+
export function createValidationResult(errors, warnings, durationMs, engineVersion, note) {
|
|
27
33
|
const violations = errors.map(formatViolation);
|
|
28
34
|
const warningViolations = warnings.map(formatViolation);
|
|
29
35
|
return {
|
|
@@ -35,6 +41,7 @@ export function createValidationResult(errors, warnings, durationMs, engineVersi
|
|
|
35
41
|
},
|
|
36
42
|
violations,
|
|
37
43
|
warnings: warningViolations.length > 0 ? warningViolations : undefined,
|
|
44
|
+
note,
|
|
38
45
|
stats: {
|
|
39
46
|
durationMs: Math.round(durationMs),
|
|
40
47
|
engineVersion,
|
package/cli/json-output.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface ValidationResult {
|
|
|
27
27
|
};
|
|
28
28
|
violations: ConstraintViolation[];
|
|
29
29
|
warnings?: ConstraintViolation[];
|
|
30
|
+
/** Set when tokens were validated but no active constraint referenced any of them. */
|
|
31
|
+
note?: string;
|
|
30
32
|
stats: {
|
|
31
33
|
durationMs: number;
|
|
32
34
|
engineVersion: string;
|
|
@@ -68,13 +70,18 @@ export function formatViolation(issue: ConstraintIssue): ConstraintViolation {
|
|
|
68
70
|
message: issue.message,
|
|
69
71
|
};
|
|
70
72
|
|
|
71
|
-
if (issue.
|
|
73
|
+
if (issue.involvedTokens?.length) {
|
|
74
|
+
violation.nodes = issue.involvedTokens;
|
|
75
|
+
} else if (issue.id) {
|
|
72
76
|
violation.nodes = [issue.id];
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
// Add context from where field or other metadata
|
|
76
|
-
if (issue.where) {
|
|
77
|
-
violation.context = {
|
|
80
|
+
if (issue.where || issue.metadata) {
|
|
81
|
+
violation.context = {
|
|
82
|
+
...(issue.where ? { where: issue.where } : {}),
|
|
83
|
+
...(issue.metadata ?? {}),
|
|
84
|
+
};
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
return violation;
|
|
@@ -87,7 +94,8 @@ export function createValidationResult(
|
|
|
87
94
|
errors: ConstraintIssue[],
|
|
88
95
|
warnings: ConstraintIssue[],
|
|
89
96
|
durationMs: number,
|
|
90
|
-
engineVersion: string
|
|
97
|
+
engineVersion: string,
|
|
98
|
+
note?: string
|
|
91
99
|
): ValidationResult {
|
|
92
100
|
const violations = errors.map(formatViolation);
|
|
93
101
|
const warningViolations = warnings.map(formatViolation);
|
|
@@ -101,6 +109,7 @@ export function createValidationResult(
|
|
|
101
109
|
},
|
|
102
110
|
violations,
|
|
103
111
|
warnings: warningViolations.length > 0 ? warningViolations : undefined,
|
|
112
|
+
note,
|
|
104
113
|
stats: {
|
|
105
114
|
durationMs: Math.round(durationMs),
|
|
106
115
|
engineVersion,
|
package/cli/types.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface BuildOptions extends GlobalOptions {
|
|
|
45
45
|
export interface ValidateOptions extends GlobalOptions {
|
|
46
46
|
strict?: boolean;
|
|
47
47
|
constraints?: string[];
|
|
48
|
+
'tokens-path'?: string;
|
|
49
|
+
'constraints-dir'?: string;
|
|
48
50
|
perf?: boolean;
|
|
49
51
|
budgetTotalMs?: number;
|
|
50
52
|
budgetPerBpMs?: number;
|