graphwise 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +81 -30
  2. package/dist/async/index.cjs +243 -0
  3. package/dist/async/index.cjs.map +1 -0
  4. package/dist/async/index.js +230 -0
  5. package/dist/async/index.js.map +1 -0
  6. package/dist/expansion/dfs-priority.d.ts +11 -0
  7. package/dist/expansion/dfs-priority.d.ts.map +1 -1
  8. package/dist/expansion/dome.d.ts +20 -0
  9. package/dist/expansion/dome.d.ts.map +1 -1
  10. package/dist/expansion/edge.d.ts +18 -0
  11. package/dist/expansion/edge.d.ts.map +1 -1
  12. package/dist/expansion/flux.d.ts +16 -0
  13. package/dist/expansion/flux.d.ts.map +1 -1
  14. package/dist/expansion/frontier-balanced.d.ts +11 -0
  15. package/dist/expansion/frontier-balanced.d.ts.map +1 -1
  16. package/dist/expansion/fuse.d.ts +16 -0
  17. package/dist/expansion/fuse.d.ts.map +1 -1
  18. package/dist/expansion/hae.d.ts +16 -0
  19. package/dist/expansion/hae.d.ts.map +1 -1
  20. package/dist/expansion/lace.d.ts +16 -0
  21. package/dist/expansion/lace.d.ts.map +1 -1
  22. package/dist/expansion/maze.d.ts +17 -0
  23. package/dist/expansion/maze.d.ts.map +1 -1
  24. package/dist/expansion/pipe.d.ts +16 -0
  25. package/dist/expansion/pipe.d.ts.map +1 -1
  26. package/dist/expansion/random-priority.d.ts +18 -0
  27. package/dist/expansion/random-priority.d.ts.map +1 -1
  28. package/dist/expansion/reach.d.ts +17 -0
  29. package/dist/expansion/reach.d.ts.map +1 -1
  30. package/dist/expansion/sage.d.ts +15 -0
  31. package/dist/expansion/sage.d.ts.map +1 -1
  32. package/dist/expansion/sift.d.ts +16 -0
  33. package/dist/expansion/sift.d.ts.map +1 -1
  34. package/dist/expansion/standard-bfs.d.ts +11 -0
  35. package/dist/expansion/standard-bfs.d.ts.map +1 -1
  36. package/dist/expansion/tide.d.ts +16 -0
  37. package/dist/expansion/tide.d.ts.map +1 -1
  38. package/dist/expansion/warp.d.ts +16 -0
  39. package/dist/expansion/warp.d.ts.map +1 -1
  40. package/dist/index/index.cjs +842 -215
  41. package/dist/index/index.cjs.map +1 -1
  42. package/dist/index/index.js +793 -211
  43. package/dist/index/index.js.map +1 -1
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/ranking/mi/adamic-adar.d.ts +8 -0
  47. package/dist/ranking/mi/adamic-adar.d.ts.map +1 -1
  48. package/dist/ranking/mi/adaptive.d.ts +8 -0
  49. package/dist/ranking/mi/adaptive.d.ts.map +1 -1
  50. package/dist/ranking/mi/cosine.d.ts +7 -0
  51. package/dist/ranking/mi/cosine.d.ts.map +1 -1
  52. package/dist/ranking/mi/etch.d.ts +8 -0
  53. package/dist/ranking/mi/etch.d.ts.map +1 -1
  54. package/dist/ranking/mi/hub-promoted.d.ts +7 -0
  55. package/dist/ranking/mi/hub-promoted.d.ts.map +1 -1
  56. package/dist/ranking/mi/jaccard.d.ts +7 -0
  57. package/dist/ranking/mi/jaccard.d.ts.map +1 -1
  58. package/dist/ranking/mi/notch.d.ts +8 -0
  59. package/dist/ranking/mi/notch.d.ts.map +1 -1
  60. package/dist/ranking/mi/overlap-coefficient.d.ts +7 -0
  61. package/dist/ranking/mi/overlap-coefficient.d.ts.map +1 -1
  62. package/dist/ranking/mi/resource-allocation.d.ts +8 -0
  63. package/dist/ranking/mi/resource-allocation.d.ts.map +1 -1
  64. package/dist/ranking/mi/scale.d.ts +7 -0
  65. package/dist/ranking/mi/scale.d.ts.map +1 -1
  66. package/dist/ranking/mi/skew.d.ts +7 -0
  67. package/dist/ranking/mi/skew.d.ts.map +1 -1
  68. package/dist/ranking/mi/sorensen.d.ts +7 -0
  69. package/dist/ranking/mi/sorensen.d.ts.map +1 -1
  70. package/dist/ranking/mi/span.d.ts +8 -0
  71. package/dist/ranking/mi/span.d.ts.map +1 -1
  72. package/dist/ranking/mi/types.d.ts +12 -0
  73. package/dist/ranking/mi/types.d.ts.map +1 -1
  74. package/dist/ranking/parse.d.ts +24 -1
  75. package/dist/ranking/parse.d.ts.map +1 -1
  76. package/package.json +6 -1
@@ -6,199 +6,7 @@ const require_kmeans = require("../kmeans-BIgSyGKu.cjs");
6
6
  const require_utils = require("../utils/index.cjs");
7
7
  const require_seeds = require("../seeds/index.cjs");
8
8
  const require_gpu = require("../gpu/index.cjs");
9
- //#region src/async/utils.ts
10
- /**
11
- * Async utility functions.
12
- *
13
- * @module async/utils
14
- */
15
- /** Collect an AsyncIterable into a readonly array. */
16
- async function collectAsyncIterable(iter) {
17
- const result = [];
18
- for await (const item of iter) result.push(item);
19
- return result;
20
- }
21
- /** Default yield strategy: setTimeout(0) to yield to the event loop. */
22
- function defaultYieldStrategy() {
23
- return new Promise((r) => {
24
- setTimeout(r, 0);
25
- });
26
- }
27
- //#endregion
28
- //#region src/async/runners.ts
29
- /**
30
- * Resolve a single GraphOp against a synchronous ReadableGraph.
31
- *
32
- * Returns a tagged GraphOpResponse so the receiving generator can narrow
33
- * the result type without type assertions.
34
- *
35
- * @param graph - The synchronous graph to query
36
- * @param op - The operation to resolve
37
- * @returns The tagged response
38
- */
39
- function resolveSyncOp(graph, op) {
40
- switch (op.tag) {
41
- case "neighbours": return {
42
- tag: "neighbours",
43
- value: Array.from(graph.neighbours(op.id, op.direction))
44
- };
45
- case "degree": return {
46
- tag: "degree",
47
- value: graph.degree(op.id, op.direction)
48
- };
49
- case "getNode": return {
50
- tag: "getNode",
51
- value: graph.getNode(op.id)
52
- };
53
- case "getEdge": return {
54
- tag: "getEdge",
55
- value: graph.getEdge(op.source, op.target)
56
- };
57
- case "hasNode": return {
58
- tag: "hasNode",
59
- value: graph.hasNode(op.id)
60
- };
61
- case "yield": return { tag: "yield" };
62
- case "progress": return { tag: "progress" };
63
- }
64
- }
65
- /**
66
- * Drive a generator to completion using a synchronous graph.
67
- *
68
- * The generator yields GraphOp requests; each is resolved immediately
69
- * against the graph and the tagged response is fed back via gen.next().
70
- *
71
- * @param gen - The generator to drive
72
- * @param graph - The graph to resolve ops against
73
- * @returns The generator's return value
74
- */
75
- function runSync(gen, graph) {
76
- let step = gen.next();
77
- while (step.done !== true) {
78
- const response = resolveSyncOp(graph, step.value);
79
- step = gen.next(response);
80
- }
81
- return step.value;
82
- }
83
- /**
84
- * Resolve a single GraphOp against an async ReadableGraph.
85
- *
86
- * AsyncIterables (neighbours) are collected into readonly arrays so the
87
- * generator receives the same value type as in sync mode. Returns a tagged
88
- * GraphOpResponse for type-safe narrowing without assertions.
89
- *
90
- * @param graph - The async graph to query
91
- * @param op - The operation to resolve
92
- * @returns A promise resolving to the tagged response
93
- */
94
- async function resolveAsyncOp(graph, op) {
95
- switch (op.tag) {
96
- case "neighbours": return {
97
- tag: "neighbours",
98
- value: await collectAsyncIterable(graph.neighbours(op.id, op.direction))
99
- };
100
- case "degree": return {
101
- tag: "degree",
102
- value: await graph.degree(op.id, op.direction)
103
- };
104
- case "getNode": return {
105
- tag: "getNode",
106
- value: await graph.getNode(op.id)
107
- };
108
- case "getEdge": return {
109
- tag: "getEdge",
110
- value: await graph.getEdge(op.source, op.target)
111
- };
112
- case "hasNode": return {
113
- tag: "hasNode",
114
- value: await graph.hasNode(op.id)
115
- };
116
- case "yield": return { tag: "yield" };
117
- case "progress": return { tag: "progress" };
118
- }
119
- }
120
- /**
121
- * Drive a generator to completion using an async graph.
122
- *
123
- * Extends sync semantics with:
124
- * - Cancellation via AbortSignal (throws DOMException "AbortError")
125
- * - Cooperative yielding at `yield` ops (calls yieldStrategy)
126
- * - Progress callbacks at `progress` ops (may be async for backpressure)
127
- * - Error propagation: graph errors are forwarded via gen.throw(); if the
128
- * generator does not handle them, they propagate to the caller
129
- *
130
- * @param gen - The generator to drive
131
- * @param graph - The async graph to resolve ops against
132
- * @param options - Runner configuration
133
- * @returns A promise resolving to the generator's return value
134
- */
135
- async function runAsync(gen, graph, options) {
136
- const signal = options?.signal;
137
- const onProgress = options?.onProgress;
138
- const yieldStrategy = options?.yieldStrategy ?? defaultYieldStrategy;
139
- let step = gen.next();
140
- while (step.done !== true) {
141
- if (signal?.aborted === true) {
142
- const abortError = new DOMException("Aborted", "AbortError");
143
- try {
144
- gen.throw(abortError);
145
- } catch {
146
- throw abortError;
147
- }
148
- throw abortError;
149
- }
150
- const op = step.value;
151
- if (op.tag === "yield") {
152
- await yieldStrategy();
153
- step = gen.next({ tag: "yield" });
154
- continue;
155
- }
156
- if (op.tag === "progress") {
157
- if (onProgress !== void 0) {
158
- const maybePromise = onProgress(op.stats);
159
- if (maybePromise instanceof Promise) await maybePromise;
160
- }
161
- step = gen.next({ tag: "progress" });
162
- continue;
163
- }
164
- let response;
165
- try {
166
- response = await resolveAsyncOp(graph, op);
167
- } catch (error) {
168
- step = gen.throw(error);
169
- continue;
170
- }
171
- step = gen.next(response);
172
- }
173
- return step.value;
174
- }
175
- //#endregion
176
- //#region src/async/ops.ts
177
- function* opNeighbours(id, direction) {
178
- const response = yield direction !== void 0 ? {
179
- tag: "neighbours",
180
- id,
181
- direction
182
- } : {
183
- tag: "neighbours",
184
- id
185
- };
186
- if (response.tag !== "neighbours") throw new TypeError(`Expected neighbours response, got ${response.tag}`);
187
- return response.value;
188
- }
189
- function* opDegree(id, direction) {
190
- const response = yield direction !== void 0 ? {
191
- tag: "degree",
192
- id,
193
- direction
194
- } : {
195
- tag: "degree",
196
- id
197
- };
198
- if (response.tag !== "degree") throw new TypeError(`Expected degree response, got ${response.tag}`);
199
- return response.value;
200
- }
201
- //#endregion
9
+ const require_async = require("../async/index.cjs");
202
10
  //#region src/expansion/base-helpers.ts
