UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

129 lines 4.1 kB
/** * JSON Graph Backend * * Default zero-dependency implementation of GraphBackend wrapping the * existing DependencyGraph adjacency list format. Suitable for projects * with <5k nodes. * * @implements #727 * @source @src/artifacts/graph-backend.ts * @tests @test/unit/artifacts/graph-backend.test.ts */ import { normalizeEdges } from '../types.js'; /** * JSON-backed graph using plain objects and JS Set operations. * * This is the default backend — always available, no external dependencies. */ export class JsonGraphBackend { graph = new Map(); // --- Mutation --- addNode(id, attrs) { if (!this.graph.has(id)) { this.graph.set(id, { upstream: [], downstream: [], attrs: attrs ?? {} }); } else if (attrs) { const node = this.graph.get(id); Object.assign(node.attrs, attrs); } } addEdge(source, target, type = 'depends-on', attrs) { // Ensure both nodes exist this.addNode(source); this.addNode(target); const sourceNode = this.graph.get(source); const targetNode = this.graph.get(target); // Add downstream edge on source sourceNode.downstream.push({ path: target, type, ...attrs }); // Add upstream edge on target targetNode.upstream.push({ path: source, type, ...attrs }); } // --- Query --- hasNode(id) { return this.graph.has(id); } hasEdge(source, target, edgeType) { const node = this.graph.get(source); if (!node) return false; return node.downstream.some(e => e.path === target && (!edgeType || e.type === edgeType)); } getNodeAttrs(id) { return this.graph.get(id)?.attrs; } nodes() { return [...this.graph.keys()]; } // --- Traversal --- neighbors(nodeId, direction, edgeType) { const node = this.graph.get(nodeId); if (!node) return []; const results = new Set(); if (direction === 'in' || direction === 'both') { for (const edge of node.upstream) { if (!edgeType || edge.type === edgeType) { results.add(edge.path); } } } if (direction === 'out' || direction === 'both') { for (const edge of node.downstream) { if (!edgeType || edge.type === edgeType) { results.add(edge.path); } } } return [...results]; } // --- Set operations --- intersection(setA, setB) { const b = new Set(setB); return setA.filter(x => b.has(x)); } difference(setA, setB) { const b = new Set(setB); return setA.filter(x => !b.has(x)); } union(setA, setB) { return [...new Set([...setA, ...setB])]; } // --- Persistence --- serialize() { const result = {}; for (const [id, node] of this.graph) { result[id] = { upstream: [...node.upstream], downstream: [...node.downstream], }; } return result; } deserialize(data) { this.graph.clear(); for (const [id, node] of Object.entries(data)) { const upstream = normalizeEdges(node.upstream); const downstream = normalizeEdges(node.downstream); this.graph.set(id, { upstream, downstream, attrs: {} }); } // Ensure all referenced nodes exist for (const [, node] of this.graph) { for (const edge of [...node.upstream, ...node.downstream]) { if (!this.graph.has(edge.path)) { this.graph.set(edge.path, { upstream: [], downstream: [], attrs: {} }); } } } } nodeCount() { return this.graph.size; } edgeCount() { let count = 0; for (const [, node] of this.graph) { count += node.downstream.length; } return count; } } //# sourceMappingURL=json-backend.js.map