@g1cloud/entity-modeler-next 5.2.51 → 5.2.53
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/agent/resolver.d.ts +52 -3
- package/dist/core/embedGraph.d.ts +19 -0
- package/dist/core/scaffoldJava.d.ts +15 -0
- package/dist/editor/association.d.ts +5 -0
- package/dist/entity-modeler-next.css +1 -1
- package/dist/entity-modeler.js +11696 -11575
- package/dist/entity-modeler.umd.cjs +42 -42
- package/dist/i18n/ko.d.ts +2 -0
- package/package.json +1 -1
package/dist/agent/resolver.d.ts
CHANGED
|
@@ -122,15 +122,34 @@ export type ResolveError = {
|
|
|
122
122
|
*
|
|
123
123
|
* ★**연쇄 임베드(임베더블 → «다른» 임베더블)와 갈라야 한다** — 그쪽은 **정당하고 실재한다**
|
|
124
124
|
* (프로덕션 v2 43문서 실측: 연쇄 **3** · 자기 임베드 **0** — `ApplyPriceDcPromo.dcRule → PromoDcRule` 등).
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
125
|
+
*
|
|
126
|
+
* ★★**판정은 이제 `core/embedGraph.findEmbedCycle` 이 한다** — GUI 와 «같은 술어»다. 종전엔 이 자리가
|
|
127
|
+
* `from.modelId === to.modelId` 단순 비교였고 GUI 는 임베더블↔임베더블을 통째로 무시해서, **두 경로가
|
|
128
|
+
* 서로 다른 것을 막고 둘 다 간접 순환(A→B→A)을 못 봤다**(자기 비교는 1단만 본다). 그 GUI 쪽 차단은
|
|
129
|
+
* 해제됐고(연쇄는 정당), 남은 거부가 이 코드와 아래 `embed-cycle-unsupported` 둘이다.
|
|
128
130
|
*/
|
|
129
131
|
| {
|
|
130
132
|
code: 'embed-self-unsupported';
|
|
131
133
|
opIndex: number;
|
|
132
134
|
handle: AssocHandle;
|
|
133
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* 임베드가 **순환**을 이룬다 — 자기 임베드의 «간접» 형태(A 가 B 를 품고 B 가 A 를 품는다).
|
|
138
|
+
*
|
|
139
|
+
* ★**코드를 자기 임베드와 가르는 이유**는 호출자가 **고칠 자리가 다르기** 때문이다: 자기 임베드는
|
|
140
|
+
* 보낸 op 자체가 잘못이지만, 순환은 **이미 저장된 반대 방향 임베드**가 상대편이라 그쪽을 먼저 정리해야
|
|
141
|
+
* 한다. 그래서 `path` 에 순환을 이루는 엔터티 **이름**을 순서대로 담는다(모호할 때 사람이 고를 수 있는
|
|
142
|
+
* 지목 수단 — modelId 나열만으론 어느 자리를 지울지 못 고른다).
|
|
143
|
+
*
|
|
144
|
+
* ⚠️**같은 배치 안에서 나눠 보내는 우회도 막는다** — `A→B` 와 `B→A` 를 두 op 으로 보내면 각 op 은
|
|
145
|
+
* 저장 전 모델만 보고 무해해 보인다. 선행 op 이 만든 간선을 `pending` 으로 함께 넘겨 판정한다.
|
|
146
|
+
*/
|
|
147
|
+
| {
|
|
148
|
+
code: 'embed-cycle-unsupported';
|
|
149
|
+
opIndex: number;
|
|
150
|
+
handle: AssocHandle;
|
|
151
|
+
path: string[];
|
|
152
|
+
}
|
|
134
153
|
/**
|
|
135
154
|
* `association.add` 의 end 중 하나가 `SERIALIZED_TYPE` — **거부 사유가 임베더블과 다르다**.
|
|
136
155
|
*
|
|
@@ -323,6 +342,36 @@ export type ResolveError = {
|
|
|
323
342
|
entity: Handle;
|
|
324
343
|
handle: Handle;
|
|
325
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* `@MapsId` 파생 식별이 **방출 시점에 합성하는 스칼라 `@Id` 필드명**과 같은 이름으로 속성을 만들려 함.
|
|
347
|
+
*
|
|
348
|
+
* ★그 필드는 **모델 속성이 아니다** — 모델은 FK 속성 «하나»만 갖고 코드젠이 두 필드를 낸다
|
|
349
|
+
* (`core/scaffoldJava.derivedIdScalarNameOf`). 그대로 통과시키면 관용구⑤(FK-as-PK) 형상이 되어
|
|
350
|
+
* 같은 물리 컬럼을 두 속성이 쓰고 `CHK-NAME-2`(컬럼 중복) error + DDL 중복으로 착지한다.
|
|
351
|
+
*
|
|
352
|
+
* ⚠️★**유입 경로는 「오독」이 아니라 「규칙 준수」다** — sync 의 diff 규칙이 「코드에 있고 모델에
|
|
353
|
+
* 없음 → `attribute.add`」이고 코드에는 그 스칼라가 실재한다. 그래서 방어가 **세 겹**이다:
|
|
354
|
+
* `/text` 의 `derivedIdField=` 표시(읽는 쪽이 존재를 안다) · playbook 예외(규칙이 스스로 면제한다) ·
|
|
355
|
+
* **이 거부**(둘 다 안 읽혔을 때의 구조적 차단). 앞 둘은 준수에 기대고 이것만 기대지 않는다.
|
|
356
|
+
*
|
|
357
|
+
* ★★**거부는 «갈 곳»을 함께 말한다** — `fk` 가 그 스칼라를 내는 FK 속성명이다. 필드명을 바꾸려는
|
|
358
|
+
* 의도였다면 답은 새 속성이 아니라 그 FK 에 대한 `attribute.update {derivedIdFieldName}` 이다.
|
|
359
|
+
*
|
|
360
|
+
* ⚠️★**`attribute.update {name}` 의 rename 은 «의도적으로» 막지 않는다** — 이 축의 유입원인 sync 는
|
|
361
|
+
* 속성을 **이름으로 매칭**하므로 rename op 자체를 낼 수 없다(이름이 다르면 add/remove 로 갈린다 —
|
|
362
|
+
* playbook §4 의 「수정」 비교 필드에 `name` 이 없는 이유가 그것이다) ⇒ 남는 경로는 **사람이 지시한
|
|
363
|
+
* AI 조작**뿐이고 그때는 의도가 있다. 발동 근거 0 인 가드를 세우지 않는 것이 이 리포 규율이다.
|
|
364
|
+
* ⚠️단 그 경로의 착지점은 **무신호**다(물리명은 안 겹쳐 `CHK-NAME-2` 가 안 울리고, 스칼라는 모델에
|
|
365
|
+
* 없어 `CHK-NAME-1` 도 못 본다 — 코드젠 산출에서 필드명이 겹쳐 컴파일이 깨진다).
|
|
366
|
+
* 트리거 = **사람 지시 rename 으로 그 형상이 실제로 관측될 때**.
|
|
367
|
+
*/
|
|
368
|
+
| {
|
|
369
|
+
code: 'derived-id-scalar-conflict';
|
|
370
|
+
opIndex: number;
|
|
371
|
+
entity: Handle;
|
|
372
|
+
handle: Handle;
|
|
373
|
+
fk: string;
|
|
374
|
+
}
|
|
326
375
|
/** EMBED_PREDEF SHARED_REF 서브컬럼의 공유 대상 물리명이 소속 엔터티 내 컬럼과 0개 매칭. */
|
|
327
376
|
| {
|
|
328
377
|
code: 'shared-column-target-not-found';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Entity, ModelId } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 아직 모델에 반영되지 않은 임베드 간선 — AI 배치의 **같은 배치 선행 op** 산출분.
|
|
4
|
+
*
|
|
5
|
+
* ★이게 없으면 한 배치 안에서 `A→B`·`B→A` 를 나눠 보내 순환을 만들 수 있다(각 op 은 저장 전 모델만
|
|
6
|
+
* 보므로 혼자서는 무해해 보인다). resolver 가 `addedAttributes` 에서 뽑아 넘긴다.
|
|
7
|
+
*/
|
|
8
|
+
export interface PendingEmbedEdge {
|
|
9
|
+
ownerId: ModelId;
|
|
10
|
+
embeddableRef: ModelId;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `ownerId` 가 `embeddableId` 를 임베드하면 순환이 되는가 — 되면 **그 순환 경로**, 아니면 `null`.
|
|
14
|
+
*
|
|
15
|
+
* 경로는 `findIdentifyingCycles`(validation)의 관례를 따라 **닫힘 반복 없이 멤버만** 담는다:
|
|
16
|
+
* 자기 임베드는 `[A]`, `A→B→A` 는 `[A, B]`. 호출자는 `!= null` 로 거부하고, 필요하면 그 경로를
|
|
17
|
+
* 메시지·에러 payload 로 쓴다(«고칠 수 있는 자리»를 지목하기 위한 정보다).
|
|
18
|
+
*/
|
|
19
|
+
export declare function findEmbedCycle(entities: readonly Entity[], ownerId: ModelId, embeddableId: ModelId, pending?: readonly PendingEmbedEdge[]): ModelId[] | null;
|
|
@@ -187,6 +187,21 @@ export declare function isMapsIdFk(fk: Attribute, assoc: Association | undefined
|
|
|
187
187
|
* (호출부가 fillIn으로 표면화).
|
|
188
188
|
*/
|
|
189
189
|
export declare function mapsIdScalarName(fk: Attribute, assoc: Association, byId: Map<ModelId, Entity>): string;
|
|
190
|
+
/**
|
|
191
|
+
* 이 속성이 **방출 시점에 합성되는** `@MapsId` 스칼라 `@Id` 를 낳으면 그 **필드명**, 아니면 빈 문자열.
|
|
192
|
+
*
|
|
193
|
+
* ★**「모델 속성 하나가 Java 필드 «둘» 을 낸다」의 단일 출처**다 — 판정(`isMapsIdFk`)·관계 조달
|
|
194
|
+
* (`findFkOwningAssociation`)·이름 파생(`mapsIdScalarName`) 셋을 한 자리에 묶어, 소비처가 조건을
|
|
195
|
+
* 재기술하지 않게 한다. 재기술하면 축이 갈린다(이 리포가 반복해서 대가를 치른 형태).
|
|
196
|
+
*
|
|
197
|
+
* ⚠️**소비처는 같은 술어를 쓰되 처방이 다르다**: 코드젠은 **방출**하고(`buildClassFieldPlan`),
|
|
198
|
+
* 캔버스는 코드명으로 **표시**하며(`view/columnResolution`), `/text` 는 `derivedIdField=` 로
|
|
199
|
+
* **자기기술**하고(`core/text`), AI 쓰기 경로는 같은 이름의 속성 생성을 **거부**한다(`agent/resolver`).
|
|
200
|
+
* 넷이 한 술어를 공유하는 것이 이 함수의 목적이다.
|
|
201
|
+
*
|
|
202
|
+
* 근거=`.claude/docs/MapsIdIdiom-entry_2026-08-26.md` §3.A
|
|
203
|
+
*/
|
|
204
|
+
export declare function derivedIdScalarNameOf(attr: Attribute, entity: Entity, logical: LogicalModel, byId?: Map<ModelId, Entity>): string;
|
|
190
205
|
/**
|
|
191
206
|
* `@MapsId` 스칼라 @Id의 **Java 타입** — 이름과 **같은 경로**(`derivedFrom.sourceAttributeRef`)로 조달한다.
|
|
192
207
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Ref } from 'vue';
|
|
2
2
|
import { Command, EditorState } from '../command/types';
|
|
3
|
+
import { Translator } from '../i18n';
|
|
3
4
|
import { EditorController } from './controller';
|
|
4
5
|
import { ModelId } from '../core/types';
|
|
5
6
|
/** 이 축이 팩토리 클로저에서 쓰는 것 전부. 늘어나면 여기 선언이 먼저 늘어난다(= 의존 인벤토리). */
|
|
@@ -14,5 +15,9 @@ export interface AssociationContext {
|
|
|
14
15
|
gateEntityLocked: (id: ModelId) => boolean;
|
|
15
16
|
/** 잠금 게이트(관계) — 양끝 중 하나라도 잠기면 차단(그 의미론의 단일 출처). */
|
|
16
17
|
gateAssocLocked: (assocId: ModelId) => boolean;
|
|
18
|
+
/** 번역 — 아래 임베드 순환 차단의 «이유» 안내에 쓴다(엔티티·속성 축의 구조 차단 안내와 같은 짝). */
|
|
19
|
+
t: Translator;
|
|
20
|
+
/** 구조 차단 안내 — 무음 차단은 "고장"으로 읽힌다(VE-2 규율 · 직렬화 타입 분기의 실사용 보고 선례). */
|
|
21
|
+
emitGateNotice: (text: string) => void;
|
|
17
22
|
}
|
|
18
23
|
export declare function createAssociationApi(ctx: AssociationContext): Pick<EditorController, 'addAssociation' | 'addEmbedAssociation' | 'setEmbedCardinality' | 'removeAssociation' | 'setAssociationIdentifying' | 'updateAssociationEnd'>;
|