@g1cloud/bpmn-modeler-next 5.0.0-alpha.99 → 5.0.0-beta.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  - **패키지**: `@g1cloud/bpmn-modeler-next`
6
6
  - **스택**: TypeScript · Vue 3 · Vite(library mode) · Vitest 4(browser mode) · 캔버스 = **bpmn-js 18 래핑**
7
- - **상태**: `5.0.0-alpha.1` (활발한 개발 중)
7
+ - **상태**: beta (`5.0.0-beta.*`)
8
8
 
9
9
  ---
10
10
 
@@ -94,7 +94,9 @@ import {
94
94
  createHeadlessBpmnModeler, // 에디터 없는 하강용 modeler (Node + jsdom)
95
95
  APPLY_BPMN_OPS_SCHEMA, // applyOps 도구 input_schema (op 어휘의 JSON Schema 단일 출처)
96
96
  SYMBOLIC_OP_KINDS, // op 종류 17
97
- MVP_FLOW_NODE_TYPES, // MVP 노드 타입 9
97
+ IR_FLOW_NODE_TYPES, // describe 가 내는 노드 타입 12 (read)
98
+ ADDABLE_FLOW_NODE_TYPES, // op 로 만들 수 있는 노드 타입 10 (write · read 의 부분집합)
99
+ IR_EVENT_DEFINITIONS, // 이벤트 정의 — 'timer' (intermediateCatchEvent 전용)
98
100
  BWL_SIDES, // 주석 방위 8
99
101
  } from '@g1cloud/bpmn-modeler-next'
