repo-codegraph 0.0.0-stage → 0.2.1
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.md +462 -2
- package/dist/analyzer/analyzeProject.d.ts +17 -0
- package/dist/analyzer/analyzeProject.d.ts.map +1 -0
- package/dist/analyzer/analyzeProject.js +338 -0
- package/dist/analyzer/analyzeProject.js.map +1 -0
- package/dist/analyzer/buildDefinitionGraph.d.ts +47 -0
- package/dist/analyzer/buildDefinitionGraph.d.ts.map +1 -0
- package/dist/analyzer/buildDefinitionGraph.js +60 -0
- package/dist/analyzer/buildDefinitionGraph.js.map +1 -0
- package/dist/analyzer/incrementalIndex.d.ts +21 -0
- package/dist/analyzer/incrementalIndex.d.ts.map +1 -0
- package/dist/analyzer/incrementalIndex.js +115 -0
- package/dist/analyzer/incrementalIndex.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +249 -0
- package/dist/cli.js.map +1 -0
- package/dist/files/discoverSourceFiles.d.ts +8 -0
- package/dist/files/discoverSourceFiles.d.ts.map +1 -0
- package/dist/files/discoverSourceFiles.js +46 -0
- package/dist/files/discoverSourceFiles.js.map +1 -0
- package/dist/git/diffImpact.d.ts +22 -0
- package/dist/git/diffImpact.d.ts.map +1 -0
- package/dist/git/diffImpact.js +113 -0
- package/dist/git/diffImpact.js.map +1 -0
- package/dist/graph/DirectedGraph.d.ts +44 -0
- package/dist/graph/DirectedGraph.d.ts.map +1 -0
- package/dist/graph/DirectedGraph.js +258 -0
- package/dist/graph/DirectedGraph.js.map +1 -0
- package/dist/graph/types.d.ts +13 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/projectSession.d.ts +18 -0
- package/dist/mcp/projectSession.d.ts.map +1 -0
- package/dist/mcp/projectSession.js +86 -0
- package/dist/mcp/projectSession.js.map +1 -0
- package/dist/mcp/server.d.ts +23 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +512 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +3 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +35 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/parser/extractDeclarations.d.ts +30 -0
- package/dist/parser/extractDeclarations.d.ts.map +1 -0
- package/dist/parser/extractDeclarations.js +83 -0
- package/dist/parser/extractDeclarations.js.map +1 -0
- package/dist/parser/parseSourceFile.d.ts +4 -0
- package/dist/parser/parseSourceFile.d.ts.map +1 -0
- package/dist/parser/parseSourceFile.js +9 -0
- package/dist/parser/parseSourceFile.js.map +1 -0
- package/dist/persistence/jsonPersistence.d.ts +7 -0
- package/dist/persistence/jsonPersistence.d.ts.map +1 -0
- package/dist/persistence/jsonPersistence.js +98 -0
- package/dist/persistence/jsonPersistence.js.map +1 -0
- package/dist/queries/analyzeImpact.d.ts +14 -0
- package/dist/queries/analyzeImpact.d.ts.map +1 -0
- package/dist/queries/analyzeImpact.js +56 -0
- package/dist/queries/analyzeImpact.js.map +1 -0
- package/dist/queries/codeGraphQueries.d.ts +7 -0
- package/dist/queries/codeGraphQueries.d.ts.map +1 -0
- package/dist/queries/codeGraphQueries.js +86 -0
- package/dist/queries/codeGraphQueries.js.map +1 -0
- package/dist/visualization/viewer.d.ts +17 -0
- package/dist/visualization/viewer.d.ts.map +1 -0
- package/dist/visualization/viewer.js +349 -0
- package/dist/visualization/viewer.js.map +1 -0
- package/package.json +55 -3
package/README.md
CHANGED
|
@@ -1,3 +1,463 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Codegraph
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Codegraph statically analyzes TypeScript repositories and exposes source-code
|
|
4
|
+
relationships as a directed graph. The current in-memory engine discovers source
|
|
5
|
+
files, parses declarations, resolves internal modules and compiler-resolvable calls,
|
|
6
|
+
answers graph queries, calculates reverse impact, and maps tracked Git changes to
|
|
7
|
+
the smallest enclosing current symbol.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Current capabilities
|
|
12
|
+
|
|
13
|
+
- File, function, class, interface, and method nodes with source ranges.
|
|
14
|
+
- `DEFINES`, `IMPORTS`, and `CALLS` edges with provenance and confidence.
|
|
15
|
+
- Relative imports, configured TypeScript path aliases, and barrel re-exports.
|
|
16
|
+
- Default, named, namespace, aliased, and side-effect import syntax.
|
|
17
|
+
- Direct, imported, `this`, concrete-object, injected-object, and interface calls
|
|
18
|
+
when TypeScript can resolve an indexed declaration.
|
|
19
|
+
- Symbol search, dependencies, dependents, shortest paths, reverse impact, and
|
|
20
|
+
Git-diff impact analysis.
|
|
21
|
+
- A local stdio MCP server with bounded, source-linked tools for coding agents.
|
|
22
|
+
- A local, interactive force-directed visualization with search, filters, and
|
|
23
|
+
focused dependency neighborhoods.
|
|
24
|
+
|
|
25
|
+
## CLI
|
|
26
|
+
|
|
27
|
+
During development:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run codegraph -- index .
|
|
31
|
+
npm run codegraph -- find AuthService --root .
|
|
32
|
+
npm run codegraph -- deps AuthService.login --root .
|
|
33
|
+
npm run codegraph -- dependents AuthService.login --root .
|
|
34
|
+
npm run codegraph -- path AuthController.login TokenService.generate --root .
|
|
35
|
+
npm run codegraph -- impact AuthService.validateToken --root .
|
|
36
|
+
npm run codegraph -- diff --root .
|
|
37
|
+
npm run codegraph -- visualize .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`index` writes a versioned JSON snapshot to `.codegraph/index.json`. Query
|
|
41
|
+
commands rebuild from source by default; pass `--graph` to explicitly reuse the
|
|
42
|
+
snapshot when startup speed matters:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm run codegraph -- index .
|
|
46
|
+
npm run codegraph -- find AuthService --graph .codegraph/index.json
|
|
47
|
+
npm run codegraph -- impact AuthService.login --graph .codegraph/index.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
After indexing, open the interactive network in the default browser:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm run codegraph -- visualize .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The viewer reads `.codegraph/index.json`, binds only to `127.0.0.1`, and contains
|
|
57
|
+
no CDN scripts or hosted services, so the graph stays on the user's machine.
|
|
58
|
+
Use `--graph <file>` for a custom index, `--port <number>` for a fixed port, or
|
|
59
|
+
`--no-open` to print the URL without launching a browser. Scroll to zoom, drag
|
|
60
|
+
to pan, search by file or symbol, filter relationship types, and click a node to
|
|
61
|
+
focus on its immediate incoming and outgoing neighborhood.
|
|
62
|
+
Hovering over a node reveals its file or symbol name without permanently
|
|
63
|
+
cluttering the graph with labels.
|
|
64
|
+
|
|
65
|
+
To create a self-contained file that can be opened in an IDE HTML preview,
|
|
66
|
+
export the same visualization instead of starting the local server:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run codegraph -- visualize . --output .codegraph/visualization.html
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
All graph data, styles, and interaction code are embedded in the generated HTML;
|
|
73
|
+
it does not fetch the index or load scripts from the network.
|
|
74
|
+
|
|
75
|
+
The **Ask Codegraph** box supports local graph questions such as “What calls
|
|
76
|
+
`analyzeProject`?”, “What does `runCli` call?”, “What imports `server.ts`?”, and
|
|
77
|
+
“Where is `ProjectSession` defined?”. It matches the question to indexed files
|
|
78
|
+
and symbols, selects the relevant relationship and direction, focuses the graph,
|
|
79
|
+
and lists connected nodes. This is deterministic graph querying rather than an
|
|
80
|
+
LLM, so it does not send repository data to an external service.
|
|
81
|
+
Source paths in node details and question results are IDE deep links; clicking
|
|
82
|
+
one opens the indexed file at its source line in VS Code-compatible editors.
|
|
83
|
+
Change-impact questions use incoming `CALLS` relationships, so they show callers
|
|
84
|
+
that may be affected rather than functions the changed symbol itself depends on.
|
|
85
|
+
|
|
86
|
+
After source files change, update the saved index incrementally:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run codegraph -- update .
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`update` hashes current source files, detects additions, edits, and deletions,
|
|
93
|
+
expands the affected set through reverse imports, replaces stale nodes and edges,
|
|
94
|
+
and reuses unaffected graph data. Use `--graph <file>` when the index is stored
|
|
95
|
+
somewhere other than `.codegraph/index.json`.
|
|
96
|
+
|
|
97
|
+
Use `--output <file>` to choose another index location. A cached graph can become
|
|
98
|
+
stale after source edits, so rerun `index` before relying on cached results. JSON
|
|
99
|
+
is used because the current workflow writes and loads the whole graph and benefits
|
|
100
|
+
from a portable, inspectable format. SQLite should be reconsidered when incremental
|
|
101
|
+
indexing requires partial updates, database indexes, transactions, or concurrent
|
|
102
|
+
readers.
|
|
103
|
+
|
|
104
|
+
`deps` and `dependents` follow `CALLS` for symbols and `IMPORTS` for file paths.
|
|
105
|
+
`path` uses BFS and therefore returns a minimum-edge path across `CALLS` and
|
|
106
|
+
`IMPORTS`. `impact` performs reverse BFS with cycle protection.
|
|
107
|
+
|
|
108
|
+
## MCP for coding agents
|
|
109
|
+
|
|
110
|
+
The npm package includes a second executable, `codegraph-mcp`. It is a local
|
|
111
|
+
stdio process, not a hosted service: the coding agent starts it inside the
|
|
112
|
+
consumer repository and communicates with it using MCP messages.
|
|
113
|
+
|
|
114
|
+
Install the package in the TypeScript project that should be analyzed:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm install --save-dev codegraph
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The MCP client launches the project-local executable with:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npx --no-install codegraph-mcp --root .
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`--root` identifies the repository Codegraph should index and defaults to the
|
|
127
|
+
process working directory. Use `--index <file>` to override the default
|
|
128
|
+
`.codegraph/index.json` location.
|
|
129
|
+
|
|
130
|
+
### Codex
|
|
131
|
+
|
|
132
|
+
Run from the consumer project root:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
codex mcp add codegraph -- npx --no-install codegraph-mcp --root .
|
|
136
|
+
codex mcp list
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Claude Code
|
|
140
|
+
|
|
141
|
+
Run from the consumer project root:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
claude mcp add codegraph --scope project -- npx --no-install codegraph-mcp --root .
|
|
145
|
+
claude mcp list
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Claude Code stores the shared project configuration in `.mcp.json`.
|
|
149
|
+
|
|
150
|
+
### Cursor
|
|
151
|
+
|
|
152
|
+
Create `.cursor/mcp.json` in the consumer project:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"mcpServers": {
|
|
157
|
+
"codegraph": {
|
|
158
|
+
"command": "npx",
|
|
159
|
+
"args": ["--no-install", "codegraph-mcp", "--root", "."]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Restart Cursor after adding the configuration.
|
|
166
|
+
|
|
167
|
+
### Other MCP clients
|
|
168
|
+
|
|
169
|
+
Any MCP client that supports local stdio servers can use the same executable.
|
|
170
|
+
The configuration filename and location depend on the client, but the common
|
|
171
|
+
configuration shape is:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"mcpServers": {
|
|
176
|
+
"codegraph": {
|
|
177
|
+
"command": "npx",
|
|
178
|
+
"args": ["--no-install", "codegraph-mcp", "--root", "."]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The server exposes a deliberately small surface:
|
|
185
|
+
|
|
186
|
+
| Tool | Purpose |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `find_symbol` | Resolve a name to stable node IDs and source ranges. |
|
|
189
|
+
| `get_dependencies` | Follow immediate outgoing `CALLS` or `IMPORTS`. |
|
|
190
|
+
| `get_dependents` | Follow immediate incoming `CALLS` or `IMPORTS`. |
|
|
191
|
+
| `find_path` | Find a bounded minimum-edge path between two nodes. |
|
|
192
|
+
| `analyze_impact` | Calculate bounded direct and transitive reverse impact. |
|
|
193
|
+
| `analyze_diff` | Map the tracked Git diff to symbols and blast radius. |
|
|
194
|
+
| `refresh_index` | Refresh the in-memory graph and persisted JSON index. |
|
|
195
|
+
|
|
196
|
+
All query tools are read-only. Results include repository-relative source paths
|
|
197
|
+
and ranges so an agent can read only the relevant code with its normal file
|
|
198
|
+
tools. Limits and depth bounds prevent dumping the complete graph into model
|
|
199
|
+
context. The server builds a fresh in-memory graph on the first query; agents
|
|
200
|
+
should call `refresh_index` after editing source files.
|
|
201
|
+
|
|
202
|
+
To make tool selection more reliable, add equivalent instructions to
|
|
203
|
+
`AGENTS.md`, `CLAUDE.md`, or the MCP client's project rules:
|
|
204
|
+
|
|
205
|
+
```md
|
|
206
|
+
Use the CodeGraph MCP tools when investigating this TypeScript codebase.
|
|
207
|
+
|
|
208
|
+
Before modifying an existing symbol:
|
|
209
|
+
|
|
210
|
+
1. Use `find_symbol` to locate it.
|
|
211
|
+
2. Use `get_dependencies` to understand what it uses.
|
|
212
|
+
3. Use `get_dependents` or `analyze_impact` to identify what could break.
|
|
213
|
+
4. After editing source files, call `refresh_index`.
|
|
214
|
+
5. Use `analyze_diff` to review the impact of the changes.
|
|
215
|
+
|
|
216
|
+
Prefer concise CodeGraph results over reading unrelated files.
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Applications embedding Codegraph can also construct the server themselves and
|
|
220
|
+
connect any MCP transport:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { createCodeGraphMcpServer } from "codegraph/mcp";
|
|
224
|
+
|
|
225
|
+
const server = createCodeGraphMcpServer({ rootDirectory: process.cwd() });
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Static-analysis limits
|
|
229
|
+
|
|
230
|
+
- Dynamic element calls such as `handlers[type]()` may remain unresolved.
|
|
231
|
+
- Calls inside arrow functions and anonymous function expressions are not assigned
|
|
232
|
+
to a caller node yet because those declarations are not modeled.
|
|
233
|
+
- Interface dispatch points to the compile-time interface method with `inferred`
|
|
234
|
+
confidence; it does not guess a runtime implementation.
|
|
235
|
+
- Package and standard-library targets are intentionally outside the project graph.
|
|
236
|
+
- Multiple calls between the same two symbols currently produce one `CALLS` edge.
|
|
237
|
+
- Overloads and declaration merging can collide under the current syntax-derived
|
|
238
|
+
symbol IDs and need semantic identity work.
|
|
239
|
+
- `codegraph diff` analyzes tracked changes relative to `HEAD`. Untracked files and
|
|
240
|
+
deleted symbols are not recoverable from the current AST, and changed lines outside
|
|
241
|
+
modeled declarations are reported as unmapped.
|
|
242
|
+
- Query commands rebuild from source unless `--graph` explicitly selects a persisted
|
|
243
|
+
JSON index. `update` performs incremental graph replacement, but it still creates a
|
|
244
|
+
fresh project-wide TypeScript Program so semantic resolution remains correct.
|
|
245
|
+
|
|
246
|
+
## Releasing
|
|
247
|
+
|
|
248
|
+
The first npm release was published manually. Subsequent versions publish
|
|
249
|
+
automatically when a GitHub Release is published. Before creating the release:
|
|
250
|
+
|
|
251
|
+
1. Update `version` in `package.json` and `package-lock.json`.
|
|
252
|
+
2. Merge that version change to `main`.
|
|
253
|
+
3. Create and publish a GitHub Release whose tag is exactly `v<version>` (for
|
|
254
|
+
example, package version `0.2.0` requires tag `v0.2.0`).
|
|
255
|
+
|
|
256
|
+
The release workflow verifies the tag, installs from the lockfile, runs the type
|
|
257
|
+
check, tests, and build, then publishes the public package to npm. The npm package
|
|
258
|
+
must have `.github/workflows/publish.yml` configured as a GitHub Actions trusted
|
|
259
|
+
publisher for repository `RuchiPurohit/codegraph` with direct `npm publish`
|
|
260
|
+
allowed. No long-lived npm token is required.
|
|
261
|
+
|
|
262
|
+
## The graph model
|
|
263
|
+
|
|
264
|
+
A **node** (or vertex) represents an entity. An **edge** represents a relationship
|
|
265
|
+
between two nodes. In a directed graph, the arrow has meaning:
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
AuthController.login --CALLS--> AuthService.login --CALLS--> TokenService.generate
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The first method calls—and therefore depends on—the second. If `A -> B` means A
|
|
272
|
+
depends on B, `dependencies(A)` follows outgoing arrows and `dependents(B)` follows
|
|
273
|
+
incoming arrows backward. These queries answer different questions:
|
|
274
|
+
|
|
275
|
+
```text
|
|
276
|
+
dependencies(AuthController.login) = [AuthService.login]
|
|
277
|
+
dependents(AuthService.login) = [AuthController.login]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
An **outgoing edge** starts at the node being examined; an **incoming edge** ends
|
|
281
|
+
there. An **adjacency list** stores each node beside its outgoing relationships. A
|
|
282
|
+
**reverse adjacency list** stores incoming relationships too. A **traversal** visits
|
|
283
|
+
reachable nodes by following edges. A **cycle** is a path that returns to an already
|
|
284
|
+
visited node, such as `A -> B -> C -> A`. Nodes are **disconnected** when no path in
|
|
285
|
+
the chosen direction connects them; a traversal from one component cannot visit
|
|
286
|
+
another.
|
|
287
|
+
|
|
288
|
+
A directed graph fits Codegraph because relationships such as `CALLS`, `IMPORTS`,
|
|
289
|
+
and `EXTENDS` are directional. Who a function calls and who calls that function are
|
|
290
|
+
both important, but they are not interchangeable.
|
|
291
|
+
|
|
292
|
+
## Representation choice
|
|
293
|
+
|
|
294
|
+
Let `V` be the number of nodes and `E` the number of edges.
|
|
295
|
+
|
|
296
|
+
| Representation | Storage | Add edge | Get outgoing neighbors | Good fit? |
|
|
297
|
+
| --- | ---: | ---: | ---: | --- |
|
|
298
|
+
| Adjacency matrix | `O(V²)` | `O(1)` | `O(V)` | Fast edge checks, but wasteful for sparse graphs |
|
|
299
|
+
| Adjacency list | `O(V + E)` | expected `O(1)` | `O(out-degree)` | Best fit for Codegraph |
|
|
300
|
+
| Edge list | `O(E)` | `O(1)` | `O(E)` | Simple storage, slow relationship queries |
|
|
301
|
+
|
|
302
|
+
Source-code graphs are generally sparse: each symbol relates to a small fraction
|
|
303
|
+
of all symbols. An adjacency list stores relationships that actually exist. This
|
|
304
|
+
implementation stores both outgoing and reverse adjacency, using more memory—each
|
|
305
|
+
edge is indexed twice—to make both dependencies and dependents efficient.
|
|
306
|
+
|
|
307
|
+
The nested shape is:
|
|
308
|
+
|
|
309
|
+
```text
|
|
310
|
+
Map<node id, Map<neighbor id, Map<edge type, edge>>>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The final map makes `(from, to, type)` the edge identity. Thus duplicate `CALLS`
|
|
314
|
+
edges are rejected, while `A --CALLS--> B` and `A --IMPORTS--> B` can coexist.
|
|
315
|
+
`Map` also preserves insertion order, producing deterministic traversal results.
|
|
316
|
+
|
|
317
|
+
Approximate operation costs are:
|
|
318
|
+
|
|
319
|
+
| Operation | Time | Extra space |
|
|
320
|
+
| --- | ---: | ---: |
|
|
321
|
+
| Add node | expected `O(1)` | `O(1)` |
|
|
322
|
+
| Add edge | expected `O(1)` | `O(1)` (indexed twice) |
|
|
323
|
+
| Get dependencies | `O(out-neighbors)` | output size |
|
|
324
|
+
| Get dependents | `O(in-neighbors)` | output size |
|
|
325
|
+
| BFS / DFS | `O(V + E)` reachable | `O(V)` reachable |
|
|
326
|
+
| Find path with BFS | `O(V + E)` reachable | `O(V)` reachable |
|
|
327
|
+
|
|
328
|
+
An adjacency matrix would be worth reconsidering only for a small, very dense graph
|
|
329
|
+
where constant-time arbitrary edge checks dominate. An edge list may be useful as a
|
|
330
|
+
serialization format later, but is not a good query index.
|
|
331
|
+
|
|
332
|
+
## Types and TypeScript concepts
|
|
333
|
+
|
|
334
|
+
`GraphNode<NodeData>` separates stable identity (`id`) from domain data. Later,
|
|
335
|
+
`NodeData` can describe a file or symbol without teaching the graph engine those
|
|
336
|
+
concepts. `GraphEdge<EdgeType, EdgeData>` stores direction, relationship type, and
|
|
337
|
+
optional metadata. Later, edge data might hold source location or resolution status.
|
|
338
|
+
|
|
339
|
+
`DirectedGraph<NodeData, EdgeType, EdgeData>` introduces only three useful generics:
|
|
340
|
+
|
|
341
|
+
- `NodeData`: the application-specific node payload; defaults to `unknown`.
|
|
342
|
+
- `EdgeType extends string`: restricts relationships to string names while allowing
|
|
343
|
+
a union such as `"CALLS" | "IMPORTS"`.
|
|
344
|
+
- `EdgeData`: optional relationship metadata; defaults to `unknown`.
|
|
345
|
+
|
|
346
|
+
`unknown` is safer than `any`: consumers must narrow it before using it. `readonly`
|
|
347
|
+
prevents mutation through these public types, although it is shallow and does not
|
|
348
|
+
deep-freeze nested objects. `Map<K, V>` associates unique keys with values; `Set<T>`
|
|
349
|
+
stores unique values and is used by traversals to remember visited node IDs.
|
|
350
|
+
|
|
351
|
+
## Behavioral decisions
|
|
352
|
+
|
|
353
|
+
- Duplicate node IDs throw. Silent overwrite could hide a symbol-ID bug.
|
|
354
|
+
- Edges require both endpoints to exist. This keeps every adjacency entry valid.
|
|
355
|
+
- Duplicate `(from, to, type)` edges throw. Different edge types may coexist.
|
|
356
|
+
- Neighbor and dependency queries return unique destination nodes, even if several
|
|
357
|
+
edge types connect the same pair.
|
|
358
|
+
- `getNode` returns `undefined` when absent, matching normal `Map.get` semantics.
|
|
359
|
+
- Operations that require an existing node throw for a missing ID.
|
|
360
|
+
- Self-edges are allowed. The generic engine models facts; later domain layers can
|
|
361
|
+
decide whether a particular self-relationship is meaningful.
|
|
362
|
+
- `findPath(A, A)` returns `[A]`, the valid zero-edge path from a node to itself.
|
|
363
|
+
|
|
364
|
+
## Important code path walkthrough
|
|
365
|
+
|
|
366
|
+
### 1. Types
|
|
367
|
+
|
|
368
|
+
[`src/graph/types.ts`](src/graph/types.ts) defines generic nodes and edges. They know
|
|
369
|
+
nothing about source files, functions, ASTs, or Codegraph-specific relationships.
|
|
370
|
+
|
|
371
|
+
### 2. Internal data structures
|
|
372
|
+
|
|
373
|
+
[`src/graph/DirectedGraph.ts`](src/graph/DirectedGraph.ts) holds a node `Map` and two
|
|
374
|
+
adjacency maps. Their `private` modifier hides implementation details. Their
|
|
375
|
+
`readonly` modifier prevents assigning a different map to a field; the contents of
|
|
376
|
+
the existing map intentionally remain mutable.
|
|
377
|
+
|
|
378
|
+
### 3. `addNode`
|
|
379
|
+
|
|
380
|
+
`addNode` checks `nodes.has(id)`, stores the node, and creates empty outgoing and
|
|
381
|
+
incoming buckets. Creating all three together maintains the graph invariant. The
|
|
382
|
+
expected time and added space are both `O(1)`.
|
|
383
|
+
|
|
384
|
+
### 4. `addEdge`
|
|
385
|
+
|
|
386
|
+
`addEdge` first validates both endpoints. It indexes the same immutable edge object
|
|
387
|
+
under the source's outgoing map and destination's incoming map. A nested map lookup
|
|
388
|
+
detects duplicates without building a delimiter-based string key (which could have
|
|
389
|
+
escaping or collision problems). Expected insertion time is `O(1)`.
|
|
390
|
+
|
|
391
|
+
### 5. Outgoing adjacency
|
|
392
|
+
|
|
393
|
+
For `A -> B`, the outgoing index stores B under A. Reading A's keys therefore gives
|
|
394
|
+
its direct dependencies. Multiple edge types to B live one level deeper and do not
|
|
395
|
+
duplicate B in node-level queries.
|
|
396
|
+
|
|
397
|
+
### 6. Reverse adjacency
|
|
398
|
+
|
|
399
|
+
The incoming index stores A under B for the same edge. Without it, finding B's
|
|
400
|
+
dependents would scan every edge, costing `O(E)`. The tradeoff is a second index and
|
|
401
|
+
therefore `O(E)` additional references.
|
|
402
|
+
|
|
403
|
+
### 7. Dependencies and dependents
|
|
404
|
+
|
|
405
|
+
`getDependencies` reads outgoing neighbor IDs; `getDependents` reads incoming IDs.
|
|
406
|
+
Both translate IDs back to nodes. Work is proportional to the number of direct
|
|
407
|
+
neighbors returned.
|
|
408
|
+
|
|
409
|
+
### 8. BFS
|
|
410
|
+
|
|
411
|
+
Breadth-first search uses a first-in, first-out queue, so it visits all nodes one edge
|
|
412
|
+
away before nodes two edges away. `nextIndex` advances through an array instead of
|
|
413
|
+
calling `shift()`, which would repeatedly move remaining elements. Nodes enter the
|
|
414
|
+
visited `Set` when enqueued, preventing duplicates and guaranteeing termination on
|
|
415
|
+
cycles. Across the reachable graph, time is `O(V + E)` and space is `O(V)`.
|
|
416
|
+
|
|
417
|
+
### 9. DFS
|
|
418
|
+
|
|
419
|
+
Depth-first search uses an explicit last-in, first-out stack rather than recursion.
|
|
420
|
+
This avoids overflowing JavaScript's call stack on a deep repository graph. Neighbors
|
|
421
|
+
are pushed in reverse so the earliest inserted neighbor is visited first. DFS has the
|
|
422
|
+
same asymptotic bounds as BFS, but explores deeply before returning to siblings.
|
|
423
|
+
Neither order is universally better; they answer different traversal needs.
|
|
424
|
+
|
|
425
|
+
### 10. `findPath`
|
|
426
|
+
|
|
427
|
+
Path search uses BFS, which finds a minimum-edge path in an unweighted graph. When B
|
|
428
|
+
is first discovered from A it records `parent[B] = A`. On reaching the target, it
|
|
429
|
+
walks parents backward and reverses the result. A parent map is more memory-efficient
|
|
430
|
+
than copying a complete path into every queue entry. Time is `O(V + E)` and auxiliary
|
|
431
|
+
space is `O(V)` over reachable nodes.
|
|
432
|
+
|
|
433
|
+
### 11. Tests
|
|
434
|
+
|
|
435
|
+
[`test/DirectedGraph.test.ts`](test/DirectedGraph.test.ts) covers nodes, duplicate and
|
|
436
|
+
invalid edges, multiple relationship types, self-edges, directionality, deterministic
|
|
437
|
+
BFS/DFS, cycles, disconnected components, shortest paths, missing paths, and the
|
|
438
|
+
zero-edge path. Node's built-in test runner supplies test organization and assertions;
|
|
439
|
+
Node loads the TypeScript files through `tsx` without a separate test framework.
|
|
440
|
+
|
|
441
|
+
## Run it
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
npm install
|
|
445
|
+
npm test
|
|
446
|
+
npm run check
|
|
447
|
+
npm run build
|
|
448
|
+
npm run example
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Understanding check
|
|
452
|
+
|
|
453
|
+
1. If `A -> B` means A depends on B, what do `getDependencies("A")` and
|
|
454
|
+
`getDependents("B")` each return?
|
|
455
|
+
2. Why does this implementation pay the memory cost of an incoming adjacency map?
|
|
456
|
+
3. Why does adding a duplicate node throw instead of overwrite?
|
|
457
|
+
4. Why is an edge identified by `from + to + type`, rather than only `from + to`?
|
|
458
|
+
5. Why must both endpoint nodes exist before adding an edge?
|
|
459
|
+
6. What different jobs do `Map` and `Set` perform here?
|
|
460
|
+
7. Why is a node marked visited when BFS enqueues it rather than when it is removed?
|
|
461
|
+
8. Why does BFS produce a shortest path by edge count in this unweighted graph?
|
|
462
|
+
9. Why can iterative DFS be safer than recursive DFS for a large codebase?
|
|
463
|
+
10. Why do BFS and DFS take `O(V + E)` time and `O(V)` extra space in the worst case?
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CodeGraph } from "./buildDefinitionGraph.js";
|
|
2
|
+
export type AnalysisDiagnosticKind = "compiler" | "unresolved-import" | "external-import" | "unresolved-call" | "external-call" | "unmodeled-caller" | "ambiguous-call";
|
|
3
|
+
export interface AnalysisDiagnostic {
|
|
4
|
+
readonly kind: AnalysisDiagnosticKind;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
readonly file: string | null;
|
|
7
|
+
readonly line: number | null;
|
|
8
|
+
readonly column: number | null;
|
|
9
|
+
}
|
|
10
|
+
export interface ProjectAnalysis {
|
|
11
|
+
readonly rootDirectory: string;
|
|
12
|
+
readonly graph: CodeGraph;
|
|
13
|
+
readonly diagnostics: readonly AnalysisDiagnostic[];
|
|
14
|
+
readonly fileHashes: Readonly<Record<string, string>>;
|
|
15
|
+
}
|
|
16
|
+
export declare function analyzeProject(rootDirectory: string): Promise<ProjectAnalysis>;
|
|
17
|
+
//# sourceMappingURL=analyzeProject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzeProject.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyzeProject.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,SAAS,EAIf,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV,mBAAmB,GACnB,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,CAAC;AAErB,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACpD,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAED,wBAAsB,cAAc,CAClC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,eAAe,CAAC,CA+C1B"}
|