design-constraint-validator 1.0.0 → 1.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 (116) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +215 -659
  3. package/adapters/README.md +46 -46
  4. package/adapters/css.ts +116 -116
  5. package/adapters/js.ts +14 -14
  6. package/adapters/json.ts +45 -45
  7. package/cli/build-css.ts +32 -32
  8. package/cli/commands/build.ts +65 -65
  9. package/cli/commands/graph.d.ts.map +1 -1
  10. package/cli/commands/graph.js +26 -10
  11. package/cli/commands/graph.ts +180 -137
  12. package/cli/commands/index.ts +7 -7
  13. package/cli/commands/patch-apply.ts +80 -80
  14. package/cli/commands/patch.ts +22 -22
  15. package/cli/commands/set.d.ts.map +1 -1
  16. package/cli/commands/set.js +12 -4
  17. package/cli/commands/set.ts +239 -225
  18. package/cli/commands/utils.ts +50 -50
  19. package/cli/commands/validate.d.ts.map +1 -1
  20. package/cli/commands/validate.js +86 -33
  21. package/cli/commands/validate.ts +176 -115
  22. package/cli/commands/why.d.ts.map +1 -1
  23. package/cli/commands/why.js +86 -20
  24. package/cli/commands/why.ts +158 -46
  25. package/cli/config-schema.ts +27 -27
  26. package/cli/config.ts +35 -35
  27. package/cli/constraint-registry.d.ts +101 -0
  28. package/cli/constraint-registry.d.ts.map +1 -0
  29. package/cli/constraint-registry.js +225 -0
  30. package/cli/constraint-registry.ts +304 -0
  31. package/cli/constraints-loader.d.ts +30 -0
  32. package/cli/constraints-loader.d.ts.map +1 -0
  33. package/cli/constraints-loader.js +58 -0
  34. package/cli/constraints-loader.ts +83 -0
  35. package/cli/cross-axis-loader.d.ts +91 -0
  36. package/cli/cross-axis-loader.d.ts.map +1 -0
  37. package/cli/cross-axis-loader.js +222 -0
  38. package/cli/cross-axis-loader.ts +289 -0
  39. package/cli/dcv.js +4 -0
  40. package/cli/dcv.ts +111 -107
  41. package/cli/engine-helpers.d.ts +33 -0
  42. package/cli/engine-helpers.d.ts.map +1 -1
  43. package/cli/engine-helpers.js +87 -22
  44. package/cli/engine-helpers.ts +133 -61
  45. package/cli/graph-poset.ts +74 -74
  46. package/cli/json-output.d.ts +64 -0
  47. package/cli/json-output.d.ts.map +1 -0
  48. package/cli/json-output.js +107 -0
  49. package/cli/json-output.ts +177 -0
  50. package/cli/result.ts +27 -27
  51. package/cli/run.ts +54 -54
  52. package/cli/smoke-test.ts +40 -40
  53. package/cli/types.d.ts +6 -0
  54. package/cli/types.d.ts.map +1 -1
  55. package/cli/types.ts +84 -78
  56. package/core/breakpoints.ts +50 -50
  57. package/core/cli-format.ts +31 -31
  58. package/core/color.ts +148 -148
  59. package/core/constraints/cross-axis.ts +114 -114
  60. package/core/constraints/monotonic-lightness.ts +38 -38
  61. package/core/constraints/monotonic.ts +74 -74
  62. package/core/constraints/threshold.ts +43 -43
  63. package/core/constraints/wcag.ts +70 -70
  64. package/core/cross-axis-config.d.ts +29 -0
  65. package/core/cross-axis-config.d.ts.map +1 -1
  66. package/core/cross-axis-config.js +29 -0
  67. package/core/cross-axis-config.ts +181 -151
  68. package/core/engine.d.ts +95 -0
  69. package/core/engine.d.ts.map +1 -1
  70. package/core/engine.js +22 -0
  71. package/core/engine.ts +167 -65
  72. package/core/flatten.ts +116 -116
  73. package/core/image-export.ts +48 -48
  74. package/core/index.d.ts +9 -30
  75. package/core/index.d.ts.map +1 -1
  76. package/core/index.js +7 -54
  77. package/core/index.ts +10 -72
  78. package/core/patch.ts +134 -134
  79. package/core/poset.ts +311 -311
  80. package/core/why.ts +63 -63
  81. package/package.json +96 -90
  82. package/themes/color.lg.order.json +15 -15
  83. package/themes/color.md.order.json +15 -15
  84. package/themes/color.order.json +15 -15
  85. package/themes/color.sm.order.json +15 -15
  86. package/themes/cross-axis.rules.json +35 -35
  87. package/themes/cross-axis.sm.rules.json +12 -12
  88. package/themes/layout.lg.order.json +18 -18
  89. package/themes/layout.md.order.json +18 -18
  90. package/themes/layout.order.json +18 -18
  91. package/themes/layout.sm.order.json +18 -18
  92. package/themes/spacing.order.json +14 -14
  93. package/themes/typography.lg.order.json +15 -15
  94. package/themes/typography.md.order.json +15 -15
  95. package/themes/typography.order.json +15 -15
  96. package/themes/typography.sm.order.json +15 -15
  97. package/dist/test-overrides-removal.json +0 -4
  98. package/dist/tmp.patch.json +0 -35
  99. package/tokens/overrides/base.json +0 -22
  100. package/tokens/overrides/lg.json +0 -20
  101. package/tokens/overrides/md.json +0 -16
  102. package/tokens/overrides/sm.json +0 -16
  103. package/tokens/overrides/viol.color.json +0 -6
  104. package/tokens/overrides/viol.typography.json +0 -6
  105. package/tokens/tokens.demo-violations.json +0 -116
  106. package/tokens/tokens.example.json +0 -128
  107. package/tokens/tokens.json +0 -67
  108. package/tokens/tokens.multi-violations.json +0 -21
  109. package/tokens/tokens.schema.d.ts +0 -2298
  110. package/tokens/tokens.schema.d.ts.map +0 -1
  111. package/tokens/tokens.schema.js +0 -148
  112. package/tokens/tokens.schema.ts +0 -196
  113. package/tokens/tokens.test.json +0 -38
  114. package/tokens/tokens.touch-violation.json +0 -8
  115. package/tokens/typography.classes.css +0 -11
  116. package/tokens/typography.css +0 -20
