gitnexus 1.6.3-rc.8 → 1.6.3
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 +21 -5
- package/dist/_shared/graph/types.d.ts +16 -0
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +20 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +11 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/def-index.js +2 -2
- package/dist/_shared/scope-resolution/def-index.js.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
- package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
- package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/parsed-file.js +54 -0
- package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
- package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
- package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/position-index.js +2 -2
- package/dist/_shared/scope-resolution/position-index.js.map +1 -1
- package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
- package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/reference-site.js +24 -0
- package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.js +44 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +3 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +11 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.js +35 -4
- package/dist/cli/analyze.d.ts +27 -0
- package/dist/cli/analyze.js +31 -1
- package/dist/cli/clean.js +19 -1
- package/dist/cli/group.js +73 -0
- package/dist/cli/index-repo.js +8 -1
- package/dist/cli/index.js +26 -1
- package/dist/cli/list.js +11 -1
- package/dist/cli/remove.d.ts +30 -0
- package/dist/cli/remove.js +99 -0
- package/dist/cli/setup.js +185 -57
- package/dist/cli/tool.d.ts +5 -0
- package/dist/cli/tool.js +42 -0
- package/dist/config/ignore-service.d.ts +9 -0
- package/dist/config/ignore-service.js +80 -13
- package/dist/core/embedding-mode.d.ts +30 -0
- package/dist/core/embedding-mode.js +30 -0
- package/dist/core/embeddings/ast-utils.js +22 -22
- package/dist/core/embeddings/chunker.js +30 -25
- package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
- package/dist/core/embeddings/embedding-pipeline.js +15 -6
- package/dist/core/embeddings/text-generator.d.ts +1 -1
- package/dist/core/embeddings/text-generator.js +33 -24
- package/dist/core/embeddings/types.d.ts +43 -1
- package/dist/core/embeddings/types.js +101 -29
- package/dist/core/git-staleness.d.ts +18 -0
- package/dist/core/git-staleness.js +108 -0
- package/dist/core/graph/graph.js +115 -20
- package/dist/core/graph/types.d.ts +12 -1
- package/dist/core/group/config-parser.d.ts +4 -0
- package/dist/core/group/config-parser.js +18 -1
- package/dist/core/group/cross-impact.d.ts +41 -0
- package/dist/core/group/cross-impact.js +441 -0
- package/dist/core/group/extractors/http-patterns/php.js +126 -18
- package/dist/core/group/group-path-utils.d.ts +17 -0
- package/dist/core/group/group-path-utils.js +40 -0
- package/dist/core/group/resolve-at-member.d.ts +10 -0
- package/dist/core/group/resolve-at-member.js +31 -0
- package/dist/core/group/service.d.ts +9 -0
- package/dist/core/group/service.js +259 -25
- package/dist/core/group/types.d.ts +30 -0
- package/dist/core/ingestion/ast-cache.d.ts +16 -1
- package/dist/core/ingestion/ast-cache.js +14 -2
- package/dist/core/ingestion/call-processor.js +9 -0
- package/dist/core/ingestion/emit-references.d.ts +88 -0
- package/dist/core/ingestion/emit-references.js +229 -0
- package/dist/core/ingestion/filesystem-walker.js +6 -4
- package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
- package/dist/core/ingestion/finalize-orchestrator.js +139 -0
- package/dist/core/ingestion/framework-detection.js +6 -2
- package/dist/core/ingestion/import-processor.js +4 -0
- package/dist/core/ingestion/import-resolvers/python.js +9 -6
- package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
- package/dist/core/ingestion/import-target-adapter.js +95 -0
- package/dist/core/ingestion/language-provider.d.ts +36 -33
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
- package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
- package/dist/core/ingestion/languages/csharp/arity.js +37 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/captures.js +249 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
- package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
- package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
- package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
- package/dist/core/ingestion/languages/csharp/index.js +82 -0
- package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
- package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
- package/dist/core/ingestion/languages/csharp/query.js +515 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
- package/dist/core/ingestion/languages/csharp.js +14 -0
- package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
- package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
- package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
- package/dist/core/ingestion/languages/python/arity.js +38 -0
- package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/python/captures.js +106 -0
- package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
- package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
- package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/python/import-target.js +99 -0
- package/dist/core/ingestion/languages/python/index.d.ts +80 -0
- package/dist/core/ingestion/languages/python/index.js +80 -0
- package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/python/interpret.js +191 -0
- package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
- package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
- package/dist/core/ingestion/languages/python/query.d.ts +9 -0
- package/dist/core/ingestion/languages/python/query.js +267 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
- package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
- package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
- package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
- package/dist/core/ingestion/languages/python.js +14 -0
- package/dist/core/ingestion/model/method-registry.d.ts +9 -0
- package/dist/core/ingestion/model/method-registry.js +4 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
- package/dist/core/ingestion/model/semantic-model.js +55 -0
- package/dist/core/ingestion/mro-processor.js +38 -22
- package/dist/core/ingestion/parsing-processor.d.ts +18 -1
- package/dist/core/ingestion/parsing-processor.js +45 -11
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
- package/dist/core/ingestion/pipeline.js +2 -1
- package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
- package/dist/core/ingestion/registry-primary-flag.js +111 -0
- package/dist/core/ingestion/resolve-references.d.ts +63 -0
- package/dist/core/ingestion/resolve-references.js +175 -0
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
- package/dist/core/ingestion/scope-extractor.d.ts +86 -0
- package/dist/core/ingestion/scope-extractor.js +758 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
- package/dist/core/ingestion/shadow-harness.d.ts +113 -0
- package/dist/core/ingestion/shadow-harness.js +148 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
- package/dist/core/ingestion/utils/ast-helpers.js +70 -0
- package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
- package/dist/core/ingestion/utils/max-file-size.js +52 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
- package/dist/core/ingestion/workers/parse-worker.js +57 -21
- package/dist/core/lbug/lbug-adapter.d.ts +22 -2
- package/dist/core/lbug/lbug-adapter.js +58 -14
- package/dist/core/lbug/pool-adapter.d.ts +17 -0
- package/dist/core/lbug/pool-adapter.js +24 -14
- package/dist/core/run-analyze.d.ts +32 -0
- package/dist/core/run-analyze.js +74 -19
- package/dist/core/search/bm25-index.d.ts +18 -0
- package/dist/core/search/bm25-index.js +125 -12
- package/dist/core/tree-sitter/parser-loader.js +6 -1
- package/dist/mcp/local/local-backend.d.ts +67 -3
- package/dist/mcp/local/local-backend.js +296 -34
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +100 -17
- package/dist/mcp/tools.d.ts +4 -1
- package/dist/mcp/tools.js +75 -54
- package/dist/server/api.js +6 -2
- package/dist/storage/git.d.ts +49 -0
- package/dist/storage/git.js +111 -0
- package/dist/storage/repo-manager.d.ts +246 -1
- package/dist/storage/repo-manager.js +391 -9
- package/package.json +7 -6
- package/scripts/bench-scope-resolution.ts +134 -0
- package/scripts/ci-list-migrated-languages.ts +24 -0
- package/skills/gitnexus-cli.md +1 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 of the RFC #909 ingestion lifecycle: drain `ReferenceIndex`
|
|
3
|
+
* into the knowledge graph as labeled edges with `confidence` and
|
|
4
|
+
* `evidence` properties (Ring 2 PKG #925).
|
|
5
|
+
*
|
|
6
|
+
* The resolution phase (future PR) writes `Reference` records into
|
|
7
|
+
* `model.scopes.referenceSites`-derived `ReferenceIndex`; this module
|
|
8
|
+
* materializes those records as `GraphRelationship`s via
|
|
9
|
+
* `graph.addRelationship`. Every emitted edge carries:
|
|
10
|
+
*
|
|
11
|
+
* - `type`: one of `'CALLS' | 'ACCESSES' | 'INHERITS' | 'USES'`
|
|
12
|
+
* (mapped from `Reference.kind` — `'read'` and `'write'` both route
|
|
13
|
+
* to `ACCESSES`; `'type-reference'` and `'import-use'` route to
|
|
14
|
+
* `USES`; `'call'` stays `CALLS`; `'inherits'` stays `INHERITS`).
|
|
15
|
+
* - `confidence`: the pre-computed confidence from the Reference record.
|
|
16
|
+
* - `reason`: human-readable summary (`"scope-resolution: call | confidence 0.75"`).
|
|
17
|
+
* - `evidence`: the full `ResolutionEvidence[]` trace — additive graph
|
|
18
|
+
* property (see `GraphRelationship.evidence` in gitnexus-shared),
|
|
19
|
+
* so queries that don't know about it are unaffected.
|
|
20
|
+
* - `step`: carries the reference's access-kind discriminant when
|
|
21
|
+
* available (`1` for read, `2` for write) so `ACCESSES` edges retain
|
|
22
|
+
* the read/write distinction without forcing a new edge type.
|
|
23
|
+
*
|
|
24
|
+
* ## Optional scope-tree flush
|
|
25
|
+
*
|
|
26
|
+
* When `INGESTION_EMIT_SCOPES=1` is set, this module also emits:
|
|
27
|
+
*
|
|
28
|
+
* - `Scope` nodes for every `Scope` in the tree
|
|
29
|
+
* - `CONTAINS` edges from parent scope to child scope
|
|
30
|
+
* - `DEFINES` edges from scope to its `ownedDefs` members
|
|
31
|
+
* - `IMPORTS` edges from scope to `targetModuleScope` of each finalized
|
|
32
|
+
* `ImportEdge` that carries one
|
|
33
|
+
*
|
|
34
|
+
* Off by default — existing queries that don't know about `Scope` nodes
|
|
35
|
+
* continue to work, and the storage cost is opt-in.
|
|
36
|
+
*
|
|
37
|
+
* ## Source-of-truth: the caller def for a reference
|
|
38
|
+
*
|
|
39
|
+
* A `Reference` says "some code inside `fromScope` references `toDef`".
|
|
40
|
+
* The graph wants `(callerNodeId, calleeNodeId)`. We resolve the caller
|
|
41
|
+
* by walking up the scope tree from `fromScope` until we find a scope
|
|
42
|
+
* whose `ownedDefs` contains a Function-like def. If no such ancestor
|
|
43
|
+
* exists, the edge is attributed to the first def owned by the innermost
|
|
44
|
+
* ancestor scope, and if THAT produces nothing either the edge is
|
|
45
|
+
* skipped (with a count returned in `EmitStats.skippedNoCaller`).
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* Drain `referenceIndex.bySourceScope` into graph edges.
|
|
49
|
+
*
|
|
50
|
+
* The scope-tree flush is controlled separately by
|
|
51
|
+
* `INGESTION_EMIT_SCOPES` — callers can run `emitReferencesToGraph`
|
|
52
|
+
* without scope-node emission or layer the two calls as needed.
|
|
53
|
+
*/
|
|
54
|
+
export function emitReferencesToGraph(input) {
|
|
55
|
+
const { graph, scopes, referenceIndex } = input;
|
|
56
|
+
const sourceLabel = input.sourceLabel ?? 'scope-resolution';
|
|
57
|
+
let edgesEmitted = 0;
|
|
58
|
+
let skippedNoCaller = 0;
|
|
59
|
+
let skippedMissingTarget = 0;
|
|
60
|
+
for (const [fromScope, refs] of referenceIndex.bySourceScope) {
|
|
61
|
+
for (const ref of refs) {
|
|
62
|
+
const targetDef = scopes.defs.get(ref.toDef);
|
|
63
|
+
if (targetDef === undefined) {
|
|
64
|
+
skippedMissingTarget++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const callerId = resolveCallerNodeId(fromScope, scopes);
|
|
68
|
+
if (callerId === undefined) {
|
|
69
|
+
skippedNoCaller++;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
graph.addRelationship(buildRelationship(ref, callerId, targetDef, sourceLabel));
|
|
73
|
+
edgesEmitted++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const scopeStats = isScopeEmissionEnabled()
|
|
77
|
+
? emitScopeGraph({ graph, scopes })
|
|
78
|
+
: { scopeNodesEmitted: 0, scopeEdgesEmitted: 0 };
|
|
79
|
+
return { edgesEmitted, skippedNoCaller, skippedMissingTarget, ...scopeStats };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Emit `Scope` nodes + `CONTAINS`/`DEFINES`/`IMPORTS` edges representing
|
|
83
|
+
* the lexical scope tree itself. Skipped unless `INGESTION_EMIT_SCOPES=1`
|
|
84
|
+
* at the public entry point; exported here for tests that want to
|
|
85
|
+
* exercise the path directly.
|
|
86
|
+
*/
|
|
87
|
+
export function emitScopeGraph(input) {
|
|
88
|
+
const { graph, scopes } = input;
|
|
89
|
+
let scopeNodesEmitted = 0;
|
|
90
|
+
let scopeEdgesEmitted = 0;
|
|
91
|
+
for (const scope of scopes.scopeTree.byId.values()) {
|
|
92
|
+
graph.addNode({
|
|
93
|
+
id: scope.id,
|
|
94
|
+
label: 'CodeElement', // the generic bucket for non-symbol graph nodes
|
|
95
|
+
properties: {
|
|
96
|
+
name: scope.kind,
|
|
97
|
+
filePath: scope.filePath,
|
|
98
|
+
startLine: scope.range.startLine,
|
|
99
|
+
endLine: scope.range.endLine,
|
|
100
|
+
description: `Scope: ${scope.kind}`,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
scopeNodesEmitted++;
|
|
104
|
+
if (scope.parent !== null) {
|
|
105
|
+
graph.addRelationship({
|
|
106
|
+
id: `rel:contains:${scope.parent}->${scope.id}`,
|
|
107
|
+
sourceId: scope.parent,
|
|
108
|
+
targetId: scope.id,
|
|
109
|
+
type: 'CONTAINS',
|
|
110
|
+
confidence: 1,
|
|
111
|
+
reason: 'scope-tree parent/child',
|
|
112
|
+
});
|
|
113
|
+
scopeEdgesEmitted++;
|
|
114
|
+
}
|
|
115
|
+
for (const def of scope.ownedDefs) {
|
|
116
|
+
graph.addRelationship({
|
|
117
|
+
id: `rel:defines:${scope.id}->${def.nodeId}`,
|
|
118
|
+
sourceId: scope.id,
|
|
119
|
+
targetId: def.nodeId,
|
|
120
|
+
type: 'DEFINES',
|
|
121
|
+
confidence: 1,
|
|
122
|
+
reason: 'scope.ownedDefs',
|
|
123
|
+
});
|
|
124
|
+
scopeEdgesEmitted++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const [scopeId, edges] of scopes.imports) {
|
|
128
|
+
for (const edge of edges) {
|
|
129
|
+
if (edge.targetModuleScope === undefined)
|
|
130
|
+
continue;
|
|
131
|
+
graph.addRelationship({
|
|
132
|
+
id: `rel:imports:${scopeId}->${edge.targetModuleScope}:${edge.localName}`,
|
|
133
|
+
sourceId: scopeId,
|
|
134
|
+
targetId: edge.targetModuleScope,
|
|
135
|
+
type: 'IMPORTS',
|
|
136
|
+
confidence: edge.linkStatus === 'unresolved' ? 0.5 : 1,
|
|
137
|
+
reason: `import ${edge.kind} ${edge.localName}`,
|
|
138
|
+
});
|
|
139
|
+
scopeEdgesEmitted++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { scopeNodesEmitted, scopeEdgesEmitted };
|
|
143
|
+
}
|
|
144
|
+
// ─── Internal ───────────────────────────────────────────────────────────────
|
|
145
|
+
/** Accepted truthy values for `INGESTION_EMIT_SCOPES`. */
|
|
146
|
+
const TRUTHY = new Set(['true', '1', 'yes']);
|
|
147
|
+
function isScopeEmissionEnabled() {
|
|
148
|
+
const raw = process.env['INGESTION_EMIT_SCOPES'];
|
|
149
|
+
if (raw === undefined)
|
|
150
|
+
return false;
|
|
151
|
+
return TRUTHY.has(raw.trim().toLowerCase());
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Walk up from `startScope` looking for the first ancestor scope whose
|
|
155
|
+
* `ownedDefs` contains a Function-like def (Function / Method /
|
|
156
|
+
* Constructor). Fall back to the innermost ancestor's first `ownedDef`
|
|
157
|
+
* if none is found; return `undefined` if all ancestors have no defs.
|
|
158
|
+
*/
|
|
159
|
+
function resolveCallerNodeId(startScope, scopes) {
|
|
160
|
+
const tree = scopes.scopeTree;
|
|
161
|
+
let current = startScope;
|
|
162
|
+
const visited = new Set();
|
|
163
|
+
let firstOwnedFallback;
|
|
164
|
+
while (current !== null) {
|
|
165
|
+
if (visited.has(current))
|
|
166
|
+
break;
|
|
167
|
+
visited.add(current);
|
|
168
|
+
const scope = tree.getScope(current);
|
|
169
|
+
if (scope === undefined)
|
|
170
|
+
break;
|
|
171
|
+
// Prefer a Function-like owner.
|
|
172
|
+
const fnDef = scope.ownedDefs.find((d) => isFunctionLike(d.type));
|
|
173
|
+
if (fnDef !== undefined)
|
|
174
|
+
return fnDef.nodeId;
|
|
175
|
+
// Stash the first owned def we see as a conservative fallback.
|
|
176
|
+
if (firstOwnedFallback === undefined && scope.ownedDefs.length > 0) {
|
|
177
|
+
firstOwnedFallback = scope.ownedDefs[0].nodeId;
|
|
178
|
+
}
|
|
179
|
+
current = scope.parent;
|
|
180
|
+
}
|
|
181
|
+
return firstOwnedFallback;
|
|
182
|
+
}
|
|
183
|
+
function isFunctionLike(type) {
|
|
184
|
+
return type === 'Function' || type === 'Method' || type === 'Constructor';
|
|
185
|
+
}
|
|
186
|
+
function buildRelationship(ref, callerId, targetDef, sourceLabel) {
|
|
187
|
+
const type = mapKindToType(ref.kind);
|
|
188
|
+
const reason = `${sourceLabel}: ${ref.kind} | confidence ${ref.confidence.toFixed(3)}`;
|
|
189
|
+
// `step` encodes read/write discriminator for ACCESSES edges (1=read, 2=write).
|
|
190
|
+
// Other kinds omit `step`.
|
|
191
|
+
const step = ref.kind === 'read' ? 1 : ref.kind === 'write' ? 2 : undefined;
|
|
192
|
+
return {
|
|
193
|
+
id: `rel:${type}:${callerId}->${targetDef.nodeId}:${ref.atRange.startLine}:${ref.atRange.startCol}`,
|
|
194
|
+
sourceId: callerId,
|
|
195
|
+
targetId: targetDef.nodeId,
|
|
196
|
+
type,
|
|
197
|
+
confidence: ref.confidence,
|
|
198
|
+
reason,
|
|
199
|
+
evidence: ref.evidence.map(serializeEvidence),
|
|
200
|
+
...(step !== undefined ? { step } : {}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Map a `Reference.kind` to an existing `RelationshipType`. Read/write
|
|
205
|
+
* both fold into `ACCESSES`; `type-reference` + `import-use` both fold
|
|
206
|
+
* into `USES`. This keeps the graph schema additive — no new
|
|
207
|
+
* RelationshipType values are introduced by this module.
|
|
208
|
+
*/
|
|
209
|
+
function mapKindToType(kind) {
|
|
210
|
+
switch (kind) {
|
|
211
|
+
case 'call':
|
|
212
|
+
return 'CALLS';
|
|
213
|
+
case 'read':
|
|
214
|
+
case 'write':
|
|
215
|
+
return 'ACCESSES';
|
|
216
|
+
case 'inherits':
|
|
217
|
+
return 'INHERITS';
|
|
218
|
+
case 'type-reference':
|
|
219
|
+
case 'import-use':
|
|
220
|
+
return 'USES';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function serializeEvidence(e) {
|
|
224
|
+
return {
|
|
225
|
+
kind: e.kind,
|
|
226
|
+
weight: e.weight,
|
|
227
|
+
...(e.note !== undefined ? { note: e.note } : {}),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { isVerboseIngestionEnabled } from './utils/verbose.js';
|
|
2
|
+
import { DEFAULT_MAX_FILE_SIZE_BYTES, getMaxFileSizeBytes } from './utils/max-file-size.js';
|
|
2
3
|
import fs from 'fs/promises';
|
|
3
4
|
import path from 'path';
|
|
4
5
|
import { glob } from 'glob';
|
|
5
6
|
import { createIgnoreFilter } from '../../config/ignore-service.js';
|
|
6
7
|
const READ_CONCURRENCY = 32;
|
|
7
|
-
/** Skip files larger than 512KB — they're usually generated/vendored and crash tree-sitter */
|
|
8
|
-
const MAX_FILE_SIZE = 512 * 1024;
|
|
9
8
|
/**
|
|
10
9
|
* Phase 1: Scan repository — stat files to get paths + sizes, no content loaded.
|
|
11
10
|
* Memory: ~10MB for 100K files vs ~1GB+ with content.
|
|
12
11
|
*/
|
|
13
12
|
export const walkRepositoryPaths = async (repoPath, onProgress) => {
|
|
14
13
|
const ignoreFilter = await createIgnoreFilter(repoPath);
|
|
14
|
+
const maxFileSizeBytes = getMaxFileSizeBytes();
|
|
15
15
|
const filtered = await glob('**/*', {
|
|
16
16
|
cwd: repoPath,
|
|
17
17
|
nodir: true,
|
|
@@ -27,7 +27,7 @@ export const walkRepositoryPaths = async (repoPath, onProgress) => {
|
|
|
27
27
|
const results = await Promise.allSettled(batch.map(async (relativePath) => {
|
|
28
28
|
const fullPath = path.join(repoPath, relativePath);
|
|
29
29
|
const stat = await fs.stat(fullPath);
|
|
30
|
-
if (stat.size >
|
|
30
|
+
if (stat.size > maxFileSizeBytes) {
|
|
31
31
|
skippedLarge++;
|
|
32
32
|
skippedLargePaths.push(relativePath.replace(/\\/g, '/'));
|
|
33
33
|
return null;
|
|
@@ -46,7 +46,9 @@ export const walkRepositoryPaths = async (repoPath, onProgress) => {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
if (skippedLarge > 0) {
|
|
49
|
-
|
|
49
|
+
const isDefault = maxFileSizeBytes === DEFAULT_MAX_FILE_SIZE_BYTES;
|
|
50
|
+
const suffix = isDefault ? ', likely generated/vendored' : '';
|
|
51
|
+
console.warn(` Skipped ${skippedLarge} large files (>${maxFileSizeBytes / 1024}KB${suffix})`);
|
|
50
52
|
if (isVerboseIngestionEnabled()) {
|
|
51
53
|
for (const p of skippedLargePaths) {
|
|
52
54
|
console.warn(` - ${p}`);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `finalizeScopeModel` — turn a workspace's `ParsedFile[]` into a
|
|
3
|
+
* materialized `ScopeResolutionIndexes` (RFC §3.2 Phase 2; Ring 2 PKG #921).
|
|
4
|
+
*
|
|
5
|
+
* Thin integration glue, per issue #884's boundary: all algorithmic logic
|
|
6
|
+
* lives in `gitnexus-shared` (finalize algorithm #915, the four per-file
|
|
7
|
+
* indexes #913, the method-dispatch materialization #914, the scope tree
|
|
8
|
+
* #912). This file does three things only:
|
|
9
|
+
*
|
|
10
|
+
* 1. Map `ParsedFile[]` → `FinalizeInput` and call shared `finalize()`.
|
|
11
|
+
* 2. Build the four workspace-wide indexes from the union of per-file
|
|
12
|
+
* defs/scopes/modules/qualified-names.
|
|
13
|
+
* 3. Bundle the results into `ScopeResolutionIndexes` for
|
|
14
|
+
* `MutableSemanticModel.attachScopeIndexes(...)`.
|
|
15
|
+
*
|
|
16
|
+
* ## What this module is NOT responsible for
|
|
17
|
+
*
|
|
18
|
+
* - Invoking tree-sitter or running AST walks. That's the extractor (#919).
|
|
19
|
+
* - Per-language import-target resolution. Hooks are plumbed through
|
|
20
|
+
* but default to "unresolved" when no provider supplies them — the
|
|
21
|
+
* real adapters land with #922.
|
|
22
|
+
* - Populating `ReferenceIndex`. That's the resolution phase (#925).
|
|
23
|
+
* - Deciding which language uses registry-primary lookup. That's the
|
|
24
|
+
* flag reader (#924).
|
|
25
|
+
*
|
|
26
|
+
* ## Empty-input behavior
|
|
27
|
+
*
|
|
28
|
+
* When `parsedFiles` is empty (the common case today — no language has
|
|
29
|
+
* migrated yet), the orchestrator produces a valid but empty bundle: all
|
|
30
|
+
* indexes are zero-sized, the scope tree is empty, and
|
|
31
|
+
* `finalize.stats.totalFiles === 0`. This lets downstream consumers
|
|
32
|
+
* safely consult `model.scopes` without branching on presence.
|
|
33
|
+
*/
|
|
34
|
+
import type { FinalizeHooks, ParsedFile, WorkspaceIndex } from '../../_shared/index.js';
|
|
35
|
+
import type { ScopeResolutionIndexes } from './model/scope-resolution-indexes.js';
|
|
36
|
+
/**
|
|
37
|
+
* Options forwarded to the orchestrator. All fields optional so callers
|
|
38
|
+
* that don't yet have per-language hooks (today) get sensible defaults;
|
|
39
|
+
* #922 will populate `hooks.resolveImportTarget` + friends per language.
|
|
40
|
+
*/
|
|
41
|
+
export interface FinalizeOrchestratorOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Hooks forwarded to shared `finalize()`. Any omitted field gets a
|
|
44
|
+
* no-op default: unresolved targets, empty wildcard expansion, append
|
|
45
|
+
* merge for bindings.
|
|
46
|
+
*/
|
|
47
|
+
readonly hooks?: Partial<FinalizeHooks>;
|
|
48
|
+
/**
|
|
49
|
+
* Opaque workspace context forwarded to hooks. `undefined` today; Ring
|
|
50
|
+
* 2 PKG #922 populates this with a real cross-file index for the
|
|
51
|
+
* per-language resolvers.
|
|
52
|
+
*/
|
|
53
|
+
readonly workspaceIndex?: WorkspaceIndex;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Produce a fully materialized `ScopeResolutionIndexes` from the
|
|
57
|
+
* workspace's per-file artifacts.
|
|
58
|
+
*
|
|
59
|
+
* Pure function (given pure hooks). No I/O, no globals consulted. The
|
|
60
|
+
* pipeline calls this once per ingestion run and hands the result to
|
|
61
|
+
* `MutableSemanticModel.attachScopeIndexes`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function finalizeScopeModel(parsedFiles: readonly ParsedFile[], options?: FinalizeOrchestratorOptions): ScopeResolutionIndexes;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `finalizeScopeModel` — turn a workspace's `ParsedFile[]` into a
|
|
3
|
+
* materialized `ScopeResolutionIndexes` (RFC §3.2 Phase 2; Ring 2 PKG #921).
|
|
4
|
+
*
|
|
5
|
+
* Thin integration glue, per issue #884's boundary: all algorithmic logic
|
|
6
|
+
* lives in `gitnexus-shared` (finalize algorithm #915, the four per-file
|
|
7
|
+
* indexes #913, the method-dispatch materialization #914, the scope tree
|
|
8
|
+
* #912). This file does three things only:
|
|
9
|
+
*
|
|
10
|
+
* 1. Map `ParsedFile[]` → `FinalizeInput` and call shared `finalize()`.
|
|
11
|
+
* 2. Build the four workspace-wide indexes from the union of per-file
|
|
12
|
+
* defs/scopes/modules/qualified-names.
|
|
13
|
+
* 3. Bundle the results into `ScopeResolutionIndexes` for
|
|
14
|
+
* `MutableSemanticModel.attachScopeIndexes(...)`.
|
|
15
|
+
*
|
|
16
|
+
* ## What this module is NOT responsible for
|
|
17
|
+
*
|
|
18
|
+
* - Invoking tree-sitter or running AST walks. That's the extractor (#919).
|
|
19
|
+
* - Per-language import-target resolution. Hooks are plumbed through
|
|
20
|
+
* but default to "unresolved" when no provider supplies them — the
|
|
21
|
+
* real adapters land with #922.
|
|
22
|
+
* - Populating `ReferenceIndex`. That's the resolution phase (#925).
|
|
23
|
+
* - Deciding which language uses registry-primary lookup. That's the
|
|
24
|
+
* flag reader (#924).
|
|
25
|
+
*
|
|
26
|
+
* ## Empty-input behavior
|
|
27
|
+
*
|
|
28
|
+
* When `parsedFiles` is empty (the common case today — no language has
|
|
29
|
+
* migrated yet), the orchestrator produces a valid but empty bundle: all
|
|
30
|
+
* indexes are zero-sized, the scope tree is empty, and
|
|
31
|
+
* `finalize.stats.totalFiles === 0`. This lets downstream consumers
|
|
32
|
+
* safely consult `model.scopes` without branching on presence.
|
|
33
|
+
*/
|
|
34
|
+
import { buildDefIndex, buildMethodDispatchIndex, buildModuleScopeIndex, buildQualifiedNameIndex, buildScopeTree, finalize, } from '../../_shared/index.js';
|
|
35
|
+
/**
|
|
36
|
+
* Produce a fully materialized `ScopeResolutionIndexes` from the
|
|
37
|
+
* workspace's per-file artifacts.
|
|
38
|
+
*
|
|
39
|
+
* Pure function (given pure hooks). No I/O, no globals consulted. The
|
|
40
|
+
* pipeline calls this once per ingestion run and hands the result to
|
|
41
|
+
* `MutableSemanticModel.attachScopeIndexes`.
|
|
42
|
+
*/
|
|
43
|
+
export function finalizeScopeModel(parsedFiles, options = {}) {
|
|
44
|
+
const hooks = withDefaultHooks(options.hooks ?? {});
|
|
45
|
+
const workspaceIndex = options.workspaceIndex ?? undefined;
|
|
46
|
+
// ── Step 1: Shared finalize — runs SCC-aware cross-file link + binding
|
|
47
|
+
// materialization. Returns linked imports + merged bindings per module
|
|
48
|
+
// scope + SCC condensation + stats.
|
|
49
|
+
const finalizeInput = {
|
|
50
|
+
files: parsedFiles.map(toFinalizeFile),
|
|
51
|
+
workspaceIndex,
|
|
52
|
+
};
|
|
53
|
+
const finalizeOut = finalize(finalizeInput, hooks);
|
|
54
|
+
// ── Step 2: Workspace-wide indexes built from the per-file unions.
|
|
55
|
+
// These are pure aggregations — no algorithm beyond what the builders
|
|
56
|
+
// in gitnexus-shared already encapsulate (first-write-wins, qname
|
|
57
|
+
// collision buckets, etc.).
|
|
58
|
+
const allScopes = [];
|
|
59
|
+
const allDefs = [];
|
|
60
|
+
const moduleEntries = [];
|
|
61
|
+
const allReferenceSites = [];
|
|
62
|
+
for (const file of parsedFiles) {
|
|
63
|
+
for (const s of file.scopes)
|
|
64
|
+
allScopes.push(s);
|
|
65
|
+
for (const d of file.localDefs)
|
|
66
|
+
allDefs.push(d);
|
|
67
|
+
moduleEntries.push({ filePath: file.filePath, moduleScopeId: file.moduleScope });
|
|
68
|
+
}
|
|
69
|
+
// References kept out of the loop above to centralize list-init.
|
|
70
|
+
allReferenceSites.push(...collectReferenceSites(parsedFiles));
|
|
71
|
+
const scopeTree = buildScopeTree(allScopes);
|
|
72
|
+
const defs = buildDefIndex(allDefs);
|
|
73
|
+
const qualifiedNames = buildQualifiedNameIndex(allDefs);
|
|
74
|
+
const moduleScopes = buildModuleScopeIndex(moduleEntries);
|
|
75
|
+
// ── Step 3: MethodDispatchIndex. Today we lack per-language MRO
|
|
76
|
+
// strategies wired into this orchestrator (that belongs with the
|
|
77
|
+
// HeritageMap bridge, a separate piece of work). Ship an EMPTY index
|
|
78
|
+
// so the bundle shape is consistent; the callbacks return `[]` for
|
|
79
|
+
// every owner and `implementsOf` returns `[]`. Populating this
|
|
80
|
+
// properly is tracked alongside the per-language provider hooks.
|
|
81
|
+
const methodDispatch = buildMethodDispatchIndex({
|
|
82
|
+
owners: [], // empty → no MRO entries; `mroFor(x)` returns the frozen empty array
|
|
83
|
+
computeMro: () => [],
|
|
84
|
+
implementsOf: () => [],
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
scopeTree,
|
|
88
|
+
defs,
|
|
89
|
+
qualifiedNames,
|
|
90
|
+
moduleScopes,
|
|
91
|
+
methodDispatch,
|
|
92
|
+
imports: finalizeOut.imports,
|
|
93
|
+
bindings: finalizeOut.bindings,
|
|
94
|
+
referenceSites: Object.freeze([...allReferenceSites]),
|
|
95
|
+
sccs: finalizeOut.sccs,
|
|
96
|
+
stats: finalizeOut.stats,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// ─── Internal ───────────────────────────────────────────────────────────────
|
|
100
|
+
/** Shape-reduce a `ParsedFile` to the narrower `FinalizeFile` the shared
|
|
101
|
+
* algorithm reads. The subset is stable — `FinalizeFile` is a proper
|
|
102
|
+
* subset of `ParsedFile`. */
|
|
103
|
+
function toFinalizeFile(file) {
|
|
104
|
+
return {
|
|
105
|
+
filePath: file.filePath,
|
|
106
|
+
moduleScope: file.moduleScope,
|
|
107
|
+
parsedImports: file.parsedImports,
|
|
108
|
+
localDefs: file.localDefs,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Flatten every file's reference sites into one list. Order reflects
|
|
112
|
+
* input-file order, then capture order inside each file. Deterministic. */
|
|
113
|
+
function collectReferenceSites(parsedFiles) {
|
|
114
|
+
const out = [];
|
|
115
|
+
for (const file of parsedFiles) {
|
|
116
|
+
for (const site of file.referenceSites)
|
|
117
|
+
out.push(site);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fill in no-op defaults for any omitted hook. Keeps `finalize()`
|
|
123
|
+
* behavior well-defined for the zero-provider case today:
|
|
124
|
+
*
|
|
125
|
+
* - `resolveImportTarget: () => null` — every import edge ends up
|
|
126
|
+
* `linkStatus: 'unresolved'` (or dynamic-unresolved pass-through).
|
|
127
|
+
* - `expandsWildcardTo: () => []` — wildcards don't materialize.
|
|
128
|
+
* - `mergeBindings: (existing, incoming) => [...existing, ...incoming]`
|
|
129
|
+
* — append without precedence; providers override to implement local-
|
|
130
|
+
* shadows-import and similar rules.
|
|
131
|
+
*/
|
|
132
|
+
function withDefaultHooks(partial) {
|
|
133
|
+
return {
|
|
134
|
+
resolveImportTarget: partial.resolveImportTarget ?? (() => null),
|
|
135
|
+
expandsWildcardTo: partial.expandsWildcardTo ?? (() => []),
|
|
136
|
+
mergeBindings: partial.mergeBindings ??
|
|
137
|
+
((existing, incoming) => [...existing, ...incoming]),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -21,10 +21,14 @@ import { SupportedLanguages } from '../../_shared/index.js';
|
|
|
21
21
|
*/
|
|
22
22
|
export function detectFrameworkFromPath(filePath) {
|
|
23
23
|
// Normalize path separators and ensure leading slash for consistent matching
|
|
24
|
-
|
|
24
|
+
const originalPath = filePath.replace(/\\/g, '/');
|
|
25
|
+
let p = originalPath.toLowerCase();
|
|
25
26
|
if (!p.startsWith('/')) {
|
|
26
27
|
p = '/' + p; // Add leading slash so patterns like '/app/' match 'app/...'
|
|
27
28
|
}
|
|
29
|
+
const originalPathWithLeadingSlash = originalPath.startsWith('/')
|
|
30
|
+
? originalPath
|
|
31
|
+
: `/${originalPath}`;
|
|
28
32
|
// ========== JAVASCRIPT / TYPESCRIPT FRAMEWORKS ==========
|
|
29
33
|
// Next.js - Pages Router (high confidence)
|
|
30
34
|
if (p.includes('/pages/') && !p.includes('/_') && !p.includes('/api/')) {
|
|
@@ -91,7 +95,7 @@ export function detectFrameworkFromPath(filePath) {
|
|
|
91
95
|
if ((p.includes('/components/') || p.includes('/views/')) &&
|
|
92
96
|
(p.endsWith('.tsx') || p.endsWith('.jsx'))) {
|
|
93
97
|
// Only boost if PascalCase filename (likely a component, not util)
|
|
94
|
-
const fileName =
|
|
98
|
+
const fileName = originalPathWithLeadingSlash.split('/').pop() || '';
|
|
95
99
|
if (/^[A-Z]/.test(fileName)) {
|
|
96
100
|
return { framework: 'react', entryPointMultiplier: 1.5, reason: 'react-component' };
|
|
97
101
|
}
|
|
@@ -9,6 +9,7 @@ import { getTreeSitterBufferSize } from './constants.js';
|
|
|
9
9
|
import { loadImportConfigs } from './language-config.js';
|
|
10
10
|
import { buildSuffixIndex } from './import-resolvers/utils.js';
|
|
11
11
|
import { isDev } from './utils/env.js';
|
|
12
|
+
import { isRegistryPrimary } from './registry-primary-flag.js';
|
|
12
13
|
/** Group files by provider (only those with implicit import wiring), then call each wirer
|
|
13
14
|
* with its own language's files. O(n) over files, O(1) per provider lookup. */
|
|
14
15
|
function wireImplicitImports(files, importMap, addImportEdge, projectConfig) {
|
|
@@ -64,6 +65,9 @@ export function preprocessImportPath(sourceText, importNode, provider) {
|
|
|
64
65
|
function createImportEdgeHelpers(graph, importMap) {
|
|
65
66
|
let totalImportsResolved = 0;
|
|
66
67
|
const addImportGraphEdge = (filePath, resolvedPath) => {
|
|
68
|
+
const language = getLanguageFromFilename(filePath);
|
|
69
|
+
if (language !== null && isRegistryPrimary(language))
|
|
70
|
+
return;
|
|
67
71
|
const sourceId = generateId('File', filePath);
|
|
68
72
|
const targetId = generateId('File', resolvedPath);
|
|
69
73
|
const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
|
|
@@ -45,12 +45,15 @@ export function resolvePythonImportInternal(currentFile, importPath, allFiles) {
|
|
|
45
45
|
return null;
|
|
46
46
|
// Normalize for Windows backslashes
|
|
47
47
|
const importerDir = currentFile.replace(/\\/g, '/').split('/').slice(0, -1).join('/');
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
// Proximity check — only applies when the importer lives in a subdirectory.
|
|
49
|
+
// Root-level importers (importerDir === '') skip straight to the ancestor
|
|
50
|
+
// walk below, which handles the root case correctly (prefix becomes '').
|
|
51
|
+
if (importerDir) {
|
|
52
|
+
if (allFiles.has(`${importerDir}/${pathLike}/__init__.py`))
|
|
53
|
+
return `${importerDir}/${pathLike}/__init__.py`;
|
|
54
|
+
if (allFiles.has(`${importerDir}/${pathLike}.py`))
|
|
55
|
+
return `${importerDir}/${pathLike}.py`;
|
|
56
|
+
}
|
|
54
57
|
// Ancestor directory walk — Python resolves bare imports against sys.path entries,
|
|
55
58
|
// which typically includes the project root and package directories. Walk up from the
|
|
56
59
|
// importer's directory to find the module in an ancestor, preferring the closest match.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge between CLI-package per-language `ImportResolverFn`s and the
|
|
3
|
+
* shared `FinalizeHooks.resolveImportTarget` contract
|
|
4
|
+
* (RFC §5.2; Ring 2 PKG #922).
|
|
5
|
+
*
|
|
6
|
+
* The shared finalize algorithm (#915) asks one question:
|
|
7
|
+
*
|
|
8
|
+
* resolveImportTarget(targetRaw, fromFile, workspaceIndex): string | null
|
|
9
|
+
*
|
|
10
|
+
* The CLI already has 16 language-specific resolvers satisfying a
|
|
11
|
+
* richer signature:
|
|
12
|
+
*
|
|
13
|
+
* ImportResolverFn(rawImportPath, filePath, resolveCtx): ImportResult
|
|
14
|
+
*
|
|
15
|
+
* This module builds a dispatch adapter — one FinalizeHook implementation
|
|
16
|
+
* that looks up the file's language from its path and delegates to the
|
|
17
|
+
* right per-language resolver. Callers package per-language resolvers +
|
|
18
|
+
* a shared `ResolveCtx` into an opaque `ImportTargetWorkspace` and pass
|
|
19
|
+
* it as `workspaceIndex` to `finalizeScopeModel`.
|
|
20
|
+
*
|
|
21
|
+
* ## What's deliberately NOT here
|
|
22
|
+
*
|
|
23
|
+
* - **Re-implementation of any per-language resolver.** We wrap the
|
|
24
|
+
* existing `importResolver` field on each `LanguageProvider` — the
|
|
25
|
+
* same code path the legacy DAG uses today.
|
|
26
|
+
* - **Dynamic-import handling.** The shared finalize algorithm short-
|
|
27
|
+
* circuits `ParsedImport { kind: 'dynamic-unresolved' }` before
|
|
28
|
+
* calling `resolveImportTarget`, so the adapter never sees those.
|
|
29
|
+
* - **`importPathPreprocessor`.** Preprocessing belongs inside the
|
|
30
|
+
* provider's `interpretImport` hook (which writes the final
|
|
31
|
+
* `ParsedImport.targetRaw`). By the time finalize passes a
|
|
32
|
+
* `targetRaw` to this adapter, it is the string the provider wants
|
|
33
|
+
* resolved verbatim.
|
|
34
|
+
*/
|
|
35
|
+
import { type SupportedLanguages, type WorkspaceIndex } from '../../_shared/index.js';
|
|
36
|
+
import type { ImportResolverFn, ResolveCtx } from './import-resolvers/types.js';
|
|
37
|
+
import type { LanguageProvider } from './language-provider.js';
|
|
38
|
+
/** A single language's resolver bundled with the context it needs. */
|
|
39
|
+
export interface LanguageResolverEntry {
|
|
40
|
+
readonly resolver: ImportResolverFn;
|
|
41
|
+
readonly ctx: ResolveCtx;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The opaque `workspaceIndex` shape recognized by
|
|
45
|
+
* `resolveImportTargetAcrossLanguages`. Built once per ingestion run via
|
|
46
|
+
* `buildImportTargetWorkspace`, threaded through `finalizeScopeModel`.
|
|
47
|
+
*/
|
|
48
|
+
export interface ImportTargetWorkspace {
|
|
49
|
+
readonly perLanguage: ReadonlyMap<SupportedLanguages, LanguageResolverEntry>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build the workspace index from a map of language → provider. Providers
|
|
53
|
+
* whose `importResolver` is absent are silently skipped (no language will
|
|
54
|
+
* ever hit that branch at dispatch time).
|
|
55
|
+
*
|
|
56
|
+
* The `resolveCtx` is shared across all languages. Callers assemble it
|
|
57
|
+
* once per run (the existing pipeline already does this for the legacy
|
|
58
|
+
* DAG) and hand it to both the legacy resolution path and this factory.
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildImportTargetWorkspace(providers: ReadonlyMap<SupportedLanguages, LanguageProvider>, resolveCtx: ResolveCtx): ImportTargetWorkspace;
|
|
61
|
+
/**
|
|
62
|
+
* The FinalizeHooks-compatible implementation. Dispatches on `fromFile`'s
|
|
63
|
+
* extension → per-language resolver. Returns the first resolved file,
|
|
64
|
+
* or `null` if the resolver returns `null` or doesn't know about the
|
|
65
|
+
* language.
|
|
66
|
+
*
|
|
67
|
+
* Picks the first entry of `files[]` for both `'files'` and `'package'`
|
|
68
|
+
* result kinds — the legacy pipeline uses the whole array, but the
|
|
69
|
+
* shared `finalize()` hook contract is single-file. If the workspace
|
|
70
|
+
* later needs richer semantics (split-target packages), this is the
|
|
71
|
+
* single site to extend.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveImportTargetAcrossLanguages(targetRaw: string, fromFile: string, workspaceIndex: WorkspaceIndex): string | null;
|