design-constraint-validator 1.1.0 → 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.
Files changed (99) hide show
  1. package/README.md +90 -21
  2. package/adapters/decisionthemes.d.ts +44 -0
  3. package/adapters/decisionthemes.d.ts.map +1 -0
  4. package/adapters/decisionthemes.js +35 -0
  5. package/adapters/decisionthemes.ts +59 -0
  6. package/cli/commands/build.js +1 -1
  7. package/cli/commands/build.ts +1 -1
  8. package/cli/commands/graph.js +4 -4
  9. package/cli/commands/graph.ts +4 -4
  10. package/cli/commands/validate.d.ts.map +1 -1
  11. package/cli/commands/validate.js +43 -6
  12. package/cli/commands/validate.ts +41 -8
  13. package/cli/config-schema.d.ts +39 -0
  14. package/cli/config-schema.d.ts.map +1 -1
  15. package/cli/config-schema.js +4 -2
  16. package/cli/config-schema.ts +4 -2
  17. package/cli/config.d.ts.map +1 -1
  18. package/cli/config.js +8 -2
  19. package/cli/config.ts +8 -2
  20. package/cli/constraint-registry.d.ts +16 -0
  21. package/cli/constraint-registry.d.ts.map +1 -1
  22. package/cli/constraint-registry.js +64 -31
  23. package/cli/constraint-registry.ts +67 -31
  24. package/cli/dcv.js +8 -24
  25. package/cli/dcv.ts +8 -20
  26. package/cli/json-output.d.ts +8 -1
  27. package/cli/json-output.d.ts.map +1 -1
  28. package/cli/json-output.js +13 -4
  29. package/cli/json-output.ts +20 -4
  30. package/cli/types.d.ts +2 -0
  31. package/cli/types.d.ts.map +1 -1
  32. package/cli/types.ts +2 -0
  33. package/cli/validate-api.d.ts +40 -0
  34. package/cli/validate-api.d.ts.map +1 -0
  35. package/cli/validate-api.js +85 -0
  36. package/cli/validate-api.ts +126 -0
  37. package/cli/version-banner.d.ts +20 -0
  38. package/cli/version-banner.d.ts.map +1 -0
  39. package/cli/version-banner.js +49 -0
  40. package/cli/version-banner.ts +61 -0
  41. package/core/breakpoints.d.ts +8 -2
  42. package/core/breakpoints.d.ts.map +1 -1
  43. package/core/breakpoints.js +24 -3
  44. package/core/breakpoints.ts +22 -3
  45. package/core/color.js +4 -4
  46. package/core/color.ts +4 -4
  47. package/core/constraints/monotonic-lightness.d.ts.map +1 -1
  48. package/core/constraints/monotonic-lightness.js +9 -5
  49. package/core/constraints/monotonic-lightness.ts +9 -4
  50. package/core/constraints/wcag.d.ts.map +1 -1
  51. package/core/constraints/wcag.js +6 -0
  52. package/core/constraints/wcag.ts +6 -0
  53. package/core/dtcg.d.ts +38 -0
  54. package/core/dtcg.d.ts.map +1 -0
  55. package/core/dtcg.js +88 -0
  56. package/core/dtcg.ts +102 -0
  57. package/core/engine.d.ts +6 -0
  58. package/core/engine.d.ts.map +1 -1
  59. package/core/engine.ts +7 -0
  60. package/core/flatten.d.ts +5 -3
  61. package/core/flatten.d.ts.map +1 -1
  62. package/core/flatten.js +24 -10
  63. package/core/flatten.ts +39 -16
  64. package/core/image-export.d.ts.map +1 -1
  65. package/core/image-export.js +10 -7
  66. package/core/image-export.ts +9 -6
  67. package/core/index.d.ts +2 -0
  68. package/core/index.d.ts.map +1 -1
  69. package/core/index.js +4 -0
  70. package/core/index.ts +6 -0
  71. package/core/why.d.ts +1 -1
  72. package/core/why.d.ts.map +1 -1
  73. package/core/why.ts +1 -1
  74. package/mcp/contracts.d.ts +118 -0
  75. package/mcp/contracts.d.ts.map +1 -0
  76. package/mcp/contracts.js +30 -0
  77. package/mcp/contracts.ts +51 -0
  78. package/mcp/index.d.ts +9 -0
  79. package/mcp/index.d.ts.map +1 -0
  80. package/mcp/index.js +32 -0
  81. package/mcp/index.ts +70 -0
  82. package/mcp/tools.d.ts +52 -0
  83. package/mcp/tools.d.ts.map +1 -0
  84. package/mcp/tools.js +172 -0
  85. package/mcp/tools.ts +254 -0
  86. package/package.json +41 -26
  87. package/server.json +21 -0
  88. package/cli/constraints-loader.d.ts +0 -30
  89. package/cli/constraints-loader.d.ts.map +0 -1
  90. package/cli/constraints-loader.js +0 -58
  91. package/cli/constraints-loader.ts +0 -83
  92. package/cli/engine-helpers.d.ts +0 -41
  93. package/cli/engine-helpers.d.ts.map +0 -1
  94. package/cli/engine-helpers.js +0 -135
  95. package/cli/engine-helpers.ts +0 -133
  96. package/core/cross-axis-config.d.ts +0 -34
  97. package/core/cross-axis-config.d.ts.map +0 -1
  98. package/core/cross-axis-config.js +0 -173
  99. package/core/cross-axis-config.ts +0 -182
