design-constraint-validator 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +17 -6
  2. package/cli/commands/build.d.ts.map +1 -1
  3. package/cli/commands/build.js +32 -24
  4. package/cli/commands/build.ts +26 -17
  5. package/cli/commands/graph.d.ts.map +1 -1
  6. package/cli/commands/graph.js +33 -16
  7. package/cli/commands/graph.ts +28 -15
  8. package/cli/commands/patch-apply.d.ts.map +1 -1
  9. package/cli/commands/patch-apply.js +4 -1
  10. package/cli/commands/patch-apply.ts +4 -1
  11. package/cli/commands/set.d.ts.map +1 -1
  12. package/cli/commands/set.js +18 -19
  13. package/cli/commands/set.ts +19 -19
  14. package/cli/commands/utils.d.ts +1 -0
  15. package/cli/commands/utils.d.ts.map +1 -1
  16. package/cli/commands/utils.js +20 -1
  17. package/cli/commands/utils.ts +23 -1
  18. package/cli/commands/validate.d.ts.map +1 -1
  19. package/cli/commands/validate.js +5 -17
  20. package/cli/commands/validate.ts +10 -18
  21. package/cli/commands/why.d.ts.map +1 -1
  22. package/cli/commands/why.js +22 -10
  23. package/cli/commands/why.ts +20 -9
  24. package/cli/config-schema.d.ts +144 -178
  25. package/cli/config-schema.d.ts.map +1 -1
  26. package/cli/config-schema.js +25 -5
  27. package/cli/config-schema.ts +27 -5
  28. package/cli/constraint-registry.d.ts.map +1 -1
  29. package/cli/constraint-registry.js +53 -15
  30. package/cli/constraint-registry.ts +53 -18
  31. package/cli/cross-axis-loader.d.ts +62 -0
  32. package/cli/cross-axis-loader.d.ts.map +1 -1
  33. package/cli/cross-axis-loader.js +186 -31
  34. package/cli/cross-axis-loader.ts +199 -24
  35. package/cli/dcv.js +23 -1
  36. package/cli/dcv.ts +23 -1
  37. package/cli/types.d.ts +19 -9
  38. package/cli/types.d.ts.map +1 -1
  39. package/cli/types.ts +23 -10
  40. package/cli/validate-api.d.ts.map +1 -1
  41. package/cli/validate-api.js +6 -1
  42. package/cli/validate-api.ts +6 -1
  43. package/core/constraints/cross-axis.d.ts.map +1 -1
  44. package/core/constraints/cross-axis.js +37 -9
  45. package/core/constraints/cross-axis.ts +37 -9
  46. package/core/constraints/monotonic.d.ts.map +1 -1
  47. package/core/constraints/monotonic.js +32 -8
  48. package/core/constraints/monotonic.ts +29 -8
  49. package/core/constraints/threshold.d.ts.map +1 -1
  50. package/core/constraints/threshold.js +24 -4
  51. package/core/constraints/threshold.ts +23 -4
  52. package/core/constraints/wcag.js +1 -1
  53. package/core/constraints/wcag.ts +1 -1
  54. package/core/flatten.d.ts.map +1 -1
  55. package/core/flatten.js +8 -0
  56. package/core/flatten.ts +9 -0
  57. package/core/poset.d.ts +6 -1
  58. package/core/poset.d.ts.map +1 -1
  59. package/core/poset.js +7 -2
  60. package/core/poset.ts +7 -2
  61. package/mcp/contracts.d.ts +1456 -13
  62. package/mcp/contracts.d.ts.map +1 -1
  63. package/mcp/contracts.js +45 -1
  64. package/mcp/contracts.ts +55 -1
  65. package/mcp/index.d.ts +6 -4
  66. package/mcp/index.d.ts.map +1 -1
  67. package/mcp/index.js +6 -3
  68. package/mcp/index.ts +28 -1
  69. package/mcp/insights.d.ts +94 -0
  70. package/mcp/insights.d.ts.map +1 -0
  71. package/mcp/insights.js +445 -0
  72. package/mcp/insights.ts +541 -0
  73. package/mcp/tools.d.ts +14 -3
  74. package/mcp/tools.d.ts.map +1 -1
  75. package/mcp/tools.js +133 -6
  76. package/mcp/tools.ts +188 -11
  77. package/package.json +1 -6
  78. package/server.json +2 -2
