@contentful/experience-design-system-cli 2.24.0 → 2.24.1-dev-build-58e271c.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 (38) hide show
  1. package/README.md +23 -0
  2. package/dist/package.json +2 -1
  3. package/dist/src/analytics/apply.d.ts +6 -0
  4. package/dist/src/analytics/apply.js +30 -0
  5. package/dist/src/analytics/client.d.ts +7 -0
  6. package/dist/src/analytics/client.js +68 -0
  7. package/dist/src/analytics/constants.d.ts +4 -0
  8. package/dist/src/analytics/constants.js +4 -0
  9. package/dist/src/analytics/env.d.ts +4 -0
  10. package/dist/src/analytics/env.js +14 -0
  11. package/dist/src/analytics/exit.d.ts +5 -0
  12. package/dist/src/analytics/exit.js +24 -0
  13. package/dist/src/analytics/index.d.ts +10 -0
  14. package/dist/src/analytics/index.js +9 -0
  15. package/dist/src/analytics/normalize.d.ts +3 -0
  16. package/dist/src/analytics/normalize.js +18 -0
  17. package/dist/src/analytics/os.d.ts +2 -0
  18. package/dist/src/analytics/os.js +13 -0
  19. package/dist/src/analytics/session.d.ts +3 -0
  20. package/dist/src/analytics/session.js +15 -0
  21. package/dist/src/analytics/tracker.d.ts +17 -0
  22. package/dist/src/analytics/tracker.js +126 -0
  23. package/dist/src/analytics/types.d.ts +28 -0
  24. package/dist/src/analytics/types.js +1 -0
  25. package/dist/src/analyze/command.js +8 -2
  26. package/dist/src/analyze/select/command.js +13 -9
  27. package/dist/src/analyze/select-agent/command.js +14 -11
  28. package/dist/src/apply/api-client.d.ts +3 -0
  29. package/dist/src/apply/api-client.js +14 -1
  30. package/dist/src/apply/command.d.ts +1 -1
  31. package/dist/src/apply/command.js +71 -50
  32. package/dist/src/generate/command.js +15 -9
  33. package/dist/src/generate/edit/command.js +1 -0
  34. package/dist/src/import/orchestrator.js +18 -12
  35. package/dist/src/index.js +6 -1
  36. package/dist/src/print/command.js +16 -13
  37. package/dist/src/program.js +8 -1
  38. package/package.json +6 -5
@@ -6,17 +6,19 @@ import { openPipelineDb, getOrCreateSession, createStep, updateStep, findLatestS
6
6
  import { detectSlotCycles, formatSlotCycleReport } from '../apply/command.js';
7
7
  import { PREVIEW_ERROR_PREFIX, VALIDATION_FAILED_CODE, parsePreviewValidationErrors } from '../apply/api-client.js';
8
8
  import { buildPostPushUrl } from '../lib/contentful-urls.js';
9
- import { getDebugLogger, debugEnvForSubprocess } from '../lib/debug-logger.js';
9
+ import { getDebugLogger } from '../lib/debug-logger.js';
10
+ import { bindAnalyticsSession, emitSessionStarted } from '../analytics/index.js';
11
+ import { pipelineSubprocessEnv } from '../analytics/env.js';
10
12
  function findCliPath() {
11
13
  return join(fileURLToPath(import.meta.url), '..', '..', '..', '..', 'bin', 'cli.js');
12
14
  }
13
- async function runStep(args, cliPath, env = {}, streamStderr = false) {
15
+ async function runStep(args, cliPath, analyticsSessionId, env = {}, streamStderr = false) {
14
16
  const debug = getDebugLogger();
15
17
  const startedAt = Date.now();
16
18
  debug.event('import', 'subprocess.spawn', { cliPath, args });
17
19
  return new Promise((res) => {
18
20
  const child = execFile('node', [cliPath, ...args], {
19
- env: debugEnvForSubprocess({ ...process.env, ...env }),
21
+ env: pipelineSubprocessEnv({ ...process.env, ...env }, analyticsSessionId),
20
22
  });
21
23
  let stdout = '';
22
24
  let stderr = '';
@@ -108,6 +110,10 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
108
110
  inputPath: projectRoot,
109
111
  outDir,
110
112
  });
113
+ await bindAnalyticsSession(sessionId, {
114
+ ...(opts.spaceId ? { space_key: opts.spaceId, environment_key: opts.environmentId } : {}),
115
+ });
116
+ await emitSessionStarted('import');
111
117
  progressWriter(`Experience Design System CLI — Pipeline Import`);
112
118
  progressWriter(`Project: ${projectRoot}`);
113
119
  progressWriter(`Output: ${outDir}`);
@@ -159,7 +165,7 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
159
165
  if (opts.agent)
160
166
  analyzeArgs.push('--agent', opts.agent);
161
167
  }
162
- const r = await runStep(analyzeArgs, cliPath);
168
+ const r = await runStep(analyzeArgs, cliPath, sessionId);
163
169
  const durationMs = Date.now() - t0;
164
170
  if (r.exitCode !== 0) {
165
171
  if (r.stderr)
@@ -231,7 +237,7 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
231
237
  editArgs.push('--select-all');
232
238
  }
233
239
  }
234
- const rEdit = await runStep(editArgs, cliPath, { FORCE_COLOR: '1' }, useAgentSelect);
240
+ const rEdit = await runStep(editArgs, cliPath, sessionId, { FORCE_COLOR: '1' }, useAgentSelect);
235
241
  const editDurationMs = Date.now() - t0Edit;
236
242
  if (rEdit.exitCode !== 0) {
237
243
  if (rEdit.stderr && !useAgentSelect)
@@ -292,7 +298,7 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
292
298
  extractSession: extractSessionId ?? '',
293
299
  });