@@ -4,16 +4,19 @@ export declare const WcagRuleSchema: z.ZodObject<{
4
4
  background: z.ZodString;
5
5
  ratio: z.ZodOptional<z.ZodNumber>;
6
6
  description: z.ZodOptional<z.ZodString>;
7
+ backdrop: z.ZodOptional<z.ZodString>;
7
8
  }, "strip", z.ZodTypeAny, {
8
9
  foreground: string;
9
10
  background: string;
10
11
  description?: string | undefined;
11
12
  ratio?: number | undefined;
13
+ backdrop?: string | undefined;
12
14
  }, {
13
15
  foreground: string;
14
16
  background: string;
15
17
  description?: string | undefined;
16
18
  ratio?: number | undefined;
19
+ backdrop?: string | undefined;
17
20
  }>;
18
21
  export declare const ConstraintsSchema: z.ZodObject<{
19
22
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -21,16 +24,19 @@ export declare const ConstraintsSchema: z.ZodObject<{
21
24
  background: z.ZodString;
22
25
  ratio: z.ZodOptional<z.ZodNumber>;
23
26
  description: z.ZodOptional<z.ZodString>;
27
+ backdrop: z.ZodOptional<z.ZodString>;
24
28
  }, "strip", z.ZodTypeAny, {
25
29
  foreground: string;
26
30
  background: string;
27
31
  description?: string | undefined;
28
32
  ratio?: number | undefined;
33
+ backdrop?: string | undefined;
29
34
  }, {
30
35
  foreground: string;
31
36
  background: string;
32
37
  description?: string | undefined;
33
38
  ratio?: number | undefined;
39
+ backdrop?: string | undefined;
34
40
  }>, "many">>;
35
41
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
36
42
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -38,16 +44,19 @@ export declare const ConstraintsSchema: z.ZodObject<{
38
44
  background: z.ZodString;
39
45
  ratio: z.ZodOptional<z.ZodNumber>;
40
46
  description: z.ZodOptional<z.ZodString>;
47
+ backdrop: z.ZodOptional<z.ZodString>;
41
48
  }, "strip", z.ZodTypeAny, {
42
49
  foreground: string;
43
50
  background: string;
44
51
  description?: string | undefined;
45
52
  ratio?: number | undefined;
53
+ backdrop?: string | undefined;
46
54
  }, {
47
55
  foreground: string;
48
56
  background: string;
49
57
  description?: string | undefined;
50
58
  ratio?: number | undefined;
59
+ backdrop?: string | undefined;
51
60
  }>, "many">>;
52
61
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
53
62
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -55,16 +64,19 @@ export declare const ConstraintsSchema: z.ZodObject<{
55
64
  background: z.ZodString;
56
65
  ratio: z.ZodOptional<z.ZodNumber>;
57
66
  description: z.ZodOptional<z.ZodString>;
67
+ backdrop: z.ZodOptional<z.ZodString>;
58
68
  }, "strip", z.ZodTypeAny, {
59
69
  foreground: string;
60
70
  background: string;
61
71
  description?: string | undefined;
62
72
  ratio?: number | undefined;
73
+ backdrop?: string | undefined;
63
74
  }, {
64
75
  foreground: string;
65
76
  background: string;
66
77
  description?: string | undefined;
67
78
  ratio?: number | undefined;
79
+ backdrop?: string | undefined;
68
80
  }>, "many">>;
69
81
  }, z.ZodTypeAny, "passthrough">>;
