@ncds/ui-admin-mcp 1.0.0-alpha.2 → 1.0.0-alpha.20

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.
Files changed (100) hide show
  1. package/bin/components.bundle.js +15 -19
  2. package/bin/definitions/external/editor.d.ts +50 -0
  3. package/bin/definitions/external/editor.js +53 -0
  4. package/bin/definitions/external/step-guide.d.ts +61 -0
  5. package/bin/definitions/external/step-guide.js +52 -0
  6. package/bin/definitions/instructions.md +194 -10
  7. package/bin/definitions/js-api.json +352 -0
  8. package/bin/definitions/rules.json +36 -4
  9. package/bin/definitions/tool-definitions.json +33 -9
  10. package/bin/overrides/composition.json +2500 -0
  11. package/bin/server.d.ts +5 -0
  12. package/bin/server.js +97 -33
  13. package/bin/server.mjs +0 -0
  14. package/bin/tools/external/editor.d.ts +18 -0
  15. package/bin/tools/external/editor.js +88 -0
  16. package/bin/tools/external/step-guide.d.ts +19 -0
  17. package/bin/tools/external/step-guide.js +79 -0
  18. package/bin/tools/getComponentProps.d.ts +3 -0
  19. package/bin/tools/getComponentProps.js +12 -3
  20. package/bin/tools/listCompositionOverrides.d.ts +61 -0
  21. package/bin/tools/listCompositionOverrides.js +156 -0
  22. package/bin/tools/ping.d.ts +1 -1
  23. package/bin/tools/renderToHtml.d.ts +38 -7
  24. package/bin/tools/renderToHtml.js +785 -110
  25. package/bin/tools/searchComponent.d.ts +5 -0
  26. package/bin/tools/searchComponent.js +3 -3
  27. package/bin/tools/validateHtml.d.ts +8 -6
  28. package/bin/tools/validateHtml.js +74 -6
  29. package/bin/types.d.ts +60 -1
  30. package/bin/utils/bemValidator.d.ts +16 -8
  31. package/bin/utils/bemValidator.js +16 -4
  32. package/bin/utils/compliance.d.ts +7 -6
  33. package/bin/utils/compliance.js +8 -4
  34. package/bin/utils/dataLoader.d.ts +43 -14
  35. package/bin/utils/dataLoader.js +125 -22
  36. package/bin/utils/domEnvironment.js +51 -0
  37. package/bin/utils/fuzzyMatch.d.ts +4 -0
  38. package/bin/utils/fuzzyMatch.js +13 -3
  39. package/bin/utils/logger.d.ts +5 -5
  40. package/bin/utils/logger.js +5 -5
  41. package/bin/utils/response.d.ts +4 -2
  42. package/bin/utils/response.js +15 -4
  43. package/bin/utils/tokenValidator.d.ts +4 -3
  44. package/bin/utils/tokenValidator.js +13 -11
  45. package/bin/version.d.ts +4 -2
  46. package/bin/version.js +4 -2
  47. package/data/_icons.json +357 -2
  48. package/data/_meta.json +4 -5
  49. package/data/_tokens.json +8 -8
  50. package/data/badge-group.json +181 -4
  51. package/data/badge.json +146 -14
  52. package/data/block-container.json +95 -0
  53. package/data/block-header.json +208 -0
  54. package/data/bread-crumb.json +38 -2
  55. package/data/button-group.json +59 -0
  56. package/data/button.json +124 -1
  57. package/data/carousel-arrow.json +6 -11
  58. package/data/carousel-number-group.json +2 -12
  59. package/data/checkbox.json +1 -1
  60. package/data/combo-box.json +32 -8
  61. package/data/data-grid.json +240 -0
  62. package/data/date-picker.json +22 -2
  63. package/data/divider.json +1 -1
  64. package/data/dot.json +2 -2
  65. package/data/dropdown.json +187 -20
  66. package/data/editor.json +85 -0
  67. package/data/empty-state.json +168 -3
  68. package/data/featured-icon.json +20 -5
  69. package/data/file-input.json +176 -10
  70. package/data/horizontal-tab.json +219 -3
  71. package/data/image-file-input.json +176 -10
  72. package/data/input-base.json +165 -4
  73. package/data/modal.json +266 -5
  74. package/data/notification.json +56 -40
  75. package/data/number-input.json +164 -4
  76. package/data/page-title.json +135 -0
  77. package/data/pagination.json +8 -4
  78. package/data/password-input.json +252 -13
  79. package/data/progress-bar.json +28 -8
  80. package/data/progress-circle.json +9 -6
  81. package/data/radio.json +4 -3
  82. package/data/range-date-picker-with-buttons.json +187 -7
  83. package/data/range-date-picker.json +186 -6
  84. package/data/select-box.json +48 -16
  85. package/data/select.json +35 -25
  86. package/data/slider.json +1 -1
  87. package/data/spinner.json +3 -4
  88. package/data/step-guide.json +130 -0
  89. package/data/switch.json +66 -6
  90. package/data/table.json +293 -0
  91. package/data/tag.json +68 -6
  92. package/data/textarea.json +1 -1
  93. package/data/toggle.json +2 -2
  94. package/data/tooltip.json +16 -3
  95. package/data/vertical-tab.json +220 -3
  96. package/package.json +27 -25
  97. package/bin/instructions.d.ts +0 -1
  98. package/bin/instructions.js +0 -14
  99. package/bin/tools/getComponentHtml.d.ts +0 -3
  100. package/bin/tools/getComponentHtml.js +0 -30
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listCompositionOverrides = exports.buildListCompositionOverridesResponse = exports.buildCompositionOverridesSummary = void 0;
4
+ const response_js_1 = require("../utils/response.js");
5
+ /**
6
+ * coverageScore 가중치 — 각 보강 영역의 점수 기여도.
7
+ * 시나리오(canonicalExamples)가 가장 큰 가중치를 가지는 이유: AI hallucination 차단력이 가장 큼.
8
+ */
9
+ const COVERAGE_WEIGHTS = {
10
+ CANONICAL_EXAMPLES_FULL: 0.5,
11
+ CANONICAL_EXAMPLES_PARTIAL: 0.25,
12
+ ALLOWED_CHILDREN_FULL: 0.2,
13
+ ALLOWED_CHILDREN_PARTIAL: 0.1,
14
+ BEM_CLASSES_EXTRA: 0.1,
15
+ DESCRIPTION_EXTRA: 0.1,
16
+ ALIASES_EXTRA: 0.1,
17
+ };
18
+ /**
19
+ * 전체 가중치 합 (5 영역 모두 적용 가능한 경우의 최대).
20
+ * applicableMax = TOTAL_WEIGHT - Σ(notApplicable 영역 가중치) 로 동적 계산.
21
+ */
22
+ const TOTAL_WEIGHT = 1.0;
23
+ /**
24
+ * 영역별 가중치 맵 — applicableMax 동적 계산 시 사용.
25
+ * canonicalExamples 는 항상 적용 가능하므로 포함하지 않음 (분모에서 제외되지 않음).
26
+ */
27
+ const AREA_WEIGHT_MAP = {
28
+ allowedChildren: COVERAGE_WEIGHTS.ALLOWED_CHILDREN_FULL,
29
+ bemClassesExtra: COVERAGE_WEIGHTS.BEM_CLASSES_EXTRA,
30
+ descriptionExtra: COVERAGE_WEIGHTS.DESCRIPTION_EXTRA,
31
+ aliasesExtra: COVERAGE_WEIGHTS.ALIASES_EXTRA,
32
+ };
33
+ /** coverageNote tier 임계값. */
34
+ const TIER_THRESHOLD_HIGH = 0.7;
35
+ const TIER_THRESHOLD_MEDIUM = 0.4;
36
+ /** 점수 cap (이론적 최대값). */
37
+ const SCORE_CAP = 1;
38
+ /** 소수점 2자리 반올림용 — `Math.round(x * 100) / 100`. */
39
+ const TWO_DECIMAL_BASE = 100;
40
+ /** AI 가 UI 를 얼마나 정확히 그릴 수 있는지 추정 — 각 composition overrides 보강이 다른 정확도 영역을 커버한다 */
41
+ const METRIC_DESCRIPTIONS = {
42
+ canonicalExamples: 'AI 가 시나리오별로 분기 가능한 정답 트리 수 (form / data-grid / with-tooltip 등). 가장 강한 정확도 신호 — 0 이면 AI 가 prop 조합을 추측해야 한다.',
43
+ hasCanonicalExample: 'legacy 단일 시나리오 존재 여부 — 진입용 표준 트리 1건. canonicalExamples 가 비어있는 컴포넌트의 fallback.',
44
+ bemClassesExtra: 'extract 가 못 잡은 BEM 클래스 화이트리스트 수. validate_html 이 합법/위법을 정확히 판정 — 발명 클래스 사용 차단력.',
45
+ allowedChildrenKeys: 'compound 부모-자식 제약 노드 수. 잘못된 자식 끼우기 / 컨텍스트 위반 (Table.Pagination in DataGrid 등) 차단력.',
46
+ allowedParentsKeys: '역방향 제약 노드 수 — 자식이 어느 부모 안에만 허용되는지 명시.',
47
+ descriptionExtra: 'list_components 응답에 자연어 용도/모드 명시 여부. 같은 컴포넌트가 다른 모드(horizontal/vertical) 로 쓰일 때 AI 가 모드를 인지하는 정확도.',
48
+ aliasesExtra: 'search_component 한국어/유사 키워드 매칭 별칭 수. AI 가 "폼 레이아웃", "세로 테이블" 같은 비공식 용어로 검색해도 정답 컴포넌트에 도달.',
49
+ coverageScore: '자기 카테고리 천장 대비 정규화 점수 (0.0 ~ 1.0). 1.0 이면 컴포넌트 구조상 가능한 모든 보강 영역을 완료. applicableMax = 1.0 − Σ(notApplicable 영역 가중치)로 동적 계산되어 모든 컴포넌트가 동일 척도. 절대 가중합이 아니라 정규화 비율이므로 컴포넌트 종류 무관하게 직접 비교 가능.',
50
+ notApplicable: '컴포넌트 구조상 적용 불가한 보강 영역 목록. non-compound 는 자동으로 ["allowedChildren"] 포함 (자식 없어 부모-자식 제약 의미 없음). composition.json notApplicableAreas 선언(예: extract 가 BEM 을 완전히 잡는 컴포넌트 → ["bemClassesExtra"])과 union 되어 최종 notApplicable 결정. 점수 분모에서 자동 제외되어 천장이 1.0 으로 통일된다.',
51
+ };
52
+ const COVERAGE_FORMULA = 'rawScore (가중 합) = canonicalExamples>=2: +0.50 (>=1 또는 hasCanonicalExample: +0.25) + allowedChildrenKeys>=2: +0.20 (>=1: +0.10, compound 만 적용) + bemClassesExtra>=1: +0.10 + descriptionExtra: +0.10 + aliasesExtra>=1: +0.10. applicableMax = 1.0 − Σ(notApplicable 영역 가중치) — non-compound 는 자동으로 allowedChildren(0.20) 제외, composition.json notApplicableAreas 선언 시 해당 가중치 추가 차감. coverageScore = rawScore / applicableMax (Math.min(1.0, ...)). 임계값: high>=0.70, medium>=0.40, 그 외 low. coverageScore 1.0 = 자기 카테고리에서 더 채울 영역 없음.';
53
+ /** 적용 가능 영역의 절대 가중치 합 (정규화 전 raw 점수). */
54
+ const computeRawScore = (s) => {
55
+ let score = 0;
56
+ if (s.canonicalExamples.length >= 2)
57
+ score += COVERAGE_WEIGHTS.CANONICAL_EXAMPLES_FULL;
58
+ else if (s.canonicalExamples.length >= 1 || s.hasCanonicalExample)
59
+ score += COVERAGE_WEIGHTS.CANONICAL_EXAMPLES_PARTIAL;
60
+ if (s.allowedChildrenKeys.length >= 2)
61
+ score += COVERAGE_WEIGHTS.ALLOWED_CHILDREN_FULL;
62
+ else if (s.allowedChildrenKeys.length >= 1)
63
+ score += COVERAGE_WEIGHTS.ALLOWED_CHILDREN_PARTIAL;
64
+ if (s.bemClassesExtra >= 1)
65
+ score += COVERAGE_WEIGHTS.BEM_CLASSES_EXTRA;
66
+ if (s.descriptionExtra)
67
+ score += COVERAGE_WEIGHTS.DESCRIPTION_EXTRA;
68
+ if (s.aliasesExtra >= 1)
69
+ score += COVERAGE_WEIGHTS.ALIASES_EXTRA;
70
+ return score;
71
+ };
72
+ /**
73
+ * 컴포넌트의 notApplicable 영역 목록.
74
+ * 구조적 제외(non-compound → allowedChildren) + entry 의 notApplicableAreas 선언을 union.
75
+ */
76
+ const getNotApplicable = (isCompound, entryNotApplicableAreas) => {
77
+ const structural = isCompound ? [] : ['allowedChildren'];
78
+ return [...new Set([...structural, ...entryNotApplicableAreas])];
79
+ };
80
+ /**
81
+ * applicableMax = TOTAL_WEIGHT - Σ(notApplicable 영역 가중치).
82
+ * compound 기본: 1.0. non-compound 기본: 0.8 (allowedChildren 0.2 제외).
83
+ * notApplicableAreas 추가 선언 시 해당 가중치만큼 추가 차감 → coverageScore 1.0 = 실제 달성 가능 최대.
84
+ */
85
+ const getApplicableMax = (notApplicable) => {
86
+ const excluded = notApplicable.reduce((sum, area) => sum + (AREA_WEIGHT_MAP[area] ?? 0), 0);
87
+ return Math.round((TOTAL_WEIGHT - excluded) * TWO_DECIMAL_BASE) / TWO_DECIMAL_BASE;
88
+ };
89
+ /** rawScore → coverageScore (자기 카테고리 천장 기준 0~1.0 정규화). */
90
+ const normalizeScore = (rawScore, applicableMax) => Math.min(SCORE_CAP, Math.round((rawScore / applicableMax) * TWO_DECIMAL_BASE) / TWO_DECIMAL_BASE);
91
+ /** coverageScore 를 high/medium/low tier 로 분류. */
92
+ const getTier = (score) => {
93
+ if (score >= TIER_THRESHOLD_HIGH)
94
+ return 'high';
95
+ if (score >= TIER_THRESHOLD_MEDIUM)
96
+ return 'medium';
97
+ return 'low';
98
+ };
99
+ const noteFor = (s, score) => {
100
+ const strengths = [];
101
+ if (s.canonicalExamples.length >= 2)
102
+ strengths.push(`시나리오 ${s.canonicalExamples.length}`);
103
+ else if (s.canonicalExamples.length >= 1 || s.hasCanonicalExample)
104
+ strengths.push('시나리오 1');
105
+ if (s.allowedChildrenKeys.length >= 1)
106
+ strengths.push(`compound 제약 ${s.allowedChildrenKeys.length}`);
107
+ if (s.bemClassesExtra >= 1)
108
+ strengths.push(`BEM 화이트리스트 ${s.bemClassesExtra}`);
109
+ if (s.descriptionExtra)
110
+ strengths.push('모드 인지 보강');
111
+ if (s.aliasesExtra >= 1)
112
+ strengths.push(`검색 별칭 ${s.aliasesExtra}`);
113
+ const tier = getTier(score);
114
+ return strengths.length > 0
115
+ ? `${tier} · ${strengths.join(' + ')}`
116
+ : `${tier} · 보강 거의 없음 — 직접 작성 hallucination 위험`;
117
+ };
118
+ /** 단일 entry 를 row 로 변환 — compound 여부에 따라 applicableMax 가 결정되고 coverageScore 가 정규화된다. */
119
+ const toSummary = (component, entry, componentMap) => {
120
+ const base = {
121
+ component,
122
+ canonicalExamples: Object.keys(entry.canonicalExamples ?? {}).filter((k) => !k.startsWith('_')),
123
+ hasCanonicalExample: !!entry.canonicalExample,
124
+ bemClassesExtra: (entry.bemClassesExtra ?? []).length,
125
+ allowedChildrenKeys: Object.keys(entry.allowedChildren ?? {}),
126
+ allowedParentsKeys: Object.keys(entry.allowedParents ?? {}),
127
+ descriptionExtra: !!entry.descriptionExtra,
128
+ aliasesExtra: (entry.aliasesExtra ?? []).length,
129
+ };
130
+ const subComponentCount = Object.keys(componentMap.get(component)?.subComponents ?? {}).length;
131
+ const isCompound = subComponentCount > 0;
132
+ const notApplicable = getNotApplicable(isCompound, entry.notApplicableAreas ?? []);
133
+ const applicableMax = getApplicableMax(notApplicable);
134
+ const rawScore = computeRawScore(base);
135
+ const coverageScore = normalizeScore(rawScore, applicableMax);
136
+ return { ...base, coverageScore, notApplicable, coverageNote: noteFor(base, coverageScore) };
137
+ };
138
+ /** composition overrides map → 요약 결과 (순수 함수). coverageScore 내림차순 + 동률은 이름 알파벳. */
139
+ const buildCompositionOverridesSummary = (compositionOverrides, componentMap) => {
140
+ const overrides = Object.entries(compositionOverrides)
141
+ .map(([component, entry]) => toSummary(component, entry, componentMap))
142
+ .sort((a, b) => b.coverageScore - a.coverageScore || a.component.localeCompare(b.component));
143
+ return {
144
+ total: overrides.length,
145
+ metrics: METRIC_DESCRIPTIONS,
146
+ coverageScoreFormula: COVERAGE_FORMULA,
147
+ overrides,
148
+ };
149
+ };
150
+ exports.buildCompositionOverridesSummary = buildCompositionOverridesSummary;
151
+ /** server.ts 부팅 시 1회 호출 — 응답 사전 직렬화 */
152
+ const buildListCompositionOverridesResponse = (compositionOverrides, componentMap) => (0, response_js_1.successResponse)((0, exports.buildCompositionOverridesSummary)(compositionOverrides, componentMap));
153
+ exports.buildListCompositionOverridesResponse = buildListCompositionOverridesResponse;
154
+ /** list_composition_overrides tool — 사전 직렬화된 응답을 그대로 반환 */
155
+ const listCompositionOverrides = (prebuilt) => prebuilt;
156
+ exports.listCompositionOverrides = listCompositionOverrides;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ping tool — NCUA MCP 서버 health check + capabilities/rules 제공 (순수 함수)
3
3
  */
