@contentful/experience-design-system-cli 2.37.1-dev-build-0b7e3e1f.0 → 2.37.1-dev-build-414ce827.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
@@ -9648,8 +9648,8 @@ function extractAccessorProperties(classDecl, applyRuntimeFieldDenylist = false)
9648
9648
  accessors.set(name, entry);
9649
9649
  }
9650
9650
  for (const [name, { getter, setter }] of accessors) {
9651
- const hasPropertyDecorator = getter?.getDecorators().some((decorator) => decorator.getName() === "property") || setter?.getDecorators().some((decorator) => decorator.getName() === "property");
9652
- 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))
9653
9653
  continue;
9654
9654
  const type = getter?.getReturnTypeNode()?.getText() ?? setter?.getParameters()[0]?.getTypeNode()?.getText() ?? "any";
9655
9655
  const accessorNode = getter ?? setter;
@@ -12919,9 +12919,9 @@ function generateUlid(now = Date.now()) {
12919
12919
  tsBytes[i] = n & 255;
12920
12920
  n = Math.floor(n / 256);
12921
12921
  }
12922
- const ts3 = encodeBase32(tsBytes, 10);
12922
+ const ts6 = encodeBase32(tsBytes, 10);
12923
12923
  const rand = encodeBase32(randomBytes2(10), 16);
12924
- return (ts3 + rand).toUpperCase();
12924
+ return (ts6 + rand).toUpperCase();
12925
12925
  }
12926
12926
 
12927
12927
  // packages/experience-design-system-backend-pipeline/dist/src/persistence/src/runs/repositories/read-runs-file.js
@@ -15335,7 +15335,610 @@ async function runMapTokens(request) {
15335
15335
  import { mkdir as mkdir4 } from "node:fs/promises";
15336
15336
  import { relative as relative2 } from "node:path";
15337
15337
 
15338
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/quality/non-authorable-filter.js
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";
15340
+
15341
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/tsx-shared.js
15342
+ import { Project as Project8 } from "ts-morph";
15343
+
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";
15346
+
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";
15349
+
15350
+ // packages/experience-design-system-extraction/dist/src/extract/framework-adapters/shared/helpers/resolve-tsx-tsconfig-path.js
15351
+ import ts3 from "typescript";
15352
+
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
+ ];
15398
+ }
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"]
15441
+ }
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
15462
+ }
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
15498
+ }
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");
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);
15676
+ return {
15677
+ confidence: mapIssueCountToConfidence(count),
15678
+ reasons
15679
+ };
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";
15696
+
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
15759
+ function isStringLiteralUnion(type) {
15760
+ return type.includes("|") && type.includes("'");
15761
+ }
15762
+ function isSimpleType(type) {
15763
+ const t = type.trim();
15764
+ if (t === "string" || t === "boolean" || t === "number")
15765
+ return true;
15766
+ return isStringLiteralUnion(t);
15767
+ }
15768
+ function isBooleanType(type) {
15769
+ return type.trim() === "boolean";
15770
+ }
15771
+ function isStringType(type) {
15772
+ return type.trim() === "string";
15773
+ }
15774
+ function isNumberType(type) {
15775
+ return type.trim() === "number";
15776
+ }
15777
+ function isComplexType(type) {
15778
+ return !isSimpleType(type);
15779
+ }
15780
+
15781
+ // packages/experience-design-system-extraction/dist/src/extract/services/classification/helpers/detect-dom-props.js
15782
+ var DOM_PASS_THROUGH_PROPS = /* @__PURE__ */ new Set([
15783
+ // Bare HTML / framework styling pass-through
15784
+ "className",
15785
+ "class",
15786
+ "classes",
15787
+ "classNames",
15788
+ "rootClassName",
15789
+ "prefixCls",
15790
+ "style",
15791
+ "styles",
15792
+ // Bare HTML attributes
15793
+ "id",
15794
+ "role",
15795
+ "tabIndex",
15796
+ "tabindex",
15797
+ "htmlFor",
15798
+ "for",
15799
+ "slot",
15800
+ "is",
15801
+ "lang",
15802
+ "dir",
15803
+ "hidden",
15804
+ "draggable",
15805
+ "spellCheck",
15806
+ "spellcheck",
15807
+ "contentEditable",
15808
+ "contenteditable",
15809
+ "inputMode",
15810
+ "inputmode",
15811
+ "autoComplete",
15812
+ "autocomplete",
15813
+ "autoFocus",
15814
+ "autofocus",
15815
+ "translate",
15816
+ "part",
15817
+ "exportparts",
15818
+ "aria",
15819
+ // Framework theming / pass-through escape hatches — dev-facing, never marketer-configurable
15820
+ "dt",
15821
+ "pt",
15822
+ "ptOptions",
15823
+ "unstyled",
15824
+ "sx",
15825
+ // Polymorphic component props — change rendered HTML/component, not marketer-visible behavior
15826
+ "as",
15827
+ "element",
15828
+ "component",
15829
+ // QA / vendor test attributes
15830
+ "dataQa",
15831
+ "data-qa",
15832
+ // Vue v-model internals — framework wiring, never marketer-configurable
15833
+ "modelValue",
15834
+ "modelModifiers"
15835
+ ]);
15836
+ function isDomPassThroughProp(name) {
15837
+ if (DOM_PASS_THROUGH_PROPS.has(name))
15838
+ return true;
15839
+ if (/^aria[-A-Z]/.test(name))
15840
+ return true;
15841
+ return name.startsWith("data-");
15842
+ }
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
15339
15942
  var HANDLER_TYPE_PATTERN = /=>|EventHandler|Dispatch<|SetStateAction/;