70
82
  export declare const DcvConfigSchema: z.ZodObject<{
@@ -75,16 +87,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
75
87
  background: z.ZodString;
76
88
  ratio: z.ZodOptional<z.ZodNumber>;
77
89
  description: z.ZodOptional<z.ZodString>;
90
+ backdrop: z.ZodOptional<z.ZodString>;
78
91
  }, "strip", z.ZodTypeAny, {
79
92
  foreground: string;
80
93
  background: string;
81
94
  description?: string | undefined;
82
95
  ratio?: number | undefined;
96
+ backdrop?: string | undefined;
83
97
  }, {
84
98
  foreground: string;
85
99
  background: string;
86
100
  description?: string | undefined;
87
101
  ratio?: number | undefined;
102
+ backdrop?: string | undefined;
88
103
  }>, "many">>;
89
104
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
90
105
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -92,16 +107,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
92
107
  background: z.ZodString;
93
108
  ratio: z.ZodOptional<z.ZodNumber>;
94
109
  description: z.ZodOptional<z.ZodString>;
110
+ backdrop: z.ZodOptional<z.ZodString>;
95
111
  }, "strip", z.ZodTypeAny, {
96
112
  foreground: string;
97
113
  background: string;
98
114
  description?: string | undefined;
99
115
  ratio?: number | undefined;
116
+ backdrop?: string | undefined;
100
117
  }, {
101
118
  foreground: string;
102
119
  background: string;
103
120
  description?: string | undefined;
104
121
  ratio?: number | undefined;
122
+ backdrop?: string | undefined;
105
123
  }>, "many">>;
106
124
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
107
125
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -109,16 +127,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
109
127
  background: z.ZodString;
110
128
  ratio: z.ZodOptional<z.ZodNumber>;
111
129
  description: z.ZodOptional<z.ZodString>;
130
+ backdrop: z.ZodOptional<z.ZodString>;
112
131
  }, "strip", z.ZodTypeAny, {
113
132
  foreground: string;
114
133
  background: string;
115
134
  description?: string | undefined;
116
135
  ratio?: number | undefined;
136
+ backdrop?: string | undefined;
117
137
  }, {
118
138
  foreground: string;
119
139
  background: string;
120
140
  description?: string | undefined;
121
141
  ratio?: number | undefined;
142
+ backdrop?: string | undefined;
122
143
  }>, "many">>;
123
144
  }, z.ZodTypeAny, "passthrough">>>;
124
145
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
@@ -129,16 +150,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
129
150
  background: z.ZodString;
130
151
  ratio: z.ZodOptional<z.ZodNumber>;
131
152
  description: z.ZodOptional<z.ZodString>;
153
+ backdrop: z.ZodOptional<z.ZodString>;
132
154
  }, "strip", z.ZodTypeAny, {
133
155
  foreground: string;
134
156
  background: string;
135
157
  description?: string | undefined;
136
158
  ratio?: number | undefined;
159
+ backdrop?: string | undefined;
137
160
  }, {
138
161
  foreground: string;
139
162
  background: string;
140
163
  description?: string | undefined;
141
164
  ratio?: number | undefined;
165
+ backdrop?: string | undefined;
142
166
  }>, "many">>;
143
167
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
144
168
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -146,16 +170,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
146
170
  background: z.ZodString;
147
171
  ratio: z.ZodOptional<z.ZodNumber>;
148
172
  description: z.ZodOptional<z.ZodString>;
173
+ backdrop: z.ZodOptional<z.ZodString>;
149
174
  }, "strip", z.ZodTypeAny, {
150
175
  foreground: string;
151
176
  background: string;
152
177
  description?: string | undefined;
153
178
  ratio?: number | undefined;
179
+ backdrop?: string | undefined;
154
180
  }, {
155
181
  foreground: string;
156
182
  background: string;
157
183
  description?: string | undefined;
158
184
  ratio?: number | undefined;
185
+ backdrop?: string | undefined;
159
186
  }>, "many">>;
