@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.
Files changed (68) hide show
  1. package/README.md +1383 -0
  2. package/dist/Graph/batch.d.mts +48 -0
  3. package/dist/Graph/batch.mjs +57 -0
  4. package/dist/Graph/create.d.mts +37 -0
  5. package/dist/Graph/create.mjs +37 -0
  6. package/dist/Graph/findEdge.d.mts +61 -0
  7. package/dist/Graph/findEdge.mjs +10 -0
  8. package/dist/Graph/findEdges.d.mts +66 -0
  9. package/dist/Graph/findEdges.mjs +12 -0
  10. package/dist/Graph/findNeighbor.d.mts +55 -0
  11. package/dist/Graph/findNeighbor.mjs +16 -0
  12. package/dist/Graph/findNeighbors.d.mts +60 -0
  13. package/dist/Graph/findNeighbors.mjs +18 -0
  14. package/dist/Graph/findNode.d.mts +68 -0
  15. package/dist/Graph/findNode.mjs +7 -0
  16. package/dist/Graph/findNodes.d.mts +64 -0
  17. package/dist/Graph/findNodes.mjs +9 -0
  18. package/dist/Graph/forEachEdge.d.mts +53 -0
  19. package/dist/Graph/forEachEdge.mjs +56 -0
  20. package/dist/Graph/forEachNeighbor.d.mts +53 -0
  21. package/dist/Graph/forEachNeighbor.mjs +59 -0
  22. package/dist/Graph/forEachNode.d.mts +50 -0
  23. package/dist/Graph/forEachNode.mjs +51 -0
  24. package/dist/Graph/fromJS.d.mts +56 -0
  25. package/dist/Graph/fromJS.mjs +62 -0
  26. package/dist/Graph/getEdge.d.mts +51 -0
  27. package/dist/Graph/getEdge.mjs +54 -0
  28. package/dist/Graph/getEdges.d.mts +48 -0
  29. package/dist/Graph/getEdges.mjs +51 -0
  30. package/dist/Graph/getNeighbor.d.mts +51 -0
  31. package/dist/Graph/getNeighbor.mjs +56 -0
  32. package/dist/Graph/getNeighbors.d.mts +48 -0
  33. package/dist/Graph/getNeighbors.mjs +58 -0
  34. package/dist/Graph/getNode.d.mts +49 -0
  35. package/dist/Graph/getNode.mjs +52 -0
  36. package/dist/Graph/getNodes.d.mts +47 -0
  37. package/dist/Graph/getNodes.mjs +48 -0
  38. package/dist/Graph/hasEdge.d.mts +47 -0
  39. package/dist/Graph/hasEdge.mjs +50 -0
  40. package/dist/Graph/hasNode.d.mts +43 -0
  41. package/dist/Graph/hasNode.mjs +46 -0
  42. package/dist/Graph/index.d.mts +69 -0
  43. package/dist/Graph/index.mjs +65 -0
  44. package/dist/Graph/internals/core.mjs +51 -0
  45. package/dist/Graph/internals/parseNodeIdentifier.mjs +7 -0
  46. package/dist/Graph/internals/types.d.mts +16 -0
  47. package/dist/Graph/mapEdge.d.mts +53 -0
  48. package/dist/Graph/mapEdge.mjs +60 -0
  49. package/dist/Graph/mapNode.d.mts +66 -0
  50. package/dist/Graph/mapNode.mjs +83 -0
  51. package/dist/Graph/mergeEdge.d.mts +53 -0
  52. package/dist/Graph/mergeEdge.mjs +63 -0
  53. package/dist/Graph/mergeNode.d.mts +51 -0
  54. package/dist/Graph/mergeNode.mjs +60 -0
  55. package/dist/Graph/removeEdge.d.mts +50 -0
  56. package/dist/Graph/removeEdge.mjs +71 -0
  57. package/dist/Graph/removeNode.d.mts +47 -0
  58. package/dist/Graph/removeNode.mjs +71 -0
  59. package/dist/Graph/setEdge.d.mts +53 -0
  60. package/dist/Graph/setEdge.mjs +78 -0
  61. package/dist/Graph/setNode.d.mts +44 -0
  62. package/dist/Graph/setNode.mjs +50 -0
  63. package/dist/Graph/toJS.d.mts +60 -0
  64. package/dist/Graph/toJS.mjs +82 -0
  65. package/dist/Graph/types.d.mts +29 -0
  66. package/dist/index.d.mts +2 -0
  67. package/dist/index.mjs +3 -0
  68. package/package.json +1 -1
package/README.md CHANGED
@@ -2,8 +2,1391 @@
2
2
 
3
3
  <h1>graph</h1>
4
4
 
