@contentful/experience-design-system-cli 2.26.1-dev-build-a852362.0 → 2.26.1-dev-build-f03f014.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 (204) hide show
  1. package/dist/package.json +14 -10
  2. package/dist/src/index.js +34937 -9
  3. package/dist/src/lib/cli-path.d.ts +4 -0
  4. package/dist/src/runs/export-helpers.d.ts +1 -12
  5. package/dist/src/runs/push-helpers.d.ts +1 -6
  6. package/package.json +12 -7
  7. package/skills/generate-components.md +427 -0
  8. package/skills/generate-tokens.md +194 -0
  9. package/skills/select-components.md +224 -0
  10. package/dist/src/analytics/apply.js +0 -30
  11. package/dist/src/analytics/client.js +0 -81
  12. package/dist/src/analytics/constants.js +0 -4
  13. package/dist/src/analytics/env.js +0 -14
  14. package/dist/src/analytics/exit.js +0 -24
  15. package/dist/src/analytics/index.js +0 -9
  16. package/dist/src/analytics/normalize.js +0 -18
  17. package/dist/src/analytics/os.js +0 -13
  18. package/dist/src/analytics/session.js +0 -15
  19. package/dist/src/analytics/tracker.js +0 -126
  20. package/dist/src/analytics/types.js +0 -1
  21. package/dist/src/analyze/build-analyze-view-rows.js +0 -63
  22. package/dist/src/analyze/command.js +0 -599
  23. package/dist/src/analyze/composite-closure.js +0 -0
  24. package/dist/src/analyze/composition/agent-parser/author-prompt.js +0 -22
  25. package/dist/src/analyze/composition/agent-parser/extract-parser.js +0 -44
  26. package/dist/src/analyze/composition/agent-parser/load-prompt.js +0 -31
  27. package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +0 -88
  28. package/dist/src/analyze/composition/agent-parser/sandbox.js +0 -187
  29. package/dist/src/analyze/composition/apply-mapping.js +0 -67
  30. package/dist/src/analyze/composition/candidate-critic-agent.js +0 -39
  31. package/dist/src/analyze/composition/candidate-critic.js +0 -63
  32. package/dist/src/analyze/composition/candidate-files.js +0 -131
  33. package/dist/src/analyze/composition/composition-cache-key.js +0 -19
  34. package/dist/src/analyze/composition/interchange-schema.js +0 -61
  35. package/dist/src/analyze/composition/manifest-doc-evidence.js +0 -136
  36. package/dist/src/analyze/composition/merge-edges.js +0 -70
  37. package/dist/src/analyze/composition/parse-map-edges.js +0 -55
  38. package/dist/src/analyze/composition/resolve-mapping-cli.js +0 -29
  39. package/dist/src/analyze/composition/resolve-mapping.js +0 -111
  40. package/dist/src/analyze/cycle-detection.js +0 -234
  41. package/dist/src/analyze/cycle-view.js +0 -28
  42. package/dist/src/analyze/fuzzy-search.js +0 -55
  43. package/dist/src/analyze/issue-inheritance.js +0 -108
  44. package/dist/src/analyze/lineage.js +0 -0
  45. package/dist/src/analyze/scope-gate-cascade.js +0 -170
  46. package/dist/src/analyze/search-neighborhood.js +0 -56
  47. package/dist/src/analyze/select/command.js +0 -484
  48. package/dist/src/analyze/select/index.js +0 -5
  49. package/dist/src/analyze/select/parser.js +0 -53
  50. package/dist/src/analyze/select/persistence.js +0 -80
  51. package/dist/src/analyze/select/preview-annotations.js +0 -62
  52. package/dist/src/analyze/select/stdout.js +0 -3
  53. package/dist/src/analyze/select/tui/App.js +0 -483
  54. package/dist/src/analyze/select/tui/components/ComponentDetail.js +0 -48
  55. package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +0 -125
  56. package/dist/src/analyze/select/tui/components/FieldEditor.js +0 -1131
  57. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +0 -36
  58. package/dist/src/analyze/select/tui/components/GotoBanner.js +0 -30
  59. package/dist/src/analyze/select/tui/components/GroupedSidebar.js +0 -517
  60. package/dist/src/analyze/select/tui/components/HelpOverlay.js +0 -17
  61. package/dist/src/analyze/select/tui/components/JsonEditor.js +0 -131
  62. package/dist/src/analyze/select/tui/components/JsonPanel.js +0 -66
  63. package/dist/src/analyze/select/tui/components/LineagePanel.js +0 -22
  64. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +0 -29
  65. package/dist/src/analyze/select/tui/components/QuitDialog.js +0 -14
  66. package/dist/src/analyze/select/tui/components/RationalePanel.js +0 -80
  67. package/dist/src/analyze/select/tui/components/Sidebar.js +0 -92
  68. package/dist/src/analyze/select/tui/components/SourcePanel.js +0 -52
  69. package/dist/src/analyze/select/tui/components/StatusBar.js +0 -7
  70. package/dist/src/analyze/select/tui/components/TopBar.js +0 -10
  71. package/dist/src/analyze/select/tui/components/removed-components-text.js +0 -11
  72. package/dist/src/analyze/select/tui/hooks/scroll-offset.js +0 -26
  73. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +0 -66
  74. package/dist/src/analyze/select/tui/hooks/useKeymap.js +0 -67
  75. package/dist/src/analyze/select/tui/hooks/useSession.js +0 -52
  76. package/dist/src/analyze/select/tui/hooks/useUndo.js +0 -26
  77. package/dist/src/analyze/select/tui/theme.js +0 -28
  78. package/dist/src/analyze/select/types.js +0 -39
  79. package/dist/src/analyze/select-agent/command.js +0 -558
  80. package/dist/src/analyze/select-agent/context-builder.js +0 -197
  81. package/dist/src/analyze/select-agent/show-rationale.js +0 -92
  82. package/dist/src/analyze/selection-cascade.js +0 -19
  83. package/dist/src/analyze/slot-graph.js +0 -17
  84. package/dist/src/analyze/tui/AnalyzeView.js +0 -64
  85. package/dist/src/apply/api-client.js +0 -440
  86. package/dist/src/apply/command.js +0 -845
  87. package/dist/src/apply/manifest.js +0 -1
  88. package/dist/src/apply/preview-utils.js +0 -26
  89. package/dist/src/apply/tui/SelectView.js +0 -34
  90. package/dist/src/apply/tui/ServerApplyView.js +0 -49
  91. package/dist/src/apply/tui/ServerPreviewView.js +0 -23
  92. package/dist/src/credentials-store.js +0 -74
  93. package/dist/src/generate/command.js +0 -597
  94. package/dist/src/generate/edit/command.js +0 -127
  95. package/dist/src/generate/tui/GenerateView.js +0 -10
  96. package/dist/src/host-utils.js +0 -26
  97. package/dist/src/import/agent-model-resolve.js +0 -35
  98. package/dist/src/import/auto-filter-resolve.js +0 -16
  99. package/dist/src/import/command.js +0 -380
  100. package/dist/src/import/cycle-auto-reject.js +0 -14
  101. package/dist/src/import/orchestrator.js +0 -603
  102. package/dist/src/import/path-utils.js +0 -30
  103. package/dist/src/import/picker-dispatch.js +0 -44
  104. package/dist/src/import/print-prompt.js +0 -31
  105. package/dist/src/import/strip-allowed-components.js +0 -22
  106. package/dist/src/import/tui/CustomPromptBanner.js +0 -8
  107. package/dist/src/import/tui/WizardApp.js +0 -1670
  108. package/dist/src/import/tui/ai-flag.js +0 -6
  109. package/dist/src/import/tui/auto-filter-error.js +0 -29
  110. package/dist/src/import/tui/autocomplete.js +0 -32
  111. package/dist/src/import/tui/components/AutoFilterBanner.js +0 -19
  112. package/dist/src/import/tui/components/CounterStrip.js +0 -12
  113. package/dist/src/import/tui/components/LegendEntry.js +0 -6
  114. package/dist/src/import/tui/cycle-panel-scroll.js +0 -33
  115. package/dist/src/import/tui/final-review-host.js +0 -25
  116. package/dist/src/import/tui/group-collapse.js +0 -11
  117. package/dist/src/import/tui/history.js +0 -53
  118. package/dist/src/import/tui/hooks/useLineage.js +0 -50
  119. package/dist/src/import/tui/hooks/useOverlayPanel.js +0 -27
  120. package/dist/src/import/tui/lineage-layout.js +0 -82
  121. package/dist/src/import/tui/merge-ai-decisions.js +0 -22
  122. package/dist/src/import/tui/push-decision-gate-helpers.js +0 -8
  123. package/dist/src/import/tui/push-progress.js +0 -1
  124. package/dist/src/import/tui/run-print-files-helpers.js +0 -8
  125. package/dist/src/import/tui/run-teaser.js +0 -9
  126. package/dist/src/import/tui/runLivePreview.js +0 -82
  127. package/dist/src/import/tui/runScopeGate.js +0 -28
  128. package/dist/src/import/tui/scope-gate-columns.js +0 -94
  129. package/dist/src/import/tui/scope-gate-host.js +0 -28
  130. package/dist/src/import/tui/sidebar-width.js +0 -3
  131. package/dist/src/import/tui/spawn-generate.js +0 -53
  132. package/dist/src/import/tui/step-filters.js +0 -42
  133. package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +0 -726
  134. package/dist/src/import/tui/steps/AtomicScopeGateStep.js +0 -210
  135. package/dist/src/import/tui/steps/CredentialsStep.js +0 -108
  136. package/dist/src/import/tui/steps/DoneStep.js +0 -24
  137. package/dist/src/import/tui/steps/ErrorStep.js +0 -16
  138. package/dist/src/import/tui/steps/GateStep.js +0 -22
  139. package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -1618
  140. package/dist/src/import/tui/steps/PathValidationStep.js +0 -152
  141. package/dist/src/import/tui/steps/PreviewStep.js +0 -37
  142. package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +0 -23
  143. package/dist/src/import/tui/steps/PushDecisionGateStep.js +0 -73
  144. package/dist/src/import/tui/steps/PushingStep.js +0 -24
  145. package/dist/src/import/tui/steps/RunningStep.js +0 -21
  146. package/dist/src/import/tui/steps/ScopeGateStep.js +0 -907
  147. package/dist/src/import/tui/steps/TokenInputStep.js +0 -88
  148. package/dist/src/import/tui/steps/WelcomeStep.js +0 -34
  149. package/dist/src/import/tui/steps/WizardPreviewStep.js +0 -169
  150. package/dist/src/import/tui/steps/auto-reject-decision.js +0 -9
  151. package/dist/src/import/tui/steps/breaking-change-format.js +0 -24
  152. package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
  153. package/dist/src/import/tui/steps/preview-diff.js +0 -175
  154. package/dist/src/import/tui/useFinalizePreview.js +0 -0
  155. package/dist/src/import/tui/useLivePreview.js +0 -122
  156. package/dist/src/import/tui/wizard-422-helpers.js +0 -60
  157. package/dist/src/import/tui/wizard-generate-progress.js +0 -25
  158. package/dist/src/import/tui/wizard-save-flow.js +0 -20
  159. package/dist/src/import/tui/wizard-state-transitions.js +0 -32
  160. package/dist/src/lib/agent-model-options.js +0 -15
  161. package/dist/src/lib/command-options.js +0 -30
  162. package/dist/src/lib/composition-mode.js +0 -39
  163. package/dist/src/lib/contentful-urls.js +0 -27
  164. package/dist/src/lib/debug-logger.js +0 -229
  165. package/dist/src/lib/debug-preamble.js +0 -33
  166. package/dist/src/lib/error-parser.js +0 -269
  167. package/dist/src/lib/prompt-overrides.js +0 -69
  168. package/dist/src/lib/terminal-capabilities.js +0 -51
  169. package/dist/src/lib/user-agent.js +0 -29
  170. package/dist/src/output/format.js +0 -110
  171. package/dist/src/print/command.js +0 -221
  172. package/dist/src/print/validate/tui/ValidateView.js +0 -37
  173. package/dist/src/print/validate/validators/cdf-validator.js +0 -104
  174. package/dist/src/print/validate/validators/dtcg-validator.js +0 -110
  175. package/dist/src/print/validate/validators/format-errors.js +0 -18
  176. package/dist/src/program.js +0 -105
  177. package/dist/src/runs/export-helpers.js +0 -59
  178. package/dist/src/runs/fingerprint.js +0 -75
  179. package/dist/src/runs/ls-command.js +0 -137
  180. package/dist/src/runs/modify-launcher.js +0 -42
  181. package/dist/src/runs/path-prompt.js +0 -72
  182. package/dist/src/runs/push-creds-prompt.js +0 -39
  183. package/dist/src/runs/push-helpers.js +0 -72
  184. package/dist/src/runs/push-launcher.js +0 -77
  185. package/dist/src/runs/replay-helpers.js +0 -164
  186. package/dist/src/runs/resolve-run-target.js +0 -45
  187. package/dist/src/runs/run-picker-mount.js +0 -72
  188. package/dist/src/runs/run-picker.js +0 -129
  189. package/dist/src/runs/save-conflict.js +0 -56
  190. package/dist/src/runs/save-path-resolver.js +0 -75
  191. package/dist/src/runs/staleness.js +0 -140
  192. package/dist/src/runs/store.js +0 -136
  193. package/dist/src/session/cache-keys.js +0 -44
  194. package/dist/src/session/command.js +0 -261
  195. package/dist/src/session/db.js +0 -1538
  196. package/dist/src/session/migration.js +0 -117
  197. package/dist/src/session/session-id.js +0 -212
  198. package/dist/src/session/stats.js +0 -89
  199. package/dist/src/setup/analytics-prompt.js +0 -24
  200. package/dist/src/setup/auto-filter-prompt.js +0 -24
  201. package/dist/src/setup/command.js +0 -966
  202. package/dist/src/setup/composition-mode-prompt.js +0 -37
  203. package/dist/src/setup/debug-mode-prompt.js +0 -22
  204. package/dist/src/types.js +0 -1
