graphwise 1.5.2 → 1.7.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 (38) hide show
  1. package/README.md +36 -11
  2. package/dist/__test__/fixtures/index.d.ts +1 -0
  3. package/dist/__test__/fixtures/index.d.ts.map +1 -1
  4. package/dist/__test__/fixtures/wrap-async.d.ts +10 -0
  5. package/dist/__test__/fixtures/wrap-async.d.ts.map +1 -0
  6. package/dist/async/index.d.ts +11 -0
  7. package/dist/async/index.d.ts.map +1 -0
  8. package/dist/async/ops.d.ts +10 -0
  9. package/dist/async/ops.d.ts.map +1 -0
  10. package/dist/async/protocol.d.ts +62 -0
  11. package/dist/async/protocol.d.ts.map +1 -0
  12. package/dist/async/runners.d.ts +55 -0
  13. package/dist/async/runners.d.ts.map +1 -0
  14. package/dist/async/runners.unit.test.d.ts +8 -0
  15. package/dist/async/runners.unit.test.d.ts.map +1 -0
  16. package/dist/async/types.d.ts +15 -0
  17. package/dist/async/types.d.ts.map +1 -0
  18. package/dist/async/utils.d.ts +10 -0
  19. package/dist/async/utils.d.ts.map +1 -0
  20. package/dist/expansion/base-core.d.ts +24 -0
  21. package/dist/expansion/base-core.d.ts.map +1 -0
  22. package/dist/expansion/base-core.unit.test.d.ts +10 -0
  23. package/dist/expansion/base-core.unit.test.d.ts.map +1 -0
  24. package/dist/expansion/base-helpers.d.ts +57 -0
  25. package/dist/expansion/base-helpers.d.ts.map +1 -0
  26. package/dist/expansion/base.d.ts +32 -1
  27. package/dist/expansion/base.d.ts.map +1 -1
  28. package/dist/graph/async-interfaces.d.ts +31 -0
  29. package/dist/graph/async-interfaces.d.ts.map +1 -0
  30. package/dist/graph/async-interfaces.unit.test.d.ts +2 -0
  31. package/dist/graph/async-interfaces.unit.test.d.ts.map +1 -0
  32. package/dist/graph/index.d.ts +1 -0
  33. package/dist/graph/index.d.ts.map +1 -1
  34. package/dist/index/index.cjs +416 -82
  35. package/dist/index/index.cjs.map +1 -1
  36. package/dist/index/index.js +416 -83
  37. package/dist/index/index.js.map +1 -1
  38. package/package.json +1 -1
@@ -6,22 +6,326 @@ 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/expansion/base.ts
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
+ }
10
65
  /**
11
- * Default priority function - degree-ordered (DOME).
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
202
+ //#region src/expansion/base-helpers.ts
203
+ /**
204
+ * Check whether expansion should continue given current progress.
205
+ *
206
+ * Returns shouldContinue=false as soon as any configured limit is reached,
207
+ * along with the appropriate termination reason.
208
+ *
209
+ * @param iterations - Number of iterations completed so far
210
+ * @param nodesVisited - Number of distinct nodes visited so far
211
+ * @param pathsFound - Number of paths discovered so far
212
+ * @param limits - Configured expansion limits (0 = unlimited)
213
+ * @returns Whether to continue and the termination reason if stopping
214
+ */
215
+ function continueExpansion(iterations, nodesVisited, pathsFound, limits) {
216
+ if (limits.maxIterations > 0 && iterations >= limits.maxIterations) return {
217
+ shouldContinue: false,
218
+ termination: "limit"
219
+ };
220
+ if (limits.maxNodes > 0 && nodesVisited >= limits.maxNodes) return {
221
+ shouldContinue: false,
222
+ termination: "limit"
223
+ };
224
+ if (limits.maxPaths > 0 && pathsFound >= limits.maxPaths) return {
225
+ shouldContinue: false,
226
+ termination: "limit"
227
+ };
228
+ return {
229
+ shouldContinue: true,
230
+ termination: "exhausted"
231
+ };
232
+ }
233
+ /**
234
+ * Reconstruct path from collision point.
235
+ *
236
+ * Traces backwards through the predecessor maps of both frontiers from the
237
+ * collision node, then concatenates the two halves to form the full path.
238
+ *
239
+ * @param collisionNode - The node where the two frontiers met
240
+ * @param frontierA - Index of the first frontier
241
+ * @param frontierB - Index of the second frontier
242
+ * @param predecessors - Predecessor maps, one per frontier
243
+ * @param seeds - Seed nodes, one per frontier
244
+ * @returns The reconstructed path, or null if seeds are missing
245
+ */
246
+ function reconstructPath$1(collisionNode, frontierA, frontierB, predecessors, seeds) {
247
+ const pathA = [collisionNode];
248
+ const predA = predecessors[frontierA];
249
+ if (predA !== void 0) {
250
+ let node = collisionNode;
251
+ let next = predA.get(node);
252
+ while (next !== null && next !== void 0) {
253
+ node = next;
254
+ pathA.unshift(node);
255
+ next = predA.get(node);
256
+ }
257
+ }
258
+ const pathB = [];
259
+ const predB = predecessors[frontierB];
260
+ if (predB !== void 0) {
261
+ let node = collisionNode;
262
+ let next = predB.get(node);
263
+ while (next !== null && next !== void 0) {
264
+ node = next;
265
+ pathB.push(node);
266
+ next = predB.get(node);
267
+ }
268
+ }
269
+ const fullPath = [...pathA, ...pathB];
270
+ const seedA = seeds[frontierA];
271
+ const seedB = seeds[frontierB];
272
+ if (seedA === void 0 || seedB === void 0) return null;
273
+ return {
274
+ fromSeed: seedA,
275
+ toSeed: seedB,
276
+ nodes: fullPath
277
+ };
278
+ }
279
+ /**
280
+ * Create an empty expansion result for early termination (e.g. no seeds given).
281
+ *
282
+ * @param algorithm - Name of the algorithm producing this result
283
+ * @param startTime - performance.now() timestamp taken before the algorithm began
284
+ * @returns An ExpansionResult with zero paths and zero stats
285
+ */
286
+ function emptyResult$2(algorithm, startTime) {
287
+ return {
288
+ paths: [],
289
+ sampledNodes: /* @__PURE__ */ new Set(),
290
+ sampledEdges: /* @__PURE__ */ new Set(),
291
+ visitedPerFrontier: [],
292
+ stats: {
293
+ iterations: 0,
294
+ nodesVisited: 0,
295
+ edgesTraversed: 0,
296
+ pathsFound: 0,
297
+ durationMs: performance.now() - startTime,
298
+ algorithm,
299
+ termination: "exhausted"
300
+ }
301
+ };
302
+ }
303
+ //#endregion
304
+ //#region src/expansion/base-core.ts
305
+ /**
306
+ * Default priority function — degree-ordered (DOME).
307
+ *
308
+ * Lower degree = higher priority, so sparse nodes are explored before hubs.
12
309
  */