@@ -5,7 +5,7 @@ export declare const WcagRuleSchema: z.ZodObject<{
5
5
  ratio: z.ZodOptional<z.ZodNumber>;
6
6
  description: z.ZodOptional<z.ZodString>;
7
7
  backdrop: z.ZodOptional<z.ZodString>;
8
- }, "strip", z.ZodTypeAny, {
8
+ }, "strict", z.ZodTypeAny, {
9
9
  foreground: string;
10
10
  background: string;
11
11
  description?: string | undefined;
@@ -18,14 +18,35 @@ export declare const WcagRuleSchema: z.ZodObject<{
18
18
  ratio?: number | undefined;
19
19
  backdrop?: string | undefined;
20
20
  }>;
21
+ export declare const ThresholdRuleSchema: z.ZodObject<{
22
+ id: z.ZodString;
23
+ op: z.ZodEnum<["<=", ">="]>;
24
+ valuePx: z.ZodNumber;
25
+ where: z.ZodOptional<z.ZodString>;
26
+ level: z.ZodOptional<z.ZodEnum<["error", "warn"]>>;
27
+ }, "strict", z.ZodTypeAny, {
28
+ id: string;
29
+ op: "<=" | ">=";
30
+ valuePx: number;
31
+ where?: string | undefined;
32
+ level?: "error" | "warn" | undefined;
33
+ }, {
34
+ id: string;
35
+ op: "<=" | ">=";
36
+ valuePx: number;
37
+ where?: string | undefined;
38
+ level?: "error" | "warn" | undefined;
39
+ }>;
21
40
  export declare const ConstraintsSchema: z.ZodObject<{
41
+ enableBuiltInWcagDefaults: z.ZodOptional<z.ZodBoolean>;
42
+ enableBuiltInThreshold: z.ZodOptional<z.ZodBoolean>;
22
43
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
23
44
  foreground: z.ZodString;
24
45
  background: z.ZodString;
25
46
  ratio: z.ZodOptional<z.ZodNumber>;
26
47
  description: z.ZodOptional<z.ZodString>;
27
48
  backdrop: z.ZodOptional<z.ZodString>;
28
- }, "strip", z.ZodTypeAny, {
49
+ }, "strict", z.ZodTypeAny, {
29
50
  foreground: string;
30
51
  background: string;
31
52
  description?: string | undefined;
@@ -38,57 +59,73 @@ export declare const ConstraintsSchema: z.ZodObject<{
38
59
  ratio?: number | undefined;
39
60
  backdrop?: string | undefined;
40
61
  }>, "many">>;
41
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
42
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
43
- foreground: z.ZodString;
44
- background: z.ZodString;
45
- ratio: z.ZodOptional<z.ZodNumber>;
46
- description: z.ZodOptional<z.ZodString>;
47
- backdrop: z.ZodOptional<z.ZodString>;
48
- }, "strip", z.ZodTypeAny, {
49
- foreground: string;
50
- background: string;
51
- description?: string | undefined;
52
- ratio?: number | undefined;
53
- backdrop?: string | undefined;
62
+ thresholds: z.ZodOptional<z.ZodArray<z.ZodObject<{
63
+ id: z.ZodString;
64
+ op: z.ZodEnum<["<=", ">="]>;
65
+ valuePx: z.ZodNumber;
66
+ where: z.ZodOptional<z.ZodString>;
67
+ level: z.ZodOptional<z.ZodEnum<["error", "warn"]>>;
68
+ }, "strict", z.ZodTypeAny, {
69
+ id: string;
70
+ op: "<=" | ">=";
71
+ valuePx: number;
72
+ where?: string | undefined;
73
+ level?: "error" | "warn" | undefined;
54
74
  }, {
55
- foreground: string;
56
- background: string;
57
- description?: string | undefined;
58
- ratio?: number | undefined;
59
- backdrop?: string | undefined;
75
+ id: string;
76
+ op: "<=" | ">=";
77
+ valuePx: number;
78
+ where?: string | undefined;
79
+ level?: "error" | "warn" | undefined;
60
80
  }>, "many">>;
61
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
62
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
63
- foreground: z.ZodString;
64
- background: z.ZodString;
65
- ratio: z.ZodOptional<z.ZodNumber>;
66
- description: z.ZodOptional<z.ZodString>;
67
- backdrop: z.ZodOptional<z.ZodString>;
68
- }, "strip", z.ZodTypeAny, {
81
+ }, "strict", z.ZodTypeAny, {
82
+ enableBuiltInWcagDefaults?: boolean | undefined;
83
+ enableBuiltInThreshold?: boolean | undefined;
84
+ wcag?: {
69
85
  foreground: string;
70
86
  background: string;
71
87
  description?: string | undefined;
72
88
  ratio?: number | undefined;
73
89
  backdrop?: string | undefined;
74
- }, {
90
+ }[] | undefined;
91
+ thresholds?: {
92
+ id: string;
93
+ op: "<=" | ">=";
94
+ valuePx: number;
95
+ where?: string | undefined;
96
+ level?: "error" | "warn" | undefined;
97
+ }[] | undefined;
98
+ }, {
99
+ enableBuiltInWcagDefaults?: boolean | undefined;
100
+ enableBuiltInThreshold?: boolean | undefined;
101
+ wcag?: {
75
102
  foreground: string;
76
103
  background: string;
77
104
  description?: string | undefined;
78
105
  ratio?: number | undefined;
79
106
  backdrop?: string | undefined;
80
- }>, "many">>;
81
- }, z.ZodTypeAny, "passthrough">>;
107
+ }[] | undefined;
108
+ thresholds?: {
109
+ id: string;
110
+ op: "<=" | ">=";
111
+ valuePx: number;
112
+ where?: string | undefined;
113
+ level?: "error" | "warn" | undefined;
114
+ }[] | undefined;
115
+ }>;
82
116
  export declare const DcvConfigSchema: z.ZodObject<{
117
+ $schema: z.ZodOptional<z.ZodString>;
83
118
  version: z.ZodOptional<z.ZodString>;
84
119
  constraints: z.ZodOptional<z.ZodObject<{
120
+ enableBuiltInWcagDefaults: z.ZodOptional<z.ZodBoolean>;
121
+ enableBuiltInThreshold: z.ZodOptional<z.ZodBoolean>;
85
122
  wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
86
123
  foreground: z.ZodString;
87
124
  background: z.ZodString;
88
125
  ratio: z.ZodOptional<z.ZodNumber>;
89
126
  description: z.ZodOptional<z.ZodString>;
90
127
  backdrop: z.ZodOptional<z.ZodString>;
91
- }, "strip", z.ZodTypeAny, {
128
+ }, "strict", z.ZodTypeAny, {
92
129
  foreground: string;
93
130
  background: string;
94
131
  description?: string | undefined;
@@ -101,174 +138,103 @@ export declare const DcvConfigSchema: z.ZodObject<{
101
138
  ratio?: number | undefined;
102
139
  backdrop?: string | undefined;
103
140
  }>, "many">>;
104
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
105
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
106
- foreground: z.ZodString;
107
- background: z.ZodString;
108
- ratio: z.ZodOptional<z.ZodNumber>;
109
- description: z.ZodOptional<z.ZodString>;
110
- backdrop: z.ZodOptional<z.ZodString>;
111
- }, "strip", z.ZodTypeAny, {
112
- foreground: string;
113
- background: string;
114
- description?: string | undefined;
115
- ratio?: number | undefined;
116
- backdrop?: string | undefined;
141
+ thresholds: z.ZodOptional<z.ZodArray<z.ZodObject<{
142
+ id: z.ZodString;
143
+ op: z.ZodEnum<["<=", ">="]>;
144
+ valuePx: z.ZodNumber;
145
+ where: z.ZodOptional<z.ZodString>;
146
+ level: z.ZodOptional<z.ZodEnum<["error", "warn"]>>;
147
+ }, "strict", z.ZodTypeAny, {
148
+ id: string;
149
+ op: "<=" | ">=";
150
+ valuePx: number;
151
+ where?: string | undefined;
152
+ level?: "error" | "warn" | undefined;
117
153
  }, {
118
- foreground: string;
119
- background: string;
120
- description?: string | undefined;
121
- ratio?: number | undefined;
122
- backdrop?: string | undefined;
154
+ id: string;
155
+ op: "<=" | ">=";
156
+ valuePx: number;
157
+ where?: string | undefined;
158
+ level?: "error" | "warn" | undefined;
123
159
  }>, "many">>;
124
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
125
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
126
- foreground: z.ZodString;
127
- background: z.ZodString;
128
- ratio: z.ZodOptional<z.ZodNumber>;
129
- description: z.ZodOptional<z.ZodString>;
130
- backdrop: z.ZodOptional<z.ZodString>;
131
- }, "strip", z.ZodTypeAny, {
160
+ }, "strict", z.ZodTypeAny, {
161
+ enableBuiltInWcagDefaults?: boolean | undefined;
162
+ enableBuiltInThreshold?: boolean | undefined;
163
+ wcag?: {
132
164
  foreground: string;
133
165
  background: string;
134
166
  description?: string | undefined;
135
167
  ratio?: number | undefined;
136
168
  backdrop?: string | undefined;
137
- }, {
138
- foreground: string;
139
- background: string;
140
- description?: string | undefined;
141
- ratio?: number | undefined;
142
- backdrop?: string | undefined;
143
- }>, "many">>;
144
- }, z.ZodTypeAny, "passthrough">>>;
145
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
146
- version: z.ZodOptional<z.ZodString>;
147
- constraints: z.ZodOptional<z.ZodObject<{
148
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
149
- foreground: z.ZodString;
150
- background: z.ZodString;
151
- ratio: z.ZodOptional<z.ZodNumber>;
152
- description: z.ZodOptional<z.ZodString>;
153
- backdrop: z.ZodOptional<z.ZodString>;
154
- }, "strip", z.ZodTypeAny, {
155
- foreground: string;
156
- background: string;
157
- description?: string | undefined;
158
- ratio?: number | undefined;
159
- backdrop?: string | undefined;
160
- }, {
161
- foreground: string;
162
- background: string;
163
- description?: string | undefined;
164
- ratio?: number | undefined;
165
- backdrop?: string | undefined;
166
- }>, "many">>;
167
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
168
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
169
- foreground: z.ZodString;
170
- background: z.ZodString;
171
- ratio: z.ZodOptional<z.ZodNumber>;
172
- description: z.ZodOptional<z.ZodString>;
173
- backdrop: z.ZodOptional<z.ZodString>;
174
- }, "strip", z.ZodTypeAny, {
175
- foreground: string;
176
- background: string;
177
- description?: string | undefined;
178
- ratio?: number | undefined;
179
- backdrop?: string | undefined;
180
- }, {
181
- foreground: string;
182
- background: string;
183
- description?: string | undefined;
184
- ratio?: number | undefined;
185
- backdrop?: string | undefined;
186
- }>, "many">>;
187
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
188
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
189
- foreground: z.ZodString;
190
- background: z.ZodString;
191
- ratio: z.ZodOptional<z.ZodNumber>;
192
- description: z.ZodOptional<z.ZodString>;
193
- backdrop: z.ZodOptional<z.ZodString>;
194
- }, "strip", z.ZodTypeAny, {
195
- foreground: string;
196
- background: string;
197
- description?: string | undefined;
198
- ratio?: number | undefined;
199
- backdrop?: string | undefined;
200
- }, {
201
- foreground: string;
202
- background: string;
203
- description?: string | undefined;
204
- ratio?: number | undefined;
205
- backdrop?: string | undefined;
206
- }>, "many">>;
207
- }, z.ZodTypeAny, "passthrough">>>;
208
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
209
- version: z.ZodOptional<z.ZodString>;
210
- constraints: z.ZodOptional<z.ZodObject<{
211
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
212
- foreground: z.ZodString;
213
- background: z.ZodString;
214
- ratio: z.ZodOptional<z.ZodNumber>;
215
- description: z.ZodOptional<z.ZodString>;
216
- backdrop: z.ZodOptional<z.ZodString>;
217
- }, "strip", z.ZodTypeAny, {
218
- foreground: string;
219
- background: string;
220
- description?: string | undefined;
221
- ratio?: number | undefined;
222
- backdrop?: string | undefined;
223
- }, {
224
- foreground: string;
225
- background: string;
226
- description?: string | undefined;
227
- ratio?: number | undefined;
228
- backdrop?: string | undefined;
229
- }>, "many">>;
230
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
231
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
232
- foreground: z.ZodString;
233
- background: z.ZodString;
234
- ratio: z.ZodOptional<z.ZodNumber>;
235
- description: z.ZodOptional<z.ZodString>;
236
- backdrop: z.ZodOptional<z.ZodString>;
237
- }, "strip", z.ZodTypeAny, {
169
+ }[] | undefined;
170
+ thresholds?: {
171
+ id: string;
172
+ op: "<=" | ">=";
173
+ valuePx: number;
174
+ where?: string | undefined;
175
+ level?: "error" | "warn" | undefined;
176
+ }[] | undefined;
177
+ }, {
178
+ enableBuiltInWcagDefaults?: boolean | undefined;
179
+ enableBuiltInThreshold?: boolean | undefined;
180
+ wcag?: {
238
181
  foreground: string;
239
182
  background: string;
240
183
  description?: string | undefined;
241
184
  ratio?: number | undefined;
242
185
  backdrop?: string | undefined;
243
- }, {
186
+ }[] | undefined;
187
+ thresholds?: {
188
+ id: string;
189
+ op: "<=" | ">=";
190
+ valuePx: number;
191
+ where?: string | undefined;
192
+ level?: "error" | "warn" | undefined;
193
+ }[] | undefined;
194
+ }>>;
195
+ }, "strict", z.ZodTypeAny, {
196
+ $schema?: string | undefined;
197
+ version?: string | undefined;
198
+ constraints?: {
199
+ enableBuiltInWcagDefaults?: boolean | undefined;
200
+ enableBuiltInThreshold?: boolean | undefined;
201
+ wcag?: {
244
202
  foreground: string;
245
203
  background: string;
246
204
  description?: string | undefined;
247
205
  ratio?: number | undefined;
248
206
  backdrop?: string | undefined;
249
- }>, "many">>;
250
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
251
- wcag: z.ZodOptional<z.ZodArray<z.ZodObject<{
252
- foreground: z.ZodString;
253
- background: z.ZodString;
254
- ratio: z.ZodOptional<z.ZodNumber>;
255
- description: z.ZodOptional<z.ZodString>;
256
- backdrop: z.ZodOptional<z.ZodString>;
257
- }, "strip", z.ZodTypeAny, {
258
- foreground: string;
259
- background: string;
260
- description?: string | undefined;
261
- ratio?: number | undefined;
262
- backdrop?: string | undefined;
263
- }, {
207
+ }[] | undefined;
208
+ thresholds?: {
209
+ id: string;
210
+ op: "<=" | ">=";
211
+ valuePx: number;
212
+ where?: string | undefined;
213
+ level?: "error" | "warn" | undefined;
214
+ }[] | undefined;
215
+ } | undefined;
216
+ }, {
217
+ $schema?: string | undefined;
218
+ version?: string | undefined;
219
+ constraints?: {
220
+ enableBuiltInWcagDefaults?: boolean | undefined;
221
+ enableBuiltInThreshold?: boolean | undefined;
222
+ wcag?: {
264
223
  foreground: string;
265
224
  background: string;
266
225
  description?: string | undefined;
267
226
  ratio?: number | undefined;
268
227
  backdrop?: string | undefined;
269
- }>, "many">>;
270
- }, z.ZodTypeAny, "passthrough">>>;
271
- }, z.ZodTypeAny, "passthrough">>;
228
+ }[] | undefined;
229
+ thresholds?: {
230
+ id: string;
231
+ op: "<=" | ">=";
232
+ valuePx: number;
233
+ where?: string | undefined;
234
+ level?: "error" | "warn" | undefined;
235
+ }[] | undefined;
236
+ } | undefined;
237
+ }>;
272
238
  export type DcvConfigParsed = z.infer<typeof DcvConfigSchema>;
