design-constraint-validator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +659 -0
  3. package/adapters/README.md +46 -0
  4. package/adapters/css.d.ts +44 -0
  5. package/adapters/css.d.ts.map +1 -0
  6. package/adapters/css.js +97 -0
  7. package/adapters/css.ts +116 -0
  8. package/adapters/js.d.ts +3 -0
  9. package/adapters/js.d.ts.map +1 -0
  10. package/adapters/js.js +15 -0
  11. package/adapters/js.ts +14 -0
  12. package/adapters/json.d.ts +18 -0
  13. package/adapters/json.d.ts.map +1 -0
  14. package/adapters/json.js +35 -0
  15. package/adapters/json.ts +45 -0
  16. package/cli/build-css.d.ts +2 -0
  17. package/cli/build-css.d.ts.map +1 -0
  18. package/cli/build-css.js +23 -0
  19. package/cli/build-css.ts +32 -0
  20. package/cli/commands/build.d.ts +5 -0
  21. package/cli/commands/build.d.ts.map +1 -0
  22. package/cli/commands/build.js +89 -0
  23. package/cli/commands/build.ts +65 -0
  24. package/cli/commands/graph.d.ts +3 -0
  25. package/cli/commands/graph.d.ts.map +1 -0
  26. package/cli/commands/graph.js +219 -0
  27. package/cli/commands/graph.ts +137 -0
  28. package/cli/commands/index.d.ts +8 -0
  29. package/cli/commands/index.d.ts.map +1 -0
  30. package/cli/commands/index.js +7 -0
  31. package/cli/commands/index.ts +7 -0
  32. package/cli/commands/patch-apply.d.ts +3 -0
  33. package/cli/commands/patch-apply.d.ts.map +1 -0
  34. package/cli/commands/patch-apply.js +75 -0
  35. package/cli/commands/patch-apply.ts +80 -0
  36. package/cli/commands/patch.d.ts +3 -0
  37. package/cli/commands/patch.d.ts.map +1 -0
  38. package/cli/commands/patch.js +21 -0
  39. package/cli/commands/patch.ts +22 -0
  40. package/cli/commands/set.d.ts +3 -0
  41. package/cli/commands/set.d.ts.map +1 -0
  42. package/cli/commands/set.js +286 -0
  43. package/cli/commands/set.ts +225 -0
  44. package/cli/commands/utils.d.ts +4 -0
  45. package/cli/commands/utils.d.ts.map +1 -0
  46. package/cli/commands/utils.js +51 -0
  47. package/cli/commands/utils.ts +50 -0
  48. package/cli/commands/validate.d.ts +3 -0
  49. package/cli/commands/validate.d.ts.map +1 -0
  50. package/cli/commands/validate.js +131 -0
  51. package/cli/commands/validate.ts +115 -0
  52. package/cli/commands/why.d.ts +3 -0
  53. package/cli/commands/why.d.ts.map +1 -0
  54. package/cli/commands/why.js +64 -0
  55. package/cli/commands/why.ts +46 -0
  56. package/cli/config-schema.d.ts +238 -0
  57. package/cli/config-schema.d.ts.map +1 -0
  58. package/cli/config-schema.js +21 -0
  59. package/cli/config-schema.ts +27 -0
  60. package/cli/config.d.ts +4 -0
  61. package/cli/config.d.ts.map +1 -0
  62. package/cli/config.js +37 -0
  63. package/cli/config.ts +35 -0
  64. package/cli/dcv.d.ts +3 -0
  65. package/cli/dcv.d.ts.map +1 -0
  66. package/cli/dcv.js +86 -0
  67. package/cli/dcv.ts +107 -0
  68. package/cli/engine-helpers.d.ts +8 -0
  69. package/cli/engine-helpers.d.ts.map +1 -0
  70. package/cli/engine-helpers.js +70 -0
  71. package/cli/engine-helpers.ts +61 -0
  72. package/cli/graph-poset.d.ts +9 -0
  73. package/cli/graph-poset.d.ts.map +1 -0
  74. package/cli/graph-poset.js +58 -0
  75. package/cli/graph-poset.ts +74 -0
  76. package/cli/index.d.ts +3 -0
  77. package/cli/index.d.ts.map +1 -0
  78. package/cli/index.js +2 -0
  79. package/cli/index.ts +2 -0
  80. package/cli/result.d.ts +17 -0
  81. package/cli/result.d.ts.map +1 -0
  82. package/cli/result.js +29 -0
  83. package/cli/result.ts +27 -0
  84. package/cli/run.d.ts +3 -0
  85. package/cli/run.d.ts.map +1 -0
  86. package/cli/run.js +47 -0
  87. package/cli/run.ts +54 -0
  88. package/cli/smoke-test.d.ts +2 -0
  89. package/cli/smoke-test.d.ts.map +1 -0
  90. package/cli/smoke-test.js +33 -0
  91. package/cli/smoke-test.ts +40 -0
  92. package/cli/types.d.ts +86 -0
  93. package/cli/types.d.ts.map +1 -0
  94. package/cli/types.js +1 -0
  95. package/cli/types.ts +78 -0
  96. package/core/breakpoints.d.ts +12 -0
  97. package/core/breakpoints.d.ts.map +1 -0
  98. package/core/breakpoints.js +48 -0
  99. package/core/breakpoints.ts +50 -0
  100. package/core/cli-format.d.ts +8 -0
  101. package/core/cli-format.d.ts.map +1 -0
  102. package/core/cli-format.js +29 -0
  103. package/core/cli-format.ts +31 -0
  104. package/core/color.d.ts +14 -0
  105. package/core/color.d.ts.map +1 -0
  106. package/core/color.js +136 -0
  107. package/core/color.ts +148 -0
  108. package/core/constraints/cross-axis.d.ts +33 -0
  109. package/core/constraints/cross-axis.d.ts.map +1 -0
  110. package/core/constraints/cross-axis.js +93 -0
  111. package/core/constraints/cross-axis.ts +114 -0
  112. package/core/constraints/monotonic-lightness.d.ts +5 -0
  113. package/core/constraints/monotonic-lightness.d.ts.map +1 -0
  114. package/core/constraints/monotonic-lightness.js +37 -0
  115. package/core/constraints/monotonic-lightness.ts +38 -0
  116. package/core/constraints/monotonic.d.ts +7 -0
  117. package/core/constraints/monotonic.d.ts.map +1 -0
  118. package/core/constraints/monotonic.js +65 -0
  119. package/core/constraints/monotonic.ts +74 -0
  120. package/core/constraints/threshold.d.ts +10 -0
  121. package/core/constraints/threshold.d.ts.map +1 -0
  122. package/core/constraints/threshold.js +36 -0
  123. package/core/constraints/threshold.ts +43 -0
  124. package/core/constraints/wcag.d.ts +11 -0
  125. package/core/constraints/wcag.d.ts.map +1 -0
  126. package/core/constraints/wcag.js +53 -0
  127. package/core/constraints/wcag.ts +70 -0
  128. package/core/cross-axis-config.d.ts +5 -0
  129. package/core/cross-axis-config.d.ts.map +1 -0
  130. package/core/cross-axis-config.js +144 -0
  131. package/core/cross-axis-config.ts +152 -0
  132. package/core/engine.d.ts +32 -0
  133. package/core/engine.d.ts.map +1 -0
  134. package/core/engine.js +46 -0
  135. package/core/engine.ts +65 -0
  136. package/core/flatten.d.ts +20 -0
  137. package/core/flatten.d.ts.map +1 -0
  138. package/core/flatten.js +80 -0
  139. package/core/flatten.ts +116 -0
  140. package/core/image-export.d.ts +10 -0
  141. package/core/image-export.d.ts.map +1 -0
  142. package/core/image-export.js +43 -0
  143. package/core/image-export.ts +48 -0
  144. package/core/index.d.ts +31 -0
  145. package/core/index.d.ts.map +1 -0
  146. package/core/index.js +54 -0
  147. package/core/index.ts +72 -0
  148. package/core/patch.d.ts +28 -0
  149. package/core/patch.d.ts.map +1 -0
  150. package/core/patch.js +110 -0
  151. package/core/patch.ts +134 -0
  152. package/core/poset.d.ts +41 -0
  153. package/core/poset.d.ts.map +1 -0
  154. package/core/poset.js +275 -0
  155. package/core/poset.ts +311 -0
  156. package/core/why.d.ts +17 -0
  157. package/core/why.d.ts.map +1 -0
  158. package/core/why.js +45 -0
  159. package/core/why.ts +63 -0
  160. package/dist/test-overrides-removal.json +4 -0
  161. package/dist/tmp.patch.json +35 -0
  162. package/package.json +90 -0
  163. package/themes/color.lg.order.json +15 -0
  164. package/themes/color.md.order.json +15 -0
  165. package/themes/color.order.json +15 -0
  166. package/themes/color.sm.order.json +15 -0
  167. package/themes/cross-axis.rules.json +36 -0
  168. package/themes/cross-axis.sm.rules.json +12 -0
  169. package/themes/layout.lg.order.json +18 -0
  170. package/themes/layout.md.order.json +18 -0
  171. package/themes/layout.order.json +18 -0
  172. package/themes/layout.sm.order.json +18 -0
  173. package/themes/spacing.order.json +14 -0
  174. package/themes/typography.lg.order.json +15 -0
  175. package/themes/typography.md.order.json +15 -0
  176. package/themes/typography.order.json +15 -0
  177. package/themes/typography.sm.order.json +15 -0
  178. package/tokens/overrides/base.json +22 -0
  179. package/tokens/overrides/lg.json +20 -0
  180. package/tokens/overrides/md.json +16 -0
  181. package/tokens/overrides/sm.json +16 -0
  182. package/tokens/overrides/viol.color.json +6 -0
  183. package/tokens/overrides/viol.typography.json +6 -0
  184. package/tokens/tokens.demo-violations.json +116 -0
  185. package/tokens/tokens.example.json +128 -0
  186. package/tokens/tokens.json +67 -0
  187. package/tokens/tokens.multi-violations.json +21 -0
  188. package/tokens/tokens.schema.d.ts +2298 -0
  189. package/tokens/tokens.schema.d.ts.map +1 -0
  190. package/tokens/tokens.schema.js +148 -0
  191. package/tokens/tokens.schema.ts +196 -0
  192. package/tokens/tokens.test.json +38 -0
  193. package/tokens/tokens.touch-violation.json +8 -0
  194. package/tokens/typography.classes.css +11 -0
  195. package/tokens/typography.css +20 -0
