@monstermann/graph 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1383 -0
- package/dist/Graph/batch.d.mts +47 -0
- package/dist/Graph/batch.mjs +57 -0
- package/dist/Graph/create.d.mts +36 -0
- package/dist/Graph/create.mjs +37 -0
- package/dist/Graph/findEdge.d.mts +60 -0
- package/dist/Graph/findEdge.mjs +10 -0
- package/dist/Graph/findEdges.d.mts +65 -0
- package/dist/Graph/findEdges.mjs +12 -0
- package/dist/Graph/findNeighbor.d.mts +54 -0
- package/dist/Graph/findNeighbor.mjs +16 -0
- package/dist/Graph/findNeighbors.d.mts +59 -0
- package/dist/Graph/findNeighbors.mjs +18 -0
- package/dist/Graph/findNode.d.mts +67 -0
- package/dist/Graph/findNode.mjs +7 -0
- package/dist/Graph/findNodes.d.mts +63 -0
- package/dist/Graph/findNodes.mjs +9 -0
- package/dist/Graph/forEachEdge.d.mts +52 -0
- package/dist/Graph/forEachEdge.mjs +56 -0
- package/dist/Graph/forEachNeighbor.d.mts +52 -0
- package/dist/Graph/forEachNeighbor.mjs +59 -0
- package/dist/Graph/forEachNode.d.mts +49 -0
- package/dist/Graph/forEachNode.mjs +51 -0
- package/dist/Graph/fromJS.d.mts +55 -0
- package/dist/Graph/fromJS.mjs +62 -0
- package/dist/Graph/getEdge.d.mts +50 -0
- package/dist/Graph/getEdge.mjs +54 -0
- package/dist/Graph/getEdges.d.mts +47 -0
- package/dist/Graph/getEdges.mjs +51 -0
- package/dist/Graph/getNeighbor.d.mts +50 -0
- package/dist/Graph/getNeighbor.mjs +56 -0
- package/dist/Graph/getNeighbors.d.mts +47 -0
- package/dist/Graph/getNeighbors.mjs +58 -0
- package/dist/Graph/getNode.d.mts +48 -0
- package/dist/Graph/getNode.mjs +52 -0
- package/dist/Graph/getNodes.d.mts +46 -0
- package/dist/Graph/getNodes.mjs +48 -0
- package/dist/Graph/hasEdge.d.mts +46 -0
- package/dist/Graph/hasEdge.mjs +50 -0
- package/dist/Graph/hasNode.d.mts +42 -0
- package/dist/Graph/hasNode.mjs +46 -0
- package/dist/Graph/index.d.mts +68 -0
- package/dist/Graph/index.mjs +65 -0
- package/dist/Graph/internals/core.mjs +51 -0
- package/dist/Graph/internals/parseNodeIdentifier.mjs +7 -0
- package/dist/Graph/internals/types.d.mts +16 -0
- package/dist/Graph/mapEdge.d.mts +52 -0
- package/dist/Graph/mapEdge.mjs +60 -0
- package/dist/Graph/mapNode.d.mts +65 -0
- package/dist/Graph/mapNode.mjs +83 -0
- package/dist/Graph/mergeEdge.d.mts +52 -0
- package/dist/Graph/mergeEdge.mjs +63 -0
- package/dist/Graph/mergeNode.d.mts +50 -0
- package/dist/Graph/mergeNode.mjs +60 -0
- package/dist/Graph/removeEdge.d.mts +49 -0
- package/dist/Graph/removeEdge.mjs +71 -0
- package/dist/Graph/removeNode.d.mts +46 -0
- package/dist/Graph/removeNode.mjs +71 -0
- package/dist/Graph/setEdge.d.mts +52 -0
- package/dist/Graph/setEdge.mjs +78 -0
- package/dist/Graph/setNode.d.mts +43 -0
- package/dist/Graph/setNode.mjs +50 -0
- package/dist/Graph/toJS.d.mts +59 -0
- package/dist/Graph/toJS.mjs +82 -0
- package/dist/Graph/types.d.mts +29 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Edges, Graph, Node } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/batch.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* # batch
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* function Graph.batch(
|
|
9
|
+
* graph: Graph,
|
|
10
|
+
* transform: (graph: Graph) => Graph | void,
|
|
11
|
+
* ): Graph
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Performs multiple graph mutations efficiently by batching them together. Returns the original graph if no changes were made, optimizing for structural sharing.
|
|
15
|
+
*
|
|
16
|
+
* ## Example
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { Graph } from "@monstermann/graph";
|
|
20
|
+
*
|
|
21
|
+
* type Nodes =
|
|
22
|
+
* | { type: "Task"; id: string; title: string }
|
|
23
|
+
* | { type: "Section"; id: string; name: string }
|
|
24
|
+
* | { type: "Project"; id: string; name: string };
|
|
25
|
+
*
|
|
26
|
+
* type Edges = {
|
|
27
|
+
* Project: { Task: void };
|
|
28
|
+
* Section: { Task: void };
|
|
29
|
+
* };
|
|
30
|
+
*
|
|
31
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
32
|
+
*
|
|
33
|
+
* // Add multiple nodes and edges in a single batch
|
|
34
|
+
* const updated = Graph.batch(graph, (g) => {
|
|
35
|
+
* g = Graph.setNode(g, { type: "Project", id: "1", name: "My Project" });
|
|
36
|
+
* g = Graph.setNode(g, { type: "Task", id: "1", title: "Task 1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "2", title: "Task 2" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
|
|
39
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"]);
|
|
40
|
+
* return g;
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
declare function batch<N extends Node, E extends Edges<N>>(prevGraph: Graph<N, E>, transform: (graph: Graph<N, E>) => Graph<N, E> | void): Graph<N, E>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { batch };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//#region src/Graph/batch.ts
|
|
2
|
+
/**
|
|
3
|
+
* # batch
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* function Graph.batch(
|
|
7
|
+
* graph: Graph,
|
|
8
|
+
* transform: (graph: Graph) => Graph | void,
|
|
9
|
+
* ): Graph
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Performs multiple graph mutations efficiently by batching them together. Returns the original graph if no changes were made, optimizing for structural sharing.
|
|
13
|
+
*
|
|
14
|
+
* ## Example
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { Graph } from "@monstermann/graph";
|
|
18
|
+
*
|
|
19
|
+
* type Nodes =
|
|
20
|
+
* | { type: "Task"; id: string; title: string }
|
|
21
|
+
* | { type: "Section"; id: string; name: string }
|
|
22
|
+
* | { type: "Project"; id: string; name: string };
|
|
23
|
+
*
|
|
24
|
+
* type Edges = {
|
|
25
|
+
* Project: { Task: void };
|
|
26
|
+
* Section: { Task: void };
|
|
27
|
+
* };
|
|
28
|
+
*
|
|
29
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
30
|
+
*
|
|
31
|
+
* // Add multiple nodes and edges in a single batch
|
|
32
|
+
* const updated = Graph.batch(graph, (g) => {
|
|
33
|
+
* g = Graph.setNode(g, { type: "Project", id: "1", name: "My Project" });
|
|
34
|
+
* g = Graph.setNode(g, { type: "Task", id: "1", title: "Task 1" });
|
|
35
|
+
* g = Graph.setNode(g, { type: "Task", id: "2", title: "Task 2" });
|
|
36
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
|
|
37
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"]);
|
|
38
|
+
* return g;
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
*/
|
|
43
|
+
function batch(prevGraph, transform) {
|
|
44
|
+
if (prevGraph.has("clones")) return transform(prevGraph) ?? prevGraph;
|
|
45
|
+
const clone = new Map(prevGraph);
|
|
46
|
+
clone.set("clones", new Set([clone]));
|
|
47
|
+
const nextGraph = transform(clone) ?? clone;
|
|
48
|
+
if (isEqual(prevGraph, nextGraph)) return prevGraph;
|
|
49
|
+
nextGraph.delete("clones");
|
|
50
|
+
return nextGraph;
|
|
51
|
+
}
|
|
52
|
+
function isEqual(before, after) {
|
|
53
|
+
return before.get("nodes") === after.get("nodes") && before.get("edges") === after.get("edges");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { batch };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Edges, Graph, Node, NodeType } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/create.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* # create
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* function Graph.create<Nodes, Edges>(): Graph<Nodes, Edges>
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* Creates a new empty graph with the provided nodes & edges configuration.
|
|
12
|
+
*
|
|
13
|
+
* ## Example
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { Graph } from "@monstermann/graph";
|
|
17
|
+
*
|
|
18
|
+
* type Nodes =
|
|
19
|
+
* | { type: "Task"; id: string }
|
|
20
|
+
* | { type: "Section"; id: string }
|
|
21
|
+
* | { type: "Project"; id: string };
|
|
22
|
+
*
|
|
23
|
+
* type Edges = {
|
|
24
|
+
* // Project <-> Task
|
|
25
|
+
* Project: { Task: void };
|
|
26
|
+
* // Section <-> Task
|
|
27
|
+
* Section: { Task: void };
|
|
28
|
+
* };
|
|
29
|
+
*
|
|
30
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
declare function create<N extends Node, E extends Edges<N> = Record<NodeType<N>, Record<NodeType<N>, void>>>(): Graph<N, E>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { create };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/Graph/create.ts
|
|
2
|
+
/**
|
|
3
|
+
* # create
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* function Graph.create<Nodes, Edges>(): Graph<Nodes, Edges>
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* Creates a new empty graph with the provided nodes & edges configuration.
|
|
10
|
+
*
|
|
11
|
+
* ## Example
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { Graph } from "@monstermann/graph";
|
|
15
|
+
*
|
|
16
|
+
* type Nodes =
|
|
17
|
+
* | { type: "Task"; id: string }
|
|
18
|
+
* | { type: "Section"; id: string }
|
|
19
|
+
* | { type: "Project"; id: string };
|
|
20
|
+
*
|
|
21
|
+
* type Edges = {
|
|
22
|
+
* // Project <-> Task
|
|
23
|
+
* Project: { Task: void };
|
|
24
|
+
* // Section <-> Task
|
|
25
|
+
* Section: { Task: void };
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
function create() {
|
|
33
|
+
return /* @__PURE__ */ new Map();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { create };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/findEdge.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* # findEdge
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* function Graph.findEdge(
|
|
10
|
+
* graph: Graph,
|
|
11
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
12
|
+
* type: NodeType,
|
|
13
|
+
* find: (edge: EdgeData) => boolean,
|
|
14
|
+
* ): EdgeData | undefined
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Finds the first edge of a specific type from a source node that matches the predicate. Returns `undefined` if no matching edge is found.
|
|
18
|
+
*
|
|
19
|
+
* ## Example
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Graph } from "@monstermann/graph";
|
|
23
|
+
*
|
|
24
|
+
* type Nodes =
|
|
25
|
+
* | { type: "Task"; id: string }
|
|
26
|
+
* | { type: "Section"; id: string }
|
|
27
|
+
* | { type: "Project"; id: string };
|
|
28
|
+
*
|
|
29
|
+
* type Edges = {
|
|
30
|
+
* Project: { Task: { priority: number; assignedAt: Date } };
|
|
31
|
+
* Section: { Task: void };
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
35
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
36
|
+
* g = Graph.setNode(g, { type: "Task", id: "1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "2" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
|
|
39
|
+
* priority: 1,
|
|
40
|
+
* assignedAt: new Date(),
|
|
41
|
+
* });
|
|
42
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], {
|
|
43
|
+
* priority: 2,
|
|
44
|
+
* assignedAt: new Date(),
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* const highPriorityEdge = Graph.findEdge(
|
|
48
|
+
* g,
|
|
49
|
+
* ["Project", "1"],
|
|
50
|
+
* "Task",
|
|
51
|
+
* (edge) => edge.priority > 1,
|
|
52
|
+
* );
|
|
53
|
+
* // highPriorityEdge: { priority: 2, assignedAt: Date }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
declare function findEdge<N extends Node, E extends Edges<N>, N1 extends keyof Bimap<E> & NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>, E2 extends Bimap<E>[N1][N2]>(graph: Graph<N, E>, source: NodeIdentifier<N, N1>, type: N2, find: (edge: NoInfer<Bimap<E>[N1][N2]>) => edge is E2): E2 | undefined;
|
|
58
|
+
declare function findEdge<N extends Node, E extends Edges<N>, N1 extends keyof Bimap<E> & NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>>(graph: Graph<N, E>, source: NodeIdentifier<N, N1>, type: N2, find: (edge: NoInfer<Bimap<E>[N1][N2]>) => boolean): Bimap<E>[N1][N2] | undefined;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { findEdge };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { parseNodeIdentifier } from "./internals/parseNodeIdentifier.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/findEdge.ts
|
|
4
|
+
function findEdge(graph, source, type, find) {
|
|
5
|
+
const [sourceType, sourceId] = parseNodeIdentifier(source);
|
|
6
|
+
for (const edge of graph.get("edges")?.get(sourceType)?.get(sourceId)?.get(type)?.values() ?? []) if (find(edge)) return edge;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { findEdge };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/findEdges.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* # findEdges
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* function Graph.findEdges(
|
|
10
|
+
* graph: Graph,
|
|
11
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
12
|
+
* type: NodeType,
|
|
13
|
+
* find: (edge: EdgeData) => boolean,
|
|
14
|
+
* ): EdgeData[]
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Finds all edges of a specific type from a source node that match the predicate.
|
|
18
|
+
*
|
|
19
|
+
* ## Example
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Graph } from "@monstermann/graph";
|
|
23
|
+
*
|
|
24
|
+
* type Nodes =
|
|
25
|
+
* | { type: "Task"; id: string }
|
|
26
|
+
* | { type: "Section"; id: string }
|
|
27
|
+
* | { type: "Project"; id: string };
|
|
28
|
+
*
|
|
29
|
+
* type Edges = {
|
|
30
|
+
* Project: { Task: { priority: number; assignedAt: Date } };
|
|
31
|
+
* Section: { Task: void };
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
35
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
36
|
+
* g = Graph.setNode(g, { type: "Task", id: "1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "2" });
|
|
38
|
+
* g = Graph.setNode(g, { type: "Task", id: "3" });
|
|
39
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
|
|
40
|
+
* priority: 1,
|
|
41
|
+
* assignedAt: new Date(),
|
|
42
|
+
* });
|
|
43
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], {
|
|
44
|
+
* priority: 2,
|
|
45
|
+
* assignedAt: new Date(),
|
|
46
|
+
* });
|
|
47
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "3"], {
|
|
48
|
+
* priority: 3,
|
|
49
|
+
* assignedAt: new Date(),
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* const highPriorityEdges = Graph.findEdges(
|
|
53
|
+
* g,
|
|
54
|
+
* ["Project", "1"],
|
|
55
|
+
* "Task",
|
|
56
|
+
* (edge) => edge.priority >= 2,
|
|
57
|
+
* );
|
|
58
|
+
* // highPriorityEdges: [{ priority: 2, ... }, { priority: 3, ... }]
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
*/
|
|
62
|
+
declare function findEdges<N extends Node, E extends Edges<N>, N1 extends keyof Bimap<E> & NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>, E2 extends Bimap<E>[N1][N2]>(graph: Graph<N, E>, source: NodeIdentifier<N, N1>, type: N2, find: (edge: NoInfer<Bimap<E>[N1][N2]>) => edge is E2): E2[];
|
|
63
|
+
declare function findEdges<N extends Node, E extends Edges<N>, N1 extends keyof Bimap<E> & NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>>(graph: Graph<N, E>, source: NodeIdentifier<N, N1>, type: N2, find: (edge: NoInfer<Bimap<E>[N1][N2]>) => boolean): Bimap<E>[N1][N2][];
|
|
64
|
+
//#endregion
|
|
65
|
+
export { findEdges };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { parseNodeIdentifier } from "./internals/parseNodeIdentifier.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/findEdges.ts
|
|
4
|
+
function findEdges(graph, source, type, find) {
|
|
5
|
+
const result = [];
|
|
6
|
+
const [sourceType, sourceId] = parseNodeIdentifier(source);
|
|
7
|
+
for (const edge of graph.get("edges")?.get(sourceType)?.get(sourceId)?.get(type)?.values() ?? []) if (find(edge)) result.push(edge);
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { findEdges };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeOfType, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/findNeighbor.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* # findNeighbor
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* function Graph.findNeighbor(
|
|
10
|
+
* graph: Graph,
|
|
11
|
+
* node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
12
|
+
* type: NodeType,
|
|
13
|
+
* find: (target: Node, edge: EdgeData, source: Node) => boolean,
|
|
14
|
+
* ): Node | undefined
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Finds the first neighbor node of a specific type that matches the predicate. The predicate receives the target node, edge data, and source node. Returns `undefined` if no matching neighbor is found.
|
|
18
|
+
*
|
|
19
|
+
* ## Example
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Graph } from "@monstermann/graph";
|
|
23
|
+
*
|
|
24
|
+
* type Nodes =
|
|
25
|
+
* | { type: "Task"; id: string; title: string }
|
|
26
|
+
* | { type: "Section"; id: string }
|
|
27
|
+
* | { type: "Project"; id: string };
|
|
28
|
+
*
|
|
29
|
+
* type Edges = {
|
|
30
|
+
* Project: { Task: { priority: number } };
|
|
31
|
+
* Section: { Task: void };
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
35
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
36
|
+
* g = Graph.setNode(g, { type: "Task", id: "1", title: "Low Priority Task" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "2", title: "High Priority Task" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
|
|
39
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 3 });
|
|
40
|
+
*
|
|
41
|
+
* const highPriorityTask = Graph.findNeighbor(
|
|
42
|
+
* g,
|
|
43
|
+
* ["Project", "1"],
|
|
44
|
+
* "Task",
|
|
45
|
+
* (task, edge, project) => edge.priority > 2,
|
|
46
|
+
* );
|
|
47
|
+
* // highPriorityTask: { type: "Task", id: "2", title: "High Priority Task" }
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
declare function findNeighbor<N extends Node, E extends Edges<N>, N1 extends NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>, N3 extends NodeOfType<N, N2>>(graph: Graph<N, E>, node: NodeIdentifier<N, N1>, type: N2, find: (target: NoInfer<NodeOfType<N, N2>>, edge: NoInfer<Bimap<E>[N1][N2]>, source: NoInfer<NodeOfType<N, N1>>) => target is N3): N3 | undefined;
|
|
52
|
+
declare function findNeighbor<N extends Node, E extends Edges<N>, N1 extends NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>>(graph: Graph<N, E>, node: NodeIdentifier<N, N1>, type: N2, find: (target: NoInfer<NodeOfType<N, N2>>, edge: NoInfer<Bimap<E>[N1][N2]>, source: NoInfer<NodeOfType<N, N1>>) => boolean): NodeOfType<N, N2> | undefined;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { findNeighbor };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getNode } from "./getNode.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/findNeighbor.ts
|
|
4
|
+
function findNeighbor(graph, node, type, find) {
|
|
5
|
+
const sourceNode = getNode(graph, node);
|
|
6
|
+
if (!sourceNode) return void 0;
|
|
7
|
+
const nodeMap = graph.get("nodes")?.get(type);
|
|
8
|
+
if (!nodeMap) return void 0;
|
|
9
|
+
for (const [targetId, edge] of graph.get("edges")?.get(sourceNode.type)?.get(sourceNode.id)?.get(type) ?? []) {
|
|
10
|
+
const targetNode = nodeMap.get(targetId);
|
|
11
|
+
if (find(targetNode, edge, sourceNode)) return targetNode;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { findNeighbor };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeOfType, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/findNeighbors.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* # findNeighbors
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* function Graph.findNeighbors(
|
|
10
|
+
* graph: Graph,
|
|
11
|
+
* node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
12
|
+
* type: NodeType,
|
|
13
|
+
* find: (target: Node, edge: EdgeData, source: Node) => boolean,
|
|
14
|
+
* ): Node[]
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Finds all neighbor nodes of a specific type that match the predicate. The predicate receives the target node, edge data, and source node.
|
|
18
|
+
*
|
|
19
|
+
* ## Example
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Graph } from "@monstermann/graph";
|
|
23
|
+
*
|
|
24
|
+
* type Nodes =
|
|
25
|
+
* | { type: "Task"; id: string; title: string }
|
|
26
|
+
* | { type: "Section"; id: string }
|
|
27
|
+
* | { type: "Project"; id: string };
|
|
28
|
+
*
|
|
29
|
+
* type Edges = {
|
|
30
|
+
* Project: { Task: { priority: number } };
|
|
31
|
+
* Section: { Task: void };
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
35
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
36
|
+
* g = Graph.setNode(g, { type: "Task", id: "1", title: "Low Priority Task" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "2", title: "Medium Priority Task" });
|
|
38
|
+
* g = Graph.setNode(g, { type: "Task", id: "3", title: "High Priority Task" });
|
|
39
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
|
|
40
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 2 });
|
|
41
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "3"], { priority: 3 });
|
|
42
|
+
*
|
|
43
|
+
* const mediumAndHighPriority = Graph.findNeighbors(
|
|
44
|
+
* g,
|
|
45
|
+
* ["Project", "1"],
|
|
46
|
+
* "Task",
|
|
47
|
+
* (task, edge, project) => edge.priority >= 2,
|
|
48
|
+
* );
|
|
49
|
+
* // mediumAndHighPriority: [
|
|
50
|
+
* // { type: "Task", id: "2", title: "Medium Priority Task" },
|
|
51
|
+
* // { type: "Task", id: "3", title: "High Priority Task" }
|
|
52
|
+
* // ]
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
declare function findNeighbors<N extends Node, E extends Edges<N>, N1 extends NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>, N3 extends NodeOfType<N, N2>>(graph: Graph<N, E>, node: NodeIdentifier<N, N1>, type: N2, find: (target: NoInfer<NodeOfType<N, N2>>, edge: NoInfer<Bimap<E>[N1][N2]>, source: NoInfer<NodeOfType<N, N1>>) => target is N3): N3[];
|
|
57
|
+
declare function findNeighbors<N extends Node, E extends Edges<N>, N1 extends NodeType<N>, N2 extends keyof Bimap<E>[N1] & NodeType<N>>(graph: Graph<N, E>, node: NodeIdentifier<N, N1>, type: N2, find: (target: NoInfer<NodeOfType<N, N2>>, edge: NoInfer<Bimap<E>[N1][N2]>, source: NoInfer<NodeOfType<N, N1>>) => boolean): NodeOfType<N, N2>[];
|
|
58
|
+
//#endregion
|
|
59
|
+
export { findNeighbors };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getNode } from "./getNode.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/findNeighbors.ts
|
|
4
|
+
function findNeighbors(graph, node, type, find) {
|
|
5
|
+
const result = [];
|
|
6
|
+
const sourceNode = getNode(graph, node);
|
|
7
|
+
if (!sourceNode) return result;
|
|
8
|
+
const nodeMap = graph.get("nodes")?.get(type);
|
|
9
|
+
if (!nodeMap) return result;
|
|
10
|
+
for (const [targetId, edge] of graph.get("edges")?.get(sourceNode.type)?.get(sourceNode.id)?.get(type) ?? []) {
|
|
11
|
+
const targetNode = nodeMap.get(targetId);
|
|
12
|
+
if (find(targetNode, edge, sourceNode)) result.push(targetNode);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { findNeighbors };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Edges, Graph, Node, NodeOfType, NodeType } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/findNode.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* # findNode
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* function Graph.findNode(
|
|
9
|
+
* graph: Graph,
|
|
10
|
+
* type: NodeType,
|
|
11
|
+
* find: (node: Node) => boolean,
|
|
12
|
+
* ): Node | undefined
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Finds the first node of a specific type that matches the predicate. Returns `undefined` if no matching node is found.
|
|
16
|
+
*
|
|
17
|
+
* ## Example
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Graph } from "@monstermann/graph";
|
|
21
|
+
*
|
|
22
|
+
* type Nodes =
|
|
23
|
+
* | { type: "Task"; id: string; title: string; completed: boolean }
|
|
24
|
+
* | { type: "Section"; id: string }
|
|
25
|
+
* | { type: "Project"; id: string };
|
|
26
|
+
*
|
|
27
|
+
* type Edges = {
|
|
28
|
+
* Project: { Task: void };
|
|
29
|
+
* Section: { Task: void };
|
|
30
|
+
* };
|
|
31
|
+
*
|
|
32
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
33
|
+
* let g = Graph.setNode(graph, {
|
|
34
|
+
* type: "Task",
|
|
35
|
+
* id: "1",
|
|
36
|
+
* title: "First Task",
|
|
37
|
+
* completed: false,
|
|
38
|
+
* });
|
|
39
|
+
* g = Graph.setNode(g, {
|
|
40
|
+
* type: "Task",
|
|
41
|
+
* id: "2",
|
|
42
|
+
* title: "Second Task",
|
|
43
|
+
* completed: true,
|
|
44
|
+
* });
|
|
45
|
+
* g = Graph.setNode(g, {
|
|
46
|
+
* type: "Task",
|
|
47
|
+
* id: "3",
|
|
48
|
+
* title: "Third Task",
|
|
49
|
+
* completed: false,
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* const completedTask = Graph.findNode(g, "Task", (task) => task.completed);
|
|
53
|
+
* // completedTask: { type: "Task", id: "2", title: "Second Task", completed: true }
|
|
54
|
+
*
|
|
55
|
+
* const taskWithTitle = Graph.findNode(
|
|
56
|
+
* g,
|
|
57
|
+
* "Task",
|
|
58
|
+
* (task) => task.title === "First Task",
|
|
59
|
+
* );
|
|
60
|
+
* // taskWithTitle: { type: "Task", id: "1", title: "First Task", completed: false }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
declare function findNode<N extends Node, E extends Edges<N>, U extends NodeType<N>, V extends NodeOfType<N, U>>(graph: Graph<N, E>, type: U, find: (node: NoInfer<NodeOfType<N, U>>) => node is V): V | undefined;
|
|
65
|
+
declare function findNode<N extends Node, E extends Edges<N>, U extends NodeType<N>>(graph: Graph<N, E>, type: U, find: (node: NoInfer<NodeOfType<N, U>>) => boolean): NodeOfType<N, U> | undefined;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { findNode };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Edges, Graph, Node, NodeOfType, NodeType } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/findNodes.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* # findNodes
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* function Graph.findNodes(
|
|
9
|
+
* graph: Graph,
|
|
10
|
+
* type: NodeType,
|
|
11
|
+
* find: (node: Node) => boolean,
|
|
12
|
+
* ): Node[]
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Finds all nodes of a specific type that match the predicate.
|
|
16
|
+
*
|
|
17
|
+
* ## Example
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Graph } from "@monstermann/graph";
|
|
21
|
+
*
|
|
22
|
+
* type Nodes =
|
|
23
|
+
* | { type: "Task"; id: string; title: string; completed: boolean }
|
|
24
|
+
* | { type: "Section"; id: string }
|
|
25
|
+
* | { type: "Project"; id: string };
|
|
26
|
+
*
|
|
27
|
+
* type Edges = {
|
|
28
|
+
* Project: { Task: void };
|
|
29
|
+
* Section: { Task: void };
|
|
30
|
+
* };
|
|
31
|
+
*
|
|
32
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
33
|
+
* let g = Graph.setNode(graph, {
|
|
34
|
+
* type: "Task",
|
|
35
|
+
* id: "1",
|
|
36
|
+
* title: "First Task",
|
|
37
|
+
* completed: false,
|
|
38
|
+
* });
|
|
39
|
+
* g = Graph.setNode(g, {
|
|
40
|
+
* type: "Task",
|
|
41
|
+
* id: "2",
|
|
42
|
+
* title: "Second Task",
|
|
43
|
+
* completed: true,
|
|
44
|
+
* });
|
|
45
|
+
* g = Graph.setNode(g, {
|
|
46
|
+
* type: "Task",
|
|
47
|
+
* id: "3",
|
|
48
|
+
* title: "Third Task",
|
|
49
|
+
* completed: false,
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* const incompleteTasks = Graph.findNodes(g, "Task", (task) => !task.completed);
|
|
53
|
+
* // incompleteTasks: [
|
|
54
|
+
* // { type: "Task", id: "1", title: "First Task", completed: false },
|
|
55
|
+
* // { type: "Task", id: "3", title: "Third Task", completed: false }
|
|
56
|
+
* // ]
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
*/
|
|
60
|
+
declare function findNodes<N extends Node, E extends Edges<N>, U extends NodeType<N>, V extends NodeOfType<N, U>>(graph: Graph<N, E>, type: U, find: (node: NoInfer<NodeOfType<N, U>>) => node is V): V[];
|
|
61
|
+
declare function findNodes<N extends Node, E extends Edges<N>, U extends NodeType<N>>(graph: Graph<N, E>, type: U, find: (node: NoInfer<NodeOfType<N, U>>) => boolean): NodeOfType<N, U>[];
|
|
62
|
+
//#endregion
|
|
63
|
+
export { findNodes };
|