@contentful/experience-design-system-cli 2.23.2-dev-build-8b2cfc9.0 → 2.23.2-dev-build-5146e8a.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.
- package/README.md +2 -24
- package/dist/package.json +1 -2
- package/dist/src/analyze/command.js +0 -4
- package/dist/src/apply/api-client.d.ts +4 -4
- package/dist/src/apply/api-client.js +5 -16
- package/dist/src/apply/command.js +24 -27
- package/dist/src/import/command.js +6 -1
- package/dist/src/import/orchestrator.d.ts +1 -0
- package/dist/src/import/orchestrator.js +3 -6
- package/dist/src/import/tui/WizardApp.d.ts +2 -1
- package/dist/src/import/tui/WizardApp.js +2 -2
- package/dist/src/index.js +1 -5
- package/dist/src/lib/command-options.d.ts +1 -0
- package/dist/src/lib/command-options.js +3 -0
- package/dist/src/program.js +1 -8
- package/dist/src/runs/modify-launcher.d.ts +2 -0
- package/dist/src/runs/modify-launcher.js +2 -0
- package/dist/src/runs/push-helpers.d.ts +1 -0
- package/dist/src/runs/push-helpers.js +3 -0
- package/dist/src/runs/replay-helpers.d.ts +4 -0
- package/dist/src/runs/replay-helpers.js +3 -0
- package/package.json +5 -6
- package/dist/src/analytics/apply.d.ts +0 -6
- package/dist/src/analytics/apply.js +0 -30
- package/dist/src/analytics/client.d.ts +0 -3
- package/dist/src/analytics/client.js +0 -39
- package/dist/src/analytics/index.d.ts +0 -6
- package/dist/src/analytics/index.js +0 -5
- package/dist/src/analytics/normalize.d.ts +0 -3
- package/dist/src/analytics/normalize.js +0 -17
- package/dist/src/analytics/os.d.ts +0 -2
- package/dist/src/analytics/os.js +0 -13
- package/dist/src/analytics/tracker.d.ts +0 -19
- package/dist/src/analytics/tracker.js +0 -157
- package/dist/src/analytics/types.d.ts +0 -29
- 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. 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-
|
|
3
|
+
"version": "2.23.2-dev-build-5146e8a.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 { bindAnalyticsSession, emitSessionStarted, enrichCommandResult } 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,8 +264,6 @@ export function registerAnalyzeCommand(program) {
|
|
|
265
264
|
inputPath: projectRoot,
|
|
266
265
|
outDir,
|
|
267
266
|
});
|
|
268
|
-
await bindAnalyticsSession(sessionId);
|
|
269
|
-
await emitSessionStarted('analyze_extract');
|
|
270
267
|
const stepId = createStep(db, sessionId, 'analyze extract', {
|
|
271
268
|
project: projectRoot,
|
|
272
269
|
});
|
|
@@ -540,7 +537,6 @@ export function registerAnalyzeCommand(program) {
|
|
|
540
537
|
storeSlotCycles(db, sessionId, withBreaks);
|
|
541
538
|
storeScannedFiles(db, sessionId, sourceFiles.map((f) => relative(projectRoot, f)));
|
|
542
539
|
updateStep(db, stepId, 'complete', { sessionId });
|
|
543
|
-
enrichCommandResult({ extracted_component_count: validatedComponents.length });
|
|
544
540
|
db.close();
|
|
545
541
|
const allWarnings = [...extraction.warnings, ...filterWarnings];
|
|
546
542
|
const { rows: componentRows, totalErrors } = buildAnalyzeViewRows(filteredComponents, validatedComponents, allWarnings);
|
|
@@ -45,9 +45,6 @@ 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;
|
|
@@ -55,7 +52,10 @@ export declare class ImportApiClient {
|
|
|
55
52
|
validateToken(): Promise<void>;
|
|
56
53
|
checkPreflight(): Promise<void>;
|
|
57
54
|
previewImport(manifest: ManifestPayload): Promise<ServerPreviewResponse>;
|
|
58
|
-
applyImport(manifest: ManifestPayload,
|
|
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)
|
|
@@ -348,12 +335,14 @@ export class ImportApiClient {
|
|
|
348
335
|
debug.event('apply', 'preview.ok', { status: result.response.status, durationMs: Date.now() - startedAt });
|
|
349
336
|
return sanitizePreviewResponse(parsed);
|
|
350
337
|
}
|
|
351
|
-
async applyImport(manifest,
|
|
338
|
+
async applyImport(manifest, options) {
|
|
339
|
+
const { acknowledgeBreakingChanges, allowDeletions = false } = options;
|
|
352
340
|
const debug = getDebugLogger();
|
|
353
341
|
const startedAt = Date.now();
|
|
354
342
|
debug.event('apply', 'apply.request', {
|
|
355
343
|
url: `${this.base()}/design_systems/imports/apply`,
|
|
356
344
|
acknowledgeBreakingChanges,
|
|
345
|
+
allowDeletions,
|
|
357
346
|
});
|
|
358
347
|
let result;
|
|
359
348
|
try {
|
|
@@ -361,7 +350,7 @@ export class ImportApiClient {
|
|
|
361
350
|
baseUrl: this.host,
|
|
362
351
|
headers: this.headers(),
|
|
363
352
|
path: { spaceId: this.spaceId, environmentId: this.environmentId },
|
|
364
|
-
body: { ...manifest, acknowledgeBreakingChanges },
|
|
353
|
+
body: { ...manifest, acknowledgeBreakingChanges, allowDeletions },
|
|
365
354
|
parseAs: 'json',
|
|
366
355
|
});
|
|
367
356
|
}
|
|
@@ -12,13 +12,11 @@ import { ServerPreviewApp, ServerPreviewConfirm, ServerApplyProgress, ServerAppl
|
|
|
12
12
|
import { SelectView, makeSelectKey } from './tui/SelectView.js';
|
|
13
13
|
import { buildPostPushUrl } from '../lib/contentful-urls.js';
|
|
14
14
|
import { resolveCompositionMode } from '../lib/composition-mode.js';
|
|
15
|
-
import { addArtifactInputOptions, addCompositionOptions, addContentfulTargetOptions, addSelectionOptions, } from '../lib/command-options.js';
|
|
15
|
+
import { addAllowDeletionsOption, addArtifactInputOptions, addCompositionOptions, addContentfulTargetOptions, addSelectionOptions, } from '../lib/command-options.js';
|
|
16
16
|
import { stripAllowedComponents } from '../import/strip-allowed-components.js';
|
|
17
17
|
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
18
18
|
import { getInteractiveTerminalSupport, requireInteractiveTerminal } from '../lib/terminal-capabilities.js';
|
|
19
|
-
import { bindAnalyticsSessionId, completeActiveCommand, failActiveCommand, recordApplyOutcome, recordContentfulContext, } from '../analytics/index.js';
|
|
20
19
|
function die(message) {
|
|
21
|
-
void failActiveCommand({ exit_code: 1 });
|
|
22
20
|
process.stderr.write(`${message}\n`);
|
|
23
21
|
process.exit(1);
|
|
24
22
|
}
|
|
@@ -433,12 +431,6 @@ export function registerApplyCommand(program) {
|
|
|
433
431
|
throw e;
|
|
434
432
|
}
|
|
435
433
|
const { components, tokens, client } = inputs;
|
|
436
|
-
const spaceId = opts.spaceId;
|
|
437
|
-
const environmentId = opts.environmentId;
|
|
438
|
-
await bindAnalyticsSessionId(opts.session, {
|
|
439
|
-
space_key: spaceId,
|
|
440
|
-
environment_key: environmentId,
|
|
441
|
-
});
|
|
442
434
|
try {
|
|
443
435
|
await client.validateToken();
|
|
444
436
|
}
|
|
@@ -458,7 +450,8 @@ export function registerApplyCommand(program) {
|
|
|
458
450
|
die(`Error: ${formatApiError(e)}`);
|
|
459
451
|
throw e;
|
|
460
452
|
}
|
|
461
|
-
|
|
453
|
+
const spaceId = opts.spaceId;
|
|
454
|
+
const environmentId = opts.environmentId;
|
|
462
455
|
if (getInteractiveTerminalSupport().supported) {
|
|
463
456
|
const { waitUntilExit } = render(createElement(ServerPreviewApp, {
|
|
464
457
|
preview,
|
|
@@ -476,6 +469,7 @@ export function registerApplyCommand(program) {
|
|
|
476
469
|
addArtifactInputOptions(pushCmd);
|
|
477
470
|
addContentfulTargetOptions(pushCmd);
|
|
478
471
|
addCompositionOptions(pushCmd);
|
|
472
|
+
addAllowDeletionsOption(pushCmd);
|
|
479
473
|
pushCmd
|
|
480
474
|
.option('--yes', 'Skip interactive confirmation')
|
|
481
475
|
.option('--verbose', 'Show all entity progress including skipped/unchanged')
|
|
@@ -497,12 +491,6 @@ export function registerApplyCommand(program) {
|
|
|
497
491
|
throw e;
|
|
498
492
|
}
|
|
499
493
|
const { components, tokens, client } = inputs;
|
|
500
|
-
const spaceId = opts.spaceId;
|
|
501
|
-
const environmentId = opts.environmentId;
|
|
502
|
-
await bindAnalyticsSessionId(opts.session, {
|
|
503
|
-
space_key: spaceId,
|
|
504
|
-
environment_key: environmentId,
|
|
505
|
-
});
|
|
506
494
|
assertNoSlotCycles(components);
|
|
507
495
|
try {
|
|
508
496
|
await client.validateToken();
|
|
@@ -522,6 +510,8 @@ export function registerApplyCommand(program) {
|
|
|
522
510
|
die(`Error: ${formatApiError(e, opts.verbose)}`);
|
|
523
511
|
throw e;
|
|
524
512
|
}
|
|
513
|
+
const spaceId = opts.spaceId;
|
|
514
|
+
const environmentId = opts.environmentId;
|
|
525
515
|
if (opts.dryRun) {
|
|
526
516
|
if (isTTY) {
|
|
527
517
|
const { waitUntilExit } = render(createElement(ServerPreviewApp, {
|
|
@@ -558,7 +548,10 @@ export function registerApplyCommand(program) {
|
|
|
558
548
|
}
|
|
559
549
|
let operation;
|
|
560
550
|
try {
|
|
561
|
-
operation = await client.applyImport(manifest,
|
|
551
|
+
operation = await client.applyImport(manifest, {
|
|
552
|
+
acknowledgeBreakingChanges: breakingWithImpact || opts.force === true,
|
|
553
|
+
allowDeletions: opts.allowDeletions === true,
|
|
554
|
+
});
|
|
562
555
|
}
|
|
563
556
|
catch (e) {
|
|
564
557
|
if (e instanceof ApiError)
|
|
@@ -575,14 +568,8 @@ export function registerApplyCommand(program) {
|
|
|
575
568
|
throw e;
|
|
576
569
|
}
|
|
577
570
|
const summary = buildApplyOutput(operation, spaceId, environmentId, opts.host);
|
|
578
|
-
recordApplyOutcome(client, spaceId, environmentId, operation);
|
|
579
571
|
process.stdout.write(JSON.stringify(summary, null, 2) + '\n');
|
|
580
|
-
|
|
581
|
-
if (exitCode === 0)
|
|
582
|
-
await completeActiveCommand();
|
|
583
|
-
else
|
|
584
|
-
await failActiveCommand({ exit_code: exitCode });
|
|
585
|
-
process.exit(exitCode);
|
|
572
|
+
process.exit(operation.sys.status === 'succeeded' ? 0 : 1);
|
|
586
573
|
return;
|
|
587
574
|
}
|
|
588
575
|
await new Promise((resolvePromise) => {
|
|
@@ -594,7 +581,10 @@ export function registerApplyCommand(program) {
|
|
|
594
581
|
}));
|
|
595
582
|
let operation;
|
|
596
583
|
try {
|
|
597
|
-
operation = await client.applyImport(manifest,
|
|
584
|
+
operation = await client.applyImport(manifest, {
|
|
585
|
+
acknowledgeBreakingChanges: acknowledge,
|
|
586
|
+
allowDeletions: opts.allowDeletions === true,
|
|
587
|
+
});
|
|
598
588
|
}
|
|
599
589
|
catch (e) {
|
|
600
590
|
if (e instanceof ApiError) {
|
|
@@ -657,6 +647,7 @@ export function registerApplyCommand(program) {
|
|
|
657
647
|
addContentfulTargetOptions(selectCmd);
|
|
658
648
|
addCompositionOptions(selectCmd);
|
|
659
649
|
addSelectionOptions(selectCmd);
|
|
650
|
+
addAllowDeletionsOption(selectCmd);
|
|
660
651
|
selectCmd.option('--force', 'Skip confirmation for breaking changes').action(async (opts) => {
|
|
661
652
|
const nonInteractive = opts.selectAll || (opts.select ?? []).length > 0 || (opts.deselect ?? []).length > 0;
|
|
662
653
|
if (!nonInteractive) {
|
|
@@ -724,7 +715,10 @@ export function registerApplyCommand(program) {
|
|
|
724
715
|
}
|
|
725
716
|
let operation;
|
|
726
717
|
try {
|
|
727
|
-
operation = await client.applyImport(filteredManifest,
|
|
718
|
+
operation = await client.applyImport(filteredManifest, {
|
|
719
|
+
acknowledgeBreakingChanges: hasBreaking || opts.force === true,
|
|
720
|
+
allowDeletions: opts.allowDeletions === true,
|
|
721
|
+
});
|
|
728
722
|
}
|
|
729
723
|
catch (e) {
|
|
730
724
|
if (e instanceof ApiError)
|
|
@@ -766,7 +760,10 @@ export function registerApplyCommand(program) {
|
|
|
766
760
|
}));
|
|
767
761
|
let operation;
|
|
768
762
|
try {
|
|
769
|
-
operation = await client.applyImport(filteredManifest,
|
|
763
|
+
operation = await client.applyImport(filteredManifest, {
|
|
764
|
+
acknowledgeBreakingChanges: hasBreaking,
|
|
765
|
+
allowDeletions: opts.allowDeletions === true,
|
|
766
|
+
});
|
|
770
767
|
}
|
|
771
768
|
catch (e) {
|
|
772
769
|
if (e instanceof ApiError) {
|
|
@@ -4,7 +4,7 @@ import { resolveAutoFilter } from './auto-filter-resolve.js';
|
|
|
4
4
|
import { resolveAgent, resolveModel } from './agent-model-resolve.js';
|
|
5
5
|
import { addAgentModelOptions } from '../lib/agent-model-options.js';
|
|
6
6
|
import { resolveCompositionMode } from '../lib/composition-mode.js';
|
|
7
|
-
import { addCompositionOptions } from '../lib/command-options.js';
|
|
7
|
+
import { addAllowDeletionsOption, addCompositionOptions } from '../lib/command-options.js';
|
|
8
8
|
import { isConflictMode } from '../runs/save-path-resolver.js';
|
|
9
9
|
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
10
10
|
import { DEFAULT_CONFIGURED_HOST, toConfiguredHost } from '../host-utils.js';
|
|
@@ -46,6 +46,7 @@ export function registerImportCommand(program) {
|
|
|
46
46
|
.option('--print-prompt', 'Print the generate components prompt without invoking the agent. Replaces the legacy --dry-run prompt-print behaviour on this command.')
|
|
47
47
|
.option('--auto-accept-scope', 'Accept all extracted components without prompting (for scripted/non-TTY callers)');
|
|
48
48
|
addCompositionOptions(cmd);
|
|
49
|
+
addAllowDeletionsOption(cmd);
|
|
49
50
|
cmd
|
|
50
51
|
.option('--composition-map <path>', 'Consume a hand-authored parent→children interchange map (implies --composite)')
|
|
51
52
|
.option('--composition-agent', 'Opt into agentic mapping resolution when deterministic sources find no groups (implies --composite)')
|
|
@@ -136,6 +137,7 @@ export function registerImportCommand(program) {
|
|
|
136
137
|
...(opts.host ? { host: opts.host } : {}),
|
|
137
138
|
interactive: interactiveTerminalSupported,
|
|
138
139
|
...(opts.force ? { force: true } : {}),
|
|
140
|
+
...(opts.allowDeletions ? { allowDeletions: true } : {}),
|
|
139
141
|
});
|
|
140
142
|
return;
|
|
141
143
|
}
|
|
@@ -166,6 +168,7 @@ export function registerImportCommand(program) {
|
|
|
166
168
|
...(opts.saveAsNew ? { saveAsNew: true } : {}),
|
|
167
169
|
...(opts.outDir ? { outDir: opts.outDir } : {}),
|
|
168
170
|
...(opts.force ? { force: true } : {}),
|
|
171
|
+
...(opts.allowDeletions ? { allowDeletions: true } : {}),
|
|
169
172
|
});
|
|
170
173
|
return;
|
|
171
174
|
}
|
|
@@ -297,6 +300,7 @@ export function registerImportCommand(program) {
|
|
|
297
300
|
selectPromptPath: opts.selectPromptPath ?? creds.selectPromptPath,
|
|
298
301
|
generatePromptPath: opts.generatePromptPath ?? creds.generatePromptPath,
|
|
299
302
|
...(opts.rawTokens ? { initialRawTokensPath: resolve(opts.rawTokens) } : {}),
|
|
303
|
+
allowDeletions: opts.allowDeletions === true,
|
|
300
304
|
...pickerProps,
|
|
301
305
|
}));
|
|
302
306
|
unmountInk = unmount;
|
|
@@ -357,6 +361,7 @@ export function registerImportCommand(program) {
|
|
|
357
361
|
dryRun: dryRunForward,
|
|
358
362
|
selectPromptPath: opts.selectPromptPath,
|
|
359
363
|
autoRejectCycles: opts.autoRejectCycles ?? false,
|
|
364
|
+
allowDeletions: opts.allowDeletions ?? false,
|
|
360
365
|
compositionMode: headlessCompositionMode,
|
|
361
366
|
...(opts.compositionMap ? { compositionMap: opts.compositionMap } : {}),
|
|
362
367
|
...(opts.compositionAgent ? { compositionAgent: true } : {}),
|
|
@@ -26,6 +26,7 @@ export interface PipelineOptions {
|
|
|
26
26
|
selectPromptPath?: string;
|
|
27
27
|
/** When true, auto-reject cycle participants and retry push instead of surfacing an error. */
|
|
28
28
|
autoRejectCycles?: boolean;
|
|
29
|
+
allowDeletions?: boolean;
|
|
29
30
|
compositionMode?: CompositionMode;
|
|
30
31
|
compositionMap?: string;
|
|
31
32
|
compositionAgent?: boolean;
|
|
@@ -7,7 +7,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
9
|
import { getDebugLogger, debugEnvForSubprocess } from '../lib/debug-logger.js';
|
|
10
|
-
import { bindAnalyticsSession, emitSessionStarted } from '../analytics/index.js';
|
|
11
10
|
function findCliPath() {
|
|
12
11
|
return join(fileURLToPath(import.meta.url), '..', '..', '..', '..', 'bin', 'cli.js');
|
|
13
12
|
}
|
|
@@ -17,7 +16,7 @@ async function runStep(args, cliPath, env = {}, streamStderr = false) {
|
|
|
17
16
|
debug.event('import', 'subprocess.spawn', { cliPath, args });
|
|
18
17
|
return new Promise((res) => {
|
|
19
18
|
const child = execFile('node', [cliPath, ...args], {
|
|
20
|
-
env: debugEnvForSubprocess({ ...process.env, ...env
|
|
19
|
+
env: debugEnvForSubprocess({ ...process.env, ...env }),
|
|
21
20
|
});
|
|
22
21
|
let stdout = '';
|
|
23
22
|
let stderr = '';
|
|
@@ -109,10 +108,6 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
|
|
|
109
108
|
inputPath: projectRoot,
|
|
110
109
|
outDir,
|
|
111
110
|
});
|
|
112
|
-
await bindAnalyticsSession(sessionId, {
|
|
113
|
-
...(opts.spaceId ? { space_key: opts.spaceId, environment_key: opts.environmentId } : {}),
|
|
114
|
-
});
|
|
115
|
-
await emitSessionStarted('import');
|
|
116
111
|
progressWriter(`Experience Design System CLI — Pipeline Import`);
|
|
117
112
|
progressWriter(`Project: ${projectRoot}`);
|
|
118
113
|
progressWriter(`Output: ${outDir}`);
|
|
@@ -423,6 +418,8 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
|
|
|
423
418
|
pushArgs.push('--host', opts.host);
|
|
424
419
|
if (opts.verbose)
|
|
425
420
|
pushArgs.push('--verbose');
|
|
421
|
+
if (opts.allowDeletions)
|
|
422
|
+
pushArgs.push('--allow-deletions');
|
|
426
423
|
pushArgs.push('--yes');
|
|
427
424
|
const pushStepId = createStep(db, sessionId, 'apply push', {
|
|
428
425
|
components: componentsPath,
|
|
@@ -66,5 +66,6 @@ export type WizardAppProps = {
|
|
|
66
66
|
initialRawTokensPath?: string;
|
|
67
67
|
initialRuns?: RunRecord[];
|
|
68
68
|
onRunPicked?: (selection: RunPickerSelection) => void;
|
|
69
|
+
allowDeletions?: boolean;
|
|
69
70
|
};
|
|
70
|
-
export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, initialAgent, initialModel, initialProjectPath, host, autoAcceptScope, autoRejectCycles, compositionMode, compositionMap, compositionAgent, compositionAgentMode, compositionRefresh, generateMap, promptOverrides, noCache, autoFilter, livePreview, noPush, noSave, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, }?: WizardAppProps): React.ReactElement;
|
|
71
|
+
export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, initialAgent, initialModel, initialProjectPath, host, autoAcceptScope, autoRejectCycles, compositionMode, compositionMap, compositionAgent, compositionAgentMode, compositionRefresh, generateMap, promptOverrides, noCache, autoFilter, livePreview, noPush, noSave, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, allowDeletions, }?: WizardAppProps): React.ReactElement;
|
|
@@ -138,7 +138,7 @@ function logStep(entry) {
|
|
|
138
138
|
appendFileSync(WIZARD_LOG, line);
|
|
139
139
|
getDebugLogger().event('wizard', 'step', entry);
|
|
140
140
|
}
|
|
141
|
-
export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master', initialCmaToken = '', initialHost, initialAgent, initialModel, initialProjectPath, host, autoAcceptScope = false, autoRejectCycles = false, compositionMode = 'atomic', compositionMap, compositionAgent = false, compositionAgentMode, compositionRefresh = false, generateMap, promptOverrides, noCache = false, autoFilter = true, livePreview = true, noPush = false, noSave = false, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, } = {}) {
|
|
141
|
+
export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master', initialCmaToken = '', initialHost, initialAgent, initialModel, initialProjectPath, host, autoAcceptScope = false, autoRejectCycles = false, compositionMode = 'atomic', compositionMap, compositionAgent = false, compositionAgentMode, compositionRefresh = false, generateMap, promptOverrides, noCache = false, autoFilter = true, livePreview = true, noPush = false, noSave = false, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, allowDeletions = false, } = {}) {
|
|
142
142
|
const defaultConfiguredHost = toConfiguredHost(host || process.env['EDS_HOST']) ?? DEFAULT_CONFIGURED_HOST;
|
|
143
143
|
const resolveWizardHost = (hostValue) => hostValue || defaultConfiguredHost;
|
|
144
144
|
const { stdout } = useStdout();
|
|
@@ -986,7 +986,7 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
986
986
|
environmentId,
|
|
987
987
|
host: resolvedHost,
|
|
988
988
|
});
|
|
989
|
-
let operation = await client.applyImport(manifest, acknowledgeBreakingChanges);
|
|
989
|
+
let operation = await client.applyImport(manifest, { acknowledgeBreakingChanges, allowDeletions });
|
|
990
990
|
try {
|
|
991
991
|
logStep({
|
|
992
992
|
applyResponse: {
|
package/dist/src/index.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { createProgram } from './program.js';
|
|
2
|
-
import { failActiveCommand } from './analytics/index.js';
|
|
3
2
|
createProgram()
|
|
4
3
|
.parseAsync()
|
|
5
|
-
.catch(
|
|
6
|
-
await failActiveCommand({
|
|
7
|
-
error_name: err instanceof Error ? err.name : 'Error',
|
|
8
|
-
});
|
|
4
|
+
.catch((err) => {
|
|
9
5
|
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
10
6
|
process.exit(1);
|
|
11
7
|
});
|
|
@@ -3,3 +3,4 @@ export declare function addArtifactInputOptions(cmd: Command): Command;
|
|
|
3
3
|
export declare function addContentfulTargetOptions(cmd: Command): Command;
|
|
4
4
|
export declare function addCompositionOptions(cmd: Command): Command;
|
|
5
5
|
export declare function addSelectionOptions(cmd: Command): Command;
|
|
6
|
+
export declare function addAllowDeletionsOption(cmd: Command): Command;
|
|
@@ -25,3 +25,6 @@ export function addSelectionOptions(cmd) {
|
|
|
25
25
|
.option('--select <pattern>', 'Select entities by ID pattern (repeatable)', collectOptionValue, [])
|
|
26
26
|
.option('--deselect <pattern>', 'Deselect entities by ID pattern (repeatable)', collectOptionValue, []);
|
|
27
27
|
}
|
|
28
|
+
export function addAllowDeletionsOption(cmd) {
|
|
29
|
+
return cmd.option('--allow-deletions', 'Allow the push to delete remote ComponentTypes/DesignTokens missing from the manifest (default: skip them)');
|
|
30
|
+
}
|
package/dist/src/program.js
CHANGED
|
@@ -12,7 +12,6 @@ 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, noteCommandStart, registerAnalyticsExitHook } from './analytics/index.js';
|
|
16
15
|
const require = createRequire(import.meta.url);
|
|
17
16
|
const pkg = require('../package.json');
|
|
18
17
|
/**
|
|
@@ -74,7 +73,6 @@ export function createProgram() {
|
|
|
74
73
|
// and a bright-green "debug logs at <path>" banner is printed to stderr.
|
|
75
74
|
program.option('--debug', 'Write a JSONL trace of every decision to ~/.contentful/experience-design-system-cli/debug/');
|
|
76
75
|
program.option('--no-debug', 'Force debug logging off (overrides EDSI_DEBUG and persisted setup preference)');
|
|
77
|
-
registerAnalyticsExitHook();
|
|
78
76
|
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
79
77
|
// Merge opts from actionCommand and all ancestors — root-level --debug
|
|
80
78
|
// set alongside a subcommand ends up on the root command's opts, not the
|
|
@@ -91,12 +89,7 @@ export function createProgram() {
|
|
|
91
89
|
const chain = [];
|
|
92
90
|
for (let c = actionCommand; c && c.parent; c = c.parent)
|
|
93
91
|
chain.unshift(c.name());
|
|
94
|
-
|
|
95
|
-
noteCommandStart(commandChain);
|
|
96
|
-
await beginCommand(commandChain, { ...(debug !== undefined ? { debug } : {}) });
|
|
97
|
-
});
|
|
98
|
-
program.hook('postAction', async () => {
|
|
99
|
-
await completeActiveCommand();
|
|
92
|
+
await beginCommand(chain.join(' ') || actionCommand.name(), { ...(debug !== undefined ? { debug } : {}) });
|
|
100
93
|
});
|
|
101
94
|
return program;
|
|
102
95
|
}
|
|
@@ -26,5 +26,7 @@ export type ModifyLauncherInput = {
|
|
|
26
26
|
initialHost?: string;
|
|
27
27
|
/** Pre-fill CMA token (from credentials.json / env). */
|
|
28
28
|
initialCmaToken?: string;
|
|
29
|
+
/** From `--allow-deletions` flag. Forwarded to wizard's push step. */
|
|
30
|
+
allowDeletions?: boolean;
|
|
29
31
|
};
|
|
30
32
|
export declare function launchModifyWizard(input: ModifyLauncherInput): Promise<void>;
|
|
@@ -35,6 +35,8 @@ export async function launchModifyWizard(input) {
|
|
|
35
35
|
props.initialHost = input.initialHost;
|
|
36
36
|
if (input.initialCmaToken)
|
|
37
37
|
props.initialCmaToken = input.initialCmaToken;
|
|
38
|
+
if (input.allowDeletions !== undefined)
|
|
39
|
+
props.allowDeletions = input.allowDeletions;
|
|
38
40
|
const { waitUntilExit } = render(createElement(WizardApp, props));
|
|
39
41
|
await waitUntilExit();
|
|
40
42
|
}
|
|
@@ -16,6 +16,8 @@ export type ReplayRunOptions = {
|
|
|
16
16
|
interactive?: boolean;
|
|
17
17
|
/** When true, bypass the source/saved-file staleness check. */
|
|
18
18
|
force?: boolean;
|
|
19
|
+
/** From `--allow-deletions` flag. Forwarded verbatim to the pushed session's apply request. */
|
|
20
|
+
allowDeletions?: boolean;
|
|
19
21
|
/**
|
|
20
22
|
* Test seam: replace the interactive prompt with a deterministic resolver.
|
|
21
23
|
* The CLI surface never sets this; only used by tests.
|
|
@@ -57,6 +59,8 @@ export type ModifyRunOptions = {
|
|
|
57
59
|
outDir?: string;
|
|
58
60
|
/** When true, bypass the source/saved-file staleness check. */
|
|
59
61
|
force?: boolean;
|
|
62
|
+
/** From `--allow-deletions` flag. Forwarded through the modify wizard's push step. */
|
|
63
|
+
allowDeletions?: boolean;
|
|
60
64
|
};
|
|
61
65
|
/**
|
|
62
66
|
* Re-open the wizard with a prior run's extract/generate session pre-populated
|
|
@@ -83,6 +83,7 @@ export async function replayRun(opts) {
|
|
|
83
83
|
environmentId,
|
|
84
84
|
cmaToken,
|
|
85
85
|
...(host ? { host } : {}),
|
|
86
|
+
...(opts.allowDeletions ? { allowDeletions: true } : {}),
|
|
86
87
|
});
|
|
87
88
|
if (!result.ok) {
|
|
88
89
|
throw new Error(result.error);
|
|
@@ -98,6 +99,7 @@ export async function replayRun(opts) {
|
|
|
98
99
|
environmentId,
|
|
99
100
|
cmaToken,
|
|
100
101
|
...(host ? { host } : {}),
|
|
102
|
+
...(opts.allowDeletions ? { allowDeletions: true } : {}),
|
|
101
103
|
});
|
|
102
104
|
if (!tokenResult.ok) {
|
|
103
105
|
throw new Error(tokenResult.error);
|
|
@@ -157,5 +159,6 @@ export async function modifyRun(opts) {
|
|
|
157
159
|
...(mergedEnvironmentId ? { initialEnvironmentId: mergedEnvironmentId } : {}),
|
|
158
160
|
...(mergedHost ? { initialHost: mergedHost } : {}),
|
|
159
161
|
...(mergedCmaToken ? { initialCmaToken: mergedCmaToken } : {}),
|
|
162
|
+
...(opts.allowDeletions !== undefined ? { allowDeletions: opts.allowDeletions } : {}),
|
|
160
163
|
});
|
|
161
164
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.23.2-dev-build-
|
|
3
|
+
"version": "2.23.2-dev-build-5146e8a.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,16 +28,15 @@
|
|
|
28
28
|
"prompts/"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@segment/analytics-node": "^3.0.0",
|
|
32
31
|
"commander": "^13.1.0",
|
|
33
32
|
"ink": "^4.4.1",
|
|
34
33
|
"react": "^18.3.1",
|
|
35
34
|
"react-devtools-core": "^4.19.1",
|
|
36
35
|
"react-dom": "^18.3.1",
|
|
37
|
-
"@contentful/experience-design-system-client": "2.23.2-dev-build-
|
|
38
|
-
"@contentful/experience-design-system-
|
|
39
|
-
"@contentful/experience-design-system-generation": "2.23.2-dev-build-
|
|
40
|
-
"@contentful/experience-design-system-
|
|
36
|
+
"@contentful/experience-design-system-client": "2.23.2-dev-build-5146e8a.0",
|
|
37
|
+
"@contentful/experience-design-system-types": "2.23.2-dev-build-5146e8a.0",
|
|
38
|
+
"@contentful/experience-design-system-generation": "2.23.2-dev-build-5146e8a.0",
|
|
39
|
+
"@contentful/experience-design-system-extraction": "2.23.2-dev-build-5146e8a.0"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@tsconfig/node24": "^24.0.4",
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { ApplyOperationResponse } from '@contentful/experience-design-system-types';
|
|
2
|
-
import type { ImportApiClient } from '../apply/api-client.js';
|
|
3
|
-
/** Record apply/preview API outcomes on the active command. */
|
|
4
|
-
export declare function recordApplyOutcome(client: ImportApiClient, spaceId: string, environmentId: string, operation: ApplyOperationResponse): void;
|
|
5
|
-
/** Record Contentful target context without an apply operation (e.g. preview-only). */
|
|
6
|
-
export declare function recordContentfulContext(client: ImportApiClient, spaceId: string, environmentId: string): void;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { enrichCommandResult, setCommandContext } from './tracker.js';
|
|
2
|
-
function countWriteResult(items, entityType) {
|
|
3
|
-
const subset = items.filter((item) => item.entityType === entityType);
|
|
4
|
-
return {
|
|
5
|
-
created_count: subset.filter((item) => item.action === 'create' && item.status === 'succeeded').length,
|
|
6
|
-
updated_count: subset.filter((item) => item.action === 'update' && item.status === 'succeeded').length,
|
|
7
|
-
failed_count: subset.filter((item) => item.status === 'failed').length,
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
/** Record apply/preview API outcomes on the active command. */
|
|
11
|
-
export function recordApplyOutcome(client, spaceId, environmentId, operation) {
|
|
12
|
-
setCommandContext({
|
|
13
|
-
space_key: spaceId,
|
|
14
|
-
environment_key: environmentId,
|
|
15
|
-
x_contentful_request_id: client.getLastRequestId(),
|
|
16
|
-
});
|
|
17
|
-
enrichCommandResult({
|
|
18
|
-
dsi_operation_id: operation.sys.id,
|
|
19
|
-
component_type_result: countWriteResult(operation.items ?? [], 'ComponentType'),
|
|
20
|
-
design_token_result: countWriteResult(operation.items ?? [], 'DesignToken'),
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
/** Record Contentful target context without an apply operation (e.g. preview-only). */
|
|
24
|
-
export function recordContentfulContext(client, spaceId, environmentId) {
|
|
25
|
-
setCommandContext({
|
|
26
|
-
space_key: spaceId,
|
|
27
|
-
environment_key: environmentId,
|
|
28
|
-
x_contentful_request_id: client.getLastRequestId(),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
import { Analytics } from '@segment/analytics-node';
|
|
3
|
-
const require = createRequire(import.meta.url);
|
|
4
|
-
const pkg = require('../../package.json');
|
|
5
|
-
// Anonymous usage telemetry for the CLI. Disabled when DISABLE_ANALYTICS is set
|
|
6
|
-
// or when no write key is configured. Never blocks command execution.
|
|
7
|
-
export function cliVersion() {
|
|
8
|
-
return pkg.version;
|
|
9
|
-
}
|
|
10
|
-
export function analyticsEnabled() {
|
|
11
|
-
return !process.env.DISABLE_ANALYTICS && Boolean(resolveWriteKey());
|
|
12
|
-
}
|
|
13
|
-
function resolveWriteKey() {
|
|
14
|
-
const key = (process.env.SEGMENT_WRITE_KEY ?? '').trim();
|
|
15
|
-
return key.length > 0 ? key : undefined;
|
|
16
|
-
}
|
|
17
|
-
export async function trackEvent(event, properties, anonymousId) {
|
|
18
|
-
if (!analyticsEnabled())
|
|
19
|
-
return;
|
|
20
|
-
const writeKey = resolveWriteKey();
|
|
21
|
-
if (!writeKey)
|
|
22
|
-
return;
|
|
23
|
-
try {
|
|
24
|
-
const client = new Analytics({ writeKey });
|
|
25
|
-
client.on('error', () => {
|
|
26
|
-
/* never block the CLI on telemetry errors */
|
|
27
|
-
});
|
|
28
|
-
client.track({
|
|
29
|
-
event,
|
|
30
|
-
properties,
|
|
31
|
-
anonymousId,
|
|
32
|
-
timestamp: new Date(),
|
|
33
|
-
});
|
|
34
|
-
await client.closeAndFlush();
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
// Telemetry must never affect CLI exit codes or output.
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { analyticsEnabled, cliVersion, trackEvent } from './client.js';
|
|
2
|
-
export { normalizeCommand } from './normalize.js';
|
|
3
|
-
export { getOsName } from './os.js';
|
|
4
|
-
export { bindAnalyticsSession, bindAnalyticsSessionId, completeActiveCommand, emitSessionStarted, enrichCommandResult, failActiveCommand, getBoundSessionId, noteCommandStart, registerAnalyticsExitHook, resetAnalyticsStateForTests, setCommandContext, } from './tracker.js';
|
|
5
|
-
export { recordApplyOutcome, recordContentfulContext } from './apply.js';
|
|
6
|
-
export type { CommandCompletion, CommandContext, CommandFailure, DsiCliCommand, EntryCommand, OsName, WriteResult, } from './types.js';
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { analyticsEnabled, cliVersion, trackEvent } from './client.js';
|
|
2
|
-
export { normalizeCommand } from './normalize.js';
|
|
3
|
-
export { getOsName } from './os.js';
|
|
4
|
-
export { bindAnalyticsSession, bindAnalyticsSessionId, completeActiveCommand, emitSessionStarted, enrichCommandResult, failActiveCommand, getBoundSessionId, noteCommandStart, registerAnalyticsExitHook, resetAnalyticsStateForTests, setCommandContext, } from './tracker.js';
|
|
5
|
-
export { recordApplyOutcome, recordContentfulContext } from './apply.js';
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const COMMAND_MAP = {
|
|
2
|
-
'analyze extract': 'analyze_extract',
|
|
3
|
-
'analyze select': 'analyze_select',
|
|
4
|
-
'generate components': 'generate_components',
|
|
5
|
-
'generate tokens': 'generate_tokens',
|
|
6
|
-
'generate edit': 'generate_edit',
|
|
7
|
-
'apply preview': 'apply_preview',
|
|
8
|
-
'apply select': 'apply_select',
|
|
9
|
-
'apply push': 'apply_push',
|
|
10
|
-
'print components': 'print_components',
|
|
11
|
-
'print tokens': 'print_tokens',
|
|
12
|
-
import: 'import',
|
|
13
|
-
};
|
|
14
|
-
/** Map a Commander command chain (e.g. "apply push") to a tracked command id, if any. */
|
|
15
|
-
export function normalizeCommand(commandChain) {
|
|
16
|
-
return COMMAND_MAP[commandChain];
|
|
17
|
-
}
|
package/dist/src/analytics/os.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const OS_NAMES = {
|
|
2
|
-
android: 'Android',
|
|
3
|
-
aix: 'Linux',
|
|
4
|
-
darwin: 'macOS',
|
|
5
|
-
freebsd: 'Linux',
|
|
6
|
-
linux: 'Linux',
|
|
7
|
-
openbsd: 'Linux',
|
|
8
|
-
sunos: 'Linux',
|
|
9
|
-
win32: 'Windows',
|
|
10
|
-
};
|
|
11
|
-
export function getOsName() {
|
|
12
|
-
return OS_NAMES[process.platform] ?? 'other';
|
|
13
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { CommandCompletion, CommandContext, CommandFailure, EntryCommand } from './types.js';
|
|
2
|
-
/** Record the start of a tracked command. Invoked is deferred until a session is bound. */
|
|
3
|
-
export declare function noteCommandStart(commandChain: string): void;
|
|
4
|
-
/** Bind a session id, minting one when the command has no pipeline session. */
|
|
5
|
-
export declare function bindAnalyticsSessionId(sessionId: string | undefined, context?: CommandContext): Promise<string>;
|
|
6
|
-
/** Attach Contentful target context for the active command. */
|
|
7
|
-
export declare function setCommandContext(context: CommandContext): void;
|
|
8
|
-
/** Merge optional completion fields before the terminal event fires. */
|
|
9
|
-
export declare function enrichCommandResult(fields: CommandCompletion): void;
|
|
10
|
-
export declare function getBoundSessionId(): string | undefined;
|
|
11
|
-
/** Bind the pipeline session and emit invoked once both command and session are known. */
|
|
12
|
-
export declare function bindAnalyticsSession(id: string, context?: CommandContext): Promise<void>;
|
|
13
|
-
/** Emit session_started once per new pipeline head. */
|
|
14
|
-
export declare function emitSessionStarted(entryCommand: EntryCommand): Promise<void>;
|
|
15
|
-
export declare function completeActiveCommand(): Promise<void>;
|
|
16
|
-
export declare function failActiveCommand(fields?: CommandFailure): Promise<void>;
|
|
17
|
-
/** Best-effort terminal event when the process exits without a clean postAction hook. */
|
|
18
|
-
export declare function registerAnalyticsExitHook(): void;
|
|
19
|
-
export declare function resetAnalyticsStateForTests(): void;
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { cliVersion, trackEvent } from './client.js';
|
|
2
|
-
import { generateSessionId } from '../session/session-id.js';
|
|
3
|
-
import { normalizeCommand } from './normalize.js';
|
|
4
|
-
import { getOsName } from './os.js';
|
|
5
|
-
let pending = null;
|
|
6
|
-
let sessionId;
|
|
7
|
-
let sessionStartedEmitted = false;
|
|
8
|
-
function isPipelineStep() {
|
|
9
|
-
return process.env.EDS_IMPORT_PIPELINE === '1';
|
|
10
|
-
}
|
|
11
|
-
function buildBaseProps(command) {
|
|
12
|
-
return {
|
|
13
|
-
dsi_session_id: sessionId,
|
|
14
|
-
command,
|
|
15
|
-
...(isPipelineStep() ? { is_pipeline_step: true } : {}),
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
function mergeContext(target, source) {
|
|
19
|
-
if (source.space_key !== undefined)
|
|
20
|
-
target.space_key = source.space_key;
|
|
21
|
-
if (source.environment_key !== undefined)
|
|
22
|
-
target.environment_key = source.environment_key;
|
|
23
|
-
if (source.x_contentful_request_id !== undefined) {
|
|
24
|
-
target.x_contentful_request_id = source.x_contentful_request_id;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/** Record the start of a tracked command. Invoked is deferred until a session is bound. */
|
|
28
|
-
export function noteCommandStart(commandChain) {
|
|
29
|
-
const command = normalizeCommand(commandChain);
|
|
30
|
-
if (!command)
|
|
31
|
-
return;
|
|
32
|
-
pending = {
|
|
33
|
-
command,
|
|
34
|
-
startedAt: Date.now(),
|
|
35
|
-
invoked: false,
|
|
36
|
-
terminalEmitted: false,
|
|
37
|
-
context: {},
|
|
38
|
-
completion: {},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
/** Bind a session id, minting one when the command has no pipeline session. */
|
|
42
|
-
export async function bindAnalyticsSessionId(sessionId, context) {
|
|
43
|
-
const id = sessionId ?? generateSessionId();
|
|
44
|
-
await bindAnalyticsSession(id, context);
|
|
45
|
-
return id;
|
|
46
|
-
}
|
|
47
|
-
/** Attach Contentful target context for the active command. */
|
|
48
|
-
export function setCommandContext(context) {
|
|
49
|
-
if (!pending)
|
|
50
|
-
return;
|
|
51
|
-
mergeContext(pending.context, context);
|
|
52
|
-
mergeContext(pending.completion, context);
|
|
53
|
-
}
|
|
54
|
-
/** Merge optional completion fields before the terminal event fires. */
|
|
55
|
-
export function enrichCommandResult(fields) {
|
|
56
|
-
if (!pending)
|
|
57
|
-
return;
|
|
58
|
-
Object.assign(pending.completion, fields);
|
|
59
|
-
mergeContext(pending.context, fields);
|
|
60
|
-
}
|
|
61
|
-
export function getBoundSessionId() {
|
|
62
|
-
return sessionId;
|
|
63
|
-
}
|
|
64
|
-
/** Bind the pipeline session and emit invoked once both command and session are known. */
|
|
65
|
-
export async function bindAnalyticsSession(id, context) {
|
|
66
|
-
sessionId = id;
|
|
67
|
-
if (context)
|
|
68
|
-
setCommandContext(context);
|
|
69
|
-
await emitInvokedIfReady();
|
|
70
|
-
}
|
|
71
|
-
/** Emit session_started once per new pipeline head. */
|
|
72
|
-
export async function emitSessionStarted(entryCommand) {
|
|
73
|
-
if (!sessionId || sessionStartedEmitted)
|
|
74
|
-
return;
|
|
75
|
-
sessionStartedEmitted = true;
|
|
76
|
-
await trackEvent('dsi_cli_session_started', {
|
|
77
|
-
dsi_session_id: sessionId,
|
|
78
|
-
entry_command: entryCommand,
|
|
79
|
-
cli_version: cliVersion(),
|
|
80
|
-
node_version: process.version,
|
|
81
|
-
os_name: getOsName(),
|
|
82
|
-
}, sessionId);
|
|
83
|
-
await emitInvokedIfReady();
|
|
84
|
-
}
|
|
85
|
-
async function emitInvokedIfReady() {
|
|
86
|
-
if (!pending || pending.invoked || !sessionId)
|
|
87
|
-
return;
|
|
88
|
-
pending.invoked = true;
|
|
89
|
-
await trackEvent('dsi_cli_command_invoked', {
|
|
90
|
-
...buildBaseProps(pending.command),
|
|
91
|
-
...pending.context,
|
|
92
|
-
}, sessionId);
|
|
93
|
-
}
|
|
94
|
-
export async function completeActiveCommand() {
|
|
95
|
-
if (!pending || pending.terminalEmitted || !sessionId || !pending.invoked)
|
|
96
|
-
return;
|
|
97
|
-
pending.terminalEmitted = true;
|
|
98
|
-
const durationMs = Date.now() - pending.startedAt;
|
|
99
|
-
await trackEvent('dsi_cli_command_completed', {
|
|
100
|
-
...buildBaseProps(pending.command),
|
|
101
|
-
...pending.context,
|
|
102
|
-
...pending.completion,
|
|
103
|
-
outcome: 'ok',
|
|
104
|
-
duration_ms: durationMs,
|
|
105
|
-
}, sessionId);
|
|
106
|
-
}
|
|
107
|
-
export async function failActiveCommand(fields = {}) {
|
|
108
|
-
if (!pending || pending.terminalEmitted || !sessionId || !pending.invoked)
|
|
109
|
-
return;
|
|
110
|
-
pending.terminalEmitted = true;
|
|
111
|
-
const durationMs = Date.now() - pending.startedAt;
|
|
112
|
-
const outcome = fields.exit_code === 130 ? 'interrupted' : 'error';
|
|
113
|
-
await trackEvent('dsi_cli_command_failed', {
|
|
114
|
-
...buildBaseProps(pending.command),
|
|
115
|
-
...pending.context,
|
|
116
|
-
...fields,
|
|
117
|
-
outcome,
|
|
118
|
-
duration_ms: durationMs,
|
|
119
|
-
}, sessionId);
|
|
120
|
-
}
|
|
121
|
-
let exitHookRegistered = false;
|
|
122
|
-
/** Best-effort terminal event when the process exits without a clean postAction hook. */
|
|
123
|
-
export function registerAnalyticsExitHook() {
|
|
124
|
-
if (exitHookRegistered)
|
|
125
|
-
return;
|
|
126
|
-
exitHookRegistered = true;
|
|
127
|
-
process.on('exit', (code) => {
|
|
128
|
-
if (!pending || pending.terminalEmitted || !sessionId || !pending.invoked)
|
|
129
|
-
return;
|
|
130
|
-
const durationMs = Date.now() - pending.startedAt;
|
|
131
|
-
const outcome = code === 0 ? 'ok' : code === 130 ? 'interrupted' : 'error';
|
|
132
|
-
const event = code === 0 ? 'dsi_cli_command_completed' : 'dsi_cli_command_failed';
|
|
133
|
-
const props = code === 0
|
|
134
|
-
? {
|
|
135
|
-
...buildBaseProps(pending.command),
|
|
136
|
-
...pending.context,
|
|
137
|
-
...pending.completion,
|
|
138
|
-
outcome: 'ok',
|
|
139
|
-
duration_ms: durationMs,
|
|
140
|
-
}
|
|
141
|
-
: {
|
|
142
|
-
...buildBaseProps(pending.command),
|
|
143
|
-
...pending.context,
|
|
144
|
-
outcome: outcome === 'interrupted' ? 'interrupted' : 'error',
|
|
145
|
-
duration_ms: durationMs,
|
|
146
|
-
exit_code: code,
|
|
147
|
-
};
|
|
148
|
-
// Synchronous best-effort — async flush cannot run on 'exit'.
|
|
149
|
-
void trackEvent(event, props, sessionId);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
export function resetAnalyticsStateForTests() {
|
|
153
|
-
pending = null;
|
|
154
|
-
sessionId = undefined;
|
|
155
|
-
sessionStartedEmitted = false;
|
|
156
|
-
exitHookRegistered = false;
|
|
157
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/** Tracked CLI commands — closed set aligned with the published event schema. */
|
|
2
|
-
export type DsiCliCommand = 'analyze_extract' | 'analyze_select' | 'generate_components' | 'generate_tokens' | 'generate_edit' | 'apply_preview' | 'apply_select' | 'apply_push' | 'print_components' | 'print_tokens' | 'import';
|
|
3
|
-
export type EntryCommand = 'analyze_extract' | 'import';
|
|
4
|
-
export type OsName = 'macOS' | 'Linux' | 'Windows' | 'Android' | 'other';
|
|
5
|
-
export type WriteResult = {
|
|
6
|
-
created_count: number;
|
|
7
|
-
updated_count: number;
|
|
8
|
-
failed_count: number;
|
|
9
|
-
};
|
|
10
|
-
export type CommandContext = {
|
|
11
|
-
space_key?: string;
|
|
12
|
-
environment_key?: string;
|
|
13
|
-
x_contentful_request_id?: string;
|
|
14
|
-
};
|
|
15
|
-
export type CommandCompletion = CommandContext & {
|
|
16
|
-
dsi_operation_id?: string;
|
|
17
|
-
extracted_component_count?: number;
|
|
18
|
-
accepted_component_count?: number;
|
|
19
|
-
component_type_result?: WriteResult;
|
|
20
|
-
design_token_result?: WriteResult;
|
|
21
|
-
};
|
|
22
|
-
export type CommandFailure = CommandContext & {
|
|
23
|
-
dsi_operation_id?: string;
|
|
24
|
-
error_name?: string;
|
|
25
|
-
error_code?: string;
|
|
26
|
-
http_status_code?: number;
|
|
27
|
-
x_contentful_request_id?: string;
|
|
28
|
-
exit_code?: number;
|
|
29
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|