203
11
  /**
204
12
  * Check whether expansion should continue given current progress.
@@ -345,7 +153,7 @@ function* baseCore(graphMeta, seeds, config, graphRef) {
345
153
  predecessors[i]?.set(seedNode, null);
346
154
  combinedVisited.set(seedNode, i);
347
155
  allVisited.add(seedNode);
348
- const seedDegree = yield* opDegree(seedNode);
156
+ const seedDegree = yield* require_async.opDegree(seedNode);
349
157
  const seedPriority = priority(seedNode, buildPriorityContext(seedNode, i, combinedVisited, allVisited, [], 0, seedDegree, graphRef));
350
158
  queues[i]?.push({
351
159
  nodeId: seedNode,
@@ -412,7 +220,7 @@ function* baseCore(graphMeta, seeds, config, graphRef) {
412
220
  }
413
221
  }
414
222
  }
415
- const neighbours = yield* opNeighbours(nodeId);
223
+ const neighbours = yield* require_async.opNeighbours(nodeId);
416
224
  for (const neighbour of neighbours) {
417
225
  edgesTraversed++;
418
226
  const [s, t] = nodeId < neighbour ? [nodeId, neighbour] : [neighbour, nodeId];
@@ -424,7 +232,7 @@ function* baseCore(graphMeta, seeds, config, graphRef) {
424
232
  targets.add(t);
425
233
  const fv = visitedByFrontier[activeFrontier];
426
234
  if (fv === void 0 || fv.has(neighbour)) continue;
427
- const neighbourDegree = yield* opDegree(neighbour);
235
+ const neighbourDegree = yield* require_async.opDegree(neighbour);
428
236
  const neighbourPriority = priority(neighbour, buildPriorityContext(neighbour, activeFrontier, combinedVisited, allVisited, discoveredPaths, iterations + 1, neighbourDegree, graphRef));
429
237
  queue.push({
430
238
  nodeId: neighbour,
@@ -520,7 +328,7 @@ function buildPriorityContext(_nodeId, frontierIndex, combinedVisited, allVisite
520
328
  * @returns Expansion result with discovered paths
521
329
  */