@@ -0,0 +1,65 @@
1
+ import { join, dirname, resolve } from 'node:path';
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
3
+ import { flattenTokens, type FlatToken } from '../../core/flatten.js';
4
+ import { valuesToCss, type ManifestRow } from '../../adapters/css.js';
5
+ import { emitJSON } from '../../adapters/json.js';
6
+ import { emitJS } from '../../adapters/js.js';
7
+ import type { BuildOptions } from '../types.js';
8
+
9
+ export async function buildCommand(options: BuildOptions & { [k: string]: any }): Promise<void> {
10
+ const { loadTokensWithBreakpoint } = await import('../../core/breakpoints.js');
11
+ const tokens = loadTokensWithBreakpoint();
12
+ const { flat } = flattenTokens(tokens);
13
+ let allValues = Object.fromEntries(Object.values(flat).map(t => [t.id, (t as FlatToken).value]));
14
+ if (options.theme) {
15
+ const themePath = join('tokens/themes', `${options.theme}.json`);
16
+ if (existsSync(themePath)) {
17
+ const themeTokens = JSON.parse(readFileSync(themePath, 'utf8'));
18
+ Object.assign(allValues, themeTokens);
19
+ }
20
+ }
21
+ const format = options.format || 'css';
22
+ const defaultOutput = `dist/tokens.${format}`;
23
+ let manifest: ManifestRow[] | undefined;
24
+ if (options.mapper) {
25
+ try {
26
+ const mp = resolve(options.mapper);
27
+ if (!existsSync(mp)) throw new Error(`mapper file not found: ${mp}`);
28
+ manifest = JSON.parse(readFileSync(mp, 'utf8')) as ManifestRow[];
29
+ if (!Array.isArray(manifest)) throw new Error('mapper manifest must be an array');
30
+ } catch (e) {
31
+ const msg = e instanceof Error ? e.message : String(e);
32
+ console.error(`Failed to load mapper manifest: ${msg}`);
33
+ process.exit(1);
34
+ }
35
+ }
36
+ const allFormats = options.allFormats ?? options['all-formats'];
37
+ if (allFormats) {
38
+ const dir = 'dist'; if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
39
+ const css = valuesToCss(allValues, { manifest });
40
+ writeFileSync('dist/tokens.css', css, 'utf8');
41
+ if (options.dryRun) console.log(css);
42
+ writeFileSync('dist/tokens.json', emitJSON(allValues, manifest), 'utf8');
43
+ writeFileSync('dist/tokens.js', emitJS(allValues, manifest), 'utf8');
44
+ console.log(`Tokens written (all formats) to dist/ (css/json/js)${manifest ? ' with mapper' : ''}`);
45
+ return;
46
+ }
47
+ const dryRun = options.dryRun ?? options['dry-run'];
48
+ if (format === 'css') {
49
+ const css = valuesToCss(allValues, { manifest });
50
+ if (dryRun) { console.log(css); return; }
51
+ const outPath = options.output || defaultOutput; const dir = dirname(outPath); if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
52
+ writeFileSync(outPath, css, 'utf8');
53
+ console.log(`CSS tokens written to ${outPath}${manifest ? ' (manifest mapper applied)' : ''}`);
54
+ } else if (format === 'json') {
55
+ const outPath = options.output || defaultOutput; const dir = dirname(outPath); if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
56
+ if (dryRun) { console.log(emitJSON(allValues, manifest)); return; }
57
+ writeFileSync(outPath, emitJSON(allValues, manifest), 'utf8');
58
+ console.log(`JSON tokens written to ${outPath}${manifest ? ' (manifest mapper applied)' : ''}`);
59
+ } else if (format === 'js') {
60
+ const outPath = options.output || defaultOutput; const dir = dirname(outPath); if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
61
+ if (dryRun) { console.log(emitJS(allValues, manifest)); return; }
62
+ writeFileSync(outPath, emitJS(allValues, manifest), 'utf8');
63
+ console.log(`JS tokens written to ${outPath}${manifest ? ' (manifest mapper applied)' : ''}`);
64
+ }
65
+ }
@@ -0,0 +1,3 @@
1
+ import type { GraphOptions } from '../types.js';
2
+ export declare function graphCommand(options: GraphOptions): Promise<void>;
3
+ //# sourceMappingURL=graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["graph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAyBhD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA8GvE"}
@@ -0,0 +1,219 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
+ import { flattenTokens } from '../../core/flatten.js';
3
+ import { exportGraphImage } from '../../core/image-export.js';
4
+ // Local helper for non-poset dependency graphs
5
+ function generateDependencyGraph(edges, format) {
6
+ switch (format) {
7
+ case 'dot': {
8
+ const dotNodes = new Set();
9
+ edges.forEach(([f, t]) => { dotNodes.add(f); dotNodes.add(t); });
10
+ let dot = 'digraph tokens {\n rankdir=LR;\n node [shape=box, style=rounded];\n\n';
11
+ dotNodes.forEach(n => { dot += ` "${n}";\n`; });
12
+ dot += '\n';
13
+ edges.forEach(([f, t]) => { dot += ` "${f}" -> "${t}";\n`; });
14
+ return dot + '}\n';
15
+ }
16
+ case 'mermaid': {
17
+ let mermaid = 'graph LR\n';
18
+ const mermaidNodes = new Set();
19
+ edges.forEach(([from, to]) => {
20
+ const fromId = from.replace(/[^a-zA-Z0-9]/g, '_');
21
+ const toId = to.replace(/[^a-zA-Z0-9]/g, '_');
22
+ if (!mermaidNodes.has(fromId)) {
23
+ mermaid += ` ${fromId}["${from}"]\n`;
24
+ mermaidNodes.add(fromId);
25
+ }
26
+ if (!mermaidNodes.has(toId)) {
27
+ mermaid += ` ${toId}["${to}"]\n`;
28
+ mermaidNodes.add(toId);
29
+ }
30
+ mermaid += ` ${fromId} --> ${toId}\n`;
31
+ });
32
+ return mermaid;
33
+ }
34
+ case 'json':
35
+ default: return JSON.stringify({ nodes: Array.from(new Set(edges.flat())), edges }, null, 2);
36
+ }
37
+ }
38
+ export async function graphCommand(options) {
39
+ const { parseBreakpoints } = await import('../../core/breakpoints.js');
40
+ const bps = parseBreakpoints(process.argv);
41
+ const plan = bps.length ? bps : [undefined];
42
+ if (options.hasse) {
43
+ const name = options.hasse;
44
+ const bundle = options.bundle;
45
+ const fmt = (options.format === 'json' ? 'mermaid' : options.format);
46
+ const imageFrom = options.imageFrom || 'mermaid';
47
+ const filterPrefixes = options.filterPrefix ? options.filterPrefix.split(',').map(s => s.trim()).filter(Boolean) : [];
48
+ const excludePrefixes = options.excludePrefix ? options.excludePrefix.split(',').map(s => s.trim()).filter(Boolean) : [];
49
+ const onlyViolations = options.onlyViolations || false;
50
+ const highlightViolations = options.highlightViolations || false;
51
+ const violationColor = options.violationColor || '#ff2d55';
52
+ const labelViolations = options.labelViolations || false;
53
+ const labelTruncate = Math.max(0, options.labelTruncate || 0);
54
+ const minSeverity = options.minSeverity || 'warn';
55
+ const focus = options.focus;
56
+ const radius = Math.max(0, options.radius || 1);
57
+ for (const breakpoint of plan) {
58
+ const suffixParts = [];
59
+ if (breakpoint)
60
+ suffixParts.push(breakpoint);
61
+ if (filterPrefixes.length)
62
+ suffixParts.push(filterPrefixes.join('_'));
63
+ if (excludePrefixes.length)
64
+ suffixParts.push('not-' + excludePrefixes.join('_'));
65
+ if (focus)
66
+ suffixParts.push(`focus-${focus.replace(/[^\w.*-]/g, '_')}-r${radius}`);
67
+ if (onlyViolations)
68
+ suffixParts.push('violations');
69
+ else if (highlightViolations)
70
+ suffixParts.push('highlight-violations');
71
+ if (labelViolations)
72
+ suffixParts.push('labeled');
73
+ const suffix = suffixParts.length ? '-' + suffixParts.map(s => s.replace(/[^\w.-]/g, '_')).join('__') : '';
74
+ const baseFmt = fmt === 'svg' || fmt === 'png' ? (imageFrom === 'dot' ? 'dot' : 'mermaid') : fmt;
75
+ const ext = baseFmt === 'mermaid' ? 'mmd' : 'dot';
76
+ const outDir = 'dist/graphs';
77
+ const baseFile = `${outDir}/${name}${suffix}-hasse.${ext}`;
78
+ try {
79
+ const src = `themes/${name}.order.json`;
80
+ if (!existsSync(src)) {
81
+ console.error(`❌ Order constraint file not found: ${src}`);
82
+ process.exit(1);
83
+ }
84
+ const { order } = JSON.parse(readFileSync(src, 'utf8'));
85
+ const { buildPoset, transitiveReduction, toMermaidHasseStyled, toDotHasseStyled, filterByPrefix, filterExcludePrefix, khopSubgraph, pickSeedsByPattern } = await import('../../core/poset.js');
86
+ let g = buildPoset(order);
87
+ if (filterPrefixes.length)
88
+ g = filterByPrefix(g, filterPrefixes);
89
+ if (excludePrefixes.length)
90
+ g = filterExcludePrefix(g, excludePrefixes);
91
+ let h = transitiveReduction(g);
92
+ if (focus) {
93
+ const nodes = new Set([...h.keys(), ...Array.from(h.values()).flatMap(s => [...s])]);
94
+ const seeds = pickSeedsByPattern(nodes, focus);
95
+ h = khopSubgraph(h, seeds, radius);
96
+ }
97
+ let highlight;
98
+ let edgeLabels;
99
+ if (onlyViolations || highlightViolations || labelViolations) {
100
+ const { loadTokensWithBreakpoint } = await import('../../core/breakpoints.js');
101
+ const tokens = loadTokensWithBreakpoint(breakpoint);
102
+ const { flattenTokens } = await import('../../core/flatten.js');
103
+ const { Engine } = await import('../../core/engine.js');
104
+ const { MonotonicPlugin, parseSize } = await import('../../core/constraints/monotonic.js');
105
+ const { MonotonicLightness } = await import('../../core/constraints/monotonic-lightness.js');
106
+ const { ThresholdPlugin } = await import('../../core/constraints/threshold.js');
107
+ const { flat, edges } = flattenTokens(tokens);
108
+ const init = {};
109
+ Object.values(flat).forEach(t => { const ft = t; init[ft.id] = ft.value; });
110
+ const engine = new Engine(init, edges);
111
+ const allIdsInHasse = new Set([...h.keys(), ...Array.from(h.values()).flatMap(s => [...s])]);
112
+ let issues = [];
113
+ if (name === 'color') {
114
+ const colorOrders = order;
115
+ issues = MonotonicLightness(colorOrders).evaluate(engine, allIdsInHasse);
116
+ }
117
+ else {
118
+ const numericOrders = order;
119
+ issues = MonotonicPlugin(numericOrders, parseSize, 'monotonic').evaluate(engine, allIdsInHasse);
120
+ }
121
+ const thresholdIssues = ThresholdPlugin([{ id: 'control.size.min', op: '>=', valuePx: 44, where: 'Touch target (WCAG / Apple HIG)' }]).evaluate(engine, allIdsInHasse);
122
+ issues.push(...thresholdIssues);
123
+ const severityRank = { error: 2, warn: 1 };
124
+ const filteredIssues = issues.filter(it => { const level = it.level || 'error'; return severityRank[level] >= severityRank[minSeverity]; });
125
+ const edgeViol = new Set();
126
+ const nodeViol = new Set();
127
+ if (labelViolations)
128
+ edgeLabels = new Map();
129
+ for (const it of filteredIssues) {
130
+ if (typeof it.id === 'string' && it.id.includes('|')) {
131
+ const [a, b] = it.id.split('|');
132
+ edgeViol.add(`${a}|${b}`);
133
+ nodeViol.add(a);
134
+ nodeViol.add(b);
135
+ if (labelViolations && edgeLabels) {
136
+ let label = it.message ?? 'violation';
137
+ if (labelTruncate > 0 && label.length > labelTruncate)
138
+ label = label.slice(0, labelTruncate - 1) + '…';
139
+ edgeLabels.set(`${a}|${b}`, label);
140
+ }
141
+ }
142
+ else if (typeof it.id === 'string')
143
+ nodeViol.add(it.id);
144
+ }
145
+ highlight = { nodes: nodeViol, edges: edgeViol, color: violationColor };
146
+ if (onlyViolations) {
147
+ const pruned = new Map();
148
+ for (const [u, vs] of h) {
149
+ for (const v of vs) {
150
+ if (edgeViol.has(`${u}|${v}`) || nodeViol.has(u) || nodeViol.has(v)) {
151
+ if (!pruned.has(u))
152
+ pruned.set(u, new Set());
153
+ pruned.get(u).add(v);
154
+ if (!pruned.has(v))
155
+ pruned.set(v, new Set());
156
+ }
157
+ }
158
+ }
159
+ h = pruned;
160
+ }
161
+ }
162
+ const bpLabel = breakpoint ? ` @${breakpoint}` : '';
163
+ const title = `${name}${suffix ? ' ' + suffix : ''}${bpLabel} (Hasse)`;
164
+ const mermaidContent = toMermaidHasseStyled(h, { title, highlight, labels: edgeLabels });
165
+ const dotContent = toDotHasseStyled(h, { title, highlight, labels: edgeLabels });
166
+ mkdirSync(outDir, { recursive: true });
167
+ if (bundle) {
168
+ const mFile = baseFile.replace(/\.dot$/, '-bundle.mmd').replace(/\.mmd$/, '-bundle.mmd');
169
+ const dFile = baseFile.replace(/\.mmd$/, '-bundle.dot').replace(/\.dot$/, '-bundle.dot');
170
+ writeFileSync(mFile, mermaidContent);
171
+ writeFileSync(dFile, dotContent);
172
+ }
173
+ const baseContent = baseFmt === 'mermaid' ? mermaidContent : dotContent;
174
+ writeFileSync(baseFile, baseContent);
175
+ if (fmt === 'svg' || fmt === 'png') {
176
+ const imgFile = `${outDir}/${name}${suffix}-hasse.${fmt}`;
177
+ const { ok, hint } = exportGraphImage(baseFile, imgFile, fmt, imageFrom);
178
+ if (ok)
179
+ console.log(`✓ Wrote ${baseFile} and ${imgFile}`);
180
+ else
181
+ console.log(`✓ Wrote ${baseFile} (image export skipped). ${hint}`);
182
+ }
183
+ else {
184
+ const hasViolations = highlight && (highlight.edges.size > 0 || highlight.nodes.size > 0);
185
+ const message = (onlyViolations || highlightViolations) && !hasViolations ? `✓ Wrote ${baseFile} (no violations in slice)` : `✓ Wrote ${baseFile}`;
186
+ console.log(message);
187
+ }
188
+ }
189
+ catch (error) {
190
+ console.error(`❌ Error generating Hasse diagram for ${breakpoint}:`, error);
191
+ process.exit(1);
192
+ }
193
+ }
194
+ return;
195
+ }
196
+ for (const breakpoint of plan) {
197
+ const { loadTokensWithBreakpoint } = await import('../../core/breakpoints.js');
198
+ const tokens = loadTokensWithBreakpoint(breakpoint);
199
+ const { edges } = flattenTokens(tokens);
200
+ let filteredEdges = edges;
201
+ if (options.filter) {
202
+ const filterRegex = new RegExp(options.filter);
203
+ filteredEdges = edges.filter(([from, to]) => filterRegex.test(from) || filterRegex.test(to));
204
+ }
205
+ const format = options.format || 'json';
206
+ const graph = generateDependencyGraph(filteredEdges, format);
207
+ if (options.output) {
208
+ const bpSuffix = breakpoint ? `.${breakpoint}` : '';
209
+ const outputPath = options.output.replace(/(\.[^.]+)$/, `${bpSuffix}$1`);
210
+ writeFileSync(outputPath, graph, 'utf8');
211
+ console.log(`Dependency graph written to: ${outputPath}`);
212
+ }
213
+ else {
214
+ if (breakpoint)
215
+ console.log(`\n=== ${breakpoint.toUpperCase()} ===`);
216
+ console.log(graph);
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,137 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
+ import type { GraphOptions } from '../types.js';
3
+ import { flattenTokens, type FlatToken } from '../../core/flatten.js';
4
+ import { exportGraphImage } from '../../core/image-export.js';
5
+
6
+ // Local helper for non-poset dependency graphs
7
+ function generateDependencyGraph(edges: Array<[string, string]>, format: string): string {
8
+ switch (format) {
9
+ case 'dot': {
10
+ const dotNodes = new Set<string>(); edges.forEach(([f,t]) => { dotNodes.add(f); dotNodes.add(t); });
11
+ let dot = 'digraph tokens {\n rankdir=LR;\n node [shape=box, style=rounded];\n\n';
12
+ dotNodes.forEach(n => { dot += ` "${n}";\n`; });
13
+ dot += '\n'; edges.forEach(([f,t]) => { dot += ` "${f}" -> "${t}";\n`; });
14
+ return dot + '}\n'; }
15
+ case 'mermaid': {
16
+ let mermaid = 'graph LR\n'; const mermaidNodes = new Set<string>();
17
+ edges.forEach(([from,to]) => { const fromId = from.replace(/[^a-zA-Z0-9]/g,'_'); const toId = to.replace(/[^a-zA-Z0-9]/g,'_');
18
+ if (!mermaidNodes.has(fromId)) { mermaid += ` ${fromId}["${from}"]\n`; mermaidNodes.add(fromId); }
19
+ if (!mermaidNodes.has(toId)) { mermaid += ` ${toId}["${to}"]\n`; mermaidNodes.add(toId); }
20
+ mermaid += ` ${fromId} --> ${toId}\n`; });
21
+ return mermaid; }
22
+ case 'json':
23
+ default: return JSON.stringify({ nodes: Array.from(new Set(edges.flat())), edges }, null, 2);
24
+ }
25
+ }
26
+
27
+ export async function graphCommand(options: GraphOptions): Promise<void> {
28
+ const { parseBreakpoints } = await import('../../core/breakpoints.js');
29
+ const bps = parseBreakpoints(process.argv);
30
+ const plan = bps.length ? bps : [undefined];
31
+ if (options.hasse) {
32
+ const name = options.hasse;
33
+ const bundle = (options as any).bundle;
34
+ const fmt = (options.format === 'json' ? 'mermaid' : options.format) as 'mermaid' | 'dot' | 'svg' | 'png';
35
+ const imageFrom = options.imageFrom || 'mermaid';
36
+ const filterPrefixes = options.filterPrefix ? options.filterPrefix.split(',').map(s=>s.trim()).filter(Boolean) : [];
37
+ const excludePrefixes = options.excludePrefix ? options.excludePrefix.split(',').map(s=>s.trim()).filter(Boolean) : [];
38
+ const onlyViolations = options.onlyViolations || false;
39
+ const highlightViolations = options.highlightViolations || false;
40
+ const violationColor = options.violationColor || '#ff2d55';
41
+ const labelViolations = options.labelViolations || false;
42
+ const labelTruncate = Math.max(0, options.labelTruncate || 0);
43
+ const minSeverity = options.minSeverity || 'warn';
44
+ const focus = options.focus; const radius = Math.max(0, options.radius || 1);
45
+ for (const breakpoint of plan) {
46
+ const suffixParts: string[] = [];
47
+ if (breakpoint) suffixParts.push(breakpoint);
48
+ if (filterPrefixes.length) suffixParts.push(filterPrefixes.join('_'));
49
+ if (excludePrefixes.length) suffixParts.push('not-' + excludePrefixes.join('_'));
50
+ if (focus) suffixParts.push(`focus-${focus.replace(/[^\w.*-]/g,'_')}-r${radius}`);
51
+ if (onlyViolations) suffixParts.push('violations'); else if (highlightViolations) suffixParts.push('highlight-violations');
52
+ if (labelViolations) suffixParts.push('labeled');
53
+ const suffix = suffixParts.length ? '-' + suffixParts.map(s=>s.replace(/[^\w.-]/g,'_')).join('__') : '';
54
+ const baseFmt = fmt === 'svg' || fmt === 'png' ? (imageFrom === 'dot' ? 'dot' : 'mermaid') : fmt;
55
+ const ext = baseFmt === 'mermaid' ? 'mmd' : 'dot';
56
+ const outDir = 'dist/graphs'; const baseFile = `${outDir}/${name}${suffix}-hasse.${ext}`;
57
+ try {
58
+ const src = `themes/${name}.order.json`;
59
+ if (!existsSync(src)) { console.error(`❌ Order constraint file not found: ${src}`); process.exit(1); }
60
+ const { order } = JSON.parse(readFileSync(src, 'utf8'));
61
+ const { buildPoset, transitiveReduction, toMermaidHasseStyled, toDotHasseStyled, filterByPrefix, filterExcludePrefix, khopSubgraph, pickSeedsByPattern } = await import('../../core/poset.js');
62
+ let g = buildPoset(order);
63
+ if (filterPrefixes.length) g = filterByPrefix(g, filterPrefixes);
64
+ if (excludePrefixes.length) g = filterExcludePrefix(g, excludePrefixes);
65
+ let h = transitiveReduction(g);
66
+ if (focus) { const nodes = new Set<string>([...h.keys(), ...Array.from(h.values()).flatMap(s=>[...s])]); const seeds = pickSeedsByPattern(nodes, focus); h = khopSubgraph(h, seeds, radius); }
67
+ let highlight: { nodes: Set<string>; edges: Set<string>; color?: string } | undefined; let edgeLabels: Map<string,string> | undefined;
68
+ if (onlyViolations || highlightViolations || labelViolations) {
69
+ const { loadTokensWithBreakpoint } = await import('../../core/breakpoints.js');
70
+ const tokens = loadTokensWithBreakpoint(breakpoint); const { flattenTokens } = await import('../../core/flatten.js');
71
+ const { Engine } = await import('../../core/engine.js');
72
+ const { MonotonicPlugin, parseSize } = await import('../../core/constraints/monotonic.js');
73
+ const { MonotonicLightness } = await import('../../core/constraints/monotonic-lightness.js');
74
+ const { ThresholdPlugin } = await import('../../core/constraints/threshold.js');
75
+ const { flat, edges } = flattenTokens(tokens); const init: Record<string,string|number> = {}; Object.values(flat).forEach(t => { const ft = t as FlatToken; init[ft.id] = ft.value; });
76
+ const engine = new Engine(init, edges);
77
+ const allIdsInHasse = new Set<string>([...h.keys(), ...Array.from(h.values()).flatMap(s=>[...s])]);
78
+ let issues: any[] = [];
79
+ if (name === 'color') {
80
+ const colorOrders = order as [string, '<='|'>=', string][]; issues = MonotonicLightness(colorOrders).evaluate(engine, allIdsInHasse);
81
+ } else {
82
+ const numericOrders = order as [string, '<='|'>=', string][]; issues = MonotonicPlugin(numericOrders, parseSize, 'monotonic').evaluate(engine, allIdsInHasse);
83
+ }
84
+ const thresholdIssues = ThresholdPlugin([{ id: 'control.size.min', op: '>=', valuePx: 44, where: 'Touch target (WCAG / Apple HIG)' }]).evaluate(engine, allIdsInHasse); issues.push(...thresholdIssues);
85
+ const severityRank = { error: 2, warn: 1 } as const;
86
+ const filteredIssues = issues.filter(it => { const level = (it.level as 'warn'|'error') || 'error'; return severityRank[level] >= severityRank[minSeverity]; });
87
+ const edgeViol = new Set<string>(); const nodeViol = new Set<string>(); if (labelViolations) edgeLabels = new Map<string,string>();
88
+ for (const it of filteredIssues) {
89
+ if (typeof it.id === 'string' && it.id.includes('|')) { const [a,b] = it.id.split('|'); edgeViol.add(`${a}|${b}`); nodeViol.add(a); nodeViol.add(b); if (labelViolations && edgeLabels) { let label = it.message ?? 'violation'; if (labelTruncate > 0 && label.length > labelTruncate) label = label.slice(0, labelTruncate - 1) + '…'; edgeLabels.set(`${a}|${b}`, label); } }
90
+ else if (typeof it.id === 'string') nodeViol.add(it.id);
91
+ }
92
+ highlight = { nodes: nodeViol, edges: edgeViol, color: violationColor };
93
+ if (onlyViolations) {
94
+ const pruned: Map<string, Set<string>> = new Map();
95
+ for (const [u, vs] of h) {
96
+ for (const v of vs) {
97
+ if (edgeViol.has(`${u}|${v}`) || nodeViol.has(u) || nodeViol.has(v)) { if (!pruned.has(u)) pruned.set(u, new Set()); pruned.get(u)!.add(v); if (!pruned.has(v)) pruned.set(v, new Set()); }
98
+ }
99
+ }
100
+ h = pruned;
101
+ }
102
+ }
103
+ const bpLabel = breakpoint ? ` @${breakpoint}` : '';
104
+ const title = `${name}${suffix ? ' ' + suffix : ''}${bpLabel} (Hasse)`;
105
+ const mermaidContent = toMermaidHasseStyled(h, { title, highlight, labels: edgeLabels });
106
+ const dotContent = toDotHasseStyled(h, { title, highlight, labels: edgeLabels });
107
+ mkdirSync(outDir, { recursive: true });
108
+ if (bundle) {
109
+ const mFile = baseFile.replace(/\.dot$/,'-bundle.mmd').replace(/\.mmd$/,'-bundle.mmd');
110
+ const dFile = baseFile.replace(/\.mmd$/,'-bundle.dot').replace(/\.dot$/,'-bundle.dot');
111
+ writeFileSync(mFile, mermaidContent);
112
+ writeFileSync(dFile, dotContent);
113
+ }
114
+ const baseContent = baseFmt === 'mermaid' ? mermaidContent : dotContent;
115
+ writeFileSync(baseFile, baseContent);
116
+ if (fmt === 'svg' || fmt === 'png') {
117
+ const imgFile = `${outDir}/${name}${suffix}-hasse.${fmt}`;
118
+ const { ok, hint } = exportGraphImage(baseFile, imgFile, fmt, imageFrom);
119
+ if (ok) console.log(`✓ Wrote ${baseFile} and ${imgFile}`); else console.log(`✓ Wrote ${baseFile} (image export skipped). ${hint}`);
120
+ } else {
121
+ const hasViolations = highlight && (highlight.edges.size > 0 || highlight.nodes.size > 0);
122
+ const message = (onlyViolations || highlightViolations) && !hasViolations ? `✓ Wrote ${baseFile} (no violations in slice)` : `✓ Wrote ${baseFile}`;
123
+ console.log(message);
124
+ }
125
+ } catch (error) { console.error(`❌ Error generating Hasse diagram for ${breakpoint}:`, error); process.exit(1); }
126
+ }
127
+ return; }
128
+ for (const breakpoint of plan) {
129
+ const { loadTokensWithBreakpoint } = await import('../../core/breakpoints.js');
130
+ const tokens = loadTokensWithBreakpoint(breakpoint); const { edges } = flattenTokens(tokens);
131
+ let filteredEdges = edges;
132
+ if (options.filter) { const filterRegex = new RegExp(options.filter); filteredEdges = edges.filter(([from,to]) => filterRegex.test(from) || filterRegex.test(to)); }
133
+ const format = options.format || 'json'; const graph = generateDependencyGraph(filteredEdges, format);
134
+ if (options.output) { const bpSuffix = breakpoint ? `.${breakpoint}` : ''; const outputPath = options.output.replace(/(\.[^.]+)$/ , `${bpSuffix}$1`); writeFileSync(outputPath, graph, 'utf8'); console.log(`Dependency graph written to: ${outputPath}`); }
135
+ else { if (breakpoint) console.log(`\n=== ${breakpoint.toUpperCase()} ===`); console.log(graph); }
136
+ }
137
+ }
@@ -0,0 +1,8 @@
1
+ export { setCommand } from './set.js';
2
+ export { buildCommand } from './build.js';
3
+ export { validateCommand } from './validate.js';
4
+ export { graphCommand } from './graph.js';
5
+ export { whyCommand } from './why.js';
6
+ export { patchCommand } from './patch.js';
7
+ export { patchApplyCommand } from './patch-apply.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { setCommand } from './set.js';
2
+ export { buildCommand } from './build.js';
3
+ export { validateCommand } from './validate.js';
4
+ export { graphCommand } from './graph.js';
5
+ export { whyCommand } from './why.js';
6
+ export { patchCommand } from './patch.js';
7
+ export { patchApplyCommand } from './patch-apply.js';
@@ -0,0 +1,7 @@
1
+ export { setCommand } from './set.js';
2
+ export { buildCommand } from './build.js';
3
+ export { validateCommand } from './validate.js';
4
+ export { graphCommand } from './graph.js';
5
+ export { whyCommand } from './why.js';
6
+ export { patchCommand } from './patch.js';
7
+ export { patchApplyCommand } from './patch-apply.js';
@@ -0,0 +1,3 @@
1
+ import type { PatchApplyOptions } from '../types.js';
2
+ export declare function patchApplyCommand(opts: PatchApplyOptions): Promise<void>;
3
+ //# sourceMappingURL=patch-apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch-apply.d.ts","sourceRoot":"","sources":["patch-apply.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAkCrD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4C9E"}
@@ -0,0 +1,75 @@
1
+ import { loadTokens, outputResult } from './utils.js';
2
+ import fs from 'node:fs';
3
+ import { flattenTokens } from '../../core/flatten.js';
4
+ import { createHash } from 'node:crypto';
5
+ function applyChange(root, id, to, type) {
6
+ const parts = id.split('.');
7
+ let cur = root;
8
+ for (let i = 0; i < parts.length; i++) {
9
+ const p = parts[i];
10
+ if (i === parts.length - 1) {
11
+ if (type === 'remove') {
12
+ if (cur[p] && typeof cur[p] === 'object') {
13
+ delete cur[p].$value; // delete leaf value
14
+ }
15
+ }
16
+ else {
17
+ if (!cur[p] || typeof cur[p] !== 'object')
18
+ cur[p] = {};
19
+ cur[p].$value = to;
20
+ }
21
+ }
22
+ else {
23
+ if (!cur[p] || typeof cur[p] !== 'object')
24
+ cur[p] = {};
25
+ cur = cur[p];
26
+ }
27
+ }
28
+ }
29
+ export async function patchApplyCommand(opts) {
30
+ const tokens = loadTokens(opts.tokens || 'tokens/tokens.example.json');
31
+ // Compute current base tokens hash for drift detection (same logic as buildPatch)
32
+ function computeBaseHash(toks) {
33
+ const flat = flattenTokens(JSON.parse(JSON.stringify(toks))).flat;
34
+ const values = {};
35
+ Object.keys(flat).sort().forEach(id => { values[id] = flat[id]?.value; });
36
+ // Keep deterministic ordering
37
+ const ordered = Object.keys(values).sort().reduce((acc, k) => { acc[k] = values[k]; return acc; }, {});
38
+ return createHash('sha256').update(JSON.stringify(ordered)).digest('hex');
39
+ }
40
+ // Parse patch
41
+ let patchDoc;
42
+ if (fs.existsSync(opts.patch)) {
43
+ patchDoc = JSON.parse(fs.readFileSync(opts.patch, 'utf8'));
44
+ }
45
+ else if (opts.patch.trim().startsWith('{')) {
46
+ patchDoc = JSON.parse(opts.patch);
47
+ }
48
+ else {
49
+ throw new Error(`Patch not found: ${opts.patch}`);
50
+ }
51
+ if (patchDoc.version !== 1)
52
+ throw new Error('Unsupported patch version');
53
+ if (patchDoc.baseTokensHash) {
54
+ const currentHash = computeBaseHash(tokens);
55
+ if (currentHash !== patchDoc.baseTokensHash) {
56
+ console.warn(`⚠ Base tokens hash mismatch. Patch built against ${patchDoc.baseTokensHash} but current base is ${currentHash}. Proceeding (use --dry-run to inspect first).`);
57
+ }
58
+ }
59
+ // Apply changes
60
+ for (const c of patchDoc.changes) {
61
+ applyChange(tokens, c.id, c.to, c.type);
62
+ }
63
+ if (opts.dryRun) {
64
+ outputResult(tokens, 'json');
65
+ return;
66
+ }
67
+ if (opts.output) {
68
+ fs.writeFileSync(opts.output, JSON.stringify(tokens, null, 2));
69
+ if (!opts.quiet)
70
+ console.log(`✔ Patch applied to ${opts.output}`);
71
+ }
72
+ else {
73
+ outputResult(tokens, 'json');
74
+ }
75
+ }
@@ -0,0 +1,80 @@
1
+ import { loadTokens, outputResult } from './utils.js';
2
+ import type { PatchApplyOptions } from '../types.js';
3
+ import type { TokenNode } from '../../core/flatten.js';
4
+ import fs from 'node:fs';
5
+ import { flattenTokens } from '../../core/flatten.js';
6
+ import { createHash } from 'node:crypto';
7
+
8
+ interface PatchDocumentV1 {
9
+ version: 1;
10
+ changes: Array<{ id: string; from: any; to: any; type: 'modify'|'add'|'remove' }>;
11
+ patch: Record<string, any>;
12
+ baseTokensHash?: string;
13
+ }
14
+
15
+ function applyChange(root: any, id: string, to: any, type: 'modify'|'add'|'remove') {
16
+ const parts = id.split('.');
17
+ let cur: any = root;
18
+ for (let i = 0; i < parts.length; i++) {
19
+ const p = parts[i];
20
+ if (i === parts.length - 1) {
21
+ if (type === 'remove') {
22
+ if (cur[p] && typeof cur[p] === 'object') {
23
+ delete cur[p].$value; // delete leaf value
24
+ }
25
+ } else {
26
+ if (!cur[p] || typeof cur[p] !== 'object') cur[p] = {};
27
+ cur[p].$value = to;
28
+ }
29
+ } else {
30
+ if (!cur[p] || typeof cur[p] !== 'object') cur[p] = {};
31
+ cur = cur[p];
32
+ }
33
+ }
34
+ }
35
+
36
+ export async function patchApplyCommand(opts: PatchApplyOptions): Promise<void> {
37
+ const tokens: TokenNode = loadTokens(opts.tokens || 'tokens/tokens.example.json');
38
+ // Compute current base tokens hash for drift detection (same logic as buildPatch)
39
+ function computeBaseHash(toks: TokenNode): string {
40
+ const flat = flattenTokens(JSON.parse(JSON.stringify(toks))).flat as Record<string, any>;
41
+ const values: Record<string, any> = {};
42
+ Object.keys(flat).sort().forEach(id => { values[id] = flat[id]?.value; });
43
+ // Keep deterministic ordering
44
+ const ordered = Object.keys(values).sort().reduce((acc, k) => { acc[k] = values[k]; return acc; }, {} as Record<string, any>);
45
+ return createHash('sha256').update(JSON.stringify(ordered)).digest('hex');
46
+ }
47
+ // Parse patch
48
+ let patchDoc: PatchDocumentV1;
49
+ if (fs.existsSync(opts.patch)) {
50
+ patchDoc = JSON.parse(fs.readFileSync(opts.patch, 'utf8'));
51
+ } else if (opts.patch.trim().startsWith('{')) {
52
+ patchDoc = JSON.parse(opts.patch);
53
+ } else {
54
+ throw new Error(`Patch not found: ${opts.patch}`);
55
+ }
56
+ if (patchDoc.version !== 1) throw new Error('Unsupported patch version');
57
+ if (patchDoc.baseTokensHash) {
58
+ const currentHash = computeBaseHash(tokens);
59
+ if (currentHash !== patchDoc.baseTokensHash) {
60
+ console.warn(`⚠ Base tokens hash mismatch. Patch built against ${patchDoc.baseTokensHash} but current base is ${currentHash}. Proceeding (use --dry-run to inspect first).`);
61
+ }
62
+ }
63
+
64
+ // Apply changes
65
+ for (const c of patchDoc.changes) {
66
+ applyChange(tokens, c.id, c.to, c.type);
67
+ }
68
+
69
+ if (opts.dryRun) {
70
+ outputResult(tokens, 'json');
71
+ return;
72
+ }
73
+
74
+ if (opts.output) {
75
+ fs.writeFileSync(opts.output, JSON.stringify(tokens, null, 2));
76
+ if (!opts.quiet) console.log(`✔ Patch applied to ${opts.output}`);
77
+ } else {
78
+ outputResult(tokens, 'json');
79
+ }
80
+ }
@@ -0,0 +1,3 @@
1
+ import type { PatchOptions } from '../types.js';
2
+ export declare function patchCommand(opts: PatchOptions): Promise<void>;
3
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["patch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBpE"}