@nithin554/code-crawler 0.1.38 → 0.1.40

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 (44) hide show
  1. package/README.md +206 -33
  2. package/dist/crawler.d.ts +27 -1
  3. package/dist/crawler.js +360 -11
  4. package/dist/crawler.js.map +1 -1
  5. package/dist/graph/queries.d.ts +70 -4
  6. package/dist/graph/queries.js +125 -1
  7. package/dist/graph/queries.js.map +1 -1
  8. package/dist/graph/schema.d.ts +24 -2
  9. package/dist/graph/schema.js +9 -0
  10. package/dist/graph/schema.js.map +1 -1
  11. package/dist/parser/controlFlow.d.ts +27 -0
  12. package/dist/parser/controlFlow.js +82 -0
  13. package/dist/parser/controlFlow.js.map +1 -0
  14. package/dist/parser/language.d.ts +101 -2
  15. package/dist/parser/languages/index.js +4 -0
  16. package/dist/parser/languages/index.js.map +1 -1
  17. package/dist/parser/languages/java.js +160 -6
  18. package/dist/parser/languages/java.js.map +1 -1
  19. package/dist/parser/languages/python.d.ts +2 -0
  20. package/dist/parser/languages/python.js +956 -0
  21. package/dist/parser/languages/python.js.map +1 -0
  22. package/dist/parser/languages/typescript.d.ts +2 -0
  23. package/dist/parser/languages/typescript.js +1111 -0
  24. package/dist/parser/languages/typescript.js.map +1 -0
  25. package/dist/parser/moduleId.d.ts +35 -0
  26. package/dist/parser/moduleId.js +93 -0
  27. package/dist/parser/moduleId.js.map +1 -0
  28. package/dist/parser/treeSitter.d.ts +6 -0
  29. package/dist/parser/treeSitter.js +13 -2
  30. package/dist/parser/treeSitter.js.map +1 -1
  31. package/dist/resolver/common.d.ts +52 -0
  32. package/dist/resolver/common.js +624 -0
  33. package/dist/resolver/common.js.map +1 -0
  34. package/dist/resolver/dynamic.js +66 -1
  35. package/dist/resolver/dynamic.js.map +1 -1
  36. package/dist/resolver/genericResolver.d.ts +45 -0
  37. package/dist/resolver/genericResolver.js +235 -0
  38. package/dist/resolver/genericResolver.js.map +1 -0
  39. package/dist/resolver/types.d.ts +24 -1
  40. package/dist/resolver/types.js +50 -0
  41. package/dist/resolver/types.js.map +1 -1
  42. package/dist/server.js +108 -5
  43. package/dist/server.js.map +1 -1
  44. package/package.json +2 -2
package/README.md CHANGED
@@ -3,12 +3,16 @@
3
3
  **Static + dynamic call tracing MCP server backed by a persistent property graph of your codebase.**
4
4
 
5
5
  Code-Crawler indexes a project into a SQLite-backed property graph — files, packages,
6
- classes, methods, fields, imports, annotations, **call edges**, **virtual dispatch**, and
7
- **aspect advice** — then exposes that graph to LLM clients as MCP tools: find callers,
8
- trace call paths, explore neighborhoods, follow runtime dispatch, and search symbols
9
- across the whole codebase.
6
+ modules, classes, interfaces, methods, fields, imports, annotations/decorators, **call
7
+ edges**, **virtual dispatch**, and **aspect advice** — then exposes that graph to LLM
8
+ clients as MCP tools: find callers, trace call paths, explore neighborhoods, follow
9
+ runtime dispatch, and search symbols across the whole codebase.
10
+
11
+ Supports **Java**, **Python**, and **TypeScript/JavaScript** projects, and can index all
12
+ three into the same graph.
10
13
 
11
14
  📖 **Documentation site:** <https://nithin554.github.io/code-crawler/>
15
+ 📘 **Task-oriented guide:** [`USAGE.md`](USAGE.md)
12
16
 