522
330
  function base(graph, seeds, config) {
523
- return runSync(baseCore({
331
+ return require_async.runSync(baseCore({
524
332
  directed: graph.directed,
525
333
  nodeCount: graph.nodeCount,
526
334
  edgeCount: graph.edgeCount
@@ -554,11 +362,23 @@ async function baseAsync(graph, seeds, config) {
554
362
  if (config?.signal !== void 0) runnerOptions.signal = config.signal;
555
363
  if (config?.onProgress !== void 0) runnerOptions.onProgress = config.onProgress;
556
364
  if (config?.yieldStrategy !== void 0) runnerOptions.yieldStrategy = config.yieldStrategy;
557
- return runAsync(gen, graph, runnerOptions);
365
+ return require_async.runAsync(gen, graph, runnerOptions);
558
366
  }
559
367
  //#endregion
560
368
  //#region src/expansion/dome.ts
561
369
  /**
370
+ * DOME priority: lower degree is expanded first.
371
+ */
372
+ function domePriority(_nodeId, context) {
373
+ return context.degree;
374
+ }
375
+ /**
376
+ * DOME high-degree priority: negate degree to prioritise high-degree nodes.
377
+ */
378
+ function domeHighDegreePriority(_nodeId, context) {
379
+ return -context.degree;
380
+ }
381
+ /**
562
382
  * Run DOME expansion (degree-ordered).
563
383
  *
564
384
  * @param graph - Source graph
@@ -567,24 +387,46 @@ async function baseAsync(graph, seeds, config) {
567
387
  * @returns Expansion result with discovered paths
568
388
  */
569
389
  function dome(graph, seeds, config) {
570
- const domePriority = (nodeId, context) => {
571
- return context.degree;
572
- };
573
390
  return base(graph, seeds, {
574
391
  ...config,
575
392
  priority: domePriority
576
393
  });
577
394
  }
578
395
  /**
396
+ * Run DOME expansion asynchronously (degree-ordered).
397
+ *
398
+ * @param graph - Async source graph
399
+ * @param seeds - Seed nodes for expansion
400
+ * @param config - Expansion and async runner configuration
401
+ * @returns Promise resolving to the expansion result
402
+ */
403
+ async function domeAsync(graph, seeds, config) {
404
+ return baseAsync(graph, seeds, {
405
+ ...config,
406
+ priority: domePriority
407
+ });
408
+ }
409
+ /**
579
410
  * DOME with reverse priority (high degree first).
580
411
  */
581
412
  function domeHighDegree(graph, seeds, config) {
582
- const domePriority = (nodeId, context) => {
583
- return -context.degree;
584
- };
585
413
  return base(graph, seeds, {
586
414
  ...config,
587
- priority: domePriority
415
+ priority: domeHighDegreePriority
416
+ });
417
+ }
418
+ /**
419
+ * Run DOME high-degree expansion asynchronously (high degree first).
420
+ *
421
+ * @param graph - Async source graph
422
+ * @param seeds - Seed nodes for expansion
423
+ * @param config - Expansion and async runner configuration
424
+ * @returns Promise resolving to the expansion result
425
+ */
426
+ async function domeHighDegreeAsync(graph, seeds, config) {
427
+ return baseAsync(graph, seeds, {
428
+ ...config,
429
+ priority: domeHighDegreePriority
588
430
  });
589
431
  }
590
432
  //#endregion
@@ -629,6 +471,26 @@ function hae(graph, seeds, config) {
629
471
  priority: createHAEPriority(typeMapper)
630
472
  });
631
473
  }
474
+ /**
475
+ * Run HAE expansion asynchronously.
476
+ *
477
+ * Note: the HAE priority function accesses `context.graph` to retrieve
478
+ * neighbour types. Full async equivalence requires PriorityContext
479
+ * refactoring (Phase 4b deferred). This export establishes the async API
480
+ * surface; use with a `wrapAsync`-wrapped sync graph for testing.
481
+ *
482
+ * @param graph - Async source graph
483
+ * @param seeds - Seed nodes for expansion
484
+ * @param config - HAE configuration combined with async runner options
485
+ * @returns Promise resolving to the expansion result
486
+ */
487
+ async function haeAsync(graph, seeds, config) {
488
+ const typeMapper = config?.typeMapper ?? defaultTypeMapper$1;
489
+ return baseAsync(graph, seeds, {
490
+ ...config,
491
+ priority: createHAEPriority(typeMapper)
492
+ });
493
+ }
632
494
  //#endregion
633
495
  //#region src/expansion/edge.ts
634
496
  /** Default type mapper: reads `node.type`, falling back to "default". */
@@ -650,6 +512,27 @@ function edge(graph, seeds, config) {
650
512
  typeMapper: defaultTypeMapper
651
513
  });
652
514
  }
515
+ /**
516
+ * Run EDGE expansion asynchronously.
517
+ *
518
+ * Delegates to `haeAsync` with the default `node.type` mapper.
519
+ *
520
+ * Note: the HAE priority function accesses `context.graph` to retrieve
521
+ * neighbour types. Full async equivalence requires PriorityContext
522
+ * refactoring (Phase 4b deferred). This export establishes the async API
523
+ * surface; use with a `wrapAsync`-wrapped sync graph for testing.
524
+ *
525
+ * @param graph - Async source graph
526
+ * @param seeds - Seed nodes for expansion
527
+ * @param config - Expansion and async runner configuration
528
+ * @returns Promise resolving to the expansion result
529
+ */
530
+ async function edgeAsync(graph, seeds, config) {
531
+ return haeAsync(graph, seeds, {
532
+ ...config,
533
+ typeMapper: defaultTypeMapper
534
+ });
535
+ }
653
536
  //#endregion
654
537
  //#region src/expansion/pipe.ts
655
538
  /**
@@ -685,6 +568,25 @@ function pipe(graph, seeds, config) {
685
568
  priority: pipePriority
686
569
  });
687
570
  }
571
+ /**
572
+ * Run PIPE expansion asynchronously.
573
+ *
574
+ * Note: the PIPE priority function accesses `context.graph` to retrieve
575
+ * neighbour lists. Full async equivalence requires PriorityContext
576
+ * refactoring (Phase 4b deferred). This export establishes the async API
577
+ * surface; use with a `wrapAsync`-wrapped sync graph for testing.
578
+ *
579
+ * @param graph - Async source graph
580
+ * @param seeds - Seed nodes for expansion
581
+ * @param config - Expansion and async runner configuration
582
+ * @returns Promise resolving to the expansion result
583
+ */
584
+ async function pipeAsync(graph, seeds, config) {
585
+ return baseAsync(graph, seeds, {
586
+ ...config,
587
+ priority: pipePriority
588
+ });
589
+ }
688
590
  //#endregion
689
591
  //#region src/expansion/priority-helpers.ts
690
592
  /**
@@ -781,6 +683,34 @@ function sage(graph, seeds, config) {
781
683
  priority: sagePriority
782
684
  });
783
685
  }
686
+ /**
687
+ * Run SAGE expansion asynchronously.
688
+ *
689
+ * Creates fresh closure state (salienceCounts, phase tracking) for this
690
+ * invocation. The SAGE priority function does not access `context.graph`
691
+ * directly, so it is safe to use in async mode via `baseAsync`.
692
+ *
693
+ * @param graph - Async source graph
694
+ * @param seeds - Seed nodes for expansion
695
+ * @param config - Expansion and async runner configuration
696
+ * @returns Promise resolving to the expansion result
697
+ */
698
+ async function sageAsync(graph, seeds, config) {
699
+ const salienceCounts = /* @__PURE__ */ new Map();
700
+ let inPhase2 = false;
701
+ let lastPathCount = 0;
702
+ function sagePriority(nodeId, context) {
703
+ const pathCount = context.discoveredPaths.length;
704
+ if (pathCount > 0 && !inPhase2) inPhase2 = true;
705
+ if (pathCount > lastPathCount) lastPathCount = updateSalienceCounts(salienceCounts, context.discoveredPaths, lastPathCount);
706
+ if (!inPhase2) return Math.log(context.degree + 1);
707
+ return -((salienceCounts.get(nodeId) ?? 0) * 1e3 - context.degree);
708
+ }
709
+ return baseAsync(graph, seeds, {
710
+ ...config,
711
+ priority: sagePriority
712
+ });
713
+ }
784
714
  //#endregion
785
715
  //#region src/ranking/mi/jaccard.ts
786
716
  /**
@@ -798,6 +728,23 @@ function jaccard(graph, source, target, config) {
798
728
  if (sourceNeighbours.size === 0 && targetNeighbours.size === 0) return 0;
799
729
  return Math.max(epsilon, jaccardScore);
800
730
  }
731
+ /**
732
+ * Async variant of Jaccard similarity for use with async graph data sources.
733
+ *
734
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
735
+ */
736
+ async function jaccardAsync(graph, source, target, config) {
737
+ const { epsilon = 1e-10 } = config ?? {};
738
+ const [sourceNeighboursArr, targetNeighboursArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
739
+ const srcSet = new Set(sourceNeighboursArr.filter((n) => n !== target));
740
+ const tgtSet = new Set(targetNeighboursArr.filter((n) => n !== source));
741
+ if (srcSet.size === 0 && tgtSet.size === 0) return 0;
742
+ let intersection = 0;
743
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
744
+ const union = srcSet.size + tgtSet.size - intersection;
745
+ const jaccardScore = union > 0 ? intersection / union : 0;
746
+ return Math.max(epsilon, jaccardScore);
747
+ }
801
748
  //#endregion
802
749
  //#region src/expansion/reach.ts
803
750
  /**
@@ -854,6 +801,40 @@ function reach(graph, seeds, config) {
854
801
  priority: reachPriority
855
802
  });
856
803
  }
804
+ /**
805
+ * Run REACH expansion asynchronously.
806
+ *
807
+ * Creates fresh closure state (phase tracking, Jaccard cache) for this
808
+ * invocation. The REACH priority function uses `jaccard(context.graph, ...)`
809
+ * in Phase 2; in async mode `context.graph` is the sentinel and will throw.
810
+ * Full async equivalence requires PriorityContext refactoring (Phase 4b
811
+ * deferred). This export establishes the async API surface.
812
+ *
813
+ * @param graph - Async source graph
814
+ * @param seeds - Seed nodes for expansion
815
+ * @param config - Expansion and async runner configuration
816
+ * @returns Promise resolving to the expansion result
817
+ */
818
+ async function reachAsync(graph, seeds, config) {
819
+ let inPhase2 = false;
820
+ function reachPriority(nodeId, context) {
821
+ if (context.discoveredPaths.length > 0 && !inPhase2) inPhase2 = true;
822
+ if (!inPhase2) return Math.log(context.degree + 1);
823
+ let totalMI = 0;
824
+ let endpointCount = 0;
825
+ for (const path of context.discoveredPaths) {
826
+ totalMI += jaccard(context.graph, nodeId, path.fromSeed.id);
827
+ totalMI += jaccard(context.graph, nodeId, path.toSeed.id);
828
+ endpointCount += 2;
829
+ }
830
+ const miHat = endpointCount > 0 ? totalMI / endpointCount : 0;
831
+ return Math.log(context.degree + 1) * (1 - miHat);
832
+ }
833
+ return baseAsync(graph, seeds, {
834
+ ...config,
835
+ priority: reachPriority
836
+ });
837
+ }
857
838
  //#endregion
858
839
  //#region src/expansion/maze.ts
859
840
  /** Default threshold for switching to phase 2 (after M paths) */
@@ -900,6 +881,46 @@ function maze(graph, seeds, config) {
900
881
  priority: mazePriority
901
882
  });
902
883
  }
884
+ /**
885
+ * Run MAZE expansion asynchronously.
886
+ *
887
+ * Creates fresh closure state (salienceCounts, phase tracking) for this
888
+ * invocation. The MAZE priority function accesses `context.graph` to
889
+ * retrieve neighbour lists for path potential computation. Full async
890
+ * equivalence requires PriorityContext refactoring (Phase 4b deferred).
891
+ * This export establishes the async API surface.
892
+ *
893
+ * @param graph - Async source graph
894
+ * @param seeds - Seed nodes for expansion
895
+ * @param config - Expansion and async runner configuration
896
+ * @returns Promise resolving to the expansion result
897
+ */
898
+ async function mazeAsync(graph, seeds, config) {
899
+ const salienceCounts = /* @__PURE__ */ new Map();
900
+ let inPhase2 = false;
901
+ let lastPathCount = 0;
902
+ function mazePriority(nodeId, context) {
903
+ const pathCount = context.discoveredPaths.length;
904
+ if (pathCount >= DEFAULT_PHASE2_THRESHOLD && !inPhase2) {
905
+ inPhase2 = true;
906
+ updateSalienceCounts(salienceCounts, context.discoveredPaths, 0);
907
+ }
908
+ if (inPhase2 && pathCount > lastPathCount) lastPathCount = updateSalienceCounts(salienceCounts, context.discoveredPaths, lastPathCount);
909
+ const nodeNeighbours = context.graph.neighbours(nodeId);
910
+ let pathPotential = 0;
911
+ for (const neighbour of nodeNeighbours) {
912
+ const visitedBy = context.visitedByFrontier.get(neighbour);
913
+ if (visitedBy !== void 0 && visitedBy !== context.frontierIndex) pathPotential++;
914
+ }
915
+ if (!inPhase2) return context.degree / (1 + pathPotential);
916
+ const salience = salienceCounts.get(nodeId) ?? 0;
917
+ return context.degree / (1 + pathPotential) * (1 / (1 + SALIENCE_WEIGHT * salience));
918
+ }
919
+ return baseAsync(graph, seeds, {
920
+ ...config,
921
+ priority: mazePriority
922
+ });
923
+ }
903
924
  //#endregion
904
925
  //#region src/expansion/tide.ts
905
926
  /**
@@ -931,6 +952,25 @@ function tide(graph, seeds, config) {
931
952
  priority: tidePriority
932
953
  });
933
954
  }
955
+ /**
956
+ * Run TIDE expansion asynchronously.
957
+ *
958
+ * Note: the TIDE priority function accesses `context.graph` to retrieve
959
+ * neighbour lists and per-neighbour degrees. Full async equivalence
960
+ * requires PriorityContext refactoring (Phase 4b deferred). This export
961
+ * establishes the async API surface.
962
+ *
963
+ * @param graph - Async source graph
964
+ * @param seeds - Seed nodes for expansion
965
+ * @param config - Expansion and async runner configuration
966
+ * @returns Promise resolving to the expansion result
967
+ */
968
+ async function tideAsync(graph, seeds, config) {
969
+ return baseAsync(graph, seeds, {
970
+ ...config,
971
+ priority: tidePriority
972
+ });
973
+ }
934
974
  //#endregion
935
975
  //#region src/expansion/lace.ts
936
976
  /**
@@ -961,6 +1001,27 @@ function lace(graph, seeds, config) {
961
1001
  priority
962
1002
  });
963
1003
  }
1004
+ /**
1005
+ * Run LACE expansion asynchronously.
1006
+ *
1007
+ * Note: the LACE priority function accesses `context.graph` via
1008
+ * `avgFrontierMI`. Full async equivalence requires PriorityContext
1009
+ * refactoring (Phase 4b deferred). This export establishes the async
1010
+ * API surface.
1011
+ *
1012
+ * @param graph - Async source graph
1013
+ * @param seeds - Seed nodes for expansion
1014
+ * @param config - LACE configuration combined with async runner options
1015
+ * @returns Promise resolving to the expansion result
1016
+ */
1017
+ async function laceAsync(graph, seeds, config) {
1018
+ const { mi = jaccard, ...restConfig } = config ?? {};
1019
+ const priority = (nodeId, context) => lacePriority(nodeId, context, mi);
1020
+ return baseAsync(graph, seeds, {
1021
+ ...restConfig,
1022
+ priority
1023
+ });
1024
+ }
964
1025
  //#endregion
965
1026
  //#region src/expansion/warp.ts
966
1027
  /**
@@ -993,6 +1054,25 @@ function warp(graph, seeds, config) {
993
1054
  priority: warpPriority
994
1055
  });
995
1056
  }
1057
+ /**
1058
+ * Run WARP expansion asynchronously.
1059
+ *
1060
+ * Note: the WARP priority function accesses `context.graph` via
1061
+ * `countCrossFrontierNeighbours`. Full async equivalence requires
1062
+ * PriorityContext refactoring (Phase 4b deferred). This export
1063
+ * establishes the async API surface.
1064
+ *
1065
+ * @param graph - Async source graph
1066
+ * @param seeds - Seed nodes for expansion
1067
+ * @param config - Expansion and async runner configuration
1068
+ * @returns Promise resolving to the expansion result
1069
+ */
1070
+ async function warpAsync(graph, seeds, config) {
1071
+ return baseAsync(graph, seeds, {
1072
+ ...config,
1073
+ priority: warpPriority
1074
+ });
1075
+ }
996
1076
  //#endregion
997
1077
  //#region src/expansion/fuse.ts
998
1078
  /**
@@ -1025,6 +1105,27 @@ function fuse(graph, seeds, config) {
1025
1105
  priority
1026
1106
  });
1027
1107
  }
1108
+ /**
1109
+ * Run FUSE expansion asynchronously.
1110
+ *
1111
+ * Note: the FUSE priority function accesses `context.graph` via
1112
+ * `avgFrontierMI`. Full async equivalence requires PriorityContext
1113
+ * refactoring (Phase 4b deferred). This export establishes the async
1114
+ * API surface.
1115
+ *
1116
+ * @param graph - Async source graph
1117
+ * @param seeds - Seed nodes for expansion
1118
+ * @param config - FUSE configuration combined with async runner options
1119
+ * @returns Promise resolving to the expansion result
1120
+ */
1121
+ async function fuseAsync(graph, seeds, config) {
1122
+ const { mi = jaccard, salienceWeight = .5, ...restConfig } = config ?? {};
1123
+ const priority = (nodeId, context) => fusePriority(nodeId, context, mi, salienceWeight);
1124
+ return baseAsync(graph, seeds, {
1125
+ ...restConfig,
1126
+ priority
1127
+ });
1128
+ }
1028
1129
  //#endregion
1029
1130
  //#region src/expansion/sift.ts
1030
1131
  /**
@@ -1057,6 +1158,27 @@ function sift(graph, seeds, config) {
1057
1158
  priority
1058
1159
  });
1059
1160
  }
1161
+ /**
1162
+ * Run SIFT expansion asynchronously.
1163
+ *
1164
+ * Note: the SIFT priority function accesses `context.graph` via
1165
+ * `avgFrontierMI`. Full async equivalence requires PriorityContext
1166
+ * refactoring (Phase 4b deferred). This export establishes the async
1167
+ * API surface.
1168
+ *
1169
+ * @param graph - Async source graph
1170
+ * @param seeds - Seed nodes for expansion
1171
+ * @param config - SIFT (REACHConfig) configuration combined with async runner options
1172
+ * @returns Promise resolving to the expansion result
1173
+ */
1174
+ async function siftAsync(graph, seeds, config) {
1175
+ const { mi = jaccard, miThreshold = .25, ...restConfig } = config ?? {};
1176
+ const priority = (nodeId, context) => siftPriority(nodeId, context, mi, miThreshold);
1177
+ return baseAsync(graph, seeds, {
1178
+ ...restConfig,
1179
+ priority
1180
+ });
1181
+ }
1060
1182
  //#endregion
1061
1183
  //#region src/expansion/flux.ts
1062
1184
  /**
@@ -1113,9 +1235,36 @@ function flux(graph, seeds, config) {
1113
1235
  priority
1114
1236
  });
1115
1237
  }
1238
+ /**
1239
+ * Run FLUX expansion asynchronously.
1240
+ *
1241
+ * Note: the FLUX priority function accesses `context.graph` to compute
1242
+ * local density and cross-frontier bridge scores. Full async equivalence
1243
+ * requires PriorityContext refactoring (Phase 4b deferred). This export
1244
+ * establishes the async API surface.
1245
+ *
1246
+ * @param graph - Async source graph
1247
+ * @param seeds - Seed nodes for expansion
1248
+ * @param config - FLUX (MAZEConfig) configuration combined with async runner options
1249
+ * @returns Promise resolving to the expansion result
1250
+ */
1251
+ async function fluxAsync(graph, seeds, config) {
1252
+ const { densityThreshold = .5, bridgeThreshold = .3, ...restConfig } = config ?? {};
1253
+ const priority = (nodeId, context) => fluxPriority(nodeId, context, densityThreshold, bridgeThreshold);
1254
+ return baseAsync(graph, seeds, {
1255
+ ...restConfig,
1256
+ priority
1257
+ });
1258
+ }
1116
1259
  //#endregion
1117
1260
  //#region src/expansion/standard-bfs.ts
1118
1261
  /**
1262
+ * BFS priority: discovery iteration order (FIFO).
1263
+ */
1264
+ function bfsPriority(_nodeId, context) {
1265
+ return context.iteration;
1266
+ }
1267
+ /**
1119
1268
  * Run standard BFS expansion (FIFO discovery order).
1120
1269
  *
1121
1270
  * @param graph - Source graph
@@ -1124,17 +1273,35 @@ function flux(graph, seeds, config) {
1124
1273
  * @returns Expansion result with discovered paths
1125
1274
  */
1126
1275
  function standardBfs(graph, seeds, config) {
1127
- const bfsPriority = (_nodeId, context) => {
1128
- return context.iteration;
1129
- };
1130
1276
  return base(graph, seeds, {
1131
1277
  ...config,
1132
1278
  priority: bfsPriority
1133
1279
  });
1134
1280
  }
1281
+ /**
1282
+ * Run standard BFS expansion asynchronously (FIFO discovery order).
1283
+ *
1284
+ * @param graph - Async source graph
1285
+ * @param seeds - Seed nodes for expansion
1286
+ * @param config - Expansion and async runner configuration
1287
+ * @returns Promise resolving to the expansion result
1288
+ */
1289
+ async function standardBfsAsync(graph, seeds, config) {
1290
+ return baseAsync(graph, seeds, {
1291
+ ...config,
1292
+ priority: bfsPriority
1293
+ });
1294
+ }
1135
1295
  //#endregion
1136
1296
  //#region src/expansion/frontier-balanced.ts
1137
1297
  /**
1298
+ * Frontier-balanced priority: frontier index dominates, then discovery iteration.
1299
+ * Scales frontier index by 1e9 to ensure round-robin ordering across frontiers.
1300
+ */
1301
+ function balancedPriority(_nodeId, context) {
1302
+ return context.frontierIndex * 1e9 + context.iteration;
1303
+ }
1304
+ /**
1138
1305
  * Run frontier-balanced expansion (round-robin across frontiers).
1139
1306
  *
1140
1307
  * @param graph - Source graph
@@ -1143,14 +1310,25 @@ function standardBfs(graph, seeds, config) {
1143
1310
  * @returns Expansion result with discovered paths
1144
1311
  */
1145
1312
  function frontierBalanced(graph, seeds, config) {
1146
- const balancedPriority = (_nodeId, context) => {
1147
- return context.frontierIndex * 1e9 + context.iteration;
1148
- };
1149
1313
  return base(graph, seeds, {
1150
1314
  ...config,
1151
1315
  priority: balancedPriority
1152
1316
  });
1153
1317
  }
1318
+ /**
1319
+ * Run frontier-balanced expansion asynchronously (round-robin across frontiers).
1320
+ *
1321
+ * @param graph - Async source graph
1322
+ * @param seeds - Seed nodes for expansion
1323
+ * @param config - Expansion and async runner configuration
1324
+ * @returns Promise resolving to the expansion result
1325
+ */
1326
+ async function frontierBalancedAsync(graph, seeds, config) {
1327
+ return baseAsync(graph, seeds, {
1328
+ ...config,
1329
+ priority: balancedPriority
1330
+ });
1331
+ }
1154
1332
  //#endregion
1155
1333
  //#region src/expansion/random-priority.ts
1156
1334
  /**
@@ -1170,6 +1348,12 @@ function seededRandom$1(input, seed = 0) {
1170
1348
  return (h >>> 0) / 4294967295;
1171
1349
  }
1172
1350
  /**
1351
+ * Build a seeded random priority function for a given seed value.
1352
+ */
1353
+ function makeRandomPriorityFn(seed) {
1354
+ return (nodeId) => seededRandom$1(nodeId, seed);
1355
+ }
1356
+ /**
1173
1357
  * Run random-priority expansion (null hypothesis baseline).
1174
1358
  *
1175
1359
  * @param graph - Source graph
@@ -1179,12 +1363,24 @@ function seededRandom$1(input, seed = 0) {
1179
1363
  */
1180
1364
  function randomPriority(graph, seeds, config) {
1181
1365
  const { seed = 0 } = config ?? {};
1182
- const randomPriorityFn = (nodeId, context) => {
1183
- return seededRandom$1(nodeId, seed);
1184
- };
1185
1366
  return base(graph, seeds, {
1186
1367
  ...config,
1187
- priority: randomPriorityFn
1368
+ priority: makeRandomPriorityFn(seed)
1369
+ });
1370
+ }
1371
+ /**
1372
+ * Run random-priority expansion asynchronously (null hypothesis baseline).
1373
+ *
1374
+ * @param graph - Async source graph
1375
+ * @param seeds - Seed nodes for expansion
1376
+ * @param config - Expansion and async runner configuration
1377
+ * @returns Promise resolving to the expansion result
1378
+ */
1379
+ async function randomPriorityAsync(graph, seeds, config) {
1380
+ const { seed = 0 } = config ?? {};
1381
+ return baseAsync(graph, seeds, {
1382
+ ...config,
1383
+ priority: makeRandomPriorityFn(seed)
1188
1384
  });
1189
1385
  }
1190
1386
  //#endregion
@@ -1216,6 +1412,20 @@ function dfsPriority(graph, seeds, config) {
1216
1412
  priority: dfsPriorityFn
1217
1413
  });
1218
1414
  }
