@uigraph/sdk 1.1.34
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 +7 -0
- package/dist/__animated-nodes.CUxruu-b.cjs +1 -0
- package/dist/__animated-nodes.C_COfaOh.mjs +94 -0
- package/dist/__animated-nodes.CrIWsAOl.cjs +99 -0
- package/dist/__animated-nodes.DI0XYTuw.mjs +2 -0
- package/dist/__aws-icons.BEeU8uCn.cjs +1 -0
- package/dist/__aws-icons.Vs3L4Tqu.cjs +5920 -0
- package/dist/__aws-icons.agWBtDk7.mjs +2 -0
- package/dist/__aws-icons.d3E71nM8.mjs +5915 -0
- package/dist/__azure-icons.BpVFW8Gy.cjs +5552 -0
- package/dist/__azure-icons.CHjqkspj.mjs +5547 -0
- package/dist/__azure-icons.DJJasII-.cjs +1 -0
- package/dist/__azure-icons.DgR1F6C6.mjs +2 -0
- package/dist/__component-type.69UkUMKl.cjs +54 -0
- package/dist/__component-type.CO8FgnQE.d.mts +24 -0
- package/dist/__component-type.CYmD-KIu.mjs +23 -0
- package/dist/__component-type.tW2Xh7Xk.d.cts +24 -0
- package/dist/__headless.AhKww75N.cjs +110 -0
- package/dist/__headless.B7jwmaZK.d.cts +127 -0
- package/dist/__headless.CP3up7Jj.mjs +104 -0
- package/dist/__headless.DiJZCsKx.d.mts +127 -0
- package/dist/__index.BC7AYPW7.d.cts +116 -0
- package/dist/__index.Banqjcwy.d.mts +116 -0
- package/dist/animated-nodes.cjs +3 -0
- package/dist/animated-nodes.d.cts +16 -0
- package/dist/animated-nodes.d.mts +16 -0
- package/dist/animated-nodes.mjs +3 -0
- package/dist/aws-icons.cjs +3 -0
- package/dist/aws-icons.d.cts +11 -0
- package/dist/aws-icons.d.mts +11 -0
- package/dist/aws-icons.mjs +3 -0
- package/dist/azure-icons.cjs +3 -0
- package/dist/azure-icons.d.cts +11 -0
- package/dist/azure-icons.d.mts +11 -0
- package/dist/azure-icons.mjs +3 -0
- package/dist/browser.cjs +81 -0
- package/dist/browser.d.cts +10 -0
- package/dist/browser.d.mts +10 -0
- package/dist/browser.mjs +79 -0
- package/dist/headless.cjs +3 -0
- package/dist/headless.d.cts +3 -0
- package/dist/headless.d.mts +3 -0
- package/dist/headless.mjs +3 -0
- package/dist/index.cjs +4629 -0
- package/dist/index.d.cts +767 -0
- package/dist/index.d.mts +767 -0
- package/dist/index.mjs +4589 -0
- package/package.json +109 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,4589 @@
|
|
|
1
|
+
import { t as ComponentInputType } from "./__component-type.CYmD-KIu.mjs";
|
|
2
|
+
import { t as contextSchema } from "./__headless.CP3up7Jj.mjs";
|
|
3
|
+
import { arrayNonNullable, generateUUID as generateUUID$1, objectPick } from "daily-code";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { MarkerType, Position } from "@xyflow/react";
|
|
6
|
+
import dagre from "dagre";
|
|
7
|
+
import mermaid from "mermaid";
|
|
8
|
+
import markdownToDelta from "markdown-to-quill-delta";
|
|
9
|
+
function mapDynamoTypeToAstType(dynamoType) {
|
|
10
|
+
switch (dynamoType) {
|
|
11
|
+
case "S": return "string";
|
|
12
|
+
case "N": return "number";
|
|
13
|
+
case "B": return "binary";
|
|
14
|
+
default: return "string";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function convertDynamoSchemaToAst(schema) {
|
|
18
|
+
var _schema$name;
|
|
19
|
+
const pkName = schema.primaryKey.partitionKey;
|
|
20
|
+
const pkType = schema.primaryKey.partitionKeyType;
|
|
21
|
+
const skName = schema.primaryKey.sortKey;
|
|
22
|
+
const skType = schema.primaryKey.sortKeyType;
|
|
23
|
+
const mergedAttributes = [];
|
|
24
|
+
const attributeByName = /* @__PURE__ */ new Map();
|
|
25
|
+
(schema.attributes || []).forEach((attribute) => {
|
|
26
|
+
const key = attribute.name.trim();
|
|
27
|
+
if (!key || attributeByName.has(key)) return;
|
|
28
|
+
attributeByName.set(key, attribute);
|
|
29
|
+
mergedAttributes.push(attribute);
|
|
30
|
+
});
|
|
31
|
+
const tableName = ((_schema$name = schema.name) === null || _schema$name === void 0 ? void 0 : _schema$name.trim()) || "dynamodb_table";
|
|
32
|
+
const baseColumns = [{
|
|
33
|
+
type: "column",
|
|
34
|
+
name: pkName,
|
|
35
|
+
dataType: { name: mapDynamoTypeToAstType(pkType || "string") },
|
|
36
|
+
nullable: false,
|
|
37
|
+
comment: void 0
|
|
38
|
+
}];
|
|
39
|
+
if (skName) baseColumns.push({
|
|
40
|
+
type: "column",
|
|
41
|
+
name: skName,
|
|
42
|
+
dataType: { name: mapDynamoTypeToAstType(skType || "string") },
|
|
43
|
+
nullable: false,
|
|
44
|
+
comment: void 0
|
|
45
|
+
});
|
|
46
|
+
const attributeColumns = mergedAttributes.map((attribute) => ({
|
|
47
|
+
type: "column",
|
|
48
|
+
name: attribute.name,
|
|
49
|
+
dataType: { name: attribute.type },
|
|
50
|
+
nullable: !attribute.required,
|
|
51
|
+
comment: attribute.notes
|
|
52
|
+
}));
|
|
53
|
+
const indexes = schema.globalSecondaryIndexes.map((gsi) => ({
|
|
54
|
+
type: "index",
|
|
55
|
+
name: gsi.name || "gsi",
|
|
56
|
+
columns: [{ name: gsi.partitionKey || pkName }, ...gsi.sortKey ? [{ name: gsi.sortKey }] : []],
|
|
57
|
+
unique: false
|
|
58
|
+
}));
|
|
59
|
+
return {
|
|
60
|
+
dialect: "dynamodb",
|
|
61
|
+
tables: [{
|
|
62
|
+
type: "table",
|
|
63
|
+
id: generateUUID$1(),
|
|
64
|
+
name: tableName,
|
|
65
|
+
columns: [...baseColumns, ...attributeColumns],
|
|
66
|
+
constraints: [{
|
|
67
|
+
type: "primary_key",
|
|
68
|
+
columns: skName ? [pkName, skName] : [pkName]
|
|
69
|
+
}],
|
|
70
|
+
indexes,
|
|
71
|
+
options: { comment: schema.description }
|
|
72
|
+
}],
|
|
73
|
+
metadata: { createdAt: /* @__PURE__ */ new Date() }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function convertMongoSchemaToAst(schema) {
|
|
77
|
+
return {
|
|
78
|
+
dialect: "mongodb",
|
|
79
|
+
tables: schema.collections.map((collection) => {
|
|
80
|
+
function buildColumnsFromField(field, prefix = "") {
|
|
81
|
+
const columns$1 = [];
|
|
82
|
+
const columnName = prefix ? `${prefix}.${field.name}` : field.name;
|
|
83
|
+
if (field.type === "array") {
|
|
84
|
+
const itemType = field.itemType || "string";
|
|
85
|
+
if (itemType === "object" && field.itemFields) field.itemFields.forEach((itemField) => {
|
|
86
|
+
columns$1.push(...buildColumnsFromField(itemField, `${columnName}[]`));
|
|
87
|
+
});
|
|
88
|
+
else columns$1.push({
|
|
89
|
+
type: "column",
|
|
90
|
+
name: columnName,
|
|
91
|
+
dataType: { name: `${itemType}[]` },
|
|
92
|
+
nullable: !field.required
|
|
93
|
+
});
|
|
94
|
+
} else if (field.type === "object" && field.fields) field.fields.forEach((nestedField) => {
|
|
95
|
+
columns$1.push(...buildColumnsFromField(nestedField, columnName));
|
|
96
|
+
});
|
|
97
|
+
else columns$1.push({
|
|
98
|
+
type: "column",
|
|
99
|
+
name: columnName,
|
|
100
|
+
dataType: { name: field.type },
|
|
101
|
+
nullable: !field.required
|
|
102
|
+
});
|
|
103
|
+
return columns$1;
|
|
104
|
+
}
|
|
105
|
+
const columns = collection.fields.flatMap((field) => buildColumnsFromField(field));
|
|
106
|
+
const indexes = collection.indexes.map((index) => ({
|
|
107
|
+
type: "index",
|
|
108
|
+
name: index.name || `idx_${collection.name}`,
|
|
109
|
+
columns: index.fields.map((field) => ({ name: field.fieldName })),
|
|
110
|
+
unique: index.unique
|
|
111
|
+
}));
|
|
112
|
+
return {
|
|
113
|
+
type: "table",
|
|
114
|
+
id: collection.id,
|
|
115
|
+
name: collection.name,
|
|
116
|
+
columns,
|
|
117
|
+
constraints: [],
|
|
118
|
+
indexes,
|
|
119
|
+
options: { comment: collection.description }
|
|
120
|
+
};
|
|
121
|
+
}),
|
|
122
|
+
metadata: { createdAt: /* @__PURE__ */ new Date() }
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function convertJsonSchemaToAst(schema) {
|
|
126
|
+
function buildColumnFromField(field, prefix = "") {
|
|
127
|
+
const columns = [];
|
|
128
|
+
const columnName = prefix ? `${prefix}.${field.name}` : field.name;
|
|
129
|
+
if (field.type === "array") {
|
|
130
|
+
const itemType = field.itemType || "string";
|
|
131
|
+
if (itemType === "object" && field.itemFields) field.itemFields.forEach((itemField) => {
|
|
132
|
+
columns.push(...buildColumnFromField(itemField, `${columnName}[]`));
|
|
133
|
+
});
|
|
134
|
+
else columns.push({
|
|
135
|
+
type: "column",
|
|
136
|
+
name: columnName,
|
|
137
|
+
dataType: { name: `${itemType}[]` },
|
|
138
|
+
nullable: !field.required,
|
|
139
|
+
comment: "notes" in field ? field.notes : void 0
|
|
140
|
+
});
|
|
141
|
+
} else if (field.type === "object" && field.fields) field.fields.forEach((nestedField) => {
|
|
142
|
+
columns.push(...buildColumnFromField(nestedField, columnName));
|
|
143
|
+
});
|
|
144
|
+
else columns.push({
|
|
145
|
+
type: "column",
|
|
146
|
+
name: columnName,
|
|
147
|
+
dataType: { name: field.type },
|
|
148
|
+
nullable: !field.required,
|
|
149
|
+
comment: "notes" in field ? field.notes : void 0
|
|
150
|
+
});
|
|
151
|
+
return columns;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
dialect: "json",
|
|
155
|
+
tables: schema.collections.map((collection) => {
|
|
156
|
+
const columns = collection.fields.flatMap((field) => buildColumnFromField(field));
|
|
157
|
+
const constraints = collection.fields.filter((field) => {
|
|
158
|
+
var _field$reference;
|
|
159
|
+
return (_field$reference = field.reference) === null || _field$reference === void 0 ? void 0 : _field$reference.collectionId;
|
|
160
|
+
}).map((field) => {
|
|
161
|
+
const target = schema.collections.find((c) => {
|
|
162
|
+
var _field$reference2;
|
|
163
|
+
return c.id === ((_field$reference2 = field.reference) === null || _field$reference2 === void 0 ? void 0 : _field$reference2.collectionId);
|
|
164
|
+
});
|
|
165
|
+
const targetField = target === null || target === void 0 ? void 0 : target.fields.find((f) => {
|
|
166
|
+
var _field$reference3;
|
|
167
|
+
return f.id === ((_field$reference3 = field.reference) === null || _field$reference3 === void 0 ? void 0 : _field$reference3.fieldId);
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
type: "foreign_key",
|
|
171
|
+
columns: [field.name],
|
|
172
|
+
referencedTable: (target === null || target === void 0 ? void 0 : target.name) || "",
|
|
173
|
+
referencedColumns: targetField ? [targetField.name] : ["id"]
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
const indexes = collection.indexes.map((index) => ({
|
|
177
|
+
type: "index",
|
|
178
|
+
name: index.name || `idx_${collection.name}`,
|
|
179
|
+
columns: index.fields.map((field) => ({ name: field })),
|
|
180
|
+
unique: index.unique,
|
|
181
|
+
where: index.ttl ? `ttl:${index.ttl}` : void 0
|
|
182
|
+
}));
|
|
183
|
+
return {
|
|
184
|
+
type: "table",
|
|
185
|
+
id: collection.id,
|
|
186
|
+
name: collection.name,
|
|
187
|
+
columns,
|
|
188
|
+
constraints,
|
|
189
|
+
indexes,
|
|
190
|
+
options: { comment: collection.description }
|
|
191
|
+
};
|
|
192
|
+
}),
|
|
193
|
+
metadata: { createdAt: /* @__PURE__ */ new Date() }
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const DialectIdSchema = z.union([
|
|
197
|
+
z.literal("dynamodb"),
|
|
198
|
+
z.literal("mongodb"),
|
|
199
|
+
z.literal("json")
|
|
200
|
+
]);
|
|
201
|
+
const EditorSupportedDialectSchema = DialectIdSchema.or(z.literal("nosql"));
|
|
202
|
+
const BasicDetailsSchema = z.object({
|
|
203
|
+
name: z.string(),
|
|
204
|
+
description: z.string().optional()
|
|
205
|
+
});
|
|
206
|
+
const NestedFieldSchema = z.lazy(() => z.object({
|
|
207
|
+
id: z.string(),
|
|
208
|
+
name: z.string(),
|
|
209
|
+
type: z.string(),
|
|
210
|
+
required: z.boolean(),
|
|
211
|
+
fields: z.array(NestedFieldSchema).optional(),
|
|
212
|
+
itemType: z.string().optional(),
|
|
213
|
+
itemFields: z.array(NestedFieldSchema).optional()
|
|
214
|
+
}));
|
|
215
|
+
const NoSqlFieldSchema = z.lazy(() => z.object({
|
|
216
|
+
id: z.string(),
|
|
217
|
+
name: z.string(),
|
|
218
|
+
type: z.string(),
|
|
219
|
+
required: z.boolean(),
|
|
220
|
+
isArray: z.boolean(),
|
|
221
|
+
reference: z.object({
|
|
222
|
+
collectionId: z.string(),
|
|
223
|
+
fieldId: z.string().optional()
|
|
224
|
+
}).optional(),
|
|
225
|
+
notes: z.string().optional(),
|
|
226
|
+
fields: z.array(NestedFieldSchema).optional(),
|
|
227
|
+
itemType: z.string().optional(),
|
|
228
|
+
itemFields: z.array(NestedFieldSchema).optional()
|
|
229
|
+
}));
|
|
230
|
+
const DocumentIndexSchema = z.object({
|
|
231
|
+
id: z.string(),
|
|
232
|
+
name: z.string(),
|
|
233
|
+
fields: z.array(z.string()),
|
|
234
|
+
unique: z.boolean(),
|
|
235
|
+
ttl: z.string().optional()
|
|
236
|
+
});
|
|
237
|
+
const DocumentCollectionSchema = z.object({
|
|
238
|
+
id: z.string(),
|
|
239
|
+
name: z.string(),
|
|
240
|
+
label: z.string().optional(),
|
|
241
|
+
description: z.string().optional(),
|
|
242
|
+
tags: z.string().optional(),
|
|
243
|
+
fields: z.array(NoSqlFieldSchema),
|
|
244
|
+
indexes: z.array(DocumentIndexSchema),
|
|
245
|
+
sample: z.string().optional()
|
|
246
|
+
});
|
|
247
|
+
const MongoNestedFieldSchema = z.lazy(() => z.object({
|
|
248
|
+
id: z.string(),
|
|
249
|
+
name: z.string(),
|
|
250
|
+
type: z.string(),
|
|
251
|
+
required: z.boolean(),
|
|
252
|
+
fields: z.array(MongoNestedFieldSchema).optional(),
|
|
253
|
+
itemType: z.string().optional(),
|
|
254
|
+
itemFields: z.array(MongoNestedFieldSchema).optional(),
|
|
255
|
+
refCollectionId: z.string().nullable().optional()
|
|
256
|
+
}));
|
|
257
|
+
const MongoIndexFieldSchema = z.object({
|
|
258
|
+
id: z.string(),
|
|
259
|
+
fieldName: z.string(),
|
|
260
|
+
order: z.union([z.literal(1), z.literal(-1)])
|
|
261
|
+
});
|
|
262
|
+
const MongoIndexSchema = z.object({
|
|
263
|
+
id: z.string(),
|
|
264
|
+
name: z.string(),
|
|
265
|
+
fields: z.array(MongoIndexFieldSchema),
|
|
266
|
+
unique: z.boolean()
|
|
267
|
+
});
|
|
268
|
+
const MongoCollectionSchema = z.object({
|
|
269
|
+
id: z.string(),
|
|
270
|
+
name: z.string(),
|
|
271
|
+
tags: z.string(),
|
|
272
|
+
description: z.string(),
|
|
273
|
+
fields: z.array(MongoNestedFieldSchema),
|
|
274
|
+
indexes: z.array(MongoIndexSchema)
|
|
275
|
+
});
|
|
276
|
+
const DynamoGsiSchema = z.object({
|
|
277
|
+
id: z.string(),
|
|
278
|
+
name: z.string(),
|
|
279
|
+
partitionKey: z.string(),
|
|
280
|
+
partitionKeyType: z.string().optional(),
|
|
281
|
+
sortKey: z.string().optional(),
|
|
282
|
+
sortKeyType: z.string().optional()
|
|
283
|
+
});
|
|
284
|
+
const DynamoAttributeSchema = z.lazy(() => z.object({
|
|
285
|
+
id: z.string(),
|
|
286
|
+
name: z.string(),
|
|
287
|
+
type: z.string(),
|
|
288
|
+
required: z.boolean(),
|
|
289
|
+
notes: z.string().optional(),
|
|
290
|
+
fields: z.array(DynamoAttributeSchema).optional(),
|
|
291
|
+
itemType: z.string().optional(),
|
|
292
|
+
itemFields: z.array(DynamoAttributeSchema).optional()
|
|
293
|
+
}));
|
|
294
|
+
const DynamoEditorSchema = BasicDetailsSchema.extend({
|
|
295
|
+
dialect: z.literal("dynamodb"),
|
|
296
|
+
primaryKey: z.object({
|
|
297
|
+
partitionKey: z.string(),
|
|
298
|
+
partitionKeyType: z.string().optional(),
|
|
299
|
+
sortKey: z.string().optional(),
|
|
300
|
+
sortKeyType: z.string().optional()
|
|
301
|
+
}),
|
|
302
|
+
globalSecondaryIndexes: z.array(DynamoGsiSchema),
|
|
303
|
+
attributes: z.array(DynamoAttributeSchema)
|
|
304
|
+
});
|
|
305
|
+
const MongoEditorSchema = BasicDetailsSchema.extend({
|
|
306
|
+
dialect: z.literal("mongodb"),
|
|
307
|
+
collections: z.array(MongoCollectionSchema)
|
|
308
|
+
});
|
|
309
|
+
const JsonEditorSchema = BasicDetailsSchema.extend({
|
|
310
|
+
dialect: z.literal("json"),
|
|
311
|
+
collections: z.array(DocumentCollectionSchema)
|
|
312
|
+
});
|
|
313
|
+
function convertNoSQLToAst(input) {
|
|
314
|
+
const dynamoDbSchema = DynamoEditorSchema.safeParse(input);
|
|
315
|
+
if (dynamoDbSchema.success) return convertDynamoSchemaToAst(dynamoDbSchema.data);
|
|
316
|
+
const mongoDbSchema = MongoEditorSchema.safeParse(input);
|
|
317
|
+
if (mongoDbSchema.success) return convertMongoSchemaToAst(mongoDbSchema.data);
|
|
318
|
+
const jsonSchema = JsonEditorSchema.safeParse(input);
|
|
319
|
+
if (jsonSchema.success) return convertJsonSchemaToAst(jsonSchema.data);
|
|
320
|
+
throw new Error("Input does not match any supported NoSQL schema format");
|
|
321
|
+
}
|
|
322
|
+
function _typeof(o) {
|
|
323
|
+
"@babel/helpers - typeof";
|
|
324
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
325
|
+
return typeof o$1;
|
|
326
|
+
} : function(o$1) {
|
|
327
|
+
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
328
|
+
}, _typeof(o);
|
|
329
|
+
}
|
|
330
|
+
function toPrimitive(t, r) {
|
|
331
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
332
|
+
var e = t[Symbol.toPrimitive];
|
|
333
|
+
if (void 0 !== e) {
|
|
334
|
+
var i = e.call(t, r || "default");
|
|
335
|
+
if ("object" != _typeof(i)) return i;
|
|
336
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
337
|
+
}
|
|
338
|
+
return ("string" === r ? String : Number)(t);
|
|
339
|
+
}
|
|
340
|
+
function toPropertyKey(t) {
|
|
341
|
+
var i = toPrimitive(t, "string");
|
|
342
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
343
|
+
}
|
|
344
|
+
function _defineProperty(e, r, t) {
|
|
345
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
346
|
+
value: t,
|
|
347
|
+
enumerable: !0,
|
|
348
|
+
configurable: !0,
|
|
349
|
+
writable: !0
|
|
350
|
+
}) : e[r] = t, e;
|
|
351
|
+
}
|
|
352
|
+
function ownKeys(e, r) {
|
|
353
|
+
var t = Object.keys(e);
|
|
354
|
+
if (Object.getOwnPropertySymbols) {
|
|
355
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
356
|
+
r && (o = o.filter(function(r$1) {
|
|
357
|
+
return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
|
|
358
|
+
})), t.push.apply(t, o);
|
|
359
|
+
}
|
|
360
|
+
return t;
|
|
361
|
+
}
|
|
362
|
+
function _objectSpread2(e) {
|
|
363
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
364
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
365
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
|
|
366
|
+
_defineProperty(e, r$1, t[r$1]);
|
|
367
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
|
|
368
|
+
Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return e;
|
|
372
|
+
}
|
|
373
|
+
function createEdgeMarker(marker) {
|
|
374
|
+
if (!(marker === null || marker === void 0 ? void 0 : marker.type) || marker.type === "none") return void 0;
|
|
375
|
+
if (marker.type === MarkerType.Arrow || marker.type === MarkerType.ArrowClosed) return _objectSpread2({
|
|
376
|
+
color: "context-stroke",
|
|
377
|
+
strokeWidth: 1.5
|
|
378
|
+
}, marker);
|
|
379
|
+
return marker.type;
|
|
380
|
+
}
|
|
381
|
+
const DEFAULT_NODE_WIDTH = 350;
|
|
382
|
+
const DEFAULT_NODE_X_OFFSET = 0;
|
|
383
|
+
const DEFAULT_NODE_X_OFFSET_LAYER = 100;
|
|
384
|
+
const DEFAULT_NODE_X_SPACING = 450;
|
|
385
|
+
const DEFAULT_NODE_Y_OFFSET = 100;
|
|
386
|
+
const DEFAULT_NODE_Y_OFFSET_LAYER = 100;
|
|
387
|
+
const DEFAULT_NODE_Y_SPACING = 600;
|
|
388
|
+
function computeLayeredLayout(nodes, edges, config = {}) {
|
|
389
|
+
const { horizontalSpacing = DEFAULT_NODE_X_SPACING, verticalSpacing = DEFAULT_NODE_Y_SPACING, startX = DEFAULT_NODE_X_OFFSET_LAYER, startY = DEFAULT_NODE_Y_OFFSET_LAYER } = config;
|
|
390
|
+
const { acyclicEdges, reversedEdges } = breakCycles(nodes, edges);
|
|
391
|
+
const joinTables = detectJoinTables(nodes, edges);
|
|
392
|
+
const layers = assignLayers(nodes, acyclicEdges, joinTables);
|
|
393
|
+
minimizeCrossings(layers, acyclicEdges, joinTables, 4);
|
|
394
|
+
adjacentSwapOptimization(layers, acyclicEdges);
|
|
395
|
+
positionJoinTables(nodes, layers, joinTables, acyclicEdges);
|
|
396
|
+
const positions = assignCoordinates(nodes, layers, horizontalSpacing, verticalSpacing, startX, startY);
|
|
397
|
+
balanceColumns(positions, layers);
|
|
398
|
+
return {
|
|
399
|
+
positions,
|
|
400
|
+
layers,
|
|
401
|
+
reversedEdges,
|
|
402
|
+
ports: calculatePorts(nodes)
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function breakCycles(nodes, edges) {
|
|
406
|
+
const reversedEdges = /* @__PURE__ */ new Set();
|
|
407
|
+
const acyclicEdges = [];
|
|
408
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
409
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
410
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
411
|
+
nodes.forEach((node) => {
|
|
412
|
+
outgoing.set(node.id, /* @__PURE__ */ new Set());
|
|
413
|
+
incoming.set(node.id, /* @__PURE__ */ new Set());
|
|
414
|
+
});
|
|
415
|
+
edges.forEach((edge) => {
|
|
416
|
+
var _outgoing$get, _incoming$get;
|
|
417
|
+
const key = `${edge.source}->${edge.target}`;
|
|
418
|
+
edgeMap.set(key, edge);
|
|
419
|
+
(_outgoing$get = outgoing.get(edge.source)) === null || _outgoing$get === void 0 || _outgoing$get.add(edge.target);
|
|
420
|
+
(_incoming$get = incoming.get(edge.target)) === null || _incoming$get === void 0 || _incoming$get.add(edge.source);
|
|
421
|
+
});
|
|
422
|
+
const remaining = new Set(nodes.map((n) => n.id));
|
|
423
|
+
const leftSequence = [];
|
|
424
|
+
const rightSequence = [];
|
|
425
|
+
while (remaining.size > 0) {
|
|
426
|
+
const sources = [...remaining].filter((node) => {
|
|
427
|
+
return ![...incoming.get(node) || /* @__PURE__ */ new Set()].some((src) => remaining.has(src));
|
|
428
|
+
});
|
|
429
|
+
if (sources.length > 0) {
|
|
430
|
+
sources.forEach((node) => {
|
|
431
|
+
leftSequence.push(node);
|
|
432
|
+
remaining.delete(node);
|
|
433
|
+
});
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const sinks = [...remaining].filter((node) => {
|
|
437
|
+
return ![...outgoing.get(node) || /* @__PURE__ */ new Set()].some((tgt) => remaining.has(tgt));
|
|
438
|
+
});
|
|
439
|
+
if (sinks.length > 0) {
|
|
440
|
+
sinks.forEach((node) => {
|
|
441
|
+
rightSequence.unshift(node);
|
|
442
|
+
remaining.delete(node);
|
|
443
|
+
});
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
let maxDelta = -Infinity;
|
|
447
|
+
let maxNode = remaining.values().next().value;
|
|
448
|
+
remaining.forEach((node) => {
|
|
449
|
+
const delta = (outgoing.get(node) || /* @__PURE__ */ new Set()).size - (incoming.get(node) || /* @__PURE__ */ new Set()).size;
|
|
450
|
+
if (delta > maxDelta) {
|
|
451
|
+
maxDelta = delta;
|
|
452
|
+
maxNode = node;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
if (maxNode) {
|
|
456
|
+
leftSequence.push(maxNode);
|
|
457
|
+
remaining.delete(maxNode);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const ordering = [...leftSequence, ...rightSequence];
|
|
461
|
+
const position = new Map(ordering.map((id, idx) => [id, idx]));
|
|
462
|
+
edges.forEach((edge) => {
|
|
463
|
+
if (position.get(edge.source) < position.get(edge.target)) acyclicEdges.push(edge);
|
|
464
|
+
else {
|
|
465
|
+
const key = `${edge.source}->${edge.target}`;
|
|
466
|
+
reversedEdges.add(key);
|
|
467
|
+
acyclicEdges.push(_objectSpread2(_objectSpread2({}, edge), {}, {
|
|
468
|
+
source: edge.target,
|
|
469
|
+
target: edge.source
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
return {
|
|
474
|
+
acyclicEdges,
|
|
475
|
+
reversedEdges
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function assignLayers(nodes, edges, joinTables) {
|
|
479
|
+
const layers = /* @__PURE__ */ new Map();
|
|
480
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
481
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
482
|
+
nodes.forEach((node) => {
|
|
483
|
+
outgoing.set(node.id, /* @__PURE__ */ new Set());
|
|
484
|
+
incoming.set(node.id, /* @__PURE__ */ new Set());
|
|
485
|
+
});
|
|
486
|
+
edges.forEach((edge) => {
|
|
487
|
+
var _outgoing$get2, _incoming$get2;
|
|
488
|
+
(_outgoing$get2 = outgoing.get(edge.source)) === null || _outgoing$get2 === void 0 || _outgoing$get2.add(edge.target);
|
|
489
|
+
(_incoming$get2 = incoming.get(edge.target)) === null || _incoming$get2 === void 0 || _incoming$get2.add(edge.source);
|
|
490
|
+
});
|
|
491
|
+
const hubScores = /* @__PURE__ */ new Map();
|
|
492
|
+
nodes.forEach((node) => {
|
|
493
|
+
const outDegree = (outgoing.get(node.id) || /* @__PURE__ */ new Set()).size;
|
|
494
|
+
const inDegree = (incoming.get(node.id) || /* @__PURE__ */ new Set()).size;
|
|
495
|
+
const score = Math.max(-2, Math.min(2, outDegree - inDegree));
|
|
496
|
+
hubScores.set(node.id, score * .3);
|
|
497
|
+
});
|
|
498
|
+
const visited = /* @__PURE__ */ new Set();
|
|
499
|
+
function assignLayer(nodeId, layer) {
|
|
500
|
+
if (!layers.has(nodeId) || layers.get(nodeId) < layer) layers.set(nodeId, layer);
|
|
501
|
+
if (visited.has(nodeId)) return;
|
|
502
|
+
visited.add(nodeId);
|
|
503
|
+
(outgoing.get(nodeId) || /* @__PURE__ */ new Set()).forEach((child) => {
|
|
504
|
+
assignLayer(child, layer + 1);
|
|
505
|
+
});
|
|
506
|
+
visited.delete(nodeId);
|
|
507
|
+
}
|
|
508
|
+
const roots = nodes.filter((node) => {
|
|
509
|
+
return (incoming.get(node.id) || /* @__PURE__ */ new Set()).size === 0;
|
|
510
|
+
});
|
|
511
|
+
if (roots.length === 0) [...nodes].sort((a, b) => {
|
|
512
|
+
return (hubScores.get(b.id) || 0) - (hubScores.get(a.id) || 0);
|
|
513
|
+
}).slice(0, 3).forEach((node) => assignLayer(node.id, 0));
|
|
514
|
+
else roots.forEach((root) => assignLayer(root.id, 0));
|
|
515
|
+
nodes.forEach((node) => {
|
|
516
|
+
if (!layers.has(node.id)) layers.set(node.id, 0);
|
|
517
|
+
});
|
|
518
|
+
nodes.forEach((node) => {
|
|
519
|
+
const baseRank = layers.get(node.id);
|
|
520
|
+
const bias = hubScores.get(node.id) || 0;
|
|
521
|
+
const adjustedRank = Math.max(0, Math.round(baseRank - bias));
|
|
522
|
+
layers.set(node.id, adjustedRank);
|
|
523
|
+
});
|
|
524
|
+
joinTables.forEach((joinId) => {
|
|
525
|
+
const parents = [...incoming.get(joinId) || /* @__PURE__ */ new Set()];
|
|
526
|
+
if (parents.length >= 2) {
|
|
527
|
+
const parentRanks = parents.map((p) => layers.get(p) || 0);
|
|
528
|
+
const maxParentRank = Math.max(...parentRanks);
|
|
529
|
+
layers.set(joinId, maxParentRank + 1);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
const layerGroups = /* @__PURE__ */ new Map();
|
|
533
|
+
layers.forEach((layer, nodeId) => {
|
|
534
|
+
if (!layerGroups.has(layer)) layerGroups.set(layer, []);
|
|
535
|
+
layerGroups.get(layer).push(nodeId);
|
|
536
|
+
});
|
|
537
|
+
return layerGroups;
|
|
538
|
+
}
|
|
539
|
+
function detectJoinTables(nodes, _edges) {
|
|
540
|
+
const joinTables = /* @__PURE__ */ new Set();
|
|
541
|
+
nodes.forEach((node) => {
|
|
542
|
+
const fkCount = node.columns.filter((c) => c.isForeignKey).length;
|
|
543
|
+
const nonFkCount = node.columns.filter((c) => !c.isForeignKey).length;
|
|
544
|
+
if (fkCount >= 2 && nonFkCount <= 2) joinTables.add(node.id);
|
|
545
|
+
});
|
|
546
|
+
return joinTables;
|
|
547
|
+
}
|
|
548
|
+
function minimizeCrossings(layers, edges, joinTables, iterations) {
|
|
549
|
+
const maxLayer = Math.max(...layers.keys());
|
|
550
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
551
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
552
|
+
edges.forEach((edge) => {
|
|
553
|
+
if (!outgoing.has(edge.source)) outgoing.set(edge.source, []);
|
|
554
|
+
outgoing.get(edge.source).push({
|
|
555
|
+
target: edge.target,
|
|
556
|
+
weight: edge.weight
|
|
557
|
+
});
|
|
558
|
+
if (!incoming.has(edge.target)) incoming.set(edge.target, []);
|
|
559
|
+
incoming.get(edge.target).push({
|
|
560
|
+
source: edge.source,
|
|
561
|
+
weight: edge.weight
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
for (let iter = 0; iter < iterations; iter++) {
|
|
565
|
+
for (let layer = 1; layer <= maxLayer; layer++) {
|
|
566
|
+
const nodes = layers.get(layer) || [];
|
|
567
|
+
if (nodes.length <= 1) continue;
|
|
568
|
+
const prevLayer = layers.get(layer - 1) || [];
|
|
569
|
+
const positionMap = new Map(prevLayer.map((id, idx) => [id, idx]));
|
|
570
|
+
const metrics = nodes.map((nodeId) => {
|
|
571
|
+
const parents = incoming.get(nodeId) || [];
|
|
572
|
+
if (parents.length === 0) return {
|
|
573
|
+
nodeId,
|
|
574
|
+
barycenter: 0,
|
|
575
|
+
median: 0
|
|
576
|
+
};
|
|
577
|
+
const positions = parents.map((p) => positionMap.get(p.source)).filter((pos) => pos !== void 0).sort((a, b) => a - b);
|
|
578
|
+
const weightedSum = parents.reduce((sum, p) => {
|
|
579
|
+
const pos = positionMap.get(p.source);
|
|
580
|
+
return pos !== void 0 ? sum + pos * p.weight : sum;
|
|
581
|
+
}, 0);
|
|
582
|
+
const totalWeight = parents.reduce((sum, p) => sum + p.weight, 0);
|
|
583
|
+
return {
|
|
584
|
+
nodeId,
|
|
585
|
+
barycenter: totalWeight > 0 ? weightedSum / totalWeight : 0,
|
|
586
|
+
median: positions.length > 0 ? positions[Math.floor(positions.length / 2)] : 0
|
|
587
|
+
};
|
|
588
|
+
});
|
|
589
|
+
metrics.sort((a, b) => {
|
|
590
|
+
return (joinTables.has(a.nodeId) ? a.median : a.barycenter) - (joinTables.has(b.nodeId) ? b.median : b.barycenter);
|
|
591
|
+
});
|
|
592
|
+
layers.set(layer, metrics.map((m) => m.nodeId));
|
|
593
|
+
}
|
|
594
|
+
for (let layer = maxLayer - 1; layer >= 0; layer--) {
|
|
595
|
+
const nodes = layers.get(layer) || [];
|
|
596
|
+
if (nodes.length <= 1) continue;
|
|
597
|
+
const nextLayer = layers.get(layer + 1) || [];
|
|
598
|
+
const positionMap = new Map(nextLayer.map((id, idx) => [id, idx]));
|
|
599
|
+
const metrics = nodes.map((nodeId) => {
|
|
600
|
+
const children = outgoing.get(nodeId) || [];
|
|
601
|
+
if (children.length === 0) {
|
|
602
|
+
const currentLayer = layers.get(layer) || [];
|
|
603
|
+
return {
|
|
604
|
+
nodeId,
|
|
605
|
+
barycenter: currentLayer.indexOf(nodeId),
|
|
606
|
+
median: currentLayer.indexOf(nodeId)
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
const positions = children.map((c) => positionMap.get(c.target)).filter((pos) => pos !== void 0).sort((a, b) => a - b);
|
|
610
|
+
const weightedSum = children.reduce((sum, c) => {
|
|
611
|
+
const pos = positionMap.get(c.target);
|
|
612
|
+
return pos !== void 0 ? sum + pos * c.weight : sum;
|
|
613
|
+
}, 0);
|
|
614
|
+
const totalWeight = children.reduce((sum, c) => sum + c.weight, 0);
|
|
615
|
+
return {
|
|
616
|
+
nodeId,
|
|
617
|
+
barycenter: totalWeight > 0 ? weightedSum / totalWeight : positions[0] || 0,
|
|
618
|
+
median: positions.length > 0 ? positions[Math.floor(positions.length / 2)] : 0
|
|
619
|
+
};
|
|
620
|
+
});
|
|
621
|
+
metrics.sort((a, b) => {
|
|
622
|
+
return (joinTables.has(a.nodeId) ? a.median : a.barycenter) - (joinTables.has(b.nodeId) ? b.median : b.barycenter);
|
|
623
|
+
});
|
|
624
|
+
layers.set(layer, metrics.map((m) => m.nodeId));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function adjacentSwapOptimization(layers, edges) {
|
|
629
|
+
const maxLayer = Math.max(...layers.keys());
|
|
630
|
+
new Set(edges.map((e) => `${e.source}->${e.target}`));
|
|
631
|
+
function countCrossings(layer, idx1, idx2) {
|
|
632
|
+
const node1 = layer[idx1];
|
|
633
|
+
const node2 = layer[idx2];
|
|
634
|
+
let crossings = 0;
|
|
635
|
+
edges.forEach((e1) => {
|
|
636
|
+
if (e1.source !== node1 && e1.target !== node1) return;
|
|
637
|
+
edges.forEach((e2) => {
|
|
638
|
+
if (e2.source !== node2 && e2.target !== node2) return;
|
|
639
|
+
const e1SourceIdx = layer.indexOf(e1.source);
|
|
640
|
+
const e1TargetIdx = layer.indexOf(e1.target);
|
|
641
|
+
const e2SourceIdx = layer.indexOf(e2.source);
|
|
642
|
+
const e2TargetIdx = layer.indexOf(e2.target);
|
|
643
|
+
if (e1SourceIdx < e2SourceIdx && e1TargetIdx > e2TargetIdx || e1SourceIdx > e2SourceIdx && e1TargetIdx < e2TargetIdx) crossings++;
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
return crossings;
|
|
647
|
+
}
|
|
648
|
+
for (let layer = 0; layer <= maxLayer; layer++) {
|
|
649
|
+
const layerNodes = layers.get(layer) || [];
|
|
650
|
+
if (layerNodes.length <= 1) continue;
|
|
651
|
+
let improved = true;
|
|
652
|
+
let iterations = 0;
|
|
653
|
+
const maxIterations = 5;
|
|
654
|
+
while (improved && iterations < maxIterations) {
|
|
655
|
+
improved = false;
|
|
656
|
+
iterations++;
|
|
657
|
+
for (let i = 0; i < layerNodes.length - 1; i++) {
|
|
658
|
+
const before = countCrossings(layerNodes, i, i + 1);
|
|
659
|
+
[layerNodes[i], layerNodes[i + 1]] = [layerNodes[i + 1], layerNodes[i]];
|
|
660
|
+
if (countCrossings(layerNodes, i, i + 1) < before) improved = true;
|
|
661
|
+
else [layerNodes[i], layerNodes[i + 1]] = [layerNodes[i + 1], layerNodes[i]];
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
layers.set(layer, layerNodes);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
function positionJoinTables(nodes, layers, joinTables, edges) {
|
|
668
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
669
|
+
nodes.forEach((node) => incoming.set(node.id, /* @__PURE__ */ new Set()));
|
|
670
|
+
edges.forEach((edge) => {
|
|
671
|
+
var _incoming$get3;
|
|
672
|
+
(_incoming$get3 = incoming.get(edge.target)) === null || _incoming$get3 === void 0 || _incoming$get3.add(edge.source);
|
|
673
|
+
});
|
|
674
|
+
joinTables.forEach((joinId) => {
|
|
675
|
+
const parents = [...incoming.get(joinId) || /* @__PURE__ */ new Set()];
|
|
676
|
+
if (parents.length >= 2) {
|
|
677
|
+
let joinLayer = -1;
|
|
678
|
+
let joinIndex = -1;
|
|
679
|
+
layers.forEach((layerNodes, layerIdx) => {
|
|
680
|
+
const idx = layerNodes.indexOf(joinId);
|
|
681
|
+
if (idx !== -1) {
|
|
682
|
+
joinLayer = layerIdx;
|
|
683
|
+
joinIndex = idx;
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
if (joinLayer === -1) return;
|
|
687
|
+
const parentPositions = [];
|
|
688
|
+
layers.forEach((layerNodes, layerIdx) => {
|
|
689
|
+
if (layerIdx < joinLayer) parents.forEach((parent) => {
|
|
690
|
+
const idx = layerNodes.indexOf(parent);
|
|
691
|
+
if (idx !== -1) parentPositions.push(idx);
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
if (parentPositions.length >= 2) {
|
|
695
|
+
const avgPosition = parentPositions.reduce((a, b) => a + b, 0) / parentPositions.length;
|
|
696
|
+
const targetIndex = Math.round(avgPosition);
|
|
697
|
+
const layerNodes = layers.get(joinLayer);
|
|
698
|
+
layerNodes.splice(joinIndex, 1);
|
|
699
|
+
const insertIdx = Math.min(targetIndex, layerNodes.length);
|
|
700
|
+
layerNodes.splice(insertIdx, 0, joinId);
|
|
701
|
+
layers.set(joinLayer, layerNodes);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
function assignCoordinates(nodes, layers, horizontalSpacing, verticalSpacing, startX, startY) {
|
|
707
|
+
const positions = {};
|
|
708
|
+
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
709
|
+
layers.forEach((layerNodes, layerIndex) => {
|
|
710
|
+
let currentX = startX;
|
|
711
|
+
layerNodes.forEach((nodeId) => {
|
|
712
|
+
const node = nodeMap.get(nodeId);
|
|
713
|
+
const nodeWidth = (node === null || node === void 0 ? void 0 : node.width) || 350;
|
|
714
|
+
positions[nodeId] = {
|
|
715
|
+
x: currentX,
|
|
716
|
+
y: startY + layerIndex * verticalSpacing
|
|
717
|
+
};
|
|
718
|
+
currentX += nodeWidth + (horizontalSpacing - nodeWidth);
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
return positions;
|
|
722
|
+
}
|
|
723
|
+
function balanceColumns(positions, layers) {
|
|
724
|
+
layers.forEach((layerNodes) => {
|
|
725
|
+
if (layerNodes.length <= 2) return;
|
|
726
|
+
const xPositions = layerNodes.map((id) => positions[id].x).sort((a, b) => a - b);
|
|
727
|
+
const median = xPositions[Math.floor(xPositions.length / 2)];
|
|
728
|
+
layerNodes.forEach((id) => {
|
|
729
|
+
const delta = (median - positions[id].x) * .1;
|
|
730
|
+
positions[id].x += delta;
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
function calculatePorts(nodes) {
|
|
735
|
+
const ports = {};
|
|
736
|
+
nodes.forEach((node) => {
|
|
737
|
+
const left = [];
|
|
738
|
+
const right = [];
|
|
739
|
+
[...node.columns].sort((a, b) => {
|
|
740
|
+
if (a.isPrimaryKey && !b.isPrimaryKey) return -1;
|
|
741
|
+
if (!a.isPrimaryKey && b.isPrimaryKey) return 1;
|
|
742
|
+
if (a.isForeignKey && !b.isForeignKey) return 1;
|
|
743
|
+
if (!a.isForeignKey && b.isForeignKey) return -1;
|
|
744
|
+
return 0;
|
|
745
|
+
}).forEach((col) => {
|
|
746
|
+
if (col.isPrimaryKey) left.push(col.name);
|
|
747
|
+
if (col.isForeignKey) right.push(col.name);
|
|
748
|
+
});
|
|
749
|
+
ports[node.id] = {
|
|
750
|
+
left,
|
|
751
|
+
right
|
|
752
|
+
};
|
|
753
|
+
});
|
|
754
|
+
return ports;
|
|
755
|
+
}
|
|
756
|
+
function generateTableNodeId(baseId, tableId = null) {
|
|
757
|
+
return `${baseId}-table-${tableId !== null && tableId !== void 0 ? tableId : generateUUID()}`;
|
|
758
|
+
}
|
|
759
|
+
function generateUUID() {
|
|
760
|
+
let d = (/* @__PURE__ */ new Date()).getTime();
|
|
761
|
+
let d2 = typeof performance !== "undefined" && performance.now ? performance.now() * 1e3 : 0;
|
|
762
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
763
|
+
let r = Math.random() * 16;
|
|
764
|
+
if (d > 0) {
|
|
765
|
+
r = (d + r) % 16 | 0;
|
|
766
|
+
d = Math.floor(d / 16);
|
|
767
|
+
} else {
|
|
768
|
+
r = (d2 + r) % 16 | 0;
|
|
769
|
+
d2 = Math.floor(d2 / 16);
|
|
770
|
+
}
|
|
771
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
var AstToUiConverter = class {
|
|
775
|
+
static updateReactFlow({ nodes, edges, schema, sourceId, oldDataSources }) {
|
|
776
|
+
console.log("UPDATE:", {
|
|
777
|
+
nodes,
|
|
778
|
+
edges,
|
|
779
|
+
schema,
|
|
780
|
+
sourceId,
|
|
781
|
+
oldDataSources
|
|
782
|
+
});
|
|
783
|
+
const oldSource = oldDataSources.find((s) => s.id === sourceId);
|
|
784
|
+
const sourceNodePrefix = `${sourceId}-table-`;
|
|
785
|
+
const positions = this.calculateSmartLayout(schema);
|
|
786
|
+
const nodeTableMap = new Map(nodes.map((node) => {
|
|
787
|
+
var _oldSource$schemaAst$;
|
|
788
|
+
return [oldSource === null || oldSource === void 0 || (_oldSource$schemaAst$ = oldSource.schemaAst.tables.find((t) => {
|
|
789
|
+
var _localTable;
|
|
790
|
+
return t.id === ((_localTable = node.data.localTable) === null || _localTable === void 0 ? void 0 : _localTable.tableId);
|
|
791
|
+
})) === null || _oldSource$schemaAst$ === void 0 ? void 0 : _oldSource$schemaAst$.name, node];
|
|
792
|
+
}));
|
|
793
|
+
console.log("Old node table map:", nodeTableMap);
|
|
794
|
+
const tableNames = new Set(schema.tables.map((table) => table.name));
|
|
795
|
+
const sourceNodes = schema.tables.map((table, index) => {
|
|
796
|
+
var _ref, _ref2, _prevNode$position, _prevNode$data, _prevNode$data$style, _prevNode$data2, _baseNode$data;
|
|
797
|
+
const prevNode = nodeTableMap.get(table.name);
|
|
798
|
+
const position = (_ref = (_ref2 = (_prevNode$position = prevNode === null || prevNode === void 0 ? void 0 : prevNode.position) !== null && _prevNode$position !== void 0 ? _prevNode$position : table.position) !== null && _ref2 !== void 0 ? _ref2 : positions[table.name]) !== null && _ref !== void 0 ? _ref : {
|
|
799
|
+
x: 100,
|
|
800
|
+
y: 100 + index * 210
|
|
801
|
+
};
|
|
802
|
+
const baseNode = this.tableAstToNode(sourceId, table, position, tableNames);
|
|
803
|
+
if (!prevNode) return baseNode;
|
|
804
|
+
const mergedNode = _objectSpread2(_objectSpread2(_objectSpread2({}, prevNode), baseNode), {}, { data: _objectSpread2(_objectSpread2(_objectSpread2({}, (_prevNode$data = prevNode.data) !== null && _prevNode$data !== void 0 ? _prevNode$data : {}), baseNode.data), {}, { style: (_prevNode$data$style = (_prevNode$data2 = prevNode.data) === null || _prevNode$data2 === void 0 ? void 0 : _prevNode$data2.style) !== null && _prevNode$data$style !== void 0 ? _prevNode$data$style : (_baseNode$data = baseNode.data) === null || _baseNode$data === void 0 ? void 0 : _baseNode$data.style }) });
|
|
805
|
+
if (prevNode.style) mergedNode.style = prevNode.style;
|
|
806
|
+
if (prevNode.width !== void 0) mergedNode.width = prevNode.width;
|
|
807
|
+
if (prevNode.height !== void 0) mergedNode.height = prevNode.height;
|
|
808
|
+
return mergedNode;
|
|
809
|
+
}).filter((node) => {
|
|
810
|
+
var _schema$tables$find;
|
|
811
|
+
return nodeTableMap.has((_schema$tables$find = schema.tables.find((t) => {
|
|
812
|
+
var _localTable2;
|
|
813
|
+
return t.id === ((_localTable2 = node.data.localTable) === null || _localTable2 === void 0 ? void 0 : _localTable2.tableId);
|
|
814
|
+
})) === null || _schema$tables$find === void 0 ? void 0 : _schema$tables$find.name);
|
|
815
|
+
});
|
|
816
|
+
const validNodeIds = new Set(sourceNodes.map((node) => node.id));
|
|
817
|
+
const sourceEdges = schema.tables.flatMap((table) => this.createEdgesFromTable(sourceId, table, schema.tables).filter((edge) => validNodeIds.has(edge.source) && validNodeIds.has(edge.target)));
|
|
818
|
+
const preservedNodes = nodes.filter((node) => !node.id.startsWith(sourceNodePrefix));
|
|
819
|
+
const preservedEdges = edges.filter((edge) => !edge.id.startsWith(sourceNodePrefix));
|
|
820
|
+
return {
|
|
821
|
+
nodes: [...preservedNodes, ...sourceNodes],
|
|
822
|
+
edges: [...preservedEdges, ...sourceEdges]
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
static toReactFlow(schema, sourceId) {
|
|
826
|
+
const nodes = [];
|
|
827
|
+
const edges = [];
|
|
828
|
+
const tableNames = new Set(schema.tables.map((table) => table.name));
|
|
829
|
+
const positions = this.calculateSmartLayout(schema);
|
|
830
|
+
schema.tables.forEach((table, index) => {
|
|
831
|
+
const position = table.position || positions[table.name] || {
|
|
832
|
+
x: 100,
|
|
833
|
+
y: 100 + index * 210
|
|
834
|
+
};
|
|
835
|
+
const node = this.tableAstToNode(sourceId, table, position, tableNames);
|
|
836
|
+
nodes.push(node);
|
|
837
|
+
const tableEdges = this.createEdgesFromTable(sourceId, table, schema.tables);
|
|
838
|
+
edges.push(...tableEdges);
|
|
839
|
+
});
|
|
840
|
+
return {
|
|
841
|
+
nodes,
|
|
842
|
+
edges
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
static calculateSmartLayout(schema) {
|
|
846
|
+
const positions = {};
|
|
847
|
+
const tableNames = new Set(schema.tables.map((table) => table.name));
|
|
848
|
+
const relationships = /* @__PURE__ */ new Map();
|
|
849
|
+
schema.tables.forEach((table) => {
|
|
850
|
+
if (!relationships.has(table.name)) relationships.set(table.name, /* @__PURE__ */ new Set());
|
|
851
|
+
this.extractForeignKeys(table).forEach((fk) => {
|
|
852
|
+
if (!tableNames.has(fk.referencedTable)) return;
|
|
853
|
+
relationships.get(table.name).add(fk.referencedTable);
|
|
854
|
+
if (!relationships.has(fk.referencedTable)) relationships.set(fk.referencedTable, /* @__PURE__ */ new Set());
|
|
855
|
+
relationships.get(fk.referencedTable).add(table.name);
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
const visited = /* @__PURE__ */ new Set();
|
|
859
|
+
const components = [];
|
|
860
|
+
function dfs(tableName, component) {
|
|
861
|
+
visited.add(tableName);
|
|
862
|
+
component.push(tableName);
|
|
863
|
+
(relationships.get(tableName) || /* @__PURE__ */ new Set()).forEach((neighbor) => {
|
|
864
|
+
if (!visited.has(neighbor)) dfs(neighbor, component);
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
schema.tables.forEach((table) => {
|
|
868
|
+
if (!visited.has(table.name)) {
|
|
869
|
+
const component = [];
|
|
870
|
+
dfs(table.name, component);
|
|
871
|
+
components.push(component);
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
let componentOffsetX = DEFAULT_NODE_X_OFFSET;
|
|
875
|
+
components.forEach((component) => {
|
|
876
|
+
if (component.length === 1) {
|
|
877
|
+
positions[component[0]] = {
|
|
878
|
+
x: componentOffsetX,
|
|
879
|
+
y: DEFAULT_NODE_Y_OFFSET
|
|
880
|
+
};
|
|
881
|
+
componentOffsetX += DEFAULT_NODE_X_SPACING;
|
|
882
|
+
} else {
|
|
883
|
+
const componentPositions = this.layoutComponentHierarchical(component, relationships, componentOffsetX, schema);
|
|
884
|
+
Object.assign(positions, componentPositions);
|
|
885
|
+
componentOffsetX = Math.max(...Object.values(componentPositions).map((p) => p.x)) + DEFAULT_NODE_WIDTH + DEFAULT_NODE_X_SPACING;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
return positions;
|
|
889
|
+
}
|
|
890
|
+
static layoutComponentHierarchical(tables, relationships, startX, schema) {
|
|
891
|
+
const tableMap = new Map(schema.tables.filter((t) => tables.includes(t.name)).map((t) => [t.name, t]));
|
|
892
|
+
const nodes = arrayNonNullable(tables.map((tableName) => {
|
|
893
|
+
const table = tableMap.get(tableName);
|
|
894
|
+
if (!table) return null;
|
|
895
|
+
return {
|
|
896
|
+
id: tableName,
|
|
897
|
+
width: 350,
|
|
898
|
+
height: Math.max(200, table.columns.length * 30 + 80),
|
|
899
|
+
columns: table.columns.map((col) => {
|
|
900
|
+
const primaryKeys = this.extractPrimaryKeys(table);
|
|
901
|
+
const foreignKeys = this.extractForeignKeys(table);
|
|
902
|
+
return {
|
|
903
|
+
name: col.name,
|
|
904
|
+
isPrimaryKey: primaryKeys.includes(col.name),
|
|
905
|
+
isForeignKey: foreignKeys.some((fk) => fk.columns.includes(col.name))
|
|
906
|
+
};
|
|
907
|
+
})
|
|
908
|
+
};
|
|
909
|
+
}));
|
|
910
|
+
const edges = [];
|
|
911
|
+
const componentSet = new Set(tables);
|
|
912
|
+
schema.tables.forEach((table) => {
|
|
913
|
+
if (!componentSet.has(table.name)) return;
|
|
914
|
+
this.extractForeignKeys(table).forEach((fk) => {
|
|
915
|
+
if (componentSet.has(fk.referencedTable) && tableMap.has(fk.referencedTable)) {
|
|
916
|
+
const primaryKeys = this.extractPrimaryKeys(table);
|
|
917
|
+
const isPrimaryFK = fk.columns.some((col) => primaryKeys.includes(col));
|
|
918
|
+
edges.push({
|
|
919
|
+
source: table.name,
|
|
920
|
+
target: fk.referencedTable,
|
|
921
|
+
sourceColumn: fk.columns[0],
|
|
922
|
+
targetColumn: fk.referencedColumns[0],
|
|
923
|
+
weight: isPrimaryFK ? 2 : 1
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
return computeLayeredLayout(nodes, edges, {
|
|
929
|
+
horizontalSpacing: DEFAULT_NODE_X_SPACING,
|
|
930
|
+
verticalSpacing: DEFAULT_NODE_Y_SPACING,
|
|
931
|
+
startY: DEFAULT_NODE_Y_OFFSET,
|
|
932
|
+
startX
|
|
933
|
+
}).positions;
|
|
934
|
+
}
|
|
935
|
+
static tableAstToNode(sourceId, table, position, _validTableNames) {
|
|
936
|
+
return {
|
|
937
|
+
id: generateTableNodeId(sourceId, table.id),
|
|
938
|
+
type: "databaseTableSQL",
|
|
939
|
+
width: DEFAULT_NODE_WIDTH,
|
|
940
|
+
position,
|
|
941
|
+
data: { localTable: {
|
|
942
|
+
baseId: sourceId,
|
|
943
|
+
tableId: table.id
|
|
944
|
+
} }
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
static extractPrimaryKeys(table) {
|
|
948
|
+
const pkConstraint = table.constraints.find((c) => c.type === "primary_key");
|
|
949
|
+
if (pkConstraint) return pkConstraint.columns;
|
|
950
|
+
return table.columns.filter((col) => col.autoIncrement).map((col) => col.name);
|
|
951
|
+
}
|
|
952
|
+
static extractForeignKeys(table) {
|
|
953
|
+
return table.constraints.filter((c) => c.type === "foreign_key");
|
|
954
|
+
}
|
|
955
|
+
static formatDataType(column) {
|
|
956
|
+
const { dataType } = column;
|
|
957
|
+
let formatted = dataType.name;
|
|
958
|
+
if (dataType.parameters && dataType.parameters.length > 0) formatted += `(${dataType.parameters.join(", ")})`;
|
|
959
|
+
if (dataType.unsigned) formatted += " UNSIGNED";
|
|
960
|
+
if (dataType.timezone) formatted += " WITH TIME ZONE";
|
|
961
|
+
return formatted;
|
|
962
|
+
}
|
|
963
|
+
static createEdgesFromTable(baseId, sourceTable, validTables) {
|
|
964
|
+
const edges = [];
|
|
965
|
+
this.extractForeignKeys(sourceTable).forEach((fk, index) => {
|
|
966
|
+
const targetTable = validTables.find((t) => t.id === fk.referencedTable || t.name === fk.referencedTable);
|
|
967
|
+
if (!targetTable) return;
|
|
968
|
+
const sourceNodeId = generateTableNodeId(baseId, sourceTable.id);
|
|
969
|
+
const targetNodeId = generateTableNodeId(baseId, targetTable.id);
|
|
970
|
+
const edgeId = `${sourceNodeId}-fk-${index}`;
|
|
971
|
+
const fkCols = fk.columns;
|
|
972
|
+
const allNotNull = fkCols.every((cn) => {
|
|
973
|
+
const col = sourceTable.columns.find((c) => c.name === cn);
|
|
974
|
+
return col ? col.nullable === false : false;
|
|
975
|
+
});
|
|
976
|
+
const hasUniqueConstraint = sourceTable.constraints.some((c) => c.type === "unique" && c.columns.length === fkCols.length && fkCols.every((cn) => c.columns.includes(cn)));
|
|
977
|
+
const hasUniqueIndex = sourceTable.indexes.some((idx) => idx.unique && idx.columns.length === fkCols.length && fkCols.every((cn) => idx.columns.some((ic) => ic.name === cn)));
|
|
978
|
+
const isUnique = hasUniqueConstraint || hasUniqueIndex;
|
|
979
|
+
const startMarkerType = allNotNull ? "erdOnlyOne" : "erdZeroOrOne";
|
|
980
|
+
const endMarkerType = isUnique ? "erdZeroOrOne" : "erdZeroOrMany";
|
|
981
|
+
edges.push({
|
|
982
|
+
id: edgeId,
|
|
983
|
+
source: sourceNodeId,
|
|
984
|
+
target: targetNodeId,
|
|
985
|
+
type: "smoothstep",
|
|
986
|
+
animated: false,
|
|
987
|
+
markerEnd: createEdgeMarker({ type: endMarkerType }),
|
|
988
|
+
markerStart: createEdgeMarker({ type: startMarkerType }),
|
|
989
|
+
label: `${fk.columns.join(", ")} → ${fk.referencedColumns.join(", ")}`,
|
|
990
|
+
style: {
|
|
991
|
+
stroke: "#3b82f6",
|
|
992
|
+
strokeWidth: 2.5
|
|
993
|
+
},
|
|
994
|
+
labelStyle: {
|
|
995
|
+
fontSize: 11,
|
|
996
|
+
fill: "#475569",
|
|
997
|
+
fontWeight: 500
|
|
998
|
+
},
|
|
999
|
+
labelBgStyle: {
|
|
1000
|
+
fill: "#ffffff",
|
|
1001
|
+
fillOpacity: .85
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
return edges;
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
var AstToSqlGenerator = class {
|
|
1009
|
+
constructor(dialect = "mysql", indentSize = 2) {
|
|
1010
|
+
_defineProperty(this, "dialect", void 0);
|
|
1011
|
+
_defineProperty(this, "indentSize", void 0);
|
|
1012
|
+
this.dialect = dialect;
|
|
1013
|
+
this.indentSize = indentSize;
|
|
1014
|
+
}
|
|
1015
|
+
generateNoSQL(schema) {
|
|
1016
|
+
return JSON.stringify({ tables: schema.tables }, null, this.indentSize);
|
|
1017
|
+
}
|
|
1018
|
+
generateSQL(schema) {
|
|
1019
|
+
const statements = [];
|
|
1020
|
+
statements.push(`-- Generated SQL Schema (${schema.dialect.toUpperCase()})`);
|
|
1021
|
+
statements.push(`-- Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
1022
|
+
statements.push("");
|
|
1023
|
+
schema.tables.forEach((table) => {
|
|
1024
|
+
const sql = this.generateCreateTable(table);
|
|
1025
|
+
statements.push(sql);
|
|
1026
|
+
statements.push("");
|
|
1027
|
+
});
|
|
1028
|
+
return statements.join("\n");
|
|
1029
|
+
}
|
|
1030
|
+
generate(schema) {
|
|
1031
|
+
switch (this.dialect) {
|
|
1032
|
+
case "dynamodb":
|
|
1033
|
+
case "mongodb":
|
|
1034
|
+
case "json": return this.generateNoSQL(schema);
|
|
1035
|
+
default: return this.generateSQL(schema);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
generateCreateTable(table) {
|
|
1039
|
+
const lines = [];
|
|
1040
|
+
const indent = " ".repeat(this.indentSize);
|
|
1041
|
+
const tableName = this.quoteIdentifier(table.name);
|
|
1042
|
+
lines.push(`CREATE TABLE IF NOT EXISTS ${tableName} (`);
|
|
1043
|
+
const definitions = [];
|
|
1044
|
+
table.columns.forEach((column) => {
|
|
1045
|
+
definitions.push(this.generateColumn(column));
|
|
1046
|
+
});
|
|
1047
|
+
table.constraints.forEach((constraint) => {
|
|
1048
|
+
definitions.push(this.generateConstraint(constraint));
|
|
1049
|
+
});
|
|
1050
|
+
if (this.dialect === "mysql") table.indexes.forEach((index) => {
|
|
1051
|
+
definitions.push(this.generateIndex(index));
|
|
1052
|
+
});
|
|
1053
|
+
lines.push(definitions.map((def) => `${indent}${def}`).join(",\n"));
|
|
1054
|
+
lines.push(")");
|
|
1055
|
+
const options = this.generateTableOptions(table);
|
|
1056
|
+
if (options) lines[lines.length - 1] += ` ${options}`;
|
|
1057
|
+
lines[lines.length - 1] += ";";
|
|
1058
|
+
if (this.dialect === "postgresql") table.indexes.forEach((index) => {
|
|
1059
|
+
lines.push("");
|
|
1060
|
+
lines.push(this.generatePostgresIndex(index, table.name));
|
|
1061
|
+
});
|
|
1062
|
+
return lines.join("\n");
|
|
1063
|
+
}
|
|
1064
|
+
generateColumn(column) {
|
|
1065
|
+
const parts = [];
|
|
1066
|
+
parts.push(this.quoteIdentifier(column.name));
|
|
1067
|
+
parts.push(this.generateDataType(column.dataType));
|
|
1068
|
+
parts.push(column.nullable ? "NULL" : "NOT NULL");
|
|
1069
|
+
if (column.defaultValue) parts.push(`DEFAULT ${this.generateDefaultValue(column.defaultValue)}`);
|
|
1070
|
+
if (column.autoIncrement) {
|
|
1071
|
+
if (this.dialect === "mysql") parts.push("AUTO_INCREMENT");
|
|
1072
|
+
else if (this.dialect === "postgresql") {}
|
|
1073
|
+
}
|
|
1074
|
+
if (column.comment) {
|
|
1075
|
+
if (this.dialect === "mysql") parts.push(`COMMENT '${column.comment.replace(/'/g, "''")}'`);
|
|
1076
|
+
}
|
|
1077
|
+
return parts.join(" ");
|
|
1078
|
+
}
|
|
1079
|
+
generateDataType(dataType) {
|
|
1080
|
+
let type = dataType.name;
|
|
1081
|
+
if (dataType.parameters && dataType.parameters.length > 0) type += `(${dataType.parameters.join(", ")})`;
|
|
1082
|
+
if (dataType.unsigned) type += " UNSIGNED";
|
|
1083
|
+
if (dataType.timezone) type += " WITH TIME ZONE";
|
|
1084
|
+
return type;
|
|
1085
|
+
}
|
|
1086
|
+
generateDefaultValue(defaultValue) {
|
|
1087
|
+
if (defaultValue.type === "literal") {
|
|
1088
|
+
if (defaultValue.value === null) return "NULL";
|
|
1089
|
+
if (typeof defaultValue.value === "string") return `'${defaultValue.value.replace(/'/g, "''")}'`;
|
|
1090
|
+
return String(defaultValue.value);
|
|
1091
|
+
}
|
|
1092
|
+
if (defaultValue.type === "function" || defaultValue.type === "expression") return defaultValue.raw || String(defaultValue.value);
|
|
1093
|
+
return String(defaultValue.value);
|
|
1094
|
+
}
|
|
1095
|
+
generateConstraint(constraint) {
|
|
1096
|
+
switch (constraint.type) {
|
|
1097
|
+
case "primary_key": return this.generatePrimaryKey(constraint);
|
|
1098
|
+
case "foreign_key": return this.generateForeignKey(constraint);
|
|
1099
|
+
case "unique": return this.generateUnique(constraint);
|
|
1100
|
+
case "check": return this.generateCheck(constraint);
|
|
1101
|
+
default: return "";
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
generatePrimaryKey(constraint) {
|
|
1105
|
+
const columns = constraint.columns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
1106
|
+
if (constraint.name) return `CONSTRAINT ${this.quoteIdentifier(constraint.name)} PRIMARY KEY (${columns})`;
|
|
1107
|
+
return `PRIMARY KEY (${columns})`;
|
|
1108
|
+
}
|
|
1109
|
+
generateForeignKey(constraint) {
|
|
1110
|
+
const parts = [];
|
|
1111
|
+
if (constraint.name) parts.push(`CONSTRAINT ${this.quoteIdentifier(constraint.name)}`);
|
|
1112
|
+
const columns = constraint.columns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
1113
|
+
const refTable = this.quoteIdentifier(constraint.referencedTable);
|
|
1114
|
+
const refColumns = constraint.referencedColumns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
1115
|
+
parts.push(`FOREIGN KEY (${columns})`);
|
|
1116
|
+
parts.push(`REFERENCES ${refTable} (${refColumns})`);
|
|
1117
|
+
if (constraint.onDelete) parts.push(`ON DELETE ${constraint.onDelete}`);
|
|
1118
|
+
if (constraint.onUpdate) parts.push(`ON UPDATE ${constraint.onUpdate}`);
|
|
1119
|
+
return parts.join(" ");
|
|
1120
|
+
}
|
|
1121
|
+
generateUnique(constraint) {
|
|
1122
|
+
const columns = constraint.columns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
1123
|
+
if (constraint.name) return `CONSTRAINT ${this.quoteIdentifier(constraint.name)} UNIQUE (${columns})`;
|
|
1124
|
+
return `UNIQUE (${columns})`;
|
|
1125
|
+
}
|
|
1126
|
+
generateCheck(constraint) {
|
|
1127
|
+
if (constraint.name) return `CONSTRAINT ${this.quoteIdentifier(constraint.name)} CHECK (${constraint.expression})`;
|
|
1128
|
+
return `CHECK (${constraint.expression})`;
|
|
1129
|
+
}
|
|
1130
|
+
generateIndex(index) {
|
|
1131
|
+
const parts = [];
|
|
1132
|
+
if (index.unique) parts.push("UNIQUE");
|
|
1133
|
+
parts.push("INDEX");
|
|
1134
|
+
parts.push(this.quoteIdentifier(index.name));
|
|
1135
|
+
const columns = index.columns.map((col) => {
|
|
1136
|
+
let colDef = this.quoteIdentifier(col.name);
|
|
1137
|
+
if (col.length) colDef += `(${col.length})`;
|
|
1138
|
+
if (col.order) colDef += ` ${col.order}`;
|
|
1139
|
+
return colDef;
|
|
1140
|
+
});
|
|
1141
|
+
parts.push(`(${columns.join(", ")})`);
|
|
1142
|
+
if (index.method && this.dialect === "mysql") parts.push(`USING ${index.method.toUpperCase()}`);
|
|
1143
|
+
return parts.join(" ");
|
|
1144
|
+
}
|
|
1145
|
+
generatePostgresIndex(index, tableName) {
|
|
1146
|
+
const parts = [];
|
|
1147
|
+
parts.push("CREATE");
|
|
1148
|
+
if (index.unique) parts.push("UNIQUE");
|
|
1149
|
+
parts.push("INDEX");
|
|
1150
|
+
parts.push(this.quoteIdentifier(index.name));
|
|
1151
|
+
parts.push("ON");
|
|
1152
|
+
parts.push(this.quoteIdentifier(tableName));
|
|
1153
|
+
if (index.method) parts.push(`USING ${index.method.toUpperCase()}`);
|
|
1154
|
+
const columns = index.columns.map((col) => {
|
|
1155
|
+
let colDef = this.quoteIdentifier(col.name);
|
|
1156
|
+
if (col.order) colDef += ` ${col.order}`;
|
|
1157
|
+
return colDef;
|
|
1158
|
+
});
|
|
1159
|
+
parts.push(`(${columns.join(", ")})`);
|
|
1160
|
+
if (index.where) parts.push(`WHERE ${index.where}`);
|
|
1161
|
+
return parts.join(" ") + ";";
|
|
1162
|
+
}
|
|
1163
|
+
generateTableOptions(table) {
|
|
1164
|
+
if (!table.options) return null;
|
|
1165
|
+
const parts = [];
|
|
1166
|
+
if (this.dialect === "mysql") {
|
|
1167
|
+
if (table.options.engine) parts.push(`ENGINE=${table.options.engine}`);
|
|
1168
|
+
if (table.options.charset) parts.push(`CHARACTER SET ${table.options.charset}`);
|
|
1169
|
+
if (table.options.collation) parts.push(`COLLATE ${table.options.collation}`);
|
|
1170
|
+
if (table.options.autoIncrement) parts.push(`AUTO_INCREMENT=${table.options.autoIncrement}`);
|
|
1171
|
+
if (table.options.comment) parts.push(`COMMENT='${table.options.comment.replace(/'/g, "''")}'`);
|
|
1172
|
+
}
|
|
1173
|
+
if (this.dialect === "postgresql") {
|
|
1174
|
+
if (table.options.tablespace) parts.push(`TABLESPACE ${table.options.tablespace}`);
|
|
1175
|
+
}
|
|
1176
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
1177
|
+
}
|
|
1178
|
+
quoteIdentifier(identifier) {
|
|
1179
|
+
switch (this.dialect) {
|
|
1180
|
+
case "mysql": return `\`${identifier}\``;
|
|
1181
|
+
case "postgresql": return `"${identifier}"`;
|
|
1182
|
+
case "sqlite": return `"${identifier}"`;
|
|
1183
|
+
default: return identifier;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
var SqlToAstParser = class {
|
|
1188
|
+
constructor(dialect = "mysql") {
|
|
1189
|
+
_defineProperty(this, "dialect", void 0);
|
|
1190
|
+
this.dialect = dialect;
|
|
1191
|
+
}
|
|
1192
|
+
parseSQL(sqlContent) {
|
|
1193
|
+
const tables = this.extractTables(sqlContent);
|
|
1194
|
+
return {
|
|
1195
|
+
dialect: this.dialect,
|
|
1196
|
+
tables,
|
|
1197
|
+
metadata: { createdAt: /* @__PURE__ */ new Date() }
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
parseNoSQL(noSQLJsonContent) {
|
|
1201
|
+
const parsed = JSON.parse(noSQLJsonContent || "{}");
|
|
1202
|
+
const existingTables = Array.isArray(parsed.tables) ? parsed.tables : null;
|
|
1203
|
+
if (existingTables && existingTables.length > 0) return {
|
|
1204
|
+
dialect: this.dialect,
|
|
1205
|
+
tables: existingTables,
|
|
1206
|
+
metadata: { createdAt: /* @__PURE__ */ new Date() }
|
|
1207
|
+
};
|
|
1208
|
+
const tables = (Array.isArray(parsed.collections) ? parsed.collections : []).map((collection) => {
|
|
1209
|
+
if (!collection || !collection.name) return null;
|
|
1210
|
+
const fields = collection.fields && typeof collection.fields === "object" ? collection.fields : {};
|
|
1211
|
+
const columns = Object.entries(fields).map(([name, type]) => ({
|
|
1212
|
+
type: "column",
|
|
1213
|
+
name,
|
|
1214
|
+
dataType: this.parseNoSqlType(String(type || "string")),
|
|
1215
|
+
nullable: true
|
|
1216
|
+
}));
|
|
1217
|
+
return {
|
|
1218
|
+
type: "table",
|
|
1219
|
+
id: generateUUID$1(),
|
|
1220
|
+
name: collection.name,
|
|
1221
|
+
columns,
|
|
1222
|
+
constraints: [],
|
|
1223
|
+
indexes: []
|
|
1224
|
+
};
|
|
1225
|
+
}).filter(Boolean);
|
|
1226
|
+
return {
|
|
1227
|
+
dialect: this.dialect,
|
|
1228
|
+
tables,
|
|
1229
|
+
metadata: { createdAt: /* @__PURE__ */ new Date() }
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
parse(sqlContent, useStrictNoSQL = false) {
|
|
1233
|
+
switch (this.dialect) {
|
|
1234
|
+
case "json":
|
|
1235
|
+
case "mongodb":
|
|
1236
|
+
case "dynamodb":
|
|
1237
|
+
if (useStrictNoSQL) return convertNoSQLToAst(JSON.parse(sqlContent));
|
|
1238
|
+
return this.parseNoSQL(sqlContent);
|
|
1239
|
+
default: return this.parseSQL(sqlContent);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
static detectDialect(sqlContent) {
|
|
1243
|
+
const content = sqlContent.toLowerCase();
|
|
1244
|
+
try {
|
|
1245
|
+
JSON.parse(sqlContent);
|
|
1246
|
+
return "json";
|
|
1247
|
+
} catch (_unused) {}
|
|
1248
|
+
if (content.includes("serial") || content.includes("bigserial") || content.includes("::") || content.includes("nextval(") || content.includes("create schema")) return "postgresql";
|
|
1249
|
+
if (content.includes("engine=") || content.includes("auto_increment") || content.includes("unsigned") || content.includes("character set") || content.includes("collate")) return "mysql";
|
|
1250
|
+
return "mysql";
|
|
1251
|
+
}
|
|
1252
|
+
extractTables(sqlContent) {
|
|
1253
|
+
const tables = [];
|
|
1254
|
+
const tableMap = /* @__PURE__ */ new Map();
|
|
1255
|
+
const cleanedSQL = this.cleanSql(sqlContent);
|
|
1256
|
+
const createTableRegex = /CREATE\s+TABLE[\s\S]*?;/gi;
|
|
1257
|
+
let match;
|
|
1258
|
+
while ((match = createTableRegex.exec(cleanedSQL)) !== null) {
|
|
1259
|
+
const statement = match[0];
|
|
1260
|
+
const components = this.extractCreateTableComponents(statement);
|
|
1261
|
+
if (!components) continue;
|
|
1262
|
+
const { rawIdentifier, tableName, tableBody, tableOptions } = components;
|
|
1263
|
+
const tableKey = this.normalizeTableKey(rawIdentifier);
|
|
1264
|
+
if (tableMap.has(tableKey)) continue;
|
|
1265
|
+
try {
|
|
1266
|
+
const table = this.parseTableDefinition(tableName, tableBody, tableOptions);
|
|
1267
|
+
tables.push(table);
|
|
1268
|
+
tableMap.set(tableKey, table);
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
console.error("Error parsing table:", error);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
this.applyAlterTableStatements(cleanedSQL, tableMap);
|
|
1274
|
+
return tables;
|
|
1275
|
+
}
|
|
1276
|
+
extractCreateTableComponents(statement) {
|
|
1277
|
+
const trimmedStatement = statement.trim();
|
|
1278
|
+
const headerMatch = /^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:`[^`]+`|"[^"]+"|'[^']+'|\[[^\]]+\]|\w+)(?:\.(?:`[^`]+`|"[^"]+"|'[^']+'|\[[^\]]+\]|\w+))?)\s*\(/i.exec(trimmedStatement);
|
|
1279
|
+
if (!headerMatch) return null;
|
|
1280
|
+
const rawIdentifier = headerMatch[1];
|
|
1281
|
+
const openParenIndex = headerMatch[0].length - 1;
|
|
1282
|
+
const closeParenIndex = this.findClosingParenthesis(trimmedStatement, openParenIndex);
|
|
1283
|
+
if (closeParenIndex === -1) return null;
|
|
1284
|
+
const body = trimmedStatement.slice(openParenIndex + 1, closeParenIndex);
|
|
1285
|
+
const options = trimmedStatement.slice(closeParenIndex + 1, trimmedStatement.length - 1).trim();
|
|
1286
|
+
return {
|
|
1287
|
+
rawIdentifier,
|
|
1288
|
+
tableName: this.normalizeTableName(rawIdentifier),
|
|
1289
|
+
tableBody: body,
|
|
1290
|
+
tableOptions: options
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
findClosingParenthesis(str, startIndex) {
|
|
1294
|
+
let depth = 0;
|
|
1295
|
+
let inQuotes = false;
|
|
1296
|
+
let quoteChar = "";
|
|
1297
|
+
for (let i = startIndex; i < str.length; i++) {
|
|
1298
|
+
const char = str[i];
|
|
1299
|
+
const prevChar = i > 0 ? str[i - 1] : "";
|
|
1300
|
+
if ((char === "\"" || char === "'" || char === "`") && prevChar !== "\\") {
|
|
1301
|
+
if (!inQuotes) {
|
|
1302
|
+
inQuotes = true;
|
|
1303
|
+
quoteChar = char;
|
|
1304
|
+
} else if (char === quoteChar) {
|
|
1305
|
+
inQuotes = false;
|
|
1306
|
+
quoteChar = "";
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (inQuotes) continue;
|
|
1310
|
+
if (char === "(") depth++;
|
|
1311
|
+
else if (char === ")") {
|
|
1312
|
+
depth--;
|
|
1313
|
+
if (depth === 0) return i;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return -1;
|
|
1317
|
+
}
|
|
1318
|
+
applyAlterTableStatements(sqlContent, tableMap) {
|
|
1319
|
+
const alterRegex = /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?((?:`[^`]+`|"[^"]+"|'[^']+'|\[[^\]]+\]|[\w$]+)(?:\.(?:`[^`]+`|"[^"]+"|'[^']+'|\[[^\]]+\]|[\w$]+))?)\s+([\s\S]*?);/gi;
|
|
1320
|
+
let match;
|
|
1321
|
+
while ((match = alterRegex.exec(sqlContent)) !== null) {
|
|
1322
|
+
const rawIdentifier = match[1];
|
|
1323
|
+
const actionsBlock = match[2] || "";
|
|
1324
|
+
if (!rawIdentifier) continue;
|
|
1325
|
+
const table = tableMap.get(this.normalizeTableKey(rawIdentifier));
|
|
1326
|
+
if (!table) continue;
|
|
1327
|
+
const actions = this.splitTableBody(actionsBlock);
|
|
1328
|
+
for (const action of actions) {
|
|
1329
|
+
const trimmed = action.trim();
|
|
1330
|
+
if (!trimmed) continue;
|
|
1331
|
+
if (/^ADD\s+/i.test(trimmed)) {
|
|
1332
|
+
const definition = trimmed.replace(/^ADD\s+/i, "").trim();
|
|
1333
|
+
if (!definition) continue;
|
|
1334
|
+
const constraint = this.parseConstraint(definition);
|
|
1335
|
+
if (constraint) {
|
|
1336
|
+
table.constraints.push(constraint);
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
const index = this.parseIndex(definition);
|
|
1340
|
+
if (index) table.indexes.push(index);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
cleanSql(sql) {
|
|
1346
|
+
return sql.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\r\n/g, "\n").trim();
|
|
1347
|
+
}
|
|
1348
|
+
cleanIdentifier(identifier) {
|
|
1349
|
+
return identifier.replace(/[`"'\[\]]/g, "").trim();
|
|
1350
|
+
}
|
|
1351
|
+
normalizeTableName(identifier) {
|
|
1352
|
+
const cleaned = this.cleanIdentifier(identifier);
|
|
1353
|
+
if (!cleaned) return "";
|
|
1354
|
+
const parts = cleaned.split(".");
|
|
1355
|
+
return parts[parts.length - 1];
|
|
1356
|
+
}
|
|
1357
|
+
normalizeTableKey(identifier) {
|
|
1358
|
+
return this.cleanIdentifier(identifier).toLowerCase();
|
|
1359
|
+
}
|
|
1360
|
+
parseNoSqlType(rawType) {
|
|
1361
|
+
const trimmed = rawType.trim();
|
|
1362
|
+
const lower = trimmed.toLowerCase();
|
|
1363
|
+
if (lower.startsWith("array<") && lower.endsWith(">")) return { name: `array<${trimmed.slice(6, -1).trim() || "string"}>` };
|
|
1364
|
+
return { name: trimmed || "string" };
|
|
1365
|
+
}
|
|
1366
|
+
parseTableDefinition(tableName, tableBody, tableOptionsStr) {
|
|
1367
|
+
const columns = [];
|
|
1368
|
+
const constraints = [];
|
|
1369
|
+
const indexes = [];
|
|
1370
|
+
const pendingColumns = [];
|
|
1371
|
+
const lines = this.splitTableBody(tableBody);
|
|
1372
|
+
for (const line of lines) {
|
|
1373
|
+
const trimmedLine = line.trim();
|
|
1374
|
+
if (!trimmedLine) continue;
|
|
1375
|
+
try {
|
|
1376
|
+
const tableLevelPattern = /^(?:CONSTRAINT|PRIMARY\s+KEY|FOREIGN\s+KEY|UNIQUE|CHECK|INDEX|KEY)\b/i;
|
|
1377
|
+
let column = null;
|
|
1378
|
+
if (!tableLevelPattern.test(trimmedLine)) {
|
|
1379
|
+
column = this.parseColumn(trimmedLine);
|
|
1380
|
+
if (column) {
|
|
1381
|
+
pendingColumns.push({
|
|
1382
|
+
column,
|
|
1383
|
+
hasType: !!column.dataType.name
|
|
1384
|
+
});
|
|
1385
|
+
this.applyInlineColumnConstraints(trimmedLine, column, constraints);
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
const constraint = this.parseConstraint(trimmedLine);
|
|
1390
|
+
if (constraint) {
|
|
1391
|
+
constraints.push(constraint);
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
const index = this.parseIndex(trimmedLine);
|
|
1395
|
+
if (index) {
|
|
1396
|
+
indexes.push(index);
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
if (!column) column = this.parseColumn(trimmedLine);
|
|
1400
|
+
if (column) {
|
|
1401
|
+
pendingColumns.push({
|
|
1402
|
+
column,
|
|
1403
|
+
hasType: !!column.dataType.name
|
|
1404
|
+
});
|
|
1405
|
+
this.applyInlineColumnConstraints(trimmedLine, column, constraints);
|
|
1406
|
+
}
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
console.warn("Error parsing line:", trimmedLine, error);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
const typedColumnsCount = pendingColumns.filter((c) => c.hasType).length;
|
|
1412
|
+
pendingColumns.forEach((entry, index) => {
|
|
1413
|
+
if (!entry.hasType) {
|
|
1414
|
+
const hasTypedAfter = pendingColumns.slice(index + 1).some((c) => c.hasType);
|
|
1415
|
+
if (!(typedColumnsCount < 4 && hasTypedAfter)) return;
|
|
1416
|
+
}
|
|
1417
|
+
columns.push(entry.column);
|
|
1418
|
+
});
|
|
1419
|
+
const options = this.parseTableOptions(tableOptionsStr);
|
|
1420
|
+
return {
|
|
1421
|
+
type: "table",
|
|
1422
|
+
id: generateUUID$1(),
|
|
1423
|
+
name: tableName,
|
|
1424
|
+
columns,
|
|
1425
|
+
constraints,
|
|
1426
|
+
indexes,
|
|
1427
|
+
options
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
splitTableBody(body) {
|
|
1431
|
+
const lines = [];
|
|
1432
|
+
let currentLine = "";
|
|
1433
|
+
let parenthesesDepth = 0;
|
|
1434
|
+
let inQuotes = false;
|
|
1435
|
+
let quoteChar = "";
|
|
1436
|
+
for (let i = 0; i < body.length; i++) {
|
|
1437
|
+
const char = body[i];
|
|
1438
|
+
const prevChar = i > 0 ? body[i - 1] : "";
|
|
1439
|
+
if ((char === "\"" || char === "'" || char === "`") && prevChar !== "\\") {
|
|
1440
|
+
if (!inQuotes) {
|
|
1441
|
+
inQuotes = true;
|
|
1442
|
+
quoteChar = char;
|
|
1443
|
+
} else if (char === quoteChar) {
|
|
1444
|
+
inQuotes = false;
|
|
1445
|
+
quoteChar = "";
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (!inQuotes) {
|
|
1449
|
+
if (char === "(") parenthesesDepth++;
|
|
1450
|
+
else if (char === ")") parenthesesDepth--;
|
|
1451
|
+
}
|
|
1452
|
+
if (char === "," && parenthesesDepth === 0 && !inQuotes) {
|
|
1453
|
+
lines.push(currentLine.trim());
|
|
1454
|
+
currentLine = "";
|
|
1455
|
+
} else currentLine += char;
|
|
1456
|
+
}
|
|
1457
|
+
if (currentLine.trim()) lines.push(currentLine.trim());
|
|
1458
|
+
return lines;
|
|
1459
|
+
}
|
|
1460
|
+
parseColumn(line) {
|
|
1461
|
+
var _ref, _ref2, _ref3, _ref4, _match$, _match$2;
|
|
1462
|
+
const match = line.match(/^(?:`([^`]+)`|"([^"]+)"|'([^']+)'|\[([^\]]+)\]|([a-zA-Z_][\w$]*))\s+([\s\S]+)$/i);
|
|
1463
|
+
if (!match) return null;
|
|
1464
|
+
const rawName = (_ref = (_ref2 = (_ref3 = (_ref4 = (_match$ = match[1]) !== null && _match$ !== void 0 ? _match$ : match[2]) !== null && _ref4 !== void 0 ? _ref4 : match[3]) !== null && _ref3 !== void 0 ? _ref3 : match[4]) !== null && _ref2 !== void 0 ? _ref2 : match[5]) !== null && _ref !== void 0 ? _ref : "";
|
|
1465
|
+
if (!rawName) return null;
|
|
1466
|
+
const definition = (_match$2 = match[6]) === null || _match$2 === void 0 ? void 0 : _match$2.trim().replace(/,+$/, "");
|
|
1467
|
+
if (!definition) return null;
|
|
1468
|
+
const { dataTypeStr, constraintsStr } = this.splitColumnDefinition(definition);
|
|
1469
|
+
const constraintsSegment = constraintsStr || "";
|
|
1470
|
+
const dataType = this.parseDataType(dataTypeStr);
|
|
1471
|
+
let nullable = !/NOT\s+NULL/i.test(constraintsSegment);
|
|
1472
|
+
const autoIncrement = /AUTO_INCREMENT/i.test(constraintsSegment) || /AUTOINCREMENT/i.test(constraintsSegment) || /(SERIAL|BIGSERIAL)/i.test(dataTypeStr);
|
|
1473
|
+
if (autoIncrement || /\bPRIMARY\s+KEY\b/i.test(constraintsSegment)) nullable = false;
|
|
1474
|
+
let defaultValue;
|
|
1475
|
+
const defaultMatch = constraintsSegment.match(/DEFAULT\s+((?:'[^']*'|"[^"]*"|\([^)]*\)|[^\s,]+))/i);
|
|
1476
|
+
if (defaultMatch) defaultValue = this.parseDefaultValue(defaultMatch[1]);
|
|
1477
|
+
let comment;
|
|
1478
|
+
const commentMatch = constraintsSegment.match(/COMMENT\s+['"]([^'"]+)['"]/i);
|
|
1479
|
+
if (commentMatch) comment = commentMatch[1];
|
|
1480
|
+
const column = {
|
|
1481
|
+
type: "column",
|
|
1482
|
+
name: this.cleanIdentifier(rawName),
|
|
1483
|
+
dataType,
|
|
1484
|
+
nullable,
|
|
1485
|
+
defaultValue,
|
|
1486
|
+
autoIncrement,
|
|
1487
|
+
comment
|
|
1488
|
+
};
|
|
1489
|
+
const collateMatch = constraintsSegment.match(/COLLATE\s+([^\s,]+)/i) || line.match(/COLLATE\s+([^\s,]+)/i);
|
|
1490
|
+
if (collateMatch) column.collation = this.cleanIdentifier(collateMatch[1]);
|
|
1491
|
+
const charsetMatch = constraintsSegment.match(/CHARACTER\s+SET\s+([^\s,]+)/i) || constraintsSegment.match(/CHARSET\s*=?\s*([^\s,]+)/i) || line.match(/CHARACTER\s+SET\s+([^\s,]+)/i) || line.match(/CHARSET\s*=?\s*([^\s,]+)/i);
|
|
1492
|
+
if (charsetMatch) column.charset = this.cleanIdentifier(charsetMatch[1]);
|
|
1493
|
+
return column;
|
|
1494
|
+
}
|
|
1495
|
+
splitColumnDefinition(definition) {
|
|
1496
|
+
const match = /\b(?:GENERATED|DEFAULT|NOT\s+NULL|NULL|UNIQUE|PRIMARY\s+KEY|CHECK|REFERENCES|COLLATE|COMMENT|AUTO(?:_|\s*)INCREMENT|ON\s+UPDATE|ON\s+DELETE|CONSTRAINT)\b/i.exec(definition);
|
|
1497
|
+
if (!match) return {
|
|
1498
|
+
dataTypeStr: definition.trim(),
|
|
1499
|
+
constraintsStr: ""
|
|
1500
|
+
};
|
|
1501
|
+
return {
|
|
1502
|
+
dataTypeStr: definition.slice(0, match.index).trim(),
|
|
1503
|
+
constraintsStr: definition.slice(match.index).trim()
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
applyInlineColumnConstraints(line, column, constraints) {
|
|
1507
|
+
const columnName = column.name;
|
|
1508
|
+
if (/\bPRIMARY\s+KEY\b/i.test(line)) {
|
|
1509
|
+
const nameMatch = line.match(/CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?.*PRIMARY\s+KEY/i);
|
|
1510
|
+
const name = nameMatch ? this.cleanIdentifier(nameMatch[1]) : void 0;
|
|
1511
|
+
const existing = constraints.find((c) => c.type === "primary_key");
|
|
1512
|
+
if (existing) {
|
|
1513
|
+
if (!existing.columns.includes(columnName)) existing.columns.push(columnName);
|
|
1514
|
+
} else constraints.push({
|
|
1515
|
+
type: "primary_key",
|
|
1516
|
+
name,
|
|
1517
|
+
columns: [columnName]
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
if (/\bUNIQUE\b/i.test(line) && !/\bUNIQUE\s+(?:KEY|INDEX)\b/i.test(line)) {
|
|
1521
|
+
const nameMatch = line.match(/CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?.*UNIQUE/i);
|
|
1522
|
+
const name = nameMatch ? this.cleanIdentifier(nameMatch[1]) : void 0;
|
|
1523
|
+
if (!constraints.some((c) => c.type === "unique" && c.columns.length === 1 && c.columns[0] === columnName)) constraints.push({
|
|
1524
|
+
type: "unique",
|
|
1525
|
+
name,
|
|
1526
|
+
columns: [columnName]
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
const referencesMatch = line.match(/REFERENCES\s+([^\s(]+)\s*\(([^)]+)\)/i);
|
|
1530
|
+
if (referencesMatch) {
|
|
1531
|
+
const nameMatch = line.match(/CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?.*FOREIGN\s+KEY/i);
|
|
1532
|
+
const name = nameMatch ? this.cleanIdentifier(nameMatch[1]) : void 0;
|
|
1533
|
+
const referencedTable = this.normalizeTableName(referencesMatch[1]);
|
|
1534
|
+
const referencedColumns = referencesMatch[2].split(",").map((c) => this.cleanIdentifier(c.trim()));
|
|
1535
|
+
const onDelete = this.extractReferentialAction(line, "DELETE");
|
|
1536
|
+
const onUpdate = this.extractReferentialAction(line, "UPDATE");
|
|
1537
|
+
if (!constraints.some((c) => c.type === "foreign_key" && c.columns.length === 1 && c.columns[0] === columnName && c.referencedTable === referencedTable)) constraints.push({
|
|
1538
|
+
type: "foreign_key",
|
|
1539
|
+
name,
|
|
1540
|
+
columns: [columnName],
|
|
1541
|
+
referencedTable,
|
|
1542
|
+
referencedColumns,
|
|
1543
|
+
onDelete,
|
|
1544
|
+
onUpdate
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
if (/CHECK\s*\(/i.test(line)) {
|
|
1548
|
+
const check = this.parseCheck(line);
|
|
1549
|
+
if (check && !constraints.some((c) => c.type === "check" && c.expression === check.expression)) constraints.push(check);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
parseDataType(dataTypeStr) {
|
|
1553
|
+
const match = dataTypeStr.match(/^(\w+)(?:\(([^)]+)\))?(.*)?$/i);
|
|
1554
|
+
if (!match) return { name: dataTypeStr.trim().toUpperCase() };
|
|
1555
|
+
const name = match[1].toUpperCase();
|
|
1556
|
+
const paramsStr = match[2];
|
|
1557
|
+
const modifiers = match[3] || "";
|
|
1558
|
+
const dataType = { name };
|
|
1559
|
+
if (paramsStr) dataType.parameters = paramsStr.split(",").map((p) => {
|
|
1560
|
+
const trimmed = p.trim();
|
|
1561
|
+
const num = Number(trimmed);
|
|
1562
|
+
return isNaN(num) ? trimmed : num;
|
|
1563
|
+
});
|
|
1564
|
+
dataType.unsigned = /UNSIGNED/i.test(modifiers);
|
|
1565
|
+
dataType.timezone = /WITH TIME ZONE/i.test(modifiers);
|
|
1566
|
+
return dataType;
|
|
1567
|
+
}
|
|
1568
|
+
parseDefaultValue(value) {
|
|
1569
|
+
const trimmed = value.trim();
|
|
1570
|
+
if (/^NULL$/i.test(trimmed)) return {
|
|
1571
|
+
type: "literal",
|
|
1572
|
+
value: null,
|
|
1573
|
+
raw: trimmed
|
|
1574
|
+
};
|
|
1575
|
+
if (/\w+\(.*\)/i.test(trimmed)) return {
|
|
1576
|
+
type: "function",
|
|
1577
|
+
value: trimmed,
|
|
1578
|
+
raw: trimmed
|
|
1579
|
+
};
|
|
1580
|
+
if (/^['"].*['"]$/.test(trimmed)) return {
|
|
1581
|
+
type: "literal",
|
|
1582
|
+
value: trimmed.slice(1, -1),
|
|
1583
|
+
raw: trimmed
|
|
1584
|
+
};
|
|
1585
|
+
const num = Number(trimmed);
|
|
1586
|
+
if (!isNaN(num)) return {
|
|
1587
|
+
type: "literal",
|
|
1588
|
+
value: num,
|
|
1589
|
+
raw: trimmed
|
|
1590
|
+
};
|
|
1591
|
+
return {
|
|
1592
|
+
type: "expression",
|
|
1593
|
+
value: trimmed,
|
|
1594
|
+
raw: trimmed
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
parseConstraint(line) {
|
|
1598
|
+
if (/PRIMARY\s+KEY/i.test(line)) return this.parsePrimaryKey(line);
|
|
1599
|
+
if (/FOREIGN\s+KEY/i.test(line)) return this.parseForeignKey(line);
|
|
1600
|
+
if (/UNIQUE/i.test(line) && !/INDEX|KEY/i.test(line)) return this.parseUnique(line);
|
|
1601
|
+
if (/CHECK/i.test(line)) return this.parseCheck(line);
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
parsePrimaryKey(line) {
|
|
1605
|
+
const match = line.match(/(?:CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?\s+)?PRIMARY\s+KEY\s*\(([^)]+)\)/i);
|
|
1606
|
+
if (!match) return null;
|
|
1607
|
+
return {
|
|
1608
|
+
type: "primary_key",
|
|
1609
|
+
name: match[1] ? this.cleanIdentifier(match[1]) : void 0,
|
|
1610
|
+
columns: match[2].split(",").map((c) => this.cleanIdentifier(c.trim()))
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
parseForeignKey(line) {
|
|
1614
|
+
const match = line.match(/(?:CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?\s+)?FOREIGN\s+KEY\s*\(([^)]+)\)\s*REFERENCES\s+((?:["'`]?[\w$]+["'`"]?|\[[^\]]+\])(?:\.(?:["'`]?[\w$]+["'`"]?|\[[^\]]+\]))?)\s*\(([^)]+)\)/i);
|
|
1615
|
+
if (!match) return null;
|
|
1616
|
+
return {
|
|
1617
|
+
type: "foreign_key",
|
|
1618
|
+
name: match[1] ? this.cleanIdentifier(match[1]) : void 0,
|
|
1619
|
+
columns: match[2].split(",").map((c) => this.cleanIdentifier(c.trim())),
|
|
1620
|
+
referencedTable: this.normalizeTableName(match[3]),
|
|
1621
|
+
referencedColumns: match[4].split(",").map((c) => this.cleanIdentifier(c.trim())),
|
|
1622
|
+
onDelete: this.extractReferentialAction(line, "DELETE"),
|
|
1623
|
+
onUpdate: this.extractReferentialAction(line, "UPDATE")
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
extractReferentialAction(segment, actionType) {
|
|
1627
|
+
const regex = new RegExp(`ON\\s+${actionType}\\s+([A-Z\\s]+?)(?=\\s+ON\\s+(?:DELETE|UPDATE)\\b|,|\\)|$)`, "i");
|
|
1628
|
+
const match = segment.match(regex);
|
|
1629
|
+
if (!match) return void 0;
|
|
1630
|
+
const normalized = match[1].trim().replace(/\s+/g, " ").toUpperCase();
|
|
1631
|
+
if (normalized === "CASCADE" || normalized === "SET NULL" || normalized === "SET DEFAULT" || normalized === "RESTRICT" || normalized === "NO ACTION") return normalized;
|
|
1632
|
+
}
|
|
1633
|
+
parseUnique(line) {
|
|
1634
|
+
const match = line.match(/(?:CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?\s+)?UNIQUE\s*\(([^)]+)\)/i);
|
|
1635
|
+
if (!match) return null;
|
|
1636
|
+
return {
|
|
1637
|
+
type: "unique",
|
|
1638
|
+
name: match[1] ? this.cleanIdentifier(match[1]) : void 0,
|
|
1639
|
+
columns: match[2].split(",").map((c) => this.cleanIdentifier(c.trim()))
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
parseCheck(line) {
|
|
1643
|
+
const match = line.match(/(?:CONSTRAINT\s+["'`]?([^"'`\s]+)["'`"]?\s+)?CHECK\s*\(([\s\S]*?)\)/i);
|
|
1644
|
+
if (!match) return null;
|
|
1645
|
+
return {
|
|
1646
|
+
type: "check",
|
|
1647
|
+
name: match[1] ? this.cleanIdentifier(match[1]) : void 0,
|
|
1648
|
+
expression: match[2].trim()
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
parseIndex(line) {
|
|
1652
|
+
var _match$3;
|
|
1653
|
+
const match = line.match(/(UNIQUE\s+)?(?:INDEX|KEY)\s+["'`]?([^"'`\s]+)["'`"]?\s*\(([^)]+)\)(?:\s+USING\s+(\w+))?/i);
|
|
1654
|
+
if (!match) return null;
|
|
1655
|
+
const unique = !!match[1];
|
|
1656
|
+
const name = this.cleanIdentifier(match[2]);
|
|
1657
|
+
const columnsStr = match[3];
|
|
1658
|
+
const method = (_match$3 = match[4]) === null || _match$3 === void 0 ? void 0 : _match$3.toLowerCase();
|
|
1659
|
+
return {
|
|
1660
|
+
type: "index",
|
|
1661
|
+
name,
|
|
1662
|
+
columns: columnsStr.split(",").map((col) => {
|
|
1663
|
+
var _ref5, _ref6, _ref7, _ref8, _colMatch$, _colMatch$2;
|
|
1664
|
+
const colMatch = col.trim().match(/^(?:`([^`]+)`|"([^"]+)"|'([^']+)'|\[([^\]]+)\]|([^\s(]+))(?:\((\d+)\))?(?:\s+(ASC|DESC))?$/i);
|
|
1665
|
+
if (!colMatch) return { name: this.cleanIdentifier(col.trim()) };
|
|
1666
|
+
return {
|
|
1667
|
+
name: this.cleanIdentifier((_ref5 = (_ref6 = (_ref7 = (_ref8 = (_colMatch$ = colMatch[1]) !== null && _colMatch$ !== void 0 ? _colMatch$ : colMatch[2]) !== null && _ref8 !== void 0 ? _ref8 : colMatch[3]) !== null && _ref7 !== void 0 ? _ref7 : colMatch[4]) !== null && _ref6 !== void 0 ? _ref6 : colMatch[5]) !== null && _ref5 !== void 0 ? _ref5 : ""),
|
|
1668
|
+
length: colMatch[6] ? Number(colMatch[6]) : void 0,
|
|
1669
|
+
order: (_colMatch$2 = colMatch[7]) === null || _colMatch$2 === void 0 ? void 0 : _colMatch$2.toUpperCase()
|
|
1670
|
+
};
|
|
1671
|
+
}),
|
|
1672
|
+
unique,
|
|
1673
|
+
method
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
parseTableOptions(optionsStr) {
|
|
1677
|
+
if (!optionsStr.trim()) return void 0;
|
|
1678
|
+
const options = {};
|
|
1679
|
+
const engineMatch = optionsStr.match(/ENGINE\s*=\s*(\w+)/i);
|
|
1680
|
+
if (engineMatch) options.engine = engineMatch[1];
|
|
1681
|
+
const charsetMatch = optionsStr.match(/(?:DEFAULT\s+)?(?:CHARACTER\s+SET|CHARSET)\s*=?\s*([^\s;]+)/i);
|
|
1682
|
+
if (charsetMatch) options.charset = this.cleanIdentifier(charsetMatch[1]);
|
|
1683
|
+
const collateMatch = optionsStr.match(/COLLATE\s*=?\s*([^\s;]+)/i);
|
|
1684
|
+
if (collateMatch) options.collation = this.cleanIdentifier(collateMatch[1]);
|
|
1685
|
+
const autoIncMatch = optionsStr.match(/AUTO_INCREMENT\s*=\s*(\d+)/i);
|
|
1686
|
+
if (autoIncMatch) options.autoIncrement = Number(autoIncMatch[1]);
|
|
1687
|
+
const commentMatch = optionsStr.match(/COMMENT\s*=\s*['"]([^'"]+)['"]/i);
|
|
1688
|
+
if (commentMatch) options.comment = commentMatch[1];
|
|
1689
|
+
return Object.keys(options).length > 0 ? options : void 0;
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
1692
|
+
function mergeComponentFields(prev, next) {
|
|
1693
|
+
if (prev === void 0 && next === void 0) return;
|
|
1694
|
+
const prevFields = prev !== null && prev !== void 0 ? prev : [];
|
|
1695
|
+
const nextFields = next !== null && next !== void 0 ? next : [];
|
|
1696
|
+
const prevFieldsMap = getFieldDataMap(prevFields);
|
|
1697
|
+
const updated = nextFields.map((field) => {
|
|
1698
|
+
const prevData = prevFieldsMap[field.label];
|
|
1699
|
+
if (!prevData) return field;
|
|
1700
|
+
return _objectSpread2(_objectSpread2({}, field), {}, { data: prevData });
|
|
1701
|
+
});
|
|
1702
|
+
const missingFields = prevFields.filter((field) => !nextFields.some((f) => f.label === field.label));
|
|
1703
|
+
return [...updated, ...missingFields];
|
|
1704
|
+
}
|
|
1705
|
+
function getFieldDataMap(fields) {
|
|
1706
|
+
const result = {};
|
|
1707
|
+
for (const field of fields) result[field.label] = field.data;
|
|
1708
|
+
return result;
|
|
1709
|
+
}
|
|
1710
|
+
function syncBaseData(prev, next, options) {
|
|
1711
|
+
const updated = {
|
|
1712
|
+
nodes: next.nodes.map((nextNode) => {
|
|
1713
|
+
var _prevNode$width, _prevNode$height, _prevNode$position, _prevNode$data, _nextNode$data;
|
|
1714
|
+
const prevNode = prev.nodes.find((n) => n.id === nextNode.id);
|
|
1715
|
+
if (!prevNode) return nextNode;
|
|
1716
|
+
return _objectSpread2(_objectSpread2(_objectSpread2({}, prevNode), nextNode), {}, {
|
|
1717
|
+
width: (options === null || options === void 0 ? void 0 : options.includeNodeDimensions) ? (_prevNode$width = prevNode.width) !== null && _prevNode$width !== void 0 ? _prevNode$width : nextNode.width : nextNode.width,
|
|
1718
|
+
height: (options === null || options === void 0 ? void 0 : options.includeNodeDimensions) ? (_prevNode$height = prevNode.height) !== null && _prevNode$height !== void 0 ? _prevNode$height : nextNode.height : nextNode.height,
|
|
1719
|
+
position: (options === null || options === void 0 ? void 0 : options.includeNodePositions) ? (_prevNode$position = prevNode.position) !== null && _prevNode$position !== void 0 ? _prevNode$position : nextNode.position : nextNode.position,
|
|
1720
|
+
data: _objectSpread2(_objectSpread2(_objectSpread2({}, prevNode.data), nextNode.data), {}, { componentFields: mergeComponentFields((_prevNode$data = prevNode.data) === null || _prevNode$data === void 0 ? void 0 : _prevNode$data.componentFields, (_nextNode$data = nextNode.data) === null || _nextNode$data === void 0 ? void 0 : _nextNode$data.componentFields) })
|
|
1721
|
+
});
|
|
1722
|
+
}),
|
|
1723
|
+
edges: next.edges.map((nextEdge) => {
|
|
1724
|
+
var _prevEdge$data, _nextEdge$data;
|
|
1725
|
+
const prevEdge = prev.edges.find((e) => e.id === nextEdge.id);
|
|
1726
|
+
if (!prevEdge) return nextEdge;
|
|
1727
|
+
return _objectSpread2(_objectSpread2(_objectSpread2({}, prevEdge), nextEdge), {}, { data: _objectSpread2(_objectSpread2(_objectSpread2({}, prevEdge.data), nextEdge.data), {}, { componentFields: mergeComponentFields((_prevEdge$data = prevEdge.data) === null || _prevEdge$data === void 0 ? void 0 : _prevEdge$data.componentFields, (_nextEdge$data = nextEdge.data) === null || _nextEdge$data === void 0 ? void 0 : _nextEdge$data.componentFields) }) });
|
|
1728
|
+
})
|
|
1729
|
+
};
|
|
1730
|
+
if (options === null || options === void 0 ? void 0 : options.keepPrev) {
|
|
1731
|
+
const missingNodes = prev.nodes.filter((n) => {
|
|
1732
|
+
var _n$data;
|
|
1733
|
+
if (((_n$data = n.data) === null || _n$data === void 0 ? void 0 : _n$data.source) === "mermaid") return false;
|
|
1734
|
+
return !updated.nodes.some((n2) => n2.id === n.id);
|
|
1735
|
+
});
|
|
1736
|
+
const missingEdges = prev.edges.filter((e) => {
|
|
1737
|
+
var _e$data;
|
|
1738
|
+
if (((_e$data = e.data) === null || _e$data === void 0 ? void 0 : _e$data.source) === "mermaid") return false;
|
|
1739
|
+
return !updated.edges.some((e2) => e2.id === e.id);
|
|
1740
|
+
});
|
|
1741
|
+
updated.nodes = [...missingNodes, ...updated.nodes];
|
|
1742
|
+
updated.edges = [...missingEdges, ...updated.edges];
|
|
1743
|
+
}
|
|
1744
|
+
return updated;
|
|
1745
|
+
}
|
|
1746
|
+
function sanitizeMermaidLabels(src) {
|
|
1747
|
+
if (!src) return src;
|
|
1748
|
+
const subgraphFixed = src.replace(/([A-Za-z0-9_]+)\[((?:(?![\"']).)*?)\]/g, (m, id, label) => {
|
|
1749
|
+
if (/^[\"']/.test(label)) return m;
|
|
1750
|
+
if (/[()\"\[\],:;]/.test(label)) return `${id}[\"${String(label).replace(/\\/g, "\\\\").replace(/\"/g, "\\\"")}\"]`;
|
|
1751
|
+
return m;
|
|
1752
|
+
}).replace(/^([ \t]*subgraph\s+)([^\n\r]+)(\|[^\n\r]*)?$/gim, (m, pre, title, rest) => {
|
|
1753
|
+
const t = String(title).trim();
|
|
1754
|
+
if (/^[\"']/.test(t)) return m;
|
|
1755
|
+
if (/[()\"\[\],:;]/.test(t)) return `${pre}\"${t.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"")}\"${rest || ""}`;
|
|
1756
|
+
return m;
|
|
1757
|
+
});
|
|
1758
|
+
const diagRegex = /\b(graph|flowchart|sequenceDiagram|stateDiagram|classDiagram|gantt|journey|erDiagram|gitGraph|pie|timeline|infoDiagram)\b/i;
|
|
1759
|
+
const allStarts = [];
|
|
1760
|
+
let mm;
|
|
1761
|
+
const globalRegex = new RegExp(diagRegex.source, "gim");
|
|
1762
|
+
while ((mm = globalRegex.exec(subgraphFixed)) !== null) {
|
|
1763
|
+
allStarts.push(mm.index);
|
|
1764
|
+
if (globalRegex.lastIndex === mm.index) globalRegex.lastIndex++;
|
|
1765
|
+
}
|
|
1766
|
+
if (allStarts.length <= 1) return subgraphFixed;
|
|
1767
|
+
const first = allStarts[0];
|
|
1768
|
+
const second = allStarts[1];
|
|
1769
|
+
return subgraphFixed.slice(first, second).trim();
|
|
1770
|
+
}
|
|
1771
|
+
function flattenMetaData(fields, fieldsValues) {
|
|
1772
|
+
const output = {};
|
|
1773
|
+
fields.forEach((field) => {
|
|
1774
|
+
var _fieldsValues$find;
|
|
1775
|
+
const data = (_fieldsValues$find = fieldsValues.find((f) => (f === null || f === void 0 ? void 0 : f.componentFieldId) === field.componentFieldId)) === null || _fieldsValues$find === void 0 ? void 0 : _fieldsValues$find.data;
|
|
1776
|
+
const id = field.componentFieldId;
|
|
1777
|
+
if (!id) return;
|
|
1778
|
+
switch (field.type) {
|
|
1779
|
+
case ComponentInputType.TextInput:
|
|
1780
|
+
case ComponentInputType.TextBox:
|
|
1781
|
+
case ComponentInputType.DropdownSelect:
|
|
1782
|
+
case ComponentInputType.SearchSelect:
|
|
1783
|
+
case ComponentInputType.CodeEditor:
|
|
1784
|
+
case ComponentInputType.URLInput:
|
|
1785
|
+
case ComponentInputType.ColorPicker:
|
|
1786
|
+
case ComponentInputType.DatePicker:
|
|
1787
|
+
var _data$0$value, _data$;
|
|
1788
|
+
output[id] = String((_data$0$value = data === null || data === void 0 || (_data$ = data[0]) === null || _data$ === void 0 ? void 0 : _data$.value) !== null && _data$0$value !== void 0 ? _data$0$value : "");
|
|
1789
|
+
break;
|
|
1790
|
+
case ComponentInputType.MultiSelect:
|
|
1791
|
+
case ComponentInputType.CheckboxGroup:
|
|
1792
|
+
case ComponentInputType.TagInput:
|
|
1793
|
+
var _data$0$value2, _data$2;
|
|
1794
|
+
output[id] = (_data$0$value2 = data === null || data === void 0 || (_data$2 = data[0]) === null || _data$2 === void 0 ? void 0 : _data$2.value) !== null && _data$0$value2 !== void 0 ? _data$0$value2 : [];
|
|
1795
|
+
break;
|
|
1796
|
+
case ComponentInputType.NumberInput:
|
|
1797
|
+
case ComponentInputType.Slider:
|
|
1798
|
+
var _data$3;
|
|
1799
|
+
output[id] = (data === null || data === void 0 || (_data$3 = data[0]) === null || _data$3 === void 0 ? void 0 : _data$3.value) ? Number(data[0].value) : null;
|
|
1800
|
+
break;
|
|
1801
|
+
case ComponentInputType.FileUpload:
|
|
1802
|
+
case ComponentInputType.LinkOrFileUpload:
|
|
1803
|
+
var _data$0$value3, _data$4;
|
|
1804
|
+
output[id] = (_data$0$value3 = data === null || data === void 0 || (_data$4 = data[0]) === null || _data$4 === void 0 ? void 0 : _data$4.value) !== null && _data$0$value3 !== void 0 ? _data$0$value3 : "";
|
|
1805
|
+
break;
|
|
1806
|
+
case ComponentInputType.RichTextEditor:
|
|
1807
|
+
var _data$5;
|
|
1808
|
+
output[id] = (_data$5 = data === null || data === void 0 ? void 0 : data[0]) !== null && _data$5 !== void 0 ? _data$5 : void 0;
|
|
1809
|
+
break;
|
|
1810
|
+
case ComponentInputType.KeyValueList:
|
|
1811
|
+
output[id] = data !== null && data !== void 0 ? data : [];
|
|
1812
|
+
break;
|
|
1813
|
+
case ComponentInputType.BooleanToggle:
|
|
1814
|
+
var _data$0$value4, _data$6;
|
|
1815
|
+
output[id] = (_data$0$value4 = data === null || data === void 0 || (_data$6 = data[0]) === null || _data$6 === void 0 ? void 0 : _data$6.value) !== null && _data$0$value4 !== void 0 ? _data$0$value4 : false;
|
|
1816
|
+
break;
|
|
1817
|
+
case ComponentInputType.DateRangePicker:
|
|
1818
|
+
output[id] = data === null || data === void 0 ? void 0 : data[0];
|
|
1819
|
+
break;
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
return output;
|
|
1823
|
+
}
|
|
1824
|
+
function buildMetaData(fields, data) {
|
|
1825
|
+
return fields.map((field) => {
|
|
1826
|
+
var _field$type, _field$required, _field$label, _field$order, _localData;
|
|
1827
|
+
let localData;
|
|
1828
|
+
const value = data[field.componentFieldId];
|
|
1829
|
+
switch (field.type) {
|
|
1830
|
+
case ComponentInputType.TextInput:
|
|
1831
|
+
case ComponentInputType.TextBox:
|
|
1832
|
+
case ComponentInputType.DropdownSelect:
|
|
1833
|
+
case ComponentInputType.SearchSelect:
|
|
1834
|
+
case ComponentInputType.CodeEditor:
|
|
1835
|
+
case ComponentInputType.URLInput:
|
|
1836
|
+
case ComponentInputType.ColorPicker:
|
|
1837
|
+
case ComponentInputType.DatePicker:
|
|
1838
|
+
localData = [{ value: String(value !== null && value !== void 0 ? value : "") }];
|
|
1839
|
+
break;
|
|
1840
|
+
case ComponentInputType.MultiSelect:
|
|
1841
|
+
case ComponentInputType.CheckboxGroup:
|
|
1842
|
+
case ComponentInputType.TagInput:
|
|
1843
|
+
localData = [{ value: value !== null && value !== void 0 ? value : [] }];
|
|
1844
|
+
break;
|
|
1845
|
+
case ComponentInputType.NumberInput:
|
|
1846
|
+
case ComponentInputType.Slider:
|
|
1847
|
+
localData = [{ value: value != null ? Number(value) : null }];
|
|
1848
|
+
break;
|
|
1849
|
+
case ComponentInputType.FileUpload:
|
|
1850
|
+
case ComponentInputType.LinkOrFileUpload:
|
|
1851
|
+
localData = [{ value: value !== null && value !== void 0 ? value : "" }];
|
|
1852
|
+
break;
|
|
1853
|
+
case ComponentInputType.RichTextEditor:
|
|
1854
|
+
localData = [value];
|
|
1855
|
+
break;
|
|
1856
|
+
case ComponentInputType.KeyValueList:
|
|
1857
|
+
localData = value !== null && value !== void 0 ? value : [];
|
|
1858
|
+
break;
|
|
1859
|
+
case ComponentInputType.BooleanToggle:
|
|
1860
|
+
localData = [{ value: value !== null && value !== void 0 ? value : false }];
|
|
1861
|
+
break;
|
|
1862
|
+
case ComponentInputType.DateRangePicker:
|
|
1863
|
+
localData = [value];
|
|
1864
|
+
break;
|
|
1865
|
+
default:
|
|
1866
|
+
localData = value !== null && value !== void 0 ? value : null;
|
|
1867
|
+
break;
|
|
1868
|
+
}
|
|
1869
|
+
return _objectSpread2(_objectSpread2({}, field), {}, {
|
|
1870
|
+
componentFieldId: field.componentFieldId,
|
|
1871
|
+
type: (_field$type = field.type) !== null && _field$type !== void 0 ? _field$type : ComponentInputType.TextInput,
|
|
1872
|
+
required: (_field$required = field.required) !== null && _field$required !== void 0 ? _field$required : false,
|
|
1873
|
+
label: (_field$label = field.label) !== null && _field$label !== void 0 ? _field$label : "",
|
|
1874
|
+
order: (_field$order = field.order) !== null && _field$order !== void 0 ? _field$order : 0,
|
|
1875
|
+
data: (_localData = localData) !== null && _localData !== void 0 ? _localData : []
|
|
1876
|
+
});
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
function generateComponentFieldInput(options) {
|
|
1880
|
+
var _options$componentFie;
|
|
1881
|
+
const componentId = (_options$componentFie = options.componentFieldId) !== null && _options$componentFie !== void 0 ? _options$componentFie : String(Math.random());
|
|
1882
|
+
const [metaData] = buildMetaData([_objectSpread2(_objectSpread2({}, options), {}, {
|
|
1883
|
+
hidden: true,
|
|
1884
|
+
componentFieldId: componentId
|
|
1885
|
+
})], { [componentId]: options.type === ComponentInputType.RichTextEditor && typeof options.data === "string" ? markdownToDelta(options.data) : options.data });
|
|
1886
|
+
return metaData;
|
|
1887
|
+
}
|
|
1888
|
+
function generateComponentFieldNameInput(value) {
|
|
1889
|
+
return generateComponentFieldInput({
|
|
1890
|
+
componentFieldId: "name",
|
|
1891
|
+
label: "Name",
|
|
1892
|
+
type: ComponentInputType.TextInput,
|
|
1893
|
+
data: value
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
function getComponentFieldByLabel(fields, label) {
|
|
1897
|
+
return fields.find((field) => {
|
|
1898
|
+
var _field$label;
|
|
1899
|
+
return ((_field$label = field.label) === null || _field$label === void 0 ? void 0 : _field$label.toLowerCase()) === label.toLowerCase();
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
const LAYOUT_SPACING = {
|
|
1903
|
+
SUBGRAPH_HEADER_HEIGHT: 35,
|
|
1904
|
+
SUBGRAPH_PADDING: 8,
|
|
1905
|
+
SUBGRAPH_CONTENT_TOP_MARGIN: 10,
|
|
1906
|
+
NODE_SEPARATION_HORIZONTAL: 80,
|
|
1907
|
+
NODE_SEPARATION_VERTICAL: 100,
|
|
1908
|
+
CONTAINER_SEPARATION_HORIZONTAL: 120,
|
|
1909
|
+
CONTAINER_SEPARATION_VERTICAL: 160,
|
|
1910
|
+
NESTED_SUBGRAPH_SEPARATION_HORIZONTAL: 120,
|
|
1911
|
+
NESTED_SUBGRAPH_SEPARATION_VERTICAL: 140,
|
|
1912
|
+
META_GRAPH_MARGIN: 100,
|
|
1913
|
+
NESTED_CONTENT_MARGIN: 40,
|
|
1914
|
+
MIXED_CONTENT_VERTICAL_SPACING: 100,
|
|
1915
|
+
MIXED_CONTENT_HORIZONTAL_SPACING: 120
|
|
1916
|
+
};
|
|
1917
|
+
({
|
|
1918
|
+
NETWORK_SIMPLEX: "network-simplex",
|
|
1919
|
+
TIGHT_TREE: "tight-tree",
|
|
1920
|
+
LONGEST_PATH: "longest-path"
|
|
1921
|
+
}).TIGHT_TREE;
|
|
1922
|
+
const SEQUENCE_LAYOUT = {
|
|
1923
|
+
COLUMN_WIDTH: 360,
|
|
1924
|
+
ROW_HEIGHT: 60,
|
|
1925
|
+
HEADER_HEIGHT: 40,
|
|
1926
|
+
MESSAGE_NODE_WIDTH: 120,
|
|
1927
|
+
MESSAGE_NODE_HEIGHT: 36,
|
|
1928
|
+
SELF_LOOP_OFFSET: 80,
|
|
1929
|
+
PARTICIPANT_NODE_WIDTH: 10
|
|
1930
|
+
};
|
|
1931
|
+
const LABEL_TYPE_PREFIX = /^\s*type:(\w+)\s*[:|-]\s*/i;
|
|
1932
|
+
const TAG_TO_NODE_TYPE = {
|
|
1933
|
+
builder: "builder",
|
|
1934
|
+
code: "code",
|
|
1935
|
+
text: "text",
|
|
1936
|
+
table: "table",
|
|
1937
|
+
image: "image",
|
|
1938
|
+
cloud: "cloud",
|
|
1939
|
+
comment: "comment",
|
|
1940
|
+
default: "default"
|
|
1941
|
+
};
|
|
1942
|
+
function parseLabelTag(label) {
|
|
1943
|
+
const match = label.match(LABEL_TYPE_PREFIX);
|
|
1944
|
+
if (match) return {
|
|
1945
|
+
tag: match[1].toLowerCase(),
|
|
1946
|
+
displayLabel: label.slice(match[0].length).trim()
|
|
1947
|
+
};
|
|
1948
|
+
return {
|
|
1949
|
+
tag: null,
|
|
1950
|
+
displayLabel: label
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
function resolvePortalNodeType(hasImageUrl, tag) {
|
|
1954
|
+
if (hasImageUrl || tag === "image") return "image";
|
|
1955
|
+
if (tag && TAG_TO_NODE_TYPE[tag]) return TAG_TO_NODE_TYPE[tag];
|
|
1956
|
+
return "shape";
|
|
1957
|
+
}
|
|
1958
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
1959
|
+
try {
|
|
1960
|
+
var i = n[a](c), u = i.value;
|
|
1961
|
+
} catch (n$1) {
|
|
1962
|
+
e(n$1);
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
1966
|
+
}
|
|
1967
|
+
function _asyncToGenerator(n) {
|
|
1968
|
+
return function() {
|
|
1969
|
+
var t = this, e = arguments;
|
|
1970
|
+
return new Promise(function(r, o) {
|
|
1971
|
+
var a = n.apply(t, e);
|
|
1972
|
+
function _next(n$1) {
|
|
1973
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "next", n$1);
|
|
1974
|
+
}
|
|
1975
|
+
function _throw(n$1) {
|
|
1976
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n$1);
|
|
1977
|
+
}
|
|
1978
|
+
_next(void 0);
|
|
1979
|
+
});
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
mermaid.initialize({
|
|
1983
|
+
startOnLoad: false,
|
|
1984
|
+
theme: "default",
|
|
1985
|
+
flowchart: {
|
|
1986
|
+
htmlLabels: false,
|
|
1987
|
+
curve: "linear"
|
|
1988
|
+
}
|
|
1989
|
+
});
|
|
1990
|
+
function detectDiagramType(code) {
|
|
1991
|
+
const lines = code.split("\n");
|
|
1992
|
+
for (const line of lines) {
|
|
1993
|
+
const trimmed = line.trim().toLowerCase();
|
|
1994
|
+
if (trimmed.startsWith("sequencediagram")) return "sequence";
|
|
1995
|
+
if (trimmed.startsWith("flowchart") || trimmed.startsWith("graph")) return "flowchart";
|
|
1996
|
+
}
|
|
1997
|
+
return "flowchart";
|
|
1998
|
+
}
|
|
1999
|
+
function parseSequenceDiagram(code) {
|
|
2000
|
+
const participants = [];
|
|
2001
|
+
const messages = [];
|
|
2002
|
+
const participantMap = /* @__PURE__ */ new Map();
|
|
2003
|
+
const lines = code.split("\n");
|
|
2004
|
+
let messageRow = 0;
|
|
2005
|
+
for (const line of lines) {
|
|
2006
|
+
const trimmed = line.trim();
|
|
2007
|
+
if (!trimmed || trimmed.toLowerCase() === "sequencediagram") continue;
|
|
2008
|
+
const participantMatch = trimmed.match(/^(?:participant|actor)\s+(\S+)(?:\s+as\s+(.+))?$/i);
|
|
2009
|
+
if (participantMatch) {
|
|
2010
|
+
var _participantMatch$;
|
|
2011
|
+
const id = participantMatch[1];
|
|
2012
|
+
const alias = (_participantMatch$ = participantMatch[2]) === null || _participantMatch$ === void 0 ? void 0 : _participantMatch$.trim();
|
|
2013
|
+
if (!participantMap.has(id)) {
|
|
2014
|
+
const index = participants.length;
|
|
2015
|
+
participantMap.set(id, index);
|
|
2016
|
+
participants.push({
|
|
2017
|
+
id,
|
|
2018
|
+
name: alias !== null && alias !== void 0 ? alias : id,
|
|
2019
|
+
alias,
|
|
2020
|
+
index
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
const messageMatch = trimmed.match(/^([A-Za-z0-9_]+)\s*(-->>|-->|--\)|->>|->|-\))\s*([A-Za-z0-9_]+)\s*:\s*(.*)$/);
|
|
2026
|
+
if (messageMatch) {
|
|
2027
|
+
const [, from, arrow, to, label] = messageMatch;
|
|
2028
|
+
if (!participantMap.has(from)) {
|
|
2029
|
+
const index = participants.length;
|
|
2030
|
+
participantMap.set(from, index);
|
|
2031
|
+
participants.push({
|
|
2032
|
+
id: from,
|
|
2033
|
+
name: from,
|
|
2034
|
+
index
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
if (!participantMap.has(to)) {
|
|
2038
|
+
const index = participants.length;
|
|
2039
|
+
participantMap.set(to, index);
|
|
2040
|
+
participants.push({
|
|
2041
|
+
id: to,
|
|
2042
|
+
name: to,
|
|
2043
|
+
index
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
const lineStyle = arrow.startsWith("--") ? "dashed" : "solid";
|
|
2047
|
+
const arrowType = arrow.includes(">>") ? "filled" : arrow.includes(")") ? "open" : "none";
|
|
2048
|
+
messages.push({
|
|
2049
|
+
from,
|
|
2050
|
+
to,
|
|
2051
|
+
label: label.trim(),
|
|
2052
|
+
lineStyle,
|
|
2053
|
+
arrowType,
|
|
2054
|
+
rowIndex: messageRow++
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
return {
|
|
2059
|
+
participants,
|
|
2060
|
+
messages
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
const MERMAID_TO_PORTAL_SHAPE = {
|
|
2064
|
+
rect: "rectangle",
|
|
2065
|
+
round: "rounded-rect",
|
|
2066
|
+
stadium: "terminator",
|
|
2067
|
+
circle: "ellipse",
|
|
2068
|
+
diamond: "diamond"
|
|
2069
|
+
};
|
|
2070
|
+
const SUBGRAPH_HEADER_HEIGHT = LAYOUT_SPACING.SUBGRAPH_HEADER_HEIGHT;
|
|
2071
|
+
const SUBGRAPH_PADDING = LAYOUT_SPACING.SUBGRAPH_PADDING;
|
|
2072
|
+
const SUBGRAPH_CONTENT_TOP_MARGIN = LAYOUT_SPACING.SUBGRAPH_CONTENT_TOP_MARGIN;
|
|
2073
|
+
const NODE_SEPARATION_HORIZONTAL = LAYOUT_SPACING.NODE_SEPARATION_HORIZONTAL;
|
|
2074
|
+
const NODE_SEPARATION_VERTICAL = LAYOUT_SPACING.NODE_SEPARATION_VERTICAL;
|
|
2075
|
+
const CONTAINER_SEPARATION_HORIZONTAL = LAYOUT_SPACING.CONTAINER_SEPARATION_HORIZONTAL;
|
|
2076
|
+
const CONTAINER_SEPARATION_VERTICAL = LAYOUT_SPACING.CONTAINER_SEPARATION_VERTICAL;
|
|
2077
|
+
const NESTED_SUBGRAPH_SEPARATION_HORIZONTAL = LAYOUT_SPACING.NESTED_SUBGRAPH_SEPARATION_HORIZONTAL;
|
|
2078
|
+
const NESTED_SUBGRAPH_SEPARATION_VERTICAL = LAYOUT_SPACING.NESTED_SUBGRAPH_SEPARATION_VERTICAL;
|
|
2079
|
+
const META_GRAPH_MARGIN = LAYOUT_SPACING.META_GRAPH_MARGIN;
|
|
2080
|
+
const NESTED_CONTENT_MARGIN = LAYOUT_SPACING.NESTED_CONTENT_MARGIN;
|
|
2081
|
+
const MIXED_CONTENT_VERTICAL_SPACING = LAYOUT_SPACING.MIXED_CONTENT_VERTICAL_SPACING;
|
|
2082
|
+
const MIXED_CONTENT_HORIZONTAL_SPACING = LAYOUT_SPACING.MIXED_CONTENT_HORIZONTAL_SPACING;
|
|
2083
|
+
const DAGRE_RANKER = "tight-tree";
|
|
2084
|
+
const DEBUG = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.DEBUG_MERMAID === "true";
|
|
2085
|
+
function debugLog(...args) {
|
|
2086
|
+
if (DEBUG) console.log("[MermaidConverter]", ...args);
|
|
2087
|
+
}
|
|
2088
|
+
function getNodeShape(nodeDefinition) {
|
|
2089
|
+
if (nodeDefinition.includes("{") && nodeDefinition.includes("}")) return "diamond";
|
|
2090
|
+
if (nodeDefinition.includes("((") && nodeDefinition.includes("))")) return "circle";
|
|
2091
|
+
if (nodeDefinition.includes("([") && nodeDefinition.includes("])")) return "stadium";
|
|
2092
|
+
if (nodeDefinition.includes("[") && nodeDefinition.includes("]")) return "rect";
|
|
2093
|
+
if (nodeDefinition.includes("(") && nodeDefinition.includes(")")) return "round";
|
|
2094
|
+
return "rect";
|
|
2095
|
+
}
|
|
2096
|
+
function parseMermaidCode(code) {
|
|
2097
|
+
const nodes = [];
|
|
2098
|
+
const edges = [];
|
|
2099
|
+
const subgraphs = [];
|
|
2100
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
2101
|
+
const subgraphMap = /* @__PURE__ */ new Map();
|
|
2102
|
+
const nodeDefinitions = /* @__PURE__ */ new Map();
|
|
2103
|
+
let direction = "TB";
|
|
2104
|
+
let cleanCode = code.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("%%")).join("\n");
|
|
2105
|
+
const preprocessedLines = [];
|
|
2106
|
+
const rawLines = cleanCode.split("\n");
|
|
2107
|
+
let i = 0;
|
|
2108
|
+
while (i < rawLines.length) {
|
|
2109
|
+
const line = rawLines[i].trim();
|
|
2110
|
+
const openBrackets = (line.match(/[\[\(\{]/g) || []).length;
|
|
2111
|
+
const closeBrackets = (line.match(/[\]\)\}]/g) || []).length;
|
|
2112
|
+
if (openBrackets > closeBrackets && i < rawLines.length - 1) {
|
|
2113
|
+
let combinedLine = line;
|
|
2114
|
+
let j = i + 1;
|
|
2115
|
+
let currentOpenBrackets = openBrackets;
|
|
2116
|
+
let currentCloseBrackets = closeBrackets;
|
|
2117
|
+
while (j < rawLines.length && currentOpenBrackets > currentCloseBrackets) {
|
|
2118
|
+
const nextLine = rawLines[j].trim();
|
|
2119
|
+
combinedLine += " " + nextLine;
|
|
2120
|
+
currentOpenBrackets += (nextLine.match(/[\[\(\{]/g) || []).length;
|
|
2121
|
+
currentCloseBrackets += (nextLine.match(/[\]\)\}]/g) || []).length;
|
|
2122
|
+
j++;
|
|
2123
|
+
}
|
|
2124
|
+
preprocessedLines.push(combinedLine);
|
|
2125
|
+
i = j;
|
|
2126
|
+
} else {
|
|
2127
|
+
preprocessedLines.push(line);
|
|
2128
|
+
i++;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
cleanCode = preprocessedLines.join("\n");
|
|
2132
|
+
debugLog("Clean code:", cleanCode);
|
|
2133
|
+
const directionMatch = cleanCode.match(/(?:flowchart|graph)\s+(TB|TD|BT|RL|LR)/i);
|
|
2134
|
+
if (directionMatch) {
|
|
2135
|
+
direction = directionMatch[1].toUpperCase();
|
|
2136
|
+
if (direction === "TD") direction = "TB";
|
|
2137
|
+
debugLog("Detected graph direction:", direction);
|
|
2138
|
+
}
|
|
2139
|
+
const lines = cleanCode.split("\n");
|
|
2140
|
+
const subgraphStack = [];
|
|
2141
|
+
function enhancedCleanLabel(label) {
|
|
2142
|
+
return label.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]*>/g, "").replace(/\\u([0-9a-fA-F]{4})/g, (match, code$1) => {
|
|
2143
|
+
try {
|
|
2144
|
+
return String.fromCharCode(parseInt(code$1, 16));
|
|
2145
|
+
} catch (_unused) {
|
|
2146
|
+
debugLog(`Warning: Could not parse unicode character: ${match}`);
|
|
2147
|
+
return match;
|
|
2148
|
+
}
|
|
2149
|
+
}).replace(/\\n/g, "\n").replace(/\s*\n\s*/g, "\n").trim();
|
|
2150
|
+
}
|
|
2151
|
+
debugLog("Pre-scanning for node definitions...");
|
|
2152
|
+
lines.forEach((line, lineIndex) => {
|
|
2153
|
+
const trimmedLine = line.trim();
|
|
2154
|
+
if (!trimmedLine || trimmedLine.startsWith("subgraph") || trimmedLine === "end" || trimmedLine.startsWith("%%")) return;
|
|
2155
|
+
const nodeDefPattern = /(^|[\s\-\>]|\|[^|]*\|)([A-Za-z0-9_]+)([\[\(\{])/g;
|
|
2156
|
+
let match;
|
|
2157
|
+
const processedMatches = /* @__PURE__ */ new Set();
|
|
2158
|
+
while ((match = nodeDefPattern.exec(trimmedLine)) !== null) {
|
|
2159
|
+
const prefix = match[1];
|
|
2160
|
+
const nodeId = match[2];
|
|
2161
|
+
const openChar = match[3];
|
|
2162
|
+
const matchStart = match.index + prefix.length;
|
|
2163
|
+
if (processedMatches.has(matchStart) || nodeDefinitions.has(nodeId)) continue;
|
|
2164
|
+
processedMatches.add(matchStart);
|
|
2165
|
+
const openIndex = matchStart + nodeId.length;
|
|
2166
|
+
const closeChar = openChar === "[" ? "]" : openChar === "(" ? ")" : "}";
|
|
2167
|
+
let closeIndex = -1;
|
|
2168
|
+
let depth = 0;
|
|
2169
|
+
for (let i$1 = openIndex; i$1 < trimmedLine.length; i$1++) {
|
|
2170
|
+
const char = trimmedLine[i$1];
|
|
2171
|
+
if (char === openChar) depth++;
|
|
2172
|
+
else if (char === closeChar) {
|
|
2173
|
+
depth--;
|
|
2174
|
+
if (depth === 0) {
|
|
2175
|
+
closeIndex = i$1;
|
|
2176
|
+
break;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
let fullDef = nodeId;
|
|
2181
|
+
let shapeDef = "";
|
|
2182
|
+
if (closeIndex !== -1) {
|
|
2183
|
+
fullDef = trimmedLine.slice(matchStart, closeIndex + 1);
|
|
2184
|
+
shapeDef = trimmedLine.slice(openIndex, closeIndex + 1);
|
|
2185
|
+
} else {
|
|
2186
|
+
const fallback = trimmedLine.slice(matchStart).match(/([A-Za-z0-9_]+)([\[\(\{][^\]\)\}]*[\]\)\}])/);
|
|
2187
|
+
if (fallback && fallback[1] === nodeId) {
|
|
2188
|
+
fullDef = fallback[0];
|
|
2189
|
+
shapeDef = fallback[2];
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (shapeDef) {
|
|
2193
|
+
const shape = getNodeShape(fullDef);
|
|
2194
|
+
let rawLabel = nodeId;
|
|
2195
|
+
const labelContentMatch = shapeDef.match(new RegExp("^[\\[\\(\\{](.*)[\\]\\)\\}]$", "s"));
|
|
2196
|
+
if (labelContentMatch) {
|
|
2197
|
+
rawLabel = labelContentMatch[1];
|
|
2198
|
+
rawLabel = rawLabel.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
|
|
2199
|
+
}
|
|
2200
|
+
const label = enhancedCleanLabel(rawLabel);
|
|
2201
|
+
nodeDefinitions.set(nodeId, {
|
|
2202
|
+
label,
|
|
2203
|
+
shape,
|
|
2204
|
+
fullDef
|
|
2205
|
+
});
|
|
2206
|
+
debugLog(`Pre-scan found node definition: ${nodeId} -> "${label}" (${shape}) from line ${lineIndex + 1}`);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2210
|
+
debugLog("First pass: identifying subgraphs...");
|
|
2211
|
+
for (let i$1 = 0; i$1 < lines.length; i$1++) {
|
|
2212
|
+
const line = lines[i$1].trim();
|
|
2213
|
+
if (!line) continue;
|
|
2214
|
+
if (line.startsWith("subgraph")) {
|
|
2215
|
+
const rest = line.slice(8).trim();
|
|
2216
|
+
let subgraphId;
|
|
2217
|
+
let subgraphTitle;
|
|
2218
|
+
const quoteMatch = rest.match(/^(["'])(.*?)\1/);
|
|
2219
|
+
if (quoteMatch) {
|
|
2220
|
+
subgraphTitle = quoteMatch[2];
|
|
2221
|
+
subgraphId = subgraphTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || `sg-${i$1}`;
|
|
2222
|
+
} else {
|
|
2223
|
+
const bracketMatch = rest.match(/^([^\s\[]+)(?:\s*\[(.+?)\])?/);
|
|
2224
|
+
if (bracketMatch) {
|
|
2225
|
+
subgraphId = bracketMatch[1];
|
|
2226
|
+
if (bracketMatch[2]) subgraphTitle = bracketMatch[2];
|
|
2227
|
+
}
|
|
2228
|
+
if (!subgraphTitle && rest.indexOf(" ") !== -1) {
|
|
2229
|
+
subgraphTitle = rest;
|
|
2230
|
+
subgraphId = subgraphTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || `sg-${i$1}`;
|
|
2231
|
+
}
|
|
2232
|
+
if (!subgraphTitle) {
|
|
2233
|
+
const altQuote = rest.match(/^[^\s]+\s+(["'])(.*?)\1/);
|
|
2234
|
+
if (altQuote) subgraphTitle = altQuote[2];
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
if (subgraphId) {
|
|
2238
|
+
const parentId = subgraphStack.length > 0 ? subgraphStack[subgraphStack.length - 1] : void 0;
|
|
2239
|
+
const cleanTitle = subgraphTitle ? enhancedCleanLabel(subgraphTitle) : subgraphId;
|
|
2240
|
+
debugLog(`Found subgraph: ${subgraphId}, title: "${cleanTitle}", parent: ${parentId || "none"}`);
|
|
2241
|
+
subgraphStack.push(subgraphId);
|
|
2242
|
+
const newSubgraph = {
|
|
2243
|
+
id: subgraphId,
|
|
2244
|
+
title: cleanTitle,
|
|
2245
|
+
nodes: [],
|
|
2246
|
+
parentId,
|
|
2247
|
+
childrenIds: []
|
|
2248
|
+
};
|
|
2249
|
+
subgraphMap.set(subgraphId, newSubgraph);
|
|
2250
|
+
if (parentId) {
|
|
2251
|
+
const parentSubgraph = subgraphMap.get(parentId);
|
|
2252
|
+
if (parentSubgraph) parentSubgraph.childrenIds.push(subgraphId);
|
|
2253
|
+
}
|
|
2254
|
+
subgraphs.push(newSubgraph);
|
|
2255
|
+
}
|
|
2256
|
+
} else if (line.toLowerCase().startsWith("direction ")) {
|
|
2257
|
+
const dirMatch = line.match(/^direction\s+(TB|TD|BT|RL|LR)$/i);
|
|
2258
|
+
if (dirMatch && subgraphStack.length > 0) {
|
|
2259
|
+
const top = subgraphStack[subgraphStack.length - 1];
|
|
2260
|
+
const sg = subgraphMap.get(top);
|
|
2261
|
+
if (sg) {
|
|
2262
|
+
const d = dirMatch[1].toUpperCase();
|
|
2263
|
+
sg.direction = d === "TD" ? "TB" : d;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
} else if (line === "end" && subgraphStack.length > 0) subgraphStack.pop();
|
|
2267
|
+
}
|
|
2268
|
+
subgraphStack.length = 0;
|
|
2269
|
+
function createOrGetNode(nodeId, currentSubgraph) {
|
|
2270
|
+
if (nodeMap.has(nodeId)) {
|
|
2271
|
+
const existingNode = nodeMap.get(nodeId);
|
|
2272
|
+
if (currentSubgraph && !existingNode.subgraph) {
|
|
2273
|
+
existingNode.subgraph = currentSubgraph;
|
|
2274
|
+
const subgraph = subgraphMap.get(currentSubgraph);
|
|
2275
|
+
if (subgraph && !subgraph.nodes.includes(nodeId)) subgraph.nodes.push(nodeId);
|
|
2276
|
+
debugLog(`Updated existing node ${nodeId} to be part of subgraph ${currentSubgraph}`);
|
|
2277
|
+
}
|
|
2278
|
+
return existingNode;
|
|
2279
|
+
}
|
|
2280
|
+
const nodeDef = nodeDefinitions.get(nodeId);
|
|
2281
|
+
let label;
|
|
2282
|
+
let shape;
|
|
2283
|
+
if (nodeDef) {
|
|
2284
|
+
label = nodeDef.label;
|
|
2285
|
+
shape = nodeDef.shape;
|
|
2286
|
+
debugLog(`Creating node ${nodeId} using pre-scanned definition: "${label}" (${shape})`);
|
|
2287
|
+
} else {
|
|
2288
|
+
label = nodeId;
|
|
2289
|
+
shape = "rect";
|
|
2290
|
+
debugLog(`Creating simple fallback node: ${nodeId}`);
|
|
2291
|
+
}
|
|
2292
|
+
const node = {
|
|
2293
|
+
id: nodeId,
|
|
2294
|
+
label,
|
|
2295
|
+
shape,
|
|
2296
|
+
subgraph: currentSubgraph,
|
|
2297
|
+
parentSubgraph: subgraphStack.length > 1 ? subgraphStack[subgraphStack.length - 2] : void 0
|
|
2298
|
+
};
|
|
2299
|
+
nodes.push(node);
|
|
2300
|
+
nodeMap.set(nodeId, node);
|
|
2301
|
+
if (currentSubgraph) {
|
|
2302
|
+
const subgraph = subgraphMap.get(currentSubgraph);
|
|
2303
|
+
if (subgraph) subgraph.nodes.push(nodeId);
|
|
2304
|
+
}
|
|
2305
|
+
return node;
|
|
2306
|
+
}
|
|
2307
|
+
debugLog("Second pass: processing nodes and edges...");
|
|
2308
|
+
for (let i$1 = 0; i$1 < lines.length; i$1++) {
|
|
2309
|
+
const line = lines[i$1].trim();
|
|
2310
|
+
if (!line) continue;
|
|
2311
|
+
if (line.startsWith("subgraph")) {
|
|
2312
|
+
const rest = line.slice(8).trim();
|
|
2313
|
+
let subgraphId;
|
|
2314
|
+
const quoteMatch = rest.match(/^(?:["'])(.*?)(?:["'])/);
|
|
2315
|
+
if (quoteMatch) subgraphId = quoteMatch[1].toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || `sg-${i$1}`;
|
|
2316
|
+
else {
|
|
2317
|
+
const bracketMatch = rest.match(/^([^\s\[]+)(?:\s*\[(.+?)\])?/);
|
|
2318
|
+
if (bracketMatch) {
|
|
2319
|
+
const idToken = bracketMatch[1];
|
|
2320
|
+
if (bracketMatch[2]) subgraphId = idToken;
|
|
2321
|
+
else if (rest.indexOf(" ") !== -1) subgraphId = rest.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || `sg-${i$1}`;
|
|
2322
|
+
else subgraphId = idToken;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
if (subgraphId) {
|
|
2326
|
+
subgraphStack.push(subgraphId);
|
|
2327
|
+
debugLog(`Entering subgraph: ${subgraphId}, stack: [${subgraphStack.join(", ")}]`);
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
if (line === "end") {
|
|
2332
|
+
if (subgraphStack.length > 0) {
|
|
2333
|
+
const exitingSubgraph = subgraphStack[subgraphStack.length - 1];
|
|
2334
|
+
subgraphStack.pop();
|
|
2335
|
+
debugLog(`Exiting subgraph: ${exitingSubgraph}, stack: [${subgraphStack.join(", ")}]`);
|
|
2336
|
+
} else debugLog("Warning: Found \"end\" without matching subgraph start");
|
|
2337
|
+
continue;
|
|
2338
|
+
}
|
|
2339
|
+
if (line.startsWith("direction ") || line.startsWith("flowchart ") || line.startsWith("graph ") || line.startsWith("%%")) {
|
|
2340
|
+
debugLog(`Skipping line: ${line}`);
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
const currentSubgraph = subgraphStack.length > 0 ? subgraphStack[subgraphStack.length - 1] : void 0;
|
|
2344
|
+
debugLog(`Processing line: "${line}" in subgraph: ${currentSubgraph || "none"}`);
|
|
2345
|
+
function extractToken(str, startIndex) {
|
|
2346
|
+
const idMatch = str.slice(startIndex).match(/^\s*([A-Za-z0-9_]+)/);
|
|
2347
|
+
if (!idMatch) return null;
|
|
2348
|
+
const id = idMatch[1];
|
|
2349
|
+
const idx = startIndex + idMatch[0].length;
|
|
2350
|
+
const rest = str.slice(idx);
|
|
2351
|
+
const openCharMatch = rest.match(/^[\s]*([\[\(\{])/);
|
|
2352
|
+
if (openCharMatch) {
|
|
2353
|
+
const openChar = openCharMatch[1];
|
|
2354
|
+
const openPos = idx + rest.indexOf(openChar);
|
|
2355
|
+
const closeChar = openChar === "[" ? "]" : openChar === "(" ? ")" : "}";
|
|
2356
|
+
const closePos = str.indexOf(closeChar, openPos + 1);
|
|
2357
|
+
if (closePos !== -1) return {
|
|
2358
|
+
id,
|
|
2359
|
+
full: str.slice(startIndex + idMatch[0].search(/\S/), closePos + 1).trim(),
|
|
2360
|
+
endIndex: closePos + 1
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
return {
|
|
2364
|
+
id,
|
|
2365
|
+
full: id,
|
|
2366
|
+
endIndex: idx
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
function parseEdge(str) {
|
|
2370
|
+
try {
|
|
2371
|
+
let i$2 = 0;
|
|
2372
|
+
const src = extractToken(str, i$2);
|
|
2373
|
+
if (!src) return null;
|
|
2374
|
+
i$2 = src.endIndex;
|
|
2375
|
+
while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
|
|
2376
|
+
const arrowHeads = [
|
|
2377
|
+
"-.->",
|
|
2378
|
+
"-->",
|
|
2379
|
+
"==>",
|
|
2380
|
+
"->>",
|
|
2381
|
+
"<->",
|
|
2382
|
+
"-<>",
|
|
2383
|
+
"<-",
|
|
2384
|
+
"->"
|
|
2385
|
+
];
|
|
2386
|
+
let foundArrowIndex = -1;
|
|
2387
|
+
let foundArrow = "";
|
|
2388
|
+
for (const ah of arrowHeads) {
|
|
2389
|
+
const idx = str.indexOf(ah, i$2);
|
|
2390
|
+
if (idx !== -1 && (foundArrowIndex === -1 || idx < foundArrowIndex)) {
|
|
2391
|
+
foundArrowIndex = idx;
|
|
2392
|
+
foundArrow = ah;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
let op = null;
|
|
2396
|
+
let edgeLabel = "";
|
|
2397
|
+
if (foundArrowIndex !== -1) {
|
|
2398
|
+
const between = str.slice(i$2, foundArrowIndex);
|
|
2399
|
+
const prePipeMatch = between.match(/\|(.*?)\|/);
|
|
2400
|
+
if (prePipeMatch) edgeLabel = prePipeMatch[1];
|
|
2401
|
+
else {
|
|
2402
|
+
const inline = between.replace(/^\s*[\-\.=:\~]+\s*/g, "").replace(/\s*[\-\.=:\~]+\s*$/g, "").trim();
|
|
2403
|
+
if (inline) edgeLabel = inline;
|
|
2404
|
+
}
|
|
2405
|
+
op = foundArrow;
|
|
2406
|
+
i$2 = foundArrowIndex + foundArrow.length;
|
|
2407
|
+
while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
|
|
2408
|
+
if (str[i$2] === "|") {
|
|
2409
|
+
const next = str.indexOf("|", i$2 + 1);
|
|
2410
|
+
if (next !== -1) {
|
|
2411
|
+
edgeLabel = str.slice(i$2 + 1, next);
|
|
2412
|
+
i$2 = next + 1;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
} else {
|
|
2416
|
+
for (const o of [
|
|
2417
|
+
"---",
|
|
2418
|
+
"-.-",
|
|
2419
|
+
"::",
|
|
2420
|
+
":-:",
|
|
2421
|
+
"...",
|
|
2422
|
+
"~",
|
|
2423
|
+
"==="
|
|
2424
|
+
].sort((a, b) => b.length - a.length)) if (str.startsWith(o, i$2)) {
|
|
2425
|
+
op = o;
|
|
2426
|
+
i$2 += o.length;
|
|
2427
|
+
break;
|
|
2428
|
+
}
|
|
2429
|
+
if (!op) return null;
|
|
2430
|
+
while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
|
|
2431
|
+
if (str[i$2] === "|") {
|
|
2432
|
+
const next = str.indexOf("|", i$2 + 1);
|
|
2433
|
+
if (next !== -1) {
|
|
2434
|
+
edgeLabel = str.slice(i$2 + 1, next);
|
|
2435
|
+
i$2 = next + 1;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
while (i$2 < str.length && /\s/.test(str[i$2])) i$2++;
|
|
2440
|
+
const tgt = extractToken(str, i$2);
|
|
2441
|
+
if (!tgt) return null;
|
|
2442
|
+
const trailing = str.slice(tgt.endIndex).trimStart();
|
|
2443
|
+
if (!edgeLabel && !trailing.startsWith(":::")) {
|
|
2444
|
+
const colonLabelMatch = trailing.match(/^:\s*(.+)$/);
|
|
2445
|
+
if (colonLabelMatch) edgeLabel = colonLabelMatch[1].trim();
|
|
2446
|
+
}
|
|
2447
|
+
const isReverseArrow = op === "<-";
|
|
2448
|
+
return {
|
|
2449
|
+
sourceId: isReverseArrow ? tgt.id : src.id,
|
|
2450
|
+
sourceFull: isReverseArrow ? tgt.full : src.full,
|
|
2451
|
+
targetId: isReverseArrow ? src.id : tgt.id,
|
|
2452
|
+
targetFull: isReverseArrow ? src.full : tgt.full,
|
|
2453
|
+
edgeType: isReverseArrow ? "->" : op,
|
|
2454
|
+
edgeLabel
|
|
2455
|
+
};
|
|
2456
|
+
} catch (_unused2) {
|
|
2457
|
+
return null;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
const parsedEdge = parseEdge(line);
|
|
2461
|
+
if (!parsedEdge) debugLog(`Line "${line}" did not match edge pattern - checking for standalone nodes`);
|
|
2462
|
+
if (parsedEdge) try {
|
|
2463
|
+
const { sourceId, targetId, edgeType, edgeLabel } = parsedEdge;
|
|
2464
|
+
debugLog(`Found edge: ${sourceId} ${edgeType} ${targetId} with label: "${edgeLabel}" in context: ${currentSubgraph || "global"}`);
|
|
2465
|
+
const isSourceSubgraph = subgraphMap.has(sourceId);
|
|
2466
|
+
const isTargetSubgraph = subgraphMap.has(targetId);
|
|
2467
|
+
debugLog(`Source "${sourceId}" is ${isSourceSubgraph ? "a subgraph" : "a node"}`);
|
|
2468
|
+
debugLog(`Target "${targetId}" is ${isTargetSubgraph ? "a subgraph" : "a node"}`);
|
|
2469
|
+
if (!isSourceSubgraph) {
|
|
2470
|
+
const existingSource = nodeMap.get(sourceId);
|
|
2471
|
+
if (existingSource) debugLog(`Source ${sourceId} already exists in subgraph: ${existingSource.subgraph || "none"}`);
|
|
2472
|
+
else createOrGetNode(sourceId, currentSubgraph);
|
|
2473
|
+
}
|
|
2474
|
+
if (!isTargetSubgraph) {
|
|
2475
|
+
const existingTarget = nodeMap.get(targetId);
|
|
2476
|
+
if (existingTarget) debugLog(`Target ${targetId} already exists in subgraph: ${existingTarget.subgraph || "none"}`);
|
|
2477
|
+
else {
|
|
2478
|
+
const targetSubgraph = currentSubgraph;
|
|
2479
|
+
debugLog(`Creating target ${targetId} with subgraph assignment: ${targetSubgraph || "none"} (current subgraph: ${currentSubgraph || "none"})`);
|
|
2480
|
+
createOrGetNode(targetId, targetSubgraph);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
edges.push({
|
|
2484
|
+
source: sourceId,
|
|
2485
|
+
target: targetId,
|
|
2486
|
+
label: enhancedCleanLabel(edgeLabel),
|
|
2487
|
+
type: edgeType,
|
|
2488
|
+
isSourceSubgraph,
|
|
2489
|
+
isTargetSubgraph
|
|
2490
|
+
});
|
|
2491
|
+
debugLog(`Added edge: ${sourceId} -> ${targetId} (source subgraph: ${isSourceSubgraph}, target subgraph: ${isTargetSubgraph})`);
|
|
2492
|
+
} catch (error) {
|
|
2493
|
+
debugLog(`Error parsing edge: ${line}`, error);
|
|
2494
|
+
}
|
|
2495
|
+
else try {
|
|
2496
|
+
const nodePatterns = [/^([A-Za-z0-9_]+)([\[\(\{][^\]\)\}]*[\]\)\}])/, /^([A-Za-z0-9_]+)$/];
|
|
2497
|
+
let foundStandaloneNode = false;
|
|
2498
|
+
for (const pattern of nodePatterns) {
|
|
2499
|
+
const nodeMatch = line.match(pattern);
|
|
2500
|
+
if (nodeMatch && !nodeMap.has(nodeMatch[1])) {
|
|
2501
|
+
const nodeId = nodeMatch[1];
|
|
2502
|
+
if (subgraphMap.has(nodeId)) {
|
|
2503
|
+
debugLog(`Skipping node creation for ${nodeId} as it's a subgraph`);
|
|
2504
|
+
break;
|
|
2505
|
+
}
|
|
2506
|
+
debugLog(`Found standalone node definition: ${nodeId} in context: ${currentSubgraph || "global"}`);
|
|
2507
|
+
createOrGetNode(nodeId, currentSubgraph);
|
|
2508
|
+
foundStandaloneNode = true;
|
|
2509
|
+
break;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
if (!foundStandaloneNode) debugLog(`Line "${line}" did not match any pattern (edge or standalone node)`);
|
|
2513
|
+
} catch (error) {
|
|
2514
|
+
debugLog(`Error parsing standalone node: ${line}`, error);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
debugLog("=== FINAL VERIFICATION ===");
|
|
2518
|
+
debugLog("Subgraph hierarchy:");
|
|
2519
|
+
subgraphs.forEach((sg) => {
|
|
2520
|
+
debugLog(`- ${sg.id}: "${sg.title}" (parent: ${sg.parentId || "none"}, children: ${sg.childrenIds.join(", ") || "none"}, nodes: ${sg.nodes.length})`);
|
|
2521
|
+
});
|
|
2522
|
+
debugLog("Final node assignments:");
|
|
2523
|
+
nodes.forEach((node) => {
|
|
2524
|
+
debugLog(`- ${node.id}: "${node.label}" in subgraph ${node.subgraph || "none"} (shape: ${node.shape})`);
|
|
2525
|
+
});
|
|
2526
|
+
debugLog("Final edges:");
|
|
2527
|
+
edges.forEach((edge, index) => {
|
|
2528
|
+
debugLog(`- Edge ${index}: ${edge.source} -> ${edge.target} (label: "${edge.label}", type: "${edge.type}")`);
|
|
2529
|
+
});
|
|
2530
|
+
return {
|
|
2531
|
+
nodes,
|
|
2532
|
+
edges,
|
|
2533
|
+
subgraphs,
|
|
2534
|
+
direction
|
|
2535
|
+
};
|
|
2536
|
+
}
|
|
2537
|
+
function calculateNodeSize(label, shape, isImageNode = false) {
|
|
2538
|
+
if (isImageNode) return {
|
|
2539
|
+
width: 80,
|
|
2540
|
+
height: 80
|
|
2541
|
+
};
|
|
2542
|
+
const lines = label.split("\n");
|
|
2543
|
+
function measureLineWidth(text) {
|
|
2544
|
+
try {
|
|
2545
|
+
if (typeof document !== "undefined") {
|
|
2546
|
+
const ctx = document.createElement("canvas").getContext("2d");
|
|
2547
|
+
if (ctx) {
|
|
2548
|
+
ctx.font = "600 13px -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif";
|
|
2549
|
+
return ctx.measureText(text).width;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
} catch (_unused3) {}
|
|
2553
|
+
return text.length * 8;
|
|
2554
|
+
}
|
|
2555
|
+
const baseWidth = Math.max(...lines.map((line) => Math.ceil(measureLineWidth(line)))) + 30;
|
|
2556
|
+
const baseHeight = lines.length * 18 + 20;
|
|
2557
|
+
const width = Math.max(80, baseWidth + 30);
|
|
2558
|
+
const height = Math.max(40, baseHeight + 20);
|
|
2559
|
+
if (shape === "diamond") return {
|
|
2560
|
+
width: Math.max(90, Math.ceil(width * 1.05)),
|
|
2561
|
+
height: Math.max(90, Math.ceil(height * 1.05))
|
|
2562
|
+
};
|
|
2563
|
+
if (shape === "circle") {
|
|
2564
|
+
const size = Math.max(width, height) + 10;
|
|
2565
|
+
return {
|
|
2566
|
+
width: size,
|
|
2567
|
+
height: size
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2570
|
+
return {
|
|
2571
|
+
width,
|
|
2572
|
+
height
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
function extractImageUrl(label) {
|
|
2576
|
+
const match = label.match(/https?:\/\/[^\s]+\.(jpg|jpeg|png|gif|svg|webp)(\?[^\s]*)?/i);
|
|
2577
|
+
if (match) {
|
|
2578
|
+
const imageUrl = match[0];
|
|
2579
|
+
return {
|
|
2580
|
+
imageUrl,
|
|
2581
|
+
cleanLabel: label.replace(imageUrl, "").trim()
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
return {
|
|
2585
|
+
imageUrl: null,
|
|
2586
|
+
cleanLabel: label
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
function processSubgraphsInHierarchicalOrder(subgraphs) {
|
|
2590
|
+
const result = [];
|
|
2591
|
+
const processed = /* @__PURE__ */ new Set();
|
|
2592
|
+
subgraphs.forEach((subgraph) => {
|
|
2593
|
+
if (!subgraph.parentId) {
|
|
2594
|
+
result.push(subgraph);
|
|
2595
|
+
processed.add(subgraph.id);
|
|
2596
|
+
}
|
|
2597
|
+
});
|
|
2598
|
+
let lastProcessedCount = 0;
|
|
2599
|
+
while (processed.size < subgraphs.length && lastProcessedCount !== processed.size) {
|
|
2600
|
+
lastProcessedCount = processed.size;
|
|
2601
|
+
subgraphs.forEach((subgraph) => {
|
|
2602
|
+
if (!processed.has(subgraph.id) && subgraph.parentId && processed.has(subgraph.parentId)) {
|
|
2603
|
+
result.push(subgraph);
|
|
2604
|
+
processed.add(subgraph.id);
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
subgraphs.forEach((subgraph) => {
|
|
2609
|
+
if (!processed.has(subgraph.id)) {
|
|
2610
|
+
debugLog(`Warning: Subgraph ${subgraph.id} has circular reference or missing parent. Adding it anyway.`);
|
|
2611
|
+
result.push(subgraph);
|
|
2612
|
+
}
|
|
2613
|
+
});
|
|
2614
|
+
return result;
|
|
2615
|
+
}
|
|
2616
|
+
function layoutSubgraphs(nodes, edges, subgraphs, direction) {
|
|
2617
|
+
const subgraphLayouts = /* @__PURE__ */ new Map();
|
|
2618
|
+
const orderedSubgraphs = processSubgraphsInHierarchicalOrder(subgraphs);
|
|
2619
|
+
debugLog(`Laying out ${orderedSubgraphs.length} subgraphs in hierarchical order`);
|
|
2620
|
+
orderedSubgraphs.forEach((subgraph) => {
|
|
2621
|
+
const subgraphNodes = nodes.filter((n) => n.subgraph === subgraph.id);
|
|
2622
|
+
const subgraphEdges = edges.filter((e) => {
|
|
2623
|
+
const sourceNode = nodes.find((n) => n.id === e.source);
|
|
2624
|
+
const targetNode = nodes.find((n) => n.id === e.target);
|
|
2625
|
+
return (sourceNode === null || sourceNode === void 0 ? void 0 : sourceNode.subgraph) === subgraph.id && (targetNode === null || targetNode === void 0 ? void 0 : targetNode.subgraph) === subgraph.id;
|
|
2626
|
+
});
|
|
2627
|
+
debugLog(`Laying out subgraph: ${subgraph.id} with ${subgraphNodes.length} nodes and ${subgraphEdges.length} edges`);
|
|
2628
|
+
const g = new dagre.graphlib.Graph();
|
|
2629
|
+
g.setGraph({
|
|
2630
|
+
rankdir: subgraph.direction || direction,
|
|
2631
|
+
nodesep: NODE_SEPARATION_HORIZONTAL,
|
|
2632
|
+
ranksep: NODE_SEPARATION_VERTICAL,
|
|
2633
|
+
marginx: SUBGRAPH_PADDING,
|
|
2634
|
+
marginy: SUBGRAPH_PADDING + SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN,
|
|
2635
|
+
ranker: DAGRE_RANKER
|
|
2636
|
+
});
|
|
2637
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2638
|
+
subgraphNodes.forEach((node) => {
|
|
2639
|
+
const { imageUrl } = extractImageUrl(node.label);
|
|
2640
|
+
const size = calculateNodeSize(node.label, node.shape, !!imageUrl);
|
|
2641
|
+
g.setNode(node.id, {
|
|
2642
|
+
width: size.width,
|
|
2643
|
+
height: size.height
|
|
2644
|
+
});
|
|
2645
|
+
});
|
|
2646
|
+
subgraphEdges.forEach((edge) => {
|
|
2647
|
+
g.setEdge(edge.source, edge.target);
|
|
2648
|
+
});
|
|
2649
|
+
dagre.layout(g);
|
|
2650
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
2651
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
2652
|
+
subgraphNodes.forEach((node) => {
|
|
2653
|
+
const nodeLayout = g.node(node.id);
|
|
2654
|
+
if (!nodeLayout) {
|
|
2655
|
+
debugLog(`Warning: No layout information for node ${node.id} in subgraph ${subgraph.id}`);
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
const size = calculateNodeSize(node.label, node.shape);
|
|
2659
|
+
nodePositions.set(node.id, {
|
|
2660
|
+
x: nodeLayout.x,
|
|
2661
|
+
y: nodeLayout.y,
|
|
2662
|
+
width: size.width,
|
|
2663
|
+
height: size.height
|
|
2664
|
+
});
|
|
2665
|
+
minX = Math.min(minX, nodeLayout.x - size.width / 2);
|
|
2666
|
+
maxX = Math.max(maxX, nodeLayout.x + size.width / 2);
|
|
2667
|
+
minY = Math.min(minY, nodeLayout.y - size.height / 2);
|
|
2668
|
+
maxY = Math.max(maxY, nodeLayout.y + size.height / 2);
|
|
2669
|
+
});
|
|
2670
|
+
if (subgraphNodes.length === 0 || minX === Infinity || minY === Infinity) {
|
|
2671
|
+
const defaultWidth = 200;
|
|
2672
|
+
const defaultHeight = 100;
|
|
2673
|
+
minX = 0;
|
|
2674
|
+
minY = 0;
|
|
2675
|
+
maxX = defaultWidth;
|
|
2676
|
+
maxY = defaultHeight;
|
|
2677
|
+
debugLog(`Using default dimensions for subgraph ${subgraph.id}: ${defaultWidth}x${defaultHeight}`);
|
|
2678
|
+
}
|
|
2679
|
+
const offsetX = -minX + SUBGRAPH_PADDING;
|
|
2680
|
+
const offsetY = -minY + SUBGRAPH_PADDING + SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN;
|
|
2681
|
+
nodePositions.forEach((pos, nodeId) => {
|
|
2682
|
+
nodePositions.set(nodeId, _objectSpread2(_objectSpread2({}, pos), {}, {
|
|
2683
|
+
x: pos.x + offsetX,
|
|
2684
|
+
y: pos.y + offsetY
|
|
2685
|
+
}));
|
|
2686
|
+
});
|
|
2687
|
+
validateNodeSpacing(nodePositions, subgraph.id);
|
|
2688
|
+
const baseWidth = maxX - minX + SUBGRAPH_PADDING * 2;
|
|
2689
|
+
const baseHeight = maxY - minY + SUBGRAPH_PADDING * 2 + SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN;
|
|
2690
|
+
const width = baseWidth + 4;
|
|
2691
|
+
const height = baseHeight + 4;
|
|
2692
|
+
subgraphLayouts.set(subgraph.id, {
|
|
2693
|
+
id: subgraph.id,
|
|
2694
|
+
title: subgraph.title,
|
|
2695
|
+
nodes: nodePositions,
|
|
2696
|
+
width,
|
|
2697
|
+
height,
|
|
2698
|
+
parentId: subgraph.parentId
|
|
2699
|
+
});
|
|
2700
|
+
debugLog(`Subgraph ${subgraph.id} sizing: base(${baseWidth.toFixed(1)}x${baseHeight.toFixed(1)}) = final(${width.toFixed(1)}x${height.toFixed(1)})`);
|
|
2701
|
+
});
|
|
2702
|
+
recalculateParentSubgraphSizes(subgraphLayouts, orderedSubgraphs, direction);
|
|
2703
|
+
return subgraphLayouts;
|
|
2704
|
+
}
|
|
2705
|
+
function recalculateParentSubgraphSizes(subgraphLayouts, orderedSubgraphs, direction) {
|
|
2706
|
+
for (let i = orderedSubgraphs.length - 1; i >= 0; i--) {
|
|
2707
|
+
const subgraph = orderedSubgraphs[i];
|
|
2708
|
+
const layout = subgraphLayouts.get(subgraph.id);
|
|
2709
|
+
if (!layout) continue;
|
|
2710
|
+
const childSubgraphs = orderedSubgraphs.filter((sg) => sg.parentId === subgraph.id);
|
|
2711
|
+
if (childSubgraphs.length === 0) continue;
|
|
2712
|
+
let maxContentRight = 0;
|
|
2713
|
+
let maxContentBottom = 0;
|
|
2714
|
+
layout.nodes.forEach((nodePos) => {
|
|
2715
|
+
const nodeRight = nodePos.x + nodePos.width / 2;
|
|
2716
|
+
const nodeBottom = nodePos.y + nodePos.height / 2;
|
|
2717
|
+
maxContentRight = Math.max(maxContentRight, nodeRight);
|
|
2718
|
+
maxContentBottom = Math.max(maxContentBottom, nodeBottom);
|
|
2719
|
+
});
|
|
2720
|
+
const isHorizontal = direction === "LR" || direction === "RL";
|
|
2721
|
+
childSubgraphs.forEach((childSg) => {
|
|
2722
|
+
const childLayout = subgraphLayouts.get(childSg.id);
|
|
2723
|
+
if (childLayout) {
|
|
2724
|
+
const estimatedChildX = isHorizontal ? Math.max(SUBGRAPH_PADDING + childLayout.width / 2, maxContentRight + MIXED_CONTENT_HORIZONTAL_SPACING + childLayout.width / 2) : SUBGRAPH_PADDING + childLayout.width / 2;
|
|
2725
|
+
const estimatedChildY = isHorizontal ? Math.max(SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN + SUBGRAPH_PADDING + childLayout.height / 2, childLayout.height / 2) : Math.max(SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN + SUBGRAPH_PADDING + childLayout.height / 2, maxContentBottom + MIXED_CONTENT_VERTICAL_SPACING + childLayout.height / 2);
|
|
2726
|
+
const childRight = estimatedChildX + childLayout.width / 2;
|
|
2727
|
+
const childBottom = estimatedChildY + childLayout.height / 2;
|
|
2728
|
+
maxContentRight = Math.max(maxContentRight, childRight);
|
|
2729
|
+
maxContentBottom = Math.max(maxContentBottom, childBottom);
|
|
2730
|
+
}
|
|
2731
|
+
});
|
|
2732
|
+
const minRequiredWidth = maxContentRight + SUBGRAPH_PADDING * 3;
|
|
2733
|
+
const minRequiredHeight = maxContentBottom + SUBGRAPH_PADDING * 3;
|
|
2734
|
+
const absoluteMinWidth = 300;
|
|
2735
|
+
const absoluteMinHeight = 200;
|
|
2736
|
+
const finalWidth = Math.max(layout.width, minRequiredWidth, absoluteMinWidth);
|
|
2737
|
+
const finalHeight = Math.max(layout.height, minRequiredHeight, absoluteMinHeight);
|
|
2738
|
+
if (finalWidth > layout.width || finalHeight > layout.height) {
|
|
2739
|
+
const oldWidth = layout.width;
|
|
2740
|
+
const oldHeight = layout.height;
|
|
2741
|
+
layout.width = finalWidth;
|
|
2742
|
+
layout.height = finalHeight;
|
|
2743
|
+
debugLog(`Pre-sized parent ${subgraph.id}: ${oldWidth}x${oldHeight} → ${layout.width}x${layout.height} to contain ${childSubgraphs.length} children + nodes`);
|
|
2744
|
+
childSubgraphs.forEach((child) => {
|
|
2745
|
+
const childLayout = subgraphLayouts.get(child.id);
|
|
2746
|
+
if (childLayout) debugLog(` Child ${child.id}: ${childLayout.width}x${childLayout.height}`);
|
|
2747
|
+
});
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
function calculateConnectionWeights(nodes, edges) {
|
|
2752
|
+
const weights = /* @__PURE__ */ new Map();
|
|
2753
|
+
edges.forEach((edge) => {
|
|
2754
|
+
const sourceNode = nodes.find((n) => n.id === edge.source);
|
|
2755
|
+
const targetNode = nodes.find((n) => n.id === edge.target);
|
|
2756
|
+
if (!sourceNode || !targetNode) return;
|
|
2757
|
+
const sourceContainer = sourceNode.subgraph || sourceNode.id;
|
|
2758
|
+
const targetContainer = targetNode.subgraph || targetNode.id;
|
|
2759
|
+
if (sourceContainer === targetContainer) return;
|
|
2760
|
+
if (!weights.has(sourceContainer)) weights.set(sourceContainer, /* @__PURE__ */ new Map());
|
|
2761
|
+
const sourceWeights = weights.get(sourceContainer);
|
|
2762
|
+
const currentWeight = sourceWeights.get(targetContainer) || 0;
|
|
2763
|
+
sourceWeights.set(targetContainer, currentWeight + 1);
|
|
2764
|
+
});
|
|
2765
|
+
return weights;
|
|
2766
|
+
}
|
|
2767
|
+
function adjustParentSizesAfterPositioning(subgraphLayouts, subgraphPositions, orderedSubgraphs, direction) {
|
|
2768
|
+
const isHorizontal = direction === "LR" || direction === "RL";
|
|
2769
|
+
let changed = true;
|
|
2770
|
+
let iterations = 0;
|
|
2771
|
+
while (changed && iterations < 5) {
|
|
2772
|
+
iterations++;
|
|
2773
|
+
changed = false;
|
|
2774
|
+
orderedSubgraphs.forEach((subgraph) => {
|
|
2775
|
+
const layout = subgraphLayouts.get(subgraph.id);
|
|
2776
|
+
const position = subgraphPositions.get(subgraph.id);
|
|
2777
|
+
if (!layout || !position) return;
|
|
2778
|
+
let maxNodeRight = 0;
|
|
2779
|
+
let maxNodeBottom = SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN + SUBGRAPH_PADDING;
|
|
2780
|
+
layout.nodes.forEach((nodePos) => {
|
|
2781
|
+
const nodeRight = nodePos.x + nodePos.width / 2;
|
|
2782
|
+
const nodeBottom = nodePos.y + nodePos.height / 2;
|
|
2783
|
+
maxNodeRight = Math.max(maxNodeRight, nodeRight);
|
|
2784
|
+
maxNodeBottom = Math.max(maxNodeBottom, nodeBottom);
|
|
2785
|
+
});
|
|
2786
|
+
let maxChildRight = 0;
|
|
2787
|
+
let maxChildBottom = 0;
|
|
2788
|
+
orderedSubgraphs.filter((sg) => sg.parentId === subgraph.id).forEach((child) => {
|
|
2789
|
+
const childLayout = subgraphLayouts.get(child.id);
|
|
2790
|
+
const childPosition = subgraphPositions.get(child.id);
|
|
2791
|
+
if (!childLayout || !childPosition) return;
|
|
2792
|
+
const relX = childPosition.x - position.x;
|
|
2793
|
+
const relY = childPosition.y - position.y;
|
|
2794
|
+
maxChildRight = Math.max(maxChildRight, relX + childLayout.width);
|
|
2795
|
+
maxChildBottom = Math.max(maxChildBottom, relY + childLayout.height);
|
|
2796
|
+
});
|
|
2797
|
+
const contentMaxRight = Math.max(maxNodeRight, maxChildRight);
|
|
2798
|
+
const contentMaxBottom = Math.max(maxNodeBottom, maxChildBottom);
|
|
2799
|
+
const requiredWidth = contentMaxRight + (isHorizontal ? SUBGRAPH_PADDING * 4 : SUBGRAPH_PADDING * 3);
|
|
2800
|
+
const requiredHeight = contentMaxBottom + SUBGRAPH_PADDING * 3;
|
|
2801
|
+
const newWidth = Math.max(layout.width, requiredWidth, isHorizontal ? 600 : 240);
|
|
2802
|
+
const newHeight = Math.max(layout.height, requiredHeight, 200);
|
|
2803
|
+
if (newWidth > layout.width || newHeight > layout.height) {
|
|
2804
|
+
debugLog(`Post-positioning resize (iter ${iterations}): ${subgraph.id} ${layout.width}x${layout.height} → ${newWidth}x${newHeight}`);
|
|
2805
|
+
layout.width = newWidth;
|
|
2806
|
+
layout.height = newHeight;
|
|
2807
|
+
changed = true;
|
|
2808
|
+
}
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
function getParentNodesBottomBoundary(parentId, parentLayout) {
|
|
2813
|
+
let maxBottom = 0;
|
|
2814
|
+
parentLayout.nodes.forEach((nodePos) => {
|
|
2815
|
+
const nodeBottom = nodePos.y + nodePos.height / 2;
|
|
2816
|
+
maxBottom = Math.max(maxBottom, nodeBottom);
|
|
2817
|
+
});
|
|
2818
|
+
debugLog(`Parent ${parentId} nodes extend to bottom Y=${maxBottom}`);
|
|
2819
|
+
return maxBottom;
|
|
2820
|
+
}
|
|
2821
|
+
function validateNodeSpacing(nodePositions, subgraphId) {
|
|
2822
|
+
const positions = Array.from(nodePositions.values());
|
|
2823
|
+
let hasOverlap = false;
|
|
2824
|
+
for (let i = 0; i < positions.length; i++) for (let j = i + 1; j < positions.length; j++) {
|
|
2825
|
+
const node1 = positions[i];
|
|
2826
|
+
const node2 = positions[j];
|
|
2827
|
+
const centerDistance = Math.sqrt(Math.pow(node1.x - node2.x, 2) + Math.pow(node1.y - node2.y, 2));
|
|
2828
|
+
const minDistance = (node1.width + node2.width) / 2 + (node1.height + node2.height) / 2 + 20;
|
|
2829
|
+
if (centerDistance < minDistance) {
|
|
2830
|
+
hasOverlap = true;
|
|
2831
|
+
debugLog(`Warning: Potential node overlap in subgraph ${subgraphId} - distance: ${centerDistance.toFixed(1)}, required: ${minDistance.toFixed(1)}`);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
if (!hasOverlap) debugLog(`✓ Node spacing validated for subgraph ${subgraphId} - no overlaps detected`);
|
|
2835
|
+
}
|
|
2836
|
+
function layoutMetaGraph(nodes, edges, subgraphLayouts, direction) {
|
|
2837
|
+
const g = new dagre.graphlib.Graph();
|
|
2838
|
+
g.setGraph({
|
|
2839
|
+
rankdir: direction,
|
|
2840
|
+
nodesep: CONTAINER_SEPARATION_HORIZONTAL,
|
|
2841
|
+
ranksep: CONTAINER_SEPARATION_VERTICAL,
|
|
2842
|
+
marginx: META_GRAPH_MARGIN,
|
|
2843
|
+
marginy: META_GRAPH_MARGIN,
|
|
2844
|
+
ranker: DAGRE_RANKER
|
|
2845
|
+
});
|
|
2846
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2847
|
+
debugLog("Laying out meta-graph");
|
|
2848
|
+
const connectionWeights = calculateConnectionWeights(nodes, edges);
|
|
2849
|
+
subgraphLayouts.forEach((layout, id) => {
|
|
2850
|
+
if (!layout.parentId) {
|
|
2851
|
+
g.setNode(id, {
|
|
2852
|
+
width: layout.width,
|
|
2853
|
+
height: layout.height
|
|
2854
|
+
});
|
|
2855
|
+
debugLog(`Added subgraph ${id} to meta-graph (width=${layout.width}, height=${layout.height})`);
|
|
2856
|
+
}
|
|
2857
|
+
});
|
|
2858
|
+
const standaloneNodes = nodes.filter((n) => !n.subgraph);
|
|
2859
|
+
standaloneNodes.forEach((node) => {
|
|
2860
|
+
const { imageUrl } = extractImageUrl(node.label);
|
|
2861
|
+
const size = calculateNodeSize(node.label, node.shape, !!imageUrl);
|
|
2862
|
+
g.setNode(node.id, {
|
|
2863
|
+
width: size.width,
|
|
2864
|
+
height: size.height
|
|
2865
|
+
});
|
|
2866
|
+
debugLog(`Added standalone node ${node.id} to meta-graph`);
|
|
2867
|
+
});
|
|
2868
|
+
connectionWeights.forEach((targets, sourceId) => {
|
|
2869
|
+
targets.forEach((weight, targetId) => {
|
|
2870
|
+
const sourceLayout = subgraphLayouts.get(sourceId);
|
|
2871
|
+
const targetLayout = subgraphLayouts.get(targetId);
|
|
2872
|
+
if (sourceLayout && sourceLayout.parentId === targetId || targetLayout && targetLayout.parentId === sourceId) return;
|
|
2873
|
+
const sourceIsTopLevel = !sourceLayout || !sourceLayout.parentId;
|
|
2874
|
+
const targetIsTopLevel = !targetLayout || !targetLayout.parentId;
|
|
2875
|
+
if (sourceIsTopLevel && targetIsTopLevel) {
|
|
2876
|
+
if (g.hasNode(sourceId) && g.hasNode(targetId)) {
|
|
2877
|
+
if (!g.hasEdge(sourceId, targetId)) {
|
|
2878
|
+
g.setEdge(sourceId, targetId, { weight });
|
|
2879
|
+
debugLog(`Added meta-edge from ${sourceId} to ${targetId} with weight ${weight}`);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
});
|
|
2884
|
+
});
|
|
2885
|
+
dagre.layout(g);
|
|
2886
|
+
const subgraphPositions = /* @__PURE__ */ new Map();
|
|
2887
|
+
const standalonePositions = /* @__PURE__ */ new Map();
|
|
2888
|
+
subgraphLayouts.forEach((layout, id) => {
|
|
2889
|
+
if (!layout.parentId) {
|
|
2890
|
+
const node = g.node(id);
|
|
2891
|
+
if (node) {
|
|
2892
|
+
subgraphPositions.set(id, {
|
|
2893
|
+
x: node.x - layout.width / 2,
|
|
2894
|
+
y: node.y - layout.height / 2
|
|
2895
|
+
});
|
|
2896
|
+
debugLog(`Positioned subgraph ${id} at (${node.x - layout.width / 2}, ${node.y - layout.height / 2})`);
|
|
2897
|
+
} else debugLog(`Warning: No position for subgraph ${id} in meta-graph`);
|
|
2898
|
+
}
|
|
2899
|
+
});
|
|
2900
|
+
const processedSubgraphs = /* @__PURE__ */ new Set();
|
|
2901
|
+
function layoutChildrenWithinParent(parentId) {
|
|
2902
|
+
const parentPos = subgraphPositions.get(parentId);
|
|
2903
|
+
const parentLayout = subgraphLayouts.get(parentId);
|
|
2904
|
+
if (!parentPos || !parentLayout) return false;
|
|
2905
|
+
const childIds = [];
|
|
2906
|
+
subgraphLayouts.forEach((layout, id) => {
|
|
2907
|
+
if (layout.parentId === parentId) childIds.push(id);
|
|
2908
|
+
});
|
|
2909
|
+
if (childIds.length === 0) return false;
|
|
2910
|
+
let maxNodeBottom = 0;
|
|
2911
|
+
const parentNodesBottom = getParentNodesBottomBoundary(parentId, parentLayout);
|
|
2912
|
+
if (parentNodesBottom > 0) {
|
|
2913
|
+
maxNodeBottom = parentNodesBottom;
|
|
2914
|
+
debugLog(`Parent ${parentId} has nodes extending to Y=${maxNodeBottom}, will position child subgraphs below this`);
|
|
2915
|
+
}
|
|
2916
|
+
const cg = new dagre.graphlib.Graph();
|
|
2917
|
+
const parentDir = direction;
|
|
2918
|
+
cg.setGraph({
|
|
2919
|
+
rankdir: parentDir,
|
|
2920
|
+
nodesep: NESTED_SUBGRAPH_SEPARATION_HORIZONTAL,
|
|
2921
|
+
ranksep: NESTED_SUBGRAPH_SEPARATION_VERTICAL,
|
|
2922
|
+
marginx: NESTED_CONTENT_MARGIN,
|
|
2923
|
+
marginy: NESTED_CONTENT_MARGIN,
|
|
2924
|
+
ranker: DAGRE_RANKER
|
|
2925
|
+
});
|
|
2926
|
+
cg.setDefaultEdgeLabel(() => ({}));
|
|
2927
|
+
childIds.forEach((cid) => {
|
|
2928
|
+
const cl = subgraphLayouts.get(cid);
|
|
2929
|
+
cg.setNode(cid, {
|
|
2930
|
+
width: cl.width,
|
|
2931
|
+
height: cl.height
|
|
2932
|
+
});
|
|
2933
|
+
});
|
|
2934
|
+
let hasEdges = false;
|
|
2935
|
+
childIds.forEach((sourceId) => {
|
|
2936
|
+
const targets = connectionWeights.get(sourceId);
|
|
2937
|
+
if (!targets) return;
|
|
2938
|
+
targets.forEach((weight, targetId) => {
|
|
2939
|
+
if (childIds.includes(targetId) && !cg.hasEdge(sourceId, targetId)) {
|
|
2940
|
+
cg.setEdge(sourceId, targetId, { weight });
|
|
2941
|
+
hasEdges = true;
|
|
2942
|
+
}
|
|
2943
|
+
});
|
|
2944
|
+
});
|
|
2945
|
+
if (!hasEdges && childIds.length > 1) for (let i = 0; i < childIds.length - 1; i++) cg.setEdge(childIds[i], childIds[i + 1], { weight: 1 });
|
|
2946
|
+
dagre.layout(cg);
|
|
2947
|
+
let minLeft = Infinity, minTop = Infinity, maxRight = -Infinity, maxBottom = -Infinity;
|
|
2948
|
+
const childTopLefts = /* @__PURE__ */ new Map();
|
|
2949
|
+
childIds.forEach((cid) => {
|
|
2950
|
+
const n = cg.node(cid);
|
|
2951
|
+
const cl = subgraphLayouts.get(cid);
|
|
2952
|
+
const left = n.x - cl.width / 2;
|
|
2953
|
+
const top = n.y - cl.height / 2;
|
|
2954
|
+
const right = n.x + cl.width / 2;
|
|
2955
|
+
const bottom = n.y + cl.height / 2;
|
|
2956
|
+
childTopLefts.set(cid, {
|
|
2957
|
+
x: left,
|
|
2958
|
+
y: top
|
|
2959
|
+
});
|
|
2960
|
+
minLeft = Math.min(minLeft, left);
|
|
2961
|
+
minTop = Math.min(minTop, top);
|
|
2962
|
+
maxRight = Math.max(maxRight, right);
|
|
2963
|
+
maxBottom = Math.max(maxBottom, bottom);
|
|
2964
|
+
});
|
|
2965
|
+
let contentOriginX = parentPos.x + SUBGRAPH_PADDING;
|
|
2966
|
+
let contentOriginY = parentPos.y + SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN + SUBGRAPH_PADDING;
|
|
2967
|
+
const isHorizontal = parentDir === "LR" || parentDir === "RL";
|
|
2968
|
+
if (isHorizontal) {
|
|
2969
|
+
const parentNodesMaxRight = Array.from(parentLayout.nodes.values()).reduce((acc, n) => Math.max(acc, n.x + n.width / 2), 0);
|
|
2970
|
+
if (parentNodesMaxRight > 0) {
|
|
2971
|
+
const proposedX = parentPos.x + Math.max(SUBGRAPH_PADDING, parentNodesMaxRight + MIXED_CONTENT_HORIZONTAL_SPACING);
|
|
2972
|
+
contentOriginX = Math.max(contentOriginX, proposedX);
|
|
2973
|
+
debugLog(`Adjusted child subgraph start position to X=${contentOriginX} for LR/RL to avoid parent nodes`);
|
|
2974
|
+
}
|
|
2975
|
+
} else if (maxNodeBottom > 0) {
|
|
2976
|
+
const nodeBottomInParentCoords = maxNodeBottom;
|
|
2977
|
+
const proposedY = parentPos.y + nodeBottomInParentCoords + MIXED_CONTENT_VERTICAL_SPACING;
|
|
2978
|
+
contentOriginY = Math.max(contentOriginY, proposedY);
|
|
2979
|
+
debugLog(`Adjusted child subgraph start position to Y=${contentOriginY} to avoid parent nodes (added ${MIXED_CONTENT_VERTICAL_SPACING}px spacing)`);
|
|
2980
|
+
}
|
|
2981
|
+
const availableWidth = parentLayout.width - SUBGRAPH_PADDING * 2;
|
|
2982
|
+
const usedVerticalSpace = contentOriginY - parentPos.y;
|
|
2983
|
+
const availableHeight = parentLayout.height - usedVerticalSpace - SUBGRAPH_PADDING;
|
|
2984
|
+
const contentWidth = maxRight - minLeft;
|
|
2985
|
+
const contentHeight = maxBottom - minTop;
|
|
2986
|
+
const centerOffsetX = isHorizontal ? Math.max(0, (availableWidth - contentWidth) / 2) : 0;
|
|
2987
|
+
const centerOffsetY = isHorizontal ? 0 : Math.max(0, (availableHeight - contentHeight) / 2);
|
|
2988
|
+
childIds.forEach((cid) => {
|
|
2989
|
+
const tl = childTopLefts.get(cid);
|
|
2990
|
+
const absX = contentOriginX + centerOffsetX + (tl.x - minLeft);
|
|
2991
|
+
const absY = contentOriginY + centerOffsetY + (tl.y - minTop);
|
|
2992
|
+
subgraphPositions.set(cid, {
|
|
2993
|
+
x: absX,
|
|
2994
|
+
y: absY
|
|
2995
|
+
});
|
|
2996
|
+
processedSubgraphs.add(cid);
|
|
2997
|
+
debugLog(`Positioned nested subgraph "${cid}" within parent ${parentId} at (${absX}, ${absY}) with centering`);
|
|
2998
|
+
});
|
|
2999
|
+
const requiredWidth = isHorizontal ? Math.max(contentWidth + SUBGRAPH_PADDING * 6, contentOriginX - parentPos.x + contentWidth + SUBGRAPH_PADDING * 3) : contentWidth + SUBGRAPH_PADDING * 6;
|
|
3000
|
+
const childrenBottomBoundary = contentOriginY + centerOffsetY + (maxBottom - minTop) - parentPos.y;
|
|
3001
|
+
const minRequiredHeight = isHorizontal ? Math.max(SUBGRAPH_HEADER_HEIGHT + SUBGRAPH_CONTENT_TOP_MARGIN + SUBGRAPH_PADDING * 2 + contentHeight + SUBGRAPH_PADDING * 2, 300) : Math.max(childrenBottomBoundary + SUBGRAPH_PADDING * 4, maxNodeBottom + MIXED_CONTENT_VERTICAL_SPACING + contentHeight + SUBGRAPH_PADDING * 4);
|
|
3002
|
+
const finalRequiredWidth = Math.max(requiredWidth, isHorizontal ? 600 : 400);
|
|
3003
|
+
const finalRequiredHeight = Math.max(minRequiredHeight, 300);
|
|
3004
|
+
if (finalRequiredWidth > parentLayout.width || finalRequiredHeight > parentLayout.height) {
|
|
3005
|
+
const oldWidth = parentLayout.width;
|
|
3006
|
+
const oldHeight = parentLayout.height;
|
|
3007
|
+
parentLayout.width = Math.max(parentLayout.width, finalRequiredWidth);
|
|
3008
|
+
parentLayout.height = Math.max(parentLayout.height, finalRequiredHeight);
|
|
3009
|
+
debugLog(`Expanded parent ${parentId} from ${oldWidth}x${oldHeight} to ${parentLayout.width}x${parentLayout.height} to fit nodes + ${childIds.length} child subgraphs (overflow-safe)`);
|
|
3010
|
+
}
|
|
3011
|
+
return true;
|
|
3012
|
+
}
|
|
3013
|
+
const processedParents = /* @__PURE__ */ new Set();
|
|
3014
|
+
let madeProgress = true;
|
|
3015
|
+
let safetyCounter = 0;
|
|
3016
|
+
while (madeProgress && safetyCounter < 100) {
|
|
3017
|
+
safetyCounter++;
|
|
3018
|
+
madeProgress = false;
|
|
3019
|
+
subgraphLayouts.forEach((_, id) => {
|
|
3020
|
+
if (subgraphPositions.has(id) && !processedParents.has(id)) {
|
|
3021
|
+
if (layoutChildrenWithinParent(id)) {
|
|
3022
|
+
madeProgress = true;
|
|
3023
|
+
processedParents.add(id);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
});
|
|
3027
|
+
}
|
|
3028
|
+
if (safetyCounter === 100) debugLog("Warning: nested subgraph layout reached iteration cap; potential cyclic dependency");
|
|
3029
|
+
standaloneNodes.forEach((node) => {
|
|
3030
|
+
const nodeLayout = g.node(node.id);
|
|
3031
|
+
if (nodeLayout) {
|
|
3032
|
+
const size = calculateNodeSize(node.label, node.shape);
|
|
3033
|
+
standalonePositions.set(node.id, {
|
|
3034
|
+
x: nodeLayout.x - size.width / 2,
|
|
3035
|
+
y: nodeLayout.y - size.height / 2
|
|
3036
|
+
});
|
|
3037
|
+
debugLog(`Positioned standalone node ${node.id} at (${nodeLayout.x - size.width / 2}, ${nodeLayout.y - size.height / 2})`);
|
|
3038
|
+
} else debugLog(`Warning: No position for standalone node ${node.id} in meta-graph`);
|
|
3039
|
+
});
|
|
3040
|
+
return {
|
|
3041
|
+
subgraphPositions,
|
|
3042
|
+
standalonePositions
|
|
3043
|
+
};
|
|
3044
|
+
}
|
|
3045
|
+
function createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgraphPositions, standalonePositions, direction) {
|
|
3046
|
+
const reactFlowNodes = [];
|
|
3047
|
+
debugLog("Creating React Flow elements");
|
|
3048
|
+
function getNodeColors(shape) {
|
|
3049
|
+
const colors = {
|
|
3050
|
+
rect: ["#E3F2FD", "#1976D2"],
|
|
3051
|
+
diamond: ["#FFF3E0", "#F57C00"],
|
|
3052
|
+
circle: ["#E8F5E8", "#388E3C"],
|
|
3053
|
+
stadium: ["#F3E5F5", "#7B1FA2"],
|
|
3054
|
+
round: ["#FCE4EC", "#C2185B"]
|
|
3055
|
+
}[shape] || ["#F0F4F8", "#2D3748"];
|
|
3056
|
+
return {
|
|
3057
|
+
backgroundColor: colors[0],
|
|
3058
|
+
borderColor: colors[1]
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
function getSubgraphColors(index) {
|
|
3062
|
+
const subgraphColors = [
|
|
3063
|
+
{
|
|
3064
|
+
bg: "rgba(227, 242, 253, 0.4)",
|
|
3065
|
+
border: "#1976D2"
|
|
3066
|
+
},
|
|
3067
|
+
{
|
|
3068
|
+
bg: "rgba(232, 245, 233, 0.4)",
|
|
3069
|
+
border: "#388E3C"
|
|
3070
|
+
},
|
|
3071
|
+
{
|
|
3072
|
+
bg: "rgba(243, 229, 245, 0.4)",
|
|
3073
|
+
border: "#7B1FA2"
|
|
3074
|
+
},
|
|
3075
|
+
{
|
|
3076
|
+
bg: "rgba(255, 243, 224, 0.4)",
|
|
3077
|
+
border: "#F57C00"
|
|
3078
|
+
},
|
|
3079
|
+
{
|
|
3080
|
+
bg: "rgba(252, 228, 236, 0.4)",
|
|
3081
|
+
border: "#C2185B"
|
|
3082
|
+
}
|
|
3083
|
+
];
|
|
3084
|
+
return subgraphColors[index % subgraphColors.length];
|
|
3085
|
+
}
|
|
3086
|
+
processSubgraphsInHierarchicalOrder(subgraphs).forEach((subgraph, index) => {
|
|
3087
|
+
const layout = subgraphLayouts.get(subgraph.id);
|
|
3088
|
+
const position = subgraphPositions.get(subgraph.id);
|
|
3089
|
+
if (layout && position) {
|
|
3090
|
+
const colors = getSubgraphColors(index);
|
|
3091
|
+
let finalPosition = position;
|
|
3092
|
+
if (layout.parentId) {
|
|
3093
|
+
const parentAbsPos = subgraphPositions.get(layout.parentId);
|
|
3094
|
+
if (parentAbsPos) finalPosition = {
|
|
3095
|
+
x: position.x - parentAbsPos.x,
|
|
3096
|
+
y: position.y - parentAbsPos.y
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
reactFlowNodes.push({
|
|
3100
|
+
id: `subgraph-${subgraph.id}`,
|
|
3101
|
+
type: "group",
|
|
3102
|
+
position: finalPosition,
|
|
3103
|
+
data: {
|
|
3104
|
+
backgroundColor: colors.bg,
|
|
3105
|
+
borderColor: colors.border,
|
|
3106
|
+
childNodes: subgraph.nodes,
|
|
3107
|
+
componentFields: [generateComponentFieldNameInput(subgraph.title)]
|
|
3108
|
+
},
|
|
3109
|
+
style: {
|
|
3110
|
+
width: layout.width,
|
|
3111
|
+
height: layout.height,
|
|
3112
|
+
zIndex: 0
|
|
3113
|
+
},
|
|
3114
|
+
selectable: true,
|
|
3115
|
+
draggable: true,
|
|
3116
|
+
connectable: true,
|
|
3117
|
+
parentId: layout.parentId ? `subgraph-${layout.parentId}` : void 0,
|
|
3118
|
+
extent: layout.parentId ? "parent" : void 0,
|
|
3119
|
+
zIndex: layout.parentId ? 1 : 0
|
|
3120
|
+
});
|
|
3121
|
+
}
|
|
3122
|
+
});
|
|
3123
|
+
nodes.forEach((node) => {
|
|
3124
|
+
const colors = getNodeColors(node.shape);
|
|
3125
|
+
const { imageUrl, cleanLabel } = extractImageUrl(node.label);
|
|
3126
|
+
const { tag, displayLabel } = parseLabelTag(cleanLabel);
|
|
3127
|
+
const portalType = resolvePortalNodeType(!!imageUrl, tag);
|
|
3128
|
+
let position;
|
|
3129
|
+
let parentNode;
|
|
3130
|
+
if (node.subgraph) {
|
|
3131
|
+
const subgraphLayout = subgraphLayouts.get(node.subgraph);
|
|
3132
|
+
const subgraphPosition = subgraphPositions.get(node.subgraph);
|
|
3133
|
+
const nodeLayout = subgraphLayout === null || subgraphLayout === void 0 ? void 0 : subgraphLayout.nodes.get(node.id);
|
|
3134
|
+
if (nodeLayout && subgraphPosition) {
|
|
3135
|
+
position = {
|
|
3136
|
+
x: nodeLayout.x - nodeLayout.width / 2,
|
|
3137
|
+
y: nodeLayout.y - nodeLayout.height / 2
|
|
3138
|
+
};
|
|
3139
|
+
parentNode = `subgraph-${node.subgraph}`;
|
|
3140
|
+
debugLog(`Node ${node.id} positioned at (${position.x}, ${position.y}) within subgraph ${node.subgraph}`);
|
|
3141
|
+
} else {
|
|
3142
|
+
position = {
|
|
3143
|
+
x: 0,
|
|
3144
|
+
y: 0
|
|
3145
|
+
};
|
|
3146
|
+
debugLog(`Warning: Missing layout data for node ${node.id} in subgraph ${node.subgraph}`);
|
|
3147
|
+
}
|
|
3148
|
+
} else {
|
|
3149
|
+
position = standalonePositions.get(node.id) || {
|
|
3150
|
+
x: 0,
|
|
3151
|
+
y: 0
|
|
3152
|
+
};
|
|
3153
|
+
debugLog(`Standalone node ${node.id} positioned at (${position.x}, ${position.y})`);
|
|
3154
|
+
}
|
|
3155
|
+
const isHorizontal = direction === "LR" || direction === "RL";
|
|
3156
|
+
const sourcePos = isHorizontal ? Position.Right : Position.Bottom;
|
|
3157
|
+
const targetPos = isHorizontal ? Position.Left : Position.Top;
|
|
3158
|
+
let wrapperWidth = 150;
|
|
3159
|
+
let wrapperHeight = 60;
|
|
3160
|
+
if (node.subgraph) {
|
|
3161
|
+
const subgraphLayout = subgraphLayouts.get(node.subgraph);
|
|
3162
|
+
const nodeLayout = subgraphLayout === null || subgraphLayout === void 0 ? void 0 : subgraphLayout.nodes.get(node.id);
|
|
3163
|
+
if (nodeLayout) {
|
|
3164
|
+
wrapperWidth = Math.max(20, Math.round(nodeLayout.width));
|
|
3165
|
+
wrapperHeight = Math.max(20, Math.round(nodeLayout.height));
|
|
3166
|
+
}
|
|
3167
|
+
} else {
|
|
3168
|
+
const size = calculateNodeSize(node.label, node.shape, !!imageUrl);
|
|
3169
|
+
wrapperWidth = Math.max(20, Math.round(size.width));
|
|
3170
|
+
wrapperHeight = Math.max(20, Math.round(size.height));
|
|
3171
|
+
}
|
|
3172
|
+
let data;
|
|
3173
|
+
switch (portalType.toLowerCase()) {
|
|
3174
|
+
case "image":
|
|
3175
|
+
data = { src: imageUrl !== null && imageUrl !== void 0 ? imageUrl : "" };
|
|
3176
|
+
break;
|
|
3177
|
+
case "shape":
|
|
3178
|
+
var _MERMAID_TO_PORTAL_SH;
|
|
3179
|
+
data = {
|
|
3180
|
+
shape: (_MERMAID_TO_PORTAL_SH = MERMAID_TO_PORTAL_SHAPE[node.shape]) !== null && _MERMAID_TO_PORTAL_SH !== void 0 ? _MERMAID_TO_PORTAL_SH : "rectangle",
|
|
3181
|
+
componentFields: [generateComponentFieldNameInput(displayLabel)],
|
|
3182
|
+
fill: colors.backgroundColor,
|
|
3183
|
+
stroke: colors.borderColor,
|
|
3184
|
+
strokeWidth: 2
|
|
3185
|
+
};
|
|
3186
|
+
break;
|
|
3187
|
+
case "default":
|
|
3188
|
+
data = {
|
|
3189
|
+
name: displayLabel,
|
|
3190
|
+
label: displayLabel,
|
|
3191
|
+
description: "",
|
|
3192
|
+
componentId: "file-note"
|
|
3193
|
+
};
|
|
3194
|
+
break;
|
|
3195
|
+
case "builder":
|
|
3196
|
+
data = {
|
|
3197
|
+
componentName: "file-note",
|
|
3198
|
+
componentFields: [
|
|
3199
|
+
generateComponentFieldNameInput(displayLabel),
|
|
3200
|
+
generateComponentFieldInput({
|
|
3201
|
+
label: "Label",
|
|
3202
|
+
data: displayLabel,
|
|
3203
|
+
type: ComponentInputType.TextInput
|
|
3204
|
+
}),
|
|
3205
|
+
generateComponentFieldInput({
|
|
3206
|
+
label: "Description",
|
|
3207
|
+
data: "",
|
|
3208
|
+
type: ComponentInputType.TextBox
|
|
3209
|
+
})
|
|
3210
|
+
]
|
|
3211
|
+
};
|
|
3212
|
+
break;
|
|
3213
|
+
case "text":
|
|
3214
|
+
data = { componentFields: [generateComponentFieldInput({
|
|
3215
|
+
componentFieldId: "text",
|
|
3216
|
+
label: "Text",
|
|
3217
|
+
data: displayLabel,
|
|
3218
|
+
type: ComponentInputType.TextBox
|
|
3219
|
+
})] };
|
|
3220
|
+
break;
|
|
3221
|
+
case "code":
|
|
3222
|
+
data = { componentFields: [generateComponentFieldNameInput(displayLabel), generateComponentFieldInput({
|
|
3223
|
+
label: "Code",
|
|
3224
|
+
data: "",
|
|
3225
|
+
type: ComponentInputType.CodeEditor
|
|
3226
|
+
})] };
|
|
3227
|
+
break;
|
|
3228
|
+
case "table":
|
|
3229
|
+
data = {
|
|
3230
|
+
componentFields: [generateComponentFieldNameInput(displayLabel)],
|
|
3231
|
+
columns: [
|
|
3232
|
+
"Task",
|
|
3233
|
+
"Owner",
|
|
3234
|
+
"Status",
|
|
3235
|
+
"Due"
|
|
3236
|
+
],
|
|
3237
|
+
rows: [[
|
|
3238
|
+
"Website revamp",
|
|
3239
|
+
"Amara",
|
|
3240
|
+
"In progress",
|
|
3241
|
+
"May 12"
|
|
3242
|
+
], [
|
|
3243
|
+
"Marketing sync",
|
|
3244
|
+
"Liu",
|
|
3245
|
+
"Blocked",
|
|
3246
|
+
"May 15"
|
|
3247
|
+
]]
|
|
3248
|
+
};
|
|
3249
|
+
break;
|
|
3250
|
+
case "cloud":
|
|
3251
|
+
data = { componentFields: [generateComponentFieldNameInput(displayLabel)] };
|
|
3252
|
+
break;
|
|
3253
|
+
case "comment":
|
|
3254
|
+
data = { componentFields: [generateComponentFieldNameInput(displayLabel)] };
|
|
3255
|
+
break;
|
|
3256
|
+
default: data = {
|
|
3257
|
+
name: displayLabel,
|
|
3258
|
+
label: displayLabel,
|
|
3259
|
+
description: "",
|
|
3260
|
+
componentId: "file-note"
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
reactFlowNodes.push({
|
|
3264
|
+
id: node.id,
|
|
3265
|
+
type: portalType,
|
|
3266
|
+
position,
|
|
3267
|
+
data,
|
|
3268
|
+
style: {
|
|
3269
|
+
width: wrapperWidth,
|
|
3270
|
+
height: wrapperHeight
|
|
3271
|
+
},
|
|
3272
|
+
sourcePosition: sourcePos,
|
|
3273
|
+
targetPosition: targetPos,
|
|
3274
|
+
parentId: parentNode,
|
|
3275
|
+
extent: parentNode ? "parent" : void 0,
|
|
3276
|
+
draggable: true,
|
|
3277
|
+
zIndex: 1
|
|
3278
|
+
});
|
|
3279
|
+
});
|
|
3280
|
+
return {
|
|
3281
|
+
nodes: reactFlowNodes,
|
|
3282
|
+
edges: edges.map((edge, index) => {
|
|
3283
|
+
const edgeColors = [
|
|
3284
|
+
"#1976D2",
|
|
3285
|
+
"#388E3C",
|
|
3286
|
+
"#F57C00",
|
|
3287
|
+
"#7B1FA2",
|
|
3288
|
+
"#C2185B"
|
|
3289
|
+
];
|
|
3290
|
+
const edgeColor = edgeColors[index % edgeColors.length];
|
|
3291
|
+
const edgeStyle = {
|
|
3292
|
+
stroke: edgeColor,
|
|
3293
|
+
strokeWidth: 2.5
|
|
3294
|
+
};
|
|
3295
|
+
const edgeType = "default";
|
|
3296
|
+
const animated = true;
|
|
3297
|
+
switch (edge.type) {
|
|
3298
|
+
case "-->":
|
|
3299
|
+
case "->": break;
|
|
3300
|
+
case "---":
|
|
3301
|
+
edgeStyle.strokeDasharray = "8,4";
|
|
3302
|
+
break;
|
|
3303
|
+
case "-.-":
|
|
3304
|
+
edgeStyle.strokeDasharray = "4,4";
|
|
3305
|
+
break;
|
|
3306
|
+
case "==>":
|
|
3307
|
+
case "===>":
|
|
3308
|
+
edgeStyle.strokeWidth = 4;
|
|
3309
|
+
break;
|
|
3310
|
+
}
|
|
3311
|
+
const sourceId = edge.isSourceSubgraph ? `subgraph-${edge.source}` : edge.source;
|
|
3312
|
+
const targetId = edge.isTargetSubgraph ? `subgraph-${edge.target}` : edge.target;
|
|
3313
|
+
return {
|
|
3314
|
+
id: `edge-${edge.source}-${edge.target}-${index}`,
|
|
3315
|
+
source: sourceId,
|
|
3316
|
+
target: targetId,
|
|
3317
|
+
label: edge.label,
|
|
3318
|
+
type: edgeType,
|
|
3319
|
+
animated,
|
|
3320
|
+
style: edgeStyle,
|
|
3321
|
+
labelStyle: {
|
|
3322
|
+
fontSize: "12px",
|
|
3323
|
+
fontWeight: "500",
|
|
3324
|
+
color: edgeColor,
|
|
3325
|
+
backgroundColor: "white",
|
|
3326
|
+
padding: "2px 6px",
|
|
3327
|
+
borderRadius: "4px",
|
|
3328
|
+
border: `1px solid ${edgeColor}`
|
|
3329
|
+
},
|
|
3330
|
+
markerEnd: {
|
|
3331
|
+
type: MarkerType.ArrowClosed,
|
|
3332
|
+
width: 20,
|
|
3333
|
+
height: 20,
|
|
3334
|
+
color: edgeColor
|
|
3335
|
+
},
|
|
3336
|
+
sourceHandle: direction === "LR" || direction === "RL" ? "source-right" : "source-bottom",
|
|
3337
|
+
targetHandle: direction === "LR" || direction === "RL" ? "target-left" : "target-top",
|
|
3338
|
+
zIndex: 0
|
|
3339
|
+
};
|
|
3340
|
+
})
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
function layoutGraph(nodes, edges, subgraphs, direction) {
|
|
3344
|
+
debugLog("Starting graph layout with direction:", direction);
|
|
3345
|
+
debugLog(`Input: ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs`);
|
|
3346
|
+
const subgraphLayouts = layoutSubgraphs(nodes, edges, subgraphs, direction);
|
|
3347
|
+
const { subgraphPositions, standalonePositions } = layoutMetaGraph(nodes, edges, subgraphLayouts, direction);
|
|
3348
|
+
adjustParentSizesAfterPositioning(subgraphLayouts, subgraphPositions, processSubgraphsInHierarchicalOrder(subgraphs), direction);
|
|
3349
|
+
return createReactFlowElements(nodes, edges, subgraphs, subgraphLayouts, subgraphPositions, standalonePositions, direction);
|
|
3350
|
+
}
|
|
3351
|
+
function getParticipantX(index) {
|
|
3352
|
+
const { COLUMN_WIDTH } = SEQUENCE_LAYOUT;
|
|
3353
|
+
return index * COLUMN_WIDTH + COLUMN_WIDTH / 2;
|
|
3354
|
+
}
|
|
3355
|
+
function getRowY(rowIndex) {
|
|
3356
|
+
const { HEADER_HEIGHT, ROW_HEIGHT } = SEQUENCE_LAYOUT;
|
|
3357
|
+
return HEADER_HEIGHT + rowIndex * ROW_HEIGHT + ROW_HEIGHT / 2;
|
|
3358
|
+
}
|
|
3359
|
+
function rowHandleId(rowIndex, side, handleType) {
|
|
3360
|
+
return `row-${rowIndex}-${side}-${handleType}`;
|
|
3361
|
+
}
|
|
3362
|
+
function convertSequenceDiagramToReactFlow(_x2) {
|
|
3363
|
+
return _convertSequenceDiagramToReactFlow.apply(this, arguments);
|
|
3364
|
+
}
|
|
3365
|
+
function _convertSequenceDiagramToReactFlow() {
|
|
3366
|
+
_convertSequenceDiagramToReactFlow = _asyncToGenerator(function* (mermaidCode) {
|
|
3367
|
+
const { participants, messages } = parseSequenceDiagram(mermaidCode);
|
|
3368
|
+
const { PARTICIPANT_NODE_WIDTH, MESSAGE_NODE_WIDTH, MESSAGE_NODE_HEIGHT, SELF_LOOP_OFFSET } = SEQUENCE_LAYOUT;
|
|
3369
|
+
const hasSelfLoop = messages.some((m) => m.from === m.to);
|
|
3370
|
+
const rowCount = messages.length + (hasSelfLoop ? 1 : 0);
|
|
3371
|
+
const nodes = [];
|
|
3372
|
+
const edges = [];
|
|
3373
|
+
for (const p of participants) nodes.push({
|
|
3374
|
+
id: `participant-${p.id}`,
|
|
3375
|
+
type: "sequenceParticipant",
|
|
3376
|
+
position: {
|
|
3377
|
+
x: getParticipantX(p.index) - PARTICIPANT_NODE_WIDTH / 2,
|
|
3378
|
+
y: 0
|
|
3379
|
+
},
|
|
3380
|
+
data: {
|
|
3381
|
+
source: "mermaid",
|
|
3382
|
+
label: p.name,
|
|
3383
|
+
rowCount,
|
|
3384
|
+
componentFields: [generateComponentFieldNameInput(p.name), generateComponentFieldInput({
|
|
3385
|
+
componentFieldId: "color",
|
|
3386
|
+
label: "Color",
|
|
3387
|
+
data: "#000000",
|
|
3388
|
+
type: ComponentInputType.ColorPicker
|
|
3389
|
+
})]
|
|
3390
|
+
}
|
|
3391
|
+
});
|
|
3392
|
+
for (const m of messages) {
|
|
3393
|
+
const fromParticipant = participants.find((p) => p.id === m.from);
|
|
3394
|
+
const toParticipant = participants.find((p) => p.id === m.to);
|
|
3395
|
+
if (!fromParticipant || !toParticipant) continue;
|
|
3396
|
+
const fromIndex = fromParticipant.index;
|
|
3397
|
+
const toIndex = toParticipant.index;
|
|
3398
|
+
const isSelf = fromIndex === toIndex;
|
|
3399
|
+
const goesRight = fromIndex < toIndex;
|
|
3400
|
+
const centerX = isSelf ? getParticipantX(fromIndex) + SELF_LOOP_OFFSET : (getParticipantX(fromIndex) + getParticipantX(toIndex)) / 2;
|
|
3401
|
+
const messageId = `message-${m.rowIndex}`;
|
|
3402
|
+
nodes.push({
|
|
3403
|
+
id: messageId,
|
|
3404
|
+
type: "shape",
|
|
3405
|
+
position: {
|
|
3406
|
+
x: centerX - MESSAGE_NODE_WIDTH / 2,
|
|
3407
|
+
y: getRowY(m.rowIndex) - MESSAGE_NODE_HEIGHT / 2
|
|
3408
|
+
},
|
|
3409
|
+
data: {
|
|
3410
|
+
source: "mermaid",
|
|
3411
|
+
shape: "rectangle",
|
|
3412
|
+
fill: "#f8fafc",
|
|
3413
|
+
stroke: "#CCCCCC",
|
|
3414
|
+
strokeWidth: 1,
|
|
3415
|
+
componentFields: [generateComponentFieldNameInput(m.label)]
|
|
3416
|
+
},
|
|
3417
|
+
style: {
|
|
3418
|
+
width: MESSAGE_NODE_WIDTH,
|
|
3419
|
+
height: MESSAGE_NODE_HEIGHT
|
|
3420
|
+
}
|
|
3421
|
+
});
|
|
3422
|
+
const edgeStyle = _objectSpread2({
|
|
3423
|
+
stroke: "#94a3b8",
|
|
3424
|
+
strokeWidth: 2
|
|
3425
|
+
}, m.lineStyle === "dashed" ? { strokeDasharray: "4 4" } : {});
|
|
3426
|
+
const markerEnd = m.arrowType !== "none" ? {
|
|
3427
|
+
type: m.arrowType === "filled" ? MarkerType.ArrowClosed : MarkerType.Arrow,
|
|
3428
|
+
width: 16,
|
|
3429
|
+
height: 16,
|
|
3430
|
+
color: "#94a3b8"
|
|
3431
|
+
} : void 0;
|
|
3432
|
+
const sourceSide = goesRight || isSelf ? "right" : "left";
|
|
3433
|
+
const targetSide = isSelf ? "right" : goesRight ? "left" : "right";
|
|
3434
|
+
edges.push({
|
|
3435
|
+
id: `edge-${m.rowIndex}-a`,
|
|
3436
|
+
source: `participant-${m.from}`,
|
|
3437
|
+
target: messageId,
|
|
3438
|
+
sourceHandle: rowHandleId(m.rowIndex, sourceSide, "source"),
|
|
3439
|
+
targetHandle: isSelf ? "target-top" : sourceSide === "right" ? "target-left" : "target-right",
|
|
3440
|
+
type: "smoothstep",
|
|
3441
|
+
style: edgeStyle,
|
|
3442
|
+
data: { source: "mermaid" }
|
|
3443
|
+
});
|
|
3444
|
+
edges.push(_objectSpread2(_objectSpread2({
|
|
3445
|
+
id: `edge-${m.rowIndex}-b`,
|
|
3446
|
+
source: messageId,
|
|
3447
|
+
target: `participant-${m.to}`,
|
|
3448
|
+
sourceHandle: isSelf ? "source-bottom" : goesRight ? "source-right" : "source-left",
|
|
3449
|
+
targetHandle: rowHandleId(m.rowIndex + (isSelf ? 1 : 0), targetSide, "target"),
|
|
3450
|
+
type: "smoothstep",
|
|
3451
|
+
style: m.lineStyle === "dashed" ? edgeStyle : {
|
|
3452
|
+
stroke: "#94a3b8",
|
|
3453
|
+
strokeWidth: 2
|
|
3454
|
+
}
|
|
3455
|
+
}, markerEnd ? { markerEnd } : {}), {}, { data: { source: "mermaid" } }));
|
|
3456
|
+
}
|
|
3457
|
+
return {
|
|
3458
|
+
nodes,
|
|
3459
|
+
edges
|
|
3460
|
+
};
|
|
3461
|
+
});
|
|
3462
|
+
return _convertSequenceDiagramToReactFlow.apply(this, arguments);
|
|
3463
|
+
}
|
|
3464
|
+
function convertFlowchartToReactFlow(_x3) {
|
|
3465
|
+
return _convertFlowchartToReactFlow.apply(this, arguments);
|
|
3466
|
+
}
|
|
3467
|
+
function _convertFlowchartToReactFlow() {
|
|
3468
|
+
_convertFlowchartToReactFlow = _asyncToGenerator(function* (mermaidCode) {
|
|
3469
|
+
debugLog("Starting Mermaid to React Flow conversion");
|
|
3470
|
+
debugLog("Mermaid code:", mermaidCode);
|
|
3471
|
+
const { nodes, edges, subgraphs, direction } = parseMermaidCode(mermaidCode);
|
|
3472
|
+
if (nodes.length === 0) {
|
|
3473
|
+
debugLog("No nodes found in Mermaid diagram");
|
|
3474
|
+
return {
|
|
3475
|
+
nodes: [],
|
|
3476
|
+
edges: []
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
debugLog(`Parsed ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs`);
|
|
3480
|
+
const graphedResult = layoutGraph(nodes, edges, subgraphs, direction);
|
|
3481
|
+
return {
|
|
3482
|
+
nodes: graphedResult.nodes.map((node) => _objectSpread2(_objectSpread2({}, node), {}, { data: _objectSpread2(_objectSpread2({}, node.data), {}, { source: "mermaid" }) })),
|
|
3483
|
+
edges: graphedResult.edges.map((edge) => _objectSpread2(_objectSpread2({}, edge), {}, { data: _objectSpread2(_objectSpread2({}, edge.data), {}, { source: "mermaid" }) }))
|
|
3484
|
+
};
|
|
3485
|
+
});
|
|
3486
|
+
return _convertFlowchartToReactFlow.apply(this, arguments);
|
|
3487
|
+
}
|
|
3488
|
+
function convertMermaidToReactFlow(_x4) {
|
|
3489
|
+
return _convertMermaidToReactFlow.apply(this, arguments);
|
|
3490
|
+
}
|
|
3491
|
+
function _convertMermaidToReactFlow() {
|
|
3492
|
+
_convertMermaidToReactFlow = _asyncToGenerator(function* (mermaidCode) {
|
|
3493
|
+
try {
|
|
3494
|
+
if (detectDiagramType(mermaidCode) === "sequence") return convertSequenceDiagramToReactFlow(mermaidCode);
|
|
3495
|
+
return convertFlowchartToReactFlow(mermaidCode);
|
|
3496
|
+
} catch (error) {
|
|
3497
|
+
console.error("Error converting Mermaid to React Flow:", error);
|
|
3498
|
+
return {
|
|
3499
|
+
nodes: [],
|
|
3500
|
+
edges: []
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
});
|
|
3504
|
+
return _convertMermaidToReactFlow.apply(this, arguments);
|
|
3505
|
+
}
|
|
3506
|
+
const BOUND_PADDING = 20;
|
|
3507
|
+
function createGroupNode(options) {
|
|
3508
|
+
var _options$id, _options$name;
|
|
3509
|
+
return {
|
|
3510
|
+
id: (_options$id = options.id) !== null && _options$id !== void 0 ? _options$id : generateUUID$1(),
|
|
3511
|
+
type: "group",
|
|
3512
|
+
position: {
|
|
3513
|
+
x: options.bounds.x,
|
|
3514
|
+
y: options.bounds.y
|
|
3515
|
+
},
|
|
3516
|
+
data: {
|
|
3517
|
+
childNodes: options.nodes,
|
|
3518
|
+
componentFields: [generateComponentFieldNameInput((_options$name = options.name) !== null && _options$name !== void 0 ? _options$name : "Group")]
|
|
3519
|
+
},
|
|
3520
|
+
style: {
|
|
3521
|
+
width: options.bounds.width + 20,
|
|
3522
|
+
height: options.bounds.height + 50
|
|
3523
|
+
}
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3526
|
+
function generateGroupNodeFromNodes(id, name, nodes) {
|
|
3527
|
+
const bounds = nodes.map((node) => {
|
|
3528
|
+
var _ref, _ref2, _node$height, _node$style, _node$measured, _ref3, _ref4, _node$width, _node$style2, _node$measured2;
|
|
3529
|
+
return {
|
|
3530
|
+
top: node.position.y,
|
|
3531
|
+
left: node.position.x,
|
|
3532
|
+
bottom: node.position.y + ((_ref = (_ref2 = (_node$height = node.height) !== null && _node$height !== void 0 ? _node$height : (_node$style = node.style) === null || _node$style === void 0 ? void 0 : _node$style.height) !== null && _ref2 !== void 0 ? _ref2 : (_node$measured = node.measured) === null || _node$measured === void 0 ? void 0 : _node$measured.height) !== null && _ref !== void 0 ? _ref : 0),
|
|
3533
|
+
right: node.position.x + ((_ref3 = (_ref4 = (_node$width = node.width) !== null && _node$width !== void 0 ? _node$width : (_node$style2 = node.style) === null || _node$style2 === void 0 ? void 0 : _node$style2.width) !== null && _ref4 !== void 0 ? _ref4 : (_node$measured2 = node.measured) === null || _node$measured2 === void 0 ? void 0 : _node$measured2.width) !== null && _ref3 !== void 0 ? _ref3 : 0)
|
|
3534
|
+
};
|
|
3535
|
+
});
|
|
3536
|
+
const minX = Math.min(...bounds.map((b) => b.left));
|
|
3537
|
+
const minY = Math.min(...bounds.map((b) => b.top));
|
|
3538
|
+
const maxX = Math.max(...bounds.map((b) => b.right));
|
|
3539
|
+
const maxY = Math.max(...bounds.map((b) => b.bottom));
|
|
3540
|
+
return createGroupNode({
|
|
3541
|
+
id,
|
|
3542
|
+
name,
|
|
3543
|
+
nodes: nodes.map((node) => node.id),
|
|
3544
|
+
bounds: {
|
|
3545
|
+
x: minX - BOUND_PADDING,
|
|
3546
|
+
y: minY - BOUND_PADDING,
|
|
3547
|
+
width: maxX - minX + BOUND_PADDING * 2,
|
|
3548
|
+
height: maxY - minY + BOUND_PADDING * 2
|
|
3549
|
+
}
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
function resolveCloudIcon(_x, _x2) {
|
|
3553
|
+
return _resolveCloudIcon.apply(this, arguments);
|
|
3554
|
+
}
|
|
3555
|
+
function _resolveCloudIcon() {
|
|
3556
|
+
_resolveCloudIcon = _asyncToGenerator(function* (cloud, serviceName) {
|
|
3557
|
+
const cloudName = cloud === null || cloud === void 0 ? void 0 : cloud.toLowerCase();
|
|
3558
|
+
const icon = (cloudName === "azure" ? [...(yield import("./__azure-icons.DgR1F6C6.mjs")).default] : cloudName === "aws" ? [...(yield import("./__aws-icons.agWBtDk7.mjs")).default] : [...(yield import("./__aws-icons.agWBtDk7.mjs")).default, ...(yield import("./__azure-icons.DgR1F6C6.mjs")).default]).find((icon$1) => icon$1.name.toLowerCase() === serviceName.toLowerCase());
|
|
3559
|
+
if (!icon) return null;
|
|
3560
|
+
return `/${icon.cloud.toLowerCase()}-icons/${icon.relativePath}`;
|
|
3561
|
+
});
|
|
3562
|
+
return _resolveCloudIcon.apply(this, arguments);
|
|
3563
|
+
}
|
|
3564
|
+
function resolveAnimatedNode(_x3) {
|
|
3565
|
+
return _resolveAnimatedNode.apply(this, arguments);
|
|
3566
|
+
}
|
|
3567
|
+
function _resolveAnimatedNode() {
|
|
3568
|
+
_resolveAnimatedNode = _asyncToGenerator(function* (animatedIcon) {
|
|
3569
|
+
const node = (yield import("./__animated-nodes.DI0XYTuw.mjs")).default.find((node$1) => node$1.name.toLowerCase() === animatedIcon.toLowerCase());
|
|
3570
|
+
if (!node) return null;
|
|
3571
|
+
return `/animated-nodes/${node.fileName}`;
|
|
3572
|
+
});
|
|
3573
|
+
return _resolveAnimatedNode.apply(this, arguments);
|
|
3574
|
+
}
|
|
3575
|
+
const EXPECTED_NODE_SIZE = {
|
|
3576
|
+
databaseTableSQL: {
|
|
3577
|
+
width: 500,
|
|
3578
|
+
height: 400
|
|
3579
|
+
},
|
|
3580
|
+
table: {
|
|
3581
|
+
width: 600,
|
|
3582
|
+
height: 400
|
|
3583
|
+
},
|
|
3584
|
+
code: {
|
|
3585
|
+
width: 300,
|
|
3586
|
+
height: 400
|
|
3587
|
+
}
|
|
3588
|
+
};
|
|
3589
|
+
const GROUP_BOUND_PADDING = 20;
|
|
3590
|
+
const GROUP_WIDTH_PADDING = 20;
|
|
3591
|
+
const GROUP_HEIGHT_PADDING = 50;
|
|
3592
|
+
const MIN_HORIZONTAL_GAP = 140;
|
|
3593
|
+
const ROW_ALIGNMENT_TOLERANCE = 80;
|
|
3594
|
+
function resolveDimension(value) {
|
|
3595
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3596
|
+
}
|
|
3597
|
+
function getNodeSize(node) {
|
|
3598
|
+
var _ref, _ref2, _ref3, _resolveDimension, _node$style, _node$measured, _ref4, _ref5, _ref6, _resolveDimension2, _node$style2, _node$measured2;
|
|
3599
|
+
const expectedSize = node.type ? EXPECTED_NODE_SIZE[node.type] : void 0;
|
|
3600
|
+
return {
|
|
3601
|
+
width: (_ref = (_ref2 = (_ref3 = (_resolveDimension = resolveDimension(node.width)) !== null && _resolveDimension !== void 0 ? _resolveDimension : resolveDimension((_node$style = node.style) === null || _node$style === void 0 ? void 0 : _node$style.width)) !== null && _ref3 !== void 0 ? _ref3 : resolveDimension((_node$measured = node.measured) === null || _node$measured === void 0 ? void 0 : _node$measured.width)) !== null && _ref2 !== void 0 ? _ref2 : expectedSize === null || expectedSize === void 0 ? void 0 : expectedSize.width) !== null && _ref !== void 0 ? _ref : 0,
|
|
3602
|
+
height: (_ref4 = (_ref5 = (_ref6 = (_resolveDimension2 = resolveDimension(node.height)) !== null && _resolveDimension2 !== void 0 ? _resolveDimension2 : resolveDimension((_node$style2 = node.style) === null || _node$style2 === void 0 ? void 0 : _node$style2.height)) !== null && _ref6 !== void 0 ? _ref6 : resolveDimension((_node$measured2 = node.measured) === null || _node$measured2 === void 0 ? void 0 : _node$measured2.height)) !== null && _ref5 !== void 0 ? _ref5 : expectedSize === null || expectedSize === void 0 ? void 0 : expectedSize.height) !== null && _ref4 !== void 0 ? _ref4 : 0
|
|
3603
|
+
};
|
|
3604
|
+
}
|
|
3605
|
+
function resizeNodesLayouts(nodes) {
|
|
3606
|
+
const resizeRules = nodes.map((node) => {
|
|
3607
|
+
if (!node.type) return;
|
|
3608
|
+
const expectedSize = EXPECTED_NODE_SIZE[node.type];
|
|
3609
|
+
if (!expectedSize) return;
|
|
3610
|
+
const size = getNodeSize(node);
|
|
3611
|
+
return {
|
|
3612
|
+
startX: node.position.x + expectedSize.width,
|
|
3613
|
+
startY: node.position.y + expectedSize.height,
|
|
3614
|
+
deltaX: Math.max(0, size.width - expectedSize.width),
|
|
3615
|
+
deltaY: Math.max(0, size.height - expectedSize.height)
|
|
3616
|
+
};
|
|
3617
|
+
}).filter((rule) => Boolean(rule));
|
|
3618
|
+
const resizedNodes = nodes.map((node) => {
|
|
3619
|
+
var _node$data;
|
|
3620
|
+
const childNodeIds = (_node$data = node.data) === null || _node$data === void 0 ? void 0 : _node$data.childNodes;
|
|
3621
|
+
if (Array.isArray(childNodeIds)) return _objectSpread2({}, node);
|
|
3622
|
+
const offsetX = resizeRules.reduce((total, rule) => {
|
|
3623
|
+
if (!rule || rule.deltaX === 0 || node.position.x < rule.startX) return total;
|
|
3624
|
+
return total + rule.deltaX;
|
|
3625
|
+
}, 0);
|
|
3626
|
+
const offsetY = resizeRules.reduce((total, rule) => {
|
|
3627
|
+
if (!rule || rule.deltaY === 0 || node.position.y < rule.startY) return total;
|
|
3628
|
+
return total + rule.deltaY;
|
|
3629
|
+
}, 0);
|
|
3630
|
+
if (offsetX === 0 && offsetY === 0) return node;
|
|
3631
|
+
return _objectSpread2(_objectSpread2({}, node), {}, { position: {
|
|
3632
|
+
x: node.position.x + offsetX,
|
|
3633
|
+
y: node.position.y + offsetY
|
|
3634
|
+
} });
|
|
3635
|
+
});
|
|
3636
|
+
const resizedNodesById = new Map(resizedNodes.map((node) => [node.id, node]));
|
|
3637
|
+
const rowNodes = resizedNodes.filter((node) => {
|
|
3638
|
+
var _node$data2;
|
|
3639
|
+
return !Array.isArray((_node$data2 = node.data) === null || _node$data2 === void 0 ? void 0 : _node$data2.childNodes);
|
|
3640
|
+
}).sort((a, b) => a.position.y === b.position.y ? a.position.x - b.position.x : a.position.y - b.position.y);
|
|
3641
|
+
const rows = [];
|
|
3642
|
+
for (const node of rowNodes) {
|
|
3643
|
+
const row = rows.find((currentRow) => Math.abs(currentRow[0].position.y - node.position.y) <= ROW_ALIGNMENT_TOLERANCE);
|
|
3644
|
+
if (row) {
|
|
3645
|
+
row.push(node);
|
|
3646
|
+
continue;
|
|
3647
|
+
}
|
|
3648
|
+
rows.push([node]);
|
|
3649
|
+
}
|
|
3650
|
+
for (const row of rows) {
|
|
3651
|
+
row.sort((a, b) => a.position.x - b.position.x);
|
|
3652
|
+
for (let index = 1; index < row.length; index += 1) {
|
|
3653
|
+
const previousNode = row[index - 1];
|
|
3654
|
+
const currentNode = row[index];
|
|
3655
|
+
const previousNodeSize = getNodeSize(previousNode);
|
|
3656
|
+
const minimumX = previousNode.position.x + previousNodeSize.width + MIN_HORIZONTAL_GAP;
|
|
3657
|
+
if (currentNode.position.x >= minimumX) continue;
|
|
3658
|
+
const shiftedNode = _objectSpread2(_objectSpread2({}, currentNode), {}, { position: {
|
|
3659
|
+
x: minimumX,
|
|
3660
|
+
y: currentNode.position.y
|
|
3661
|
+
} });
|
|
3662
|
+
row[index] = shiftedNode;
|
|
3663
|
+
resizedNodesById.set(shiftedNode.id, shiftedNode);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
return resizedNodes.map((node) => {
|
|
3667
|
+
var _resizedNodesById$get, _node$data3;
|
|
3668
|
+
const shiftedNode = (_resizedNodesById$get = resizedNodesById.get(node.id)) !== null && _resizedNodesById$get !== void 0 ? _resizedNodesById$get : node;
|
|
3669
|
+
const childNodeIds = (_node$data3 = node.data) === null || _node$data3 === void 0 ? void 0 : _node$data3.childNodes;
|
|
3670
|
+
if (!Array.isArray(childNodeIds)) return shiftedNode;
|
|
3671
|
+
const childNodes = childNodeIds.map((childNodeId) => typeof childNodeId === "string" ? resizedNodesById.get(childNodeId) : void 0).filter((childNode) => Boolean(childNode));
|
|
3672
|
+
if (childNodes.length === 0) return shiftedNode;
|
|
3673
|
+
const bounds = childNodes.map((childNode) => {
|
|
3674
|
+
const size = getNodeSize(childNode);
|
|
3675
|
+
return {
|
|
3676
|
+
left: childNode.position.x,
|
|
3677
|
+
top: childNode.position.y,
|
|
3678
|
+
right: childNode.position.x + size.width,
|
|
3679
|
+
bottom: childNode.position.y + size.height
|
|
3680
|
+
};
|
|
3681
|
+
});
|
|
3682
|
+
const minX = Math.min(...bounds.map((bound) => bound.left));
|
|
3683
|
+
const minY = Math.min(...bounds.map((bound) => bound.top));
|
|
3684
|
+
const maxX = Math.max(...bounds.map((bound) => bound.right));
|
|
3685
|
+
const maxY = Math.max(...bounds.map((bound) => bound.bottom));
|
|
3686
|
+
return _objectSpread2(_objectSpread2({}, shiftedNode), {}, {
|
|
3687
|
+
position: {
|
|
3688
|
+
x: minX - GROUP_BOUND_PADDING,
|
|
3689
|
+
y: minY - GROUP_BOUND_PADDING
|
|
3690
|
+
},
|
|
3691
|
+
style: _objectSpread2(_objectSpread2({}, shiftedNode.style), {}, {
|
|
3692
|
+
width: maxX - minX + GROUP_BOUND_PADDING * 2 + GROUP_WIDTH_PADDING,
|
|
3693
|
+
height: maxY - minY + GROUP_BOUND_PADDING * 2 + GROUP_HEIGHT_PADDING
|
|
3694
|
+
})
|
|
3695
|
+
});
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
function hasValidPosition(position) {
|
|
3699
|
+
if (!position) return false;
|
|
3700
|
+
return Number.isFinite(position.x) && Number.isFinite(position.y);
|
|
3701
|
+
}
|
|
3702
|
+
function normalizeContextMarker(marker) {
|
|
3703
|
+
if (!marker) return;
|
|
3704
|
+
const markerType = marker.type.trim().toLowerCase();
|
|
3705
|
+
let type;
|
|
3706
|
+
if (markerType === "arrow") type = MarkerType.Arrow;
|
|
3707
|
+
else if (markerType === "arrowclosed" || markerType === "arrow-closed" || markerType === "arrow_closed") type = MarkerType.ArrowClosed;
|
|
3708
|
+
if (!type) return;
|
|
3709
|
+
if (marker.color) return {
|
|
3710
|
+
type,
|
|
3711
|
+
color: marker.color
|
|
3712
|
+
};
|
|
3713
|
+
return { type };
|
|
3714
|
+
}
|
|
3715
|
+
function convertMermaidToReactFlowWithContext(_x3, _x4, _x5) {
|
|
3716
|
+
return _convertMermaidToReactFlowWithContext.apply(this, arguments);
|
|
3717
|
+
}
|
|
3718
|
+
function _convertMermaidToReactFlowWithContext() {
|
|
3719
|
+
_convertMermaidToReactFlowWithContext = _asyncToGenerator(function* (mermaidCode, context, options) {
|
|
3720
|
+
var _context$groups;
|
|
3721
|
+
const validatedContext = contextSchema.parse(context);
|
|
3722
|
+
const reactFlowData = yield convertMermaidToReactFlow(mermaidCode);
|
|
3723
|
+
const rfNodesPromises = reactFlowData.nodes.map(function() {
|
|
3724
|
+
var _ref = _asyncToGenerator(function* (node) {
|
|
3725
|
+
var _validatedContext$nod, _clonedNode$data$comp, _ctx$data, _clonedNode$data, _ctx$style, _ctx$style2, _ctx$style3, _ctx$style4;
|
|
3726
|
+
const ctx = (_validatedContext$nod = validatedContext.nodes) === null || _validatedContext$nod === void 0 ? void 0 : _validatedContext$nod[node.id];
|
|
3727
|
+
if (!ctx) return node;
|
|
3728
|
+
const clonedNode = JSON.parse(JSON.stringify(node));
|
|
3729
|
+
const componentFields = (_clonedNode$data$comp = clonedNode.data.componentFields) !== null && _clonedNode$data$comp !== void 0 ? _clonedNode$data$comp : [];
|
|
3730
|
+
(_ctx$data = ctx.data) !== null && _ctx$data !== void 0 || (ctx.data = {});
|
|
3731
|
+
(_clonedNode$data = clonedNode.data) !== null && _clonedNode$data !== void 0 || (clonedNode.data = {});
|
|
3732
|
+
if (hasValidPosition(ctx.___position)) clonedNode.position = {
|
|
3733
|
+
x: ctx.___position.x,
|
|
3734
|
+
y: ctx.___position.y
|
|
3735
|
+
};
|
|
3736
|
+
if (ctx.type) {
|
|
3737
|
+
clonedNode.type = ctx.type;
|
|
3738
|
+
delete clonedNode.style;
|
|
3739
|
+
if (ctx.type === "cloud") {
|
|
3740
|
+
clonedNode.height = 150;
|
|
3741
|
+
clonedNode.width = 150;
|
|
3742
|
+
if (ctx.service) {
|
|
3743
|
+
const cloudIcon = (options === null || options === void 0 ? void 0 : options.resolveCloudIcon) ? yield options.resolveCloudIcon(ctx.cloud, ctx.service) : yield resolveCloudIcon(ctx.cloud, ctx.service);
|
|
3744
|
+
if (cloudIcon) clonedNode.data.iconSrc = cloudIcon;
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
if (ctx.type === "image" && ctx.src) clonedNode.data = _objectSpread2(_objectSpread2({}, clonedNode.data), {}, { src: ctx.src });
|
|
3748
|
+
if (ctx.type === "gif") {
|
|
3749
|
+
if (ctx.animatedIcon) {
|
|
3750
|
+
const animatedNodeSrc = yield resolveAnimatedNode(ctx.animatedIcon);
|
|
3751
|
+
if (animatedNodeSrc) clonedNode.data = _objectSpread2(_objectSpread2({}, clonedNode.data), {}, { src: animatedNodeSrc });
|
|
3752
|
+
} else if (ctx.src) clonedNode.data = _objectSpread2(_objectSpread2({}, clonedNode.data), {}, { src: ctx.src });
|
|
3753
|
+
}
|
|
3754
|
+
if (ctx.type === "text" && ctx.value) {
|
|
3755
|
+
const textComponentField = generateComponentFieldInput({
|
|
3756
|
+
type: ComponentInputType.TextInput,
|
|
3757
|
+
componentFieldId: "text",
|
|
3758
|
+
label: "Text",
|
|
3759
|
+
data: ctx.value,
|
|
3760
|
+
isReadonly: true
|
|
3761
|
+
});
|
|
3762
|
+
componentFields.unshift(textComponentField);
|
|
3763
|
+
}
|
|
3764
|
+
if (ctx.type === "code" && ctx.value) {
|
|
3765
|
+
const textComponentField = generateComponentFieldInput({
|
|
3766
|
+
type: ComponentInputType.CodeEditor,
|
|
3767
|
+
componentFieldId: "code",
|
|
3768
|
+
label: "Code",
|
|
3769
|
+
data: ctx.value,
|
|
3770
|
+
isReadonly: true
|
|
3771
|
+
});
|
|
3772
|
+
componentFields.unshift(textComponentField);
|
|
3773
|
+
}
|
|
3774
|
+
if (ctx.type === "table" && ctx.table) {
|
|
3775
|
+
var _ctx$table$rows, _ctx$table$columns;
|
|
3776
|
+
clonedNode.data.title = ctx.name;
|
|
3777
|
+
clonedNode.data.rows = (_ctx$table$rows = ctx.table.rows) !== null && _ctx$table$rows !== void 0 ? _ctx$table$rows : [];
|
|
3778
|
+
clonedNode.data.columns = (_ctx$table$columns = ctx.table.columns) !== null && _ctx$table$columns !== void 0 ? _ctx$table$columns : [];
|
|
3779
|
+
}
|
|
3780
|
+
if (ctx.type === "data-source" || ctx.type === "db-table") {
|
|
3781
|
+
ctx.type = "databaseTableSQL";
|
|
3782
|
+
clonedNode.type = "databaseTableSQL";
|
|
3783
|
+
}
|
|
3784
|
+
if (ctx.type === "component") {
|
|
3785
|
+
ctx.type = "builder";
|
|
3786
|
+
clonedNode.type = "builder";
|
|
3787
|
+
clonedNode.data.componentId = ctx.componentId;
|
|
3788
|
+
}
|
|
3789
|
+
if (ctx.type === "shape" && ctx.shape) {
|
|
3790
|
+
clonedNode.data.shape = ctx.shape;
|
|
3791
|
+
if (ctx.shape === "or" || ctx.shape === "summing-junction") {
|
|
3792
|
+
var _clonedNode$width, _clonedNode$height;
|
|
3793
|
+
const maxSize = Math.max((_clonedNode$width = clonedNode.width) !== null && _clonedNode$width !== void 0 ? _clonedNode$width : 0, (_clonedNode$height = clonedNode.height) !== null && _clonedNode$height !== void 0 ? _clonedNode$height : 0, 200);
|
|
3794
|
+
clonedNode.width = maxSize;
|
|
3795
|
+
clonedNode.height = maxSize;
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
if (ctx.name) ctx.data["Name"] = {
|
|
3800
|
+
type: ComponentInputType.TextInput,
|
|
3801
|
+
value: ctx.name
|
|
3802
|
+
};
|
|
3803
|
+
for (const key in ctx.data) {
|
|
3804
|
+
const metaInput = ctx.data[key];
|
|
3805
|
+
if (metaInput === void 0) continue;
|
|
3806
|
+
const componentField = getComponentFieldByLabel(componentFields, key);
|
|
3807
|
+
const newComponentField = generateComponentFieldInput({
|
|
3808
|
+
label: key,
|
|
3809
|
+
type: metaInput.type,
|
|
3810
|
+
data: metaInput.value,
|
|
3811
|
+
options: metaInput.options
|
|
3812
|
+
});
|
|
3813
|
+
if (!componentField) componentFields.push(newComponentField);
|
|
3814
|
+
else {
|
|
3815
|
+
componentField.type = newComponentField.type;
|
|
3816
|
+
componentField.data = newComponentField.data;
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
if (ctx.dbConfig) clonedNode.data.serviceTable = {
|
|
3820
|
+
serviceId: ctx.dbConfig.service,
|
|
3821
|
+
serviceDbId: ctx.dbConfig.database,
|
|
3822
|
+
tableName: ctx.dbConfig.tableName
|
|
3823
|
+
};
|
|
3824
|
+
if ((_ctx$style = ctx.style) === null || _ctx$style === void 0 ? void 0 : _ctx$style.height) clonedNode.height = ctx.style.height;
|
|
3825
|
+
if ((_ctx$style2 = ctx.style) === null || _ctx$style2 === void 0 ? void 0 : _ctx$style2.width) clonedNode.width = ctx.style.width;
|
|
3826
|
+
clonedNode.data = _objectSpread2(_objectSpread2(_objectSpread2({}, clonedNode.data), objectPick((_ctx$style3 = ctx.style) !== null && _ctx$style3 !== void 0 ? _ctx$style3 : {}, [
|
|
3827
|
+
"fill",
|
|
3828
|
+
"stroke",
|
|
3829
|
+
"strokeWidth",
|
|
3830
|
+
"strokeStyle",
|
|
3831
|
+
"borderRadius",
|
|
3832
|
+
"borderAnimationEnabled"
|
|
3833
|
+
])), {}, {
|
|
3834
|
+
componentFields,
|
|
3835
|
+
strokeAnimation: ((_ctx$style4 = ctx.style) === null || _ctx$style4 === void 0 ? void 0 : _ctx$style4.borderAnimationEnabled) ? "dash" : void 0
|
|
3836
|
+
}, ctx === null || ctx === void 0 ? void 0 : ctx.___internal);
|
|
3837
|
+
return clonedNode;
|
|
3838
|
+
});
|
|
3839
|
+
return function(_x) {
|
|
3840
|
+
return _ref.apply(this, arguments);
|
|
3841
|
+
};
|
|
3842
|
+
}());
|
|
3843
|
+
const resolvedNodes = yield Promise.all(rfNodesPromises);
|
|
3844
|
+
const groupNodes = Object.entries((_context$groups = context.groups) !== null && _context$groups !== void 0 ? _context$groups : {}).map(([nodeId, groupCtx]) => {
|
|
3845
|
+
var _groupCtx$nodes, _groupCtx$name;
|
|
3846
|
+
const childNodes = arrayNonNullable(((_groupCtx$nodes = groupCtx.nodes) !== null && _groupCtx$nodes !== void 0 ? _groupCtx$nodes : []).map((nodeId$1) => resolvedNodes.find((n) => n.id === nodeId$1)));
|
|
3847
|
+
return generateGroupNodeFromNodes(nodeId, (_groupCtx$name = groupCtx.name) !== null && _groupCtx$name !== void 0 ? _groupCtx$name : "Group", childNodes);
|
|
3848
|
+
});
|
|
3849
|
+
const rfEdgesPromises = reactFlowData.edges.map(function() {
|
|
3850
|
+
var _ref2 = _asyncToGenerator(function* (edge) {
|
|
3851
|
+
var _validatedContext$edg, _ctx$type, _ctx$sourceHandle, _ctx$targetHandle, _normalizeContextMark, _normalizeContextMark2, _ctx$label, _ctx$style$borderAnim, _ctx$style5, _ctx$style$stroke, _ctx$style6, _edge$style, _ctx$style$strokeWidt, _ctx$style7, _edge$style2, _ctx$style8, _ctx$style9, _ctx$style10, _edge$style3;
|
|
3852
|
+
const ctx = (_validatedContext$edg = validatedContext.edges) === null || _validatedContext$edg === void 0 ? void 0 : _validatedContext$edg[`${edge.source}-${edge.target}`];
|
|
3853
|
+
if (!ctx) return edge;
|
|
3854
|
+
return _objectSpread2(_objectSpread2({}, edge), {}, {
|
|
3855
|
+
type: (_ctx$type = ctx.type) !== null && _ctx$type !== void 0 ? _ctx$type : edge.type,
|
|
3856
|
+
sourceHandle: (_ctx$sourceHandle = ctx.sourceHandle) !== null && _ctx$sourceHandle !== void 0 ? _ctx$sourceHandle : edge.sourceHandle,
|
|
3857
|
+
targetHandle: (_ctx$targetHandle = ctx.targetHandle) !== null && _ctx$targetHandle !== void 0 ? _ctx$targetHandle : edge.targetHandle,
|
|
3858
|
+
markerStart: (_normalizeContextMark = normalizeContextMarker(ctx.markerStart)) !== null && _normalizeContextMark !== void 0 ? _normalizeContextMark : edge.markerStart,
|
|
3859
|
+
markerEnd: (_normalizeContextMark2 = normalizeContextMarker(ctx.markerEnd)) !== null && _normalizeContextMark2 !== void 0 ? _normalizeContextMark2 : edge.markerEnd,
|
|
3860
|
+
data: _objectSpread2(_objectSpread2({}, edge.data), ctx === null || ctx === void 0 ? void 0 : ctx.___internal),
|
|
3861
|
+
label: (_ctx$label = ctx.label) !== null && _ctx$label !== void 0 ? _ctx$label : edge.label,
|
|
3862
|
+
animated: (_ctx$style$borderAnim = (_ctx$style5 = ctx.style) === null || _ctx$style5 === void 0 ? void 0 : _ctx$style5.borderAnimationEnabled) !== null && _ctx$style$borderAnim !== void 0 ? _ctx$style$borderAnim : edge.animated,
|
|
3863
|
+
style: _objectSpread2(_objectSpread2({}, edge.style), {}, {
|
|
3864
|
+
stroke: (_ctx$style$stroke = (_ctx$style6 = ctx.style) === null || _ctx$style6 === void 0 ? void 0 : _ctx$style6.stroke) !== null && _ctx$style$stroke !== void 0 ? _ctx$style$stroke : (_edge$style = edge.style) === null || _edge$style === void 0 ? void 0 : _edge$style.stroke,
|
|
3865
|
+
strokeWidth: (_ctx$style$strokeWidt = (_ctx$style7 = ctx.style) === null || _ctx$style7 === void 0 ? void 0 : _ctx$style7.strokeWidth) !== null && _ctx$style$strokeWidt !== void 0 ? _ctx$style$strokeWidt : (_edge$style2 = edge.style) === null || _edge$style2 === void 0 ? void 0 : _edge$style2.strokeWidth,
|
|
3866
|
+
strokeDasharray: ((_ctx$style8 = ctx.style) === null || _ctx$style8 === void 0 ? void 0 : _ctx$style8.strokeStyle) === "dashed" ? "4 2" : ((_ctx$style9 = ctx.style) === null || _ctx$style9 === void 0 ? void 0 : _ctx$style9.strokeStyle) === "dotted" ? "1 2" : ((_ctx$style10 = ctx.style) === null || _ctx$style10 === void 0 ? void 0 : _ctx$style10.strokeStyle) === "solid" ? void 0 : (_edge$style3 = edge.style) === null || _edge$style3 === void 0 ? void 0 : _edge$style3.strokeDasharray
|
|
3867
|
+
})
|
|
3868
|
+
});
|
|
3869
|
+
});
|
|
3870
|
+
return function(_x2) {
|
|
3871
|
+
return _ref2.apply(this, arguments);
|
|
3872
|
+
};
|
|
3873
|
+
}());
|
|
3874
|
+
const resolvedEdges = yield Promise.all(rfEdgesPromises);
|
|
3875
|
+
const combinedNodes = [...groupNodes, ...resolvedNodes.map((node) => {
|
|
3876
|
+
const nodeParent = groupNodes.find((groupNode) => {
|
|
3877
|
+
var _groupNode$data$child;
|
|
3878
|
+
return (_groupNode$data$child = groupNode.data.childNodes) === null || _groupNode$data$child === void 0 ? void 0 : _groupNode$data$child.includes(node.id);
|
|
3879
|
+
});
|
|
3880
|
+
if (nodeParent) return _objectSpread2(_objectSpread2({}, node), {}, { parentId: nodeParent.id });
|
|
3881
|
+
return node;
|
|
3882
|
+
})];
|
|
3883
|
+
return {
|
|
3884
|
+
edges: resolvedEdges,
|
|
3885
|
+
nodes: (options === null || options === void 0 ? void 0 : options.repositionNodes) ? resizeNodesLayouts(combinedNodes) : combinedNodes
|
|
3886
|
+
};
|
|
3887
|
+
});
|
|
3888
|
+
return _convertMermaidToReactFlowWithContext.apply(this, arguments);
|
|
3889
|
+
}
|
|
3890
|
+
const CONTEXT_SHAPES = new Set([
|
|
3891
|
+
"rectangle",
|
|
3892
|
+
"rounded-rect",
|
|
3893
|
+
"ellipse",
|
|
3894
|
+
"diamond",
|
|
3895
|
+
"triangle",
|
|
3896
|
+
"parallelogram",
|
|
3897
|
+
"trapezoid",
|
|
3898
|
+
"hexagon",
|
|
3899
|
+
"document",
|
|
3900
|
+
"cylinder",
|
|
3901
|
+
"delay",
|
|
3902
|
+
"off-page-connector",
|
|
3903
|
+
"display",
|
|
3904
|
+
"collate",
|
|
3905
|
+
"sort",
|
|
3906
|
+
"terminator",
|
|
3907
|
+
"or",
|
|
3908
|
+
"database",
|
|
3909
|
+
"multiple-documents",
|
|
3910
|
+
"subroutine",
|
|
3911
|
+
"manual-input",
|
|
3912
|
+
"summing-junction",
|
|
3913
|
+
"internal-storage"
|
|
3914
|
+
]);
|
|
3915
|
+
const COMPONENT_INPUT_TYPES = new Set(Object.values(ComponentInputType));
|
|
3916
|
+
const INLINE_MERMAID_LABEL_MAX_LENGTH = 32;
|
|
3917
|
+
const KNOWN_NODE_DATA_KEYS = new Set([
|
|
3918
|
+
"componentFields",
|
|
3919
|
+
"shape",
|
|
3920
|
+
"fill",
|
|
3921
|
+
"stroke",
|
|
3922
|
+
"strokeWidth",
|
|
3923
|
+
"strokeStyle",
|
|
3924
|
+
"borderRadius",
|
|
3925
|
+
"borderAnimationEnabled",
|
|
3926
|
+
"strokeAnimation",
|
|
3927
|
+
"src",
|
|
3928
|
+
"animatedIcon",
|
|
3929
|
+
"iconSrc",
|
|
3930
|
+
"componentId",
|
|
3931
|
+
"serviceTable",
|
|
3932
|
+
"title",
|
|
3933
|
+
"columns",
|
|
3934
|
+
"rows",
|
|
3935
|
+
"name",
|
|
3936
|
+
"label",
|
|
3937
|
+
"value",
|
|
3938
|
+
"cloud",
|
|
3939
|
+
"service",
|
|
3940
|
+
"childNodes"
|
|
3941
|
+
]);
|
|
3942
|
+
function isComponentInputType(value) {
|
|
3943
|
+
return COMPONENT_INPUT_TYPES.has(value);
|
|
3944
|
+
}
|
|
3945
|
+
function toRecord(value) {
|
|
3946
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
3947
|
+
return value;
|
|
3948
|
+
}
|
|
3949
|
+
function pickString(value) {
|
|
3950
|
+
if (typeof value !== "string") return;
|
|
3951
|
+
const trimmed = value.trim();
|
|
3952
|
+
if (!trimmed) return;
|
|
3953
|
+
return trimmed;
|
|
3954
|
+
}
|
|
3955
|
+
function pickPosition(value) {
|
|
3956
|
+
const position = toRecord(value);
|
|
3957
|
+
if (!position) return;
|
|
3958
|
+
if (typeof position.x !== "number" || !Number.isFinite(position.x)) return;
|
|
3959
|
+
if (typeof position.y !== "number" || !Number.isFinite(position.y)) return;
|
|
3960
|
+
return {
|
|
3961
|
+
x: position.x,
|
|
3962
|
+
y: position.y
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3965
|
+
function getFieldValue(fieldData) {
|
|
3966
|
+
if (!Array.isArray(fieldData) || fieldData.length !== 1) return fieldData;
|
|
3967
|
+
const first = toRecord(fieldData[0]);
|
|
3968
|
+
if (!first) return fieldData;
|
|
3969
|
+
if (!("value" in first)) return fieldData;
|
|
3970
|
+
return first.value;
|
|
3971
|
+
}
|
|
3972
|
+
function getFieldByLabel(fields, label) {
|
|
3973
|
+
return fields.find((field) => {
|
|
3974
|
+
var _pickString;
|
|
3975
|
+
return ((_pickString = pickString(field.label)) === null || _pickString === void 0 ? void 0 : _pickString.toLowerCase()) === label.toLowerCase() && pickString(field.type);
|
|
3976
|
+
});
|
|
3977
|
+
}
|
|
3978
|
+
function getFieldString(fields, label) {
|
|
3979
|
+
const field = getFieldByLabel(fields, label);
|
|
3980
|
+
if (!field) return;
|
|
3981
|
+
return pickString(getFieldValue(field.data));
|
|
3982
|
+
}
|
|
3983
|
+
function isEmptyFieldValue(value) {
|
|
3984
|
+
if (typeof value === "string") return value.trim().length === 0;
|
|
3985
|
+
return value === void 0 || value === null;
|
|
3986
|
+
}
|
|
3987
|
+
function parseStrokeStyle(dashArray) {
|
|
3988
|
+
if (typeof dashArray !== "string" || !dashArray.trim()) return;
|
|
3989
|
+
const normalized = dashArray.replaceAll(",", " ").replace(/\s+/g, " ").trim();
|
|
3990
|
+
if (normalized === "1 2") return "dotted";
|
|
3991
|
+
if (normalized === "4 2" || normalized === "4 4" || normalized === "8 4") return "dashed";
|
|
3992
|
+
return "solid";
|
|
3993
|
+
}
|
|
3994
|
+
function normalizeMarker(marker) {
|
|
3995
|
+
if (typeof marker === "string") return { type: marker };
|
|
3996
|
+
const markerRecord = toRecord(marker);
|
|
3997
|
+
if (!markerRecord) return;
|
|
3998
|
+
const type = pickString(markerRecord.type);
|
|
3999
|
+
if (!type) return;
|
|
4000
|
+
const color = pickString(markerRecord.color);
|
|
4001
|
+
if (color) return {
|
|
4002
|
+
type,
|
|
4003
|
+
color
|
|
4004
|
+
};
|
|
4005
|
+
return { type };
|
|
4006
|
+
}
|
|
4007
|
+
function escapeMermaidText(value) {
|
|
4008
|
+
return value.replaceAll("\"", "\\\"");
|
|
4009
|
+
}
|
|
4010
|
+
function canInlineMermaidLabel(value) {
|
|
4011
|
+
if (value.length > INLINE_MERMAID_LABEL_MAX_LENGTH) return false;
|
|
4012
|
+
if (value.includes("\n") || value.includes("\r")) return false;
|
|
4013
|
+
return true;
|
|
4014
|
+
}
|
|
4015
|
+
function normalizeDetailedMermaidLabel(value) {
|
|
4016
|
+
return value.split(/\r?\n/).map((line) => line.replace(/\s+/g, " ").trim()).filter((line) => line.length > 0).join("\n");
|
|
4017
|
+
}
|
|
4018
|
+
function toComponentFields(value) {
|
|
4019
|
+
if (!Array.isArray(value)) return [];
|
|
4020
|
+
return value.map((field) => toRecord(field)).filter((field) => field !== void 0);
|
|
4021
|
+
}
|
|
4022
|
+
function buildContextEdges(edges, nodeIdMap) {
|
|
4023
|
+
const contextEdges = {};
|
|
4024
|
+
for (const edge of edges) {
|
|
4025
|
+
var _toRecord, _toRecord2;
|
|
4026
|
+
const source = nodeIdMap.get(edge.source);
|
|
4027
|
+
const target = nodeIdMap.get(edge.target);
|
|
4028
|
+
if (!source || !target) continue;
|
|
4029
|
+
const edgeContext = {};
|
|
4030
|
+
const edgeType = pickString(edge.type);
|
|
4031
|
+
if (edgeType) edgeContext.type = edgeType;
|
|
4032
|
+
const sourceHandle = pickString(edge.sourceHandle);
|
|
4033
|
+
if (sourceHandle) edgeContext.sourceHandle = sourceHandle;
|
|
4034
|
+
const targetHandle = pickString(edge.targetHandle);
|
|
4035
|
+
if (targetHandle) edgeContext.targetHandle = targetHandle;
|
|
4036
|
+
const edgeLabel = pickString(edge.label);
|
|
4037
|
+
if (edgeLabel) edgeContext.label = edgeLabel;
|
|
4038
|
+
const edgeStyleRecord = (_toRecord = toRecord(edge.style)) !== null && _toRecord !== void 0 ? _toRecord : {};
|
|
4039
|
+
const edgeStyle = {};
|
|
4040
|
+
const stroke = pickString(edgeStyleRecord.stroke);
|
|
4041
|
+
if (stroke) edgeStyle.stroke = stroke;
|
|
4042
|
+
if (typeof edgeStyleRecord.strokeWidth === "number") edgeStyle.strokeWidth = edgeStyleRecord.strokeWidth;
|
|
4043
|
+
const strokeStyle = parseStrokeStyle(edgeStyleRecord.strokeDasharray);
|
|
4044
|
+
if (strokeStyle) edgeStyle.strokeStyle = strokeStyle;
|
|
4045
|
+
if (typeof edge.animated === "boolean") edgeStyle.borderAnimationEnabled = edge.animated;
|
|
4046
|
+
if (Object.keys(edgeStyle).length > 0) edgeContext.style = edgeStyle;
|
|
4047
|
+
const markerStart = normalizeMarker(edge.markerStart);
|
|
4048
|
+
if (markerStart) edgeContext.markerStart = markerStart;
|
|
4049
|
+
const markerEnd = normalizeMarker(edge.markerEnd);
|
|
4050
|
+
if (markerEnd) edgeContext.markerEnd = markerEnd;
|
|
4051
|
+
const edgeData = (_toRecord2 = toRecord(edge.data)) !== null && _toRecord2 !== void 0 ? _toRecord2 : {};
|
|
4052
|
+
if (Object.keys(edgeData).length > 0) edgeContext.___internal = edgeData;
|
|
4053
|
+
if (Object.keys(edgeContext).length > 0) contextEdges[`${source}-${target}`] = edgeContext;
|
|
4054
|
+
}
|
|
4055
|
+
return contextEdges;
|
|
4056
|
+
}
|
|
4057
|
+
function buildContextGroups(groupNodes, nodeIdMap) {
|
|
4058
|
+
const contextGroups = {};
|
|
4059
|
+
for (const groupNode of groupNodes) {
|
|
4060
|
+
var _toRecord;
|
|
4061
|
+
const groupData = (_toRecord = toRecord(groupNode.data)) !== null && _toRecord !== void 0 ? _toRecord : {};
|
|
4062
|
+
const childNodes = Array.isArray(groupData.childNodes) ? groupData.childNodes.map((childId) => typeof childId === "string" ? nodeIdMap.get(childId) : void 0).filter((childId) => childId !== void 0) : [];
|
|
4063
|
+
const name = getFieldString(Array.isArray(groupData.componentFields) ? groupData.componentFields.map((field) => toRecord(field)).filter((field) => field !== void 0) : [], "Name");
|
|
4064
|
+
if (!name && childNodes.length === 0) continue;
|
|
4065
|
+
contextGroups[groupNode.id] = {
|
|
4066
|
+
name: name !== null && name !== void 0 ? name : void 0,
|
|
4067
|
+
nodes: childNodes
|
|
4068
|
+
};
|
|
4069
|
+
}
|
|
4070
|
+
return contextGroups;
|
|
4071
|
+
}
|
|
4072
|
+
function buildContextNodes(graphNodes, nodeIdMap) {
|
|
4073
|
+
const contextNodes = {};
|
|
4074
|
+
for (const node of graphNodes) {
|
|
4075
|
+
var _toRecord, _toRecord2, _pickString, _pickString2;
|
|
4076
|
+
const mappedNodeId = nodeIdMap.get(node.id);
|
|
4077
|
+
if (!mappedNodeId) continue;
|
|
4078
|
+
const nodeData = (_toRecord = toRecord(node.data)) !== null && _toRecord !== void 0 ? _toRecord : {};
|
|
4079
|
+
const nodeStyle = (_toRecord2 = toRecord(node.style)) !== null && _toRecord2 !== void 0 ? _toRecord2 : {};
|
|
4080
|
+
const componentFields = Array.isArray(nodeData.componentFields) ? nodeData.componentFields.map((field) => toRecord(field)).filter((field) => field !== void 0) : [];
|
|
4081
|
+
const nodeContext = {};
|
|
4082
|
+
const nodeType = pickString(node.type);
|
|
4083
|
+
if (nodeType) nodeContext.type = nodeType;
|
|
4084
|
+
const nameField = getFieldByLabel(componentFields, "Name");
|
|
4085
|
+
const nameRawValue = nameField ? getFieldValue(nameField.data) : void 0;
|
|
4086
|
+
const name = pickString(nameRawValue);
|
|
4087
|
+
if (name) nodeContext.name = name;
|
|
4088
|
+
const textField = nodeType === "text" ? getFieldByLabel(componentFields, "Text") : void 0;
|
|
4089
|
+
const textRawValue = textField ? getFieldValue(textField.data) : void 0;
|
|
4090
|
+
const textValue = pickString(textRawValue);
|
|
4091
|
+
if (nodeType === "text") {
|
|
4092
|
+
if (textValue) nodeContext.value = textValue;
|
|
4093
|
+
}
|
|
4094
|
+
const codeField = nodeType === "code" ? getFieldByLabel(componentFields, "Code") : void 0;
|
|
4095
|
+
const codeRawValue = codeField ? getFieldValue(codeField.data) : void 0;
|
|
4096
|
+
const codeValue = pickString(codeRawValue);
|
|
4097
|
+
if (nodeType === "code") {
|
|
4098
|
+
if (codeValue) nodeContext.value = codeValue;
|
|
4099
|
+
}
|
|
4100
|
+
const src = pickString(nodeData.src);
|
|
4101
|
+
if (src) nodeContext.src = src;
|
|
4102
|
+
const animatedIcon = pickString(nodeData.animatedIcon);
|
|
4103
|
+
if (animatedIcon) nodeContext.animatedIcon = animatedIcon;
|
|
4104
|
+
const cloud = pickString(nodeData.cloud);
|
|
4105
|
+
if (cloud) nodeContext.cloud = cloud;
|
|
4106
|
+
const service = pickString(nodeData.service);
|
|
4107
|
+
if (service) nodeContext.service = service;
|
|
4108
|
+
const componentId = pickString(nodeData.componentId);
|
|
4109
|
+
if (componentId) nodeContext.componentId = componentId;
|
|
4110
|
+
const shape = pickString(nodeData.shape);
|
|
4111
|
+
if (shape && CONTEXT_SHAPES.has(shape)) nodeContext.shape = shape;
|
|
4112
|
+
const style = {};
|
|
4113
|
+
const width = typeof node.width === "number" ? node.width : typeof nodeStyle.width === "number" ? nodeStyle.width : void 0;
|
|
4114
|
+
if (typeof width === "number") style.width = width;
|
|
4115
|
+
const height = typeof node.height === "number" ? node.height : typeof nodeStyle.height === "number" ? nodeStyle.height : void 0;
|
|
4116
|
+
if (typeof height === "number") style.height = height;
|
|
4117
|
+
const fill = (_pickString = pickString(nodeData.fill)) !== null && _pickString !== void 0 ? _pickString : pickString(nodeStyle.fill);
|
|
4118
|
+
if (fill) style.fill = fill;
|
|
4119
|
+
const stroke = (_pickString2 = pickString(nodeData.stroke)) !== null && _pickString2 !== void 0 ? _pickString2 : pickString(nodeStyle.stroke);
|
|
4120
|
+
if (stroke) style.stroke = stroke;
|
|
4121
|
+
const strokeWidth = typeof nodeData.strokeWidth === "number" ? nodeData.strokeWidth : typeof nodeStyle.strokeWidth === "number" ? nodeStyle.strokeWidth : void 0;
|
|
4122
|
+
if (typeof strokeWidth === "number") style.strokeWidth = strokeWidth;
|
|
4123
|
+
const borderRadius = typeof nodeData.borderRadius === "number" ? nodeData.borderRadius : typeof nodeStyle.borderRadius === "number" ? nodeStyle.borderRadius : void 0;
|
|
4124
|
+
if (typeof borderRadius === "number") style.borderRadius = borderRadius;
|
|
4125
|
+
const strokeStyleFromData = pickString(nodeData.strokeStyle);
|
|
4126
|
+
if (strokeStyleFromData === "solid" || strokeStyleFromData === "dashed" || strokeStyleFromData === "dotted") style.strokeStyle = strokeStyleFromData;
|
|
4127
|
+
else {
|
|
4128
|
+
const strokeStyleFromDash = parseStrokeStyle(nodeStyle.strokeDasharray);
|
|
4129
|
+
if (strokeStyleFromDash) style.strokeStyle = strokeStyleFromDash;
|
|
4130
|
+
}
|
|
4131
|
+
const borderAnimationEnabled = typeof nodeData.borderAnimationEnabled === "boolean" ? nodeData.borderAnimationEnabled : nodeData.strokeAnimation === "dash" ? true : void 0;
|
|
4132
|
+
if (typeof borderAnimationEnabled === "boolean") style.borderAnimationEnabled = borderAnimationEnabled;
|
|
4133
|
+
if (Object.keys(style).length > 0) nodeContext.style = style;
|
|
4134
|
+
const tableColumns = Array.isArray(nodeData.columns) ? nodeData.columns.filter((value) => typeof value === "string") : void 0;
|
|
4135
|
+
const tableRows = Array.isArray(nodeData.rows) ? nodeData.rows.map((row) => Array.isArray(row) ? row.filter((value) => typeof value === "string") : void 0).filter((row) => row !== void 0) : void 0;
|
|
4136
|
+
if (tableColumns || tableRows) nodeContext.table = {
|
|
4137
|
+
columns: tableColumns !== null && tableColumns !== void 0 ? tableColumns : [],
|
|
4138
|
+
rows: tableRows !== null && tableRows !== void 0 ? tableRows : []
|
|
4139
|
+
};
|
|
4140
|
+
const serviceTable = toRecord(nodeData.serviceTable);
|
|
4141
|
+
const serviceId = pickString(serviceTable === null || serviceTable === void 0 ? void 0 : serviceTable.serviceId);
|
|
4142
|
+
const serviceDbId = pickString(serviceTable === null || serviceTable === void 0 ? void 0 : serviceTable.serviceDbId);
|
|
4143
|
+
const tableName = pickString(serviceTable === null || serviceTable === void 0 ? void 0 : serviceTable.tableName);
|
|
4144
|
+
if (serviceId && serviceDbId && tableName) nodeContext.dbConfig = {
|
|
4145
|
+
service: serviceId,
|
|
4146
|
+
database: serviceDbId,
|
|
4147
|
+
tableName
|
|
4148
|
+
};
|
|
4149
|
+
const dynamicData = {};
|
|
4150
|
+
for (const field of componentFields) {
|
|
4151
|
+
const label$1 = pickString(field.label);
|
|
4152
|
+
const type = pickString(field.type);
|
|
4153
|
+
if (!label$1 || !type || !isComponentInputType(type)) continue;
|
|
4154
|
+
const componentType = type;
|
|
4155
|
+
const normalizedLabel = label$1.toLowerCase();
|
|
4156
|
+
if (normalizedLabel === "name") continue;
|
|
4157
|
+
if (nodeType === "text" && normalizedLabel === "text") continue;
|
|
4158
|
+
if (nodeType === "code" && normalizedLabel === "code") continue;
|
|
4159
|
+
const options = Array.isArray(field.options) ? field.options.filter((option) => typeof option === "string") : void 0;
|
|
4160
|
+
dynamicData[label$1] = {
|
|
4161
|
+
type: componentType,
|
|
4162
|
+
value: getFieldValue(field.data),
|
|
4163
|
+
options: options && options.length > 0 ? options : void 0
|
|
4164
|
+
};
|
|
4165
|
+
}
|
|
4166
|
+
if (nameField && !name && isEmptyFieldValue(nameRawValue)) {
|
|
4167
|
+
const nameType = pickString(nameField.type);
|
|
4168
|
+
if (nameType && isComponentInputType(nameType)) {
|
|
4169
|
+
const nameOptions = Array.isArray(nameField.options) ? nameField.options.filter((option) => typeof option === "string") : void 0;
|
|
4170
|
+
dynamicData.Name = {
|
|
4171
|
+
type: nameType,
|
|
4172
|
+
value: "",
|
|
4173
|
+
options: nameOptions && nameOptions.length > 0 ? nameOptions : void 0
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
if (textField && !textValue && isEmptyFieldValue(textRawValue)) {
|
|
4178
|
+
const textType = pickString(textField.type);
|
|
4179
|
+
if (textType && isComponentInputType(textType)) {
|
|
4180
|
+
const textOptions = Array.isArray(textField.options) ? textField.options.filter((option) => typeof option === "string") : void 0;
|
|
4181
|
+
dynamicData.Text = {
|
|
4182
|
+
type: textType,
|
|
4183
|
+
value: "",
|
|
4184
|
+
options: textOptions && textOptions.length > 0 ? textOptions : void 0
|
|
4185
|
+
};
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
if (codeField && !codeValue && isEmptyFieldValue(codeRawValue)) {
|
|
4189
|
+
const codeType = pickString(codeField.type);
|
|
4190
|
+
if (codeType && isComponentInputType(codeType)) {
|
|
4191
|
+
const codeOptions = Array.isArray(codeField.options) ? codeField.options.filter((option) => typeof option === "string") : void 0;
|
|
4192
|
+
dynamicData.Code = {
|
|
4193
|
+
type: codeType,
|
|
4194
|
+
value: "",
|
|
4195
|
+
options: codeOptions && codeOptions.length > 0 ? codeOptions : void 0
|
|
4196
|
+
};
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
if (Object.keys(dynamicData).length > 0) nodeContext.data = dynamicData;
|
|
4200
|
+
const position = pickPosition(node.position);
|
|
4201
|
+
if (position) nodeContext.___position = position;
|
|
4202
|
+
const internalData = Object.entries(nodeData).reduce((acc, [key, value]) => {
|
|
4203
|
+
if (KNOWN_NODE_DATA_KEYS.has(key)) return acc;
|
|
4204
|
+
acc[key] = value;
|
|
4205
|
+
return acc;
|
|
4206
|
+
}, {});
|
|
4207
|
+
if (nodeType === "builder" && Array.isArray(nodeData.componentFields)) internalData.componentFields = nodeData.componentFields;
|
|
4208
|
+
const iconSrc = pickString(nodeData.iconSrc);
|
|
4209
|
+
if (iconSrc) internalData.iconSrc = iconSrc;
|
|
4210
|
+
const label = pickString(nodeData.label);
|
|
4211
|
+
if (label) internalData.label = label;
|
|
4212
|
+
const internalCloud = pickString(nodeData.cloud);
|
|
4213
|
+
if (internalCloud) internalData.cloud = internalCloud;
|
|
4214
|
+
if (Object.keys(internalData).length > 0) nodeContext.___internal = internalData;
|
|
4215
|
+
if (Object.keys(nodeContext).length > 0) contextNodes[mappedNodeId] = nodeContext;
|
|
4216
|
+
}
|
|
4217
|
+
return contextNodes;
|
|
4218
|
+
}
|
|
4219
|
+
function buildValidatedContext(graphNodes, edges, groupNodes, nodeIdMap) {
|
|
4220
|
+
const contextNodes = buildContextNodes(graphNodes, nodeIdMap);
|
|
4221
|
+
const contextEdges = buildContextEdges(edges, nodeIdMap);
|
|
4222
|
+
const contextGroups = buildContextGroups(groupNodes, nodeIdMap);
|
|
4223
|
+
const context = {};
|
|
4224
|
+
if (Object.keys(contextNodes).length > 0) context.nodes = contextNodes;
|
|
4225
|
+
if (Object.keys(contextEdges).length > 0) context.edges = contextEdges;
|
|
4226
|
+
if (Object.keys(contextGroups).length > 0) context.groups = contextGroups;
|
|
4227
|
+
return contextSchema.parse(context);
|
|
4228
|
+
}
|
|
4229
|
+
function inferDirection(nodes, edges) {
|
|
4230
|
+
const nodeMap = new Map(nodes.map((node) => [node.id, node]));
|
|
4231
|
+
let horizontalWeight = 0;
|
|
4232
|
+
let verticalWeight = 0;
|
|
4233
|
+
let weightedEdges = 0;
|
|
4234
|
+
for (const edge of edges) {
|
|
4235
|
+
const sourceNode = nodeMap.get(edge.source);
|
|
4236
|
+
const targetNode = nodeMap.get(edge.target);
|
|
4237
|
+
if (!sourceNode || !targetNode) continue;
|
|
4238
|
+
horizontalWeight += Math.abs(targetNode.position.x - sourceNode.position.x);
|
|
4239
|
+
verticalWeight += Math.abs(targetNode.position.y - sourceNode.position.y);
|
|
4240
|
+
weightedEdges += 1;
|
|
4241
|
+
}
|
|
4242
|
+
if (weightedEdges > 0) return horizontalWeight >= verticalWeight ? "LR" : "TB";
|
|
4243
|
+
const xs = nodes.map((node) => node.position.x);
|
|
4244
|
+
const ys = nodes.map((node) => node.position.y);
|
|
4245
|
+
return Math.max(...xs, 0) - Math.min(...xs, 0) >= Math.max(...ys, 0) - Math.min(...ys, 0) ? "LR" : "TB";
|
|
4246
|
+
}
|
|
4247
|
+
function isAlreadyNormalizedId(sourceId) {
|
|
4248
|
+
return /^[A-Za-z]+$/.test(sourceId);
|
|
4249
|
+
}
|
|
4250
|
+
function indexToAlphabetId(index) {
|
|
4251
|
+
let current = index;
|
|
4252
|
+
let result = "";
|
|
4253
|
+
while (current >= 0) {
|
|
4254
|
+
result = String.fromCharCode(current % 26 + 65) + result;
|
|
4255
|
+
current = Math.floor(current / 26) - 1;
|
|
4256
|
+
}
|
|
4257
|
+
return result;
|
|
4258
|
+
}
|
|
4259
|
+
function buildNodeIdMap(graphNodes) {
|
|
4260
|
+
const usedMermaidIds = /* @__PURE__ */ new Set();
|
|
4261
|
+
const nodeIdMap = /* @__PURE__ */ new Map();
|
|
4262
|
+
let alphabeticIndex = 0;
|
|
4263
|
+
graphNodes.forEach((node) => {
|
|
4264
|
+
if (isAlreadyNormalizedId(node.id) && !usedMermaidIds.has(node.id)) {
|
|
4265
|
+
nodeIdMap.set(node.id, node.id);
|
|
4266
|
+
usedMermaidIds.add(node.id);
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
let mappedNodeId = indexToAlphabetId(alphabeticIndex);
|
|
4270
|
+
alphabeticIndex += 1;
|
|
4271
|
+
while (usedMermaidIds.has(mappedNodeId)) {
|
|
4272
|
+
mappedNodeId = indexToAlphabetId(alphabeticIndex);
|
|
4273
|
+
alphabeticIndex += 1;
|
|
4274
|
+
}
|
|
4275
|
+
nodeIdMap.set(node.id, mappedNodeId);
|
|
4276
|
+
usedMermaidIds.add(mappedNodeId);
|
|
4277
|
+
});
|
|
4278
|
+
return nodeIdMap;
|
|
4279
|
+
}
|
|
4280
|
+
function resolveMermaidNodeLabel(node) {
|
|
4281
|
+
const nodeType = node.type;
|
|
4282
|
+
const nodeData = toRecord(node.data);
|
|
4283
|
+
const componentFields = Array.isArray(nodeData === null || nodeData === void 0 ? void 0 : nodeData.componentFields) ? nodeData.componentFields.map((field) => toRecord(field)).filter((field) => field !== void 0) : [];
|
|
4284
|
+
const name = getFieldString(componentFields, "Name");
|
|
4285
|
+
if (nodeType === "text") return getFieldString(componentFields, "Text");
|
|
4286
|
+
if (nodeType === "code") return getFieldString(componentFields, "Code");
|
|
4287
|
+
if (nodeType === "shape") return name;
|
|
4288
|
+
if (nodeType === "cloud") return name;
|
|
4289
|
+
if (nodeType === "table") return name;
|
|
4290
|
+
if (nodeType === "gif") return name;
|
|
4291
|
+
}
|
|
4292
|
+
function formatInlineRichTextSegment(text, attributes) {
|
|
4293
|
+
const trimmed = text.trim();
|
|
4294
|
+
if (!trimmed) return "";
|
|
4295
|
+
let output = trimmed;
|
|
4296
|
+
const link = pickString(attributes === null || attributes === void 0 ? void 0 : attributes.link);
|
|
4297
|
+
if ((attributes === null || attributes === void 0 ? void 0 : attributes.code) === true) output = `\`${output}\``;
|
|
4298
|
+
if ((attributes === null || attributes === void 0 ? void 0 : attributes.bold) === true) output = `**${output}**`;
|
|
4299
|
+
if (link) output = `[${output}](${link})`;
|
|
4300
|
+
return output;
|
|
4301
|
+
}
|
|
4302
|
+
function collectQuillOps(value) {
|
|
4303
|
+
const ops = [];
|
|
4304
|
+
function visit(input) {
|
|
4305
|
+
if (!input) return;
|
|
4306
|
+
if (Array.isArray(input)) {
|
|
4307
|
+
for (const item of input) visit(item);
|
|
4308
|
+
return;
|
|
4309
|
+
}
|
|
4310
|
+
const record = toRecord(input);
|
|
4311
|
+
if (!record) return;
|
|
4312
|
+
if (Array.isArray(record.ops)) {
|
|
4313
|
+
visit(record.ops);
|
|
4314
|
+
return;
|
|
4315
|
+
}
|
|
4316
|
+
if ("insert" in record) {
|
|
4317
|
+
ops.push(record);
|
|
4318
|
+
return;
|
|
4319
|
+
}
|
|
4320
|
+
if ("value" in record) visit(record.value);
|
|
4321
|
+
}
|
|
4322
|
+
visit(value);
|
|
4323
|
+
return ops;
|
|
4324
|
+
}
|
|
4325
|
+
function formatQuillRichTextAsMarkdown(value) {
|
|
4326
|
+
const ops = collectQuillOps(value);
|
|
4327
|
+
if (ops.length === 0) return;
|
|
4328
|
+
const lines = [];
|
|
4329
|
+
let currentLine = "";
|
|
4330
|
+
for (const op of ops) {
|
|
4331
|
+
const insert = op.insert;
|
|
4332
|
+
if (typeof insert !== "string") continue;
|
|
4333
|
+
const attributes = toRecord(op.attributes);
|
|
4334
|
+
const parts = insert.split("\n");
|
|
4335
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
4336
|
+
const part = parts[index];
|
|
4337
|
+
if (part) {
|
|
4338
|
+
const formattedPart = formatInlineRichTextSegment(part, attributes);
|
|
4339
|
+
if (formattedPart) {
|
|
4340
|
+
if (currentLine) currentLine += " ";
|
|
4341
|
+
currentLine += formattedPart;
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
if (!(index < parts.length - 1)) continue;
|
|
4345
|
+
const normalizedLine = currentLine.replace(/\s+/g, " ").trim();
|
|
4346
|
+
if (normalizedLine) if ((attributes === null || attributes === void 0 ? void 0 : attributes.list) === "bullet" || (attributes === null || attributes === void 0 ? void 0 : attributes.list) === "ordered") lines.push(`- ${normalizedLine}`);
|
|
4347
|
+
else lines.push(normalizedLine);
|
|
4348
|
+
currentLine = "";
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
const trailingLine = currentLine.replace(/\s+/g, " ").trim();
|
|
4352
|
+
if (trailingLine) lines.push(trailingLine);
|
|
4353
|
+
if (lines.length === 0) return;
|
|
4354
|
+
return lines.join("\n");
|
|
4355
|
+
}
|
|
4356
|
+
function formatDetailValue(value) {
|
|
4357
|
+
if (typeof value === "string") {
|
|
4358
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
4359
|
+
if (!normalized) return;
|
|
4360
|
+
return normalized;
|
|
4361
|
+
}
|
|
4362
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
4363
|
+
if (Array.isArray(value)) {
|
|
4364
|
+
const formatted = value.map((item) => formatDetailValue(item)).filter((item) => item !== void 0);
|
|
4365
|
+
if (formatted.length === 0) return;
|
|
4366
|
+
return formatted.join(",");
|
|
4367
|
+
}
|
|
4368
|
+
const valueRecord = toRecord(value);
|
|
4369
|
+
if (valueRecord) {
|
|
4370
|
+
const entries = Object.entries(valueRecord).slice(0, 2).map(([key, item]) => {
|
|
4371
|
+
const formatted = formatDetailValue(item);
|
|
4372
|
+
if (!formatted) return;
|
|
4373
|
+
return `${key}:${formatted}`;
|
|
4374
|
+
}).filter((entry) => entry !== void 0);
|
|
4375
|
+
if (entries.length === 0) return;
|
|
4376
|
+
return entries.join(",");
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
function getNodeName(nodeData, componentFields) {
|
|
4380
|
+
var _getFieldString;
|
|
4381
|
+
return (_getFieldString = getFieldString(componentFields, "Name")) !== null && _getFieldString !== void 0 ? _getFieldString : pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.name);
|
|
4382
|
+
}
|
|
4383
|
+
function buildFieldLines(componentFields, excludedLabels) {
|
|
4384
|
+
const lines = [];
|
|
4385
|
+
for (const field of componentFields) {
|
|
4386
|
+
const label = pickString(field.label);
|
|
4387
|
+
const type = pickString(field.type);
|
|
4388
|
+
if (!label || !type || !isComponentInputType(type)) continue;
|
|
4389
|
+
if (excludedLabels.has(label.toLowerCase())) continue;
|
|
4390
|
+
const rawValue = getFieldValue(field.data);
|
|
4391
|
+
const value = type === ComponentInputType.RichTextEditor ? formatQuillRichTextAsMarkdown(rawValue) : formatDetailValue(rawValue);
|
|
4392
|
+
const options = Array.isArray(field.options) ? field.options.filter((option) => typeof option === "string") : [];
|
|
4393
|
+
if (!value && options.length === 0) continue;
|
|
4394
|
+
if (value && options.length > 0) {
|
|
4395
|
+
lines.push(`${label}: ${value} [${options.join("/")}]`);
|
|
4396
|
+
continue;
|
|
4397
|
+
}
|
|
4398
|
+
if (value) {
|
|
4399
|
+
lines.push(`${label}: ${value}`);
|
|
4400
|
+
continue;
|
|
4401
|
+
}
|
|
4402
|
+
lines.push(`${label}: [${options.join("/")}]`);
|
|
4403
|
+
}
|
|
4404
|
+
return lines;
|
|
4405
|
+
}
|
|
4406
|
+
function buildDetailedLabel(lines) {
|
|
4407
|
+
const compact = lines.map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4408
|
+
if (compact.length === 0) return;
|
|
4409
|
+
return compact.join("\n");
|
|
4410
|
+
}
|
|
4411
|
+
function resolveMermaidDetailedNodeLabel(node) {
|
|
4412
|
+
const nodeType = pickString(node.type);
|
|
4413
|
+
const nodeData = toRecord(node.data);
|
|
4414
|
+
const componentFields = toComponentFields(nodeData === null || nodeData === void 0 ? void 0 : nodeData.componentFields);
|
|
4415
|
+
const name = getNodeName(nodeData, componentFields);
|
|
4416
|
+
if (nodeType === "text") {
|
|
4417
|
+
var _getFieldString2;
|
|
4418
|
+
const text = (_getFieldString2 = getFieldString(componentFields, "Text")) !== null && _getFieldString2 !== void 0 ? _getFieldString2 : pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.value);
|
|
4419
|
+
return buildDetailedLabel([text ? `Text: ${text}` : "Text", ...buildFieldLines(componentFields, new Set(["name", "text"]))]);
|
|
4420
|
+
}
|
|
4421
|
+
if (nodeType === "code") {
|
|
4422
|
+
var _getFieldString3, _code$split$;
|
|
4423
|
+
const code = (_getFieldString3 = getFieldString(componentFields, "Code")) !== null && _getFieldString3 !== void 0 ? _getFieldString3 : pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.value);
|
|
4424
|
+
const firstLine = code === null || code === void 0 || (_code$split$ = code.split(/\r?\n/)[0]) === null || _code$split$ === void 0 ? void 0 : _code$split$.trim();
|
|
4425
|
+
return buildDetailedLabel([firstLine ? `Code: ${firstLine}` : "Code", ...buildFieldLines(componentFields, new Set(["name", "code"]))]);
|
|
4426
|
+
}
|
|
4427
|
+
if (nodeType === "data-source" || nodeType === "databaseTableSQL") {
|
|
4428
|
+
var _pickString, _pickString2, _pickString3;
|
|
4429
|
+
const lines = [];
|
|
4430
|
+
const serviceTable = toRecord(nodeData === null || nodeData === void 0 ? void 0 : nodeData.serviceTable);
|
|
4431
|
+
const dbConfig = toRecord(nodeData === null || nodeData === void 0 ? void 0 : nodeData.dbConfig);
|
|
4432
|
+
const database = (_pickString = pickString(serviceTable === null || serviceTable === void 0 ? void 0 : serviceTable.serviceDbId)) !== null && _pickString !== void 0 ? _pickString : pickString(dbConfig === null || dbConfig === void 0 ? void 0 : dbConfig.database);
|
|
4433
|
+
const tableName = (_pickString2 = pickString(serviceTable === null || serviceTable === void 0 ? void 0 : serviceTable.tableName)) !== null && _pickString2 !== void 0 ? _pickString2 : pickString(dbConfig === null || dbConfig === void 0 ? void 0 : dbConfig.tableName);
|
|
4434
|
+
const dbService = (_pickString3 = pickString(serviceTable === null || serviceTable === void 0 ? void 0 : serviceTable.serviceId)) !== null && _pickString3 !== void 0 ? _pickString3 : pickString(dbConfig === null || dbConfig === void 0 ? void 0 : dbConfig.service);
|
|
4435
|
+
const dataSourceName = name !== null && name !== void 0 ? name : tableName;
|
|
4436
|
+
lines.push(dataSourceName ? `DataSource: ${dataSourceName}` : "DataSource");
|
|
4437
|
+
if (database && tableName) lines.push(`db: ${database}.${tableName}`);
|
|
4438
|
+
if (dbService) lines.push(`service: ${dbService}`);
|
|
4439
|
+
lines.push(...buildFieldLines(componentFields, new Set(["name"])));
|
|
4440
|
+
return buildDetailedLabel(lines);
|
|
4441
|
+
}
|
|
4442
|
+
if (nodeType === "cloud") {
|
|
4443
|
+
const lines = [];
|
|
4444
|
+
lines.push(name ? `Cloud: ${name}` : "Cloud");
|
|
4445
|
+
const cloud = pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.cloud);
|
|
4446
|
+
if (cloud) lines.push(`provider: ${cloud}`);
|
|
4447
|
+
const service = pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.service);
|
|
4448
|
+
if (service) lines.push(`service: ${service}`);
|
|
4449
|
+
lines.push(...buildFieldLines(componentFields, new Set(["name"])));
|
|
4450
|
+
return buildDetailedLabel(lines);
|
|
4451
|
+
}
|
|
4452
|
+
if (nodeType === "table" || nodeType === "dataTableInterface") {
|
|
4453
|
+
var _pickString4;
|
|
4454
|
+
const lines = [];
|
|
4455
|
+
const table = toRecord(nodeData === null || nodeData === void 0 ? void 0 : nodeData.table);
|
|
4456
|
+
const tableName = (_pickString4 = pickString(table === null || table === void 0 ? void 0 : table.name)) !== null && _pickString4 !== void 0 ? _pickString4 : name;
|
|
4457
|
+
lines.push(tableName ? `Table: ${tableName}` : "Table");
|
|
4458
|
+
const columns = Array.isArray(nodeData === null || nodeData === void 0 ? void 0 : nodeData.columns) ? nodeData.columns.filter((value) => typeof value === "string").length : Array.isArray(table === null || table === void 0 ? void 0 : table.columns) ? table.columns.length : 0;
|
|
4459
|
+
const rows = Array.isArray(nodeData === null || nodeData === void 0 ? void 0 : nodeData.rows) ? nodeData.rows.filter((value) => Array.isArray(value)).length : Array.isArray(table === null || table === void 0 ? void 0 : table.rows) ? table.rows.length : 0;
|
|
4460
|
+
lines.push(`columns: ${columns}`);
|
|
4461
|
+
lines.push(`rows: ${rows}`);
|
|
4462
|
+
lines.push(...buildFieldLines(componentFields, new Set(["name"])));
|
|
4463
|
+
return buildDetailedLabel(lines);
|
|
4464
|
+
}
|
|
4465
|
+
if (nodeType === "shape") {
|
|
4466
|
+
const fieldLines = buildFieldLines(componentFields, /* @__PURE__ */ new Set());
|
|
4467
|
+
if (fieldLines.length > 0) return buildDetailedLabel(fieldLines);
|
|
4468
|
+
if (name) return `Name: ${name}`;
|
|
4469
|
+
return;
|
|
4470
|
+
}
|
|
4471
|
+
if (nodeType === "gif") {
|
|
4472
|
+
const src = pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.src);
|
|
4473
|
+
return buildDetailedLabel([
|
|
4474
|
+
name ? `Gif: ${name}` : src ? `Gif: ${src}` : "Gif",
|
|
4475
|
+
...src ? [`src: ${src}`] : [],
|
|
4476
|
+
...buildFieldLines(componentFields, new Set(["name"]))
|
|
4477
|
+
]);
|
|
4478
|
+
}
|
|
4479
|
+
if (nodeType === "image") {
|
|
4480
|
+
const src = pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.src);
|
|
4481
|
+
return buildDetailedLabel([
|
|
4482
|
+
name ? `Image: ${name}` : src ? `Image: ${src}` : "Image",
|
|
4483
|
+
...src ? [`src: ${src}`] : [],
|
|
4484
|
+
...buildFieldLines(componentFields, new Set(["name"]))
|
|
4485
|
+
]);
|
|
4486
|
+
}
|
|
4487
|
+
if (nodeType === "group") {
|
|
4488
|
+
const childCount = Array.isArray(nodeData === null || nodeData === void 0 ? void 0 : nodeData.childNodes) ? nodeData.childNodes.length : 0;
|
|
4489
|
+
return buildDetailedLabel([
|
|
4490
|
+
name ? `Group: ${name}` : "Group",
|
|
4491
|
+
`children: ${childCount}`,
|
|
4492
|
+
...buildFieldLines(componentFields, new Set(["name"]))
|
|
4493
|
+
]);
|
|
4494
|
+
}
|
|
4495
|
+
if (nodeType === "sequenceParticipant") {
|
|
4496
|
+
var _getFieldString4, _getFieldString5;
|
|
4497
|
+
const label = (_getFieldString4 = getFieldString(componentFields, "Label")) !== null && _getFieldString4 !== void 0 ? _getFieldString4 : pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.label);
|
|
4498
|
+
const participantName = name !== null && name !== void 0 ? name : label;
|
|
4499
|
+
const rowCount = typeof (nodeData === null || nodeData === void 0 ? void 0 : nodeData.rowCount) === "number" ? nodeData.rowCount : void 0;
|
|
4500
|
+
const color = (_getFieldString5 = getFieldString(componentFields, "Color")) !== null && _getFieldString5 !== void 0 ? _getFieldString5 : pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.color);
|
|
4501
|
+
return buildDetailedLabel([
|
|
4502
|
+
participantName ? `SequenceParticipant: ${participantName}` : "SequenceParticipant",
|
|
4503
|
+
...typeof rowCount === "number" ? [`rows: ${rowCount}`] : [],
|
|
4504
|
+
...color ? [`color: ${color}`] : [],
|
|
4505
|
+
...buildFieldLines(componentFields, new Set([
|
|
4506
|
+
"name",
|
|
4507
|
+
"label",
|
|
4508
|
+
"color"
|
|
4509
|
+
]))
|
|
4510
|
+
]);
|
|
4511
|
+
}
|
|
4512
|
+
if (nodeType === "comment") return buildDetailedLabel([`Comment: ${(typeof (nodeData === null || nodeData === void 0 ? void 0 : nodeData.isResolved) === "boolean" ? nodeData.isResolved : void 0) ? "Resolved" : "Open"}`, ...buildFieldLines(componentFields, new Set(["name"]))]);
|
|
4513
|
+
if (nodeType === "builder") {
|
|
4514
|
+
const componentId = pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.componentId);
|
|
4515
|
+
return buildDetailedLabel([
|
|
4516
|
+
name ? `Builder: ${name}` : "Builder",
|
|
4517
|
+
...componentId ? [`componentId: ${componentId}`] : [],
|
|
4518
|
+
...buildFieldLines(componentFields, new Set(["name"]))
|
|
4519
|
+
]);
|
|
4520
|
+
}
|
|
4521
|
+
const baseLabel = resolveMermaidNodeLabel(node);
|
|
4522
|
+
if (baseLabel) {
|
|
4523
|
+
const lines = [`${nodeType ? nodeType.charAt(0).toUpperCase() + nodeType.slice(1) : "Node"}: ${baseLabel}`];
|
|
4524
|
+
lines.push(...buildFieldLines(componentFields, new Set([
|
|
4525
|
+
"name",
|
|
4526
|
+
"text",
|
|
4527
|
+
"code"
|
|
4528
|
+
])));
|
|
4529
|
+
return buildDetailedLabel(lines);
|
|
4530
|
+
}
|
|
4531
|
+
const fallbackLabel = pickString(nodeData === null || nodeData === void 0 ? void 0 : nodeData.label);
|
|
4532
|
+
if (fallbackLabel) return `${nodeType ? nodeType.charAt(0).toUpperCase() + nodeType.slice(1) : "Node"}: ${fallbackLabel}`;
|
|
4533
|
+
}
|
|
4534
|
+
function escapeMermaidEdgeLabelText(value) {
|
|
4535
|
+
return value.replaceAll("|", "/").replace(/\s+/g, " ").trim();
|
|
4536
|
+
}
|
|
4537
|
+
function resolveMermaidEdgeLabel(edge) {
|
|
4538
|
+
return pickString(edge.label);
|
|
4539
|
+
}
|
|
4540
|
+
function buildMermaidNodeLines(graphNodes, nodeIdMap, detailedContext) {
|
|
4541
|
+
return graphNodes.map((node) => {
|
|
4542
|
+
var _nodeIdMap$get;
|
|
4543
|
+
const mappedNodeId = (_nodeIdMap$get = nodeIdMap.get(node.id)) !== null && _nodeIdMap$get !== void 0 ? _nodeIdMap$get : node.id;
|
|
4544
|
+
const mermaidLabel = detailedContext ? resolveMermaidDetailedNodeLabel(node) : resolveMermaidNodeLabel(node);
|
|
4545
|
+
if (!mermaidLabel) return mappedNodeId;
|
|
4546
|
+
if (detailedContext) {
|
|
4547
|
+
const normalizedDetailedLabel = normalizeDetailedMermaidLabel(mermaidLabel);
|
|
4548
|
+
if (!normalizedDetailedLabel) return mappedNodeId;
|
|
4549
|
+
return `${mappedNodeId}["${escapeMermaidText(normalizedDetailedLabel)}"]`;
|
|
4550
|
+
}
|
|
4551
|
+
if (!canInlineMermaidLabel(mermaidLabel)) return mappedNodeId;
|
|
4552
|
+
return `${mappedNodeId}["${escapeMermaidText(mermaidLabel)}"]`;
|
|
4553
|
+
});
|
|
4554
|
+
}
|
|
4555
|
+
function buildMermaidEdgeLines(edges, nodeIdMap, detailedContext) {
|
|
4556
|
+
const mermaidEdgeLines = [];
|
|
4557
|
+
for (const edge of edges) {
|
|
4558
|
+
const source = nodeIdMap.get(edge.source);
|
|
4559
|
+
const target = nodeIdMap.get(edge.target);
|
|
4560
|
+
if (!source || !target) continue;
|
|
4561
|
+
if (detailedContext) {
|
|
4562
|
+
const mermaidEdgeLabel = resolveMermaidEdgeLabel(edge);
|
|
4563
|
+
if (mermaidEdgeLabel) {
|
|
4564
|
+
mermaidEdgeLines.push(`${source} -->|${escapeMermaidEdgeLabelText(mermaidEdgeLabel)}| ${target}`);
|
|
4565
|
+
continue;
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
mermaidEdgeLines.push(`${source} --> ${target}`);
|
|
4569
|
+
}
|
|
4570
|
+
return mermaidEdgeLines;
|
|
4571
|
+
}
|
|
4572
|
+
function convertUiGraphToMermaid(input, options) {
|
|
4573
|
+
const groupNodes = input.nodes.filter((node) => node.type === "group");
|
|
4574
|
+
const graphNodes = input.nodes.filter((node) => node.type !== "group");
|
|
4575
|
+
const direction = inferDirection(graphNodes, input.edges);
|
|
4576
|
+
const nodeIdMap = buildNodeIdMap(graphNodes);
|
|
4577
|
+
const mermaidNodeLines = buildMermaidNodeLines(graphNodes, nodeIdMap, (options === null || options === void 0 ? void 0 : options.detailedContext) === true);
|
|
4578
|
+
const mermaidEdgeLines = buildMermaidEdgeLines(input.edges, nodeIdMap, (options === null || options === void 0 ? void 0 : options.detailedContext) === true);
|
|
4579
|
+
const context = buildValidatedContext(graphNodes, input.edges, groupNodes, nodeIdMap);
|
|
4580
|
+
return {
|
|
4581
|
+
mermaid: [
|
|
4582
|
+
`flowchart ${direction}`,
|
|
4583
|
+
...mermaidNodeLines,
|
|
4584
|
+
...mermaidEdgeLines
|
|
4585
|
+
].join("\n"),
|
|
4586
|
+
context
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
export { AstToSqlGenerator, AstToUiConverter, BasicDetailsSchema, ComponentInputType, DialectIdSchema, DocumentCollectionSchema, DocumentIndexSchema, DynamoAttributeSchema, DynamoEditorSchema, DynamoGsiSchema, EditorSupportedDialectSchema, JsonEditorSchema, MongoCollectionSchema, MongoEditorSchema, MongoIndexFieldSchema, MongoIndexSchema, MongoNestedFieldSchema, NestedFieldSchema, NoSqlFieldSchema, SqlToAstParser, buildMetaData, contextSchema, convertDynamoSchemaToAst, convertJsonSchemaToAst, convertMermaidToReactFlow, convertMermaidToReactFlowWithContext, convertMongoSchemaToAst, convertNoSQLToAst, convertUiGraphToMermaid, flattenMetaData, generateTableNodeId, generateUUID, sanitizeMermaidLabels, syncBaseData };
|