13
17
  ```
14
18
  ┌─────────────────────── MCP (stdio) ───────────────────────┐
@@ -41,18 +45,36 @@ and answers them instantly with bounded recursive SQL queries.
41
45
 
42
46
  ## Features
43
47
 
44
- - **Java-first** via the `tree-sitter-java` grammar (WASM — no native build step).
45
- The parser/resolver layer is designed so more languages plug in per-language config modules.
48
+ - **Three languages, one graph**: Java (`tree-sitter-java`), Python
49
+ (`tree-sitter-python`), and TypeScript/JavaScript (`tree-sitter-typescript`,
50
+ `tree-sitter-tsx`, `tree-sitter-javascript`) — all WASM, no native build step.
51
+ Language is detected per file, so a polyglot repo lands in a single graph.
46
52
  - **Dynamic tracing on a static graph** — `DYNAMIC_DISPATCH` edges connect interface/base
47
53
  calls to every override in the project, and `ADVISES` edges weave AspectJ/Spring AOP
48
54
  advice into execution traces (`before`, `after`, `around`, `after-returning`,
49
55
  `after-throwing`, named `@Pointcut` references, `&&`/`||`/`!` and `execution`, `within`,
50
56
  `@annotation`, `@within`, `target`, `this`, `args` primitives).
51
- - **Annotations as first-class nodes** — `@Transactional`, `@Cacheable`, `@Async`, `@Retryable`
52
- and friends become `annotation` nodes with `ANNOTATED_WITH` edges, so you can query
53
- decorators on types, methods, and fields.
57
+ - **Run-time indirection is named, not lost** — Java reflection/`ServiceLoader`, Python
58
+ `getattr`/`importlib`, and JS `require`/dynamic `import()` produce `REFLECTS` edges to
59
+ the resolved symbol when the target is statically determinable, or to a
60
+ `__reflect__:` marker when it is not.
61
+ - **Framework wiring** — dependency injection (`@Autowired`, NestJS constructor
62
+ injection, FastAPI `Depends`) becomes `INJECTS` edges, and HTTP routes
63
+ (`@GetMapping`, `@RequestMapping`, NestJS `@Controller`/`@Get`, FastAPI/Flask
64
+ decorators, Express `app.get`) become `ROUTES` edges to the handler, which is also
65
+ marked as an entry point.
66
+ - **Control flow on call edges** — every `CALLS` edge records whether the call is
67
+ conditional and the compact source text of its enclosing guards/loops, so an agent can
68
+ tell a happy path from an error path without reading the file.
69
+ - **External dependencies are explicit** — third-party imports and runtime globals
70
+ become package-level `external_symbol` nodes with `REFERENCES` edges and a
71
+ `find_external_dependencies` summary, distinct from project code.
72
+ - **Annotations as first-class nodes** — `@Transactional`, `@Cacheable`, `@Async`,
73
+ `@Retryable`, Python decorators, and TS decorators become `annotation` nodes with
74
+ `ANNOTATED_WITH` edges, so you can query decorators on types, methods, and fields.
54
75
  - **Call resolution heuristics** that go beyond naive string matching:
55
76
  receiver types inferred from locals, parameters, fields, `new T()` expressions,
77
+ `self`/`this` scoping, dotted paths (`service.repo.find`), loop-variable element types,
56
78
  bare/`this`/`super` calls, static/qualified calls, and superclass chains.
57
79
  Unresolvable calls become `unknown_symbol` phantom nodes with `resolved:false` edges,
58
80
  so the graph stays traversable.
@@ -70,7 +92,7 @@ Requires Node.js ≥ 20.
70
92
  ```bash
71
93
  npm install
72
94
  npm run build # tsc → dist/
