@mark1russell7/collections 0.6.0 → 0.7.1

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 CHANGED
@@ -11,17 +11,21 @@ npm i @mark1russell7/collections
11
11
  ## Lists, maps and behaviors
12
12
 
13
13
  ```ts
14
+ import assert from "node:assert/strict";
14
15
  import { arrayList, compose, hashMap, lruMap } from "@mark1russell7/collections";
15
16
 
16
17
  const list = arrayList<number>();
17
18
  list.add(1);
18
19
  list.add(2);
20
+ assert.deepEqual(list.toArray(), [1, 2]);
19
21
 
20
22
  const cache = compose(lruMap<string, number>({ capacity: 2 }))(hashMap<string, number>());
21
23
  cache.set("a", 1);
22
24
  cache.set("b", 2);
23
25
  cache.set("c", 3);
24
- cache.has("a"); // false: the map removed the oldest key
26
+ // The map removed the oldest key
27
+ assert.equal(cache.has("a"), false);
28
+ assert.equal(cache.get("c"), 3);
25
29
  ```
26
30
 
27
31
  ## The summary tree
@@ -29,6 +33,7 @@ cache.has("a"); // false: the map removed the oldest key
29
33
  A summary tree is a persistent sequence. Each node keeps the sum of its leaves in a monoid. Each change gives a new tree in logarithmic time, and the old tree stays the same.
30
34
 
31
35
  ```ts
36
+ import assert from "node:assert/strict";
32
37
  import { summaryTree, type SummarySpec } from "@mark1russell7/collections";
33
38
 
34
39
  const text: SummarySpec<string, string> = {
@@ -36,7 +41,7 @@ const text: SummarySpec<string, string> = {
36
41
  measure: (leaf) => leaf,
37
42
  };
38
43
  const tree = summaryTree(text, ["a", "b", "c"]);
39
- tree.summary; // "abc"
44
+ assert.equal(tree.summary, "abc");
40
45
  ```
41
46
 
42
47
  ## Dependency graphs
@@ -44,23 +49,37 @@ tree.summary; // "abc"
44
49
  The entry `./dag` gives the levels of a graph, and a run of each level with a limit of concurrency.
45
50
 
46
51
  ```ts
52
+ import assert from "node:assert/strict";
47
53
  import { buildLeveledDAG, getTopologicalOrder } from "@mark1russell7/collections/dag";
48
54
 
49
55
  const nodes = new Map([
50
56
  ["app", { id: "app", dependencies: ["lib"] }],
51
57
  ["lib", { id: "lib", dependencies: [] }],
52
58
  ]);
53
- getTopologicalOrder(buildLeveledDAG(nodes)).map((node) => node.id); // ["lib", "app"]
59
+ assert.deepEqual(getTopologicalOrder(buildLeveledDAG(nodes)).map((node) => node.id), ["lib", "app"]);
54
60
  ```
55
61
 
56
62
  The entry `./graph` gives `hubBreadthFirstOrder`: the order of a weighted graph, from its largest hub, with no other imports.
57
63
 
58
64
  ```ts
65
+ import assert from "node:assert/strict";
59
66
  import { hubBreadthFirstOrder } from "@mark1russell7/collections/graph";
60
67
 
61
- hubBreadthFirstOrder(3, [{ a: 0, b: 1 }, { a: 1, b: 2 }]); // [1, 0, 2]
68
+ assert.deepEqual(hubBreadthFirstOrder(3, [{ a: 0, b: 1 }, { a: 1, b: 2 }]), [1, 0, 2]);
62
69
  ```
63
70
 
