dsh-gitnexus-plus 0.1.3 → 0.1.5
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/README-CN.md +34 -10
- package/README.md +79 -14
- package/lib/bin/graph-helper.d.ts +21 -0
- package/lib/bin/graph-helper.d.ts.map +1 -0
- package/lib/bin/graph-helper.js +71 -0
- package/lib/bin/graph-helper.js.map +1 -0
- package/lib/client.js +4402 -2371
- package/lib/gitnexus/analyze.d.ts +54 -8
- package/lib/gitnexus/analyze.d.ts.map +1 -1
- package/lib/gitnexus/analyze.js +355 -84
- package/lib/gitnexus/analyze.js.map +1 -1
- package/lib/gitnexus/commands.d.ts +19 -2
- package/lib/gitnexus/commands.d.ts.map +1 -1
- package/lib/gitnexus/commands.js +42 -4
- package/lib/gitnexus/commands.js.map +1 -1
- package/lib/gitnexus/coverage.d.ts +8 -0
- package/lib/gitnexus/coverage.d.ts.map +1 -1
- package/lib/gitnexus/coverage.js +14 -2
- package/lib/gitnexus/coverage.js.map +1 -1
- package/lib/gitnexus/delta.d.ts +144 -0
- package/lib/gitnexus/delta.d.ts.map +1 -0
- package/lib/gitnexus/delta.js +347 -0
- package/lib/gitnexus/delta.js.map +1 -0
- package/lib/gitnexus/discover.d.ts +91 -0
- package/lib/gitnexus/discover.d.ts.map +1 -0
- package/lib/gitnexus/discover.js +214 -0
- package/lib/gitnexus/discover.js.map +1 -0
- package/lib/gitnexus/git-activity.d.ts +13 -21
- package/lib/gitnexus/git-activity.d.ts.map +1 -1
- package/lib/gitnexus/git-activity.js +60 -26
- package/lib/gitnexus/git-activity.js.map +1 -1
- package/lib/gitnexus/graph.d.ts +135 -0
- package/lib/gitnexus/graph.d.ts.map +1 -0
- package/lib/gitnexus/graph.js +474 -0
- package/lib/gitnexus/graph.js.map +1 -0
- package/lib/gitnexus/index-meta.d.ts +34 -6
- package/lib/gitnexus/index-meta.d.ts.map +1 -1
- package/lib/gitnexus/index-meta.js +47 -7
- package/lib/gitnexus/index-meta.js.map +1 -1
- package/lib/gitnexus/install.d.ts +11 -0
- package/lib/gitnexus/install.d.ts.map +1 -1
- package/lib/gitnexus/install.js +65 -9
- package/lib/gitnexus/install.js.map +1 -1
- package/lib/gitnexus/root.d.ts +0 -3
- package/lib/gitnexus/root.d.ts.map +1 -1
- package/lib/gitnexus/root.js +64 -5
- package/lib/gitnexus/root.js.map +1 -1
- package/lib/gitnexus/search.d.ts +34 -8
- package/lib/gitnexus/search.d.ts.map +1 -1
- package/lib/gitnexus/search.js +89 -13
- package/lib/gitnexus/search.js.map +1 -1
- package/lib/index.d.ts +13 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +189 -7
- package/lib/index.js.map +1 -1
- package/lib/routes/analyze.d.ts +8 -6
- package/lib/routes/analyze.d.ts.map +1 -1
- package/lib/routes/analyze.js +25 -6
- package/lib/routes/analyze.js.map +1 -1
- package/lib/routes/coverage.d.ts +16 -10
- package/lib/routes/coverage.d.ts.map +1 -1
- package/lib/routes/coverage.js +31 -22
- package/lib/routes/coverage.js.map +1 -1
- package/lib/routes/delta.d.ts +22 -0
- package/lib/routes/delta.d.ts.map +1 -0
- package/lib/routes/delta.js +50 -0
- package/lib/routes/delta.js.map +1 -0
- package/lib/routes/file-diff.d.ts +19 -0
- package/lib/routes/file-diff.d.ts.map +1 -0
- package/lib/routes/file-diff.js +69 -0
- package/lib/routes/file-diff.js.map +1 -0
- package/lib/routes/graph.d.ts +22 -0
- package/lib/routes/graph.d.ts.map +1 -0
- package/lib/routes/graph.js +71 -0
- package/lib/routes/graph.js.map +1 -0
- package/lib/routes/guard.d.ts +3 -6
- package/lib/routes/guard.d.ts.map +1 -1
- package/lib/routes/guard.js +3 -6
- package/lib/routes/guard.js.map +1 -1
- package/lib/routes/projects.d.ts +21 -0
- package/lib/routes/projects.d.ts.map +1 -0
- package/lib/routes/projects.js +61 -0
- package/lib/routes/projects.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded graph projection for the panel's graph view.
|
|
3
|
+
*
|
|
4
|
+
* WHY A PROJECTION AND NOT CYPHER OVER HTTP
|
|
5
|
+
* -----------------------------------------
|
|
6
|
+
* The graph itself is the product here: it answers "what is in this index" better than
|
|
7
|
+
* any table. But a view that accepts Cypher from a browser turns a read-only panel into
|
|
8
|
+
* an arbitrary query endpoint over the user's code graph — and the roadmap's rule is a
|
|
9
|
+
* whitelist of projections, never raw forwarding. So this module exposes exactly two
|
|
10
|
+
* shapes:
|
|
11
|
+
*
|
|
12
|
+
* `communities` every community as a node, with aggregated cross-community edges
|
|
13
|
+
* `community` one community's member symbols and the relations among them
|
|
14
|
+
*
|
|
15
|
+
* Both are bounded by a node budget and an edge budget, and both report what they left
|
|
16
|
+
* out. A 93k-node repository must not be able to freeze the panel, and a view that
|
|
17
|
+
* silently drops half the graph is worse than one that says it did.
|
|
18
|
+
*
|
|
19
|
+
* WHERE THIS RUNS
|
|
20
|
+
* ---------------
|
|
21
|
+
* `projectGraph` talks to LadybugDB, so it can only run on the system Node — the host
|
|
22
|
+
* is Electron's Node and cannot load the native addon (see `search.ts`). The host calls
|
|
23
|
+
* `projectViaHelper`, which starts `lib/bin/graph-helper.js` and parses one JSON object,
|
|
24
|
+
* exactly like the search path. Keeping the query code in the helper process also means
|
|
25
|
+
* a query that crashes the native engine takes down a short-lived child, not the panel.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, statSync } from 'node:fs';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
import { isProjectRoot, readIndexMeta } from './index-meta.js';
|
|
30
|
+
import { loadAdapter } from './search.js';
|
|
31
|
+
export const GRAPH_VIEWS = ['communities', 'community'];
|
|
32
|
+
export class GraphError extends Error {
|
|
33
|
+
code;
|
|
34
|
+
status;
|
|
35
|
+
constructor(code, status, message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'GraphError';
|
|
38
|
+
this.code = code;
|
|
39
|
+
this.status = status;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const DEFAULT_NODE_BUDGET = 220;
|
|
43
|
+
const DEFAULT_EDGE_BUDGET = 800;
|
|
44
|
+
/** The character set a node/community id is allowed to contain. */
|
|
45
|
+
const ID_PATTERN = /^[\p{L}\p{N}_.:@/#+\-~ ]{1,200}$/u;
|
|
46
|
+
/**
|
|
47
|
+
* Reduce a caller-supplied id to something safe inside a single-quoted Cypher literal.
|
|
48
|
+
*
|
|
49
|
+
* The id is interpolated rather than bound because LadybugDB's prepare path does not
|
|
50
|
+
* accept a parameter in every position this query needs (the same limitation `search.ts`
|
|
51
|
+
* documents for `CONTAINS`). Safety comes from **refusing** anything outside a known
|
|
52
|
+
* alphabet — quotes and backslashes cannot appear — rather than from escaping.
|
|
53
|
+
*
|
|
54
|
+
* @returns the id, or null when it is not acceptable
|
|
55
|
+
*/
|
|
56
|
+
export function sanitizeGraphId(raw) {
|
|
57
|
+
if (typeof raw !== 'string')
|
|
58
|
+
return null;
|
|
59
|
+
const trimmed = raw.trim();
|
|
60
|
+
if (trimmed === '')
|
|
61
|
+
return null;
|
|
62
|
+
return ID_PATTERN.test(trimmed) ? trimmed : null;
|
|
63
|
+
}
|
|
64
|
+
/** Whether a value names a projection this module implements. */
|
|
65
|
+
export function isGraphView(value) {
|
|
66
|
+
return value === 'communities' || value === 'community';
|
|
67
|
+
}
|
|
68
|
+
function clampBudget(value, fallback, max) {
|
|
69
|
+
if (value === undefined || !Number.isFinite(value))
|
|
70
|
+
return fallback;
|
|
71
|
+
return Math.max(1, Math.min(max, Math.floor(value)));
|
|
72
|
+
}
|
|
73
|
+
function toLine(value) {
|
|
74
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
75
|
+
return Math.trunc(value);
|
|
76
|
+
if (typeof value === 'bigint')
|
|
77
|
+
return Number(value);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function asString(value) {
|
|
81
|
+
return typeof value === 'string' ? value : '';
|
|
82
|
+
}
|
|
83
|
+
/** Rows from `executePrepared` are the driver's objects; narrow them field by field. */
|
|
84
|
+
function rowsOf(value) {
|
|
85
|
+
return Array.isArray(value)
|
|
86
|
+
? value.filter((row) => row !== null && typeof row === 'object')
|
|
87
|
+
: [];
|
|
88
|
+
}
|
|
89
|
+
/** One scalar `count(*)` result, defaulting to 0 when the shape is unexpected. */
|
|
90
|
+
function countOf(value) {
|
|
91
|
+
const [first] = rowsOf(value);
|
|
92
|
+
const total = first === undefined ? undefined : first.total ?? first.count ?? Object.values(first)[0];
|
|
93
|
+
return typeof total === 'number' && Number.isFinite(total) ? total : 0;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Read one projection.
|
|
97
|
+
*
|
|
98
|
+
* @param repoPath absolute repository root
|
|
99
|
+
*/
|
|
100
|
+
export async function projectGraph(repoPath, options = {}) {
|
|
101
|
+
const absRepo = path.resolve(repoPath);
|
|
102
|
+
const view = options.view ?? 'communities';
|
|
103
|
+
if (!isGraphView(view)) {
|
|
104
|
+
throw new GraphError('invalid-view', 400, `未知的图视图:${String(options.view)}`);
|
|
105
|
+
}
|
|
106
|
+
const nodeBudget = clampBudget(options.nodeBudget, DEFAULT_NODE_BUDGET, 1000);
|
|
107
|
+
const edgeBudget = clampBudget(options.edgeBudget, DEFAULT_EDGE_BUDGET, 4000);
|
|
108
|
+
if (!(await isProjectRoot(absRepo))) {
|
|
109
|
+
throw new GraphError('not-a-project', 404, '目录不是 GitNexus 索引,也不是 git 项目根。');
|
|
110
|
+
}
|
|
111
|
+
const meta = await readIndexMeta(absRepo);
|
|
112
|
+
if (meta === null) {
|
|
113
|
+
throw new GraphError('no-index', 409, '该项目还没有 GitNexus 索引;初始化后才能看图。');
|
|
114
|
+
}
|
|
115
|
+
const lbugPath = path.join(absRepo, '.gitnexus', 'lbug');
|
|
116
|
+
if (!existsSync(lbugPath)) {
|
|
117
|
+
throw new GraphError('no-index-store', 409, '索引缺少 .gitnexus/lbug 图存储,无法读图。');
|
|
118
|
+
}
|
|
119
|
+
let scope = null;
|
|
120
|
+
const notes = [];
|
|
121
|
+
if (view === 'community') {
|
|
122
|
+
scope = sanitizeGraphId(options.id);
|
|
123
|
+
if (scope === null) {
|
|
124
|
+
throw new GraphError('invalid-id', 400, 'community 视图需要合法的 id 参数。');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// One adapter load per projection, and one connection for both the page of rows and
|
|
128
|
+
// its total. Reopening the store per query would double the cost of every drill-down.
|
|
129
|
+
let adapter;
|
|
130
|
+
try {
|
|
131
|
+
adapter = await loadAdapter({ env: options.env, managedPrefix: options.managedPrefix });
|
|
132
|
+
}
|
|
133
|
+
catch (cause) {
|
|
134
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
135
|
+
throw new GraphError('graph-unavailable', 503, `图数据库适配器不可用:${message}`);
|
|
136
|
+
}
|
|
137
|
+
const runQuery = async (cypher) => {
|
|
138
|
+
try {
|
|
139
|
+
return await adapter.withLbugDb(lbugPath, async () => adapter.executePrepared(cypher, {}), {
|
|
140
|
+
readOnly: true,
|
|
141
|
+
skipFts: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (cause) {
|
|
145
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
146
|
+
if (/busy|locked|lock held|another process|timeout/i.test(message)) {
|
|
147
|
+
throw new GraphError('index-busy', 503, '索引正被另一个 GitNexus 操作占用,稍后重试。');
|
|
148
|
+
}
|
|
149
|
+
throw new GraphError('graph-failed', 500, `图查询失败:${message}`);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const nodes = [];
|
|
153
|
+
const edges = [];
|
|
154
|
+
let total = 0;
|
|
155
|
+
if (view === 'communities') {
|
|
156
|
+
const communityRows = rowsOf(await runQuery(`MATCH (c:Community)
|
|
157
|
+
RETURN c.id AS id, c.label AS label, c.symbolCount AS symbolCount
|
|
158
|
+
ORDER BY symbolCount DESC
|
|
159
|
+
LIMIT ${nodeBudget}`));
|
|
160
|
+
total = countOf(await runQuery('MATCH (c:Community) RETURN count(*) AS total'));
|
|
161
|
+
for (const row of communityRows) {
|
|
162
|
+
const id = asString(row.id);
|
|
163
|
+
if (id === '')
|
|
164
|
+
continue;
|
|
165
|
+
const label = asString(row.label);
|
|
166
|
+
nodes.push({
|
|
167
|
+
id,
|
|
168
|
+
kind: 'Community',
|
|
169
|
+
name: label === '' ? id : label,
|
|
170
|
+
filePath: null,
|
|
171
|
+
startLine: null,
|
|
172
|
+
symbolCount: typeof row.symbolCount === 'number' ? row.symbolCount : 0,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Cross-community edges are aggregated from the member-to-member relations: one
|
|
176
|
+
// edge per pair of communities per relation type. Two directions are collapsed
|
|
177
|
+
// (`ca.id < cb.id`) so the view does not draw every link twice.
|
|
178
|
+
const edgeRows = rowsOf(await runQuery(`MATCH (a)-[r:CodeRelation]->(b)
|
|
179
|
+
MATCH (a)-[:CodeRelation {type:'MEMBER_OF'}]->(ca:Community)
|
|
180
|
+
MATCH (b)-[:CodeRelation {type:'MEMBER_OF'}]->(cb:Community)
|
|
181
|
+
WHERE ca.id < cb.id
|
|
182
|
+
RETURN ca.id AS source, cb.id AS target, r.type AS type, count(*) AS weight
|
|
183
|
+
ORDER BY weight DESC
|
|
184
|
+
LIMIT ${edgeBudget}`));
|
|
185
|
+
for (const row of edgeRows) {
|
|
186
|
+
const source = asString(row.source);
|
|
187
|
+
const target = asString(row.target);
|
|
188
|
+
if (source === '' || target === '')
|
|
189
|
+
continue;
|
|
190
|
+
edges.push({
|
|
191
|
+
source,
|
|
192
|
+
target,
|
|
193
|
+
type: asString(row.type),
|
|
194
|
+
weight: typeof row.weight === 'number' ? row.weight : 1,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const scopeLiteral = scope;
|
|
200
|
+
const memberRows = rowsOf(await runQuery(`MATCH (s)-[:CodeRelation {type:'MEMBER_OF'}]->(c:Community {id:'${scopeLiteral}'})
|
|
201
|
+
RETURN s.id AS id, label(s) AS kind, s.name AS name,
|
|
202
|
+
s.filePath AS filePath, s.startLine AS startLine
|
|
203
|
+
ORDER BY name
|
|
204
|
+
LIMIT ${nodeBudget}`));
|
|
205
|
+
total = countOf(await runQuery(`MATCH (s)-[:CodeRelation {type:'MEMBER_OF'}]->(c:Community {id:'${scopeLiteral}'})
|
|
206
|
+
RETURN count(*) AS total`));
|
|
207
|
+
for (const row of memberRows) {
|
|
208
|
+
const id = asString(row.id);
|
|
209
|
+
if (id === '')
|
|
210
|
+
continue;
|
|
211
|
+
const { name, filePath, startLine, kind } = row;
|
|
212
|
+
nodes.push({
|
|
213
|
+
id,
|
|
214
|
+
kind: asString(kind),
|
|
215
|
+
name: asString(name) === '' ? id : asString(name),
|
|
216
|
+
filePath: typeof filePath === 'string' && filePath !== '' ? filePath : null,
|
|
217
|
+
startLine: toLine(startLine),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const edgeRows = rowsOf(await runQuery(`MATCH (a)-[r:CodeRelation]->(b)
|
|
221
|
+
MATCH (a)-[:CodeRelation {type:'MEMBER_OF'}]->(c:Community {id:'${scopeLiteral}'})
|
|
222
|
+
MATCH (b)-[:CodeRelation {type:'MEMBER_OF'}]->(c)
|
|
223
|
+
RETURN a.id AS source, b.id AS target, r.type AS type, count(*) AS weight
|
|
224
|
+
ORDER BY weight DESC
|
|
225
|
+
LIMIT ${edgeBudget}`));
|
|
226
|
+
for (const row of edgeRows) {
|
|
227
|
+
const source = asString(row.source);
|
|
228
|
+
const target = asString(row.target);
|
|
229
|
+
if (source === '' || target === '')
|
|
230
|
+
continue;
|
|
231
|
+
edges.push({
|
|
232
|
+
source,
|
|
233
|
+
target,
|
|
234
|
+
type: asString(row.type),
|
|
235
|
+
weight: typeof row.weight === 'number' ? row.weight : 1,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
if (total === 0 && nodes.length === 0) {
|
|
239
|
+
notes.push('该社区没有成员符号;索引可能是旧版本,或社区已被重新划分。');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// An edge whose endpoint fell outside the node budget cannot be drawn. Dropping it
|
|
243
|
+
// here keeps the client from having to special-case dangling ids, and the count is
|
|
244
|
+
// reported rather than silently swallowed.
|
|
245
|
+
const present = new Set(nodes.map((node) => node.id));
|
|
246
|
+
const kept = edges.filter((edge) => present.has(edge.source) && present.has(edge.target));
|
|
247
|
+
const droppedEdges = edges.length - kept.length;
|
|
248
|
+
if (droppedEdges > 0) {
|
|
249
|
+
notes.push(`${droppedEdges} 条边因端点超出节点预算未显示`);
|
|
250
|
+
}
|
|
251
|
+
const truncated = total > nodes.length || edges.length >= edgeBudget;
|
|
252
|
+
if (total > nodes.length) {
|
|
253
|
+
notes.push(view === 'communities'
|
|
254
|
+
? `共 ${total} 个社区,显示符号数最多的 ${nodes.length} 个`
|
|
255
|
+
: `该社区共 ${total} 个符号,显示前 ${nodes.length} 个`);
|
|
256
|
+
}
|
|
257
|
+
if (edges.length >= edgeBudget) {
|
|
258
|
+
notes.push(`边达到上限 ${edgeBudget},只保留了权重最高的部分`);
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
view,
|
|
262
|
+
scope,
|
|
263
|
+
nodes,
|
|
264
|
+
edges: kept,
|
|
265
|
+
total,
|
|
266
|
+
truncated,
|
|
267
|
+
budget: { nodes: nodeBudget, edges: edgeBudget },
|
|
268
|
+
notes,
|
|
269
|
+
readAt: new Date().toISOString(),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function quoteFor(value) {
|
|
273
|
+
return process.platform === 'win32'
|
|
274
|
+
? `'${value.replace(/'/g, "''")}'`
|
|
275
|
+
: `'${value.replace(/'/g, "'\\''")}'`;
|
|
276
|
+
}
|
|
277
|
+
function parseHelperOutput(text) {
|
|
278
|
+
const start = text.indexOf('{');
|
|
279
|
+
const end = text.lastIndexOf('}');
|
|
280
|
+
if (start < 0 || end <= start)
|
|
281
|
+
return null;
|
|
282
|
+
try {
|
|
283
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
284
|
+
return parsed !== null && typeof parsed === 'object' ? parsed : null;
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function graphErrorCode(value) {
|
|
291
|
+
switch (value) {
|
|
292
|
+
case 'invalid-view':
|
|
293
|
+
case 'invalid-id':
|
|
294
|
+
case 'not-a-project':
|
|
295
|
+
case 'no-index':
|
|
296
|
+
case 'no-index-store':
|
|
297
|
+
case 'graph-unavailable':
|
|
298
|
+
case 'index-busy':
|
|
299
|
+
case 'graph-failed':
|
|
300
|
+
return value;
|
|
301
|
+
default:
|
|
302
|
+
return 'graph-failed';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function asProjection(value) {
|
|
306
|
+
if (value === null || typeof value !== 'object')
|
|
307
|
+
return null;
|
|
308
|
+
const raw = value;
|
|
309
|
+
if (!Array.isArray(raw.nodes) || !Array.isArray(raw.edges))
|
|
310
|
+
return null;
|
|
311
|
+
if (!isGraphView(raw.view))
|
|
312
|
+
return null;
|
|
313
|
+
const nodes = [];
|
|
314
|
+
for (const item of raw.nodes) {
|
|
315
|
+
if (item === null || typeof item !== 'object')
|
|
316
|
+
continue;
|
|
317
|
+
const row = item;
|
|
318
|
+
if (typeof row.id !== 'string' || row.id === '')
|
|
319
|
+
continue;
|
|
320
|
+
nodes.push({
|
|
321
|
+
id: row.id,
|
|
322
|
+
kind: typeof row.kind === 'string' ? row.kind : 'Unknown',
|
|
323
|
+
name: typeof row.name === 'string' && row.name !== '' ? row.name : row.id,
|
|
324
|
+
filePath: typeof row.filePath === 'string' && row.filePath !== '' ? row.filePath : null,
|
|
325
|
+
startLine: toLine(row.startLine),
|
|
326
|
+
...(typeof row.symbolCount === 'number' ? { symbolCount: row.symbolCount } : {}),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const edges = [];
|
|
330
|
+
for (const item of raw.edges) {
|
|
331
|
+
if (item === null || typeof item !== 'object')
|
|
332
|
+
continue;
|
|
333
|
+
const row = item;
|
|
334
|
+
if (typeof row.source !== 'string' || typeof row.target !== 'string')
|
|
335
|
+
continue;
|
|
336
|
+
edges.push({
|
|
337
|
+
source: row.source,
|
|
338
|
+
target: row.target,
|
|
339
|
+
type: typeof row.type === 'string' ? row.type : 'Related',
|
|
340
|
+
weight: typeof row.weight === 'number' && Number.isFinite(row.weight) ? row.weight : 1,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const budget = (raw.budget ?? {});
|
|
344
|
+
return {
|
|
345
|
+
view: raw.view,
|
|
346
|
+
scope: typeof raw.scope === 'string' ? raw.scope : null,
|
|
347
|
+
nodes,
|
|
348
|
+
edges,
|
|
349
|
+
total: typeof raw.total === 'number' ? raw.total : nodes.length,
|
|
350
|
+
truncated: raw.truncated === true,
|
|
351
|
+
budget: {
|
|
352
|
+
nodes: typeof budget.nodes === 'number' ? budget.nodes : nodes.length,
|
|
353
|
+
edges: typeof budget.edges === 'number' ? budget.edges : edges.length,
|
|
354
|
+
},
|
|
355
|
+
notes: Array.isArray(raw.notes) ? raw.notes.filter((note) => typeof note === 'string') : [],
|
|
356
|
+
readAt: typeof raw.readAt === 'string' ? raw.readAt : new Date().toISOString(),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Read a projection in a child Node process.
|
|
361
|
+
*
|
|
362
|
+
* Same execution boundary as `searchViaHelper`, for the same measured reason: the host
|
|
363
|
+
* cannot load `lbugjs.node`. A failure to start the child is reported as
|
|
364
|
+
* `graph-unavailable`, and this plugin never widens the sandbox to make it work.
|
|
365
|
+
*/
|
|
366
|
+
export async function projectViaHelper(repoPath, view, id, options) {
|
|
367
|
+
const absRepo = path.resolve(repoPath);
|
|
368
|
+
if (!(await isProjectRoot(absRepo))) {
|
|
369
|
+
throw new GraphError('not-a-project', 404, '目录不是 GitNexus 索引,也不是 git 项目根。');
|
|
370
|
+
}
|
|
371
|
+
const meta = await readIndexMeta(absRepo);
|
|
372
|
+
if (meta === null) {
|
|
373
|
+
throw new GraphError('no-index', 409, '该项目还没有 GitNexus 索引;初始化后才能看图。');
|
|
374
|
+
}
|
|
375
|
+
if (!existsSync(path.join(absRepo, '.gitnexus', 'lbug'))) {
|
|
376
|
+
throw new GraphError('no-index-store', 409, '索引缺少 .gitnexus/lbug 图存储,无法读图。');
|
|
377
|
+
}
|
|
378
|
+
if (options.shell === null) {
|
|
379
|
+
throw new GraphError('graph-unavailable', 503, '当前部署没有 shell 执行器,无法启动图投影子进程;读图需要系统 Node 加载 GitNexus 原生模块。');
|
|
380
|
+
}
|
|
381
|
+
const budgetNodes = clampBudget(options.nodeBudget, DEFAULT_NODE_BUDGET, 1000);
|
|
382
|
+
const budgetEdges = clampBudget(options.edgeBudget, DEFAULT_EDGE_BUDGET, 4000);
|
|
383
|
+
// A projection is a pure function of the index, the view, the id and the budgets, and
|
|
384
|
+
// producing it costs a Node cold start plus the native open. Switching communities and
|
|
385
|
+
// pressing re-read are common; the index mtime is part of the key, so a refresh
|
|
386
|
+
// invalidates without any cross-module wiring.
|
|
387
|
+
const cacheKey = projectionCacheKey(absRepo, view, id, budgetNodes, budgetEdges);
|
|
388
|
+
const cached = cacheKey === null ? undefined : projectionCache.get(cacheKey);
|
|
389
|
+
if (cached !== undefined && Date.now() - cached.at < PROJECTION_CACHE_TTL_MS) {
|
|
390
|
+
return cached.value;
|
|
391
|
+
}
|
|
392
|
+
const args = [
|
|
393
|
+
options.helperPath,
|
|
394
|
+
absRepo,
|
|
395
|
+
view,
|
|
396
|
+
id ?? '',
|
|
397
|
+
options.managedPrefix ?? '',
|
|
398
|
+
String(budgetNodes),
|
|
399
|
+
String(budgetEdges),
|
|
400
|
+
]
|
|
401
|
+
.map(quoteFor)
|
|
402
|
+
.join(' ');
|
|
403
|
+
const command = `node ${args}`;
|
|
404
|
+
const sandboxPolicy = options.sandboxPolicy?.(path.join(absRepo, '.gitnexus'));
|
|
405
|
+
let result;
|
|
406
|
+
try {
|
|
407
|
+
const spec = options.shell.resolve({
|
|
408
|
+
command,
|
|
409
|
+
workdir: absRepo,
|
|
410
|
+
timeoutMs: 25_000,
|
|
411
|
+
onExpiry: 'kill',
|
|
412
|
+
stdoutMaxBytes: 4 * 1024 * 1024,
|
|
413
|
+
...(sandboxPolicy === undefined ? {} : { sandboxPolicy }),
|
|
414
|
+
});
|
|
415
|
+
const execution = await options.shell.execute(spec);
|
|
416
|
+
result = await execution.result();
|
|
417
|
+
}
|
|
418
|
+
catch (cause) {
|
|
419
|
+
throw new GraphError('graph-unavailable', 503, `无法启动图投影子进程:${cause instanceof Error ? cause.message : String(cause)}`);
|
|
420
|
+
}
|
|
421
|
+
if (result.timedOut === true) {
|
|
422
|
+
throw new GraphError('graph-failed', 504, '图投影子进程超时(25 秒)。');
|
|
423
|
+
}
|
|
424
|
+
const stdout = typeof result.stdout?.text === 'string' ? result.stdout.text : '';
|
|
425
|
+
const stderr = typeof result.stderr?.text === 'string' ? result.stderr.text : '';
|
|
426
|
+
const parsed = parseHelperOutput(stdout);
|
|
427
|
+
if (parsed === null) {
|
|
428
|
+
const tail = stderr.trim().slice(-600);
|
|
429
|
+
throw new GraphError('graph-failed', 500, `图投影子进程没有返回可解析结果${result.exitCode === undefined ? '' : ` (exit ${String(result.exitCode)})`}` + (tail === '' ? '' : `:${tail}`));
|
|
430
|
+
}
|
|
431
|
+
if (parsed.ok !== true) {
|
|
432
|
+
const status = typeof parsed.status === 'number' ? parsed.status : 500;
|
|
433
|
+
const message = typeof parsed.message === 'string' ? parsed.message : '读取图失败。';
|
|
434
|
+
throw new GraphError(graphErrorCode(parsed.code), status, message);
|
|
435
|
+
}
|
|
436
|
+
const projection = asProjection(parsed.projection);
|
|
437
|
+
if (projection === null) {
|
|
438
|
+
throw new GraphError('graph-failed', 500, '图投影子进程返回了无法识别的结果形状。');
|
|
439
|
+
}
|
|
440
|
+
if (cacheKey !== null)
|
|
441
|
+
rememberProjection(cacheKey, projection);
|
|
442
|
+
return projection;
|
|
443
|
+
}
|
|
444
|
+
/** How many projections to keep; each is bounded by the budgets, so this is small. */
|
|
445
|
+
const PROJECTION_CACHE_MAX = 8;
|
|
446
|
+
const PROJECTION_CACHE_TTL_MS = 60_000;
|
|
447
|
+
const projectionCache = new Map();
|
|
448
|
+
function rememberProjection(key, value) {
|
|
449
|
+
projectionCache.delete(key);
|
|
450
|
+
projectionCache.set(key, { at: Date.now(), value });
|
|
451
|
+
while (projectionCache.size > PROJECTION_CACHE_MAX) {
|
|
452
|
+
const oldest = projectionCache.keys().next().value;
|
|
453
|
+
if (oldest === undefined)
|
|
454
|
+
break;
|
|
455
|
+
projectionCache.delete(oldest);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Cache key for one projection, or `null` when the index cannot be statted.
|
|
460
|
+
*
|
|
461
|
+
* The index file's mtime is in the key: a refresh rewrites it, so a cached projection
|
|
462
|
+
* from before the refresh can never be served after it.
|
|
463
|
+
*/
|
|
464
|
+
function projectionCacheKey(repoPath, view, id, nodes, edges) {
|
|
465
|
+
try {
|
|
466
|
+
const metaFile = path.join(repoPath, '.gitnexus', 'gitnexus.json');
|
|
467
|
+
const mtime = statSync(metaFile).mtimeMs;
|
|
468
|
+
return [repoPath, view, id ?? '', nodes, edges, mtime].join('\u0000');
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/gitnexus/graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -61,21 +61,49 @@ export declare function findRegistryEntry(target: string, options?: RegistryOpti
|
|
|
61
61
|
* registry at all — and a plugin that refused to describe the user's own working
|
|
62
62
|
* directory because a global file elsewhere had not heard of it would be wrong.
|
|
63
63
|
* This is not by itself the route bound any more: a git project with no index still
|
|
64
|
-
* has a readable working tree, so the routes
|
|
65
|
-
* accepts either an index or a `.git` marker
|
|
64
|
+
* has a readable working tree, so the routes that need a graph use
|
|
65
|
+
* {@link isProjectRoot}, which accepts either an index or a `.git` marker, and the
|
|
66
|
+
* scan/delta routes use {@link isExistingDirectory}, which also accepts a plain
|
|
67
|
+
* folder that GitNexus could index with `--skip-git`.
|
|
66
68
|
*/
|
|
67
69
|
export declare function hasIndex(repoPath: string): Promise<boolean>;
|
|
68
70
|
/**
|
|
69
|
-
* Whether a directory
|
|
71
|
+
* Whether a directory carries a `.git` marker.
|
|
72
|
+
*
|
|
73
|
+
* `.git` may be a directory or a file (worktrees and submodules), so this checks
|
|
74
|
+
* existence rather than type. The distinction matters to the maintenance commands:
|
|
75
|
+
* GitNexus refuses to analyze a folder without a `.git` unless `--skip-git` is
|
|
76
|
+
* passed, and it reports the resulting index as having no commit baseline.
|
|
77
|
+
*/
|
|
78
|
+
export declare function hasGitMarker(repoPath: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Whether a directory is a project the read routes may describe.
|
|
70
81
|
*
|
|
71
82
|
* Deliberately wider than {@link hasIndex}: a git repository with no index still has
|
|
72
83
|
* a readable working tree, and both the scan-only coverage report and the initialize
|
|
73
84
|
* action are for exactly that state. A directory with neither an index nor a `.git`
|
|
74
|
-
* marker is refused
|
|
75
|
-
*
|
|
76
|
-
*
|
|
85
|
+
* marker is refused by the routes that need git or a graph (search, graph, file
|
|
86
|
+
* history); the coverage and delta routes use {@link isExistingDirectory} instead,
|
|
87
|
+
* because GitNexus can index a plain folder with `--skip-git`.
|
|
77
88
|
*/
|
|
78
89
|
export declare function isProjectRoot(repoPath: string): Promise<boolean>;
|
|
90
|
+
/**
|
|
91
|
+
* Whether a path is an existing directory.
|
|
92
|
+
*
|
|
93
|
+
* This is the bound for the scan-only coverage report and the delta gate. The panel
|
|
94
|
+
* reports the working directory the operator is *in*, and that directory need not be
|
|
95
|
+
* a git repository: GitNexus indexes any folder with `--skip-git`, and the scan and
|
|
96
|
+
* the language breakdown need neither git nor an index. Refusing such a directory
|
|
97
|
+
* left the panel with a raw 404 and no numbers at all — the reported bug.
|
|
98
|
+
*
|
|
99
|
+
* This is still not a directory listing: the route describes exactly the path it is
|
|
100
|
+
* given and offers no way to browse a parent or a sibling. Filesystem roots are
|
|
101
|
+
* excluded by {@link isFilesystemRoot}, because scanning a whole drive is not a
|
|
102
|
+
* project report.
|
|
103
|
+
*/
|
|
104
|
+
export declare function isExistingDirectory(candidate: string): Promise<boolean>;
|
|
105
|
+
/** Whether a path is a filesystem root (`D:\`, `/`, a UNC share root). */
|
|
106
|
+
export declare function isFilesystemRoot(candidate: string): boolean;
|
|
79
107
|
/** `indexCoverage` as the `analyze` that produced the index wrote it. */
|
|
80
108
|
export interface RawIndexCoverage {
|
|
81
109
|
maxFileSizeBytes?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-meta.d.ts","sourceRoot":"","sources":["../../src/gitnexus/index-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAItE;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAa1F;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAY/B;AAED
|
|
1
|
+
{"version":3,"file":"index-meta.d.ts","sourceRoot":"","sources":["../../src/gitnexus/index-meta.ts"],"names":[],"mappings":"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"}
|
|
@@ -74,8 +74,10 @@ export async function findRegistryEntry(target, options = {}) {
|
|
|
74
74
|
* registry at all — and a plugin that refused to describe the user's own working
|
|
75
75
|
* directory because a global file elsewhere had not heard of it would be wrong.
|
|
76
76
|
* This is not by itself the route bound any more: a git project with no index still
|
|
77
|
-
* has a readable working tree, so the routes
|
|
78
|
-
* accepts either an index or a `.git` marker
|
|
77
|
+
* has a readable working tree, so the routes that need a graph use
|
|
78
|
+
* {@link isProjectRoot}, which accepts either an index or a `.git` marker, and the
|
|
79
|
+
* scan/delta routes use {@link isExistingDirectory}, which also accepts a plain
|
|
80
|
+
* folder that GitNexus could index with `--skip-git`.
|
|
79
81
|
*/
|
|
80
82
|
export async function hasIndex(repoPath) {
|
|
81
83
|
const dir = path.join(repoPath, '.gitnexus');
|
|
@@ -91,25 +93,63 @@ export async function hasIndex(repoPath) {
|
|
|
91
93
|
return false;
|
|
92
94
|
}
|
|
93
95
|
/**
|
|
94
|
-
* Whether a directory
|
|
96
|
+
* Whether a directory carries a `.git` marker.
|
|
97
|
+
*
|
|
98
|
+
* `.git` may be a directory or a file (worktrees and submodules), so this checks
|
|
99
|
+
* existence rather than type. The distinction matters to the maintenance commands:
|
|
100
|
+
* GitNexus refuses to analyze a folder without a `.git` unless `--skip-git` is
|
|
101
|
+
* passed, and it reports the resulting index as having no commit baseline.
|
|
102
|
+
*/
|
|
103
|
+
export function hasGitMarker(repoPath) {
|
|
104
|
+
try {
|
|
105
|
+
return existsSync(path.join(repoPath, '.git'));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether a directory is a project the read routes may describe.
|
|
95
113
|
*
|
|
96
114
|
* Deliberately wider than {@link hasIndex}: a git repository with no index still has
|
|
97
115
|
* a readable working tree, and both the scan-only coverage report and the initialize
|
|
98
116
|
* action are for exactly that state. A directory with neither an index nor a `.git`
|
|
99
|
-
* marker is refused
|
|
100
|
-
*
|
|
101
|
-
*
|
|
117
|
+
* marker is refused by the routes that need git or a graph (search, graph, file
|
|
118
|
+
* history); the coverage and delta routes use {@link isExistingDirectory} instead,
|
|
119
|
+
* because GitNexus can index a plain folder with `--skip-git`.
|
|
102
120
|
*/
|
|
103
121
|
export async function isProjectRoot(repoPath) {
|
|
104
122
|
if (await hasIndex(repoPath))
|
|
105
123
|
return true;
|
|
124
|
+
return hasGitMarker(repoPath);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Whether a path is an existing directory.
|
|
128
|
+
*
|
|
129
|
+
* This is the bound for the scan-only coverage report and the delta gate. The panel
|
|
130
|
+
* reports the working directory the operator is *in*, and that directory need not be
|
|
131
|
+
* a git repository: GitNexus indexes any folder with `--skip-git`, and the scan and
|
|
132
|
+
* the language breakdown need neither git nor an index. Refusing such a directory
|
|
133
|
+
* left the panel with a raw 404 and no numbers at all — the reported bug.
|
|
134
|
+
*
|
|
135
|
+
* This is still not a directory listing: the route describes exactly the path it is
|
|
136
|
+
* given and offers no way to browse a parent or a sibling. Filesystem roots are
|
|
137
|
+
* excluded by {@link isFilesystemRoot}, because scanning a whole drive is not a
|
|
138
|
+
* project report.
|
|
139
|
+
*/
|
|
140
|
+
export async function isExistingDirectory(candidate) {
|
|
106
141
|
try {
|
|
107
|
-
return
|
|
142
|
+
return (await fs.stat(candidate)).isDirectory();
|
|
108
143
|
}
|
|
109
144
|
catch {
|
|
110
145
|
return false;
|
|
111
146
|
}
|
|
112
147
|
}
|
|
148
|
+
/** Whether a path is a filesystem root (`D:\`, `/`, a UNC share root). */
|
|
149
|
+
export function isFilesystemRoot(candidate) {
|
|
150
|
+
const resolved = path.resolve(candidate);
|
|
151
|
+
return path.dirname(resolved) === resolved;
|
|
152
|
+
}
|
|
113
153
|
/**
|
|
114
154
|
* Read the index manifest for a repository.
|
|
115
155
|
*
|