73
- npm test # vitest (80 tests)
95
+ npm test # vitest (125 tests)
74
96
  ```
75
97
 
76
98
  ## CLI
@@ -129,10 +151,15 @@ if green, **auto-releases**:
129
151
  Point the client at the same command. The typical flow for an LLM agent:
130
152
 
131
153
  1. `crawl_project` the repo once.
132
- 2. `search_symbols` to locate the symbol of interest.
133
- 3. `get_callers` / `get_callees` / `trace_path` to reason about the call graph.
134
- 4. `trace_execution` to follow runtime dispatch and aspect advice; `find_aspects`,
154
+ 2. `find_entry_points` to see where execution actually starts (routes, listeners, mains).
155
+ 3. `search_symbols` to locate the symbol of interest.
156
+ 4. `get_callers` / `get_callees` / `trace_path` to reason about the call graph.
157
+ 5. `trace_execution` to follow runtime dispatch and aspect advice; `find_aspects`,
135
158
  `get_decorators`, and `find_annotated` to inspect behaviour-changing annotations.
159
+ 6. `find_routes`, `find_injections`, `find_reflections`, and `find_external_dependencies`
160
+ to map framework wiring and third-party surface.
161
+
162
+ See [`USAGE.md`](USAGE.md) for the full task-oriented walkthrough.
136
163
 
137
164
  ## MCP surface
138
165
 
@@ -154,6 +181,11 @@ Point the client at the same command. The typical flow for an LLM agent:
154
181
  | `find_annotated` | Every symbol annotated with a given annotation |
155
182
  | `find_hotspots` | Rank methods by fan-in, fan-out, and advice interceptions |
156
183
  | `find_dead_code` | Methods with no incoming call/dispatch/advice edges |
184
+ | `find_entry_points` | Framework-invoked roots: HTTP routes, listeners, schedulers, CLI mains, tests |
185
+ | `find_routes` | HTTP route → handler mappings (`@GetMapping`, NestJS, FastAPI, Express) |
186
+ | `find_injections` | DI-supplied dependencies (`@Autowired`, NestJS, FastAPI `Depends`) |
187
+ | `find_reflections` | Run-time lookups: `Class.forName`, `getattr`, `importlib`, dynamic `import()` |
188
+ | `find_external_dependencies` | Third-party packages with reference and file counts |
157
189
  | `get_subgraph` | Neighborhood extraction for visualization |
158
190
  | `get_schema` | Describe the graph model |
159
191
  | `clear_index` | Reset the database |
@@ -163,15 +195,18 @@ Point the client at the same command. The typical flow for an LLM agent:
163
195
 
164
196
  ## Graph model
165
197
 
166
- **Nodes** — `kind` is one of `file | package | class | interface | enum | method |
167
- constructor | field | import | aspect | annotation_type | annotation | unknown_symbol`.
168
- Every node has a globally unique `qualifiedName` and a JSON `properties` blob
169
- (signature, modifiers, lines, annotations, ...).
198
+ **Nodes** — `kind` is one of `file | package | module | class | interface | enum | method |
199
+ constructor | field | import | external_symbol | aspect | annotation_type | annotation |
200
+ unknown_symbol`. Every node has a globally unique `qualifiedName` and a JSON `properties`
201
+ blob (signature, modifiers, lines, annotations, ...).
170
202
 
171
203
  **Edges** — `kind` is one of `CALLS | DECLARED_IN | CONTAINS | IMPLEMENTS | EXTENDS |
172
- IMPORTS | REFERENCES | DYNAMIC_DISPATCH | ADVISES | ANNOTATED_WITH`. `CALLS` edges carry
173
- `line`, `resolved`, and `confidence`; `ANNOTATED_WITH` edges carry `line` and `arguments`;
174
- `ADVISES` edges carry `adviceKind` and the resolved `pointcut`.
204
+ IMPORTS | REFERENCES | DYNAMIC_DISPATCH | ADVISES | ANNOTATED_WITH | REFLECTS | INJECTS |
205
+ ROUTES`. `CALLS` edges carry `line`, `resolved`, `confidence`, and — when the call sits
206
+ behind control flow — `conditional`, `controlFlow`, and `guards`; `ANNOTATED_WITH` edges
207
+ carry `line` and `arguments`; `ADVISES` edges carry `adviceKind` and the resolved
208
+ `pointcut`; `INJECTS` carries `injectedName` and `framework`; `REFLECTS` carries
209
+ `mechanism`; `REFERENCES` carries `external`.
175
210
 
176
211
  Id scheme:
177
212
 
@@ -184,6 +219,13 @@ Id scheme:
184
219
  | annotation | `@Transactional` |
185
220
  | import | `__import__:java.util.List` |
186
221
  | unresolved call | `__unknown__:System.out.println` |
222
+ | Python/TS module | `module:app.service` |
223
+ | module-level function | `module:app.api#read_order` |
224
+ | module-level field | `module:app.service$AUDIT_LOG` |
225
+ | inline arrow handler | `module:src.routes#<anonymous@7>` |
226
+ | external package | `ext:fastapi` |
227
+ | reflective marker | `__reflect__:./plugins/${name}` |
228
+ | injection marker | `__inject__:Repo` |
187
229
 
188
230
  ## Dynamic behaviour
189
231
 
@@ -236,7 +278,79 @@ Framework decorators (`@Transactional`, `@Cacheable`, `@Async`, `@Retryable`,
236
278
  `@PreAuthorize`, `@Scheduled`, ...) are additionally flagged `decorator: true` so
