@contentful/experience-design-system-cli 2.37.1-dev-build-daf63213.0 → 2.37.1-dev-build-f22bf3b7.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
@@ -32,7 +32,7 @@ var init_package_root = __esm({
32
32
  }
33
33
  });
34
34
 
35
- // packages/experience-design-system-cli/src/tui/upgrade/version.ts
35
+ // packages/experience-design-system-cli/src/tui/home/version.ts
36
36
  import { readFileSync as readFileSync2 } from "node:fs";
37
37
  import { join as join2 } from "node:path";
38
38
  import semver from "semver";
@@ -65,7 +65,7 @@ async function checkForUpgrade() {
65
65
  }
66
66
  var PACKAGE_NAME, TAGS_URL;
67
67
  var init_version = __esm({
68
- "packages/experience-design-system-cli/src/tui/upgrade/version.ts"() {
68
+ "packages/experience-design-system-cli/src/tui/home/version.ts"() {
69
69
  "use strict";
70
70
  init_package_root();
71
71
  PACKAGE_NAME = "@contentful/experience-design-system-cli";
@@ -2330,7 +2330,7 @@ var init_debug_store = __esm({
2330
2330
 
2331
2331
  // packages/experience-design-system-cli/app.tsx
2332
2332
  import { join as join26 } from "node:path";
2333
- import { useEffect as useEffect28, useState as useState37 } from "react";
2333
+ import { useEffect as useEffect27, useState as useState36 } from "react";
2334
2334
 
2335
2335
  // packages/experience-design-system-cli/src/tui/home/home.tsx
2336
2336
  import { useEffect as useEffect2, useState as useState2 } from "react";
@@ -2431,18 +2431,14 @@ var SUBTITLE = "Let's import your design system into Contentful";
2431
2431
  var MIN_TERMINAL_WIDTH = 164;
2432
2432
  var START_ITEMS = [
2433
2433
  { label: "Import", screen: "import" },
2434
- { label: "Upgrade Version", screen: "upgrade" },
2435
2434
  { label: "Settings", screen: "settings" },
2436
2435
  { label: "Help", screen: "help" }
2437
2436
  ];
2438
- function upgradeItemDisplay(upgradeCheck) {
2437
+ function versionStatus(upgradeCheck) {
2439
2438
  if (upgradeCheck?.status === "update-available") {
2440
- return { label: `Upgrade Version (v${upgradeCheck.latest} available)`, color: PALETTE.warning };
2441
- }
2442
- if (upgradeCheck?.status === "up-to-date") {
2443
- return { label: "Upgrade Version (up to date)", color: PALETTE.muted };
2439
+ return { text: ` (upgrade available v${upgradeCheck.latest})`, color: PALETTE.success };
2444
2440
  }
2445
- return { label: "Upgrade Version" };
2441
+ return void 0;
2446
2442
  }
2447
2443
  function HomeScreen({ onNavigate }) {
2448
2444
  const [focusIdx, setFocusIdx] = useState2(0);
@@ -2450,7 +2446,7 @@ function HomeScreen({ onNavigate }) {
2450
2446
  const { exit } = useApp();
2451
2447
  const terminalWidth = useTerminalWidth();
2452
2448
  const tooNarrow = terminalWidth < MIN_TERMINAL_WIDTH;
2453
- const upgradeDisabled = upgradeCheck?.status === "up-to-date";
2449
+ const status = versionStatus(upgradeCheck);
2454
2450
  useEffect2(() => {
2455
2451
  checkForUpgrade().then(setUpgradeCheck).catch(() => setUpgradeCheck({ status: "error" }));
2456
2452
  }, []);
@@ -2470,7 +2466,6 @@ function HomeScreen({ onNavigate }) {
2470
2466
  }
2471
2467
  if (key.return) {
2472
2468
  const item = START_ITEMS[focusIdx];
2473
- if (item.screen === "upgrade" && upgradeDisabled) return;
2474
2469
  onNavigate(item.screen);
2475
2470
  return;
2476
2471
  }
@@ -2488,26 +2483,16 @@ function HomeScreen({ onNavigate }) {
2488
2483
  /* @__PURE__ */ jsx2(Text2, { color: PALETTE.muted, children: SUBTITLE }),
2489
2484
  /* @__PURE__ */ jsxs(Text2, { color: PALETTE.muted, children: [
2490
2485
  "v",
2491
- VERSION
2486
+ VERSION,
2487
+ status && /* @__PURE__ */ jsx2(Text2, { color: status.color, children: status.text })
2492
2488
  ] })
2493
2489
  ] }),
2494
2490
  /* @__PURE__ */ jsx2(Box, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: START_ITEMS.map((item, i) => {
2495
2491
  const focused = i === focusIdx;
2496
- const isUpgradeItem = item.screen === "upgrade";
2497
- const display = isUpgradeItem ? upgradeItemDisplay(upgradeCheck) : { label: item.label };
2498
- const dimmed = isUpgradeItem && upgradeDisabled;
2499
- return /* @__PURE__ */ jsxs(
2500
- Text2,
2501
- {
2502
- bold: focused && !dimmed,
2503
- color: dimmed ? PALETTE.muted : focused ? PALETTE.accent : display.color,
2504
- children: [
2505
- focused ? `${FOCUS_MARKER} ` : " ",
2506
- display.label
2507
- ]
2508
- },
2492
+ return /* @__PURE__ */ jsxs(Text2, { bold: focused, color: focused ? PALETTE.accent : void 0, children: [
2493
+ focused ? `${FOCUS_MARKER} ` : " ",
2509
2494
  item.label
2510
- );
2495
+ ] }, item.label);
2511
2496
  }) }) }),
2512
2497
  /* @__PURE__ */ jsx2(Box, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx2(
2513
2498
  ControlHints,
@@ -9663,8 +9648,8 @@ function extractAccessorProperties(classDecl, applyRuntimeFieldDenylist = false)
9663
9648
  accessors.set(name, entry);
9664
9649
  }
9665
9650
  for (const [name, { getter, setter }] of accessors) {
9666
- const hasPropertyDecorator = getter?.getDecorators().some((decorator) => decorator.getName() === "property") || setter?.getDecorators().some((decorator) => decorator.getName() === "property");
9667
- if (!hasPropertyDecorator && !(getter && setter))
9651
+ const hasPropertyDecorator2 = getter?.getDecorators().some((decorator) => decorator.getName() === "property") || setter?.getDecorators().some((decorator) => decorator.getName() === "property");
9652
+ if (!hasPropertyDecorator2 && !(getter && setter))
9668
9653
  continue;
9669
9654
  const type = getter?.getReturnTypeNode()?.getText() ?? setter?.getParameters()[0]?.getTypeNode()?.getText() ?? "any";
9670
9655
  const accessorNode = getter ?? setter;
@@ -12934,9 +12919,9 @@ function generateUlid(now = Date.now()) {
12934
12919
  tsBytes[i] = n & 255;
12935
12920
  n = Math.floor(n / 256);
12936
12921
  }
12937
- const ts3 = encodeBase32(tsBytes, 10);
12922
+ const ts6 = encodeBase32(tsBytes, 10);
12938
12923
  const rand = encodeBase32(randomBytes2(10), 16);
12939
- return (ts3 + rand).toUpperCase();
12924
+ return (ts6 + rand).toUpperCase();
12940
12925
  }
12941
12926
 
12942
12927
  // packages/experience-design-system-backend-pipeline/dist/src/persistence/src/runs/repositories/read-runs-file.js
@@ -15350,292 +15335,427 @@ async function runMapTokens(request) {
15350
15335
  import { mkdir as mkdir4 } from "node:fs/promises";
15351
15336
  import { relative as relative2 } from "node:path";
15352
15337
 
15353
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/quality/non-authorable-filter.js
15354
- var HANDLER_TYPE_PATTERN = /=>|EventHandler|Dispatch<|SetStateAction/;
15355
- var REF_TYPE_PATTERN = /Ref<|RefObject<|MutableRefObject/;
15356
- function isHandlerOrRefProp(prop) {
15357
- if (HANDLER_TYPE_PATTERN.test(prop.type))
15358
- return true;
15359
- if (REF_TYPE_PATTERN.test(prop.type))
15360
- return true;
15361
- if (/^on[A-Z]/.test(prop.name) || /^set[A-Z]/.test(prop.name))
15362
- return true;
15363
- if (prop.name === "ref" || prop.name === "innerRef")
15364
- return true;
15365
- return false;
15366
- }
15367
- function hasNoPropsAndNoSlots(props, slots) {
15368
- return props.length === 0 && slots.length === 0;
15369
- }
15370
- function isContextProviderWithValueProp(props, usesCreateContext) {
15371
- return !!usesCreateContext && props.some((p) => p.name === "value");
15372
- }
15373
- function isHardwiredContextProvider(props, usesCreateContext) {
15374
- return !!usesCreateContext && props.length === 0;
15375
- }
15376
- function isSingleDataPropContextWrapper(props, usesCreateContext) {
15377
- return !!usesCreateContext && props.length === 1 && !isHandlerOrRefProp(props[0]);
15378
- }
15379
- function hasOnlyEventHandlers(props) {
15380
- return props.length > 0 && props.every(isHandlerOrRefProp);
15381
- }
15382
- function hasNoAuthoringSurface(component) {
15383
- const { props, slots, usesCreateContext } = component;
15384
- if (hasNoPropsAndNoSlots(props, slots))
15385
- return { skip: true, reason: "component has no props and no slots" };
15386
- if (isContextProviderWithValueProp(props, usesCreateContext))
15387
- return {
15388
- skip: true,
15389
- reason: "source uses createContext and component exposes a Context.Provider value prop"
15390
- };
15391
- if (isHardwiredContextProvider(props, usesCreateContext))
15392
- return {
15393
- skip: true,
15394
- reason: "source uses createContext and component has no props"
15395
- };
15396
- if (isSingleDataPropContextWrapper(props, usesCreateContext))
15397
- return {
15398
- skip: true,
15399
- reason: "source uses createContext and component has a single non-handler prop"
15400
- };
15401
- if (hasOnlyEventHandlers(props))
15402
- return { skip: true, reason: "every prop is a handler or ref" };
15403
- return { skip: false };
15404
- }
15338
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/astro/helpers/collect-astro-props-bindings.js
15339
+ import { Node as Node10, Project as Project7 } from "ts-morph";
15405
15340
 
15406
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/evidence/source-inspection.js
15407
- import { readFile as readFile15 } from "node:fs/promises";
15341
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/tsx-shared.js
15342
+ import { Project as Project8 } from "ts-morph";
15408
15343
 
15409
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/constants/review-reasons.js
15410
- var HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON = "data-fetch-wrapper";
15411
- var POSSIBLE_DATA_FETCH_WRAPPER_REASON = "possible-data-fetch-wrapper";
15412
- var ZERO_SURFACE_RENDERED_UI_REASON = "zero-surface:rendered-ui";
15344
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/tsx-node-utils.js
15345
+ import { Node as Node12, SyntaxKind as SyntaxKind7 } from "ts-morph";
15413
15346
 
15414
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/framework-adapters/shared/parse-imported-names.js
15415
- function parseImportedNames(importClause) {
15416
- const trimmed = importClause.trim();
15417
- if (!trimmed)
15418
- return [];
15419
- const names = [];
15420
- const defaultAndNamed = trimmed.split(",").map((part) => part.trim());
15421
- for (const part of defaultAndNamed) {
15422
- if (!part)
15423
- continue;
15424
- if (part.startsWith("{") && part.endsWith("}")) {
15425
- for (const named of part.slice(1, -1).split(",").map((item) => item.trim()).filter(Boolean)) {
15426
- const [local2] = named.split(/\s+as\s+/i);
15427
- if (local2)
15428
- names.push(local2.trim());
15429
- }
15430
- continue;
15431
- }
15432
- const [local] = part.split(/\s+as\s+/i);
15433
- if (local)
15434
- names.push(local.trim());
15435
- }
15436
- return names;
15437
- }
15347
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/resolve-tsx-workspace-imports.js
15348
+ import { Node as Node11 } from "ts-morph";
15438
15349
 
15439
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/evidence/source-inspection.js
15440
- var INFRA_PROP_NAMES = /* @__PURE__ */ new Set(["id", "locale", "preview", "slug", "topic", "previousComponent", "__typename"]);
15441
- var VISIBLE_UI_TAG_PATTERN = /<(?:[A-Z][A-Za-z0-9_.]*|div|span|section|main|article|header|footer|nav|aside|img|video|p|h[1-6]|ul|ol|li|button|input|textarea|select|form|label|table|tbody|thead|tr|td|th)\b/;
15442
- var GENERATED_IMPORT_PATTERN = /from\s+['"][^'"]*__generated[^'"]*['"]/;
15443
- var GENERATED_QUERY_HOOK_PATTERN = /\buse[A-Z][A-Za-z0-9]*(?:Lazy|Suspense)?Query\s*\(/;
15444
- var GQL_FILENAME_PATTERN = /(?:-gql|-ggl)\.[cm]?[jt]sx?$/i;
15445
- var LOADING_NULL_GUARD_PATTERN = /if\s*\([^)]*(?:isLoading|loading|!data|!\w+Collection|!\w+Item|!\w+)\s*[^)]*\)\s*return\s+null\b/;
15446
- var IMPORT_PATTERN = /import\s+(?:type\s+)?(.+?)\s+from\s+['"]([^'"]+)['"]/g;
15447
- function escapeRegExp(value) {
15448
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15449
- }
15450
- function collectSiblingRendererImports(sourceText) {
15451
- const names = /* @__PURE__ */ new Set();
15452
- for (const match2 of sourceText.matchAll(IMPORT_PATTERN)) {
15453
- const importClause = match2[1]?.trim() ?? "";
15454
- const importPath = match2[2]?.trim() ?? "";
15455
- if (!importPath.startsWith("./"))
15456
- continue;
15457
- if (importPath.includes("__generated"))
15458
- continue;
15459
- if (/-g(?:ql|gl)(?:$|\.)/i.test(importPath))
15460
- continue;
15461
- for (const name of parseImportedNames(importClause)) {
15462
- if (name)
15463
- names.add(name);
15464
- }
15465
- }
15466
- return [...names];
15467
- }
15468
- function hasSiblingForwardRender(sourceText, siblingImports) {
15469
- return siblingImports.some((name) => {
15470
- const renderPattern = new RegExp(`<${escapeRegExp(name)}\\b[\\s\\S]*?(?:/>|</${escapeRegExp(name)}>)`);
15471
- if (!renderPattern.test(sourceText))
15472
- return false;
15473
- const siblingSpreadPattern = new RegExp(`<${escapeRegExp(name)}\\b[^>]*\\{\\.\\.\\.(?!props\\b)[^}]+\\}`);
15474
- const propsPlusSiblingSpreadPattern = new RegExp(`<${escapeRegExp(name)}\\b[^>]*\\{\\.\\.\\.props\\}[^>]*\\{\\.\\.\\.(?!props\\b)[^}]+\\}`);
15475
- return siblingSpreadPattern.test(sourceText) || propsPlusSiblingSpreadPattern.test(sourceText);
15476
- });
15477
- }
15478
- function hasVisibleUiRender(sourceText) {
15479
- return /return\s*(?:\(|<)/.test(sourceText) && VISIBLE_UI_TAG_PATTERN.test(sourceText);
15480
- }
15481
- function infraPropNames(component) {
15482
- const props = component.props.map((prop) => prop.name);
15483
- return props.length > 0 && props.every((prop) => INFRA_PROP_NAMES.has(prop)) ? props : [];
15484
- }
15485
- function scoreToConfidence(score) {
15486
- if (score <= 0)
15487
- return 0;
15488
- if (score <= 2)
15489
- return 2;
15490
- if (score <= 4)
15491
- return 3;
15492
- if (score <= 6)
15493
- return 4;
15494
- return 5;
15495
- }
15496
- function dedupeReasons(reasons) {
15497
- return [...new Set(reasons)];
15498
- }
15499
- async function inspectComponentSource(component) {
15500
- let sourceText = "";
15501
- try {
15502
- sourceText = await readFile15(component.source, "utf8");
15503
- } catch {
15504
- return {
15505
- wrapperConfidence: 0,
15506
- reviewReasons: [],
15507
- keepDespiteZeroSurface: false
15508
- };
15509
- }
15510
- const reviewReasons = [];
15511
- let wrapperScore = 0;
15512
- const hasGeneratedQueryHook = GENERATED_IMPORT_PATTERN.test(sourceText) && GENERATED_QUERY_HOOK_PATTERN.test(sourceText);
15513
- const infraProps = infraPropNames(component);
15514
- const siblingImports = collectSiblingRendererImports(sourceText);
15515
- if (GQL_FILENAME_PATTERN.test(component.source)) {
15516
- reviewReasons.push("data-wrapper:gql-filename");
15517
- wrapperScore += 1;
15518
- }
15519
- if (hasGeneratedQueryHook) {
15520
- reviewReasons.push("data-wrapper:generated-query-hook");
15521
- wrapperScore += 3;
15522
- }
15523
- if (siblingImports.length > 0 && (hasGeneratedQueryHook || infraProps.length > 0)) {
15524
- reviewReasons.push("data-wrapper:sibling-renderer-import");
15525
- wrapperScore += 1;
15526
- }
15527
- if (hasSiblingForwardRender(sourceText, siblingImports)) {
15528
- reviewReasons.push("data-wrapper:fetch-forward-render");
15529
- wrapperScore += 3;
15530
- }
15531
- if (sourceText.includes("useContentfulLiveUpdates(") || sourceText.includes("useContentfulContext(")) {
15532
- reviewReasons.push("data-wrapper:contentful-runtime");
15533
- wrapperScore += 1;
15534
- }
15535
- if (infraProps.length > 0) {
15536
- reviewReasons.push("data-wrapper:infra-props");
15537
- wrapperScore += 2;
15538
- }
15539
- if (LOADING_NULL_GUARD_PATTERN.test(sourceText)) {
15540
- reviewReasons.push("data-wrapper:loading-null-guard");
15541
- wrapperScore += 1;
15542
- }
15543
- const wrapperConfidence = scoreToConfidence(wrapperScore);
15544
- if (wrapperConfidence >= 4) {
15545
- reviewReasons.unshift(HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON);
15546
- } else if (wrapperConfidence === 3) {
15547
- reviewReasons.unshift(POSSIBLE_DATA_FETCH_WRAPPER_REASON);
15548
- }
15549
- const keepDespiteZeroSurface = component.props.length === 0 && component.slots.length === 0 && hasVisibleUiRender(sourceText);
15550
- if (keepDespiteZeroSurface) {
15551
- reviewReasons.push(ZERO_SURFACE_RENDERED_UI_REASON);
15552
- }
15553
- return {
15554
- wrapperConfidence,
15555
- reviewReasons: dedupeReasons(reviewReasons),
15556
- keepDespiteZeroSurface
15557
- };
15558
- }
15559
- function describeReviewReason(reason) {
15560
- switch (reason) {
15561
- case HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON:
15562
- return "high-confidence data-fetch wrapper";
15563
- case POSSIBLE_DATA_FETCH_WRAPPER_REASON:
15564
- return "possible data-fetch wrapper";
15565
- case "data-wrapper:gql-filename":
15566
- return "source file follows a gql wrapper naming pattern";
15567
- case "data-wrapper:generated-query-hook":
15568
- return "imports and calls a generated query hook";
15569
- case "data-wrapper:sibling-renderer-import":
15570
- return "imports a sibling renderer from the same folder";
15571
- case "data-wrapper:fetch-forward-render":
15572
- return "forwards fetched data into a sibling renderer";
15573
- case "data-wrapper:contentful-runtime":
15574
- return "uses Contentful runtime hooks";
15575
- case "data-wrapper:infra-props":
15576
- return "only exposes infra-fetch props";
15577
- case "data-wrapper:loading-null-guard":
15578
- return "returns early while loading or when fetched data is missing";
15579
- case ZERO_SURFACE_RENDERED_UI_REASON:
15580
- return "source renders visible/compositional UI despite zero extracted props and slots";
15581
- default:
15582
- return reason;
15583
- }
15584
- }
15585
- function describeReviewReasons(reasons) {
15586
- return dedupeReasons(reasons.map(describeReviewReason));
15587
- }
15350
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/resolve-tsx-tsconfig-path.js
15351
+ import ts3 from "typescript";
15588
15352
 
15589
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/quality/evaluate-extraction-quality.js
15590
- function wrapperConfidenceToIssueCount(confidence) {
15591
- if (confidence >= 4)
15592
- return 2;
15593
- if (confidence === 3)
15594
- return 1;
15595
- return 0;
15353
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/tsx-renderable-exports.js
15354
+ import { Node as Node13 } from "ts-morph";
15355
+
15356
+ // packages/experience-design-system-extraction/dist/src/extract/helpers/evidence/collect-jsx-component-references.js
15357
+ import { Node as Node15, SyntaxKind as SyntaxKind9 } from "ts-morph";
15358
+
15359
+ // packages/experience-design-system-extraction/dist/src/extract/helpers/evidence/ast/get-jsx-ast-data.js
15360
+ import { Node as Node14 } from "ts-morph";
15361
+
15362
+ // packages/experience-design-system-extraction/dist/src/extract/helpers/evidence/ast/collect-structural-jsx-evidence.js
15363
+ import { SyntaxKind as SyntaxKind8 } from "ts-morph";
15364
+
15365
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/extract-from-react-source-file.js
15366
+ import { SyntaxKind as SyntaxKind14 } from "ts-morph";
15367
+
15368
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/extract-react-component-from-export.js
15369
+ import { Node as Node32 } from "ts-morph";
15370
+
15371
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/collect-prop-dataflow.js
15372
+ import { Node as Node17, SyntaxKind as SyntaxKind10 } from "ts-morph";
15373
+
15374
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/resolve-component-function.js
15375
+ import { Node as Node16 } from "ts-morph";
15376
+
15377
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/extract-react-props-from-type.js
15378
+ import { Node as Node24 } from "ts-morph";
15379
+
15380
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/dom-attribute-surfaces.js
15381
+ import { Node as Node18 } from "ts-morph";
15382
+ function createTextControlSurface2(onChangeType, specificProps) {
15383
+ return [
15384
+ { name: "autoComplete", type: "string", required: false },
15385
+ ...specificProps,
15386
+ { name: "disabled", type: "boolean", required: false },
15387
+ { name: "name", type: "string", required: false },
15388
+ { name: "onChange", type: onChangeType, required: false },
15389
+ { name: "placeholder", type: "string", required: false },
15390
+ { name: "readOnly", type: "boolean", required: false },
15391
+ { name: "required", type: "boolean", required: false },
15392
+ {
15393
+ name: "value",
15394
+ type: "string | number | readonly string[]",
15395
+ required: false
15396
+ }
15397
+ ];
15596
15398
  }
15597
- async function evaluateExtractionQuality(components, initialWarnings = []) {
15598
- const inspectedComponents = await Promise.all(components.map(async (component) => ({
15599
- component,
15600
- inspection: await inspectComponentSource(component)
15601
- })));
15602
- const scoredComponents = [];
15603
- const warnings = [...initialWarnings];
15604
- for (const { component, inspection } of inspectedComponents) {
15605
- const verdict = hasNoAuthoringSurface(component);
15606
- const keepDespiteZeroSurface = verdict.skip && verdict.reason === "component has no props and no slots" && inspection.keepDespiteZeroSurface;
15607
- const retainedForReview = verdict.skip && !keepDespiteZeroSurface;
15608
- if (retainedForReview) {
15609
- warnings.push(`${component.name}: requires operator review (${verdict.reason})`);
15399
+ var DOM_ATTRIBUTE_PROP_SURFACES2 = {
15400
+ HTMLProps: [],
15401
+ HTMLAttributes: [
15402
+ { name: "className", type: "string", required: false },
15403
+ { name: "hidden", type: "boolean", required: false },
15404
+ { name: "id", type: "string", required: false },
15405
+ {
15406
+ name: "onClick",
15407
+ type: "MouseEventHandler<HTMLElement>",
15408
+ required: false
15409
+ },
15410
+ { name: "style", type: "CSSProperties", required: false },
15411
+ { name: "tabIndex", type: "number", required: false },
15412
+ { name: "title", type: "string", required: false }
15413
+ ],
15414
+ ImgHTMLAttributes: [
15415
+ { name: "alt", type: "string", required: false },
15416
+ { name: "crossOrigin", type: "string", required: false },
15417
+ { name: "height", type: "number | string", required: false },
15418
+ {
15419
+ name: "loading",
15420
+ type: "string",
15421
+ required: false,
15422
+ allowedValues: ["eager", "lazy"]
15423
+ },
15424
+ { name: "sizes", type: "string", required: false },
15425
+ { name: "src", type: "string", required: false },
15426
+ { name: "srcSet", type: "string", required: false },
15427
+ { name: "width", type: "number | string", required: false }
15428
+ ],
15429
+ LiHTMLAttributes: [],
15430
+ AnchorHTMLAttributes: [
15431
+ { name: "download", type: "boolean | string", required: false },
15432
+ { name: "href", type: "string", required: false },
15433
+ { name: "hrefLang", type: "string", required: false },
15434
+ { name: "referrerPolicy", type: "string", required: false },
15435
+ { name: "rel", type: "string", required: false },
15436
+ {
15437
+ name: "target",
15438
+ type: "string",
15439
+ required: false,
15440
+ allowedValues: ["_blank", "_parent", "_self", "_top"]
15610
15441
  }
15611
- if (keepDespiteZeroSurface) {
15612
- warnings.push(`${component.name}: retained despite 0 props/slots because the source renders visible or compositional UI`);
15442
+ ],
15443
+ ButtonHTMLAttributes: [
15444
+ { name: "autoFocus", type: "boolean", required: false },
15445
+ { name: "disabled", type: "boolean", required: false },
15446
+ { name: "form", type: "string", required: false },
15447
+ { name: "formAction", type: "string", required: false },
15448
+ { name: "formMethod", type: "string", required: false },
15449
+ { name: "formNoValidate", type: "boolean", required: false },
15450
+ { name: "formTarget", type: "string", required: false },
15451
+ { name: "name", type: "string", required: false },
15452
+ {
15453
+ name: "type",
15454
+ type: "string",
15455
+ required: false,
15456
+ allowedValues: ["button", "reset", "submit"]
15457
+ },
15458
+ {
15459
+ name: "value",
15460
+ type: "string | number | readonly string[]",
15461
+ required: false
15613
15462
  }
15614
- if (inspection.reviewReasons.length > 0) {
15615
- const reviewNotes = describeReviewReasons(inspection.reviewReasons).filter((note) => note !== "high-confidence data-fetch wrapper").join("; ");
15616
- if (reviewNotes)
15617
- warnings.push(`${component.name}: ${reviewNotes}`);
15463
+ ],
15464
+ InputHTMLAttributes: createTextControlSurface2("ChangeEventHandler<HTMLInputElement>", [
15465
+ { name: "checked", type: "boolean", required: false },
15466
+ { name: "max", type: "number | string", required: false },
15467
+ { name: "maxLength", type: "number", required: false },
15468
+ { name: "min", type: "number | string", required: false },
15469
+ { name: "minLength", type: "number", required: false },
15470
+ { name: "type", type: "string", required: false }
15471
+ ]),
15472
+ FieldsetHTMLAttributes: [
15473
+ { name: "disabled", type: "boolean", required: false },
15474
+ { name: "form", type: "string", required: false },
15475
+ { name: "name", type: "string", required: false }
15476
+ ],
15477
+ LabelHTMLAttributes: [
15478
+ { name: "form", type: "string", required: false },
15479
+ { name: "htmlFor", type: "string", required: false }
15480
+ ],
15481
+ SelectHTMLAttributes: [
15482
+ { name: "autoComplete", type: "string", required: false },
15483
+ { name: "disabled", type: "boolean", required: false },
15484
+ { name: "form", type: "string", required: false },
15485
+ { name: "multiple", type: "boolean", required: false },
15486
+ { name: "name", type: "string", required: false },
15487
+ {
15488
+ name: "onChange",
15489
+ type: "ChangeEventHandler<HTMLSelectElement>",
15490
+ required: false
15491
+ },
15492
+ { name: "required", type: "boolean", required: false },
15493
+ { name: "size", type: "number", required: false },
15494
+ {
15495
+ name: "value",
15496
+ type: "string | number | readonly string[]",
15497
+ required: false
15618
15498
  }
15619
- const extractorReasons = component.reviewReasons ?? [];
15620
- const nonAuthorableReason = retainedForReview ? [`non-authorable:${verdict.reason}`] : [];
15621
- const { confidence, reasons } = computeExtractionScore(component, {
15622
- additionalIssueCount: wrapperConfidenceToIssueCount(inspection.wrapperConfidence) + extractorReasons.length + nonAuthorableReason.length,
15623
- additionalReasons: [...extractorReasons, ...inspection.reviewReasons, ...nonAuthorableReason]
15624
- });
15625
- scoredComponents.push({
15626
- ...component,
15627
- extractionConfidence: confidence,
15628
- reviewReasons: reasons,
15629
- needsReview: deriveNeedsReview(confidence) || inspection.wrapperConfidence >= 4 || inspection.keepDespiteZeroSurface || retainedForReview || extractorReasons.includes("props-type-unresolved") || (component.needsReview ?? false)
15630
- });
15499
+ ],
15500
+ SVGAttributes: [
15501
+ {
15502
+ name: "focusable",
15503
+ type: 'boolean | "auto"',
15504
+ required: false,
15505
+ allowedValues: ["auto"]
15506
+ },
15507
+ { name: "height", type: "number | string", required: false },
15508
+ { name: "viewBox", type: "string", required: false },
15509
+ { name: "width", type: "number | string", required: false }
15510
+ ],
15511
+ SVGProps: [],
15512
+ TextareaHTMLAttributes: createTextControlSurface2("ChangeEventHandler<HTMLTextAreaElement>", [
15513
+ { name: "cols", type: "number", required: false },
15514
+ { name: "rows", type: "number", required: false },
15515
+ { name: "wrap", type: "string", required: false }
15516
+ ]),
15517
+ TdHTMLAttributes: [
15518
+ {
15519
+ name: "align",
15520
+ type: "string",
15521
+ required: false,
15522
+ allowedValues: ["center", "char", "justify", "left", "right"]
15523
+ },
15524
+ { name: "colSpan", type: "number", required: false },
15525
+ { name: "headers", type: "string", required: false },
15526
+ { name: "rowSpan", type: "number", required: false },
15527
+ { name: "scope", type: "string", required: false }
15528
+ ]
15529
+ };
15530
+
15531
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/collect-dom-wrapper-contexts.js
15532
+ import { Node as Node19 } from "ts-morph";
15533
+
15534
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/detect-dom-is-pure-wrapper.js
15535
+ import { Node as Node20 } from "ts-morph";
15536
+
15537
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/detect-omit-wrapped-custom-props.js
15538
+ import { Node as Node21 } from "ts-morph";
15539
+
15540
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/detect-pick-type-patterns.js
15541
+ import { Node as Node22 } from "ts-morph";
15542
+
15543
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/detect-react-polymorphic.js
15544
+ import { Node as Node23 } from "ts-morph";
15545
+
15546
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/resolve-react-props-type.js
15547
+ import { Node as Node25 } from "ts-morph";
15548
+
15549
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/extract-react-binding-defaults.js
15550
+ import { Node as Node28 } from "ts-morph";
15551
+
15552
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/filter-impl-alias-props.js
15553
+ import { Node as Node26 } from "ts-morph";
15554
+
15555
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/extract-binding-fallback-props.js
15556
+ import { Node as Node27, SyntaxKind as SyntaxKind11 } from "ts-morph";
15557
+
15558
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/collect-react-impl-hints.js
15559
+ import { Node as Node30, SyntaxKind as SyntaxKind13 } from "ts-morph";
15560
+
15561
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/infer-primitive-dom-props.js
15562
+ import { Node as Node29, SyntaxKind as SyntaxKind12 } from "ts-morph";
15563
+
15564
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/react/helpers/extract-react-prop-types.js
15565
+ import { Node as Node31 } from "ts-morph";
15566
+
15567
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/stencil/extractor.js
15568
+ import { Project as Project9, Node as Node37 } from "ts-morph";
15569
+
15570
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/stencil/helpers/detect-stencil-class.js
15571
+ import { Node as Node33 } from "ts-morph";
15572
+
15573
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/stencil/helpers/extract-stencil-props.js
15574
+ import { Node as Node35 } from "ts-morph";
15575
+
15576
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/stencil/helpers/stencil-prop-helpers.js
15577
+ import { Node as Node34, SyntaxKind as SyntaxKind15 } from "ts-morph";
15578
+
15579
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/stencil/helpers/extract-stencil-slots.js
15580
+ import { Node as Node36 } from "ts-morph";
15581
+
15582
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/svelte/helpers/resolve-svelte-unresolved-retry.js
15583
+ import { Project as Project12, ScriptTarget as ScriptTarget4, ModuleKind as ModuleKind4, ts as ts5 } from "ts-morph";
15584
+
15585
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/scoring/collect-scoring-inputs.js
15586
+ var OPAQUE_TYPES2 = /* @__PURE__ */ new Set(["any", "unknown", "object", "Record<string, unknown>", "Record<string, any>"]);
15587
+ var OBVIOUS_PROP_NAMES2 = /* @__PURE__ */ new Set([
15588
+ "className",
15589
+ "style",
15590
+ "id",
15591
+ "children",
15592
+ "disabled",
15593
+ "hidden",
15594
+ "onClick",
15595
+ "onChange",
15596
+ "onSubmit",
15597
+ "onBlur",
15598
+ "onFocus",
15599
+ "href",
15600
+ "src",
15601
+ "alt",
15602
+ "type",
15603
+ "value",
15604
+ "checked",
15605
+ "placeholder",
15606
+ "label",
15607
+ "title",
15608
+ "name",
15609
+ "required",
15610
+ "readOnly",
15611
+ "autoFocus",
15612
+ "tabIndex",
15613
+ "role",
15614
+ "aria-label",
15615
+ "aria-describedby"
15616
+ ]);
15617
+ function isWidePrimitiveUnion2(type) {
15618
+ const parts = type.split("|").map((p) => p.trim());
15619
+ if (parts.length < 3)
15620
+ return false;
15621
+ const basePrimitives = /* @__PURE__ */ new Set(["string", "number", "boolean"]);
15622
+ const nullability = /* @__PURE__ */ new Set(["null", "undefined"]);
15623
+ const baseCount = parts.filter((p) => basePrimitives.has(p)).length;
15624
+ const nullabilityCount = parts.filter((p) => nullability.has(p)).length;
15625
+ return baseCount >= 3 || baseCount >= 2 && nullabilityCount > 0 && baseCount + nullabilityCount >= 3;
15626
+ }
15627
+ function countExtractionIssues(component, options = {}) {
15628
+ let count = 0;
15629
+ const reasons = [];
15630
+ if (component.props.length === 0 && component.slots.length === 0) {
15631
+ count++;
15632
+ reasons.push("no-props-or-slots");
15631
15633
  }
15634
+ if (component.props.length > 50) {
15635
+ count++;
15636
+ reasons.push(`high-prop-count:${component.props.length}`);
15637
+ }
15638
+ for (const prop of component.props) {
15639
+ if (OPAQUE_TYPES2.has(prop.type.trim())) {
15640
+ count++;
15641
+ reasons.push(`opaque-type:${prop.name}`);
15642
+ break;
15643
+ }
15644
+ if (isWidePrimitiveUnion2(prop.type)) {
15645
+ count++;
15646
+ reasons.push(`wide-union:${prop.name}`);
15647
+ break;
15648
+ }
15649
+ if (!prop.description && !OBVIOUS_PROP_NAMES2.has(prop.name)) {
15650
+ count++;
15651
+ reasons.push("props-missing-description");
15652
+ break;
15653
+ }
15654
+ }
15655
+ count += options.additionalIssueCount ?? 0;
15656
+ if (options.additionalReasons && options.additionalReasons.length > 0) {
15657
+ reasons.push(...options.additionalReasons);
15658
+ }
15659
+ return { count, reasons: [...new Set(reasons)] };
15660
+ }
15661
+ function mapIssueCountToConfidence(count) {
15662
+ if (count === 0)
15663
+ return 5;
15664
+ if (count === 1)
15665
+ return 4;
15666
+ if (count === 2)
15667
+ return 3;
15668
+ if (count === 3)
15669
+ return 2;
15670
+ return 1;
15671
+ }
15672
+
15673
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/scoring/compute-extraction-score.js
15674
+ function computeExtractionScore2(component, options = {}) {
15675
+ const { count, reasons } = countExtractionIssues(component, options);
15632
15676
  return {
15633
- components: validateExtractedComponents(scoredComponents),
15634
- warnings
15677
+ confidence: mapIssueCountToConfidence(count),
15678
+ reasons
15635
15679
  };
15636
15680
  }
15681
+ function deriveNeedsReview2(confidence) {
15682
+ return confidence <= 2;
15683
+ }
15684
+
15685
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/svelte/helpers/resolve-svelte-type-checker.js
15686
+ import { Project as Project10, ScriptTarget as ScriptTarget2, ModuleKind as ModuleKind2, ts as ts4 } from "ts-morph";
15687
+
15688
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/svelte/helpers/extract-svelte-declaration-data.js
15689
+ import { Node as Node38 } from "ts-morph";
15690
+
15691
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/svelte/helpers/resolve-svelte-external-type-readers.js
15692
+ import { Node as Node40, Project as Project11, ScriptTarget as ScriptTarget3, ModuleKind as ModuleKind3 } from "ts-morph";
15693
+
15694
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/svelte/helpers/extract-svelte-external-type-members.js
15695
+ import { Node as Node39 } from "ts-morph";
15637
15696
 
15638
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/pre-classify.js
15697
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/svelte/helpers/extract-from-svelte-file.js
15698
+ import { parse as parseSvelte2 } from "svelte/compiler";
15699
+
15700
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue-tsx/extractor.js
15701
+ import { Node as Node44 } from "ts-morph";
15702
+
15703
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue-tsx/helpers/extract-vue-tsx-props.js
15704
+ import { Node as Node42 } from "ts-morph";
15705
+
15706
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue-tsx/helpers/resolve-ast-object.js
15707
+ import { Node as Node41, SyntaxKind as SyntaxKind16 } from "ts-morph";
15708
+
15709
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue-tsx/helpers/extract-vue-tsx-slots.js
15710
+ import { Node as Node43, SyntaxKind as SyntaxKind17 } from "ts-morph";
15711
+
15712
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue/extractor.js
15713
+ import { parse as parseSFC3 } from "@vue/compiler-sfc";
15714
+
15715
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue/helpers/extract-vue-setup-props.js
15716
+ import { Project as Project14, Node as Node47 } from "ts-morph";
15717
+
15718
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue/helpers/extract-vue-setup-object-props.js
15719
+ import { Project as Project13, Node as Node46 } from "ts-morph";
15720
+
15721
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue/helpers/extract-vue-object-props.js
15722
+ import { Node as Node45 } from "ts-morph";
15723
+
15724
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/vue/helpers/extract-vue-options-props.js
15725
+ import { parse as parseSFC2 } from "@vue/compiler-sfc";
15726
+ import { Node as Node48, Project as Project15 } from "ts-morph";
15727
+
15728
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/extractor.js
15729
+ import { Project as Project16 } from "ts-morph";
15730
+
15731
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/extract-wc-observed-attrs.js
15732
+ import { Node as Node49 } from "ts-morph";
15733
+
15734
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/extract-wc-class-properties.js
15735
+ import { Node as Node50 } from "ts-morph";
15736
+
15737
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/resolve-wc-decorator-tag.js
15738
+ import { Node as Node51 } from "ts-morph";
15739
+
15740
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/resolve-wc-sibling-definition.js
15741
+ import { Node as Node54, SyntaxKind as SyntaxKind19 } from "ts-morph";
15742
+
15743
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/resolve-wc-import.js
15744
+ import { Node as Node53, SyntaxKind as SyntaxKind18 } from "ts-morph";
15745
+
15746
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/resolve-wc-import-source.js
15747
+ import { Node as Node52 } from "ts-morph";
15748
+
15749
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/extract-wc-template-content.js
15750
+ import { Node as Node56 } from "ts-morph";
15751
+
15752
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/collect-wc-html-templates.js
15753
+ import { Node as Node55 } from "ts-morph";
15754
+
15755
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/web-components/helpers/resolve-wc-base-class.js
15756
+ import { Node as Node57, SyntaxKind as SyntaxKind20 } from "ts-morph";
15757
+
15758
+ // packages/experience-design-system-extraction/dist/src/extract/services/classification/helpers/check-prop-types.js
15639
15759
  function isStringLiteralUnion(type) {
15640
15760
  return type.includes("|") && type.includes("'");
15641
15761
  }
@@ -15657,6 +15777,8 @@ function isNumberType(type) {
15657
15777
  function isComplexType(type) {
15658
15778
  return !isSimpleType(type);
15659
15779
  }
15780
+
15781
+ // packages/experience-design-system-extraction/dist/src/extract/services/classification/helpers/detect-dom-props.js
15660
15782
  var DOM_PASS_THROUGH_PROPS = /* @__PURE__ */ new Set([
15661
15783
  // Bare HTML / framework styling pass-through
15662
15784
  "className",
@@ -15718,100 +15840,454 @@ function isDomPassThroughProp(name) {
15718
15840
  return true;
15719
15841
  return name.startsWith("data-");
15720
15842
  }
15721
- function preClassifyProp(prop) {
15722
- const { name, type } = prop;
15723
- if (/^on[A-Z]/.test(name) || type.includes("=> void") || type.includes("EventHandler")) {
15724
- return { category: "exclude" };
15843
+
15844
+ // packages/experience-design-system-extraction/dist/src/extract/services/classification/classify-component-props.js
15845
+ function preClassifyProp(prop) {
15846
+ const { name, type } = prop;
15847
+ if (/^on[A-Z]/.test(name) || type.includes("=> void") || type.includes("EventHandler")) {
15848
+ return { category: "exclude" };
15849
+ }
15850
+ if (name === "ref" || name === "innerRef" || type.includes("Ref<") || type.includes("RefObject<")) {
15851
+ return { category: "exclude" };
15852
+ }
15853
+ if (name === "testId" || name === "data-testid" || name === "dataTestId") {
15854
+ return { category: "exclude" };
15855
+ }
15856
+ if (name === "key") {
15857
+ return { category: "exclude" };
15858
+ }
15859
+ if (type.includes("Dispatch<") || type.includes("SetStateAction")) {
15860
+ return { category: "exclude" };
15861
+ }
15862
+ if (isDomPassThroughProp(name)) {
15863
+ return { category: "exclude" };
15864
+ }
15865
+ if (name === "name" && prop.domAttribute) {
15866
+ return { category: "exclude" };
15867
+ }
15868
+ if (isStringLiteralUnion(type)) {
15869
+ return { category: "design", cdfTypeHint: "enum" };
15870
+ }
15871
+ if (!isComplexType(type)) {
15872
+ const designNameStart = /^(variant|size|spacing|gap|color|bg|theme|align|layout|orientation|position)/i;
15873
+ const designNameEnd = /(Color|Size|Variant|Style)$/;
15874
+ if (designNameStart.test(name) || designNameEnd.test(name)) {
15875
+ return { category: "design", cdfTypeHint: "string" };
15876
+ }
15877
+ }
15878
+ if (isBooleanType(type)) {
15879
+ const stateNames = ["disabled", "loading", "expanded", "isOpen", "selected", "checked", "active", "preview"];
15880
+ if (stateNames.includes(name)) {
15881
+ return { category: "state", cdfTypeHint: "boolean" };
15882
+ }
15883
+ }
15884
+ if (isBooleanType(type)) {
15885
+ const visualToggle = /^(hide|show|enable|disable|vertical|horizontal|reverse|bold|italic|imageOn|with)/i;
15886
+ if (visualToggle.test(name)) {
15887
+ return { category: "design", cdfTypeHint: "boolean" };
15888
+ }
15889
+ }
15890
+ if (name === "componentId" || name === "sectionKey" || name === "locale" || name === "variantIndex") {
15891
+ return { category: "state", cdfTypeHint: "string" };
15892
+ }
15893
+ if (isStringType(type)) {
15894
+ const urlNameStart = /^(href|url|link|src)/i;
15895
+ const urlNameEnd = /(Url|Href|Link|Src)$/;
15896
+ if (urlNameStart.test(name) || urlNameEnd.test(name)) {
15897
+ return { category: "content", cdfTypeHint: "string" };
15898
+ }
15899
+ }
15900
+ if (isStringType(type)) {
15901
+ const textNameStart = /^(label|title|text|description|caption|heading|subheading|body|alt|name|placeholder|summary)/i;
15902
+ const textNameEnd = /(Text|Label|Title|Name)$/;
15903
+ if (textNameStart.test(name) || textNameEnd.test(name)) {
15904
+ return { category: "content", cdfTypeHint: "string" };
15905
+ }
15906
+ }
15907
+ if (isStringType(type)) {
15908
+ return { category: "content", cdfTypeHint: "string" };
15909
+ }
15910
+ if (isBooleanType(type)) {
15911
+ return { category: "design", cdfTypeHint: "boolean" };
15912
+ }
15913
+ if (isNumberType(type)) {
15914
+ return { category: "design", cdfTypeHint: "string" };
15915
+ }
15916
+ return void 0;
15917
+ }
15918
+ function preClassifyComponent(component) {
15919
+ const props = component.props.flatMap((prop) => {
15920
+ const result = preClassifyProp(prop);
15921
+ if (result?.category === "exclude") {
15922
+ return [];
15923
+ }
15924
+ const { domAttribute: _domAttribute, ...authorableProp } = prop;
15925
+ if (prop.category) {
15926
+ return [authorableProp];
15927
+ }
15928
+ if (!result) {
15929
+ return [authorableProp];
15930
+ }
15931
+ return [
15932
+ {
15933
+ ...authorableProp,
15934
+ category: result.category
15935
+ }
15936
+ ];
15937
+ });
15938
+ return { ...component, props };
15939
+ }
15940
+
15941
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/authorability/evaluate-authorability.js
15942
+ var HANDLER_TYPE_PATTERN = /=>|EventHandler|Dispatch<|SetStateAction/;
15943
+ var REF_TYPE_PATTERN = /Ref<|RefObject<|MutableRefObject/;
15944
+ function isHandlerOrRefProp(prop) {
15945
+ if (HANDLER_TYPE_PATTERN.test(prop.type))
15946
+ return true;
15947
+ if (REF_TYPE_PATTERN.test(prop.type))
15948
+ return true;
15949
+ if (/^on[A-Z]/.test(prop.name) || /^set[A-Z]/.test(prop.name))
15950
+ return true;
15951
+ if (prop.name === "ref" || prop.name === "innerRef")
15952
+ return true;
15953
+ return false;
15954
+ }
15955
+ function hasNoPropsAndNoSlots(props, slots) {
15956
+ return props.length === 0 && slots.length === 0;
15957
+ }
15958
+ function isContextProviderWithValueProp(props, usesCreateContext) {
15959
+ return !!usesCreateContext && props.some((p) => p.name === "value");
15960
+ }
15961
+ function isHardwiredContextProvider(props, usesCreateContext) {
15962
+ return !!usesCreateContext && props.length === 0;
15963
+ }
15964
+ function isSingleDataPropContextWrapper(props, usesCreateContext) {
15965
+ return !!usesCreateContext && props.length === 1 && !isHandlerOrRefProp(props[0]);
15966
+ }
15967
+ function hasOnlyEventHandlers(props) {
15968
+ return props.length > 0 && props.every(isHandlerOrRefProp);
15969
+ }
15970
+ function hasNoAuthoringSurface(component) {
15971
+ const { props, slots, usesCreateContext } = component;
15972
+ if (hasNoPropsAndNoSlots(props, slots))
15973
+ return { skip: true, reason: "component has no props and no slots" };
15974
+ if (isContextProviderWithValueProp(props, usesCreateContext))
15975
+ return {
15976
+ skip: true,
15977
+ reason: "source uses createContext and component exposes a Context.Provider value prop"
15978
+ };
15979
+ if (isHardwiredContextProvider(props, usesCreateContext))
15980
+ return {
15981
+ skip: true,
15982
+ reason: "source uses createContext and component has no props"
15983
+ };
15984
+ if (isSingleDataPropContextWrapper(props, usesCreateContext))
15985
+ return {
15986
+ skip: true,
15987
+ reason: "source uses createContext and component has a single non-handler prop"
15988
+ };
15989
+ if (hasOnlyEventHandlers(props))
15990
+ return { skip: true, reason: "every prop is a handler or ref" };
15991
+ return { skip: false };
15992
+ }
15993
+
15994
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/inspection/inspect-component-source.js
15995
+ import { readFile as readFile15 } from "node:fs/promises";
15996
+
15997
+ // packages/experience-design-system-extraction/dist/src/extract/helpers/evidence/parse-source-imports.js
15998
+ function parseImportedNames(importClause) {
15999
+ const trimmed = importClause.trim();
16000
+ if (!trimmed)
16001
+ return [];
16002
+ const names = [];
16003
+ const defaultAndNamed = trimmed.split(",").map((part) => part.trim());
16004
+ for (const part of defaultAndNamed) {
16005
+ if (!part)
16006
+ continue;
16007
+ if (part.startsWith("{") && part.endsWith("}")) {
16008
+ for (const named of part.slice(1, -1).split(",").map((item) => item.trim()).filter(Boolean)) {
16009
+ const [local2] = named.split(/\s+as\s+/i);
16010
+ if (local2)
16011
+ names.push(local2.trim());
16012
+ }
16013
+ continue;
16014
+ }
16015
+ const [local] = part.split(/\s+as\s+/i);
16016
+ if (local)
16017
+ names.push(local.trim());
16018
+ }
16019
+ return names;
16020
+ }
16021
+
16022
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/inspection/detect-data-wrapper-patterns.js
16023
+ var INFRA_PROP_NAMES = /* @__PURE__ */ new Set(["id", "locale", "preview", "slug", "topic", "previousComponent", "__typename"]);
16024
+ var VISIBLE_UI_TAG_PATTERN = /<(?:[A-Z][A-Za-z0-9_.]*|div|span|section|main|article|header|footer|nav|aside|img|video|p|h[1-6]|ul|ol|li|button|input|textarea|select|form|label|table|tbody|thead|tr|td|th)\b/;
16025
+ var GENERATED_IMPORT_PATTERN = /from\s+['"][^'"]*__generated[^'"]*['"]/;
16026
+ var GENERATED_QUERY_HOOK_PATTERN = /\buse[A-Z][A-Za-z0-9]*(?:Lazy|Suspense)?Query\s*\(/;
16027
+ var GQL_FILENAME_PATTERN = /(?:-gql|-ggl)\.[cm]?[jt]sx?$/i;
16028
+ var LOADING_NULL_GUARD_PATTERN = /if\s*\([^)]*(?:isLoading|loading|!data|!\w+Collection|!\w+Item|!\w+)\s*[^)]*\)\s*return\s+null\b/;
16029
+ var IMPORT_PATTERN = /import\s+(?:type\s+)?(.+?)\s+from\s+['"]([^'"]+)['"]/g;
16030
+ function escapeRegExp(value) {
16031
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16032
+ }
16033
+ function collectSiblingRendererImports(sourceText) {
16034
+ const names = /* @__PURE__ */ new Set();
16035
+ for (const match2 of sourceText.matchAll(IMPORT_PATTERN)) {
16036
+ const importClause = match2[1]?.trim() ?? "";
16037
+ const importPath = match2[2]?.trim() ?? "";
16038
+ if (!importPath.startsWith("./"))
16039
+ continue;
16040
+ if (importPath.includes("__generated"))
16041
+ continue;
16042
+ if (/-g(?:ql|gl)(?:$|\.)/i.test(importPath))
16043
+ continue;
16044
+ for (const name of parseImportedNames(importClause)) {
16045
+ if (name)
16046
+ names.add(name);
16047
+ }
15725
16048
  }
15726
- if (name === "ref" || name === "innerRef" || type.includes("Ref<") || type.includes("RefObject<")) {
15727
- return { category: "exclude" };
16049
+ return [...names];
16050
+ }
16051
+ function hasSiblingForwardRender(sourceText, siblingImports) {
16052
+ return siblingImports.some((name) => {
16053
+ const renderPattern = new RegExp(`<${escapeRegExp(name)}\\b[\\s\\S]*?(?:/>|</${escapeRegExp(name)}>)`);
16054
+ if (!renderPattern.test(sourceText))
16055
+ return false;
16056
+ const siblingSpreadPattern = new RegExp(`<${escapeRegExp(name)}\\b[^>]*\\{\\.\\.\\.(?!props\\b)[^}]+\\}`);
16057
+ const propsPlusSiblingSpreadPattern = new RegExp(`<${escapeRegExp(name)}\\b[^>]*\\{\\.\\.\\.props\\}[^>]*\\{\\.\\.\\.(?!props\\b)[^}]+\\}`);
16058
+ return siblingSpreadPattern.test(sourceText) || propsPlusSiblingSpreadPattern.test(sourceText);
16059
+ });
16060
+ }
16061
+ function hasVisibleUiRender(sourceText) {
16062
+ return /return\s*(?:\(|<)/.test(sourceText) && VISIBLE_UI_TAG_PATTERN.test(sourceText);
16063
+ }
16064
+ function collectInfraPropNames(component) {
16065
+ const props = component.props.map((prop) => prop.name);
16066
+ return props.length > 0 && props.every((prop) => INFRA_PROP_NAMES.has(prop)) ? props : [];
16067
+ }
16068
+ function mapScoreToWrapperConfidence(score) {
16069
+ if (score <= 0)
16070
+ return 0;
16071
+ if (score <= 2)
16072
+ return 2;
16073
+ if (score <= 4)
16074
+ return 3;
16075
+ if (score <= 6)
16076
+ return 4;
16077
+ return 5;
16078
+ }
16079
+ function dedupeStrings(values) {
16080
+ return [...new Set(values)];
16081
+ }
16082
+
16083
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/inspection/inspect-component-source.js
16084
+ var HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON = "data-fetch-wrapper";
16085
+ var POSSIBLE_DATA_FETCH_WRAPPER_REASON = "possible-data-fetch-wrapper";
16086
+ var ZERO_SURFACE_RENDERED_UI_REASON = "zero-surface:rendered-ui";
16087
+ async function inspectComponentSource(component) {
16088
+ let sourceText = "";
16089
+ try {
16090
+ sourceText = await readFile15(component.source, "utf8");
16091
+ } catch {
16092
+ return {
16093
+ wrapperConfidence: 0,
16094
+ reviewReasons: [],
16095
+ keepDespiteZeroSurface: false
16096
+ };
15728
16097
  }
15729
- if (name === "testId" || name === "data-testid" || name === "dataTestId") {
15730
- return { category: "exclude" };
16098
+ const reviewReasons = [];
16099
+ let wrapperScore = 0;
16100
+ const hasGeneratedQueryHook = GENERATED_IMPORT_PATTERN.test(sourceText) && GENERATED_QUERY_HOOK_PATTERN.test(sourceText);
16101
+ const infraProps = collectInfraPropNames(component);
16102
+ const siblingImports = collectSiblingRendererImports(sourceText);
16103
+ if (GQL_FILENAME_PATTERN.test(component.source)) {
16104
+ reviewReasons.push("data-wrapper:gql-filename");
16105
+ wrapperScore += 1;
15731
16106
  }
15732
- if (name === "key") {
15733
- return { category: "exclude" };
16107
+ if (hasGeneratedQueryHook) {
16108
+ reviewReasons.push("data-wrapper:generated-query-hook");
16109
+ wrapperScore += 3;
15734
16110
  }
15735
- if (type.includes("Dispatch<") || type.includes("SetStateAction")) {
15736
- return { category: "exclude" };
16111
+ if (siblingImports.length > 0 && (hasGeneratedQueryHook || infraProps.length > 0)) {
16112
+ reviewReasons.push("data-wrapper:sibling-renderer-import");
16113
+ wrapperScore += 1;
15737
16114
  }
15738
- if (isDomPassThroughProp(name)) {
15739
- return { category: "exclude" };
16115
+ if (hasSiblingForwardRender(sourceText, siblingImports)) {
16116
+ reviewReasons.push("data-wrapper:fetch-forward-render");
16117
+ wrapperScore += 3;
15740
16118
  }
15741
- if (name === "name" && prop.domAttribute) {
15742
- return { category: "exclude" };
16119
+ if (sourceText.includes("useContentfulLiveUpdates(") || sourceText.includes("useContentfulContext(")) {
16120
+ reviewReasons.push("data-wrapper:contentful-runtime");
16121
+ wrapperScore += 1;
15743
16122
  }
15744
- if (isStringLiteralUnion(type)) {
15745
- return { category: "design", cdfTypeHint: "enum" };
16123
+ if (infraProps.length > 0) {
16124
+ reviewReasons.push("data-wrapper:infra-props");
16125
+ wrapperScore += 2;
15746
16126
  }
15747
- if (!isComplexType(type)) {
15748
- const designNameStart = /^(variant|size|spacing|gap|color|bg|theme|align|layout|orientation|position)/i;
15749
- const designNameEnd = /(Color|Size|Variant|Style)$/;
15750
- if (designNameStart.test(name) || designNameEnd.test(name)) {
15751
- return { category: "design", cdfTypeHint: "string" };
15752
- }
16127
+ if (LOADING_NULL_GUARD_PATTERN.test(sourceText)) {
16128
+ reviewReasons.push("data-wrapper:loading-null-guard");
16129
+ wrapperScore += 1;
15753
16130
  }
15754
- if (isBooleanType(type)) {
15755
- const stateNames = ["disabled", "loading", "expanded", "isOpen", "selected", "checked", "active", "preview"];
15756
- if (stateNames.includes(name)) {
15757
- return { category: "state", cdfTypeHint: "boolean" };
15758
- }
16131
+ const wrapperConfidence = mapScoreToWrapperConfidence(wrapperScore);
16132
+ if (wrapperConfidence >= 4) {
16133
+ reviewReasons.unshift(HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON);
16134
+ } else if (wrapperConfidence === 3) {
16135
+ reviewReasons.unshift(POSSIBLE_DATA_FETCH_WRAPPER_REASON);
15759
16136
  }
15760
- if (isBooleanType(type)) {
15761
- const visualToggle = /^(hide|show|enable|disable|vertical|horizontal|reverse|bold|italic|imageOn|with)/i;
15762
- if (visualToggle.test(name)) {
15763
- return { category: "design", cdfTypeHint: "boolean" };
15764
- }
16137
+ const keepDespiteZeroSurface = component.props.length === 0 && component.slots.length === 0 && hasVisibleUiRender(sourceText);
16138
+ if (keepDespiteZeroSurface) {
16139
+ reviewReasons.push(ZERO_SURFACE_RENDERED_UI_REASON);
15765
16140
  }
15766
- if (name === "componentId" || name === "sectionKey" || name === "locale" || name === "variantIndex") {
15767
- return { category: "state", cdfTypeHint: "string" };
16141
+ return {
16142
+ wrapperConfidence,
16143
+ reviewReasons: dedupeStrings(reviewReasons),
16144
+ keepDespiteZeroSurface
16145
+ };
16146
+ }
16147
+ function describeReviewReason(reason) {
16148
+ switch (reason) {
16149
+ case HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON:
16150
+ return "high-confidence data-fetch wrapper";
16151
+ case POSSIBLE_DATA_FETCH_WRAPPER_REASON:
16152
+ return "possible data-fetch wrapper";
16153
+ case "data-wrapper:gql-filename":
16154
+ return "source file follows a gql wrapper naming pattern";
16155
+ case "data-wrapper:generated-query-hook":
16156
+ return "imports and calls a generated query hook";
16157
+ case "data-wrapper:sibling-renderer-import":
16158
+ return "imports a sibling renderer from the same folder";
16159
+ case "data-wrapper:fetch-forward-render":
16160
+ return "forwards fetched data into a sibling renderer";
16161
+ case "data-wrapper:contentful-runtime":
16162
+ return "uses Contentful runtime hooks";
16163
+ case "data-wrapper:infra-props":
16164
+ return "only exposes infra-fetch props";
16165
+ case "data-wrapper:loading-null-guard":
16166
+ return "returns early while loading or when fetched data is missing";
16167
+ case ZERO_SURFACE_RENDERED_UI_REASON:
16168
+ return "source renders visible/compositional UI despite zero extracted props and slots";
16169
+ default:
16170
+ return reason;
15768
16171
  }
15769
- if (isStringType(type)) {
15770
- const urlNameStart = /^(href|url|link|src)/i;
15771
- const urlNameEnd = /(Url|Href|Link|Src)$/;
15772
- if (urlNameStart.test(name) || urlNameEnd.test(name)) {
15773
- return { category: "content", cdfTypeHint: "string" };
15774
- }
16172
+ }
16173
+ function describeReviewReasons(reasons) {
16174
+ return dedupeStrings(reasons.map(describeReviewReason));
16175
+ }
16176
+
16177
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/inspection/validate-extracted-components.js
16178
+ function validateExtractedComponents2(components) {
16179
+ const nameCounts = /* @__PURE__ */ new Map();
16180
+ for (const component of components) {
16181
+ const key = component.name.trim();
16182
+ nameCounts.set(key, (nameCounts.get(key) ?? 0) + 1);
15775
16183
  }
15776
- if (isStringType(type)) {
15777
- const textNameStart = /^(label|title|text|description|caption|heading|subheading|body|alt|name|placeholder|summary)/i;
15778
- const textNameEnd = /(Text|Label|Title|Name)$/;
15779
- if (textNameStart.test(name) || textNameEnd.test(name)) {
15780
- return { category: "content", cdfTypeHint: "string" };
16184
+ return components.map((component) => {
16185
+ const issues = [];
16186
+ if (!component.name.trim()) {
16187
+ issues.push({
16188
+ severity: "error",
16189
+ code: "EMPTY_COMPONENT_NAME",
16190
+ message: "Component name must not be empty"
16191
+ });
15781
16192
  }
15782
- }
15783
- if (isStringType(type)) {
15784
- return { category: "content", cdfTypeHint: "string" };
15785
- }
15786
- if (isBooleanType(type)) {
15787
- return { category: "design", cdfTypeHint: "boolean" };
15788
- }
15789
- if (isNumberType(type)) {
15790
- return { category: "design", cdfTypeHint: "string" };
15791
- }
15792
- return void 0;
16193
+ for (let i = 0; i < component.props.length; i++) {
16194
+ if (!component.props[i].name.trim()) {
16195
+ issues.push({
16196
+ severity: "error",
16197
+ code: "EMPTY_PROP_NAME",
16198
+ message: `Prop at index ${i} has an empty name`,
16199
+ field: `props[${i}].name`
16200
+ });
16201
+ }
16202
+ }
16203
+ for (let i = 0; i < component.slots.length; i++) {
16204
+ if (!component.slots[i].name.trim()) {
16205
+ issues.push({
16206
+ severity: "warning",
16207
+ code: "EMPTY_SLOT_NAME",
16208
+ message: `Slot at index ${i} has an empty name`,
16209
+ field: `slots[${i}].name`
16210
+ });
16211
+ }
16212
+ }
16213
+ const propNames = new Set(component.props.map((p) => p.name.trim()).filter(Boolean));
16214
+ for (let i = 0; i < component.slots.length; i++) {
16215
+ const slotName = component.slots[i].name.trim();
16216
+ if (slotName && propNames.has(slotName)) {
16217
+ issues.push({
16218
+ severity: "error",
16219
+ code: "PROP_SLOT_NAME_COLLISION",
16220
+ message: `"${slotName}" is used as both a prop name and a slot name`,
16221
+ field: `slots[${i}].name`
16222
+ });
16223
+ }
16224
+ }
16225
+ const nameKey = component.name.trim();
16226
+ if (nameKey && (nameCounts.get(nameKey) ?? 0) > 1) {
16227
+ issues.push({
16228
+ severity: "error",
16229
+ code: "DUPLICATE_COMPONENT_NAME",
16230
+ message: `Component name "${component.name}" appears more than once in the extracted set`
16231
+ });
16232
+ }
16233
+ if (component.props.length === 0 && component.slots.length === 0) {
16234
+ issues.push({
16235
+ severity: "warning",
16236
+ code: "EMPTY_COMPONENT",
16237
+ message: "Component has no props or slots and will be filtered out during generation"
16238
+ });
16239
+ }
16240
+ return { ...component, validationIssues: issues };
16241
+ });
16242
+ }
16243
+
16244
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/evaluate-extraction-quality.js
16245
+ function wrapperConfidenceToIssueCount(confidence) {
16246
+ if (confidence >= 4)
16247
+ return 2;
16248
+ if (confidence === 3)
16249
+ return 1;
16250
+ return 0;
15793
16251
  }
15794
- function preClassifyComponent(component) {
15795
- const props = component.props.flatMap((prop) => {
15796
- const result = preClassifyProp(prop);
15797
- if (result?.category === "exclude") {
15798
- return [];
16252
+ async function evaluateExtractionQuality(components, initialWarnings = []) {
16253
+ const inspectedComponents = await Promise.all(components.map(async (component) => ({
16254
+ component,
16255
+ inspection: await inspectComponentSource(component)
16256
+ })));
16257
+ const scoredComponents = [];
16258
+ const warnings = [...initialWarnings];
16259
+ for (const { component, inspection } of inspectedComponents) {
16260
+ const verdict = hasNoAuthoringSurface(component);
16261
+ const keepDespiteZeroSurface = verdict.skip && verdict.reason === "component has no props and no slots" && inspection.keepDespiteZeroSurface;
16262
+ const retainedForReview = verdict.skip && !keepDespiteZeroSurface;
16263
+ if (retainedForReview) {
16264
+ warnings.push(`${component.name}: requires operator review (${verdict.reason})`);
15799
16265
  }
15800
- const { domAttribute: _domAttribute, ...authorableProp } = prop;
15801
- if (prop.category) {
15802
- return [authorableProp];
16266
+ if (keepDespiteZeroSurface) {
16267
+ warnings.push(`${component.name}: retained despite 0 props/slots because the source renders visible or compositional UI`);
15803
16268
  }
15804
- if (!result) {
15805
- return [authorableProp];
16269
+ if (inspection.reviewReasons.length > 0) {
16270
+ const reviewNotes = describeReviewReasons(inspection.reviewReasons).filter((note) => note !== "high-confidence data-fetch wrapper").join("; ");
16271
+ if (reviewNotes)
16272
+ warnings.push(`${component.name}: ${reviewNotes}`);
15806
16273
  }
15807
- return [
15808
- {
15809
- ...authorableProp,
15810
- category: result.category
15811
- }
15812
- ];
15813
- });
15814
- return { ...component, props };
16274
+ const extractorReasons = component.reviewReasons ?? [];
16275
+ const nonAuthorableReason = retainedForReview ? [`non-authorable:${verdict.reason}`] : [];
16276
+ const { confidence, reasons } = computeExtractionScore2(component, {
16277
+ additionalIssueCount: wrapperConfidenceToIssueCount(inspection.wrapperConfidence) + extractorReasons.length + nonAuthorableReason.length,
16278
+ additionalReasons: [...extractorReasons, ...inspection.reviewReasons, ...nonAuthorableReason]
16279
+ });
16280
+ scoredComponents.push({
16281
+ ...component,
16282
+ extractionConfidence: confidence,
16283
+ reviewReasons: reasons,
16284
+ needsReview: deriveNeedsReview2(confidence) || inspection.wrapperConfidence >= 4 || inspection.keepDespiteZeroSurface || retainedForReview || extractorReasons.includes("props-type-unresolved") || (component.needsReview ?? false)
16285
+ });
16286
+ }
16287
+ return {
16288
+ components: validateExtractedComponents2(scoredComponents),
16289
+ warnings
16290
+ };
15815
16291
  }
15816
16292
 
15817
16293
  // packages/experience-design-system-backend-pipeline/dist/src/orchestrators/run-extract-and-compose-session/helpers/emit-pre-classification-exclusions.js
@@ -28089,68 +28565,44 @@ function SettingsScreen({
28089
28565
 
28090
28566
  // packages/experience-design-system-cli/src/tui/settings/configuration/ConfigurationScreen.tsx
28091
28567
  import { useState as useState33 } from "react";
28092
- import { Box as Box48, Text as Text51, useInput as useInput13 } from "ink";
28568
+ import { Box as Box48, Text as Text51, useInput as useInput14 } from "ink";
28093
28569
 
28094
28570
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentsScreen.tsx
28095
- import { useEffect as useEffect23, useState as useState31 } from "react";
28571
+ import { useEffect as useEffect23, useRef as useRef13, useState as useState31 } from "react";
28096
28572
  import { Box as Box46, Text as Text49, useInput as useInput12 } from "ink";
28097
28573
  init_debug_store();
28098
28574
 
28099
- // packages/experience-design-system-cli/src/tui/settings/shared/v1-store.ts
28575
+ // packages/experience-design-system-cli/src/tui/settings/shared/debug-trail.ts
28100
28576
  init_config();
28101
- var readV1Store = readSettings2;
28102
- var writeV1Store = writeSettings2;
28103
-
28104
- // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-store.ts
28105
- var AGENT_LABELS = {
28106
- claude: "Claude Code",
28107
- codex: "OpenAI Codex",
28108
- opencode: "OpenCode",
28109
- cursor: "Cursor",
28110
- copilot: "GitHub Copilot"
28111
- };
28112
- var asString = (value) => typeof value === "string" ? value : "";
28113
- function emptyAgents() {
28114
- return Object.fromEntries(AGENT_NAMES.map((name) => [name, { model: "", binary: "" }]));
28577
+ import { existsSync as existsSync10 } from "node:fs";
28578
+ function diffSnapshots(before, after) {
28579
+ const names = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])];
28580
+ const changed_fields = names.filter((name) => before[name] !== after[name]);
28581
+ return { changed: changed_fields.length > 0, changed_fields };
28582
+ }
28583
+ var DEFAULT_TRAIL_LIMIT = 30;
28584
+ function createTrail(limit = DEFAULT_TRAIL_LIMIT) {
28585
+ const actions = [];
28586
+ return {
28587
+ record(action) {
28588
+ actions.push(action);
28589
+ if (actions.length > limit) actions.shift();
28590
+ },
28591
+ entries: () => [...actions]
28592
+ };
28115
28593
  }
28116
- async function readAgentConfig() {
28117
- const store = await readV1Store();
28118
- const stored = asString(store["agent"]);
28119
- const defaultChosen = isAgentName(stored);
28120
- const defaultAgent = defaultChosen ? stored : DEFAULT_AGENT_NAME;
28121
- const agents = emptyAgents();
28122
- const saved = store["agents"];
28123
- if (saved && typeof saved === "object") {
28124
- for (const name of AGENT_NAMES) {
28125
- const entry = saved[name];
28126
- if (entry && typeof entry === "object") {
28127
- const fields2 = entry;
28128
- agents[name] = { model: asString(fields2["model"]), binary: asString(fields2["binary"]) };
28129
- }
28130
- }
28594
+ var MIN_SECRET_LENGTH = 6;
28595
+ function scrubError(error, secrets) {
28596
+ let message = error instanceof Error ? error.message : String(error);
28597
+ for (const secret of secrets) {
28598
+ if (secret.length < MIN_SECRET_LENGTH) continue;
28599
+ for (const form of /* @__PURE__ */ new Set([secret, encodeURIComponent(secret)])) message = message.split(form).join("[redacted]");
28131
28600
  }
28132
- const legacyModel = asString(store["agentModel"]);
28133
- if (legacyModel && !agents[defaultAgent].model)
28134
- agents[defaultAgent] = { ...agents[defaultAgent], model: legacyModel };
28135
- return { defaultAgent, defaultChosen, agents };
28601
+ return message;
28136
28602
  }
28137
- async function writeAgentConfig(config) {
28138
- const existing = await readV1Store();
28139
- const merged = { ...existing };
28140
- const agents = {};
28141
- for (const name of AGENT_NAMES) {
28142
- const model = config.agents[name].model.trim();
28143
- const binary = config.agents[name].binary.trim();
28144
- if (model || binary) agents[name] = { ...model ? { model } : {}, ...binary ? { binary } : {} };
28145
- }
28146
- const defaultModel = config.defaultChosen ? agents[config.defaultAgent]?.model : void 0;
28147
- if (config.defaultChosen) merged["agent"] = config.defaultAgent;
28148
- else delete merged["agent"];
28149
- if (defaultModel) merged["agentModel"] = defaultModel;
28150
- else delete merged["agentModel"];
28151
- if (Object.keys(agents).length > 0) merged["agents"] = agents;
28152
- else delete merged["agents"];
28153
- await writeV1Store(merged);
28603
+ function describeSettingsFile() {
28604
+ const path5 = configFilePath2();
28605
+ return { path: path5, eds_home_set: Boolean(process.env["EDS_HOME"]), exists: existsSync10(path5) };
28154
28606
  }
28155
28607
 
28156
28608
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-status.ts
@@ -28356,6 +28808,94 @@ async function withBinaryEnv(name, binary, run) {
28356
28808
  }
28357
28809
  }
28358
28810
 
28811
+ // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-debug.ts
28812
+ function agentSnapshot(config) {
28813
+ const snapshot = { default_agent: config.defaultAgent };
28814
+ for (const name of AGENT_NAMES) {
28815
+ snapshot[`${name}.model`] = config.agents[name].model;
28816
+ snapshot[`${name}.binary`] = config.agents[name].binary;
28817
+ }
28818
+ return snapshot;
28819
+ }
28820
+ function environmentFrom(checks, savedBinary, envValue) {
28821
+ const path5 = checks.find((check) => check.step === "path");
28822
+ const version = checks.find((check) => check.step === "version");
28823
+ const overridden = envValue !== void 0 && envValue !== "" && envValue !== savedBinary.trim();
28824
+ return {
28825
+ found: path5?.ok === true,
28826
+ resolved_path: path5?.ok ? path5.detail : "",
28827
+ version: version?.ok ? version.detail : "",
28828
+ env_override: overridden ? envValue : ""
28829
+ };
28830
+ }
28831
+ async function describeAgents(config) {
28832
+ const entries = await Promise.all(
28833
+ AGENT_NAMES.map(async (name) => {
28834
+ const saved = config.agents[name].binary;
28835
+ const checks = await inspectAgent(name, saved);
28836
+ return [name, environmentFrom(checks, saved, process.env[`EDS_AGENT_BINARY_${name.toUpperCase()}`])];
28837
+ })
28838
+ );
28839
+ return Object.fromEntries(entries);
28840
+ }
28841
+
28842
+ // packages/experience-design-system-cli/src/tui/settings/shared/v1-store.ts
28843
+ init_config();
28844
+ var readV1Store = readSettings2;
28845
+ var writeV1Store = writeSettings2;
28846
+
28847
+ // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-store.ts
28848
+ var AGENT_LABELS = {
28849
+ claude: "Claude Code",
28850
+ codex: "OpenAI Codex",
28851
+ opencode: "OpenCode",
28852
+ cursor: "Cursor",
28853
+ copilot: "GitHub Copilot"
28854
+ };
28855
+ var asString = (value) => typeof value === "string" ? value : "";
28856
+ function emptyAgents() {
28857
+ return Object.fromEntries(AGENT_NAMES.map((name) => [name, { model: "", binary: "" }]));
28858
+ }
28859
+ async function readAgentConfig() {
28860
+ const store = await readV1Store();
28861
+ const stored = asString(store["agent"]);
28862
+ const defaultChosen = isAgentName(stored);
28863
+ const defaultAgent = defaultChosen ? stored : DEFAULT_AGENT_NAME;
28864
+ const agents = emptyAgents();
28865
+ const saved = store["agents"];
28866
+ if (saved && typeof saved === "object") {
28867
+ for (const name of AGENT_NAMES) {
28868
+ const entry = saved[name];
28869
+ if (entry && typeof entry === "object") {
28870
+ const fields2 = entry;
28871
+ agents[name] = { model: asString(fields2["model"]), binary: asString(fields2["binary"]) };
28872
+ }
28873
+ }
28874
+ }
28875
+ const legacyModel = asString(store["agentModel"]);
28876
+ if (legacyModel && !agents[defaultAgent].model)
28877
+ agents[defaultAgent] = { ...agents[defaultAgent], model: legacyModel };
28878
+ return { defaultAgent, defaultChosen, agents };
28879
+ }
28880
+ async function writeAgentConfig(config) {
28881
+ const existing = await readV1Store();
28882
+ const merged = { ...existing };
28883
+ const agents = {};
28884
+ for (const name of AGENT_NAMES) {
28885
+ const model = config.agents[name].model.trim();
28886
+ const binary = config.agents[name].binary.trim();
28887
+ if (model || binary) agents[name] = { ...model ? { model } : {}, ...binary ? { binary } : {} };
28888
+ }
28889
+ const defaultModel = config.defaultChosen ? agents[config.defaultAgent]?.model : void 0;
28890
+ if (config.defaultChosen) merged["agent"] = config.defaultAgent;
28891
+ else delete merged["agent"];
28892
+ if (defaultModel) merged["agentModel"] = defaultModel;
28893
+ else delete merged["agentModel"];
28894
+ if (Object.keys(agents).length > 0) merged["agents"] = agents;
28895
+ else delete merged["agents"];
28896
+ await writeV1Store(merged);
28897
+ }
28898
+
28359
28899
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentDetail.tsx
28360
28900
  import { useEffect as useEffect22, useState as useState30 } from "react";
28361
28901
  import { Box as Box45, Text as Text48 } from "ink";
@@ -28557,7 +29097,8 @@ function AgentDetail({
28557
29097
  name,
28558
29098
  config,
28559
29099
  setConfig,
28560
- onBack
29100
+ onBack,
29101
+ record: record3
28561
29102
  }) {
28562
29103
  const settings = config.agents[name];
28563
29104
  const [saving, setSaving] = useState30(false);
@@ -28580,10 +29121,12 @@ function AgentDetail({
28580
29121
  setNotice(null);
28581
29122
  writeAgentConfig(config).then(() => {
28582
29123
  applyBinaryOverrides(config);
29124
+ record3(`saved ${name}`);
28583
29125
  setNotice({ kind: "success", text: `Saved: ${AGENT_LABELS[name]} settings are written to config.json` });
28584
- }).catch(
28585
- (err) => setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` })
28586
- ).finally(() => setSaving(false));
29126
+ }).catch((err) => {
29127
+ record3(`saving ${name} failed`, err);
29128
+ setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` });
29129
+ }).finally(() => setSaving(false));
28587
29130
  };
28588
29131
  const runTest = () => {
28589
29132
  setTesting(true);
@@ -28591,12 +29134,15 @@ function AgentDetail({
28591
29134
  testAgent(name, settings.binary, settings.model).then((result) => {
28592
29135
  setChecks(result);
28593
29136
  const outcome = testOutcome(result);
29137
+ if (!outcome || !outcome.ok)
29138
+ record3(`test of ${name} failed: ${outcome?.text ?? "the agent could not be started"}`);
28594
29139
  setNotice(
28595
29140
  outcome ? { kind: outcome.ok ? "success" : "error", text: outcome.text } : { kind: "error", text: "Test not run: the agent could not be started (see Status)" }
28596
29141
  );
28597
- }).catch(
28598
- (err) => setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` })
28599
- ).finally(() => setTesting(false));
29142
+ }).catch((err) => {
29143
+ record3(`test of ${name} could not run`, err);
29144
+ setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` });
29145
+ }).finally(() => setTesting(false));
28600
29146
  };
28601
29147
  const action = {
28602
29148
  label: "Run test",
@@ -28617,6 +29163,7 @@ function AgentDetail({
28617
29163
  onChange: (key, value) => {
28618
29164
  setConfig({ ...config, agents: { ...config.agents, [name]: { ...settings, [key]: value } } });
28619
29165
  setNotice(null);
29166
+ record3(`edited ${name}.${key}`);
28620
29167
  },
28621
29168
  onSave: save,
28622
29169
  onQuit: onBack,
@@ -28643,6 +29190,12 @@ function AgentsScreen({ onDone }) {
28643
29190
  const [saving, setSaving] = useState31(false);
28644
29191
  const [loadError, setLoadError] = useState31(null);
28645
29192
  const [checks, setChecks] = useState31({});
29193
+ const visit = useRef13({
29194
+ trail: createTrail(),
29195
+ before: void 0,
29196
+ environment: void 0,
29197
+ error: ""
29198
+ }).current;
28646
29199
  const inspectAll = (loaded) => {
28647
29200
  for (const name of AGENT_NAMES) {
28648
29201
  void inspectAgent(name, loaded.agents[name].binary).then(
@@ -28654,14 +29207,38 @@ function AgentsScreen({ onDone }) {
28654
29207
  readAgentConfig().then((loaded) => {
28655
29208
  setConfig(loaded);
28656
29209
  inspectAll(loaded);
29210
+ visit.before = agentSnapshot(loaded);
28657
29211
  startDebugRun({
28658
29212
  flow: "settings/agents",
28659
29213
  step: "01-agents",
28660
29214
  menuOption: "Configuration > Agents",
28661
- inputs: { default_agent: loaded.defaultAgent }
29215
+ inputs: { ...visit.before, default_chosen: loaded.defaultChosen ?? false }
28662
29216
  });
28663
- }).catch((err) => setLoadError(err instanceof Error ? err.message : String(err)));
29217
+ void describeAgents(loaded).then((found) => {
29218
+ visit.environment = found;
29219
+ });
29220
+ }).catch((err) => {
29221
+ visit.error = scrubError(err, []);
29222
+ startDebugRun({ flow: "settings/agents", step: "01-agents", menuOption: "Configuration > Agents", inputs: {} });
29223
+ setLoadError(visit.error);
29224
+ });
28664
29225
  }, []);
29226
+ const endVisit = (current) => {
29227
+ const after = current ? agentSnapshot(current) : void 0;
29228
+ const change = visit.before && after ? diffSnapshots(visit.before, after) : { changed: false, changed_fields: [] };
29229
+ void finishDebugRun({
29230
+ outputs: {
29231
+ ...after ?? {},
29232
+ ...change,
29233
+ actions: visit.trail.entries(),
29234
+ // Missing only when the user left before the probes finished.
29235
+ ...visit.environment ? { agents: visit.environment } : {},
29236
+ ...visit.error ? { error: visit.error } : {}
29237
+ },
29238
+ status: visit.error ? "error" : "success",
29239
+ exitMethod: change.changed ? "saved" : "discarded"
29240
+ });
29241
+ };
28665
29242
  const makeDefault = (name) => {
28666
29243
  if (!config || saving) return;
28667
29244
  if (name === config.defaultAgent) {
@@ -28673,15 +29250,16 @@ function AgentsScreen({ onDone }) {
28673
29250
  writeAgentConfig({ ...config, defaultAgent: name, defaultChosen: true }).then(() => {
28674
29251
  setConfig((current) => current ? { ...current, defaultAgent: name, defaultChosen: true } : current);
28675
29252
  setNotice({ kind: "success", text: `Default agent is now ${AGENT_LABELS[name]}` });
28676
- }).catch(
28677
- (err) => setNotice({
28678
- kind: "error",
28679
- text: `Default agent not changed: ${err instanceof Error ? err.message : String(err)}`
28680
- })
28681
- ).finally(() => setSaving(false));
29253
+ visit.trail.record(`made ${name} the default`);
29254
+ }).catch((err) => {
29255
+ visit.error = scrubError(err, []);
29256
+ visit.trail.record(`making ${name} the default failed`);
29257
+ setNotice({ kind: "error", text: `Default agent not changed: ${visit.error}` });
29258
+ }).finally(() => setSaving(false));
28682
29259
  };
28683
29260
  useInput12((input, key) => {
28684
29261
  if (loadError && (key.escape || input === "q")) {
29262
+ endVisit(null);
28685
29263
  onDone();
28686
29264
  return;
28687
29265
  }
@@ -28706,7 +29284,7 @@ function AgentsScreen({ onDone }) {
28706
29284
  return;
28707
29285
  }
28708
29286
  if (key.escape || input === "q") {
28709
- void finishDebugRun({ outputs: { default_agent: config.defaultAgent }, status: "success", exitMethod: "saved" });
29287
+ endVisit(config);
28710
29288
  onDone();
28711
29289
  }
28712
29290
  });
@@ -28731,6 +29309,10 @@ function AgentsScreen({ onDone }) {
28731
29309
  name: open,
28732
29310
  config,
28733
29311
  setConfig,
29312
+ record: (action, error) => {
29313
+ visit.trail.record(action);
29314
+ if (error !== void 0) visit.error = scrubError(error, []);
29315
+ },
28734
29316
  onBack: () => {
28735
29317
  setOpen(null);
28736
29318
  void readAgentConfig().then((saved) => {
@@ -28797,8 +29379,8 @@ function AgentsScreen({ onDone }) {
28797
29379
  }
28798
29380
 
28799
29381
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
28800
- import { useEffect as useEffect24, useState as useState32 } from "react";
28801
- import { Box as Box47, Text as Text50 } from "ink";
29382
+ import { useEffect as useEffect24, useRef as useRef14, useState as useState32 } from "react";
29383
+ import { Box as Box47, Text as Text50, useInput as useInput13 } from "ink";
28802
29384
  init_debug_store();
28803
29385
 
28804
29386
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/project-store.ts
@@ -28827,7 +29409,7 @@ async function writeCredentials(config) {
28827
29409
  }
28828
29410
 
28829
29411
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
28830
- import { jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
29412
+ import { Fragment as Fragment19, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
28831
29413
  var FIELDS2 = [
28832
29414
  { key: "spaceId", label: "Space ID" },
28833
29415
  { key: "environmentId", label: "Environment ID" },
@@ -28848,45 +29430,94 @@ function debugFields(config) {
28848
29430
  contentful_base_dir: config?.contentfulBaseDir ?? ""
28849
29431
  };
28850
29432
  }
29433
+ var snapshotOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
28851
29434
  var valuesOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
28852
29435
  function ProjectScreen({ onDone }) {
28853
29436
  const [config, setConfig] = useState32(null);
28854
29437
  const [saving, setSaving] = useState32(false);
28855
29438
  const [notice, setNotice] = useState32(null);
28856
- const [lastSaved, setLastSaved] = useState32(null);
29439
+ const [loadError, setLoadError] = useState32(null);
29440
+ const visit = useRef14({
29441
+ trail: createTrail(),
29442
+ before: void 0,
29443
+ persisted: void 0,
29444
+ error: ""
29445
+ }).current;
29446
+ const scrub = (err) => scrubError(err, [config?.cmaToken ?? ""]);
28857
29447
  useEffect24(() => {
28858
- void readCredentials().then((cfg) => {
29448
+ readCredentials().then((cfg) => {
28859
29449
  setConfig(cfg);
29450
+ visit.before = snapshotOf(cfg);
29451
+ visit.persisted = visit.before;
29452
+ startDebugRun({
29453
+ flow: "settings/contentful-configuration",
29454
+ step: "01-configuration",
29455
+ menuOption: "Configuration > Project",
29456
+ inputs: { ...debugFields(cfg), settings_file: describeSettingsFile() }
29457
+ });
29458
+ }).catch((err) => {
29459
+ visit.error = scrubError(err, []);
28860
29460
  startDebugRun({
28861
29461
  flow: "settings/contentful-configuration",
28862
29462
  step: "01-configuration",
28863
29463
  menuOption: "Configuration > Project",
28864
- inputs: debugFields(cfg)
29464
+ inputs: { settings_file: describeSettingsFile() }
28865
29465
  });
29466
+ setLoadError(visit.error);
28866
29467
  });
28867
29468
  }, []);
29469
+ const endVisit = (current) => {
29470
+ const onScreen = current ? snapshotOf(current) : void 0;
29471
+ const change = visit.before && visit.persisted ? diffSnapshots(visit.before, visit.persisted) : { changed: false, changed_fields: [] };
29472
+ const unsaved = visit.persisted && onScreen ? diffSnapshots(visit.persisted, onScreen) : { changed: false, changed_fields: [] };
29473
+ void finishDebugRun({
29474
+ outputs: {
29475
+ ...debugFields(current),
29476
+ ...change,
29477
+ unsaved_edits: unsaved.changed_fields,
29478
+ actions: visit.trail.entries(),
29479
+ ...visit.error ? { error: visit.error } : {}
29480
+ },
29481
+ status: visit.error ? "error" : "success",
29482
+ exitMethod: change.changed ? "saved" : "discarded"
29483
+ });
29484
+ };
29485
+ useInput13((input, key) => {
29486
+ if (!config && loadError && (key.escape || input === "q")) {
29487
+ endVisit(null);
29488
+ onDone();
29489
+ }
29490
+ });
28868
29491
  if (!config) {
28869
29492
  return /* @__PURE__ */ jsxs48(Box47, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
28870
29493
  /* @__PURE__ */ jsx59(Text50, { bold: true, children: "Project" }),
28871
29494
  /* @__PURE__ */ jsx59(Text50, { children: " " }),
28872
- /* @__PURE__ */ jsx59(Text50, { color: PALETTE.accent, children: "Loading..." })
29495
+ loadError ? /* @__PURE__ */ jsxs48(Fragment19, { children: [
29496
+ /* @__PURE__ */ jsxs48(Text50, { color: PALETTE.error, children: [
29497
+ "\u2717 Could not read the settings: ",
29498
+ loadError
29499
+ ] }),
29500
+ /* @__PURE__ */ jsx59(Text50, { children: " " }),
29501
+ /* @__PURE__ */ jsx59(ControlHints, { hints: [{ keys: "Esc/q", label: "back" }] })
29502
+ ] }) : /* @__PURE__ */ jsx59(Text50, { color: PALETTE.accent, children: "Loading..." })
28873
29503
  ] });
28874
29504
  }
28875
29505
  const save = () => {
28876
29506
  setSaving(true);
28877
29507
  setNotice(null);
28878
29508
  writeCredentials(config).then(() => {
28879
- setLastSaved(config);
29509
+ visit.persisted = snapshotOf(config);
29510
+ visit.trail.record("saved");
28880
29511
  if (config.contentfulBaseDir) void startDebugSessionIn(config.contentfulBaseDir, { replace: true });
28881
29512
  setNotice({ kind: "success", text: "Project settings saved" });
28882
29513
  }).catch((err) => {
29514
+ visit.error = scrub(err);
29515
+ visit.trail.record("save failed");
28883
29516
  setNotice({ kind: "error", text: err instanceof Error ? err.message : "Failed to save" });
28884
29517
  }).finally(() => setSaving(false));
28885
29518
  };
28886
29519
  const quit = () => {
28887
- void finishDebugRun(
28888
- lastSaved ? { outputs: debugFields(lastSaved), status: "success", exitMethod: "saved" } : { outputs: {}, status: "success", exitMethod: "discarded" }
28889
- );
29520
+ endVisit(config);
28890
29521
  onDone();
28891
29522
  };
28892
29523
  return /* @__PURE__ */ jsx59(
@@ -28899,6 +29530,7 @@ function ProjectScreen({ onDone }) {
28899
29530
  onChange: (key, value) => {
28900
29531
  setConfig({ ...config, [key]: value });
28901
29532
  setNotice(null);
29533
+ visit.trail.record(`edited ${key}`);
28902
29534
  },
28903
29535
  onSave: save,
28904
29536
  onQuit: quit,
@@ -28922,7 +29554,7 @@ function ConfigurationScreen({ onDone }) {
28922
29554
  const [focus, setFocus] = useState33(0);
28923
29555
  const [open, setOpen] = useState33(null);
28924
29556
  const close = () => setOpen(null);
28925
- useInput13((input, key) => {
29557
+ useInput14((input, key) => {
28926
29558
  if (open) return;
28927
29559
  if (key.upArrow) {
28928
29560
  setFocus((i) => (i - 1 + SECTIONS.length) % SECTIONS.length);
@@ -28964,7 +29596,7 @@ function ConfigurationScreen({ onDone }) {
28964
29596
 
28965
29597
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
28966
29598
  import { useEffect as useEffect25, useState as useState34 } from "react";
28967
- import { Box as Box49, Text as Text52, useInput as useInput14 } from "ink";
29599
+ import { Box as Box49, Text as Text52, useInput as useInput15 } from "ink";
28968
29600
 
28969
29601
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/analytics-store.ts
28970
29602
  async function readAnalyticsSetting() {
@@ -28983,7 +29615,7 @@ async function writeAnalyticsSetting(setting) {
28983
29615
 
28984
29616
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
28985
29617
  init_debug_store();
28986
- import { Fragment as Fragment19, jsx as jsx61, jsxs as jsxs50 } from "react/jsx-runtime";
29618
+ import { Fragment as Fragment20, jsx as jsx61, jsxs as jsxs50 } from "react/jsx-runtime";
28987
29619
  function OptInAnalyticsScreen({ onDone }) {
28988
29620
  const [loading, setLoading] = useState34(true);
28989
29621
  const [enabled, setEnabled] = useState34(true);
@@ -28999,7 +29631,7 @@ function OptInAnalyticsScreen({ onDone }) {
28999
29631
  });
29000
29632
  });
29001
29633
  }, []);
29002
- useInput14((input, key) => {
29634
+ useInput15((input, key) => {
29003
29635
  if (loading) return;
29004
29636
  if (key.return || input === " ") {
29005
29637
  setEnabled((current) => {
@@ -29017,7 +29649,7 @@ function OptInAnalyticsScreen({ onDone }) {
29017
29649
  return /* @__PURE__ */ jsxs50(Box49, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29018
29650
  /* @__PURE__ */ jsx61(Text52, { bold: true, children: "Settings \u203A Opt-in Analytics" }),
29019
29651
  /* @__PURE__ */ jsx61(Text52, { children: " " }),
29020
- loading ? /* @__PURE__ */ jsx61(Text52, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs50(Fragment19, { children: [
29652
+ loading ? /* @__PURE__ */ jsx61(Text52, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs50(Fragment20, { children: [
29021
29653
  /* @__PURE__ */ jsxs50(Text52, { children: [
29022
29654
  "Share anonymous usage data:",
29023
29655
  " ",
@@ -29040,89 +29672,16 @@ function OptInAnalyticsScreen({ onDone }) {
29040
29672
  ] });
29041
29673
  }
29042
29674
 
29043
- // packages/experience-design-system-cli/src/tui/upgrade/PageContainer.tsx
29044
- import { useEffect as useEffect26, useState as useState35 } from "react";
29045
- import { Box as Box50, Text as Text53, useInput as useInput15 } from "ink";
29046
- init_version();
29047
- init_debug_store();
29048
- import { jsx as jsx62, jsxs as jsxs51 } from "react/jsx-runtime";
29049
- var UPGRADE_COMMANDS = ["git pull", "pnpm install", "pnpm build"];
29050
- function UpgradeScreen({ onDone }) {
29051
- const [upgradeCheck, setUpgradeCheck] = useState35();
29052
- useEffect26(() => {
29053
- startDebugRun({
29054
- flow: "upgrade",
29055
- step: "01-upgrade",
29056
- menuOption: "Upgrade",
29057
- inputs: { currentVersion: readPackageVersion() }
29058
- });
29059
- checkForUpgrade().then(setUpgradeCheck).catch(() => setUpgradeCheck({ status: "error" }));
29060
- }, []);
29061
- useInput15((_input, key) => {
29062
- if (key.return) {
29063
- void finishDebugRun({
29064
- outputs: upgradeCheck ?? {},
29065
- status: upgradeCheck?.status === "error" ? "error" : "success",
29066
- exitMethod: "saved"
29067
- });
29068
- onDone();
29069
- return;
29070
- }
29071
- });
29072
- return /* @__PURE__ */ jsxs51(Box50, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29073
- /* @__PURE__ */ jsx62(Text53, { bold: true, children: "Upgrade" }),
29074
- /* @__PURE__ */ jsx62(Text53, { children: " " }),
29075
- renderBody(upgradeCheck),
29076
- /* @__PURE__ */ jsx62(Text53, { children: " " }),
29077
- /* @__PURE__ */ jsxs51(Text53, { bold: true, color: PALETTE.accent, children: [
29078
- FOCUS_MARKER,
29079
- " Go Back"
29080
- ] })
29081
- ] });
29082
- }
29083
- function renderBody(upgradeCheck) {
29084
- if (!upgradeCheck) {
29085
- return /* @__PURE__ */ jsx62(Text53, { color: PALETTE.muted, children: "Checking for updates\u2026" });
29086
- }
29087
- if (upgradeCheck.status === "error") {
29088
- return /* @__PURE__ */ jsx62(Text53, { color: PALETTE.warning, children: "Couldn't check for updates \u2014 check your network connection and try again." });
29089
- }
29090
- if (upgradeCheck.status === "up-to-date") {
29091
- return /* @__PURE__ */ jsxs51(Text53, { color: PALETTE.success, children: [
29092
- "You're already on the latest version (v",
29093
- upgradeCheck.current,
29094
- ")."
29095
- ] });
29096
- }
29097
- return /* @__PURE__ */ jsxs51(Box50, { flexDirection: "column", children: [
29098
- /* @__PURE__ */ jsxs51(Text53, { color: PALETTE.muted, children: [
29099
- /* @__PURE__ */ jsxs51(Text53, { bold: true, color: PALETTE.success, children: [
29100
- "v",
29101
- upgradeCheck.latest
29102
- ] }),
29103
- " ",
29104
- "is available (you're on ",
29105
- /* @__PURE__ */ jsxs51(Text53, { bold: true, children: [
29106
- "v",
29107
- upgradeCheck.current
29108
- ] }),
29109
- "). Exit this program, cd into the root of your local clone of contentful/experience-design-system-sdk-public, then run:"
29110
- ] }),
29111
- /* @__PURE__ */ jsx62(Text53, { children: " " }),
29112
- /* @__PURE__ */ jsx62(Box50, { flexDirection: "column", borderStyle: "round", borderColor: PALETTE.border, paddingX: 1, children: UPGRADE_COMMANDS.map((cmd) => /* @__PURE__ */ jsx62(Text53, { bold: true, children: cmd }, cmd)) })
29113
- ] });
29114
- }
29115
-
29116
29675
  // packages/experience-design-system-cli/src/tui/settings/debug-mode/screen.tsx
29117
- import { useEffect as useEffect27, useState as useState36 } from "react";
29118
- import { Box as Box51, Text as Text54, useInput as useInput16 } from "ink";
29676
+ import { useEffect as useEffect26, useState as useState35 } from "react";
29677
+ import { Box as Box50, Text as Text53, useInput as useInput16 } from "ink";
29119
29678
  init_debug_mode_store();
29120
29679
  init_debug_store();
29121
- import { jsx as jsx63, jsxs as jsxs52 } from "react/jsx-runtime";
29680
+ import { jsx as jsx62, jsxs as jsxs51 } from "react/jsx-runtime";
29122
29681
  function DebugModeScreen({ onDone }) {
29123
- const [loading, setLoading] = useState36(true);
29124
- const [enabled, setEnabled] = useState36(true);
29125
- useEffect27(() => {
29682
+ const [loading, setLoading] = useState35(true);
29683
+ const [enabled, setEnabled] = useState35(true);
29684
+ useEffect26(() => {
29126
29685
  readDebugModeSetting().then((setting) => {
29127
29686
  setEnabled(setting.enabled);
29128
29687
  setLoading(false);
@@ -29149,16 +29708,16 @@ function DebugModeScreen({ onDone }) {
29149
29708
  onDone();
29150
29709
  }
29151
29710
  });
29152
- return /* @__PURE__ */ jsxs52(Box51, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29153
- /* @__PURE__ */ jsx63(Text54, { bold: true, children: "Settings \u203A Debug Mode" }),
29154
- /* @__PURE__ */ jsx63(Text54, { children: " " }),
29155
- loading ? /* @__PURE__ */ jsx63(Text54, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs52(Text54, { children: [
29711
+ return /* @__PURE__ */ jsxs51(Box50, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29712
+ /* @__PURE__ */ jsx62(Text53, { bold: true, children: "Settings \u203A Debug Mode" }),
29713
+ /* @__PURE__ */ jsx62(Text53, { children: " " }),
29714
+ loading ? /* @__PURE__ */ jsx62(Text53, { color: PALETTE.muted, children: "Loading\u2026" }) : /* @__PURE__ */ jsxs51(Text53, { children: [
29156
29715
  "Debug logging:",
29157
29716
  " ",
29158
- /* @__PURE__ */ jsx63(Text54, { bold: true, color: enabled ? PALETTE.success : PALETTE.muted, children: enabled ? "On" : "Off" })
29717
+ /* @__PURE__ */ jsx62(Text53, { bold: true, color: enabled ? PALETTE.success : PALETTE.muted, children: enabled ? "On" : "Off" })
29159
29718
  ] }),
29160
- /* @__PURE__ */ jsx63(Text54, { children: " " }),
29161
- /* @__PURE__ */ jsx63(
29719
+ /* @__PURE__ */ jsx62(Text53, { children: " " }),
29720
+ /* @__PURE__ */ jsx62(
29162
29721
  ControlHints,
29163
29722
  {
29164
29723
  hints: [
@@ -29171,18 +29730,18 @@ function DebugModeScreen({ onDone }) {
29171
29730
  }
29172
29731
 
29173
29732
  // packages/experience-design-system-cli/app.tsx
29174
- import { Fragment as Fragment20, jsx as jsx64 } from "react/jsx-runtime";
29733
+ import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
29175
29734
  function App({ importOptions, inkClear }) {
29176
- const [screen, setScreen] = useState37("start");
29177
- const [importExitCode, setImportExitCode] = useState37();
29735
+ const [screen, setScreen] = useState36("start");
29736
+ const [importExitCode, setImportExitCode] = useState36();
29178
29737
  const showImportResult = importExitCode !== void 0;
29179
- const [projectPath, setProjectPath] = useState37();
29180
- const [tokensPath, setTokensPath] = useState37();
29181
- const [scannedFiles, setScannedFiles] = useState37();
29182
- const [pendingLaunch, setPendingLaunch] = useState37();
29183
- const [extractionRun, setExtractionRun] = useState37();
29738
+ const [projectPath, setProjectPath] = useState36();
29739
+ const [tokensPath, setTokensPath] = useState36();
29740
+ const [scannedFiles, setScannedFiles] = useState36();
29741
+ const [pendingLaunch, setPendingLaunch] = useState36();
29742
+ const [extractionRun, setExtractionRun] = useState36();
29184
29743
  const extraction = useExtraction(extractionRun?.input);
29185
- const [scopeRun, setScopeRun] = useState37();
29744
+ const [scopeRun, setScopeRun] = useState36();
29186
29745
  const scopeFlow = useScopeGateFlow(
29187
29746
  scopeRun && {
29188
29747
  sessionId: scopeRun.extraction.sessionId,
@@ -29197,12 +29756,12 @@ function App({ importOptions, inkClear }) {
29197
29756
  }
29198
29757
  }
29199
29758
  );
29200
- const [generationInput, setGenerationInput] = useState37();
29759
+ const [generationInput, setGenerationInput] = useState36();
29201
29760
  const generation = useGeneration(generationInput);
29202
- const [flowStart, setFlowStart] = useState37();
29761
+ const [flowStart, setFlowStart] = useState36();
29203
29762
  const flow = useFlow(flowStart, { beforeStepChange: inkClear });
29204
- const [importDefaults, setImportDefaults] = useState37();
29205
- useEffect28(() => {
29763
+ const [importDefaults, setImportDefaults] = useState36();
29764
+ useEffect27(() => {
29206
29765
  if (screen !== "import") {
29207
29766
  setImportDefaults(void 0);
29208
29767
  return;
@@ -29262,12 +29821,12 @@ function App({ importOptions, inkClear }) {
29262
29821
  }
29263
29822
  });
29264
29823
  };
29265
- useEffect28(() => {
29824
+ useEffect27(() => {
29266
29825
  if (!extractionRun || extraction?.status !== "done" || !extraction.result.ok) return;
29267
29826
  setExtractionRun(void 0);
29268
29827
  setScopeRun({ ...extractionRun, extraction: extraction.result.extraction });
29269
29828
  }, [extraction, extractionRun]);
29270
- useEffect28(() => {
29829
+ useEffect27(() => {
29271
29830
  if (!scopeRun || scopeFlow?.status !== "confirmed" || generationInput) return;
29272
29831
  const { extraction: produced, input, generatePromptPath } = scopeRun;
29273
29832
  setGenerationInput({
@@ -29285,7 +29844,7 @@ function App({ importOptions, inkClear }) {
29285
29844
  ...generatePromptPath ? { generatePromptPath } : {}
29286
29845
  });
29287
29846
  }, [scopeFlow, scopeRun, generationInput]);
29288
- useEffect28(() => {
29847
+ useEffect27(() => {
29289
29848
  if (!scopeRun || generation?.status !== "done" || !generation.result.ok) return;
29290
29849
  const { credentialsSkipped, tokenStrategy, extraction: produced, input, generatePromptPath } = scopeRun;
29291
29850
  inkClear();
@@ -29328,9 +29887,9 @@ function App({ importOptions, inkClear }) {
29328
29887
  };
29329
29888
  switch (screen) {
29330
29889
  case "import":
29331
- if (showImportResult) return /* @__PURE__ */ jsx64(ImportScreen, { exitCode: importExitCode, onDone: finishImportResult });
29890
+ if (showImportResult) return /* @__PURE__ */ jsx63(ImportScreen, { exitCode: importExitCode, onDone: finishImportResult });
29332
29891
  if (flow && flowStart) {
29333
- return /* @__PURE__ */ jsx64(
29892
+ return /* @__PURE__ */ jsx63(
29334
29893
  FlowScreen,
29335
29894
  {
29336
29895
  state: flow.state,
@@ -29340,12 +29899,12 @@ function App({ importOptions, inkClear }) {
29340
29899
  }
29341
29900
  );
29342
29901
  }
29343
- if (importDefaults === void 0) return /* @__PURE__ */ jsx64(Fragment20, {});
29902
+ if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment21, {});
29344
29903
  if (projectPath === void 0) {
29345
- return /* @__PURE__ */ jsx64(WelcomeScreen, { onContinue: setProjectPath, onQuit: leaveImport, initialPath: importDefaults.componentDir });
29904
+ return /* @__PURE__ */ jsx63(WelcomeScreen, { onContinue: setProjectPath, onQuit: leaveImport, initialPath: importDefaults.componentDir });
29346
29905
  }
29347
29906
  if (tokensPath === void 0) {
29348
- return /* @__PURE__ */ jsx64(
29907
+ return /* @__PURE__ */ jsx63(
29349
29908
  TokenInputScreen,
29350
29909
  {
29351
29910
  initialPath: importDefaults.tokenFile,
@@ -29355,7 +29914,7 @@ function App({ importOptions, inkClear }) {
29355
29914
  );
29356
29915
  }
29357
29916
  if (scannedFiles === void 0) {
29358
- return /* @__PURE__ */ jsx64(
29917
+ return /* @__PURE__ */ jsx63(
29359
29918
  PathValidationScreen,
29360
29919
  {
29361
29920
  projectPath,
@@ -29367,10 +29926,10 @@ function App({ importOptions, inkClear }) {
29367
29926
  }
29368
29927
  if (extractionRun) {
29369
29928
  if (extraction?.status === "done" && !extraction.result.ok) {
29370
- return /* @__PURE__ */ jsx64(ErrorStep, { stepName: extraction.result.step, message: extraction.result.message, onExit: leaveImport });
29929
+ return /* @__PURE__ */ jsx63(ErrorStep, { stepName: extraction.result.step, message: extraction.result.message, onExit: leaveImport });
29371
29930
  }
29372
29931
  if (extraction?.status === "running") {
29373
- return /* @__PURE__ */ jsx64(
29932
+ return /* @__PURE__ */ jsx63(
29374
29933
  ExtractionScreen,
29375
29934
  {
29376
29935
  progress: extraction.progress,
@@ -29379,14 +29938,14 @@ function App({ importOptions, inkClear }) {
29379
29938
  }
29380
29939
  );
29381
29940
  }
29382
- return /* @__PURE__ */ jsx64(Fragment20, {});
29941
+ return /* @__PURE__ */ jsx63(Fragment21, {});
29383
29942
  }
29384
29943
  if (scopeRun && scopeFlow) {
29385
29944
  if (scopeFlow.status === "failed") {
29386
- return /* @__PURE__ */ jsx64(ErrorStep, { stepName: "scope gate", message: scopeFlow.message, onExit: leaveImport });
29945
+ return /* @__PURE__ */ jsx63(ErrorStep, { stepName: "scope gate", message: scopeFlow.message, onExit: leaveImport });
29387
29946
  }
29388
29947
  if (scopeFlow.status === "deciding") {
29389
- return /* @__PURE__ */ jsx64(
29948
+ return /* @__PURE__ */ jsx63(
29390
29949
  ScopeGateScreen,
29391
29950
  {
29392
29951
  components: scopeFlow.load.components,
@@ -29396,7 +29955,7 @@ function App({ importOptions, inkClear }) {
29396
29955
  );
29397
29956
  }
29398
29957
  if (generation?.status === "done" && !generation.result.ok) {
29399
- return /* @__PURE__ */ jsx64(ErrorStep, { stepName: generation.result.step, message: generation.result.message, onExit: leaveImport });
29958
+ return /* @__PURE__ */ jsx63(ErrorStep, { stepName: generation.result.step, message: generation.result.message, onExit: leaveImport });
29400
29959
  }
29401
29960
  if (generation?.status === "running") {
29402
29961
  const hasRawTokens = scopeRun.input.rawTokensPath !== "";
@@ -29404,7 +29963,7 @@ function App({ importOptions, inkClear }) {
29404
29963
  hasRawTokens,
29405
29964
  mapTokensEligible: generation.progress.mapTokensEligible
29406
29965
  });
29407
- return /* @__PURE__ */ jsx64(
29966
+ return /* @__PURE__ */ jsx63(
29408
29967
  GenerationScreen,
29409
29968
  {
29410
29969
  progress: generation.progress,
@@ -29416,10 +29975,10 @@ function App({ importOptions, inkClear }) {
29416
29975
  }
29417
29976
  );
29418
29977
  }
29419
- return /* @__PURE__ */ jsx64(Fragment20, {});
29978
+ return /* @__PURE__ */ jsx63(Fragment21, {});
29420
29979
  }
29421
29980
  if (pendingLaunch?.cached) {
29422
- return /* @__PURE__ */ jsx64(
29981
+ return /* @__PURE__ */ jsx63(
29423
29982
  TokenReuseScreen,
29424
29983
  {
29425
29984
  tokensPath: pendingLaunch.cached.tokensPath,
@@ -29430,7 +29989,7 @@ function App({ importOptions, inkClear }) {
29430
29989
  }
29431
29990
  );
29432
29991
  }
29433
- return /* @__PURE__ */ jsx64(
29992
+ return /* @__PURE__ */ jsx63(
29434
29993
  CredentialsScreen,
29435
29994
  {
29436
29995
  initial: importDefaults.credentials,
@@ -29439,19 +29998,17 @@ function App({ importOptions, inkClear }) {
29439
29998
  }
29440
29999
  );
29441
30000
  case "help":
29442
- return /* @__PURE__ */ jsx64(HelpScreen, { onDone: goToStart });
29443
- case "upgrade":
29444
- return /* @__PURE__ */ jsx64(UpgradeScreen, { onDone: goToStart });
30001
+ return /* @__PURE__ */ jsx63(HelpScreen, { onDone: goToStart });
29445
30002
  case "settings":
29446
- return /* @__PURE__ */ jsx64(SettingsScreen, { onNavigate: setScreen, onBack: goToStart });
30003
+ return /* @__PURE__ */ jsx63(SettingsScreen, { onNavigate: setScreen, onBack: goToStart });
29447
30004
  case "settings-configuration":
29448
- return /* @__PURE__ */ jsx64(ConfigurationScreen, { onDone: goToSettings });
30005
+ return /* @__PURE__ */ jsx63(ConfigurationScreen, { onDone: goToSettings });
29449
30006
  case "settings-opt-in-analytics":
29450
- return /* @__PURE__ */ jsx64(OptInAnalyticsScreen, { onDone: goToSettings });
30007
+ return /* @__PURE__ */ jsx63(OptInAnalyticsScreen, { onDone: goToSettings });
29451
30008
  case "settings-debug-mode":
29452
- return /* @__PURE__ */ jsx64(DebugModeScreen, { onDone: goToSettings });
30009
+ return /* @__PURE__ */ jsx63(DebugModeScreen, { onDone: goToSettings });
29453
30010
  case "start":
29454
- return /* @__PURE__ */ jsx64(HomeScreen, { onNavigate: setScreen });
30011
+ return /* @__PURE__ */ jsx63(HomeScreen, { onNavigate: setScreen });
29455
30012
  }
29456
30013
  }
29457
30014
  async function runApp(renderApp, importOptions) {
@@ -29459,7 +30016,7 @@ async function runApp(renderApp, importOptions) {
29459
30016
  instance.clear?.();
29460
30017
  process.stdout.write("\x1B[H\x1B[0J");
29461
30018
  };
29462
- const instance = renderApp(/* @__PURE__ */ jsx64(App, { importOptions, inkClear }));
30019
+ const instance = renderApp(/* @__PURE__ */ jsx63(App, { importOptions, inkClear }));
29463
30020
  await instance.waitUntilExit();
29464
30021
  }
29465
30022
  export {