1415
+ /**
1416
+ * Run DFS-priority expansion asynchronously (LIFO discovery order).
1417
+ *
1418
+ * @param graph - Async source graph
1419
+ * @param seeds - Seed nodes for expansion
1420
+ * @param config - Expansion and async runner configuration
1421
+ * @returns Promise resolving to the expansion result
1422
+ */
1423
+ async function dfsPriorityAsync(graph, seeds, config) {
1424
+ return baseAsync(graph, seeds, {
1425
+ ...config,
1426
+ priority: dfsPriorityFn
1427
+ });
1428
+ }
1219
1429
  //#endregion
1220
1430
  //#region src/expansion/k-hop.ts
1221
1431
  /**
@@ -1573,6 +1783,74 @@ function parse(graph, paths, config) {
1573
1783
  };
1574
1784
  }
1575
1785
  /**
1786
+ * Rank paths using async PARSE (Path-Aware Ranking via Salience Estimation).
1787
+ *
1788
+ * Async variant suitable for use with remote or lazy graph data sources.
1789
+ * Computes geometric mean of edge MI scores for each path using Promise.all
1790
+ * for parallelism, then sorts by salience (highest first).
1791
+ *
1792
+ * @param graph - Async source graph
1793
+ * @param paths - Paths to rank
1794
+ * @param config - Configuration options
1795
+ * @returns Ranked paths with statistics
1796
+ */
1797
+ async function parseAsync(graph, paths, config) {
1798
+ const startTime = performance.now();
1799
+ const { mi = jaccardAsync, epsilon = 1e-10 } = config ?? {};
1800
+ const rankedPaths = [];
1801
+ for (const path of paths) {
1802
+ const salience = await computePathSalienceAsync(graph, path, mi, epsilon);
1803
+ rankedPaths.push({
1804
+ ...path,
1805
+ salience
1806
+ });
1807
+ }
1808
+ rankedPaths.sort((a, b) => b.salience - a.salience);
1809
+ const endTime = performance.now();
1810
+ const saliences = rankedPaths.map((p) => p.salience);
1811
+ const meanSalience = saliences.length > 0 ? saliences.reduce((a, b) => a + b, 0) / saliences.length : 0;
1812
+ const sortedSaliences = [...saliences].sort((a, b) => a - b);
1813
+ const mid = Math.floor(sortedSaliences.length / 2);
1814
+ const medianSalience = sortedSaliences.length > 0 ? sortedSaliences.length % 2 !== 0 ? sortedSaliences[mid] ?? 0 : ((sortedSaliences[mid - 1] ?? 0) + (sortedSaliences[mid] ?? 0)) / 2 : 0;
1815
+ const maxSalience = sortedSaliences.length > 0 ? sortedSaliences[sortedSaliences.length - 1] ?? 0 : 0;
1816
+ const minSalience = sortedSaliences.length > 0 ? sortedSaliences[0] ?? 0 : 0;
1817
+ return {
1818
+ paths: rankedPaths,
1819
+ stats: {
1820
+ pathsRanked: rankedPaths.length,
1821
+ meanSalience,
1822
+ medianSalience,
1823
+ maxSalience,
1824
+ minSalience,
1825
+ durationMs: endTime - startTime
1826
+ }
1827
+ };
1828
+ }
1829
+ /**
1830
+ * Compute salience for a single path asynchronously.
1831
+ *
1832
+ * Uses geometric mean of edge MI scores for length-unbiased ranking.
1833
+ * Edge MI values are computed in parallel via Promise.all.
1834
+ */
1835
+ async function computePathSalienceAsync(graph, path, mi, epsilon) {
1836
+ const nodes = path.nodes;
1837
+ if (nodes.length < 2) return epsilon;
1838
+ const edgeMIs = await Promise.all(nodes.slice(0, -1).map((source, i) => {
1839
+ const target = nodes[i + 1];
1840
+ if (target !== void 0) return mi(graph, source, target);
1841
+ return Promise.resolve(epsilon);
1842
+ }));
1843
+ let productMi = 1;
1844
+ let edgeCount = 0;
1845
+ for (const edgeMi of edgeMIs) {
1846
+ productMi *= Math.max(epsilon, edgeMi);
1847
+ edgeCount++;
1848
+ }
1849
+ if (edgeCount === 0) return epsilon;
1850
+ const salience = Math.pow(productMi, 1 / edgeCount);
1851
+ return Math.max(epsilon, Math.min(1, salience));
1852
+ }
1853
+ /**
1576
1854
  * Compute salience for a single path.
1577
1855
  *
1578
1856
  * Uses geometric mean of edge MI scores for length-unbiased ranking.
@@ -1620,6 +1898,29 @@ function adamicAdar(graph, source, target, config) {
1620
1898
  }
1621
1899
  return Math.max(epsilon, score);
1622
1900
  }
1901
+ /**
1902
+ * Async variant of Adamic-Adar index for use with async graph data sources.
1903
+ *
1904
+ * Fetches both neighbourhoods concurrently, then fetches degree for each common
1905
+ * neighbour to compute the inverse-log-degree weighted sum.
1906
+ */
1907
+ async function adamicAdarAsync(graph, source, target, config) {
1908
+ const { epsilon = 1e-10, normalise = true } = config ?? {};
1909
+ const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
1910
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
1911
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
1912
+ const commonNeighbours = [];
1913
+ for (const n of srcSet) if (tgtSet.has(n)) commonNeighbours.push(n);
1914
+ if (commonNeighbours.length === 0) return epsilon;
1915
+ const degrees = await Promise.all(commonNeighbours.map((n) => graph.degree(n)));
1916
+ let score = 0;
1917
+ for (const degree of degrees) score += 1 / Math.log(degree + 1);
1918
+ if (normalise) {
1919
+ const maxScore = commonNeighbours.length / Math.log(2);
1920
+ score = score / maxScore;
1921
+ }
1922
+ return Math.max(epsilon, score);
1923
+ }
1623
1924
  //#endregion