237
279
  `get_decorators` and `find_annotated` can surface runtime-semantic annotations.
238
280
 
239
- ## Java resolution heuristics
281
+ Python decorators and TypeScript decorators are modelled the same way: `@router.get(...)`,
282
+ `@injectable`, `@Controller(...)`, `@Get(':id')` and friends land in `annotations` on the
283
+ declaring type/method, and framework ones additionally produce routes, injections, or
284
+ entry-point marks.
285
+
286
+ ### Reflection and run-time indirection
287
+
288
+ Lookups the compiler cannot see through are recorded as `REFLECTS` edges. When the target
289
+ is statically determinable — a string literal, a literal prefix plus a variable
290
+ (`importlib.import_module("app.plugins." + name)`), `getattr(obj, "run")`,
291
+ `require("./x")` — the edge points at the resolved symbol. Otherwise it points at a
292
+ `__reflect__:` marker carrying the `mechanism`:
293
+
294
+ ```text
295
+ module:app.dynamic#load_plugin --REFLECTS--> __reflect__:app.plugins. (importlib.import_module)
296
+ module:src.dynamic#loadPlugin --REFLECTS--> __reflect__:./plugins/${name} (import())
297
+ ```
298
+
299
+ Dynamic module prefixes that belong to the project's own namespace stay in-project; only
300
+ genuinely foreign packages become external dependencies.
301
+
302
+ ### Dependency injection
303
+
304
+ Dependencies the framework supplies are `INJECTS` edges instead of missing calls
305
+ (`find_injections`):
306
+
307
+ | Framework | Detected from |
308
+ | --- | --- |
309
+ | Spring | `@Autowired` fields/methods/constructors |
310
+ | NestJS | constructor parameters with decorators; `@Injectable()` classes |
311
+ | FastAPI | `Depends(...)` parameters |
312
+ | dependency-injector | constructor parameters of `@injectable` classes |
313
+ | plain TS/JS | constructor parameters typed with a project class |
314
+
315
+ Primitive and structural parameter types (`string`, `{ id: string }`, `list[str]`) are
316
+ counted but produce no edge, so `@Body() body: { id: string }` does not pollute the graph.
317
+
318
+ ### Framework entry points and routes
319
+
320
+ Methods a framework invokes have no source-level caller, so marking them keeps dead-code
321
+ analysis honest and gives `trace_execution` a root (`find_entry_points`, `find_routes`):
322
+
323
+ | Language | Recognised |
324
+ | --- | --- |
325
+ | Java | `@GetMapping`, `@PostMapping`, `@RequestMapping`, `@Scheduled`, `@EventListener`, listener annotations, tests, `main` |
326
+ | Python | FastAPI (`@router.get`, `@app.post`), Flask (`@bp.route`), `if __name__ == "__main__"`, `pytest` tests |
327
+ | TypeScript | NestJS `@Controller` + `@Get`/`@Post`/... , Express `app.get('/x', handler)`, Jest tests |
328
+
329
+ Express handlers written inline get a synthetic method node
330
+ (`module:src.routes#<anonymous@7>`) so their body still gets call edges, containment, and
331
+ a route edge.
332
+
333
+ ### Control flow on call edges
334
+
335
+ Each `CALLS` edge records whether it is guarded:
336
+
337
+ ```json
338
+ {
339
+ "kind": "CALLS",
340
+ "source": "app.repository.OrderRepository#create",
341
+ "target": "ext:builtins",
342
+ "properties": { "line": 14, "conditional": true, "controlFlow": ["branch", "loop"],
343
+ "guards": ["row.get(\"id\") == order_id", "self.rows"] }
344
+ }
345
+ ```
346
+
347
+ `controlFlow` uses `branch | loop | exception | predicate`. Guards are source text, not
348
+ symbolic constraints: they tell an agent *that* a call is conditional and *what* the
349
+ condition looked like, without pretending to model its truth value.
350
+
351
+ ## Resolution heuristics
352
+
353
+ Java:
240
354
 
241
355
  1. **Constructor calls** (`new Foo(...)`) → resolve `Foo` → `#<init>` (default ctor if implicit).
242
356
  2. **Bare / `this.` / `super.` calls** → enclosing type + superclass chain.
