Try a Proof Tree
Apply logical tactics to branch and navigate the mathematical proof tree. Click nodes to connect/disconnect, track real-time telemetry, and run/rollback proof states.
The problem
JSON Schema is useful, but nested structures can be hard to follow in a text editor. SchemaFlow lets you compose them as a graph, inspect the connections, and see the generated schema as you work.
The approach: React renders the graph editor while TypeScript logic tracks dependencies, checks for cycles, and generates JSON Schema.
Reported project measurements: Sub-8ms canvas node interaction latency; 100% cyclical dependency detection and resolution via $ref pointers; automated real-time OpenAPI v3 code generation.
How it works
Immutable Graph State Core: State is modeled as a Directed Acyclic Graph (DAG) using a customized Zustand store with memoized selectors to eliminate unnecessary React re-renders.
Cyclical Dependency Detection: Depth-first search (DFS) traversal detects recursive schema references, automatically synthesizing $ref pointer definitions rather than entering infinite compilation loops.
Web Worker AST Compiler: Offloads heavy schema AST generation and JSON/YAML serialization to Web Workers, keeping the canvas render loop completely free of main-thread computation.
How the pieces connect
flowchart TD
A[Interactive Visual Canvas: React Flow] --> B[Zustand DAG Graph State Store]
B --> C[DFS Cyclical Reference Detector]
C -->|Valid / Resolved DAG| D[Web Worker AST Compiler]
D --> E[JSON Schema AST Generator]
E --> F1[JSON Schema Draft-07 Spec]
E --> F2[OpenAPI v3 Specification]
E --> F3[TypeScript Type Definitions]
F1 & F2 & F3 --> G[Live Code Preview & Registry Exporter]
Implementation notes
DAG Node State & Connection Definition (src/store/graph_store.ts)
// Immutable Graph State Model
interface SchemaNode {
id: string;
type: "string" | "number" | "object" | "array" | "ref";
properties: Record;
position: { x: number; y: number };
}
interface SchemaConnection {
sourceId: string;
targetId: string;
sourceHandle?: string;
targetHandle?: string;
}
DFS Recursive Reference Resolver (src/compiler/cycle_detector.ts)
// Depth-First Search for recursive reference resolution
function detectAndResolveCycles(nodes: SchemaNode[], connections: SchemaConnection[]): Map {
const adj = new Map();
nodes.forEach((n) => adj.set(n.id, []));
connections.forEach((c) => adj.get(c.sourceId)?.push(c.targetId));
const visited = new Set();
const recursionStack = new Set();
const refMap = new Map();
function dfs(nodeId: string): boolean {
visited.add(nodeId);
recursionStack.add(nodeId);
for (const neighbor of adj.get(nodeId) || []) {
if (!visited.has(neighbor)) {
if (dfs(neighbor)) return true;
} else if (recursionStack.has(neighbor)) {
refMap.set(neighbor, `#/definitions/${neighbor}`);
}
}
recursionStack.delete(nodeId);
return false;
}
nodes.forEach((n) => { if (!visited.has(n.id)) dfs(n.id); });
return refMap;
}
Tradeoffs and lessons
- Worker Thread Offloading: Moving JSON Schema serialization to Web Workers prevented frame drops on complex graphs containing 100+ interconnected schema nodes.
- Graceful Recursive
$refHandling: Detecting cycles and transforming them into JSON Schema$refdefinitions prevented browser tab crashes on recursive types (like file trees).