@layerzerolabs/dfs 0.0.19 → 0.0.21

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.
@@ -1,18 +1,18 @@
1
1
 
2
- > @layerzerolabs/dfs@0.0.2 build /home/runner/work/monorepo-internal/monorepo-internal/packages/dfs
2
+ > @layerzerolabs/dfs@0.0.2 build /home/runner/work/monorepo-internal/monorepo-internal/packages/framework/dfs
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.4.0
8
- CLI Using tsup config: /home/runner/work/monorepo-internal/monorepo-internal/packages/dfs/tsup.config.ts
8
+ CLI Using tsup config: /home/runner/work/monorepo-internal/monorepo-internal/packages/framework/dfs/tsup.config.ts
9
9
  CLI Target: es2022
10
10
  CLI Cleaning output folder
11
11
  CJS Build start
12
12
  ESM Build start
13
- CJS dist/index.cjs 1.38 KB
14
- CJS dist/index.cjs.map 5.33 KB
15
- CJS ⚡️ Build success in 120ms
16
- ESM dist/index.js 1.36 KB
17
- ESM dist/index.js.map 5.32 KB
18
- ESM ⚡️ Build success in 120ms
13
+ CJS dist/index.cjs 2.78 KB
14
+ CJS dist/index.cjs.map 8.97 KB
15
+ CJS ⚡️ Build success in 86ms
16
+ ESM dist/index.js 2.76 KB
17
+ ESM dist/index.js.map 8.96 KB
18
+ ESM ⚡️ Build success in 86ms
@@ -1,4 +1,4 @@
1
1
 
2
- > @layerzerolabs/dfs@0.0.2 lint /home/runner/work/monorepo-internal/monorepo-internal/packages/dfs
2
+ > @layerzerolabs/dfs@0.0.2 lint /home/runner/work/monorepo-internal/monorepo-internal/packages/framework/dfs
3
3
  > eslint . --max-warnings 0
4
4
 
@@ -1,15 +1,15 @@
1
1
 
2
- > @layerzerolabs/dfs@0.0.2 test /home/runner/work/monorepo-internal/monorepo-internal/packages/dfs
2
+ > @layerzerolabs/dfs@0.0.2 test /home/runner/work/monorepo-internal/monorepo-internal/packages/framework/dfs
3
3
  > vitest --run --pass-with-no-tests
4
4
 
5
5
 
6
-  RUN  v3.2.3 /home/runner/work/monorepo-internal/monorepo-internal/packages/dfs
6
+  RUN  v3.2.3 /home/runner/work/monorepo-internal/monorepo-internal/packages/framework/dfs
7
7
 
8
- ✓ test/dfs.test.ts (3 tests) 310ms
9
- ✓ DI Depth-first-search > The handlers for each of a node's dependencies should be completed before that node  304ms
8
+ ✓ test/dfs.test.ts (3 tests) 306ms
9
+ ✓ DI Depth-first-search > The handlers for each of a node's dependencies should be completed before that node  302ms
10
10
 
11
11
   Test Files  1 passed (1)
12
12
   Tests  3 passed (3)
13
-  Start at  23:51:51
14
-  Duration  1.19s (transform 136ms, setup 0ms, collect 81ms, tests 310ms, environment 0ms, prepare 358ms)
13
+  Start at  01:58:06
14
+  Duration  650ms (transform 66ms, setup 0ms, collect 55ms, tests 306ms, environment 0ms, prepare 102ms)
15
15
 
package/dist/index.cjs CHANGED
@@ -4,42 +4,66 @@ var __defProp = Object.defineProperty;
4
4
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
5
 
6
6
  // src/index.ts
