@layerzerolabs/dfs 0.0.30 → 0.0.31

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.
@@ -10,9 +10,9 @@
10
10
  CLI Cleaning output folder
11
11
  CJS Build start
12
12
  ESM Build start
13
- CJS dist/index.cjs 3.27 KB
14
- CJS dist/index.cjs.map 11.45 KB
15
- CJS ⚡️ Build success in 152ms
16
- ESM dist/index.js 3.25 KB
17
- ESM dist/index.js.map 11.45 KB
18
- ESM ⚡️ Build success in 153ms
13
+ ESM dist/index.js 3.42 KB
14
+ ESM dist/index.js.map 11.75 KB
15
+ ESM ⚡️ Build success in 101ms
16
+ CJS dist/index.cjs 3.43 KB
17
+ CJS dist/index.cjs.map 11.76 KB
18
+ CJS ⚡️ Build success in 101ms
@@ -5,11 +5,11 @@
5
5
 
6
6
   RUN  v3.2.3 /home/runner/work/monorepo-internal/monorepo-internal/packages/framework/dfs
7
7
 
8
- ✓ test/dfs.test.ts (7 tests) 605ms
9
- ✓ DI Depth-first-search > The handlers for each of a node's dependencies should be completed before that node  302ms
8
+ ✓ test/dfs.test.ts (7 tests) 573ms
9
+ ✓ DI Depth-first-search > The handlers for each of a node's dependencies should be completed before that node  303ms
10
10
 
11
11
   Test Files  1 passed (1)
12
12
   Tests  7 passed (7)
13
-  Start at  00:32:10
14
-  Duration  1.36s (transform 225ms, setup 0ms, collect 211ms, tests 605ms, environment 0ms, prepare 331ms)
13
+  Start at  22:01:43
14
+  Duration  962ms (transform 69ms, setup 0ms, collect 68ms, tests 573ms, environment 0ms, prepare 90ms)
15
15
 
