callman-core 1.29.0 → 1.30.0
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/dist/ai/prompts/diagram.d.ts.map +1 -1
- package/dist/ai/prompts/diagram.js +24 -7
- package/dist/ai/prompts/diagram.js.map +1 -1
- package/dist/ai/types.d.ts +1 -1
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/diagram/componentCatalog.d.ts +3 -0
- package/dist/diagram/componentCatalog.d.ts.map +1 -1
- package/dist/diagram/componentCatalog.js +5 -1
- package/dist/diagram/componentCatalog.js.map +1 -1
- package/dist/diagram/database/derive.d.ts +14 -0
- package/dist/diagram/database/derive.d.ts.map +1 -0
- package/dist/diagram/database/derive.js +114 -0
- package/dist/diagram/database/derive.js.map +1 -0
- package/dist/diagram/database/diff.d.ts +48 -0
- package/dist/diagram/database/diff.d.ts.map +1 -0
- package/dist/diagram/database/diff.js +109 -0
- package/dist/diagram/database/diff.js.map +1 -0
- package/dist/diagram/database/edit.d.ts +113 -0
- package/dist/diagram/database/edit.d.ts.map +1 -0
- package/dist/diagram/database/edit.js +359 -0
- package/dist/diagram/database/edit.js.map +1 -0
- package/dist/diagram/database/fromSchema.d.ts +12 -0
- package/dist/diagram/database/fromSchema.d.ts.map +1 -0
- package/dist/diagram/database/fromSchema.js +182 -0
- package/dist/diagram/database/fromSchema.js.map +1 -0
- package/dist/diagram/database/index.d.ts +9 -0
- package/dist/diagram/database/index.d.ts.map +1 -0
- package/dist/diagram/database/index.js +14 -0
- package/dist/diagram/database/index.js.map +1 -0
- package/dist/diagram/database/model.d.ts +93 -0
- package/dist/diagram/database/model.d.ts.map +1 -0
- package/dist/diagram/database/model.js +294 -0
- package/dist/diagram/database/model.js.map +1 -0
- package/dist/diagram/database/patch.d.ts +14 -0
- package/dist/diagram/database/patch.d.ts.map +1 -0
- package/dist/diagram/database/patch.js +186 -0
- package/dist/diagram/database/patch.js.map +1 -0
- package/dist/diagram/database/place.d.ts +4 -0
- package/dist/diagram/database/place.d.ts.map +1 -0
- package/dist/diagram/database/place.js +22 -0
- package/dist/diagram/database/place.js.map +1 -0
- package/dist/diagram/database/types.d.ts +50 -0
- package/dist/diagram/database/types.d.ts.map +1 -0
- package/dist/diagram/database/types.js +106 -0
- package/dist/diagram/database/types.js.map +1 -0
- package/dist/diagram/index.d.ts +1 -0
- package/dist/diagram/index.d.ts.map +1 -1
- package/dist/diagram/index.js +1 -0
- package/dist/diagram/index.js.map +1 -1
- package/dist/diagram/layout.d.ts +1 -0
- package/dist/diagram/layout.d.ts.map +1 -1
- package/dist/diagram/layout.js +2 -1
- package/dist/diagram/layout.js.map +1 -1
- package/dist/diagram/ops.d.ts +1 -1
- package/dist/diagram/ops.d.ts.map +1 -1
- package/dist/diagram/ops.js +42 -0
- package/dist/diagram/ops.js.map +1 -1
- package/dist/diagram/opsReference.d.ts +1 -1
- package/dist/diagram/opsReference.d.ts.map +1 -1
- package/dist/diagram/opsReference.js +9 -0
- package/dist/diagram/opsReference.js.map +1 -1
- package/dist/diagram/patchInput.d.ts +65 -1
- package/dist/diagram/patchInput.d.ts.map +1 -1
- package/dist/diagram/patchInput.js +414 -11
- package/dist/diagram/patchInput.js.map +1 -1
- package/dist/diagram/schema.d.ts +2 -2
- package/dist/diagram/schema.d.ts.map +1 -1
- package/dist/diagram/schema.js +3 -3
- package/dist/diagram/schema.js.map +1 -1
- package/dist/diagram/views.d.ts +13 -0
- package/dist/diagram/views.d.ts.map +1 -1
- package/dist/diagram/views.js +49 -0
- package/dist/diagram/views.js.map +1 -1
- package/dist/host/db/introspection/index.d.ts +9 -0
- package/dist/host/db/introspection/index.d.ts.map +1 -0
- package/dist/host/db/introspection/index.js +35 -0
- package/dist/host/db/introspection/index.js.map +1 -0
- package/dist/host/db/introspection/mongo.d.ts +7 -0
- package/dist/host/db/introspection/mongo.d.ts.map +1 -0
- package/dist/host/db/introspection/mongo.js +183 -0
- package/dist/host/db/introspection/mongo.js.map +1 -0
- package/dist/host/db/introspection/mssql.d.ts +6 -0
- package/dist/host/db/introspection/mssql.d.ts.map +1 -0
- package/dist/host/db/introspection/mssql.js +211 -0
- package/dist/host/db/introspection/mssql.js.map +1 -0
- package/dist/host/db/introspection/mysql.d.ts +5 -0
- package/dist/host/db/introspection/mysql.d.ts.map +1 -0
- package/dist/host/db/introspection/mysql.js +163 -0
- package/dist/host/db/introspection/mysql.js.map +1 -0
- package/dist/host/db/introspection/oracle.d.ts +12 -0
- package/dist/host/db/introspection/oracle.d.ts.map +1 -0
- package/dist/host/db/introspection/oracle.js +225 -0
- package/dist/host/db/introspection/oracle.js.map +1 -0
- package/dist/host/db/introspection/postgres.d.ts +5 -0
- package/dist/host/db/introspection/postgres.d.ts.map +1 -0
- package/dist/host/db/introspection/postgres.js +172 -0
- package/dist/host/db/introspection/postgres.js.map +1 -0
- package/dist/host/db/introspection/shared.d.ts +29 -0
- package/dist/host/db/introspection/shared.d.ts.map +1 -0
- package/dist/host/db/introspection/shared.js +119 -0
- package/dist/host/db/introspection/shared.js.map +1 -0
- package/dist/host/db/introspection/types.d.ts +58 -0
- package/dist/host/db/introspection/types.d.ts.map +1 -0
- package/dist/host/db/introspection/types.js +6 -0
- package/dist/host/db/introspection/types.js.map +1 -0
- package/dist/host/db/mongoAdapter.d.ts +1 -0
- package/dist/host/db/mongoAdapter.d.ts.map +1 -1
- package/dist/host/db/mongoAdapter.js +13 -0
- package/dist/host/db/mongoAdapter.js.map +1 -1
- package/dist/host/db/mssqlAdapter.d.ts +1 -0
- package/dist/host/db/mssqlAdapter.d.ts.map +1 -1
- package/dist/host/db/mssqlAdapter.js +3 -1
- package/dist/host/db/mssqlAdapter.js.map +1 -1
- package/dist/host/db/mysqlAdapter.d.ts +1 -0
- package/dist/host/db/mysqlAdapter.d.ts.map +1 -1
- package/dist/host/db/mysqlAdapter.js +11 -0
- package/dist/host/db/mysqlAdapter.js.map +1 -1
- package/dist/host/db/oracleAdapter.d.ts +1 -0
- package/dist/host/db/oracleAdapter.d.ts.map +1 -1
- package/dist/host/db/oracleAdapter.js +22 -0
- package/dist/host/db/oracleAdapter.js.map +1 -1
- package/dist/host/db/postgresAdapter.d.ts +1 -0
- package/dist/host/db/postgresAdapter.d.ts.map +1 -1
- package/dist/host/db/postgresAdapter.js +13 -0
- package/dist/host/db/postgresAdapter.js.map +1 -1
- package/dist/host/db/queryExecutor.d.ts +2 -1
- package/dist/host/db/queryExecutor.d.ts.map +1 -1
- package/dist/host/db/queryExecutor.js +1 -1
- package/dist/host/db/queryExecutor.js.map +1 -1
- package/dist/host/index.d.ts +7 -0
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +9 -0
- package/dist/host/index.js.map +1 -1
- package/dist-cjs/ai/prompts/diagram.js +23 -6
- package/dist-cjs/ai/prompts/diagram.js.map +1 -1
- package/dist-cjs/diagram/componentCatalog.js +5 -1
- package/dist-cjs/diagram/componentCatalog.js.map +1 -1
- package/dist-cjs/diagram/database/derive.js +119 -0
- package/dist-cjs/diagram/database/derive.js.map +1 -0
- package/dist-cjs/diagram/database/diff.js +115 -0
- package/dist-cjs/diagram/database/diff.js.map +1 -0
- package/dist-cjs/diagram/database/edit.js +366 -0
- package/dist-cjs/diagram/database/edit.js.map +1 -0
- package/dist-cjs/diagram/database/fromSchema.js +188 -0
- package/dist-cjs/diagram/database/fromSchema.js.map +1 -0
- package/dist-cjs/diagram/database/index.js +32 -0
- package/dist-cjs/diagram/database/index.js.map +1 -0
- package/dist-cjs/diagram/database/model.js +311 -0
- package/dist-cjs/diagram/database/model.js.map +1 -0
- package/dist-cjs/diagram/database/patch.js +190 -0
- package/dist-cjs/diagram/database/patch.js.map +1 -0
- package/dist-cjs/diagram/database/place.js +26 -0
- package/dist-cjs/diagram/database/place.js.map +1 -0
- package/dist-cjs/diagram/database/types.js +122 -0
- package/dist-cjs/diagram/database/types.js.map +1 -0
- package/dist-cjs/diagram/index.js +1 -0
- package/dist-cjs/diagram/index.js.map +1 -1
- package/dist-cjs/diagram/layout.js +3 -2
- package/dist-cjs/diagram/layout.js.map +1 -1
- package/dist-cjs/diagram/ops.js +42 -0
- package/dist-cjs/diagram/ops.js.map +1 -1
- package/dist-cjs/diagram/opsReference.js +9 -0
- package/dist-cjs/diagram/opsReference.js.map +1 -1
- package/dist-cjs/diagram/patchInput.js +414 -11
- package/dist-cjs/diagram/patchInput.js.map +1 -1
- package/dist-cjs/diagram/schema.js +3 -3
- package/dist-cjs/diagram/schema.js.map +1 -1
- package/dist-cjs/diagram/views.js +51 -1
- package/dist-cjs/diagram/views.js.map +1 -1
- package/dist-cjs/host/db/introspection/index.js +59 -0
- package/dist-cjs/host/db/introspection/index.js.map +1 -0
- package/dist-cjs/host/db/introspection/mongo.js +189 -0
- package/dist-cjs/host/db/introspection/mongo.js.map +1 -0
- package/dist-cjs/host/db/introspection/mssql.js +216 -0
- package/dist-cjs/host/db/introspection/mssql.js.map +1 -0
- package/dist-cjs/host/db/introspection/mysql.js +167 -0
- package/dist-cjs/host/db/introspection/mysql.js.map +1 -0
- package/dist-cjs/host/db/introspection/oracle.js +230 -0
- package/dist-cjs/host/db/introspection/oracle.js.map +1 -0
- package/dist-cjs/host/db/introspection/postgres.js +176 -0
- package/dist-cjs/host/db/introspection/postgres.js.map +1 -0
- package/dist-cjs/host/db/introspection/shared.js +133 -0
- package/dist-cjs/host/db/introspection/shared.js.map +1 -0
- package/dist-cjs/host/db/introspection/types.js +7 -0
- package/dist-cjs/host/db/introspection/types.js.map +1 -0
- package/dist-cjs/host/db/mongoAdapter.js +15 -1
- package/dist-cjs/host/db/mongoAdapter.js.map +1 -1
- package/dist-cjs/host/db/mssqlAdapter.js +5 -2
- package/dist-cjs/host/db/mssqlAdapter.js.map +1 -1
- package/dist-cjs/host/db/mysqlAdapter.js +13 -1
- package/dist-cjs/host/db/mysqlAdapter.js.map +1 -1
- package/dist-cjs/host/db/oracleAdapter.js +24 -1
- package/dist-cjs/host/db/oracleAdapter.js.map +1 -1
- package/dist-cjs/host/db/postgresAdapter.js +15 -1
- package/dist-cjs/host/db/postgresAdapter.js.map +1 -1
- package/dist-cjs/host/db/queryExecutor.js +5 -4
- package/dist-cjs/host/db/queryExecutor.js.map +1 -1
- package/dist-cjs/host/index.js +24 -1
- package/dist-cjs/host/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// by the MCP server (behind its zod schemas), the AI EDIT mode and imports.
|
|
6
6
|
// Moved from the MCP server's build.ts / patch.ts.
|
|
7
7
|
import { BPMN_CONTAINER_TYPES, bpmnElementForType, getBpmnType, isBpmnContainerType, suggestBpmnTypes } from "./bpmn/types.js";
|
|
8
|
+
import { applyDatabaseEdits, DATABASE_GRAMMAR_VERSION, DatabaseEditError, databaseEntitySize, DB_NODE_TYPES, emptyDatabaseModel, findEntity, isValidCardinality, kindForDatabaseType, markersForCardinality, parseDatabaseModel, serializeDatabaseModel, suggestDatabaseTypes, } from "./database/index.js";
|
|
8
9
|
import { DIAGRAM_COMPONENT_CATALOG, getDiagramComponent, searchDiagramComponents } from "./componentCatalog.js";
|
|
9
10
|
import { isBusComponentType, isDefaultEdgeStyle, semanticStyle } from "./edgeSemantics.js";
|
|
10
11
|
import { absolutePosition, absoluteRect, boundingBox, indexNodes, toParentRelative } from "./geometry.js";
|
|
@@ -52,6 +53,8 @@ export const isContainerType = (type) => {
|
|
|
52
53
|
};
|
|
53
54
|
export const containerTypes = () => DIAGRAM_COMPONENT_CATALOG.filter((entry) => entry.container || entry.swimlane).map((entry) => entry.type);
|
|
54
55
|
export const parentWarning = (id, parentId, parentType, kind = "canvas") => {
|
|
56
|
+
if (kind === "database")
|
|
57
|
+
return `Node ${id} sits inside "${parentId}" — database diagrams have no containers; the parent is ignored.`;
|
|
55
58
|
if (kind === "bpmn") {
|
|
56
59
|
return isBpmnContainerType(parentType)
|
|
57
60
|
? null
|
|
@@ -77,6 +80,16 @@ export const edgeStyleFor = (semantic, color) => {
|
|
|
77
80
|
};
|
|
78
81
|
const resolveType = (type, id, strict, warnings, opIndex, kind = "canvas") => {
|
|
79
82
|
const trimmed = type.trim();
|
|
83
|
+
if (kind === "database") {
|
|
84
|
+
if (DB_NODE_TYPES.includes(trimmed))
|
|
85
|
+
return { type: trimmed, known: true };
|
|
86
|
+
const suggestions = suggestDatabaseTypes(trimmed);
|
|
87
|
+
if (strict) {
|
|
88
|
+
throw new DiagramOpError("DIAGRAM_UNKNOWN_COMPONENT_TYPE", `Unknown database type "${trimmed}" on ${id}. A database diagram has db.table, db.view and db.collection (prefer the DATABASE OPS: addTable, addColumn, addRelationship …), or pass strictTypes:false to draw it as a table.`, opIndex, { unknownTypes: [{ type: trimmed, suggestions }] });
|
|
89
|
+
}
|
|
90
|
+
warnings.push(`Unknown database type "${trimmed}" on ${id} → drawn as a table`);
|
|
91
|
+
return { type: "db.table", known: false };
|
|
92
|
+
}
|
|
80
93
|
if (kind === "bpmn") {
|
|
81
94
|
if (getBpmnType(trimmed))
|
|
82
95
|
return { type: trimmed, known: true };
|
|
@@ -100,18 +113,38 @@ const resolveType = (type, id, strict, warnings, opIndex, kind = "canvas") => {
|
|
|
100
113
|
};
|
|
101
114
|
const nodeFromInput = (input, id, strict, warnings, opIndex, kind = "canvas") => {
|
|
102
115
|
const { type, known } = resolveType(input.type, id, strict, warnings, opIndex, kind);
|
|
103
|
-
const def = kind === "
|
|
116
|
+
const def = kind === "canvas" ? getDiagramComponent(type) : undefined;
|
|
104
117
|
const bpmn = kind === "bpmn" ? getBpmnType(type) : undefined;
|
|
105
|
-
const
|
|
106
|
-
|
|
118
|
+
const dbRaw = kind === "database" ? (input.data?.db ?? {}) : undefined;
|
|
119
|
+
const label = kind === "database"
|
|
120
|
+
? (typeof dbRaw?.name === "string" && dbRaw.name.trim()) || input.label || id
|
|
121
|
+
: input.label !== undefined || !known
|
|
122
|
+
? (input.label ?? input.type)
|
|
123
|
+
: def?.defaultLabel;
|
|
124
|
+
// A BPMN node always knows which element it stands for; a database node its
|
|
125
|
+
// table name and kind (its `columns` stay the authoring input until the
|
|
126
|
+
// model absorbs them).
|
|
107
127
|
const data = kind === "bpmn"
|
|
108
128
|
? { ...(input.data ?? {}), bpmn: { ...(input.data?.bpmn ?? {}), element: bpmnElementForType(type) ?? "task" } }
|
|
109
|
-
:
|
|
129
|
+
: kind === "database"
|
|
130
|
+
? { ...(input.data ?? {}), db: { ...dbRaw, name: label, kind: kindForDatabaseType(type) } }
|
|
131
|
+
: input.data;
|
|
132
|
+
const dbColumns = Array.isArray(dbRaw?.columns) ? dbRaw.columns : [];
|
|
133
|
+
const dbSize = kind === "database"
|
|
134
|
+
? databaseEntitySize({
|
|
135
|
+
name: String(label),
|
|
136
|
+
...(typeof dbRaw?.schema === "string" ? { schema: dbRaw.schema } : {}),
|
|
137
|
+
columns: dbColumns.map((column) => {
|
|
138
|
+
const raw = (column ?? {});
|
|
139
|
+
return { name: typeof raw.name === "string" ? raw.name : "", dataType: typeof raw.dataType === "string" ? raw.dataType : typeof raw.type === "string" ? raw.type : "" };
|
|
140
|
+
}),
|
|
141
|
+
})
|
|
142
|
+
: undefined;
|
|
110
143
|
const node = {
|
|
111
144
|
id,
|
|
112
145
|
type,
|
|
113
146
|
position: input.position ?? { x: 0, y: 0 },
|
|
114
|
-
size: input.size ?? bpmn?.size ?? def?.defaultSize ?? { width: 140, height: 60 },
|
|
147
|
+
size: input.size ?? bpmn?.size ?? dbSize ?? def?.defaultSize ?? { width: 140, height: 60 },
|
|
115
148
|
...(label !== undefined ? { label } : {}),
|
|
116
149
|
...(input.parent ? { parentId: input.parent } : {}),
|
|
117
150
|
...(input.style ? { style: applyStyleInput(undefined, input.style) } : {}),
|
|
@@ -130,19 +163,26 @@ const edgeFromInput = (input, id, typeOf, kind = "canvas") => {
|
|
|
130
163
|
// A fresh edge into a broker reads as a bus line — the studio's own rule.
|
|
131
164
|
if (kind === "canvas" && !semantic && (isBusComponentType(typeOf(input.source)) || isBusComponentType(typeOf(input.target))))
|
|
132
165
|
semantic = "bus";
|
|
133
|
-
// A BPMN flow is a sequence flow unless said otherwise
|
|
166
|
+
// A BPMN flow is a sequence flow unless said otherwise; a database edge is
|
|
167
|
+
// a relationship (manual, N:1 unless said otherwise) with crow's-foot ends.
|
|
168
|
+
const dbRaw = kind === "database" ? (input.data?.db ?? {}) : undefined;
|
|
169
|
+
const cardinality = isValidCardinality(dbRaw?.cardinality) ? dbRaw.cardinality : "N:1";
|
|
134
170
|
const data = kind === "bpmn"
|
|
135
171
|
? { ...(input.data ?? {}), bpmn: { flow: "sequence", ...(input.data?.bpmn ?? {}) } }
|
|
136
|
-
:
|
|
172
|
+
: kind === "database"
|
|
173
|
+
? { ...(input.data ?? {}), db: { kind: "manual", sourceColumns: [], targetColumns: [], ...dbRaw, cardinality } }
|
|
174
|
+
: input.data;
|
|
175
|
+
const dbMarkers = kind === "database" ? markersForCardinality(cardinality) : undefined;
|
|
137
176
|
const style = edgeStyleFor(semantic, input.color);
|
|
177
|
+
const markerStart = input.markerStart ?? dbMarkers?.markerStart;
|
|
138
178
|
return {
|
|
139
179
|
id,
|
|
140
180
|
source: input.source,
|
|
141
181
|
target: input.target,
|
|
142
182
|
...(input.label ? { label: input.label } : {}),
|
|
143
183
|
...(input.routing ? { routing: input.routing } : {}),
|
|
144
|
-
markerEnd: input.markerEnd ?? "classicThin",
|
|
145
|
-
...(
|
|
184
|
+
markerEnd: input.markerEnd ?? dbMarkers?.markerEnd ?? "classicThin",
|
|
185
|
+
...(markerStart && markerStart !== "none" ? { markerStart } : {}),
|
|
146
186
|
...(input.sourceSide ? { sourceHandle: input.sourceSide } : {}),
|
|
147
187
|
...(input.targetSide ? { targetHandle: input.targetSide } : {}),
|
|
148
188
|
...(style && (!isDefaultEdgeStyle(style) || semantic === "sync" || input.color) ? { style } : {}),
|
|
@@ -152,7 +192,9 @@ const edgeFromInput = (input, id, typeOf, kind = "canvas") => {
|
|
|
152
192
|
// --- lowering -----------------------------------------------------------------------
|
|
153
193
|
export const lowerPatchOps = (doc, input, options = {}) => {
|
|
154
194
|
const strict = options.strictTypes !== false;
|
|
155
|
-
const kind = options.kind ?? (doc.source?.format === "bpmn" ? "bpmn" : "canvas");
|
|
195
|
+
const kind = options.kind ?? (doc.source?.format === "bpmn" ? "bpmn" : doc.source?.format === "database" ? "database" : "canvas");
|
|
196
|
+
if (kind === "database")
|
|
197
|
+
return lowerDatabasePatchOps(doc, input, { ...options, strict });
|
|
156
198
|
const warnings = [];
|
|
157
199
|
const unpositionedNodeIds = [];
|
|
158
200
|
const idMap = {};
|
|
@@ -442,12 +484,373 @@ export const lowerPatchOps = (doc, input, options = {}) => {
|
|
|
442
484
|
case "relayout":
|
|
443
485
|
relayout = { scope: op.scope, ...(op.direction ? { direction: op.direction } : {}) };
|
|
444
486
|
return;
|
|
487
|
+
case "addTable":
|
|
488
|
+
case "renameTable":
|
|
489
|
+
case "removeTable":
|
|
490
|
+
case "setTableComment":
|
|
491
|
+
case "addColumn":
|
|
492
|
+
case "alterColumn":
|
|
493
|
+
case "removeColumn":
|
|
494
|
+
case "setPrimaryKey":
|
|
495
|
+
case "addRelationship":
|
|
496
|
+
case "updateRelationship":
|
|
497
|
+
case "removeRelationship":
|
|
498
|
+
case "hideTable":
|
|
499
|
+
case "showTable":
|
|
500
|
+
throw new DiagramOpError("DIAGRAM_INVALID_OP", `"${op.op}" is a database-diagram op; this diagram is a ${kind} diagram.`, index);
|
|
445
501
|
default:
|
|
446
502
|
throw new DiagramOpError("DIAGRAM_INVALID_OP", `Unknown op "${op.op}"`, index);
|
|
447
503
|
}
|
|
448
504
|
});
|
|
449
505
|
return { ops, warnings, unpositionedNodeIds, ...(relayout ? { relayout } : {}), idMap };
|
|
450
506
|
};
|
|
507
|
+
// --- database diagrams -------------------------------------------------------------
|
|
508
|
+
//
|
|
509
|
+
// Every structural op (semantic or generic) becomes an edit of the database
|
|
510
|
+
// model; the batch then yields ONE setSource op (the model re-derives the
|
|
511
|
+
// nodes/edges) followed by the presentation ops (positions, visibility,
|
|
512
|
+
// routing) that address nodes by their entity ids.
|
|
513
|
+
const DATABASE_ERROR_CODES = {
|
|
514
|
+
DIAGRAM_TABLE_NOT_FOUND: "DIAGRAM_TABLE_NOT_FOUND",
|
|
515
|
+
DIAGRAM_COLUMN_NOT_FOUND: "DIAGRAM_COLUMN_NOT_FOUND",
|
|
516
|
+
DIAGRAM_RELATIONSHIP_NOT_FOUND: "DIAGRAM_RELATIONSHIP_NOT_FOUND",
|
|
517
|
+
DIAGRAM_DUPLICATE_TABLE: "DIAGRAM_DUPLICATE_TABLE",
|
|
518
|
+
DIAGRAM_DUPLICATE_COLUMN: "DIAGRAM_DUPLICATE_COLUMN",
|
|
519
|
+
DIAGRAM_INVALID_OP: "DIAGRAM_INVALID_OP",
|
|
520
|
+
};
|
|
521
|
+
const columnInputsOf = (value) => {
|
|
522
|
+
if (!Array.isArray(value))
|
|
523
|
+
return undefined;
|
|
524
|
+
return value
|
|
525
|
+
.filter((item) => Boolean(item) && typeof item === "object" && typeof item.name === "string")
|
|
526
|
+
.map((item) => ({
|
|
527
|
+
...(typeof item.id === "string" ? { id: item.id } : {}),
|
|
528
|
+
name: String(item.name),
|
|
529
|
+
dataType: typeof item.dataType === "string" ? item.dataType : typeof item.type === "string" ? item.type : "",
|
|
530
|
+
...(typeof item.nullable === "boolean" ? { nullable: item.nullable } : {}),
|
|
531
|
+
...(typeof item.primaryKey === "boolean" ? { primaryKey: item.primaryKey } : {}),
|
|
532
|
+
...(typeof item.unique === "boolean" ? { unique: item.unique } : {}),
|
|
533
|
+
...(item.defaultValue !== undefined && item.defaultValue !== null ? { defaultValue: String(item.defaultValue) } : {}),
|
|
534
|
+
...(typeof item.autoIncrement === "boolean" ? { autoIncrement: item.autoIncrement } : {}),
|
|
535
|
+
...(typeof item.comment === "string" ? { comment: item.comment } : {}),
|
|
536
|
+
}));
|
|
537
|
+
};
|
|
538
|
+
const lowerDatabasePatchOps = (doc, input, options) => {
|
|
539
|
+
const warnings = [];
|
|
540
|
+
const idMap = {};
|
|
541
|
+
const presentation = [];
|
|
542
|
+
const positioned = new Map();
|
|
543
|
+
const unpositionedNodeIds = [];
|
|
544
|
+
let relayout;
|
|
545
|
+
const parsed = doc.source?.format === "database" ? parseDatabaseModel(doc.source.text) : null;
|
|
546
|
+
if (parsed && !parsed.ok)
|
|
547
|
+
throw new DiagramOpError("DIAGRAM_DOCUMENT_INVALID", `The database model cannot be read: ${parsed.errors.join("; ")}`);
|
|
548
|
+
let model = parsed?.model ?? emptyDatabaseModel();
|
|
549
|
+
let changed = false;
|
|
550
|
+
const mapId = (id) => idMap[id] ?? id;
|
|
551
|
+
const run = (edits, index) => {
|
|
552
|
+
try {
|
|
553
|
+
const result = applyDatabaseEdits(model, edits);
|
|
554
|
+
model = result.model;
|
|
555
|
+
changed = changed || result.changed;
|
|
556
|
+
warnings.push(...result.warnings);
|
|
557
|
+
Object.assign(idMap, result.idMap);
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
if (error instanceof DatabaseEditError)
|
|
562
|
+
throw new DiagramOpError(DATABASE_ERROR_CODES[error.code], error.message, index);
|
|
563
|
+
throw error;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
const entityIdOf = (ref, index) => {
|
|
567
|
+
const entity = findEntity(model, mapId(ref));
|
|
568
|
+
if (!entity)
|
|
569
|
+
throw new DiagramOpError("DIAGRAM_TABLE_NOT_FOUND", `Table "${ref}" does not exist.`, index);
|
|
570
|
+
return entity.id;
|
|
571
|
+
};
|
|
572
|
+
const relationshipIdOf = (ref) => mapId(ref);
|
|
573
|
+
const noContainers = (index) => {
|
|
574
|
+
throw new DiagramOpError("DIAGRAM_INVALID_OP", "Database diagrams have no containers (group / ungroup / reparent do not apply).", index);
|
|
575
|
+
};
|
|
576
|
+
const endInput = (value) => (typeof value === "string" ? value : { ...value, table: mapId(value.table) });
|
|
577
|
+
input.forEach((op, index) => {
|
|
578
|
+
switch (op.op) {
|
|
579
|
+
case "setTitle":
|
|
580
|
+
presentation.push({ op: "setTitle", title: op.title });
|
|
581
|
+
return;
|
|
582
|
+
case "addTable": {
|
|
583
|
+
const result = run([{ kind: "addTable", table: op.table }], index);
|
|
584
|
+
const id = result.addedEntityIds[0];
|
|
585
|
+
if (op.position)
|
|
586
|
+
positioned.set(id, op.position);
|
|
587
|
+
else
|
|
588
|
+
unpositionedNodeIds.push(id);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
case "renameTable":
|
|
592
|
+
run([{ kind: "renameTable", table: mapId(op.table), name: op.name, ...(op.schema !== undefined ? { schema: op.schema } : {}) }], index);
|
|
593
|
+
return;
|
|
594
|
+
case "removeTable":
|
|
595
|
+
run([{ kind: "removeTable", table: mapId(op.table) }], index);
|
|
596
|
+
return;
|
|
597
|
+
case "setTableComment":
|
|
598
|
+
run([{ kind: "setTableComment", table: mapId(op.table), comment: op.comment }], index);
|
|
599
|
+
return;
|
|
600
|
+
case "addColumn":
|
|
601
|
+
run([{ kind: "addColumn", table: mapId(op.table), column: op.column, ...(op.after !== undefined ? { after: op.after } : {}) }], index);
|
|
602
|
+
return;
|
|
603
|
+
case "alterColumn":
|
|
604
|
+
run([{ kind: "alterColumn", table: mapId(op.table), column: op.column, changes: op.changes }], index);
|
|
605
|
+
return;
|
|
606
|
+
case "removeColumn":
|
|
607
|
+
run([{ kind: "removeColumn", table: mapId(op.table), column: op.column }], index);
|
|
608
|
+
return;
|
|
609
|
+
case "setPrimaryKey":
|
|
610
|
+
run([{ kind: "setPrimaryKey", table: mapId(op.table), columns: op.columns }], index);
|
|
611
|
+
return;
|
|
612
|
+
case "addRelationship":
|
|
613
|
+
run([
|
|
614
|
+
{
|
|
615
|
+
kind: "addRelationship",
|
|
616
|
+
...(op.id ? { id: op.id } : {}),
|
|
617
|
+
source: endInput(op.source),
|
|
618
|
+
target: endInput(op.target),
|
|
619
|
+
...(op.cardinality ? { cardinality: op.cardinality } : {}),
|
|
620
|
+
...(op.kind ? { relationshipKind: op.kind } : {}),
|
|
621
|
+
...(op.name ? { name: op.name } : {}),
|
|
622
|
+
...(op.label ? { label: op.label } : {}),
|
|
623
|
+
},
|
|
624
|
+
], index);
|
|
625
|
+
return;
|
|
626
|
+
case "updateRelationship": {
|
|
627
|
+
const { kind: relationshipKind, source, target, ...rest } = op.changes;
|
|
628
|
+
run([
|
|
629
|
+
{
|
|
630
|
+
kind: "updateRelationship",
|
|
631
|
+
relationship: relationshipIdOf(op.relationship),
|
|
632
|
+
changes: { ...rest, ...(source !== undefined ? { source: endInput(source) } : {}), ...(target !== undefined ? { target: endInput(target) } : {}), ...(relationshipKind ? { relationshipKind } : {}) },
|
|
633
|
+
},
|
|
634
|
+
], index);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
case "removeRelationship":
|
|
638
|
+
run([{ kind: "removeRelationship", relationship: relationshipIdOf(op.relationship) }], index);
|
|
639
|
+
return;
|
|
640
|
+
case "hideTable":
|
|
641
|
+
presentation.push({ op: "updateNode", id: entityIdOf(op.table, index), changes: { hidden: true } });
|
|
642
|
+
return;
|
|
643
|
+
case "showTable":
|
|
644
|
+
presentation.push({ op: "updateNode", id: entityIdOf(op.table, index), changes: { hidden: false } });
|
|
645
|
+
return;
|
|
646
|
+
case "addNode": {
|
|
647
|
+
const { type } = resolveType(op.node.type, op.node.id ?? op.node.label ?? "node", options.strict, warnings, index, "database");
|
|
648
|
+
if (op.node.parent)
|
|
649
|
+
warnings.push(parentWarning(op.node.id ?? "node", op.node.parent, undefined, "database") ?? "");
|
|
650
|
+
const raw = op.node.data?.db ?? {};
|
|
651
|
+
const name = (typeof raw.name === "string" && raw.name.trim()) || (op.node.label ?? "").trim() || op.node.id?.trim() || "table";
|
|
652
|
+
const result = run([
|
|
653
|
+
{
|
|
654
|
+
kind: "addTable",
|
|
655
|
+
table: {
|
|
656
|
+
...(op.node.id?.trim() ? { id: op.node.id.trim() } : {}),
|
|
657
|
+
name,
|
|
658
|
+
...(typeof raw.schema === "string" && raw.schema.trim() ? { schema: raw.schema.trim() } : {}),
|
|
659
|
+
kind: kindForDatabaseType(type),
|
|
660
|
+
...(columnInputsOf(raw.columns ?? raw.columnInputs) ? { columns: columnInputsOf(raw.columns ?? raw.columnInputs) } : {}),
|
|
661
|
+
...(typeof raw.comment === "string" && raw.comment.trim() ? { comment: raw.comment.trim() } : {}),
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
], index);
|
|
665
|
+
const id = result.addedEntityIds[0];
|
|
666
|
+
if (op.node.position)
|
|
667
|
+
positioned.set(id, op.node.position);
|
|
668
|
+
else
|
|
669
|
+
unpositionedNodeIds.push(id);
|
|
670
|
+
if (op.node.hidden)
|
|
671
|
+
presentation.push({ op: "updateNode", id, changes: { hidden: true } });
|
|
672
|
+
if (op.node.collapsed)
|
|
673
|
+
presentation.push({ op: "updateNode", id, changes: { collapsed: true } });
|
|
674
|
+
if (op.node.locked)
|
|
675
|
+
presentation.push({ op: "updateNode", id, changes: { locked: true } });
|
|
676
|
+
if (op.node.style)
|
|
677
|
+
presentation.push({ op: "updateNode", id, changes: { style: styleInputToChanges(undefined, op.node.style) } });
|
|
678
|
+
if (op.node.size)
|
|
679
|
+
presentation.push({ op: "resizeNode", id, size: op.node.size });
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
case "updateNode": {
|
|
683
|
+
const id = entityIdOf(op.id, index);
|
|
684
|
+
const changes = op.changes;
|
|
685
|
+
if (changes.parent)
|
|
686
|
+
noContainers(index);
|
|
687
|
+
const edits = [];
|
|
688
|
+
if (changes.type !== undefined) {
|
|
689
|
+
const { type } = resolveType(changes.type, id, options.strict, warnings, index, "database");
|
|
690
|
+
edits.push({ kind: "setTableKind", table: id, entityKind: kindForDatabaseType(type) });
|
|
691
|
+
}
|
|
692
|
+
const raw = changes.data && typeof changes.data.db === "object" && changes.data.db ? changes.data.db : undefined;
|
|
693
|
+
const requestedName = typeof raw?.name === "string" ? raw.name : typeof changes.label === "string" ? changes.label : undefined;
|
|
694
|
+
if (requestedName !== undefined) {
|
|
695
|
+
const dot = requestedName.lastIndexOf(".");
|
|
696
|
+
const schema = typeof raw?.schema === "string" ? raw.schema : dot > 0 ? requestedName.slice(0, dot) : undefined;
|
|
697
|
+
edits.push({ kind: "renameTable", table: id, name: dot > 0 && typeof raw?.name !== "string" ? requestedName.slice(dot + 1) : requestedName, ...(schema !== undefined ? { schema } : {}) });
|
|
698
|
+
}
|
|
699
|
+
else if (typeof raw?.schema === "string") {
|
|
700
|
+
const entity = findEntity(model, id);
|
|
701
|
+
if (entity)
|
|
702
|
+
edits.push({ kind: "renameTable", table: id, name: entity.name, schema: raw.schema || null });
|
|
703
|
+
}
|
|
704
|
+
if (raw && "comment" in raw)
|
|
705
|
+
edits.push({ kind: "setTableComment", table: id, comment: typeof raw.comment === "string" ? raw.comment : null });
|
|
706
|
+
const columns = raw ? columnInputsOf(raw.columns ?? raw.columnInputs) : undefined;
|
|
707
|
+
if (columns)
|
|
708
|
+
edits.push({ kind: "setColumns", table: id, columns });
|
|
709
|
+
if (edits.length > 0)
|
|
710
|
+
run(edits, index);
|
|
711
|
+
const nodeChanges = {};
|
|
712
|
+
if (changes.rotation !== undefined)
|
|
713
|
+
nodeChanges.rotation = changes.rotation;
|
|
714
|
+
if (changes.locked !== undefined)
|
|
715
|
+
nodeChanges.locked = changes.locked;
|
|
716
|
+
if (changes.hidden !== undefined)
|
|
717
|
+
nodeChanges.hidden = changes.hidden;
|
|
718
|
+
if (changes.collapsed !== undefined)
|
|
719
|
+
nodeChanges.collapsed = changes.collapsed;
|
|
720
|
+
if (changes.style !== undefined)
|
|
721
|
+
nodeChanges.style = changes.style === null ? null : styleInputToChanges(doc.nodes.find((node) => node.id === id)?.style, changes.style);
|
|
722
|
+
if (Object.keys(nodeChanges).length > 0)
|
|
723
|
+
presentation.push({ op: "updateNode", id, changes: nodeChanges });
|
|
724
|
+
if (changes.size !== undefined)
|
|
725
|
+
presentation.push({ op: "resizeNode", id, size: changes.size });
|
|
726
|
+
if (changes.position !== undefined)
|
|
727
|
+
presentation.push({ op: "moveNodes", moves: [{ id, position: changes.position }] });
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
case "removeNode":
|
|
731
|
+
run([{ kind: "removeTable", table: entityIdOf(op.id, index) }], index);
|
|
732
|
+
return;
|
|
733
|
+
case "addEdge": {
|
|
734
|
+
const raw = op.edge.data?.db ?? {};
|
|
735
|
+
const refs = (value) => (Array.isArray(value) ? value.filter((item) => typeof item === "string") : []);
|
|
736
|
+
const result = run([
|
|
737
|
+
{
|
|
738
|
+
kind: "addRelationship",
|
|
739
|
+
...(op.edge.id?.trim() ? { id: op.edge.id.trim() } : {}),
|
|
740
|
+
source: { table: entityIdOf(op.edge.source, index), columns: refs(raw.sourceColumns) },
|
|
741
|
+
target: { table: entityIdOf(op.edge.target, index), columns: refs(raw.targetColumns) },
|
|
742
|
+
...(isValidCardinality(raw.cardinality) ? { cardinality: raw.cardinality } : {}),
|
|
743
|
+
relationshipKind: raw.kind === "foreignKey" ? "foreignKey" : "manual",
|
|
744
|
+
...(typeof raw.name === "string" ? { name: raw.name } : {}),
|
|
745
|
+
...(op.edge.label ? { label: op.edge.label } : {}),
|
|
746
|
+
},
|
|
747
|
+
], index);
|
|
748
|
+
const id = result.addedRelationshipIds[0];
|
|
749
|
+
if (id && (op.edge.routing || op.edge.sourceSide || op.edge.targetSide || op.edge.color)) {
|
|
750
|
+
const edgeChanges = {};
|
|
751
|
+
if (op.edge.routing)
|
|
752
|
+
edgeChanges.routing = op.edge.routing;
|
|
753
|
+
if (op.edge.sourceSide)
|
|
754
|
+
edgeChanges.sourceHandle = op.edge.sourceSide;
|
|
755
|
+
if (op.edge.targetSide)
|
|
756
|
+
edgeChanges.targetHandle = op.edge.targetSide;
|
|
757
|
+
if (op.edge.color)
|
|
758
|
+
edgeChanges.style = { stroke: toTokenRef(op.edge.color) };
|
|
759
|
+
presentation.push({ op: "updateEdge", id, changes: edgeChanges });
|
|
760
|
+
}
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
case "updateEdge": {
|
|
764
|
+
const id = relationshipIdOf(op.id);
|
|
765
|
+
const changes = op.changes;
|
|
766
|
+
const raw = changes.data && typeof changes.data.db === "object" && changes.data.db ? changes.data.db : undefined;
|
|
767
|
+
const refs = (value) => (Array.isArray(value) ? value.filter((item) => typeof item === "string") : undefined);
|
|
768
|
+
const relationshipChanges = {};
|
|
769
|
+
if (changes.source !== undefined || refs(raw?.sourceColumns))
|
|
770
|
+
relationshipChanges.source = { table: changes.source !== undefined ? entityIdOf(changes.source, index) : (findEntity(model, id) ? model.relationships.find((r) => r.id === id)?.source.entityId ?? "" : ""), columns: refs(raw?.sourceColumns) ?? (changes.source !== undefined ? [] : undefined) };
|
|
771
|
+
if (changes.target !== undefined || refs(raw?.targetColumns))
|
|
772
|
+
relationshipChanges.target = { table: changes.target !== undefined ? entityIdOf(changes.target, index) : model.relationships.find((r) => r.id === id)?.target.entityId ?? "", columns: refs(raw?.targetColumns) ?? (changes.target !== undefined ? [] : undefined) };
|
|
773
|
+
if (isValidCardinality(raw?.cardinality))
|
|
774
|
+
relationshipChanges.cardinality = raw.cardinality;
|
|
775
|
+
if (raw && (raw.kind === "manual" || raw.kind === "foreignKey"))
|
|
776
|
+
relationshipChanges.relationshipKind = raw.kind;
|
|
777
|
+
if (raw && "name" in raw)
|
|
778
|
+
relationshipChanges.name = typeof raw.name === "string" ? raw.name : null;
|
|
779
|
+
if (changes.label !== undefined)
|
|
780
|
+
relationshipChanges.label = changes.label === "" ? null : changes.label;
|
|
781
|
+
if (Object.keys(relationshipChanges).length > 0)
|
|
782
|
+
run([{ kind: "updateRelationship", relationship: id, changes: relationshipChanges }], index);
|
|
783
|
+
const edgeChanges = {};
|
|
784
|
+
if (changes.routing !== undefined)
|
|
785
|
+
edgeChanges.routing = changes.routing;
|
|
786
|
+
if (changes.sourceSide !== undefined)
|
|
787
|
+
edgeChanges.sourceHandle = changes.sourceSide;
|
|
788
|
+
if (changes.targetSide !== undefined)
|
|
789
|
+
edgeChanges.targetHandle = changes.targetSide;
|
|
790
|
+
if (changes.color !== undefined)
|
|
791
|
+
edgeChanges.style = { stroke: changes.color === null ? null : toTokenRef(changes.color) };
|
|
792
|
+
if (Object.keys(edgeChanges).length > 0)
|
|
793
|
+
presentation.push({ op: "updateEdge", id, changes: edgeChanges });
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
case "removeEdge":
|
|
797
|
+
run([{ kind: "removeRelationship", relationship: relationshipIdOf(op.id) }], index);
|
|
798
|
+
return;
|
|
799
|
+
case "moveNode":
|
|
800
|
+
presentation.push({ op: "moveNodes", moves: [{ id: entityIdOf(op.id, index), position: op.position }] });
|
|
801
|
+
return;
|
|
802
|
+
case "setEdgeRoute": {
|
|
803
|
+
const changes = {};
|
|
804
|
+
if (op.routing !== undefined)
|
|
805
|
+
changes.routing = op.routing;
|
|
806
|
+
if (op.sourceSide !== undefined)
|
|
807
|
+
changes.sourceHandle = op.sourceSide;
|
|
808
|
+
if (op.targetSide !== undefined)
|
|
809
|
+
changes.targetHandle = op.targetSide;
|
|
810
|
+
if (op.waypoints !== undefined)
|
|
811
|
+
changes.waypoints = op.waypoints;
|
|
812
|
+
if (Object.keys(changes).length === 0) {
|
|
813
|
+
changes.waypoints = null;
|
|
814
|
+
changes.sourceAnchor = null;
|
|
815
|
+
changes.targetAnchor = null;
|
|
816
|
+
}
|
|
817
|
+
presentation.push({ op: "updateEdge", id: relationshipIdOf(op.id), changes });
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
case "reorder": {
|
|
821
|
+
const id = entityIdOf(op.id, index);
|
|
822
|
+
const count = doc.nodes.filter((node) => !node.parentId).length;
|
|
823
|
+
presentation.push({ op: "reorder", kind: "node", id, index: op.index === "front" ? Math.max(0, count - 1) : op.index === "back" ? 0 : op.index });
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
case "setCanvas":
|
|
827
|
+
presentation.push({ op: "setCanvas", changes: op.canvas });
|
|
828
|
+
return;
|
|
829
|
+
case "relayout":
|
|
830
|
+
relayout = { scope: op.scope, ...(op.direction ? { direction: op.direction } : {}) };
|
|
831
|
+
return;
|
|
832
|
+
case "reparent":
|
|
833
|
+
case "group":
|
|
834
|
+
case "ungroup":
|
|
835
|
+
noContainers(index);
|
|
836
|
+
return;
|
|
837
|
+
default:
|
|
838
|
+
throw new DiagramOpError("DIAGRAM_INVALID_OP", `Unknown op "${op.op}"`, index);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
const ops = [];
|
|
842
|
+
if (changed) {
|
|
843
|
+
ops.push({
|
|
844
|
+
op: "setSource",
|
|
845
|
+
source: { format: "database", text: serializeDatabaseModel(model), diagramType: "database", grammar: DATABASE_GRAMMAR_VERSION, valid: true },
|
|
846
|
+
resync: true,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
for (const [id, position] of positioned)
|
|
850
|
+
ops.push({ op: "moveNodes", moves: [{ id, position }] });
|
|
851
|
+
ops.push(...presentation);
|
|
852
|
+
return { ops, warnings: warnings.filter(Boolean), unpositionedNodeIds, ...(relayout ? { relayout } : {}), idMap };
|
|
853
|
+
};
|
|
451
854
|
export const buildDiagramFromInput = (input, options = {}) => {
|
|
452
855
|
const kind = options.kind ?? "canvas";
|
|
453
856
|
const strict = options.strictTypes !== false;
|
|
@@ -476,7 +879,7 @@ export const buildDiagramFromInput = (input, options = {}) => {
|
|
|
476
879
|
const entry = error.data?.unknownTypes?.[0];
|
|
477
880
|
if (entry)
|
|
478
881
|
unknown.push(entry);
|
|
479
|
-
return nodeFromInput({ ...node, type: kind === "bpmn" ? "bpmn.task" : "rect", label: node.label ?? node.type }, id, false, [], index, kind);
|
|
882
|
+
return nodeFromInput({ ...node, type: kind === "bpmn" ? "bpmn.task" : kind === "database" ? "db.table" : "rect", label: node.label ?? node.type }, id, false, [], index, kind);
|
|
480
883
|
}
|
|
481
884
|
throw error;
|
|
482
885
|
}
|