7
- var dfs = /* @__PURE__ */ __name((node, handler, prehandler = (node2) => node2, _visited = /* @__PURE__ */ new Map(), _returns = {}, _backtrack = []) => {
8
- const _node = prehandler(node);
9
- if (_visited.has(_node.name)) {
10
- return async () => {
11
- await _visited.get(_node.name);
12
- return _returns;
13
- };
14
- }
15
- let resolver;
16
- _visited.set(_node.name, new Promise((res) => {
17
- resolver = res;
18
- }));
19
- const resolverFunctions = [];
20
- if (_node.dependencies) {
21
- const nextBacktrack = [
22
- ..._backtrack,
23
- _node
24
- ];
25
- for (const [_, dependencyValue] of Object.entries(_node.dependencies)) {
26
- resolverFunctions.push(dfs(dependencyValue, handler, prehandler, _visited, _returns, nextBacktrack));
7
+ var buildAncestorsByNodeName = /* @__PURE__ */ __name((node, prehandler) => {
8
+ const ancestorsByNodeName = /* @__PURE__ */ new Map();
9
+ const dependentCountByNodeName = /* @__PURE__ */ new Map();
10
+ const initializeMaps = /* @__PURE__ */ __name((node2) => {
11
+ ancestorsByNodeName.set(node2.name, /* @__PURE__ */ new Set([]));
12
+ for (const dependencyValue of Object.values(node2.dependencies)) {
13
+ const inDegree = dependentCountByNodeName.get(dependencyValue.name) || 0;
14
+ dependentCountByNodeName.set(dependencyValue.name, inDegree + 1);
15
+ if (!ancestorsByNodeName.has(dependencyValue.name)) {
16
+ initializeMaps(dependencyValue);
17
+ }
18
+ }
19
+ }, "initializeMaps");
20
+ initializeMaps(node);
21
+ let queue = [
22
+ prehandler(node)
23
+ ];
24
+ while (queue.length > 0) {
25
+ const curNode = queue.shift();
26
+ for (const dependencyValue of Object.values(curNode.dependencies)) {
27
+ const inDegree = dependentCountByNodeName.get(dependencyValue.name);
28
+ if (inDegree === 1) {
29
+ const preHandledDependency = prehandler(dependencyValue);
30
+ queue.push(preHandledDependency);
31
+ }
32
+ dependentCountByNodeName.set(dependencyValue.name, inDegree - 1);
33
+ const childAncestry = ancestorsByNodeName.get(dependencyValue.name);
34
+ const nodeAncestry = ancestorsByNodeName.get(curNode.name);
35
+ childAncestry.add(curNode);
36
+ for (const ancestor of nodeAncestry) {
37
+ childAncestry.add(ancestor);
38
+ }
27
39
  }
28
40
  }
29
- return async () => {
30
- try {
31
- await Promise.all(resolverFunctions.map((f) => f()));
32
- const regRes = await handler(_node);
33
- if (regRes) {
34
- (_returns[regRes.key] ??= {})[_node.name] = regRes.value;
41
+ return ancestorsByNodeName;
42
+ }, "buildAncestorsByNodeName");
43
+ var dfs = /* @__PURE__ */ __name((node, handler, prehandler = (node2) => node2, _returns = {}) => {
44
+ const ancestorsByNodeName = buildAncestorsByNodeName(node, prehandler);
45
+ const nodeResolverPromises = /* @__PURE__ */ new Map();
46
+ const resolveNode = /* @__PURE__ */ __name(async (node2) => {
47
+ const prehandledNode = prehandler(node2);
48
+ const childrenPromises = [];
49
+ for (const dependencyValue of Object.values(prehandledNode.dependencies)) {
50
+ if (!nodeResolverPromises.has(dependencyValue.name)) {
51
+ nodeResolverPromises.set(dependencyValue.name, resolveNode(dependencyValue));
35
52
  }
36
- resolver();
37
- return _returns;
38
- } catch (error) {
39
- console.error(`Failed at ${_backtrack.map((n) => n.name).join("/")}
40
- ${error?.message}`);
41
- throw error;
53
+ childrenPromises.push(nodeResolverPromises.get(dependencyValue.name));
42
54
  }
55
+ await Promise.all(childrenPromises);
56
+ const ancestry = ancestorsByNodeName.get(prehandledNode.name);
57
+ const regRes = await handler(node2, [
58
+ ...ancestry
59
+ ]);
60
+ if (regRes) {
61
+ (_returns[regRes.key] ??= {})[prehandledNode.name] = regRes.value;
62
+ }
63
+ }, "resolveNode");
64
+ return async () => {
65
+ await resolveNode(node);
66
+ return _returns;
43
67
  };
44
68
  }, "dfs");
45
69
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["dfs","node","handler","prehandler","_visited","Map","_returns","_backtrack","_node","has","name","get","resolver","set","Promise","res","resolverFunctions","dependencies","nextBacktrack","_","dependencyValue","Object","entries","push","all","map","f","regRes","key","value","error","console","n","join","message"],"mappings":";;;;;;AA8BO,IAAMA,sBAAM,MAAA,CAAA,CACfC,IAAAA,EACAC,OAAAA,EACAC,UAAAA,GAAqC,CAACF,KAAAA,KAASA,KAAAA,EAC/CG,QAAAA,mBAAuC,IAAIC,KAAAA,EAC3CC,QAAAA,GAAwB,EAAC,EACzBC,UAAAA,GAA6D,EAAA,KAAE;AAE/D,EAAA,MAAMC,KAAAA,GAAQL,WAAWF,IAAAA,CAAAA;AACzB,EAAA,IAAIG,QAAAA,CAASK,GAAAA,CAAID,KAAAA,CAAME,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAO,YAAA;AACH,MAAA,MAAMN,QAAAA,CAASO,GAAAA,CAAIH,KAAAA,CAAME,IAAI,CAAA;AAC7B,MAAA,OAAOJ,QAAAA;AACX,IAAA,CAAA;AACJ,EAAA;AACA,EAAA,IAAIM,QAAAA;AAEJR,EAAAA,QAAAA,CAASS,IACLL,KAAAA,CAAME,IAAAA,EACN,IAAII,OAAAA,CAAc,CAACC,GAAAA,KAAAA;AACfH,IAAAA,QAAAA,GAAWG,GAAAA;AACf,EAAA,CAAA,CAAA,CAAA;AAIJ,EAAA,MAAMC,oBAA4C,EAAA;AAClD,EAAA,IAAIR,MAAMS,YAAAA,EAAc;AACpB,IAAA,MAAMC,aAAAA,GAAgB;AAAIX,MAAAA,GAAAA,UAAAA;AAAYC,MAAAA;;AACtC,IAAA,KAAA,MAAW,CAACW,GAAGC,eAAAA,CAAAA,IAAoBC,OAAOC,OAAAA,CAAQd,KAAAA,CAAMS,YAAY,CAAA,EAAG;AACnED,MAAAA,iBAAAA,CAAkBO,IAAAA,CACdvB,IAAIoB,eAAAA,EAAiBlB,OAAAA,EAASC,YAAYC,QAAAA,EAAUE,QAAAA,EAAUY,aAAAA,CAAAA,CAAAA;AAEtE,IAAA;AACJ,EAAA;AAEA,EAAA,OAAO,YAAA;AACH,IAAA,IAAI;AACA,MAAA,MAAMJ,OAAAA,CAAQU,IAAIR,iBAAAA,CAAkBS,GAAAA,CAAI,CAACC,CAAAA,KAAMA,CAAAA,EAAAA,CAAAA,CAAAA;AAC/C,MAAA,MAAMC,MAAAA,GAAS,MAAMzB,OAAAA,CAAQM,KAAAA,CAAAA;AAC7B,MAAA,IAAImB,MAAAA,EAAQ;AACP,QAAA,CAACrB,QAAAA,CAAiBqB,OAAOC,GAAG,CAAA,KAAM,EAAC,EAAGpB,KAAAA,CAAME,IAAI,CAAA,GAAIiB,MAAAA,CAAOE,KAAAA;AAChE,MAAA;AACAjB,MAAAA,QAAAA,EAAAA;AACA,MAAA,OAAON,QAAAA;AACX,IAAA,CAAA,CAAA,OAASwB,KAAAA,EAAY;AACjBC,MAAAA,OAAAA,CAAQD,KAAAA,CACJ,CAAA,UAAA,EAAavB,UAAAA,CAAWkB,GAAAA,CAAI,CAACO,CAAAA,KAAMA,CAAAA,CAAEtB,IAAI,CAAA,CAAEuB,IAAAA,CAAK,GAAA,CAAA;AAASH,EAAAA,KAAAA,EAAOI,OAAAA,CAAAA,CAAS,CAAA;AAE7E,MAAA,MAAMJ,KAAAA;AACV,IAAA;AACJ,EAAA,CAAA;AACJ,CAAA,EAnDmB,KAAA","file":"index.cjs","sourcesContent":["import type { Dependencies, DependencyNode } from '@layerzerolabs/dependency-graph';\n\n/**\n * A registrar is a simple interface for an object that provides the ability to traverse the dependency graph.\n * It is implicit in this definition that the registrar should also *register* values adhering to the schemata\n * of the graph.\n */\nexport interface Registrar<ReturnType> {\n traverseDependencies: (rootNode: DependencyNode<any, any>) => Promise<ReturnType>;\n}\nexport type NodeHandlerFunction = (\n node: DependencyNode<any, Dependencies>,\n) => Promise<{ key: string; value: any }>;\nexport type NodePreHandlerFunction = (\n node: DependencyNode<any, Dependencies>,\n) => DependencyNode<any, Dependencies>;\n\n/**\n * Performs a depth-first-search on a tree of dependency nodes, and returns a function\n * that will call the handler for each node in the tree, ordered s.t. the handler of N\n * will be called only after the handlers of dependencies(N) have been called.\n * The resolver function will only call the handler once for each unique definition node.\n * The resolver function returns an object whose keys are the keys defined\n * by each of the handlers, and whose values are objects whose keys are the names\n * of the nodes resolved and whose values are the values defined by the handlers.\n * @param node the root node of the tree\n * @param handler a function that accepts a node and registers it\n * @param prehandler a function that accepts a node and returns a node. will be use to pre-process the graph\n * @returns a resolver function\n */\nexport const dfs = <DependencyName extends string, ReturnTypes>(\n node: DependencyNode<DependencyName, Dependencies>,\n handler: NodeHandlerFunction,\n prehandler: NodePreHandlerFunction = (node) => node,\n _visited: Map<string, Promise<void>> = new Map(),\n _returns: ReturnTypes = {} as any,\n _backtrack: DependencyNode<DependencyName, Dependencies>[] = [],\n): (() => Promise<ReturnTypes>) => {\n const _node = prehandler(node);\n if (_visited.has(_node.name)) {\n return async () => {\n await _visited.get(_node.name);\n return _returns;\n };\n }\n let resolver: (value: void | PromiseLike<void>) => void;\n\n _visited.set(\n _node.name,\n new Promise<void>((res) => {\n resolver = res;\n }),\n );\n\n // Resolve dependencies first\n const resolverFunctions: (() => Promise<any>)[] = [];\n if (_node.dependencies) {\n const nextBacktrack = [..._backtrack, _node];\n for (const [_, dependencyValue] of Object.entries(_node.dependencies)) {\n resolverFunctions.push(\n dfs(dependencyValue, handler, prehandler, _visited, _returns, nextBacktrack),\n );\n }\n }\n\n return async () => {\n try {\n await Promise.all(resolverFunctions.map((f) => f()));\n const regRes = await handler(_node);\n if (regRes) {\n ((_returns as any)[regRes.key] ??= {})[_node.name] = regRes.value;\n }\n resolver();\n return _returns;\n } catch (error: any) {\n console.error(\n `Failed at ${_backtrack.map((n) => n.name).join('/')}\\n${error?.message}`,\n );\n throw error;\n }\n };\n};\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["buildAncestorsByNodeName","node","prehandler","ancestorsByNodeName","Map","dependentCountByNodeName","initializeMaps","set","name","Set","dependencyValue","Object","values","dependencies","inDegree","get","has","queue","length","curNode","shift","preHandledDependency","push","childAncestry","nodeAncestry","add","ancestor","dfs","handler","_returns","nodeResolverPromises","resolveNode","prehandledNode","childrenPromises","Promise","all","ancestry","regRes","key","value"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { DependencyNode } from '@layerzerolabs/dependency-graph';\n\n/**\n * A registrar is a simple interface for an object that provides the ability to traverse the dependency graph.\n * It is implicit in this definition that the registrar should also *register* values adhering to the schemata\n * of the graph.\n */\nexport interface Registrar<ReturnType> {\n traverseDependencies: (rootNode: DependencyNode) => Promise<ReturnType>;\n}\nexport type NodeHandlerFunction = (\n node: DependencyNode,\n ancestry: DependencyNode[],\n) => Promise<{ key: string; value: any }>;\nexport type NodePreHandlerFunction = (node: DependencyNode) => DependencyNode;\n\nconst buildAncestorsByNodeName = (node: DependencyNode, prehandler: NodePreHandlerFunction) => {\n const ancestorsByNodeName = new Map<string, Set<DependencyNode>>();\n const dependentCountByNodeName = new Map<string, number>();\n\n // If A depends on B and C, B depends on C, we initialize with:\n // ancestorsByNodeName: { A: Set([]), B: Set([]), C: Set([]) }\n // dependentsCountByNodeName: { B: 1, C: 2 }\n const initializeMaps = (node: DependencyNode) => {\n ancestorsByNodeName.set(node.name, new Set([]));\n for (const dependencyValue of Object.values(node.dependencies)) {\n const inDegree = dependentCountByNodeName.get(dependencyValue.name) || 0;\n dependentCountByNodeName.set(dependencyValue.name, inDegree + 1);\n if (!ancestorsByNodeName.has(dependencyValue.name)) {\n initializeMaps(dependencyValue);\n }\n }\n };\n initializeMaps(node);\n\n // topological sorted bfs exploration\n // induction by level, initial level has correct initial state, build level + 1\n // bfs is used to build ancestry correctly\n // this should actually be a queue, but its probably fine for now\n let queue = [prehandler(node)];\n while (queue.length > 0) {\n const curNode = queue.shift()!;\n\n // add the new processable dependencies to the queue, update their state.\n for (const dependencyValue of Object.values(curNode.dependencies)) {\n const inDegree = dependentCountByNodeName.get(dependencyValue.name)!;\n // We are the last edge missing in the graph for this dependency -> we can process it after us.\n if (inDegree === 1) {\n const preHandledDependency = prehandler(dependencyValue);\n queue.push(preHandledDependency);\n }\n // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.\n dependentCountByNodeName.set(dependencyValue.name, inDegree - 1);\n\n // add our ancestry to them, js sets maintain order of insertion, this means their ancestors are in distance order due to BFS properties.\n const childAncestry = ancestorsByNodeName.get(dependencyValue.name)!;\n const nodeAncestry = ancestorsByNodeName.get(curNode.name)!;\n childAncestry.add(curNode);\n for (const ancestor of nodeAncestry) {\n childAncestry.add(ancestor);\n }\n }\n }\n\n return ancestorsByNodeName;\n};\n\n/**\n * Performs a depth-first-search on a tree of dependency nodes, and returns a function\n * that will call the handler for each node in the tree, ordered s.t. the handler of N\n * will be called only after the handlers of dependencies(N) have been called.\n * The resolver function will only call the handler once for each unique definition node.\n * The resolver function returns an object whose keys are the keys defined\n * by each of the handlers, and whose values are objects whose keys are the names\n * of the nodes resolved and whose values are the values defined by the handlers.\n * @param node the root node of the tree\n * @param handler a function that accepts a node and registers it\n * @param prehandler a function that accepts a node and returns a node. will be use to pre-process the graph\n * @returns a resolver function\n */\nexport const dfs = <ReturnTypes>(\n node: DependencyNode,\n handler: NodeHandlerFunction,\n prehandler: NodePreHandlerFunction = (node) => node,\n _returns: ReturnTypes = {} as any,\n): (() => Promise<ReturnTypes>) => {\n const ancestorsByNodeName = buildAncestorsByNodeName(node, prehandler);\n\n // Maintains Map<node.name, Promise<void>> -> the promise to resolve the node, for all nodes.\n const nodeResolverPromises = new Map<string, Promise<void>>();\n const resolveNode = async (node: DependencyNode) => {\n const prehandledNode = prehandler(node);\n // first wait for all its children\n const childrenPromises = [];\n for (const dependencyValue of Object.values(prehandledNode.dependencies)) {\n // Grab the cached promise or create it, we want to have only 1 handler promise for each node.\n if (!nodeResolverPromises.has(dependencyValue.name)) {\n nodeResolverPromises.set(dependencyValue.name, resolveNode(dependencyValue));\n }\n childrenPromises.push(nodeResolverPromises.get(dependencyValue.name)!);\n }\n await Promise.all(childrenPromises);\n\n // Then, you can process yourself\n const ancestry = ancestorsByNodeName.get(prehandledNode.name)!;\n const regRes = await handler(node, [...ancestry]);\n if (regRes) {\n ((_returns as any)[regRes.key] ??= {})[prehandledNode.name] = regRes.value;\n }\n };\n\n return async () => {\n await resolveNode(node);\n return _returns;\n };\n};\n"]}
package/dist/index.d.ts CHANGED
@@ -1,17 +1,17 @@
1
- import type { Dependencies, DependencyNode } from '@layerzerolabs/dependency-graph';
1
+ import type { DependencyNode } from '@layerzerolabs/dependency-graph';
2
2
  /**
3
3
  * A registrar is a simple interface for an object that provides the ability to traverse the dependency graph.
4
4
  * It is implicit in this definition that the registrar should also *register* values adhering to the schemata
5
5
  * of the graph.
6
6
  */
7
7
  export interface Registrar<ReturnType> {
8
- traverseDependencies: (rootNode: DependencyNode<any, any>) => Promise<ReturnType>;
8
+ traverseDependencies: (rootNode: DependencyNode) => Promise<ReturnType>;
9
9
  }
10
- export type NodeHandlerFunction = (node: DependencyNode<any, Dependencies>) => Promise<{
10
+ export type NodeHandlerFunction = (node: DependencyNode, ancestry: DependencyNode[]) => Promise<{
11
11
  key: string;
12
12
  value: any;
13
13
  }>;
14
- export type NodePreHandlerFunction = (node: DependencyNode<any, Dependencies>) => DependencyNode<any, Dependencies>;
14
+ export type NodePreHandlerFunction = (node: DependencyNode) => DependencyNode;
15
15
  /**
16
16
  * Performs a depth-first-search on a tree of dependency nodes, and returns a function
17
17
  * that will call the handler for each node in the tree, ordered s.t. the handler of N
@@ -25,5 +25,5 @@ export type NodePreHandlerFunction = (node: DependencyNode<any, Dependencies>) =
25
25
  * @param prehandler a function that accepts a node and returns a node. will be use to pre-process the graph
26
26
  * @returns a resolver function
27
27
  */
28
- export declare const dfs: <DependencyName extends string, ReturnTypes>(node: DependencyNode<DependencyName, Dependencies>, handler: NodeHandlerFunction, prehandler?: NodePreHandlerFunction, _visited?: Map<string, Promise<void>>, _returns?: ReturnTypes, _backtrack?: DependencyNode<DependencyName, Dependencies>[]) => (() => Promise<ReturnTypes>);
28
+ export declare const dfs: <ReturnTypes>(node: DependencyNode, handler: NodeHandlerFunction, prehandler?: NodePreHandlerFunction, _returns?: ReturnTypes) => (() => Promise<ReturnTypes>);
29
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEpF;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,UAAU;IACjC,oBAAoB,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACrF;AACD,MAAM,MAAM,mBAAmB,GAAG,CAC9B,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,KACtC,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAC1C,MAAM,MAAM,sBAAsB,GAAG,CACjC,IAAI,EAAE,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,KACtC,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AAEvC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,GAAG,GAAI,cAAc,SAAS,MAAM,EAAE,WAAW,EAC1D,MAAM,cAAc,CAAC,cAAc,EAAE,YAAY,CAAC,EAClD,SAAS,mBAAmB,EAC5B,aAAY,sBAAuC,EACnD,WAAU,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAa,EAChD,WAAU,WAAuB,EACjC,aAAY,cAAc,CAAC,cAAc,EAAE,YAAY,CAAC,EAAO,KAChE,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,CA4C7B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,UAAU;IACjC,oBAAoB,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAC3E;AACD,MAAM,MAAM,mBAAmB,GAAG,CAC9B,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,cAAc,EAAE,KACzB,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAC1C,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE,cAAc,KAAK,cAAc,CAAC;AAqD9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,GAAG,GAAI,WAAW,EAC3B,MAAM,cAAc,EACpB,SAAS,mBAAmB,EAC5B,aAAY,sBAAuC,EACnD,WAAU,WAAuB,KAClC,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,CA8B7B,CAAC"}
package/dist/index.js CHANGED
@@ -2,42 +2,66 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/index.ts
5
- var dfs = /* @__PURE__ */ __name((node, handler, prehandler = (node2) => node2, _visited = /* @__PURE__ */ new Map(), _returns = {}, _backtrack = []) => {
6
- const _node = prehandler(node);
7
- if (_visited.has(_node.name)) {
8
- return async () => {
9
- await _visited.get(_node.name);
10
- return _returns;
11
- };
12
- }
13
- let resolver;
14
- _visited.set(_node.name, new Promise((res) => {
15
- resolver = res;
16
- }));
17
- const resolverFunctions = [];
18
- if (_node.dependencies) {
19
- const nextBacktrack = [
20
- ..._backtrack,
21
- _node
22
- ];
23
- for (const [_, dependencyValue] of Object.entries(_node.dependencies)) {
24
- resolverFunctions.push(dfs(dependencyValue, handler, prehandler, _visited, _returns, nextBacktrack));
5
+ var buildAncestorsByNodeName = /* @__PURE__ */ __name((node, prehandler) => {
6
+ const ancestorsByNodeName = /* @__PURE__ */ new Map();
7
+ const dependentCountByNodeName = /* @__PURE__ */ new Map();
8
+ const initializeMaps = /* @__PURE__ */ __name((node2) => {
9
+ ancestorsByNodeName.set(node2.name, /* @__PURE__ */ new Set([]));
10
+ for (const dependencyValue of Object.values(node2.dependencies)) {
11
+ const inDegree = dependentCountByNodeName.get(dependencyValue.name) || 0;
12
+ dependentCountByNodeName.set(dependencyValue.name, inDegree + 1);
13
+ if (!ancestorsByNodeName.has(dependencyValue.name)) {
14
+ initializeMaps(dependencyValue);
15
+ }
16
+ }
17
+ }, "initializeMaps");
18
+ initializeMaps(node);
19
+ let queue = [
20
+ prehandler(node)
21
+ ];
22
+ while (queue.length > 0) {
23
+ const curNode = queue.shift();
24
+ for (const dependencyValue of Object.values(curNode.dependencies)) {
25
+ const inDegree = dependentCountByNodeName.get(dependencyValue.name);
26
+ if (inDegree === 1) {
27
+ const preHandledDependency = prehandler(dependencyValue);
28
+ queue.push(preHandledDependency);
29
+ }
30
+ dependentCountByNodeName.set(dependencyValue.name, inDegree - 1);
31
+ const childAncestry = ancestorsByNodeName.get(dependencyValue.name);
32
+ const nodeAncestry = ancestorsByNodeName.get(curNode.name);
33
+ childAncestry.add(curNode);
34
+ for (const ancestor of nodeAncestry) {
35
+ childAncestry.add(ancestor);
36
+ }
25
37
  }
26
38
  }
27
- return async () => {
28
- try {
29
- await Promise.all(resolverFunctions.map((f) => f()));
30
- const regRes = await handler(_node);
31
- if (regRes) {
32
- (_returns[regRes.key] ??= {})[_node.name] = regRes.value;
39
+ return ancestorsByNodeName;
40
+ }, "buildAncestorsByNodeName");
41
+ var dfs = /* @__PURE__ */ __name((node, handler, prehandler = (node2) => node2, _returns = {}) => {
42
+ const ancestorsByNodeName = buildAncestorsByNodeName(node, prehandler);
43
+ const nodeResolverPromises = /* @__PURE__ */ new Map();
44
+ const resolveNode = /* @__PURE__ */ __name(async (node2) => {
45
+ const prehandledNode = prehandler(node2);
46
+ const childrenPromises = [];
47
+ for (const dependencyValue of Object.values(prehandledNode.dependencies)) {
48
+ if (!nodeResolverPromises.has(dependencyValue.name)) {
49
+ nodeResolverPromises.set(dependencyValue.name, resolveNode(dependencyValue));
33
50
  }
34
- resolver();
35
- return _returns;
36
- } catch (error) {
37
- console.error(`Failed at ${_backtrack.map((n) => n.name).join("/")}
38
- ${error?.message}`);
39
- throw error;
51
+ childrenPromises.push(nodeResolverPromises.get(dependencyValue.name));
40
52
  }
53
+ await Promise.all(childrenPromises);
54
+ const ancestry = ancestorsByNodeName.get(prehandledNode.name);
55
+ const regRes = await handler(node2, [
56
+ ...ancestry
57
+ ]);
58
+ if (regRes) {
59
+ (_returns[regRes.key] ??= {})[prehandledNode.name] = regRes.value;
60
+ }
61
+ }, "resolveNode");
62
+ return async () => {
63
+ await resolveNode(node);
64
+ return _returns;
41
65
  };
42
66
  }, "dfs");
43
67
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["dfs","node","handler","prehandler","_visited","Map","_returns","_backtrack","_node","has","name","get","resolver","set","Promise","res","resolverFunctions","dependencies","nextBacktrack","_","dependencyValue","Object","entries","push","all","map","f","regRes","key","value","error","console","n","join","message"],"mappings":";;;;AA8BO,IAAMA,sBAAM,MAAA,CAAA,CACfC,IAAAA,EACAC,OAAAA,EACAC,UAAAA,GAAqC,CAACF,KAAAA,KAASA,KAAAA,EAC/CG,QAAAA,mBAAuC,IAAIC,KAAAA,EAC3CC,QAAAA,GAAwB,EAAC,EACzBC,UAAAA,GAA6D,EAAA,KAAE;AAE/D,EAAA,MAAMC,KAAAA,GAAQL,WAAWF,IAAAA,CAAAA;AACzB,EAAA,IAAIG,QAAAA,CAASK,GAAAA,CAAID,KAAAA,CAAME,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAO,YAAA;AACH,MAAA,MAAMN,QAAAA,CAASO,GAAAA,CAAIH,KAAAA,CAAME,IAAI,CAAA;AAC7B,MAAA,OAAOJ,QAAAA;AACX,IAAA,CAAA;AACJ,EAAA;AACA,EAAA,IAAIM,QAAAA;AAEJR,EAAAA,QAAAA,CAASS,IACLL,KAAAA,CAAME,IAAAA,EACN,IAAII,OAAAA,CAAc,CAACC,GAAAA,KAAAA;AACfH,IAAAA,QAAAA,GAAWG,GAAAA;AACf,EAAA,CAAA,CAAA,CAAA;AAIJ,EAAA,MAAMC,oBAA4C,EAAA;AAClD,EAAA,IAAIR,MAAMS,YAAAA,EAAc;AACpB,IAAA,MAAMC,aAAAA,GAAgB;AAAIX,MAAAA,GAAAA,UAAAA;AAAYC,MAAAA;;AACtC,IAAA,KAAA,MAAW,CAACW,GAAGC,eAAAA,CAAAA,IAAoBC,OAAOC,OAAAA,CAAQd,KAAAA,CAAMS,YAAY,CAAA,EAAG;AACnED,MAAAA,iBAAAA,CAAkBO,IAAAA,CACdvB,IAAIoB,eAAAA,EAAiBlB,OAAAA,EAASC,YAAYC,QAAAA,EAAUE,QAAAA,EAAUY,aAAAA,CAAAA,CAAAA;AAEtE,IAAA;AACJ,EAAA;AAEA,EAAA,OAAO,YAAA;AACH,IAAA,IAAI;AACA,MAAA,MAAMJ,OAAAA,CAAQU,IAAIR,iBAAAA,CAAkBS,GAAAA,CAAI,CAACC,CAAAA,KAAMA,CAAAA,EAAAA,CAAAA,CAAAA;AAC/C,MAAA,MAAMC,MAAAA,GAAS,MAAMzB,OAAAA,CAAQM,KAAAA,CAAAA;AAC7B,MAAA,IAAImB,MAAAA,EAAQ;AACP,QAAA,CAACrB,QAAAA,CAAiBqB,OAAOC,GAAG,CAAA,KAAM,EAAC,EAAGpB,KAAAA,CAAME,IAAI,CAAA,GAAIiB,MAAAA,CAAOE,KAAAA;AAChE,MAAA;AACAjB,MAAAA,QAAAA,EAAAA;AACA,MAAA,OAAON,QAAAA;AACX,IAAA,CAAA,CAAA,OAASwB,KAAAA,EAAY;AACjBC,MAAAA,OAAAA,CAAQD,KAAAA,CACJ,CAAA,UAAA,EAAavB,UAAAA,CAAWkB,GAAAA,CAAI,CAACO,CAAAA,KAAMA,CAAAA,CAAEtB,IAAI,CAAA,CAAEuB,IAAAA,CAAK,GAAA,CAAA;AAASH,EAAAA,KAAAA,EAAOI,OAAAA,CAAAA,CAAS,CAAA;AAE7E,MAAA,MAAMJ,KAAAA;AACV,IAAA;AACJ,EAAA,CAAA;AACJ,CAAA,EAnDmB,KAAA","file":"index.js","sourcesContent":["import type { Dependencies, DependencyNode } from '@layerzerolabs/dependency-graph';\n\n/**\n * A registrar is a simple interface for an object that provides the ability to traverse the dependency graph.\n * It is implicit in this definition that the registrar should also *register* values adhering to the schemata\n * of the graph.\n */\nexport interface Registrar<ReturnType> {\n traverseDependencies: (rootNode: DependencyNode<any, any>) => Promise<ReturnType>;\n}\nexport type NodeHandlerFunction = (\n node: DependencyNode<any, Dependencies>,\n) => Promise<{ key: string; value: any }>;\nexport type NodePreHandlerFunction = (\n node: DependencyNode<any, Dependencies>,\n) => DependencyNode<any, Dependencies>;\n\n/**\n * Performs a depth-first-search on a tree of dependency nodes, and returns a function\n * that will call the handler for each node in the tree, ordered s.t. the handler of N\n * will be called only after the handlers of dependencies(N) have been called.\n * The resolver function will only call the handler once for each unique definition node.\n * The resolver function returns an object whose keys are the keys defined\n * by each of the handlers, and whose values are objects whose keys are the names\n * of the nodes resolved and whose values are the values defined by the handlers.\n * @param node the root node of the tree\n * @param handler a function that accepts a node and registers it\n * @param prehandler a function that accepts a node and returns a node. will be use to pre-process the graph\n * @returns a resolver function\n */\nexport const dfs = <DependencyName extends string, ReturnTypes>(\n node: DependencyNode<DependencyName, Dependencies>,\n handler: NodeHandlerFunction,\n prehandler: NodePreHandlerFunction = (node) => node,\n _visited: Map<string, Promise<void>> = new Map(),\n _returns: ReturnTypes = {} as any,\n _backtrack: DependencyNode<DependencyName, Dependencies>[] = [],\n): (() => Promise<ReturnTypes>) => {\n const _node = prehandler(node);\n if (_visited.has(_node.name)) {\n return async () => {\n await _visited.get(_node.name);\n return _returns;\n };\n }\n let resolver: (value: void | PromiseLike<void>) => void;\n\n _visited.set(\n _node.name,\n new Promise<void>((res) => {\n resolver = res;\n }),\n );\n\n // Resolve dependencies first\n const resolverFunctions: (() => Promise<any>)[] = [];\n if (_node.dependencies) {\n const nextBacktrack = [..._backtrack, _node];\n for (const [_, dependencyValue] of Object.entries(_node.dependencies)) {\n resolverFunctions.push(\n dfs(dependencyValue, handler, prehandler, _visited, _returns, nextBacktrack),\n );\n }\n }\n\n return async () => {\n try {\n await Promise.all(resolverFunctions.map((f) => f()));\n const regRes = await handler(_node);\n if (regRes) {\n ((_returns as any)[regRes.key] ??= {})[_node.name] = regRes.value;\n }\n resolver();\n return _returns;\n } catch (error: any) {\n console.error(\n `Failed at ${_backtrack.map((n) => n.name).join('/')}\\n${error?.message}`,\n );\n throw error;\n }\n };\n};\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["buildAncestorsByNodeName","node","prehandler","ancestorsByNodeName","Map","dependentCountByNodeName","initializeMaps","set","name","Set","dependencyValue","Object","values","dependencies","inDegree","get","has","queue","length","curNode","shift","preHandledDependency","push","childAncestry","nodeAncestry","add","ancestor","dfs","handler","_returns","nodeResolverPromises","resolveNode","prehandledNode","childrenPromises","Promise","all","ancestry","regRes","key","value"],"mappings":"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","file":"index.js","sourcesContent":["import type { DependencyNode } from '@layerzerolabs/dependency-graph';\n\n/**\n * A registrar is a simple interface for an object that provides the ability to traverse the dependency graph.\n * It is implicit in this definition that the registrar should also *register* values adhering to the schemata\n * of the graph.\n */\nexport interface Registrar<ReturnType> {\n traverseDependencies: (rootNode: DependencyNode) => Promise<ReturnType>;\n}\nexport type NodeHandlerFunction = (\n node: DependencyNode,\n ancestry: DependencyNode[],\n) => Promise<{ key: string; value: any }>;\nexport type NodePreHandlerFunction = (node: DependencyNode) => DependencyNode;\n\nconst buildAncestorsByNodeName = (node: DependencyNode, prehandler: NodePreHandlerFunction) => {\n const ancestorsByNodeName = new Map<string, Set<DependencyNode>>();\n const dependentCountByNodeName = new Map<string, number>();\n\n // If A depends on B and C, B depends on C, we initialize with:\n // ancestorsByNodeName: { A: Set([]), B: Set([]), C: Set([]) }\n // dependentsCountByNodeName: { B: 1, C: 2 }\n const initializeMaps = (node: DependencyNode) => {\n ancestorsByNodeName.set(node.name, new Set([]));\n for (const dependencyValue of Object.values(node.dependencies)) {\n const inDegree = dependentCountByNodeName.get(dependencyValue.name) || 0;\n dependentCountByNodeName.set(dependencyValue.name, inDegree + 1);\n if (!ancestorsByNodeName.has(dependencyValue.name)) {\n initializeMaps(dependencyValue);\n }\n }\n };\n initializeMaps(node);\n\n // topological sorted bfs exploration\n // induction by level, initial level has correct initial state, build level + 1\n // bfs is used to build ancestry correctly\n // this should actually be a queue, but its probably fine for now\n let queue = [prehandler(node)];\n while (queue.length > 0) {\n const curNode = queue.shift()!;\n\n // add the new processable dependencies to the queue, update their state.\n for (const dependencyValue of Object.values(curNode.dependencies)) {\n const inDegree = dependentCountByNodeName.get(dependencyValue.name)!;\n // We are the last edge missing in the graph for this dependency -> we can process it after us.\n if (inDegree === 1) {\n const preHandledDependency = prehandler(dependencyValue);\n queue.push(preHandledDependency);\n }\n // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.\n dependentCountByNodeName.set(dependencyValue.name, inDegree - 1);\n\n // add our ancestry to them, js sets maintain order of insertion, this means their ancestors are in distance order due to BFS properties.\n const childAncestry = ancestorsByNodeName.get(dependencyValue.name)!;\n const nodeAncestry = ancestorsByNodeName.get(curNode.name)!;\n childAncestry.add(curNode);\n for (const ancestor of nodeAncestry) {\n childAncestry.add(ancestor);\n }\n }\n }\n\n return ancestorsByNodeName;\n};\n\n/**\n * Performs a depth-first-search on a tree of dependency nodes, and returns a function\n * that will call the handler for each node in the tree, ordered s.t. the handler of N\n * will be called only after the handlers of dependencies(N) have been called.\n * The resolver function will only call the handler once for each unique definition node.\n * The resolver function returns an object whose keys are the keys defined\n * by each of the handlers, and whose values are objects whose keys are the names\n * of the nodes resolved and whose values are the values defined by the handlers.\n * @param node the root node of the tree\n * @param handler a function that accepts a node and registers it\n * @param prehandler a function that accepts a node and returns a node. will be use to pre-process the graph\n * @returns a resolver function\n */\nexport const dfs = <ReturnTypes>(\n node: DependencyNode,\n handler: NodeHandlerFunction,\n prehandler: NodePreHandlerFunction = (node) => node,\n _returns: ReturnTypes = {} as any,\n): (() => Promise<ReturnTypes>) => {\n const ancestorsByNodeName = buildAncestorsByNodeName(node, prehandler);\n\n // Maintains Map<node.name, Promise<void>> -> the promise to resolve the node, for all nodes.\n const nodeResolverPromises = new Map<string, Promise<void>>();\n const resolveNode = async (node: DependencyNode) => {\n const prehandledNode = prehandler(node);\n // first wait for all its children\n const childrenPromises = [];\n for (const dependencyValue of Object.values(prehandledNode.dependencies)) {\n // Grab the cached promise or create it, we want to have only 1 handler promise for each node.\n if (!nodeResolverPromises.has(dependencyValue.name)) {\n nodeResolverPromises.set(dependencyValue.name, resolveNode(dependencyValue));\n }\n childrenPromises.push(nodeResolverPromises.get(dependencyValue.name)!);\n }\n await Promise.all(childrenPromises);\n\n // Then, you can process yourself\n const ancestry = ancestorsByNodeName.get(prehandledNode.name)!;\n const regRes = await handler(node, [...ancestry]);\n if (regRes) {\n ((_returns as any)[regRes.key] ??= {})[prehandledNode.name] = regRes.value;\n }\n };\n\n return async () => {\n await resolveNode(node);\n return _returns;\n };\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layerzerolabs/dfs",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,13 +14,13 @@
14
14
  "module": "./dist/index.js",
15
15
  "types": "./dist/index.d.ts",
16
16
  "dependencies": {
17
- "@layerzerolabs/dependency-graph": "0.0.19"
17
+ "@layerzerolabs/dependency-graph": "0.0.21"
18
18
  },
19
19
  "devDependencies": {
20
20
  "tsup": "^8.4.0",
21
21
  "vitest": "^3.2.3",
22
- "@layerzerolabs/typescript-configuration": "0.0.19",
23
- "@layerzerolabs/tsup-configuration": "0.0.19"
22
+ "@layerzerolabs/tsup-configuration": "0.0.21",
23
+ "@layerzerolabs/typescript-configuration": "0.0.21"
24
24
  },
25
25
  "publishConfig": {
26
26
  "access": "restricted",
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Dependencies, DependencyNode } from '@layerzerolabs/dependency-graph';
1
+ import type { DependencyNode } from '@layerzerolabs/dependency-graph';
2
2
 
3
3
  /**
4
4
  * A registrar is a simple interface for an object that provides the ability to traverse the dependency graph.
@@ -6,14 +6,64 @@ import type { Dependencies, DependencyNode } from '@layerzerolabs/dependency-gra
6
6
  * of the graph.
7
7
  */
8
8
  export interface Registrar<ReturnType> {
9
- traverseDependencies: (rootNode: DependencyNode<any, any>) => Promise<ReturnType>;
9
+ traverseDependencies: (rootNode: DependencyNode) => Promise<ReturnType>;
10
10
  }
11
11
  export type NodeHandlerFunction = (
12
- node: DependencyNode<any, Dependencies>,
12
+ node: DependencyNode,
13
+ ancestry: DependencyNode[],
13
14
  ) => Promise<{ key: string; value: any }>;
14
- export type NodePreHandlerFunction = (
15
- node: DependencyNode<any, Dependencies>,
16
- ) => DependencyNode<any, Dependencies>;
15
+ export type NodePreHandlerFunction = (node: DependencyNode) => DependencyNode;
16
+
17
+ const buildAncestorsByNodeName = (node: DependencyNode, prehandler: NodePreHandlerFunction) => {
18
+ const ancestorsByNodeName = new Map<string, Set<DependencyNode>>();
19
+ const dependentCountByNodeName = new Map<string, number>();
20
+
21
+ // If A depends on B and C, B depends on C, we initialize with:
22
+ // ancestorsByNodeName: { A: Set([]), B: Set([]), C: Set([]) }
23
+ // dependentsCountByNodeName: { B: 1, C: 2 }
24
+ const initializeMaps = (node: DependencyNode) => {
25
+ ancestorsByNodeName.set(node.name, new Set([]));
26
+ for (const dependencyValue of Object.values(node.dependencies)) {
27
+ const inDegree = dependentCountByNodeName.get(dependencyValue.name) || 0;
28
+ dependentCountByNodeName.set(dependencyValue.name, inDegree + 1);
29
+ if (!ancestorsByNodeName.has(dependencyValue.name)) {
30
+ initializeMaps(dependencyValue);
31
+ }
32
+ }
33
+ };
34
+ initializeMaps(node);
35
+
36
+ // topological sorted bfs exploration
37
+ // induction by level, initial level has correct initial state, build level + 1
38
+ // bfs is used to build ancestry correctly
39
+ // this should actually be a queue, but its probably fine for now
40
+ let queue = [prehandler(node)];
41
+ while (queue.length > 0) {
42
+ const curNode = queue.shift()!;
43
+
44
+ // add the new processable dependencies to the queue, update their state.
45
+ for (const dependencyValue of Object.values(curNode.dependencies)) {
46
+ const inDegree = dependentCountByNodeName.get(dependencyValue.name)!;
47
+ // We are the last edge missing in the graph for this dependency -> we can process it after us.
48
+ if (inDegree === 1) {
49
+ const preHandledDependency = prehandler(dependencyValue);
50
+ queue.push(preHandledDependency);
51
+ }
52
+ // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.
53
+ dependentCountByNodeName.set(dependencyValue.name, inDegree - 1);
54
+
55
+ // add our ancestry to them, js sets maintain order of insertion, this means their ancestors are in distance order due to BFS properties.
56
+ const childAncestry = ancestorsByNodeName.get(dependencyValue.name)!;
57
+ const nodeAncestry = ancestorsByNodeName.get(curNode.name)!;
58
+ childAncestry.add(curNode);
59
+ for (const ancestor of nodeAncestry) {
60
+ childAncestry.add(ancestor);
61
+ }
62
+ }
63
+ }
64
+
65
+ return ancestorsByNodeName;
66
+ };
17
67
 
18
68
  /**
19
69
  * Performs a depth-first-search on a tree of dependency nodes, and returns a function
@@ -28,55 +78,39 @@ export type NodePreHandlerFunction = (
28
78
  * @param prehandler a function that accepts a node and returns a node. will be use to pre-process the graph
29
79
  * @returns a resolver function
30
80
  */
31
- export const dfs = <DependencyName extends string, ReturnTypes>(
32
- node: DependencyNode<DependencyName, Dependencies>,
81
+ export const dfs = <ReturnTypes>(
82
+ node: DependencyNode,
33
83
  handler: NodeHandlerFunction,
34
84
  prehandler: NodePreHandlerFunction = (node) => node,
35
- _visited: Map<string, Promise<void>> = new Map(),
36
85
  _returns: ReturnTypes = {} as any,
37
- _backtrack: DependencyNode<DependencyName, Dependencies>[] = [],
38
86
  ): (() => Promise<ReturnTypes>) => {
39
- const _node = prehandler(node);
40
- if (_visited.has(_node.name)) {
41
- return async () => {
42
- await _visited.get(_node.name);
43
- return _returns;
44
- };
45
- }
46
- let resolver: (value: void | PromiseLike<void>) => void;
87
+ const ancestorsByNodeName = buildAncestorsByNodeName(node, prehandler);
47
88
 
48
- _visited.set(
49
- _node.name,
50
- new Promise<void>((res) => {
51
- resolver = res;
52
- }),
53
- );
89
+ // Maintains Map<node.name, Promise<void>> -> the promise to resolve the node, for all nodes.
90
+ const nodeResolverPromises = new Map<string, Promise<void>>();
91
+ const resolveNode = async (node: DependencyNode) => {
92
+ const prehandledNode = prehandler(node);
93
+ // first wait for all its children
94
+ const childrenPromises = [];
95
+ for (const dependencyValue of Object.values(prehandledNode.dependencies)) {
96
+ // Grab the cached promise or create it, we want to have only 1 handler promise for each node.
97
+ if (!nodeResolverPromises.has(dependencyValue.name)) {
98
+ nodeResolverPromises.set(dependencyValue.name, resolveNode(dependencyValue));
99
+ }
100
+ childrenPromises.push(nodeResolverPromises.get(dependencyValue.name)!);
101
+ }
102
+ await Promise.all(childrenPromises);
54
103
 
55
- // Resolve dependencies first
56
- const resolverFunctions: (() => Promise<any>)[] = [];
57
- if (_node.dependencies) {
58
- const nextBacktrack = [..._backtrack, _node];
59
- for (const [_, dependencyValue] of Object.entries(_node.dependencies)) {
60
- resolverFunctions.push(
61
- dfs(dependencyValue, handler, prehandler, _visited, _returns, nextBacktrack),
62
- );
104
+ // Then, you can process yourself
105
+ const ancestry = ancestorsByNodeName.get(prehandledNode.name)!;
106
+ const regRes = await handler(node, [...ancestry]);
107
+ if (regRes) {
108
+ ((_returns as any)[regRes.key] ??= {})[prehandledNode.name] = regRes.value;
63
109
  }
64
- }
110
+ };
65
111
 
66
112
  return async () => {
67
- try {
68
- await Promise.all(resolverFunctions.map((f) => f()));
69
- const regRes = await handler(_node);
70
- if (regRes) {
71
- ((_returns as any)[regRes.key] ??= {})[_node.name] = regRes.value;
72
- }
73
- resolver();
74
- return _returns;
75
- } catch (error: any) {
76
- console.error(
77
- `Failed at ${_backtrack.map((n) => n.name).join('/')}\n${error?.message}`,
78
- );
79
- throw error;
80
- }
113
+ await resolveNode(node);
114
+ return _returns;
81
115
  };
82
116
  };
package/test/dfs.test.ts CHANGED
@@ -2,7 +2,7 @@ import { describe, expect, test } from 'vitest';
2
2
 
3
3
  import { Dependencies, DependencyNode } from '@layerzerolabs/dependency-graph';
4
4
 
5
- import { dfs } from '../src/';
5
+ import { dfs } from '../src';
6
6
 
7
7
  const SimpleClassA = class<
8
8
  Name extends string,