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.
Files changed (84) hide show
  1. package/README-CN.md +34 -10
  2. package/README.md +79 -14
  3. package/lib/bin/graph-helper.d.ts +21 -0
  4. package/lib/bin/graph-helper.d.ts.map +1 -0
  5. package/lib/bin/graph-helper.js +71 -0
  6. package/lib/bin/graph-helper.js.map +1 -0
  7. package/lib/client.js +4402 -2371
  8. package/lib/gitnexus/analyze.d.ts +54 -8
  9. package/lib/gitnexus/analyze.d.ts.map +1 -1
  10. package/lib/gitnexus/analyze.js +355 -84
  11. package/lib/gitnexus/analyze.js.map +1 -1
  12. package/lib/gitnexus/commands.d.ts +19 -2
  13. package/lib/gitnexus/commands.d.ts.map +1 -1
  14. package/lib/gitnexus/commands.js +42 -4
  15. package/lib/gitnexus/commands.js.map +1 -1
  16. package/lib/gitnexus/coverage.d.ts +8 -0
  17. package/lib/gitnexus/coverage.d.ts.map +1 -1
  18. package/lib/gitnexus/coverage.js +14 -2
  19. package/lib/gitnexus/coverage.js.map +1 -1
  20. package/lib/gitnexus/delta.d.ts +144 -0
  21. package/lib/gitnexus/delta.d.ts.map +1 -0
  22. package/lib/gitnexus/delta.js +347 -0
  23. package/lib/gitnexus/delta.js.map +1 -0
  24. package/lib/gitnexus/discover.d.ts +91 -0
  25. package/lib/gitnexus/discover.d.ts.map +1 -0
  26. package/lib/gitnexus/discover.js +214 -0
  27. package/lib/gitnexus/discover.js.map +1 -0
  28. package/lib/gitnexus/git-activity.d.ts +13 -21
  29. package/lib/gitnexus/git-activity.d.ts.map +1 -1
  30. package/lib/gitnexus/git-activity.js +60 -26
  31. package/lib/gitnexus/git-activity.js.map +1 -1
  32. package/lib/gitnexus/graph.d.ts +135 -0
  33. package/lib/gitnexus/graph.d.ts.map +1 -0
  34. package/lib/gitnexus/graph.js +474 -0
  35. package/lib/gitnexus/graph.js.map +1 -0
  36. package/lib/gitnexus/index-meta.d.ts +34 -6
  37. package/lib/gitnexus/index-meta.d.ts.map +1 -1
  38. package/lib/gitnexus/index-meta.js +47 -7
  39. package/lib/gitnexus/index-meta.js.map +1 -1
  40. package/lib/gitnexus/install.d.ts +11 -0
  41. package/lib/gitnexus/install.d.ts.map +1 -1
  42. package/lib/gitnexus/install.js +65 -9
  43. package/lib/gitnexus/install.js.map +1 -1
  44. package/lib/gitnexus/root.d.ts +0 -3
  45. package/lib/gitnexus/root.d.ts.map +1 -1
  46. package/lib/gitnexus/root.js +64 -5
  47. package/lib/gitnexus/root.js.map +1 -1
  48. package/lib/gitnexus/search.d.ts +34 -8
  49. package/lib/gitnexus/search.d.ts.map +1 -1
  50. package/lib/gitnexus/search.js +89 -13
  51. package/lib/gitnexus/search.js.map +1 -1
  52. package/lib/index.d.ts +13 -0
  53. package/lib/index.d.ts.map +1 -1
  54. package/lib/index.js +189 -7
  55. package/lib/index.js.map +1 -1
  56. package/lib/routes/analyze.d.ts +8 -6
  57. package/lib/routes/analyze.d.ts.map +1 -1
  58. package/lib/routes/analyze.js +25 -6
  59. package/lib/routes/analyze.js.map +1 -1
  60. package/lib/routes/coverage.d.ts +16 -10
  61. package/lib/routes/coverage.d.ts.map +1 -1
  62. package/lib/routes/coverage.js +31 -22
  63. package/lib/routes/coverage.js.map +1 -1
  64. package/lib/routes/delta.d.ts +22 -0
  65. package/lib/routes/delta.d.ts.map +1 -0
  66. package/lib/routes/delta.js +50 -0
  67. package/lib/routes/delta.js.map +1 -0
  68. package/lib/routes/file-diff.d.ts +19 -0
  69. package/lib/routes/file-diff.d.ts.map +1 -0
  70. package/lib/routes/file-diff.js +69 -0
  71. package/lib/routes/file-diff.js.map +1 -0
  72. package/lib/routes/graph.d.ts +22 -0
  73. package/lib/routes/graph.d.ts.map +1 -0
  74. package/lib/routes/graph.js +71 -0
  75. package/lib/routes/graph.js.map +1 -0
  76. package/lib/routes/guard.d.ts +3 -6
  77. package/lib/routes/guard.d.ts.map +1 -1
  78. package/lib/routes/guard.js +3 -6
  79. package/lib/routes/guard.js.map +1 -1
  80. package/lib/routes/projects.d.ts +21 -0
  81. package/lib/routes/projects.d.ts.map +1 -0
  82. package/lib/routes/projects.js +61 -0
  83. package/lib/routes/projects.js.map +1 -0
  84. 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 use {@link isProjectRoot}, which
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 is a project the routes may describe.
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, which is what keeps these routes from becoming a filesystem
75
- * probe. `.git` may be a directory or a file (worktrees and submodules), so this
76
- * checks existence rather than type.
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":"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"}
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 use {@link isProjectRoot}, which
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 is a project the routes may describe.
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, which is what keeps these routes from becoming a filesystem
100
- * probe. `.git` may be a directory or a file (worktrees and submodules), so this
101
- * checks existence rather than type.
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 existsSync(path.join(repoPath, '.git'));
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
  *