71
+ ## Guides
72
+
73
+ Each guide has examples, and a test runs each example:
74
+
75
+ - [Lists](https://github.com/mark1russell7/collections/blob/main/docs/guides/lists.md): `arrayList` and `linkedList`
76
+ - [Maps and sets](https://github.com/mark1russell7/collections/blob/main/docs/guides/maps-and-sets.md): hash maps, tree maps, linked maps, and their sets
77
+ - [Queues](https://github.com/mark1russell7/collections/blob/main/docs/guides/queues.md): `arrayDeque` and `priorityQueue`
78
+ - [Dependency graphs](https://github.com/mark1russell7/collections/blob/main/docs/guides/dag.md): levels, the topological order, and a run with `executeDAG`
79
+ - [Graph orders](https://github.com/mark1russell7/collections/blob/main/docs/guides/graph.md): `hubBreadthFirstOrder`
80
+ - [Layers](https://github.com/mark1russell7/collections/blob/main/docs/guides/layers.md): `compose`, its types and its laws, and behaviors on a collection
81
+ - [Behaviors](https://github.com/mark1russell7/collections/blob/main/docs/guides/behaviors.md): readonly views, safe reads and the locks
82
+
64
83
  ## Contract checks
65
84
 
66
85
  The entry `./testing` gives the small checks of a contract suite: `holds`, `same`, `rejects`, `deepEqual`, `show` and `ContractViolation`. These checks import no test framework.
@@ -1 +1 @@
1
- {"version":3,"file":"layers.js","names":[],"sources":["../../src/core/layers.ts"],"sourcesContent":["/**\n * This module holds the one fold of a stack of layers. A layer wraps the next thing, so a stack\n * of layers wraps a base. The next thing can be a collection, a function or any other value.\n *\n * This file has no imports. A package that needs only the fold loads this one file.\n *\n * @example\n * const parse = compose(\n * (next: (text: string) => number) => (text: string) => next(text.trim()),\n * (next: (text: string) => number) => (text: string) => next(text) * 2\n * )(Number);\n * parse(\" 21 \"); // 42\n */\n\n/**\n * A layer takes the next thing and gives a wrapped thing.\n *\n * @template Inner The type that the layer wraps\n * @template Outer The type that the layer gives\n */\nexport type Layer<Inner = any, Outer = Inner> = (next: Inner) => Outer;\n\n/** This type gives the type that a layer wraps. */\nexport type InnerOf<L> = L extends (next: infer I) => any ? I : never;\n\n/** This type gives the type that a layer gives. */\nexport type OuterOf<L> = L extends (next: any) => infer O ? O : never;\n\n/**\n * This type checks each neighbor pair of a list of layers. The result of the inner neighbor must\n * fit the input of the outer neighbor. At the first bad pair, the compiler marks that argument.\n *\n * The type is tail recursive, so the compiler accepts a chain of 100 layers.\n */\nexport type Chain<L extends readonly unknown[], Done extends readonly unknown[] = readonly []> =\n L extends readonly [infer A, infer B, ...infer R]\n ? [OuterOf<B>] extends [InnerOf<A>]\n ? Chain<readonly [B, ...R], readonly [...Done, A]>\n : readonly [...Done, A, Layer<any, InnerOf<A>>, ...R]\n : readonly [...Done, ...L];\n\n/**\n * This type gives the function that {@link compose} returns. Its input is the input of the last\n * layer, and its result is the result of the first layer. No layers give the identity function.\n * A list that the compiler cannot see as a tuple gives the types of one element.\n */\nexport type Composed<L extends readonly unknown[]> =\n L extends readonly []\n ? <B>(base: B) => B\n : L extends readonly [infer First, ...unknown[]]\n ? L extends readonly [...unknown[], infer Last]\n ? (base: InnerOf<Last>) => OuterOf<First>\n : never\n : L extends readonly (infer E)[]\n ? (base: InnerOf<E>) => OuterOf<E>\n : never;\n\n/** This type gives the intersection of the results of a list of layers. */\nexport type Outers<L extends readonly unknown[], Sum = unknown> =\n L extends readonly [infer First, ...infer Rest] ? Outers<Rest, Sum & OuterOf<First>> : Sum;\n\n/**\n * This type gives the function for layers that keep each capability of the layers under them.\n * Its result has the result of each layer and the base. Use it only when that law holds.\n */\nexport type Stacked<L extends readonly unknown[]> =\n L extends readonly [...unknown[], infer Last]\n ? (base: InnerOf<Last>) => Outers<L> & InnerOf<Last>\n : Composed<L>;\n\n/**\n * This function folds layers into one function that wraps a base. The first layer is the outermost.\n *\n * The fold holds four laws:\n * - Identity: `compose()(b)` is `b`.\n * - One layer: `compose(f)(b)` is `f(b)`.\n * - Associativity: `compose(f, compose(g, h))` equals `compose(compose(f, g), h)`.\n * - Order: `compose(f, g)(b)` is `f(g(b))`.\n *\n * @param layers The layers, from the outermost to the innermost\n * @returns A function that wraps a base in the layers\n */\nexport function compose<const L extends readonly Layer[]>(...layers: L & Chain<L>): Composed<L> {\n const list: readonly Layer[] = layers;\n return ((base: unknown) => list.reduceRight((acc, layer) => layer(acc), base)) as Composed<L>;\n}\n"],"mappings":";;;;;;;;;;;;;AAkFA,SAAgB,QAA0C,GAAG,QAAmC;CAC9F,MAAM,OAAyB;CAC/B,SAAS,SAAkB,KAAK,aAAa,KAAK,UAAU,MAAM,GAAG,GAAG,IAAI;AAC9E"}
1
+ {"version":3,"file":"layers.js","names":[],"sources":["../../src/core/layers.ts"],"sourcesContent":["/**\n * This module holds the one fold of a stack of layers. A layer wraps the next thing, so a stack\n * of layers wraps a base. The next thing can be a collection, a function or any other value.\n *\n * This file has no imports. A package that needs only the fold loads this one file.\n *\n * @example\n * const parse = compose(\n * (next: (text: string) => number) => (text: string) => next(text.trim()),\n * (next: (text: string) => number) => (text: string) => next(text) * 2\n * )(Number);\n * parse(\" 21 \"); // 42\n */\n\n/**\n * A layer takes the next thing and gives a wrapped thing.\n *\n * @template Inner The type that the layer wraps\n * @template Outer The type that the layer gives\n */\nexport type Layer<Inner = any, Outer = Inner> = (next: Inner) => Outer;\n\n/** This type gives the type that a layer wraps. */\nexport type InnerOf<L> = L extends (next: infer I) => any ? I : never;\n\n/** This type gives the type that a layer gives. */\nexport type OuterOf<L> = L extends (next: any) => infer O ? O : never;\n\n/**\n * This type checks each neighbor pair of a list of layers. The result of the inner neighbor must\n * fit the input of the outer neighbor. A bad pair makes the compiler refuse the call. The compiler\n * marks the first argument, not the bad pair. Its message shows the type that the first bad pair\n * needs. At the place of the inner neighbor, it needs a layer that gives the input of the outer one.\n *\n * The type is tail recursive, so the compiler accepts a chain of 100 layers.\n */\nexport type Chain<L extends readonly unknown[], Done extends readonly unknown[] = readonly []> =\n L extends readonly [infer A, infer B, ...infer R]\n ? [OuterOf<B>] extends [InnerOf<A>]\n ? Chain<readonly [B, ...R], readonly [...Done, A]>\n : readonly [...Done, A, Layer<any, InnerOf<A>>, ...R]\n : readonly [...Done, ...L];\n\n/**\n * This type gives the function that {@link compose} returns. Its input is the input of the last\n * layer, and its result is the result of the first layer. No layers give the identity function.\n * A list that the compiler cannot see as a tuple gives the types of one element.\n */\nexport type Composed<L extends readonly unknown[]> =\n L extends readonly []\n ? <B>(base: B) => B\n : L extends readonly [infer First, ...unknown[]]\n ? L extends readonly [...unknown[], infer Last]\n ? (base: InnerOf<Last>) => OuterOf<First>\n : never\n : L extends readonly (infer E)[]\n ? (base: InnerOf<E>) => OuterOf<E>\n : never;\n\n/** This type gives the intersection of the results of a list of layers. */\nexport type Outers<L extends readonly unknown[], Sum = unknown> =\n L extends readonly [infer First, ...infer Rest] ? Outers<Rest, Sum & OuterOf<First>> : Sum;\n\n/**\n * This type gives the function for layers that keep each capability of the layers under them.\n * Its result has the result of each layer and the base. Use it only when that law holds.\n */\nexport type Stacked<L extends readonly unknown[]> =\n L extends readonly [...unknown[], infer Last]\n ? (base: InnerOf<Last>) => Outers<L> & InnerOf<Last>\n : Composed<L>;\n\n/**\n * This function folds layers into one function that wraps a base. The first layer is the outermost.\n *\n * The fold holds four laws:\n * - Identity: `compose()(b)` is `b`.\n * - One layer: `compose(f)(b)` is `f(b)`.\n * - Associativity: `compose(f, compose(g, h))` equals `compose(compose(f, g), h)`.\n * - Order: `compose(f, g)(b)` is `f(g(b))`.\n *\n * @param layers The layers, from the outermost to the innermost\n * @returns A function that wraps a base in the layers\n */\nexport function compose<const L extends readonly Layer[]>(...layers: L & Chain<L>): Composed<L> {\n const list: readonly Layer[] = layers;\n return ((base: unknown) => list.reduceRight((acc, layer) => layer(acc), base)) as Composed<L>;\n}\n"],"mappings":";;;;;;;;;;;;;AAoFA,SAAgB,QAA0C,GAAG,QAAmC;CAC9F,MAAM,OAAyB;CAC/B,SAAS,SAAkB,KAAK,aAAa,KAAK,UAAU,MAAM,GAAG,GAAG,IAAI;AAC9E"}
@@ -36,7 +36,10 @@ interface DependencyDAG<TNode extends DAGNode = DAGNode> {
36
36
  interface DAGExecutionOptions<TNode extends DAGNode = DAGNode> {
37
37
  /** Max parallel operations per level (default: 4) */
38
38
  concurrency?: number;
39
- /** When this value is true, the run stops on the first error. The default is true. */
39
+ /**
40
+ * When this value is true, no level starts after a level with a failure. The default is true.
41
+ * Each node of the level with the failure runs, also a node that did not start before the failure.
42
+ */
40
43
  failFast?: boolean;
41
44
  /**
42
45
  * When this value is true, a node whose dependency failed or was skipped does not start. Its result has `skipped: true`.
@@ -119,9 +122,11 @@ export declare function visualizeDAG<TNode extends DAGNode>(dag: DependencyDAG<T
119
122
  * With `skipDependents` (the default), a node whose dependency failed or was skipped does not start. Its result has
120
123
  * `skipped: true` and an error that names those dependencies. A dependency outside the DAG does not count.
121
124
  *
122
- * `failFast` operates at level granularity. When a node in a level fails, each
123
- * node already in flight in that level continues to completion. No later level
124
- * starts. (There is no mid-level cancellation.)
125
+ * `failFast` operates on levels. When a node of a level fails, each node of that level continues to its end.
126
+ * This includes the nodes that did not start before the failure, when `concurrency` is less than the
127
+ * size of the level. No later level starts. The run does not cancel a node.
128
+ *
129
+ * A node that did not start (`skipped: true`) calls neither `onNodeStart` nor `onNodeComplete`.
125
130
  */
126
131
  export declare function executeDAG<TNode extends DAGNode>(dag: DependencyDAG<TNode>, processor: (node: TNode) => Promise<NodeResult<TNode>>, options?: DAGExecutionOptions<TNode>): Promise<DAGResult<TNode>>;
127
132
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/dag/types.ts","../../src/dag/traversal.ts","../../src/dag/executor.ts","../../src/dag/builder.ts"],"mappings":";;;;;;;;;UAUiB,QAAQ;;EAEvB;;EAEA;;EAEA;;EAEA,OAAO;;;;;UAMQ,cAAc,cAAc,UAAU;;EAErD,OAAO,YAAY;;EAEnB,QAAQ;;EAER;;EAEA;;;;;UAMe,oBAAoB,cAAc,UAAU;;EAE3D;;EAEA;;;;;EAKA;;EAEA,eAAe,MAAM;;EAErB,kBAAkB,QAAQ,WAAW;;;;;UAMtB,WAAW,cAAc,UAAU;;EAElD,MAAM;;EAEN;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;;EAEA;;;;;UAMe,UAAU,cAAc,UAAU;;EAEjD;;EAEA,SAAS,YAAY,WAAW;;EAEhC;;EAEA;;EAEA;;;;;KAMU,cAAc,cAAc,UAAU,YAChD,MAAM,UACH,QAAQ,WAAW;;;;;;;;;;;;wBC3ER,gBAAgB,cAAc,SAC5C,OAAO,YAAY,SAClB,cAAc;;;;wBA+ED,oBAAoB,cAAc,SAChD,KAAK,cAAc,SAClB;;;;wBAOa,aAAa,cAAc,SACzC,KAAK,cAAc;;;;;;;;;;;;;;;;;;wBClCC,WAAW,cAAc,SAC7C,KAAK,cAAc,QACnB,YAAY,MAAM,UAAU,QAAQ,WAAW,SAC/C,UAAS,oBAAoB,SAC5B,QAAQ,UAAU;;;;wBAwFC,qBAAqB,cAAc,SACvD,KAAK,cAAc,QACnB,YAAY,MAAM,UAAU,QAAQ,WAAW,SAC/C,UAAS,KAAK,oBAAoB,yBACjC,QAAQ,UAAU;;;;wBAOL,gBAAgB,cAAc,SAC5C,KAAK,MAAM,UAAU,iBACnB,MAAM,UAAU,QAAQ,WAAW;;;;wBC7KvB,kBAAkB,cAAc,SAC9C,OAAO,YAAY,QACnB,iBACC,YAAY;;;;wBAyBC,aAAa,cAAc,SACzC,OAAO,YAAY,QACnB,aACC;;;;wBA0Ba,eAAe,cAAc,SAC3C,OAAO,YAAY,QACnB,aACC;;;;wBAmCa,WAAW,iBACzB,YACA,yBACA,OAAO,oBACN,QAAQ;;;;wBAWK,aAAa,cAAc,SACzC,OAAO,UACN,YAAY"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/dag/types.ts","../../src/dag/traversal.ts","../../src/dag/executor.ts","../../src/dag/builder.ts"],"mappings":";;;;;;;;;UAUiB,QAAQ;;EAEvB;;EAEA;;EAEA;;EAEA,OAAO;;;;;UAMQ,cAAc,cAAc,UAAU;;EAErD,OAAO,YAAY;;EAEnB,QAAQ;;EAER;;EAEA;;;;;UAMe,oBAAoB,cAAc,UAAU;;EAE3D;;;;;EAKA;;;;;EAKA;;EAEA,eAAe,MAAM;;EAErB,kBAAkB,QAAQ,WAAW;;;;;UAMtB,WAAW,cAAc,UAAU;;EAElD,MAAM;;EAEN;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;;EAEA;;;;;UAMe,UAAU,cAAc,UAAU;;EAEjD;;EAEA,SAAS,YAAY,WAAW;;EAEhC;;EAEA;;EAEA;;;;;KAMU,cAAc,cAAc,UAAU,YAChD,MAAM,UACH,QAAQ,WAAW;;;;;;;;;;;;wBC9ER,gBAAgB,cAAc,SAC5C,OAAO,YAAY,SAClB,cAAc;;;;wBA+ED,oBAAoB,cAAc,SAChD,KAAK,cAAc,SAClB;;;;wBAOa,aAAa,cAAc,SACzC,KAAK,cAAc;;;;;;;;;;;;;;;;;;;;wBChCC,WAAW,cAAc,SAC7C,KAAK,cAAc,QACnB,YAAY,MAAM,UAAU,QAAQ,WAAW,SAC/C,UAAS,oBAAoB,SAC5B,QAAQ,UAAU;;;;wBAwFC,qBAAqB,cAAc,SACvD,KAAK,cAAc,QACnB,YAAY,MAAM,UAAU,QAAQ,WAAW,SAC/C,UAAS,KAAK,oBAAoB,yBACjC,QAAQ,UAAU;;;;wBAOL,gBAAgB,cAAc,SAC5C,KAAK,MAAM,UAAU,iBACnB,MAAM,UAAU,QAAQ,WAAW;;;;wBC/KvB,kBAAkB,cAAc,SAC9C,OAAO,YAAY,QACnB,iBACC,YAAY;;;;wBAyBC,aAAa,cAAc,SACzC,OAAO,YAAY,QACnB,aACC;;;;wBA0Ba,eAAe,cAAc,SAC3C,OAAO,YAAY,QACnB,aACC;;;;wBAmCa,WAAW,iBACzB,YACA,yBACA,OAAO,oBACN,QAAQ;;;;wBAWK,aAAa,cAAc,SACzC,OAAO,UACN,YAAY"}
@@ -125,9 +125,11 @@ function notify(name, listener, arg) {
125
125
  * With `skipDependents` (the default), a node whose dependency failed or was skipped does not start. Its result has
126
126
  * `skipped: true` and an error that names those dependencies. A dependency outside the DAG does not count.
127
127
  *
128
- * `failFast` operates at level granularity. When a node in a level fails, each
129
- * node already in flight in that level continues to completion. No later level
130
- * starts. (There is no mid-level cancellation.)
128
+ * `failFast` operates on levels. When a node of a level fails, each node of that level continues to its end.
129
+ * This includes the nodes that did not start before the failure, when `concurrency` is less than the
130
+ * size of the level. No later level starts. The run does not cancel a node.
131
+ *
132
+ * A node that did not start (`skipped: true`) calls neither `onNodeStart` nor `onNodeComplete`.
131
133
  */
132
134
  async function executeDAG(dag, processor, options = {}) {
133
135
  const { concurrency = 4, failFast = true, skipDependents = true, onNodeStart, onNodeComplete } = options;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/dag/traversal.ts","../../src/dag/executor.ts","../../src/dag/builder.ts"],"sourcesContent":["/**\n * DAG Traversal using Kahn's Algorithm\n *\n * This module implements topological sort with level assignment for parallel execution.\n * Level 0 = leaves (no dependencies), higher levels depend on lower levels.\n */\n\nimport type { DAGNode, DependencyDAG } from \"./types.js\";\n\n/**\n * Build a leveled DAG from nodes using Kahn's algorithm\n *\n * This assigns levels to each node where:\n * - Level 0: Nodes with no dependencies (leaves)\n * - Level N: Nodes whose dependencies are all at level < N\n *\n * Nodes at the same level can be processed in parallel.\n *\n * @throws Error if a cycle is detected\n */\nexport function buildLeveledDAG<TNode extends DAGNode>(\n nodes: Map<string, TNode>\n): DependencyDAG<TNode> {\n const inDegree = new Map<string, number>();\n const dependents = new Map<string, Set<string>>();\n\n for (const [name, node] of nodes) {\n // A set: a dependency that a node lists twice counts once. (`dependents` is a set, so it\n // takes the in-degree down once: a repeated dependency was \"Circular\": deep dive DATA-21.)\n const depsInDag = new Set(node.dependencies.filter((dep) => nodes.has(dep)));\n inDegree.set(name, depsInDag.size);\n dependents.set(name, new Set());\n }\n\n for (const [name, node] of nodes) {\n for (const dep of node.dependencies) {\n if (dependents.has(dep)) {\n dependents.get(dep)!.add(name);\n }\n }\n }\n\n let queue: string[] = [];\n for (const [name, degree] of inDegree) {\n if (degree === 0) {\n queue.push(name);\n }\n }\n\n const levels: TNode[][] = [];\n const nodeLevel = new Map<string, number>();\n let processedCount = 0;\n\n while (queue.length > 0) {\n const currentLevel = levels.length;\n const levelNodes: TNode[] = [];\n const nextQueue: string[] = [];\n\n for (const name of queue) {\n const node = nodes.get(name)!;\n node.level = currentLevel;\n nodeLevel.set(name, currentLevel);\n levelNodes.push(node);\n processedCount++;\n\n for (const dependent of dependents.get(name) ?? []) {\n const newDegree = inDegree.get(dependent)! - 1;\n inDegree.set(dependent, newDegree);\n\n if (newDegree === 0) {\n nextQueue.push(dependent);\n }\n }\n }\n\n levels.push(levelNodes);\n queue = nextQueue;\n }\n\n if (processedCount !== nodes.size) {\n const cycleNodes = [...nodes.keys()].filter((n) => !nodeLevel.has(n));\n throw new Error(\"Circular dependency detected involving: \" + cycleNodes.join(\", \"));\n }\n\n const roots = [...nodes.keys()].filter((name) => {\n const deps = dependents.get(name);\n return !deps || deps.size === 0;\n });\n\n const leaves = [...nodes.keys()].filter((name) => {\n const node = nodes.get(name)!;\n const depsInDag = node.dependencies.filter((dep) => nodes.has(dep));\n return depsInDag.length === 0;\n });\n\n return { nodes, levels, roots, leaves };\n}\n\n/**\n * Get the topological order (flat list, leaves first)\n */\nexport function getTopologicalOrder<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>\n): TNode[] {\n return dag.levels.flat();\n}\n\n/**\n * Visualize the DAG structure (for debugging)\n */\nexport function visualizeDAG<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>\n): string {\n const lines = [\"DAG Structure:\"];\n\n for (let i = 0; i < dag.levels.length; i++) {\n const level = dag.levels[i]!;\n lines.push(\" Level \" + i + \":\");\n for (const node of level) {\n const deps =\n node.dependencies.length > 0\n ? \" -> [\" + node.dependencies.join(\", \") + \"]\"\n : \" (leaf)\";\n lines.push(\" - \" + node.id + deps);\n }\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * DAG Executor with parallel level-based execution\n */\n\nimport type {\n DependencyDAG,\n DAGNode,\n DAGExecutionOptions,\n NodeResult,\n DAGResult,\n} from \"./types.js\";\n\n/**\n * Start a batch of items with concurrency limit\n */\nasync function executeWithConcurrency<T, R>(\n items: T[],\n fn: (item: T) => Promise<R>,\n limit: number\n): Promise<R[]> {\n const results: R[] = [];\n const executing = new Set<Promise<void>>();\n\n for (const item of items) {\n const promise = fn(item)\n .then((result) => {\n results.push(result);\n })\n .finally(() => {\n // Rejection-safe cleanup: always remove the settled promise from the\n // executing set, even if `fn` rejected, so the set never leaks a\n // settled (possibly rejected) promise into later `race`/`all` waits.\n executing.delete(promise);\n });\n\n executing.add(promise);\n\n if (executing.size >= limit) {\n await Promise.race(executing);\n }\n }\n\n await Promise.all(executing);\n return results;\n}\n\n/**\n * Call a listener. A listener that throws does not stop the run: the function gives a log\n * line for its error (deep dive DATA-21).\n */\nfunction notify<T>(name: string, listener: ((arg: T) => void) | undefined, arg: T): string | undefined {\n if (!listener) return undefined;\n try {\n listener(arg);\n return undefined;\n } catch (error) {\n return name + \" threw: \" + (error instanceof Error ? error.message : String(error));\n }\n}\n\n/**\n * Start DAG with level-based parallelization\n *\n * - Levels are processed sequentially (level 0 first, then level 1, and so on)\n * - Within each level, nodes are processed in parallel up to concurrency limit\n * - Supports fail-fast or continue-on-error modes\n *\n * A processor that throws does NOT reject the whole run. The run records the throw\n * as a failed NodeResult, so partial results are always given.\n *\n * With `skipDependents` (the default), a node whose dependency failed or was skipped does not start. Its result has\n * `skipped: true` and an error that names those dependencies. A dependency outside the DAG does not count.\n *\n * `failFast` operates at level granularity. When a node in a level fails, each\n * node already in flight in that level continues to completion. No later level\n * starts. (There is no mid-level cancellation.)\n */\nexport async function executeDAG<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>,\n processor: (node: TNode) => Promise<NodeResult<TNode>>,\n options: DAGExecutionOptions<TNode> = {}\n): Promise<DAGResult<TNode>> {\n const {\n concurrency = 4,\n failFast = true,\n skipDependents = true,\n onNodeStart,\n onNodeComplete,\n } = options;\n\n const results = new Map<string, NodeResult<TNode>>();\n const failedNodes: string[] = [];\n const skippedNodes: string[] = [];\n // The ids of the nodes that failed or were skipped\n const blocked = new Set<string>();\n const startTime = Date.now();\n let shouldStop = false;\n\n for (const level of dag.levels) {\n if (shouldStop) break;\n\n const runnable: TNode[] = [];\n for (const node of level) {\n const blockers = skipDependents ? node.dependencies.filter((dep) => blocked.has(dep)) : [];\n if (blockers.length === 0) {\n runnable.push(node);\n continue;\n }\n const message = \"Skipped \" + node.id + \": the dependencies \" + blockers.join(\", \") + \" failed or were skipped\";\n results.set(node.id, { node, success: false, skipped: true, error: new Error(message), duration: 0, logs: [message] });\n skippedNodes.push(node.id);\n blocked.add(node.id);\n }\n\n const levelResults = await executeWithConcurrency(\n runnable,\n async (node) => {\n const startError = notify(\"onNodeStart\", onNodeStart, node);\n const nodeStart = Date.now();\n let result: NodeResult<TNode>;\n try {\n result = await processor(node);\n } catch (error) {\n // Convert a thrown processor error into a failed NodeResult instead\n // of rejecting the entire run and discarding partial results.\n const err =\n error instanceof Error ? error : new Error(String(error));\n result = {\n node,\n success: false,\n error: err,\n duration: Date.now() - nodeStart,\n logs: [\"Processor threw: \" + err.message],\n };\n }\n const completeError = notify(\"onNodeComplete\", onNodeComplete, result);\n const listenerErrors = [startError, completeError].filter((line): line is string => line !== undefined);\n if (listenerErrors.length > 0) {\n result = { ...result, logs: [...(result.logs ?? []), ...listenerErrors] };\n }\n return result;\n },\n concurrency\n );\n\n for (const result of levelResults) {\n results.set(result.node.id, result);\n if (!result.success) {\n failedNodes.push(result.node.id);\n blocked.add(result.node.id);\n if (failFast) {\n shouldStop = true;\n }\n }\n }\n }\n\n return {\n success: failedNodes.length === 0 && skippedNodes.length === 0,\n results,\n failedNodes,\n skippedNodes,\n totalDuration: Date.now() - startTime,\n };\n}\n\n/**\n * Start DAG sequentially (no parallelization)\n */\nexport async function executeDAGSequential<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>,\n processor: (node: TNode) => Promise<NodeResult<TNode>>,\n options: Omit<DAGExecutionOptions<TNode>, \"concurrency\"> = {}\n): Promise<DAGResult<TNode>> {\n return executeDAG(dag, processor, { ...options, concurrency: 1 });\n}\n\n/**\n * Make a simple processor that wraps an async function\n */\nexport function createProcessor<TNode extends DAGNode>(\n fn: (node: TNode) => Promise<void>\n): (node: TNode) => Promise<NodeResult<TNode>> {\n return async (node: TNode): Promise<NodeResult<TNode>> => {\n const startTime = Date.now();\n const logs: string[] = [];\n\n try {\n logs.push(\"Starting \" + node.id);\n await fn(node);\n logs.push(\"Completed \" + node.id);\n\n return {\n node,\n success: true,\n duration: Date.now() - startTime,\n logs,\n };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logs.push(\"Failed \" + node.id + \": \" + err.message);\n\n return {\n node,\n success: false,\n error: err,\n duration: Date.now() - startTime,\n logs,\n };\n }\n };\n}\n","/**\n * DAG Builder utilities\n */\n\nimport type { DAGNode } from \"./types.js\";\n\n/**\n * Filter DAG to only include nodes reachable from a root\n */\nexport function filterDAGFromRoot<TNode extends DAGNode>(\n nodes: Map<string, TNode>,\n rootId: string\n): Map<string, TNode> {\n const filtered = new Map<string, TNode>();\n const visited = new Set<string>();\n\n function visit(id: string): void {\n if (visited.has(id)) return;\n visited.add(id);\n\n const node = nodes.get(id);\n if (!node) return;\n\n filtered.set(id, node);\n\n for (const dep of node.dependencies) {\n visit(dep);\n }\n }\n\n visit(rootId);\n return filtered;\n}\n\n/**\n * Get all ancestors of a node (nodes that this node depends on, transitively)\n */\nexport function getAncestors<TNode extends DAGNode>(\n nodes: Map<string, TNode>,\n id: string\n): Set<string> {\n const ancestors = new Set<string>();\n const visited = new Set<string>();\n\n function visit(n: string): void {\n if (visited.has(n)) return;\n visited.add(n);\n\n const node = nodes.get(n);\n if (!node) return;\n\n for (const dep of node.dependencies) {\n if (nodes.has(dep)) {\n ancestors.add(dep);\n visit(dep);\n }\n }\n }\n\n visit(id);\n return ancestors;\n}\n\n/**\n * Get all descendants of a node (nodes that depend on this node, transitively)\n */\nexport function getDescendants<TNode extends DAGNode>(\n nodes: Map<string, TNode>,\n id: string\n): Set<string> {\n const descendants = new Set<string>();\n const visited = new Set<string>();\n\n // Build reverse dependency map\n const dependents = new Map<string, Set<string>>();\n for (const [nodeId, node] of nodes) {\n for (const dep of node.dependencies) {\n if (!dependents.has(dep)) {\n dependents.set(dep, new Set());\n }\n dependents.get(dep)!.add(nodeId);\n }\n }\n\n function visit(n: string): void {\n if (visited.has(n)) return;\n visited.add(n);\n\n const deps = dependents.get(n);\n if (!deps) return;\n\n for (const dep of deps) {\n descendants.add(dep);\n visit(dep);\n }\n }\n\n visit(id);\n return descendants;\n}\n\n/**\n * Make a DAG node from minimal input\n */\nexport function createNode<TData = unknown>(\n id: string,\n dependencies: string[] = [],\n data?: TData | undefined\n): DAGNode<TData> {\n const node: DAGNode<TData> = { id, dependencies };\n if (data !== undefined) {\n node.data = data;\n }\n return node;\n}\n\n/**\n * Build a Map of nodes from an array\n */\nexport function buildNodeMap<TNode extends DAGNode>(\n nodes: TNode[]\n): Map<string, TNode> {\n return new Map(nodes.map((n) => [n.id, n]));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/dag/traversal.ts","../../src/dag/executor.ts","../../src/dag/builder.ts"],"sourcesContent":["/**\n * DAG Traversal using Kahn's Algorithm\n *\n * This module implements topological sort with level assignment for parallel execution.\n * Level 0 = leaves (no dependencies), higher levels depend on lower levels.\n */\n\nimport type { DAGNode, DependencyDAG } from \"./types.js\";\n\n/**\n * Build a leveled DAG from nodes using Kahn's algorithm\n *\n * This assigns levels to each node where:\n * - Level 0: Nodes with no dependencies (leaves)\n * - Level N: Nodes whose dependencies are all at level < N\n *\n * Nodes at the same level can be processed in parallel.\n *\n * @throws Error if a cycle is detected\n */\nexport function buildLeveledDAG<TNode extends DAGNode>(\n nodes: Map<string, TNode>\n): DependencyDAG<TNode> {\n const inDegree = new Map<string, number>();\n const dependents = new Map<string, Set<string>>();\n\n for (const [name, node] of nodes) {\n // A set: a dependency that a node lists twice counts once. (`dependents` is a set, so it\n // takes the in-degree down once: a repeated dependency was \"Circular\": deep dive DATA-21.)\n const depsInDag = new Set(node.dependencies.filter((dep) => nodes.has(dep)));\n inDegree.set(name, depsInDag.size);\n dependents.set(name, new Set());\n }\n\n for (const [name, node] of nodes) {\n for (const dep of node.dependencies) {\n if (dependents.has(dep)) {\n dependents.get(dep)!.add(name);\n }\n }\n }\n\n let queue: string[] = [];\n for (const [name, degree] of inDegree) {\n if (degree === 0) {\n queue.push(name);\n }\n }\n\n const levels: TNode[][] = [];\n const nodeLevel = new Map<string, number>();\n let processedCount = 0;\n\n while (queue.length > 0) {\n const currentLevel = levels.length;\n const levelNodes: TNode[] = [];\n const nextQueue: string[] = [];\n\n for (const name of queue) {\n const node = nodes.get(name)!;\n node.level = currentLevel;\n nodeLevel.set(name, currentLevel);\n levelNodes.push(node);\n processedCount++;\n\n for (const dependent of dependents.get(name) ?? []) {\n const newDegree = inDegree.get(dependent)! - 1;\n inDegree.set(dependent, newDegree);\n\n if (newDegree === 0) {\n nextQueue.push(dependent);\n }\n }\n }\n\n levels.push(levelNodes);\n queue = nextQueue;\n }\n\n if (processedCount !== nodes.size) {\n const cycleNodes = [...nodes.keys()].filter((n) => !nodeLevel.has(n));\n throw new Error(\"Circular dependency detected involving: \" + cycleNodes.join(\", \"));\n }\n\n const roots = [...nodes.keys()].filter((name) => {\n const deps = dependents.get(name);\n return !deps || deps.size === 0;\n });\n\n const leaves = [...nodes.keys()].filter((name) => {\n const node = nodes.get(name)!;\n const depsInDag = node.dependencies.filter((dep) => nodes.has(dep));\n return depsInDag.length === 0;\n });\n\n return { nodes, levels, roots, leaves };\n}\n\n/**\n * Get the topological order (flat list, leaves first)\n */\nexport function getTopologicalOrder<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>\n): TNode[] {\n return dag.levels.flat();\n}\n\n/**\n * Visualize the DAG structure (for debugging)\n */\nexport function visualizeDAG<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>\n): string {\n const lines = [\"DAG Structure:\"];\n\n for (let i = 0; i < dag.levels.length; i++) {\n const level = dag.levels[i]!;\n lines.push(\" Level \" + i + \":\");\n for (const node of level) {\n const deps =\n node.dependencies.length > 0\n ? \" -> [\" + node.dependencies.join(\", \") + \"]\"\n : \" (leaf)\";\n lines.push(\" - \" + node.id + deps);\n }\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * DAG Executor with parallel level-based execution\n */\n\nimport type {\n DependencyDAG,\n DAGNode,\n DAGExecutionOptions,\n NodeResult,\n DAGResult,\n} from \"./types.js\";\n\n/**\n * Start a batch of items with concurrency limit\n */\nasync function executeWithConcurrency<T, R>(\n items: T[],\n fn: (item: T) => Promise<R>,\n limit: number\n): Promise<R[]> {\n const results: R[] = [];\n const executing = new Set<Promise<void>>();\n\n for (const item of items) {\n const promise = fn(item)\n .then((result) => {\n results.push(result);\n })\n .finally(() => {\n // Rejection-safe cleanup: always remove the settled promise from the\n // executing set, even if `fn` rejected, so the set never leaks a\n // settled (possibly rejected) promise into later `race`/`all` waits.\n executing.delete(promise);\n });\n\n executing.add(promise);\n\n if (executing.size >= limit) {\n await Promise.race(executing);\n }\n }\n\n await Promise.all(executing);\n return results;\n}\n\n/**\n * Call a listener. A listener that throws does not stop the run: the function gives a log\n * line for its error (deep dive DATA-21).\n */\nfunction notify<T>(name: string, listener: ((arg: T) => void) | undefined, arg: T): string | undefined {\n if (!listener) return undefined;\n try {\n listener(arg);\n return undefined;\n } catch (error) {\n return name + \" threw: \" + (error instanceof Error ? error.message : String(error));\n }\n}\n\n/**\n * Start DAG with level-based parallelization\n *\n * - Levels are processed sequentially (level 0 first, then level 1, and so on)\n * - Within each level, nodes are processed in parallel up to concurrency limit\n * - Supports fail-fast or continue-on-error modes\n *\n * A processor that throws does NOT reject the whole run. The run records the throw\n * as a failed NodeResult, so partial results are always given.\n *\n * With `skipDependents` (the default), a node whose dependency failed or was skipped does not start. Its result has\n * `skipped: true` and an error that names those dependencies. A dependency outside the DAG does not count.\n *\n * `failFast` operates on levels. When a node of a level fails, each node of that level continues to its end.\n * This includes the nodes that did not start before the failure, when `concurrency` is less than the\n * size of the level. No later level starts. The run does not cancel a node.\n *\n * A node that did not start (`skipped: true`) calls neither `onNodeStart` nor `onNodeComplete`.\n */\nexport async function executeDAG<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>,\n processor: (node: TNode) => Promise<NodeResult<TNode>>,\n options: DAGExecutionOptions<TNode> = {}\n): Promise<DAGResult<TNode>> {\n const {\n concurrency = 4,\n failFast = true,\n skipDependents = true,\n onNodeStart,\n onNodeComplete,\n } = options;\n\n const results = new Map<string, NodeResult<TNode>>();\n const failedNodes: string[] = [];\n const skippedNodes: string[] = [];\n // The ids of the nodes that failed or were skipped\n const blocked = new Set<string>();\n const startTime = Date.now();\n let shouldStop = false;\n\n for (const level of dag.levels) {\n if (shouldStop) break;\n\n const runnable: TNode[] = [];\n for (const node of level) {\n const blockers = skipDependents ? node.dependencies.filter((dep) => blocked.has(dep)) : [];\n if (blockers.length === 0) {\n runnable.push(node);\n continue;\n }\n const message = \"Skipped \" + node.id + \": the dependencies \" + blockers.join(\", \") + \" failed or were skipped\";\n results.set(node.id, { node, success: false, skipped: true, error: new Error(message), duration: 0, logs: [message] });\n skippedNodes.push(node.id);\n blocked.add(node.id);\n }\n\n const levelResults = await executeWithConcurrency(\n runnable,\n async (node) => {\n const startError = notify(\"onNodeStart\", onNodeStart, node);\n const nodeStart = Date.now();\n let result: NodeResult<TNode>;\n try {\n result = await processor(node);\n } catch (error) {\n // Convert a thrown processor error into a failed NodeResult instead\n // of rejecting the entire run and discarding partial results.\n const err =\n error instanceof Error ? error : new Error(String(error));\n result = {\n node,\n success: false,\n error: err,\n duration: Date.now() - nodeStart,\n logs: [\"Processor threw: \" + err.message],\n };\n }\n const completeError = notify(\"onNodeComplete\", onNodeComplete, result);\n const listenerErrors = [startError, completeError].filter((line): line is string => line !== undefined);\n if (listenerErrors.length > 0) {\n result = { ...result, logs: [...(result.logs ?? []), ...listenerErrors] };\n }\n return result;\n },\n concurrency\n );\n\n for (const result of levelResults) {\n results.set(result.node.id, result);\n if (!result.success) {\n failedNodes.push(result.node.id);\n blocked.add(result.node.id);\n if (failFast) {\n shouldStop = true;\n }\n }\n }\n }\n\n return {\n success: failedNodes.length === 0 && skippedNodes.length === 0,\n results,\n failedNodes,\n skippedNodes,\n totalDuration: Date.now() - startTime,\n };\n}\n\n/**\n * Start DAG sequentially (no parallelization)\n */\nexport async function executeDAGSequential<TNode extends DAGNode>(\n dag: DependencyDAG<TNode>,\n processor: (node: TNode) => Promise<NodeResult<TNode>>,\n options: Omit<DAGExecutionOptions<TNode>, \"concurrency\"> = {}\n): Promise<DAGResult<TNode>> {\n return executeDAG(dag, processor, { ...options, concurrency: 1 });\n}\n\n/**\n * Make a simple processor that wraps an async function\n */\nexport function createProcessor<TNode extends DAGNode>(\n fn: (node: TNode) => Promise<void>\n): (node: TNode) => Promise<NodeResult<TNode>> {\n return async (node: TNode): Promise<NodeResult<TNode>> => {\n const startTime = Date.now();\n const logs: string[] = [];\n\n try {\n logs.push(\"Starting \" + node.id);\n await fn(node);\n logs.push(\"Completed \" + node.id);\n\n return {\n node,\n success: true,\n duration: Date.now() - startTime,\n logs,\n };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logs.push(\"Failed \" + node.id + \": \" + err.message);\n\n return {\n node,\n success: false,\n error: err,\n duration: Date.now() - startTime,\n logs,\n };\n }\n };\n}\n","/**\n * DAG Builder utilities\n */\n\nimport type { DAGNode } from \"./types.js\";\n\n/**\n * Filter DAG to only include nodes reachable from a root\n */\nexport function filterDAGFromRoot<TNode extends DAGNode>(\n nodes: Map<string, TNode>,\n rootId: string\n): Map<string, TNode> {\n const filtered = new Map<string, TNode>();\n const visited = new Set<string>();\n\n function visit(id: string): void {\n if (visited.has(id)) return;\n visited.add(id);\n\n const node = nodes.get(id);\n if (!node) return;\n\n filtered.set(id, node);\n\n for (const dep of node.dependencies) {\n visit(dep);\n }\n }\n\n visit(rootId);\n return filtered;\n}\n\n/**\n * Get all ancestors of a node (nodes that this node depends on, transitively)\n */\nexport function getAncestors<TNode extends DAGNode>(\n nodes: Map<string, TNode>,\n id: string\n): Set<string> {\n const ancestors = new Set<string>();\n const visited = new Set<string>();\n\n function visit(n: string): void {\n if (visited.has(n)) return;\n visited.add(n);\n\n const node = nodes.get(n);\n if (!node) return;\n\n for (const dep of node.dependencies) {\n if (nodes.has(dep)) {\n ancestors.add(dep);\n visit(dep);\n }\n }\n }\n\n visit(id);\n return ancestors;\n}\n\n/**\n * Get all descendants of a node (nodes that depend on this node, transitively)\n */\nexport function getDescendants<TNode extends DAGNode>(\n nodes: Map<string, TNode>,\n id: string\n): Set<string> {\n const descendants = new Set<string>();\n const visited = new Set<string>();\n\n // Build reverse dependency map\n const dependents = new Map<string, Set<string>>();\n for (const [nodeId, node] of nodes) {\n for (const dep of node.dependencies) {\n if (!dependents.has(dep)) {\n dependents.set(dep, new Set());\n }\n dependents.get(dep)!.add(nodeId);\n }\n }\n\n function visit(n: string): void {\n if (visited.has(n)) return;\n visited.add(n);\n\n const deps = dependents.get(n);\n if (!deps) return;\n\n for (const dep of deps) {\n descendants.add(dep);\n visit(dep);\n }\n }\n\n visit(id);\n return descendants;\n}\n\n/**\n * Make a DAG node from minimal input\n */\nexport function createNode<TData = unknown>(\n id: string,\n dependencies: string[] = [],\n data?: TData | undefined\n): DAGNode<TData> {\n const node: DAGNode<TData> = { id, dependencies };\n if (data !== undefined) {\n node.data = data;\n }\n return node;\n}\n\n/**\n * Build a Map of nodes from an array\n */\nexport function buildNodeMap<TNode extends DAGNode>(\n nodes: TNode[]\n): Map<string, TNode> {\n return new Map(nodes.map((n) => [n.id, n]));\n}\n"],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { i as Layer, s as Stacked, t as Chain } from "./layers.js";
2
+ import { Optional, Result } from "@mark1russell7/optional";
2
3
  /**
3
4
  * Core traits and capability interfaces for the collections framework.
4
5
  *
@@ -138,191 +139,6 @@ export interface Freezable<T> {
138
139
  /** This value gives true if this collection is frozen. */
139
140
  readonly isFrozen: boolean;
140
141
  }
141
- /**
142
- * Functional effect types for error handling and optional values.
143
- *
144
- * This module gives Option<T> and Result<T,E> types for explicit, type-safe
145
- * error handling without exceptions. This module includes async variants.
146
- */
147
- /**
148
- * This type represents the absence of a value.
149
- */
150
- export interface None {
151
- readonly _tag: "None";
152
- }
153
- /**
154
- * This type represents the presence of a value.
155
- */
156
- export interface Some<T> {
157
- readonly _tag: "Some";
158
- readonly value: T;
159
- }
160
- /**
161
- * Option<T> represents a value that is present or absent.
162
- * This is a type-safe alternative to null/undefined.
163
- *
164
- * @example
165
- * function findUser(id: string): Option<User> {
166
- * const user = db.get(id);
167
- * return user ? Some(user) : None;
168
- * }
169
- */
170
- export type Option<T> = None | Some<T>;
171
- /**
172
- * The singleton None value.
173
- */
174
- export declare const None: None;
175
- /**
176
- * This function constructs a Some value containing the given value.
177
- */
178
- export declare const Some: <T>(value: T) => Some<T>;
179
- /**
180
- * Type guard to check if an Option is None.
181
- */
182
- export declare const isNone: <T>(opt: Option<T>) => opt is None;
183
- /**
184
- * Type guard to check if an Option is Some.
185
- */
186
- export declare const isSome: <T>(opt: Option<T>) => opt is Some<T>;
187
- /**
188
- * This function extracts the value from a Some, or gives the default value if None.
189
- */
190
- export declare const getOrElse: <T>(opt: Option<T>, defaultValue: T) => T;
191
- /**
192
- * This function extracts the value from a Some, or throws an error if None.
193
- */
194
- export declare const unwrap: <T>(opt: Option<T>) => T;
195
- /**
196
- * This function maps a function over the value in a Some, or gives None.
197
- */
198
- export declare const mapOption: <T, U>(opt: Option<T>, f: (value: T) => U) => Option<U>;
199
- /**
200
- * Chains Option-returning operations.
201
- */
202
- export declare const flatMapOption: <T, U>(opt: Option<T>, f: (value: T) => Option<U>) => Option<U>;
203
- /**
204
- * This function converts a nullable value to an Option.
205
- */
206
- export declare const fromNullable: <T>(value: T | null | undefined) => Option<T>;
207
- /**
208
- * This function converts an Option to a nullable value.
209
- */
210
- export declare const toNullable: <T>(opt: Option<T>) => T | undefined;
211
- /**
212
- * This type represents a successful result containing a value.
213
- */
214
- export interface Ok<T> {
215
- readonly _tag: "Ok";
216
- readonly value: T;
217
- /** This method calls the `Ok` case with the value. */
218
- match<U>(cases: {
219
- Ok: (value: T) => U;
220
- Err: (error: never) => U;
221
- }): U;
222
- }
223
- /**
224
- * This type represents a failed result containing an error.
225
- */
226
- export interface Err<E> {
227
- readonly _tag: "Err";
228
- readonly error: E;
229
- /** This method calls the `Err` case with the error. */
230
- match<U>(cases: {
231
- Ok: (value: never) => U;
232
- Err: (error: E) => U;
233
- }): U;
234
- }
235
- /**
236
- * Result<T, E> represents the result of an operation that can fail.
237
- * It contains either a success value (Ok) or an error value (Err).
238
- *
239
- * @example
240
- * function divide(a: number, b: number): Result<number, string> {
241
- * if (b === 0) return Err("Division by zero");
242
- * return Ok(a / b);
243
- * }
244
- */
245
- export type Result<T, E> = Ok<T> | Err<E>;
246
- /**
247
- * This function constructs an Ok result containing the given value.
248
- */
249
- export declare const Ok: <T>(value: T) => Ok<T>;
250
- /**
251
- * This function constructs an Err result containing the given error.
252
- */
253
- export declare const Err: <E>(error: E) => Err<E>;
254
- /**
255
- * Type guard to check if a Result is Ok.
256
- */
257
- export declare const isOk: <T, E>(result: Result<T, E>) => result is Ok<T>;
258
- /**
259
- * Type guard to check if a Result is Err.
260
- */
261
- export declare const isErr: <T, E>(result: Result<T, E>) => result is Err<E>;
262
- /**
263
- * This function extracts the value from an Ok, or gives the default value if Err.
264
- */
265
- export declare const getOrElseResult: <T, E>(result: Result<T, E>, defaultValue: T) => T;
266
- /**
267
- * This function extracts the value from an Ok, or throws the error if Err.
268
- */
269
- export declare const unwrapResult: <T, E>(result: Result<T, E>) => T;
270
- /**
271
- * This function maps a function over the value in an Ok, or gives the Err unchanged.
272
- */
273
- export declare const mapResult: <T, U, E>(result: Result<T, E>, f: (value: T) => U) => Result<U, E>;
274
- /**
275
- * This function maps a function over the error in an Err, or gives the Ok unchanged.
276
- */
277
- export declare const mapError: <T, E, F>(result: Result<T, E>, f: (error: E) => F) => Result<T, F>;
278
- /**
279
- * Chains Result-returning operations.
280
- */
281
- export declare const flatMapResult: <T, U, E>(result: Result<T, E>, f: (value: T) => Result<U, E>) => Result<U, E>;
282
- /**
283
- * This function converts a throwing function into a Result-returning function.
284
- */
285
- export declare const tryCatch: <T, E = unknown>(fn: () => T, onError?: (error: unknown) => E) => Result<T, E>;
286
- /**
287
- * This function converts an async throwing function into a Result-returning async function.
288
- */
289
- export declare const tryCatchAsync: <T, E = unknown>(fn: () => Promise<T>, onError?: (error: unknown) => E) => Promise<Result<T, E>>;
290
- /**
291
- * Async variant of Option<T>.
292
- */
293
- export type AsyncOption<T> = Promise<Option<T>>;
294
- /**
295
- * Async variant of Result<T, E>.
296
- */
297
- export type AsyncResult<T, E> = Promise<Result<T, E>>;
298
- /**
299
- * This function maps a function over an async Option.
300
- */
301
- export declare const mapAsyncOption: <T, U>(opt: AsyncOption<T>, f: (value: T) => U | Promise<U>) => AsyncOption<U>;
302
- /**
303
- * This function maps a function over an async Result.
304
- */
305
- export declare const mapAsyncResult: <T, U, E>(result: AsyncResult<T, E>, f: (value: T) => U | Promise<U>) => AsyncResult<U, E>;
306
- /**
307
- * This function converts an Option to a Result.
308
- * None becomes Err with the given error.
309
- */
310
- export declare const optionToResult: <T, E>(opt: Option<T>, error: E) => Result<T, E>;
311
- /**
312
- * This function converts a Result to an Option.
313
- * Err becomes None, discarding the error.
314
- */
315
- export declare const resultToOption: <T, E>(result: Result<T, E>) => Option<T>;
316
- /**
317
- * This function combines multiple Options into an Option of an array.
318
- * This function gives None if any input is None.
319
- */
320
- export declare const sequence: <T>(opts: Option<T>[]) => Option<T[]>;
321
- /**
322
- * This function combines multiple Results into a Result of an array.
323
- * This function gives the first Err encountered, or Ok with all values.
324
- */
325
- export declare const sequenceResults: <T, E>(results: Result<T, E>[]) => Result<T[], E>;
326
142
  /**
327
143
  * Policy types for configurable collection behaviors.
328
144
  *
@@ -1073,10 +889,9 @@ export interface ReadonlyList<T> extends ReadonlyCollection<T> {
1073
889
  */
1074
890
  lastIndexOf(element: T, fromIndex?: number): number;
1075
891
  /**
1076
- * This method gives a view of the portion of this list between fromIndex (inclusive)
1077
- * and toIndex (exclusive).
892
+ * This method gives a new list with the elements from fromIndex (inclusive) to toIndex (exclusive).
1078
893
  *
1079
- * Changes to the sublist are reflected in the original list.
894
+ * The new list is a copy, not a view. A change to one list does not change the other list.
1080
895
  *
1081
896
  * @param fromIndex Start index (inclusive)
1082
897
  * @param toIndex End index (exclusive)
@@ -1155,7 +970,8 @@ export interface List<T> extends ReadonlyList<T>, Collection<T> {
1155
970
  */
1156
971
  reverse(): void;
1157
972
  /**
1158
- * This method gives a mutable sublist view.
973
+ * This method gives a new mutable list with the elements from fromIndex (inclusive) to toIndex (exclusive).
974
+ * The new list is a copy, not a view.
1159
975
  *
1160
976
  * @param fromIndex Start index (inclusive)
1161
977
  * @param toIndex End index (exclusive)
@@ -2418,6 +2234,8 @@ export declare class HashMap<K, V> implements MapLike<K, V> {
2418
2234
  get(key: K): V;
2419
2235
  getOrUndefined(key: K): V | undefined;
2420
2236
  getOrDefault(key: K, defaultValue: V): V;
2237
+ /** This method finds the node of a key, or gives `null`. */
2238
+ private findNode;
2421
2239
  containsValue(value: V): boolean;
2422
2240
  set(key: K, value: V): V | undefined;
2423
2241
  setIfAbsent(key: K, value: V): V;
@@ -3382,9 +3200,9 @@ export declare const unmodifiableCollection: typeof readonlyCollection;
3382
3200
  */
3383
3201
  export interface SafeListOperations<T> {
3384
3202
  /**
3385
- * Get element at index, giving Option.
3203
+ * Get element at index, as an optional.
3386
3204
  */
3387
- get(index: number): Option<T>;
3205
+ get(index: number): Optional<T>;
3388
3206
  /**
3389
3207
  * Set element at index, giving Result with old value or error.
3390
3208
  */
@@ -3394,72 +3212,72 @@ export interface SafeListOperations<T> {
3394
3212
  */
3395
3213
  removeAt(index: number): Result<T, string>;
3396
3214
  /**
3397
- * Get first element as Option.
3215
+ * Get first element as an optional.
3398
3216
  */
3399
- first(): Option<T>;
3217
+ first(): Optional<T>;
3400
3218
  /**
3401
- * Get last element as Option.
3219
+ * Get last element as an optional.
3402
3220
  */
3403
- last(): Option<T>;
3221
+ last(): Optional<T>;
3404
3222
  /**
3405
- * Pop element, giving Option.
3223
+ * Pop element, as an optional.
3406
3224
  */
3407
- pop(): Option<T>;
3225
+ pop(): Optional<T>;
3408
3226
  /**
3409
- * Shift element, giving Option.
3227
+ * Shift element, as an optional.
3410
3228
  */
3411
- shift(): Option<T>;
3229
+ shift(): Optional<T>;
3412
3230
  }
3413
3231
  /**
3414
3232
  * Safe operations for Queue.
3415
3233
  */
3416
3234
  export interface SafeQueueOperations<T> {
3417
3235
  /**
3418
- * Peek at head element as Option.
3236
+ * Peek at head element as an optional.
3419
3237
  */
3420
- peek(): Option<T>;
3238
+ peek(): Optional<T>;
3421
3239
  /**
3422
- * Poll element as Option.
3240
+ * Poll element as an optional.
3423
3241
  */
3424
- poll(): Option<T>;
3242
+ poll(): Optional<T>;
3425
3243
  /**
3426
- * Dequeue element as Option.
3244
+ * Dequeue element as an optional.
3427
3245
  */
3428
- dequeue(): Option<T>;
3246
+ dequeue(): Optional<T>;
3429
3247
  }
3430
3248
  /**
3431
3249
  * Safe operations for Deque.
3432
3250
  */
3433
3251
  export interface SafeDequeOperations<T> extends SafeQueueOperations<T> {
3434
3252
  /**
3435
- * Peek at first element as Option.
3253
+ * Peek at first element as an optional.
3436
3254
  */
3437
- peekFirst(): Option<T>;
3255
+ peekFirst(): Optional<T>;
3438
3256
  /**
3439
- * Peek at last element as Option.
3257
+ * Peek at last element as an optional.
3440
3258
  */
3441
- peekLast(): Option<T>;
3259
+ peekLast(): Optional<T>;
3442
3260
  /**
3443
- * Poll first element as Option.
3261
+ * Poll first element as an optional.
3444
3262
  */
3445
- pollFirst(): Option<T>;
3263
+ pollFirst(): Optional<T>;
3446
3264
  /**
3447
- * Poll last element as Option.
3265
+ * Poll last element as an optional.
3448
3266
  */
3449
- pollLast(): Option<T>;
3267
+ pollLast(): Optional<T>;
3450
3268
  /**
3451
- * Pop element as Option.
3269
+ * Pop element as an optional.
3452
3270
  */
3453
- pop(): Option<T>;
3271
+ pop(): Optional<T>;
3454
3272
  }
3455
3273
  /**
3456
3274
  * Safe operations for Map.
3457
3275
  */
3458
3276
  export interface SafeMapOperations<K, V> {
3459
3277
  /**
3460
- * Get value for key as Option.
3278
+ * Get value for key as an optional.
3461
3279
  */
3462
- get(key: K): Option<V>;
3280
+ get(key: K): Optional<V>;
3463
3281
  /**
3464
3282
  * Set value for key, giving Result with old value or success.
3465
3283
  */
@@ -3472,7 +3290,7 @@ export interface SafeMapOperations<K, V> {
3472
3290
  /**
3473
3291
  * This function makes a safe List middleware.
3474
3292
  *
3475
- * This function exposes a `safe` property with Option/Result-based operations.
3293
+ * This function exposes a `safe` property with Optional/Result-based operations.
3476
3294
  */
3477
3295
  export declare function safeList<T>(): Middleware<List<T>, List<T> & {
3478
3296
  safe: SafeListOperations<T>;