Implement fromJSON, export, toJSON, toString serialization for FlowGraph

This commit is contained in:
2026-05-21 21:51:24 +00:00
parent 7a5bd2f513
commit 5cfc8882bd
3 changed files with 294 additions and 8 deletions

View File

@@ -1,13 +1,15 @@
import { DirectedGraph } from "graphology";
import type { TSchema, Static } from "@alkdev/typebox";
import { Value } from "@alkdev/typebox/value";
import { willCreateCycle, topologicalSort, hasCycle } from "graphology-dag";
import {
DuplicateNodeError,
DuplicateEdgeError,
NodeNotFoundError,
CycleError,
InvalidInputError,
} from "../error/index.js";
import type { CallStatus, AnyValidationError } from "../error/index.js";
import type { CallStatus, AnyValidationError, ValidationError } from "../error/index.js";
import {
findCycles,
reachableFrom as reachableFromFn,
@@ -16,8 +18,10 @@ import { validate as _validate } from "./validation.js";
import {
OperationNodeAttrs as OperationNodeAttrsSchema,
OperationEdgeAttrs as OperationEdgeAttrsSchema,
OperationGraphSerialized,
CallGraphSerialized,
} from "../schema/index.js";
import type { OperationNodeAttrs } from "../schema/index.js";
import type { OperationNodeAttrs, FlowGraphSerialized } from "../schema/index.js";
import { typeCompat, type TypeCompatResult } from "../analysis/type-compat.js";
export interface FlowGraphOptions {
@@ -367,10 +371,64 @@ export class FlowGraph<
throw new Error("not implemented");
}
export(): FlowGraphSerialized {
return this._graph.export() as unknown as FlowGraphSerialized;
}
toJSON(): FlowGraphSerialized {
return this.export();
}
toString(): string {
return JSON.stringify(this.export());
}
static fromJSON(
_data: unknown,
data: FlowGraphSerialized,
): FlowGraph<TSchema, TSchema> {
throw new Error("not implemented");
const opCheck = Value.Check(OperationGraphSerialized, data);
const callCheck = Value.Check(CallGraphSerialized, data);
if (!opCheck && !callCheck) {
const errors: ValidationError[] = [];
const opIter = Value.Errors(OperationGraphSerialized, data as Record<string, unknown>);
for (const err of opIter) {
errors.push({
type: "schema",
nodeKey: "",
field: err.path.replace(/^\//, "") || err.path,
message: err.message,
value: err.value,
});
}
if (errors.length === 0) {
const callIter = Value.Errors(CallGraphSerialized, data as Record<string, unknown>);
for (const err of callIter) {
errors.push({
type: "schema",
nodeKey: "",
field: err.path.replace(/^\//, "") || err.path,
message: err.message,
value: err.value,
});
}
}
throw new InvalidInputError(errors);
}
const fg = new FlowGraph<TSchema, TSchema>();
for (const node of data.nodes) {
fg._graph.addNode(node.key, node.attributes as Attrs);
}
for (const edge of data.edges) {
fg._graph.addEdgeWithKey(edge.key, edge.source, edge.target, edge.attributes as Attrs);
}
if (hasCycle(fg._graph)) {
const cycles = findCycles(fg._graph);
throw new CycleError(cycles);
}
return fg;
}
private _findPath(from: string, to: string): string[] {