273
239
  export declare function validateConfig(raw: unknown): {
274
240
  value?: DcvConfigParsed;
@@ -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;;;;;;;;;;;;;;;;;;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"}
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAMhB,CAAC;AAEZ,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;EAMrB,CAAC;AAEZ,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKnB,CAAC;AAEZ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMjB,CAAC;AAEZ,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"}
@@ -1,18 +1,38 @@
1
1
  import { z } from 'zod';
2
+ // Schemas are `.strict()` on purpose (TASK-037): a validator must never silently
3
+ // accept a typo'd key, because "the rule never ran" is indistinguishable from
4
+ // "the rule passed" — a false green. A misspelled block (`wcagg`), a typo'd
5
+ // field (`levle`), or an out-of-range bound is rejected loudly, not dropped.
6
+ // WCAG contrast ratios are bounded by the spec to (1:1, 21:1]: 1 means "no
7
+ // contrast required" (a no-op rule) and 21 is the maximum achievable (#000 on
8
+ // #fff). Anything outside that is a config error, not a stricter policy.
2
9
  export const WcagRuleSchema = z.object({
3
10
  foreground: z.string(),
4
11
  background: z.string(),
5
- ratio: z.number().positive().optional(),
12
+ ratio: z.number().finite().gt(1).max(21).optional(),
6
13
  description: z.string().optional(),
7
14
  backdrop: z.string().optional()
8
- });
15
+ }).strict();
16
+ export const ThresholdRuleSchema = z.object({
17
+ id: z.string(),
18
+ op: z.enum(['<=', '>=']),
19
+ valuePx: z.number().finite().nonnegative(),
20
+ where: z.string().optional(),
21
+ level: z.enum(['error', 'warn']).optional()
22
+ }).strict();
9
23
  export const ConstraintsSchema = z.object({
10
- wcag: z.array(WcagRuleSchema).optional()
11
- }).passthrough();
24
+ enableBuiltInWcagDefaults: z.boolean().optional(),
25
+ enableBuiltInThreshold: z.boolean().optional(),
26
+ wcag: z.array(WcagRuleSchema).optional(),
27
+ thresholds: z.array(ThresholdRuleSchema).optional()
28
+ }).strict();
12
29
  export const DcvConfigSchema = z.object({
30
+ // `$schema` is the conventional editor-hint key; allow it so strictness does
31
+ // not punish a well-formed config, but still reject unknown top-level typos.
32
+ $schema: z.string().optional(),
13
33
  version: z.string().optional(),
14
34
  constraints: ConstraintsSchema.optional()
15
- }).passthrough();
35
+ }).strict();
16
36
  export function validateConfig(raw) {
17
37
  const res = DcvConfigSchema.safeParse(raw);
18
38
  if (!res.success) {
@@ -1,21 +1,43 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ // Schemas are `.strict()` on purpose (TASK-037): a validator must never silently
4
+ // accept a typo'd key, because "the rule never ran" is indistinguishable from
5
+ // "the rule passed" — a false green. A misspelled block (`wcagg`), a typo'd
6
+ // field (`levle`), or an out-of-range bound is rejected loudly, not dropped.
7
+
8
+ // WCAG contrast ratios are bounded by the spec to (1:1, 21:1]: 1 means "no
9
+ // contrast required" (a no-op rule) and 21 is the maximum achievable (#000 on
10
+ // #fff). Anything outside that is a config error, not a stricter policy.
3
11
  export const WcagRuleSchema = z.object({
4
12
  foreground: z.string(),
5
13
  background: z.string(),
6
- ratio: z.number().positive().optional(),
14
+ ratio: z.number().finite().gt(1).max(21).optional(),
7
15
  description: z.string().optional(),
8
16
  backdrop: z.string().optional()
9
- });
17
+ }).strict();
18
+
19
+ export const ThresholdRuleSchema = z.object({
20
+ id: z.string(),
21
+ op: z.enum(['<=', '>=']),
22
+ valuePx: z.number().finite().nonnegative(),
23
+ where: z.string().optional(),
24
+ level: z.enum(['error', 'warn']).optional()
25
+ }).strict();
10
26
 
