gitnexus 1.6.13-rc.82 → 1.6.13-rc.83
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.
|
@@ -12,6 +12,12 @@ import type { NodeLabel } from '../../_shared/index.js';
|
|
|
12
12
|
import { KnowledgeGraph } from '../graph/types.js';
|
|
13
13
|
/** Graphology Graph instance type (AbstractGraph from graphology-types avoids CJS/ESM interop namespace issue) */
|
|
14
14
|
type GraphInstance = AbstractGraph<Attributes, Attributes, Attributes>;
|
|
15
|
+
/** Result returned by leiden.detailed() */
|
|
16
|
+
interface LeidenDetailedResult {
|
|
17
|
+
communities: Record<string, number>;
|
|
18
|
+
count: number;
|
|
19
|
+
modularity: number;
|
|
20
|
+
}
|
|
15
21
|
type CommunityEngine = 'graphology' | 'icebug';
|
|
16
22
|
export type CommunityDetectionEngine = CommunityEngine | 'auto';
|
|
17
23
|
export interface CommunityDetectionOptions {
|
|
@@ -48,6 +54,11 @@ export interface CommunityCsr {
|
|
|
48
54
|
indptr: BigUint64Array;
|
|
49
55
|
indices: BigUint64Array;
|
|
50
56
|
}
|
|
57
|
+
interface CommunityEngineResult extends LeidenDetailedResult {
|
|
58
|
+
engine: CommunityEngine;
|
|
59
|
+
engineRequested: CommunityDetectionEngine;
|
|
60
|
+
fallbackReason?: string;
|
|
61
|
+
}
|
|
51
62
|
export declare const resolveCommunityDetectionEngine: (raw?: string) => CommunityDetectionEngine;
|
|
52
63
|
export interface CommunityNode {
|
|
53
64
|
id: string;
|
|
@@ -92,6 +103,7 @@ export declare const processCommunities: (knowledgeGraph: KnowledgeGraph, onProg
|
|
|
92
103
|
export declare const buildCommunityProjection: (knowledgeGraph: KnowledgeGraph) => CommunityProjection;
|
|
93
104
|
export declare const buildCommunityCsr: (projection: CommunityProjection) => CommunityCsr;
|
|
94
105
|
export declare const buildGraphologyGraph: (projection: CommunityProjection) => GraphInstance;
|
|
106
|
+
export declare const runGraphologyLeiden: (graph: GraphInstance, isLarge: boolean, engineRequested: CommunityDetectionEngine, timeoutMs?: number) => Promise<CommunityEngineResult>;
|
|
95
107
|
/**
|
|
96
108
|
* Runs Leiden in a worker so a native crash cannot take the analyze process
|
|
97
109
|
* with it. Written against @ladybugmem/icebug's published surface (lib/index.js
|
|
@@ -21,7 +21,9 @@ const __dirname = dirname(__filename);
|
|
|
21
21
|
// Navigate to package root (works from both src/ and dist/)
|
|
22
22
|
const leidenPath = resolve(__dirname, '..', '..', '..', 'vendor', 'leiden', 'index.cjs');
|
|
23
23
|
const _require = createRequire(import.meta.url);
|
|
24
|
-
|
|
24
|
+
// The Leiden worker loads its own copy; this load keeps a missing vendor/ asset
|
|
25
|
+
// failing at import time and keeps it visible to dockerfile-runtime-asset-parity.
|
|
26
|
+
_require(leidenPath);
|
|
25
27
|
/**
|
|
26
28
|
* Deterministic PRNG (mulberry32) seed for the vendored Leiden algorithm.
|
|
27
29
|
* Vendored Leiden defaults `rng: Math.random`, which makes community
|
|
@@ -30,15 +32,6 @@ const leiden = _require(leidenPath);
|
|
|
30
32
|
* incremental-indexing equivalence test (incremental ≡ full rebuild).
|
|
31
33
|
*/
|
|
32
34
|
const LEIDEN_SEED = 0xc0de;
|
|
33
|
-
function createSeededRng(seed) {
|
|
34
|
-
let s = seed >>> 0;
|
|
35
|
-
return () => {
|
|
36
|
-
s = (s + 0x6d2b79f5) >>> 0;
|
|
37
|
-
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
38
|
-
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
39
|
-
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
35
|
const COMMUNITY_ENGINE_ENV = 'GITNEXUS_COMMUNITY_ENGINE';
|
|
43
36
|
/**
|
|
44
37
|
* Not a declared dependency: the prebuilds need system Arrow 24, libomp and
|
|
@@ -289,19 +282,96 @@ const runCommunityEngine = async (graph, projection, engineRequested, options, o
|
|
|
289
282
|
const fallbackReason = error instanceof Error ? error.message : String(error);
|
|
290
283
|
onProgress?.(`Icebug community engine unavailable, falling back to Graphology: ${fallbackReason}`, 35);
|
|
291
284
|
const fallback = await runGraphologyLeiden(graph, projection.isLarge, engineRequested);
|
|
292
|
-
return {
|
|
285
|
+
return {
|
|
286
|
+
...fallback,
|
|
287
|
+
fallbackReason: fallback.fallbackReason
|
|
288
|
+
? `${fallbackReason}; ${fallback.fallbackReason}`
|
|
289
|
+
: fallbackReason,
|
|
290
|
+
};
|
|
293
291
|
}
|
|
294
292
|
};
|
|
295
|
-
|
|
293
|
+
/**
|
|
294
|
+
* Runs the vendored Leiden in a worker so LEIDEN_TIMEOUT_MS can actually fire.
|
|
295
|
+
* leiden.detailed is synchronous and, on some graphs, never returns
|
|
296
|
+
* (graphology/graphology#557); a timer on the main thread cannot interrupt it.
|
|
297
|
+
* The worker is pure JS, so terminate() is safe (unlike the icebug N-API worker).
|
|
298
|
+
*/
|
|
299
|
+
const GRAPHOLOGY_WORKER_SOURCE = `
|
|
300
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
301
|
+
try {
|
|
302
|
+
const Graph = require(workerData.graphologyPath);
|
|
303
|
+
const leiden = require(workerData.leidenPath);
|
|
304
|
+
const graph = Graph.from(workerData.graph);
|
|
305
|
+
const rng = (() => {
|
|
306
|
+
let s = workerData.seed >>> 0;
|
|
307
|
+
return () => {
|
|
308
|
+
s = (s + 0x6d2b79f5) >>> 0;
|
|
309
|
+
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
310
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
311
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
312
|
+
};
|
|
313
|
+
})();
|
|
314
|
+
const d = leiden.detailed(graph, {
|
|
315
|
+
resolution: workerData.resolution,
|
|
316
|
+
maxIterations: workerData.maxIterations,
|
|
317
|
+
rng,
|
|
318
|
+
});
|
|
319
|
+
parentPort.postMessage({
|
|
320
|
+
ok: true,
|
|
321
|
+
communities: d.communities,
|
|
322
|
+
count: d.count,
|
|
323
|
+
modularity: d.modularity,
|
|
324
|
+
});
|
|
325
|
+
} catch (error) {
|
|
326
|
+
parentPort.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
327
|
+
}
|
|
328
|
+
`;
|
|
329
|
+
export const runGraphologyLeiden = async (graph, isLarge, engineRequested, timeoutMs = LEIDEN_TIMEOUT_MS) => {
|
|
296
330
|
try {
|
|
297
|
-
const details = await Promise
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
const details = await new Promise((resolve, reject) => {
|
|
332
|
+
const worker = new Worker(GRAPHOLOGY_WORKER_SOURCE, {
|
|
333
|
+
eval: true,
|
|
334
|
+
workerData: {
|
|
335
|
+
graphologyPath: _require.resolve('graphology'),
|
|
336
|
+
leidenPath,
|
|
337
|
+
graph: graph.export(),
|
|
338
|
+
seed: LEIDEN_SEED,
|
|
339
|
+
resolution: isLarge ? 2.0 : 1.0,
|
|
340
|
+
maxIterations: isLarge ? 3 : 0,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
let settled = false;
|
|
344
|
+
const timeout = setTimeout(() => {
|
|
345
|
+
settled = true;
|
|
346
|
+
void worker.terminate();
|
|
347
|
+
reject(new Error('Leiden timeout'));
|
|
348
|
+
}, timeoutMs);
|
|
349
|
+
worker.once('message', (message) => {
|
|
350
|
+
settled = true;
|
|
351
|
+
clearTimeout(timeout);
|
|
352
|
+
if (message.ok === true) {
|
|
353
|
+
resolve({
|
|
354
|
+
communities: message.communities,
|
|
355
|
+
count: message.count,
|
|
356
|
+
modularity: message.modularity,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
reject(new Error(message.error));
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
worker.once('error', (error) => {
|
|
364
|
+
settled = true;
|
|
365
|
+
clearTimeout(timeout);
|
|
366
|
+
reject(error);
|
|
367
|
+
});
|
|
368
|
+
worker.once('exit', (code) => {
|
|
369
|
+
if (settled)
|
|
370
|
+
return;
|
|
371
|
+
clearTimeout(timeout);
|
|
372
|
+
reject(new Error(`Graphology Leiden worker exited with code ${code}`));
|
|
373
|
+
});
|
|
374
|
+
});
|
|
305
375
|
return { ...details, engine: 'graphology', engineRequested };
|
|
306
376
|
}
|
|
307
377
|
catch (e) {
|
package/package.json
CHANGED
|
@@ -289,6 +289,7 @@ const SPAWN_CLI = [
|
|
|
289
289
|
// Worker threads tests — exercise real worker_threads which have
|
|
290
290
|
// platform-specific behavior (thread spawning, IPC, exit handling)
|
|
291
291
|
const WORKER_THREADS = [
|
|
292
|
+
'test/unit/community-processor.test.ts',
|
|
292
293
|
'test/integration/worker-pool.test.ts',
|
|
293
294
|
'test/integration/parse-impl-quarantine-cache-skip.test.ts',
|
|
294
295
|
];
|