Fix StartOS 0.4 TypeScript packaging to match SDK API
This commit is contained in:
+272
@@ -0,0 +1,272 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Graph = void 0;
|
||||
/**
|
||||
* A directed graph data structure supporting vertex/edge management and graph traversal algorithms
|
||||
* including breadth-first search, reverse BFS, and shortest path computation.
|
||||
*
|
||||
* @typeParam VMetadata - The type of metadata stored on vertices
|
||||
* @typeParam EMetadata - The type of metadata stored on edges
|
||||
*/
|
||||
class Graph {
|
||||
constructor() {
|
||||
this.vertices = [];
|
||||
}
|
||||
/**
|
||||
* Serializes the graph to a JSON string for debugging.
|
||||
* @param metadataRepr - Optional function to transform metadata values before serialization
|
||||
* @returns A pretty-printed JSON string of the graph structure
|
||||
*/
|
||||
dump(metadataRepr = (a) => a) {
|
||||
const seen = new WeakSet();
|
||||
return JSON.stringify(this.vertices, (k, v) => {
|
||||
if (k === 'metadata')
|
||||
return metadataRepr(v);
|
||||
if (k === 'from')
|
||||
return metadataRepr(v.metadata);
|
||||
if (k === 'to')
|
||||
return metadataRepr(v.metadata);
|
||||
return v;
|
||||
}, 2);
|
||||
}
|
||||
/**
|
||||
* Adds a new vertex to the graph, optionally connecting it to existing vertices via edges.
|
||||
* @param metadata - The metadata to attach to the new vertex
|
||||
* @param fromEdges - Edges pointing from existing vertices to this new vertex
|
||||
* @param toEdges - Edges pointing from this new vertex to existing vertices
|
||||
* @returns The newly created vertex
|
||||
*/
|
||||
addVertex(metadata, fromEdges, toEdges) {
|
||||
const vertex = {
|
||||
metadata,
|
||||
edges: [],
|
||||
};
|
||||
for (let edge of fromEdges) {
|
||||
const vEdge = {
|
||||
metadata: edge.metadata,
|
||||
from: edge.from,
|
||||
to: vertex,
|
||||
};
|
||||
edge.from.edges.push(vEdge);
|
||||
vertex.edges.push(vEdge);
|
||||
}
|
||||
for (let edge of toEdges) {
|
||||
const vEdge = {
|
||||
metadata: edge.metadata,
|
||||
from: vertex,
|
||||
to: edge.to,
|
||||
};
|
||||
edge.to.edges.push(vEdge);
|
||||
vertex.edges.push(vEdge);
|
||||
}
|
||||
this.vertices.push(vertex);
|
||||
return vertex;
|
||||
}
|
||||
/**
|
||||
* Returns a generator that yields all vertices matching the predicate.
|
||||
* @param predicate - A function to test each vertex
|
||||
* @returns A generator of matching vertices
|
||||
*/
|
||||
findVertex(predicate) {
|
||||
const veritces = this.vertices;
|
||||
function* gen() {
|
||||
for (let vertex of veritces) {
|
||||
if (predicate(vertex)) {
|
||||
yield vertex;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return gen();
|
||||
}
|
||||
/**
|
||||
* Adds a directed edge between two existing vertices.
|
||||
* @param metadata - The metadata to attach to the edge
|
||||
* @param from - The source vertex
|
||||
* @param to - The destination vertex
|
||||
* @returns The newly created edge
|
||||
*/
|
||||
addEdge(metadata, from, to) {
|
||||
const edge = {
|
||||
metadata,
|
||||
from,
|
||||
to,
|
||||
};
|
||||
edge.from.edges.push(edge);
|
||||
edge.to.edges.push(edge);
|
||||
return edge;
|
||||
}
|
||||
/**
|
||||
* Performs a breadth-first traversal following outgoing edges from the starting vertex or vertices.
|
||||
* @param from - A starting vertex, or a predicate to select multiple starting vertices
|
||||
* @returns A generator yielding vertices in BFS order
|
||||
*/
|
||||
breadthFirstSearch(from) {
|
||||
const visited = [];
|
||||
function* rec(vertex) {
|
||||
if (visited.includes(vertex)) {
|
||||
return null;
|
||||
}
|
||||
visited.push(vertex);
|
||||
yield vertex;
|
||||
let generators = vertex.edges
|
||||
.filter((e) => e.from === vertex)
|
||||
.map((e) => rec(e.to));
|
||||
while (generators.length) {
|
||||
let prev = generators;
|
||||
generators = [];
|
||||
for (let gen of prev) {
|
||||
const next = gen.next();
|
||||
if (!next.done) {
|
||||
generators.push(gen);
|
||||
yield next.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (from instanceof Function) {
|
||||
let generators = this.vertices.filter(from).map(rec);
|
||||
return (function* () {
|
||||
while (generators.length) {
|
||||
let prev = generators;
|
||||
generators = [];
|
||||
for (let gen of prev) {
|
||||
const next = gen.next();
|
||||
if (!next.done) {
|
||||
generators.push(gen);
|
||||
yield next.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
}
|
||||
else {
|
||||
return rec(from);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Performs a reverse breadth-first traversal following incoming edges from the starting vertex or vertices.
|
||||
* @param to - A starting vertex, or a predicate to select multiple starting vertices
|
||||
* @returns A generator yielding vertices in reverse BFS order
|
||||
*/
|
||||
reverseBreadthFirstSearch(to) {
|
||||
const visited = [];
|
||||
function* rec(vertex) {
|
||||
if (visited.includes(vertex)) {
|
||||
return null;
|
||||
}
|
||||
visited.push(vertex);
|
||||
yield vertex;
|
||||
let generators = vertex.edges
|
||||
.filter((e) => e.to === vertex)
|
||||
.map((e) => rec(e.from));
|
||||
while (generators.length) {
|
||||
let prev = generators;
|
||||
generators = [];
|
||||
for (let gen of prev) {
|
||||
const next = gen.next();
|
||||
if (!next.done) {
|
||||
generators.push(gen);
|
||||
yield next.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (to instanceof Function) {
|
||||
let generators = this.vertices.filter(to).map(rec);
|
||||
return (function* () {
|
||||
while (generators.length) {
|
||||
let prev = generators;
|
||||
generators = [];
|
||||
for (let gen of prev) {
|
||||
const next = gen.next();
|
||||
if (!next.done) {
|
||||
generators.push(gen);
|
||||
yield next.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
}
|
||||
else {
|
||||
return rec(to);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Finds the shortest path (by edge count) between two vertices using BFS.
|
||||
* @param from - The starting vertex, or a predicate to select starting vertices
|
||||
* @param to - The target vertex, or a predicate to identify target vertices
|
||||
* @returns An array of edges forming the shortest path, or `null` if no path exists
|
||||
*/
|
||||
shortestPath(from, to) {
|
||||
const isDone = to instanceof Function
|
||||
? to
|
||||
: (v) => v === to;
|
||||
const path = [];
|
||||
const visited = [];
|
||||
function* check(vertex, path) {
|
||||
if (isDone(vertex)) {
|
||||
return path;
|
||||
}
|
||||
if (visited.includes(vertex)) {
|
||||
return null;
|
||||
}
|
||||
visited.push(vertex);
|
||||
yield;
|
||||
let generators = vertex.edges
|
||||
.filter((e) => e.from === vertex)
|
||||
.map((e) => check(e.to, [...path, e]));
|
||||
while (generators.length) {
|
||||
let prev = generators;
|
||||
generators = [];
|
||||
for (let gen of prev) {
|
||||
const next = gen.next();
|
||||
if (next.done === true) {
|
||||
if (next.value) {
|
||||
return next.value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
generators.push(gen);
|
||||
yield;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (from instanceof Function) {
|
||||
let generators = this.vertices.filter(from).map((v) => check(v, []));
|
||||
while (generators.length) {
|
||||
let prev = generators;
|
||||
generators = [];
|
||||
for (let gen of prev) {
|
||||
const next = gen.next();
|
||||
if (next.done === true) {
|
||||
if (next.value) {
|
||||
return next.value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
generators.push(gen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const gen = check(from, []);
|
||||
while (true) {
|
||||
const next = gen.next();
|
||||
if (next.done) {
|
||||
return next.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
exports.Graph = Graph;
|
||||
//# sourceMappingURL=graph.js.map
|
||||
Reference in New Issue
Block a user