@contentful/experience-design-system-cli 2.23.2-dev-build-753ceef.0 → 2.23.2-dev-build-e996ffb.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 (69) hide show
  1. package/README.md +2 -24
  2. package/dist/package.json +1 -2
  3. package/dist/src/analyze/command.js +2 -8
  4. package/dist/src/analyze/select/command.js +9 -13
  5. package/dist/src/analyze/select/tui/App.js +4 -1
  6. package/dist/src/analyze/select-agent/command.js +11 -14
  7. package/dist/src/apply/api-client.d.ts +5 -5
  8. package/dist/src/apply/api-client.js +8 -18
  9. package/dist/src/apply/command.d.ts +1 -1
  10. package/dist/src/apply/command.js +77 -81
  11. package/dist/src/apply/preview-utils.d.ts +5 -1
  12. package/dist/src/apply/preview-utils.js +5 -1
  13. package/dist/src/apply/tui/ServerApplyView.d.ts +6 -3
  14. package/dist/src/apply/tui/ServerApplyView.js +15 -6
  15. package/dist/src/apply/tui/ServerPreviewView.d.ts +2 -1
  16. package/dist/src/apply/tui/ServerPreviewView.js +3 -3
  17. package/dist/src/generate/command.js +9 -15
  18. package/dist/src/generate/edit/command.js +0 -1
  19. package/dist/src/import/command.js +6 -1
  20. package/dist/src/import/orchestrator.d.ts +1 -0
  21. package/dist/src/import/orchestrator.js +14 -18
  22. package/dist/src/import/tui/WizardApp.d.ts +2 -1
  23. package/dist/src/import/tui/WizardApp.js +8 -8
  24. package/dist/src/import/tui/final-review-host.d.ts +2 -1
  25. package/dist/src/import/tui/final-review-host.js +2 -2
  26. package/dist/src/import/tui/runLivePreview.d.ts +3 -0
  27. package/dist/src/import/tui/runLivePreview.js +4 -1
  28. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +2 -1
  29. package/dist/src/import/tui/steps/GenerateReviewStep.js +3 -3
  30. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +6 -2
  31. package/dist/src/import/tui/steps/WizardPreviewStep.js +12 -3
  32. package/dist/src/import/tui/useFinalizePreview.d.ts +2 -0
  33. package/dist/src/import/tui/useFinalizePreview.js +0 -0
  34. package/dist/src/import/tui/useLivePreview.d.ts +2 -0
  35. package/dist/src/import/tui/useLivePreview.js +1 -0
  36. package/dist/src/index.js +1 -6
  37. package/dist/src/lib/command-options.d.ts +1 -0
  38. package/dist/src/lib/command-options.js +3 -0
  39. package/dist/src/print/command.js +13 -16
  40. package/dist/src/program.js +1 -8
  41. package/dist/src/runs/modify-launcher.d.ts +2 -0
  42. package/dist/src/runs/modify-launcher.js +2 -0
  43. package/dist/src/runs/push-helpers.d.ts +1 -0
  44. package/dist/src/runs/push-helpers.js +3 -0
  45. package/dist/src/runs/replay-helpers.d.ts +4 -0
  46. package/dist/src/runs/replay-helpers.js +3 -0
  47. package/package.json +5 -6
  48. package/dist/src/analytics/apply.d.ts +0 -6
  49. package/dist/src/analytics/apply.js +0 -30
  50. package/dist/src/analytics/client.d.ts +0 -7
  51. package/dist/src/analytics/client.js +0 -68
  52. package/dist/src/analytics/constants.d.ts +0 -4
  53. package/dist/src/analytics/constants.js +0 -4
  54. package/dist/src/analytics/env.d.ts +0 -4
  55. package/dist/src/analytics/env.js +0 -14
  56. package/dist/src/analytics/exit.d.ts +0 -5
  57. package/dist/src/analytics/exit.js +0 -24
  58. package/dist/src/analytics/index.d.ts +0 -10
  59. package/dist/src/analytics/index.js +0 -9
  60. package/dist/src/analytics/normalize.d.ts +0 -3
  61. package/dist/src/analytics/normalize.js +0 -18
  62. package/dist/src/analytics/os.d.ts +0 -2
  63. package/dist/src/analytics/os.js +0 -13
  64. package/dist/src/analytics/session.d.ts +0 -3
  65. package/dist/src/analytics/session.js +0 -15
  66. package/dist/src/analytics/tracker.d.ts +0 -17
  67. package/dist/src/analytics/tracker.js +0 -126
  68. package/dist/src/analytics/types.d.ts +0 -28
  69. package/dist/src/analytics/types.js +0 -1