1624
1925
  //#region src/ranking/mi/cosine.ts
1625
1926
  /**
@@ -1641,6 +1942,23 @@ function cosine(graph, source, target, config) {
1641
1942
  const score = intersection / denominator;
1642
1943
  return Math.max(epsilon, score);
1643
1944
  }
1945
+ /**
1946
+ * Async variant of cosine similarity for use with async graph data sources.
1947
+ *
1948
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
1949
+ */
1950
+ async function cosineAsync(graph, source, target, config) {
1951
+ const { epsilon = 1e-10 } = config ?? {};
1952
+ const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
1953
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
1954
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
1955
+ let intersection = 0;
1956
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
1957
+ const denominator = Math.sqrt(srcSet.size) * Math.sqrt(tgtSet.size);
1958
+ if (denominator === 0) return 0;
1959
+ const score = intersection / denominator;
1960
+ return Math.max(epsilon, score);
1961
+ }
1644
1962
  //#endregion
1645
1963
  //#region src/ranking/mi/sorensen.ts
1646
1964
  /**
@@ -1662,6 +1980,23 @@ function sorensen(graph, source, target, config) {
1662
1980
  const score = 2 * intersection / denominator;
1663
1981
  return Math.max(epsilon, score);
1664
1982
  }
1983
+ /**
1984
+ * Async variant of Sorensen-Dice coefficient for use with async graph data sources.
1985
+ *
1986
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
1987
+ */
1988
+ async function sorensenAsync(graph, source, target, config) {
1989
+ const { epsilon = 1e-10 } = config ?? {};
1990
+ const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
1991
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
1992
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
1993
+ let intersection = 0;
1994
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
1995
+ const denominator = srcSet.size + tgtSet.size;
1996
+ if (denominator === 0) return 0;
1997
+ const score = 2 * intersection / denominator;
1998
+ return Math.max(epsilon, score);
1999
+ }
1665
2000
  //#endregion
