ice-entity-designer-dsl 0.0.1
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 +70 -0
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +1 -0
- package/dist/index.umd.js +1 -0
- package/dist/types/compiler/dslToScene.d.ts +8 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/runtime/renderDsl.d.ts +6 -0
- package/dist/types/types.d.ts +73 -0
- package/dist/types/validate.d.ts +3 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ice-entity-designer-dsl
|
|
2
|
+
|
|
3
|
+
JSON-first ER DSL for AI agents to drive `ice-entity-designer` without learning the imperative canvas API.
|
|
4
|
+
|
|
5
|
+
The package contains:
|
|
6
|
+
|
|
7
|
+
- DSL types and schema
|
|
8
|
+
- structural validator
|
|
9
|
+
- compiler from DSL to Entity/Relation props
|
|
10
|
+
- browser runtime that renders DSL through `ice-entity-designer`
|
|
11
|
+
- browser example with a JSON editor
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm run build
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Browser usage
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<canvas id="canvas" width="1200" height="800"></canvas>
|
|
24
|
+
|
|
25
|
+
<script src="node_modules/ice-entity-designer/dist/index.umd.js"></script>
|
|
26
|
+
<script src="dist/index.umd.js"></script>
|
|
27
|
+
<script>
|
|
28
|
+
const dsl = {
|
|
29
|
+
schemaVersion: 1,
|
|
30
|
+
layout: 'layered',
|
|
31
|
+
entities: [
|
|
32
|
+
{ id: 'customer', name: 'Customer', fields: [{ name: 'id', type: 'number', primary: true }] },
|
|
33
|
+
{ id: 'order', name: 'Order', fields: [{ name: 'id', type: 'number', primary: true }] },
|
|
34
|
+
],
|
|
35
|
+
relations: [
|
|
36
|
+
{ source: 'customer', target: 'order', type: 'one-to-many' },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const { ice, designer } = ICEDSL.renderDsl('canvas', dsl);
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Node/ESM usage
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { validateDsl, compileDsl, renderDsl } from 'ice-render-dsl';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API
|
|
51
|
+
|
|
52
|
+
- `validateDsl(dsl)`
|
|
53
|
+
- `compileDsl(dsl)`
|
|
54
|
+
- `renderDsl(canvasOrId, dsl)`
|
|
55
|
+
- `DSL_SCHEMA_VERSION`
|
|
56
|
+
|
|
57
|
+
## Example
|
|
58
|
+
|
|
59
|
+
Open `examples/entity-editor-dsl.html` after building.
|
|
60
|
+
|
|
61
|
+
## Agent discovery
|
|
62
|
+
|
|
63
|
+
Agents can use this project through:
|
|
64
|
+
|
|
65
|
+
1. npm package exports
|
|
66
|
+
2. `AGENTS.md`
|
|
67
|
+
3. `skills/ice-render-dsl/SKILL.md`
|
|
68
|
+
4. optional MCP wrapper in a separate package
|
|
69
|
+
|
|
70
|
+
The core runtime does not require MCP.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("ice-entity-designer");function validateDsl(e){const t=[];if(!e||"object"!=typeof e||Array.isArray(e))return{valid:!1,errors:["DSL root must be an object"]};if(void 0!==e.schemaVersion&&1!==e.schemaVersion&&t.push("Unsupported schemaVersion: "+e.schemaVersion),Array.isArray(e.entities)){const i=new Set;e.entities.forEach((e,r)=>{const n=`entities[${r}]`;e&&"object"==typeof e?("string"==typeof e.id&&e.id.trim()?i.has(e.id)?t.push(`${n}.id is duplicated: ${e.id}`):i.add(e.id):t.push(n+".id must be a non-empty string"),Array.isArray(e.fields)?e.fields.forEach((e,i)=>{e&&"object"==typeof e&&"string"==typeof e.name&&e.name.trim()||t.push(`${n}.fields[${i}].name must be a non-empty string`)}):t.push(n+".fields must be an array")):t.push(n+" must be an object")})}else t.push("entities must be an array");return void 0===e.relations||Array.isArray(e.relations)?(e.relations||[]).forEach((e,i)=>{const r=`relations[${i}]`;e&&"object"==typeof e?("string"==typeof e.source&&e.source.trim()||t.push(r+".source must be a non-empty string"),"string"==typeof e.target&&e.target.trim()||t.push(r+".target must be a non-empty string")):t.push(r+" must be an object")}):t.push("relations must be an array"),{valid:0===t.length,errors:t}}function entityName(e){return e.entityName||e.name||e.id}function relationType(e){return e.relationType||e.type||"one-to-many"}function compileDsl(e){return{entities:e.entities.map(e=>({id:e.id,entityName:entityName(e),fields:e.fields||[],left:e.left,top:e.top,width:e.width,height:e.height,style:e.style,headerStyle:e.headerStyle,fieldStyle:e.fieldStyle,dividerStyle:e.dividerStyle,draggable:e.draggable,interactive:e.interactive})),relations:(e.relations||[]).map((e,t)=>({id:e.id||"relation-"+t,relationType:relationType(e),sourceId:e.source,targetId:e.target,sourceField:e.sourceField||"id",targetField:e.targetField||"id",nullable:e.nullable,onDelete:e.onDelete,onUpdate:e.onUpdate,joinTableName:e.joinTableName,fromKey:e.fromKey,toKey:e.toKey,sourceCardinality:e.sourceCardinality,targetCardinality:e.targetCardinality,label:e.label,style:e.style,arrow:e.arrow,linkShape:e.linkShape,routeType:e.routeType,routeOffset:e.routeOffset,curveType:e.curveType,lineDash:e.lineDash})),layout:e.layout,options:e.options}}exports.DSL_SCHEMA_VERSION=1,exports.compileDsl=compileDsl,exports.renderDsl=function renderDsl(t,i){const r=validateDsl(i);if(!r.valid)throw new Error(r.errors.join("\n"));const n=compileDsl(i),a=(new e.ICE).init(t),o=new e.EntityDesigner(a);if(n.entities.forEach(e=>{o.createEntity(e)}),function positionLinks(e,t){const i=new Map;return e.entities.forEach(e=>{i.set(e.state.id,e.getMinBoundingBox(!0))}),t.relations.map(e=>{const t=i.get(e.sourceId),r=i.get(e.targetId);let n="R",a="L";if(t&&r){const e=r.center[0]-t.center[0],i=r.center[1]-t.center[1];Math.abs(e)>Math.abs(i)?(n=e>=0?"R":"L",a=e>=0?"L":"R"):(n=i>=0?"B":"T",a=i>=0?"T":"B")}return{...e,routeType:e.routeType||"orthogonal",linkShape:e.linkShape||"visio",links:{start:{id:e.sourceId,position:n},end:{id:e.targetId,position:a}}}})}(o,n).forEach(e=>{o.createRelation(e)}),"layered"===n.layout||"horizontal"===n.layout){const t=n.options||{};new e.ICELayeredLayout({gapX:t.gapX||120,gapY:t.gapY||50}).layoutContainer(a)}return{ice:a,designer:o}},exports.validateDsl=validateDsl;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ICE as e,EntityDesigner as t,ICELayeredLayout as i}from"ice-entity-designer";const r=1;function validateDsl(e){const t=[];if(!e||"object"!=typeof e||Array.isArray(e))return{valid:!1,errors:["DSL root must be an object"]};if(void 0!==e.schemaVersion&&1!==e.schemaVersion&&t.push("Unsupported schemaVersion: "+e.schemaVersion),Array.isArray(e.entities)){const i=new Set;e.entities.forEach((e,r)=>{const n=`entities[${r}]`;e&&"object"==typeof e?("string"==typeof e.id&&e.id.trim()?i.has(e.id)?t.push(`${n}.id is duplicated: ${e.id}`):i.add(e.id):t.push(n+".id must be a non-empty string"),Array.isArray(e.fields)?e.fields.forEach((e,i)=>{e&&"object"==typeof e&&"string"==typeof e.name&&e.name.trim()||t.push(`${n}.fields[${i}].name must be a non-empty string`)}):t.push(n+".fields must be an array")):t.push(n+" must be an object")})}else t.push("entities must be an array");return void 0===e.relations||Array.isArray(e.relations)?(e.relations||[]).forEach((e,i)=>{const r=`relations[${i}]`;e&&"object"==typeof e?("string"==typeof e.source&&e.source.trim()||t.push(r+".source must be a non-empty string"),"string"==typeof e.target&&e.target.trim()||t.push(r+".target must be a non-empty string")):t.push(r+" must be an object")}):t.push("relations must be an array"),{valid:0===t.length,errors:t}}function entityName(e){return e.entityName||e.name||e.id}function relationType(e){return e.relationType||e.type||"one-to-many"}function compileDsl(e){return{entities:e.entities.map(e=>({id:e.id,entityName:entityName(e),fields:e.fields||[],left:e.left,top:e.top,width:e.width,height:e.height,style:e.style,headerStyle:e.headerStyle,fieldStyle:e.fieldStyle,dividerStyle:e.dividerStyle,draggable:e.draggable,interactive:e.interactive})),relations:(e.relations||[]).map((e,t)=>({id:e.id||"relation-"+t,relationType:relationType(e),sourceId:e.source,targetId:e.target,sourceField:e.sourceField||"id",targetField:e.targetField||"id",nullable:e.nullable,onDelete:e.onDelete,onUpdate:e.onUpdate,joinTableName:e.joinTableName,fromKey:e.fromKey,toKey:e.toKey,sourceCardinality:e.sourceCardinality,targetCardinality:e.targetCardinality,label:e.label,style:e.style,arrow:e.arrow,linkShape:e.linkShape,routeType:e.routeType,routeOffset:e.routeOffset,curveType:e.curveType,lineDash:e.lineDash})),layout:e.layout,options:e.options}}function renderDsl(r,n){const a=validateDsl(n);if(!a.valid)throw new Error(a.errors.join("\n"));const o=compileDsl(n),s=(new e).init(r),l=new t(s);if(o.entities.forEach(e=>{l.createEntity(e)}),function positionLinks(e,t){const i=new Map;return e.entities.forEach(e=>{i.set(e.state.id,e.getMinBoundingBox(!0))}),t.relations.map(e=>{const t=i.get(e.sourceId),r=i.get(e.targetId);let n="R",a="L";if(t&&r){const e=r.center[0]-t.center[0],i=r.center[1]-t.center[1];Math.abs(e)>Math.abs(i)?(n=e>=0?"R":"L",a=e>=0?"L":"R"):(n=i>=0?"B":"T",a=i>=0?"T":"B")}return{...e,routeType:e.routeType||"orthogonal",linkShape:e.linkShape||"visio",links:{start:{id:e.sourceId,position:n},end:{id:e.targetId,position:a}}}})}(l,o).forEach(e=>{l.createRelation(e)}),"layered"===o.layout||"horizontal"===o.layout){const e=o.options||{};new i({gapX:e.gapX||120,gapY:e.gapY||50}).layoutContainer(s)}return{ice:s,designer:l}}export{r as DSL_SCHEMA_VERSION,compileDsl,renderDsl,validateDsl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("ice-entity-designer")):"function"==typeof define&&define.amd?define(["exports","ice-entity-designer"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ICEDSL={},e.IED)}(this,(function(e,t){"use strict";function validateDsl(e){const t=[];if(!e||"object"!=typeof e||Array.isArray(e))return{valid:!1,errors:["DSL root must be an object"]};if(void 0!==e.schemaVersion&&1!==e.schemaVersion&&t.push("Unsupported schemaVersion: "+e.schemaVersion),Array.isArray(e.entities)){const i=new Set;e.entities.forEach((e,r)=>{const n=`entities[${r}]`;e&&"object"==typeof e?("string"==typeof e.id&&e.id.trim()?i.has(e.id)?t.push(`${n}.id is duplicated: ${e.id}`):i.add(e.id):t.push(n+".id must be a non-empty string"),Array.isArray(e.fields)?e.fields.forEach((e,i)=>{e&&"object"==typeof e&&"string"==typeof e.name&&e.name.trim()||t.push(`${n}.fields[${i}].name must be a non-empty string`)}):t.push(n+".fields must be an array")):t.push(n+" must be an object")})}else t.push("entities must be an array");return void 0===e.relations||Array.isArray(e.relations)?(e.relations||[]).forEach((e,i)=>{const r=`relations[${i}]`;e&&"object"==typeof e?("string"==typeof e.source&&e.source.trim()||t.push(r+".source must be a non-empty string"),"string"==typeof e.target&&e.target.trim()||t.push(r+".target must be a non-empty string")):t.push(r+" must be an object")}):t.push("relations must be an array"),{valid:0===t.length,errors:t}}function entityName(e){return e.entityName||e.name||e.id}function relationType(e){return e.relationType||e.type||"one-to-many"}function compileDsl(e){return{entities:e.entities.map(e=>({id:e.id,entityName:entityName(e),fields:e.fields||[],left:e.left,top:e.top,width:e.width,height:e.height,style:e.style,headerStyle:e.headerStyle,fieldStyle:e.fieldStyle,dividerStyle:e.dividerStyle,draggable:e.draggable,interactive:e.interactive})),relations:(e.relations||[]).map((e,t)=>({id:e.id||"relation-"+t,relationType:relationType(e),sourceId:e.source,targetId:e.target,sourceField:e.sourceField||"id",targetField:e.targetField||"id",nullable:e.nullable,onDelete:e.onDelete,onUpdate:e.onUpdate,joinTableName:e.joinTableName,fromKey:e.fromKey,toKey:e.toKey,sourceCardinality:e.sourceCardinality,targetCardinality:e.targetCardinality,label:e.label,style:e.style,arrow:e.arrow,linkShape:e.linkShape,routeType:e.routeType,routeOffset:e.routeOffset,curveType:e.curveType,lineDash:e.lineDash})),layout:e.layout,options:e.options}}e.DSL_SCHEMA_VERSION=1,e.compileDsl=compileDsl,e.renderDsl=function renderDsl(e,i){const r=validateDsl(i);if(!r.valid)throw new Error(r.errors.join("\n"));const n=compileDsl(i),o=(new t.ICE).init(e),a=new t.EntityDesigner(o);if(n.entities.forEach(e=>{a.createEntity(e)}),function positionLinks(e,t){const i=new Map;return e.entities.forEach(e=>{i.set(e.state.id,e.getMinBoundingBox(!0))}),t.relations.map(e=>{const t=i.get(e.sourceId),r=i.get(e.targetId);let n="R",o="L";if(t&&r){const e=r.center[0]-t.center[0],i=r.center[1]-t.center[1];Math.abs(e)>Math.abs(i)?(n=e>=0?"R":"L",o=e>=0?"L":"R"):(n=i>=0?"B":"T",o=i>=0?"T":"B")}return{...e,routeType:e.routeType||"orthogonal",linkShape:e.linkShape||"visio",links:{start:{id:e.sourceId,position:n},end:{id:e.targetId,position:o}}}})}(a,n).forEach(e=>{a.createRelation(e)}),"layered"===n.layout||"horizontal"===n.layout){const e=n.options||{};new t.ICELayeredLayout({gapX:e.gapX||120,gapY:e.gapY||50}).layoutContainer(o)}return{ice:o,designer:a}},e.validateDsl=validateDsl,Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DslDocument } from '../types';
|
|
2
|
+
export declare type CompiledScene = {
|
|
3
|
+
entities: Array<Record<string, any>>;
|
|
4
|
+
relations: Array<Record<string, any>>;
|
|
5
|
+
layout?: string;
|
|
6
|
+
options?: Record<string, any>;
|
|
7
|
+
};
|
|
8
|
+
export declare function compileDsl(dsl: DslDocument): CompiledScene;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export { DSL_SCHEMA_VERSION, validateDsl } from './validate';
|
|
3
|
+
export { compileDsl } from './compiler/dslToScene';
|
|
4
|
+
export type { CompiledScene } from './compiler/dslToScene';
|
|
5
|
+
export { renderDsl } from './runtime/renderDsl';
|
|
6
|
+
export type { RenderDslResult } from './runtime/renderDsl';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export declare type DslRelationType = 'one-to-many' | 'many-to-one' | 'one-to-one' | 'many-to-many';
|
|
2
|
+
export declare type DslField = {
|
|
3
|
+
name: string;
|
|
4
|
+
type?: string;
|
|
5
|
+
length?: number | string;
|
|
6
|
+
primary?: boolean;
|
|
7
|
+
foreignKey?: boolean;
|
|
8
|
+
generated?: boolean;
|
|
9
|
+
autoIncrement?: boolean;
|
|
10
|
+
nullable?: boolean;
|
|
11
|
+
unique?: boolean;
|
|
12
|
+
default?: any;
|
|
13
|
+
comment?: string;
|
|
14
|
+
index?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare type DslEntity = {
|
|
17
|
+
id: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
entityName?: string;
|
|
20
|
+
fields?: DslField[];
|
|
21
|
+
left?: number;
|
|
22
|
+
top?: number;
|
|
23
|
+
width?: number;
|
|
24
|
+
height?: number;
|
|
25
|
+
style?: Record<string, any>;
|
|
26
|
+
headerStyle?: Record<string, any>;
|
|
27
|
+
fieldStyle?: Record<string, any>;
|
|
28
|
+
dividerStyle?: Record<string, any>;
|
|
29
|
+
draggable?: boolean;
|
|
30
|
+
interactive?: boolean;
|
|
31
|
+
};
|
|
32
|
+
export declare type DslRelation = {
|
|
33
|
+
id?: string;
|
|
34
|
+
source: string;
|
|
35
|
+
target: string;
|
|
36
|
+
type?: DslRelationType;
|
|
37
|
+
relationType?: DslRelationType;
|
|
38
|
+
sourceField?: string;
|
|
39
|
+
targetField?: string;
|
|
40
|
+
nullable?: boolean;
|
|
41
|
+
onDelete?: string;
|
|
42
|
+
onUpdate?: string;
|
|
43
|
+
joinTableName?: string;
|
|
44
|
+
fromKey?: string;
|
|
45
|
+
toKey?: string;
|
|
46
|
+
sourceCardinality?: string;
|
|
47
|
+
targetCardinality?: string;
|
|
48
|
+
label?: string;
|
|
49
|
+
style?: Record<string, any>;
|
|
50
|
+
arrow?: string;
|
|
51
|
+
linkShape?: string;
|
|
52
|
+
routeType?: string;
|
|
53
|
+
routeOffset?: number;
|
|
54
|
+
curveType?: string;
|
|
55
|
+
lineDash?: number[];
|
|
56
|
+
};
|
|
57
|
+
export declare type DslLayout = 'grid' | 'layered' | 'horizontal' | 'vertical';
|
|
58
|
+
export declare type DslDocument = {
|
|
59
|
+
schemaVersion?: number;
|
|
60
|
+
entities: DslEntity[];
|
|
61
|
+
relations?: DslRelation[];
|
|
62
|
+
layout?: DslLayout;
|
|
63
|
+
options?: {
|
|
64
|
+
fitViewport?: boolean;
|
|
65
|
+
routeType?: string;
|
|
66
|
+
gapX?: number;
|
|
67
|
+
gapY?: number;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export declare type DslValidationResult = {
|
|
71
|
+
valid: boolean;
|
|
72
|
+
errors: string[];
|
|
73
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ice-entity-designer-dsl",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A JSON-first DSL layer for AI agents to drive ice-entity-designer.",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"browser": "dist/index.umd.js",
|
|
8
|
+
"types": "dist/types/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "npm run clean && npm run build:types && npm run build:js",
|
|
14
|
+
"build:js": "rollup -c --environment NODE_ENV:production",
|
|
15
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"dev": "rollup -c -w --environment NODE_ENV:development",
|
|
18
|
+
"test": "jest --runInBand",
|
|
19
|
+
"types:check": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ice-entity-designer": "file:../ice-entity-designer"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@babel/core": "7.17.8",
|
|
26
|
+
"@babel/preset-env": "7.16.11",
|
|
27
|
+
"@babel/preset-typescript": "7.9.0",
|
|
28
|
+
"@rollup/plugin-commonjs": "21.0.2",
|
|
29
|
+
"@rollup/plugin-json": "4.1.0",
|
|
30
|
+
"@rollup/plugin-node-resolve": "13.1.3",
|
|
31
|
+
"@types/node": "^14.18.63",
|
|
32
|
+
"jest": "25.4.0",
|
|
33
|
+
"rimraf": "3.0.2",
|
|
34
|
+
"rollup": "2.70.1",
|
|
35
|
+
"rollup-plugin-babel": "4.4.0",
|
|
36
|
+
"rollup-plugin-terser": "5.3.0",
|
|
37
|
+
"typescript": "4.6.2"
|
|
38
|
+
}
|
|
39
|
+
}
|