@contentful/experience-design-system-cli 2.18.1-dev-build-2117d43.0 → 2.18.1-dev-build-55a7404.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 +1 -1
- package/dist/package.json +1 -1
- package/dist/src/analyze/select/tui/App.js +4 -1
- package/dist/src/apply/api-client.d.ts +1 -1
- package/dist/src/apply/api-client.js +7 -1
- package/dist/src/apply/command.js +6 -6
- package/dist/src/apply/preview-utils.d.ts +4 -2
- package/dist/src/apply/preview-utils.js +8 -4
- package/dist/src/apply/tui/ServerApplyView.d.ts +3 -2
- package/dist/src/apply/tui/ServerApplyView.js +13 -5
- package/dist/src/apply/tui/ServerPreviewView.js +4 -1
- package/dist/src/import/tui/WizardApp.js +4 -4
- package/dist/src/import/tui/final-review-host.d.ts +2 -1
- package/dist/src/import/tui/final-review-host.js +2 -2
- package/dist/src/import/tui/runLivePreview.d.ts +3 -0
- package/dist/src/import/tui/runLivePreview.js +4 -1
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +2 -1
- package/dist/src/import/tui/steps/GenerateReviewStep.js +3 -3
- package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +5 -2
- package/dist/src/import/tui/steps/WizardPreviewStep.js +9 -5
- package/dist/src/import/tui/useFinalizePreview.d.ts +2 -0
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.d.ts +2 -0
- package/dist/src/import/tui/useLivePreview.js +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -214,7 +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
|
+
| `--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`. |
|
|
218
218
|
| `--dry-run` | _(deprecated)_ | Deprecated alias for `--print-prompt`. Emits a stderr deprecation notice; prompt-print semantics will be removed in a future release. |
|
|
219
219
|
|
|
220
220
|
### Run-picker at wizard start
|
package/dist/package.json
CHANGED
|
@@ -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
|
-
|
|
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 ?? '';
|
|
@@ -50,7 +50,7 @@ export declare class ImportApiClient {
|
|
|
50
50
|
private headers;
|
|
51
51
|
private requestWithRetry;
|
|
52
52
|
validateToken(): Promise<void>;
|
|
53
|
-
previewImport(manifest: ManifestPayload): Promise<ServerPreviewResponse>;
|
|
53
|
+
previewImport(manifest: ManifestPayload, allowDeletions?: boolean): Promise<ServerPreviewResponse>;
|
|
54
54
|
applyImport(manifest: ManifestPayload, options: {
|
|
55
55
|
acknowledgeBreakingChanges: boolean;
|
|
56
56
|
allowDeletions?: boolean;
|
|
@@ -268,19 +268,25 @@ export class ImportApiClient {
|
|
|
268
268
|
throw new ApiError(`unexpected error validating token: ${res.status}`, res.status, await res.text());
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
|
-
async previewImport(manifest) {
|
|
271
|
+
async previewImport(manifest, allowDeletions = false) {
|
|
272
272
|
const debug = getDebugLogger();
|
|
273
273
|
const startedAt = Date.now();
|
|
274
274
|
debug.event('apply', 'preview.request', {
|
|
275
275
|
url: `${this.base()}/design_systems/imports/preview`,
|
|
276
276
|
componentCount: manifest.components?.length ?? 0,
|
|
277
277
|
tokenCount: manifest.designTokens?.length ?? 0,
|
|
278
|
+
allowDeletions,
|
|
278
279
|
});
|
|
279
280
|
const result = await this.requestWithRetry('preview', PREVIEW_ERROR_PREFIX, () => designSystemImportSourcelessPreview({
|
|
280
281
|
baseUrl: this.host,
|
|
281
282
|
headers: this.headers(),
|
|
282
283
|
path: { spaceId: this.spaceId, environmentId: this.environmentId },
|
|
283
284
|
body: manifest,
|
|
285
|
+
// The generated query type only declares `access_token` — the backend
|
|
286
|
+
// reads `allowDeletions` off the raw query string with no OpenAPI
|
|
287
|
+
// parameter declared for it. Cast narrowly here rather than widening
|
|
288
|
+
// the generated type for an undocumented param.
|
|
289
|
+
query: { allowDeletions: String(allowDeletions) },
|
|
284
290
|
parseAs: 'json',
|
|
285
291
|
}));
|
|
286
292
|
if (!result.response.ok) {
|
|
@@ -504,7 +504,7 @@ export function registerApplyCommand(program) {
|
|
|
504
504
|
const manifest = buildManifest(components, tokens);
|
|
505
505
|
let preview;
|
|
506
506
|
try {
|
|
507
|
-
preview = await client.previewImport(manifest);
|
|
507
|
+
preview = await client.previewImport(manifest, opts.allowDeletions === true);
|
|
508
508
|
}
|
|
509
509
|
catch (e) {
|
|
510
510
|
if (e instanceof ApiError)
|
|
@@ -575,7 +575,7 @@ export function registerApplyCommand(program) {
|
|
|
575
575
|
return;
|
|
576
576
|
}
|
|
577
577
|
await new Promise((resolvePromise) => {
|
|
578
|
-
const runApply = async (acknowledge) => {
|
|
578
|
+
const runApply = async (acknowledge, applyDeletions) => {
|
|
579
579
|
instance.rerender(createElement(ServerApplyProgress, {
|
|
580
580
|
spaceId,
|
|
581
581
|
environmentId,
|
|
@@ -585,7 +585,7 @@ export function registerApplyCommand(program) {
|
|
|
585
585
|
try {
|
|
586
586
|
operation = await client.applyImport(manifest, {
|
|
587
587
|
acknowledgeBreakingChanges: acknowledge,
|
|
588
|
-
allowDeletions:
|
|
588
|
+
allowDeletions: applyDeletions,
|
|
589
589
|
});
|
|
590
590
|
}
|
|
591
591
|
catch (e) {
|
|
@@ -635,8 +635,8 @@ export function registerApplyCommand(program) {
|
|
|
635
635
|
environmentId,
|
|
636
636
|
breakingWithImpact,
|
|
637
637
|
allowDeletions: opts.allowDeletions === true,
|
|
638
|
-
onConfirm: (acknowledge) => {
|
|
639
|
-
void runApply(acknowledge);
|
|
638
|
+
onConfirm: (acknowledge, applyDeletions) => {
|
|
639
|
+
void runApply(acknowledge, applyDeletions);
|
|
640
640
|
},
|
|
641
641
|
onCancel: () => {
|
|
642
642
|
process.exit(0);
|
|
@@ -680,7 +680,7 @@ export function registerApplyCommand(program) {
|
|
|
680
680
|
const fullManifest = buildManifest(components, tokens);
|
|
681
681
|
let preview;
|
|
682
682
|
try {
|
|
683
|
-
preview = await client.previewImport(fullManifest);
|
|
683
|
+
preview = await client.previewImport(fullManifest, opts.allowDeletions === true);
|
|
684
684
|
}
|
|
685
685
|
catch (e) {
|
|
686
686
|
if (e instanceof ApiError)
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { ServerPreviewResponse } from '@contentful/experience-design-system-types';
|
|
2
2
|
/**
|
|
3
3
|
* True when a preview response describes zero server-side changes across
|
|
4
|
-
* every diff bucket (components, tokens, taxonomies)
|
|
4
|
+
* every diff bucket (components, tokens, taxonomies) AND no suppressedDeletions.
|
|
5
|
+
* Used by:
|
|
5
6
|
* - `experiences apply` (CLI): short-circuit the confirm-and-push step.
|
|
6
7
|
* - `experiences import` wizard: block finalize when the resulting push
|
|
7
8
|
* would be a pure no-op (INTEG-4411 refined guard).
|
|
8
9
|
*
|
|
9
10
|
* A push that produces ANY entry in ANY bucket — including a rejection that
|
|
10
|
-
* removes a server-side component — is NOT empty.
|
|
11
|
+
* removes a server-side component — is NOT empty. Similarly, a preview with
|
|
12
|
+
* ANY suppressedDeletions (when fetched with allowDeletions: false) is NOT empty.
|
|
11
13
|
*/
|
|
12
14
|
export declare function isEmptyPreview(preview: ServerPreviewResponse): boolean;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* True when a preview response describes zero server-side changes across
|
|
3
|
-
* every diff bucket (components, tokens, taxonomies)
|
|
3
|
+
* every diff bucket (components, tokens, taxonomies) AND no suppressedDeletions.
|
|
4
|
+
* Used by:
|
|
4
5
|
* - `experiences apply` (CLI): short-circuit the confirm-and-push step.
|
|
5
6
|
* - `experiences import` wizard: block finalize when the resulting push
|
|
6
7
|
* would be a pure no-op (INTEG-4411 refined guard).
|
|
7
8
|
*
|
|
8
9
|
* A push that produces ANY entry in ANY bucket — including a rejection that
|
|
9
|
-
* removes a server-side component — is NOT empty.
|
|
10
|
+
* removes a server-side component — is NOT empty. Similarly, a preview with
|
|
11
|
+
* ANY suppressedDeletions (when fetched with allowDeletions: false) is NOT empty.
|
|
10
12
|
*/
|
|
11
13
|
export function isEmptyPreview(preview) {
|
|
12
|
-
const { components, tokens, taxonomies } = preview;
|
|
14
|
+
const { components, tokens, taxonomies, suppressedDeletions } = preview;
|
|
13
15
|
return (components.new.length === 0 &&
|
|
14
16
|
components.changed.length === 0 &&
|
|
15
17
|
components.removed.length === 0 &&
|
|
@@ -18,5 +20,7 @@ export function isEmptyPreview(preview) {
|
|
|
18
20
|
tokens.removed.length === 0 &&
|
|
19
21
|
taxonomies.new.length === 0 &&
|
|
20
22
|
taxonomies.changed.length === 0 &&
|
|
21
|
-
taxonomies.removed.length === 0
|
|
23
|
+
taxonomies.removed.length === 0 &&
|
|
24
|
+
(suppressedDeletions?.components ?? 0) === 0 &&
|
|
25
|
+
(suppressedDeletions?.tokens ?? 0) === 0);
|
|
22
26
|
}
|
|
@@ -5,11 +5,12 @@ interface ServerPreviewConfirmProps {
|
|
|
5
5
|
spaceId: string;
|
|
6
6
|
environmentId: string;
|
|
7
7
|
breakingWithImpact: boolean;
|
|
8
|
+
/** The value the preview was actually fetched with. */
|
|
8
9
|
allowDeletions: boolean;
|
|
9
|
-
onConfirm: (acknowledge: boolean) => void;
|
|
10
|
+
onConfirm: (acknowledge: boolean, allowDeletions: boolean) => void;
|
|
10
11
|
onCancel: () => void;
|
|
11
12
|
}
|
|
12
|
-
export declare function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions, onConfirm, onCancel, }: ServerPreviewConfirmProps): React.ReactElement;
|
|
13
|
+
export declare function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions: fetchedAllowDeletions, onConfirm, onCancel, }: ServerPreviewConfirmProps): React.ReactElement;
|
|
13
14
|
interface ServerPreviewAppProps {
|
|
14
15
|
preview: ServerPreviewResponse;
|
|
15
16
|
spaceId: string;
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { Box, Text, useInput } from 'ink';
|
|
3
4
|
import { ServerPreviewView } from './ServerPreviewView.js';
|
|
4
5
|
import { buildPostPushUrl } from '../../lib/contentful-urls.js';
|
|
5
6
|
import { formatEdsiError } from '../../lib/error-parser.js';
|
|
6
|
-
export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions, onConfirm, onCancel, }) {
|
|
7
|
+
export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions: fetchedAllowDeletions, onConfirm, onCancel, }) {
|
|
8
|
+
const [allowDeletions, setAllowDeletions] = useState(fetchedAllowDeletions);
|
|
9
|
+
const removedCount = preview.components.removed.length + preview.tokens.removed.length;
|
|
7
10
|
useInput((input, key) => {
|
|
8
|
-
if (key.return)
|
|
9
|
-
onConfirm(breakingWithImpact);
|
|
11
|
+
if (key.return) {
|
|
12
|
+
onConfirm(breakingWithImpact, allowDeletions);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if ((input === 'x' || input === 'X') && fetchedAllowDeletions && removedCount > 0) {
|
|
16
|
+
setAllowDeletions((prev) => !prev);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
10
19
|
if (key.escape || input === 'q')
|
|
11
20
|
onCancel();
|
|
12
21
|
});
|
|
13
|
-
|
|
14
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId, allowDeletions: allowDeletions }), _jsxs(Box, { paddingX: 2, flexDirection: "column", children: [breakingWithImpact && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply."] })), allowDeletions && removedCount > 0 && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] })), _jsxs(Text, { children: [' ', "Press ", _jsx(Text, { bold: true, children: "Enter" }), " to apply, ", _jsx(Text, { bold: true, children: "Esc" }), " to cancel"] })] })] }));
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId, allowDeletions: allowDeletions }), _jsxs(Box, { paddingX: 2, flexDirection: "column", children: [breakingWithImpact && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply."] })), allowDeletions && removedCount > 0 && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] })), fetchedAllowDeletions && removedCount > 0 && (_jsxs(Text, { dimColor: true, children: [' ', "[x] ", allowDeletions ? '[✓]' : '[ ]', " Also delete ", removedCount, " ", removedCount === 1 ? 'entity' : 'entities'] })), _jsxs(Text, { children: [' ', "Press ", _jsx(Text, { bold: true, children: "Enter" }), " to apply, ", _jsx(Text, { bold: true, children: "Esc" }), " to cancel"] })] })] }));
|
|
15
23
|
}
|
|
16
24
|
export function ServerPreviewApp({ preview, spaceId, environmentId, allowDeletions, }) {
|
|
17
25
|
useInput((input, key) => {
|
|
@@ -19,5 +19,8 @@ export function ServerPreviewView({ preview, spaceId, environmentId, allowDeleti
|
|
|
19
19
|
const totalTokens = tokens.new.length + tokens.changed.length + tokens.unchanged.length + tokens.removed.length;
|
|
20
20
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Preview \u2014 ", environmentId, " @ ", spaceId] }), _jsx(Text, { children: " " }), totalComponents > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Component Types (", totalComponents, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", components.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", components.changed.length, " to update"] }), _jsxs(Text, { color: allowDeletions ? 'red' : 'yellow', children: [' ', allowDeletions ? '✗' : '⊘', " ", components.removed.length, " to ", allowDeletions ? 'delete' : 'skip'] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", components.unchanged.length, " unchanged"] }), components.changed.map((item, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: item.hasPendingDraftChanges })] }), _jsx(BreakingBadge, { item: item })] }, i))), _jsx(Text, { children: " " })] })), totalTokens > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Design Tokens (", totalTokens, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", tokens.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", tokens.changed.length, " to update"] }), _jsxs(Text, { color: allowDeletions ? 'red' : 'yellow', children: [' ', allowDeletions ? '✗' : '⊘', " ", tokens.removed.length, " to ", allowDeletions ? 'delete' : 'skip'] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", tokens.unchanged.length, " unchanged"] }), tokens.changed
|
|
21
21
|
.filter((t) => t.hasPendingDraftChanges)
|
|
22
|
-
.map((item, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: true })] }, i))), _jsx(Text, { children: " " })] })),
|
|
22
|
+
.map((item, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: true })] }, i))), _jsx(Text, { children: " " })] })), !allowDeletions &&
|
|
23
|
+
(preview.suppressedDeletions?.components ?? 0) + (preview.suppressedDeletions?.tokens ?? 0) > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "yellow", children: [' ', "\u2298 ", (preview.suppressedDeletions?.components ?? 0) + (preview.suppressedDeletions?.tokens ?? 0), ' ', (preview.suppressedDeletions?.components ?? 0) + (preview.suppressedDeletions?.tokens ?? 0) === 1
|
|
24
|
+
? 'entity'
|
|
25
|
+
: 'entities', ' ', "skipped (rerun with --allow-deletions to remove them)"] }) })), _jsx(Text, { dimColor: true, children: " Press Q to exit." })] }));
|
|
23
26
|
}
|
|
@@ -826,7 +826,7 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
826
826
|
tokens = await readTokensFromPath('tokens', tokensPath);
|
|
827
827
|
}
|
|
828
828
|
let manifest = buildManifest(components, tokens, { deleteAllComponents: allowEmptyDeleteAllRef.current });
|
|
829
|
-
let preview = await client.previewImport(manifest);
|
|
829
|
+
let preview = await client.previewImport(manifest, allowDeletions);
|
|
830
830
|
if (extractSessionId) {
|
|
831
831
|
let needsRepreview = false;
|
|
832
832
|
const db = openPipelineDb();
|
|
@@ -848,7 +848,7 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
848
848
|
if (needsRepreview) {
|
|
849
849
|
components = loadCDFComponents(db, extractSessionId);
|
|
850
850
|
manifest = buildManifest(components, tokens, { deleteAllComponents: allowEmptyDeleteAllRef.current });
|
|
851
|
-
preview = await client.previewImport(manifest);
|
|
851
|
+
preview = await client.previewImport(manifest, allowDeletions);
|
|
852
852
|
}
|
|
853
853
|
}
|
|
854
854
|
finally {
|
|
@@ -1433,7 +1433,7 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
1433
1433
|
return (_jsx(RunningStep, { stepNumber: stepNum, totalSteps: totalSteps, title: "Generating definitions", description: `${formatAcceptanceSummary({ accepted: state.acceptedCount, autoRejected: state.autoRejectedCount })} ${state.agent} is mapping your TypeScript types to Contentful's CDF format.${hasTokens ? ' Using your design tokens for prop resolution.' : ''}`, detail: progressDetail }));
|
|
1434
1434
|
}
|
|
1435
1435
|
case 'final-review': {
|
|
1436
|
-
return (_jsx(FinalReviewHost, { extractSessionId: state.extractSessionId, generatedCount: state.generatedCount, autoAccept: autoAcceptScope, compositionMode: compositionMode, livePreview: livePreview, spaceId: state.spaceId, environmentId: state.environmentId, cmaToken: state.cmaToken, host: state.host, tokensPath: state.tokensPath, initialFinalizeError: state.finalizeErrorBanner, onFinalize: (accepted, rejected, unresolved) => {
|
|
1436
|
+
return (_jsx(FinalReviewHost, { extractSessionId: state.extractSessionId, generatedCount: state.generatedCount, autoAccept: autoAcceptScope, compositionMode: compositionMode, livePreview: livePreview, spaceId: state.spaceId, environmentId: state.environmentId, cmaToken: state.cmaToken, host: state.host, tokensPath: state.tokensPath, initialFinalizeError: state.finalizeErrorBanner, allowDeletions: allowDeletions, onFinalize: (accepted, rejected, unresolved) => {
|
|
1437
1437
|
process.stderr.write(`Accepted: ${accepted} Rejected: ${rejected} Unresolved: ${unresolved}\n`);
|
|
1438
1438
|
let acceptedCount = accepted;
|
|
1439
1439
|
const detectAcceptedCycles = () => {
|
|
@@ -1601,7 +1601,7 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
1601
1601
|
// present-but-empty (only $schema), so editing would dead-end on the
|
|
1602
1602
|
// "No generated definitions found" screen.
|
|
1603
1603
|
const editableComponentCount = Object.keys(state.manifest?.componentsManifest ?? {}).filter((k) => k !== '$schema').length;
|
|
1604
|
-
return (_jsx(WizardPreviewStep, { preview: state.serverPreview, spaceId: state.spaceId, environmentId: state.environmentId, stepNumber: totalSteps, totalSteps: totalSteps,
|
|
1604
|
+
return (_jsx(WizardPreviewStep, { preview: state.serverPreview, spaceId: state.spaceId, environmentId: state.environmentId, stepNumber: totalSteps, totalSteps: totalSteps, allowDeletions: allowDeletions, onConfirm: (acknowledge, deleteMissing) => {
|
|
1605
1605
|
void runPush(state.manifest, state.spaceId, state.environmentId, state.cmaToken, state.host, acknowledge, deleteMissing, state.serverPreview);
|
|
1606
1606
|
}, ...(editableComponentCount > 0 ? { onEdit: () => void runEditFromPreview() } : {}), onSaveFiles: () => {
|
|
1607
1607
|
void startSaveFlow();
|
|
@@ -14,5 +14,6 @@ export type FinalReviewHostProps = {
|
|
|
14
14
|
host?: string;
|
|
15
15
|
tokensPath?: string;
|
|
16
16
|
initialFinalizeError?: string | null;
|
|
17
|
+
allowDeletions?: boolean;
|
|
17
18
|
};
|
|
18
|
-
export declare function FinalReviewHost({ extractSessionId, generatedCount, autoAccept, compositionMode, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, }: FinalReviewHostProps): React.ReactElement;
|
|
19
|
+
export declare function FinalReviewHost({ extractSessionId, generatedCount, autoAccept, compositionMode, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, allowDeletions, }: FinalReviewHostProps): React.ReactElement;
|
|
@@ -4,7 +4,7 @@ import { PALETTE } from '../../analyze/select/tui/theme.js';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { GenerateReviewStep } from './steps/GenerateReviewStep.js';
|
|
6
6
|
import { AtomicGenerateReviewStep } from './steps/AtomicGenerateReviewStep.js';
|
|
7
|
-
export function FinalReviewHost({ extractSessionId, generatedCount, autoAccept, compositionMode = 'atomic', onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, }) {
|
|
7
|
+
export function FinalReviewHost({ extractSessionId, generatedCount, autoAccept, compositionMode = 'atomic', onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, allowDeletions, }) {
|
|
8
8
|
if (!extractSessionId) {
|
|
9
9
|
return (_jsx(Box, { paddingX: 2, paddingY: 1, children: _jsx(Text, { color: PALETTE.error, children: "Error: no session ID \u2014 cannot load generated definitions." }) }));
|
|
10
10
|
}
|
|
@@ -15,7 +15,7 @@ export function FinalReviewHost({ extractSessionId, generatedCount, autoAccept,
|
|
|
15
15
|
// passes projectSlotGraph to FieldEditor and never walks closures/cycles, so
|
|
16
16
|
// slot-composition editing and every hierarchy affordance stay absent.
|
|
17
17
|
const StepComponent = compositionMode === 'atomic' ? AtomicGenerateReviewStep : GenerateReviewStep;
|
|
18
|
-
return (_jsx(StepComponent, { extractSessionId: extractSessionId, onFinalize: onFinalize, onQuit: onQuit, livePreview: livePreview, spaceId: spaceId, environmentId: environmentId, cmaToken: cmaToken, host: host, tokensPath: tokensPath, initialFinalizeError: initialFinalizeError }));
|
|
18
|
+
return (_jsx(StepComponent, { extractSessionId: extractSessionId, onFinalize: onFinalize, onQuit: onQuit, livePreview: livePreview, spaceId: spaceId, environmentId: environmentId, cmaToken: cmaToken, host: host, tokensPath: tokensPath, initialFinalizeError: initialFinalizeError, ...(compositionMode !== 'atomic' ? { allowDeletions } : {}) }));
|
|
19
19
|
}
|
|
20
20
|
function FinalReviewAutoAccept({ generatedCount, onFinalize, }) {
|
|
21
21
|
React.useEffect(() => {
|
|
@@ -26,6 +26,9 @@ export type RunLivePreviewOptions = {
|
|
|
26
26
|
* full delete. Lets the Finalize dialog show exactly what the accepted push
|
|
27
27
|
* would delete, independent of the session's on-disk generated rows. */
|
|
28
28
|
acceptedKeys?: ReadonlySet<string>;
|
|
29
|
+
/** Forwarded verbatim to `previewImport`. Governs whether the response
|
|
30
|
+
* includes removed entities or a suppressed-count summary instead. */
|
|
31
|
+
allowDeletions?: boolean;
|
|
29
32
|
};
|
|
30
33
|
/**
|
|
31
34
|
* Pure async helper used by `useLivePreview` to re-fire `previewImport` after a
|
|
@@ -60,7 +60,10 @@ export async function runLivePreview(opts) {
|
|
|
60
60
|
timeoutHandle = setTimeout(() => reject(new TimeoutError()), timeoutMs);
|
|
61
61
|
});
|
|
62
62
|
try {
|
|
63
|
-
const response = (await Promise.race([
|
|
63
|
+
const response = (await Promise.race([
|
|
64
|
+
client.previewImport(manifest, opts.allowDeletions === true),
|
|
65
|
+
timeoutPromise,
|
|
66
|
+
]));
|
|
64
67
|
if (process.env['EDS_VERBOSE']) {
|
|
65
68
|
const durationMs = Date.now() - startedAt;
|
|
66
69
|
try {
|
|
@@ -11,6 +11,7 @@ type GenerateReviewStepProps = {
|
|
|
11
11
|
host?: string;
|
|
12
12
|
tokensPath?: string;
|
|
13
13
|
initialFinalizeError?: string | null;
|
|
14
|
+
allowDeletions?: boolean;
|
|
14
15
|
};
|
|
15
16
|
export declare function sortComponentsForSidebar<T extends {
|
|
16
17
|
key: string;
|
|
@@ -33,4 +34,4 @@ export interface BreakingRow {
|
|
|
33
34
|
}
|
|
34
35
|
export declare function buildBreakingRows(breakingChanges: BreakingComponent[]): BreakingRow[];
|
|
35
36
|
export declare function deriveBreakingChanges(response: ServerPreviewResponse): BreakingComponent[];
|
|
36
|
-
export declare function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, }: GenerateReviewStepProps): React.ReactElement;
|
|
37
|
+
export declare function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, initialFinalizeError, allowDeletions, }: GenerateReviewStepProps): React.ReactElement;
|
|
@@ -174,7 +174,7 @@ export function deriveBreakingChanges(response) {
|
|
|
174
174
|
}
|
|
175
175
|
return out;
|
|
176
176
|
}
|
|
177
|
-
export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, livePreview = true, spaceId = '', environmentId = '', cmaToken = '', host = '', tokensPath = '', initialFinalizeError = null, }) {
|
|
177
|
+
export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, livePreview = true, spaceId = '', environmentId = '', cmaToken = '', host = '', tokensPath = '', initialFinalizeError = null, allowDeletions = false, }) {
|
|
178
178
|
const { stdout } = useStdout();
|
|
179
179
|
const terminalWidth = stdout?.columns ?? 80;
|
|
180
180
|
const [components, setComponents] = useState([]);
|
|
@@ -271,9 +271,8 @@ export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, liveP
|
|
|
271
271
|
cmaToken,
|
|
272
272
|
host,
|
|
273
273
|
onResult: handleLivePreviewResult,
|
|
274
|
-
// With nothing accepted, preview the delete-all diff so the review UI shows
|
|
275
|
-
// which existing components a push would remove (instead of an empty preview).
|
|
276
274
|
deleteAllComponents: acceptedCountForPreview === 0,
|
|
275
|
+
allowDeletions,
|
|
277
276
|
});
|
|
278
277
|
const SPINNER_FRAMES = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏';
|
|
279
278
|
const [spinnerTick, setSpinnerTick] = useState(0);
|
|
@@ -352,6 +351,7 @@ export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, liveP
|
|
|
352
351
|
cmaToken,
|
|
353
352
|
host,
|
|
354
353
|
acceptedKeys: new Set(components.filter((c) => c.status === 'accepted').map((c) => c.key)),
|
|
354
|
+
allowDeletions,
|
|
355
355
|
});
|
|
356
356
|
const handleFinalizeConfirm = () => {
|
|
357
357
|
const acceptedCount = components.filter((c) => c.status === 'accepted').length;
|
|
@@ -12,11 +12,14 @@ type WizardPreviewStepProps = {
|
|
|
12
12
|
environmentId: string;
|
|
13
13
|
stepNumber: number;
|
|
14
14
|
totalSteps: number;
|
|
15
|
-
|
|
15
|
+
/** The value the preview was actually fetched with — not a hint. When
|
|
16
|
+
* `false`, the server never returned removed entities, so there is
|
|
17
|
+
* nothing to render item-by-item or toggle over. */
|
|
18
|
+
allowDeletions?: boolean;
|
|
16
19
|
onConfirm: (acknowledge: boolean, allowDeletions: boolean) => void;
|
|
17
20
|
onEdit?: () => void;
|
|
18
21
|
onSaveFiles?: () => void;
|
|
19
22
|
onQuit: () => void;
|
|
20
23
|
};
|
|
21
|
-
export declare function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber, totalSteps,
|
|
24
|
+
export declare function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber, totalSteps, allowDeletions: fetchedAllowDeletions, onConfirm, onEdit, onSaveFiles, onQuit, }: WizardPreviewStepProps): React.ReactElement;
|
|
22
25
|
export {};
|
|
@@ -89,11 +89,14 @@ export function buildPreviewDiffLines(preview) {
|
|
|
89
89
|
}
|
|
90
90
|
return lines;
|
|
91
91
|
}
|
|
92
|
-
export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber, totalSteps,
|
|
92
|
+
export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber, totalSteps, allowDeletions: fetchedAllowDeletions = false, onConfirm, onEdit, onSaveFiles, onQuit, }) {
|
|
93
93
|
const breakingWithImpact = hasBreakingChangesWithImpact(preview);
|
|
94
94
|
const [diffExpanded, setDiffExpanded] = useState(false);
|
|
95
95
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
96
|
-
|
|
96
|
+
// Local state exists only to let the user opt OUT of a deletion the fetch
|
|
97
|
+
// already surfaced — it can never turn true when the fetch used false,
|
|
98
|
+
// because there's nothing in `preview` to reveal in that case.
|
|
99
|
+
const [allowDeletions, setAllowDeletions] = useState(fetchedAllowDeletions);
|
|
97
100
|
const { stdout } = useStdout();
|
|
98
101
|
const terminalRows = stdout?.rows ?? 40;
|
|
99
102
|
const viewportHeight = Math.max(terminalRows - 14, 10);
|
|
@@ -107,12 +110,13 @@ export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber,
|
|
|
107
110
|
}, [diffExpanded, preview]);
|
|
108
111
|
const maxScroll = Math.max(0, allDiffLines.length - viewportHeight);
|
|
109
112
|
const removedCount = preview.components.removed.length + preview.tokens.removed.length;
|
|
113
|
+
const suppressedCount = (preview.suppressedDeletions?.components ?? 0) + (preview.suppressedDeletions?.tokens ?? 0);
|
|
110
114
|
useImmediateInput((input, key) => {
|
|
111
115
|
if (key.return) {
|
|
112
116
|
onConfirm(breakingWithImpact, allowDeletions);
|
|
113
117
|
return;
|
|
114
118
|
}
|
|
115
|
-
if ((input === 'x' || input === 'X') && removedCount > 0) {
|
|
119
|
+
if ((input === 'x' || input === 'X') && fetchedAllowDeletions && removedCount > 0) {
|
|
116
120
|
setAllowDeletions((prev) => !prev);
|
|
117
121
|
return;
|
|
118
122
|
}
|
|
@@ -155,12 +159,12 @@ export function WizardPreviewStep({ preview, spaceId, environmentId, stepNumber,
|
|
|
155
159
|
const { components, tokens } = preview;
|
|
156
160
|
const hasComponents = components.new.length + components.changed.length + components.removed.length > 0;
|
|
157
161
|
const hasTokens = tokens.new.length + tokens.changed.length + tokens.removed.length > 0;
|
|
158
|
-
const hasAnything = hasComponents || hasTokens;
|
|
162
|
+
const hasAnything = hasComponents || hasTokens || suppressedCount > 0;
|
|
159
163
|
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: '─'.repeat(40) }), _jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, children: ["Step ", stepNumber, " of ", totalSteps] }), _jsx(Text, { bold: true, children: "\u2014" }), _jsx(Text, { bold: true, children: "Push to Contentful" })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(40) })] }), hasAnything ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "Here's what will happen in your space:" }), hasComponents && (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Box, { gap: 1, marginTop: 1, children: _jsx(Text, { bold: true, dimColor: true, children: "ComponentTypes" }) }), components.new.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: " \uFF0B" }), _jsxs(Text, { children: [components.new.length, " will be created"] })] }), components.new.map((item, i) => {
|
|
160
164
|
const name = item.key ?? item.$name ?? 'unknown';
|
|
161
165
|
return (_jsxs(Text, { color: PALETTE.success, children: [' ', "+ ", name] }, `new-${i}`));
|
|
162
166
|
})] })), components.changed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.warning, children: " \uFF5E" }), _jsxs(Text, { children: [components.changed.length, " will be updated"] })] }), components.changed.map((item, i) => {
|
|
163
167
|
const isBreaking = item.changeClassification?.classification === 'breaking';
|
|
164
168
|
return (_jsxs(Text, { color: isBreaking ? PALETTE.error : PALETTE.warning, children: [' ', isBreaking ? '⚠' : '~', " ", item.current.name] }, `chg-${i}`));
|
|
165
|
-
})] })), components.removed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, children: allowDeletions ? ' ✗' : ' ⊘' }), _jsxs(Text, { children: [components.removed.length, " will be ", allowDeletions ? 'deleted' : 'skipped'] })] }), components.removed.map((item, i) => (_jsxs(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, dimColor: !allowDeletions, children: [' ', allowDeletions ? '✗' : '⊘', " ", item.name] }, `rm-${i}`)))] })), components.unchanged.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " \u00B7" }), _jsxs(Text, { dimColor: true, children: [components.unchanged.length, " unchanged"] })] }))] })), hasTokens && (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Box, { gap: 1, marginTop: 1, children: _jsx(Text, { bold: true, dimColor: true, children: "Design Tokens" }) }), tokens.new.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: " \uFF0B" }), _jsxs(Text, { children: [tokens.new.length, " will be created"] })] })), tokens.changed.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.warning, children: " \uFF5E" }), _jsxs(Text, { children: [tokens.changed.length, " will be updated"] })] })), tokens.removed.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, children: allowDeletions ? ' ✗' : ' ⊘' }), _jsxs(Text, { children: [tokens.removed.length, " will be ", allowDeletions ? 'deleted' : 'skipped'] })] })), tokens.unchanged.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " \u00B7" }), _jsxs(Text, { dimColor: true, children: [tokens.unchanged.length, " unchanged"] })] }))] })), diffExpanded && allDiffLines.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: '─'.repeat(40) }), _jsxs(Text, { dimColor: true, children: [' ', "Diff (", allDiffLines.length, " lines) \u2014 line ", scrollOffset + 1, "\u2013", Math.min(scrollOffset + viewportHeight, allDiffLines.length), " of ", allDiffLines.length] }), _jsx(Box, { flexDirection: "column", children: allDiffLines.slice(scrollOffset, scrollOffset + viewportHeight).map((line) => (_jsx(Box, { children: line.element }, line.key))) }), maxScroll > 0 && _jsx(Text, { dimColor: true, children: " \u2195 j/k to scroll, f/b to page" })] }))] })) : (_jsx(Text, { dimColor: true, children: "Nothing to push \u2014 everything is already up to date." })), _jsxs(Box, { gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Space:" }), _jsx(Text, { children: spaceId }), _jsx(Text, { dimColor: true, children: "/" }), _jsx(Text, { dimColor: true, children: "Environment:" }), _jsx(Text, { children: environmentId })] }), breakingWithImpact && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: PALETTE.error, bold: true, children: "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply." }) })), allowDeletions && removedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: PALETTE.error, bold: true, children: ["\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] }) })), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[Enter] Push to Contentful" }), _jsxs(Text, { dimColor: true, children: ["[d] ", diffExpanded ? 'Hide' : 'Show', " diff"] }), diffExpanded && _jsx(Text, { dimColor: true, children: "[j/k] Scroll [f/b] Page" }), onEdit && _jsx(Text, { dimColor: true, children: "[e] Edit definitions" }), onSaveFiles && _jsx(Text, { dimColor: true, children: "[s] Save files instead" }), removedCount > 0 && (_jsxs(Text, { dimColor: true, children: ["[x] ", allowDeletions ? '[✓]' : '[ ]', " Also delete ", removedCount, " ", removedCount === 1 ? 'entity' : 'entities'] })), _jsx(Text, { dimColor: true, children: "[q] Cancel" })] })] }));
|
|
169
|
+
})] })), components.removed.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, children: allowDeletions ? ' ✗' : ' ⊘' }), _jsxs(Text, { children: [components.removed.length, " will be ", allowDeletions ? 'deleted' : 'skipped'] })] }), components.removed.map((item, i) => (_jsxs(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, dimColor: !allowDeletions, children: [' ', allowDeletions ? '✗' : '⊘', " ", item.name] }, `rm-${i}`)))] })), components.unchanged.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " \u00B7" }), _jsxs(Text, { dimColor: true, children: [components.unchanged.length, " unchanged"] })] }))] })), hasTokens && (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Box, { gap: 1, marginTop: 1, children: _jsx(Text, { bold: true, dimColor: true, children: "Design Tokens" }) }), tokens.new.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: " \uFF0B" }), _jsxs(Text, { children: [tokens.new.length, " will be created"] })] })), tokens.changed.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.warning, children: " \uFF5E" }), _jsxs(Text, { children: [tokens.changed.length, " will be updated"] })] })), tokens.removed.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: allowDeletions ? PALETTE.error : PALETTE.warning, children: allowDeletions ? ' ✗' : ' ⊘' }), _jsxs(Text, { children: [tokens.removed.length, " will be ", allowDeletions ? 'deleted' : 'skipped'] })] })), tokens.unchanged.length > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " \u00B7" }), _jsxs(Text, { dimColor: true, children: [tokens.unchanged.length, " unchanged"] })] }))] })), !fetchedAllowDeletions && suppressedCount > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.warning, children: " \u2298" }), _jsxs(Text, { children: [suppressedCount, " ", suppressedCount === 1 ? 'entity' : 'entities', " skipped (rerun with --allow-deletions to remove ", suppressedCount === 1 ? 'it' : 'them', ")"] })] })), diffExpanded && allDiffLines.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: '─'.repeat(40) }), _jsxs(Text, { dimColor: true, children: [' ', "Diff (", allDiffLines.length, " lines) \u2014 line ", scrollOffset + 1, "\u2013", Math.min(scrollOffset + viewportHeight, allDiffLines.length), " of ", allDiffLines.length] }), _jsx(Box, { flexDirection: "column", children: allDiffLines.slice(scrollOffset, scrollOffset + viewportHeight).map((line) => (_jsx(Box, { children: line.element }, line.key))) }), maxScroll > 0 && _jsx(Text, { dimColor: true, children: " \u2195 j/k to scroll, f/b to page" })] }))] })) : (_jsx(Text, { dimColor: true, children: "Nothing to push \u2014 everything is already up to date." })), _jsxs(Box, { gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Space:" }), _jsx(Text, { children: spaceId }), _jsx(Text, { dimColor: true, children: "/" }), _jsx(Text, { dimColor: true, children: "Environment:" }), _jsx(Text, { children: environmentId })] }), breakingWithImpact && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: PALETTE.error, bold: true, children: "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply." }) })), allowDeletions && removedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: PALETTE.error, bold: true, children: ["\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] }) })), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[Enter] Push to Contentful" }), _jsxs(Text, { dimColor: true, children: ["[d] ", diffExpanded ? 'Hide' : 'Show', " diff"] }), diffExpanded && _jsx(Text, { dimColor: true, children: "[j/k] Scroll [f/b] Page" }), onEdit && _jsx(Text, { dimColor: true, children: "[e] Edit definitions" }), onSaveFiles && _jsx(Text, { dimColor: true, children: "[s] Save files instead" }), fetchedAllowDeletions && removedCount > 0 && (_jsxs(Text, { dimColor: true, children: ["[x] ", allowDeletions ? '[✓]' : '[ ]', " Also delete ", removedCount, " ", removedCount === 1 ? 'entity' : 'entities'] })), _jsx(Text, { dimColor: true, children: "[q] Cancel" })] })] }));
|
|
166
170
|
}
|
|
@@ -11,6 +11,8 @@ export type UseFinalizePreviewOptions = {
|
|
|
11
11
|
host: string;
|
|
12
12
|
/** Component keys the operator has accepted; drives the scoped preview. */
|
|
13
13
|
acceptedKeys: ReadonlySet<string>;
|
|
14
|
+
/** Forwarded verbatim to `runLivePreview`/`previewImport`. */
|
|
15
|
+
allowDeletions?: boolean;
|
|
14
16
|
};
|
|
15
17
|
export type UseFinalizePreviewReturn = {
|
|
16
18
|
status: FinalizePreviewStatus;
|
|
Binary file
|
|
@@ -12,6 +12,8 @@ export type UseLivePreviewOptions = {
|
|
|
12
12
|
/** Preview an empty-but-present manifest (delete-all diff) when nothing is
|
|
13
13
|
* accepted, so the final-review UI can show what a push would delete. */
|
|
14
14
|
deleteAllComponents?: boolean;
|
|
15
|
+
/** Forwarded verbatim to `runLivePreview`/`previewImport`. */
|
|
16
|
+
allowDeletions?: boolean;
|
|
15
17
|
};
|
|
16
18
|
export type LivePreviewStatus = 'idle' | 'running';
|
|
17
19
|
export type UseLivePreviewReturn = {
|
|
@@ -82,6 +82,7 @@ export function useLivePreview(opts) {
|
|
|
82
82
|
host: current.host,
|
|
83
83
|
generation,
|
|
84
84
|
deleteAllComponents: current.deleteAllComponents === true,
|
|
85
|
+
allowDeletions: current.allowDeletions === true,
|
|
85
86
|
});
|
|
86
87
|
// Discard stale responses (generation tag).
|
|
87
88
|
if (result.generation !== latestRef.current)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.18.1-dev-build-
|
|
3
|
+
"version": "2.18.1-dev-build-55a7404.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"react": "^18.3.1",
|
|
35
35
|
"react-devtools-core": "^4.19.1",
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
|
-
"@contentful/experience-design-system-
|
|
38
|
-
"@contentful/experience-design-system-
|
|
39
|
-
"@contentful/experience-design-system-
|
|
37
|
+
"@contentful/experience-design-system-types": "2.18.1-dev-build-55a7404.0",
|
|
38
|
+
"@contentful/experience-design-system-extraction": "2.18.1-dev-build-55a7404.0",
|
|
39
|
+
"@contentful/experience-design-system-client": "2.18.1-dev-build-55a7404.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@tsconfig/node24": "^24.0.3",
|