4
- import type { ComponentData, Capability } from '../types.js';
4
+ import type { Capability, ComponentData } from '../types.js';
5
5
  import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
6
6
  import { type McpToolResponse } from '../utils/response.js';
7
7
  /** ping tool 파라미터 — server.ts에서 모든 데이터를 소유하고 전달한다 */
@@ -1,21 +1,52 @@
1
- /**
2
- * render_to_html tool — 컴포넌트 속성을 전달하면 정확한 HTML + React 매핑을 반환 (순수 함수)
3
- *
4
- * DOM 환경과 React/ReactDOM은 server.ts에서 초기화하고 파라미터로 전달한다.
5
- * 이 파일에는 fs, path, require, let이 없다.
6
- */
7
- import type { ComponentData, ReactRuntime } from '../types.js';
1
+ import type { ComponentData, JsApiInfo, ReactRuntime } from '../types.js';
8
2
  import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
9
3
  import { type McpToolResponse } from '../utils/response.js';
10
4
  /** render_to_html tool 파라미터 */
11
5
  export interface RenderToHtmlParams {
12
6
  componentMap: Map<string, ComponentData>;
13
7
  bundle: Record<string, unknown>;
8
+ iconBundle: Record<string, unknown>;
14
9
  cdnMeta: CdnMeta | null;
15
10
  iconMeta: IconMeta | null;
16
11
  reactRuntime: ReactRuntime;
12
+ jsApiMap: Map<string, JsApiInfo>;
17
13
  name: string;
18
14
  props?: Record<string, unknown>;
15
+ /**
16
+ * CDN init 패턴(Story 5.8) 생성 시 `<div id>` 접미사로 사용되는 고유 식별자.
17
+ * server.ts가 매 호출마다 `crypto.randomUUID().slice(0, 6)` 방식으로 생성해 주입한다.
18
+ * renderToHtml.ts는 순수 함수 원칙(파일 헤더 주석)을 위해 내부에서 랜덤을 생성하지 않는다.
19
+ */
20
+ instanceId: string;
19
21
  }