15340
15943
  var REF_TYPE_PATTERN = /Ref<|RefObject<|MutableRefObject/;
15341
15944
  function isHandlerOrRefProp(prop) {
@@ -15388,15 +15991,10 @@ function hasNoAuthoringSurface(component) {
15388
15991
  return { skip: false };
15389
15992
  }
15390
15993
 
15391
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/evidence/source-inspection.js
15994
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/inspection/inspect-component-source.js
15392
15995
  import { readFile as readFile15 } from "node:fs/promises";
15393
15996
 
15394
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/constants/review-reasons.js
15395
- var HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON = "data-fetch-wrapper";
15396
- var POSSIBLE_DATA_FETCH_WRAPPER_REASON = "possible-data-fetch-wrapper";
15397
- var ZERO_SURFACE_RENDERED_UI_REASON = "zero-surface:rendered-ui";
15398
-
15399
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/framework-adapters/shared/parse-imported-names.js
15997
+ // packages/experience-design-system-extraction/dist/src/extract/helpers/evidence/parse-source-imports.js
15400
15998
  function parseImportedNames(importClause) {
15401
15999
  const trimmed = importClause.trim();
15402
16000
  if (!trimmed)
@@ -15421,7 +16019,7 @@ function parseImportedNames(importClause) {
15421
16019
  return names;
15422
16020
  }
15423
16021
 
15424
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/evidence/source-inspection.js
16022
+ // packages/experience-design-system-extraction/dist/src/extract/services/quality/helpers/inspection/detect-data-wrapper-patterns.js
15425
16023
  var INFRA_PROP_NAMES = /* @__PURE__ */ new Set(["id", "locale", "preview", "slug", "topic", "previousComponent", "__typename"]);
15426
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/;
15427
16025
  var GENERATED_IMPORT_PATTERN = /from\s+['"][^'"]*__generated[^'"]*['"]/;
@@ -15463,11 +16061,11 @@ function hasSiblingForwardRender(sourceText, siblingImports) {
15463
16061
  function hasVisibleUiRender(sourceText) {
15464
16062
  return /return\s*(?:\(|<)/.test(sourceText) && VISIBLE_UI_TAG_PATTERN.test(sourceText);
15465
16063
  }
15466
- function infraPropNames(component) {
16064
+ function collectInfraPropNames(component) {
15467
16065
  const props = component.props.map((prop) => prop.name);
15468
16066
  return props.length > 0 && props.every((prop) => INFRA_PROP_NAMES.has(prop)) ? props : [];
15469
16067
  }
15470
- function scoreToConfidence(score) {
16068
+ function mapScoreToWrapperConfidence(score) {
15471
16069
  if (score <= 0)
15472
16070
  return 0;
15473
16071
  if (score <= 2)
@@ -15478,9 +16076,14 @@ function scoreToConfidence(score) {
15478
16076
  return 4;
15479
16077
  return 5;
15480
16078
  }
15481
- function dedupeReasons(reasons) {
15482
- return [...new Set(reasons)];
16079
+ function dedupeStrings(values) {
16080
+ return [...new Set(values)];
15483
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";
15484
16087
  async function inspectComponentSource(component) {
15485
16088
  let sourceText = "";
15486
16089
  try {
@@ -15495,7 +16098,7 @@ async function inspectComponentSource(component) {
15495
16098
  const reviewReasons = [];
15496
16099
  let wrapperScore = 0;
15497
16100
  const hasGeneratedQueryHook = GENERATED_IMPORT_PATTERN.test(sourceText) && GENERATED_QUERY_HOOK_PATTERN.test(sourceText);
15498
- const infraProps = infraPropNames(component);
16101
+ const infraProps = collectInfraPropNames(component);
15499
16102
  const siblingImports = collectSiblingRendererImports(sourceText);
15500
16103
  if (GQL_FILENAME_PATTERN.test(component.source)) {
15501
16104
  reviewReasons.push("data-wrapper:gql-filename");
@@ -15525,7 +16128,7 @@ async function inspectComponentSource(component) {
15525
16128
  reviewReasons.push("data-wrapper:loading-null-guard");
15526
16129
  wrapperScore += 1;
15527
16130
  }
15528
- const wrapperConfidence = scoreToConfidence(wrapperScore);
16131
+ const wrapperConfidence = mapScoreToWrapperConfidence(wrapperScore);
15529
16132
  if (wrapperConfidence >= 4) {
15530
16133
  reviewReasons.unshift(HIGH_CONFIDENCE_DATA_FETCH_WRAPPER_REASON);
15531
16134
  } else if (wrapperConfidence === 3) {
@@ -15537,7 +16140,7 @@ async function inspectComponentSource(component) {
15537
16140
  }
15538
16141
  return {
15539
16142
  wrapperConfidence,
15540
- reviewReasons: dedupeReasons(reviewReasons),
16143
+ reviewReasons: dedupeStrings(reviewReasons),
15541
16144
  keepDespiteZeroSurface
15542
16145
  };
15543
16146
  }
@@ -15568,235 +16171,123 @@ function describeReviewReason(reason) {
15568
16171
  }
15569
16172
  }
15570
16173
  function describeReviewReasons(reasons) {
15571
- return dedupeReasons(reasons.map(describeReviewReason));
15572
- }
15573
-
15574
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/quality/evaluate-extraction-quality.js
15575
- function wrapperConfidenceToIssueCount(confidence) {
15576
- if (confidence >= 4)
15577
- return 2;
15578
- if (confidence === 3)
15579
- return 1;
15580
- return 0;
15581
- }
15582
- async function evaluateExtractionQuality(components, initialWarnings = []) {
15583
- const inspectedComponents = await Promise.all(components.map(async (component) => ({
15584
- component,
15585
- inspection: await inspectComponentSource(component)
15586
- })));
15587
- const scoredComponents = [];
15588
- const warnings = [...initialWarnings];
15589
- for (const { component, inspection } of inspectedComponents) {
15590
- const verdict = hasNoAuthoringSurface(component);
15591
- const keepDespiteZeroSurface = verdict.skip && verdict.reason === "component has no props and no slots" && inspection.keepDespiteZeroSurface;
15592
- const retainedForReview = verdict.skip && !keepDespiteZeroSurface;
15593
- if (retainedForReview) {
15594
- warnings.push(`${component.name}: requires operator review (${verdict.reason})`);
15595
- }
15596
- if (keepDespiteZeroSurface) {
15597
- warnings.push(`${component.name}: retained despite 0 props/slots because the source renders visible or compositional UI`);
15598
- }
15599
- if (inspection.reviewReasons.length > 0) {
15600
- const reviewNotes = describeReviewReasons(inspection.reviewReasons).filter((note) => note !== "high-confidence data-fetch wrapper").join("; ");
15601
- if (reviewNotes)
15602
- warnings.push(`${component.name}: ${reviewNotes}`);
15603
- }
15604
- const extractorReasons = component.reviewReasons ?? [];
15605
- const nonAuthorableReason = retainedForReview ? [`non-authorable:${verdict.reason}`] : [];
15606
- const { confidence, reasons } = computeExtractionScore(component, {
15607
- additionalIssueCount: wrapperConfidenceToIssueCount(inspection.wrapperConfidence) + extractorReasons.length + nonAuthorableReason.length,
15608
- additionalReasons: [...extractorReasons, ...inspection.reviewReasons, ...nonAuthorableReason]
15609
- });
15610
- scoredComponents.push({
15611
- ...component,
15612
- extractionConfidence: confidence,
15613
- reviewReasons: reasons,
15614
- needsReview: deriveNeedsReview(confidence) || inspection.wrapperConfidence >= 4 || inspection.keepDespiteZeroSurface || retainedForReview || extractorReasons.includes("props-type-unresolved") || (component.needsReview ?? false)
15615
- });
15616
- }
15617
- return {
15618
- components: validateExtractedComponents(scoredComponents),
15619
- warnings
15620
- };
16174
+ return dedupeStrings(reasons.map(describeReviewReason));
15621
16175
  }
15622
16176
 
15623
- // packages/experience-design-system-backend-pipeline/dist/src/steps/extraction/src/helpers/pre-classify.js
15624
- function isStringLiteralUnion(type) {
15625
- return type.includes("|") && type.includes("'");
15626
- }
15627
- function isSimpleType(type) {
15628
- const t = type.trim();
15629
- if (t === "string" || t === "boolean" || t === "number")
15630
- return true;
15631
- return isStringLiteralUnion(t);
15632
- }
15633
- function isBooleanType(type) {
15634
- return type.trim() === "boolean";
15635
- }
15636
- function isStringType(type) {
15637
- return type.trim() === "string";
15638
- }
15639
- function isNumberType(type) {
15640
- return type.trim() === "number";
15641
- }
15642
- function isComplexType(type) {
15643
- return !isSimpleType(type);
15644
- }
15645
- var DOM_PASS_THROUGH_PROPS = /* @__PURE__ */ new Set([
15646
- // Bare HTML / framework styling pass-through
15647
- "className",
15648
- "class",
15649
- "classes",
15650
- "classNames",
15651
- "rootClassName",
15652
- "prefixCls",
15653
- "style",
15654
- "styles",
15655
- // Bare HTML attributes
15656
- "id",
15657
- "role",
15658
- "tabIndex",
15659
- "tabindex",
15660
- "htmlFor",
15661
- "for",
15662
- "slot",
15663
- "is",
15664
- "lang",
15665
- "dir",
15666
- "hidden",
15667
- "draggable",
15668
- "spellCheck",
15669
- "spellcheck",
15670
- "contentEditable",
15671
- "contenteditable",
15672
- "inputMode",
15673
- "inputmode",
15674
- "autoComplete",
15675
- "autocomplete",
15676
- "autoFocus",
15677
- "autofocus",
15678
- "translate",
15679
- "part",
15680
- "exportparts",
15681
- "aria",
15682
- // Framework theming / pass-through escape hatches — dev-facing, never marketer-configurable
15683
- "dt",
15684
- "pt",
15685
- "ptOptions",
15686
- "unstyled",
15687
- "sx",
15688
- // Polymorphic component props — change rendered HTML/component, not marketer-visible behavior
15689
- "as",
15690
- "element",
15691
- "component",
15692
- // QA / vendor test attributes
15693
- "dataQa",
15694
- "data-qa",
15695
- // Vue v-model internals — framework wiring, never marketer-configurable
15696
- "modelValue",
15697
- "modelModifiers"
15698
- ]);
15699
- function isDomPassThroughProp(name) {
15700
- if (DOM_PASS_THROUGH_PROPS.has(name))
15701
- return true;
15702
- if (/^aria[-A-Z]/.test(name))
15703
- return true;
15704
- return name.startsWith("data-");
15705
- }
15706
- function preClassifyProp(prop) {
15707
- const { name, type } = prop;
15708
- if (/^on[A-Z]/.test(name) || type.includes("=> void") || type.includes("EventHandler")) {
15709
- return { category: "exclude" };
15710
- }
15711
- if (name === "ref" || name === "innerRef" || type.includes("Ref<") || type.includes("RefObject<")) {
15712
- return { category: "exclude" };
15713
- }
15714
- if (name === "testId" || name === "data-testid" || name === "dataTestId") {
15715
- return { category: "exclude" };
15716
- }
15717
- if (name === "key") {
15718
- return { category: "exclude" };
15719
- }
15720
- if (type.includes("Dispatch<") || type.includes("SetStateAction")) {
15721
- return { category: "exclude" };
15722
- }
15723
- if (isDomPassThroughProp(name)) {
15724
- return { category: "exclude" };
15725
- }
15726
- if (name === "name" && prop.domAttribute) {
15727
- return { category: "exclude" };
15728
- }
15729
- if (isStringLiteralUnion(type)) {
15730
- return { category: "design", cdfTypeHint: "enum" };
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);
15731
16183
  }
15732
- if (!isComplexType(type)) {
15733
- const designNameStart = /^(variant|size|spacing|gap|color|bg|theme|align|layout|orientation|position)/i;
15734
- const designNameEnd = /(Color|Size|Variant|Style)$/;
15735
- if (designNameStart.test(name) || designNameEnd.test(name)) {
15736
- return { category: "design", 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
+ });
15737
16192
  }
15738
- }
15739
- if (isBooleanType(type)) {
15740
- const stateNames = ["disabled", "loading", "expanded", "isOpen", "selected", "checked", "active", "preview"];
15741
- if (stateNames.includes(name)) {
15742
- return { category: "state", cdfTypeHint: "boolean" };
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
+ }
15743
16202
  }
15744
- }
15745
- if (isBooleanType(type)) {
15746
- const visualToggle = /^(hide|show|enable|disable|vertical|horizontal|reverse|bold|italic|imageOn|with)/i;
15747
- if (visualToggle.test(name)) {
15748
- return { category: "design", cdfTypeHint: "boolean" };
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
+ }
15749
16212
  }
15750
- }
15751
- if (name === "componentId" || name === "sectionKey" || name === "locale" || name === "variantIndex") {
15752
- return { category: "state", cdfTypeHint: "string" };
15753
- }
15754
- if (isStringType(type)) {
15755
- const urlNameStart = /^(href|url|link|src)/i;
15756
- const urlNameEnd = /(Url|Href|Link|Src)$/;
15757
- if (urlNameStart.test(name) || urlNameEnd.test(name)) {
15758
- return { category: "content", cdfTypeHint: "string" };
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
+ }
15759
16224
  }
15760
- }
15761
- if (isStringType(type)) {
15762
- const textNameStart = /^(label|title|text|description|caption|heading|subheading|body|alt|name|placeholder|summary)/i;
15763
- const textNameEnd = /(Text|Label|Title|Name)$/;
15764
- if (textNameStart.test(name) || textNameEnd.test(name)) {
15765
- return { category: "content", cdfTypeHint: "string" };
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
+ });
15766
16232
  }
15767
- }
15768
- if (isStringType(type)) {
15769
- return { category: "content", cdfTypeHint: "string" };
15770
- }
15771
- if (isBooleanType(type)) {
15772
- return { category: "design", cdfTypeHint: "boolean" };
15773
- }
15774
- if (isNumberType(type)) {
15775
- return { category: "design", cdfTypeHint: "string" };
15776
- }
15777
- return void 0;
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
+ });
15778
16242
  }
15779
- function preClassifyComponent(component) {
15780
- const props = component.props.flatMap((prop) => {
15781
- const result = preClassifyProp(prop);
15782
- if (result?.category === "exclude") {
15783
- return [];
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;
16251
+ }
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})`);
15784
16265
  }
15785
- const { domAttribute: _domAttribute, ...authorableProp } = prop;
15786
- if (prop.category) {
15787
- return [authorableProp];
16266
+ if (keepDespiteZeroSurface) {
16267
+ warnings.push(`${component.name}: retained despite 0 props/slots because the source renders visible or compositional UI`);
15788
16268
  }
15789
- if (!result) {
15790
- 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}`);
15791
16273
  }
15792
- return [
15793
- {
15794
- ...authorableProp,
15795
- category: result.category
15796
- }
15797
- ];
15798
- });
15799
- 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
+ };
15800
16291
  }
