@g1cloud/entity-modeler-next 5.0.0-alpha.2 → 5.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 (34) hide show
  1. package/README.md +31 -17
  2. package/dist/adapter/persisted.d.ts +6 -0
  3. package/dist/agent/resolver.d.ts +32 -1
  4. package/dist/agent/symbolicOp.d.ts +47 -0
  5. package/dist/command/commands.d.ts +15 -3
  6. package/dist/core/attributeOrder.d.ts +17 -0
  7. package/dist/core/dbTypes.d.ts +9 -0
  8. package/dist/core/mergeDiagram.d.ts +38 -0
  9. package/dist/core/mermaid.d.ts +6 -0
  10. package/dist/core/migrateLegacyMoney.d.ts +8 -0
  11. package/dist/core/propagation.d.ts +22 -0
  12. package/dist/core/resolve.d.ts +4 -0
  13. package/dist/core/sharedColumn.d.ts +16 -1
  14. package/dist/core/types.d.ts +18 -0
  15. package/dist/editor/controller.d.ts +42 -0
  16. package/dist/entity-modeler-next.css +1 -1
  17. package/dist/entity-modeler.js +10764 -9414
  18. package/dist/entity-modeler.umd.cjs +17 -15
  19. package/dist/index.d.ts +5 -1
  20. package/dist/view/DiagramCanvas.vue.d.ts +2 -0
  21. package/dist/view/EditModeToggle.vue.d.ts +1 -0
  22. package/dist/view/EditorToolbar.vue.d.ts +2 -0
  23. package/dist/view/EntityBasicsSection.vue.d.ts +8 -1
  24. package/dist/view/EntityIndexSection.vue.d.ts +7 -1
  25. package/dist/view/EntityNode.vue.d.ts +1 -0
  26. package/dist/view/EntityOperationsSection.vue.d.ts +7 -1
  27. package/dist/view/GroupNode.vue.d.ts +1 -0
  28. package/dist/view/ShortcutHelpModal.vue.d.ts +2 -0
  29. package/dist/view/{EntityTextPreview.vue.d.ts → TextExportModal.vue.d.ts} +9 -1
  30. package/dist/view/explorerFilter.d.ts +20 -0
  31. package/dist/view/exportImage.d.ts +14 -0
  32. package/dist/view/opConflictNotice.d.ts +12 -0
  33. package/package.json +1 -1
  34. package/dist/view/exportSvg.d.ts +0 -7
package/README.md CHANGED
@@ -1,33 +1,47 @@
1
1
  # entity-modeler-next
2
2
 
3
- Vue 3 기반 비주얼 엔티티 모델링 / 클래스 다이어그램 에디터.
4
- 라이브러리 패키지(`@g1cloud/entity-modeler-next`) 배포되며, `bluework4-tool`(Nuxt/Vue/Mongo)에 임베드된다.
3
+ Vue 3 visual entity-modeling / class-diagram editor.
4
+ Distributed as a library package (`@g1cloud/entity-modeler-next`) and embedded in `bluework4-tool` (Nuxt/Vue/Mongo).
5
5
 
6
- ## 스택
6
+ ## Stack
7
7
 
8
- TypeScript · Vue 3 · **Vue Flow** · Pinia(비강제) · Vite(library mode) · Vitest · view는 `@g1cloud/open-bluesea-core`(BS) 컴포넌트 기반
8
+ TypeScript · Vue 3 · **Vue Flow** · Pinia (opt-in) · Vite (library mode) · Vitest · views built on `@g1cloud/open-bluesea-core` (BS) components
9
9
 
10
- ## 구조
10
+ ## Install
11
+
12
+ ```bash
13
+ pnpm add @g1cloud/entity-modeler-next
14
+ ```
15
+
16
+ Requires the peer dependencies `vue ^3.5.0` and `@g1cloud/open-bluesea-core` (installed by the host).
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { EntityModeler } from '@g1cloud/entity-modeler-next'
22
+ import '@g1cloud/entity-modeler-next/style.css' // required — components render unstyled without it
23
+ ```
24
+
25
+ ## Structure
11
26
 
12
27
  ```
13
28
  src/
14
- core/ 논리/레이아웃 타입(분리)·resolve(정합성)·routing·autolayout·propagation·validation·타입 카탈로그. 프레임워크 무관
15
- command/ Command(do/undo)·CommandStack·op/opSync(시맨틱 op emit + CAS 동시성)
16
- editor/ reactive controller (CommandStack 래핑 컴포저블, EDITOR inject )
17
- view/ Vue Flow 컴포넌트 (DiagramCanvas·EntityNode·GroupNode·AssociationEdge·PropertyPanel·ValidationPanel )
18
- adapter/ 저장 스키마 매핑 (persisted v1/v2·fromPersisted/toPersisted/toPersistedV2 라운드트립)
19
- agent/ 자연어→op 트랙 (symbolicOp·resolver·schema·buildAgentBatch)
20
- dev/ 데모 하니스 (라이브러리 빌드에서 제외)
29
+ core/ logical/layout types (separated) · resolve (consistency) · routing · autolayout · propagation · validation · type catalogs. Framework-agnostic
30
+ command/ Command (do/undo) · CommandStack · op/opSync (semantic op emit + CAS concurrency)
31
+ editor/ reactive controller (composable wrapping CommandStack, EDITOR inject key)
32
+ view/ Vue Flow components (DiagramCanvas · EntityNode · GroupNode · AssociationEdge · PropertyPanel · ValidationPanel, etc.)
33
+ adapter/ storage-schema mapping (persisted v1/v2 · fromPersisted/toPersisted/toPersistedV2 round-trip)
34
+ agent/ natural-language → op track (symbolicOp · resolver · schema · buildAgentBatch)
35
+ dev/ demo harness (excluded from the library build)
21
36
  ```
22
37
 
23
- 핵심 설계: **레이아웃이 논리 모델을 `modelId`로 참조(단방향)**. 논리 모델엔 geo/waypoint/style이 없다.
24
- **현재 구조·상태·문서 인덱스의 정본은 루트 `CLAUDE.md`** (저장 스키마 영향은 `docs/M0-schema-impact.md`).
38
+ Core design: **layout references the logical model by `modelId` (one-way)**. The logical model carries no geo/waypoint/style.
25
39
 
26
- ## 스크립트
40
+ ## Scripts
27
41
 
28
42
  ```bash