22
+ /** Story 5.9: 사용자 props 에서 함수 prop 재귀 제거 (JSON 직렬화·deep merge 안전).
23
+ * cdnDefaults 는 JSON 이라 함수 없음 → 사용자 props 만 정리.
24
+ * Story 5.10: 외부 분기 모듈(`tools/external/editor.ts`)에서도 재사용 → export. */
25
+ export declare const stripFunctionProps: (obj: unknown) => unknown;
26
+ /** Story 5.9: cdnDefaults 와 사용자 props 를 deep merge.
27
+ * - 사용자 값 우선 (lodash merge 의 두 번째 인자가 우선)
28
+ * - 함수 prop 은 사용자 props 에서 stripFunctionProps 로 사전 제거
29
+ * - DatePicker `datePickerOptions` 배열 한정: 각 항목의 `options` 에 cdnDefaults 의 첫 항목 options 를 per-element 부분 merge
30
+ * - 다른 배열(`buttons` 등) 은 사용자 값 그대로
31
+ * 반환: { merged, defaultsApplied } — defaultsApplied 는 cdnDefaults 에 있고 사용자 props 에 없는 top-level 키 (defaultsUsed 응답용)
32
+ * Story 5.10: 외부 분기 모듈(`tools/external/editor.ts`)에서도 재사용 → export. */
33
+ export declare const mergeCdnDefaults: (componentName: string, cdnDefaults: Record<string, unknown> | undefined, userProps: Record<string, unknown>) => {
34
+ merged: Record<string, unknown>;
35
+ defaultsApplied: string[];
36
+ };
37
+ /** jsRequired에 따라 js 필드를 생성 */
38
+ export declare const buildJsField: (componentData: ComponentData, jsApiMap: Map<string, JsApiInfo>) => {
39
+ required: false;
40
+ } | {
41
+ api?: {
42
+ className: string;
43
+ constructor: string;
44
+ constructorParams: Record<string, string>;
45
+ methods: string[];
46
+ example: string;
47
+ } | undefined;
48
+ required: true;
49
+ description: string;
50
+ };
20
51
  /** render_to_html tool — props로 React 컴포넌트를 렌더링하여 HTML + React 매핑 반환 */
21
52
  export declare const renderToHtml: (params: RenderToHtmlParams) => McpToolResponse;