11
27
  export const ConstraintsSchema = z.object({
12
- wcag: z.array(WcagRuleSchema).optional()
13
- }).passthrough();
28
+ enableBuiltInWcagDefaults: z.boolean().optional(),
29
+ enableBuiltInThreshold: z.boolean().optional(),
30
+ wcag: z.array(WcagRuleSchema).optional(),
31
+ thresholds: z.array(ThresholdRuleSchema).optional()
32
+ }).strict();
14
33
 
15
34
  export const DcvConfigSchema = z.object({
35
+ // `$schema` is the conventional editor-hint key; allow it so strictness does
36
+ // not punish a well-formed config, but still reject unknown top-level typos.
37
+ $schema: z.string().optional(),
16
38
  version: z.string().optional(),
17
39
  constraints: ConstraintsSchema.optional()
18
- }).passthrough();
40
+ }).strict();
19
41
 
20
42
  export type DcvConfigParsed = z.infer<typeof DcvConfigSchema>;
21
43
 
@@ -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;AAIF,eAAO,MAAM,kBAAkB,EAAE,QAAQ,EAIxC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,aAAa,EAE7C,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,CAyDxG;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,gBAAgB,EAC/B,UAAU,EAAE,aAAa,GACxB,gBAAgB,EAAE,CAIpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG;IAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAyC9G"}
1
+ {"version":3,"file":"constraint-registry.d.ts","sourceRoot":"","sources":["constraint-registry.ts"],"names":[],"mappings":"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"}
@@ -11,13 +11,22 @@
11
11
  * - All entry points (validate, set, graph) use this registry for consistency