100
102
  ```
@@ -1,9 +1,41 @@
1
+ export declare const I18N_NS = "http://www.omg.org/spec/BPMN/non-normative/extensions/i18n/1.0";
2
+ export declare const I18N_PREFIX = "i18n";
3
+ /** 번역 칸 moddle 타입. */
4
+ export declare const I18N_TRANSLATION_TYPE = "i18n:Translation";
5
+ type Any = any;
6
+ /**
7
+ * 라벨 문구가 실제로 사는 (객체, 키) — bpmn-js `LabelUtil.getLabelAttr` 와 같은 판정.
8
+ * 주석은 `text`, 그룹은 `categoryValueRef.value`, 나머지는 `name`.
9
+ */
10
+ export declare function labelSlot(bo: Any): {
11
+ target: Any;
12
+ key: string;
13
+ } | undefined;
14
+ /** 요소의 번역 칸 전부(`extensionElements` 안의 `i18n:Translation`). */
15
+ export declare function translationsOf(bo: Any): Any[];
16
+ /** 번역 칸의 로케일. */
17
+ export declare function translationLocale(t: Any): string | undefined;
18
+ /** 요소의 확인 요청 표식 전부(`bwl:ReviewRequest` — 상태 표식 · C-1). */
19
+ export declare function reviewRequestsOf(bo: Any): Any[];
1
20
  /**
2
- * 레거시 bpmn-i18n-moddle 호환 (백로그 B-4).
21
+ * 문서 언어 설정(B-4) — 설계 언어(고객에게 전달하는 설계 문서의 언어 · `name` 이 따른다) + 병기 언어(함께 최신으로
22
+ * 유지할 언어들). 호스트 그룹 설정(`designLocale` · `companionLocales`)과 같은 이름이라 그대로 넘긴다.
23
+ * 미설정(`undefined`) = 기능 끔.
24
+ */
25
+ export interface BpmnDocumentLocales {
26
+ designLocale: string;
27
+ companionLocales: string[];
28
+ }
29
+ /** 문서 언어 목록 — 설계 언어가 맨 앞 · 병기 언어에서 설계 언어와 중복은 뺀다. */
30
+ export declare function documentLocalesOf(config: BpmnDocumentLocales): string[];
31
+ /** 루트 `<bpmn:definitions xml:lang>` — 번역 칸으로 판정되지 않는 `name` 의 언어. */
32
+ export declare function rootLangOf(definitions: Any): string | undefined;
33
+ /**
34
+ * 요소 라벨의 문서 언어별 문구(B-4 ④) — 표시 판정(`view/i18nDisplay` `resolve`)과 **같은 규칙**이다:
35
+ * `name` 의 언어가 그 언어면 `name` → 아니면 그 언어의 번역 칸(`xml:lang` 있는 칸 · 문구 있음) → 없으면 빠진다
36
+ * (= 미번역). `name` 의 언어 = 같은 문구의 번역 칸 로케일 → 없으면 루트 `xml:lang` → 설계 언어.
3
37
  *
4
- * 스키마 자체는 유지한다 — 기존 운영 문서(실측 112곳 확장)와의 호환 필수.
5
- * 재설계 대상은 스키마가 아니라 적용 방식: 레거시 bpmnTranslationService 의
6
- * commandStack 우회 변조를 표시용/저장용 라벨 분리로 대체한다.
38
+ * 에이전트 대면 read(`describeProcess` 의 `names`/`texts`)와 에이전트 쓰기의 「바뀌었나」 판정이 같이 쓴다.
7
39
  */
8
- export declare const I18N_NS = "http://www.omg.org/spec/BPMN/non-normative/extensions/i18n/1.0";
9
- export declare const I18N_PREFIX = "i18n";
40
+ export declare function localeTextsOf(bo: Any, config: BpmnDocumentLocales, rootLang?: string): Record<string, string>;
41
+ export {};
@@ -0,0 +1,64 @@
1
+ type Any = any;
2
+ /** 문자 체계 — 판정에 쓰는 것은 한글 · 가나뿐이다. */
3
+ export type ScriptKind = 'hangul' | 'kana' | 'mixed' | 'han' | 'other';
4
+ export interface ScriptInference {
5
+ script: ScriptKind;
6
+ /** 문자 체계로 정해지는 언어 — 한글 = ko · 가나 = ja · 그 밖 = 없음. */
7
+ locale?: 'ko' | 'ja';
8
+ }
9
+ /** 문구의 문자 체계 판정. 한글과 가나가 함께 있으면 `mixed`(판정 불가). */
10
+ export declare function inferScriptLocale(text: string): ScriptInference;
11
+ /**
12
+ * 루트 판정의 근거.
13
+ * `kept` 이미 있다 · `matched` 번역 칸과 같은 이름들의 공통 언어 · `script` 문자 체계가 한 언어로 모인다 ·
14
+ * `ambiguous` 공통 언어가 여럿이고 문자 체계로도 못 고른다 · `mixed` 근거가 서로 다른 언어 · `no-evidence` 근거 없음
15
+ */
16
+ export type RootLocaleReason = 'kept' | 'matched' | 'script' | 'ambiguous' | 'mixed' | 'no-evidence';
17
+ /** 계획 항목이 가리키는 요소 — 미리보기 화면이 사람에게 보여 줄 최소 정보. */
18
+ export interface LocaleBackfillElement {
19
+ id: string;
20
+ /** moddle 타입(`bpmn:Task` 등). */
21
+ type: string;
22
+ /** 계획 시점의 라벨 문구. 적용은 이 문구가 그대로일 때만 쓴다. */
23
+ name: string;
24
+ }
25
+ export interface LocaleBackfillPlan {
26
+ root: {
27
+ /** 지금 루트 `xml:lang`. */
28
+ current?: string;
29
+ /** 새로 쓸 값 — `current` 가 있거나 판정 불가면 없다. */
30
+ proposed?: string;
31
+ reason: RootLocaleReason;
32
+ };
33
+ /** 더할 번역 칸(본문 = 그 요소의 `name`). */
34
+ additions: Array<LocaleBackfillElement & {
35
+ locale: string;
36
+ }>;
37
+ /** 언어 기록이 없고 문자 체계로도 못 가르는 이름 — 루트를 따른다 · 사람 확인 대상. */
38
+ undecided: Array<LocaleBackfillElement & {
39
+ script: ScriptKind;
40
+ }>;
41
+ /** 이름이 언어 있는 칸 어느 것과도 다르다(칸은 있다) — 백필 대상 아님(별도 정리). */
42
+ drift: LocaleBackfillElement[];
43
+ /**
44
+ * 이름이 언어 있는 칸과 같은 요소의 분포 — 키 = 일치한 로케일(정렬 · `+` 연결, 예 `ja` · `ja+ko`).
45
+ * 루트 판정의 근거이고, 판정 불가 문서에서 사람이 루트를 고를 근거다.
46
+ */
47
+ matchedBy: Record<string, number>;
48
+ /**
49
+ * 칸 언어 ↔ 문자 체계 불일치(레거시 복사본 번역 흔적 · F8) — **기록 대상 아님**(별도 정리).
50
+ * `script` = `kana` · `hangul` 은 그 칸 언어가 아닌 문자 · `han` 은 ko 칸이 한자만이고 다른 칸과 같은 문구(참고값 · 사람 판단).
51
+ */
52
+ scriptMismatch: Array<LocaleBackfillElement & {
53
+ locale: string;
54
+ script: ScriptKind;
55
+ }>;
56
+ }
57
+ /** 언어 백필 계획 — 문서를 바꾸지 않는다. */
58
+ export declare function planLocaleBackfill(definitions: Any): LocaleBackfillPlan;
59
+ /**
60
+ * 계획을 문서에 쓴다 — 바꾼 곳 수를 돌려준다. **멱등**이고 계획 뒤 문서가 바뀌었어도 덮지 않는다:
61
+ * 루트가 이미 있으면 루트를 건너뛰고, 요소는 지금도 「언어 기록 없음 + 이름이 계획 때 그대로」일 때만 쓴다.
62
+ */
63
+ export declare function applyLocaleBackfill(definitions: Any, plan: LocaleBackfillPlan): number;
64
+ export {};
@@ -2,6 +2,7 @@ import { BpmnDocumentModelerLike } from '../core/modelerLike';
2
2
  import { SymbolicOp } from './symbolicOp';
3
3
  import { ApplyBpmnOpsResult, ResolveIssue } from './resolver';
4
4
  import { GeometryFinding } from './geometryAudit';
5
+ import { BpmnDocumentLocales } from '../adapter/i18n';
5
6
  /** op 출처 — 감사·정책 분기용. `gui` = 호스트 채팅 패널, `rest` = 외부 브리지. */
6
7
  export type BpmnOpOrigin = 'agent' | 'gui' | 'rest';
7
8
  export interface BpmnOpBatch {
@@ -54,6 +55,11 @@ export type ApplyBatchOutcome =
54
55
  export interface ApplyBpmnOpBatchOptions {
55
56
  /** 적용 시점의 권위 버전 — 호스트(영속 소유자)가 공급한다. */
56
57
  currentVersion: number;
58
+ /**
59
+ * 그 문서가 속한 그룹의 문서 언어 설정(B-4 ④) — 호스트가 공급한다(문서에 없다). 있으면 문구는 `names`/`texts`
60
+ * 로 문서 언어 전부를 받고, 없으면 평문만 받는다(`applyBpmnOps` 의 같은 옵션).
61
+ */
62
+ i18n?: BpmnDocumentLocales;
57
63
  }
58
64
  /**
59
65
  * 배치를 캔버스에 적용하고 영속 페이로드를 낸다.
@@ -64,4 +70,4 @@ export interface ApplyBpmnOpBatchOptions {
64
70
  * 적용은 단일 undo 에피소드다(`applyBpmnOps` → `command/undoEpisode`) — 배치 수십 op 가
65
71
  * 사용자 undo 1회로 되감긴다.
66
72
  */
67
- export declare function applyBpmnOpBatch(modeler: BpmnDocumentModelerLike, batch: BpmnOpBatch, { currentVersion }: ApplyBpmnOpBatchOptions): Promise<ApplyBatchOutcome>;
73
+ export declare function applyBpmnOpBatch(modeler: BpmnDocumentModelerLike, batch: BpmnOpBatch, { currentVersion, i18n }: ApplyBpmnOpBatchOptions): Promise<ApplyBatchOutcome>;
@@ -1,4 +1,6 @@
1
1
  import { ModdleElement, ParseWarning } from '../adapter/xml';
2
+ import { BpmnDocumentLocales } from '../adapter/i18n';
3
+ import { ReviewRequestValue } from '../command/setTranslations';
2
4
  import { BwlAfterColumn, BwlBand, BwlPlacement, BwlPoolOrder } from '../core/hints';
3
5
  import { IrDefinition } from '../core/ir';
4
6
  /** 파생 뷰가 표현하지 못한 요소 — 침묵 폐기 금지(에이전트가 미표현 존재를 알아야 안전). */
@@ -7,7 +9,11 @@ export interface IrOmission {
7
9
  /** bpmn $type 원형 (예: `bpmn:BoundaryEvent`) */
8
10
  type: string;
9
11
  reason:
10
- /** IR 타입 집합 밖 (intermediate 이벤트·dataObject 등 — 승격 시 자동 해소) */
12
+ /**
13
+ * IR 타입 집합 밖 (intermediateThrowEvent·dataObject 등 — 승격 시 자동 해소). 🔴 **어휘 밖 변형도
14
+ * 여기다** — `intermediateCatchEvent` 는 timer 만 표현하므로 message·signal·정의 없음·다중 정의는
15
+ * 타입이 집합 안이어도 이 사유로 나간다(B-87 ⓑ 부분 · 판정 = 타입 + eventDefinition).
16
+ */
11
17
  'type-outside-mvp'
12
18
  /**
13
19
  * 승격된 컨테이너(subProcess) **내부**라 표현하지 않음 (B-87 ⓐ = 불투명 승격).
@@ -37,15 +43,40 @@ export interface BwlHintView {
37
43
  /** 주석 id → 앵커 기준 방위 (앵커 부재분은 필터됨) */
38
44
  placements: Record<string, BwlPlacement>;
39
45
  }
46
+ /** 확인 요청 표식 한 건(B-4 ④) — `id` 요소의 `locale` 칸에 걸렸다. `sourceLocale` 이 `previous` → 지금 문구로 고쳐졌다. */
47
+ export interface ReviewRequestView extends ReviewRequestValue {
48
+ id: string;
49
+ }
50
+ /**
51
+ * 다국어 판독 결과(B-4 ④) — 문서 언어 설정이 있을 때만 낸다. IR 밖 채널(위 헤더 「다국어」).
52
+ * 에이전트는 이 값이 있으면 문구를 `names`/`texts` 로 문서 언어 전부를 써야 한다.
53
+ */
54
+ export interface I18nView {
55
+ designLocale: string;
56
+ /** 병기 언어(설계 언어 제외 · 중복 제거). */
57
+ companionLocales: string[];
58
+ /**
59
+ * 정리할 확인 요청 — 문서 언어 칸에 걸린 것만. `previous`(고치기 전 원문) → 지금 `names[sourceLocale]` 를 대조해
60
+ * 오타 수준이면 `translation.confirm`, 내용이 바뀌었으면 그 요소 update 에 `names` 로 번역을 고친다.
61
+ */
62
+ reviewRequests: ReviewRequestView[];
63
+ }
40
64
  export interface DescribeProcessResult {
41
65
  definition: IrDefinition;
42
66
  hints: BwlHintView;
43
67
  omitted: IrOmission[];
68
+ /** 문서 언어 설정이 있을 때만 있다(B-4 ④). */
69
+ i18n?: I18nView;
70
+ }
71
+ /** 판독 옵션 — 문서 밖에서만 알 수 있는 입력. */
72
+ export interface DescribeProcessOptions {
73
+ /** 그룹의 문서 언어 설정(B-4 ④) — 문서에 없어 호스트가 공급한다. 없으면 다국어 판독을 하지 않는다. */
74
+ i18n?: BpmnDocumentLocales;
44
75
  }
45
76
  export interface DescribeProcessXmlResult extends DescribeProcessResult {
46
77
  /** 입력 XML 파싱 경고 — 실운영 문서는 빈 배열이어야 한다(M1 수용 기준 1). */
47
78
  warnings: ParseWarning[];
48
79
  }
49
- export declare function describeProcess(definitions: ModdleElement): DescribeProcessResult;
80
+ export declare function describeProcess(definitions: ModdleElement, options?: DescribeProcessOptions): DescribeProcessResult;
50
81
  /** XML 문자열 입력 래퍼 — 헤드리스 read 경로(호스트 `describeProcess` API·테스트). */
51
- export declare function describeProcessXml(xml: string): Promise<DescribeProcessXmlResult>;
82
+ export declare function describeProcessXml(xml: string, options?: DescribeProcessOptions): Promise<DescribeProcessXmlResult>;
@@ -1,4 +1,5 @@
1
1
  import { default as ElementRegistry } from 'diagram-js/lib/core/ElementRegistry';
2
+ import { IrEventDefinition } from '../../core/ir';
2
3
  /** 검증이 구분하는 요소 카테고리 — bpmn $type 을 참조 적합성 판단 단위로 접는다. */
3
4
  export type ElementCategory = 'participant' | 'lane' | 'flowNode' | 'sequenceFlow' | 'messageFlow' | 'dataStore' | 'dataAssociation' | 'annotation' | 'association' | 'group' | 'other';
4
5
  export interface DocumentIndex {
@@ -19,6 +20,17 @@ export interface DocumentIndex {
19
20
  source?: string;
20
21
  target?: string;
21
22
  } | undefined;
23
+ /**
24
+ * 플로우 노드의 IR 이벤트 정의 (B-87 ⓑ 부분) — 문서의 **기존** 타이머 catch 이벤트를 메시지 끝점으로
25
+ * 지목하는 op 를 거부하려면 정의를 알아야 한다(배치 노드는 op 선언으로 안다). 이벤트가 아니거나
26
+ * 어휘 밖 정의면 undefined.
27
+ */
28
+ eventDefinitionOf(nodeId: string): IrEventDefinition | undefined;
29
+ /**
30
+ * 요소에 확인 요청 표식(`bwl:reviewRequest`)이 걸린 언어들 (B-4 ④) — `translation.confirm` 이 없는 표식을
31
+ * 지목하면 거부한다(읽은 뒤 사람이 이미 정리했을 수 있다). 없으면 빈 배열.
32
+ */
33
+ reviewRequestLocalesOf(id: string): readonly string[];
22
34
  }
23
35
  /** elementRegistry 어댑터 — 하강 직전 실문서 상태를 대변한다. */
24
36
  export declare function buildRegistryIndex(registry: ElementRegistry): DocumentIndex;
@@ -2,11 +2,12 @@ import { BpmnModelerLike } from '../../core/modelerLike';
2
2
  import { SymbolicOp } from '../symbolicOp';
3
3
  import { ResolveIssue } from './issues';
4
4
  import { GeometryFinding } from '../geometryAudit';
5
+ import { BpmnDocumentLocales } from '../../adapter/i18n';
5
6
  import { lowerOps } from './lower';
6
7
  export type { ResolveIssue } from './issues';
7
8
  export type { DocumentIndex, ElementCategory } from './documentIndex';
8
9
  export { buildRegistryIndex } from './documentIndex';
9
- export { validateOps } from './validate';
10
+ export { validateOps, type ValidateOpsOptions } from './validate';
10
11
  export { lowerOps, resolverServicesOf, type ResolverServices } from './lower';
11
12
  export { forwardThreePointCandidates, planCrossPoolRoute, planEdgeRepair, planLabelLineClearance, planLabelPlacement, planMiddleSegmentShifts, planOppositeOverlapRepair, planSegmentDetours, planBackEdgeBoundaryClearance, repairIntroducedBoundaryHugging, repairIntroducedCrossings, repairIntroducedEdgeCrossings, repairIntroducedFakeMerges, repairIntroducedOppositeOverlaps, repairIntroducedSharedSegmentLabels, followMovedEdgeLabels, redockIntroducedDetachedEndpoints, repairIntroducedLabelLines, repairIntroducedShapeLabelLines, planShapeLabelClearance, rerouteIntroducedCrossPoolEdges, } from './routeRepair';
12
13
  export interface ApplyBpmnOpsResult {
@@ -59,4 +60,15 @@ export interface ApplyBpmnOpsResult {
59
60
  };
60
61
  };
61
62
  }
62
- export declare function applyBpmnOps(modeler: BpmnModelerLike, ops: readonly SymbolicOp[], lower?: typeof lowerOps): ApplyBpmnOpsResult;
63
+ /** 배치 적용 옵션 — 문서 밖에서만 알 수 있는 입력. */
64
+ export interface ApplyBpmnOpsOptions {
65
+ /**
66
+ * 그룹의 문서 언어 설정(B-4 ④). 있으면 다국어 그룹이다 — 문구는 `names`/`texts` 로 문서 언어 전부를 받고
67
+ * (평문 거부) 하강 뒤 번역 칸을 같은 undo 에피소드에서 기록한다. 없으면 기능이 꺼진 그룹이다(평문만).
68
+ * 🔴 **문서에는 이 설정이 없다**(루트 `xml:lang` 은 만들 당시의 설계 언어일 뿐이다) — 호스트가 그룹 설정을 공급한다.
69
+ */
70
+ i18n?: BpmnDocumentLocales;
71
+ /** 테스트 시임 — 롤백 경로를 결정론적으로 검증하기 위한 하강 함수 주입점. 실사용은 기본값. */
72
+ lower?: typeof lowerOps;
73
+ }
74
+ export declare function applyBpmnOps(modeler: BpmnModelerLike, ops: readonly SymbolicOp[], options?: ApplyBpmnOpsOptions): ApplyBpmnOpsResult;
@@ -114,6 +114,31 @@ export type ResolveIssue =
114
114
  id: string;
115
115
  type: string;
116
116
  }
117
+ /**
118
+ * `type` 과 `eventDefinition` 의 짝이 어긋남 (B-87 ⓑ 부분 · 2026-09-30) — `intermediateCatchEvent` 인데
119
+ * 정의가 없거나, 다른 타입에 정의를 줬다. 🔴 **앞쪽이 더 위험하다** — 정의 없는 중간 catch 이벤트는 read 가
120
+ * 표현하지 않는 변형이라, 만들면 **다음 describe 에서 그 노드와 붙은 간선이 사라진다**(`type-outside-mvp`
121
+ * · `endpoint-outside-mvp`). `node.update` 는 같은 op 안에서 type 과 함께 줄 때만 받는다.
122
+ */
123
+ | {
124
+ kind: 'event-definition-mismatch';
125
+ opIndex: number;
126
+ id: string;
127
+ type?: string;
128
+ eventDefinition?: string;
129
+ }
130
+ /**
131
+ * 메시지 플로우의 끝점이 **타이머 catch 이벤트** (B-87 ⓑ 부분). 타이머는 시간이 지나야 진행하는 대기라
132
+ * 메시지를 주고받지 않는다 — bpmn-js 규칙(`canConnectMessageFlow`)도 거부하는 형상이고, resolver 는
133
+ * `modeling.connect` 로 규칙을 거치지 않으므로 여기서 막지 않으면 **무효 BPMN 이 그대로 저장된다**.
134
+ * 메시지를 기다리는 자리는 `receiveTask` 로 그린다(playbook §3.5-1b-1).
135
+ */
136
+ | {
137
+ kind: 'message-flow-timer-endpoint';
138
+ opIndex: number;
139
+ id: string;
140
+ ref: string;
141
+ }
117
142
  /** lanes 재선언이 기존 레인을 누락 — 레인 축소는 미실증 축(B-0)이라 거부 (세션 ②-b 판단) */
118
143
  | {
119
144
  kind: 'lane-shrink-unsupported';
@@ -127,6 +152,74 @@ export type ResolveIssue =
127
152
  opIndex: number;
128
153
  participant: string;
129
154
  }
155
+ /**
156
+ * `node.update{type}` 전환이 **다른 요소를 지웠다** — 배치 전체를 롤백하고 거부한다 (B-128 · 2026-09-30).
157
+ * bpmn-js 는 새 타입이 못 가지는 연결(게이트웨이·종료 이벤트의 메시지 · 시작 이벤트의 시퀀스 유입 · 타이머의 메시지)과
158
+ * 붙은 경계 이벤트를 **알림 없이 지운다** — 막지 않으면 `ok: true` 인 채 문서가 잘려 나간다. `dropped` 가 지워질 뻔한
159
+ * 요소 id 다. 교정 = 그 연결을 먼저 옮기거나(재배선), 지워야 한다면 사람 승인을 받아 `element.remove` 로 명시하거나,
160
+ * 그것을 가질 수 있는 타입을 고른다. ⚠ validator 가 아니라 **하강에서** 낸다 — bpmn-js 규칙을 옮겨 적지 않고
161
+ * 실제로 지운 것을 대조하기 때문이다.
162
+ */
163
+ | {
164
+ kind: 'type-change-drops-elements';
165
+ opIndex: number;
166
+ id: string;
167
+ type: string;
168
+ dropped: string[];
169
+ }
170
+ /** 평문(`name`/`text`)과 로케일 맵(`names`/`texts`)을 함께 줬다 — 어느 쪽이 진실인지 모호하다(🔁 `condition-name-conflict`). */
171
+ | {
172
+ kind: 'locale-texts-conflict';
173
+ opIndex: number;
174
+ id: string;
175
+ field: string;
176
+ }
177
+ /**
178
+ * 다국어 그룹(문서 언어 설정 있음)에 평문을 줬다 — 언어를 모르는 문구는 설계 언어 칸에 다른 언어가 들어가는 경로다
179
+ * (켠 뒤의 에이전트 배치가 언어 기록 없는 이름 · drift 를 만드는 근인). `names`/`texts` 로 문서 언어 전부를 준다.
180
+ * 🔴 **조건 라벨(`condition`)도 여기다** — name 채널의 별칭(A-5)이라 다국어 그룹에선 그 플로우의 `names` 로 쓴다.
181
+ */
182
+ | {
183
+ kind: 'locale-texts-required';
184
+ opIndex: number;
185
+ id: string;
186
+ field: string;
187
+ locales: string[];
188
+ }
189
+ /** 기능이 꺼진 그룹(문서 언어 미설정)에 로케일 맵이나 `translation.confirm` 을 줬다 — 평문을 쓴다. */
190
+ | {
191
+ kind: 'locale-texts-not-enabled';
192
+ opIndex: number;
193
+ id: string;
194
+ field: string;
195
+ }
196
+ /** 로케일 맵에 문서 언어가 빠졌다 — 에이전트 쓰기는 문서 언어 **전부**를 함께 쓴다(그래야 표식이 안 생긴다). */
197
+ | {
198
+ kind: 'locale-texts-incomplete';
199
+ opIndex: number;
200
+ id: string;
201
+ field: string;
202
+ missing: string[];
203
+ }
204
+ /** 문서 언어(설계 + 병기) 밖의 언어 — 표시도 판정도 안 하는 칸이라 아무도 못 보고 못 고친다. */
205
+ | {
206
+ kind: 'locale-not-in-document';
207
+ opIndex: number;
208
+ id: string;
209
+ field: string;
210
+ locale: string;
211
+ locales: string[];
212
+ }
213
+ /**
214
+ * `translation.confirm` 의 대상 칸에 확인 요청 표식이 없다 — 읽은 뒤 사람이 이미 정리했거나 언어를 잘못 짚었다.
215
+ * 다시 읽어 `i18n.reviewRequests` 를 본다.
216
+ */
217
+ | {
218
+ kind: 'review-request-missing';
219
+ opIndex: number;
220
+ id: string;
221
+ locale: string;
222
+ }
130
223
  /** 하강 실행 중 실패 — 실행분은 롤백됨(원자성 2차 방어) */
131
224
  | {
132
225
  kind: 'lower-failed';
@@ -0,0 +1,24 @@
1
+ import { BpmnDocumentLocales } from '../../adapter/i18n';
2
+ import { SymbolicOp } from '../symbolicOp';
3
+ import { DocumentIndex } from './documentIndex';
4
+ import { ResolveIssue } from './issues';
5
+ /** 하강 뒤 한 요소에 할 번역 쓰기 — 같은 요소를 여러 op 가 건드리면 합친다. */
6
+ export interface LocaleWrite {
7
+ /** 문서 언어 전부의 문구. `null` = 라벨 제거(칸 · 표식 전부). 없으면 문구를 쓰지 않는다. */
8
+ texts?: Record<string, string> | null;
9
+ /** 문구를 바꾸지 않고 해소할 확인 요청의 언어들(`translation.confirm`). */
10
+ confirm?: string[];
11
+ }
12
+ /**
13
+ * 다국어 검증(순수). `config` = 그룹의 문서 언어 설정 — 없으면 기능이 꺼진 그룹이다.
14
+ * 페이로드 shape(맵이 객체인가 · 값이 비지 않은 문자열인가)도 여기서 본다 — 패스 0 은 kind 별 고정 자리만 안다.
15
+ */
16
+ export declare function validateLocaleTexts(ops: readonly SymbolicOp[], index: DocumentIndex, config: BpmnDocumentLocales | undefined): ResolveIssue[];
17
+ /**
18
+ * 하강 준비 — 검증 통과가 전제다. 반환 `ops` 는 평문만 남은 사본(설계 언어 문구 · `translation.confirm` 제외)이고,
19
+ * `writes` 는 하강 뒤 요소별로 할 번역 쓰기다. `config` 가 없으면(기능 끔) 입력을 그대로 돌려준다.
20
+ */
21
+ export declare function localizeOps(ops: readonly SymbolicOp[], index: DocumentIndex, config: BpmnDocumentLocales | undefined): {
22
+ ops: SymbolicOp[];
23
+ writes: Map<string, LocaleWrite>;
24
+ };
@@ -9,6 +9,7 @@ import { default as BpmnReplace } from 'bpmn-js/lib/features/replace/BpmnReplace
9
9
  import { default as SpaceTool } from 'diagram-js/lib/features/space-tool/SpaceTool';
10
10
  import { Shape } from 'bpmn-js/lib/model/Types';
11
11
  import { SymbolicOp } from '../symbolicOp';
12
+ import { ResolveIssue } from './issues';
12
13
  import { BwlSide } from '../../core/hints';
13
14
  import { GeometryFinding, GeometryScene } from '../geometryAudit';
14
15
  /** resolver 가 대면하는 bpmn-js 서비스 표면 — 하강은 이 묶음만 사용한다. */
@@ -61,6 +62,14 @@ export declare function annotationSpot(elementRegistry: ElementRegistry, anchor:
61
62
  x: number;
62
63
  y: number;
63
64
  };
65
+ /**
66
+ * 하강 중에야 드러나는 거부를 **구조화된 issue 로** 올린다 — `applyBpmnOps` 가 자유 문장 `lower-failed` 대신
67
+ * 이 issue 를 그대로 낸다(LLM 교정 루프가 읽을 수 있게). 롤백은 다른 throw 와 같다(undo 에피소드가 되감는다).
68
+ */
69
+ export declare class LowerIssueError extends Error {
70
+ readonly issue: ResolveIssue;
71
+ constructor(issue: ResolveIssue);
72
+ }
64
73
  /** 검증 통과가 전제 — 참조 해소 실패는 여기서 프로그래밍 오류로 throw 되고 applyBpmnOps 가 롤백한다. */
65
74
  export declare function lowerOps(ops: readonly SymbolicOp[], services: ResolverServices): void;
66
75
  /** 주석 도형이 소유자로 등장할 수 있는 finding 의 소유 도형 id. */
@@ -300,8 +300,11 @@ export declare function planLabelBesideOwnLine(edge: GeometryEdge, scene: Geomet
300
300
  * — 공유 구간 검사 + 미끄럼 후보를 더한 판도 이득 0) · base 에서 이미 표류한 라벨(선 기하가 안 바뀌어 이 가지에 안 온다).
301
301
  */
302
302
  export declare function followMovedEdgeLabels(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, before: ReadonlyMap<string, EdgeLabelSnapshot>): void;
303
- /** 이 배치가 **새로 만든** 간선의 라벨을 선·경계선에서 뗀다(맨 마지막 — 라벨 귀속 보정 뒤). 선재 라벨은 불가침. */
304
- export declare function repairIntroducedLabelLines(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeEdgeIds: ReadonlySet<string>): void;
303
+ /**
304
+ * 이 배치가 **새로 만든** 간선의 라벨을 선·경계선에서 뗀다(맨 마지막 — 라벨 귀속 보정 뒤). 선재 라벨은 불가침 —
305
+ * 단 `reroutedEdgeIds`(이 배치가 끝점을 바꿔 경로를 다시 깐 선재 간선)는 대상이다(B-114 ⓔ ⓑ).
306
+ */
307
+ export declare function repairIntroducedLabelLines(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeEdgeIds: ReadonlySet<string>, reroutedEdgeIds?: ReadonlySet<string>): void;
305
308
  /**
306
309
  * 이 배치가 **새로 만든** 간선의 라벨 귀속 문제를 고친다(하강 직후, undo 에피소드 안, 경로 보정들 뒤).
307
310
  * 선재 간선의 라벨은 사람이 놓았을 수 있어 손대지 않는다(안전 성질 2).
@@ -1,4 +1,13 @@
1
+ import { BpmnDocumentLocales } from '../../adapter/i18n';
1
2
  import { SymbolicOp } from '../symbolicOp';
2
3
  import { DocumentIndex } from './documentIndex';
3
4
  import { ResolveIssue } from './issues';
4
- export declare function validateOps(ops: readonly SymbolicOp[], index: DocumentIndex): ResolveIssue[];
5
+ /** 검증 옵션 — 문서 밖에서만 알 수 있는 입력. */
6
+ export interface ValidateOpsOptions {
7
+ /**
8
+ * 그룹의 문서 언어 설정(B-4 ④) — 있으면 다국어 그룹이다(문구 = `names`/`texts` 로 문서 언어 전부 · 평문 거부).
9
+ * 없으면 기능이 꺼진 그룹이다(평문만 · 맵 거부). 그룹 설정은 문서에 없으므로 호스트가 공급한다.
10
+ */
11
+ i18n?: BpmnDocumentLocales;
12
+ }
13
+ export declare function validateOps(ops: readonly SymbolicOp[], index: DocumentIndex, options?: ValidateOpsOptions): ResolveIssue[];
@@ -1,5 +1,5 @@
1
1
  import { BwlAfterColumn, BwlBand, BwlPlacement, BwlPoolOrder } from '../core/hints';
2
- import { IrAssociation, IrDataAssociation, IrDataStore, AddableFlowNodeType, IrFlowCondition, IrGroup, IrLane, IrMessageFlow, IrNode, IrParticipant, IrSequenceFlow, IrTextAnnotation } from '../core/ir';
2
+ import { IrLocaleTexts, IrAssociation, IrDataAssociation, IrDataStore, AddableFlowNodeType, IrEventDefinition, IrFlowCondition, IrGroup, IrLane, IrMessageFlow, IrNode, IrParticipant, IrSequenceFlow, IrTextAnnotation } from '../core/ir';
3
3
  /** LLM 이 산출하는 단일 심볼릭 op. `kind` 로 판별한다. */
4
4
  export type SymbolicOp =
5
5
  /** 풀 추가. `order` = 풀 세로 순서 힌트(생략 시 op 순서가 곧 배치 순서). */
@@ -57,17 +57,21 @@ export type SymbolicOp =
57
57
  kind: 'participant.update';
58
58
  id: string;
59
59
  name?: string | null;
60
+ names?: IrLocaleTexts;
60
61
  lanes?: IrLane[];
61
62
  }
62
63
  /** `lane` = 레인 재배정(이동). `type` = 태스크/이벤트 타입 전환(bpmn-js replace 하강 — 외래 확장 속성 유실 가능성은 불변식 2 가 완충). */
63
64
  /** 노드 갱신. `band` = 기존 노드의 행 재지정(B-72 ⓐ 승격) — 기존 컨테이너에서 band 는 **행 identity** 다. */
64
- /** ⚠ `type` 은 **만들 수 있는 집합**으로 좁다 — read 집합(11)이 아니다(B-87 ⓐ). 타입이 0차 방어다. */
65
+ /** ⚠ `type` 은 **만들 수 있는 집합**으로 좁다 — read 집합이 아니다(B-87 ⓐ). 타입이 0차 방어다. */
66
+ /** `eventDefinition` = `type: 'intermediateCatchEvent'` 로 전환할 때 **함께 필수**다(`node.add` 의 `node.eventDefinition` 과 같은 짝 규칙). */
65
67
  | {
66
68
  kind: 'node.update';
67
69
  id: string;
68
70
  name?: string | null;
71
+ names?: IrLocaleTexts;
69
72
  lane?: string;
70
73
  type?: AddableFlowNodeType;
74
+ eventDefinition?: IrEventDefinition;
71
75
  band?: BwlBand;
72
76
  }
73
77
  /**
@@ -83,6 +87,7 @@ export type SymbolicOp =
83
87
  kind: 'sequenceFlow.update';
84
88
  id: string;
85
89
  name?: string | null;
90
+ names?: IrLocaleTexts;
86
91
  condition?: IrFlowCondition | null;
87
92
  source?: string;
88
93
  target?: string;
@@ -90,16 +95,19 @@ export type SymbolicOp =
90
95
  kind: 'messageFlow.update';
91
96
  id: string;
92
97
  name?: string | null;
98
+ names?: IrLocaleTexts;
93
99
  } | {
94
100
  kind: 'dataStore.update';
95
101
  id: string;
96
102
  name?: string | null;
103
+ names?: IrLocaleTexts;
97
104
  }
98
105
  /** `text` 는 주석의 본질이라 제거 불가(주석 삭제 = element.remove). `placement: null` = 힌트 제거(휴리스틱 복귀). */
99
106
  | {
100
107
  kind: 'annotation.update';
101
108
  id: string;
102
109
  text?: string;
110
+ texts?: IrLocaleTexts;
103
111
  placement?: BwlPlacement | null;
104
112
  }
105
113
  /** `members` 는 전체 재선언(생략 = 보존) — resolver 가 바운즈를 재산출한다. */
@@ -107,8 +115,20 @@ export type SymbolicOp =
107
115
  kind: 'group.update';
108
116
  id: string;
109
117
  name?: string | null;
118
+ names?: IrLocaleTexts;
110
119
  members?: string[];
111
120
  }
121
+ /**
122
+ * **확인만** — 그 요소의 `locale` 칸에 걸린 확인 요청(`bwl:reviewRequest`)을 문구를 바꾸지 않고 해소한다.
123
+ * 다른 언어가 고쳐졌지만 이 칸의 번역은 여전히 맞을 때(오타 수준 수정) 쓴다. 번역을 고쳐야 하면 이것이 아니라
124
+ * 그 요소의 `*.update` 에 `names`/`texts` 를 준다 — 문구가 **실제로 바뀐 칸**의 표식만 해소된다.
125
+ * 다국어 그룹에서만 받는다 · 그 칸에 표식이 없으면 거부한다(읽은 뒤 사람이 이미 정리했을 수 있다).
126
+ */
127
+ | {
128
+ kind: 'translation.confirm';
129
+ id: string;
130
+ locale: string;
131
+ }
112
132
  /** 요소 제거(카테고리 무관 — 도형·연결 균일, 부착 연결은 연쇄 제거). */
113
133
  | {
114
134
  kind: 'element.remove';
@@ -118,5 +138,5 @@ export type SymbolicOp =
118
138
  * 심볼릭 op 종류의 닫힌 집합(단일 출처). resolver·JSON Schema(`schema.ts`)가 공유한다.
119
139
  * 아래 컴파일타임 단언이 이 튜플과 `SymbolicOp['kind']` 의 일치를 강제 — 한쪽만 늘리면 타입 에러.
120
140
  */
121
- export declare const SYMBOLIC_OP_KINDS: readonly ["participant.add", "participant.update", "node.add", "node.update", "sequenceFlow.add", "sequenceFlow.update", "messageFlow.add", "messageFlow.update", "dataStore.add", "dataStore.update", "dataAssociation.add", "association.add", "annotation.add", "annotation.update", "group.add", "group.update", "element.remove"];
141
+ export declare const SYMBOLIC_OP_KINDS: readonly ["participant.add", "participant.update", "node.add", "node.update", "sequenceFlow.add", "sequenceFlow.update", "messageFlow.add", "messageFlow.update", "dataStore.add", "dataStore.update", "dataAssociation.add", "association.add", "annotation.add", "annotation.update", "group.add", "group.update", "translation.confirm", "element.remove"];
122
142
  export type SymbolicOpKind = (typeof SYMBOLIC_OP_KINDS)[number];