diff --git a/README.md b/README.md
index a47d958..69fc055 100644
--- a/README.md
+++ b/README.md
@@ -3,5 +3,6 @@
A collection of JavaScript utility libraries:
- [@haydn/geometry-fns](https://github.com/haydn/fns/tree/main/packages/geometry-fns)
+- [graph-fns](https://github.com/haydn/fns/tree/main/packages/graph-fns)
- [@haydn/grid-fns](https://github.com/haydn/fns/tree/main/packages/grid-fns)
- [@haydn/linear-fns](https://github.com/haydn/fns/tree/main/packages/linear-fns)
diff --git a/bunup.config.ts b/bunup.config.ts
index e0ef572..0a6f2ed 100644
--- a/bunup.config.ts
+++ b/bunup.config.ts
@@ -11,6 +11,11 @@ export default defineWorkspace([
root: "packages/geometry-fns",
config,
},
+ {
+ name: "graph-fns",
+ root: "packages/graph-fns",
+ config,
+ },
{
name: "grid-fns",
root: "packages/grid-fns",
diff --git a/package.json b/package.json
index 6892d33..43dffe6 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"add-change": "bun run changeset add",
"build": "bunup src/index.ts",
"docs": "bun run --filter '*' docs",
- "publish": "bun run --cwd packages/linear-fns publish && bun run --cwd packages/geometry-fns publish && bun run --cwd packages/grid-fns publish",
+ "publish": "bun run --cwd packages/linear-fns publish && bun run --cwd packages/geometry-fns publish && bun run --cwd packages/grid-fns publish && bun run --cwd packages/graph-fns publish",
"test": "bun test"
},
"type": "module",
diff --git a/packages/graph-fns/LICENSE b/packages/graph-fns/LICENSE
new file mode 100644
index 0000000..485cd9e
--- /dev/null
+++ b/packages/graph-fns/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2020-2025 Haydn Ewers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/graph-fns/README.md b/packages/graph-fns/README.md
new file mode 100644
index 0000000..3a98e08
--- /dev/null
+++ b/packages/graph-fns/README.md
@@ -0,0 +1,713 @@
+
+
+
+
+
A JavaScript utility library for working with graphs.
+
+
+
+
+
+
+## Features
+
+- Lightweight.
+- Pure functions.
+- TypeScript declarations included.
+- ESM package.
+
+## Installation
+
+```bash
+bun add graph-fns
+deno add npm:graph-fns
+npm install graph-fns
+pnpm add graph-fns
+yarn add graph-fns
+```
+
+## Demo
+
+https://h2788.csb.app/
+
+
+
+## Usage
+
+```js
+import { create, addEdge, isCyclic, topologicalSort, degree, addVertex } from "graph-fns";
+
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "C"]);
+//=> Graph { "A" -> "C", "B" }
+
+graph = addEdge(graph, ["B", "A"]);
+//=> Graph { "A" -> "C", "B" -> "A" }
+
+isCyclic(graph);
+//=> false
+
+topologicalSort(graph);
+//=> ["B", "A", "C"]
+
+degree(graph, "A");
+//=> 2
+
+graph = addVertex(graph, "D");
+//=> Graph { "A" -> "C", "B" -> "A", "D" }
+
+graph = addEdge(graph, ["C", "D"]);
+//=> Graph { "A" -> "C", "B" -> "A", "C" -> "D" }
+
+descendants(graph, "A");
+//=> Set { "C", "D" }
+
+graph = addEdge(graph, ["D", "B"]);
+//=> Graph { "A" -> "C", "B" -> "A", "C" -> "D", "D" -> "B" }
+
+isCyclic(graph);
+//=> true
+```
+
+
+
+## Functions
+
+- [addEdge](#addedge)
+- [addVertex](#addvertex)
+- [ancestors](#ancestors)
+- [children](#children)
+- [clone](#clone)
+- [create](#create)
+- [degree](#degree)
+- [descendants](#descendants)
+- [edges](#edges)
+- [fromD3](#fromd3)
+- [getEdge](#getedge)
+- [indegree](#indegree)
+- [isCyclic](#iscyclic)
+- [isUndirected](#isundirected)
+- [makeUndirected](#makeundirected)
+- [order](#order)
+- [outdegree](#outdegree)
+- [parents](#parents)
+- [removeEdge](#removeedge)
+- [removeVertex](#removevertex)
+- [setEdge](#setedge)
+- [size](#size)
+- [toD3](#tod3)
+- [toDirected](#todirected)
+- [topologicalSort](#topologicalsort)
+- [transpose](#transpose)
+- [vertices](#vertices)
+- [vertexPairs](#vertexpairs)
+
+### addEdge
+
+Adds a new edge to the graph from vertex `u` to vertex `v`.
+
+**Note**: `addEdge(graph, edge)` is equivalent to `setEdge(graph, edge, 1)`.
+
+Also see:
+
+- [removeEdge](#removeEdge)
+- [getEdge](#getEdge)
+- [setEdge](#setEdge)
+
+| Function | Type |
+| ---------- | ---------- |
+| `addEdge` | `(graph: Graph, [u, v]: Edge, options?: { undirected?: boolean or undefined; } or undefined) => Graph` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "B"]);
+//=> Graph { "A" -> "B", "C" }
+```
+
+
+### addVertex
+
+Adds a new vertex to the graph. The new vertex will not have any edges
+connecting it to existing vertices in the graph.
+
+**Note**: If the vertex already exists the graph will be returned unmodified.
+
+Also see:
+
+- [removeVertex](#removeVertex)
+
+| Function | Type |
+| ---------- | ---------- |
+| `addVertex` | `(graph: Graph, vertex: T2) => Graph` |
+
+### ancestors
+
+Given a [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph), returns
+all ancestors of the given vertex (i.e. vertices from which there is a
+directed path to the given vertex).
+
+**Note**: If the given graph contains cycles (checked with
+[isCyclic](#isCyclic)), an error will be thrown.
+
+Also see:
+
+- [descendants](#descendants)
+- [parents](#parents)
+
+| Function | Type |
+| ---------- | ---------- |
+| `ancestors` | `(graph: Graph, vertex: T2) => Set` |
+
+### children
+
+Returns all the vertices that are children of the given vertex (i.e. there is
+an edge starting at the given vertex going to the child vertex).
+
+**Note**: If there is an edge that both starts and ends at the given vertex,
+it will be considered a child of itself and included in the result.
+
+Also see:
+
+- [parents](#parents)
+- [descendants](#descendants)
+
+| Function | Type |
+| ---------- | ---------- |
+| `children` | `(graph: Graph, vertex: T2) => Set` |
+
+### clone
+
+Creates a copy of the graph.
+
+| Function | Type |
+| ---------- | ---------- |
+| `clone` | `(graph: Graph) => Graph` |
+
+### create
+
+| Function | Type |
+| ---------- | ---------- |
+| `create` | `(vertices: T[]) => Graph` |
+
+### degree
+
+Returns the [degree]()
+for the given vertex.
+
+By default `weighted` is `false`, if set to `true` the result will be the sum
+of the edge weights (which could be zero or a negative value).
+
+Also see:
+
+- [indegree](#indegree)
+- [outdegree](#outdegree)
+
+| Function | Type |
+| ---------- | ---------- |
+| `degree` | `(graph: Graph, vertex: T2, options?: { weighted?: boolean or undefined; undirected?: boolean or undefined; } or undefined) => number` |
+
+### descendants
+
+Given a [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph), returns
+all descendants of the given vertex (i.e. vertices to which there is a
+directed path from the given vertex).
+
+**Note**: If the given graph contains cycles (checked with
+[isCyclic](#isCyclic)), an error will be thrown.
+
+Also see:
+
+- [ancestors](#ancestors)
+- [children](#children)
+
+| Function | Type |
+| ---------- | ---------- |
+| `descendants` | `(graph: Graph, vertex: T2) => Set` |
+
+### edges
+
+Returns all the edges in the graph (i.e. any edge with a value other than
+`0`).
+
+| Function | Type |
+| ---------- | ---------- |
+| `edges` | `(graph: Graph, options?: { undirected?: boolean or undefined; } or undefined) => Set>` |
+
+### fromD3
+
+Converts a graph from a [D3Graph](#D3Graph) representation into a
+[Graph](#Graph) representation.
+
+When the D3Graph contains multiple links between two nodes the resulting
+graph will have inflated edge weights to reflect that.
+
+**Note**: Any extraneous data associated with nodes or links in the D3Graph representation will be ignored.
+
+Also see:
+
+- [toD3](#toD3)
+
+| Function | Type |
+| ---------- | ---------- |
+| `fromD3` | `(d3Graph: D3Graph, options?: { undirected?: boolean or undefined; } or undefined) => Graph` |
+
+Examples:
+
+```js
+const graph = fromD3({
+ nodes: [{ id: "A" }, { id: "B" }, { id: "C" }],
+ links: [
+ { source: "A", target: "B" },
+ { source: "A", target: "C" },
+ { source: "A", target: "C" },
+ ],
+});
+//=> Graph { "A" -> "B", "A" -> "C" }
+
+getEdge(["A", "B"]);
+//=> 1
+getEdge(["A", "C"]);
+//=> 2
+```
+
+
+### getEdge
+
+Get the weight of the given edge.
+
+Also see:
+
+- [addEdge](#addEdge)
+- [removeEdge](#removeEdge)
+- [setEdge](#setEdge)
+
+| Function | Type |
+| ---------- | ---------- |
+| `getEdge` | `(graph: Graph, [u, v]: Edge) => number` |
+
+### indegree
+
+Returns the [indegree](https://en.wikipedia.org/wiki/Indegree) for the given
+vertex.
+
+By default `weighted` is `false`, if set to `true` the result will be the sum
+of the edge weights (which could be zero or a negative value).
+
+Also see:
+
+- [degree](#degree)
+- [outdegree](#outdegree)
+
+| Function | Type |
+| ---------- | ---------- |
+| `indegree` | `(graph: Graph, vertex: T2, options?: { weighted?: boolean or undefined; } or undefined) => number` |
+
+### isCyclic
+
+Returns `true` if the graph provided contains any
+[cycles]() (including
+"loops" — an edge that starts and ends at the same vertex), otherwise returns
+`false`.
+
+| Function | Type |
+| ---------- | ---------- |
+| `isCyclic` | `(graph: Graph, options?: { undirected?: boolean or undefined; } or undefined) => boolean` |
+
+### isUndirected
+
+Returns `true` if the graph can be considered an [undirected
+graph](https://mathinsight.org/definition/undirected_graph) — every edge in
+the graph (from vertex A to B) has a mutual edge (from vertex B to A) with an
+equal weight. Loops are considered bidirectional and are allow in a
+undirected graph.
+
+| Function | Type |
+| ---------- | ---------- |
+| `isUndirected` | `(graph: Graph) => boolean` |
+
+Examples:
+
+```js
+let graph = create(["A", "B"]);
+//=> Graph { "A", "B" }
+
+isUndirected(graph);
+//=> true
+
+graph = addEdge(graph, ["A", "B"]);
+//=> Graph { "A" -> "B" }
+
+isUndirected(graph);
+//=> false
+
+graph = addEdge(graph, ["B", "A"]);
+//=> Graph { "A" <-> "B" }
+
+isUndirected(graph);
+//=> true
+```
+
+
+### makeUndirected
+
+Converts a directed graph to an undirected graph by either adding edges to
+make them mutual or balancing the weights of mutual edges that aren't already
+equal.
+
+The `merge` function is used to determine the weight of edges in cases where
+mutual edges with differing weights already exist. If not provide the default
+method is to use the highest of the two edge weights (`(a, b) => Math.max(a,
+b)`).
+
+| Function | Type |
+| ---------- | ---------- |
+| `makeUndirected` | `(graph: Graph, merge?: (a: number, b: number) => number) => Graph` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "B"]);
+//=> Graph { "A" -> "B", "C" }
+
+graph = makeUndirected(graph);
+//=> Graph { "A" <-> "B", "C" }
+```
+
+
+### order
+
+Returns the number of vertices in the graph.
+
+Also see:
+
+- [size](#size)
+
+| Function | Type |
+| ---------- | ---------- |
+| `order` | `(graph: Graph) => number` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+order(graph);
+//=> 3
+```
+
+
+### outdegree
+
+Returns the [outdegree](https://en.wikipedia.org/wiki/Outdegree) for the
+given vertex.
+
+By default `weighted` is `false`, if set to `true` the result will be the sum
+of the edge weights (which could be zero or a negative value).
+
+Also see:
+
+- [degree](#degree)
+- [indegree](#indegree)
+
+| Function | Type |
+| ---------- | ---------- |
+| `outdegree` | `(graph: Graph, vertex: T2, options?: { weighted?: boolean or undefined; } or undefined) => number` |
+
+### parents
+
+Returns all the vertices that are parents of the given vertex (i.e. there is
+an edge starting at the parent vertex going to the given vertex).
+
+**Note**: If there is an edge that both starts and ends at the given vertex,
+it will be considered a parent of itself and included in the result.
+
+Also see:
+
+- [ancestors](#ancestors)
+- [children](#children)
+
+| Function | Type |
+| ---------- | ---------- |
+| `parents` | `(graph: Graph, vertex: T2) => Set` |
+
+### removeEdge
+
+Removes an edge from a graph.
+
+**Note**: `removeEdge(graph, edge)` is equivalent to `setEdge(graph, edge, 0)`.
+
+Also see:
+
+- [addEdge](#addEdge)
+- [getEdge](#getEdge)
+- [setEdge](#setEdge)
+
+| Function | Type |
+| ---------- | ---------- |
+| `removeEdge` | `(graph: Graph, [u, v]: [T2, T2], options?: { undirected?: boolean or undefined; } or undefined) => Graph` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "B"]);
+//=> Graph { "A" -> "B", "C" }
+
+graph = removeEdge(graph, ["A", "B"]);
+//=> Graph { "A", "B", "C" }
+```
+
+
+### removeVertex
+
+Removes a vertex from a graph.
+
+Also see:
+
+- [addVertex](#addVertex)
+
+| Function | Type |
+| ---------- | ---------- |
+| `removeVertex` | `(graph: Graph, vertex: T2) => Graph>` |
+
+### setEdge
+
+Set the weight of the given edge.
+
+**Note**: `setEdge(graph, edge, 1)` is equivalent to `addEdge(graph, edge)`
+and `setEdge(graph, edge, 0)` is equivalent to `removeEdge(graph, edge)`.
+
+Also see:
+
+- [addEdge](#addEdge)
+- [getEdge](#getEdge)
+- [removeEdge](#removeEdge)
+
+| Function | Type |
+| ---------- | ---------- |
+| `setEdge` | `(graph: Graph, [u, v]: Edge, weight: number, options?: { undirected?: boolean or undefined; } or undefined) => Graph` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = setEdge(graph, ["A", "B"], 1);
+//=> Graph { "A" -> "B", "C" }
+
+graph = setEdge(graph, ["A", "B"], 0);
+//=> Graph { "A", "B", "C" }
+```
+
+
+### size
+
+Returns the number of edges in the graph.
+
+Also see:
+
+- [order](#order)
+
+| Function | Type |
+| ---------- | ---------- |
+| `size` | `(graph: Graph, options?: { undirected?: boolean or undefined; } or undefined) => number` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "B"]);
+//=> Graph { "A" -> "B", "C" }
+
+graph = addEdge(graph, ["B", "C"]);
+//=> Graph { "A" -> "B", "B" -> "C" }
+
+size(graph);
+//=> 2
+```
+
+
+### toD3
+
+Converts a graph from a [Graph](#Graph) representation into a [D3Graph](#D3Graph) representation.
+
+Edges with a weight of 2 or greater will result in multiple links being generated in the D3Graph.
+
+Also see:
+
+- [fromD3](#fromD3)
+
+| Function | Type |
+| ---------- | ---------- |
+| `toD3` | `(graph: Graph, options?: { undirected?: boolean or undefined; } or undefined) => D3Graph` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = setEdge(graph, ["A", "B"], 1);
+//=> Graph { "A" -> "B", "C" }
+
+graph = setEdge(graph, ["A", "C"], 2);
+//=> Graph { "A" -> "B", "A" -> "C" }
+
+toD3(graph);
+//=> {
+// nodes: [{ id: "A" }, { id: "B" }, { id: "C" }],
+// links: [
+// { source: "A", target: "B" },
+// { source: "A", target: "C" },
+// { source: "A", target: "C" },
+// ],
+// }
+```
+
+
+### toDirected
+
+Converts an undirected graph to a directed graph by converting reciprocal
+edges to a single directed edge.
+
+| Function | Type |
+| ---------- | ---------- |
+| `toDirected` | `(graph: Graph) => Graph` |
+
+### topologicalSort
+
+Given a [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph), returns
+an array of the graph's vertices sorted using a [topological
+sort](https://en.wikipedia.org/wiki/Topological_sorting).
+
+**Note**: If the given graph contains cycles (checked with
+[isCyclic](#isCyclic)), an error will be thrown.
+
+| Function | Type |
+| ---------- | ---------- |
+| `topologicalSort` | `(graph: Graph) => string[]` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "C"]);
+//=> Graph { "A" -> "C", "B" }
+
+graph = addEdge(graph, ["C", "B"]);
+//=> Graph { "A" -> "C", "C" -> "B" }
+
+topologicalSort(graph);
+//=> ["A", "C", "B"]
+```
+
+
+### transpose
+
+Flips the orientation of all edges in a directed graph.
+
+| Function | Type |
+| ---------- | ---------- |
+| `transpose` | `(graph: Graph) => Graph` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+graph = addEdge(graph, ["A", "B"]);
+//=> Graph { "A" -> "B", "C" }
+
+graph = addEdge(graph, ["B", "C"]);
+//=> Graph { "A" -> "B", "B" -> "C" }
+
+transpose(graph);
+//=> Graph { "B" -> "A", "C" -> "B" }
+```
+
+
+### vertices
+
+Returns the vertices in the graph.
+
+| Function | Type |
+| ---------- | ---------- |
+| `vertices` | `(graph: Graph) => Set` |
+
+### vertexPairs
+
+Returns a list of all pairs of vertices in the graph irrespective of the edges present in the graph.
+
+| Function | Type |
+| ---------- | ---------- |
+| `vertexPairs` | `(graph: Graph) => Set>` |
+
+Examples:
+
+```js
+let graph = create(["A", "B", "C"]);
+//=> Graph { "A", "B", "C" }
+
+vertexPairs(graph);
+//=> Set { ["A", "A"], ["A", "B"], ["A", "C"], ["B", "B"], ["B", "C"], ["C", "C"] }
+```
+
+
+
+
+## Types
+
+- [D3Graph](#d3graph)
+- [Edge](#edge)
+- [Graph](#graph)
+
+### D3Graph
+
+A graph in a representation convenient for using with [D3.js force-directed
+graphs](https://github.com/d3/d3-force).
+
+| Type | Type |
+| ---------- | ---------- |
+| `D3Graph` | `{ nodes: Array<{ id: T; }>; links: Array<{ source: T; target: T; }>; }` |
+
+### Edge
+
+A connection between two vertices in a graph.
+([Wikipedia]())
+
+| Type | Type |
+| ---------- | ---------- |
+| `Edge` | `[T, T]` |
+
+### Graph
+
+A graph [adjacency matrix](https://en.wikipedia.org/wiki/Adjacency_matrix)
+where each number in the matrix describes the edge from vertex `u` to vertex
+`v`. By default, a value of `1` is used to indicate there is a edge between
+the two vertices, but any value other than `0` can be used to signify the
+presence of an edge (i.e. a weighted graph). ```
+
+| Type | Type |
+| ---------- | ---------- |
+| `Graph` | `Record>` |
+
+
+
diff --git a/packages/graph-fns/logo.png b/packages/graph-fns/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..6788daa7c38183072889ba3de76044a26489a3fe
GIT binary patch
literal 16517
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
E0P)QD+W-In
literal 0
HcmV?d00001
diff --git a/packages/graph-fns/package.json b/packages/graph-fns/package.json
new file mode 100644
index 0000000..11efbee
--- /dev/null
+++ b/packages/graph-fns/package.json
@@ -0,0 +1,27 @@
+{
+ "devDependencies": {
+ "tsdoc-markdown": "^1.4.1"
+ },
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "files": ["CHANGELOG.md", "dist", "LICENSE", "logo.png", "README.md"],
+ "keywords": ["graph", "utility", "graph theory", "networks"],
+ "license": "MIT",
+ "main": "./dist/index.js",
+ "name": "graph-fns",
+ "private": false,
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/haydn/fns.git"
+ },
+ "scripts": {
+ "docs": "tsdoc --src=src/index.ts --types --noemoji",
+ "publish": "(npm view $(npm view ./ name)@$(npm view ./ version) > /dev/null 2>&1) && (echo \"This version has already been published. Skipping.\") || (bun pm pack --filename package.tgz && npm publish ../../package.tgz --access public --tag=latest)"
+ },
+ "type": "module",
+ "version": "0.4.0"
+}
diff --git a/packages/graph-fns/screenshot.png b/packages/graph-fns/screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..7486b28e27d449675a13041c7b8a093e93c8d58b
GIT binary patch
literal 74826
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