@@ -1,221 +0,0 @@
1
- import { createElement } from 'react';
2
- import { render } from 'ink';
3
- import { access, mkdir, stat, writeFile } from 'node:fs/promises';
4
- import { basename, resolve } from 'node:path';
5
- import { openPipelineDb, loadCDFComponents, loadDTCGTokens, findLatestSessionForCommand } from '../session/db.js';
6
- import { validateCDFFile } from './validate/validators/cdf-validator.js';
7
- import { validateDTCGTokenFile } from './validate/validators/dtcg-validator.js';
8
- import { formatDiagnostics } from './validate/validators/format-errors.js';
9
- import { ValidateView } from './validate/tui/ValidateView.js';
10
- import { getInteractiveTerminalSupport } from '../lib/terminal-capabilities.js';
11
- import { bindAnalyticsSessionId, exitWithAnalytics } from '../analytics/index.js';
12
- async function die(message) {
13
- process.stderr.write(`${message}\n`);
14
- return exitWithAnalytics(1);
15
- }
16
- async function pathExists(p) {
17
- return access(p)
18
- .then(() => true)
19
- .catch(() => false);
20
- }
21
- async function assertOutIsNotDirectory(outPath) {
22
- if (await pathExists(outPath)) {
23
- const s = await stat(outPath);
24
- if (s.isDirectory())
25
- await die(`Error: --out must be a file path, not a directory: ${outPath}`);
26
- }
27
- }
28
- async function resolveSession(sessionFlag, command) {
29
- const db = openPipelineDb();
30
- try {
31
- const sessionId = sessionFlag ?? findLatestSessionForCommand(db, command);
32
- if (!sessionId) {
33
- const hint = command === 'generate components' ? 'generate components' : 'generate tokens';
34
- return await die(`Error: no completed ${hint} session found. Run ${hint} first, or pass --session <id>.`);
35
- }
36
- return sessionId;
37
- }
38
- finally {
39
- db.close();
40
- }
41
- }
42
- function rebuildDTCGTree(groups, tokens) {
43
- const root = {};
44
- // Apply group descriptions
45
- for (const group of groups) {
46
- const segments = group.path.split('.');
47
- let node = root;
48
- for (const seg of segments) {
49
- if (typeof node[seg] !== 'object' || node[seg] === null) {
50
- node[seg] = {};
51
- }
52
- node = node[seg];
53
- }
54
- if (group.$description) {
55
- node['$description'] = group.$description;
56
- }
57
- }
58
- // Place leaf tokens
59
- for (const token of tokens) {
60
- const segments = token.path.split('.');
61
- const leafKey = segments[segments.length - 1];
62
- const parentSegments = segments.slice(0, -1);
63
- let node = root;
64
- for (const seg of parentSegments) {
65
- if (typeof node[seg] !== 'object' || node[seg] === null) {
66
- node[seg] = {};
67
- }
68
- node = node[seg];
69
- }
70
- const leaf = { $type: token.$type, $value: token.$value };
71
- if (token.$description)
72
- leaf['$description'] = token.$description;
73
- node[leafKey] = leaf;
74
- }
75
- return root;
76
- }
77
- export function registerPrintCommand(program) {
78
- const print = program.command('print').description('Write pipeline artifacts to JSON files or validate them');
79
- // print components
80
- print
81
- .command('components')
82
- .description('Write generated components to a CDF JSON file')
83
- .option('--session <id>', 'Session ID to print from (default: most recent generate components session)')
84
- .option('--out <path>', 'Output file path', 'components.json')
85
- .option('--allow-empty', 'Write an empty-but-present components manifest when no components are accepted (a subsequent push then removes ALL components from the target space). Without this, an empty accepted set is an error.')
86
- .action(async (opts) => {
87
- const outPath = resolve(opts.out);
88
- await assertOutIsNotDirectory(outPath);
89
- const sessionId = await resolveSession(opts.session, 'generate components');
90
- await bindAnalyticsSessionId(sessionId);
91
- const db = openPipelineDb();
92
- let components;
93
- let generateStepStatus = null;
94
- let rejectedCount = 0;
95
- try {
96
- components = loadCDFComponents(db, sessionId);
97
- const stepRow = db
98
- .prepare(`SELECT status FROM steps WHERE session_id = ? AND command = 'generate components' ORDER BY id DESC LIMIT 1`)
99
- .get(sessionId);
100
- generateStepStatus = stepRow?.status ?? null;
101
- const rejectedRow = db
102
- .prepare(`SELECT COUNT(*) AS n FROM raw_components WHERE session_id = ? AND status = 'generate-rejected'`)
103
- .get(sessionId);
104
- rejectedCount = rejectedRow?.n ?? 0;
105
- }
106
- finally {
107
- db.close();
108
- }
109
- if (components.length === 0) {
110
- if (rejectedCount > 0) {
111
- // Components WERE generated, but final review left none accepted — every
112
- // component was rejected or left unresolved. This is a legitimate
113
- // "clear the space" intent, but it's destructive, so require --allow-empty.
114
- if (!opts.allowEmpty) {
115
- await die(`Error: all ${rejectedCount} generated component${rejectedCount === 1 ? ' was' : 's were'} rejected or left unresolved at final review in session '${sessionId}', so there is nothing to save. Accept at least one component (press [a] on a row, or [A] to accept all), or pass --allow-empty to write an empty manifest that will DELETE all components from the target space on push.`);
116
- }
117
- // Fall through: write an empty-but-present components manifest so a
118
- // subsequent push removes every component from the target space.
119
- }
120
- else {
121
- await die(`Error: no generated components in session '${sessionId}'. Run generate components first.`);
122
- }
123
- }
124
- if (generateStepStatus === 'failed') {
125
- process.stderr.write(`Warning: session '${sessionId}' generate step failed — output may be incomplete (${components.length} components found)\n`);
126
- }
127
- const cdfObj = { $schema: 'https://contentful.com/schemas/cdf/v1' };
128
- const missingDescription = [];
129
- for (const { key, entry } of components) {
130
- cdfObj[key] = entry;
131
- if (!entry.$description)
132
- missingDescription.push(key);
133
- }
134
- if (missingDescription.length > 0) {
135
- process.stderr.write(`Warning: ${missingDescription.length} component${missingDescription.length === 1 ? '' : 's'} missing $description (will fail at apply push): ${missingDescription.join(', ')}\n`);
136
- }
137
- await mkdir(resolve(outPath, '..'), { recursive: true });
138
- await writeFile(outPath, `${JSON.stringify(cdfObj, null, 2)}\n`);
139
- process.stdout.write(`wrote ${basename(outPath)} (${components.length} component${components.length === 1 ? '' : 's'})\n`);
140
- });
141
- // print tokens
142
- print
143
- .command('tokens')
144
- .description('Write generated tokens to a DTCG JSON file')
145
- .option('--session <id>', 'Session ID to print from (default: most recent generate tokens session)')
146
- .option('--out <path>', 'Output file path', 'tokens.json')
147
- .action(async (opts) => {
148
- const outPath = resolve(opts.out);
149
- await assertOutIsNotDirectory(outPath);
150
- const sessionId = await resolveSession(opts.session, 'generate tokens');
151
- await bindAnalyticsSessionId(sessionId);
152
- const db = openPipelineDb();
153
- let result;
154
- try {
155
- result = loadDTCGTokens(db, sessionId);
156
- }
157
- finally {
158
- db.close();
159
- }
160
- if (result.tokens.length === 0) {
161
- await die(`Error: no generated tokens in session '${sessionId}'. Run generate tokens first.`);
162
- }
163
- const tree = rebuildDTCGTree(result.groups, result.tokens);
164
- await mkdir(resolve(outPath, '..'), { recursive: true });
165
- await writeFile(outPath, `${JSON.stringify(tree, null, 2)}\n`);
166
- process.stdout.write(`wrote ${basename(outPath)} (${result.tokens.length} token${result.tokens.length === 1 ? '' : 's'})\n`);
167
- });
168
- // print validate
169
- print
170
- .command('validate')
171
- .description('Validate CDF or DTCG files against their schemas')
172
- .option('--components <path>', 'Path to CDF components file')
173
- .option('--tokens <path>', 'Path to DTCG tokens file')
174
- .action(async (opts) => {
175
- if (!opts.components && !opts.tokens) {
176
- process.stderr.write('Error: at least one of --components or --tokens is required.\n\nUsage: print validate [--components <path>] [--tokens <path>]\n');
177
- await exitWithAnalytics(1);
178
- }
179
- const viewResults = [];
180
- if (opts.components) {
181
- const r = await validateCDFFile(opts.components);
182
- viewResults.push({
183
- filePath: opts.components,
184
- format: 'CDF v1',
185
- valid: r.valid,
186
- summary: r.summary,
187
- diagnostics: r.diagnostics,
188
- });
189
- }
190
- if (opts.tokens) {
191
- const r = await validateDTCGTokenFile(opts.tokens);
192
- viewResults.push({
193
- filePath: opts.tokens,
194
- format: 'DTCG',
195
- valid: r.valid,
196
- summary: r.summary,
197
- diagnostics: r.diagnostics,
198
- });
199
- }
200
- const failed = viewResults.some((r) => !r.valid);
201
- const exitCode = failed ? 1 : 0;
202
- if (getInteractiveTerminalSupport().supported) {
203
- const { waitUntilExit } = render(createElement(ValidateView, {
204
- results: viewResults,
205
- onExit: () => void exitWithAnalytics(exitCode),
206
- }));
207
- await waitUntilExit();
208
- }
209
- else {
210
- const output = viewResults
211
- .map((r) => formatDiagnostics({
212
- valid: r.valid,
213
- summary: r.summary ?? r.filePath,
214
- diagnostics: r.diagnostics,
215
- }))
216
- .join('\n\n');
217
- process.stdout.write(output + '\n');
218
- await exitWithAnalytics(exitCode);
219
- }
220
- });
221
- }
@@ -1,37 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { TopBar } from '../../../analyze/select/tui/components/TopBar.js';
5
- import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
6
- export function ValidateView({ results, onExit }) {
7
- const [scrollOffset, setScrollOffset] = useState(0);
8
- const allValid = results.every((r) => r.valid);
9
- useImmediateInput((input, key) => {
10
- if (input === 'q' || key.return) {
11
- onExit();
12
- return;
13
- }
14
- if (key.upArrow || input === 'k') {
15
- setScrollOffset((o) => Math.max(0, o - 1));
16
- }
17
- else if (key.downArrow || input === 'j') {
18
- setScrollOffset((o) => o + 1);
19
- }
20
- else if (input === 'g') {
21
- setScrollOffset(0);
22
- }
23
- else if (input === 'G') {
24
- setScrollOffset(100);
25
- }
26
- });
27
- const invalidResults = results.filter((r) => !r.valid);
28
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(TopBar, { subcommand: "validate", hints: [
29
- { key: '?', label: 'help' },
30
- { key: 'q', label: 'quit' },
31
- ] }), _jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [results.map((r) => {
32
- const summaryText = r.summary || (r.valid ? 'valid' : (r.diagnostics[0]?.message ?? 'invalid'));
33
- return (_jsxs(Box, { children: [_jsx(Text, { color: r.valid ? 'green' : 'red', children: r.valid ? '✓' : '✗' }), _jsx(Text, { children: ' ' + r.filePath.split('/').pop() }), _jsx(Text, { dimColor: true, children: ' ' + r.format + ' ' }), _jsx(Text, { color: r.valid ? 'green' : 'red', children: summaryText })] }, r.filePath));
34
- }), invalidResults.length > 0 && (_jsx(_Fragment, { children: invalidResults.map((r) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, children: r.filePath.split('/').pop() + ' errors' }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), r.diagnostics.slice(scrollOffset).map((d, i) => (_jsx(Box, { children: _jsx(Text, { color: "red", children: ' ✗ ' + d.path + ': ' + d.message }) }, i)))] }, r.filePath))) }))] }), _jsxs(Box, { borderStyle: "single", paddingX: 1, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: allValid
35
- ? 'All files valid'
36
- : invalidResults.length + ' file' + (invalidResults.length === 1 ? '' : 's') + ' invalid' }), _jsx(Text, { dimColor: true, children: allValid ? '[q]' : 'scroll ↑↓ [q]' })] })] }));
37
- }
@@ -1,104 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { validateCDF } from '@contentful/experience-design-system-types';
3
- function extractValue(input, path) {
4
- if (path === '/')
5
- return undefined;
6
- const parts = path.split('/').filter(Boolean);
7
- let current = input;
8
- for (const part of parts) {
9
- if (typeof current !== 'object' || current === null)
10
- return undefined;
11
- current = current[part];
12
- }
13
- if (current === undefined)
14
- return undefined;
15
- return typeof current === 'string' ? current : JSON.stringify(current);
16
- }
17
- function inferParamsShape(expected) {
18
- if (!expected)
19
- return null;
20
- try {
21
- return JSON.parse(expected);
22
- }
23
- catch {
24
- return null;
25
- }
26
- }
27
- function rewriteDiagnostic(error, input) {
28
- const params = inferParamsShape(error.expected);
29
- if (!params) {
30
- return { ...error };
31
- }
32
- let { path } = error;
33
- let message = error.message;
34
- let expected = error.expected;
35
- let actual = error.actual;
36
- if ('missingProperty' in params) {
37
- const prop = params.missingProperty;
38
- path = `${path}/${prop}`;
39
- message = `Missing required property "${prop}"`;
40
- expected = undefined;
41
- actual = undefined;
42
- }
43
- else if ('allowedValues' in params) {
44
- const values = params.allowedValues;
45
- const fieldName = path.split('/').pop() ?? path;
46
- message = `Invalid ${fieldName.replace(/^\$/, '')}`;
47
- expected = values.join(', ');
48
- actual = extractValue(input, path);
49
- }
50
- else if ('allowedValue' in params) {
51
- message = 'Invalid value';
52
- expected = String(params.allowedValue);
53
- actual = extractValue(input, path);
54
- }
55
- else {
56
- expected = JSON.stringify(params);
57
- }
58
- return {
59
- path,
60
- message,
61
- ...(expected !== undefined && { expected }),
62
- ...(actual !== undefined && { actual }),
63
- };
64
- }
65
- export async function validateCDFFile(filePath) {
66
- let content;
67
- try {
68
- content = await readFile(filePath, 'utf-8');
69
- }
70
- catch (err) {
71
- return {
72
- valid: false,
73
- summary: '',
74
- diagnostics: [{ path: filePath, message: err.message }],
75
- };
76
- }
77
- let parsed;
78
- try {
79
- parsed = JSON.parse(content);
80
- }
81
- catch (err) {
82
- return {
83
- valid: false,
84
- summary: '',
85
- diagnostics: [{ path: filePath, message: `Invalid JSON: ${err.message}` }],
86
- };
87
- }
88
- const cdfResult = validateCDF(parsed);
89
- if (!cdfResult.valid) {
90
- const diagnostics = cdfResult.errors
91
- .filter((e) => {
92
- const params = inferParamsShape(e.expected);
93
- return !params || !('passingSchemas' in params);
94
- })
95
- .map((e) => rewriteDiagnostic(e, parsed));
96
- return { valid: false, summary: '', diagnostics };
97
- }
98
- const count = cdfResult.components.length;
99
- return {
100
- valid: true,
101
- summary: `Valid CDF v1 — ${count} component${count === 1 ? '' : 's'} found`,
102
- diagnostics: [],
103
- };
104
- }
@@ -1,110 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { DESIGN_TOKEN_TYPES } from '@contentful/experience-design-system-types';
3
- /*
4
- * Strict DTCG subset validation.
5
- *
6
- * This validator is intentionally stricter than both the W3C DTCG spec and the
7
- * TypeScript types in @contentful/experience-design-system-types:
8
- *
9
- * - No group-level type inheritance. The DTCG spec allows $type on a group to
10
- * be inherited by descendants. This validator requires explicit $type on
11
- * every leaf token.
12
- *
13
- * - No string values at non-$ keys. DTCGTokenGroupNode allows string | undefined
14
- * at arbitrary keys, but this validator rejects non-object values at non-$
15
- * positions.
16
- *
17
- * Both restrictions exist because agent-generated files should be maximally
18
- * explicit and self-describing.
19
- */
20
- function walkDTCG(obj, path, diagnostics, counts) {
21
- for (const [key, value] of Object.entries(obj)) {
22
- if (key.startsWith('$'))
23
- continue;
24
- const currentPath = `${path}/${key}`;
25
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
26
- diagnostics.push({
27
- path: currentPath,
28
- message: `Expected object, got ${value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value}`,
29
- });
30
- continue;
31
- }
32
- const node = value;
33
- const hasValue = '$value' in node;
34
- const hasType = '$type' in node;
35
- if (hasValue) {
36
- counts.tokens++;
37
- if (!hasType) {
38
- diagnostics.push({
39
- path: currentPath,
40
- message: 'Token has "$value" but missing required "$type"',
41
- });
42
- }
43
- else if (typeof node.$type !== 'string' || !DESIGN_TOKEN_TYPES.includes(node.$type)) {
44
- diagnostics.push({
45
- path: `${currentPath}/$type`,
46
- message: 'Invalid design token type',
47
- expected: DESIGN_TOKEN_TYPES.join(', '),
48
- actual: String(node.$type),
49
- });
50
- }
51
- }
52
- else if (hasType) {
53
- diagnostics.push({
54
- path: currentPath,
55
- message: 'Node has "$type" but missing required "$value" (leaf tokens must have both)',
56
- });
57
- }
58
- else {
59
- counts.groups++;
60
- walkDTCG(node, currentPath, diagnostics, counts);
61
- }
62
- }
63
- }
64
- export async function validateDTCGTokenFile(filePath) {
65
- let content;
66
- try {
67
- content = await readFile(filePath, 'utf-8');
68
- }
69
- catch (err) {
70
- return {
71
- valid: false,
72
- summary: '',
73
- diagnostics: [{ path: filePath, message: err.message }],
74
- };
75
- }
76
- let parsed;
77
- try {
78
- parsed = JSON.parse(content);
79
- }
80
- catch (err) {
81
- return {
82
- valid: false,
83
- summary: '',
84
- diagnostics: [{ path: filePath, message: `Invalid JSON: ${err.message}` }],
85
- };
86
- }
87
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
88
- return {
89
- valid: false,
90
- summary: '',
91
- diagnostics: [
92
- {
93
- path: '/',
94
- message: `Expected object, got ${parsed === null ? 'null' : Array.isArray(parsed) ? 'array' : typeof parsed}`,
95
- },
96
- ],
97
- };
98
- }
99
- const diagnostics = [];
100
- const counts = { tokens: 0, groups: 0 };
101
- walkDTCG(parsed, '', diagnostics, counts);
102
- if (diagnostics.length > 0) {
103
- return { valid: false, summary: '', diagnostics };
104
- }
105
- return {
106
- valid: true,
107
- summary: `Valid DTCG token file — ${counts.tokens} token${counts.tokens === 1 ? '' : 's'} in ${counts.groups} group${counts.groups === 1 ? '' : 's'}`,
108
- diagnostics: [],
109
- };
110
- }
@@ -1,18 +0,0 @@
1
- export function formatDiagnostics(result) {
2
- if (result.valid) {
3
- return `✓ ${result.summary}`;
4
- }
5
- const count = result.diagnostics.length;
6
- const header = `✗ ${count} error${count === 1 ? '' : 's'} found`;
7
- const entries = result.diagnostics.map((d, i) => {
8
- const lines = [` ${i + 1}. ${d.path}`, ` ${d.message}`];
9
- if (d.expected !== undefined) {
10
- lines.push(` expected: ${d.expected}`);
11
- }
12
- if (d.actual !== undefined) {
13
- lines.push(` actual: ${d.actual}`);
14
- }
15
- return lines.join('\n');
16
- });
17
- return `${header}\n\n${entries.join('\n\n')}`;
18
- }
@@ -1,105 +0,0 @@
1
- import { createRequire } from 'node:module';
2
- import { spawn } from 'node:child_process';
3
- import { fileURLToPath } from 'node:url';
4
- import { dirname, resolve } from 'node:path';
5
- import { Command } from 'commander';
6
- import { registerAnalyzeCommand } from './analyze/command.js';
7
- import { registerGenerateCommand } from './generate/command.js';
8
- import { registerApplyCommand } from './apply/command.js';
9
- import { registerSessionCommand } from './session/command.js';
10
- import { registerPrintCommand } from './print/command.js';
11
- import { registerImportCommand } from './import/command.js';
12
- import { registerSetupCommand } from './setup/command.js';
13
- import { registerRunsCommand } from './runs/ls-command.js';
14
- import { beginCommand } from './lib/debug-preamble.js';
15
- import { completeActiveCommand, flushAnalytics, noteCommandStart, setPersistedAnalyticsDisabled, } from './analytics/index.js';
16
- import { readExperiencesCredentials } from './credentials-store.js';
17
- const require = createRequire(import.meta.url);
18
- const pkg = require('../package.json');
19
- /**
20
- * Run the spawn lifecycle for the build command. Extracted so the
21
- * error-surfacing path is testable: previously `child.on('error', ...)`
22
- * silently returned exit code 1 with no context, leaving the user
23
- * staring at a generic non-zero exit when `pnpm` wasn't on PATH.
24
- */
25
- export async function runBuild(opts) {
26
- return new Promise((resolvePromise) => {
27
- const child = opts.spawnFn();
28
- let settled = false;
29
- const settle = (exitCode) => {
30
- if (settled)
31
- return;
32
- settled = true;
33
- resolvePromise({ exitCode });
34
- };
35
- child.on('error', (err) => {
36
- const message = err instanceof Error ? err.message : String(err);
37
- opts.stderrWrite(`Error: failed to spawn build subprocess: ${message}\n`);
38
- settle(1);
39
- });
40
- child.on('exit', (code) => settle(code ?? 1));
41
- });
42
- }
43
- function registerBuildCommand(program) {
44
- program
45
- .command('build')
46
- .description('Rebuild from source and re-link exo/experiences binaries to this build')
47
- .action(async () => {
48
- const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
49
- process.stderr.write('⚙ Building from source...\n');
50
- const { exitCode } = await runBuild({
51
- spawnFn: () => spawn('pnpm', ['build'], { cwd: pkgRoot, stdio: 'inherit' }),
52
- stderrWrite: (s) => process.stderr.write(s),
53
- });
54
- process.exit(exitCode);
55
- });
56
- }
57
- export function createProgram() {
58
- const program = new Command()
59
- .name('experience-design-system-cli')
60
- .description('Static analysis, validation, generation, and import of Contentful design system artifacts')
61
- .version(pkg.version, '--version', 'Print version number');
62
- registerAnalyzeCommand(program);
63
- registerGenerateCommand(program);
64
- registerPrintCommand(program);
65
- registerApplyCommand(program);
66
- registerSessionCommand(program);
67
- registerImportCommand(program);
68
- registerSetupCommand(program);
69
- registerRunsCommand(program);
70
- registerBuildCommand(program);
71
- // Expose --debug on every subcommand. The flag is inherited automatically
72
- // via `option()` at program scope + `preAction` reading merged opts from all
73
- // ancestors. When set (or when EDSI_DEBUG / persisted config is on), the
74
- // process-wide DebugLogger is initialized before the subcommand action runs
75
- // and a bright-green "debug logs at <path>" banner is printed to stderr.
76
- program.option('--debug', 'Write a JSONL trace of every decision to ~/.contentful/experience-design-system-cli/debug/');
77
- program.option('--no-debug', 'Force debug logging off (overrides EDSI_DEBUG and persisted setup preference)');
78
- program.hook('preAction', async (_thisCommand, actionCommand) => {
79
- // Merge opts from actionCommand and all ancestors — root-level --debug
80
- // set alongside a subcommand ends up on the root command's opts, not the
81
- // subcommand's.
82
- let debug;
83
- for (let c = actionCommand; c; c = c.parent) {
84
- const opts = c.opts();
85
- if (opts.debug !== undefined) {
86
- debug = opts.debug;
87
- break;
88
- }
89
- }
90
- // Build a `command` label out of the actual subcommand chain (e.g. "apply push").
91
- const chain = [];
92
- for (let c = actionCommand; c && c.parent; c = c.parent)
93
- chain.unshift(c.name());
94
- const commandChain = chain.join(' ') || actionCommand.name();
95
- const { analyticsDisabled } = await readExperiencesCredentials();
96
- setPersistedAnalyticsDisabled(analyticsDisabled ?? false);
97
- noteCommandStart(commandChain);
98
- await beginCommand(commandChain, { ...(debug !== undefined ? { debug } : {}) });
99
- });
100
- program.hook('postAction', async () => {
101
- await completeActiveCommand();
102
- await flushAnalytics();
103
- });
104
- return program;
105
- }
@@ -1,59 +0,0 @@
1
- import { execFile } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
3
- import { dirname, join } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- /**
6
- * Resolve the path to the package's CLI binary (`<pkg>/bin/cli.js`).
7
- *
8
- * Walks up from this module's location until it finds a directory that
9
- * contains `bin/cli.js`. This is robust to layout differences between source
10
- * (`<pkg>/src/runs/export-helpers.ts`) and compiled output
11
- * (`<pkg>/dist/src/runs/export-helpers.js`) — the previous string-counted
12
- * `..` chain was off-by-one against the compiled layout, sending lookups to
13
- * `<pkg>/dist/bin/cli.js` (which does not exist).
14
- */
15
- function findCliPath() {
16
- let dir = dirname(fileURLToPath(import.meta.url));
17
- // Search up to 8 levels; the real distance is 3 (src) or 4 (dist/src).
18
- for (let i = 0; i < 8; i++) {
19
- const candidate = join(dir, 'bin', 'cli.js');
20
- if (existsSync(candidate))
21
- return candidate;
22
- const parent = dirname(dir);
23
- if (parent === dir)
24
- break;
25
- dir = parent;
26
- }
27
- // Fall back to the compiled-layout path; the existsSync check in runCli
28
- // will surface a clear error if it is missing.
29
- return join(fileURLToPath(import.meta.url), '..', '..', '..', '..', 'bin', 'cli.js');
30
- }
31
- function runCli(args) {
32
- const cliPath = findCliPath();
33
- if (!existsSync(cliPath)) {
34
- return Promise.resolve({
35
- exitCode: 1,
36
- stdout: '',
37
- stderr: `Cannot find CLI binary at ${cliPath}`,
38
- });
39
- }
40
- return new Promise((res) => {
41
- execFile('node', [cliPath, ...args], (err, stdout, stderr) => {
42
- res({ exitCode: err && 'code' in err && typeof err.code === 'number' ? err.code : err ? 1 : 0, stdout, stderr });
43
- });
44
- });
45
- }
46
- export async function printComponentsFromSession(opts) {
47
- const r = await runCli(['print', 'components', '--session', opts.sessionId, '--out', opts.outPath]);
48
- if (r.exitCode === 0)
49
- return { ok: true };
50
- return { ok: false, error: r.stderr.trim() || `exit ${r.exitCode}` };
51
- }
52
- export async function printTokensFromSession(opts) {
53
- const r = await runCli(['print', 'tokens', '--session', opts.sessionId, '--out', opts.outPath]);
54
- if (r.exitCode === 0)
55
- return { ok: true };
56
- return { ok: false, error: r.stderr.trim() || `exit ${r.exitCode}` };
57
- }
58
- /** Internal test surface. Not part of the public API. */
59
- export const __testing = { findCliPath };