1666
2001
  //#region src/ranking/mi/resource-allocation.ts
1667
2002
  /**
@@ -1687,6 +2022,29 @@ function resourceAllocation(graph, source, target, config) {
1687
2022
  }
1688
2023
  return Math.max(epsilon, score);
1689
2024
  }
2025
+ /**
2026
+ * Async variant of Resource Allocation index for use with async graph data sources.
2027
+ *
2028
+ * Fetches both neighbourhoods concurrently, then fetches degree for each common
2029
+ * neighbour to compute the inverse-degree weighted sum.
2030
+ */
2031
+ async function resourceAllocationAsync(graph, source, target, config) {
2032
+ const { epsilon = 1e-10, normalise = true } = config ?? {};
2033
+ const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
2034
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2035
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2036
+ const commonNeighbours = [];
2037
+ for (const n of srcSet) if (tgtSet.has(n)) commonNeighbours.push(n);
2038
+ if (commonNeighbours.length === 0) return epsilon;
2039
+ const degrees = await Promise.all(commonNeighbours.map((n) => graph.degree(n)));
2040
+ let score = 0;
2041
+ for (const degree of degrees) if (degree > 0) score += 1 / degree;
2042
+ if (normalise) {
2043
+ const maxScore = commonNeighbours.length;
2044
+ score = score / maxScore;
2045
+ }
2046
+ return Math.max(epsilon, score);
2047
+ }
1690
2048
  //#endregion
1691
2049
  //#region src/ranking/mi/overlap-coefficient.ts
1692
2050
  /**
@@ -1708,6 +2066,23 @@ function overlapCoefficient(graph, source, target, config) {
1708
2066
  const score = intersection / denominator;
1709
2067
  return Math.max(epsilon, score);
1710
2068
  }
2069
+ /**
2070
+ * Async variant of Overlap Coefficient for use with async graph data sources.
2071
+ *
2072
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
2073
+ */
2074
+ async function overlapCoefficientAsync(graph, source, target, config) {
2075
+ const { epsilon = 1e-10 } = config ?? {};
2076
+ const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
2077
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2078
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2079
+ let intersection = 0;
2080
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2081
+ const denominator = Math.min(srcSet.size, tgtSet.size);
2082
+ if (denominator === 0) return 0;
2083
+ const score = intersection / denominator;
2084
+ return Math.max(epsilon, score);
2085
+ }
1711
2086
  //#endregion
1712
2087
  //#region src/ranking/mi/hub-promoted.ts
1713
2088
  /**
@@ -1729,6 +2104,28 @@ function hubPromoted(graph, source, target, config) {
1729
2104
  const score = intersection / denominator;
1730
2105
  return Math.max(epsilon, score);
1731
2106
  }
2107
+ /**
2108
+ * Async variant of Hub Promoted index for use with async graph data sources.
2109
+ *
2110
+ * Fetches both neighbourhoods and degrees concurrently, then applies the same formula.
2111
+ */
2112
+ async function hubPromotedAsync(graph, source, target, config) {
2113
+ const { epsilon = 1e-10 } = config ?? {};
2114
+ const [sourceArr, targetArr, sourceDegree, targetDegree] = await Promise.all([
2115
+ require_async.collectAsyncIterable(graph.neighbours(source)),
2116
+ require_async.collectAsyncIterable(graph.neighbours(target)),
2117
+ graph.degree(source),
2118
+ graph.degree(target)
2119
+ ]);
2120
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2121
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2122
+ let intersection = 0;
2123
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2124
+ const denominator = Math.min(sourceDegree, targetDegree);
2125
+ if (denominator === 0) return 0;
2126
+ const score = intersection / denominator;
2127
+ return Math.max(epsilon, score);
2128
+ }
1732
2129
  //#endregion
1733
2130
  //#region src/ranking/mi/scale.ts
1734
2131
  /**
@@ -1745,6 +2142,31 @@ function scale(graph, source, target, config) {
1745
2142
  const score = jaccardScore / density;
1746
2143
  return Math.max(epsilon, score);
1747
2144
  }
2145
+ /**
2146
+ * Async variant of SCALE MI for use with async graph data sources.
2147
+ *
2148
+ * Fetches both neighbourhoods, node count, and edge count concurrently.
2149
+ */
2150
+ async function scaleAsync(graph, source, target, config) {
2151
+ const { epsilon = 1e-10 } = config ?? {};
2152
+ const [sourceArr, targetArr, n, m] = await Promise.all([
2153
+ require_async.collectAsyncIterable(graph.neighbours(source)),
2154
+ require_async.collectAsyncIterable(graph.neighbours(target)),
2155
+ graph.nodeCount,
2156
+ graph.edgeCount
2157
+ ]);
2158
+ const srcSet = new Set(sourceArr.filter((node) => node !== target));
2159
+ const tgtSet = new Set(targetArr.filter((node) => node !== source));
2160
+ let intersection = 0;
2161
+ for (const node of srcSet) if (tgtSet.has(node)) intersection++;
2162
+ const union = srcSet.size + tgtSet.size - intersection;
2163
+ const jaccardScore = union > 0 ? intersection / union : 0;
2164
+ const possibleEdges = n * (n - 1);
2165
+ const density = possibleEdges > 0 ? (graph.directed ? m : 2 * m) / possibleEdges : 0;
2166
+ if (density === 0) return epsilon;
2167
+ const score = jaccardScore / density;
2168
+ return Math.max(epsilon, score);
2169
+ }
1748
2170
  //#endregion
1749
2171
  //#region src/ranking/mi/skew.ts
1750
2172
  /**
@@ -1761,6 +2183,31 @@ function skew(graph, source, target, config) {
1761
2183
  const score = jaccardScore * sourceIdf * targetIdf;
1762
2184
  return Math.max(epsilon, score);
1763
2185
  }
2186
+ /**
2187
+ * Async variant of SKEW MI for use with async graph data sources.
2188
+ *
2189
+ * Fetches both neighbourhoods, degrees, and node count concurrently.
2190
+ */
2191
+ async function skewAsync(graph, source, target, config) {
2192
+ const { epsilon = 1e-10 } = config ?? {};
2193
+ const [sourceArr, targetArr, N, sourceDegree, targetDegree] = await Promise.all([
2194
+ require_async.collectAsyncIterable(graph.neighbours(source)),
2195
+ require_async.collectAsyncIterable(graph.neighbours(target)),
2196
+ graph.nodeCount,
2197
+ graph.degree(source),
2198
+ graph.degree(target)
2199
+ ]);
2200
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2201
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2202
+ let intersection = 0;
2203
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2204
+ const union = srcSet.size + tgtSet.size - intersection;
2205
+ const jaccardScore = union > 0 ? intersection / union : 0;
2206
+ const sourceIdf = Math.log(N / (sourceDegree + 1));
2207
+ const targetIdf = Math.log(N / (targetDegree + 1));
2208
+ const score = jaccardScore * sourceIdf * targetIdf;
2209
+ return Math.max(epsilon, score);
2210
+ }
1764
2211
  //#endregion