294
300
  const t0 = Date.now();
295
- const r = await runStep(generateArgs, cliPath, { FORCE_COLOR: '1' }, true);
301
+ const r = await runStep(generateArgs, cliPath, sessionId, { FORCE_COLOR: '1' }, true);
296
302
  const durationMs = Date.now() - t0;
297
303
  if (r.exitCode !== 0) {
298
304
  updateStep(db, stepId, 'failed', {}, r.stderr);
@@ -355,7 +361,7 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
355
361
  out: componentsPath,
356
362
  });
357
363
  const t0 = Date.now();
358
- const r = await runStep(printArgs, cliPath);
364
+ const r = await runStep(printArgs, cliPath, sessionId);
359
365
  const durationMs = Date.now() - t0;
360
366
  if (r.exitCode !== 0) {
361
367
  if (r.stderr)
@@ -425,7 +431,7 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
425
431
  components: componentsPath,
426
432
  });
427
433
  const t0 = Date.now();
428
- let r = await runStep(pushArgs, cliPath, { FORCE_COLOR: '1' }, true);
434
+ let r = await runStep(pushArgs, cliPath, sessionId, { FORCE_COLOR: '1' }, true);
429
435
  const excludedByRetry = [];
430
436
  let validationRetryCount = 0;
431
437
  while (validationRetryCount < MAX_VALIDATION_RETRIES && isPreviewValidationError(r) && extractSessionId) {
@@ -442,10 +448,10 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
442
448
  '--exclude-components',
443
449
  offenders.join(','),
444
450
  ];
445
- const rejectResult = await runStep(rejectArgs, cliPath);
451
+ const rejectResult = await runStep(rejectArgs, cliPath, sessionId);
446
452
  if (rejectResult.exitCode !== 0)
447
453
  break;
448
- r = await runStep(pushArgs, cliPath, { FORCE_COLOR: '1' }, true);
454
+ r = await runStep(pushArgs, cliPath, sessionId, { FORCE_COLOR: '1' }, true);
449
455
  validationRetryCount++;
450
456
  }
451
457
  const durationMs = Date.now() - t0;
@@ -480,10 +486,10 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
480
486
  '--exclude-components',
481
487
  cycleNames.join(','),
482
488
  ];