package/README.md CHANGED
@@ -214,6 +214,7 @@ Pass `--select-prompt-path <path>` and/or `--generate-prompt-path <path>` to swa
214
214
  | `--host <url>` | `https://api.contentful.com` | Override API base URL |
215
215
  | `--on-conflict <mode>` | _(prompt via `<SaveConflictGate>`)_ | Headless conflict resolution when a file already exists at the save path: `overwrite`, `skip`, or `fail`. Bypasses the wizard's interactive save-conflict gate. Mutex with `--no-save`. |
216
216
  | `--print-prompt` | — | Print the generate prompt to stdout and exit. Replaces the prompt-print semantics of `--dry-run`. |
217
+ | `--allow-deletions` | off (non-destructive) | Allow the push to delete remote ComponentTypes/DesignTokens missing from the manifest. Default skips them instead of deleting. Without this flag, preview responses suppress the removed-entity list and return a count instead; interactive confirm screens show an opt-out toggle (never opt-in) only when the flag is passed. Forwarded to headless subprocess pushes and `--push-from-run`. |
217
218
  | `--dry-run` | _(deprecated)_ | Deprecated alias for `--print-prompt`. Emits a stderr deprecation notice; prompt-print semantics will be removed in a future release. |
218
219
 
219
220
  ### Run-picker at wizard start
@@ -418,7 +419,7 @@ experiences apply select --space-id <id> --environment-id <env> --session <id>
418
419
  experiences apply push --space-id <id> --environment-id <env> --session <id> [--yes]
419
420
  ```
420
421
 
421
- Shared flags: `--components`, `--tokens`, `--session`, `--space-id`, `--environment-id`, `--cma-token`, `--host`, `--viewports`. `apply preview` adds `--include-unchanged`. `apply select` adds `--select-all`, `--select`, `--deselect`, `--force`. `apply push` adds `--yes`, `--verbose`, `--force`, `--dry-run`.
422
+ Shared flags: `--components`, `--tokens`, `--session`, `--space-id`, `--environment-id`, `--cma-token`, `--host`, `--viewports`. `apply preview` adds `--include-unchanged`. `apply select` adds `--select-all`, `--select`, `--deselect`, `--force`, `--allow-deletions`. `apply push` adds `--yes`, `--verbose`, `--force`, `--dry-run`, `--allow-deletions`. By default, remote ComponentTypes and DesignTokens missing from the pushed manifest are skipped, not deleted; pass `--allow-deletions` to restore the prior delete behavior.
422
423
 
423
424
  Design tokens are written first (component types may reference token kinds). Each entity write is recorded in the session database atomically — interrupted pushes resume from where they left off.
424
425
 
@@ -450,29 +451,6 @@ Wizard run history is separate: `~/.config/experiences/runs.json`.
450
451
 
451
452
  ---
452
453
 
453
- ## Usage data
454
-
455
- The CLI collects **anonymous usage data** to help us understand which commands are used and where the import workflow succeeds or fails. This data does **not** include your source code, file paths, credentials, prompts, or any content you author.
456
-
457
- What may be included:
458
-
459
- - Command name and duration
460
- - CLI, Node.js, and operating-system version
461
- - Anonymous session identifiers that link steps within a single import run
462
- - Structural counts (for example, how many components were extracted or accepted)
463
- - Space and environment IDs you pass on the command line
464
- - Contentful request IDs from API responses (to correlate failures with server logs)
465
-
466
- You can turn this off at any time:
467
-
468
- ```bash
469
- DISABLE_ANALYTICS=1 experiences import --project ./my-app
470
- ```
471
-
472
- Setting `DISABLE_ANALYTICS` to any value disables collection for that invocation.
473
-
474
- ---
475
-
476
454
  ## Development
477
455
 
478
456
  ```bash
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.23.2-dev-build-753ceef.0",
3
+ "version": "2.23.2-dev-build-e996ffb.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,7 +41,6 @@
41
41
  "@contentful/experience-design-system-extraction": "workspace:*",
42
42
  "@contentful/experience-design-system-generation": "workspace:*",