1765
2212
  //#region src/ranking/mi/span.ts
1766
2213
  /**
@@ -1774,6 +2221,40 @@ function span(graph, source, target, config) {
1774
2221
  const score = jaccardScore * (1 - Math.max(sourceCc, targetCc));
1775
2222
  return Math.max(epsilon, score);
1776
2223
  }
2224
+ /**
2225
+ * Async variant of SPAN MI for use with async graph data sources.
2226
+ *
2227
+ * Fetches both neighbourhoods concurrently, then computes the clustering
2228
+ * coefficient for each endpoint from the collected neighbour arrays.
2229
+ */
2230
+ async function spanAsync(graph, source, target, config) {
2231
+ const { epsilon = 1e-10 } = config ?? {};
2232
+ const [sourceArr, targetArr] = await Promise.all([require_async.collectAsyncIterable(graph.neighbours(source)), require_async.collectAsyncIterable(graph.neighbours(target))]);
2233
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2234
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2235
+ let intersection = 0;
2236
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2237
+ const union = srcSet.size + tgtSet.size - intersection;
2238
+ const jaccardScore = union > 0 ? intersection / union : 0;
2239
+ const computeClusteringCoefficient = async (nodeId, neighbourArr) => {
2240
+ const degree = neighbourArr.length;
2241
+ if (degree < 2) return 0;
2242
+ const pairs = [];
2243
+ for (let i = 0; i < neighbourArr.length; i++) for (let j = i + 1; j < neighbourArr.length; j++) {
2244
+ const u = neighbourArr[i];
2245
+ const v = neighbourArr[j];
2246
+ if (u !== void 0 && v !== void 0) pairs.push([u, v]);
2247
+ }
2248
+ const edgeResults = await Promise.all(pairs.flatMap(([u, v]) => [graph.getEdge(u, v), graph.getEdge(v, u)]));
2249
+ let triangleCount = 0;
2250
+ for (let i = 0; i < pairs.length; i++) if (edgeResults[2 * i] !== void 0 || edgeResults[2 * i + 1] !== void 0) triangleCount++;
2251
+ const possibleTriangles = degree * (degree - 1) / 2;
2252
+ return triangleCount / possibleTriangles;
2253
+ };
2254
+ const [sourceCc, targetCc] = await Promise.all([computeClusteringCoefficient(source, sourceArr), computeClusteringCoefficient(target, targetArr)]);
2255
+ const score = jaccardScore * (1 - Math.max(sourceCc, targetCc));
2256
+ return Math.max(epsilon, score);
2257
+ }
1777
2258
  //#endregion
1778
2259
  //#region src/ranking/mi/etch.ts
1779
2260
  /**
@@ -1789,6 +2270,37 @@ function etch(graph, source, target, config) {
1789
2270
  const score = jaccardScore * Math.log(graph.edgeCount / edgeTypeCount);
1790
2271
  return Math.max(epsilon, score);
1791
2272
  }
2273
+ /**
2274
+ * Async variant of ETCH MI for use with async graph data sources.
2275
+ *
2276
+ * Fetches both neighbourhoods and edge data concurrently, then counts
2277
+ * edges of the same type by iterating the async edge stream.
2278
+ */
2279
+ async function etchAsync(graph, source, target, config) {
2280
+ const { epsilon = 1e-10 } = config ?? {};
2281
+ const [sourceArr, targetArr, edge] = await Promise.all([
2282
+ require_async.collectAsyncIterable(graph.neighbours(source)),
2283
+ require_async.collectAsyncIterable(graph.neighbours(target)),
2284
+ graph.getEdge(source, target)
2285
+ ]);
2286
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2287
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2288
+ let intersection = 0;
2289
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2290
+ const union = srcSet.size + tgtSet.size - intersection;
2291
+ const jaccardScore = union > 0 ? intersection / union : 0;
2292
+ if (edge?.type === void 0) return Math.max(epsilon, jaccardScore);
2293
+ const edgeType = edge.type;
2294
+ let edgeTypeCount = 0;
2295
+ let totalEdges = 0;
2296
+ for await (const e of graph.edges()) {
2297
+ totalEdges++;
2298
+ if (e.type === edgeType) edgeTypeCount++;
2299
+ }
2300
+ if (edgeTypeCount === 0) return Math.max(epsilon, jaccardScore);
2301
+ const score = jaccardScore * Math.log(totalEdges / edgeTypeCount);
2302
+ return Math.max(epsilon, score);
2303
+ }
1792
2304
  //#endregion
1793
2305
  //#region src/ranking/mi/notch.ts
1794
2306
  /**
@@ -1808,6 +2320,44 @@ function notch(graph, source, target, config) {
1808
2320
  const score = jaccardScore * sourceRarity * targetRarity;
1809
2321
  return Math.max(epsilon, score);
1810
2322
  }
2323
+ /**
2324
+ * Async variant of NOTCH MI for use with async graph data sources.
2325
+ *
2326
+ * Fetches both neighbourhoods and node data concurrently, then counts
2327
+ * nodes of each type by iterating the async node stream.
2328
+ */
2329
+ async function notchAsync(graph, source, target, config) {
2330
+ const { epsilon = 1e-10 } = config ?? {};
2331
+ const [sourceArr, targetArr, sourceNode, targetNode] = await Promise.all([
2332
+ require_async.collectAsyncIterable(graph.neighbours(source)),
2333
+ require_async.collectAsyncIterable(graph.neighbours(target)),
2334
+ graph.getNode(source),
2335
+ graph.getNode(target)
2336
+ ]);
2337
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2338
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2339
+ let intersection = 0;
2340
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2341
+ const union = srcSet.size + tgtSet.size - intersection;
2342
+ const jaccardScore = union > 0 ? intersection / union : 0;
2343
+ if (sourceNode?.type === void 0 || targetNode?.type === void 0) return Math.max(epsilon, jaccardScore);
2344
+ const sourceType = sourceNode.type;
2345
+ const targetType = targetNode.type;
2346
+ let totalNodes = 0;
2347
+ let sourceTypeCount = 0;
2348
+ let targetTypeCount = 0;
2349
+ for await (const nodeId of graph.nodeIds()) {
2350
+ totalNodes++;
2351
+ const node = await graph.getNode(nodeId);
2352
+ if (node?.type === sourceType) sourceTypeCount++;
2353
+ if (node?.type === targetType) targetTypeCount++;
2354
+ }
2355
+ if (sourceTypeCount === 0 || targetTypeCount === 0) return Math.max(epsilon, jaccardScore);
2356
+ const sourceRarity = Math.log(totalNodes / sourceTypeCount);
2357
+ const targetRarity = Math.log(totalNodes / targetTypeCount);
2358
+ const score = jaccardScore * sourceRarity * targetRarity;
2359
+ return Math.max(epsilon, score);
2360
+ }
1811
2361
  //#endregion
1812
2362
  //#region src/ranking/mi/adaptive.ts
1813
2363
  /**
@@ -1840,6 +2390,38 @@ function adaptive(graph, source, target, config) {
1840
2390
  const score = (structuralWeight * structural + degreeWeight * degreeComponent + overlapWeight * overlap) / totalWeight;
1841
2391
  return Math.max(epsilon, Math.min(1, score));
1842
2392
  }
2393
+ /**
2394
+ * Async variant of Adaptive MI for use with async graph data sources.
2395
+ *
2396
+ * Fetches both neighbourhoods concurrently, then delegates degree-weighted
2397
+ * component to the async Adamic-Adar variant.
2398
+ */
2399
+ async function adaptiveAsync(graph, source, target, config) {
2400
+ const { epsilon = 1e-10, structuralWeight = .4, degreeWeight = .3, overlapWeight = .3 } = config ?? {};
2401
+ const [sourceArr, targetArr, degreeComponent] = await Promise.all([
2402
+ require_async.collectAsyncIterable(graph.neighbours(source)),
2403
+ require_async.collectAsyncIterable(graph.neighbours(target)),
2404
+ adamicAdarAsync(graph, source, target, {
2405
+ epsilon,
2406
+ normalise: true
2407
+ })
2408
+ ]);
2409
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
2410
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
2411
+ let intersection = 0;
2412
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
2413
+ const union = srcSet.size + tgtSet.size - intersection;
2414
+ const jaccardScore = union > 0 ? intersection / union : 0;
2415
+ const structural = srcSet.size === 0 && tgtSet.size === 0 ? 0 : Math.max(epsilon, jaccardScore);
2416
+ let overlap;
2417
+ if (srcSet.size > 0 && tgtSet.size > 0) {
2418
+ const minDegree = Math.min(srcSet.size, tgtSet.size);
2419
+ overlap = minDegree > 0 ? intersection / minDegree : epsilon;
2420
+ } else overlap = epsilon;
2421
+ const totalWeight = structuralWeight + degreeWeight + overlapWeight;
2422
+ const score = (structuralWeight * structural + degreeWeight * degreeComponent + overlapWeight * overlap) / totalWeight;
2423
+ return Math.max(epsilon, Math.min(1, score));
2424
+ }
1843
2425
  //#endregion