15801
16292
 
15802
16293
  // packages/experience-design-system-backend-pipeline/dist/src/orchestrators/run-extract-and-compose-session/helpers/emit-pre-classification-exclusions.js
@@ -28074,68 +28565,44 @@ function SettingsScreen({
28074
28565
 
28075
28566
  // packages/experience-design-system-cli/src/tui/settings/configuration/ConfigurationScreen.tsx
28076
28567
  import { useState as useState33 } from "react";
28077
- 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";
28078
28569
 
28079
28570
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentsScreen.tsx
28080
- import { useEffect as useEffect23, useState as useState31 } from "react";
28571
+ import { useEffect as useEffect23, useRef as useRef13, useState as useState31 } from "react";
28081
28572
  import { Box as Box46, Text as Text49, useInput as useInput12 } from "ink";
28082
28573
  init_debug_store();
28083
28574
 
28084
- // 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
28085
28576
  init_config();
28086
- var readV1Store = readSettings2;
28087
- var writeV1Store = writeSettings2;
28088
-
28089
- // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-store.ts
28090
- var AGENT_LABELS = {
28091
- claude: "Claude Code",
28092
- codex: "OpenAI Codex",
28093
- opencode: "OpenCode",
28094
- cursor: "Cursor",
28095
- copilot: "GitHub Copilot"
28096
- };
28097
- var asString = (value) => typeof value === "string" ? value : "";
28098
- function emptyAgents() {
28099
- 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
+ };
28100
28593
  }
28101
- async function readAgentConfig() {
28102
- const store = await readV1Store();
28103
- const stored = asString(store["agent"]);
28104
- const defaultChosen = isAgentName(stored);
28105
- const defaultAgent = defaultChosen ? stored : DEFAULT_AGENT_NAME;
28106
- const agents = emptyAgents();
28107
- const saved = store["agents"];
28108
- if (saved && typeof saved === "object") {
28109
- for (const name of AGENT_NAMES) {
28110
- const entry = saved[name];
28111
- if (entry && typeof entry === "object") {
28112
- const fields2 = entry;
28113
- agents[name] = { model: asString(fields2["model"]), binary: asString(fields2["binary"]) };
28114
- }
28115
- }
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]");
28116
28600
  }
28117
- const legacyModel = asString(store["agentModel"]);
28118
- if (legacyModel && !agents[defaultAgent].model)
28119
- agents[defaultAgent] = { ...agents[defaultAgent], model: legacyModel };
28120
- return { defaultAgent, defaultChosen, agents };
28601
+ return message;
28121
28602
  }
