@statelyai/graph 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -46
- package/dist/{algorithms-BHHg7lGq.mjs → algorithms-BNDQcHU3.mjs} +7 -7
- package/dist/algorithms.mjs +1 -1
- package/dist/format-support.d.mts +6 -0
- package/dist/format-support.mjs +51 -45
- package/dist/formats/cytoscape/index.mjs +24 -2
- package/dist/formats/d3/index.d.mts +7 -0
- package/dist/formats/d3/index.mjs +42 -8
- package/dist/formats/dot/index.mjs +18 -6
- package/dist/formats/elk/index.mjs +86 -23
- package/dist/formats/gexf/index.mjs +138 -17
- package/dist/formats/gml/index.mjs +44 -11
- package/dist/formats/jgf/index.mjs +24 -2
- package/dist/formats/mermaid/index.d.mts +1 -0
- package/dist/formats/mermaid/index.mjs +39 -9
- package/dist/formats/xyflow/index.d.mts +1 -0
- package/dist/formats/xyflow/index.mjs +97 -40
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{indexing-CJc-ul8e.mjs → indexing-DUl3kTqm.mjs} +18 -4
- package/dist/queries.mjs +1 -1
- package/dist/schemas.d.mts +22 -7
- package/dist/schemas.mjs +158 -2
- package/package.json +1 -1
- package/schemas/graph.schema.json +1 -0
- package/schemas/node.schema.json +1 -0
package/dist/schemas.mjs
CHANGED
|
@@ -9,7 +9,7 @@ const PortDirectionSchema = z.enum([
|
|
|
9
9
|
]);
|
|
10
10
|
const PortSchema = z.object({
|
|
11
11
|
name: z.string(),
|
|
12
|
-
direction: PortDirectionSchema
|
|
12
|
+
direction: PortDirectionSchema,
|
|
13
13
|
label: z.string().optional(),
|
|
14
14
|
data: z.any(),
|
|
15
15
|
x: z.number().optional(),
|
|
@@ -66,6 +66,162 @@ const GraphSchema = z.object({
|
|
|
66
66
|
]).optional(),
|
|
67
67
|
style: StyleSchema.optional()
|
|
68
68
|
});
|
|
69
|
+
function getValidationIssues(schema, value) {
|
|
70
|
+
const result = schema.safeParse(value);
|
|
71
|
+
if (result.success) return [];
|
|
72
|
+
return result.error.issues.map((issue) => ({
|
|
73
|
+
code: issue.code,
|
|
74
|
+
message: issue.message,
|
|
75
|
+
path: issue.path.map((segment) => typeof segment === "symbol" ? String(segment) : segment)
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
function isGraphPort(value) {
|
|
79
|
+
return PortSchema.safeParse(value).success;
|
|
80
|
+
}
|
|
81
|
+
function isGraphNode(value) {
|
|
82
|
+
return NodeSchema.safeParse(value).success;
|
|
83
|
+
}
|
|
84
|
+
function isGraphEdge(value) {
|
|
85
|
+
return EdgeSchema.safeParse(value).success;
|
|
86
|
+
}
|
|
87
|
+
function isGraph(value) {
|
|
88
|
+
return validateGraph(value).length === 0;
|
|
89
|
+
}
|
|
90
|
+
function getGraphPortIssues(value) {
|
|
91
|
+
return getValidationIssues(PortSchema, value);
|
|
92
|
+
}
|
|
93
|
+
function getGraphNodeIssues(value) {
|
|
94
|
+
return getValidationIssues(NodeSchema, value);
|
|
95
|
+
}
|
|
96
|
+
function getGraphEdgeIssues(value) {
|
|
97
|
+
return getValidationIssues(EdgeSchema, value);
|
|
98
|
+
}
|
|
99
|
+
function getGraphIssues(value) {
|
|
100
|
+
return validateGraph(value);
|
|
101
|
+
}
|
|
102
|
+
function createIssue(code, message, path) {
|
|
103
|
+
return {
|
|
104
|
+
code,
|
|
105
|
+
message,
|
|
106
|
+
path
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function getDuplicateIndexes(items, getKey) {
|
|
110
|
+
const indexesByKey = /* @__PURE__ */ new Map();
|
|
111
|
+
items.forEach((item, index) => {
|
|
112
|
+
const key = getKey(item);
|
|
113
|
+
if (key == null) return;
|
|
114
|
+
const indexes = indexesByKey.get(key) ?? [];
|
|
115
|
+
indexes.push(index);
|
|
116
|
+
indexesByKey.set(key, indexes);
|
|
117
|
+
});
|
|
118
|
+
for (const [key, indexes] of indexesByKey) if (indexes.length < 2) indexesByKey.delete(key);
|
|
119
|
+
return indexesByKey;
|
|
120
|
+
}
|
|
121
|
+
function getGraphInvariantIssues(graph) {
|
|
122
|
+
const issues = [];
|
|
123
|
+
const nodeIndexes = /* @__PURE__ */ new Map();
|
|
124
|
+
const nodesById = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const [id, indexes] of getDuplicateIndexes(graph.nodes, (node) => node.id)) for (const index of indexes) issues.push(createIssue("duplicate_node_id", `Duplicate node id "${id}"`, [
|
|
126
|
+
"nodes",
|
|
127
|
+
index,
|
|
128
|
+
"id"
|
|
129
|
+
]));
|
|
130
|
+
for (const [id, indexes] of getDuplicateIndexes(graph.edges, (edge) => edge.id)) for (const index of indexes) issues.push(createIssue("duplicate_edge_id", `Duplicate edge id "${id}"`, [
|
|
131
|
+
"edges",
|
|
132
|
+
index,
|
|
133
|
+
"id"
|
|
134
|
+
]));
|
|
135
|
+
graph.nodes.forEach((node, index) => {
|
|
136
|
+
nodeIndexes.set(node.id, index);
|
|
137
|
+
nodesById.set(node.id, node);
|
|
138
|
+
});
|
|
139
|
+
if (graph.initialNodeId && !nodeIndexes.has(graph.initialNodeId)) issues.push(createIssue("missing_initial_node", `Initial node "${graph.initialNodeId}" does not exist`, ["initialNodeId"]));
|
|
140
|
+
graph.nodes.forEach((node, index) => {
|
|
141
|
+
if (node.id === "") issues.push(createIssue("empty_node_id", "Node id must be a non-empty string", [
|
|
142
|
+
"nodes",
|
|
143
|
+
index,
|
|
144
|
+
"id"
|
|
145
|
+
]));
|
|
146
|
+
if (node.parentId === "") issues.push(createIssue("empty_parent_id", "Node parentId must be a non-empty string", [
|
|
147
|
+
"nodes",
|
|
148
|
+
index,
|
|
149
|
+
"parentId"
|
|
150
|
+
]));
|
|
151
|
+
else if (node.parentId != null && !nodeIndexes.has(node.parentId)) issues.push(createIssue("missing_parent", `Parent node "${node.parentId}" does not exist`, [
|
|
152
|
+
"nodes",
|
|
153
|
+
index,
|
|
154
|
+
"parentId"
|
|
155
|
+
]));
|
|
156
|
+
if (node.initialNodeId && !nodeIndexes.has(node.initialNodeId)) issues.push(createIssue("missing_node_initial", `Initial node "${node.initialNodeId}" does not exist`, [
|
|
157
|
+
"nodes",
|
|
158
|
+
index,
|
|
159
|
+
"initialNodeId"
|
|
160
|
+
]));
|
|
161
|
+
for (const [name, indexes] of getDuplicateIndexes(node.ports ?? [], (port) => port.name)) for (const portIndex of indexes) issues.push(createIssue("duplicate_port_name", `Duplicate port name "${name}" on node "${node.id}"`, [
|
|
162
|
+
"nodes",
|
|
163
|
+
index,
|
|
164
|
+
"ports",
|
|
165
|
+
portIndex,
|
|
166
|
+
"name"
|
|
167
|
+
]));
|
|
168
|
+
});
|
|
169
|
+
for (const node of graph.nodes) {
|
|
170
|
+
const seen = /* @__PURE__ */ new Set();
|
|
171
|
+
let current = node.parentId;
|
|
172
|
+
while (current != null) {
|
|
173
|
+
if (current === node.id || seen.has(current)) {
|
|
174
|
+
issues.push(createIssue("parent_cycle", `Node "${node.id}" is part of a parent cycle`, [
|
|
175
|
+
"nodes",
|
|
176
|
+
nodeIndexes.get(node.id) ?? 0,
|
|
177
|
+
"parentId"
|
|
178
|
+
]));
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
seen.add(current);
|
|
182
|
+
current = nodesById.get(current)?.parentId;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
graph.edges.forEach((edge, index) => {
|
|
186
|
+
if (edge.id === "") issues.push(createIssue("empty_edge_id", "Edge id must be a non-empty string", [
|
|
187
|
+
"edges",
|
|
188
|
+
index,
|
|
189
|
+
"id"
|
|
190
|
+
]));
|
|
191
|
+
const source = nodesById.get(edge.sourceId);
|
|
192
|
+
const target = nodesById.get(edge.targetId);
|
|
193
|
+
if (!source) issues.push(createIssue("missing_source_node", `Source node "${edge.sourceId}" does not exist`, [
|
|
194
|
+
"edges",
|
|
195
|
+
index,
|
|
196
|
+
"sourceId"
|
|
197
|
+
]));
|
|
198
|
+
if (!target) issues.push(createIssue("missing_target_node", `Target node "${edge.targetId}" does not exist`, [
|
|
199
|
+
"edges",
|
|
200
|
+
index,
|
|
201
|
+
"targetId"
|
|
202
|
+
]));
|
|
203
|
+
if (source && edge.sourcePort !== void 0 && !source.ports?.some((port) => port.name === edge.sourcePort)) issues.push(createIssue("missing_source_port", `Port "${edge.sourcePort}" does not exist on source node "${edge.sourceId}"`, [
|
|
204
|
+
"edges",
|
|
205
|
+
index,
|
|
206
|
+
"sourcePort"
|
|
207
|
+
]));
|
|
208
|
+
if (target && edge.targetPort !== void 0 && !target.ports?.some((port) => port.name === edge.targetPort)) issues.push(createIssue("missing_target_port", `Port "${edge.targetPort}" does not exist on target node "${edge.targetId}"`, [
|
|
209
|
+
"edges",
|
|
210
|
+
index,
|
|
211
|
+
"targetPort"
|
|
212
|
+
]));
|
|
213
|
+
});
|
|
214
|
+
return issues;
|
|
215
|
+
}
|
|
216
|
+
function validateGraph(value) {
|
|
217
|
+
const shapeResult = GraphSchema.safeParse(value);
|
|
218
|
+
if (!shapeResult.success) return shapeResult.error.issues.map((issue) => ({
|
|
219
|
+
code: issue.code,
|
|
220
|
+
message: issue.message,
|
|
221
|
+
path: issue.path.map((segment) => typeof segment === "symbol" ? String(segment) : segment)
|
|
222
|
+
}));
|
|
223
|
+
return getGraphInvariantIssues(shapeResult.data);
|
|
224
|
+
}
|
|
69
225
|
|
|
70
226
|
//#endregion
|
|
71
|
-
export { EdgeSchema, GraphSchema, NodeSchema, PortSchema };
|
|
227
|
+
export { EdgeSchema, GraphSchema, NodeSchema, PortSchema, getGraphEdgeIssues, getGraphIssues, getGraphNodeIssues, getGraphPortIssues, isGraph, isGraphEdge, isGraphNode, isGraphPort, validateGraph };
|
package/package.json
CHANGED