import { KindGuard, type TSchema } from "@alkdev/typebox"; import { Value } from "@alkdev/typebox/value"; import { assert } from "@std/assert"; import { EdgeType, GraphConfig, GraphSchema, NodeType } from "./types.ts"; export class SchemaBuilder { private schema: { config: Record; nodeTypes: Record; edgeTypes: Record; } = { config: {}, nodeTypes: {}, edgeTypes: {}, }; config(config: Partial): SchemaBuilder { const configObj = Value.Default(GraphConfig, config) as GraphConfig; this.check(GraphConfig, configObj); this.schema.config = configObj as Record; return this; } nodeType(name: string, schema: TSchema): SchemaBuilder { assert( KindGuard.IsSchema(schema), `type '${name}' is not a valid json schema.`, ); if (!this.schema.nodeTypes) this.schema.nodeTypes = {}; const nodeTypeObj: NodeType = { name, schema }; this.check(NodeType, nodeTypeObj); this.schema.nodeTypes[name] = nodeTypeObj; return this; } edgeType( name: string, schema: TSchema, options?: { allowedSourceTypes?: string[]; allowedTargetTypes?: string[] }, ): SchemaBuilder { assert( KindGuard.IsSchema(schema), `type '${name}' is not a valid json schema.`, ); if (!this.schema.edgeTypes) this.schema.edgeTypes = {}; const edgeTypeObj: EdgeType = { name, schema, ...options }; this.check(EdgeType, edgeTypeObj); this.schema.edgeTypes[name] = edgeTypeObj; return this; } check(schema: TSchema, value: unknown): void { if (!Value.Check(schema, value)) { const errors = [...Value.Errors(schema, value)]; throw new Error( `Invalid schema structure: ${ JSON.stringify(errors.map((e) => `${e.path}: ${e.message}`)) }`, ); } } build(): GraphSchema { this.check(GraphSchema, this.schema); return this.schema as GraphSchema; } }