5
+ ![Minified](https://img.shields.io/badge/Minified-5.21_KB-blue?style=flat-square&labelColor=%2315161D&color=%2369a1ff) ![Minzipped](https://img.shields.io/badge/Minzipped-1.54_KB-blue?style=flat-square&labelColor=%2315161D&color=%2369a1ff)
6
+
5
7
  **Functional graph data-structure.**
6
8
 
7
9
  [Documentation](https://MichaelOstermann.github.io/graph)
8
10
 
9
11
  </div>
12
+
13
+ ## Example
14
+
15
+ ```ts
16
+ import { Graph } from "@monstermann/graph";
17
+
18
+ // Define your node and edge types
19
+ type Nodes =
20
+ | { type: "User"; id: string; name: string }
21
+ | { type: "Post"; id: string; title: string }
22
+ | { type: "Comment"; id: string; text: string };
23
+
24
+ type Edges = {
25
+ User: { Post: { role: "author" | "editor" }; Comment: void };
26
+ Post: { Comment: void };
27
+ };
28
+
29
+ // Create a graph
30
+ const graph = Graph.create<Nodes, Edges>();
31
+
32
+ // Add nodes
33
+ let g = Graph.setNode(graph, { type: "User", id: "1", name: "Alice" });
34
+ g = Graph.setNode(g, { type: "Post", id: "1", title: "Hello World" });
35
+ g = Graph.setNode(g, { type: "Comment", id: "1", text: "Great post!" });
36
+
37
+ // Add edges with data
38
+ g = Graph.setEdge(g, ["User", "1"], ["Post", "1"], { role: "author" });
39
+ g = Graph.setEdge(g, ["User", "1"], ["Comment", "1"]);
40
+ g = Graph.setEdge(g, ["Post", "1"], ["Comment", "1"]);
41
+
42
+ // Query the graph
43
+ const user = Graph.getNode(g, ["User", "1"]);
44
+ // user: { type: "User", id: "1", name: "Alice" }
45
+
46
+ const userPosts = Graph.getNeighbors(g, ["User", "1"], "Post");
47
+ // userPosts: [{ type: "Post", id: "1", title: "Hello World" }]
48
+
49
+ const postEdge = Graph.getEdge(g, ["User", "1"], ["Post", "1"]);
50
+ // postEdge: { role: "author" }
51
+ ```
52
+
53
+ ## Installation
54
+
55
+ ```sh [npm]
56
+ npm install @monstermann/graph
57
+ ```
58
+
59
+ ```sh [pnpm]
60
+ pnpm add @monstermann/graph
61
+ ```
62
+
63
+ ```sh [yarn]
64
+ yarn add @monstermann/graph
65
+ ```
66
+
67
+ ```sh [bun]
68
+ bun add @monstermann/graph
69
+ ```
70
+
71
+ ## Tree-shaking
72
+
73
+ ### Installation
74
+
75
+ ```sh [npm]
76
+ npm install -D @monstermann/unplugin-graph
77
+ ```
78
+
79
+ ```sh [pnpm]
80
+ pnpm -D add @monstermann/unplugin-graph
81
+ ```
82
+
83
+ ```sh [yarn]
84
+ yarn -D add @monstermann/unplugin-graph
85
+ ```
86
+
87
+ ```sh [bun]
88
+ bun -D add @monstermann/unplugin-graph
89
+ ```
90
+
91
+ ### Usage
92
+
93
+ ```ts [Vite]
94
+ // vite.config.ts
95
+ import graph from "@monstermann/unplugin-graph/vite";
96
+
97
+ export default defineConfig({
98
+ plugins: [graph()],
99
+ });
100
+ ```
101
+
102
+ ```ts [Rollup]
103
+ // rollup.config.js
104
+ import graph from "@monstermann/unplugin-graph/rollup";
105
+
106
+ export default {
107
+ plugins: [graph()],
108
+ };
109
+ ```
110
+
111
+ ```ts [Rolldown]
112
+ // rolldown.config.js
113
+ import graph from "@monstermann/unplugin-graph/rolldown";
114
+
115
+ export default {
116
+ plugins: [graph()],
117
+ };
118
+ ```
119
+
120
+ ```ts [Webpack]
121
+ // webpack.config.js
122
+ const graph = require("@monstermann/unplugin-graph/webpack");
123
+
124
+ module.exports = {
125
+ plugins: [graph()],
126
+ };
127
+ ```
128
+
129
+ ```ts [Rspack]
130
+ // rspack.config.js
131
+ const graph = require("@monstermann/unplugin-graph/rspack");
132
+
133
+ module.exports = {
134
+ plugins: [graph()],
135
+ };
136
+ ```
137
+
138
+ ```ts [ESBuild]
139
+ // esbuild.config.js
140
+ import { build } from "esbuild";
141
+ import graph from "@monstermann/unplugin-graph/esbuild";
142
+
143
+ build({
144
+ plugins: [graph()],
145
+ });
146
+ ```
147
+
148
+ ## Graph
149
+
150
+ ### batch
151
+
152
+ ```ts
153
+ function Graph.batch(
154
+ graph: Graph,
155
+ transform: (graph: Graph) => Graph | void,
156
+ ): Graph
157
+ ```
158
+
159
+ Performs multiple graph mutations efficiently by batching them together. Returns the original graph if no changes were made, optimizing for structural sharing.
160
+
161
+ #### Example
162
+
163
+ ```ts
164
+ import { Graph } from "@monstermann/graph";
165
+
166
+ type Nodes =
167
+ | { type: "Task"; id: string; title: string }
168
+ | { type: "Section"; id: string; name: string }
169
+ | { type: "Project"; id: string; name: string };
170
+
171
+ type Edges = {
172
+ Project: { Task: void };
173
+ Section: { Task: void };
174
+ };
175
+
176
+ const graph = Graph.create<Nodes, Edges>();
177
+
178
+ // Add multiple nodes and edges in a single batch
179
+ const updated = Graph.batch(graph, (g) => {
180
+ g = Graph.setNode(g, { type: "Project", id: "1", name: "My Project" });
181
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "Task 1" });
182
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Task 2" });
183
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
184
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"]);
185
+ return g;
186
+ });
187
+ ```
188
+
189
+ ### create
190
+
191
+ ```ts
192
+ function Graph.create<Nodes, Edges>(): Graph<Nodes, Edges>
193
+ ```
194
+
195
+ Creates a new empty graph with the provided nodes & edges configuration.
196
+
197
+ #### Example
198
+
199
+ ```ts
200
+ import { Graph } from "@monstermann/graph";
201
+
202
+ type Nodes =
203
+ | { type: "Task"; id: string }
204
+ | { type: "Section"; id: string }
205
+ | { type: "Project"; id: string };
206
+
207
+ type Edges = {
208
+ // Project <-> Task
209
+ Project: { Task: void };
210
+ // Section <-> Task
211
+ Section: { Task: void };
212
+ };
213
+
214
+ const graph = Graph.create<Nodes, Edges>();
215
+ ```
216
+
217
+ ### findEdge
218
+
219
+ ```ts
220
+ function Graph.findEdge(
221
+ graph: Graph,
222
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
223
+ type: NodeType,
224
+ find: (edge: EdgeData) => boolean,
225
+ ): EdgeData | undefined
226
+ ```
227
+
228
+ Finds the first edge of a specific type from a source node that matches the predicate. Returns `undefined` if no matching edge is found.
229
+
230
+ #### Example
231
+
232
+ ```ts
233
+ import { Graph } from "@monstermann/graph";
234
+
235
+ type Nodes =
236
+ | { type: "Task"; id: string }
237
+ | { type: "Section"; id: string }
238
+ | { type: "Project"; id: string };
239
+
240
+ type Edges = {
241
+ Project: { Task: { priority: number; assignedAt: Date } };
242
+ Section: { Task: void };
243
+ };
244
+
245
+ const graph = Graph.create<Nodes, Edges>();
246
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
247
+ g = Graph.setNode(g, { type: "Task", id: "1" });
248
+ g = Graph.setNode(g, { type: "Task", id: "2" });
249
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
250
+ priority: 1,
251
+ assignedAt: new Date(),
252
+ });
253
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], {
254
+ priority: 2,
255
+ assignedAt: new Date(),
256
+ });
257
+
258
+ const highPriorityEdge = Graph.findEdge(
259
+ g,
260
+ ["Project", "1"],
261
+ "Task",
262
+ (edge) => edge.priority > 1,
263
+ );
264
+ // highPriorityEdge: { priority: 2, assignedAt: Date }
265
+ ```
266
+
267
+ ### findEdges
268
+
269
+ ```ts
270
+ function Graph.findEdges(
271
+ graph: Graph,
272
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
273
+ type: NodeType,
274
+ find: (edge: EdgeData) => boolean,
275
+ ): EdgeData[]
276
+ ```
277
+
278
+ Finds all edges of a specific type from a source node that match the predicate.
279
+
280
+ #### Example
281
+
282
+ ```ts
283
+ import { Graph } from "@monstermann/graph";
284
+
285
+ type Nodes =
286
+ | { type: "Task"; id: string }
287
+ | { type: "Section"; id: string }
288
+ | { type: "Project"; id: string };
289
+
290
+ type Edges = {
291
+ Project: { Task: { priority: number; assignedAt: Date } };
292
+ Section: { Task: void };
293
+ };
294
+
295
+ const graph = Graph.create<Nodes, Edges>();
296
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
297
+ g = Graph.setNode(g, { type: "Task", id: "1" });
298
+ g = Graph.setNode(g, { type: "Task", id: "2" });
299
+ g = Graph.setNode(g, { type: "Task", id: "3" });
300
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
301
+ priority: 1,
302
+ assignedAt: new Date(),
303
+ });
304
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], {
305
+ priority: 2,
306
+ assignedAt: new Date(),
307
+ });
308
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "3"], {
309
+ priority: 3,
310
+ assignedAt: new Date(),
311
+ });
312
+
313
+ const highPriorityEdges = Graph.findEdges(
314
+ g,
315
+ ["Project", "1"],
316
+ "Task",
317
+ (edge) => edge.priority >= 2,
318
+ );
319
+ // highPriorityEdges: [{ priority: 2, ... }, { priority: 3, ... }]
320
+ ```
321
+
322
+ ### findNeighbor
323
+
324
+ ```ts
325
+ function Graph.findNeighbor(
326
+ graph: Graph,
327
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
328
+ type: NodeType,
329
+ find: (target: Node, edge: EdgeData, source: Node) => boolean,
330
+ ): Node | undefined
331
+ ```
332
+
333
+ 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.
334
+
335
+ #### Example
336
+
337
+ ```ts
338
+ import { Graph } from "@monstermann/graph";
339
+
340
+ type Nodes =
341
+ | { type: "Task"; id: string; title: string }
342
+ | { type: "Section"; id: string }
343
+ | { type: "Project"; id: string };
344
+
345
+ type Edges = {
346
+ Project: { Task: { priority: number } };
347
+ Section: { Task: void };
348
+ };
349
+
350
+ const graph = Graph.create<Nodes, Edges>();
351
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
352
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "Low Priority Task" });
353
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "High Priority Task" });
354
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
355
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 3 });
356
+
357
+ const highPriorityTask = Graph.findNeighbor(
358
+ g,
359
+ ["Project", "1"],
360
+ "Task",
361
+ (task, edge, project) => edge.priority > 2,
362
+ );
363
+ // highPriorityTask: { type: "Task", id: "2", title: "High Priority Task" }
364
+ ```
365
+
366
+ ### findNeighbors
367
+
368
+ ```ts
369
+ function Graph.findNeighbors(
370
+ graph: Graph,
371
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
372
+ type: NodeType,
373
+ find: (target: Node, edge: EdgeData, source: Node) => boolean,
374
+ ): Node[]
375
+ ```
376
+
377
+ Finds all neighbor nodes of a specific type that match the predicate. The predicate receives the target node, edge data, and source node.
378
+
379
+ #### Example
380
+
381
+ ```ts
382
+ import { Graph } from "@monstermann/graph";
383
+
384
+ type Nodes =
385
+ | { type: "Task"; id: string; title: string }
386
+ | { type: "Section"; id: string }
387
+ | { type: "Project"; id: string };
388
+
389
+ type Edges = {
390
+ Project: { Task: { priority: number } };
391
+ Section: { Task: void };
392
+ };
393
+
394
+ const graph = Graph.create<Nodes, Edges>();
395
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
396
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "Low Priority Task" });
397
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Medium Priority Task" });
398
+ g = Graph.setNode(g, { type: "Task", id: "3", title: "High Priority Task" });
399
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
400
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 2 });
401
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "3"], { priority: 3 });
402
+
403
+ const mediumAndHighPriority = Graph.findNeighbors(
404
+ g,
405
+ ["Project", "1"],
406
+ "Task",
407
+ (task, edge, project) => edge.priority >= 2,
408
+ );
409
+ // mediumAndHighPriority: [
410
+ // { type: "Task", id: "2", title: "Medium Priority Task" },
411
+ // { type: "Task", id: "3", title: "High Priority Task" }
412
+ // ]
413
+ ```
414
+
415
+ ### findNode
416
+
417
+ ```ts
418
+ function Graph.findNode(
419
+ graph: Graph,
420
+ type: NodeType,
421
+ find: (node: Node) => boolean,
422
+ ): Node | undefined
423
+ ```
424
+
425
+ Finds the first node of a specific type that matches the predicate. Returns `undefined` if no matching node is found.
426
+
427
+ #### Example
428
+
429
+ ```ts
430
+ import { Graph } from "@monstermann/graph";
431
+
432
+ type Nodes =
433
+ | { type: "Task"; id: string; title: string; completed: boolean }
434
+ | { type: "Section"; id: string }
435
+ | { type: "Project"; id: string };
436
+
437
+ type Edges = {
438
+ Project: { Task: void };
439
+ Section: { Task: void };
440
+ };
441
+
442
+ const graph = Graph.create<Nodes, Edges>();
443
+ let g = Graph.setNode(graph, {
444
+ type: "Task",
445
+ id: "1",
446
+ title: "First Task",
447
+ completed: false,
448
+ });
449
+ g = Graph.setNode(g, {
450
+ type: "Task",
451
+ id: "2",
452
+ title: "Second Task",
453
+ completed: true,
454
+ });
455
+ g = Graph.setNode(g, {
456
+ type: "Task",
457
+ id: "3",
458
+ title: "Third Task",
459
+ completed: false,
460
+ });
461
+
462
+ const completedTask = Graph.findNode(g, "Task", (task) => task.completed);
463
+ // completedTask: { type: "Task", id: "2", title: "Second Task", completed: true }
464
+
465
+ const taskWithTitle = Graph.findNode(
466
+ g,
467
+ "Task",
468
+ (task) => task.title === "First Task",
469
+ );
470
+ // taskWithTitle: { type: "Task", id: "1", title: "First Task", completed: false }
471
+ ```
472
+
473
+ ### findNodes
474
+
475
+ ```ts
476
+ function Graph.findNodes(
477
+ graph: Graph,
478
+ type: NodeType,
479
+ find: (node: Node) => boolean,
480
+ ): Node[]
481
+ ```
482
+
483
+ Finds all nodes of a specific type that match the predicate.
484
+
485
+ #### Example
486
+
487
+ ```ts
488
+ import { Graph } from "@monstermann/graph";
489
+
490
+ type Nodes =
491
+ | { type: "Task"; id: string; title: string; completed: boolean }
492
+ | { type: "Section"; id: string }
493
+ | { type: "Project"; id: string };
494
+
495
+ type Edges = {
496
+ Project: { Task: void };
497
+ Section: { Task: void };
498
+ };
499
+
500
+ const graph = Graph.create<Nodes, Edges>();
501
+ let g = Graph.setNode(graph, {
502
+ type: "Task",
503
+ id: "1",
504
+ title: "First Task",
505
+ completed: false,
506
+ });
507
+ g = Graph.setNode(g, {
508
+ type: "Task",
509
+ id: "2",
510
+ title: "Second Task",
511
+ completed: true,
512
+ });
513
+ g = Graph.setNode(g, {
514
+ type: "Task",
515
+ id: "3",
516
+ title: "Third Task",
517
+ completed: false,
518
+ });
519
+
520
+ const incompleteTasks = Graph.findNodes(g, "Task", (task) => !task.completed);
521
+ // incompleteTasks: [
522
+ // { type: "Task", id: "1", title: "First Task", completed: false },
523
+ // { type: "Task", id: "3", title: "Third Task", completed: false }
524
+ // ]
525
+ ```
526
+
527
+ ### forEachEdge
528
+
529
+ ```ts
530
+ function Graph.forEachEdge(
531
+ graph: Graph,
532
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
533
+ type: NodeType,
534
+ fn: (edge: EdgeData) => void,
535
+ ): Graph
536
+ ```
537
+
538
+ Iterates over all edges of a specific type from a source node, executing a function for each edge. Returns the graph unchanged (for chaining).
539
+
540
+ #### Example
541
+
542
+ ```ts
543
+ import { Graph } from "@monstermann/graph";
544
+
545
+ type Nodes =
546
+ | { type: "Task"; id: string }
547
+ | { type: "Section"; id: string }
548
+ | { type: "Project"; id: string };
549
+
550
+ type Edges = {
551
+ Project: { Task: { priority: number } };
552
+ Section: { Task: void };
553
+ };
554
+
555
+ const graph = Graph.create<Nodes, Edges>();
556
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
557
+ g = Graph.setNode(g, { type: "Task", id: "1" });
558
+ g = Graph.setNode(g, { type: "Task", id: "2" });
559
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
560
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 2 });
561
+
562
+ Graph.forEachEdge(g, ["Project", "1"], "Task", (edge) => {
563
+ console.log(edge.priority);
564
+ });
565
+ // Logs:
566
+ // 1
567
+ // 2
568
+ ```
569
+
570
+ ### forEachNeighbor
571
+
572
+ ```ts
573
+ function Graph.forEachNeighbor(
574
+ graph: Graph,
575
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
576
+ type: NodeType,
577
+ fn: (target: Node, edge: EdgeData, source: Node) => void,
578
+ ): Graph
579
+ ```
580
+
581
+ Iterates over all neighbor nodes of a specific type, executing a function for each neighbor. The function receives the target node, edge data, and source node. Returns the graph unchanged (for chaining).
582
+
583
+ #### Example
584
+
585
+ ```ts
586
+ import { Graph } from "@monstermann/graph";
587
+
588
+ type Nodes =
589
+ | { type: "Task"; id: string; title: string }
590
+ | { type: "Section"; id: string }
591
+ | { type: "Project"; id: string; name: string };
592
+
593
+ type Edges = {
594
+ Project: { Task: { priority: number } };
595
+ Section: { Task: void };
596
+ };
597
+
598
+ const graph = Graph.create<Nodes, Edges>();
599
+ let g = Graph.setNode(graph, { type: "Project", id: "1", name: "My Project" });
600
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "First Task" });
601
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Second Task" });
602
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
603
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 2 });
604
+
605
+ Graph.forEachNeighbor(g, ["Project", "1"], "Task", (task, edge, project) => {
606
+ console.log(`${project.name}: ${task.title} (priority: ${edge.priority})`);
607
+ });
608
+ // Logs:
609
+ // "My Project: First Task (priority: 1)"
610
+ // "My Project: Second Task (priority: 2)"
611
+ ```
612
+
613
+ ### forEachNode
614
+
615
+ ```ts
616
+ function Graph.forEachNode(
617
+ graph: Graph,
618
+ type: NodeType,
619
+ fn: (node: Node) => void,
620
+ ): Graph
621
+ ```
622
+
623
+ Iterates over all nodes of a specific type, executing a function for each node. Returns the graph unchanged (for chaining).
624
+
625
+ #### Example
626
+
627
+ ```ts
628
+ import { Graph } from "@monstermann/graph";
629
+
630
+ type Nodes =
631
+ | { type: "Task"; id: string; title: string }
632
+ | { type: "Section"; id: string }
633
+ | { type: "Project"; id: string };
634
+
635
+ type Edges = {
636
+ Project: { Task: void };
637
+ Section: { Task: void };
638
+ };
639
+
640
+ const graph = Graph.create<Nodes, Edges>();
641
+ let g = Graph.setNode(graph, { type: "Task", id: "1", title: "First Task" });
642
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Second Task" });
643
+ g = Graph.setNode(g, { type: "Task", id: "3", title: "Third Task" });
644
+
645
+ Graph.forEachNode(g, "Task", (task) => {
646
+ console.log(task.title);
647
+ });
648
+ // Logs:
649
+ // "First Task"
650
+ // "Second Task"
651
+ // "Third Task"
652
+ ```
653
+
654
+ ### fromJS
655
+
656
+ ```ts
657
+ function Graph.fromJS(data: {
658
+ nodes: Node[];
659
+ edges: [NodeType, NodeId, NodeType, NodeId, EdgeData][];
660
+ }): Graph
661
+ ```
662
+
663
+ Creates a graph from a plain JavaScript object representation. Useful for deserializing graphs from JSON or other storage formats.
664
+
665
+ #### Example
666
+
667
+ ```ts
668
+ import { Graph } from "@monstermann/graph";
669
+
670
+ type Nodes =
671
+ | { type: "Task"; id: string; title: string }
672
+ | { type: "Section"; id: string }
673
+ | { type: "Project"; id: string };
674
+
675
+ type Edges = {
676
+ Project: { Task: void };
677
+ Section: { Task: void };
678
+ };
679
+
680
+ const data = {
681
+ nodes: [
682
+ { type: "Project", id: "1" },
683
+ { type: "Task", id: "1", title: "My Task" },
684
+ { type: "Task", id: "2", title: "Another Task" },
685
+ ],
686
+ edges: [
687
+ ["Project", "1", "Task", "1", undefined],
688
+ ["Project", "1", "Task", "2", undefined],
689
+ ],
690
+ };
691
+
692
+ const graph = Graph.fromJS<Nodes, Edges>(data);
693
+
694
+ Graph.hasNode(graph, ["Project", "1"]); // true
695
+ Graph.hasEdge(graph, ["Project", "1"], ["Task", "1"]); // true
696
+ ```
697
+
698
+ ### getEdge
699
+
700
+ ```ts
701
+ function Graph.getEdge(
702
+ graph: Graph,
703
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
704
+ target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
705
+ ): EdgeData | undefined
706
+ ```
707
+
708
+ Retrieves the edge data between two nodes. Returns `undefined` if the edge doesn't exist.
709
+
710
+ #### Example
711
+
712
+ ```ts
713
+ import { Graph } from "@monstermann/graph";
714
+
715
+ type Nodes =
716
+ | { type: "Task"; id: string }
717
+ | { type: "Section"; id: string }
718
+ | { type: "Project"; id: string };
719
+
720
+ type Edges = {
721
+ Project: { Task: { assignedAt: Date } };
722
+ Section: { Task: void };
723
+ };
724
+
725
+ const graph = Graph.create<Nodes, Edges>();
726
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
727
+ g = Graph.setNode(g, { type: "Task", id: "1" });
728
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
729
+ assignedAt: new Date(),
730
+ });
731
+
732
+ const edge = Graph.getEdge(g, ["Project", "1"], ["Task", "1"]);
733
+ // edge: { assignedAt: Date }
734
+
735
+ const missing = Graph.getEdge(g, ["Project", "1"], ["Task", "2"]);
736
+ // missing: undefined
737
+ ```
738
+
739
+ ### getEdges
740
+
741
+ ```ts
742
+ function Graph.getEdges(
743
+ graph: Graph,
744
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
745
+ type: NodeType,
746
+ ): EdgeData[]
747
+ ```
748
+
749
+ Retrieves all edges of a specific type from a source node.
750
+
751
+ #### Example
752
+
753
+ ```ts
754
+ import { Graph } from "@monstermann/graph";
755
+
756
+ type Nodes =
757
+ | { type: "Task"; id: string }
758
+ | { type: "Section"; id: string }
759
+ | { type: "Project"; id: string };
760
+
761
+ type Edges = {
762
+ Project: { Task: { priority: number } };
763
+ Section: { Task: void };
764
+ };
765
+
766
+ const graph = Graph.create<Nodes, Edges>();
767
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
768
+ g = Graph.setNode(g, { type: "Task", id: "1" });
769
+ g = Graph.setNode(g, { type: "Task", id: "2" });
770
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], { priority: 1 });
771
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"], { priority: 2 });
772
+
773
+ const edges = Graph.getEdges(g, ["Project", "1"], "Task");
774
+ // edges: [{ priority: 1 }, { priority: 2 }]
775
+ ```
776
+
777
+ ### getNeighbor
778
+
779
+ ```ts
780
+ function Graph.getNeighbor(
781
+ graph: Graph,
782
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
783
+ type: NodeType,
784
+ ): Node | undefined
785
+ ```
786
+
787
+ Retrieves the first neighbor node of a specific type connected to the source node. Returns `undefined` if no neighbor of the specified type exists.
788
+
789
+ #### Example
790
+
791
+ ```ts
792
+ import { Graph } from "@monstermann/graph";
793
+
794
+ type Nodes =
795
+ | { type: "Task"; id: string; title: string }
796
+ | { type: "Section"; id: string }
797
+ | { type: "Project"; id: string };
798
+
799
+ type Edges = {
800
+ Project: { Task: void };
801
+ Section: { Task: void };
802
+ };
803
+
804
+ const graph = Graph.create<Nodes, Edges>();
805
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
806
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "Task 1" });
807
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Task 2" });
808
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
809
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"]);
810
+
811
+ const firstTask = Graph.getNeighbor(g, ["Project", "1"], "Task");
812
+ // firstTask: { type: "Task", id: "1", title: "Task 1" }
813
+
814
+ const noSection = Graph.getNeighbor(g, ["Project", "1"], "Section");
815
+ // noSection: undefined
816
+ ```
817
+
818
+ ### getNeighbors
819
+
820
+ ```ts
821
+ function Graph.getNeighbors(
822
+ graph: Graph,
823
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
824
+ type: NodeType,
825
+ ): Node[]
826
+ ```
827
+
828
+ Retrieves all neighbor nodes of a specific type connected to the source node.
829
+
830
+ #### Example
831
+
832
+ ```ts
833
+ import { Graph } from "@monstermann/graph";
834
+
835
+ type Nodes =
836
+ | { type: "Task"; id: string; title: string }
837
+ | { type: "Section"; id: string }
838
+ | { type: "Project"; id: string };
839
+
840
+ type Edges = {
841
+ Project: { Task: void };
842
+ Section: { Task: void };
843
+ };
844
+
845
+ const graph = Graph.create<Nodes, Edges>();
846
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
847
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "Task 1" });
848
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Task 2" });
849
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
850
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "2"]);
851
+
852
+ const tasks = Graph.getNeighbors(g, ["Project", "1"], "Task");
853
+ // tasks: [{ type: "Task", id: "1", title: "Task 1" }, { type: "Task", id: "2", title: "Task 2" }]
854
+ ```
855
+
856
+ ### getNode
857
+
858
+ ```ts
859
+ function Graph.getNode(
860
+ graph: Graph,
861
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
862
+ ): Node | undefined
863
+ ```
864
+
865
+ Retrieves a node from the graph. Returns `undefined` if the node doesn't exist.
866
+
867
+ #### Example
868
+
869
+ ```ts
870
+ import { Graph } from "@monstermann/graph";
871
+
872
+ type Nodes =
873
+ | { type: "Task"; id: string; title: string }
874
+ | { type: "Section"; id: string }
875
+ | { type: "Project"; id: string };
876
+
877
+ type Edges = {
878
+ Project: { Task: void };
879
+ Section: { Task: void };
880
+ };
881
+
882
+ const graph = Graph.create<Nodes, Edges>();
883
+ const withNode = Graph.setNode(graph, {
884
+ type: "Task",
885
+ id: "1",
886
+ title: "My Task",
887
+ });
888
+
889
+ const task = Graph.getNode(withNode, ["Task", "1"]);
890
+ // task: { type: "Task", id: "1", title: "My Task" }
891
+
892
+ const missing = Graph.getNode(withNode, ["Task", "2"]);
893
+ // missing: undefined
894
+ ```
895
+
896
+ ### getNodes
897
+
898
+ ```ts
899
+ function Graph.getNodes(
900
+ graph: Graph,
901
+ type: NodeType,
902
+ ): Node[]
903
+ ```
904
+
905
+ Retrieves all nodes of a specific type from the graph.
906
+
907
+ #### Example
908
+
909
+ ```ts
910
+ import { Graph } from "@monstermann/graph";
911
+
912
+ type Nodes =
913
+ | { type: "Task"; id: string; title: string }
914
+ | { type: "Section"; id: string }
915
+ | { type: "Project"; id: string };
916
+
917
+ type Edges = {
918
+ Project: { Task: void };
919
+ Section: { Task: void };
920
+ };
921
+
922
+ const graph = Graph.create<Nodes, Edges>();
923
+ let g = Graph.setNode(graph, { type: "Task", id: "1", title: "Task 1" });
924
+ g = Graph.setNode(g, { type: "Task", id: "2", title: "Task 2" });
925
+ g = Graph.setNode(g, { type: "Project", id: "1" });
926
+
927
+ const tasks = Graph.getNodes(g, "Task");
928
+ // tasks: [{ type: "Task", id: "1", title: "Task 1" }, { type: "Task", id: "2", title: "Task 2" }]
929
+
930
+ const projects = Graph.getNodes(g, "Project");
931
+ // projects: [{ type: "Project", id: "1" }]
932
+ ```
933
+
934
+ ### hasEdge
935
+
936
+ ```ts
937
+ function Graph.hasEdge(
938
+ graph: Graph,
939
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
940
+ target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
941
+ ): boolean
942
+ ```
943
+
944
+ Checks if an edge exists between two nodes in the graph.
945
+
946
+ #### Example
947
+
948
+ ```ts
949
+ import { Graph } from "@monstermann/graph";
950
+
951
+ type Nodes =
952
+ | { type: "Task"; id: string }
953
+ | { type: "Section"; id: string }
954
+ | { type: "Project"; id: string };
955
+
956
+ type Edges = {
957
+ Project: { Task: void };
958
+ Section: { Task: void };
959
+ };
960
+
961
+ const graph = Graph.create<Nodes, Edges>();
962
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
963
+ g = Graph.setNode(g, { type: "Task", id: "1" });
964
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
965
+
966
+ Graph.hasEdge(g, ["Project", "1"], ["Task", "1"]); // true
967
+ Graph.hasEdge(g, ["Task", "1"], ["Project", "1"]); // true (bidirectional)
968
+ Graph.hasEdge(g, ["Project", "1"], ["Task", "2"]); // false
969
+ ```
970
+
971
+ ### hasNode
972
+
973
+ ```ts
974
+ function Graph.hasNode(
975
+ graph: Graph,
976
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
977
+ ): boolean
978
+ ```
979
+
980
+ Checks if a node exists in the graph.
981
+
982
+ #### Example
983
+
984
+ ```ts
985
+ import { Graph } from "@monstermann/graph";
986
+
987
+ type Nodes =
988
+ | { type: "Task"; id: string }
989
+ | { type: "Section"; id: string }
990
+ | { type: "Project"; id: string };
991
+
992
+ type Edges = {
993
+ Project: { Task: void };
994
+ Section: { Task: void };
995
+ };
996
+
997
+ const graph = Graph.create<Nodes, Edges>();
998
+ const withNode = Graph.setNode(graph, { type: "Task", id: "1" });
999
+
1000
+ Graph.hasNode(withNode, ["Task", "1"]); // true
1001
+ Graph.hasNode(withNode, { type: "Task", id: "1" }); // true
1002
+ Graph.hasNode(withNode, ["Task", "2"]); // false
1003
+ ```
1004
+
1005
+ ### mapEdge
1006
+
1007
+ ```ts
1008
+ function Graph.mapEdge(
1009
+ graph: Graph,
1010
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1011
+ target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1012
+ update: (edge: EdgeData) => EdgeData,
1013
+ ): Graph
1014
+ ```
1015
+
1016
+ Updates an edge by applying a transformation function. Returns a new graph instance with the updated edge.
1017
+
1018
+ #### Example
1019
+
1020
+ ```ts
1021
+ import { Graph } from "@monstermann/graph";
1022
+
1023
+ type Nodes =
1024
+ | { type: "Task"; id: string }
1025
+ | { type: "Section"; id: string }
1026
+ | { type: "Project"; id: string };
1027
+
1028
+ type Edges = {
1029
+ Project: { Task: { priority: number; assignedAt: Date } };
1030
+ Section: { Task: void };
1031
+ };
1032
+
1033
+ const graph = Graph.create<Nodes, Edges>();
1034
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
1035
+ g = Graph.setNode(g, { type: "Task", id: "1" });
1036
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
1037
+ priority: 1,
1038
+ assignedAt: new Date(),
1039
+ });
1040
+
1041
+ // Update edge data
1042
+ g = Graph.mapEdge(g, ["Project", "1"], ["Task", "1"], (edge) => ({
1043
+ ...edge,
1044
+ priority: 2,
1045
+ }));
1046
+ ```
1047
+
1048
+ ### mapNode
1049
+
1050
+ ```ts
1051
+ function Graph.mapNode(
1052
+ graph: Graph,
1053
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1054
+ update: (node: Node) => Node,
1055
+ ): Graph
1056
+ ```
1057
+
1058
+ 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.
1059
+
1060
+ #### Example
1061
+
1062
+ ```ts
1063
+ import { Graph } from "@monstermann/graph";
1064
+
1065
+ type Nodes =
1066
+ | { type: "Task"; id: string; title: string; completed: boolean }
1067
+ | { type: "Section"; id: string }
1068
+ | { type: "Project"; id: string };
1069
+
1070
+ type Edges = {
1071
+ Project: { Task: void };
1072
+ Section: { Task: void };
1073
+ };
1074
+
1075
+ const graph = Graph.create<Nodes, Edges>();
1076
+ let g = Graph.setNode(graph, {
1077
+ type: "Task",
1078
+ id: "1",
1079
+ title: "My Task",
1080
+ completed: false,
1081
+ });
1082
+
1083
+ // Update a property
1084
+ g = Graph.mapNode(g, ["Task", "1"], (task) => ({
1085
+ ...task,
1086
+ completed: true,
1087
+ }));
1088
+
1089
+ // Change id (edges are preserved)
1090
+ let g2 = Graph.setNode(graph, {
1091
+ type: "Task",
1092
+ id: "1",
1093
+ title: "Task",
1094
+ completed: false,
1095
+ });
1096
+ g2 = Graph.setNode(g2, { type: "Project", id: "1" });
1097
+ g2 = Graph.setEdge(g2, ["Project", "1"], ["Task", "1"]);
1098
+ g2 = Graph.mapNode(g2, ["Task", "1"], (task) => ({
1099
+ ...task,
1100
+ id: "2",
1101
+ }));
1102
+ // Edge now connects ["Project", "1"] -> ["Task", "2"]
1103
+ ```
1104
+
1105
+ ### mergeEdge
1106
+
1107
+ ```ts
1108
+ function Graph.mergeEdge(
1109
+ graph: Graph,
1110
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1111
+ target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1112
+ update: Partial<EdgeData>,
1113
+ ): Graph
1114
+ ```
1115
+
1116
+ Partially updates an edge by merging the provided properties. Returns a new graph instance with the updated edge.
1117
+
1118
+ #### Example
1119
+
1120
+ ```ts
1121
+ import { Graph } from "@monstermann/graph";
1122
+
1123
+ type Nodes =
1124
+ | { type: "Task"; id: string }
1125
+ | { type: "Section"; id: string }
1126
+ | { type: "Project"; id: string };
1127
+
1128
+ type Edges = {
1129
+ Project: { Task: { priority: number; assignedAt: Date } };
1130
+ Section: { Task: void };
1131
+ };
1132
+
1133
+ const graph = Graph.create<Nodes, Edges>();
1134
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
1135
+ g = Graph.setNode(g, { type: "Task", id: "1" });
1136
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
1137
+ priority: 1,
1138
+ assignedAt: new Date(),
1139
+ });
1140
+
1141
+ // Merge partial update
1142
+ g = Graph.mergeEdge(g, ["Project", "1"], ["Task", "1"], { priority: 2 });
1143
+
1144
+ const edge = Graph.getEdge(g, ["Project", "1"], ["Task", "1"]);
1145
+ // edge: { priority: 2, assignedAt: <original date> }
1146
+ ```
1147
+
1148
+ ### mergeNode
1149
+
1150
+ ```ts
1151
+ function Graph.mergeNode(
1152
+ graph: Graph,
1153
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1154
+ update: Partial<Node>,
1155
+ ): Graph
1156
+ ```
1157
+
1158
+ Partially updates a node by merging the provided properties. Returns a new graph instance with the updated node.
1159
+
1160
+ #### Example
1161
+
1162
+ ```ts
1163
+ import { Graph } from "@monstermann/graph";
1164
+
1165
+ type Nodes =
1166
+ | { type: "Task"; id: string; title: string; completed: boolean }
1167
+ | { type: "Section"; id: string }
1168
+ | { type: "Project"; id: string };
1169
+
1170
+ type Edges = {
1171
+ Project: { Task: void };
1172
+ Section: { Task: void };
1173
+ };
1174
+
1175
+ const graph = Graph.create<Nodes, Edges>();
1176
+ let g = Graph.setNode(graph, {
1177
+ type: "Task",
1178
+ id: "1",
1179
+ title: "My Task",
1180
+ completed: false,
1181
+ });
1182
+
1183
+ // Merge partial update
1184
+ g = Graph.mergeNode(g, ["Task", "1"], { completed: true });
1185
+
1186
+ const task = Graph.getNode(g, ["Task", "1"]);
1187
+ // task: { type: "Task", id: "1", title: "My Task", completed: true }
1188
+ ```
1189
+
1190
+ ### removeEdge
1191
+
1192
+ ```ts
1193
+ function Graph.removeEdge(
1194
+ graph: Graph,
1195
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1196
+ target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1197
+ ): Graph
1198
+ ```
1199
+
1200
+ Removes an edge between two nodes. The nodes remain in the graph. Returns a new graph instance without the edge.
1201
+
1202
+ #### Example
1203
+
1204
+ ```ts
1205
+ import { Graph } from "@monstermann/graph";
1206
+
1207
+ type Nodes =
1208
+ | { type: "Task"; id: string }
1209
+ | { type: "Section"; id: string }
1210
+ | { type: "Project"; id: string };
1211
+
1212
+ type Edges = {
1213
+ Project: { Task: void };
1214
+ Section: { Task: void };
1215
+ };
1216
+
1217
+ const graph = Graph.create<Nodes, Edges>();
1218
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
1219
+ g = Graph.setNode(g, { type: "Task", id: "1" });
1220
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
1221
+
1222
+ // Remove the edge
1223
+ g = Graph.removeEdge(g, ["Project", "1"], ["Task", "1"]);
1224
+
1225
+ Graph.hasNode(g, ["Project", "1"]); // true
1226
+ Graph.hasNode(g, ["Task", "1"]); // true
1227
+ Graph.hasEdge(g, ["Project", "1"], ["Task", "1"]); // false
1228
+ ```
1229
+
1230
+ ### removeNode
1231
+
1232
+ ```ts
1233
+ function Graph.removeNode(
1234
+ graph: Graph,
1235
+ node: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1236
+ ): Graph
1237
+ ```
1238
+
1239
+ Removes a node and all its associated edges from the graph. Returns a new graph instance without the node.
1240
+
1241
+ #### Example
1242
+
1243
+ ```ts
1244
+ import { Graph } from "@monstermann/graph";
1245
+
1246
+ type Nodes =
1247
+ | { type: "Task"; id: string }
1248
+ | { type: "Section"; id: string }
1249
+ | { type: "Project"; id: string };
1250
+
1251
+ type Edges = {
1252
+ Project: { Task: void };
1253
+ Section: { Task: void };
1254
+ };
1255
+
1256
+ const graph = Graph.create<Nodes, Edges>();
1257
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
1258
+ g = Graph.setNode(g, { type: "Task", id: "1" });
1259
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
1260
+
1261
+ // Remove the task (also removes the edge)
1262
+ g = Graph.removeNode(g, ["Task", "1"]);
1263
+
1264
+ Graph.hasNode(g, ["Task", "1"]); // false
1265
+ Graph.hasEdge(g, ["Project", "1"], ["Task", "1"]); // false
1266
+ ```
1267
+
1268
+ ### setEdge
1269
+
1270
+ ```ts
1271
+ function Graph.setEdge(
1272
+ graph: Graph,
1273
+ source: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1274
+ target: [NodeType, NodeId] | { type: NodeType, id: NodeId },
1275
+ data?: EdgeData,
1276
+ ): Graph
1277
+ ```
1278
+
1279
+ Adds or updates an edge between two nodes. Both nodes must exist in the graph. Returns a new graph instance with the edge set. Edges are bidirectional.
1280
+
1281
+ #### Example
1282
+
1283
+ ```ts
1284
+ import { Graph } from "@monstermann/graph";
1285
+
1286
+ type Nodes =
1287
+ | { type: "Task"; id: string }
1288
+ | { type: "Section"; id: string }
1289
+ | { type: "Project"; id: string };
1290
+
1291
+ type Edges = {
1292
+ Project: { Task: { assignedAt: Date } };
1293
+ Section: { Task: void };
1294
+ };
1295
+
1296
+ const graph = Graph.create<Nodes, Edges>();
1297
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
1298
+ g = Graph.setNode(g, { type: "Task", id: "1" });
1299
+
1300
+ // Add edge with data
1301
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"], {
1302
+ assignedAt: new Date(),
1303
+ });
1304
+
1305
+ // Add edge without data (void)
1306
+ let g2 = Graph.setNode(graph, { type: "Section", id: "1" });
1307
+ g2 = Graph.setNode(g2, { type: "Task", id: "1" });
1308
+ g2 = Graph.setEdge(g2, ["Section", "1"], ["Task", "1"]);
1309
+ ```
1310
+
1311
+ ### setNode
1312
+
1313
+ ```ts
1314
+ function Graph.setNode(
1315
+ graph: Graph,
1316
+ node: Node,
1317
+ ): Graph
1318
+ ```
1319
+
1320
+ Adds or updates a node in the graph. Returns a new graph instance with the node set.
1321
+
1322
+ #### Example
1323
+
1324
+ ```ts
1325
+ import { Graph } from "@monstermann/graph";
1326
+
1327
+ type Nodes =
1328
+ | { type: "Task"; id: string; title: string }
1329
+ | { type: "Section"; id: string }
1330
+ | { type: "Project"; id: string };
1331
+
1332
+ type Edges = {
1333
+ Project: { Task: void };
1334
+ Section: { Task: void };
1335
+ };
1336
+
1337
+ const graph = Graph.create<Nodes, Edges>();
1338
+
1339
+ // Add a new node
1340
+ const g1 = Graph.setNode(graph, { type: "Task", id: "1", title: "My Task" });
1341
+
1342
+ // Update existing node
1343
+ const g2 = Graph.setNode(g1, { type: "Task", id: "1", title: "Updated Task" });
1344
+ ```
1345
+
1346
+ ### toJS
1347
+
1348
+ ```ts
1349
+ function Graph.toJS(
1350
+ graph: Graph,
1351
+ ): {
1352
+ nodes: Node[];
1353
+ edges: [NodeType, NodeId, NodeType, NodeId, EdgeData][];
1354
+ }
1355
+ ```
1356
+
1357
+ Converts a graph to a plain JavaScript object representation. Useful for serializing graphs to JSON or other storage formats.
1358
+
1359
+ #### Example
1360
+
1361
+ ```ts
1362
+ import { Graph } from "@monstermann/graph";
1363
+
1364
+ type Nodes =
1365
+ | { type: "Task"; id: string; title: string }
1366
+ | { type: "Section"; id: string }
1367
+ | { type: "Project"; id: string };
1368
+
1369
+ type Edges = {
1370
+ Project: { Task: void };
1371
+ Section: { Task: void };
1372
+ };
1373
+
1374
+ const graph = Graph.create<Nodes, Edges>();
1375
+ let g = Graph.setNode(graph, { type: "Project", id: "1" });
1376
+ g = Graph.setNode(g, { type: "Task", id: "1", title: "My Task" });
1377
+ g = Graph.setEdge(g, ["Project", "1"], ["Task", "1"]);
1378
+
1379
+ const data = Graph.toJS(g);
1380
+ // data: {
1381
+ // nodes: [
1382
+ // { type: "Project", id: "1" },
1383
+ // { type: "Task", id: "1", title: "My Task" }
1384
+ // ],
1385
+ // edges: [
1386
+ // ["Project", "1", "Task", "1", undefined]
1387
+ // ]
1388
+ // }
1389
+
1390
+ // Can be serialized to JSON
1391
+ const json = JSON.stringify(data);
1392
+ ```