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
160 lines • 5.51 kB
JavaScript
/**
* Graphology Graph Backend
*
* Optional implementation of GraphBackend using the graphology library.
* Provides typed/attributed edges, BFS/DFS traversal, community detection,
* and shortest-path algorithms.
*
* Install: npm install graphology graphology-types graphology-operators graphology-traversal
*
* @implements #728
* @source @src/artifacts/graph-backend.ts
* @tests @test/unit/artifacts/graphology-backend.test.ts
*/
import { normalizeEdges } from '../types.js';
/**
* Graphology-backed graph with rich traversal and operator ecosystem.
*
* Uses dynamic import so the graphology package is only loaded when this
* backend is explicitly selected. Projects that don't install graphology
* pay zero cost.
*/
export class GraphologyBackend {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
graph;
constructor(graphInstance) {
this.graph = graphInstance;
}
/**
* Factory: creates a GraphologyBackend with a fresh directed multigraph.
* Throws a clear error if graphology is not installed.
*/
static async create() {
try {
const graphology = await import('graphology');
const Graph = graphology.default ?? graphology;
const graph = new Graph({ type: 'directed', multi: true });
return new GraphologyBackend(graph);
}
catch {
throw new Error('graphology backend requires: npm install graphology graphology-types graphology-operators graphology-traversal');
}
}
// --- Mutation ---
addNode(id, attrs) {
if (!this.graph.hasNode(id)) {
this.graph.addNode(id, attrs ?? {});
}
else if (attrs) {
this.graph.mergeNodeAttributes(id, attrs);
}
}
addEdge(source, target, type = 'depends-on', attrs) {
if (!this.graph.hasNode(source))
this.graph.addNode(source, {});
if (!this.graph.hasNode(target))
this.graph.addNode(target, {});
this.graph.addEdge(source, target, { type, ...attrs });
}
// --- Query ---
hasNode(id) {
return this.graph.hasNode(id);
}
hasEdge(source, target, edgeType) {
if (!edgeType)
return this.graph.hasEdge(source, target);
const edges = this.graph.edges(source, target);
return edges.some((e) => this.graph.getEdgeAttribute(e, 'type') === edgeType);
}
getNodeAttrs(id) {
if (!this.graph.hasNode(id))
return undefined;
return this.graph.getNodeAttributes(id);
}
nodes() {
return this.graph.nodes();
}
// --- Traversal ---
neighbors(nodeId, direction, edgeType) {
if (!this.graph.hasNode(nodeId))
return [];
let edgeIterator;
if (direction === 'in') {
edgeIterator = this.graph.inEdges(nodeId);
}
else if (direction === 'out') {
edgeIterator = this.graph.outEdges(nodeId);
}
else {
edgeIterator = this.graph.edges(nodeId);
}
const results = new Set();
for (const edgeKey of edgeIterator) {
if (edgeType && this.graph.getEdgeAttribute(edgeKey, 'type') !== edgeType)
continue;
const src = this.graph.source(edgeKey);
const tgt = this.graph.target(edgeKey);
// Add the "other" node
results.add(src === nodeId ? tgt : src);
}
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 = {};
this.graph.forEachNode((id) => {
const upstream = [];
const downstream = [];
for (const edgeKey of this.graph.inEdges(id)) {
upstream.push({
path: this.graph.source(edgeKey),
type: this.graph.getEdgeAttribute(edgeKey, 'type') ?? 'depends-on',
});
}
for (const edgeKey of this.graph.outEdges(id)) {
downstream.push({
path: this.graph.target(edgeKey),
type: this.graph.getEdgeAttribute(edgeKey, 'type') ?? 'depends-on',
});
}
result[id] = { upstream, downstream };
});
return result;
}
deserialize(data) {
this.graph.clear();
// First pass: add all declared nodes
for (const id of Object.keys(data)) {
if (!this.graph.hasNode(id))
this.graph.addNode(id, {});
}
// Second pass: add edges (upstream edges define the directed relationships)
for (const [id, node] of Object.entries(data)) {
const upEdges = normalizeEdges(node.upstream);
for (const edge of upEdges) {
if (!this.graph.hasNode(edge.path))
this.graph.addNode(edge.path, {});
this.graph.addEdge(edge.path, id, { type: edge.type });
}
}
}
nodeCount() {
return this.graph.order;
}
edgeCount() {
return this.graph.size;
}
}
//# sourceMappingURL=graphology-backend.js.map