12
12
  */
13
13
  import { existsSync, readFileSync } from 'node:fs';
14
- import { join } from 'node:path';
14
+ import { isAbsolute, join } from 'node:path';
15
15
  import { MonotonicPlugin, parseSize as parseSizePx } from '../core/constraints/monotonic.js';
16
16
  import { MonotonicLightness } from '../core/constraints/monotonic-lightness.js';
17
17
  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
- import { loadCrossAxisRules } from './cross-axis-loader.js';
20
+ import { loadCrossAxisRulesDetailed, referencedIdsForFile } from './cross-axis-loader.js';
21
+ /**
22
+ * A plugin that emits a fixed set of pre-computed issues unconditionally (it
23
+ * ignores the candidate set). Used to surface cross-axis loader notices —
24
+ * present-but-unusable files and skipped invalid rules (TASK-037) — through the
25
+ * normal issue channel so they appear as warnings instead of vanishing.
26
+ */
27
+ function noticePlugin(notices) {
28
+ return { id: 'cross-axis-notices', evaluate: () => notices };
29
+ }
21
30
  // Built-in default constraint rules. Shared by attachConstraints (to register
22
31
  // plugins) and collectReferencedIds (to compute coverage) so the two never drift.
23
32
  export const DEFAULT_WCAG_PAIRS = [
@@ -44,6 +53,7 @@ export function discoverConstraints(opts) {
44
53
  const { config, basePath = '.', bp, constraintsDir = 'themes' } = opts;
45
54
  const sources = [];
46
55
  const constraintsCfg = config.constraints ?? {};
56
+ const constraintsRoot = isAbsolute(constraintsDir) ? constraintsDir : join(basePath, constraintsDir);
47
57
  // 1. Built-in WCAG defaults
48
58
  const enableBuiltInWcag = constraintsCfg.enableBuiltInWcagDefaults === undefined ? true : !!constraintsCfg.enableBuiltInWcagDefaults;
49
59
  if (enableBuiltInWcag) {
@@ -72,15 +82,28 @@ export function discoverConstraints(opts) {
72
82
  op: r.op,
73
83
  valuePx: r.valuePx,
74
84
  where: r.where,
85
+ // Preserve configured severity (TASK-037): the core plugin honors `level`,
86
+ // but dropping it here silently promoted a `warn` threshold to an error.
87
+ level: r.level,
75
88
  }));
76
89
  sources.push({ type: 'custom-threshold', rules });
77
90
  }