1844
2426
  //#region src/ranking/baselines/utils.ts
1845
2427
  /**
@@ -3340,7 +3922,9 @@ exports.GPUNotAvailableError = require_gpu.GPUNotAvailableError;
3340
3922
  exports.PriorityQueue = require_structures.PriorityQueue;
3341
3923
  exports._computeMean = require_kmeans._computeMean;
3342
3924
  exports.adamicAdar = adamicAdar;
3925
+ exports.adamicAdarAsync = adamicAdarAsync;
3343
3926
  exports.adaptive = adaptive;
3927
+ exports.adaptiveAsync = adaptiveAsync;
3344
3928
  exports.approximateClusteringCoefficient = require_utils.approximateClusteringCoefficient;
3345
3929
  exports.assertWebGPUAvailable = require_gpu.assertWebGPUAvailable;
3346
3930
  exports.base = base;
@@ -3349,52 +3933,68 @@ exports.batchClusteringCoefficients = require_utils.batchClusteringCoefficients;
3349
3933
  exports.betweenness = betweenness;
3350
3934
  exports.bfs = require_traversal.bfs;
3351
3935
  exports.bfsWithPath = require_traversal.bfsWithPath;
3936
+ exports.collectAsyncIterable = require_async.collectAsyncIterable;
3352
3937
  exports.communicability = communicability;
3353
3938
  exports.computeJaccard = require_utils.computeJaccard;
3354
3939
  exports.computeTrussNumbers = computeTrussNumbers;
3355
3940
  exports.cosine = cosine;
3941
+ exports.cosineAsync = cosineAsync;
3356
3942
  exports.countEdgesOfType = require_utils.countEdgesOfType;
3357
3943
  exports.countNodesOfType = require_utils.countNodesOfType;
3358
3944
  exports.createGPUContext = require_gpu.createGPUContext;
3359
3945
  exports.createResultBuffer = require_gpu.createResultBuffer;
3360
3946
  exports.csrToGPUBuffers = require_gpu.csrToGPUBuffers;
3947
+ exports.defaultYieldStrategy = require_async.defaultYieldStrategy;
3361
3948
  exports.degreeSum = degreeSum;
3362
3949
  exports.detectWebGPU = require_gpu.detectWebGPU;
3363
3950
  exports.dfs = require_traversal.dfs;
3364
3951
  exports.dfsPriority = dfsPriority;
3952
+ exports.dfsPriorityAsync = dfsPriorityAsync;
3365
3953
  exports.dfsPriorityFn = dfsPriorityFn;
3366
3954
  exports.dfsWithPath = require_traversal.dfsWithPath;
3367
3955
  exports.dome = dome;
3956
+ exports.domeAsync = domeAsync;
3368
3957
  exports.domeHighDegree = domeHighDegree;
3958
+ exports.domeHighDegreeAsync = domeHighDegreeAsync;
3369
3959
  exports.edge = edge;
3960
+ exports.edgeAsync = edgeAsync;
3370
3961
  exports.entropyFromCounts = require_utils.entropyFromCounts;
3371
3962
  exports.enumerateMotifs = enumerateMotifs;
3372
3963
  exports.enumerateMotifsWithInstances = enumerateMotifsWithInstances;
3373
3964
  exports.etch = etch;
3965
+ exports.etchAsync = etchAsync;
3374
3966
  exports.extractEgoNetwork = extractEgoNetwork;
3375
3967
  exports.extractInducedSubgraph = extractInducedSubgraph;
3376
3968
  exports.extractKCore = extractKCore;
3377
3969
  exports.extractKTruss = extractKTruss;
3378
3970
  exports.filterSubgraph = filterSubgraph;
3379
3971
  exports.flux = flux;
3972
+ exports.fluxAsync = fluxAsync;
3380
3973
  exports.frontierBalanced = frontierBalanced;
3974
+ exports.frontierBalancedAsync = frontierBalancedAsync;
3381
3975
  exports.fuse = fuse;
3976
+ exports.fuseAsync = fuseAsync;
3382
3977
  exports.getGPUContext = require_gpu.getGPUContext;
3383
3978
  exports.getMotifName = getMotifName;
3384
3979
  exports.graphToCSR = require_gpu.graphToCSR;
3385
3980
  exports.grasp = require_seeds.grasp;
3386
3981
  exports.hae = hae;
3982
+ exports.haeAsync = haeAsync;
3387
3983
  exports.hittingTime = hittingTime;
3388
3984
  exports.hubPromoted = hubPromoted;
3985
+ exports.hubPromotedAsync = hubPromotedAsync;
3389
3986
  exports.isWebGPUAvailable = require_gpu.isWebGPUAvailable;
3390
3987
  exports.jaccard = jaccard;
3391
3988
  exports.jaccardArithmetic = jaccardArithmetic;
3989
+ exports.jaccardAsync = jaccardAsync;
3392
3990
  exports.kHop = kHop;
3393
3991
  exports.katz = katz;
3394
3992
  exports.lace = lace;
3993
+ exports.laceAsync = laceAsync;
3395
3994
  exports.localClusteringCoefficient = require_utils.localClusteringCoefficient;
3396
3995
  exports.localTypeEntropy = require_utils.localTypeEntropy;
3397
3996
  exports.maze = maze;
3997
+ exports.mazeAsync = mazeAsync;
3398
3998
  exports.miniBatchKMeans = require_kmeans.miniBatchKMeans;
3399
3999
  exports.neighbourIntersection = require_utils.neighbourIntersection;
3400
4000
  exports.neighbourOverlap = require_utils.neighbourOverlap;
@@ -3403,29 +4003,56 @@ exports.normaliseFeatures = require_kmeans.normaliseFeatures;
3403
4003
  exports.zScoreNormalise = require_kmeans.normaliseFeatures;
3404
4004
  exports.normalisedEntropy = require_utils.normalisedEntropy;
3405
4005
  exports.notch = notch;
4006
+ exports.notchAsync = notchAsync;
4007
+ exports.opDegree = require_async.opDegree;
4008
+ exports.opGetEdge = require_async.opGetEdge;
4009
+ exports.opGetNode = require_async.opGetNode;
4010
+ exports.opHasNode = require_async.opHasNode;
4011
+ exports.opNeighbours = require_async.opNeighbours;
4012
+ exports.opProgress = require_async.opProgress;
4013
+ exports.opYield = require_async.opYield;
3406
4014
  exports.overlapCoefficient = overlapCoefficient;
4015
+ exports.overlapCoefficientAsync = overlapCoefficientAsync;
3407
4016
  exports.pagerank = pagerank;
3408
4017
  exports.parse = parse;
4018
+ exports.parseAsync = parseAsync;
3409
4019
  exports.pipe = pipe;
4020
+ exports.pipeAsync = pipeAsync;
3410
4021
  exports.randomPriority = randomPriority;
4022
+ exports.randomPriorityAsync = randomPriorityAsync;
3411
4023
  exports.randomRanking = randomRanking;
3412
4024
  exports.randomWalk = randomWalk;
3413
4025
  exports.reach = reach;
4026
+ exports.reachAsync = reachAsync;
3414
4027
  exports.readBufferToCPU = require_gpu.readBufferToCPU;
3415
4028
  exports.resistanceDistance = resistanceDistance;
4029
+ exports.resolveAsyncOp = require_async.resolveAsyncOp;
4030
+ exports.resolveSyncOp = require_async.resolveSyncOp;
3416
4031
  exports.resourceAllocation = resourceAllocation;
4032
+ exports.resourceAllocationAsync = resourceAllocationAsync;
4033
+ exports.runAsync = require_async.runAsync;
4034
+ exports.runSync = require_async.runSync;
3417
4035
  exports.sage = sage;
4036
+ exports.sageAsync = sageAsync;
3418
4037
  exports.scale = scale;
4038
+ exports.scaleAsync = scaleAsync;
3419
4039
  exports.shannonEntropy = require_utils.shannonEntropy;
3420
4040
  exports.shortest = shortest;
3421
4041
  exports.sift = sift;
4042
+ exports.siftAsync = siftAsync;
3422
4043
  exports.skew = skew;
4044
+ exports.skewAsync = skewAsync;
3423
4045
  exports.sorensen = sorensen;
4046
+ exports.sorensenAsync = sorensenAsync;
3424
4047
  exports.span = span;
4048
+ exports.spanAsync = spanAsync;
3425
4049
  exports.standardBfs = standardBfs;
4050
+ exports.standardBfsAsync = standardBfsAsync;
3426
4051
  exports.stratified = require_seeds.stratified;
3427
4052
  exports.tide = tide;
4053
+ exports.tideAsync = tideAsync;
3428
4054
  exports.warp = warp;
4055
+ exports.warpAsync = warpAsync;
3429
4056
  exports.widestPath = widestPath;
3430
4057
 
3431
4058
  //# sourceMappingURL=index.cjs.map