28122
- async function writeAgentConfig(config) {
28123
- const existing = await readV1Store();
28124
- const merged = { ...existing };
28125
- const agents = {};
28126
- for (const name of AGENT_NAMES) {
28127
- const model = config.agents[name].model.trim();
28128
- const binary = config.agents[name].binary.trim();
28129
- if (model || binary) agents[name] = { ...model ? { model } : {}, ...binary ? { binary } : {} };
28130
- }
28131
- const defaultModel = config.defaultChosen ? agents[config.defaultAgent]?.model : void 0;
28132
- if (config.defaultChosen) merged["agent"] = config.defaultAgent;
28133
- else delete merged["agent"];
28134
- if (defaultModel) merged["agentModel"] = defaultModel;
28135
- else delete merged["agentModel"];
28136
- if (Object.keys(agents).length > 0) merged["agents"] = agents;
28137
- else delete merged["agents"];
28138
- 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) };
28139
28606
  }
28140
28607
 
28141
28608
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/agent-status.ts
@@ -28341,6 +28808,94 @@ async function withBinaryEnv(name, binary, run) {
28341
28808
  }
28342
28809
  }
28343
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
+
28344
28899
  // packages/experience-design-system-cli/src/tui/settings/configuration/agents/AgentDetail.tsx
28345
28900
  import { useEffect as useEffect22, useState as useState30 } from "react";