13
310
  function degreePriority(_nodeId, context) {
14
311
  return context.degree;
15
312
  }
16
313
  /**
17
- * Run BASE expansion algorithm.
314
+ * Generator core of the BASE expansion algorithm.
18
315
  *
19
- * @param graph - Source graph
316
+ * Yields GraphOp objects to request graph data, allowing the caller to
317
+ * provide a sync or async runner. The optional `graphRef` parameter is
318
+ * required when the priority function accesses `context.graph` — it is
319
+ * populated in sync mode by `base()`. In async mode (Phase 4+), a proxy
320
+ * graph may be supplied instead.
321
+ *
322
+ * @param graphMeta - Immutable graph metadata (directed, nodeCount, edgeCount)
20
323
  * @param seeds - Seed nodes for expansion
21
- * @param config - Expansion configuration
22
- * @returns Expansion result with discovered paths
324
+ * @param config - Expansion configuration (priority, limits, debug)
325
+ * @param graphRef - Optional real graph reference for context.graph in priority functions
326
+ * @returns An ExpansionResult with all discovered paths and statistics
23
327
  */
24
- function base(graph, seeds, config) {
328
+ function* baseCore(graphMeta, seeds, config, graphRef) {
25
329
  const startTime = performance.now();
26
330
  const { maxNodes = 0, maxIterations = 0, maxPaths = 0, priority = degreePriority, debug = false } = config ?? {};
27
331
  if (seeds.length === 0) return emptyResult$2("base", startTime);
@@ -41,7 +345,8 @@ function base(graph, seeds, config) {
41
345
  predecessors[i]?.set(seedNode, null);
42
346
  combinedVisited.set(seedNode, i);
43
347
  allVisited.add(seedNode);
44
- const seedPriority = priority(seedNode, createPriorityContext(graph, seedNode, i, combinedVisited, allVisited, [], 0));
348
+ const seedDegree = yield* opDegree(seedNode);
349
+ const seedPriority = priority(seedNode, buildPriorityContext(seedNode, i, combinedVisited, allVisited, [], 0, seedDegree, graphRef));
45
350
  queues[i]?.push({
46
351
  nodeId: seedNode,
47
352
  frontierIndex: i,
@@ -53,22 +358,17 @@ function base(graph, seeds, config) {
53
358
  let iterations = 0;
54
359
  let edgesTraversed = 0;
55
360
  let termination = "exhausted";
56
- const continueExpansion = () => {
57
- if (maxIterations > 0 && iterations >= maxIterations) {
58
- termination = "limit";
59
- return false;
60
- }
61
- if (maxNodes > 0 && allVisited.size >= maxNodes) {
62
- termination = "limit";
63
- return false;
64
- }
65
- if (maxPaths > 0 && discoveredPaths.length >= maxPaths) {
66
- termination = "limit";
67
- return false;
68
- }
69
- return true;
361
+ const limits = {
362
+ maxIterations,
363
+ maxNodes,
364
+ maxPaths
70
365
  };
71
- while (continueExpansion()) {
366
+ for (;;) {
367
+ const check = continueExpansion(iterations, allVisited.size, discoveredPaths.length, limits);
368
+ if (!check.shouldContinue) {
369
+ termination = check.termination;
370
+ break;
371
+ }
72
372
  let lowestPriority = Number.POSITIVE_INFINITY;
73
373
  let activeFrontier = -1;
74
374
  for (let i = 0; i < numFrontiers; i++) {
@@ -112,7 +412,7 @@ function base(graph, seeds, config) {
112
412
  }
113
413
  }
114
414
  }
115
- const neighbours = graph.neighbours(nodeId);
415
+ const neighbours = yield* opNeighbours(nodeId);
116
416
  for (const neighbour of neighbours) {
117
417
  edgesTraversed++;
118
418
  const [s, t] = nodeId < neighbour ? [nodeId, neighbour] : [neighbour, nodeId];
@@ -122,9 +422,10 @@ function base(graph, seeds, config) {
122
422
  sampledEdgeMap.set(s, targets);
123
423
  }
124
424
  targets.add(t);
125
- const frontierVisited = visitedByFrontier[activeFrontier];
126
- if (frontierVisited === void 0 || frontierVisited.has(neighbour)) continue;
127
- const neighbourPriority = priority(neighbour, createPriorityContext(graph, neighbour, activeFrontier, combinedVisited, allVisited, discoveredPaths, iterations + 1));
425
+ const fv = visitedByFrontier[activeFrontier];
426
+ if (fv === void 0 || fv.has(neighbour)) continue;
427
+ const neighbourDegree = yield* opDegree(neighbour);
428
+ const neighbourPriority = priority(neighbour, buildPriorityContext(neighbour, activeFrontier, combinedVisited, allVisited, discoveredPaths, iterations + 1, neighbourDegree, graphRef));
128
429
  queue.push({
129
430
  nodeId: neighbour,
130
431
  frontierIndex: activeFrontier,
@@ -154,12 +455,50 @@ function base(graph, seeds, config) {
154
455
  };
155
456
  }
156
457
  /**
157
- * Create priority context for a node.
458
+ * Create a sentinel ReadableGraph that throws if any member is accessed.
459
+ *
460
+ * Used in async mode when no graphRef is provided. Gives a clear error
461
+ * message rather than silently returning incorrect results if a priority
462
+ * function attempts to access `context.graph` before Phase 4b introduces
463
+ * a real async proxy.
158
464
  */
159
- function createPriorityContext(graph, nodeId, frontierIndex, combinedVisited, allVisited, discoveredPaths, iteration) {
465
+ function makeNoGraphSentinel() {
466
+ const msg = "Priority function accessed context.graph in async mode without a graph proxy. Pass a graphRef or use a priority function that does not access context.graph.";
467
+ const fail = () => {
468
+ throw new Error(msg);
469
+ };
160
470
  return {
161
- graph,
162
- degree: graph.degree(nodeId),
471
+ get directed() {
472
+ return fail();
473
+ },
474
+ get nodeCount() {
475
+ return fail();
476
+ },
477
+ get edgeCount() {
478
+ return fail();
479
+ },
480
+ hasNode: fail,
481
+ getNode: fail,
482
+ nodeIds: fail,
483
+ neighbours: fail,
484
+ degree: fail,
485
+ getEdge: fail,
486
+ edges: fail
487
+ };
488
+ }
489
+ /**
490
+ * Build a PriorityContext for a node using a pre-fetched degree.
491
+ *
492
+ * When `graphRef` is provided (sync mode), it is used as `context.graph` so
493
+ * priority functions can access the graph directly. When it is absent (async
494
+ * mode), a Proxy is used in its place that throws a clear error if any
495
+ * property is accessed — this prevents silent failures until Phase 4b
496
+ * introduces a real async proxy graph.
497
+ */
498
+ function buildPriorityContext(_nodeId, frontierIndex, combinedVisited, allVisited, discoveredPaths, iteration, degree, graphRef) {
499
+ return {
500
+ graph: graphRef ?? makeNoGraphSentinel(),
501
+ degree,
163
502
  frontierIndex,
164
503
  visitedByFrontier: combinedVisited,
165
504
  allVisited,
@@ -167,61 +506,55 @@ function createPriorityContext(graph, nodeId, frontierIndex, combinedVisited, al
167
506
  iteration
168
507
  };
169
508
  }
509
+ //#endregion
510
+ //#region src/expansion/base.ts
170
511
  /**
171
- * Reconstruct path from collision point.
512
+ * Run BASE expansion synchronously.
513
+ *
514
+ * Delegates to baseCore + runSync. Behaviour is identical to the previous
515
+ * direct implementation — all existing callers are unaffected.
516
+ *
517
+ * @param graph - Source graph
518
+ * @param seeds - Seed nodes for expansion
519
+ * @param config - Expansion configuration
520
+ * @returns Expansion result with discovered paths
172
521
  */
173
- function reconstructPath$1(collisionNode, frontierA, frontierB, predecessors, seeds) {
174
- const pathA = [collisionNode];
175
- const predA = predecessors[frontierA];
176
- if (predA !== void 0) {
177
- let node = collisionNode;
178
- let next = predA.get(node);
179
- while (next !== null && next !== void 0) {
180
- node = next;
181
- pathA.unshift(node);
182
- next = predA.get(node);
183
- }
184
- }
185
- const pathB = [];
186
- const predB = predecessors[frontierB];
187
- if (predB !== void 0) {
188
- let node = collisionNode;
189
- let next = predB.get(node);
190
- while (next !== null && next !== void 0) {
191
- node = next;
192
- pathB.push(node);
193
- next = predB.get(node);
194
- }
195
- }
196
- const fullPath = [...pathA, ...pathB];
197
- const seedA = seeds[frontierA];
198
- const seedB = seeds[frontierB];
199
- if (seedA === void 0 || seedB === void 0) return null;
200
- return {
201
- fromSeed: seedA,
202
- toSeed: seedB,
203
- nodes: fullPath
204
- };
522
+ function base(graph, seeds, config) {
523
+ return runSync(baseCore({
524
+ directed: graph.directed,
525
+ nodeCount: graph.nodeCount,
526
+ edgeCount: graph.edgeCount
527
+ }, seeds, config, graph), graph);
205
528
  }
206
529
  /**
207
- * Create an empty result for early termination.
208
- */
209
- function emptyResult$2(algorithm, startTime) {
210
- return {
211
- paths: [],
212
- sampledNodes: /* @__PURE__ */ new Set(),
213
- sampledEdges: /* @__PURE__ */ new Set(),
214
- visitedPerFrontier: [],
215
- stats: {
216
- iterations: 0,
217
- nodesVisited: 0,
218
- edgesTraversed: 0,
219
- pathsFound: 0,
220
- durationMs: performance.now() - startTime,
221
- algorithm,
222
- termination: "exhausted"
223
- }
224
- };
530
+ * Run BASE expansion asynchronously.
531
+ *
532
+ * Delegates to baseCore + runAsync. Supports:
533
+ * - Cancellation via AbortSignal (config.signal)
534
+ * - Progress callbacks (config.onProgress)
535
+ * - Custom cooperative yield strategies (config.yieldStrategy)
536
+ *
537
+ * Note: priority functions that access `context.graph` are not supported in
538
+ * async mode without a graph proxy (Phase 4b). The default degree-based
539
+ * priority (DOME) does not access context.graph and works correctly.
540
+ *
541
+ * @param graph - Async source graph
542
+ * @param seeds - Seed nodes for expansion
543
+ * @param config - Expansion and async runner configuration
544
+ * @returns Promise resolving to the expansion result
545
+ */
546
+ async function baseAsync(graph, seeds, config) {
547
+ const [nodeCount, edgeCount] = await Promise.all([graph.nodeCount, graph.edgeCount]);
548
+ const gen = baseCore({
549
+ directed: graph.directed,
550
+ nodeCount,
551
+ edgeCount
552
+ }, seeds, config);
553
+ const runnerOptions = {};
554
+ if (config?.signal !== void 0) runnerOptions.signal = config.signal;
555
+ if (config?.onProgress !== void 0) runnerOptions.onProgress = config.onProgress;
556
+ if (config?.yieldStrategy !== void 0) runnerOptions.yieldStrategy = config.yieldStrategy;
557
+ return runAsync(gen, graph, runnerOptions);
225
558
  }
226
559
  //#endregion
227
560
  //#region src/expansion/dome.ts
@@ -3011,6 +3344,7 @@ exports.adaptive = adaptive;
3011
3344
  exports.approximateClusteringCoefficient = require_utils.approximateClusteringCoefficient;
3012
3345
  exports.assertWebGPUAvailable = require_gpu.assertWebGPUAvailable;
3013
3346
  exports.base = base;
3347
+ exports.baseAsync = baseAsync;
3014
3348
  exports.batchClusteringCoefficients = require_utils.batchClusteringCoefficients;
3015
3349
  exports.betweenness = betweenness;
3016
3350
  exports.bfs = require_traversal.bfs;