29
- pnpm dev # 데모 하니스 dev 서버 (src/dev)
43
+ pnpm dev # demo harness dev server (src/dev)
30
44
  pnpm test # Vitest
31
45
  pnpm typecheck # vue-tsc --noEmit
32
- pnpm build # 라이브러리 빌드 (dist/)
46
+ pnpm build # library build (dist/)
33
47
  ```
@@ -47,6 +47,10 @@ export interface PAttribute {
47
47
  identifier: boolean;
48
48
  notNull: boolean;
49
49
  transient_?: boolean;
50
+ /** Hibernate @NaturalId(자연키/비즈니스 키). 속성 레벨. v1 레거시엔 없는 신규 옵션. */
51
+ naturalId?: boolean;
52
+ /** @NaturalId(mutable=true). undefined/false=불변(Hibernate 기본). */
53
+ naturalIdMutable?: boolean;
50
54
  description?: MultiLangText;
51
55
  attributeGroup?: string;
52
56
  groupCode?: string;
@@ -129,6 +133,7 @@ export interface PEntity {
129
133
  geo?: PGeo;
130
134
  style?: PStyle;
131
135
  collapsed?: boolean;
136
+ locked?: boolean;
132
137
  }
133
138
  export interface PIndex {
134
139
  modelId: string;
@@ -165,6 +170,7 @@ export interface PGroup {
165
170
  notes?: PNote[];
166
171
  geo?: PGeo;
167
172
  style?: PStyle;
173
+ locked?: boolean;
168
174
  }
169
175
  /**
170
176
  * 레거시 NoteModel 직렬화 형태. 연결선은 `ownedConnections`(end1/end2/locations 구조)로 저장되고
@@ -1,4 +1,4 @@
1
- import { LogicalModel, ModelId } from '../core/types';
1
+ import { EmbeddableCatalog, LogicalModel, ModelId } from '../core/types';
2
2
  import { OpShape } from '../command/op';
3
3
  import { AssocHandle, Handle, SymbolicOp } from './symbolicOp';
4
4
  /** 해소 실패 — 어느 심볼릭 op(`opIndex`)의 어떤 핸들이 0개/복수 매칭인지. */
@@ -104,6 +104,31 @@ export type ResolveError = {
104
104
  code: 'pending-entity-ref';
105
105
  opIndex: number;
106
106
  handle: Handle;
107
+ }
108
+ /**
109
+ * embeddableOverrides가 있으나 `type`이 주입된 임베더블 카탈로그의 어떤 엔트리와도 매칭되지 않음
110
+ * (카탈로그 미주입이거나 embeddable 아닌 type). 서브컬럼 오버라이드 silent drop 방지.
111
+ */
112
+ | {
113
+ code: 'embeddable-type-unresolved';
114
+ opIndex: number;
115
+ entity: Handle;
116
+ handle: Handle;
117
+ }
118
+ /** EMBED_PREDEF SHARED_REF 서브컬럼의 공유 대상 물리명이 소속 엔터티 내 컬럼과 0개 매칭. */
119
+ | {
120
+ code: 'shared-column-target-not-found';
121
+ opIndex: number;
122
+ entity: Handle;
123
+ handle: Handle;
124
+ }
125
+ /** EMBED_PREDEF SHARED_REF 공유 대상 물리명이 복수 컬럼과 매칭(모호). */
126
+ | {
127
+ code: 'shared-column-target-ambiguous';
128
+ opIndex: number;
129
+ entity: Handle;
130
+ handle: Handle;
131
+ matches: ModelId[];
107
132
  };
108
133
  export type ResolveResult = {
109
134
  ok: true;
@@ -115,6 +140,12 @@ export type ResolveResult = {
115
140
  export interface ResolverOptions {
116
141
  /** modelId 발급기(주입 시 테스트 결정성). 기본 newId. */
117
142
  mkId?: () => ModelId;
143
+ /**
144
+ * predefined 임베더블 카탈로그(호스트 주입). `attribute.add`/inline fold의 `spec.type`이 엔트리 `type`과
145
+ * 매칭되면 EMBED_PREDEF(다중 dbAttr)로 확장한다. 미주입(기본 [])이면 확장 없이 NORMAL — 카탈로그 없이는
146
+ * type이 embeddable인지 판정 불가하므로 graceful degrade(현행 동작 유지). 서버 resolve 경로가 주입 책임.
147
+ */
148
+ embeddableCatalog?: EmbeddableCatalog;
118
149
  }
119
150
  /**
120
151
  * 심볼릭 op 배치를 OpShape 배치로 해소한다.
@@ -34,6 +34,14 @@ export interface EntitySpec {
34
34
  stereotype?: ClassStereotype;
35
35
  /** 설정 시 `table.physicalName`. */
36
36
  physicalName?: string;
37
+ /**
38
+ * JPA `@Version`(낙관적 락) 사용 여부 → `Entity.jpaAttrs.useVersion`. 코드의 `@Version` 필드는 별도 속성으로
39
+ * 모델링하지 않고 이 엔티티 레벨 플래그로 흡수한다(버전 컬럼은 호스트 코드젠 소유). resolver가 true일 때만
40
+ * `jpaAttrs.useVersion:true`를 세팅(false/미설정은 생략 — round-trip diff 방지). *기존* 엔티티에 켤 땐 이 스펙이
41
+ * 아니라 `entity.update` patch의 **dotted key** `{'jpaAttrs.useVersion': true}`를 써야 형제 jpaAttrs 플래그를
42
+ * 통째 $set로 덮어쓰지 않는다(호스트 opInterpreter는 patch를 per-key $set).
43
+ */
44
+ useVersion?: boolean;
37
45
  /** inline 속성 — 배열 순서가 곧 `order`. GroupCodeEnum+groupCode 규칙은 `attribute.add`와 동일하게 강제. */
38
46
  attributes?: AttrSpec[];
39
47
  /** inline 인덱스 — 컬럼 핸들은 *이 엔티티가 inline으로 만드는* dbAttr 내에서 해소된다(속성 inline 동반 전제). */
@@ -52,6 +60,12 @@ export interface AttrSpec {
52
60
  type: string;
53
61
  identifier?: boolean;
54
62
  notNull?: boolean;
63
+ /**
64
+ * JPA `@Transient` — 비영속 속성. true면 `Attribute.transient:true`(속성 레벨 플래그). 컬럼을 만들지 않는
65
+ * 속성이므로 physicalName/dataType 등 컬럼 필드는 함께 주지 않는다(주면 dbAttr가 생성됨). false/미설정은
66
+ * 생략(round-trip diff 방지). *기존* 속성에 켤 땐 `attribute.update` patch `{transient:true}`로도 가능.
67
+ */
68
+ transient?: boolean;
55
69
  /** 설정 시 단일 dbAttr 컬럼 생성(미설정이면 name을 물리명으로). */
56
70
  physicalName?: string;
57
71
  /** dbAttr 물리 타입(예: 'BIGINT'). physicalName/dataType/length/scale 중 하나라도 있으면 dbAttr 생성. */
@@ -80,6 +94,39 @@ export interface AttrSpec {
80
94
  unique?: boolean;
81
95
  /** dbAttr 갱신 가능 여부(JPA @Column(updatable=)). */
82
96
  updatable?: boolean;
97
+ /**
98
+ * predefined 임베더블 서브컬럼 오버라이드 — `type`이 호스트 주입 임베더블 카탈로그 엔트리와 매칭될 때만
99
+ * 의미. resolver가 카탈로그 fields를 그대로 펼친 뒤(EMBED_PREDEF, 다중 dbAttr) 이 배열을 **필드명**으로
100
+ * 매칭해 서브컬럼을 덮어쓴다(JPA `@AttributeOverride` 대응). 미설정 서브컬럼은 카탈로그 기본을 상속한다.
101
+ * 카탈로그 미매칭 type에 이 배열을 주면 resolver가 `embeddable-type-unresolved`로 거부(silent drop 방지).
102
+ */
103
+ embeddableOverrides?: EmbeddableColumnOverride[];
104
+ }
105
+ /**
106
+ * predefined 임베더블(EMBED_PREDEF)의 개별 서브컬럼 오버라이드. 매칭 키는 카탈로그 필드명
107
+ * (`EmbeddableCatalogField.name`, 예: 'amount'·'currency') — 순서 독립. 미설정 필드는 오버라이드 없음(카탈로그 상속).
108
+ */
109
+ export interface EmbeddableColumnOverride {
110
+ /** 대상 카탈로그 필드명(EmbeddableCatalogField.name). 예: 'amount', 'currency'. */
111
+ field: string;
112
+ /** 서브컬럼 물리명 오버라이드(@AttributeOverride column name). 미설정 시 카탈로그 기본 컬럼명 상속. */
113
+ physicalName?: string;
114
+ /** 서브컬럼 길이 오버라이드. */
115
+ length?: number;
116
+ /** 서브컬럼 소수 자릿수 오버라이드. */
117
+ scale?: number;
118
+ /** JPA @Column(insertable=). OWN 컬럼에만 저장(SHARED_REF는 타깃 파생). */
119
+ insertable?: boolean;
120
+ /** JPA @Column(updatable=). OWN 컬럼에만 저장(SHARED_REF는 타깃 파생). */
121
+ updatable?: boolean;
122
+ /**
123
+ * SHARED_REF — 이 서브컬럼이 자체 물리 컬럼을 갖지 않고 같은 엔터티의 다른 컬럼(**물리명**)을 공유해
124
+ * read-only 투영됨(대표: Money의 currency가 별도 스칼라 통화 컬럼 공유). 설정 시 resolver가 그 물리명을
125
+ * 소속 엔터티 내 기존 컬럼 modelId로 해소해 `DbColumn.sharedColumnRef`로 바인딩하고 physicalName·
126
+ * insertable·updatable은 저장하지 않는다(타깃 파생). 타깃 컬럼은 해소 시점 존재해야 한다(부재=거부).
127
+ * JPA 코드에서 currency 서브컬럼의 `insertable=false && updatable=false`가 이 모드의 시그니처.
128
+ */
129
+ sharedColumnPhysicalName?: string;
83
130
  }
84
131
  /** association.add 페이로드 — from/to 엔티티는 resolver가 해소, modelId 발급 + end 조립. */
85
132
  export interface AssocSpec {
@@ -1,6 +1,12 @@
1
- import { Association, AssociationEnd, AssociationLayout, Attribute, Entity, EntityIndex, Geo, LogicalGroup, ModelId, Note, Operation, Point } from '../core/types';
1
+ import { Association, AssociationEnd, AssociationLayout, Attribute, Entity, EntityIndex, EntityLayout, Geo, GroupLayout, LogicalGroup, ModelId, Note, Operation, Point } from '../core/types';
2
2
  import { Command } from './types';
3
- export declare function addEntity(entity: Entity, geo: Geo): Command;
3
+ /**
4
+ * geo 외 EntityLayout 부가 필드(collapsed·displayMode·style·attributeStyles 등).
5
+ * import 등 레이아웃 스타일까지 복제하는 경로에서 전달. entityRef·geo는 별도 인자라 제외.
6
+ * locked는 뷰 어포던스라 호출자가 필요 시만 포함(import는 드롭).
7
+ */
8
+ export type EntityLayoutExtras = Partial<Omit<EntityLayout, 'entityRef' | 'geo'>>;
9
+ export declare function addEntity(entity: Entity, geo: Geo, extras?: EntityLayoutExtras): Command;
4
10
  /** 엔티티 삭제 — 참조 관계·레이아웃·그룹 멤버십까지 cascade. memento로 위치 보존 복원. */
5
11
  export declare function removeEntity(entityId: ModelId): Command;
6
12
  /** 엔티티 필드 부분 수정 (name·stereotype·package·table·jpaAttrs·description 등). 얕은 patch. */
@@ -21,6 +27,8 @@ export declare function resizeEntity(entityId: ModelId, size: {
21
27
  export declare function setEntitySwatch(entityId: ModelId, token: string | null): Command;
22
28
  /** 엔티티 접기/펼치기 (레이아웃만 변경 — 속성/메서드 칸 렌더 게이트) */
23
29
  export declare function setEntityCollapsed(entityId: ModelId, collapsed: boolean): Command;
30
+ /** 엔티티 편집 잠금 토글 (레이아웃만 변경 — setEntityCollapsed 동형, LWW) */
31
+ export declare function setEntityLocked(entityId: ModelId, locked: boolean): Command;
24
32
  /**
25
33
  * 활성 다이어그램 전체 엔티티 접기/펼치기 (D2) — 사전 per-entity collapsed를 캡처한 단일 커맨드.
26
34
  * N개 개별 커맨드가 아니라 한 번의 undo로 각 엔티티의 직전 상태를 그대로 복원한다(혼재 상태 보존).
@@ -28,7 +36,9 @@ export declare function setEntityCollapsed(entityId: ModelId, collapsed: boolean
28
36
  export declare function setAllEntitiesCollapsed(collapsed: boolean): Command;
29
37
  /** 속성 강조 색상 변경 (레이아웃 attributeStyles[attrId]) */
30
38
  export declare function setAttributeSwatch(entityId: ModelId, attributeId: ModelId, token: string | null): Command;
31
- export declare function addGroup(group: LogicalGroup, geo: Geo): Command;
39
+ /** geo GroupLayout 부가 필드(style ). entityLayout의 EntityLayoutExtras 거울. */
40
+ export type GroupLayoutExtras = Partial<Omit<GroupLayout, 'groupRef' | 'geo' | 'memberNoteRefs'>>;
41
+ export declare function addGroup(group: LogicalGroup, geo: Geo, extras?: GroupLayoutExtras): Command;
32
42
  /** 그룹 필드 부분 수정 (name·description·packageName·excludeDDLGeneration). 얕은 patch. */
33
43
  export declare function updateGroup(groupId: ModelId, patch: Partial<LogicalGroup>): Command;
34
44
  /** 그룹 해제(삭제) — 멤버 엔티티는 남고 그룹/박스만 제거 */
@@ -42,6 +52,8 @@ export declare function resizeGroup(groupId: ModelId, size: {
42
52
  }): Command;
43
53
  /** 그룹 색상 변경 (레이아웃만 변경 — setEntitySwatch와 동형) */
44
54
  export declare function setGroupSwatch(groupId: ModelId, token: string | null): Command;
55
+ /** 그룹 편집 잠금 토글 (레이아웃만 변경 — setEntityLocked 거울, LWW) */
56
+ export declare function setGroupLocked(groupId: ModelId, locked: boolean): Command;
45
57
  export declare function addAttribute(entityId: ModelId, attribute: Attribute): Command;
46
58
  /** 속성 필드 부분 수정 (name·type·dataType·dbAttrs·identifier·notNull 등). 얕은 patch. */
47
59
  export declare function updateAttribute(entityId: ModelId, attributeId: ModelId, patch: Partial<Attribute>): Command;
@@ -0,0 +1,17 @@
1
+ import { Attribute, Entity } from './types';
2
+ /** 물리 FK 컬럼을 가진 FK 여부 — derivedFrom(관계 파생) + dbAttrs(물리 컬럼) 보유. */
3
+ export declare function isPhysicalFk(a: Attribute): boolean;
4
+ /**
5
+ * 물리 FK 컬럼을 가진 FK를 순수 PK 바로 뒤로 당긴다. `순수 PK → 물리 FK → 나머지` 순으로 재배열하되,
6
+ * FK끼리·나머지끼리의 상대 순서는 원본 order를 보존한다(순서를 새로 추정하지 않음). 순수 PK가 없으면
7
+ * FK를 맨 앞으로. 물리 컬럼 없는 nav 참조(dbAttrs=0)는 추정이라 제외 — 실제로 그런 참조는 어느 엔티티
8
+ * attributes에도 물질화되지 않으므로(유령 앵커 참조) 대상이 되는 일도 없다.
9
+ *
10
+ * ★ order 필드뿐 아니라 **배열 물리 순서 자체**를 재정렬한다. 캔버스 노드(EntityNode)는 attributes를
11
+ * 배열 순서 그대로 렌더하고(order 정렬 미적용), 코드젠·탐색기는 order로 정렬하므로, 둘을 일치시켜야
12
+ * 모든 표시 경로에서 순서가 같다. [[attribute-display-order-array-vs-order-field]]
13
+ *
14
+ * 경계 강등(mergeDiagram)으로 derivedFrom이 제거된 FK는 `isPhysicalFk`가 false라 일반 컬럼으로 제자리에
15
+ * 남는다 — 강등 이후에 호출해야 한다.
16
+ */
17
+ export declare function reorderForeignAttributes(entities: Entity[]): void;
@@ -31,6 +31,15 @@ export declare function dbTypeLabel(value: string): string;
31
31
  * TEXT류(LONGVARCHAR·CLOB·BLOB)·고정 폭 타입(INTEGER·DATE·BOOLEAN 등)은 길이 무의미라 제외.
32
32
  */
33
33
  export declare const LENGTH_REQUIRED_DATA_TYPES: ReadonlySet<string>;
34
+ /**
35
+ * scale(소수점 이하 자릿수)이 의미를 갖는 타입 — 정확 수치형(NUMERIC·DECIMAL)뿐이다. 여기서 length는
36
+ * precision(전체 자릿수), scale은 그중 소수부. VARCHAR·CHAR 등 문자/이진 길이 타입과 정수·날짜·불리언
37
+ * 같은 고정 폭 타입엔 scale이 무의미하고, 근사 수치형(FLOAT·REAL·DOUBLE)은 precision만 가질 뿐 scale이 없다.
38
+ *
39
+ * 편집 UI가 이 집합 밖 타입에서 scale 입력을 비활성화하는 어포던스의 단일 출처(타입 메타데이터는 이 카탈로그
40
+ * 소유 — LENGTH_REQUIRED_DATA_TYPES와 대칭). 타입을 이 집합 밖으로 바꾸면 무의미해진 scale은 자동 환원한다.
41
+ */
42
+ export declare const SCALE_APPLICABLE_DATA_TYPES: ReadonlySet<string>;
34
43
  /**
35
44
  * dataType 셀렉트 items — 실 컬럼 후보 + 현재값 폴백 합성.
36
45
  *
@@ -0,0 +1,38 @@
1
+ import { Association, AssociationLayout, DiagramLayout, Entity, EntityLayout, GroupLayout, LogicalGroup, LogicalModel, ModelId, Point } from './types';
2
+ export interface MergeDiagramSource {
3
+ logical: LogicalModel;
4
+ /** 소스 다이어그램 레이아웃 — 임포트 서브그래프의 좌표/스타일을 함께 옮긴다. */
5
+ layout: DiagramLayout;
6
+ }
7
+ export interface MergeDiagramOptions {
8
+ /** depth-1 관련 엔티티(경계 관계의 반대편)를 선택집합에 함께 끌어와 관계를 유지(D3 옵션 토글). */
9
+ includeRelated?: boolean;
10
+ /** 병합 대상(타깃) 논리 모델 — 이름/테이블 물리명 충돌 감지에 사용(D7). */
11
+ existing: LogicalModel;
12
+ /** 임포트 서브그래프 좌표에 더할 오프셋(D8 — 빈 영역 배치는 호출자가 계산해 전달). */
13
+ offset?: Point;
14
+ /** 결정적 테스트를 위한 id 생성기 주입(기본 newId). */
15
+ idFactory?: () => ModelId;
16
+ }
17
+ export interface MergeDiagramResult {
18
+ logical: {
19
+ entities: Entity[];
20
+ associations: Association[];
21
+ groups: LogicalGroup[];
22
+ };
23
+ layout: {
24
+ entityLayouts: EntityLayout[];
25
+ associationLayouts: AssociationLayout[];
26
+ groupLayouts: GroupLayout[];
27
+ };
28
+ /** 소스 modelId → 임포트 modelId 매핑(컨트롤러 진입점·후속 배선용). */
29
+ idMap: Map<ModelId, ModelId>;
30
+ }
31
+ /**
32
+ * 소스 다이어그램의 선택 서브그래프를 새 id 체계로 재매핑해 반환한다.
33
+ *
34
+ * @param source 소스 다이어그램(로드된 논리 + 레이아웃)
35
+ * @param selectedIds 임포트할 엔티티 modelId 목록(부분 선택 — 전량은 전 엔티티 id 전달)
36
+ * @param opts 경계/충돌/오프셋 정책
37
+ */
38
+ export declare function mergeDiagram(source: MergeDiagramSource, selectedIds: ModelId[], opts: MergeDiagramOptions): MergeDiagramResult;
@@ -0,0 +1,6 @@
1
+ import { DisplayMode, LogicalModel } from './types';
2
+ export type MermaidDialect = 'er' | 'class';
3
+ /** 표시 모드 → 기본 Mermaid 방언. CLASS만 클래스 다이어그램, 나머지(논리·물리·논리·물리)는 ER. */
4
+ export declare function defaultMermaidDialect(mode: DisplayMode): MermaidDialect;
5
+ /** 논리 모델을 지정 방언의 Mermaid 소스로 직렬화. */
6
+ export declare function entityModelToMermaid(logical: LogicalModel, dialect: MermaidDialect): string;
@@ -0,0 +1,8 @@
1
+ import { LogicalModel } from './types';
2
+ /**
3
+ * 논리 모델 내 전 레거시 Money 속성을 신규 Money embeddable로 승격한 **새 모델**을 반환(순수).
4
+ * 대상이 없으면 입력 참조를 그대로 반환(no-op). 레이아웃은 논리 modelId만 참조하고 dbAttr을 참조하지
5
+ * 않으므로 무변경 — 호출자(useMigrateEntityDiagram)는 `{ ...state, logical: migrateLegacyMoney(state.logical) }`.
6
+ * amount 슬롯이 레거시 컬럼 modelId를 보존하므로 인덱스(IndexColumn.columnRef) 참조도 안전.
7
+ */
8
+ export declare function migrateLegacyMoney(logical: LogicalModel): LogicalModel;
@@ -47,3 +47,25 @@ export declare function reconcileForeignAttributes(logical: LogicalModel, childE
47
47
  * self-association은 비식별이라 자식 식별자를 바꾸지 않아 더 전파되지 않는다(자연 종료).
48
48
  */
49
49
  export declare function planPropagation(logical: LogicalModel, changedEntityId: ModelId, idFactory?: IdFactory): Map<ModelId, ReconcilePlan>;
50
+ /**
51
+ * 주어진 속성이 "연관관계가 관리하는 FK 컬럼"이면 그 소유 연관관계를 돌려준다(아니면 undefined).
52
+ *
53
+ * 논리 모델러 계보(ERwin/ER-Studio/PowerDesigner)에서 FK 컬럼은 관계가 migrate한 파생 속성이라
54
+ * 관계가 소유한다 — 컬럼을 직접 지우는 게 아니라 관계를 삭제/변경해 제거해야 한다. 이 판별로
55
+ * FK 속성의 직접 삭제를 차단(어포던스+가드)하고 사용자를 관계 삭제 경로로 유도한다.
56
+ *
57
+ * FK 컬럼 불변식(RELATION* + 물리 컬럼 보유, EntityNode.keyMarks와 동형)으로 먼저 게이트해, 관계의
58
+ * 부모측 end가 가리키는 PK(비-RELATION) 오탐을 배제한다. 그다음 두 신호로 소유 관계를 찾는다:
59
+ *
60
+ * 1순위 `derivedFrom.associationRef` — 전파가 심은 권위 신호. v2 네이티브 doc에 보존되고 어댑터
61
+ * hydration이 복원한다. GUI·sync 스킬·에이전트 어느 경로로 만들어졌든 FK 자신에 붙어 있어,
62
+ * 관계 end의 `attributeRef`가 stale해도(자기참조 재생성·sync가 링크를 다르게 심은 경우 등) 정확하다.
63
+ * 2순위 관계 end의 `attributeRef` 역참조 — `derivedFrom`이 없는 경우(레거시 v1 로드 FK 등)의 폴백.
64
+ * 방향 규약은 end2=자식이지만 드문 로드 형태가 end1에 실을 수 있어 양 end를 모두 검사한다.
65
+ *
66
+ * (초기엔 attributeRef만 신뢰했으나, sync 스킬로 만든 자기참조 FK가 `derivedFrom`은 정확한데
67
+ * end2.attributeRef는 옛 FK id를 가리키는 실사례로 derivedFrom을 1순위로 승격.)
68
+ *
69
+ * `derivedFrom`이 이미 삭제된 관계를 가리키거나(고아) 둘 다 매칭 없으면 undefined → 직접 삭제(정리) 허용.
70
+ */
71
+ export declare function findFkOwningAssociation(logical: LogicalModel, entityId: ModelId, attributeId: ModelId): Association | undefined;
@@ -3,6 +3,8 @@ export interface ResolvedEntity {
3
3
  entity: Entity;
4
4
  geo: Geo;
5
5
  collapsed: boolean;
6
+ /** 편집 잠금 (layout.locked 통과) — 캔버스 draggable·인스펙터 read-only 게이트 */
7
+ locked: boolean;
6
8
  /** 이 엔티티가 속한 그룹(있으면) — 캔버스가 parent 노드로 배치 */
7
9
  groupRef?: ModelId;
8
10
  /** 헤더 바 색상 등 (레이아웃 style 참조 통과 — swatch 변경 시 computed 무효화) */
@@ -23,6 +25,8 @@ export interface ResolvedGroup {
23
25
  geo: Geo;
24
26
  /** 그룹 배경 색상 등 (레이아웃 style 참조 통과 — swatch 변경 시 computed 무효화) */
25
27
  style?: Style;
28
+ /** 편집 잠금 (layout.locked 통과) — 그룹 draggable·논리수정 게이트 */
29
+ locked: boolean;
26
30
  }
27
31
  export interface ResolvedNoteConnection {
28
32
  noteRef: ModelId;
@@ -1,4 +1,4 @@
1
- import { DbColumn, ModelId } from './types';
1
+ import { DbColumn, Entity, ModelId } from './types';
2
2
  /** SHARED_REF 컬럼의 파생 투영값. insertable/updatable는 정의상 항상 false. */
3
3
  export interface ResolvedSharedColumn {
4
4
  /** 타깃 컬럼의 물리명(이 컬럼이 read-only로 투영되는 실제 물리 컬럼). */
@@ -6,9 +6,24 @@ export interface ResolvedSharedColumn {
6
6
  insertable: false;
7
7
  updatable: false;
8
8
  }
9
+ /**
10
+ * SHARED_REF 후보·해석의 **컬럼 스코프 엔터티** — "임베드 평탄화 후 같은 물리 테이블에 들어가는
11
+ * 컬럼을 가진 엔터티들". 일반 엔터티는 자기 자신뿐이지만, JPA_EMBEDDABLE 엔터티는 @Embedded로 owner
12
+ * 테이블에 평탄화되므로 owner 엔터티(자기를 EMBED_OWN 표시속성으로 임베드한 엔터티)의 컬럼도 같은
13
+ * 테이블 스코프다 → embeddable 안의 Money.currency가 owner의 통화 writer 컬럼을 SHARED_REF로 공유할 수
14
+ * 있다(BNKR_SALES 실측: OrderAmount·CancellationAmtDetail 등 수많은 embeddable이 owner의 단일 통화
15
+ * 컬럼을 공유하는 지배적 패턴). owner 판별 = `embedded.embeddableRef === 이 엔터티 modelId`인 EMBED_OWN
16
+ * 표시속성 보유(adapter가 owner에 push, diagramAdapter.ts:428). 다중 owner(재사용, 결정 B-1)면 합집합 —
17
+ * 실효 물리명이 같으면 각 usage의 @AttributeOverride가 정상 emit되고 어긋나면 dangling으로 표면화된다.
18
+ * 일반 엔터티엔 [self]만 반환 → 기존 동작 무회귀.
19
+ */
20
+ export declare function sharedColumnScopeEntities(entity: Entity, entities: readonly Entity[]): Entity[];
21
+ /** 스코프 엔터티들의 전 컬럼 평탄화 — 해석 인덱스(`indexColumnsByModelId`) 입력. */
22
+ export declare function collectSharedColumns(entity: Entity, entities: readonly Entity[]): DbColumn[];
9
23
  /**
10
24
  * 같은 엔터티의 컬럼들을 modelId로 인덱싱 — picker 후보 해석·SHARED_REF 타깃 조회 공용 인덱스.
11
25
  * EMBED 펼친 dbAttrs를 포함한 전 컬럼을 평탄화하므로 SHARED_REF 타깃이 임베드 내부여도 조회된다.
26
+ * (embeddable→owner 크로스 스코프가 필요하면 `collectSharedColumns`로 입력을 넓혀 넘긴다.)
12
27
  */
13
28
  export declare function indexColumnsByModelId(columns: readonly DbColumn[]): Map<ModelId, DbColumn>;
14
29
  /**
@@ -141,6 +141,17 @@ export interface Attribute {
141
141
  identifier: boolean;
142
142
  notNull: boolean;
143
143
  transient?: boolean;
144
+ /**
145
+ * Hibernate `@NaturalId` — 이 속성이 엔터티의 자연키(비즈니스 키)임을 표시. PK(@Id)와는 별개 개념
146
+ * (자연키는 정의상 대리키가 아닌 도메인 유일 식별자). 프로퍼티 어노테이션이라 컬럼 레벨(unique)이 아니라
147
+ * 속성 레벨. false는 undefined로 환원해 라운드트립 diff 방지. v1 레거시엔 없는 신규 옵션(forward).
148
+ */
149
+ naturalId?: boolean;
150
+ /**
151
+ * `@NaturalId(mutable = true)` — 자연키 값의 변경 허용. Hibernate 기본은 불변(mutable=false)이므로
152
+ * undefined/false면 불변(어노테이션 인자 생략). naturalId=false일 땐 무의미(직렬화 시 정리).
153
+ */
154
+ naturalIdMutable?: boolean;
144
155
  description?: MultiLangText;
145
156
  attributeGroup?: string;
146
157
  groupCode?: string;
@@ -340,6 +351,11 @@ export interface EntityLayout {
340
351
  entityRef: ModelId;
341
352
  geo: Geo;
342
353
  collapsed?: boolean;
354
+ /**
355
+ * 편집 잠금 — 이동·크기변경·논리수정·삭제를 차단(뷰 어포던스 계층). 논리 무관·layout LWW.
356
+ * op 인터프리터·undo·replay·복원은 무시한다(간접/시스템 쓰기는 잠금 대상 아님).
357
+ */
358
+ locked?: boolean;
343
359
  displayMode?: DisplayMode;
344
360
  /** 엔티티 헤더 바 색상 등 (style.properties.swatch = 토큰명) */
345
361
  style?: Style;
@@ -362,6 +378,8 @@ export interface GroupLayout {
362
378
  groupRef: ModelId;
363
379
  geo: Geo;
364
380
  style?: Style;
381
+ /** 편집 잠금 — 그룹 박스 이동·크기변경·그룹 논리수정·삭제 차단(EntityLayout.locked 거울). */
382
+ locked?: boolean;
365
383
  /**
366
384
  * 그룹 박스에 시각적으로 포함된 노트 멤버십(레거시 `groups[].notes`). 노트는 순수 레이아웃
367
385
  * 객체(논리 의미 없음)라 멤버십도 레이아웃에 둔다 — 엔티티 멤버십(LogicalGroup.memberEntityRefs)과 비대칭.
@@ -6,8 +6,14 @@ import { OpSyncTransport } from '../command/opSync';
6
6
  import { FieldAuditRecord } from '../core/projectFieldAudit';
7
7
  import { ResolvedDiagram } from '../core/resolve';
8
8
  import { RoutingMode } from '../core/routing';
9
+ import { MergeDiagramSource } from '../core/mergeDiagram';
9
10
  import { ValidationIssue } from '../core/validation';
10
11
  import { AssociationEnd, Attribute, DisplayMode, EmbeddableCatalog, Entity, EntityIndex, Geo, GroupCodeCatalog, LangCode, LogicalGroup, ModelId, Operation, Point, PredefinedCustomTypeCatalog } from '../core/types';
12
+ /**
13
+ * 관계 파생 FK 직접 삭제 시도 시의 안내 — 뷰 tooltip(어포던스)과 차단 시 토스트(피드백)가 공유한다.
14
+ * FK는 관계가 소유·관리하므로 컬럼을 직접 지우는 대신 연관관계선을 삭제해야 한다(Option B).
15
+ */
16
+ export declare const FK_MANAGED_DELETE_MESSAGE = "\uC678\uB798\uD0A4\uB294 \uC5F0\uAD00\uAD00\uACC4\uC5D0\uC11C \uD30C\uC0DD\uB429\uB2C8\uB2E4. \uC0AD\uC81C\uD558\uB824\uBA74 \uC5F0\uAD00\uAD00\uACC4\uC120\uC744 \uC120\uD0DD\uD574 \uC0AD\uC81C\uD558\uC138\uC694.";
11
17
  /** 엔티티 내부 속성을 가리키는 주소 원자 — 선택·컨텍스트·재정렬·연결 등 속성 단위 기능의 공통 참조점. */
12
18
  export type AttrRef = {
13
19
  entityId: ModelId;
@@ -76,6 +82,14 @@ export interface EditorController {
76
82
  dirty: Ref<boolean>;
77
83
  /** 모델 변경 신호 — 명령 적용마다 단조 증가. 외부 관찰자(감사 패널 등)가 재조회 트리거로 watch(디바운스 권장). */
78
84
  revision: Ref<number>;
85
+ /**
86
+ * 사용자에게 표시할 일시적 안내(차단된 동작 등) — 뷰가 구독해 토스트로 표시하고 자체 타이머로 dismiss.
87
+ * `id`는 같은 `text` 반복 시에도 재표시를 트리거하기 위한 단조 증가 값(동일 값 재대입은 watch 미발화이므로).
88
+ */
89
+ notice: Ref<{
90
+ text: string;
91
+ id: number;
92
+ } | null>;
79
93
  /**
80
94
  * 편집 가능 여부 — false(조회 모드)면 모든 모델 변경(추가·삭제·이동·편집·재정렬·연결·붙여넣기·
81
95
  * undo/redo·저장)이 차단된다. 선택·팬/줌·hover 강조·복사·export 같은 읽기성 동작은 유지.
@@ -208,6 +222,10 @@ export interface EditorController {
208
222
  setEntitySwatch(id: ModelId, token: string | null): void;
209
223
  /** 엔티티 접기/펼치기 — 접으면 헤더만 표시하고 속성/메서드 칸을 숨긴다 */
210
224
  setEntityCollapsed(id: ModelId, collapsed: boolean): void;
225
+ /** 엔티티 편집 잠금 토글 — 잠그면 이동·크기·논리수정·삭제 차단(잠금 해제는 항상 허용) */
226
+ setEntityLocked(id: ModelId, locked: boolean): void;
227
+ /** 엔티티 잠금 여부 조회 (레이아웃) */
228
+ isEntityLocked(id: ModelId): boolean;
211
229
  /** 활성 다이어그램 전체 엔티티 접기/펼치기 — 사전 상태 캡처 단일 커맨드(1 undo 스텝) */
212
230
  collapseAll(): void;
213
231
  expandAll(): void;
@@ -226,6 +244,10 @@ export interface EditorController {
226
244
  updateGroup(groupId: ModelId, patch: Partial<LogicalGroup>): void;
227
245
  /** 그룹 박스 색상 토큰 지정 (null이면 해제 → 기본 그룹 배경) */
228
246
  setGroupSwatch(groupId: ModelId, token: string | null): void;
247
+ /** 그룹 편집 잠금 토글 — 잠그면 그룹 박스 이동·크기·그룹 논리수정·삭제 차단 */
248
+ setGroupLocked(groupId: ModelId, locked: boolean): void;
249
+ /** 그룹 잠금 여부 조회 (레이아웃) */
250
+ isGroupLocked(groupId: ModelId): boolean;
229
251
  /** 선택 엔티티들로 새 그룹 생성 (멤버 bbox에 박스 배치) */
230
252
  createGroup(entityIds: ModelId[], name?: string): ModelId | null;
231
253
  /** 빈 그룹 박스 생성 (이후 드래그로 엔티티 편입) */
@@ -387,6 +409,14 @@ export interface EditorController {
387
409
  clipboard: Ref<ClipboardContent | null>;
388
410
  copy(): void;
389
411
  paste(): void;
412
+ /**
413
+ * 타 다이어그램(비즈모듈)의 선택 서브그래프를 현재 다이어그램에 병합 임포트(G1).
414
+ * 깊은 복사(새 id) + 경계 강등 + 물리명 충돌 suffix는 순수 `mergeDiagram`이 수행하고,
415
+ * 여기서는 결과를 기존 다이어그램 우측 빈 영역에 배치해 단일 composite로 실행한다(재전파 없음 — D6).
416
+ */
417
+ importEntities(source: MergeDiagramSource, selectedIds: ModelId[], opts?: {
418
+ includeRelated?: boolean;
419
+ }): void;
390
420
  }
391
421
  export declare const EDITOR: InjectionKey<EditorController>;
392
422
  export declare function emptyState(modelId?: string, diagramId?: string): EditorState;
@@ -410,6 +440,18 @@ export declare function createEditorController(initial?: EditorState, options?:
410
440
  historyTransport?: HistoryTransport;
411
441
  /** 시점 복원 transport(`restoreEntityDiagram` 라우트). 주입 + op-mode + editable 시 감사 패널 '복원' 활성. */
412
442
  restoreTransport?: RestoreTransport;
443
+ /**
444
+ * op 전송 실패(4xx/5xx/네트워크로 transport가 throw) 통지 — 진단용(opt-in). 하드 프리즈는 별도로
445
+ * 발생하며(opConflict.kind='missing'), 이 콜백은 소실될 원 에러를 호스트가 로깅/리포팅하도록 넘긴다.
446
+ * 미주입이어도 어댑터가 콘솔에 남긴다(정상 rev/layout 충돌은 이 경로가 아님 — throw만).
447
+ */
448
+ onOpError?: (err: unknown) => void;
449
+ /**
450
+ * 사용자 안내 통지(차단된 동작 등) — 주입 시 호스트가 자신의 토스트/알림 시스템으로 표시하고,
451
+ * lib 내장 토스트는 표시하지 않는다(호스트에서 서버 에러 토스트 등과 UX 일원화). 미주입(dev
452
+ * 하니스·미배선 호스트)이면 lib이 `notice` ref로 자체 토스트를 표시한다. `onOpError`와 동형 seam.
453
+ */
454
+ onNotice?: (message: string) => void;
413
455
  /**
414
456
  * 검증 컨텍스트 — 카탈로그/정책 의존 규칙을 깨우는 호스트 주입(미주입 시 해당 규칙 skip).
415
457
  * selectableDataTypes(CHK-TYPE-1)는 코어 상수(SELECTABLE_DATA_TYPES)라 dev/호스트가 즉시 주입 가능.