@monstermann/graph 0.0.0 → 0.1.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 +48 -0
- package/dist/Graph/batch.mjs +57 -0
- package/dist/Graph/create.d.mts +37 -0
- package/dist/Graph/create.mjs +37 -0
- package/dist/Graph/findEdge.d.mts +61 -0
- package/dist/Graph/findEdge.mjs +10 -0
- package/dist/Graph/findEdges.d.mts +66 -0
- package/dist/Graph/findEdges.mjs +12 -0
- package/dist/Graph/findNeighbor.d.mts +55 -0
- package/dist/Graph/findNeighbor.mjs +16 -0
- package/dist/Graph/findNeighbors.d.mts +60 -0
- package/dist/Graph/findNeighbors.mjs +18 -0
- package/dist/Graph/findNode.d.mts +68 -0
- package/dist/Graph/findNode.mjs +7 -0
- package/dist/Graph/findNodes.d.mts +64 -0
- package/dist/Graph/findNodes.mjs +9 -0
- package/dist/Graph/forEachEdge.d.mts +53 -0
- package/dist/Graph/forEachEdge.mjs +56 -0
- package/dist/Graph/forEachNeighbor.d.mts +53 -0
- package/dist/Graph/forEachNeighbor.mjs +59 -0
- package/dist/Graph/forEachNode.d.mts +50 -0
- package/dist/Graph/forEachNode.mjs +51 -0
- package/dist/Graph/fromJS.d.mts +56 -0
- package/dist/Graph/fromJS.mjs +62 -0
- package/dist/Graph/getEdge.d.mts +51 -0
- package/dist/Graph/getEdge.mjs +54 -0
- package/dist/Graph/getEdges.d.mts +48 -0
- package/dist/Graph/getEdges.mjs +51 -0
- package/dist/Graph/getNeighbor.d.mts +51 -0
- package/dist/Graph/getNeighbor.mjs +56 -0
- package/dist/Graph/getNeighbors.d.mts +48 -0
- package/dist/Graph/getNeighbors.mjs +58 -0
- package/dist/Graph/getNode.d.mts +49 -0
- package/dist/Graph/getNode.mjs +52 -0
- package/dist/Graph/getNodes.d.mts +47 -0
- package/dist/Graph/getNodes.mjs +48 -0
- package/dist/Graph/hasEdge.d.mts +47 -0
- package/dist/Graph/hasEdge.mjs +50 -0
- package/dist/Graph/hasNode.d.mts +43 -0
- package/dist/Graph/hasNode.mjs +46 -0
- package/dist/Graph/index.d.mts +69 -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 +53 -0
- package/dist/Graph/mapEdge.mjs +60 -0
- package/dist/Graph/mapNode.d.mts +66 -0
- package/dist/Graph/mapNode.mjs +83 -0
- package/dist/Graph/mergeEdge.d.mts +53 -0
- package/dist/Graph/mergeEdge.mjs +63 -0
- package/dist/Graph/mergeNode.d.mts +51 -0
- package/dist/Graph/mergeNode.mjs +60 -0
- package/dist/Graph/removeEdge.d.mts +50 -0
- package/dist/Graph/removeEdge.mjs +71 -0
- package/dist/Graph/removeNode.d.mts +47 -0
- package/dist/Graph/removeNode.mjs +71 -0
- package/dist/Graph/setEdge.d.mts +53 -0
- package/dist/Graph/setEdge.mjs +78 -0
- package/dist/Graph/setNode.d.mts +44 -0
- package/dist/Graph/setNode.mjs +50 -0
- package/dist/Graph/toJS.d.mts +60 -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,65 @@
|
|
|
1
|
+
import { batch } from "./batch.mjs";
|
|
2
|
+
import { create } from "./create.mjs";
|
|
3
|
+
import { findEdge } from "./findEdge.mjs";
|
|
4
|
+
import { findEdges } from "./findEdges.mjs";
|
|
5
|
+
import { getNode } from "./getNode.mjs";
|
|
6
|
+
import { findNeighbor } from "./findNeighbor.mjs";
|
|
7
|
+
import { findNeighbors } from "./findNeighbors.mjs";
|
|
8
|
+
import { findNode } from "./findNode.mjs";
|
|
9
|
+
import { findNodes } from "./findNodes.mjs";
|
|
10
|
+
import { forEachEdge } from "./forEachEdge.mjs";
|
|
11
|
+
import { forEachNeighbor } from "./forEachNeighbor.mjs";
|
|
12
|
+
import { forEachNode } from "./forEachNode.mjs";
|
|
13
|
+
import { hasNode } from "./hasNode.mjs";
|
|
14
|
+
import { setEdge } from "./setEdge.mjs";
|
|
15
|
+
import { setNode } from "./setNode.mjs";
|
|
16
|
+
import { fromJS } from "./fromJS.mjs";
|
|
17
|
+
import { getEdge } from "./getEdge.mjs";
|
|
18
|
+
import { getEdges } from "./getEdges.mjs";
|
|
19
|
+
import { getNeighbor } from "./getNeighbor.mjs";
|
|
20
|
+
import { getNeighbors } from "./getNeighbors.mjs";
|
|
21
|
+
import { getNodes } from "./getNodes.mjs";
|
|
22
|
+
import { hasEdge } from "./hasEdge.mjs";
|
|
23
|
+
import { mapEdge } from "./mapEdge.mjs";
|
|
24
|
+
import { removeNode } from "./removeNode.mjs";
|
|
25
|
+
import { mapNode } from "./mapNode.mjs";
|
|
26
|
+
import { mergeEdge } from "./mergeEdge.mjs";
|
|
27
|
+
import { mergeNode } from "./mergeNode.mjs";
|
|
28
|
+
import { removeEdge } from "./removeEdge.mjs";
|
|
29
|
+
import { toJS } from "./toJS.mjs";
|
|
30
|
+
|
|
31
|
+
//#region src/Graph/index.js
|
|
32
|
+
const Graph = {
|
|
33
|
+
batch,
|
|
34
|
+
create,
|
|
35
|
+
findEdge,
|
|
36
|
+
findEdges,
|
|
37
|
+
findNeighbor,
|
|
38
|
+
findNeighbors,
|
|
39
|
+
findNode,
|
|
40
|
+
findNodes,
|
|
41
|
+
forEachEdge,
|
|
42
|
+
forEachNeighbor,
|
|
43
|
+
forEachNode,
|
|
44
|
+
fromJS,
|
|
45
|
+
getEdge,
|
|
46
|
+
getEdges,
|
|
47
|
+
getNeighbor,
|
|
48
|
+
getNeighbors,
|
|
49
|
+
getNode,
|
|
50
|
+
getNodes,
|
|
51
|
+
hasEdge,
|
|
52
|
+
hasNode,
|
|
53
|
+
mapEdge,
|
|
54
|
+
mapNode,
|
|
55
|
+
mergeEdge,
|
|
56
|
+
mergeNode,
|
|
57
|
+
removeEdge,
|
|
58
|
+
removeNode,
|
|
59
|
+
setEdge,
|
|
60
|
+
setNode,
|
|
61
|
+
toJS
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
export { Graph };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/Graph/internals/core.ts
|
|
2
|
+
function setInGraph(graph, path, value) {
|
|
3
|
+
return setInMap(graph.get("clones"), graph, path, value);
|
|
4
|
+
}
|
|
5
|
+
function unsetInGraph(graph, path) {
|
|
6
|
+
return unsetInMap(graph.get("clones"), graph, path);
|
|
7
|
+
}
|
|
8
|
+
const emptyMap = /* @__PURE__ */ new Map();
|
|
9
|
+
function setInMap(clones, map, path, value, offset = 0) {
|
|
10
|
+
if (path.length === 0) return map;
|
|
11
|
+
const key = path[offset];
|
|
12
|
+
if (offset === path.length - 1) return set(map, key, value, clones);
|
|
13
|
+
return set(map, key, setInMap(clones, map.get(key) ?? createMap(clones), path, value, offset + 1), clones);
|
|
14
|
+
}
|
|
15
|
+
function unsetInMap(clones, map, path, offset = 0) {
|
|
16
|
+
if (path.length === 0) return emptyMap;
|
|
17
|
+
const key = path[offset];
|
|
18
|
+
const isLast = offset === path.length - 1;
|
|
19
|
+
if (!map.has(key)) return map;
|
|
20
|
+
if (isLast) return unset(map, key, clones);
|
|
21
|
+
const child = unsetInMap(clones, map.get(key), path, offset + 1);
|
|
22
|
+
if (child.size === 0) return unset(map, key, clones);
|
|
23
|
+
return set(map, key, child, clones);
|
|
24
|
+
}
|
|
25
|
+
function createMap(clones) {
|
|
26
|
+
const map = /* @__PURE__ */ new Map();
|
|
27
|
+
clones?.add(map);
|
|
28
|
+
return map;
|
|
29
|
+
}
|
|
30
|
+
function set(map, key, value, clones) {
|
|
31
|
+
if (map.get(key) === value) return map;
|
|
32
|
+
const clone = cloneMap(map, clones);
|
|
33
|
+
clone.set(key, value);
|
|
34
|
+
return clone;
|
|
35
|
+
}
|
|
36
|
+
function unset(map, key, clones) {
|
|
37
|
+
if (map.size === 1) return emptyMap;
|
|
38
|
+
const clone = cloneMap(map, clones);
|
|
39
|
+
clone.delete(key);
|
|
40
|
+
return clone;
|
|
41
|
+
}
|
|
42
|
+
function cloneMap(target, clones) {
|
|
43
|
+
if (!clones) return new Map(target);
|
|
44
|
+
if (clones.has(target)) return target;
|
|
45
|
+
const clone = new Map(target);
|
|
46
|
+
clones.add(clone);
|
|
47
|
+
return clone;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { setInGraph, unsetInGraph };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Edge, Node, NodeId, NodeOfType, NodeType } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/internals/types.d.ts
|
|
4
|
+
type GetAllTargets<E> = E extends Record<string, infer V> ? V extends Record<string, any> ? keyof V : never : never;
|
|
5
|
+
type GetReverseMappings<E, Target> = { [K in keyof E as Target extends keyof E[K] ? K : never]: E[K][Target & keyof E[K]] };
|
|
6
|
+
type Bimap<E> = { [K in keyof E | GetAllTargets<E>]: (K extends keyof E ? E[K] : {}) & GetReverseMappings<E, K> };
|
|
7
|
+
type SourceTypesMap = ReadonlyMap<any, SourceIdsMap>;
|
|
8
|
+
type SourceIdsMap = ReadonlyMap<NodeId, TargetTypesMap>;
|
|
9
|
+
type TargetTypesMap = ReadonlyMap<any, TargetIdsMap>;
|
|
10
|
+
type TargetIdsMap = ReadonlyMap<NodeId, Edge>;
|
|
11
|
+
type NodeIdsMap<T extends Node, U extends NodeType<T> = NodeType<T>> = ReadonlyMap<NodeId, NodeOfType<T, U>>;
|
|
12
|
+
interface NodeTypesMap<T extends Node> extends ReadonlyMap<NodeType<T>, NodeIdsMap<T, NodeType<T>>> {
|
|
13
|
+
get: <V$1 extends NodeType<T>>(type: V$1) => NodeIdsMap<T, V$1> | undefined;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { Bimap, NodeTypesMap, SourceTypesMap };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/mapEdge.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* # mapEdge
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* function Graph.mapEdge(
|
|
11
|
+
* graph: Graph,
|
|
12
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
13
|
+
* target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
14
|
+
* update: (edge: EdgeData) => EdgeData,
|
|
15
|
+
* ): Graph
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Updates an edge by applying a transformation function. Returns a new graph instance with the updated edge.
|
|
19
|
+
*
|
|
20
|
+
* ## Example
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { Graph } from "@monstermann/graph";
|
|
24
|
+
*
|
|
25
|
+
* type Nodes =
|
|
26
|
+
* | { type: "Task"; id: string }
|
|
27
|
+
* | { type: "Section"; id: string }
|
|
28
|
+
* | { type: "Project"; id: string };
|
|
29
|
+
*
|
|
30
|
+
* type Edges = {
|
|
31
|
+
* Project: { Task: { priority: number; assignedAt: Date } };
|
|
32
|
+
* Section: { Task: void };
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
36
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "1" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
|
|
39
|
+
* priority: 1,
|
|
40
|
+
* assignedAt: new Date(),
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Update edge data
|
|
44
|
+
* g = Graph.mapEdge(g, ["Project", "1"], ["Task", "1"], (edge) => ({
|
|
45
|
+
* ...edge,
|
|
46
|
+
* priority: 2,
|
|
47
|
+
* }));
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
declare function mapEdge<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>, target: NodeIdentifier<N, N2>, update: (edge: NoInfer<Bimap<E>[N1][N2]>) => NoInfer<Bimap<E>[N1][N2]>): Graph<N, E>;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { mapEdge };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { setEdge } from "./setEdge.mjs";
|
|
2
|
+
import { getEdge } from "./getEdge.mjs";
|
|
3
|
+
import { hasEdge } from "./hasEdge.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/Graph/mapEdge.ts
|
|
6
|
+
/**
|
|
7
|
+
* # mapEdge
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* function Graph.mapEdge(
|
|
11
|
+
* graph: Graph,
|
|
12
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
13
|
+
* target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
14
|
+
* update: (edge: EdgeData) => EdgeData,
|
|
15
|
+
* ): Graph
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Updates an edge by applying a transformation function. Returns a new graph instance with the updated edge.
|
|
19
|
+
*
|
|
20
|
+
* ## Example
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { Graph } from "@monstermann/graph";
|
|
24
|
+
*
|
|
25
|
+
* type Nodes =
|
|
26
|
+
* | { type: "Task"; id: string }
|
|
27
|
+
* | { type: "Section"; id: string }
|
|
28
|
+
* | { type: "Project"; id: string };
|
|
29
|
+
*
|
|
30
|
+
* type Edges = {
|
|
31
|
+
* Project: { Task: { priority: number; assignedAt: Date } };
|
|
32
|
+
* Section: { Task: void };
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
36
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "1" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
|
|
39
|
+
* priority: 1,
|
|
40
|
+
* assignedAt: new Date(),
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Update edge data
|
|
44
|
+
* g = Graph.mapEdge(g, ["Project", "1"], ["Task", "1"], (edge) => ({
|
|
45
|
+
* ...edge,
|
|
46
|
+
* priority: 2,
|
|
47
|
+
* }));
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
function mapEdge(graph, source, target, update) {
|
|
52
|
+
if (!hasEdge(graph, source, target)) return graph;
|
|
53
|
+
const before = getEdge(graph, source, target);
|
|
54
|
+
const after = update(before);
|
|
55
|
+
if (before === after) return graph;
|
|
56
|
+
return setEdge(graph, source, target, after);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
export { mapEdge };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeOfType, NodeType } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/mapNode.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* # mapNode
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* function Graph.mapNode(
|
|
10
|
+
* graph: Graph,
|
|
11
|
+
* node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
12
|
+
* update: (node: Node) => Node,
|
|
13
|
+
* ): Graph
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Updates a node by applying a transformation function. If the node's type or id changes, all edges are preserved. Returns a new graph instance with the updated node.
|
|
17
|
+
*
|
|
18
|
+
* ## Example
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { Graph } from "@monstermann/graph";
|
|
22
|
+
*
|
|
23
|
+
* type Nodes =
|
|
24
|
+
* | { type: "Task"; id: string; title: string; completed: boolean }
|
|
25
|
+
* | { type: "Section"; id: string }
|
|
26
|
+
* | { type: "Project"; id: string };
|
|
27
|
+
*
|
|
28
|
+
* type Edges = {
|
|
29
|
+
* Project: { Task: void };
|
|
30
|
+
* Section: { Task: void };
|
|
31
|
+
* };
|
|
32
|
+
*
|
|
33
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
34
|
+
* let g = Graph.setNode(graph, {
|
|
35
|
+
* type: "Task",
|
|
36
|
+
* id: "1",
|
|
37
|
+
* title: "My Task",
|
|
38
|
+
* completed: false,
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Update a property
|
|
42
|
+
* g = Graph.mapNode(g, ["Task", "1"], (task) => ({
|
|
43
|
+
* ...task,
|
|
44
|
+
* completed: true,
|
|
45
|
+
* }));
|
|
46
|
+
*
|
|
47
|
+
* // Change id (edges are preserved)
|
|
48
|
+
* let g2 = Graph.setNode(graph, {
|
|
49
|
+
* type: "Task",
|
|
50
|
+
* id: "1",
|
|
51
|
+
* title: "Task",
|
|
52
|
+
* completed: false,
|
|
53
|
+
* });
|
|
54
|
+
* g2 = Graph.setNode(g2, { type: "Project", id: "1" });
|
|
55
|
+
* g2 = Graph.setEdge(g2, ["Project", "1"], ["Task", "1"]);
|
|
56
|
+
* g2 = Graph.mapNode(g2, ["Task", "1"], (task) => ({
|
|
57
|
+
* ...task,
|
|
58
|
+
* id: "2",
|
|
59
|
+
* }));
|
|
60
|
+
* // Edge now connects ["Project", "1"] -> ["Task", "2"]
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
declare function mapNode<N extends Node, E extends Edges<N>, U extends NodeType<N>>(graph: Graph<N, E>, node: NodeIdentifier<N, U>, update: (node: NoInfer<NodeOfType<N, U>>) => NoInfer<NodeOfType<N, U>>): Graph<N, E>;
|
|
65
|
+
//#endregion
|
|
66
|
+
export { mapNode };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { batch } from "./batch.mjs";
|
|
2
|
+
import { getNode } from "./getNode.mjs";
|
|
3
|
+
import { setEdge } from "./setEdge.mjs";
|
|
4
|
+
import { setNode } from "./setNode.mjs";
|
|
5
|
+
import { removeNode } from "./removeNode.mjs";
|
|
6
|
+
|
|
7
|
+
//#region src/Graph/mapNode.ts
|
|
8
|
+
/**
|
|
9
|
+
* # mapNode
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* function Graph.mapNode(
|
|
13
|
+
* graph: Graph,
|
|
14
|
+
* node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
15
|
+
* update: (node: Node) => Node,
|
|
16
|
+
* ): Graph
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Updates a node by applying a transformation function. If the node's type or id changes, all edges are preserved. Returns a new graph instance with the updated node.
|
|
20
|
+
*
|
|
21
|
+
* ## Example
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Graph } from "@monstermann/graph";
|
|
25
|
+
*
|
|
26
|
+
* type Nodes =
|
|
27
|
+
* | { type: "Task"; id: string; title: string; completed: boolean }
|
|
28
|
+
* | { type: "Section"; id: string }
|
|
29
|
+
* | { type: "Project"; id: string };
|
|
30
|
+
*
|
|
31
|
+
* type Edges = {
|
|
32
|
+
* Project: { Task: void };
|
|
33
|
+
* Section: { Task: void };
|
|
34
|
+
* };
|
|
35
|
+
*
|
|
36
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
37
|
+
* let g = Graph.setNode(graph, {
|
|
38
|
+
* type: "Task",
|
|
39
|
+
* id: "1",
|
|
40
|
+
* title: "My Task",
|
|
41
|
+
* completed: false,
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* // Update a property
|
|
45
|
+
* g = Graph.mapNode(g, ["Task", "1"], (task) => ({
|
|
46
|
+
* ...task,
|
|
47
|
+
* completed: true,
|
|
48
|
+
* }));
|
|
49
|
+
*
|
|
50
|
+
* // Change id (edges are preserved)
|
|
51
|
+
* let g2 = Graph.setNode(graph, {
|
|
52
|
+
* type: "Task",
|
|
53
|
+
* id: "1",
|
|
54
|
+
* title: "Task",
|
|
55
|
+
* completed: false,
|
|
56
|
+
* });
|
|
57
|
+
* g2 = Graph.setNode(g2, { type: "Project", id: "1" });
|
|
58
|
+
* g2 = Graph.setEdge(g2, ["Project", "1"], ["Task", "1"]);
|
|
59
|
+
* g2 = Graph.mapNode(g2, ["Task", "1"], (task) => ({
|
|
60
|
+
* ...task,
|
|
61
|
+
* id: "2",
|
|
62
|
+
* }));
|
|
63
|
+
* // Edge now connects ["Project", "1"] -> ["Task", "2"]
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
function mapNode(graph, node, update) {
|
|
68
|
+
const prev = getNode(graph, node);
|
|
69
|
+
if (!prev) return graph;
|
|
70
|
+
const next = update(prev);
|
|
71
|
+
if (prev === next) return graph;
|
|
72
|
+
if (prev.id === next.id && prev.type === next.type) return setNode(graph, next);
|
|
73
|
+
return batch(graph, (graph$1) => {
|
|
74
|
+
graph$1 = setNode(graph$1, next);
|
|
75
|
+
const edges = graph$1.get("edges");
|
|
76
|
+
for (const [targetType, targetIds] of edges?.get(prev.type)?.get(prev.id) ?? []) for (const [targetId, targetData] of targetIds) graph$1 = setEdge(graph$1, [next.type, next.id], [targetType, targetId], targetData);
|
|
77
|
+
graph$1 = removeNode(graph$1, prev);
|
|
78
|
+
return graph$1;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
export { mapNode };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/mergeEdge.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* # mergeEdge
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* function Graph.mergeEdge(
|
|
11
|
+
* graph: Graph,
|
|
12
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
13
|
+
* target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
14
|
+
* update: Partial<EdgeData>,
|
|
15
|
+
* ): Graph
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Partially updates an edge by merging the provided properties. Returns a new graph instance with the updated edge.
|
|
19
|
+
*
|
|
20
|
+
* ## Example
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { Graph } from "@monstermann/graph";
|
|
24
|
+
*
|
|
25
|
+
* type Nodes =
|
|
26
|
+
* | { type: "Task"; id: string }
|
|
27
|
+
* | { type: "Section"; id: string }
|
|
28
|
+
* | { type: "Project"; id: string };
|
|
29
|
+
*
|
|
30
|
+
* type Edges = {
|
|
31
|
+
* Project: { Task: { priority: number; assignedAt: Date } };
|
|
32
|
+
* Section: { Task: void };
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
36
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "1" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
|
|
39
|
+
* priority: 1,
|
|
40
|
+
* assignedAt: new Date(),
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Merge partial update
|
|
44
|
+
* g = Graph.mergeEdge(g, ["Project", "1"], ["Task", "1"], { priority: 2 });
|
|
45
|
+
*
|
|
46
|
+
* const edge = Graph.getEdge(g, ["Project", "1"], ["Task", "1"]);
|
|
47
|
+
* // edge: { priority: 2, assignedAt: <original date> }
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
declare function mergeEdge<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>, target: NodeIdentifier<N, N2>, update: Partial<Bimap<E>[N1][N2]>): Graph<N, E>;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { mergeEdge };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { setEdge } from "./setEdge.mjs";
|
|
2
|
+
import { getEdge } from "./getEdge.mjs";
|
|
3
|
+
import { hasEdge } from "./hasEdge.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/Graph/mergeEdge.ts
|
|
6
|
+
/**
|
|
7
|
+
* # mergeEdge
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* function Graph.mergeEdge(
|
|
11
|
+
* graph: Graph,
|
|
12
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
13
|
+
* target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
14
|
+
* update: Partial<EdgeData>,
|
|
15
|
+
* ): Graph
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Partially updates an edge by merging the provided properties. Returns a new graph instance with the updated edge.
|
|
19
|
+
*
|
|
20
|
+
* ## Example
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { Graph } from "@monstermann/graph";
|
|
24
|
+
*
|
|
25
|
+
* type Nodes =
|
|
26
|
+
* | { type: "Task"; id: string }
|
|
27
|
+
* | { type: "Section"; id: string }
|
|
28
|
+
* | { type: "Project"; id: string };
|
|
29
|
+
*
|
|
30
|
+
* type Edges = {
|
|
31
|
+
* Project: { Task: { priority: number; assignedAt: Date } };
|
|
32
|
+
* Section: { Task: void };
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
36
|
+
* let g = Graph.setNode(graph, { type: "Project", id: "1" });
|
|
37
|
+
* g = Graph.setNode(g, { type: "Task", id: "1" });
|
|
38
|
+
* g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
|
|
39
|
+
* priority: 1,
|
|
40
|
+
* assignedAt: new Date(),
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Merge partial update
|
|
44
|
+
* g = Graph.mergeEdge(g, ["Project", "1"], ["Task", "1"], { priority: 2 });
|
|
45
|
+
*
|
|
46
|
+
* const edge = Graph.getEdge(g, ["Project", "1"], ["Task", "1"]);
|
|
47
|
+
* // edge: { priority: 2, assignedAt: <original date> }
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
function mergeEdge(graph, source, target, update) {
|
|
52
|
+
if (!hasEdge(graph, source, target)) return graph;
|
|
53
|
+
const edge = getEdge(graph, source, target);
|
|
54
|
+
const keys = Object.keys(update);
|
|
55
|
+
if (keys.length === 0) return graph;
|
|
56
|
+
return keys.some((k) => update[k] !== edge[k]) ? setEdge(graph, source, target, {
|
|
57
|
+
...edge,
|
|
58
|
+
...update
|
|
59
|
+
}) : graph;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
export { mergeEdge };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeOfType, NodeType } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/mergeNode.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* # mergeNode
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* function Graph.mergeNode(
|
|
10
|
+
* graph: Graph,
|
|
11
|
+
* node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
12
|
+
* update: Partial<Node>,
|
|
13
|
+
* ): Graph
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Partially updates a node by merging the provided properties. Returns a new graph instance with the updated node.
|
|
17
|
+
*
|
|
18
|
+
* ## Example
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { Graph } from "@monstermann/graph";
|
|
22
|
+
*
|
|
23
|
+
* type Nodes =
|
|
24
|
+
* | { type: "Task"; id: string; title: string; completed: boolean }
|
|
25
|
+
* | { type: "Section"; id: string }
|
|
26
|
+
* | { type: "Project"; id: string };
|
|
27
|
+
*
|
|
28
|
+
* type Edges = {
|
|
29
|
+
* Project: { Task: void };
|
|
30
|
+
* Section: { Task: void };
|
|
31
|
+
* };
|
|
32
|
+
*
|
|
33
|
+
* const graph = Graph.create<Nodes, Edges>();
|
|
34
|
+
* let g = Graph.setNode(graph, {
|
|
35
|
+
* type: "Task",
|
|
36
|
+
* id: "1",
|
|
37
|
+
* title: "My Task",
|
|
38
|
+
* completed: false,
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Merge partial update
|
|
42
|
+
* g = Graph.mergeNode(g, ["Task", "1"], { completed: true });
|
|
43
|
+
*
|
|
44
|
+
* const task = Graph.getNode(g, ["Task", "1"]);
|
|
45
|
+
* // task: { type: "Task", id: "1", title: "My Task", completed: true }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
declare function mergeNode<N extends Node, E extends Edges<N>, U extends NodeType<N>>(graph: Graph<N, E>, node: NodeIdentifier<N, U>, update: Partial<NodeOfType<N, U>>): Graph<N, E>;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { mergeNode };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mapNode } from "./mapNode.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/Graph/mergeNode.ts
|
|
4
|
+
/**
|
|
5
|
+
* # mergeNode
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* function Graph.mergeNode(
|
|
9
|
+
* graph: Graph,
|
|
10
|
+
* node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
11
|
+
* update: Partial<Node>,
|
|
12
|
+
* ): Graph
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Partially updates a node by merging the provided properties. Returns a new graph instance with the updated node.
|
|
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: "My Task",
|
|
37
|
+
* completed: false,
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // Merge partial update
|
|
41
|
+
* g = Graph.mergeNode(g, ["Task", "1"], { completed: true });
|
|
42
|
+
*
|
|
43
|
+
* const task = Graph.getNode(g, ["Task", "1"]);
|
|
44
|
+
* // task: { type: "Task", id: "1", title: "My Task", completed: true }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
function mergeNode(graph, node, update) {
|
|
49
|
+
return mapNode(graph, node, (node$1) => {
|
|
50
|
+
const keys = Object.keys(update);
|
|
51
|
+
if (keys.length === 0) return node$1;
|
|
52
|
+
return keys.some((k) => update[k] !== node$1[k]) ? {
|
|
53
|
+
...node$1,
|
|
54
|
+
...update
|
|
55
|
+
} : node$1;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
export { mergeNode };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Bimap } from "./internals/types.mjs";
|
|
2
|
+
import { Edges, Graph, Node, NodeIdentifier, NodeType } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/Graph/removeEdge.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* # removeEdge
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* function Graph.removeEdge(
|
|
11
|
+
* graph: Graph,
|
|
12
|
+
* source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
13
|
+
* target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
|
|
14
|
+
* ): Graph
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Removes an edge between two nodes. The nodes remain in the graph. Returns a new graph instance without the edge.
|
|
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: void };
|
|
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.setEdge(g, ["Project", "1"], ["Task", "1"]);
|
|
38
|
+
*
|
|
39
|
+
* // Remove the edge
|
|
40
|
+
* g = Graph.removeEdge(g, ["Project", "1"], ["Task", "1"]);
|
|
41
|
+
*
|
|
42
|
+
* Graph.hasNode(g, ["Project", "1"]); // true
|
|
43
|
+
* Graph.hasNode(g, ["Task", "1"]); // true
|
|
44
|
+
* Graph.hasEdge(g, ["Project", "1"], ["Task", "1"]); // false
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
*/
|
|
48
|
+
declare function removeEdge<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>, target: NodeIdentifier<N, N2>): Graph<N, E>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { removeEdge };
|