package/dist/index.cjs CHANGED
@@ -17,14 +17,16 @@ var buildAncestryDistanceIndex = /* @__PURE__ */ __name((node, prehandler) => {
17
17
  const initializeMaps = /* @__PURE__ */ __name((cur) => {
18
18
  ancestryDistanceIndex.set(cur.name, /* @__PURE__ */ new Map());
19
19
  for (const dep of Object.values(cur.dependencies)) {
20
- const inDegree = inDegreeByNodeName.get(dep.name) || 0;
21
- inDegreeByNodeName.set(dep.name, inDegree + 1);
22
- if (!ancestryDistanceIndex.has(dep.name)) initializeMaps(dep);
20
+ const handledDep = prehandler(dep);
21
+ const inDegree = inDegreeByNodeName.get(handledDep.name) || 0;
22
+ inDegreeByNodeName.set(handledDep.name, inDegree + 1);
23
+ if (!ancestryDistanceIndex.has(handledDep.name)) initializeMaps(handledDep);
23
24
  }
24
25
  }, "initializeMaps");
25
- initializeMaps(node);
26
+ const handled = prehandler(node);
27
+ initializeMaps(handled);
26
28
  let queue = [
27
- prehandler(node)
29
+ handled
28
30
  ];
29
31
  while (queue.length > 0) {
30
32
  const curNode = queue.shift();
@@ -36,12 +38,13 @@ var buildAncestryDistanceIndex = /* @__PURE__ */ __name((node, prehandler) => {
36
38
  ...ancestryDistanceIndex.get(curNode.name)
37
39
  ]);
38
40
  for (const dep of Object.values(curNode.dependencies)) {
39
- const inDegree = inDegreeByNodeName.get(dep.name);
41
+ const handledDep = prehandler(dep);
42
+ const inDegree = inDegreeByNodeName.get(handledDep.name);
40
43
  if (inDegree === 1) {
41
- queue.push(prehandler(dep));
44
+ queue.push(handledDep);
42
45
  }
43
- inDegreeByNodeName.set(dep.name, inDegree - 1);
44
- const childDistances = ancestryDistanceIndex.get(dep.name);
46
+ inDegreeByNodeName.set(handledDep.name, inDegree - 1);
47
+ const childDistances = ancestryDistanceIndex.get(handledDep.name);
45
48
  mergeAncestorDistances(currentMinimalDistances, childDistances);
46
49
  }
47
50
  }
@@ -67,7 +70,7 @@ var dfs = /* @__PURE__ */ __name((node, handler, prehandler = (node2) => node2,
67
70
  await Promise.all(childrenPromises);
68
71
  const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name);
69
72
  const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);
70
- const regRes = await handler(node2, sortedAncestors.map(([node3, _]) => node3));
73
+ const regRes = await handler(prehandledNode, sortedAncestors.map(([node3, _]) => node3));
71
74
  if (regRes) {
72
75
  (_returns[regRes.key] ??= {})[prehandledNode.name] = regRes.value;
73
76
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["mergeAncestorDistances","parentDistances","childDistances","ancestor","parentDist","candidate","current","get","Infinity","set","Math","min","buildAncestryDistanceIndex","node","prehandler","ancestryDistanceIndex","Map","inDegreeByNodeName","initializeMaps","cur","name","dep","Object","values","dependencies","inDegree","has","queue","length","curNode","shift","currentMinimalDistances","push","nodeName","Error","dfs","handler","_returns","nodeResolverPromises","resolveNode","prehandledNode","childrenPromises","dependencyValue","Promise","all","minimalDistances","sortedAncestors","Array","from","entries","sort","a","b","regRes","map","_","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\n// Map of ancestor node -> minimal hop distance\ntype AncestorDistanceByNode = Map<DependencyNode, number>;\n// Index from node name -> its ancestor distance map\ntype AncestryDistanceIndex = Map<string, AncestorDistanceByNode>;\n\n/**\n * In-place merge of minimal ancestry distances.\n *\n * childDistances holds minimal distances from the child to each ancestor (keyed by DependencyNode).\n * For every entry in parentDistances, we update the child's map with (parentDist + 1),\n * keeping the minimal value if the ancestor already exists.\n */\nconst mergeAncestorDistances = (\n parentDistances: AncestorDistanceByNode,\n childDistances: AncestorDistanceByNode,\n) => {\n for (const [ancestor, parentDist] of parentDistances) {\n const candidate = parentDist + 1;\n const current = childDistances.get(ancestor) || Infinity;\n childDistances.set(ancestor, Math.min(current, candidate));\n }\n};\n\n/**\n * Builds a minimal ancestry distance index for all nodes reachable from the root.\n *\n * Returns a Map: node.name -> Map<DependencyNode, distance> where distance is the minimal hop count\n * from the node to that ancestor. We perform a Kahn-style BFS over the DAG, and for each edge\n * curNode -> dep we merge curNode's minimal distances into the dependency with +1 hop and take the minimum.\n */\nconst buildAncestryDistanceIndex = (\n node: DependencyNode,\n prehandler: NodePreHandlerFunction,\n): AncestryDistanceIndex => {\n const ancestryDistanceIndex: AncestryDistanceIndex = new Map();\n const inDegreeByNodeName = new Map<string, number>();\n\n // If A depends on B and C, B depends on C, we initialize with:\n // ancestryDistanceIndex: { A: Map(), B: Map(), C: Map() }\n // inDegreeByNodeName: { B: 1, C: 2 }\n const initializeMaps = (cur: DependencyNode) => {\n ancestryDistanceIndex.set(cur.name, new Map());\n for (const dep of Object.values(cur.dependencies)) {\n const inDegree = inDegreeByNodeName.get(dep.name) || 0;\n inDegreeByNodeName.set(dep.name, inDegree + 1);\n if (!ancestryDistanceIndex.has(dep.name)) initializeMaps(dep);\n }\n };\n initializeMaps(node);\n\n // Kahn-style topological BFS accumulating minimal distance ancestors\n let queue = [prehandler(node)];\n while (queue.length > 0) {\n const curNode = queue.shift()!;\n // Include self with distance 0, then extend with the already known minimal distances\n const currentMinimalDistances: AncestorDistanceByNode = new Map([\n [curNode, 0],\n ...ancestryDistanceIndex.get(curNode.name)!,\n ]);\n // Add the new processable dependencies to the queue, update their state.\n for (const dep of Object.values(curNode.dependencies)) {\n const inDegree = inDegreeByNodeName.get(dep.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 queue.push(prehandler(dep));\n }\n // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.\n inDegreeByNodeName.set(dep.name, inDegree - 1);\n\n // Merge curNode's minimal distances (+1 hop) into the dependency's minimal distances\n const childDistances = ancestryDistanceIndex.get(dep.name)!;\n mergeAncestorDistances(currentMinimalDistances, childDistances);\n }\n }\n\n for (const [nodeName, inDegree] of inDegreeByNodeName) {\n if (inDegree !== 0) {\n throw new Error(\n `node ${nodeName} has in-degree ${inDegree}, this indicates a cycle in the graph containing the node`,\n );\n }\n }\n\n return ancestryDistanceIndex;\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 node's ancestors are sorted by non-decreasing minimal distance.\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 used 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 ancestryDistanceIndex = buildAncestryDistanceIndex(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. Build ancestry sorted by non-decreasing minimal distance.\n const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name)!;\n const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);\n const regRes = await handler(\n node,\n sortedAncestors.map(([node, _]) => node),\n );\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"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["mergeAncestorDistances","parentDistances","childDistances","ancestor","parentDist","candidate","current","get","Infinity","set","Math","min","buildAncestryDistanceIndex","node","prehandler","ancestryDistanceIndex","Map","inDegreeByNodeName","initializeMaps","cur","name","dep","Object","values","dependencies","handledDep","inDegree","has","handled","queue","length","curNode","shift","currentMinimalDistances","push","nodeName","Error","dfs","handler","_returns","nodeResolverPromises","resolveNode","prehandledNode","childrenPromises","dependencyValue","Promise","all","minimalDistances","sortedAncestors","Array","from","entries","sort","a","b","regRes","map","_","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\n// Map of ancestor node -> minimal hop distance\ntype AncestorDistanceByNode = Map<DependencyNode, number>;\n// Index from node name -> its ancestor distance map\ntype AncestryDistanceIndex = Map<string, AncestorDistanceByNode>;\n\n/**\n * In-place merge of minimal ancestry distances.\n *\n * childDistances holds minimal distances from the child to each ancestor (keyed by DependencyNode).\n * For every entry in parentDistances, we update the child's map with (parentDist + 1),\n * keeping the minimal value if the ancestor already exists.\n */\nconst mergeAncestorDistances = (\n parentDistances: AncestorDistanceByNode,\n childDistances: AncestorDistanceByNode,\n) => {\n for (const [ancestor, parentDist] of parentDistances) {\n const candidate = parentDist + 1;\n const current = childDistances.get(ancestor) || Infinity;\n childDistances.set(ancestor, Math.min(current, candidate));\n }\n};\n\n/**\n * Builds a minimal ancestry distance index for all nodes reachable from the root.\n *\n * Returns a Map: node.name -> Map<DependencyNode, distance> where distance is the minimal hop count\n * from the node to that ancestor. We perform a Kahn-style BFS over the DAG, and for each edge\n * curNode -> dep we merge curNode's minimal distances into the dependency with +1 hop and take the minimum.\n */\nconst buildAncestryDistanceIndex = (\n node: DependencyNode,\n prehandler: NodePreHandlerFunction,\n): AncestryDistanceIndex => {\n const ancestryDistanceIndex: AncestryDistanceIndex = new Map();\n const inDegreeByNodeName = new Map<string, number>();\n\n // If A depends on B and C, B depends on C, we initialize with:\n // ancestryDistanceIndex: { A: Map(), B: Map(), C: Map() }\n // inDegreeByNodeName: { B: 1, C: 2 }\n const initializeMaps = (cur: DependencyNode) => {\n ancestryDistanceIndex.set(cur.name, new Map());\n for (const dep of Object.values(cur.dependencies)) {\n const handledDep = prehandler(dep);\n const inDegree = inDegreeByNodeName.get(handledDep.name) || 0;\n inDegreeByNodeName.set(handledDep.name, inDegree + 1);\n if (!ancestryDistanceIndex.has(handledDep.name)) initializeMaps(handledDep);\n }\n };\n\n const handled = prehandler(node);\n\n initializeMaps(handled);\n\n // Kahn-style topological BFS accumulating minimal distance ancestors\n let queue = [handled];\n while (queue.length > 0) {\n const curNode = queue.shift()!;\n // Include self with distance 0, then extend with the already known minimal distances\n const currentMinimalDistances: AncestorDistanceByNode = new Map([\n [curNode, 0],\n ...ancestryDistanceIndex.get(curNode.name)!,\n ]);\n // Add the new processable dependencies to the queue, update their state.\n for (const dep of Object.values(curNode.dependencies)) {\n const handledDep = prehandler(dep);\n const inDegree = inDegreeByNodeName.get(handledDep.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 queue.push(handledDep);\n }\n // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.\n inDegreeByNodeName.set(handledDep.name, inDegree - 1);\n\n // Merge curNode's minimal distances (+1 hop) into the dependency's minimal distances\n const childDistances = ancestryDistanceIndex.get(handledDep.name)!;\n mergeAncestorDistances(currentMinimalDistances, childDistances);\n }\n }\n\n for (const [nodeName, inDegree] of inDegreeByNodeName) {\n if (inDegree !== 0) {\n throw new Error(\n `node ${nodeName} has in-degree ${inDegree}, this indicates a cycle in the graph containing the node`,\n );\n }\n }\n\n return ancestryDistanceIndex;\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 node's ancestors are sorted by non-decreasing minimal distance.\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 used 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 ancestryDistanceIndex = buildAncestryDistanceIndex(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. Build ancestry sorted by non-decreasing minimal distance.\n const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name)!;\n const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);\n const regRes = await handler(\n prehandledNode,\n sortedAncestors.map(([node, _]) => node),\n );\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"]}
@@ -1 +1 @@
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;AAwF9E;;;;;;;;;;;;;GAaG;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,CAkC7B,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;AA6F9E;;;;;;;;;;;;;GAaG;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,CAkC7B,CAAC"}
package/dist/index.js CHANGED
@@ -15,14 +15,16 @@ var buildAncestryDistanceIndex = /* @__PURE__ */ __name((node, prehandler) => {
15
15
  const initializeMaps = /* @__PURE__ */ __name((cur) => {
16
16
  ancestryDistanceIndex.set(cur.name, /* @__PURE__ */ new Map());
17
17
  for (const dep of Object.values(cur.dependencies)) {
18
- const inDegree = inDegreeByNodeName.get(dep.name) || 0;
19
- inDegreeByNodeName.set(dep.name, inDegree + 1);
20
- if (!ancestryDistanceIndex.has(dep.name)) initializeMaps(dep);
18
+ const handledDep = prehandler(dep);
19
+ const inDegree = inDegreeByNodeName.get(handledDep.name) || 0;
20
+ inDegreeByNodeName.set(handledDep.name, inDegree + 1);
21
+ if (!ancestryDistanceIndex.has(handledDep.name)) initializeMaps(handledDep);
21
22
  }
22
23
  }, "initializeMaps");
23
- initializeMaps(node);
24
+ const handled = prehandler(node);
25
+ initializeMaps(handled);
24
26
  let queue = [
25
- prehandler(node)
27
+ handled
26
28
  ];
27
29
  while (queue.length > 0) {
28
30
  const curNode = queue.shift();
@@ -34,12 +36,13 @@ var buildAncestryDistanceIndex = /* @__PURE__ */ __name((node, prehandler) => {
34
36
  ...ancestryDistanceIndex.get(curNode.name)
35
37
  ]);
36
38
  for (const dep of Object.values(curNode.dependencies)) {
37
- const inDegree = inDegreeByNodeName.get(dep.name);
39
+ const handledDep = prehandler(dep);
40
+ const inDegree = inDegreeByNodeName.get(handledDep.name);
38
41
  if (inDegree === 1) {
39
- queue.push(prehandler(dep));
42
+ queue.push(handledDep);
40
43
  }
41
- inDegreeByNodeName.set(dep.name, inDegree - 1);
42
- const childDistances = ancestryDistanceIndex.get(dep.name);
44
+ inDegreeByNodeName.set(handledDep.name, inDegree - 1);
45
+ const childDistances = ancestryDistanceIndex.get(handledDep.name);
43
46
  mergeAncestorDistances(currentMinimalDistances, childDistances);
44
47
  }
45
48
  }
@@ -65,7 +68,7 @@ var dfs = /* @__PURE__ */ __name((node, handler, prehandler = (node2) => node2,
65
68
  await Promise.all(childrenPromises);
66
69
  const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name);
67
70
  const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);
68
- const regRes = await handler(node2, sortedAncestors.map(([node3, _]) => node3));
71
+ const regRes = await handler(prehandledNode, sortedAncestors.map(([node3, _]) => node3));
69
72
  if (regRes) {
70
73
  (_returns[regRes.key] ??= {})[prehandledNode.name] = regRes.value;
71
74
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["mergeAncestorDistances","parentDistances","childDistances","ancestor","parentDist","candidate","current","get","Infinity","set","Math","min","buildAncestryDistanceIndex","node","prehandler","ancestryDistanceIndex","Map","inDegreeByNodeName","initializeMaps","cur","name","dep","Object","values","dependencies","inDegree","has","queue","length","curNode","shift","currentMinimalDistances","push","nodeName","Error","dfs","handler","_returns","nodeResolverPromises","resolveNode","prehandledNode","childrenPromises","dependencyValue","Promise","all","minimalDistances","sortedAncestors","Array","from","entries","sort","a","b","regRes","map","_","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\n// Map of ancestor node -> minimal hop distance\ntype AncestorDistanceByNode = Map<DependencyNode, number>;\n// Index from node name -> its ancestor distance map\ntype AncestryDistanceIndex = Map<string, AncestorDistanceByNode>;\n\n/**\n * In-place merge of minimal ancestry distances.\n *\n * childDistances holds minimal distances from the child to each ancestor (keyed by DependencyNode).\n * For every entry in parentDistances, we update the child's map with (parentDist + 1),\n * keeping the minimal value if the ancestor already exists.\n */\nconst mergeAncestorDistances = (\n parentDistances: AncestorDistanceByNode,\n childDistances: AncestorDistanceByNode,\n) => {\n for (const [ancestor, parentDist] of parentDistances) {\n const candidate = parentDist + 1;\n const current = childDistances.get(ancestor) || Infinity;\n childDistances.set(ancestor, Math.min(current, candidate));\n }\n};\n\n/**\n * Builds a minimal ancestry distance index for all nodes reachable from the root.\n *\n * Returns a Map: node.name -> Map<DependencyNode, distance> where distance is the minimal hop count\n * from the node to that ancestor. We perform a Kahn-style BFS over the DAG, and for each edge\n * curNode -> dep we merge curNode's minimal distances into the dependency with +1 hop and take the minimum.\n */\nconst buildAncestryDistanceIndex = (\n node: DependencyNode,\n prehandler: NodePreHandlerFunction,\n): AncestryDistanceIndex => {\n const ancestryDistanceIndex: AncestryDistanceIndex = new Map();\n const inDegreeByNodeName = new Map<string, number>();\n\n // If A depends on B and C, B depends on C, we initialize with:\n // ancestryDistanceIndex: { A: Map(), B: Map(), C: Map() }\n // inDegreeByNodeName: { B: 1, C: 2 }\n const initializeMaps = (cur: DependencyNode) => {\n ancestryDistanceIndex.set(cur.name, new Map());\n for (const dep of Object.values(cur.dependencies)) {\n const inDegree = inDegreeByNodeName.get(dep.name) || 0;\n inDegreeByNodeName.set(dep.name, inDegree + 1);\n if (!ancestryDistanceIndex.has(dep.name)) initializeMaps(dep);\n }\n };\n initializeMaps(node);\n\n // Kahn-style topological BFS accumulating minimal distance ancestors\n let queue = [prehandler(node)];\n while (queue.length > 0) {\n const curNode = queue.shift()!;\n // Include self with distance 0, then extend with the already known minimal distances\n const currentMinimalDistances: AncestorDistanceByNode = new Map([\n [curNode, 0],\n ...ancestryDistanceIndex.get(curNode.name)!,\n ]);\n // Add the new processable dependencies to the queue, update their state.\n for (const dep of Object.values(curNode.dependencies)) {\n const inDegree = inDegreeByNodeName.get(dep.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 queue.push(prehandler(dep));\n }\n // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.\n inDegreeByNodeName.set(dep.name, inDegree - 1);\n\n // Merge curNode's minimal distances (+1 hop) into the dependency's minimal distances\n const childDistances = ancestryDistanceIndex.get(dep.name)!;\n mergeAncestorDistances(currentMinimalDistances, childDistances);\n }\n }\n\n for (const [nodeName, inDegree] of inDegreeByNodeName) {\n if (inDegree !== 0) {\n throw new Error(\n `node ${nodeName} has in-degree ${inDegree}, this indicates a cycle in the graph containing the node`,\n );\n }\n }\n\n return ancestryDistanceIndex;\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 node's ancestors are sorted by non-decreasing minimal distance.\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 used 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 ancestryDistanceIndex = buildAncestryDistanceIndex(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. Build ancestry sorted by non-decreasing minimal distance.\n const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name)!;\n const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);\n const regRes = await handler(\n node,\n sortedAncestors.map(([node, _]) => node),\n );\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"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["mergeAncestorDistances","parentDistances","childDistances","ancestor","parentDist","candidate","current","get","Infinity","set","Math","min","buildAncestryDistanceIndex","node","prehandler","ancestryDistanceIndex","Map","inDegreeByNodeName","initializeMaps","cur","name","dep","Object","values","dependencies","handledDep","inDegree","has","handled","queue","length","curNode","shift","currentMinimalDistances","push","nodeName","Error","dfs","handler","_returns","nodeResolverPromises","resolveNode","prehandledNode","childrenPromises","dependencyValue","Promise","all","minimalDistances","sortedAncestors","Array","from","entries","sort","a","b","regRes","map","_","key","value"],"mappings":";;;;AA4BA,IAAMA,sBAAAA,mBAAyB,MAAA,CAAA,CAC3BC,eAAAA,EACAC,cAAAA,KAAAA;AAEA,EAAA,KAAA,MAAW,CAACC,QAAAA,EAAUC,UAAAA,CAAAA,IAAeH,eAAAA,EAAiB;AAClD,IAAA,MAAMI,YAAYD,UAAAA,GAAa,CAAA;AAC/B,IAAA,MAAME,OAAAA,GAAUJ,cAAAA,CAAeK,GAAAA,CAAIJ,QAAAA,CAAAA,IAAaK,QAAAA;AAChDN,IAAAA,cAAAA,CAAeO,IAAIN,QAAAA,EAAUO,IAAAA,CAAKC,GAAAA,CAAIL,OAAAA,EAASD,SAAAA,CAAAA,CAAAA;AACnD,EAAA;AACJ,CAAA,EAT+B,wBAAA,CAAA;AAkB/B,IAAMO,0BAAAA,mBAA6B,MAAA,CAAA,CAC/BC,IAAAA,EACAC,UAAAA,KAAAA;AAEA,EAAA,MAAMC,qBAAAA,uBAAmDC,GAAAA,EAAAA;AACzD,EAAA,MAAMC,kBAAAA,uBAAyBD,GAAAA,EAAAA;AAK/B,EAAA,MAAME,cAAAA,2BAAkBC,GAAAA,KAAAA;AACpBJ,IAAAA,qBAAAA,CAAsBN,GAAAA,CAAIU,GAAAA,CAAIC,IAAAA,kBAAM,IAAIJ,KAAAA,CAAAA;AACxC,IAAA,KAAA,MAAWK,GAAAA,IAAOC,MAAAA,CAAOC,MAAAA,CAAOJ,GAAAA,CAAIK,YAAY,CAAA,EAAG;AAC/C,MAAA,MAAMC,UAAAA,GAAaX,WAAWO,GAAAA,CAAAA;AAC9B,MAAA,MAAMK,QAAAA,GAAWT,kBAAAA,CAAmBV,GAAAA,CAAIkB,UAAAA,CAAWL,IAAI,CAAA,IAAK,CAAA;AAC5DH,MAAAA,kBAAAA,CAAmBR,GAAAA,CAAIgB,UAAAA,CAAWL,IAAAA,EAAMM,QAAAA,GAAW,CAAA,CAAA;AACnD,MAAA,IAAI,CAACX,qBAAAA,CAAsBY,GAAAA,CAAIF,WAAWL,IAAI,CAAA,iBAAkBK,UAAAA,CAAAA;AACpE,IAAA;EACJ,CAAA,EARuB,gBAAA,CAAA;AAUvB,EAAA,MAAMG,OAAAA,GAAUd,WAAWD,IAAAA,CAAAA;AAE3BK,EAAAA,cAAAA,CAAeU,OAAAA,CAAAA;AAGf,EAAA,IAAIC,KAAAA,GAAQ;AAACD,IAAAA;;AACb,EAAA,OAAOC,KAAAA,CAAMC,SAAS,CAAA,EAAG;AACrB,IAAA,MAAMC,OAAAA,GAAUF,MAAMG,KAAAA,EAAK;AAE3B,IAAA,MAAMC,uBAAAA,GAAkD,IAAIjB,GAAAA,CAAI;AAC5D,MAAA;AAACe,QAAAA,OAAAA;AAAS,QAAA;;SACPhB,qBAAAA,CAAsBR,GAAAA,CAAIwB,QAAQX,IAAI;AAC5C,KAAA,CAAA;AAED,IAAA,KAAA,MAAWC,GAAAA,IAAOC,MAAAA,CAAOC,MAAAA,CAAOQ,OAAAA,CAAQP,YAAY,CAAA,EAAG;AACnD,MAAA,MAAMC,UAAAA,GAAaX,WAAWO,GAAAA,CAAAA;AAC9B,MAAA,MAAMK,QAAAA,GAAWT,kBAAAA,CAAmBV,GAAAA,CAAIkB,UAAAA,CAAWL,IAAI,CAAA;AAEvD,MAAA,IAAIM,aAAa,CAAA,EAAG;AAChBG,QAAAA,KAAAA,CAAMK,KAAKT,UAAAA,CAAAA;AACf,MAAA;AAEAR,MAAAA,kBAAAA,CAAmBR,GAAAA,CAAIgB,UAAAA,CAAWL,IAAAA,EAAMM,QAAAA,GAAW,CAAA,CAAA;AAGnD,MAAA,MAAMxB,cAAAA,GAAiBa,qBAAAA,CAAsBR,GAAAA,CAAIkB,UAAAA,CAAWL,IAAI,CAAA;AAChEpB,MAAAA,sBAAAA,CAAuBiC,yBAAyB/B,cAAAA,CAAAA;AACpD,IAAA;AACJ,EAAA;AAEA,EAAA,KAAA,MAAW,CAACiC,QAAAA,EAAUT,QAAAA,CAAAA,IAAaT,kBAAAA,EAAoB;AACnD,IAAA,IAAIS,aAAa,CAAA,EAAG;AAChB,MAAA,MAAM,IAAIU,KAAAA,CACN,CAAA,KAAA,EAAQD,QAAAA,CAAAA,eAAAA,EAA0BT,QAAAA,CAAAA,yDAAAA,CAAmE,CAAA;AAE7G,IAAA;AACJ,EAAA;AAEA,EAAA,OAAOX,qBAAAA;AACX,CAAA,EA3DmC,4BAAA,CAAA;AA2E5B,IAAMsB,GAAAA,mBAAM,MAAA,CAAA,CACfxB,IAAAA,EACAyB,OAAAA,EACAxB,UAAAA,GAAqC,CAACD,KAAAA,KAASA,KAAAA,EAC/C0B,QAAAA,GAAwB,EAAC,KAAQ;AAEjC,EAAA,MAAMxB,qBAAAA,GAAwBH,0BAAAA,CAA2BC,IAAAA,EAAMC,UAAAA,CAAAA;AAG/D,EAAA,MAAM0B,oBAAAA,uBAA2BxB,GAAAA,EAAAA;AACjC,EAAA,MAAMyB,WAAAA,iCAAqB5B,KAAAA,KAAAA;AACvB,IAAA,MAAM6B,cAAAA,GAAiB5B,WAAWD,KAAAA,CAAAA;AAElC,IAAA,MAAM8B,mBAAmB,EAAA;AACzB,IAAA,KAAA,MAAWC,eAAAA,IAAmBtB,MAAAA,CAAOC,MAAAA,CAAOmB,cAAAA,CAAelB,YAAY,CAAA,EAAG;AAEtE,MAAA,IAAI,CAACgB,oBAAAA,CAAqBb,GAAAA,CAAIiB,eAAAA,CAAgBxB,IAAI,CAAA,EAAG;AACjDoB,QAAAA,oBAAAA,CAAqB/B,GAAAA,CAAImC,eAAAA,CAAgBxB,IAAAA,EAAMqB,WAAAA,CAAYG,eAAAA,CAAAA,CAAAA;AAC/D,MAAA;AACAD,MAAAA,gBAAAA,CAAiBT,IAAAA,CAAKM,oBAAAA,CAAqBjC,GAAAA,CAAIqC,eAAAA,CAAgBxB,IAAI,CAAA,CAAA;AACvE,IAAA;AACA,IAAA,MAAMyB,OAAAA,CAAQC,IAAIH,gBAAAA,CAAAA;AAGlB,IAAA,MAAMI,gBAAAA,GAAmBhC,qBAAAA,CAAsBR,GAAAA,CAAImC,cAAAA,CAAetB,IAAI,CAAA;AACtE,IAAA,MAAM4B,kBAAkBC,KAAAA,CAAMC,IAAAA,CAAKH,gBAAAA,CAAiBI,OAAAA,EAAO,CAAA,CAAIC,IAAAA,CAAK,CAACC,CAAAA,EAAGC,MAAMD,CAAAA,CAAE,CAAA,CAAA,GAAKC,CAAAA,CAAE,CAAA,CAAE,CAAA;AACzF,IAAA,MAAMC,MAAAA,GAAS,MAAMjB,OAAAA,CACjBI,cAAAA,EACAM,eAAAA,CAAgBQ,GAAAA,CAAI,CAAC,CAAC3C,KAAAA,EAAM4C,CAAAA,CAAAA,KAAO5C,KAAAA,CAAAA,CAAAA;AAEvC,IAAA,IAAI0C,MAAAA,EAAQ;AACP,MAAA,CAAChB,QAAAA,CAAiBgB,OAAOG,GAAG,CAAA,KAAM,EAAC,EAAGhB,cAAAA,CAAetB,IAAI,CAAA,GAAImC,MAAAA,CAAOI,KAAAA;AACzE,IAAA;EACJ,CAAA,EAvBoB,aAAA,CAAA;AAyBpB,EAAA,OAAO,YAAA;AACH,IAAA,MAAMlB,YAAY5B,IAAAA,CAAAA;AAClB,IAAA,OAAO0B,QAAAA;AACX,EAAA,CAAA;AACJ,CAAA,EAvCmB,KAAA","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\n// Map of ancestor node -> minimal hop distance\ntype AncestorDistanceByNode = Map<DependencyNode, number>;\n// Index from node name -> its ancestor distance map\ntype AncestryDistanceIndex = Map<string, AncestorDistanceByNode>;\n\n/**\n * In-place merge of minimal ancestry distances.\n *\n * childDistances holds minimal distances from the child to each ancestor (keyed by DependencyNode).\n * For every entry in parentDistances, we update the child's map with (parentDist + 1),\n * keeping the minimal value if the ancestor already exists.\n */\nconst mergeAncestorDistances = (\n parentDistances: AncestorDistanceByNode,\n childDistances: AncestorDistanceByNode,\n) => {\n for (const [ancestor, parentDist] of parentDistances) {\n const candidate = parentDist + 1;\n const current = childDistances.get(ancestor) || Infinity;\n childDistances.set(ancestor, Math.min(current, candidate));\n }\n};\n\n/**\n * Builds a minimal ancestry distance index for all nodes reachable from the root.\n *\n * Returns a Map: node.name -> Map<DependencyNode, distance> where distance is the minimal hop count\n * from the node to that ancestor. We perform a Kahn-style BFS over the DAG, and for each edge\n * curNode -> dep we merge curNode's minimal distances into the dependency with +1 hop and take the minimum.\n */\nconst buildAncestryDistanceIndex = (\n node: DependencyNode,\n prehandler: NodePreHandlerFunction,\n): AncestryDistanceIndex => {\n const ancestryDistanceIndex: AncestryDistanceIndex = new Map();\n const inDegreeByNodeName = new Map<string, number>();\n\n // If A depends on B and C, B depends on C, we initialize with:\n // ancestryDistanceIndex: { A: Map(), B: Map(), C: Map() }\n // inDegreeByNodeName: { B: 1, C: 2 }\n const initializeMaps = (cur: DependencyNode) => {\n ancestryDistanceIndex.set(cur.name, new Map());\n for (const dep of Object.values(cur.dependencies)) {\n const handledDep = prehandler(dep);\n const inDegree = inDegreeByNodeName.get(handledDep.name) || 0;\n inDegreeByNodeName.set(handledDep.name, inDegree + 1);\n if (!ancestryDistanceIndex.has(handledDep.name)) initializeMaps(handledDep);\n }\n };\n\n const handled = prehandler(node);\n\n initializeMaps(handled);\n\n // Kahn-style topological BFS accumulating minimal distance ancestors\n let queue = [handled];\n while (queue.length > 0) {\n const curNode = queue.shift()!;\n // Include self with distance 0, then extend with the already known minimal distances\n const currentMinimalDistances: AncestorDistanceByNode = new Map([\n [curNode, 0],\n ...ancestryDistanceIndex.get(curNode.name)!,\n ]);\n // Add the new processable dependencies to the queue, update their state.\n for (const dep of Object.values(curNode.dependencies)) {\n const handledDep = prehandler(dep);\n const inDegree = inDegreeByNodeName.get(handledDep.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 queue.push(handledDep);\n }\n // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.\n inDegreeByNodeName.set(handledDep.name, inDegree - 1);\n\n // Merge curNode's minimal distances (+1 hop) into the dependency's minimal distances\n const childDistances = ancestryDistanceIndex.get(handledDep.name)!;\n mergeAncestorDistances(currentMinimalDistances, childDistances);\n }\n }\n\n for (const [nodeName, inDegree] of inDegreeByNodeName) {\n if (inDegree !== 0) {\n throw new Error(\n `node ${nodeName} has in-degree ${inDegree}, this indicates a cycle in the graph containing the node`,\n );\n }\n }\n\n return ancestryDistanceIndex;\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 node's ancestors are sorted by non-decreasing minimal distance.\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 used 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 ancestryDistanceIndex = buildAncestryDistanceIndex(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. Build ancestry sorted by non-decreasing minimal distance.\n const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name)!;\n const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);\n const regRes = await handler(\n prehandledNode,\n sortedAncestors.map(([node, _]) => node),\n );\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.30",
3
+ "version": "0.0.31",
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.30"
17
+ "@layerzerolabs/dependency-graph": "0.0.31"
18
18
  },
19
19
  "devDependencies": {
20
20
  "tsup": "^8.4.0",
21
21
  "vitest": "^3.2.3",
22
- "@layerzerolabs/tsup-configuration": "0.0.30",
23
- "@layerzerolabs/typescript-configuration": "0.0.30"
22
+ "@layerzerolabs/tsup-configuration": "0.0.31",
23
+ "@layerzerolabs/typescript-configuration": "0.0.31"
24
24
  },
25
25
  "publishConfig": {
26
26
  "access": "restricted",
package/src/index.ts CHANGED
@@ -57,15 +57,19 @@ const buildAncestryDistanceIndex = (
57
57
  const initializeMaps = (cur: DependencyNode) => {
58
58
  ancestryDistanceIndex.set(cur.name, new Map());
59
59
  for (const dep of Object.values(cur.dependencies)) {
60
- const inDegree = inDegreeByNodeName.get(dep.name) || 0;
61
- inDegreeByNodeName.set(dep.name, inDegree + 1);
62
- if (!ancestryDistanceIndex.has(dep.name)) initializeMaps(dep);
60
+ const handledDep = prehandler(dep);
61
+ const inDegree = inDegreeByNodeName.get(handledDep.name) || 0;
62
+ inDegreeByNodeName.set(handledDep.name, inDegree + 1);
63
+ if (!ancestryDistanceIndex.has(handledDep.name)) initializeMaps(handledDep);
63
64
  }
64
65
  };
65
- initializeMaps(node);
66
+
67
+ const handled = prehandler(node);
68
+
69
+ initializeMaps(handled);
66
70
 
67
71
  // Kahn-style topological BFS accumulating minimal distance ancestors
68
- let queue = [prehandler(node)];
72
+ let queue = [handled];
69
73
  while (queue.length > 0) {
70
74
  const curNode = queue.shift()!;
71
75
  // Include self with distance 0, then extend with the already known minimal distances
@@ -75,16 +79,17 @@ const buildAncestryDistanceIndex = (
75
79
  ]);
76
80
  // Add the new processable dependencies to the queue, update their state.
77
81
  for (const dep of Object.values(curNode.dependencies)) {
78
- const inDegree = inDegreeByNodeName.get(dep.name)!;
82
+ const handledDep = prehandler(dep);
83
+ const inDegree = inDegreeByNodeName.get(handledDep.name)!;
79
84
  // We are the last edge missing in the graph for this dependency -> we can process it after us.
80
85
  if (inDegree === 1) {
81
- queue.push(prehandler(dep));
86
+ queue.push(handledDep);
82
87
  }
83
88
  // Reduce the in-degree of the dependency -> it basically means that this edge got removed from the graph.
84
- inDegreeByNodeName.set(dep.name, inDegree - 1);
89
+ inDegreeByNodeName.set(handledDep.name, inDegree - 1);
85
90
 
86
91
  // Merge curNode's minimal distances (+1 hop) into the dependency's minimal distances
87
- const childDistances = ancestryDistanceIndex.get(dep.name)!;
92
+ const childDistances = ancestryDistanceIndex.get(handledDep.name)!;
88
93
  mergeAncestorDistances(currentMinimalDistances, childDistances);
89
94
  }
90
95
  }
@@ -141,7 +146,7 @@ export const dfs = <ReturnTypes>(
141
146
  const minimalDistances = ancestryDistanceIndex.get(prehandledNode.name)!;
142
147
  const sortedAncestors = Array.from(minimalDistances.entries()).sort((a, b) => a[1] - b[1]);
143
148
  const regRes = await handler(
144
- node,
149
+ prehandledNode,
145
150
  sortedAncestors.map(([node, _]) => node),
146
151
  );
147
152
  if (regRes) {