@@ -246,28 +360,87 @@ Framework decorators (`@Transactional`, `@Cacheable`, `@Async`, `@Retryable`,
246
360
  5. **Otherwise** → phantom `unknown_symbol` node, `resolved:false`, with the inference
247
361
  failure reason attached to the edge.
248
362
 
363
+ Python and TypeScript add module-aware resolution on top of the same pipeline:
364
+
365
+ 1. **Import-aware scope** — `from app.repository import OrderRepository` maps the local
366
+ name to `app.repository.OrderRepository`; relative and project modules are resolved by
367
+ suffix, bare specifiers fall back to package roots.
368
+ 2. **`self.x` / `this.x`** resolve against the enclosing class's fields; a receiver that
369
+ starts with `self`/`this`/`cls`/`super` never becomes an external package.
370
+ 3. **Dotted receivers** (`service.repo.find`) are walked segment by segment: the first
371
+ segment resolves to a type, the next to a field on it, and container members such as
372
+ `list.append` are recognised as builtin rather than unknown.
373
+ 4. **Locals** are typed from parameters, annotated assignments, `new T()`, and inferred
374
+ loop/variable element types (`for row in self.rows:` → `row: dict`).
375
+ 5. **Module functions** (`module:app.api#read_order`) participate in dispatch, routes, and
376
+ entry-point marking exactly like class methods.
377
+
249
378
  Each resolved edge carries a `confidence` score and a human-readable `reason` trace
250
379
  (e.g. `via local orders: OrderService`), so LLM clients can weigh how much to trust it.
251
380
 
252
- ## Roadmap: multi-language
381
+ ## Implemented languages
382
+
383
+ | Language | Extensions | Extracted | Dynamic features |
384
+ | --- | --- | --- | --- |
385
+ | Java | `.java` | packages, classes, interfaces, enums, records, annotations, constructors, methods, fields | virtual dispatch, AspectJ/Spring AOP advice, `@Autowired`, `@RequestMapping` family, `Class.forName`, `ServiceLoader` |
386
+ | Python | `.py` | modules, classes, functions, methods, fields, decorators | `@injectable`/`Depends` injection, FastAPI/Flask routes, `getattr`, `importlib`, `__main__`, tests |
387
+ | TypeScript | `.ts`, `.tsx` | modules, classes, interfaces, type aliases, enums, methods, fields, arrow functions | NestJS/Angular decorators, DI constructor injection, NestJS/Express routes, `import()`, `require()` |
388
+ | JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | modules, classes, functions, fields | decorators, Express routes, `require()` |
389
+
390
+ Crawling a directory mixes all of them automatically — language is chosen per file from
391
+ the extension, and a single crawl can return `languages: ["java", "python", "typescript"]`.
253
392
 
254
- The parsing layer is language-agnostic. Adding a language requires:
393
+ Adding a language requires:
255
394
 
256
395
  1. `src/parser/languages/<lang>.ts` — a `LanguageConfig` implementing `parseFile`
257
- (package/module extraction, type/member extraction, raw call sites, local type tables).
396
+ (module/package extraction, type/member extraction, raw call sites, local type tables).
258
397
  2. Register it in `src/parser/languages/index.ts`.
259
- 3. `src/resolver/<lang>Resolver.ts` — translate raw call sites into resolved targets
260
- using the shared `TypeIndex`.
398
+ 3. Optionally a `src/resolver/<lang>Resolver.ts`; most languages work with the shared
399
+ `src/resolver/genericResolver.ts` and only override language-specific builtins.
261
400
 
262
- Natural next targets: **Python** (native AST or tree-sitter-python), **TypeScript/JavaScript**
263
- (tree-sitter-typescript), and **Go** (tree-sitter-go). The graph schema, store, traversal
264
- queries, and MCP surface all stay the same.
401
+ The graph schema, store, traversal queries, and MCP surface stay the same for every
402
+ language. Natural next targets: Go, C#, Ruby, Rust.
265
403
 
266
- Planned enhancements:
404
+ Not yet modelled, by design:
267
405
 
268
- - Package-level dependency graphs (module boundaries, cycle detection).
269
- - Test coverage mapping (which tests exercise a given symbol).
270
- - Incremental crawling (watch mode) and git-blame aware invalidation.
406
+ - Value flow and branch feasibility — edges say *that* a call is conditional and record
407
+ the guard text, not which branch is taken.
408
+ - Cross-repository resolution — libraries outside the crawled tree are summarised as
409
+ `ext:` packages rather than crawled.
410
+ - Fully dynamic targets (`getattr(obj, name)`, `require(userInput)`) point at a
411
+ `__reflect__:` marker instead of a symbol; there is nothing static to point at.
412
+ - Runtime DI graphs assembled from configuration files rather than code.
413
+
414
+ ## Architecture
415
+
416
+ ```
417
+ src/
418
+ ├── index.ts CLI (crawl | status | query | serve)
419
+ ├── server.ts MCP server: tools, resources, prompt (thin handlers)
420
+ ├── crawler.ts file discovery → parse → index → resolve → persist
421
+ ├── config.ts env-overridable configuration
422
+ ├── graph/
423
+ │ ├── schema.ts node/edge kinds, id scheme, property shapes
424
+ │ ├── store.ts better-sqlite3 schema + idempotent file-scoped upserts
425
+ │ └── queries.ts recursive-CTE traversal (callers/callees/path/subgraph)
426
+ ├── parser/
427
+ │ ├── language.ts LanguageConfig contract
428
+ │ ├── treeSitter.ts WASM runtime + grammar loading + CST walk helpers
429
+ │ ├── controlFlow.ts enclosing branch/loop/try/catch + guard text
430
+ │ ├── moduleId.ts import-aware module id derivation
431
+ │ └── languages/
432
+ │ ├── index.ts language registry + per-file language detection
433
+ │ ├── java.ts Java structural extraction
434
+ │ ├── python.ts Python structural extraction
435
+ │ ├── typescript.ts TypeScript/TSX structural extraction
436
+ │ └── javascript.ts JavaScript structural extraction
437
+ └── resolver/
438
+ ├── types.ts TypeIndex (global name/method index) + ResolvedCall
439
+ ├── common.ts builtin types, external packages, receiver inference
440
+ ├── dynamic.ts virtual dispatch + AspectJ pointcut matching/advice edges
441
+ ├── genericResolver.ts shared resolution for module-based languages
442
+ └── javaResolver.ts Java call-target resolution heuristics
443
+ ```
271
444
 
272
445
  ## Architecture
273
446
 
package/dist/crawler.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { GraphStore } from "./graph/store.js";
2
2
  import type { GraphEdge, GraphNode } from "./graph/schema.js";
3
- import type { ParsedFile } from "./parser/language.js";
3
+ import type { EntryPointInfo, ParsedFile } from "./parser/language.js";
4
4
  import { buildTypeIndex } from "./resolver/types.js";
5
5
  /**
6
6
  * Project crawler: discover source files, parse them, build a global type
@@ -29,6 +29,16 @@ export interface CrawlResult {
29
29
  aspects: number;
30
30
  /** Number of annotation/decorator usages indexed. */
31
31
  decorators: number;
32
+ /** Framework-invoked roots (routes, listeners, schedulers, CLI, tests). */
33
+ entryPoints: number;
34
+ /** Framework-supplied dependency edges. */
35
+ injections: number;
36
+ /** Run-time lookup edges (reflection, dynamic import, getattr). */
37
+ reflections: number;
38
+ /** Distinct external packages/specifiers referenced by the project. */
39
+ externalDependencies: number;
40
+ /** Route registrations mapped to handler methods. */
41
+ routes: number;
32
42
  languages: string[];
33
43
  durationMs: number;
34
44
  }
@@ -45,6 +55,22 @@ export declare function buildCallEdges(parsed: ParsedFile, index: ReturnType<typ
45
55
  phantomNodes: GraphNode[];
46
56
  resolved: number;
47
57
  unresolved: number;
58
+ external: number;
59
+ externalPackages: Set<string>;
60
+ };
61
+ /**
62
+ * Framework wiring that the language parsers surface but the plain call
63
+ * resolver cannot see: external dependencies, dependency injection,
64
+ * reflective lookups and route registrations.
65
+ */
66
+ export declare function buildDependencyGraph(parsed: ParsedFile, index: ReturnType<typeof buildTypeIndex>, knownNodeIds: Set<string>): {
67
+ nodes: GraphNode[];
68
+ edges: GraphEdge[];
69
+ entryPointMarks: Map<string, EntryPointInfo>;
70
+ injections: number;
71
+ reflections: number;
72
+ routes: number;
73
+ externalDependencies: Set<string>;
48
74
  };
49
75
  /**
50
76
  * Crawl a project directory and index it into the store.