# ice-entity-designer-dsl
**Repository Path**: ice-render/ice-entity-designer-dsl
## Basic Information
- **Project Name**: ice-entity-designer-dsl
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-11
- **Last Updated**: 2026-09-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# ice-entity-designer-dsl
JSON-first DSL for AI agents to drive `ice-entity-designer` without learning the imperative canvas API.
> Changelog: [CHANGELOG.md](./CHANGELOG.md).
One JSON DSL, seven document kinds — the same document drives rendering, validation and export:
- **ER document** (`entities` / `relations`): entity-relation models and database schemas.
- **Flowchart document** (`kind: "flowchart"` with `nodes` / `edges`): process flows,
decision trees, and algorithms, with optional coordinates (layered auto-layout).
- **BPMN document** (`kind: "bpmn"` with `nodes` / `edges`): business processes with
participants (pools), lanes, events, gateways and message flows. Containers are
nodes too, so the document stays a flat list; geometries are optional.
- **UML class diagram** (`kind: "uml"` with `nodes` / `edges`): classes, interfaces and
enums with free-text members (`- id: string`, `+ pay(): void`), plus the six relation
kinds (inheritance / realization / association / aggregation / composition /
dependency). Coordinates are optional — inheritance drives a top-down layering.
- **Gantt** (`kind: "gantt"` with `nodes` / `edges`): a schedule — each task carries `start`
(`YYYY-MM-DD`), `days` and `progress`; edges are finish-to-start dependencies. **No
coordinates at all**: the horizontal axis is time, the vertical axis is declaration order.
- **Statechart** (`kind: "statechart"` with `nodes` / `edges`): initial/final pseudo-states,
states, **composite states** (containers — children follow when you drag the parent) and
transitions labelled `event [guard] / action`. Coordinates are optional — the flow lays
out left to right.
- **Power document** (`kind: "power"`): one-line diagrams — equipment kinds mirror
`ice-entity-designer`'s power pack, `attachedTo` expresses a busbar T-connection and
`voltageLevel` drives the colour code. Semantic checks (voltage consistency / busbar feed /
五防) live in `result.designer.validatePower()`. Coordinates are optional.
The package contains:
- DSL types and schema, plus a structural validator that dispatches on `kind`
- compilers from DSL to component props: `compileDsl` (ER) / `compileFlowDsl` / `compileBpmnDsl` /
`compileUmlDsl` / `compileStatechartDsl` / `compileGanttDsl` / `compilePowerDsl`, sharing one
layered layout
- browser runtime that renders DSL through `ice-entity-designer`
- browser examples with a JSON editor (`examples/entity-editor-dsl.html`,
`examples/flowchart-dsl.html`, `examples/bpmn-dsl.html`, `examples/uml-dsl.html`,
`examples/statechart-dsl.html`, `examples/gantt-dsl.html`, `examples/power-dsl.html`)
- all diagram examples share `examples/canvas-interactions.js`: canvas fills the preview
pane, wheel zooms at the cursor (`ICE.zoomAt`), dragging blank space (or any middle-button
drag) pans, plus 「适应视图 / 复位视图」 buttons
## Install
```bash
npm install ice-entity-designer-dsl
```
`ice-entity-designer`(DSL 的渲染目标)与引擎 `ice-render` 都是本包的 **peer dependency**:
两者在 UMD 产物里同样是 **external**(`globals: { 'ice-render': 'ICE' }` /
`{ 'ice-entity-designer': 'IED' }`),由宿主提供 —— 所以一个纯 `
```
## Node/ESM usage
```ts
import { validateDsl, compileDsl, compileFlowDsl, renderDsl } from 'ice-entity-designer-dsl';
```
## Flowchart usage
Node coordinates are optional: if any node omits `left`/`top`, the whole graph is laid
out in layers (layer 0 = nodes without incoming edges; every other node = max
predecessor layer + 1).
```js
const flowchart = {
schemaVersion: 1,
kind: 'flowchart',
nodes: [
{ id: 'start', kind: 'terminator', title: '开始' },
{ id: 'check', kind: 'decision', title: '库存充足?' },
{ id: 'done', kind: 'terminator', title: '结束' },
{ id: 'restock', kind: 'io', title: '通知补货' },
],
edges: [
{ source: 'start', target: 'check' },
{ source: 'check', target: 'done', label: '是' },
{ source: 'check', target: 'restock', label: '否', sourcePort: 'R', targetPort: 'L' },
],
};
// kind === 'flowchart' 时 result.designer 是 FlowDesigner(createNode / createEdge / undo / …)
const result = ICEDSL.renderDsl('canvas', flowchart);
```
Node kinds: `terminator` (start/end pill) / `process` (action, default) / `decision`
(diamond) / `io` (parallelogram). Edge ports are `T` / `R` / `B` / `L` / `C` (default
`B` → `T`); `linkShape` is `visio` (orthogonal, default) or `bezier`.
## BPMN usage
Pools and lanes are nodes (`kind: "pool"` / `kind: "lane"`); everything else points
into a container with `parent`. Give coordinates and the compiler is the identity —
omit them and containers are sized from their contents while the flow nodes are laid
out left-to-right inside their container.
左侧是这份 DSL,右侧是它的渲染结果(`examples/bpmn-dsl.html`)——节点坐标全部省略,
池高、泳道条带与图元落位都是编译期算出来的:
```js
const bpmn = {
schemaVersion: 1,
kind: 'bpmn',
nodes: [
{ id: 'bank', kind: 'pool', title: '银行' },
{ id: 'accept', kind: 'lane', title: '受理岗', parent: 'bank' },
{ id: 'risk', kind: 'lane', title: '风控岗', parent: 'bank' },
{ id: 'submit', kind: 'event', title: '申请提交', eventKind: 'start', parent: 'accept' },
{ id: 'verify', kind: 'task', title: '身份核验', taskType: 'service', parent: 'accept' },
{ id: 'ok', kind: 'event', title: '申请通过', eventKind: 'end', parent: 'risk' },
],
edges: [
{ source: 'submit', target: 'verify', label: '受理' },
{ source: 'verify', target: 'ok' },
],
};
const result = ICEDSL.renderDsl('canvas', bpmn);
// result.kind === 'bpmn'; result.designer is a BpmnDesigner
result.designer.validateBpmn(); // 语义检查:每个池一个开始事件、顺序流不跨池…
const xml = IED.toBpmnXml(result.designer); // BPMN 2.0 + BPMNDI(互操作格式,不是执行模型)
const svg = result.designer.toSvg(); // 矢量 SVG(与画布同一口径,放大不糊)
```
### Gantt usage
```js
const plan = {
schemaVersion: 1,
kind: 'gantt',
nodes: [
{ id: 'review', title: '需求评审', start: '2026-03-02', days: 4, progress: 1 },
{ id: 'design', title: '交互设计', start: '2026-03-05', days: 6, progress: 0.8 },
{ id: 'frontend', title: '前端开发', start: '2026-03-10', days: 12, progress: 0.35 },
],
edges: [
{ source: 'review', target: 'design' }, // 完成 → 开始
{ source: 'design', target: 'frontend' },
],
options: { dayWidth: 28, fitViewport: true },
};
const result = ICEDSL.renderDsl('canvas', plan);
// result.kind === 'gantt'; result.designer is a GanttDesigner
result.designer.validateGantt(); // 依赖成环 / 进度越界 / 天数非法 / 资源冲突
result.designer.autoSchedule(); // 自动排程:按依赖推到「前置结束之后」(只推后不提前)
result.designer.criticalPath(); // 关键路径(按「尽早排」归一化算浮时)
result.designer.toSvg(); // 矢量导出(含日期刻度与进度)
```
`options.autoSchedule: true` 可以让**渲染时**就按依赖把排期推到最早可行(文档里写的日期当作下界),
`resource` 字段用于资源冲突检查(同一负责人时间重叠会在 `validateGantt()` 里报出)。
关键路径用 CPM 的标准口径:有前置的任务由前置的最早完工决定,没有前置的才以自身日期为锚,
所以末端任务的日期被手写得很晚、或者刚跑完 `autoSchedule()`,链路判定都不会退化成单个任务。
Fields: `start` (`YYYY-MM-DD`, required), `days` (default 1), `progress` (0..1, default 0),
`row` (defaults to declaration order). **Do not invent coordinates** — the timeline is derived;
dragging a bar in the editor snaps it to whole days.
### Statechart usage
```js
const chart = {
schemaVersion: 1,
kind: 'statechart',
nodes: [
{ id: 'start', kind: 'initial' },
{ id: 'pending', title: '待支付' },
{ id: 'paid', title: '已支付' },
{ id: 'processing', kind: 'composite', title: '订单处理' },
{ id: 'stock', title: '库存校验', parent: 'processing' }, // 子状态:进复合状态
{ id: 'done', kind: 'final' },
],
edges: [
{ source: 'start', target: 'pending' },
{ source: 'pending', target: 'paid', event: '支付成功', guard: '金额 > 0', action: '生成订单' },
{ source: 'paid', target: 'stock', event: '进入处理' },
{ source: 'stock', target: 'done', event: '已发货' },
],
};
const result = ICEDSL.renderDsl('canvas', chart);
// result.kind === 'statechart'; result.designer is a StatechartDesigner
result.designer.validateStatechart(); // 缺初始状态 / 终态出边 / 孤立节点 / 不可达
result.designer.toSvg(); // 矢量导出(实心箭头也是填充路径)
```
Node kinds: `initial` (filled dot) / `final` (bullseye) / `state` (rounded box, default) /
`composite` (container; children declared with `parent`). Transition labels are composed
from `event` / `guard` / `action` (missing parts are omitted).
### UML usage
```js
const uml = {
schemaVersion: 1,
kind: 'uml',
nodes: [
{ id: 'entity', kind: 'class', title: 'Entity', abstract: true, methods: ['+ save(): void'] },
{ id: 'user', kind: 'class', title: 'User', attributes: ['- email: string'] },
{ id: 'payable', kind: 'interface', title: 'Payable', methods: ['+ pay(): void'] },
],
edges: [
{ source: 'user', target: 'entity', type: 'inheritance' }, // source = 子类,target = 父类
{ source: 'user', target: 'payable', type: 'realization' },
],
};
const result = ICEDSL.renderDsl('canvas', uml);
// result.kind === 'uml'; result.designer is a UmlDesigner
result.designer.validateUml(); // 重名类 / 继承成环 / 悬空关系
result.designer.toSvg(); // 矢量导出
IED.toPlantUml(result.designer); // 文本互操作(PlantUML / Mermaid 语法子集)
IED.fromPlantUml(text, result.designer); // 反向导入
```
Node kinds: `class` (default) / `interface` / `enum`. Relation `type`: `inheritance` /
`realization` / `association` / `aggregation` / `composition` / `dependency` — the
direction convention is inherited from PlantUML (`A <|-- B` = B inherits A, so the
edge is `{ source: 'B', target: 'A' }`).
Node kinds: `pool` / `lane` (containers), `task` (default), `event`, `gateway`,
`subprocess`, `dataObject`, `annotation`; events carry `eventKind` (start /
intermediate / end) + `trigger`, gateways carry `gatewayType` (exclusive / parallel
/ inclusive / event), tasks carry `taskType` (none / user / service / script / send
/ receive / manual). Edge `type`: `sequence` (default) / `message` (across pools) /
`association` (data objects, annotations); sequence flows also take `condition` and
`isDefault`.
## API
- `validateDsl(dsl)` —— ER / flowchart / BPMN / UML / statechart / gantt / power documents (dispatches on `kind`); `validateFlowDsl(dsl)` / `validateBpmnDsl(dsl)` / `validateUmlDsl(dsl)` / `validateStatechartDsl(dsl)` / `validateGanttDsl(dsl)` / `validatePowerDsl(dsl)` for one kind only
- 返回值除了 `{ valid, errors }`(英文句子,给人看)还带 **`diagnostics`**:`{ severity, code, message, path }`,
其中 **`code` 才是合同**(`IED_DSL_*`,见 `src/types.ts` 的 `IED_DSL_CODES`),`path` 指出位置(如 `nodes[3].source`)。
**Agent / 工具请按 `code` 分支、按 `path` 定位**,不要匹配 `message` 里的自然语言 —— 这是
ice-render [`docs/architecture/17-i18n-boundary.md`](../ice-render/docs/architecture/17-i18n-boundary.md)
里那条「库不翻译文案,但必须给稳定 id」的契约。
- 常用码:`IED_DSL_ROOT_NOT_OBJECT` / `IED_DSL_SCHEMA_VERSION_UNSUPPORTED` / `IED_DSL_ID_INVALID` /
`IED_DSL_ID_DUPLICATED` / `IED_DSL_NOT_OBJECT` / `IED_DSL_NODES_NOT_ARRAY` / `IED_DSL_EDGES_NOT_ARRAY` /
`IED_DSL_KIND_INVALID` / `IED_DSL_FIELD_TYPE` / `IED_DSL_FIELD_ENUM` / `IED_DSL_FIELD_FORMAT` /
`IED_DSL_FIELD_RANGE` / `IED_DSL_EDGE_ENDPOINT_INVALID` / `IED_DSL_EDGE_ENDPOINT_UNKNOWN` /
`IED_DSL_EDGE_SELF_LOOP` / `IED_DSL_PARENT_INVALID` / `IED_DSL_PARENT_UNKNOWN` / `IED_DSL_PARENT_SELF` /
`IED_DSL_PARENT_KIND` / `IED_DSL_PARENT_NOT_ALLOWED` / `IED_DSL_ATTACHED_TO_UNKNOWN` /
`IED_DSL_ATTACHED_TO_NOT_BUSBAR`(完整列表见 `src/types.ts`)。
- `compileDsl(dsl)` —— ER document → `Entity` / `Relation` props
- `compileFlowDsl(dsl)` —— flowchart document → `FlowNode` / `FlowEdge` props (with layered auto-layout)
- `compileBpmnDsl(dsl)` —— BPMN document → `FlowNode` / `FlowEdge` props (container auto-geometry + container-scoped auto-layout)
- `compileUmlDsl(dsl)` —— UML document → `UmlClass` / `UmlRelation` props (inheritance-driven layering)
- `compileStatechartDsl(dsl)` —— statechart document → `StateNode` / `StateTransition` props (flow layering + composite auto-sizing)
- `compileGanttDsl(dsl)` —— gantt document → `GanttTask` / `GanttDependency` props (no coordinates; rows filled in)
- `compilePowerDsl(dsl)` —— power document → `PowerSymbol` / `PowerLine` props (busbar T-connections resolved, voltage colour code applied)
- `layeredLayout(items, edges, options)` —— the shared layered layout (`direction: "vertical" | "horizontal"`)
- `renderDsl(canvasOrId, dsl)` —— renders any kind, returns `{ kind, ice, designer }`
- `renderPowerDsl(canvasOrId, dsl)` —— power documents specifically (`renderFlowDsl` / `renderBpmnDsl` / `renderUmlDsl` / `renderStatechartDsl` / `renderGanttDsl` are the per-kind equivalents)
- Export: `result.designer.toSvg(options)` (flowchart / BPMN) or `IED.exportSvg(result.ice, options)`; `options` = `{ area: 'content' | 'viewport', padding, scale, background, includeTools }`. The SVG is regenerated from the component tree + path commands, so it matches the canvas (geometry, styles, opacity, shadows, link labels) and can be rasterised to PNG/PDF by any external tool.
- `DSL_SCHEMA_VERSION`
## Round-trip: render → edit → `toDsl()`
`renderDsl()` 返回的对象自带 `toDsl()`,用来把**用户改过的实例**写回成同一份 DSL 文档:
```js
const result = ICEDSL.renderDsl('canvas', doc);
// 用户在画布上拖动 / 改名 / 分合 / 增删……
result.designer.updateNode('check', { title: '库存够吗?', left: 500, top: 480 });
const edited = result.toDsl(); // 同一份 DSL:位置与语义改动都在
ICEDSL.validateDsl(edited).valid; // true —— 导出结果永远能过校验
ICEDSL.renderDsl('canvas', edited); // 也能直接再渲染一遍
```
单独使用时是 `toDsl(resultOrDesigner, { kind? })`(不传 `kind` 时按图元自动判别)。
契约(7 种 `kind` 都有测试兜底):
- **id 保真**:Agent 产出时用的 `id` 原样带回(导出顺序 = 实例顺序,BPMN 会把池/泳道排在业务图元之前);
文档里没写 `id` 的连线会拿到 `edge-0` 这类稳定 id,而不是每次重新生成的随机 UUID;
- **坐标是绝对坐标**:嵌套容器(BPMN 池 / 泳道、状态机复合状态)的相对坐标已沿父链还原;
- **只导出 DSL 词汇表里的字段**:引擎内部字段(zIndex、变换矩阵等)不外泄;
- **必过校验、必须可序列化**:结果里没有 `undefined`,`JSON.parse(JSON.stringify(doc))` 无损往返;
- **再渲染等价**:把导出的文档再渲染一遍,节点 id 与位置与上一遍一致;
- **推断不出来就报错**:实例里没有任何可识别图元时,`toDsl()` 抛错并提示显式传 `kind`,不会悄悄当成 ER 文档。
## Example
Open `examples/entity-editor-dsl.html` (ER), `examples/flowchart-dsl.html`
(flowchart), `examples/bpmn-dsl.html` (BPMN), `examples/uml-dsl.html` (UML),
`examples/statechart-dsl.html` (statechart), `examples/gantt-dsl.html` (gantt) or
`examples/power-dsl.html` (power one-line diagram) after building.
渲染结果可以滚轮缩放、空白处拖拽平移,工具栏还有「适应视图 / 复位视图」。
每个示例页都有一个 **「导出当前文档」** 按钮:在画布上拖动 / 改名 / 分合之后点一下,
左侧 JSON 立刻变成**你改过之后**的那份文档(仍是同一份 DSL,可直接再渲染回去)。
在控制台执行 `window.__dslResult.toDsl()` 拿到的是同一个结果。
## Agent discovery
Agents can use this project through:
1. npm package exports
2. `AGENTS.md`
3. `skills/ice-entity-designer-dsl/SKILL.md`
4. optional MCP wrapper in a separate package
The core runtime does not require MCP.