483
- const rejectResult = await runStep(rejectArgs, cliPath);
489
+ const rejectResult = await runStep(rejectArgs, cliPath, sessionId);
484
490
  if (rejectResult.exitCode === 0) {
485
491
  const retryT0 = Date.now();
486
- const retryR = await runStep(pushArgs, cliPath, { FORCE_COLOR: '1' }, true);
492
+ const retryR = await runStep(pushArgs, cliPath, sessionId, { FORCE_COLOR: '1' }, true);
487
493
  const retryDurationMs = Date.now() - t0 + (Date.now() - retryT0);
488
494
  if (isSlotCycleError(retryR)) {
489
495
  const retryReport = extractCycleReport(retryR.stderr);
package/dist/src/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import { createProgram } from './program.js';
2
+ import { failActiveCommand, flushAnalytics } from './analytics/index.js';
2
3
  createProgram()
3
4
  .parseAsync()
4
- .catch((err) => {
5
+ .catch(async (err) => {
6
+ await failActiveCommand({
7
+ error_name: err instanceof Error ? err.name : 'Error',
8
+ });
9
+ await flushAnalytics();
5
10
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
6
11
  process.exit(1);
7
12
  });
@@ -8,9 +8,10 @@ import { validateDTCGTokenFile } from './validate/validators/dtcg-validator.js';
8
8
  import { formatDiagnostics } from './validate/validators/format-errors.js';
9
9
  import { ValidateView } from './validate/tui/ValidateView.js';
10
10
  import { getInteractiveTerminalSupport } from '../lib/terminal-capabilities.js';
11
- function die(message) {
11
+ import { bindAnalyticsSessionId, exitWithAnalytics } from '../analytics/index.js';
12
+ async function die(message) {
12
13
  process.stderr.write(`${message}\n`);
13
- process.exit(1);
14
+ return exitWithAnalytics(1);
14
15
  }
15
16
  async function pathExists(p) {
16
17
  return access(p)
@@ -21,16 +22,16 @@ async function assertOutIsNotDirectory(outPath) {
21
22
  if (await pathExists(outPath)) {
22
23
  const s = await stat(outPath);
23
24
  if (s.isDirectory())
24
- die(`Error: --out must be a file path, not a directory: ${outPath}`);
25
+ await die(`Error: --out must be a file path, not a directory: ${outPath}`);
25
26
  }
26
27
  }
27
- function resolveSession(sessionFlag, command) {
28
+ async function resolveSession(sessionFlag, command) {
28
29
  const db = openPipelineDb();
29
30
  try {
30
31
  const sessionId = sessionFlag ?? findLatestSessionForCommand(db, command);
31
32
  if (!sessionId) {
32
33
  const hint = command === 'generate components' ? 'generate components' : 'generate tokens';
33
- die(`Error: no completed ${hint} session found. Run ${hint} first, or pass --session <id>.`);
34
+ return await die(`Error: no completed ${hint} session found. Run ${hint} first, or pass --session <id>.`);
34
35
  }
35
36
  return sessionId;
36
37
  }
@@ -85,7 +86,8 @@ export function registerPrintCommand(program) {
85
86
  .action(async (opts) => {
86
87
  const outPath = resolve(opts.out);
87
88
  await assertOutIsNotDirectory(outPath);
88
- const sessionId = resolveSession(opts.session, 'generate components');
89
+ const sessionId = await resolveSession(opts.session, 'generate components');
90
+ await bindAnalyticsSessionId(sessionId);
89
91
  const db = openPipelineDb();
90
92
  let components;
91
93
  let generateStepStatus = null;
@@ -110,13 +112,13 @@ export function registerPrintCommand(program) {
110
112
  // component was rejected or left unresolved. This is a legitimate
111
113
  // "clear the space" intent, but it's destructive, so require --allow-empty.
112
114
  if (!opts.allowEmpty) {
113
- 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.`);
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.`);
114
116
  }
115
117
  // Fall through: write an empty-but-present components manifest so a
116
118
  // subsequent push removes every component from the target space.
117
119
  }
118
120
  else {
119
- die(`Error: no generated components in session '${sessionId}'. Run generate components first.`);
121
+ await die(`Error: no generated components in session '${sessionId}'. Run generate components first.`);
120
122
  }
121
123
  }
122
124
  if (generateStepStatus === 'failed') {
@@ -145,7 +147,8 @@ export function registerPrintCommand(program) {
145
147
  .action(async (opts) => {
146
148
  const outPath = resolve(opts.out);
147
149
  await assertOutIsNotDirectory(outPath);
148
- const sessionId = resolveSession(opts.session, 'generate tokens');
150
+ const sessionId = await resolveSession(opts.session, 'generate tokens');
151
+ await bindAnalyticsSessionId(sessionId);
149
152
  const db = openPipelineDb();
150
153
  let result;
151
154
  try {
@@ -155,7 +158,7 @@ export function registerPrintCommand(program) {
155
158
  db.close();
156
159
  }
157
160
  if (result.tokens.length === 0) {
158
- die(`Error: no generated tokens in session '${sessionId}'. Run generate tokens first.`);
161
+ await die(`Error: no generated tokens in session '${sessionId}'. Run generate tokens first.`);
159
162
  }
160
163
  const tree = rebuildDTCGTree(result.groups, result.tokens);
161
164
  await mkdir(resolve(outPath, '..'), { recursive: true });
@@ -171,7 +174,7 @@ export function registerPrintCommand(program) {
171
174
  .action(async (opts) => {
172
175
  if (!opts.components && !opts.tokens) {
173
176
  process.stderr.write('Error: at least one of --components or --tokens is required.\n\nUsage: print validate [--components <path>] [--tokens <path>]\n');
174
- process.exit(1);
177
+ await exitWithAnalytics(1);
175
178
  }
176
179
  const viewResults = [];
177
180
  if (opts.components) {
@@ -199,7 +202,7 @@ export function registerPrintCommand(program) {
199
202
  if (getInteractiveTerminalSupport().supported) {
200
203
  const { waitUntilExit } = render(createElement(ValidateView, {
201
204
  results: viewResults,
202
- onExit: () => process.exit(exitCode),
205
+ onExit: () => void exitWithAnalytics(exitCode),
203
206
  }));
204
207
  await waitUntilExit();
205
208
  }
@@ -212,7 +215,7 @@ export function registerPrintCommand(program) {
212
215
  }))
213
216
  .join('\n\n');
214
217
  process.stdout.write(output + '\n');
215
- process.exit(exitCode);
218
+ await exitWithAnalytics(exitCode);
216
219
  }
217
220
  });
218
221
  }
@@ -12,6 +12,7 @@ import { registerImportCommand } from './import/command.js';
12
12
  import { registerSetupCommand } from './setup/command.js';
13
13
  import { registerRunsCommand } from './runs/ls-command.js';
14
14
  import { beginCommand } from './lib/debug-preamble.js';
15
+ import { completeActiveCommand, flushAnalytics, noteCommandStart } from './analytics/index.js';
15
16
  const require = createRequire(import.meta.url);
16
17
  const pkg = require('../package.json');
17
18
  /**
@@ -89,7 +90,13 @@ export function createProgram() {
89
90
  const chain = [];
90
91
  for (let c = actionCommand; c && c.parent; c = c.parent)
91
92
  chain.unshift(c.name());
92
- await beginCommand(chain.join(' ') || actionCommand.name(), { ...(debug !== undefined ? { debug } : {}) });
93
+ const commandChain = chain.join(' ') || actionCommand.name();
94
+ noteCommandStart(commandChain);
95
+ await beginCommand(commandChain, { ...(debug !== undefined ? { debug } : {}) });
96
+ });
97
+ program.hook('postAction', async () => {
98
+ await completeActiveCommand();
99
+ await flushAnalytics();
93
100
  });
94
101
  return program;
95
102
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.24.0",
3
+ "version": "2.24.1-dev-build-58e271c.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,15 +28,16 @@
28
28
  "prompts/"
29
29
  ],
30
30
  "dependencies": {
31
+ "@segment/analytics-node": "^3.0.0",
31
32
  "commander": "^13.1.0",
32
33
  "ink": "^4.4.1",
33
34
  "react": "^18.3.1",
34
35
  "react-devtools-core": "^4.19.1",
35
36
  "react-dom": "^18.3.1",
36
- "@contentful/experience-design-system-extraction": "2.24.0",
37
- "@contentful/experience-design-system-types": "2.24.0",
38
- "@contentful/experience-design-system-generation": "2.24.0",
39
- "@contentful/experience-design-system-client": "2.24.0"
37
+ "@contentful/experience-design-system-client": "2.24.1-dev-build-58e271c.0",
38
+ "@contentful/experience-design-system-extraction": "2.24.1-dev-build-58e271c.0",
39
+ "@contentful/experience-design-system-generation": "2.24.1-dev-build-58e271c.0",
40
+ "@contentful/experience-design-system-types": "2.24.1-dev-build-58e271c.0"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@tsconfig/node24": "^24.0.4",