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 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,6 @@
1
+ import type { DslDocument } from '../types';
2
+ export declare type RenderDslResult = {
3
+ ice: any;
4
+ designer: any;
5
+ };
6
+ export declare function renderDsl(canvasOrId: any, dsl: DslDocument): RenderDslResult;
@@ -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
+ };
@@ -0,0 +1,3 @@
1
+ import type { DslDocument, DslValidationResult } from './types';
2
+ export declare const DSL_SCHEMA_VERSION = 1;
3
+ export declare function validateDsl(dsl: DslDocument): DslValidationResult;
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
+ }