43
43
  "@contentful/experience-design-system-types": "workspace:*",
44
- "@segment/analytics-node": "^3.0.0",
45
44
  "commander": "^13.1.0",
46
45
  "ink": "^4.4.1",
47
46
  "react": "^18.3.1",
@@ -24,7 +24,6 @@ import { DEFAULT_AGENT_NAME, isAgentName, runAgent, } from '@contentful/experien
24
24
  import { readExperiencesCredentials } from '../credentials-store.js';
25
25
  import { buildAnalyzeViewRows, partitionGlobalWarnings } from './build-analyze-view-rows.js';
26
26
  import { getInteractiveTerminalSupport } from '../lib/terminal-capabilities.js';
27
- import { bindAnalyticsSessionId, emitSessionStarted, enrichCommandResult, exitWithAnalytics, isPipelineAnalyticsChild, } from '../analytics/index.js';
28
27
  import { getDebugLogger } from '../lib/debug-logger.js';
29
28
  const SCANNED_FILE_EXTENSIONS = new Set(['.astro', '.js', '.jsx', '.svelte', '.ts', '.tsx', '.vue']);
30
29
  /**
@@ -265,10 +264,6 @@ export function registerAnalyzeCommand(program) {
265
264
  inputPath: projectRoot,
266
265
  outDir,
267
266
  });
268
- await bindAnalyticsSessionId(sessionId);
269
- if (!isPipelineAnalyticsChild()) {
270
- await emitSessionStarted('analyze_extract');
271
- }
272
267
  const stepId = createStep(db, sessionId, 'analyze extract', {
273
268
  project: projectRoot,
274
269
  });
@@ -542,7 +537,6 @@ export function registerAnalyzeCommand(program) {
542
537
  storeSlotCycles(db, sessionId, withBreaks);
543
538
  storeScannedFiles(db, sessionId, sourceFiles.map((f) => relative(projectRoot, f)));
544
539
  updateStep(db, stepId, 'complete', { sessionId });
545
- enrichCommandResult({ extracted_component_count: validatedComponents.length });
546
540
  db.close();
547
541
  const allWarnings = [...extraction.warnings, ...filterWarnings];
548
542
  const { rows: componentRows, totalErrors } = buildAnalyzeViewRows(filteredComponents, validatedComponents, allWarnings);
@@ -564,7 +558,7 @@ export function registerAnalyzeCommand(program) {
564
558
  if (getInteractiveTerminalSupport().supported) {
565
559
  const { waitUntilExit } = render(createElement(AnalyzeView, {
566
560
  result: analyzeResult,
567
- onExit: () => void exitWithAnalytics(0),
561
+ onExit: () => process.exit(0),
568
562
  }));
569
563
  await waitUntilExit();
570
564
  }
@@ -591,7 +585,7 @@ export function registerAnalyzeCommand(program) {
591
585
  summaryLines.push('Warnings: none');
592
586
  }
593
587
  process.stderr.write(summaryLines.join('\n') + '\n');
594
- await exitWithAnalytics(0);
588
+ process.exit(0);
595
589
  }
596
590
  });
597
591
  registerAnalyzeEditCommand(analyze);
@@ -8,7 +8,6 @@ import { loadReviewInput } from './parser.js';
8
8
  import { App } from './tui/App.js';
9
9
  import { openPipelineDb, loadRawComponents, storeRawComponents, createStep, updateStep } from '../../session/db.js';
10
10
  import { validateExtractedComponents, shouldExcludeDueToValidation, formatExclusionWarning, } from '@contentful/experience-design-system-extraction';
11
- import { bindAnalyticsSessionId, enrichCommandResult, exitWithAnalytics } from '../../analytics/index.js';
12
11
  const SAFE_PATH_RE = /^[a-zA-Z0-9_.$[\]=]+$/;
13
12
  const PROTO_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
14
13
  function applyDotPath(obj, path, value) {
@@ -103,7 +102,6 @@ async function runNonInteractive(snapshot, opts, paths, sessionId) {
103
102
  }
104
103
  const accepted = snapshot.components.filter((c) => c.status === 'accepted').length;
105
104
  const rejected = snapshot.components.filter((c) => c.status === 'rejected').length + (names.length || 0);
106
- enrichCommandResult({ accepted_component_count: accepted });
107
105
  process.stderr.write(`Accepted: ${accepted} Rejected: ${rejected}\n`);
108
106
  return;
109
107
  }
@@ -132,7 +130,7 @@ async function runNonInteractive(snapshot, opts, paths, sessionId) {
132
130
  lines.push('');
133
131
  lines.push('Re-run with --exclude-invalid to auto-reject these components, or run analyze select interactively to fix them.');
134
132
  process.stderr.write(lines.join('\n') + '\n');
135
- await exitWithAnalytics(1);
133
+ process.exit(1);
136
134
  return;
137
135
  }
138
136
  }
@@ -177,14 +175,14 @@ async function runNonInteractive(snapshot, opts, paths, sessionId) {
177
175
  const parsed = JSON.parse(raw);
178
176
  if (!Array.isArray(parsed)) {
179
177
  process.stderr.write(`Error: --patch file must be a JSON array of patch operations: ${opts.patch}\n`);
180
- await exitWithAnalytics(1);
178
+ process.exit(1);
181
179
  return;
182
180
  }
183
181
  patchOps = parsed;
184
182
  }
185
183
  catch {
186
184
  process.stderr.write(`Error: cannot read or parse --patch file: ${opts.patch}\n`);
187
- await exitWithAnalytics(1);
185
+ process.exit(1);
188
186
  return;
189
187
  }
190
188
  // Warn on unknown component names
@@ -231,7 +229,6 @@ async function runNonInteractive(snapshot, opts, paths, sessionId) {
231
229
  db.close();
232
230
  }
233
231
  }
234
- enrichCommandResult({ accepted_component_count: accepted.length });
235
232
  process.stderr.write(`Accepted: ${accepted.length} Rejected: ${rejected.length}\n`);
236
233
  }
237
234
  /**
@@ -348,7 +345,7 @@ export async function rejectComponentsByName(sessionId, names, opts = {}) {
348
345
  const components = snapshot.components.map((c) => (nameSet.has(c.name) ? { ...c, status: 'rejected' } : c));
349
346
  await saveReviewState(paths.statePath, { ...snapshot, components });
350
347
  }
351
- async function resolveSessionId(sessionFlag) {
348
+ function resolveSessionId(sessionFlag) {
352
349
  if (sessionFlag)
353
350
  return sessionFlag;
354
351
  const db = openPipelineDb();
@@ -363,7 +360,7 @@ async function resolveSessionId(sessionFlag) {
363
360
  .get();
364
361
  if (!row) {
365
362
  process.stderr.write('Error: no completed analyze extract session found. Run analyze extract first, or pass --session <id>.\n');
366
- return await exitWithAnalytics(1);
363
+ process.exit(1);
367
364
  }
368
365
  return row.id;
369
366
  }
@@ -387,8 +384,7 @@ export function registerAnalyzeEditCommand(program) {
387
384
  .option('--exclude-invalid', 'With --select-all: auto-reject components with validation errors instead of failing loud (opt-in bypass for the gate)')
388
385
  .option('--exclude-components <names>', 'Comma-separated component names to force-reject, regardless of other selection flags')
389
386
  .action(async ({ session: sessionFlag, projectRoot, acceptAll, selectAll, reject, deselect, select, patch, excludeInvalid, excludeComponents, }) => {
390
- const sessionId = await resolveSessionId(sessionFlag);
391
- await bindAnalyticsSessionId(sessionId);
387
+ const sessionId = resolveSessionId(sessionFlag);
392
388
  const db = openPipelineDb();
393
389
  let rawComponentCount = 0;
394
390
  try {
@@ -399,7 +395,7 @@ export function registerAnalyzeEditCommand(program) {
399
395
  }
400
396
  if (rawComponentCount === 0) {
401
397
  process.stderr.write(`Error: session '${sessionId}' has no raw components. Run analyze extract first.\n`);
402
- await exitWithAnalytics(1);
398
+ process.exit(1);
403
399
  return;
404
400
  }
405
401
  const artifactsRoot = getRefineArtifactsRoot();
@@ -424,7 +420,7 @@ export function registerAnalyzeEditCommand(program) {
424
420
  }
425
421
  catch (error) {
426
422
  process.stderr.write(`Error: unable to initialize refine session.\n${error instanceof Error ? error.message : String(error)}\n`);
427
- await exitWithAnalytics(1);
423
+ process.exit(1);
428
424
  return;
429
425
  }
430
426
  // Non-interactive path
@@ -469,7 +465,7 @@ export function registerAnalyzeEditCommand(program) {
469
465
  });
470
466
  if (process.stdout.columns !== undefined && process.stdout.columns < 60) {
471
467
  process.stderr.write(`Error: terminal too narrow (${process.stdout.columns} cols). Resize to 60+ columns.\n`);
472
- await exitWithAnalytics(1);
468
+ process.exit(1);
473
469
  }
474
470
  const { waitUntilExit } = render(createElement(App, {
475
471
  sessionId,
@@ -80,7 +80,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
80
80
  if (!manifest.componentsManifest)
81
81
  manifest.componentsManifest = {};
82
82
  const client = new ImportApiClient({ cmaToken, spaceId, environmentId });
83
- const preview = await client.previewImport(manifest);
83
+ // This preview is display-only (component-picker annotations); it never
84
+ // triggers an apply, so always request the full diff to keep the
85
+ // existing "removed" annotation working.
86
+ const preview = await client.previewImport(manifest, true);
84
87
  const annotations = {};
85
88
  for (const item of preview.components.new) {
86
89
  const name = item.name ?? '';
@@ -11,7 +11,6 @@ import { buildRepoContextIndex, buildSelectionContext } from './context-builder.
11
11
  import { runShowRationale } from './show-rationale.js';
12
12
  import { isAbsolute, resolve } from 'node:path';
13
13
  import { getDebugLogger } from '../../lib/debug-logger.js';
14
- import { bindAnalyticsSessionId, enrichCommandResult, exitWithAnalytics } from '../../analytics/index.js';
15
14
  import { validateExtractedComponents, shouldExcludeDueToValidation, formatExclusionWarning, } from '@contentful/experience-design-system-extraction';
16
15
  const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
17
16
  export const DEFAULT_CONCURRENCY = 10;
@@ -28,7 +27,7 @@ function resolveBatchSize() {
28
27
  return DEFAULT_BATCH_SIZE;
29
28
  return Math.floor(n);
30
29
  }
31
- async function resolveSessionId(sessionFlag) {
30
+ function resolveSessionId(sessionFlag) {
32
31
  if (sessionFlag)
33
32
  return sessionFlag;
34
33
  const db = openPipelineDb();
@@ -43,7 +42,7 @@ async function resolveSessionId(sessionFlag) {
43
42
  .get();
44
43
  if (!row) {
45
44
  process.stderr.write('Error: no completed analyze extract session found. Run analyze extract first, or pass --session <id>.\n');
46
- return await exitWithAnalytics(1);
45
+ process.exit(1);
47
46
  }
48
47
  return row.id;
49
48
  }
@@ -330,7 +329,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
330
329
  catch (err) {
331
330
  const message = err instanceof Error ? err.message : String(err);
332
331
  process.stderr.write(`Error: ${message}\n`);
333
- await exitWithAnalytics(1);
332
+ process.exit(1);
334
333
  return;
335
334
  }
336
335
  }
@@ -339,7 +338,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
339
338
  const model = opts.model ?? savedCreds.agentModel;
340
339
  if (!agentName || !isAgentName(agentName)) {
341
340
  process.stderr.write(`Error: no agent configured. Pass --agent <name> or run experiences setup. Accepted values: ${AGENT_NAMES.join(', ')}\n`);
342
- await exitWithAnalytics(1);
341
+ process.exit(1);
343
342
  return;
344
343
  }
345
344
  const agent = agentName;
@@ -353,7 +352,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
353
352
  .catch(() => false);
354
353
  if (!exists) {
355
354
  process.stderr.write(`Error: custom prompt path not found: ${resolvedPath}\n`);
356
- await exitWithAnalytics(1);
355
+ process.exit(1);
357
356
  return;
358
357
  }
359
358
  if (!selectPromptPath.toLowerCase().endsWith('.md')) {
@@ -361,8 +360,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
361
360
  }
362
361
  process.stderr.write(formatCustomPromptBanner('select', resolvedPath));
363
362
  }
364
- const sessionId = await resolveSessionId(opts.session);
365
- await bindAnalyticsSessionId(sessionId);
363
+ const sessionId = resolveSessionId(opts.session);
366
364
  const selectionRoot = resolveProjectRoot(sessionId, opts.projectRoot);
367
365
  const db = openPipelineDb();
368
366
  let rawComponents;
@@ -376,7 +374,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
376
374
  }
377
375
  if (rawComponents.length === 0) {
378
376
  process.stderr.write(`Error: session '${sessionId}' has no raw components. Run analyze extract first.\n`);
379
- await exitWithAnalytics(1);
377
+ process.exit(1);
380
378
  return;
381
379
  }
382
380
  // Re-run validation (not persisted to DB, so always recompute).
@@ -401,7 +399,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
401
399
  lines.push('');
402
400
  lines.push('Re-run with --exclude-invalid to auto-reject these components, or fix them in source first.');
403
401
  process.stderr.write(lines.join('\n') + '\n');
404
- await exitWithAnalytics(1);
402
+ process.exit(1);
405
403
  return;
406
404
  }
407
405
  const componentsForAgent = validatedComponents.filter((comp) => !shouldExcludeDueToValidation(comp));
@@ -428,7 +426,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
428
426
  if (opts.dryRun) {
429
427
  if (selectionCandidates.length === 0) {
430
428
  process.stderr.write('No valid components to preview — all components were excluded due to validation errors.\n');
431
- await exitWithAnalytics(0);
429
+ process.exit(0);
432
430
  return;
433
431
  }
434
432
  const first = selectionCandidates[0];
@@ -440,7 +438,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
440
438
  skillPathOverride: selectPromptPath ? resolve(selectPromptPath) : undefined,
441
439
  });
442
440
  process.stdout.write(prompt + '\n');
443
- await exitWithAnalytics(0);
441
+ process.exit(0);
444
442
  return;
445
443
  }
446
444
  // --no-cache is the global kill-switch; --no-select-cache is the stage-
@@ -506,7 +504,7 @@ export function registerAnalyzeSelectAgentCommand(program) {
506
504
  }
507
505
  catch (error) {
508
506
  process.stderr.write(`Error: unable to initialize select session.\n${error instanceof Error ? error.message : String(error)}\n`);
509
- await exitWithAnalytics(1);
507
+ process.exit(1);
510
508
  return;
511
509
  }
512
510
  const paths = await getRefineSessionPaths(sessionId, artifactsRoot);
@@ -552,7 +550,6 @@ export function registerAnalyzeSelectAgentCommand(program) {
552
550
  finally {
553
551
  stepDb.close();
554
552
  }
555
- enrichCommandResult({ accepted_component_count: accepted.length });
556
553
  process.stderr.write(`Accepted: ${accepted.length} Rejected: ${rejected.length} Needs review: ${unresolved.length}\n`);
557
554
  });
558
555
  }
@@ -45,17 +45,17 @@ export declare class ImportApiClient {
45
45
  private spaceId;
46
46
  private environmentId;
47
47
  private retry;
48
- private lastRequestId?;
49
- getLastRequestId(): string | undefined;
50
- private noteRequestId;
51
48
  constructor(opts: ApiClientOptions);
52
49
  private base;
53
50
  private headers;
54
51
  private requestWithRetry;
55
52
  validateToken(): Promise<void>;
56
53
  checkPreflight(): Promise<void>;
57
- previewImport(manifest: ManifestPayload): Promise<ServerPreviewResponse>;
58
- applyImport(manifest: ManifestPayload, acknowledgeBreakingChanges: boolean): Promise<ApplyOperationResponse>;
54
+ previewImport(manifest: ManifestPayload, allowDeletions?: boolean): Promise<ServerPreviewResponse>;
55
+ applyImport(manifest: ManifestPayload, options: {
56
+ acknowledgeBreakingChanges: boolean;
57
+ allowDeletions?: boolean;
58
+ }): Promise<ApplyOperationResponse>;
59
59
  pollOperation(operationId: string, opts?: {
60
60
  intervalMs?: number;
61
61
  maxIntervalMs?: number;
@@ -180,15 +180,6 @@ export class ImportApiClient {
180
180
  spaceId;
181
181
  environmentId;
182
182
  retry;
183
- lastRequestId;
184
- getLastRequestId() {
185
- return this.lastRequestId;
186
- }
187
- noteRequestId(response) {
188
- const requestId = response.headers.get('x-contentful-request-id');
189
- if (requestId)
190
- this.lastRequestId = requestId;
191
- }
192
183
  constructor(opts) {
193
184
  this.host = toApiHost(opts.host);
194
185
  this.token = opts.cmaToken;
@@ -246,10 +237,8 @@ export class ImportApiClient {
246
237
  await this.retry.sleep(delayMs);
247
238
  continue;
248
239
  }
249
- if (!isTransientStatus(result.response.status)) {
250
- this.noteRequestId(result.response);
240
+ if (!isTransientStatus(result.response.status))
251
241
  return result;
252
- }
253
242
  if (attempt === this.retry.maxAttempts) {
254
243
  const body = stringifyError(result.error);
255
244
  const guidance = `The ${phase} request failed after ${attempt} attempts because the service remained unavailable. ` +
@@ -284,7 +273,6 @@ export class ImportApiClient {
284
273
  // false positives that don't apply to the design-systems API authorization path.
285
274
  const url = `${this.host}/users/me`;
286
275
  const res = await request(url, { token: this.token });
287
- this.noteRequestId(res);
288
276
  if (res.status === 401) {
289
277
  throw new ApiError('CMA token is invalid or revoked', res.status, await res.text());
290
278
  }
@@ -310,7 +298,6 @@ export class ImportApiClient {
310
298
  catch {
311
299
  return; // network error — don't block; the real call will surface it
312
300
  }
313
- this.noteRequestId(res);
314
301
  if (res.ok)
315
302
  return;
316
303
  if (res.status >= 500)
@@ -320,19 +307,20 @@ export class ImportApiClient {
320
307
  return; // legacy backend without the endpoint
321
308
  throw new ApiError(`preflight failed: ${res.status}`, res.status, body);
322
309
  }
323
- async previewImport(manifest) {
310
+ async previewImport(manifest, allowDeletions = false) {
324
311
  const debug = getDebugLogger();
325
312
  const startedAt = Date.now();
326
313
  debug.event('apply', 'preview.request', {
327
314
  url: `${this.base()}/design_systems/imports/preview`,
328
315
  componentCount: manifest.components?.length ?? 0,
329
316
  tokenCount: manifest.designTokens?.length ?? 0,
317
+ allowDeletions,
330
318
  });
331
319
  const result = await this.requestWithRetry('preview', PREVIEW_ERROR_PREFIX, () => designSystemImportSourcelessPreview({
332
320
  baseUrl: this.host,
333
321
  headers: this.headers(),
334
322
  path: { spaceId: this.spaceId, environmentId: this.environmentId },
335
- body: manifest,
323
+ body: { ...manifest, allowDeletions },
336
324
  parseAs: 'json',
337
325
  }));
338
326
  if (!result.response.ok) {
@@ -348,12 +336,14 @@ export class ImportApiClient {
348
336
  debug.event('apply', 'preview.ok', { status: result.response.status, durationMs: Date.now() - startedAt });
349
337
  return sanitizePreviewResponse(parsed);
350
338
  }
351
- async applyImport(manifest, acknowledgeBreakingChanges) {
339
+ async applyImport(manifest, options) {
340
+ const { acknowledgeBreakingChanges, allowDeletions = false } = options;
352
341
  const debug = getDebugLogger();
353
342
  const startedAt = Date.now();
354
343
  debug.event('apply', 'apply.request', {
355
344
  url: `${this.base()}/design_systems/imports/apply`,
356
345
  acknowledgeBreakingChanges,
346
+ allowDeletions,
357
347
  });
358
348
  let result;
359
349
  try {
@@ -361,7 +351,7 @@ export class ImportApiClient {
361
351
  baseUrl: this.host,
362
352
  headers: this.headers(),
363
353
  path: { spaceId: this.spaceId, environmentId: this.environmentId },
364
- body: { ...manifest, acknowledgeBreakingChanges },
354
+ body: { ...manifest, acknowledgeBreakingChanges, allowDeletions },
365
355
  parseAs: 'json',
366
356
  });
367
357
  }
@@ -11,7 +11,7 @@ export declare function formatSlotCycleReport(cycles: ReturnType<typeof findSlot
11
11
  export declare function assertNoSlotCycles(components: Array<{
12
12
  key: string;
13
13
  entry: CDFComponentEntry;
14
- }>): Promise<void>;
14
+ }>): void;
15
15
  export declare function extractComponentsFromManifest(manifest: {
16
16
  componentsManifest?: Record<string, unknown>;
17
17
  } | null | undefined): Array<{