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
@@ -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 "IS A PROJECT ROOT", NOT "IS IN THE REGISTRY"
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. The bound is now a
20
- * project root (`.gitnexus` or `.git`), which still refuses arbitrary directories.
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 route still refuses to describe arbitrary directories: the path must be a
23
- * project root — carrying `.gitnexus/gitnexus.json`, `.gitnexus/meta.json`, or a
24
- * `.git` marker — so a caller cannot use it to enumerate a filesystem. An index is
25
- * no longer required: the engine's scan-only report is what makes the "no index
26
- * yet" state informative instead of an empty command. Loopback and method guards
27
- * live in `./guard.js`.
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 { isProjectRoot } from '../gitnexus/index-meta.js';
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 changed from "is a GitNexus index" to "is a project root": a
51
- // directory with neither an index nor a `.git` marker is still refused, so the
52
- // route cannot be used to enumerate a filesystem. A git repository without an
53
- // index is the state the panel's initialize action exists for, and the engine
54
- // already produces a useful scan-only report for it (file counts, bytes,
55
- // languages, the size cap). Refusing that state was hiding data, not protecting
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-project',
61
- message: 'directory is neither a GitNexus index nor a git repository root',
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, { ok: true, report, maintenance, readAt: new Date().toISOString() });
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":"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"}
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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"}
@@ -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
- * Bounded because the cap is the only thing standing between a malformed client
85
- * and unbounded host memory — and this plugin's bodies are at most a few fields.
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;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,SAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA4B9F"}
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"}
@@ -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
- * Bounded because the cap is the only thing standing between a malformed client
151
- * and unbounded host memory — and this plugin's bodies are at most a few fields.
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) => {
@@ -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.3",
4
- "description": "DSH plugin: visualize a GitNexus project index, search its symbols by keyword, report what it left out, and run its incremental refresh from a sidebar panel — including detecting a missing GitNexus and installing one on request.",
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": {