91
+ // A breakpoint uses its own `<axis>.<bp>.order.json` when present, but MUST fall
92
+ // back to the global `<axis>.order.json` otherwise — without this, any axis
93
+ // lacking a per-bp file (e.g. spacing) contributed ZERO constraints under
94
+ // --breakpoint/--all-breakpoints, a silent false-pass (TASK-034).
95
+ const orderPathFor = (base) => {
96
+ const bpPath = bp ? join(constraintsRoot, `${base}.${bp}.order.json`) : undefined;
97
+ if (bpPath && existsSync(bpPath))
98
+ return bpPath;
99
+ const globalPath = join(constraintsRoot, `${base}.order.json`);
100
+ return existsSync(globalPath) ? globalPath : undefined;
101
+ };
78
102
  // 5. Order files (monotonic constraints)
79
103
  const axes = ['typography', 'spacing', 'layout'];
80
104
  for (const axis of axes) {
81
- const suffix = bp ? `.${bp}` : '';
82
- const orderPath = join(basePath, constraintsDir, `${axis}${suffix}.order.json`);
83
- if (existsSync(orderPath)) {
105
+ const orderPath = orderPathFor(axis);
106
+ if (orderPath) {
84
107
  try {
85
108
  const data = JSON.parse(readFileSync(orderPath, 'utf8'));
86
109
  const orders = data.order || [];
@@ -93,10 +116,9 @@ export function discoverConstraints(opts) {
93
116
  }
94
117
  }
95
118
  }
96
- // 6. Color lightness order files
97
- const suffix = bp ? `.${bp}` : '';
98
- const colorOrderPath = join(basePath, constraintsDir, `color${suffix}.order.json`);
99
- if (existsSync(colorOrderPath)) {
119
+ // 6. Color lightness order files (same bp → global fallback)
120
+ const colorOrderPath = orderPathFor('color');
121
+ if (colorOrderPath) {
100
122
  try {
101
123
  const data = JSON.parse(readFileSync(colorOrderPath, 'utf8'));
102
124
  const orders = data.order || [];
@@ -109,13 +131,13 @@ export function discoverConstraints(opts) {
109
131
  }
110
132
  }
111
133
  // 7. Cross-axis rules (global)
112
- const crossAxisPath = join(basePath, constraintsDir, 'cross-axis.rules.json');
134
+ const crossAxisPath = join(constraintsRoot, 'cross-axis.rules.json');
113
135
  if (existsSync(crossAxisPath)) {
114
136
  sources.push({ type: 'cross-axis-file', path: crossAxisPath });
115
137
  }
116
138
  // 8. Cross-axis rules (breakpoint-specific)
117
139
  if (bp) {
118
- const crossAxisBpPath = join(basePath, constraintsDir, `cross-axis.${bp}.rules.json`);
140
+ const crossAxisBpPath = join(constraintsRoot, `cross-axis.${bp}.rules.json`);
119
141
  if (existsSync(crossAxisBpPath)) {
120
142
  sources.push({ type: 'cross-axis-file', path: crossAxisBpPath, bp });
121
143
  }
@@ -170,13 +192,17 @@ export function attachConstraints(engine, sources, opts) {
170
192
  break;
171
193
  }
172
194
  case 'cross-axis-file': {
173
- // Phase 3B: Load rules from filesystem in CLI layer, pass to core plugin
174
- const rules = loadCrossAxisRules(source.path, {
195
+ // Phase 3B: Load rules from filesystem in CLI layer, pass to core plugin.
196
+ // Detailed load also surfaces notices (unusable file / skipped invalid
197
+ // rules) as warnings so they are never silently dropped (TASK-037).
198
+ const { rules, notices } = loadCrossAxisRulesDetailed(source.path, {
175
199
  bp: source.bp,
176
200
  knownIds,
177
201
  debug: crossAxisDebug,
178
202
  });
179
203
  engine.use(CrossAxisPlugin(rules, source.bp));
204
+ if (notices.length)
205
+ engine.use(noticePlugin(notices));
180
206
  break;
181
207
  }
182
208
  }
@@ -249,9 +275,21 @@ export function collectReferencedIds(sources) {
249
275
  case 'lightness-file':
250
276
  addOrders(source.orders);
251
277
  break;
252
- case 'cross-axis-file':
253
- coverageKnown = false;
278
+ case 'cross-axis-file': {
279
+ // TASK-031/037: enumerate referenced ids so coverage stays KNOWN, but
280
+ // MIRROR attach exactly — same `source.bp` filter and same shape
281
+ // validation. A rule that did not run (wrong breakpoint, or invalid)
282
+ // must not make coverage look "matched" and suppress the no-match note.
283
+ // An unusable file stays conservative (coverageKnown=false).
284
+ const { ids: caIds, coverageKnown: known } = referencedIdsForFile(source.path, source.bp);
285
+ if (!known) {
286
+ coverageKnown = false;
287
+ break;
288
+ }
289
+ for (const id of caIds)
290
+ ids.add(id);
254
291
  break;
292
+ }
255
293
  }
256
294
  }
257
295
  return { ids, coverageKnown };