160
187
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
161
188
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -163,16 +190,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
163
190
  background: z.ZodString;
164
191
  ratio: z.ZodOptional<z.ZodNumber>;
165
192
  description: z.ZodOptional<z.ZodString>;
193
+ backdrop: z.ZodOptional<z.ZodString>;
166
194
  }, "strip", z.ZodTypeAny, {
167
195
  foreground: string;
168
196
  background: string;
169
197
  description?: string | undefined;
170
198
  ratio?: number | undefined;
199
+ backdrop?: string | undefined;
171
200
  }, {
172
201
  foreground: string;
173
202
  background: string;
174
203
  description?: string | undefined;
175
204
  ratio?: number | undefined;
205
+ backdrop?: string | undefined;
176
206
  }>, "many">>;
177
207
  }, z.ZodTypeAny, "passthrough">>>;
178
208
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
@@ -183,16 +213,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
183
213
  background: z.ZodString;
184
214
  ratio: z.ZodOptional<z.ZodNumber>;
185
215
  description: z.ZodOptional<z.ZodString>;
216
+ backdrop: z.ZodOptional<z.ZodString>;
186
217
  }, "strip", z.ZodTypeAny, {
187
218
  foreground: string;
188
219
  background: string;
189
220
  description?: string | undefined;
190
221
  ratio?: number | undefined;
222
+ backdrop?: string | undefined;
191
223
  }, {
192
224
  foreground: string;
193
225
  background: string;
194
226
  description?: string | undefined;
195
227
  ratio?: number | undefined;
228
+ backdrop?: string | undefined;
196
229
  }>, "many">>;
197
230
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
198
231
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -200,16 +233,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
200
233
  background: z.ZodString;
201
234
  ratio: z.ZodOptional<z.ZodNumber>;
202
235
  description: z.ZodOptional<z.ZodString>;
236
+ backdrop: z.ZodOptional<z.ZodString>;
203
237
  }, "strip", z.ZodTypeAny, {
204
238
  foreground: string;
205
239
  background: string;
206
240
  description?: string | undefined;
207
241
  ratio?: number | undefined;
242
+ backdrop?: string | undefined;
208
243
  }, {
209
244
  foreground: string;
210
245
  background: string;
211
246
  description?: string | undefined;
212
247
  ratio?: number | undefined;
248
+ backdrop?: string | undefined;
213
249
  }>, "many">>;
214
250
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
215
251
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -217,16 +253,19 @@ export declare const DcvConfigSchema: z.ZodObject<{
217
253
  background: z.ZodString;
218
254
  ratio: z.ZodOptional<z.ZodNumber>;
219
255
  description: z.ZodOptional<z.ZodString>;
256
+ backdrop: z.ZodOptional<z.ZodString>;
220
257
  }, "strip", z.ZodTypeAny, {
221
258
  foreground: string;
222
259
  background: string;
223
260
  description?: string | undefined;
224
261
  ratio?: number | undefined;
262
+ backdrop?: string | undefined;
225
263
  }, {
226
264
  foreground: string;
227
265
  background: string;
228
266
  description?: string | undefined;
229
267
  ratio?: number | undefined;
268
+ backdrop?: string | undefined;
230
269
  }>, "many">>;
231
270
  }, z.ZodTypeAny, "passthrough">>>;
232
271
  }, z.ZodTypeAny, "passthrough">>;
@@ -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;;;;;;;;;;;;;;;EAKzB,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,CAM3F"}
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"}
@@ -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
- return { errors: res.error.errors.map(e => `${e.path.join('.') || '<root>'}: ${e.message}`) };
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
  }
@@ -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
- return { errors: res.error.errors.map(e => `${e.path.join('.')||'<root>'}: ${e.message}`) };
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":"AAEA,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,CA6BzE"}
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,CAsFxG;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,gBAAgB,EAC/B,UAAU,EAAE,aAAa,GACxB,gBAAgB,EAAE,CAIpB"}
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
- const defaultWcagPairs = [
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
- const defaultThresholds = [
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
- const defaultWcagPairs: WcagRule[] = [
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
- const defaultThresholds: ThresholdRule[] = [
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
+ }