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
package/lib/routes/coverage.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* carrier contract in `@deepseek-ai/dsh-host-webserver`: bare `node:http`, no
|
|
6
6
|
* Express, exact-then-longest-prefix matching, and a disposer for teardown.
|
|
7
7
|
*
|
|
8
|
-
* THE BOUND IS "
|
|
9
|
-
*
|
|
8
|
+
* THE BOUND IS "A DIRECTORY THE CALLER NAMED", NOT "IS IN THE REGISTRY"
|
|
9
|
+
* ----------------------------------------------------------------------
|
|
10
10
|
* An earlier revision accepted only paths present in `~/.gitnexus/registry.json`.
|
|
11
11
|
* That was wrong for the panel's actual job: it shows the project the user is
|
|
12
12
|
* *in*, and a working directory the global registry has never heard of — a fresh
|
|
@@ -16,18 +16,24 @@
|
|
|
16
16
|
*
|
|
17
17
|
* A later revision required an index. That was also wrong, for the same reason at a
|
|
18
18
|
* different state: a project with no index yet still has a readable working tree,
|
|
19
|
-
* and the engine already builds a useful scan-only report for it.
|
|
20
|
-
*
|
|
19
|
+
* and the engine already builds a useful scan-only report for it. A further revision
|
|
20
|
+
* still required a `.git` marker, which refused the exact directory this panel is
|
|
21
|
+
* most useful for on a machine without git: GitNexus indexes any folder with
|
|
22
|
+
* `--skip-git`, and the scan-only report never needed git in the first place.
|
|
21
23
|
*
|
|
22
|
-
* The
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* The bound is now an existing directory that is not a filesystem root. The route
|
|
25
|
+
* does not offer a directory listing: it describes exactly the path it is given.
|
|
26
|
+
* Loopback and method guards live in `./guard.js`.
|
|
27
|
+
*
|
|
28
|
+
* RESOLUTION IS A SEPARATE READ
|
|
29
|
+
* -----------------------------
|
|
30
|
+
* A workspace may contain several indexed projects. `/api/gitnexus/projects` resolves
|
|
31
|
+
* that list and the client chooses one; this route stays "describe this path", so a
|
|
32
|
+
* project switch does not change what a refresh means and an older client keeps the
|
|
33
|
+
* old contract.
|
|
28
34
|
*/
|
|
29
35
|
import path from 'node:path';
|
|
30
|
-
import {
|
|
36
|
+
import { isExistingDirectory, isFilesystemRoot } from '../gitnexus/index-meta.js';
|
|
31
37
|
import { guardHandler, passGuard, answerHead, sendJson } from './guard.js';
|
|
32
38
|
/**
|
|
33
39
|
* @returns disposer that unregisters the route
|
|
@@ -47,18 +53,16 @@ export function mountCoverageRoute(webServer, deps) {
|
|
|
47
53
|
return;
|
|
48
54
|
}
|
|
49
55
|
const absRepo = path.resolve(repo);
|
|
50
|
-
// The bound
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
// anything — the route only answers loopback callers.
|
|
57
|
-
if (!(await isProjectRoot(absRepo))) {
|
|
56
|
+
// The bound is the directory itself, not a project marker: a folder with no
|
|
57
|
+
// `.git` and no index is still the operator's working tree, and the engine's
|
|
58
|
+
// scan-only report is exactly what makes that state informative. Refusing it
|
|
59
|
+
// produced the raw 404 the panel rendered as a dead end. Filesystem roots are
|
|
60
|
+
// excluded — scanning a whole drive is not a project report.
|
|
61
|
+
if (!(await isExistingDirectory(absRepo)) || isFilesystemRoot(absRepo)) {
|
|
58
62
|
sendJson(res, 404, {
|
|
59
63
|
ok: false,
|
|
60
|
-
code: 'not-a-
|
|
61
|
-
message:
|
|
64
|
+
code: 'not-a-directory',
|
|
65
|
+
message: `目录不存在、不是文件夹,或是文件系统根目录:${absRepo}`,
|
|
62
66
|
});
|
|
63
67
|
return;
|
|
64
68
|
}
|
|
@@ -126,7 +130,12 @@ export function mountCoverageRoute(webServer, deps) {
|
|
|
126
130
|
// `readAt` is what makes a refresh visible: without it two reads of an unchanged
|
|
127
131
|
// repository are byte-identical on screen, so the control looks inert even when
|
|
128
132
|
// it did exactly what it was asked.
|
|
129
|
-
sendJson(res, 200, {
|
|
133
|
+
sendJson(res, 200, {
|
|
134
|
+
ok: true,
|
|
135
|
+
report,
|
|
136
|
+
maintenance,
|
|
137
|
+
readAt: new Date().toISOString(),
|
|
138
|
+
});
|
|
130
139
|
});
|
|
131
140
|
return webServer.register({ kind: 'prefix', path: '/api/gitnexus/coverage', handler });
|
|
132
141
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/routes/coverage.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/routes/coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/delta?repo=<project>` — "is a refresh worth running?"
|
|
3
|
+
*
|
|
4
|
+
* Read-only on purpose, and separated from the analyze route for the same reason that
|
|
5
|
+
* one is separate from coverage: the two halves have different method policies. This
|
|
6
|
+
* route answers a question about the working tree; it never starts, cancels or writes
|
|
7
|
+
* anything, so it takes only the read guard.
|
|
8
|
+
*
|
|
9
|
+
* The panel calls it before offering the refresh control, which is why it has to be
|
|
10
|
+
* fast. It is: on a clean working tree the candidate set is empty and no file is read.
|
|
11
|
+
* The engine's own bound is a candidate cap, not a time budget — see `gitnexus/delta.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import type { WebServerService } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { DeltaReport } from '../gitnexus/delta.js';
|
|
15
|
+
export interface DeltaRouteDeps {
|
|
16
|
+
read: (repoPath: string) => Promise<DeltaReport>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @returns disposer that unregisters the route
|
|
20
|
+
*/
|
|
21
|
+
export declare function mountDeltaRoute(webServer: WebServerService, deps: DeltaRouteDeps): () => void;
|
|
22
|
+
//# sourceMappingURL=delta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.d.ts","sourceRoot":"","sources":["../../src/routes/delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CAClD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM,IAAI,CAgC7F"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/delta?repo=<project>` — "is a refresh worth running?"
|
|
3
|
+
*
|
|
4
|
+
* Read-only on purpose, and separated from the analyze route for the same reason that
|
|
5
|
+
* one is separate from coverage: the two halves have different method policies. This
|
|
6
|
+
* route answers a question about the working tree; it never starts, cancels or writes
|
|
7
|
+
* anything, so it takes only the read guard.
|
|
8
|
+
*
|
|
9
|
+
* The panel calls it before offering the refresh control, which is why it has to be
|
|
10
|
+
* fast. It is: on a clean working tree the candidate set is empty and no file is read.
|
|
11
|
+
* The engine's own bound is a candidate cap, not a time budget — see `gitnexus/delta.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { isExistingDirectory, isFilesystemRoot } from '../gitnexus/index-meta.js';
|
|
15
|
+
import { guardHandler, passGuard, answerHead, sendJson } from './guard.js';
|
|
16
|
+
const DELTA_METHODS = new Set(['GET', 'HEAD']);
|
|
17
|
+
/**
|
|
18
|
+
* @returns disposer that unregisters the route
|
|
19
|
+
*/
|
|
20
|
+
export function mountDeltaRoute(webServer, deps) {
|
|
21
|
+
const handler = guardHandler(async (req, res) => {
|
|
22
|
+
if (!passGuard(req, res, DELTA_METHODS))
|
|
23
|
+
return;
|
|
24
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
25
|
+
const repo = url.searchParams.get('repo');
|
|
26
|
+
if (repo === null || repo.trim() === '') {
|
|
27
|
+
sendJson(res, 400, { ok: false, code: 'missing-repo', message: 'repo query parameter is required' });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const absRepo = path.resolve(repo);
|
|
31
|
+
// Same bound as the coverage route: the directory the caller named, not a project
|
|
32
|
+
// marker. A folder with no index and no `.git` is answered by the engine with
|
|
33
|
+
// `comparable: false` and an explanation — which is a state the panel renders,
|
|
34
|
+
// and more useful than a 404.
|
|
35
|
+
if (!(await isExistingDirectory(absRepo)) || isFilesystemRoot(absRepo)) {
|
|
36
|
+
sendJson(res, 404, {
|
|
37
|
+
ok: false,
|
|
38
|
+
code: 'not-a-directory',
|
|
39
|
+
message: `目录不存在、不是文件夹,或是文件系统根目录:${absRepo}`,
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (answerHead(req, res))
|
|
44
|
+
return;
|
|
45
|
+
const report = await deps.read(absRepo);
|
|
46
|
+
sendJson(res, 200, { ok: true, delta: report, readAt: new Date().toISOString() });
|
|
47
|
+
});
|
|
48
|
+
return webServer.register({ kind: 'prefix', path: '/api/gitnexus/delta', handler });
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=delta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta.js","sourceRoot":"","sources":["../../src/routes/delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3E,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAMpE;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,SAA2B,EAAE,IAAoB;IAC/E,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9C,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC;YAAE,OAAO;QAEhD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACxC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC,CAAC;YACrG,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,kFAAkF;QAClF,8EAA8E;QAC9E,+EAA+E;QAC/E,8BAA8B;QAC9B,IAAI,CAAC,CAAC,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;YACvE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;gBACjB,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,yBAAyB,OAAO,EAAE;aAC5C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC;YAAE,OAAO;QAEjC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/file-diff?repo=<project>&path=<file>&sha=<commit>`
|
|
3
|
+
*
|
|
4
|
+
* The patch behind one commit's effect on one file. Split from file-history because the
|
|
5
|
+
* two cost differently: the timeline is a `git log`, and the patch is a `git show` that
|
|
6
|
+
* can carry a whole minified file. The client asks for it only when the reader expands a
|
|
7
|
+
* commit.
|
|
8
|
+
*
|
|
9
|
+
* Read-only. The path is resolved inside the project root and the sha is checked against
|
|
10
|
+
* a hash shape before it reaches git.
|
|
11
|
+
*/
|
|
12
|
+
import type { WebServerService } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { CommitDiff } from '../gitnexus/git-activity.js';
|
|
14
|
+
export interface FileDiffRouteDeps {
|
|
15
|
+
read: (repoPath: string, filePath: string, sha: string) => Promise<CommitDiff>;
|
|
16
|
+
}
|
|
17
|
+
/** @returns disposer that unregisters the route */
|
|
18
|
+
export declare function mountFileDiffRoute(webServer: WebServerService, deps: FileDiffRouteDeps): () => void;
|
|
19
|
+
//# sourceMappingURL=file-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-diff.d.ts","sourceRoot":"","sources":["../../src/routes/file-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CAChF;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAC3B,IAAI,EAAE,iBAAiB,GACtB,MAAM,IAAI,CA2DZ"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/file-diff?repo=<project>&path=<file>&sha=<commit>`
|
|
3
|
+
*
|
|
4
|
+
* The patch behind one commit's effect on one file. Split from file-history because the
|
|
5
|
+
* two cost differently: the timeline is a `git log`, and the patch is a `git show` that
|
|
6
|
+
* can carry a whole minified file. The client asks for it only when the reader expands a
|
|
7
|
+
* commit.
|
|
8
|
+
*
|
|
9
|
+
* Read-only. The path is resolved inside the project root and the sha is checked against
|
|
10
|
+
* a hash shape before it reaches git.
|
|
11
|
+
*/
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { isProjectRoot } from '../gitnexus/index-meta.js';
|
|
14
|
+
import { guardHandler, passGuard, answerHead, sendJson } from './guard.js';
|
|
15
|
+
/** @returns disposer that unregisters the route */
|
|
16
|
+
export function mountFileDiffRoute(webServer, deps) {
|
|
17
|
+
const handler = guardHandler(async (req, res) => {
|
|
18
|
+
if (!passGuard(req, res))
|
|
19
|
+
return;
|
|
20
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
21
|
+
const repo = url.searchParams.get('repo');
|
|
22
|
+
const file = url.searchParams.get('path');
|
|
23
|
+
const sha = url.searchParams.get('sha');
|
|
24
|
+
if (repo === null ||
|
|
25
|
+
repo.trim() === '' ||
|
|
26
|
+
file === null ||
|
|
27
|
+
file.trim() === '' ||
|
|
28
|
+
sha === null ||
|
|
29
|
+
!/^[0-9a-fA-F]{7,40}$/.test(sha.trim())) {
|
|
30
|
+
sendJson(res, 400, {
|
|
31
|
+
ok: false,
|
|
32
|
+
code: 'missing-params',
|
|
33
|
+
message: 'repo, path and a 7–40 character hex sha are required',
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const absRepo = path.resolve(repo);
|
|
38
|
+
if (!(await isProjectRoot(absRepo))) {
|
|
39
|
+
sendJson(res, 404, {
|
|
40
|
+
ok: false,
|
|
41
|
+
code: 'not-a-project',
|
|
42
|
+
message: 'directory is neither a GitNexus index nor a git repository root',
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const relative = file.trim();
|
|
47
|
+
const prefix = absRepo.endsWith(path.sep) ? absRepo : `${absRepo}${path.sep}`;
|
|
48
|
+
const absolute = path.resolve(absRepo, relative);
|
|
49
|
+
// Windows paths compare case-insensitively; normalising both sides avoids a
|
|
50
|
+
// false "outside project" for a path the caller spelled with another drive case.
|
|
51
|
+
const inside = process.platform === 'win32'
|
|
52
|
+
? absolute.toLowerCase().startsWith(prefix.toLowerCase())
|
|
53
|
+
: absolute.startsWith(prefix);
|
|
54
|
+
if (!inside) {
|
|
55
|
+
sendJson(res, 400, {
|
|
56
|
+
ok: false,
|
|
57
|
+
code: 'invalid-path',
|
|
58
|
+
message: 'path must resolve inside the project root',
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (answerHead(req, res))
|
|
63
|
+
return;
|
|
64
|
+
const diff = await deps.read(absRepo, relative, sha.trim());
|
|
65
|
+
sendJson(res, 200, { ok: true, ...diff, readAt: new Date().toISOString() });
|
|
66
|
+
});
|
|
67
|
+
return webServer.register({ kind: 'prefix', path: '/api/gitnexus/file-diff', handler });
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=file-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-diff.js","sourceRoot":"","sources":["../../src/routes/file-diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/graph?repo=<project>&view=communities|community&id=<community>`
|
|
3
|
+
*
|
|
4
|
+
* The read-only endpoint behind the panel's graph view. Two views, both bounded on the
|
|
5
|
+
* server: the browser never sends Cypher and never chooses how much of the graph to
|
|
6
|
+
* load — it picks one of two projections and the budgets are applied here.
|
|
7
|
+
*
|
|
8
|
+
* `view` defaults to `communities` rather than erroring when absent: the overview is the
|
|
9
|
+
* only sensible default, and the panel's first request has no scope yet. An unknown view
|
|
10
|
+
* is a 400, not a silent fallback — a typo must not render a different graph than the
|
|
11
|
+
* caller asked for.
|
|
12
|
+
*/
|
|
13
|
+
import { type GraphProjection, type GraphView } from '../gitnexus/graph.js';
|
|
14
|
+
import type { WebServerService } from '@deepseek-ai/cordis';
|
|
15
|
+
export interface GraphRouteDeps {
|
|
16
|
+
project: (repoPath: string, view: GraphView, id: string | null) => Promise<GraphProjection>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @returns disposer that unregisters the route
|
|
20
|
+
*/
|
|
21
|
+
export declare function mountGraphRoute(webServer: WebServerService, deps: GraphRouteDeps): () => void;
|
|
22
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/routes/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAA2B,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAErG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI5D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CAC7F;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM,IAAI,CAqD7F"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/graph?repo=<project>&view=communities|community&id=<community>`
|
|
3
|
+
*
|
|
4
|
+
* The read-only endpoint behind the panel's graph view. Two views, both bounded on the
|
|
5
|
+
* server: the browser never sends Cypher and never chooses how much of the graph to
|
|
6
|
+
* load — it picks one of two projections and the budgets are applied here.
|
|
7
|
+
*
|
|
8
|
+
* `view` defaults to `communities` rather than erroring when absent: the overview is the
|
|
9
|
+
* only sensible default, and the panel's first request has no scope yet. An unknown view
|
|
10
|
+
* is a 400, not a silent fallback — a typo must not render a different graph than the
|
|
11
|
+
* caller asked for.
|
|
12
|
+
*/
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { isProjectRoot } from '../gitnexus/index-meta.js';
|
|
15
|
+
import { GraphError, isGraphView } from '../gitnexus/graph.js';
|
|
16
|
+
import { guardHandler, passGuard, answerHead, sendJson } from './guard.js';
|
|
17
|
+
const GRAPH_METHODS = new Set(['GET', 'HEAD']);
|
|
18
|
+
/**
|
|
19
|
+
* @returns disposer that unregisters the route
|
|
20
|
+
*/
|
|
21
|
+
export function mountGraphRoute(webServer, deps) {
|
|
22
|
+
const handler = guardHandler(async (req, res) => {
|
|
23
|
+
if (!passGuard(req, res, GRAPH_METHODS))
|
|
24
|
+
return;
|
|
25
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
26
|
+
const repo = url.searchParams.get('repo');
|
|
27
|
+
if (repo === null || repo.trim() === '') {
|
|
28
|
+
sendJson(res, 400, { ok: false, code: 'missing-repo', message: 'repo query parameter is required' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const rawView = url.searchParams.get('view');
|
|
32
|
+
const view = rawView === null || rawView === '' ? 'communities' : rawView;
|
|
33
|
+
if (!isGraphView(view)) {
|
|
34
|
+
sendJson(res, 400, {
|
|
35
|
+
ok: false,
|
|
36
|
+
code: 'invalid-view',
|
|
37
|
+
message: 'view must be one of: communities, community',
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const absRepo = path.resolve(repo);
|
|
42
|
+
// Same bound as every other read route: a project root, so this cannot be used to
|
|
43
|
+
// probe arbitrary directories.
|
|
44
|
+
if (!(await isProjectRoot(absRepo))) {
|
|
45
|
+
sendJson(res, 404, {
|
|
46
|
+
ok: false,
|
|
47
|
+
code: 'not-a-project',
|
|
48
|
+
message: 'directory is neither a GitNexus index nor a git repository root',
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (answerHead(req, res))
|
|
53
|
+
return;
|
|
54
|
+
const id = url.searchParams.get('id');
|
|
55
|
+
try {
|
|
56
|
+
const projection = await deps.project(absRepo, view, id === null || id === '' ? null : id);
|
|
57
|
+
sendJson(res, 200, { ok: true, projection, readAt: new Date().toISOString() });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof GraphError) {
|
|
61
|
+
sendJson(res, error.status, { ok: false, code: error.code, message: error.message });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// `guardHandler` turns anything else into a 500; rethrow so its single error
|
|
65
|
+
// path stays the only place that formats an unexpected failure.
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return webServer.register({ kind: 'prefix', path: '/api/gitnexus/graph', handler });
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/routes/graph.ts"],"names":[],"mappings":"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"}
|
package/lib/routes/guard.d.ts
CHANGED
|
@@ -81,12 +81,9 @@ export declare function passWriteGuard(req: IncomingMessage, res: ServerResponse
|
|
|
81
81
|
/**
|
|
82
82
|
* Read a JSON body with a hard byte cap.
|
|
83
83
|
*
|
|
84
|
-
*
|
|
85
|
-
* and unbounded host memory — and this plugin's
|
|
86
|
-
*
|
|
87
|
-
* @param {import('node:http').IncomingMessage} req
|
|
88
|
-
* @param {number} [maxBytes]
|
|
89
|
-
* @returns {Promise<Record<string, unknown>>}
|
|
84
|
+
* The body is bounded by `maxBytes` (8 KiB by default). That cap is the only thing
|
|
85
|
+
* standing between a malformed client and unbounded host memory — and this plugin's
|
|
86
|
+
* bodies are at most a few fields.
|
|
90
87
|
*/
|
|
91
88
|
export declare function readJsonBody(req: IncomingMessage, maxBytes?: number): Promise<unknown>;
|
|
92
89
|
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/routes/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,2CAA2C;AAC3C,eAAO,MAAM,eAAe,aAA2B,CAAC;AAExD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CASxD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,QAShH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE,WAAW,CAAC,MAAM,CAAmB,GAC7C,OAAO,CAUT;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAM7E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAcpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAmBjF;AAED
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/routes/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,2CAA2C;AAC3C,eAAO,MAAM,eAAe,aAA2B,CAAC;AAExD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CASxD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,QAShH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,GAAE,WAAW,CAAC,MAAM,CAAmB,GAC7C,OAAO,CAUT;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAM7E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAcpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAmBjF;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,SAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA4B9F"}
|
package/lib/routes/guard.js
CHANGED
|
@@ -147,12 +147,9 @@ export function passWriteGuard(req, res) {
|
|
|
147
147
|
/**
|
|
148
148
|
* Read a JSON body with a hard byte cap.
|
|
149
149
|
*
|
|
150
|
-
*
|
|
151
|
-
* and unbounded host memory — and this plugin's
|
|
152
|
-
*
|
|
153
|
-
* @param {import('node:http').IncomingMessage} req
|
|
154
|
-
* @param {number} [maxBytes]
|
|
155
|
-
* @returns {Promise<Record<string, unknown>>}
|
|
150
|
+
* The body is bounded by `maxBytes` (8 KiB by default). That cap is the only thing
|
|
151
|
+
* standing between a malformed client and unbounded host memory — and this plugin's
|
|
152
|
+
* bodies are at most a few fields.
|
|
156
153
|
*/
|
|
157
154
|
export async function readJsonBody(req, maxBytes = 8 * 1024) {
|
|
158
155
|
return new Promise((resolve, reject) => {
|
package/lib/routes/guard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/routes/guard.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/routes/guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/projects?root=<workspace>` — which projects live under a workspace.
|
|
3
|
+
*
|
|
4
|
+
* The panel's workspace is often a container rather than one project. This route is the
|
|
5
|
+
* read-only resolve step: it reports the workspace itself, every GitNexus index at or
|
|
6
|
+
* below it, and git repositories that have no index yet, with enough metadata for a
|
|
7
|
+
* switcher. It never describes an arbitrary directory outside the one the caller named.
|
|
8
|
+
* The client chooses one and asks the existing coverage/delta/graph/search routes for
|
|
9
|
+
* that exact path.
|
|
10
|
+
*
|
|
11
|
+
* Keeping resolution out of `coverage` matters: a project switch must not re-read the
|
|
12
|
+
* project list, a refresh must not silently change which project is described, and an
|
|
13
|
+
* older client keeps the old coverage contract. The walk is bounded by
|
|
14
|
+
* `findIndexProjects`; its `truncated` flag is forwarded rather than hidden.
|
|
15
|
+
*/
|
|
16
|
+
import type { WebServerService } from '@deepseek-ai/cordis';
|
|
17
|
+
/**
|
|
18
|
+
* @returns disposer that unregisters the route
|
|
19
|
+
*/
|
|
20
|
+
export declare function mountProjectsRoute(webServer: WebServerService): () => void;
|
|
21
|
+
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI5D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAuC1E"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/gitnexus/projects?root=<workspace>` — which projects live under a workspace.
|
|
3
|
+
*
|
|
4
|
+
* The panel's workspace is often a container rather than one project. This route is the
|
|
5
|
+
* read-only resolve step: it reports the workspace itself, every GitNexus index at or
|
|
6
|
+
* below it, and git repositories that have no index yet, with enough metadata for a
|
|
7
|
+
* switcher. It never describes an arbitrary directory outside the one the caller named.
|
|
8
|
+
* The client chooses one and asks the existing coverage/delta/graph/search routes for
|
|
9
|
+
* that exact path.
|
|
10
|
+
*
|
|
11
|
+
* Keeping resolution out of `coverage` matters: a project switch must not re-read the
|
|
12
|
+
* project list, a refresh must not silently change which project is described, and an
|
|
13
|
+
* older client keeps the old coverage contract. The walk is bounded by
|
|
14
|
+
* `findIndexProjects`; its `truncated` flag is forwarded rather than hidden.
|
|
15
|
+
*/
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { isExistingDirectory, isFilesystemRoot } from '../gitnexus/index-meta.js';
|
|
18
|
+
import { findIndexProjects } from '../gitnexus/discover.js';
|
|
19
|
+
import { guardHandler, passGuard, answerHead, sendJson } from './guard.js';
|
|
20
|
+
const PROJECT_METHODS = new Set(['GET', 'HEAD']);
|
|
21
|
+
/**
|
|
22
|
+
* @returns disposer that unregisters the route
|
|
23
|
+
*/
|
|
24
|
+
export function mountProjectsRoute(webServer) {
|
|
25
|
+
const handler = guardHandler(async (req, res) => {
|
|
26
|
+
if (!passGuard(req, res, PROJECT_METHODS))
|
|
27
|
+
return;
|
|
28
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
29
|
+
const root = url.searchParams.get('root');
|
|
30
|
+
if (root === null || root.trim() === '') {
|
|
31
|
+
sendJson(res, 400, {
|
|
32
|
+
ok: false,
|
|
33
|
+
code: 'missing-root',
|
|
34
|
+
message: 'root query parameter is required',
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const absRoot = path.resolve(root);
|
|
39
|
+
if (!(await isExistingDirectory(absRoot)) || isFilesystemRoot(absRoot)) {
|
|
40
|
+
sendJson(res, 404, {
|
|
41
|
+
ok: false,
|
|
42
|
+
code: 'not-a-directory',
|
|
43
|
+
message: `目录不存在、不是文件夹,或是文件系统根目录:${absRoot}`,
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (answerHead(req, res))
|
|
48
|
+
return;
|
|
49
|
+
const result = await findIndexProjects(absRoot);
|
|
50
|
+
sendJson(res, 200, {
|
|
51
|
+
ok: true,
|
|
52
|
+
root: result.root,
|
|
53
|
+
projects: result.projects,
|
|
54
|
+
visited: result.visited,
|
|
55
|
+
truncated: result.truncated,
|
|
56
|
+
readAt: new Date().toISOString(),
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
return webServer.register({ kind: 'prefix', path: '/api/gitnexus/projects', handler });
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3E,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAA2B;IAC5D,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9C,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,eAAe,CAAC;YAAE,OAAO;QAElD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACxC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;gBACjB,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,kCAAkC;aAC5C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,CAAC,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;YACvE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;gBACjB,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,yBAAyB,OAAO,EAAE;aAC5C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC;YAAE,OAAO;QAEjC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAChD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;YACjB,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACjC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,CAAC,CAAC;AACzF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gitnexus-plus",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "DSH plugin: visualize a GitNexus project index, search
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "DSH plugin: visualize a GitNexus project index and its communities, decide whether a refresh would change anything before running it, search symbols by keyword, report what the index left out, and run incremental or FTS-repair maintenance from a sidebar panel.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|