28346
28901
  import { Box as Box45, Text as Text48 } from "ink";
@@ -28542,7 +29097,8 @@ function AgentDetail({
28542
29097
  name,
28543
29098
  config,
28544
29099
  setConfig,
28545
- onBack
29100
+ onBack,
29101
+ record: record3
28546
29102
  }) {
28547
29103
  const settings = config.agents[name];
28548
29104
  const [saving, setSaving] = useState30(false);
@@ -28565,10 +29121,12 @@ function AgentDetail({
28565
29121
  setNotice(null);
28566
29122
  writeAgentConfig(config).then(() => {
28567
29123
  applyBinaryOverrides(config);
29124
+ record3(`saved ${name}`);
28568
29125
  setNotice({ kind: "success", text: `Saved: ${AGENT_LABELS[name]} settings are written to config.json` });
28569
- }).catch(
28570
- (err) => setNotice({ kind: "error", text: `Not saved: ${err instanceof Error ? err.message : String(err)}` })
28571
- ).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));
28572
29130
  };
28573
29131
  const runTest = () => {
28574
29132
  setTesting(true);
@@ -28576,12 +29134,15 @@ function AgentDetail({
28576
29134
  testAgent(name, settings.binary, settings.model).then((result) => {
28577
29135
  setChecks(result);
28578
29136
  const outcome = testOutcome(result);
29137
+ if (!outcome || !outcome.ok)
29138
+ record3(`test of ${name} failed: ${outcome?.text ?? "the agent could not be started"}`);
28579
29139
  setNotice(
28580
29140
  outcome ? { kind: outcome.ok ? "success" : "error", text: outcome.text } : { kind: "error", text: "Test not run: the agent could not be started (see Status)" }
28581
29141
  );
28582
- }).catch(
28583
- (err) => setNotice({ kind: "error", text: `Test could not run: ${err instanceof Error ? err.message : String(err)}` })
28584
- ).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));
28585
29146
  };
