@merittdev/horus 0.11.0 → 0.12.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.
- package/dist/index.cjs +246 -196
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65880,8 +65880,8 @@ init_cjs_shims();
|
|
|
65880
65880
|
|
|
65881
65881
|
// ../../packages/core/src/version.ts
|
|
65882
65882
|
init_cjs_shims();
|
|
65883
|
-
var HORUS_VERSION = true ? "0.
|
|
65884
|
-
var PINNED_SOURCE_VERSION = "
|
|
65883
|
+
var HORUS_VERSION = true ? "0.12.0" : "dev";
|
|
65884
|
+
var PINNED_SOURCE_VERSION = "2.0.0";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
65887
65887
|
init_cjs_shims();
|
|
@@ -70989,8 +70989,72 @@ var SourceHttpClient = class {
|
|
|
70989
70989
|
return this.request("POST", "/api/diff", { base: base2, compare });
|
|
70990
70990
|
}
|
|
70991
70991
|
async nodeCount() {
|
|
70992
|
-
const
|
|
70993
|
-
return Number(
|
|
70992
|
+
const o5 = await this.overview();
|
|
70993
|
+
return Number(o5.totalNodes) || 0;
|
|
70994
|
+
}
|
|
70995
|
+
// -------------------------------------------------------------------------
|
|
70996
|
+
// Typed read-path endpoints (HOR-392). These replace the CLI's raw Cypher:
|
|
70997
|
+
// the host owns the queries and returns shaped JSON, so the client never
|
|
70998
|
+
// emits or escapes Cypher on the read path.
|
|
70999
|
+
// -------------------------------------------------------------------------
|
|
71000
|
+
/** Nodes whose full content contains ANY of `tokens` — full (untruncated) content. */
|
|
71001
|
+
async contentSearch(tokens, limit = 500) {
|
|
71002
|
+
const res = await this.request("POST", "/api/content-search", {
|
|
71003
|
+
tokens,
|
|
71004
|
+
limit
|
|
71005
|
+
});
|
|
71006
|
+
return res.results ?? [];
|
|
71007
|
+
}
|
|
71008
|
+
/** Exact-name symbol lookup (file label excluded), with line ranges. */
|
|
71009
|
+
async exactSymbols(name2, limit = 10) {
|
|
71010
|
+
const res = await this.request(
|
|
71011
|
+
"GET",
|
|
71012
|
+
`/api/symbols/exact?name=${encodeURIComponent(name2)}&limit=${limit}`
|
|
71013
|
+
);
|
|
71014
|
+
return res.results ?? [];
|
|
71015
|
+
}
|
|
71016
|
+
/** Symbol nodes for the given comma-joinable lowercase labels (source-graph extraction). */
|
|
71017
|
+
async symbolsByLabel(labels, limit = 1e3) {
|
|
71018
|
+
const res = await this.request(
|
|
71019
|
+
"GET",
|
|
71020
|
+
`/api/symbols?labels=${encodeURIComponent(labels.join(","))}&limit=${limit}`
|
|
71021
|
+
);
|
|
71022
|
+
return res.symbols ?? [];
|
|
71023
|
+
}
|
|
71024
|
+
/** Batch-resolve node ids to their line ranges (CLI line hydration). */
|
|
71025
|
+
async nodesLines(ids2) {
|
|
71026
|
+
const res = await this.request(
|
|
71027
|
+
"POST",
|
|
71028
|
+
"/api/nodes/lines",
|
|
71029
|
+
{ ids: ids2 }
|
|
71030
|
+
);
|
|
71031
|
+
return res.lines ?? {};
|
|
71032
|
+
}
|
|
71033
|
+
/** Process flows a symbol participates in, with each flow's named ordered steps. */
|
|
71034
|
+
flows(nodeId) {
|
|
71035
|
+
return this.request("GET", `/api/flows/${encodeURI(nodeId)}`);
|
|
71036
|
+
}
|
|
71037
|
+
/** Method symbols of `className` defined in `file`, ordered by start line. */
|
|
71038
|
+
async classMethods(file, className) {
|
|
71039
|
+
const res = await this.request(
|
|
71040
|
+
"GET",
|
|
71041
|
+
`/api/class-methods?file=${encodeURIComponent(file)}&class=${encodeURIComponent(className)}`
|
|
71042
|
+
);
|
|
71043
|
+
return res.methods ?? [];
|
|
71044
|
+
}
|
|
71045
|
+
/** Extended node detail — node + content + callers/callees/typeRefs + imports + coupling + communities. */
|
|
71046
|
+
node(nodeId) {
|
|
71047
|
+
return this.request("GET", `/api/node/${encodeURI(nodeId)}`);
|
|
71048
|
+
}
|
|
71049
|
+
/** Community clusters with their member nodes (source-graph extraction). */
|
|
71050
|
+
async communities() {
|
|
71051
|
+
const res = await this.request("GET", "/api/communities");
|
|
71052
|
+
return res.communities ?? [];
|
|
71053
|
+
}
|
|
71054
|
+
/** Discovered execution processes with their ordered steps (source-graph extraction). */
|
|
71055
|
+
async processes() {
|
|
71056
|
+
const res = await this.request("GET", "/api/processes");
|
|
71057
|
+
return res.processes ?? [];
|
|
70994
71058
|
}
|
|
70995
71059
|
// -------------------------------------------------------------------------
|
|
70996
71060
|
// Memory vector bridge (M2). These reuse `request<T>` (retry/timeout/4xx) so
|
|
@@ -71271,13 +71335,6 @@ var SourceCodeProvider = class {
|
|
|
71271
71335
|
id = "source";
|
|
71272
71336
|
kind = "code";
|
|
71273
71337
|
// --- helpers -------------------------------------------------------------
|
|
71274
|
-
/**
|
|
71275
|
-
* Escape a node id for inlining inside a double-quoted Cypher string literal:
|
|
71276
|
-
* first double every backslash, then escape every double-quote.
|
|
71277
|
-
*/
|
|
71278
|
-
escapeId(id) {
|
|
71279
|
-
return id.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
71280
|
-
}
|
|
71281
71338
|
nodeToSymbol(n4) {
|
|
71282
71339
|
return {
|
|
71283
71340
|
id: n4.id,
|
|
@@ -71290,29 +71347,34 @@ var SourceCodeProvider = class {
|
|
|
71290
71347
|
className: n4.className
|
|
71291
71348
|
};
|
|
71292
71349
|
}
|
|
71293
|
-
|
|
71350
|
+
/** Map a serialized graph node to a `Symbol`, coercing the host's `0` line sentinel to undefined. */
|
|
71351
|
+
serializedToSymbol(n4) {
|
|
71352
|
+
const sym = {
|
|
71353
|
+
id: n4.id ?? "",
|
|
71354
|
+
name: n4.name ?? "",
|
|
71355
|
+
filePath: n4.filePath ?? ""
|
|
71356
|
+
};
|
|
71357
|
+
if (typeof n4.startLine === "number" && n4.startLine > 0) sym.startLine = n4.startLine;
|
|
71358
|
+
if (typeof n4.endLine === "number" && n4.endLine > 0) sym.endLine = n4.endLine;
|
|
71359
|
+
if (n4.className) sym.className = n4.className;
|
|
71360
|
+
if (n4.signature) sym.signature = n4.signature;
|
|
71361
|
+
if (n4.language) sym.language = n4.language;
|
|
71362
|
+
return sym;
|
|
71363
|
+
}
|
|
71364
|
+
/**
|
|
71365
|
+
* Graceful degradation (HOR-353): a 4xx for THIS repo's graph shape must not abort the
|
|
71366
|
+
* whole investigation. The caller passes a default the slice degrades to.
|
|
71367
|
+
*/
|
|
71368
|
+
async degrade(p5, fallback) {
|
|
71294
71369
|
try {
|
|
71295
|
-
return
|
|
71370
|
+
return await p5;
|
|
71296
71371
|
} catch (err2) {
|
|
71297
71372
|
if (err2 instanceof SourceHttpError && err2.status >= 400 && err2.status < 500) {
|
|
71298
|
-
return
|
|
71373
|
+
return fallback;
|
|
71299
71374
|
}
|
|
71300
71375
|
throw err2;
|
|
71301
71376
|
}
|
|
71302
71377
|
}
|
|
71303
|
-
cypherRowToSymbol(row, idIdx, nameIdx, fileIdx, lineIdx, classNameIdx) {
|
|
71304
|
-
const startLine = lineIdx != null && row[lineIdx] != null ? Number(row[lineIdx]) : void 0;
|
|
71305
|
-
const sym = {
|
|
71306
|
-
id: String(row[idIdx] ?? ""),
|
|
71307
|
-
name: String(row[nameIdx] ?? ""),
|
|
71308
|
-
filePath: String(row[fileIdx] ?? ""),
|
|
71309
|
-
startLine
|
|
71310
|
-
};
|
|
71311
|
-
if (classNameIdx != null && row[classNameIdx] != null) {
|
|
71312
|
-
sym.className = String(row[classNameIdx]);
|
|
71313
|
-
}
|
|
71314
|
-
return sym;
|
|
71315
|
-
}
|
|
71316
71378
|
// --- contract ------------------------------------------------------------
|
|
71317
71379
|
async health() {
|
|
71318
71380
|
const h3 = await this.client.health();
|
|
@@ -71322,19 +71384,22 @@ var SourceCodeProvider = class {
|
|
|
71322
71384
|
};
|
|
71323
71385
|
}
|
|
71324
71386
|
async searchSymbols(query, limit = 10) {
|
|
71325
|
-
const
|
|
71326
|
-
|
|
71327
|
-
const [exactRows, semanticRes] = await Promise.all([
|
|
71328
|
-
this.rows(exactQuery).catch(() => []),
|
|
71387
|
+
const [exactHits, semanticRes] = await Promise.all([
|
|
71388
|
+
this.degrade(this.client.exactSymbols(query, limit), []),
|
|
71329
71389
|
this.client.search(query, limit)
|
|
71330
71390
|
]);
|
|
71331
|
-
const exactSymbols =
|
|
71332
|
-
|
|
71333
|
-
|
|
71334
|
-
|
|
71335
|
-
|
|
71336
|
-
|
|
71337
|
-
|
|
71391
|
+
const exactSymbols = exactHits.map((h3) => {
|
|
71392
|
+
const sym = {
|
|
71393
|
+
id: h3.nodeId,
|
|
71394
|
+
name: h3.name,
|
|
71395
|
+
filePath: h3.filePath,
|
|
71396
|
+
score: 1
|
|
71397
|
+
// an exact-name hit is authoritative
|
|
71398
|
+
};
|
|
71399
|
+
if (h3.startLine > 0) sym.startLine = h3.startLine;
|
|
71400
|
+
if (h3.endLine > 0) sym.endLine = h3.endLine;
|
|
71401
|
+
return sym;
|
|
71402
|
+
});
|
|
71338
71403
|
const exactIds = new Set(exactSymbols.map((s5) => s5.id));
|
|
71339
71404
|
const ql = query.toLowerCase();
|
|
71340
71405
|
const semantic = semanticRes.filter((r) => !exactIds.has(r.nodeId)).map((r) => {
|
|
@@ -71356,78 +71421,54 @@ var SourceCodeProvider = class {
|
|
|
71356
71421
|
async hydrateLines(symbols) {
|
|
71357
71422
|
const ids2 = symbols.map((s5) => s5.id).filter(Boolean);
|
|
71358
71423
|
if (ids2.length === 0) return symbols;
|
|
71359
|
-
const
|
|
71360
|
-
const rows = await this.rows(`MATCH (n) WHERE n.id IN [${idList}] RETURN n.id, n.start_line, n.end_line`).catch(() => []);
|
|
71361
|
-
const lineById = /* @__PURE__ */ new Map();
|
|
71362
|
-
for (const row of rows) {
|
|
71363
|
-
const id = String(row[0] ?? "");
|
|
71364
|
-
const start2 = row[1] != null ? Number(row[1]) : void 0;
|
|
71365
|
-
const end = row[2] != null ? Number(row[2]) : void 0;
|
|
71366
|
-
lineById.set(id, { start: start2, end });
|
|
71367
|
-
}
|
|
71424
|
+
const lines = await this.degrade(this.client.nodesLines(ids2), {});
|
|
71368
71425
|
return symbols.map((s5) => {
|
|
71369
|
-
const l2 =
|
|
71426
|
+
const l2 = lines[s5.id];
|
|
71370
71427
|
if (l2 === void 0) return s5;
|
|
71371
71428
|
const out2 = { ...s5 };
|
|
71372
|
-
if (l2.
|
|
71373
|
-
if (l2.
|
|
71429
|
+
if (typeof l2.startLine === "number" && l2.startLine > 0) out2.startLine = l2.startLine;
|
|
71430
|
+
if (typeof l2.endLine === "number" && l2.endLine > 0) out2.endLine = l2.endLine;
|
|
71374
71431
|
return out2;
|
|
71375
71432
|
});
|
|
71376
71433
|
}
|
|
71377
71434
|
async context(symbolId) {
|
|
71378
|
-
const
|
|
71379
|
-
|
|
71380
|
-
|
|
71381
|
-
|
|
71382
|
-
|
|
71383
|
-
|
|
71384
|
-
|
|
71385
|
-
|
|
71386
|
-
|
|
71387
|
-
|
|
71388
|
-
|
|
71389
|
-
|
|
71390
|
-
|
|
71391
|
-
|
|
71392
|
-
]);
|
|
71393
|
-
const node = nodeRows[0];
|
|
71435
|
+
const detail = await this.degrade(this.client.node(symbolId), null);
|
|
71436
|
+
if (detail === null) {
|
|
71437
|
+
return {
|
|
71438
|
+
symbol: { id: symbolId, name: "", filePath: "" },
|
|
71439
|
+
callers: [],
|
|
71440
|
+
callees: [],
|
|
71441
|
+
imports: [],
|
|
71442
|
+
usesType: [],
|
|
71443
|
+
community: null,
|
|
71444
|
+
coupledWith: [],
|
|
71445
|
+
isDead: false
|
|
71446
|
+
};
|
|
71447
|
+
}
|
|
71448
|
+
const n4 = detail.node;
|
|
71394
71449
|
const symbol = {
|
|
71395
|
-
id:
|
|
71396
|
-
name:
|
|
71397
|
-
filePath:
|
|
71398
|
-
|
|
71399
|
-
|
|
71400
|
-
|
|
71401
|
-
|
|
71402
|
-
|
|
71403
|
-
|
|
71404
|
-
const
|
|
71405
|
-
const content = node?.[9];
|
|
71450
|
+
id: n4.id ?? symbolId,
|
|
71451
|
+
name: n4.name ?? "",
|
|
71452
|
+
filePath: n4.filePath ?? ""
|
|
71453
|
+
};
|
|
71454
|
+
if (typeof n4.startLine === "number" && n4.startLine > 0) symbol.startLine = n4.startLine;
|
|
71455
|
+
if (typeof n4.endLine === "number" && n4.endLine > 0) symbol.endLine = n4.endLine;
|
|
71456
|
+
if (n4.signature) symbol.signature = n4.signature;
|
|
71457
|
+
if (n4.className) symbol.className = n4.className;
|
|
71458
|
+
if (n4.language) symbol.language = n4.language;
|
|
71459
|
+
const content = n4.content;
|
|
71406
71460
|
const snippet = typeof content === "string" ? content.slice(0, 600) : void 0;
|
|
71407
71461
|
const sourceBody = typeof content === "string" ? content.slice(0, 2e4) : void 0;
|
|
71408
|
-
const callees =
|
|
71409
|
-
const callers =
|
|
71410
|
-
const usesType =
|
|
71411
|
-
const communityRow =
|
|
71412
|
-
const community = communityRow ? { id: String(communityRow
|
|
71413
|
-
|
|
71414
|
-
|
|
71415
|
-
|
|
71416
|
-
|
|
71417
|
-
|
|
71418
|
-
const FE = this.escapeId(String(fileId));
|
|
71419
|
-
const importsQuery = `MATCH (f)-[r:CodeRelation]->(g:File) WHERE f.id = "${FE}" AND r.rel_type = "imports" RETURN g.file_path`;
|
|
71420
|
-
const coupledQuery = `MATCH (f)-[r:CodeRelation]->(g:File) WHERE f.id = "${FE}" AND r.rel_type = "coupled_with" RETURN g.file_path, r.co_changes`;
|
|
71421
|
-
const [importRows, coupledRows] = await Promise.all([
|
|
71422
|
-
this.rows(importsQuery),
|
|
71423
|
-
this.rows(coupledQuery)
|
|
71424
|
-
]);
|
|
71425
|
-
imports = importRows.map((row) => String(row[0] ?? ""));
|
|
71426
|
-
coupledWith = coupledRows.map((row) => ({
|
|
71427
|
-
file: String(row[0] ?? ""),
|
|
71428
|
-
coChanges: Number(row[1] ?? 0)
|
|
71429
|
-
}));
|
|
71430
|
-
}
|
|
71462
|
+
const callees = (detail.callees ?? []).map((c4) => this.serializedToSymbol(c4.node));
|
|
71463
|
+
const callers = (detail.callers ?? []).map((c4) => this.serializedToSymbol(c4.node));
|
|
71464
|
+
const usesType = (detail.typeRefs ?? []).map((t2) => this.serializedToSymbol(t2));
|
|
71465
|
+
const communityRow = (detail.communities ?? [])[0];
|
|
71466
|
+
const community = communityRow ? { id: String(communityRow.id ?? ""), name: String(communityRow.name ?? "") } : null;
|
|
71467
|
+
const imports = (detail.imports ?? []).map((i3) => String(i3));
|
|
71468
|
+
const coupledWith = (detail.coupledWith ?? []).map((c4) => ({
|
|
71469
|
+
file: String(c4.file ?? ""),
|
|
71470
|
+
coChanges: Number(c4.coChanges ?? 0)
|
|
71471
|
+
}));
|
|
71431
71472
|
return {
|
|
71432
71473
|
symbol,
|
|
71433
71474
|
snippet,
|
|
@@ -71438,7 +71479,7 @@ var SourceCodeProvider = class {
|
|
|
71438
71479
|
usesType,
|
|
71439
71480
|
community,
|
|
71440
71481
|
coupledWith,
|
|
71441
|
-
isDead
|
|
71482
|
+
isDead: Boolean(n4.isDead)
|
|
71442
71483
|
};
|
|
71443
71484
|
}
|
|
71444
71485
|
async impact(symbolId, depth = 3) {
|
|
@@ -71453,21 +71494,22 @@ var SourceCodeProvider = class {
|
|
|
71453
71494
|
};
|
|
71454
71495
|
}
|
|
71455
71496
|
async flowsFor(symbolId) {
|
|
71456
|
-
const
|
|
71457
|
-
const
|
|
71458
|
-
|
|
71459
|
-
|
|
71460
|
-
|
|
71461
|
-
|
|
71462
|
-
|
|
71463
|
-
|
|
71464
|
-
|
|
71465
|
-
|
|
71466
|
-
|
|
71467
|
-
|
|
71468
|
-
|
|
71469
|
-
|
|
71470
|
-
|
|
71497
|
+
const res = await this.degrade(this.client.flows(symbolId), { processes: [], steps: [] });
|
|
71498
|
+
const steps = (res.steps ?? []).map((s5) => {
|
|
71499
|
+
const sym = { id: s5.nodeId, name: s5.name, filePath: s5.filePath };
|
|
71500
|
+
if (typeof s5.startLine === "number" && s5.startLine > 0) sym.startLine = s5.startLine;
|
|
71501
|
+
return sym;
|
|
71502
|
+
});
|
|
71503
|
+
return (res.processes ?? []).map((p5) => ({
|
|
71504
|
+
id: String(p5.id ?? ""),
|
|
71505
|
+
name: String(p5.name ?? ""),
|
|
71506
|
+
steps
|
|
71507
|
+
}));
|
|
71508
|
+
}
|
|
71509
|
+
/** Method symbols of a class (typed /api/class-methods). Used for class-seed walkthroughs. */
|
|
71510
|
+
async classMethods(file, className) {
|
|
71511
|
+
const methods = await this.degrade(this.client.classMethods(file, className), []);
|
|
71512
|
+
return methods.map((m5) => this.serializedToSymbol(m5)).filter((s5) => s5.id !== "" && s5.name !== "");
|
|
71471
71513
|
}
|
|
71472
71514
|
async detectChanges(diff) {
|
|
71473
71515
|
const d3 = await this.client.diff(diff.base, diff.compare);
|
|
@@ -94577,20 +94619,10 @@ function isClassSymbol(sym) {
|
|
|
94577
94619
|
return sym !== void 0 && sym.id.split(":", 1)[0]?.toLowerCase() === "class";
|
|
94578
94620
|
}
|
|
94579
94621
|
async function fetchClassMethods(code, cls) {
|
|
94580
|
-
const
|
|
94581
|
-
|
|
94582
|
-
if (!name2) return [];
|
|
94583
|
-
const file = safe(cls.filePath);
|
|
94622
|
+
const name2 = cls.name?.trim();
|
|
94623
|
+
if (!name2 || typeof code.classMethods !== "function") return [];
|
|
94584
94624
|
try {
|
|
94585
|
-
|
|
94586
|
-
`MATCH (m:Method) WHERE m.class_name = "${name2}"` + (file ? ` AND m.file_path = "${file}"` : "") + " RETURN m.id, m.name, m.file_path, m.start_line ORDER BY m.start_line LIMIT 40"
|
|
94587
|
-
);
|
|
94588
|
-
return res.rows.map((r) => ({
|
|
94589
|
-
id: String(r[0] ?? ""),
|
|
94590
|
-
name: String(r[1] ?? ""),
|
|
94591
|
-
filePath: String(r[2] ?? ""),
|
|
94592
|
-
...typeof r[3] === "number" ? { startLine: r[3] } : {}
|
|
94593
|
-
})).filter((s5) => s5.id !== "" && s5.name !== "");
|
|
94625
|
+
return await code.classMethods(cls.filePath ?? "", name2);
|
|
94594
94626
|
} catch {
|
|
94595
94627
|
return [];
|
|
94596
94628
|
}
|
|
@@ -101491,31 +101523,50 @@ function dedupeBySymbolFile(rows) {
|
|
|
101491
101523
|
// ../../packages/stitcher/src/stitch.ts
|
|
101492
101524
|
init_cjs_shims();
|
|
101493
101525
|
async function stitch(client, db, opts = {}) {
|
|
101494
|
-
const producerRows =
|
|
101526
|
+
const producerRows = await client.contentSearch([
|
|
101495
101527
|
// "new Queue" (no paren) so generic-typed `new Queue<T>(...)` also matches.
|
|
101496
|
-
|
|
101497
|
-
|
|
101528
|
+
"@InjectQueue(",
|
|
101529
|
+
"new Queue",
|
|
101530
|
+
"QUEUE_NAME",
|
|
101531
|
+
"QueueName",
|
|
101532
|
+
"enum ",
|
|
101533
|
+
"Object.values(",
|
|
101534
|
+
// HOR-341: dispatch tables `[Enum.MEMBER]: () => this.ctrl.handler()` map runtime
|
|
101535
|
+
// queues to owning code, and often live in a file with NO new Queue/Worker of their
|
|
101536
|
+
// own (e.g. a scheduler controller) — so pull computed-key arrow entries too.
|
|
101537
|
+
"]: ("
|
|
101538
|
+
]);
|
|
101498
101539
|
const producerClasses = producerRows.map((r) => ({
|
|
101499
|
-
name:
|
|
101500
|
-
filePath:
|
|
101501
|
-
content:
|
|
101540
|
+
name: r.name,
|
|
101541
|
+
filePath: r.filePath,
|
|
101542
|
+
content: r.content
|
|
101502
101543
|
}));
|
|
101503
|
-
const workerRows =
|
|
101504
|
-
// "new Worker" (no paren) so generic-typed `new Worker<T>(...)` also matches.
|
|
101505
|
-
'MATCH (n) WHERE n.content CONTAINS "@Processor(" OR n.content CONTAINS "new Worker" RETURN n.name, n.file_path, n.content'
|
|
101506
|
-
)).rows;
|
|
101544
|
+
const workerRows = await client.contentSearch(["@Processor(", "new Worker"]);
|
|
101507
101545
|
const workerFiles = workerRows.map((r) => ({
|
|
101508
|
-
filePath:
|
|
101509
|
-
content:
|
|
101546
|
+
filePath: r.filePath,
|
|
101547
|
+
content: r.content
|
|
101510
101548
|
}));
|
|
101511
101549
|
const graph = extractQueueGraph({ producerClasses, workerFiles });
|
|
101512
|
-
const celeryRows =
|
|
101513
|
-
|
|
101514
|
-
|
|
101550
|
+
const celeryRows = await client.contentSearch([
|
|
101551
|
+
".delay(",
|
|
101552
|
+
".apply_async(",
|
|
101553
|
+
".schedule(",
|
|
101554
|
+
".defer(",
|
|
101555
|
+
".defer_async(",
|
|
101556
|
+
".send(",
|
|
101557
|
+
".enqueue_job(",
|
|
101558
|
+
".enqueue(",
|
|
101559
|
+
"@shared_task",
|
|
101560
|
+
"@task",
|
|
101561
|
+
".task",
|
|
101562
|
+
"@actor",
|
|
101563
|
+
"periodic_task",
|
|
101564
|
+
"db_task"
|
|
101565
|
+
]);
|
|
101515
101566
|
const celeryNodes = celeryRows.map((r) => ({
|
|
101516
|
-
name:
|
|
101517
|
-
filePath:
|
|
101518
|
-
content:
|
|
101567
|
+
name: r.name,
|
|
101568
|
+
filePath: r.filePath,
|
|
101569
|
+
content: r.content
|
|
101519
101570
|
}));
|
|
101520
101571
|
const celery = extractCeleryQueueGraph(celeryNodes);
|
|
101521
101572
|
const dramatiq = extractDramatiqQueueGraph(celeryNodes);
|
|
@@ -101667,6 +101718,10 @@ async function stitchQueueMap(hostUrl, dbUrl, label) {
|
|
|
101667
101718
|
console.log(
|
|
101668
101719
|
import_picocolors6.default.dim(`[${label}] `) + `Stitched ${summary.edges} queue edge(s) across ${summary.queues} queue(s) \u2014 ${summary.producers} producer(s), ${summary.workers} worker(s).`
|
|
101669
101720
|
);
|
|
101721
|
+
} catch (err2) {
|
|
101722
|
+
console.log(
|
|
101723
|
+
import_picocolors6.default.dim(`[${label}] `) + `Queue map skipped (degraded): ${err2.message}. Stitched 0 queue edge(s).`
|
|
101724
|
+
);
|
|
101670
101725
|
} finally {
|
|
101671
101726
|
await sql2.end();
|
|
101672
101727
|
}
|
|
@@ -101692,60 +101747,49 @@ function findConfiguredRepo(config2, root) {
|
|
|
101692
101747
|
return null;
|
|
101693
101748
|
}
|
|
101694
101749
|
var SOURCE_GRAPH_PULL_LIMIT = 5e3;
|
|
101750
|
+
var SOURCE_SYMBOL_LABELS = [
|
|
101751
|
+
"class",
|
|
101752
|
+
"interface",
|
|
101753
|
+
"type_alias",
|
|
101754
|
+
"function",
|
|
101755
|
+
"method",
|
|
101756
|
+
"enum"
|
|
101757
|
+
];
|
|
101695
101758
|
async function extractSourceGraph(hostUrl, repo) {
|
|
101696
101759
|
const client = new SourceHttpClient({ baseUrl: hostUrl });
|
|
101697
101760
|
const N2 = SOURCE_GRAPH_PULL_LIMIT;
|
|
101698
|
-
const
|
|
101699
|
-
try {
|
|
101700
|
-
return (await client.cypher(query)).rows;
|
|
101701
|
-
} catch {
|
|
101702
|
-
return [];
|
|
101703
|
-
}
|
|
101704
|
-
};
|
|
101705
|
-
const symbolRows = await rows(
|
|
101706
|
-
`MATCH (n) WHERE label(n) IN ["Class","Interface","TypeAlias","Function","Method","Enum"] RETURN label(n), n.name, n.file_path, n.start_line, n.end_line, n.class_name, n.is_entry_point, n.is_exported, n.signature LIMIT ${N2 * 2}`
|
|
101707
|
-
);
|
|
101761
|
+
const symbolRows = await client.symbolsByLabel(SOURCE_SYMBOL_LABELS, N2 * 2).catch(() => []);
|
|
101708
101762
|
const symbols = symbolRows.map((r) => ({
|
|
101709
|
-
label:
|
|
101710
|
-
name:
|
|
101711
|
-
filePath: r
|
|
101712
|
-
startLine: r
|
|
101713
|
-
endLine: r
|
|
101714
|
-
className: r
|
|
101715
|
-
isEntryPoint: Boolean(r
|
|
101716
|
-
isExported: Boolean(r
|
|
101717
|
-
signature: r
|
|
101763
|
+
label: r.label,
|
|
101764
|
+
name: r.name,
|
|
101765
|
+
filePath: r.filePath || void 0,
|
|
101766
|
+
startLine: r.startLine > 0 ? r.startLine : void 0,
|
|
101767
|
+
endLine: r.endLine > 0 ? r.endLine : void 0,
|
|
101768
|
+
className: r.className || void 0,
|
|
101769
|
+
isEntryPoint: Boolean(r.isEntryPoint),
|
|
101770
|
+
isExported: Boolean(r.isExported),
|
|
101771
|
+
signature: r.signature || void 0
|
|
101718
101772
|
}));
|
|
101719
|
-
const
|
|
101720
|
-
|
|
101721
|
-
|
|
101722
|
-
|
|
101723
|
-
|
|
101724
|
-
|
|
101773
|
+
const nameById = /* @__PURE__ */ new Map();
|
|
101774
|
+
for (const r of symbolRows) {
|
|
101775
|
+
if (r.id) nameById.set(r.id, { name: r.name, filePath: r.filePath || void 0 });
|
|
101776
|
+
}
|
|
101777
|
+
const communityRows = await client.communities().catch(() => []);
|
|
101778
|
+
const communities = communityRows.map((c4) => ({
|
|
101779
|
+
id: c4.id ? String(c4.id) : void 0,
|
|
101780
|
+
name: String(c4.name ?? "")
|
|
101781
|
+
}));
|
|
101782
|
+
const processRows = await client.processes().catch(() => []);
|
|
101783
|
+
const processes = processRows.map((p5) => ({
|
|
101784
|
+
name: String(p5.name ?? ""),
|
|
101785
|
+
steps: (p5.steps ?? []).map((s5) => {
|
|
101786
|
+
const resolved = nameById.get(s5.nodeId);
|
|
101787
|
+
const component = resolved?.name ?? s5.nodeId;
|
|
101788
|
+
if (!component) return null;
|
|
101789
|
+
const detail = resolved?.filePath;
|
|
101790
|
+
return { component, ...detail ? { detail } : {} };
|
|
101791
|
+
}).filter((s5) => s5 !== null)
|
|
101725
101792
|
}));
|
|
101726
|
-
const processRows = await rows(`MATCH (n:Process) RETURN n.id, n.name LIMIT ${N2}`);
|
|
101727
|
-
const stepRows = await rows(
|
|
101728
|
-
`MATCH (s)-[r:CodeRelation]->(p:Process) WHERE r.rel_type = "step_in_process" RETURN p.id, s.name, s.file_path, r.step_number ORDER BY p.id, r.step_number LIMIT ${N2 * 4}`
|
|
101729
|
-
);
|
|
101730
|
-
const stepsByProcess = /* @__PURE__ */ new Map();
|
|
101731
|
-
for (const r of stepRows) {
|
|
101732
|
-
const pid = String(r[0] ?? "");
|
|
101733
|
-
if (!pid) continue;
|
|
101734
|
-
const component = String(r[1] ?? "");
|
|
101735
|
-
if (!component) continue;
|
|
101736
|
-
const file = r[2] != null ? String(r[2]) : void 0;
|
|
101737
|
-
const list = stepsByProcess.get(pid) ?? [];
|
|
101738
|
-
list.push({ component, ...file ? { detail: file } : {} });
|
|
101739
|
-
stepsByProcess.set(pid, list);
|
|
101740
|
-
}
|
|
101741
|
-
const processes = processRows.map((r) => {
|
|
101742
|
-
const id = r[0] != null ? String(r[0]) : void 0;
|
|
101743
|
-
return {
|
|
101744
|
-
id,
|
|
101745
|
-
name: String(r[1] ?? ""),
|
|
101746
|
-
steps: id ? stepsByProcess.get(id) ?? [] : []
|
|
101747
|
-
};
|
|
101748
|
-
});
|
|
101749
101793
|
if (symbols.length === 0 && communities.length === 0 && processes.length === 0) {
|
|
101750
101794
|
return null;
|
|
101751
101795
|
}
|
|
@@ -101919,7 +101963,13 @@ async function runIndex(opts) {
|
|
|
101919
101963
|
);
|
|
101920
101964
|
return 1;
|
|
101921
101965
|
}
|
|
101922
|
-
|
|
101966
|
+
try {
|
|
101967
|
+
await stitchQueueMap(hostUrl, dbUrl, label);
|
|
101968
|
+
} catch (err2) {
|
|
101969
|
+
console.log(
|
|
101970
|
+
import_picocolors6.default.dim(`[${label}] `) + `Queue map skipped (degraded): ${err2.message}. Stitched 0 queue edge(s).`
|
|
101971
|
+
);
|
|
101972
|
+
}
|
|
101923
101973
|
await buildKnowledgeIndex(root, config2, label, configuredProject, opts, hostUrl);
|
|
101924
101974
|
const hasLocalConfig = discoverLocalConfig(root) !== null;
|
|
101925
101975
|
if (!isConfigured && !hasLocalConfig) {
|