@g1cloud/bpmn-modeler-next 5.0.0-alpha.1 → 5.0.0-alpha.10

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.
@@ -1,6 +1,7 @@
1
1
  import { BpmnDocumentModelerLike } from '../core/modelerLike';
2
2
  import { SymbolicOp } from './symbolicOp';
3
- import { ResolveIssue } from './resolver';
3
+ import { ApplyBpmnOpsResult, ResolveIssue } from './resolver';
4
+ import { GeometryFinding } from './geometryAudit';
4
5
  /** op 출처 — 감사·정책 분기용. `gui` = 호스트 채팅 패널, `rest` = 외부 브리지. */
5
6
  export type BpmnOpOrigin = 'agent' | 'gui' | 'rest';
6
7
  export interface BpmnOpBatch {
@@ -29,10 +30,19 @@ export declare function buildBpmnOpBatch(documentId: string, baseVersion: number
29
30
  * - `conflict` → 에이전트가 read 부터 다시(재-ground 후 재생성). op 재전송은 무의미
30
31
  * - `rejected` → 어휘·참조 문제라 op 를 고쳐 재전송
31
32
  */
32
- export type ApplyBatchOutcome = {
33
+ export type ApplyBatchOutcome =
34
+ /**
35
+ * `findings` = 이 배치가 **새로 만든** 기하 결함(B-9). 거부가 아니라 품질 소견이라
36
+ * `rejected` 로 가지 않는다 — 문서는 이미 적용됐고 고칠지는 읽는 쪽이 정한다.
37
+ * 선재 결함은 담기지 않는다(하강 전후 diff). 문서 전체 감사는 `auditBpmnGeometry`.
38
+ */
39
+ {
33
40
  status: 'applied';
34
41
  version: number;
35
42
  xml: string;
43
+ findings: GeometryFinding[];
44
+ /** 간선 교차 계측(B-14 ⓐ) — 문서 전체, 하강 전/후. finding 이 아니다. */
45
+ stats: ApplyBpmnOpsResult['stats'];
36
46
  } | {
37
47
  status: 'conflict';
38
48
  baseVersion: number;
@@ -0,0 +1,206 @@
1
+ import { default as ElementRegistry } from 'diagram-js/lib/core/ElementRegistry';
2
+ import { ModdleElement, ParseWarning } from '../adapter/xml';
3
+ export interface GeometryPoint {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ export interface GeometryShape {
8
+ id: string;
9
+ /** bpmn 타입(`bpmn:Task` 등) — 판정 제외 규칙과 보고 맥락에 쓴다. */
10
+ type: string;
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ /** 다른 요소를 담는 도형(풀·레인·그룹·확장 서브프로세스) — 판정 대상에서 제외된다. */
16
+ container: boolean;
17
+ /** 경계 이벤트처럼 **호스트에 붙는 것이 정상**인 도형 — 겹침 판정에서 제외된다. */
18
+ attached: boolean;
19
+ /**
20
+ * 이 도형을 **의미론적으로** 담는 컨테이너 id(참여자 또는 확장 서브프로세스) — 없으면 루트.
21
+ * 좌표가 아니라 모델에서 온다(DI 는 프로세스 소속을 말하지 않는다). `shape-outside-container`
22
+ * 판정의 근거: 노드가 자기 풀 밖에 그려져 있으면 좌표만 보는 사람은 **남의 풀 노드로 읽는다.**
23
+ */
24
+ parent?: string;
25
+ }
26
+ /** 간선 외부 라벨의 bounds — 라벨은 판정 대상 도형이 아니지만 **어느 간선의 것으로 읽히는가**는 판정한다(B-15). */
27
+ export interface GeometryLabel {
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ }
33
+ export interface GeometryEdge {
34
+ id: string;
35
+ type: string;
36
+ waypoints: GeometryPoint[];
37
+ /** 이 간선이 자기 끝점으로 삼는 도형 id 들 — 그 도형과의 교차는 도킹이라 관통이 아니다. */
38
+ endpoints: readonly string[];
39
+ /** 이름이 있는 간선의 외부 라벨 bounds(있을 때만). `label-on-shared-segment` 의 입력. */
40
+ label?: GeometryLabel;
41
+ }
42
+ /** 판정기가 대면하는 최소 입력 — 좌표만 담는다(의미론 없음). */
43
+ export interface GeometryScene {
44
+ shapes: GeometryShape[];
45
+ edges: GeometryEdge[];
46
+ }
47
+ export type GeometryFinding =
48
+ /**
49
+ * 간선이 자기 끝점이 아닌 도형을 관통한다. 원인은 대개 **라우팅**이지 힌트가 아니다 —
50
+ * bpmn-js 는 장애물을 회피하지 않으므로 두 노드가 같은 축에 놓이면 그 사이를 지나간다.
51
+ */
52
+ {
53
+ kind: 'edge-crosses-shape';
54
+ edge: string;
55
+ shape: string;
56
+ }
57
+ /**
58
+ * 두 도형이 겹친다. `identical` = bounds 가 완전히 같다 — **사람 눈으로 볼 수 없는**
59
+ * 클래스라 따로 표시한다(하나가 다른 하나를 가린다).
60
+ */
61
+ | {
62
+ kind: 'shape-overlap';
63
+ shapes: [string, string];
64
+ identical: boolean;
65
+ }
66
+ /**
67
+ * 도형이 자기 컨테이너(풀·확장 서브프로세스) 경계 밖으로 나가 있다. 원인은 컨테이너 크기가
68
+ * 내용물을 따라 늘지 않은 것(B-12 실측: 풀 높이가 band 수를 몰라 band 2 노드가 아래 풀 안에
69
+ * 놓였다). 작은 도형(이벤트)은 아무것과도 겹치지 않아 **관통·겹침 축은 침묵**하는데, 사람은
70
+ * 그 노드를 다른 풀의 것으로 읽는다 — 의미론이 조용히 뒤바뀌는 클래스라 따로 잡는다.
71
+ */
72
+ | {
73
+ kind: 'shape-outside-container';
74
+ shape: string;
75
+ container: string;
76
+ }
77
+ /**
78
+ * 두 간선의 축 정렬 구간이 **같은 선 위에 겹치는데 진행 방향이 반대**다 — 한 선처럼 보이는데
79
+ * 화살표가 양쪽 끝에 달려 어디서 어디로 가는지 읽을 수 없다(확정 6 네 번째 표본에서 사용자가
80
+ * 지목한 "문제가 되는 겹침" — 같은 방향으로 합류·팬아웃하는 겹침은 문제가 아니라 제외한다).
81
+ * 전형 = 되돌아가는 간선이 주 줄의 정방향 간선 위를 거꾸로 달리는 것, 같은 열을 반대로 오르내리는
82
+ * 메시지플로우 둘. `length` = 겹친 길이(px).
83
+ */
84
+ | {
85
+ kind: 'edge-overlap-opposite';
86
+ edges: [string, string];
87
+ axis: 'h' | 'v';
88
+ length: number;
89
+ }
90
+ /**
91
+ * 두 간선의 축 정렬 구간이 같은 선 위에 **같은 방향으로** 겹치는데 **종류가 다르다**(실선 시퀀스 ×
92
+ * 점선 메시지 · 점선 연관) — 일곱 번째 표본에서 사용자가 지목: "같은 화살표 방향이면 겹쳐도 좋지만
93
+ * 다른 종류(실선/점선)가 겹치면 가독성을 해친다". 같은 종류·같은 방향(합류·팬아웃)은 여전히 제외.
94
+ * 전형 = 되돌아가는 시퀀스가 목표 아랫면 중심으로 들어오는 열에 메시지플로우도 중심 도킹하는 것.
95
+ */
96
+ | {
97
+ kind: 'edge-overlap-mixed';
98
+ edges: [string, string];
99
+ axis: 'h' | 'v';
100
+ length: number;
101
+ }
102
+ /**
103
+ * 간선의 **라벨이 다른 간선과 공유하는 구간 위**에 놓여 어느 간선의 라벨인지 읽을 수 없다(확정 6
104
+ * 다섯 번째 표본에서 사용자가 지목: "겹치는 선상에 있는 분기 문구가 어느 선에 해당하는지 애매").
105
+ * 전형 = 게이트웨이 팬아웃의 3점 경로(세로 → 가로)는 첫 세로 구간을 갈래끼리 공선 공유하는데,
106
+ * bpmn-js 기본 라벨 위치(중간 구간 중점 = 3점이면 첫 구간)가 정확히 그 공유 구간이라 '승인'·
107
+ * '보완 요청' 이 한 선 위에 나란히 선다. 같은 방향 공선 중첩은 선으로서는 문제가 아니지만
108
+ * (`edge-overlap-opposite` 가 제외한 이유) **라벨 귀속** 축에서는 문제다. `other` = 그 구간을
109
+ * 함께 쓰는 간선(여럿이면 id 최소).
110
+ */
111
+ | {
112
+ kind: 'label-on-shared-segment';
113
+ edge: string;
114
+ other: string;
115
+ axis: 'h' | 'v';
116
+ };
117
+ /** 발견을 전후 비교(배치가 새로 만든 것 가려내기)할 수 있게 하는 안정 키. */
118
+ export declare function geometryFindingKey(finding: GeometryFinding): string;
119
+ export interface AxisSegment {
120
+ axis: 'h' | 'v';
121
+ /** 고정 좌표(h 면 y, v 면 x). */
122
+ at: number;
123
+ lo: number;
124
+ hi: number;
125
+ /** 진행 방향 부호(+ = 오른쪽/아래). */
126
+ dir: 1 | -1;
127
+ }
128
+ /** 간선의 축 정렬 구간들(웨이포인트 순서). 사선 구간은 뺀다. */
129
+ export declare function axisSegmentsOf(edge: GeometryEdge): AxisSegment[];
130
+ /**
131
+ * 두 간선의 **읽을 수 없는** 공선 중첩 — 방향이 반대(`opposite`)이거나, 같은 방향인데 종류가 다른
132
+ * (`mixed`: 실선 × 점선) 겹침. 가장 긴 것의 (축, 길이, 종류). 없으면 `null`. 같은 종류·같은 방향
133
+ * (합류·팬아웃)은 세지 않는다 — 사용자가 그대로 두는 형상이다(표본 4·7).
134
+ */
135
+ export declare function unreadableOverlapOf(a: GeometryEdge, b: GeometryEdge): {
136
+ axis: 'h' | 'v';
137
+ length: number;
138
+ kind: 'opposite' | 'mixed';
139
+ } | null;
140
+ /** 두 간선의 **반대 방향** 공선 중첩(종류 무관). `unreadableOverlapOf` 의 부분집합 — 기존 호출자 호환. */
141
+ export declare function oppositeOverlapOf(a: GeometryEdge, b: GeometryEdge): {
142
+ axis: 'h' | 'v';
143
+ length: number;
144
+ } | null;
145
+ /** 한 간선(후보 경로)이 다른 간선들과 만드는 반대 방향 중첩 수. */
146
+ export declare function oppositeOverlapCount(edge: GeometryEdge, others: readonly GeometryEdge[]): number;
147
+ /** 한 간선(후보 경로)이 다른 간선들과 만드는 읽을 수 없는 중첩 수(반대 방향 + 종류 다름) — 보정의 목적 함수. */
148
+ export declare function unreadableOverlapCount(edge: GeometryEdge, others: readonly GeometryEdge[]): number;
149
+ /** bpmn-js `LabelUtil.FLOW_LABEL_INDENT` — 라벨 중심이 선에서 떨어지는 거리(가로 구간은 위, 세로 구간은 오른쪽). */
150
+ export declare const FLOW_LABEL_INDENT = 15;
151
+ /**
152
+ * 갈림점 바로 옆에 붙은 라벨도 모호하다(다섯 번째 표본: 갈림 16px 아래의 '보완 요청' 을 사용자가 더
153
+ * 내렸다) — 공유 구간을 양쪽으로 이만큼 넓혀 본다. 배치(`planLabelPlacement`)도 같은 폭을 뺀다.
154
+ */
155
+ export declare const LABEL_FORK_MARGIN = 20;
156
+ /**
157
+ * `seg` 가 다른 간선의 구간과 공선 공유하는 부분들(≥ `OVERLAP_MIN_LENGTH`, 방향 무관) — 갈림 여백을
158
+ * 더해 돌려준다. 판정과 배치가 **같은 목록**을 써야 "옮겼는데 여전히 잡힌다"가 나지 않는다.
159
+ */
160
+ export declare function sharedSpansOf(seg: AxisSegment, otherSegments: readonly AxisSegment[]): Array<[number, number]>;
161
+ /**
162
+ * 이 간선의 라벨이 다른 간선과 **공선 공유하는 구간**(방향 무관, ≥ `OVERLAP_MIN_LENGTH`, 갈림 여백 포함)
163
+ * 위에 놓여 있으면 그 상대(id 최소)와 축을 돌려준다. 라벨이 없거나 고유 구간에 있으면 `null`.
164
+ */
165
+ export declare function sharedSegmentUnderLabel(edge: GeometryEdge, others: readonly GeometryEdge[]): {
166
+ other: string;
167
+ axis: 'h' | 'v';
168
+ } | null;
169
+ /**
170
+ * 이 간선이 관통하는 도형들(자기 끝점 제외). 판정과 **우회 보정**(routeRepair)이 같은
171
+ * 술어를 써야 "고쳤다는데 판정은 그대로"가 나지 않는다.
172
+ *
173
+ * `targets` 는 컨테이너를 제외한 도형 목록 — 호출자가 한 번 걸러 넘긴다(반복 호출 비용).
174
+ */
175
+ export declare function crossedShapesOf(edge: GeometryEdge, targets: readonly GeometryShape[]): GeometryShape[];
176
+ /**
177
+ * 한 간선(또는 그 후보 경로)이 다른 간선들과 교차하는 수. **끝점을 공유하는 간선은 제외**한다 —
178
+ * 같은 노드에서 나가는 팬아웃·같은 노드로 드는 합류는 꺾이는 자리가 겹쳐 보여도 교차가 아니다.
179
+ */
180
+ export declare function edgeCrossCount(edge: GeometryEdge, others: readonly GeometryEdge[]): number;
181
+ /** 장면 전체의 교차 쌍(id 오름차순 정렬, 결정론적). 길이가 곧 교차 수다. */
182
+ export declare function edgeCrossingPairsOf(scene: GeometryScene): Array<[string, string]>;
183
+ /** 컨테이너를 뺀 판정 대상 도형 — 관통·겹침 양쪽의 공통 모집단. */
184
+ export declare const auditTargetsOf: (scene: GeometryScene) => GeometryShape[];
185
+ /**
186
+ * 기하 판정 (순수). 결과는 **결정론적 순서**로 정렬해 반환한다 — 전후 diff·골든 비교·회귀
187
+ * 고정이 전부 순서에 의존한다.
188
+ *
189
+ * 복잡도는 O(간선×구간×도형 + 도형²) 이고 공간 색인을 두지 않았다. 실측 규모(도형 23·간선 25)
190
+ * 에서 무시할 수준이고, 색인은 실제로 큰 문서가 관찰될 때 넣는다(조기 최적화 회피).
191
+ */
192
+ export declare function auditGeometry(scene: GeometryScene): GeometryFinding[];
193
+ export declare function sceneFromDefinitions(definitions: ModdleElement): GeometryScene;
194
+ /** 저장 진실 기준 전수 판정 — 그 문서에 있는 **전부**(선재 결함 포함). */
195
+ export declare function auditBpmnGeometry(definitions: ModdleElement): GeometryFinding[];
196
+ export interface AuditBpmnGeometryXmlResult {
197
+ findings: GeometryFinding[];
198
+ warnings: ParseWarning[];
199
+ }
200
+ /** XML 문자열 입력 래퍼 (`describeProcessXml` 대칭) — 호스트가 저장본을 그대로 감사한다. */
201
+ export declare function auditBpmnGeometryXml(xml: string): Promise<AuditBpmnGeometryXmlResult>;
202
+ /**
203
+ * 하강 직후의 실제 상태. DI 는 `BpmnUpdater` 가 커맨드마다 동기화하지만, 하강 경로에서는
204
+ * **registry 가 1차 진실**이므로 그쪽을 읽는다(한 단계 덜 거친다).
205
+ */
206
+ export declare function sceneFromRegistry(registry: ElementRegistry): GeometryScene;
@@ -10,6 +10,15 @@ export interface DocumentIndex {
10
10
  lanesOf(participantId: string): readonly string[];
11
11
  /** 플로우 노드가 속한 참여자 id (풀 없는 단일 프로세스 문서면 undefined) */
12
12
  participantOf(nodeId: string): string | undefined;
13
+ /**
14
+ * 연결의 현재 양 끝점 id — 끝점 재배선(`sequenceFlow.update` 의 source/target)에서
15
+ * **한쪽만 지정한 경우** 나머지 끝을 알아야 풀 정합을 검증할 수 있다.
16
+ * undefined = 그 id 가 문서에 없거나 연결이 아님. 끝점이 없는 연결은 필드가 undefined.
17
+ */
18
+ endpointsOf(connectionId: string): {
19
+ source?: string;
20
+ target?: string;
21
+ } | undefined;
13
22
  }
14
23
  /** elementRegistry 어댑터 — 하강 직전 실문서 상태를 대변한다. */
15
24
  export declare function buildRegistryIndex(registry: ElementRegistry): DocumentIndex;
@@ -1,14 +1,36 @@
1
1
  import { BpmnModelerLike } from '../../core/modelerLike';
2
2
  import { SymbolicOp } from '../symbolicOp';
3
3
  import { ResolveIssue } from './issues';
4
+ import { GeometryFinding } from '../geometryAudit';
4
5
  import { lowerOps } from './lower';
5
6
  export type { ResolveIssue } from './issues';
6
7
  export type { DocumentIndex, ElementCategory } from './documentIndex';
7
8
  export { buildRegistryIndex } from './documentIndex';
8
9
  export { validateOps } from './validate';
9
10
  export { lowerOps, resolverServicesOf, type ResolverServices } from './lower';
11
+ export { forwardThreePointCandidates, planCrossPoolRoute, planEdgeRepair, planLabelPlacement, planMiddleSegmentShifts, planOppositeOverlapRepair, planSegmentDetours, repairIntroducedCrossings, repairIntroducedOppositeOverlaps, repairIntroducedSharedSegmentLabels, rerouteIntroducedCrossPoolEdges, } from './routeRepair';
10
12
  export interface ApplyBpmnOpsResult {
11
13
  ok: boolean;
12
14
  issues: ResolveIssue[];
15
+ /**
16
+ * 이 배치가 **새로 만든** 기하 결함(B-9). `issues` 와 채널이 다른 이유는 성격이 다르기
17
+ * 때문이다 — issue 는 "하강할 수 없다"(→ `ok: false` → 롤백)이고 finding 은 유효하게
18
+ * 적용된 뒤의 품질 소견이다. 여기 값이 있어도 `ok` 는 참이고 문서는 이미 바뀌었다.
19
+ *
20
+ * **전후 diff 라 선재 결함은 담기지 않는다** — 남의 결함까지 돌려주면 에이전트가 그것을
21
+ * 자기 배치 탓으로 읽고 무한 교정에 빠진다. 문서 전체 감사는 `auditBpmnGeometry`.
22
+ */
23
+ findings: GeometryFinding[];
24
+ /**
25
+ * 계측(B-14 ⓐ) — finding 이 아니다. 문서 **전체**의 간선 교차 수를 하강 전/후로 준다. 사람
26
+ * 마무리의 실체가 이 축이었다(확정 6 세 번째 표본: 교차 7 → 1)는 것이 드러난 뒤 도구가 그것을
27
+ * 볼 수 있게 한 채널. 배치가 교차를 얼마나 만들었는지 = `after - before`.
28
+ */
29
+ stats: {
30
+ edgeCrossings: {
31
+ before: number;
32
+ after: number;
33
+ };
34
+ };
13
35
  }
14
36
  export declare function applyBpmnOps(modeler: BpmnModelerLike, ops: readonly SymbolicOp[], lower?: typeof lowerOps): ApplyBpmnOpsResult;
@@ -6,8 +6,23 @@
6
6
  * `opIndex` 는 배치 내 위치(0-기준) — 어느 op 가 문제인지 LLM 에게 되짚어 준다.
7
7
  */
8
8
  export type ResolveIssue =
9
- /** 신규 id 가 XML NCName 규칙 위반 (선두 숫자·콜론·공백 등) */
9
+ /**
10
+ * op 의 필수 페이로드가 없거나 타입이 어긋남 (`field` = 점 경로, 배열 원소는 `lanes[0].id` 형태).
11
+ *
12
+ * 구조 검증은 본래 JSON Schema(M2-1) 몫이지만 **그 검증을 강제하는 층이 실재하지 않는다** —
13
+ * 호스트 라우트는 `body.ops as SymbolicOp[]` 로 캐스트만 하고(시블 uiModelV2 도 동형), 스키마는
14
+ * 에이전트에게 *보여주는* 계약일 뿐 서버가 집행하지 않는다. 그 상태로 `op.participant.id` 를 읽으면
15
+ * TypeError 가 하강 밖으로 새어 500 이 되고, LLM 은 교정 단서 대신 스택트레이스를 받는다
16
+ * (M4 ⑥ 실측). 그래서 validator 가 **자기방어**한다 — 의미 검증 이전에 최소 shape 을 확인해
17
+ * 구조화 issue 로 떨어뜨린다(수용 기준 "resolve 실패는 throw 가 아닌 구조화 결과").
18
+ */
10
19
  {
20
+ kind: 'payload-invalid';
21
+ opIndex: number;
22
+ field: string;
23
+ }
24
+ /** 신규 id 가 XML NCName 규칙 위반 (선두 숫자·콜론·공백 등) */
25
+ | {
11
26
  kind: 'id-invalid';
12
27
  opIndex: number;
13
28
  id: string;
@@ -0,0 +1,88 @@
1
+ import { GeometryEdge, GeometryLabel, GeometryPoint, GeometryScene, GeometryShape } from '../geometryAudit';
2
+ import { ResolverServices } from './lower';
3
+ /**
4
+ * 축 정렬 구간이 장애물을 **완전히 가로지를 때** 그것을 우회하는 4점을 만든다(순수).
5
+ *
6
+ * 반환은 `a`·`b` **사이에 끼워 넣을** 점들이다(양 끝점은 포함하지 않는다). 두 방향(위/아래
7
+ * 또는 좌/우)을 모두 돌려주고 어느 쪽이 나은지는 호출자가 판정기로 고른다.
8
+ *
9
+ * `null` = 이 구간은 다루지 않는다:
10
+ * - 사선 구간(축 정렬이 아님) — 우회 형상이 자명하지 않다
11
+ * - 장애물이 구간 끝에 걸쳐 있음 — 끝점 도킹과 얽혀 잘못 건드리면 연결이 떨어져 보인다
12
+ */
13
+ export declare function planSegmentDetours(a: GeometryPoint, b: GeometryPoint, rect: GeometryShape, margin?: number): [GeometryPoint[], GeometryPoint[]] | null;
14
+ /**
15
+ * 꺾임점이 장애물 **안**에 있는 형상 — 구간 우회로는 못 푼다(B-13 ②, 두 번째 표본 잔존 1건).
16
+ *
17
+ * bpmn-js 의 직각 경로는 대개 h-v-h(또는 v-h-v) 3구간이고, **중간 구간의 좌표**는 양 끝점의
18
+ * 중점이다. 그 중점이 어떤 도형의 폭 안에 떨어지면 세로 구간이 그 도형을 관통하고 꺾임점도
19
+ * 그 안에 있어 `planSegmentDetours` 가 거부한다(실측: 되돌아가는 간선의 중간 x 가 게이트웨이
20
+ * 폭 안). 해법은 우회가 아니라 **중간 구간을 장애물 옆으로 옮기는 것** — 점 수는 그대로 4 다.
21
+ * 두 후보(장애물 왼쪽/오른쪽 또는 위/아래)를 돌려주고 채택은 호출자가 판정기로 한다.
22
+ *
23
+ * `null` = 4점 직각 경로가 아니다.
24
+ */
25
+ export declare function planMiddleSegmentShifts(waypoints: readonly GeometryPoint[], rect: GeometryShape, margin?: number): [GeometryPoint[], GeometryPoint[]] | null;
26
+ /**
27
+ * 정방향 간선(목표가 출발의 오른쪽, 다른 행)의 3점 후보 둘(순수) — B-17 ⓒ.
28
+ * h-v: 출발 옆면(오른쪽) → 출발 행에서 가로 → 목표 중심 열에서 목표 윗/아랫면으로
29
+ * v-h: 출발 윗/아랫면 → 목표 행까지 세로 → 목표 왼쪽 옆면으로
30
+ * 같은 행이거나 되돌아가는 간선이면 빈 배열(그쪽은 `backEdgeCandidates` 몫).
31
+ */
32
+ export declare function forwardThreePointCandidates(edge: GeometryEdge, shapes: readonly GeometryShape[]): GeometryPoint[][];
33
+ /**
34
+ * 한 간선의 관통을 없애는 웨이포인트를 찾는다(순수). 못 찾으면 `null`.
35
+ *
36
+ * 장애물을 하나씩 처리하며 **매번 두 방향을 다 재보고 관통이 더 적은 쪽**을 고른다.
37
+ * 부모 컨테이너 바운즈를 알면 그 밖으로 나가는 우회를 후순위로 민다 — 풀 밖으로 튀어나간
38
+ * 선은 관통이 없어도 사람 눈에 결함이다(판정기가 못 보는 축이라 여기서 챙긴다).
39
+ */
40
+ export declare function planEdgeRepair(edge: GeometryEdge, targets: readonly GeometryShape[], container?: {
41
+ x: number;
42
+ y: number;
43
+ width: number;
44
+ height: number;
45
+ }): GeometryPoint[] | null;
46
+ /**
47
+ * 이 배치가 **새로 만든** 관통을 고친다(하강 직후, undo 에피소드 안에서 호출된다).
48
+ *
49
+ * `beforeKeys` = 하강 전 판정 키 집합. 거기 없던 관통만 대상이다(안전 성질 2).
50
+ * 완전히 해소되는 우회만 채택하고, 아니면 손대지 않는다(안전 성질 1) — 부분 개선을 받으면
51
+ * "고쳤는데 여전히 깨져 있다"가 되어 보고와 실물이 어긋난다.
52
+ */
53
+ export declare function repairIntroducedCrossings(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeKeys: ReadonlySet<string>): void;
54
+ /**
55
+ * 풀 횡단 메시지플로우의 6점 경로 계획(순수). `null` = 이 간선은 대상이 아니거나(중간 풀 없음 ·
56
+ * 끝점 해소 불가) 더 나은 열이 없다.
57
+ */
58
+ export declare function planCrossPoolRoute(edge: GeometryEdge, scene: GeometryScene): GeometryPoint[] | null;
59
+ /**
60
+ * 이 배치가 **새로 만든** 메시지플로우 중 풀을 횡단하는 것을 재경로한다(하강 직후, undo 에피소드
61
+ * 안). `beforeEdgeIds` = 하강 전 간선 id — 그 밖의 것만 대상(안전 성질 2: 선재 경로 불가침).
62
+ */
63
+ export declare function rerouteIntroducedCrossPoolEdges(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeEdgeIds: ReadonlySet<string>): void;
64
+ /**
65
+ * 한 간선의 **읽을 수 없는 중첩**(반대 방향 · 종류 다름)을 없애거나 줄이는 경로(순수). 못 찾으면 `null`.
66
+ * 종류 다른 중첩(B-17 ⓐ)의 보정 = 도킹 편이 — "가급적 중심에 도킹하되 어쩔 수 없을 땐 약간 비껴서"
67
+ * (사용자 프레이밍). 기본은 중심이고, finding 이 났을 때만, 최소 편이(±25)부터 본다.
68
+ */
69
+ export declare function planOppositeOverlapRepair(edge: GeometryEdge, scene: GeometryScene): GeometryPoint[] | null;
70
+ /**
71
+ * 이 배치가 **새로 만든** 읽을 수 없는 중첩(반대 방향 · 종류 다름)을 고친다(하강 직후, undo 에피소드 안).
72
+ * 짝 중 새 간선만 손댄다(안전 성질 2). 둘 다 새 것이면 **시퀀스 플로우부터** — 종류 다른 중첩에서 사용자는
73
+ * 메시지플로우(중심 도킹)를 두고 시퀀스 쪽을 비켰다(표본 7).
74
+ */
75
+ export declare function repairIntroducedOppositeOverlaps(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeKeys: ReadonlySet<string>, beforeEdgeIds: ReadonlySet<string>): void;
76
+ /**
77
+ * 라벨의 새 bounds(순수). 라벨이 공유 구간 위에 없으면 `null`(옮길 이유가 없다). 고유 구간을 못
78
+ * 찾거나 옮긴 자리가 도형과 겹치면 `null`(손대지 않고 보고에 맡긴다).
79
+ *
80
+ * 구간마다 다른 간선과 공선 공유하는 부분(≥ 20px)을 빼고 남은 **첫 자유 부분** 중 라벨이 들어가는
81
+ * 곳을 고른다 — 부분 공유 구간(갈림점 아래로 이어지는 세로 구간)도 그 아래쪽이 자유 부분이다.
82
+ */
83
+ export declare function planLabelPlacement(edge: GeometryEdge, scene: GeometryScene): GeometryLabel | null;
84
+ /**
85
+ * 이 배치가 **새로 만든** 간선의 라벨 귀속 문제를 고친다(하강 직후, undo 에피소드 안, 경로 보정들 뒤).
86
+ * 선재 간선의 라벨은 사람이 놓았을 수 있어 손대지 않는다(안전 성질 2).
87
+ */
88
+ export declare function repairIntroducedSharedSegmentLabels(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeKeys: ReadonlySet<string>, beforeEdgeIds: ReadonlySet<string>): void;
@@ -62,11 +62,23 @@ export type SymbolicOp =
62
62
  name?: string | null;
63
63
  lane?: string;
64
64
  type?: IrFlowNodeType;
65
- } | {
65
+ }
66
+ /**
67
+ * `source`/`target` = 끝점 재배선(B-6 승격). 한쪽만 줘도 되고(나머지 보존) 새 끝점은
68
+ * **같은 배치에서 추가한 노드여도 된다** — 중간 삽입("A 다음에 X 를 넣어라")이 이것 하나로
69
+ * 표현되어 `element.remove` + 재연결 배치를 강요하지 않는다. resolver 는 `modeling.reconnect`
70
+ * 로 하강하고 경로는 라우터가 재산출한다(기존 벤드포인트는 버린다).
71
+ *
72
+ * ⚠ `messageFlow.update` 에는 아직 없다 — 끝점 이동 수요가 sequenceFlow 로만 관측됐고
73
+ * 어휘는 additive-only(불변식 5)라 나중에 붙이는 비용이 같다.
74
+ */
75
+ | {
66
76
  kind: 'sequenceFlow.update';
67
77
  id: string;
68
78
  name?: string | null;
69
79
  condition?: IrFlowCondition | null;
80
+ source?: string;
81
+ target?: string;
70
82
  } | {
71
83
  kind: 'messageFlow.update';
72
84
  id: string;