28586
29147
  const action = {
28587
29148
  label: "Run test",
@@ -28602,6 +29163,7 @@ function AgentDetail({
28602
29163
  onChange: (key, value) => {
28603
29164
  setConfig({ ...config, agents: { ...config.agents, [name]: { ...settings, [key]: value } } });
28604
29165
  setNotice(null);
29166
+ record3(`edited ${name}.${key}`);
28605
29167
  },
28606
29168
  onSave: save,
28607
29169
  onQuit: onBack,
@@ -28628,6 +29190,12 @@ function AgentsScreen({ onDone }) {
28628
29190
  const [saving, setSaving] = useState31(false);
28629
29191
  const [loadError, setLoadError] = useState31(null);
28630
29192
  const [checks, setChecks] = useState31({});
29193
+ const visit = useRef13({
29194
+ trail: createTrail(),
29195
+ before: void 0,
29196
+ environment: void 0,
29197
+ error: ""
29198
+ }).current;
28631
29199
  const inspectAll = (loaded) => {
28632
29200
  for (const name of AGENT_NAMES) {
28633
29201
  void inspectAgent(name, loaded.agents[name].binary).then(
@@ -28639,14 +29207,38 @@ function AgentsScreen({ onDone }) {
28639
29207
  readAgentConfig().then((loaded) => {
28640
29208
  setConfig(loaded);
28641
29209
  inspectAll(loaded);
29210
+ visit.before = agentSnapshot(loaded);
28642
29211
  startDebugRun({
28643
29212
  flow: "settings/agents",
28644
29213
  step: "01-agents",
28645
29214
  menuOption: "Configuration > Agents",
28646
- inputs: { default_agent: loaded.defaultAgent }
29215
+ inputs: { ...visit.before, default_chosen: loaded.defaultChosen ?? false }
28647
29216
  });
28648
- }).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
+ });
28649
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
+ };
28650
29242
  const makeDefault = (name) => {
28651
29243
  if (!config || saving) return;
28652
29244
  if (name === config.defaultAgent) {
@@ -28658,15 +29250,16 @@ function AgentsScreen({ onDone }) {
28658
29250
  writeAgentConfig({ ...config, defaultAgent: name, defaultChosen: true }).then(() => {
28659
29251
  setConfig((current) => current ? { ...current, defaultAgent: name, defaultChosen: true } : current);
28660
29252
  setNotice({ kind: "success", text: `Default agent is now ${AGENT_LABELS[name]}` });
28661
- }).catch(
28662
- (err) => setNotice({
28663
- kind: "error",
28664
- text: `Default agent not changed: ${err instanceof Error ? err.message : String(err)}`
28665
- })
28666
- ).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));
28667
29259
  };
28668
29260
  useInput12((input, key) => {
28669
29261
  if (loadError && (key.escape || input === "q")) {
29262
+ endVisit(null);
28670
29263
  onDone();
28671
29264
  return;
28672
29265
  }
@@ -28691,7 +29284,7 @@ function AgentsScreen({ onDone }) {
28691
29284
  return;
28692
29285
  }
28693
29286
  if (key.escape || input === "q") {
28694
- void finishDebugRun({ outputs: { default_agent: config.defaultAgent }, status: "success", exitMethod: "saved" });
29287
+ endVisit(config);
28695
29288
  onDone();
28696
29289
  }
28697
29290
  });
@@ -28716,6 +29309,10 @@ function AgentsScreen({ onDone }) {
28716
29309
  name: open,
28717
29310
  config,
28718
29311
  setConfig,
29312
+ record: (action, error) => {
29313
+ visit.trail.record(action);
29314
+ if (error !== void 0) visit.error = scrubError(error, []);
29315
+ },
28719
29316
  onBack: () => {
28720
29317
  setOpen(null);
28721
29318
  void readAgentConfig().then((saved) => {
@@ -28782,8 +29379,8 @@ function AgentsScreen({ onDone }) {
28782
29379
  }
28783
29380
 
28784
29381
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
28785
- import { useEffect as useEffect24, useState as useState32 } from "react";
28786
- 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";
28787
29384
  init_debug_store();
28788
29385
 
28789
29386
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/project-store.ts
@@ -28812,7 +29409,7 @@ async function writeCredentials(config) {
28812
29409
  }
28813
29410
 
28814
29411
  // packages/experience-design-system-cli/src/tui/settings/configuration/project/ProjectScreen.tsx
28815
- 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";
28816
29413
  var FIELDS2 = [
28817
29414
  { key: "spaceId", label: "Space ID" },
28818
29415
  { key: "environmentId", label: "Environment ID" },
@@ -28833,45 +29430,94 @@ function debugFields(config) {
28833
29430
  contentful_base_dir: config?.contentfulBaseDir ?? ""
28834
29431
  };
28835
29432
  }
29433
+ var snapshotOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
28836
29434
  var valuesOf = (config) => Object.fromEntries(FIELDS2.map(({ key }) => [key, config[key] || ""]));
28837
29435
  function ProjectScreen({ onDone }) {
28838
29436
  const [config, setConfig] = useState32(null);
28839
29437
  const [saving, setSaving] = useState32(false);
28840
29438
  const [notice, setNotice] = useState32(null);
28841
- 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 ?? ""]);
28842
29447
  useEffect24(() => {
28843
- void readCredentials().then((cfg) => {
29448
+ readCredentials().then((cfg) => {
28844
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, []);
28845
29460
  startDebugRun({
28846
29461
  flow: "settings/contentful-configuration",
28847
29462
  step: "01-configuration",
28848
29463
  menuOption: "Configuration > Project",
28849
- inputs: debugFields(cfg)
29464
+ inputs: { settings_file: describeSettingsFile() }
28850
29465
  });
29466
+ setLoadError(visit.error);
28851
29467
  });
28852
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
+ });
28853
29491
  if (!config) {
28854
29492
  return /* @__PURE__ */ jsxs48(Box47, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
28855
29493
  /* @__PURE__ */ jsx59(Text50, { bold: true, children: "Project" }),
28856
29494
  /* @__PURE__ */ jsx59(Text50, { children: " " }),
28857
- /* @__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..." })
28858
29503
  ] });
28859
29504
  }
28860
29505
  const save = () => {
28861
29506
  setSaving(true);
28862
29507
  setNotice(null);
28863
29508
  writeCredentials(config).then(() => {
28864
- setLastSaved(config);
29509
+ visit.persisted = snapshotOf(config);
29510
+ visit.trail.record("saved");
28865
29511
  if (config.contentfulBaseDir) void startDebugSessionIn(config.contentfulBaseDir, { replace: true });
28866
29512
  setNotice({ kind: "success", text: "Project settings saved" });
28867
29513
  }).catch((err) => {
29514
+ visit.error = scrub(err);
29515
+ visit.trail.record("save failed");
28868
29516
  setNotice({ kind: "error", text: err instanceof Error ? err.message : "Failed to save" });
28869
29517
  }).finally(() => setSaving(false));
28870
29518
  };
28871
29519
  const quit = () => {
28872
- void finishDebugRun(
28873
- lastSaved ? { outputs: debugFields(lastSaved), status: "success", exitMethod: "saved" } : { outputs: {}, status: "success", exitMethod: "discarded" }
28874
- );
29520
+ endVisit(config);
28875
29521
  onDone();
28876
29522
  };
28877
29523
  return /* @__PURE__ */ jsx59(
@@ -28884,6 +29530,7 @@ function ProjectScreen({ onDone }) {
28884
29530
  onChange: (key, value) => {
28885
29531
  setConfig({ ...config, [key]: value });
28886
29532
  setNotice(null);
29533
+ visit.trail.record(`edited ${key}`);
28887
29534
  },
28888
29535
  onSave: save,
28889
29536
  onQuit: quit,
@@ -28907,7 +29554,7 @@ function ConfigurationScreen({ onDone }) {
28907
29554
  const [focus, setFocus] = useState33(0);
28908
29555
  const [open, setOpen] = useState33(null);
28909
29556
  const close = () => setOpen(null);
28910
- useInput13((input, key) => {
29557
+ useInput14((input, key) => {
28911
29558
  if (open) return;
28912
29559
  if (key.upArrow) {
28913
29560
  setFocus((i) => (i - 1 + SECTIONS.length) % SECTIONS.length);
@@ -28949,7 +29596,7 @@ function ConfigurationScreen({ onDone }) {
28949
29596
 
28950
29597
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
28951
29598
  import { useEffect as useEffect25, useState as useState34 } from "react";
28952
- 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";
28953
29600
 
28954
29601
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/analytics-store.ts
28955
29602
  async function readAnalyticsSetting() {
@@ -28968,7 +29615,7 @@ async function writeAnalyticsSetting(setting) {
28968
29615
 
28969
29616
  // packages/experience-design-system-cli/src/tui/settings/opt-in-analytics/screen.tsx
28970
29617
  init_debug_store();
28971
- 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";
28972
29619
  function OptInAnalyticsScreen({ onDone }) {
28973
29620
  const [loading, setLoading] = useState34(true);
28974
29621
  const [enabled, setEnabled] = useState34(true);
@@ -28984,7 +29631,7 @@ function OptInAnalyticsScreen({ onDone }) {
28984
29631
  });
28985
29632
  });
28986
29633
  }, []);
28987
- useInput14((input, key) => {
29634
+ useInput15((input, key) => {
28988
29635
  if (loading) return;
28989
29636
  if (key.return || input === " ") {
28990
29637
  setEnabled((current) => {
@@ -29002,7 +29649,7 @@ function OptInAnalyticsScreen({ onDone }) {
29002
29649
  return /* @__PURE__ */ jsxs50(Box49, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
29003
29650
  /* @__PURE__ */ jsx61(Text52, { bold: true, children: "Settings \u203A Opt-in Analytics" }),
29004
29651
  /* @__PURE__ */ jsx61(Text52, { children: " " }),
29005
- 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: [
29006
29653
  /* @__PURE__ */ jsxs50(Text52, { children: [
29007
29654
  "Share anonymous usage data:",
29008
29655
  " ",
@@ -29027,7 +29674,7 @@ function OptInAnalyticsScreen({ onDone }) {
29027
29674
 
29028
29675
  // packages/experience-design-system-cli/src/tui/settings/debug-mode/screen.tsx
29029
29676
  import { useEffect as useEffect26, useState as useState35 } from "react";
29030
- import { Box as Box50, Text as Text53, useInput as useInput15 } from "ink";
29677
+ import { Box as Box50, Text as Text53, useInput as useInput16 } from "ink";
29031
29678
  init_debug_mode_store();
29032
29679
  init_debug_store();
29033
29680
  import { jsx as jsx62, jsxs as jsxs51 } from "react/jsx-runtime";
@@ -29046,7 +29693,7 @@ function DebugModeScreen({ onDone }) {
29046
29693
  });
29047
29694
  });
29048
29695
  }, []);
29049
- useInput15((input, key) => {
29696
+ useInput16((input, key) => {
29050
29697
  if (loading) return;
29051
29698
  if (key.return || input === " ") {
29052
29699
  setEnabled((current) => {
@@ -29083,7 +29730,7 @@ function DebugModeScreen({ onDone }) {
29083
29730
  }
29084
29731
 
29085
29732
  // packages/experience-design-system-cli/app.tsx
29086
- import { Fragment as Fragment20, jsx as jsx63 } from "react/jsx-runtime";
29733
+ import { Fragment as Fragment21, jsx as jsx63 } from "react/jsx-runtime";
29087
29734
  function App({ importOptions, inkClear }) {
29088
29735
  const [screen, setScreen] = useState36("start");
29089
29736
  const [importExitCode, setImportExitCode] = useState36();
@@ -29252,7 +29899,7 @@ function App({ importOptions, inkClear }) {
29252
29899
  }
29253
29900
  );
29254
29901
  }
29255
- if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment20, {});
29902
+ if (importDefaults === void 0) return /* @__PURE__ */ jsx63(Fragment21, {});
29256
29903
  if (projectPath === void 0) {
29257
29904
  return /* @__PURE__ */ jsx63(WelcomeScreen, { onContinue: setProjectPath, onQuit: leaveImport, initialPath: importDefaults.componentDir });
29258
29905
  }
@@ -29291,7 +29938,7 @@ function App({ importOptions, inkClear }) {
29291
29938
  }
29292
29939
  );
29293
29940
  }
29294
- return /* @__PURE__ */ jsx63(Fragment20, {});
29941
+ return /* @__PURE__ */ jsx63(Fragment21, {});
29295
29942
  }
29296
29943
  if (scopeRun && scopeFlow) {
29297
29944
  if (scopeFlow.status === "failed") {
@@ -29328,7 +29975,7 @@ function App({ importOptions, inkClear }) {
29328
29975
  }
29329
29976
  );
29330
29977
  }
29331
- return /* @__PURE__ */ jsx63(Fragment20, {});
29978
+ return /* @__PURE__ */ jsx63(Fragment21, {});
29332
29979
  }
29333
29980
  if (pendingLaunch?.cached) {
29334
29981
  return /* @__PURE__ */ jsx63(