import { Attribute, EmbeddableCatalog, Entity, LogicalModel, ModelId } from '../core/types'; import { InheritedColumn } from '../core/inheritedColumns'; import { IndexRefResolver } from '../core/indexColumnRef'; import { Translator } from '../i18n'; /** * 인덱스 편집 뷰의 **컬럼 표시 라벨** 단일 출처 — 인스펙터(`EntityIndexSection`)와 와이드 * (`EntityIndexesWide`)가 같은 규칙으로 라벨을 만든다. * * ── 왜 뽑았나 ───────────────────────────────────────────────────────────────── * 두 뷰가 각자 라벨을 조립하면서 **서로 다른 반쪽**을 갖고 있었다(VE-4 미러 드리프트 축): * - 와이드 `colLabel`은 **속성 modelId만** 조회해 **레거시 dbAttr 표기 ref가 UUID 원문**으로 보였다. * 실 데이터 인덱스 컬럼은 거의 전량 그 표기이므로(797/860 실측) 정상 컬럼까지 UUID로 렌더됐고, * 그래서 **깨진 행(dangling)과 구분이 안 됐다**. * - 인스펙터 `fallbackLabel`은 dbAttr까지 찾았지만 `physicalName || ref`라, **FK 파생 컬럼**(물리명을 * 비우고 부모 PK 컬럼명을 상속하는 정상 형상)에서 빈 문자열을 만나 결국 UUID로 떨어졌다. * * 결과는 *"인덱스 모달에서 FK를 지목할 방법이 없다"*는 사용자 보고였다 — 지목 대상은 목록에 있는데 * (FK는 전파 산출로 속성이 이미 존재한다) **화면의 라벨이 실제 컬럼명을 말해주지 않아** 코드의 * `order_area_code`와 연결할 근거가 없었다. * * ── 규칙 ───────────────────────────────────────────────────────────────────── * 물리명은 항상 **실효 값**(`resolveColumnPhysicalName` = 상속 해소 단일 출처)으로 읽는다. 캔버스 * (`columnResolution`)·DDL·코드젠이 이미 그 함수를 쓰므로 라벨이 산출물과 같은 이름을 보인다. * 표기 두 종(편집 뷰=속성 modelId / 레거시 로드=dbAttr modelId)은 `IndexRefResolver`가 접는다. * * ★i18n 문자열은 이 모듈이 소유하지 않는다 — 호출자가 `t`를 넘긴다(뷰 계층 규약). 순수 함수이므로 * 단위 테스트 대상이다(`view/` ② 순수 헬퍼 분류). */ export interface IndexLabelContext { entity: Entity; logical: LogicalModel; resolver: IndexRefResolver; /** superClass 파생 투영(상속 감사 컬럼). 미주입이면 상속 ref는 원문으로 남는다. */ inherited?: readonly InheritedColumn[]; /** EMBED_PREDEF 서브컬럼 상속 해소용(호스트 주입 카탈로그). */ embeddableCatalog?: EmbeddableCatalog; /** i18n 번역기 — `em.index.colUnspecified`(컬럼 미지정)·`em.index.unnamedAttr`(이름 없는 속성). */ t: Translator; } /** * **후보 목록** 라벨(가용 컬럼 패널·셀렉트 항목) — `속성명 (실효 물리명)`. * 종전 두 뷰의 차이도 여기서 흡수한다: 와이드는 `name (phys)`를, 인스펙터는 `name`만 냈다. * * ★**다중 컬럼 속성은 컬럼 수만 밝힌다** — 종전엔 첫 슬롯 물리명을 대표로 내세워(`name (name_1)`) * *그 컬럼을 넣는다*는 오해를 만들었다. 실제로 후보는 **속성 단위 ref** 라 인덱스에 들어가면 어느 * 컬럼인지 미지정이고(행 라벨이 그렇게 표시한다) 산출물에서 생략된다 ⇒ 두 패널이 같은 말을 하게 한다. * 컬럼 단위로 고를 수 있게 만드는 것은 별 항목(「인덱스 컬럼 단위 어포던스」)이고, 이 표기는 그때까지 * 오해를 만들지 않기 위한 최소 정정이다. */ export declare function candidateColumnLabel(attr: Attribute, ctx: IndexLabelContext): string; /** * **인덱스 컬럼 행** 라벨 — ref가 지목한 대상을 사람이 읽을 이름으로. 판정 순서: * 1. 상속 컬럼 ref(`inherited::`) → 필드명(+ 상속 표기는 호출자 배지 소관) * 2. 속성이 해소되고 **컬럼이 특정되면** → `속성명 (실효 물리명)` * 3. 속성은 있는데 컬럼이 미특정(다중 컬럼 속성 ref) → `속성명 (컬럼 미지정)` * ★어느 컬럼인지는 사용자 결정이므로 임의 슬롯을 고르지 않는다(모르는 값 발명 금지). * 4. 아무것도 못 찾음(dangling, `CHK-DSN-5`) → **ref 원문**. 진단 가능성을 남기는 종전 계약이고, * 이제 정상 컬럼은 전부 이름으로 보이므로 원문 UUID가 곧 *"이 행이 깨졌다"*는 신호가 된다. */ export declare function indexColumnRowLabel(ref: ModelId, ctx: IndexLabelContext): string; /** 인덱스 대상 후보 한 항목 — `ref`는 인덱스 `columnRef`에 그대로 들어간다. */ export interface IndexCandidate { ref: ModelId; label: string; } /** * **인덱스 대상 후보**(속성 축) — 두 편집 뷰가 공유한다. 상속 컬럼은 호출자가 뒤에 붙인다 * (뷰마다 배지·라벨 표기가 달라 이 함수는 로컬 속성만 책임진다). * * ★**다중 컬럼 속성은 컬럼 단위로 펼치고 속성 단위 항목을 내지 않는다**(사용자 결정 (가), 2026-08-28). * 종전엔 속성 하나만 후보로 냈는데, 그걸 고르면 *어느 컬럼인지 미지정*이 되어 DDL·코드젠이 그 인덱스를 * 생략했다 — **만들 수 있는 어포던스가 곧 결함 유입 경로**였다. 컬럼만 펼치면 미지정 상태가 애초에 * 생기지 않는다(이미 저장된 속성표기 ref 는 행 라벨이 「컬럼 N개 중 미지정」으로 계속 표면화한다). * * ⚠️**회귀 수정이다**. 복합 FK 가 형상 B(형제 속성 N)였을 때는 속성 하나에 컬럼 하나가 대응해 **속성 * 단위 후보가 곧 컬럼 단위**였고, 그래서 이 갭이 「실 데이터 발동 0」으로 잠복해 있었다. 전파가 컬럼 * 평탄화로 바뀐 뒤(`367e65e`) 새 복합 FK 가 전부 형상 A 가 되면서 **두 번째 이후 컬럼을 지목할 수단이 * 사라졌다**(사용자 보고, 2026-08-28). * * ★**SHARED_REF 슬롯은 제외** — 그 컬럼은 이 속성이 소유하지 않고 다른 속성의 컬럼을 참조하며(auto * read-only) DDL 에도 방출되지 않는다. 인덱스는 소유 쪽 속성에 걸어야 하므로 여기 노출하면 없는 컬럼을 * 가리키는 인덱스가 만들어진다. */ export declare function indexColumnCandidates(ctx: IndexLabelContext): IndexCandidate[];