@contentful/experience-design-system-cli 2.37.1-dev-build-9da0d812.0 → 2.37.1-dev-build-8757f66c.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/dist/app.js CHANGED
@@ -1835,6 +1835,9 @@ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
1835
1835
  // packages/experience-design-system-backend-pipeline/dist/src/persistence/src/session/repositories/migration.js
1836
1836
  import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
1837
1837
 
1838
+ // packages/experience-design-system-backend-pipeline/dist/src/persistence/src/credentials/helpers/host-utils.js
1839
+ var DEFAULT_CONFIGURED_HOST = "api.contentful.com";
1840
+
1838
1841
  // packages/experience-design-system-backend-pipeline/dist/src/persistence/src/index.js
1839
1842
  init_cache_keys();
1840
1843
 
@@ -2172,9 +2175,9 @@ var mergeConfigs = (a, b) => {
2172
2175
  config.headers = mergeHeaders(a.headers, b.headers);
2173
2176
  return config;
2174
2177
  };
2175
- var mergeHeaders = (...headers2) => {
2178
+ var mergeHeaders = (...headers) => {
2176
2179
  const mergedHeaders = new Headers();
2177
- for (const header of headers2) {
2180
+ for (const header of headers) {
2178
2181
  if (!header || typeof header !== "object") {
2179
2182
  continue;
2180
2183
  }
@@ -2393,9 +2396,587 @@ var createClient = (config = {}) => {
2393
2396
  // packages/experience-design-system-client/dist/src/generated/client.gen.js
2394
2397
  var client = createClient(createConfig());
2395
2398
 
2399
+ // packages/experience-design-system-client/dist/src/generated/sdk.gen.js
2400
+ var designSystemImportSourcelessPreview = (options) => {
2401
+ return (options.client ?? client).post({
2402
+ url: "/spaces/{spaceId}/environments/{environmentId}/design_systems/imports/preview",
2403
+ ...options,
2404
+ headers: {
2405
+ "Content-Type": "application/json",
2406
+ ...options.headers
2407
+ }
2408
+ });
2409
+ };
2410
+ var designSystemImportApply = (options) => {
2411
+ return (options.client ?? client).post({
2412
+ url: "/spaces/{spaceId}/environments/{environmentId}/design_systems/imports/apply",
2413
+ ...options,
2414
+ headers: {
2415
+ "Content-Type": "application/json",
2416
+ ...options.headers
2417
+ }
2418
+ });
2419
+ };
2420
+ var designSystemImportGetOperation = (options) => {
2421
+ return (options.client ?? client).get({
2422
+ url: "/spaces/{spaceId}/environments/{environmentId}/design_systems/imports/apply/{operationId}",
2423
+ ...options
2424
+ });
2425
+ };
2426
+
2427
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/constants/api.js
2428
+ var PREVIEW_ERROR_PREFIX = "preview failed:";
2429
+ var APPLY_ERROR_PREFIX = "apply failed:";
2430
+ var USER_AGENT = "experience-design-system-backend-pipeline";
2431
+
2432
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/http-utils.js
2433
+ var MAX_RETRY_AFTER_MS = 6e4;
2434
+ function isTransientStatus(status) {
2435
+ return status >= 500 && status <= 599;
2436
+ }
2437
+ function retryAfterMs(response) {
2438
+ const value = response.headers?.get("retry-after")?.trim();
2439
+ if (!value)
2440
+ return void 0;
2441
+ const seconds = Number(value);
2442
+ const delayMs = Number.isFinite(seconds) ? seconds * 1e3 : Date.parse(value) - Date.now();
2443
+ if (!Number.isFinite(delayMs) || delayMs < 0 || delayMs > MAX_RETRY_AFTER_MS)
2444
+ return void 0;
2445
+ return Math.round(delayMs);
2446
+ }
2447
+ function isApsDenialBody(body) {
2448
+ if (!body)
2449
+ return false;
2450
+ try {
2451
+ const parsed = JSON.parse(body);
2452
+ const id = parsed?.sys?.id;
2453
+ return id === "NotFound" || id === "AccessDenied";
2454
+ } catch {
2455
+ return false;
2456
+ }
2457
+ }
2458
+ function stringifyError(error) {
2459
+ return typeof error === "string" ? error : JSON.stringify(error ?? {});
2460
+ }
2461
+ function errorMessage(error) {
2462
+ return error instanceof Error && error.message ? error.message : String(error);
2463
+ }
2464
+ function defaultSleep(delayMs) {
2465
+ return new Promise((resolve2) => setTimeout(resolve2, delayMs));
2466
+ }
2467
+
2468
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/sanitize-preview.js
2469
+ var PROPERTY_BREAKING_REASONS = /* @__PURE__ */ new Set([
2470
+ "removed",
2471
+ "added_required_no_default",
2472
+ "type_changed",
2473
+ "validation_narrowed"
2474
+ ]);
2475
+ var SLOT_BREAKING_REASONS = /* @__PURE__ */ new Set(["slot_removed", "slot_allowed_components_narrowed"]);
2476
+ function sanitizeBreakingChanges(raw) {
2477
+ if (!Array.isArray(raw))
2478
+ return [];
2479
+ const out = [];
2480
+ for (const bc of raw) {
2481
+ if (typeof bc !== "object" || bc === null)
2482
+ continue;
2483
+ const reason = bc.reason;
2484
+ if (typeof reason !== "string")
2485
+ continue;
2486
+ if ("propertyId" in bc && typeof bc.propertyId === "string") {
2487
+ if (PROPERTY_BREAKING_REASONS.has(reason))
2488
+ out.push(bc);
2489
+ continue;
2490
+ }
2491
+ if ("slotId" in bc && typeof bc.slotId === "string") {
2492
+ if (SLOT_BREAKING_REASONS.has(reason))
2493
+ out.push(bc);
2494
+ continue;
2495
+ }
2496
+ }
2497
+ return out;
2498
+ }
2499
+ function sanitizePreviewResponse(res) {
2500
+ for (const item of res.components?.changed ?? []) {
2501
+ const cc = item.changeClassification;
2502
+ if (cc && Array.isArray(cc.breakingChanges)) {
2503
+ cc.breakingChanges = sanitizeBreakingChanges(cc.breakingChanges);
2504
+ }
2505
+ }
2506
+ return res;
2507
+ }
2508
+
2509
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/host-utils.js
2510
+ var DEFAULT_API_HOST = "https://api.contentful.com";
2511
+ function trimTrailingSlashes(value) {
2512
+ return value.replace(/\/+$/, "");
2513
+ }
2514
+ function normalizeHostInput(host) {
2515
+ const value = host?.trim();
2516
+ if (!value)
2517
+ return void 0;
2518
+ return trimTrailingSlashes(value);
2519
+ }
2520
+ function toConfiguredHost2(host) {
2521
+ const normalized = normalizeHostInput(host);
2522
+ if (!normalized)
2523
+ return void 0;
2524
+ return normalized.replace(/^https:\/\//i, "");
2525
+ }
2526
+ function toApiHost2(host) {
2527
+ const normalized = normalizeHostInput(host);
2528
+ if (!normalized)
2529
+ return DEFAULT_API_HOST;
2530
+ if (/^[a-z][a-z\d+\-.]*:\/\//i.test(normalized))
2531
+ return normalized;
2532
+ return `https://${normalized}`;
2533
+ }
2534
+
2535
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/types/api-error.js
2536
+ var ERROR_BODY_LOG_CAP = 16384;
2537
+ var ApiError = class extends Error {
2538
+ status;
2539
+ body;
2540
+ guidance;
2541
+ constructor(message, status, body, guidance) {
2542
+ super(message);
2543
+ this.status = status;
2544
+ this.body = body;
2545
+ this.guidance = guidance;
2546
+ if (body) {
2547
+ const trimmed = body.length > ERROR_BODY_LOG_CAP ? body.slice(0, ERROR_BODY_LOG_CAP) + "\u2026" : body;
2548
+ this.message = `${message}
2549
+ ${trimmed}`;
2550
+ }
2551
+ }
2552
+ };
2553
+
2554
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/api-client/api-client.js
2555
+ var ApiClient = class {
2556
+ host;
2557
+ token;
2558
+ spaceId;
2559
+ environmentId;
2560
+ retry;
2561
+ lastRequestId;
2562
+ constructor(opts) {
2563
+ this.host = toApiHost2(opts.host);
2564
+ this.token = opts.cmaToken;
2565
+ this.spaceId = opts.spaceId;
2566
+ this.environmentId = opts.environmentId;
2567
+ const initialDelayMs = Math.max(0, opts.retry?.initialDelayMs ?? 250);
2568
+ this.retry = {
2569
+ maxAttempts: Math.max(1, Math.floor(opts.retry?.maxAttempts ?? 3)),
2570
+ initialDelayMs,
2571
+ maxDelayMs: Math.max(initialDelayMs, opts.retry?.maxDelayMs ?? 2e3),
2572
+ sleep: opts.retry?.sleep ?? defaultSleep
2573
+ };
2574
+ }
2575
+ getLastRequestId() {
2576
+ return this.lastRequestId;
2577
+ }
2578
+ noteRequestId(response) {
2579
+ const id = response.headers.get("x-contentful-request-id");
2580
+ if (id)
2581
+ this.lastRequestId = id;
2582
+ }
2583
+ headers() {
2584
+ return {
2585
+ Authorization: `Bearer ${this.token}`,
2586
+ "Content-Type": "application/json",
2587
+ "X-Contentful-User-Agent": USER_AGENT
2588
+ };
2589
+ }
2590
+ async requestWithRetry(phase, errorPrefix, makeCall) {
2591
+ for (let attempt = 1; attempt <= this.retry.maxAttempts; attempt++) {
2592
+ let result;
2593
+ try {
2594
+ result = await makeCall();
2595
+ } catch (error) {
2596
+ if (attempt === this.retry.maxAttempts) {
2597
+ const body = `The request failed after ${attempt} attempts because of a network error: ${errorMessage(error)}. Check your connection and try again.`;
2598
+ throw new ApiError(`${errorPrefix} 0`, 0, body);
2599
+ }
2600
+ await this.retry.sleep(Math.min(this.retry.initialDelayMs * 2 ** (attempt - 1), this.retry.maxDelayMs));
2601
+ continue;
2602
+ }
2603
+ if (!isTransientStatus(result.response.status)) {
2604
+ this.noteRequestId(result.response);
2605
+ return result;
2606
+ }
2607
+ if (attempt === this.retry.maxAttempts) {
2608
+ const body = stringifyError(result.error);
2609
+ const guidance = `The ${phase} request failed after ${attempt} attempts because the service remained unavailable. Wait a moment and try again.`;
2610
+ throw new ApiError(`${errorPrefix} ${result.response.status}`, result.response.status, body, guidance);
2611
+ }
2612
+ const backoffMs = Math.min(this.retry.initialDelayMs * 2 ** (attempt - 1), this.retry.maxDelayMs);
2613
+ await this.retry.sleep(retryAfterMs(result.response) ?? backoffMs);
2614
+ }
2615
+ throw new Error("Retry attempts exhausted");
2616
+ }
2617
+ async validateToken() {
2618
+ const res = await fetch(`${this.host}/users/me`, { headers: this.headers() });
2619
+ this.noteRequestId(res);
2620
+ if (res.status === 401) {
2621
+ throw new ApiError("CMA token is invalid or revoked", res.status, await res.text());
2622
+ }
2623
+ if (!res.ok) {
2624
+ throw new ApiError(`unexpected error validating token: ${res.status}`, res.status, await res.text());
2625
+ }
2626
+ await this.checkPreflight();
2627
+ }
2628
+ async checkPreflight() {
2629
+ const url = `${this.host}/spaces/${this.spaceId}/environments/${this.environmentId}/design_systems/preflight`;
2630
+ let res;
2631
+ try {
2632
+ res = await fetch(url, { headers: this.headers() });
2633
+ } catch {
2634
+ return;
2635
+ }
2636
+ this.noteRequestId(res);
2637
+ if (res.ok)
2638
+ return;
2639
+ if (res.status >= 500)
2640
+ return;
2641
+ const body = await res.text();
2642
+ if (res.status === 404 && !isApsDenialBody(body))
2643
+ return;
2644
+ throw new ApiError(`preflight failed: ${res.status}`, res.status, body);
2645
+ }
2646
+ async previewImport(cdf) {
2647
+ const result = await this.requestWithRetry("preview", PREVIEW_ERROR_PREFIX, () => designSystemImportSourcelessPreview({
2648
+ baseUrl: this.host,
2649
+ headers: this.headers(),
2650
+ path: { spaceId: this.spaceId, environmentId: this.environmentId },
2651
+ body: cdf,
2652
+ parseAs: "json"
2653
+ }));
2654
+ if (!result.response.ok) {
2655
+ throw new ApiError(`${PREVIEW_ERROR_PREFIX} ${result.response.status}`, result.response.status, stringifyError(result.error));
2656
+ }
2657
+ return sanitizePreviewResponse(result.data);
2658
+ }
2659
+ async applyImport(cdf, opts) {
2660
+ let result;
2661
+ try {
2662
+ result = await designSystemImportApply({
2663
+ baseUrl: this.host,
2664
+ headers: this.headers(),
2665
+ path: { spaceId: this.spaceId, environmentId: this.environmentId },
2666
+ body: { ...cdf, acknowledgeBreakingChanges: opts.acknowledgeBreakingChanges },
2667
+ parseAs: "json"
2668
+ });
2669
+ } catch (error) {
2670
+ const body = `The apply request was not retried because its outcome is unknown and retrying could start a duplicate operation. Cause: ${errorMessage(error)}`;
2671
+ throw new ApiError(`${APPLY_ERROR_PREFIX} 0`, 0, body);
2672
+ }
2673
+ if (!result.response.ok) {
2674
+ const body = stringifyError(result.error);
2675
+ const guidance = isTransientStatus(result.response.status) ? "The apply request was not retried because the server may already have started an operation and retrying could create a duplicate." : void 0;
2676
+ throw new ApiError(`${APPLY_ERROR_PREFIX} ${result.response.status}`, result.response.status, body, guidance);
2677
+ }
2678
+ this.noteRequestId(result.response);
2679
+ return result.data;
2680
+ }
2681
+ async pollOperation(operationId, opts = {}) {
2682
+ const intervalMs = opts.intervalMs ?? 2e3;
2683
+ const maxIntervalMs = opts.maxIntervalMs ?? Math.round(intervalMs * 2.5);
2684
+ const maxAttempts = opts.maxAttempts ?? 150;
2685
+ const terminalStatuses = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
2686
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
2687
+ const result = await this.requestWithRetry("poll", "poll failed:", () => designSystemImportGetOperation({
2688
+ baseUrl: this.host,
2689
+ headers: this.headers(),
2690
+ path: { spaceId: this.spaceId, environmentId: this.environmentId, operationId },
2691
+ parseAs: "json"
2692
+ }));
2693
+ if (!result.response.ok) {
2694
+ throw new ApiError(`poll failed: ${result.response.status}`, result.response.status, stringifyError(result.error));
2695
+ }
2696
+ const op = result.data;
2697
+ opts.onProgress?.(op);
2698
+ if (terminalStatuses.has(op.sys.status))
2699
+ return op;
2700
+ if (attempt < maxAttempts - 1) {
2701
+ const progress = maxAttempts > 1 ? attempt / (maxAttempts - 1) : 0;
2702
+ const baseDelay = intervalMs + (maxIntervalMs - intervalMs) * progress;
2703
+ const jitter = Math.random() * baseDelay * 0.15;
2704
+ await new Promise((resolve2) => setTimeout(resolve2, baseDelay + jitter));
2705
+ }
2706
+ }
2707
+ throw new Error(`Operation ${operationId} timed out after ${maxAttempts} attempts`);
2708
+ }
2709
+ };
2710
+
2396
2711
  // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/controller/validation/assert-no-unresolved-slot-references.js
2397
2712
  init_shared();
2398
2713
 
2714
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/parse-binding-diagnostics.js
2715
+ function parseBindingDiagnostics(body) {
2716
+ if (!/binding configurations are invalid|Pointer path does not exist/i.test(body))
2717
+ return null;
2718
+ const locationMatches = [...body.matchAll(/(?:^|\.\s)([A-Za-z0-9_$-]+(?:\s*›\s*[A-Za-z0-9_$-]+){2,})/g)];
2719
+ const location = locationMatches.at(-1)?.[1];
2720
+ const pointerMessage = body.match(/Pointer path does not exist for '\[object Object\]':\s*(.+?)(?=\.\s*Default\s*›|$)/i)?.[1];
2721
+ const graphQlMessage = body.match(/return GraphQL validation error:\s*(.+?)(?=\.\s*Default\s*›|$)/i)?.[1];
2722
+ const heading = body.match(/^(.*?)(?=\.?\s*Pointer path does not exist|\.?\s*Default\s*›)/i)?.[1]?.trim();
2723
+ const diagnostics = [];
2724
+ if (pointerMessage)
2725
+ diagnostics.push({ message: pointerMessage.trim(), ...location ? { path: location } : {} });
2726
+ if (graphQlMessage)
2727
+ diagnostics.push({ message: graphQlMessage.trim(), ...location ? { path: location } : {} });
2728
+ return {
2729
+ code: "BindingValidationFailed",
2730
+ message: heading || "One or more binding configurations are invalid.",
2731
+ ...diagnostics.length > 0 ? { diagnostics } : {}
2732
+ };
2733
+ }
2734
+
2735
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/helpers.js
2736
+ function asRecord(value) {
2737
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
2738
+ }
2739
+ function displayValue(value) {
2740
+ if (typeof value === "string")
2741
+ return value;
2742
+ if (typeof value === "number" || typeof value === "boolean")
2743
+ return String(value);
2744
+ if (value === null || value === void 0)
2745
+ return null;
2746
+ try {
2747
+ return JSON.stringify(value);
2748
+ } catch {
2749
+ return null;
2750
+ }
2751
+ }
2752
+ function isPathSegment(value) {
2753
+ return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
2754
+ }
2755
+ function formatPath(value) {
2756
+ if (isPathSegment(value))
2757
+ return String(value) ? { path: String(value) } : { path: null };
2758
+ if (Array.isArray(value)) {
2759
+ const parts = value.filter(isPathSegment);
2760
+ return parts.length > 0 ? { path: parts.join(" \u203A ") } : { path: null };
2761
+ }
2762
+ return {};
2763
+ }
2764
+ function componentFromPath(path5) {
2765
+ if (!path5)
2766
+ return void 0;
2767
+ const manifestMatch = path5.match(/manifest:components\/([^/›]+)/);
2768
+ if (manifestMatch?.[1])
2769
+ return manifestMatch[1];
2770
+ const parts = path5.split(/[›/]/).map((part) => part.trim()).filter(Boolean);
2771
+ const componentsIndex = parts.findIndex((part) => part === "components");
2772
+ return componentsIndex === -1 ? void 0 : parts[componentsIndex + 1];
2773
+ }
2774
+ function diagnosticMessage(error) {
2775
+ const candidates = [
2776
+ ["message", error.message],
2777
+ ["details", error.details],
2778
+ ["error", error.error],
2779
+ ["name", error.name]
2780
+ ];
2781
+ for (const [messageSource, value] of candidates) {
2782
+ const message = displayValue(value);
2783
+ if (message !== null)
2784
+ return { message, messageSource };
2785
+ }
2786
+ return { message: "Validation failed" };
2787
+ }
2788
+ function parseObjectLiteralTail(body) {
2789
+ const braceStart = body.lastIndexOf("{");
2790
+ if (braceStart === -1)
2791
+ return null;
2792
+ const tail = body.slice(braceStart);
2793
+ const codeMatch = tail.match(/code:\s*['"]([^'"]+)['"]/);
2794
+ const cycleMatch = tail.match(/cycle:\s*\[\s*([^\]]*)\s*\]/);
2795
+ if (!codeMatch && !cycleMatch)
2796
+ return null;
2797
+ const code = codeMatch ? codeMatch[1] : null;
2798
+ let cycle = null;
2799
+ if (cycleMatch) {
2800
+ cycle = cycleMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter((s) => s.length > 0);
2801
+ if (cycle.length === 0)
2802
+ cycle = null;
2803
+ }
2804
+ return { code, cycle };
2805
+ }
2806
+
2807
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/parse-json-body.js
2808
+ function parseValidationDiagnostics(details) {
2809
+ if (!Array.isArray(details.errors))
2810
+ return [];
2811
+ const diagnostics = [];
2812
+ for (const rawError of details.errors) {
2813
+ const error = asRecord(rawError);
2814
+ if (!error)
2815
+ continue;
2816
+ const location = formatPath(error.path);
2817
+ const component = displayValue(error.component ?? error.componentName ?? error.componentId) ?? componentFromPath(location.path);
2818
+ diagnostics.push({ ...diagnosticMessage(error), ...location, ...component ? { component } : {} });
2819
+ }
2820
+ return diagnostics;
2821
+ }
2822
+ function parseJsonBody(body) {
2823
+ let parsed;
2824
+ try {
2825
+ parsed = JSON.parse(body);
2826
+ } catch {
2827
+ return null;
2828
+ }
2829
+ const p = asRecord(parsed);
2830
+ if (!p)
2831
+ return null;
2832
+ const details = asRecord(p.details) ?? {};
2833
+ const pick = (k) => p[k] !== void 0 ? p[k] : details[k];
2834
+ const codeRaw = pick("code") ?? asRecord(p.sys)?.id;
2835
+ const messageRaw = pick("message");
2836
+ const cycleRaw = pick("cycle");
2837
+ const out = {};
2838
+ if (typeof codeRaw === "string")
2839
+ out.code = codeRaw;
2840
+ if (typeof messageRaw === "string")
2841
+ out.message = messageRaw;
2842
+ if (Array.isArray(cycleRaw)) {
2843
+ const strs = cycleRaw.filter((x) => typeof x === "string");
2844
+ if (strs.length > 0)
2845
+ out.cycle = strs;
2846
+ }
2847
+ const diagnostics = parseValidationDiagnostics(details);
2848
+ if (diagnostics.length > 0)
2849
+ out.diagnostics = diagnostics;
2850
+ return out;
2851
+ }
2852
+
2853
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/strip-lambda-log-prefix.js
2854
+ var LAMBDA_LOG_PREFIX_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\s+[0-9a-f-]+\s+ERROR\s+(?:\[dd\.[^\]]*\]\s*)?/;
2855
+ var DD_TAG_RE = /\[dd\.(?:trace_id|span_id)=[^\]]*\]\s*/g;
2856
+ function stripLambdaLogPrefix(body) {
2857
+ return body.replace(LAMBDA_LOG_PREFIX_RE, "").replace(DD_TAG_RE, "").trim();
2858
+ }
2859
+
2860
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/parse-edsi-error.js
2861
+ var API_ERROR_PREFIX_RE = /^(?:apply|preview|poll) failed: \d+$/;
2862
+ function parseEdsiError(rawInput) {
2863
+ if (!rawInput)
2864
+ return { code: null, message: "", cycle: null, raw: true };
2865
+ const { body, prefix } = splitPhasePrefix(rawInput);
2866
+ const cleaned = stripLambdaLogPrefix(body);
2867
+ const json = parseJsonBody(cleaned) ?? parseJsonBody(body);
2868
+ const bindingFromJson = typeof json?.message === "string" ? parseBindingDiagnostics(json.message) : null;
2869
+ if (bindingFromJson) {
2870
+ return {
2871
+ code: bindingFromJson.code ?? json?.code ?? null,
2872
+ message: bindingFromJson.message ?? cleaned,
2873
+ cycle: null,
2874
+ raw: false,
2875
+ ...bindingFromJson.diagnostics ? { diagnostics: bindingFromJson.diagnostics } : {}
2876
+ };
2877
+ }
2878
+ if (json && (json.code || json.message || json.cycle)) {
2879
+ return {
2880
+ code: json.code ?? null,
2881
+ message: json.message ?? (cleaned || prefix),
2882
+ cycle: json.cycle ?? null,
2883
+ raw: false,
2884
+ ...json.diagnostics ? { diagnostics: json.diagnostics } : {}
2885
+ };
2886
+ }
2887
+ const binding = parseBindingDiagnostics(cleaned);
2888
+ if (binding) {
2889
+ return {
2890
+ code: binding.code ?? null,
2891
+ message: binding.message ?? cleaned,
2892
+ cycle: null,
2893
+ raw: false,
2894
+ ...binding.diagnostics ? { diagnostics: binding.diagnostics } : {}
2895
+ };
2896
+ }
2897
+ const literal2 = parseObjectLiteralTail(cleaned);
2898
+ if (literal2 && (literal2.code || literal2.cycle)) {
2899
+ const braceStart = cleaned.lastIndexOf("{");
2900
+ const head = braceStart === -1 ? cleaned : cleaned.slice(0, braceStart).trim();
2901
+ return { code: literal2.code ?? null, message: head || cleaned, cycle: literal2.cycle ?? null, raw: false };
2902
+ }
2903
+ return { code: null, message: cleaned || prefix || rawInput, cycle: null, raw: true };
2904
+ }
2905
+ function splitPhasePrefix(input) {
2906
+ const nlIndex = input.indexOf("\n");
2907
+ if (nlIndex === -1)
2908
+ return { body: input, prefix: "" };
2909
+ const firstLine = input.slice(0, nlIndex);
2910
+ if (!API_ERROR_PREFIX_RE.test(firstLine))
2911
+ return { body: input, prefix: "" };
2912
+ return { body: input.slice(nlIndex + 1), prefix: firstLine };
2913
+ }
2914
+
2915
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/format-parsed-edsi-error.js
2916
+ function formatParsedEdsiError(parsed, opts = {}) {
2917
+ const lines = [];
2918
+ if (parsed.code)
2919
+ lines.push(`[${parsed.code}]`);
2920
+ if (parsed.message)
2921
+ lines.push(parsed.message);
2922
+ for (const diagnostic of parsed.diagnostics ?? []) {
2923
+ const context = [
2924
+ diagnostic.component ? `Component: ${diagnostic.component}` : null,
2925
+ diagnostic.path ? `Path: ${diagnostic.path}` : null
2926
+ ].filter(Boolean);
2927
+ lines.push(`- ${diagnostic.message}${context.length > 0 ? ` (${context.join("; ")})` : ""}`);
2928
+ if (diagnostic.path === null) {
2929
+ lines.push(" Location: not provided by the server. Review the submitted CDF file; no component or field was identified.");
2930
+ }
2931
+ if (opts.verbose && diagnostic.messageSource) {
2932
+ lines.push(` Message field: ${diagnostic.messageSource}`);
2933
+ }
2934
+ }
2935
+ if (parsed.code === "BindingValidationFailed") {
2936
+ const location = parsed.diagnostics?.find((d) => d.path)?.path;
2937
+ lines.push(location ? `Next action: review the binding at ${location} and correct its pointer or GraphQL selection.` : "Next action: review the binding pointer and GraphQL selection.");
2938
+ }
2939
+ if (parsed.code === "AccessDenied") {
2940
+ lines.push("Next action: this operation requires DesignToken and Component permissions in this environment. Ask a Contentful admin to grant both on your role.");
2941
+ }
2942
+ if (parsed.code === "NotFound") {
2943
+ lines.push("Next action: verify the resource id is correct. If it should exist, your role may lack read permission on DesignToken or Component.");
2944
+ }
2945
+ if (parsed.cycle && parsed.cycle.length > 0) {
2946
+ lines.push(`Cycle: ${parsed.cycle.join(" \u2192 ")} \u2192 ${parsed.cycle[0]}`);
2947
+ lines.push("Break the cycle by removing at least one $allowedComponents entry.");
2948
+ }
2949
+ if (opts.verbose && opts.raw) {
2950
+ lines.push("");
2951
+ lines.push("--- raw ---");
2952
+ lines.push(opts.raw);
2953
+ }
2954
+ return lines.filter(Boolean).join("\n");
2955
+ }
2956
+
2957
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/format-edsi-error.js
2958
+ function formatEdsiError(raw, opts = {}) {
2959
+ const input = typeof raw === "string" ? raw : displayValue(raw);
2960
+ if (!input)
2961
+ return "";
2962
+ return formatParsedEdsiError(parseEdsiError(input), { ...opts, raw: opts.raw ?? input });
2963
+ }
2964
+
2965
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/edsi-errors/format-api-error.js
2966
+ function formatApiError(error, verbose = false) {
2967
+ const formatted = formatEdsiError(error.body || error.message, { verbose, raw: error.body }) || error.message;
2968
+ const phase = error.message.split("\n", 1)[0];
2969
+ const withPhase = /^(?:apply|preview|poll) failed: \d+$/.test(phase) && formatted !== phase ? `${phase}
2970
+ ${formatted}` : formatted;
2971
+ return error.guidance ? `${withPhase}
2972
+ ${error.guidance}` : withPhase;
2973
+ }
2974
+
2975
+ // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/controller/errors/format-api-error.js
2976
+ function formatApiError2(request) {
2977
+ return formatApiError(request.error, request.verbose ?? false);
2978
+ }
2979
+
2399
2980
  // packages/experience-design-system-backend-pipeline/dist/src/steps/apply/src/helpers/tokens/read-tokens-from-path.js
2400
2981
  init_shared();
2401
2982
 
@@ -2534,7 +3115,6 @@ import { useInput as useInput6 } from "ink";
2534
3115
 
2535
3116
  // packages/experience-design-system-cli/src/tui/import/steps/04-credentials/logic.ts
2536
3117
  var FIELDS = ["spaceId", "environmentId", "cmaToken", "host"];
2537
- var DEFAULT_HOST = "api.contentful.com";
2538
3118
  function keyAction2(input, key) {
2539
3119
  if (key.escape) return "back";
2540
3120
  if (key.tab) return "next-field";
@@ -2544,21 +3124,12 @@ function keyAction2(input, key) {
2544
3124
  function nextField(field) {
2545
3125
  return FIELDS[(FIELDS.indexOf(field) + 1) % FIELDS.length];
2546
3126
  }
2547
- function toConfiguredHost3(host) {
2548
- const value = (host ?? "").trim().replace(/\/+$/, "");
2549
- return value === "" ? DEFAULT_HOST : value.replace(/^https:\/\//i, "");
2550
- }
2551
- function toApiHost3(host) {
2552
- const value = host.trim().replace(/\/+$/, "");
2553
- if (value === "") return `https://${DEFAULT_HOST}`;
2554
- return /^[a-z][a-z\d+\-.]*:\/\//i.test(value) ? value : `https://${value}`;
2555
- }
2556
3127
  function normalize(values) {
2557
3128
  return {
2558
3129
  spaceId: values.spaceId.trim(),
2559
3130
  environmentId: values.environmentId.trim(),
2560
3131
  cmaToken: values.cmaToken.trim(),
2561
- host: toConfiguredHost3(values.host)
3132
+ host: toConfiguredHost2(values.host) ?? DEFAULT_CONFIGURED_HOST
2562
3133
  };
2563
3134
  }
2564
3135
  function missingField(values) {
@@ -2586,59 +3157,30 @@ function useCredentialsControls(handlers) {
2586
3157
  }
2587
3158
 
2588
3159
  // packages/experience-design-system-cli/src/tui/import/steps/04-credentials/validate.ts
2589
- var USER_AGENT2 = "app contentful.experience-design-system-cli";
2590
- function headers(token) {
2591
- return { Authorization: `Bearer ${token}`, "X-Contentful-User-Agent": USER_AGENT2 };
2592
- }
2593
- function isApsDenial(body) {
2594
- try {
2595
- const id = JSON.parse(body).sys?.id;
2596
- return id === "NotFound" || id === "AccessDenied";
2597
- } catch {
2598
- return false;
2599
- }
2600
- }
2601
- function messageFrom(body) {
2602
- try {
2603
- const message = JSON.parse(body).message;
2604
- return typeof message === "string" && message !== "" ? message : void 0;
2605
- } catch {
2606
- return void 0;
2607
- }
2608
- }
2609
3160
  async function validateCredentials(values) {
2610
- const base = toApiHost3(values.host);
2611
- let me;
2612
- try {
2613
- me = await fetch(`${base}/users/me`, { headers: headers(values.cmaToken) });
2614
- } catch (error) {
2615
- return { ok: false, error: `Could not reach ${base}: ${error instanceof Error ? error.message : String(error)}` };
2616
- }
2617
- if (me.status === 401) return { ok: false, error: "CMA token is invalid or revoked." };
2618
- if (!me.ok) return { ok: false, error: `Unexpected error validating the token (${me.status}).` };
2619
- let preflight;
3161
+ const host = toApiHost2(values.host);
3162
+ const client2 = new ApiClient({
3163
+ host,
3164
+ spaceId: values.spaceId,
3165
+ environmentId: values.environmentId,
3166
+ cmaToken: values.cmaToken
3167
+ });
2620
3168
  try {
2621
- preflight = await fetch(
2622
- `${base}/spaces/${values.spaceId}/environments/${values.environmentId}/design_systems/preflight`,
2623
- { headers: headers(values.cmaToken) }
2624
- );
2625
- } catch {
3169
+ await client2.validateToken();
2626
3170
  return { ok: true };
3171
+ } catch (error) {
3172
+ return { ok: false, error: toUserError(error, host) };
2627
3173
  }
2628
- if (preflight.ok || preflight.status >= 500) return { ok: true };
2629
- const body = await preflight.text();
2630
- if (preflight.status === 404 && !isApsDenial(body)) return { ok: true };
2631
- const detail = messageFrom(body);
2632
- if (preflight.status === 403) {
2633
- return { ok: false, error: `Access denied. ${detail ?? "Your token cannot write design system components here."}` };
2634
- }
2635
- if (preflight.status === 404) {
2636
- return {
2637
- ok: false,
2638
- error: `Not found. Check the space ID, environment and API host.${detail ? ` (${detail})` : ""}`
2639
- };
3174
+ }
3175
+ function toUserError(error, host) {
3176
+ if (error instanceof ApiError) {
3177
+ if (error.status === 404 && error.message.startsWith("preflight failed:")) {
3178
+ return "Not found. Check the space ID, environment and API host.";
3179
+ }
3180
+ return formatApiError2({ error });
2640
3181
  }
2641
- return { ok: false, error: `Credential check failed (${preflight.status}).${detail ? ` ${detail}` : ""}` };
3182
+ if (error instanceof Error) return `Could not reach ${host}: ${error.message}`;
3183
+ return String(error);
2642
3184
  }
2643
3185
 
2644
3186
  // packages/experience-design-system-cli/src/tui/import/steps/04-credentials/screen.tsx
@@ -2715,7 +3257,7 @@ function CredentialsScreen({
2715
3257
  value: values[field],
2716
3258
  focus: active === field && !validating,
2717
3259
  mask: field === "cmaToken" ? "\u2022" : void 0,
2718
- placeholder: field === "host" ? DEFAULT_HOST : void 0,
3260
+ placeholder: field === "host" ? DEFAULT_CONFIGURED_HOST : void 0,
2719
3261
  onChange: change(field),
2720
3262
  onSubmit: () => void submit(field)
2721
3263
  }