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