package/cli/dcv.js CHANGED
@@ -47,7 +47,11 @@ cli.command('build', 'Build token outputs', y => y
47
47
  cli.command('validate', 'Validate constraints', y => y
48
48
  .option('fail-on', { type: 'string', choices: ['off', 'warn', 'error'], default: 'error' })
49
49
  .option('summary', { type: 'string', choices: ['none', 'table', 'json'], default: 'none' })
50
+ .option('format', { type: 'string', choices: ['text', 'json'], default: 'text', describe: 'Output format' })
51
+ .option('output', { type: 'string', describe: 'Write JSON output to file' })
52
+ .option('receipt', { type: 'string', describe: 'Generate validation receipt with audit trail' })
50
53
  .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
54
+ .option('theme', { type: 'string', describe: 'Apply named theme tokens before validation' })
51
55
  .option('breakpoint', { type: 'string' })
52
56
  .option('all-breakpoints', { type: 'boolean' })
53
57
  .option('perf', { type: 'boolean', describe: 'Print timing info' })
package/cli/dcv.ts CHANGED
@@ -1,107 +1,111 @@
1
- #!/usr/bin/env node
2
- // Clean minimal DCV CLI entrypoint
3
- import yargs from 'yargs/yargs';
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
- import { type SetOptions, type BuildOptions, type ValidateOptions, type GraphOptions, type WhyOptions, type PatchOptions, type PatchApplyOptions } from './types.js';
9
- import { setCommand, buildCommand, validateCommand, graphCommand, whyCommand, patchCommand, patchApplyCommand } from './commands/index.js';
10
-
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
- const cli = yargs(hideBin(process.argv))
24
- .scriptName('dcv')
25
- .parserConfiguration({ 'camel-case-expansion': false })
26
- .option('quiet', { type: 'boolean' });
27
-
28
- cli.command<SetOptions>('set <expressions..>', 'Set token values', y => y
29
- .positional('expressions', { type: 'string', array: true })
30
- .option('dry-run', { type: 'boolean', default: false })
31
- .option('write', { type: 'boolean' })
32
- .option('json', { type: 'string' })
33
- .option('unset', { type: 'array', string: true })
34
- .option('format', { type: 'string', choices: ['json','css','js'], default: 'json' })
35
- .option('output', { type: 'string' })
36
- .option('theme', { type: 'string' })
37
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
38
- a => setCommand(a)
39
- );
40
-
41
- cli.command<BuildOptions>('build', 'Build token outputs', y => y
42
- .option('format', { type: 'string', choices: ['css','json','js'], default: 'css' })
43
- .option('all-formats', { type: 'boolean', default: false })
44
- .option('output', { type: 'string' })
45
- .option('mapper', { type: 'string' })
46
- .option('theme', { type: 'string' })
47
- .option('dry-run', { type: 'boolean', default: false })
48
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
49
- a => buildCommand(a)
50
- );
51
-
52
- cli.command<ValidateOptions>('validate', 'Validate constraints', y => y
53
- .option('fail-on', { type: 'string', choices: ['off','warn','error'], default: 'error' })
54
- .option('summary', { type: 'string', choices: ['none','table','json'], default: 'none' })
55
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
56
- .option('breakpoint', { type: 'string' })
57
- .option('all-breakpoints', { type: 'boolean' })
58
- .option('perf', { type: 'boolean', describe: 'Print timing info' })
59
- .option('budget-total-ms', { type: 'number', describe: 'Fail if total validation exceeds this (ms)' })
60
- .option('budget-per-bp-ms', { type: 'number', describe: 'Fail if any single breakpoint exceeds this (ms)' }),
61
- a => validateCommand(a)
62
- );
63
-
64
- cli.command<GraphOptions>('graph', 'Generate dependency / constraint graph', y => y
65
- .option('format', { type: 'string', choices: ['json','mermaid','dot','svg','png'], default: 'json' })
66
- .option('bundle', { type: 'boolean', describe: 'When used with --hasse export mermaid+dot (+image if svg/png requested)' })
67
- .option('hasse', { type: 'string' })
68
- .option('filter-prefix', { type: 'string' })
69
- .option('exclude-prefix', { type: 'string' })
70
- .option('only-violations', { type: 'boolean' })
71
- .option('highlight-violations', { type: 'boolean' })
72
- .option('label-violations', { type: 'boolean' })
73
- .option('label-truncate', { type: 'number', default: 0 })
74
- .option('min-severity', { type: 'string', choices: ['warn','error'], default: 'warn' })
75
- .option('focus', { type: 'string' })
76
- .option('radius', { type: 'number', default: 1 })
77
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
78
- a => graphCommand(a)
79
- );
80
-
81
- cli.command<WhyOptions>('why <tokenId>', 'Explain token provenance', y => y
82
- .positional('tokenId', { type: 'string', demandOption: true })
83
- .option('format', { type: 'string', choices: ['json','table'], default: 'json' })
84
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
85
- a => whyCommand(a)
86
- );
87
-
88
- cli.command<PatchOptions>('patch', 'Export patch (diff) from overrides', y => y
89
- .option('overrides', { type: 'string', describe: 'Path or inline JSON of flat overrides' })
90
- .option('format', { type: 'string', choices: ['json','css','js'], default: 'json' })
91
- .option('output', { type: 'string' })
92
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
93
- a => patchCommand(a)
94
- );
95
-
96
- cli.command<PatchApplyOptions>('patch:apply <patch>', 'Apply patch document to tokens', y => y
97
- .positional('patch', { type: 'string', demandOption: true })
98
- .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
99
- .option('output', { type: 'string', describe: 'Write updated tokens to this file' })
100
- .option('dry-run', { type: 'boolean', default: false }),
101
- a => patchApplyCommand(a)
102
- );
103
-
104
- cli.help().alias('h','help').strict().wrap(cli.terminalWidth());
105
- cli.parse();
106
-
107
- // EOF
1
+ #!/usr/bin/env node
2
+ // Clean minimal DCV CLI entrypoint
3
+ import yargs from 'yargs/yargs';
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
+ import { type SetOptions, type BuildOptions, type ValidateOptions, type GraphOptions, type WhyOptions, type PatchOptions, type PatchApplyOptions } from './types.js';
9
+ import { setCommand, buildCommand, validateCommand, graphCommand, whyCommand, patchCommand, patchApplyCommand } from './commands/index.js';
10
+
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
+ const cli = yargs(hideBin(process.argv))
24
+ .scriptName('dcv')
25
+ .parserConfiguration({ 'camel-case-expansion': false })
26
+ .option('quiet', { type: 'boolean' });
27
+
28
+ cli.command<SetOptions>('set <expressions..>', 'Set token values', y => y
29
+ .positional('expressions', { type: 'string', array: true })
30
+ .option('dry-run', { type: 'boolean', default: false })
31
+ .option('write', { type: 'boolean' })
32
+ .option('json', { type: 'string' })
33
+ .option('unset', { type: 'array', string: true })
34
+ .option('format', { type: 'string', choices: ['json','css','js'], default: 'json' })
35
+ .option('output', { type: 'string' })
36
+ .option('theme', { type: 'string' })
37
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
38
+ a => setCommand(a)
39
+ );
40
+
41
+ cli.command<BuildOptions>('build', 'Build token outputs', y => y
42
+ .option('format', { type: 'string', choices: ['css','json','js'], default: 'css' })
43
+ .option('all-formats', { type: 'boolean', default: false })
44
+ .option('output', { type: 'string' })
45
+ .option('mapper', { type: 'string' })
46
+ .option('theme', { type: 'string' })
47
+ .option('dry-run', { type: 'boolean', default: false })
48
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
49
+ a => buildCommand(a)
50
+ );
51
+
52
+ cli.command<ValidateOptions>('validate', 'Validate constraints', y => y
53
+ .option('fail-on', { type: 'string', choices: ['off','warn','error'], default: 'error' })
54
+ .option('summary', { type: 'string', choices: ['none','table','json'], default: 'none' })
55
+ .option('format', { type: 'string', choices: ['text','json'], default: 'text', describe: 'Output format' })
56
+ .option('output', { type: 'string', describe: 'Write JSON output to file' })
57
+ .option('receipt', { type: 'string', describe: 'Generate validation receipt with audit trail' })
58
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
59
+ .option('theme', { type: 'string', describe: 'Apply named theme tokens before validation' })
60
+ .option('breakpoint', { type: 'string' })
61
+ .option('all-breakpoints', { type: 'boolean' })
62
+ .option('perf', { type: 'boolean', describe: 'Print timing info' })
63
+ .option('budget-total-ms', { type: 'number', describe: 'Fail if total validation exceeds this (ms)' })
64
+ .option('budget-per-bp-ms', { type: 'number', describe: 'Fail if any single breakpoint exceeds this (ms)' }),
65
+ a => validateCommand(a)
66
+ );
67
+
68
+ cli.command<GraphOptions>('graph', 'Generate dependency / constraint graph', y => y
69
+ .option('format', { type: 'string', choices: ['json','mermaid','dot','svg','png'], default: 'json' })
70
+ .option('bundle', { type: 'boolean', describe: 'When used with --hasse export mermaid+dot (+image if svg/png requested)' })
71
+ .option('hasse', { type: 'string' })
72
+ .option('filter-prefix', { type: 'string' })
73
+ .option('exclude-prefix', { type: 'string' })
74
+ .option('only-violations', { type: 'boolean' })
75
+ .option('highlight-violations', { type: 'boolean' })
76
+ .option('label-violations', { type: 'boolean' })
77
+ .option('label-truncate', { type: 'number', default: 0 })
78
+ .option('min-severity', { type: 'string', choices: ['warn','error'], default: 'warn' })
79
+ .option('focus', { type: 'string' })
80
+ .option('radius', { type: 'number', default: 1 })
81
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
82
+ a => graphCommand(a)
83
+ );
84
+
85
+ cli.command<WhyOptions>('why <tokenId>', 'Explain token provenance', y => y
86
+ .positional('tokenId', { type: 'string', demandOption: true })
87
+ .option('format', { type: 'string', choices: ['json','table'], default: 'json' })
88
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
89
+ a => whyCommand(a)
90
+ );
91
+
92
+ cli.command<PatchOptions>('patch', 'Export patch (diff) from overrides', y => y
93
+ .option('overrides', { type: 'string', describe: 'Path or inline JSON of flat overrides' })
94
+ .option('format', { type: 'string', choices: ['json','css','js'], default: 'json' })
95
+ .option('output', { type: 'string' })
96
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' }),
97
+ a => patchCommand(a)
98
+ );
99
+
100
+ cli.command<PatchApplyOptions>('patch:apply <patch>', 'Apply patch document to tokens', y => y
101
+ .positional('patch', { type: 'string', demandOption: true })
102
+ .option('tokens', { type: 'string', default: 'tokens/tokens.example.json' })
103
+ .option('output', { type: 'string', describe: 'Write updated tokens to this file' })
104
+ .option('dry-run', { type: 'boolean', default: false }),
105
+ a => patchApplyCommand(a)
106
+ );
107
+
108
+ cli.help().alias('h','help').strict().wrap(cli.terminalWidth());
109
+ cli.parse();
110
+
111
+ // EOF
@@ -1,8 +1,41 @@
1
+ /**
2
+ * @deprecated This module is deprecated. Use constraint-registry.ts instead.
3
+ *
4
+ * Phase 3A (Architectural Cleanup): This file contains legacy constraint loading logic
5
+ * that has been replaced by the centralized constraint-registry.ts module.
6
+ *
7
+ * Migration guide:
8
+ * - Replace createEngine() or createValidationEngine() with:
9
+ * ```ts
10
+ * import { Engine } from '../core/engine.js';
11
+ * import { flattenTokens, type FlatToken } from '../core/flatten.js';
12
+ * import { setupConstraints } from './constraint-registry.js';
13
+ *
14
+ * const { flat, edges } = flattenTokens(tokens);
15
+ * const init = {};
16
+ * for (const t of Object.values(flat)) {
17
+ * init[(t as FlatToken).id] = (t as FlatToken).value;
18
+ * }
19
+ * const engine = new Engine(init, edges);
20
+ * const knownIds = new Set(Object.keys(init));
21
+ * setupConstraints(engine, { config, bp }, { knownIds });
22
+ * ```
23
+ *
24
+ * This file will be removed in a future major version.
25
+ */
1
26
  import { type TokenNode } from '../core/flatten.js';
2
27
  import { Engine } from '../core/engine.js';
3
28
  import { loadTokensWithBreakpoint, type Breakpoint } from '../core/breakpoints.js';
4
29
  import type { DcvConfig } from './types.js';
30
+ /**
31
+ * @deprecated Use constraint-registry.ts setupConstraints() instead.
32
+ * This function will be removed in a future major version.
33
+ */
5
34
  export declare function createEngine(tokensRoot: TokenNode, config?: DcvConfig): Engine;
35
+ /**
36
+ * @deprecated Use constraint-registry.ts setupConstraints() instead.
37
+ * This function will be removed in a future major version.
38
+ */
6
39
  export declare function createValidationEngine(tokensRoot: TokenNode, bp: Breakpoint | undefined, config: DcvConfig): Engine;
7
40
  export { loadTokensWithBreakpoint };
8
41
  //# sourceMappingURL=engine-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine-helpers.d.ts","sourceRoot":"","sources":["engine-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,SAAS,EAAkB,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAI3C,OAAO,EAA8B,wBAAwB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC/G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,YAAY,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,GAAE,SAAc,GAAG,MAAM,CA8BlF;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,CAkBnH;AAED,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
1
+ {"version":3,"file":"engine-helpers.d.ts","sourceRoot":"","sources":["engine-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAiB,KAAK,SAAS,EAAkB,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAI3C,OAAO,EAA8B,wBAAwB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC/G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAgE5C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,GAAE,SAAc,GAAG,MAAM,CAQlF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,CAgBnH;AAED,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
@@ -1,15 +1,35 @@
1
+ /**
2
+ * @deprecated This module is deprecated. Use constraint-registry.ts instead.
3
+ *
4
+ * Phase 3A (Architectural Cleanup): This file contains legacy constraint loading logic
5
+ * that has been replaced by the centralized constraint-registry.ts module.
6
+ *
7
+ * Migration guide:
8
+ * - Replace createEngine() or createValidationEngine() with:
9
+ * ```ts
10
+ * import { Engine } from '../core/engine.js';
11
+ * import { flattenTokens, type FlatToken } from '../core/flatten.js';
12
+ * import { setupConstraints } from './constraint-registry.js';
13
+ *
14
+ * const { flat, edges } = flattenTokens(tokens);
15
+ * const init = {};
16
+ * for (const t of Object.values(flat)) {
17
+ * init[(t as FlatToken).id] = (t as FlatToken).value;
18
+ * }
19
+ * const engine = new Engine(init, edges);
20
+ * const knownIds = new Set(Object.keys(init));
21
+ * setupConstraints(engine, { config, bp }, { knownIds });
22
+ * ```
23
+ *
24
+ * This file will be removed in a future major version.
25
+ */
1
26
  import { flattenTokens } from '../core/flatten.js';
2
27
  import { Engine } from '../core/engine.js';
3
28
  import { MonotonicPlugin, parseSize as parseSizePx } from '../core/constraints/monotonic.js';
4
29
  import { MonotonicLightness } from '../core/constraints/monotonic-lightness.js';
5
30
  import { WcagContrastPlugin } from '../core/constraints/wcag.js';
6
31
  import { loadOrders as loadOrdersBP, loadTokensWithBreakpoint } from '../core/breakpoints.js';
7
- export function createEngine(tokensRoot, config = {}) {
8
- const { flat, edges } = flattenTokens(tokensRoot);
9
- const init = {};
10
- for (const [id, token] of Object.entries(flat))
11
- init[id] = token.value;
12
- const engine = new Engine(init, edges);
32
+ function applyMonotonicPlugins(engine, bp) {
13
33
  function loadOrders(path) {
14
34
  try {
15
35
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -19,10 +39,11 @@ export function createEngine(tokensRoot, config = {}) {
19
39
  return [];
20
40
  }
21
41
  }
22
- const typOrders = loadOrders('themes/typography.order.json');
23
- const spacingOrders = loadOrders('themes/spacing.order.json');
24
- const layoutOrders = loadOrders('themes/layout.order.json');
25
- const colorOrders = loadOrders('themes/color.order.json');
42
+ const suffix = bp ? `.${bp}` : '';
43
+ const typOrders = loadOrders(`themes/typography${suffix}.order.json`);
44
+ const spacingOrders = loadOrders(`themes/spacing${suffix}.order.json`);
45
+ const layoutOrders = loadOrders(`themes/layout${suffix}.order.json`);
46
+ const colorOrders = loadOrders(`themes/color${suffix}.order.json`);
26
47
  if (typOrders.length)
27
48
  engine.use(MonotonicPlugin(typOrders, parseSizePx, 'monotonic-typography'));
28
49
  if (spacingOrders.length)
@@ -31,24 +52,71 @@ export function createEngine(tokensRoot, config = {}) {
31
52
  engine.use(MonotonicPlugin(layoutOrders, parseSizePx, 'monotonic-layout'));
32
53
  if (colorOrders.length)
33
54
  engine.use(MonotonicLightness(colorOrders));
34
- if (config.constraints?.wcag) {
35
- const wcagRules = config.constraints.wcag.map((r) => ({ fg: r.foreground, bg: r.background, min: r.ratio || 4.5, where: r.description || 'Unknown' }));
55
+ }
56
+ function applyWcagPlugins(engine, config) {
57
+ const constraintsCfg = config.constraints ?? {};
58
+ if (constraintsCfg.wcag) {
59
+ const wcagRules = constraintsCfg.wcag.map((r) => ({
60
+ fg: r.foreground,
61
+ bg: r.background,
62
+ min: r.ratio || 4.5,
63
+ where: r.description || 'Unknown',
64
+ }));
36
65
  engine.use(WcagContrastPlugin(wcagRules));
37
66
  }
38
- const defaultWcagPairs = [
39
- { fg: 'color.role.text.default', bg: 'color.role.bg.surface', min: 4.5, where: 'Body text on surface' },
40
- { fg: 'color.role.accent.default', bg: 'color.role.bg.surface', min: 3.0, where: 'Accent on surface' },
41
- { fg: 'color.role.focus.ring', bg: 'color.role.bg.surface', min: 3.0, where: 'Focus ring on surface', backdrop: '#ffffff' }
42
- ];
43
- engine.use(WcagContrastPlugin(defaultWcagPairs));
67
+ const enableDefaults = constraintsCfg.enableBuiltInWcagDefaults === undefined
68
+ ? true
69
+ : !!constraintsCfg.enableBuiltInWcagDefaults;
70
+ if (enableDefaults) {
71
+ const defaultWcagPairs = [
72
+ {
73
+ fg: 'color.role.text.default',
74
+ bg: 'color.role.bg.surface',
75
+ min: 4.5,
76
+ where: 'Body text on surface',
77
+ },
78
+ {
79
+ fg: 'color.role.accent.default',
80
+ bg: 'color.role.bg.surface',
81
+ min: 3.0,
82
+ where: 'Accent on surface',
83
+ },
84
+ {
85
+ fg: 'color.role.focus.ring',
86
+ bg: 'color.role.bg.surface',
87
+ min: 3.0,
88
+ where: 'Focus ring on surface',
89
+ backdrop: '#ffffff',
90
+ },
91
+ ];
92
+ engine.use(WcagContrastPlugin(defaultWcagPairs));
93
+ }
94
+ }
95
+ /**
96
+ * @deprecated Use constraint-registry.ts setupConstraints() instead.
97
+ * This function will be removed in a future major version.
98
+ */
99
+ export function createEngine(tokensRoot, config = {}) {
100
+ const { flat, edges } = flattenTokens(tokensRoot);
101
+ const init = {};
102
+ for (const [id, token] of Object.entries(flat))
103
+ init[id] = token.value;
104
+ const engine = new Engine(init, edges);
105
+ applyMonotonicPlugins(engine, undefined);
106
+ applyWcagPlugins(engine, config);
44
107
  return engine;
45
108
  }
109
+ /**
110
+ * @deprecated Use constraint-registry.ts setupConstraints() instead.
111
+ * This function will be removed in a future major version.
112
+ */
46
113
  export function createValidationEngine(tokensRoot, bp, config) {
47
114
  const { flat, edges } = flattenTokens(tokensRoot);
48
115
  const init = {};
49
116
  for (const t of Object.values(flat))
50
117
  init[t.id] = t.value;
51
118
  const engine = new Engine(init, edges);
119
+ // Use breakpoint-aware order loading where available
52
120
  const typ = loadOrdersBP('typography', bp);
53
121
  const spc = loadOrdersBP('spacing', bp);
54
122
  const lay = loadOrdersBP('layout', bp);
@@ -61,10 +129,7 @@ export function createValidationEngine(tokensRoot, bp, config) {
61
129
  engine.use(MonotonicPlugin(lay, parseSizePx, 'monotonic-layout'));
62
130
  if (col.length)
63
131
  engine.use(MonotonicLightness(col));
64
- if (config.constraints?.wcag) {
65
- const wcagRules = config.constraints.wcag.map((rule) => ({ fg: rule.foreground, bg: rule.background, min: rule.ratio || 4.5, where: rule.description || 'Unknown' }));
66
- engine.use(WcagContrastPlugin(wcagRules));
67
- }
132
+ applyWcagPlugins(engine, config);
68
133
  return engine;
69
134
  }
70
135
  export { loadTokensWithBreakpoint };
@@ -1,61 +1,133 @@
1
- import { flattenTokens, type TokenNode, type FlatToken } from '../core/flatten.js';
2
- import { Engine } from '../core/engine.js';
3
- import { MonotonicPlugin, parseSize as parseSizePx } from '../core/constraints/monotonic.js';
4
- import { MonotonicLightness } from '../core/constraints/monotonic-lightness.js';
5
- import { WcagContrastPlugin } from '../core/constraints/wcag.js';
6
- import { loadOrders as loadOrdersBP, loadTokensWithBreakpoint, type Breakpoint } from '../core/breakpoints.js';
7
- import type { DcvConfig } from './types.js';
8
-
9
- export function createEngine(tokensRoot: TokenNode, config: DcvConfig = {}): Engine {
10
- const { flat, edges } = flattenTokens(tokensRoot);
11
- const init: Record<string, string | number> = {};
12
- for (const [id, token] of Object.entries(flat)) init[id] = (token as FlatToken).value;
13
- const engine = new Engine(init, edges);
14
- function loadOrders(path: string) {
15
- try {
16
- // eslint-disable-next-line @typescript-eslint/no-require-imports
17
- return JSON.parse(require('node:fs').readFileSync(path, 'utf8')).order as [string, '<='|'>=', string][];
18
- } catch { return []; }
19
- }
20
- const typOrders = loadOrders('themes/typography.order.json');
21
- const spacingOrders = loadOrders('themes/spacing.order.json');
22
- const layoutOrders = loadOrders('themes/layout.order.json');
23
- const colorOrders = loadOrders('themes/color.order.json');
24
- if (typOrders.length) engine.use(MonotonicPlugin(typOrders, parseSizePx, 'monotonic-typography'));
25
- if (spacingOrders.length) engine.use(MonotonicPlugin(spacingOrders, parseSizePx, 'monotonic-spacing'));
26
- if (layoutOrders.length) engine.use(MonotonicPlugin(layoutOrders, parseSizePx, 'monotonic-layout'));
27
- if (colorOrders.length) engine.use(MonotonicLightness(colorOrders));
28
- if (config.constraints?.wcag) {
29
- const wcagRules = config.constraints.wcag.map((r: any) => ({ fg: r.foreground, bg: r.background, min: r.ratio || 4.5, where: r.description || 'Unknown' }));
30
- engine.use(WcagContrastPlugin(wcagRules));
31
- }
32
- const defaultWcagPairs = [
33
- { fg: 'color.role.text.default', bg: 'color.role.bg.surface', min: 4.5, where: 'Body text on surface' },
34
- { fg: 'color.role.accent.default', bg: 'color.role.bg.surface', min: 3.0, where: 'Accent on surface' },
35
- { fg: 'color.role.focus.ring', bg: 'color.role.bg.surface', min: 3.0, where: 'Focus ring on surface', backdrop: '#ffffff' }
36
- ];
37
- engine.use(WcagContrastPlugin(defaultWcagPairs));
38
- return engine;
39
- }
40
-
41
- export function createValidationEngine(tokensRoot: TokenNode, bp: Breakpoint | undefined, config: DcvConfig): Engine {
42
- const { flat, edges } = flattenTokens(tokensRoot);
43
- const init: Record<string, string | number> = {};
44
- for (const t of Object.values(flat)) init[(t as FlatToken).id] = (t as FlatToken).value;
45
- const engine = new Engine(init, edges);
46
- const typ = loadOrdersBP('typography', bp);
47
- const spc = loadOrdersBP('spacing', bp);
48
- const lay = loadOrdersBP('layout', bp);
49
- const col = loadOrdersBP('color', bp);
50
- if (typ.length) engine.use(MonotonicPlugin(typ, parseSizePx, 'monotonic-typography'));
51
- if (spc.length) engine.use(MonotonicPlugin(spc, parseSizePx, 'monotonic-spacing'));
52
- if (lay.length) engine.use(MonotonicPlugin(lay, parseSizePx, 'monotonic-layout'));
53
- if (col.length) engine.use(MonotonicLightness(col));
54
- if (config.constraints?.wcag) {
55
- const wcagRules = config.constraints.wcag.map((rule: any) => ({ fg: rule.foreground, bg: rule.background, min: rule.ratio || 4.5, where: rule.description || 'Unknown' }));
56
- engine.use(WcagContrastPlugin(wcagRules));
57
- }
58
- return engine;
59
- }
60
-
61
- export { loadTokensWithBreakpoint };
1
+ /**
2
+ * @deprecated This module is deprecated. Use constraint-registry.ts instead.
3
+ *
4
+ * Phase 3A (Architectural Cleanup): This file contains legacy constraint loading logic
5
+ * that has been replaced by the centralized constraint-registry.ts module.
6
+ *
7
+ * Migration guide:
8
+ * - Replace createEngine() or createValidationEngine() with:
9
+ * ```ts
10
+ * import { Engine } from '../core/engine.js';
11
+ * import { flattenTokens, type FlatToken } from '../core/flatten.js';
12
+ * import { setupConstraints } from './constraint-registry.js';
13
+ *
14
+ * const { flat, edges } = flattenTokens(tokens);
15
+ * const init = {};
16
+ * for (const t of Object.values(flat)) {
17
+ * init[(t as FlatToken).id] = (t as FlatToken).value;
18
+ * }
19
+ * const engine = new Engine(init, edges);
20
+ * const knownIds = new Set(Object.keys(init));
21
+ * setupConstraints(engine, { config, bp }, { knownIds });
22
+ * ```
23
+ *
24
+ * This file will be removed in a future major version.
25
+ */
26
+
27
+ import { flattenTokens, type TokenNode, type FlatToken } from '../core/flatten.js';
28
+ import { Engine } from '../core/engine.js';
29
+ import { MonotonicPlugin, parseSize as parseSizePx } from '../core/constraints/monotonic.js';
30
+ import { MonotonicLightness } from '../core/constraints/monotonic-lightness.js';
31
+ import { WcagContrastPlugin } from '../core/constraints/wcag.js';
32
+ import { loadOrders as loadOrdersBP, loadTokensWithBreakpoint, type Breakpoint } from '../core/breakpoints.js';
33
+ import type { DcvConfig } from './types.js';
34
+
35
+ function applyMonotonicPlugins(engine: Engine, bp: Breakpoint | undefined): void {
36
+ function loadOrders(path: string) {
37
+ try {
38
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
39
+ return JSON.parse(require('node:fs').readFileSync(path, 'utf8')).order as [string, '<='|'>=', string][];
40
+ } catch { return []; }
41
+ }
42
+ const suffix = bp ? `.${bp}` : '';
43
+ const typOrders = loadOrders(`themes/typography${suffix}.order.json`);
44
+ const spacingOrders = loadOrders(`themes/spacing${suffix}.order.json`);
45
+ const layoutOrders = loadOrders(`themes/layout${suffix}.order.json`);
46
+ const colorOrders = loadOrders(`themes/color${suffix}.order.json`);
47
+ if (typOrders.length) engine.use(MonotonicPlugin(typOrders, parseSizePx, 'monotonic-typography'));
48
+ if (spacingOrders.length) engine.use(MonotonicPlugin(spacingOrders, parseSizePx, 'monotonic-spacing'));
49
+ if (layoutOrders.length) engine.use(MonotonicPlugin(layoutOrders, parseSizePx, 'monotonic-layout'));
50
+ if (colorOrders.length) engine.use(MonotonicLightness(colorOrders));
51
+ }
52
+
53
+ function applyWcagPlugins(engine: Engine, config: DcvConfig): void {
54
+ const constraintsCfg = config.constraints ?? {};
55
+
56
+ if (constraintsCfg.wcag) {
57
+ const wcagRules = constraintsCfg.wcag.map((r: any) => ({
58
+ fg: r.foreground,
59
+ bg: r.background,
60
+ min: r.ratio || 4.5,
61
+ where: r.description || 'Unknown',
62
+ }));
63
+ engine.use(WcagContrastPlugin(wcagRules));
64
+ }
65
+
66
+ const enableDefaults =
67
+ constraintsCfg.enableBuiltInWcagDefaults === undefined
68
+ ? true
69
+ : !!constraintsCfg.enableBuiltInWcagDefaults;
70
+
71
+ if (enableDefaults) {
72
+ const defaultWcagPairs = [
73
+ {
74
+ fg: 'color.role.text.default',
75
+ bg: 'color.role.bg.surface',
76
+ min: 4.5,
77
+ where: 'Body text on surface',
78
+ },
79
+ {
80
+ fg: 'color.role.accent.default',
81
+ bg: 'color.role.bg.surface',
82
+ min: 3.0,
83
+ where: 'Accent on surface',
84
+ },
85
+ {
86
+ fg: 'color.role.focus.ring',
87
+ bg: 'color.role.bg.surface',
88
+ min: 3.0,
89
+ where: 'Focus ring on surface',
90
+ backdrop: '#ffffff',
91
+ },
92
+ ];
93
+ engine.use(WcagContrastPlugin(defaultWcagPairs));
94
+ }
95
+ }
96
+
97
+ /**
98
+ * @deprecated Use constraint-registry.ts setupConstraints() instead.
99
+ * This function will be removed in a future major version.
100
+ */
101
+ export function createEngine(tokensRoot: TokenNode, config: DcvConfig = {}): Engine {
102
+ const { flat, edges } = flattenTokens(tokensRoot);
103
+ const init: Record<string, string | number> = {};
104
+ for (const [id, token] of Object.entries(flat)) init[id] = (token as FlatToken).value;
105
+ const engine = new Engine(init, edges);
106
+ applyMonotonicPlugins(engine, undefined);
107
+ applyWcagPlugins(engine, config);
108
+ return engine;
109
+ }
110
+
111
+ /**
112
+ * @deprecated Use constraint-registry.ts setupConstraints() instead.
113
+ * This function will be removed in a future major version.
114
+ */
115
+ export function createValidationEngine(tokensRoot: TokenNode, bp: Breakpoint | undefined, config: DcvConfig): Engine {
116
+ const { flat, edges } = flattenTokens(tokensRoot);
117
+ const init: Record<string, string | number> = {};
118
+ for (const t of Object.values(flat)) init[(t as FlatToken).id] = (t as FlatToken).value;
119
+ const engine = new Engine(init, edges);
120
+ // Use breakpoint-aware order loading where available
121
+ const typ = loadOrdersBP('typography', bp);
122
+ const spc = loadOrdersBP('spacing', bp);
123
+ const lay = loadOrdersBP('layout', bp);
124
+ const col = loadOrdersBP('color', bp);
125
+ if (typ.length) engine.use(MonotonicPlugin(typ, parseSizePx, 'monotonic-typography'));
126
+ if (spc.length) engine.use(MonotonicPlugin(spc, parseSizePx, 'monotonic-spacing'));
127
+ if (lay.length) engine.use(MonotonicPlugin(lay, parseSizePx, 'monotonic-layout'));
128
+ if (col.length) engine.use(MonotonicLightness(col));
129
+ applyWcagPlugins(engine, config);
130
+ return engine;
131
+ }
132
+
133
+ export { loadTokensWithBreakpoint };