gitnexus 1.5.3 → 1.6.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 +10 -0
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +1 -0
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +3 -1
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/mro-strategy.d.ts +19 -0
- package/dist/_shared/mro-strategy.d.ts.map +1 -0
- package/dist/_shared/mro-strategy.js +2 -0
- package/dist/_shared/mro-strategy.js.map +1 -0
- package/dist/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +28 -4
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +30 -4
- package/dist/cli/group.d.ts +2 -0
- package/dist/cli/group.js +233 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/serve.js +4 -1
- package/dist/cli/setup.js +34 -3
- package/dist/config/ignore-service.js +8 -3
- package/dist/core/augmentation/engine.js +1 -1
- package/dist/core/git-staleness.d.ts +13 -0
- package/dist/core/git-staleness.js +29 -0
- package/dist/core/group/bridge-db.d.ts +82 -0
- package/dist/core/group/bridge-db.js +460 -0
- package/dist/core/group/bridge-schema.d.ts +27 -0
- package/dist/core/group/bridge-schema.js +55 -0
- package/dist/core/group/config-parser.d.ts +3 -0
- package/dist/core/group/config-parser.js +83 -0
- package/dist/core/group/contract-extractor.d.ts +7 -0
- package/dist/core/group/contract-extractor.js +1 -0
- package/dist/core/group/extractors/fs-utils.d.ts +10 -0
- package/dist/core/group/extractors/fs-utils.js +24 -0
- package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
- package/dist/core/group/extractors/grpc-extractor.js +386 -0
- package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
- package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
- package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
- package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
- package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
- package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
- package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
- package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
- package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/go.js +215 -0
- package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
- package/dist/core/group/extractors/http-patterns/index.js +44 -0
- package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/java.js +253 -0
- package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/http-patterns/node.js +354 -0
- package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/php.js +70 -0
- package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/python.js +133 -0
- package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
- package/dist/core/group/extractors/http-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
- package/dist/core/group/extractors/http-route-extractor.js +391 -0
- package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
- package/dist/core/group/extractors/manifest-extractor.js +235 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
- package/dist/core/group/extractors/topic-extractor.js +97 -0
- package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/go.js +120 -0
- package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
- package/dist/core/group/extractors/topic-patterns/index.js +38 -0
- package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/java.js +80 -0
- package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/topic-patterns/node.js +155 -0
- package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/python.js +116 -0
- package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
- package/dist/core/group/extractors/topic-patterns/types.js +10 -0
- package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
- package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
- package/dist/core/group/matching.d.ts +13 -0
- package/dist/core/group/matching.js +198 -0
- package/dist/core/group/normalization.d.ts +3 -0
- package/dist/core/group/normalization.js +115 -0
- package/dist/core/group/service-boundary-detector.d.ts +8 -0
- package/dist/core/group/service-boundary-detector.js +155 -0
- package/dist/core/group/service.d.ts +46 -0
- package/dist/core/group/service.js +160 -0
- package/dist/core/group/storage.d.ts +9 -0
- package/dist/core/group/storage.js +91 -0
- package/dist/core/group/sync.d.ts +21 -0
- package/dist/core/group/sync.js +148 -0
- package/dist/core/group/types.d.ts +130 -0
- package/dist/core/group/types.js +1 -0
- package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
- package/dist/core/ingestion/binding-accumulator.js +336 -0
- package/dist/core/ingestion/call-processor.d.ts +155 -24
- package/dist/core/ingestion/call-processor.js +1129 -247
- package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/generic.js +135 -0
- package/dist/core/ingestion/class-types.d.ts +34 -0
- package/dist/core/ingestion/class-types.js +1 -0
- package/dist/core/ingestion/cobol-processor.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
- package/dist/core/ingestion/entry-point-scoring.js +1 -0
- package/dist/core/ingestion/field-types.d.ts +2 -2
- package/dist/core/ingestion/filesystem-walker.js +8 -0
- package/dist/core/ingestion/framework-detection.d.ts +1 -0
- package/dist/core/ingestion/framework-detection.js +1 -0
- package/dist/core/ingestion/heritage-processor.d.ts +8 -15
- package/dist/core/ingestion/heritage-processor.js +15 -28
- package/dist/core/ingestion/import-processor.d.ts +1 -11
- package/dist/core/ingestion/import-processor.js +1 -13
- package/dist/core/ingestion/import-resolvers/utils.js +1 -0
- package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/vue.js +9 -0
- package/dist/core/ingestion/language-config.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +14 -3
- package/dist/core/ingestion/languages/c-cpp.js +168 -1
- package/dist/core/ingestion/languages/csharp.js +20 -0
- package/dist/core/ingestion/languages/dart.js +26 -4
- package/dist/core/ingestion/languages/go.js +22 -0
- package/dist/core/ingestion/languages/index.d.ts +1 -0
- package/dist/core/ingestion/languages/index.js +2 -0
- package/dist/core/ingestion/languages/java.js +17 -0
- package/dist/core/ingestion/languages/kotlin.js +24 -1
- package/dist/core/ingestion/languages/php.js +23 -11
- package/dist/core/ingestion/languages/python.js +9 -0
- package/dist/core/ingestion/languages/ruby.js +43 -0
- package/dist/core/ingestion/languages/rust.js +38 -0
- package/dist/core/ingestion/languages/swift.js +31 -0
- package/dist/core/ingestion/languages/typescript.d.ts +1 -0
- package/dist/core/ingestion/languages/typescript.js +52 -3
- package/dist/core/ingestion/languages/vue.d.ts +13 -0
- package/dist/core/ingestion/languages/vue.js +81 -0
- package/dist/core/ingestion/markdown-processor.d.ts +1 -1
- package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
- package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
- package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
- package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
- package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
- package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
- package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
- package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
- package/dist/core/ingestion/method-extractors/generic.js +84 -17
- package/dist/core/ingestion/method-types.d.ts +29 -0
- package/dist/core/ingestion/model/field-registry.d.ts +18 -0
- package/dist/core/ingestion/model/field-registry.js +22 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
- package/dist/core/ingestion/model/heritage-map.js +159 -0
- package/dist/core/ingestion/model/index.d.ts +20 -0
- package/dist/core/ingestion/model/index.js +41 -0
- package/dist/core/ingestion/model/method-registry.d.ts +62 -0
- package/dist/core/ingestion/model/method-registry.js +130 -0
- package/dist/core/ingestion/model/registration-table.d.ts +139 -0
- package/dist/core/ingestion/model/registration-table.js +224 -0
- package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
- package/dist/core/ingestion/model/resolution-context.js +337 -0
- package/dist/core/ingestion/model/resolve.d.ts +56 -0
- package/dist/core/ingestion/model/resolve.js +297 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
- package/dist/core/ingestion/model/semantic-model.js +120 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
- package/dist/core/ingestion/model/symbol-table.js +206 -0
- package/dist/core/ingestion/model/type-registry.d.ts +39 -0
- package/dist/core/ingestion/model/type-registry.js +62 -0
- package/dist/core/ingestion/mro-processor.d.ts +5 -4
- package/dist/core/ingestion/mro-processor.js +311 -107
- package/dist/core/ingestion/parsing-processor.d.ts +5 -4
- package/dist/core/ingestion/parsing-processor.js +224 -87
- package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
- package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/index.js +22 -0
- package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
- package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
- package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
- package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
- package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
- package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
- package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
- package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
- package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
- package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
- package/dist/core/ingestion/pipeline-phases/types.js +37 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
- package/dist/core/ingestion/pipeline.d.ts +18 -10
- package/dist/core/ingestion/pipeline.js +66 -1410
- package/dist/core/ingestion/process-processor.js +1 -1
- package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
- package/dist/core/ingestion/tree-sitter-queries.js +90 -0
- package/dist/core/ingestion/type-env.d.ts +15 -2
- package/dist/core/ingestion/type-env.js +163 -102
- package/dist/core/ingestion/type-extractors/csharp.js +17 -0
- package/dist/core/ingestion/type-extractors/jvm.js +11 -0
- package/dist/core/ingestion/type-extractors/php.js +0 -55
- package/dist/core/ingestion/type-extractors/ruby.js +0 -32
- package/dist/core/ingestion/type-extractors/swift.js +13 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
- package/dist/core/ingestion/type-extractors/typescript.js +66 -69
- package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
- package/dist/core/ingestion/utils/ast-helpers.js +157 -573
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +10 -0
- package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
- package/dist/core/ingestion/utils/graph-sort.js +100 -0
- package/dist/core/ingestion/utils/method-props.d.ts +32 -0
- package/dist/core/ingestion/utils/method-props.js +147 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
- package/dist/core/ingestion/workers/parse-worker.js +469 -200
- package/dist/core/lbug/lbug-adapter.d.ts +6 -0
- package/dist/core/lbug/lbug-adapter.js +134 -27
- package/dist/core/lbug/pool-adapter.d.ts +76 -0
- package/dist/core/lbug/pool-adapter.js +522 -0
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +1 -1
- package/dist/core/search/bm25-index.js +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -0
- package/dist/core/wiki/graph-queries.js +1 -1
- package/dist/mcp/core/embedder.js +6 -5
- package/dist/mcp/core/lbug-adapter.d.ts +3 -63
- package/dist/mcp/core/lbug-adapter.js +3 -484
- package/dist/mcp/local/local-backend.d.ts +31 -2
- package/dist/mcp/local/local-backend.js +255 -46
- package/dist/mcp/resources.js +5 -4
- package/dist/mcp/staleness.d.ts +3 -13
- package/dist/mcp/staleness.js +2 -31
- package/dist/mcp/tools.js +80 -4
- package/dist/server/analyze-job.d.ts +2 -0
- package/dist/server/analyze-job.js +4 -0
- package/dist/server/api.d.ts +20 -1
- package/dist/server/api.js +306 -71
- package/dist/server/git-clone.d.ts +2 -1
- package/dist/server/git-clone.js +98 -5
- package/dist/storage/git.d.ts +13 -0
- package/dist/storage/git.js +25 -0
- package/dist/storage/repo-manager.js +1 -1
- package/package.json +9 -3
- package/scripts/patch-tree-sitter-swift.cjs +78 -0
- package/vendor/tree-sitter-proto/binding.gyp +30 -0
- package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-proto/package.json +18 -0
- package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
- package/vendor/tree-sitter-proto/src/parser.c +10149 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
- package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
- package/dist/core/ingestion/named-binding-processor.js +0 -42
- package/dist/core/ingestion/resolution-context.d.ts +0 -58
- package/dist/core/ingestion/resolution-context.js +0 -135
- package/dist/core/ingestion/symbol-table.d.ts +0 -79
- package/dist/core/ingestion/symbol-table.js +0 -115
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-file binding propagation — extracted from pipeline.ts.
|
|
3
|
+
*
|
|
4
|
+
* Seeds downstream files with resolved type bindings from upstream exports.
|
|
5
|
+
* Files are processed in topological import order so upstream bindings
|
|
6
|
+
* are available when downstream files are re-resolved.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { processCalls, buildImportedReturnTypes, buildImportedRawReturnTypes, } from '../call-processor.js';
|
|
11
|
+
import { createASTCache } from '../ast-cache.js';
|
|
12
|
+
import { getLanguageFromFilename } from '../../../_shared/index.js';
|
|
13
|
+
import { readFileContents } from '../filesystem-walker.js';
|
|
14
|
+
import { isLanguageAvailable } from '../../tree-sitter/parser-loader.js';
|
|
15
|
+
import { topologicalLevelSort } from '../utils/graph-sort.js';
|
|
16
|
+
import { isDev } from '../utils/env.js';
|
|
17
|
+
/** Max AST trees to keep in LRU cache for cross-file binding propagation. */
|
|
18
|
+
const AST_CACHE_CAP = 50;
|
|
19
|
+
/** Minimum percentage of files that must benefit from cross-file seeding. */
|
|
20
|
+
const CROSS_FILE_SKIP_THRESHOLD = 0.03;
|
|
21
|
+
/** Hard cap on files re-processed during cross-file propagation. */
|
|
22
|
+
const MAX_CROSS_FILE_REPROCESS = 2000;
|
|
23
|
+
/**
|
|
24
|
+
* Cross-file binding propagation.
|
|
25
|
+
* Returns the number of files re-processed.
|
|
26
|
+
*/
|
|
27
|
+
export async function runCrossFileBindingPropagation(graph, ctx, parseExportedTypeMap, allPathSet, totalFiles, repoPath, pipelineStart, onProgress) {
|
|
28
|
+
if (parseExportedTypeMap.size === 0 || ctx.namedImportMap.size === 0)
|
|
29
|
+
return 0;
|
|
30
|
+
// Build a local mutable working copy. Per-file re-resolution below mutates
|
|
31
|
+
// this map (each `processCalls` writes that file's exports back into it so
|
|
32
|
+
// later iterations in the same level/loop can resolve transitive bindings).
|
|
33
|
+
// Owning a local copy here keeps `ParseOutput.exportedTypeMap` truly
|
|
34
|
+
// read-only at the phase boundary — no cast, no shared-mutable handoff.
|
|
35
|
+
const exportedTypeMap = new Map();
|
|
36
|
+
for (const [fp, exports] of parseExportedTypeMap) {
|
|
37
|
+
exportedTypeMap.set(fp, new Map(exports));
|
|
38
|
+
}
|
|
39
|
+
const { levels, cycleCount } = topologicalLevelSort(ctx.importMap);
|
|
40
|
+
if (isDev && cycleCount > 0) {
|
|
41
|
+
console.log(`🔄 ${cycleCount} files in import cycles (processed last in undefined order)`);
|
|
42
|
+
}
|
|
43
|
+
let filesWithGaps = 0;
|
|
44
|
+
const gapThreshold = Math.max(1, Math.ceil(totalFiles * CROSS_FILE_SKIP_THRESHOLD));
|
|
45
|
+
outer: for (const level of levels) {
|
|
46
|
+
for (const filePath of level) {
|
|
47
|
+
const imports = ctx.namedImportMap.get(filePath);
|
|
48
|
+
if (!imports)
|
|
49
|
+
continue;
|
|
50
|
+
for (const [, binding] of imports) {
|
|
51
|
+
const upstream = exportedTypeMap.get(binding.sourcePath);
|
|
52
|
+
if (upstream?.has(binding.exportedName)) {
|
|
53
|
+
filesWithGaps++;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
const def = ctx.model.symbols.lookupExactFull(binding.sourcePath, binding.exportedName);
|
|
57
|
+
if (def?.returnType) {
|
|
58
|
+
filesWithGaps++;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (filesWithGaps >= gapThreshold)
|
|
63
|
+
break outer;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const gapRatio = totalFiles > 0 ? filesWithGaps / totalFiles : 0;
|
|
67
|
+
if (gapRatio < CROSS_FILE_SKIP_THRESHOLD && filesWithGaps < gapThreshold) {
|
|
68
|
+
if (isDev) {
|
|
69
|
+
console.log(`⏭️ Cross-file re-resolution skipped (${filesWithGaps}/${totalFiles} files, ${(gapRatio * 100).toFixed(1)}% < ${CROSS_FILE_SKIP_THRESHOLD * 100}% threshold)`);
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
// Intentionally reports `phase: 'parsing'` rather than a separate
|
|
74
|
+
// 'crossFile' phase: cross-file re-resolution is logically a continuation of
|
|
75
|
+
// the parsing/resolution work and is bucketed under "parsing" in any
|
|
76
|
+
// telemetry that groups events by phase name. Kept consistent with the
|
|
77
|
+
// upstream `parse` phase's progress events so the UI shows one continuous
|
|
78
|
+
// progress segment instead of a phase flicker. If a future change splits
|
|
79
|
+
// this out into its own phase, also rename `parse-impl.ts` per-chunk
|
|
80
|
+
// progress events accordingly.
|
|
81
|
+
onProgress({
|
|
82
|
+
phase: 'parsing',
|
|
83
|
+
percent: 82,
|
|
84
|
+
message: `Cross-file type propagation (${filesWithGaps}+ files)...`,
|
|
85
|
+
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
|
|
86
|
+
});
|
|
87
|
+
let crossFileResolved = 0;
|
|
88
|
+
const crossFileStart = Date.now();
|
|
89
|
+
const astCache = createASTCache(AST_CACHE_CAP);
|
|
90
|
+
for (const level of levels) {
|
|
91
|
+
const levelCandidates = [];
|
|
92
|
+
for (const filePath of level) {
|
|
93
|
+
if (crossFileResolved + levelCandidates.length >= MAX_CROSS_FILE_REPROCESS)
|
|
94
|
+
break;
|
|
95
|
+
const imports = ctx.namedImportMap.get(filePath);
|
|
96
|
+
if (!imports)
|
|
97
|
+
continue;
|
|
98
|
+
const seeded = new Map();
|
|
99
|
+
for (const [localName, binding] of imports) {
|
|
100
|
+
const upstream = exportedTypeMap.get(binding.sourcePath);
|
|
101
|
+
if (upstream) {
|
|
102
|
+
const type = upstream.get(binding.exportedName);
|
|
103
|
+
if (type)
|
|
104
|
+
seeded.set(localName, type);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const importedReturns = buildImportedReturnTypes(filePath, ctx.namedImportMap, ctx.model.symbols);
|
|
108
|
+
const importedRawReturns = buildImportedRawReturnTypes(filePath, ctx.namedImportMap, ctx.model.symbols);
|
|
109
|
+
if (seeded.size === 0 && importedReturns.size === 0)
|
|
110
|
+
continue;
|
|
111
|
+
if (!allPathSet.has(filePath))
|
|
112
|
+
continue;
|
|
113
|
+
const lang = getLanguageFromFilename(filePath);
|
|
114
|
+
if (!lang || !isLanguageAvailable(lang))
|
|
115
|
+
continue;
|
|
116
|
+
levelCandidates.push({ filePath, seeded, importedReturns, importedRawReturns });
|
|
117
|
+
}
|
|
118
|
+
if (levelCandidates.length === 0)
|
|
119
|
+
continue;
|
|
120
|
+
const levelPaths = levelCandidates.map((c) => c.filePath);
|
|
121
|
+
const contentMap = await readFileContents(repoPath, levelPaths);
|
|
122
|
+
for (const { filePath, seeded, importedReturns, importedRawReturns } of levelCandidates) {
|
|
123
|
+
const content = contentMap.get(filePath);
|
|
124
|
+
if (!content)
|
|
125
|
+
continue;
|
|
126
|
+
const reFile = [{ path: filePath, content }];
|
|
127
|
+
const bindings = new Map();
|
|
128
|
+
if (seeded.size > 0)
|
|
129
|
+
bindings.set(filePath, seeded);
|
|
130
|
+
const importedReturnTypesMap = new Map();
|
|
131
|
+
if (importedReturns.size > 0) {
|
|
132
|
+
importedReturnTypesMap.set(filePath, importedReturns);
|
|
133
|
+
}
|
|
134
|
+
const importedRawReturnTypesMap = new Map();
|
|
135
|
+
if (importedRawReturns.size > 0) {
|
|
136
|
+
importedRawReturnTypesMap.set(filePath, importedRawReturns);
|
|
137
|
+
}
|
|
138
|
+
await processCalls(graph, reFile, astCache, ctx, undefined, exportedTypeMap, bindings.size > 0 ? bindings : undefined, importedReturnTypesMap.size > 0 ? importedReturnTypesMap : undefined, importedRawReturnTypesMap.size > 0 ? importedRawReturnTypesMap : undefined);
|
|
139
|
+
crossFileResolved++;
|
|
140
|
+
}
|
|
141
|
+
if (crossFileResolved >= MAX_CROSS_FILE_REPROCESS) {
|
|
142
|
+
if (isDev)
|
|
143
|
+
console.log(`⚠️ Cross-file re-resolution capped at ${MAX_CROSS_FILE_REPROCESS} files`);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
astCache.clear();
|
|
148
|
+
if (isDev) {
|
|
149
|
+
const elapsed = Date.now() - crossFileStart;
|
|
150
|
+
const totalElapsed = Date.now() - pipelineStart;
|
|
151
|
+
const reResolutionPct = totalElapsed > 0 ? ((elapsed / totalElapsed) * 100).toFixed(1) : '0';
|
|
152
|
+
console.log(`🔗 Cross-file re-resolution: ${crossFileResolved} candidates re-processed` +
|
|
153
|
+
` in ${elapsed}ms (${reResolutionPct}% of total ingestion time so far)`);
|
|
154
|
+
}
|
|
155
|
+
return crossFileResolved;
|
|
156
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: crossFile
|
|
3
|
+
*
|
|
4
|
+
* Cross-file binding propagation: seeds downstream files with resolved
|
|
5
|
+
* type bindings from upstream exports. Files are processed in topological
|
|
6
|
+
* import order so upstream bindings are available when downstream files
|
|
7
|
+
* are re-resolved.
|
|
8
|
+
*
|
|
9
|
+
* @deps parse, routes, tools, orm (waits for all post-parse phases)
|
|
10
|
+
* @reads exportedTypeMap, allPaths, totalFiles
|
|
11
|
+
* @writes graph (refined CALLS edges via re-resolution)
|
|
12
|
+
*
|
|
13
|
+
* **Accumulator ownership / residual risk.** This phase is the sole
|
|
14
|
+
* disposer of the `BindingAccumulator` produced by `parse`. The dispose
|
|
15
|
+
* call lives inside a `finally` block in `execute()` so that a throw
|
|
16
|
+
* inside `runCrossFileBindingPropagation` (or anywhere else in the body)
|
|
17
|
+
* still releases the accumulator's heap. The dependency declaration
|
|
18
|
+
* (`deps: ['parse', 'routes', 'tools', 'orm']`) plus the runner's
|
|
19
|
+
* topological scheduling guarantee that every other consumer of the
|
|
20
|
+
* accumulator has finished before this phase starts, so disposing here
|
|
21
|
+
* is correct.
|
|
22
|
+
*
|
|
23
|
+
* The residual risk is intentional and accepted: if a future phase is
|
|
24
|
+
* inserted between `parse` and `crossFile` that reads the accumulator
|
|
25
|
+
* and throws, `crossFile.execute()` never runs and the accumulator
|
|
26
|
+
* leaks. Any author inserting a new phase between `parse` and
|
|
27
|
+
* `crossFile` MUST either route the new phase's output through
|
|
28
|
+
* `crossFile` (so disposal still happens here) or take ownership of
|
|
29
|
+
* the accumulator's lifetime explicitly (its own try/finally that
|
|
30
|
+
* disposes on the failure path). Do not silently rely on the GC.
|
|
31
|
+
*/
|
|
32
|
+
import type { PipelinePhase } from './types.js';
|
|
33
|
+
export interface CrossFileOutput {
|
|
34
|
+
/** Number of files re-processed during cross-file propagation. */
|
|
35
|
+
filesReprocessed: number;
|
|
36
|
+
}
|
|
37
|
+
export declare const crossFilePhase: PipelinePhase<CrossFileOutput>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: crossFile
|
|
3
|
+
*
|
|
4
|
+
* Cross-file binding propagation: seeds downstream files with resolved
|
|
5
|
+
* type bindings from upstream exports. Files are processed in topological
|
|
6
|
+
* import order so upstream bindings are available when downstream files
|
|
7
|
+
* are re-resolved.
|
|
8
|
+
*
|
|
9
|
+
* @deps parse, routes, tools, orm (waits for all post-parse phases)
|
|
10
|
+
* @reads exportedTypeMap, allPaths, totalFiles
|
|
11
|
+
* @writes graph (refined CALLS edges via re-resolution)
|
|
12
|
+
*
|
|
13
|
+
* **Accumulator ownership / residual risk.** This phase is the sole
|
|
14
|
+
* disposer of the `BindingAccumulator` produced by `parse`. The dispose
|
|
15
|
+
* call lives inside a `finally` block in `execute()` so that a throw
|
|
16
|
+
* inside `runCrossFileBindingPropagation` (or anywhere else in the body)
|
|
17
|
+
* still releases the accumulator's heap. The dependency declaration
|
|
18
|
+
* (`deps: ['parse', 'routes', 'tools', 'orm']`) plus the runner's
|
|
19
|
+
* topological scheduling guarantee that every other consumer of the
|
|
20
|
+
* accumulator has finished before this phase starts, so disposing here
|
|
21
|
+
* is correct.
|
|
22
|
+
*
|
|
23
|
+
* The residual risk is intentional and accepted: if a future phase is
|
|
24
|
+
* inserted between `parse` and `crossFile` that reads the accumulator
|
|
25
|
+
* and throws, `crossFile.execute()` never runs and the accumulator
|
|
26
|
+
* leaks. Any author inserting a new phase between `parse` and
|
|
27
|
+
* `crossFile` MUST either route the new phase's output through
|
|
28
|
+
* `crossFile` (so disposal still happens here) or take ownership of
|
|
29
|
+
* the accumulator's lifetime explicitly (its own try/finally that
|
|
30
|
+
* disposes on the failure path). Do not silently rely on the GC.
|
|
31
|
+
*/
|
|
32
|
+
import { getPhaseOutput } from './types.js';
|
|
33
|
+
import { runCrossFileBindingPropagation } from './cross-file-impl.js';
|
|
34
|
+
import { isDev } from '../utils/env.js';
|
|
35
|
+
export const crossFilePhase = {
|
|
36
|
+
name: 'crossFile',
|
|
37
|
+
deps: ['parse', 'routes', 'tools', 'orm'],
|
|
38
|
+
async execute(ctx, deps) {
|
|
39
|
+
const { exportedTypeMap, allPathSet, totalFiles, bindingAccumulator, resolutionContext } = getPhaseOutput(deps, 'parse');
|
|
40
|
+
try {
|
|
41
|
+
// Telemetry must run BEFORE dispose: totalBindings, fileCount, and
|
|
42
|
+
// estimateMemoryBytes() all return 0 once dispose() clears the
|
|
43
|
+
// internal maps.
|
|
44
|
+
if (isDev) {
|
|
45
|
+
if (bindingAccumulator.totalBindings > 0) {
|
|
46
|
+
const memKB = Math.round(bindingAccumulator.estimateMemoryBytes() / 1024);
|
|
47
|
+
console.log(`📦 BindingAccumulator: ${bindingAccumulator.totalBindings} bindings across ${bindingAccumulator.fileCount} files (~${memKB} KB)`);
|
|
48
|
+
}
|
|
49
|
+
else if (totalFiles > 0) {
|
|
50
|
+
console.log(`📦 BindingAccumulator: EMPTY — 0 bindings across 0 files despite ${totalFiles} parsed files. If the codebase has typed bindings, this indicates an upstream regression.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const filesReprocessed = await runCrossFileBindingPropagation(ctx.graph, resolutionContext, exportedTypeMap, allPathSet, totalFiles, ctx.repoPath, ctx.pipelineStart, ctx.onProgress);
|
|
54
|
+
return { filesReprocessed };
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
// Single dispose call site for the accumulator — runs on both the
|
|
58
|
+
// happy path and the throw path so the heap is always released
|
|
59
|
+
// before the runner moves on (or surfaces the error).
|
|
60
|
+
bindingAccumulator.dispose();
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline Phases — barrel export.
|
|
3
|
+
*
|
|
4
|
+
* Exports all phases, the runner, types, and shared utilities
|
|
5
|
+
* for the ingestion pipeline.
|
|
6
|
+
*/
|
|
7
|
+
export { scanPhase, type ScanOutput } from './scan.js';
|
|
8
|
+
export { structurePhase, type StructureOutput } from './structure.js';
|
|
9
|
+
export { markdownPhase, type MarkdownOutput } from './markdown.js';
|
|
10
|
+
export { cobolPhase, type CobolOutput } from './cobol.js';
|
|
11
|
+
export { parsePhase, type ParseOutput } from './parse.js';
|
|
12
|
+
export { routesPhase, type RoutesOutput, type RouteEntry } from './routes.js';
|
|
13
|
+
export { toolsPhase, type ToolsOutput, type ToolDef } from './tools.js';
|
|
14
|
+
export { ormPhase, type ORMOutput } from './orm.js';
|
|
15
|
+
export { crossFilePhase, type CrossFileOutput } from './cross-file.js';
|
|
16
|
+
export { mroPhase, type MROOutput } from './mro.js';
|
|
17
|
+
export { communitiesPhase, type CommunitiesOutput } from './communities.js';
|
|
18
|
+
export { processesPhase, type ProcessesOutput } from './processes.js';
|
|
19
|
+
export { runPipeline } from './runner.js';
|
|
20
|
+
export type { PipelinePhase, PipelineContext, PhaseResult } from './types.js';
|
|
21
|
+
export { getPhaseOutput } from './types.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline Phases — barrel export.
|
|
3
|
+
*
|
|
4
|
+
* Exports all phases, the runner, types, and shared utilities
|
|
5
|
+
* for the ingestion pipeline.
|
|
6
|
+
*/
|
|
7
|
+
// ── Phase exports (in dependency order) ────────────────────────────────────
|
|
8
|
+
export { scanPhase } from './scan.js';
|
|
9
|
+
export { structurePhase } from './structure.js';
|
|
10
|
+
export { markdownPhase } from './markdown.js';
|
|
11
|
+
export { cobolPhase } from './cobol.js';
|
|
12
|
+
export { parsePhase } from './parse.js';
|
|
13
|
+
export { routesPhase } from './routes.js';
|
|
14
|
+
export { toolsPhase } from './tools.js';
|
|
15
|
+
export { ormPhase } from './orm.js';
|
|
16
|
+
export { crossFilePhase } from './cross-file.js';
|
|
17
|
+
export { mroPhase } from './mro.js';
|
|
18
|
+
export { communitiesPhase } from './communities.js';
|
|
19
|
+
export { processesPhase } from './processes.js';
|
|
20
|
+
// ── Infrastructure ─────────────────────────────────────────────────────────
|
|
21
|
+
export { runPipeline } from './runner.js';
|
|
22
|
+
export { getPhaseOutput } from './types.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: markdown
|
|
3
|
+
*
|
|
4
|
+
* Processes Markdown/MDX files to extract headings and cross-links.
|
|
5
|
+
*
|
|
6
|
+
* @deps structure
|
|
7
|
+
* @reads scannedFiles, allPaths (from structure phase)
|
|
8
|
+
* @writes graph (Markdown section nodes + cross-link edges)
|
|
9
|
+
*/
|
|
10
|
+
import type { PipelinePhase } from './types.js';
|
|
11
|
+
export interface MarkdownOutput {
|
|
12
|
+
/** Number of markdown sections extracted. */
|
|
13
|
+
sections: number;
|
|
14
|
+
/** Number of cross-links created. */
|
|
15
|
+
links: number;
|
|
16
|
+
}
|
|
17
|
+
export declare const markdownPhase: PipelinePhase<MarkdownOutput>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: markdown
|
|
3
|
+
*
|
|
4
|
+
* Processes Markdown/MDX files to extract headings and cross-links.
|
|
5
|
+
*
|
|
6
|
+
* @deps structure
|
|
7
|
+
* @reads scannedFiles, allPaths (from structure phase)
|
|
8
|
+
* @writes graph (Markdown section nodes + cross-link edges)
|
|
9
|
+
*/
|
|
10
|
+
import { getPhaseOutput } from './types.js';
|
|
11
|
+
import { processMarkdown } from '../markdown-processor.js';
|
|
12
|
+
import { readFileContents } from '../filesystem-walker.js';
|
|
13
|
+
import { isDev } from '../utils/env.js';
|
|
14
|
+
export const markdownPhase = {
|
|
15
|
+
name: 'markdown',
|
|
16
|
+
deps: ['structure'],
|
|
17
|
+
async execute(ctx, deps) {
|
|
18
|
+
const { scannedFiles, allPathSet } = getPhaseOutput(deps, 'structure');
|
|
19
|
+
const mdScanned = scannedFiles.filter((f) => f.path.endsWith('.md') || f.path.endsWith('.mdx'));
|
|
20
|
+
if (mdScanned.length === 0) {
|
|
21
|
+
return { sections: 0, links: 0 };
|
|
22
|
+
}
|
|
23
|
+
const mdContents = await readFileContents(ctx.repoPath, mdScanned.map((f) => f.path));
|
|
24
|
+
const mdFiles = mdScanned
|
|
25
|
+
.filter((f) => mdContents.has(f.path))
|
|
26
|
+
.map((f) => ({ path: f.path, content: mdContents.get(f.path) }));
|
|
27
|
+
const mdResult = processMarkdown(ctx.graph, mdFiles, allPathSet);
|
|
28
|
+
if (isDev) {
|
|
29
|
+
console.log(` Markdown: ${mdResult.sections} sections, ${mdResult.links} cross-links from ${mdFiles.length} files`);
|
|
30
|
+
}
|
|
31
|
+
return { sections: mdResult.sections, links: mdResult.links };
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: mro
|
|
3
|
+
*
|
|
4
|
+
* Computes Method Resolution Order (MRO) and creates METHOD_OVERRIDES
|
|
5
|
+
* and METHOD_IMPLEMENTS edges.
|
|
6
|
+
*
|
|
7
|
+
* @deps crossFile
|
|
8
|
+
* @reads graph (all nodes and relationships)
|
|
9
|
+
* @writes graph (METHOD_OVERRIDES, METHOD_IMPLEMENTS edges)
|
|
10
|
+
*/
|
|
11
|
+
import type { PipelinePhase } from './types.js';
|
|
12
|
+
export interface MROOutput {
|
|
13
|
+
entries: number;
|
|
14
|
+
ambiguityCount: number;
|
|
15
|
+
overrideEdges: number;
|
|
16
|
+
methodImplementsEdges: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const mroPhase: PipelinePhase<MROOutput>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: mro
|
|
3
|
+
*
|
|
4
|
+
* Computes Method Resolution Order (MRO) and creates METHOD_OVERRIDES
|
|
5
|
+
* and METHOD_IMPLEMENTS edges.
|
|
6
|
+
*
|
|
7
|
+
* @deps crossFile
|
|
8
|
+
* @reads graph (all nodes and relationships)
|
|
9
|
+
* @writes graph (METHOD_OVERRIDES, METHOD_IMPLEMENTS edges)
|
|
10
|
+
*/
|
|
11
|
+
import { getPhaseOutput } from './types.js';
|
|
12
|
+
import { computeMRO } from '../mro-processor.js';
|
|
13
|
+
import { isDev } from '../utils/env.js';
|
|
14
|
+
export const mroPhase = {
|
|
15
|
+
name: 'mro',
|
|
16
|
+
deps: ['crossFile', 'structure'],
|
|
17
|
+
async execute(ctx, deps) {
|
|
18
|
+
const { totalFiles } = getPhaseOutput(deps, 'structure');
|
|
19
|
+
ctx.onProgress({
|
|
20
|
+
phase: 'enriching',
|
|
21
|
+
percent: 83,
|
|
22
|
+
message: 'Computing method resolution order...',
|
|
23
|
+
stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: ctx.graph.nodeCount },
|
|
24
|
+
});
|
|
25
|
+
const mroResult = computeMRO(ctx.graph);
|
|
26
|
+
if (isDev && mroResult.entries.length > 0) {
|
|
27
|
+
console.log(`🔀 MRO: ${mroResult.entries.length} classes analyzed, ${mroResult.ambiguityCount} ambiguities, ${mroResult.overrideEdges} METHOD_OVERRIDES, ${mroResult.methodImplementsEdges} METHOD_IMPLEMENTS`);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
entries: mroResult.entries.length,
|
|
31
|
+
ambiguityCount: mroResult.ambiguityCount,
|
|
32
|
+
overrideEdges: mroResult.overrideEdges,
|
|
33
|
+
methodImplementsEdges: mroResult.methodImplementsEdges,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline ORM query extraction (sequential fallback path).
|
|
3
|
+
*
|
|
4
|
+
* Extracts Prisma and Supabase query calls from source content using
|
|
5
|
+
* regex patterns. Used by the sequential parse path when workers are
|
|
6
|
+
* not available — the worker path extracts ORM queries via tree-sitter
|
|
7
|
+
* queries instead.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type { ExtractedORMQuery } from '../workers/parse-worker.js';
|
|
12
|
+
/**
|
|
13
|
+
* Extract ORM query calls from file content using regex.
|
|
14
|
+
*
|
|
15
|
+
* Fast-path: skips files that don't contain `prisma.` or `supabase.from`.
|
|
16
|
+
* Results are appended to the `out` array (push pattern avoids allocation).
|
|
17
|
+
*
|
|
18
|
+
* @param filePath Relative path of the source file
|
|
19
|
+
* @param content File content string
|
|
20
|
+
* @param out Output array to append extracted queries to
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractORMQueriesInline(filePath: string, content: string, out: ExtractedORMQuery[]): void;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline ORM query extraction (sequential fallback path).
|
|
3
|
+
*
|
|
4
|
+
* Extracts Prisma and Supabase query calls from source content using
|
|
5
|
+
* regex patterns. Used by the sequential parse path when workers are
|
|
6
|
+
* not available — the worker path extracts ORM queries via tree-sitter
|
|
7
|
+
* queries instead.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
// ── Regex patterns ─────────────────────────────────────────────────────────
|
|
12
|
+
/** Matches Prisma client method calls: `prisma.user.findMany(...)` */
|
|
13
|
+
const PRISMA_QUERY_RE = /\bprisma\.(\w+)\.(findMany|findFirst|findUnique|findUniqueOrThrow|findFirstOrThrow|create|createMany|update|updateMany|delete|deleteMany|upsert|count|aggregate|groupBy)\s*\(/g;
|
|
14
|
+
/** Matches Supabase client method calls: `supabase.from('users').select(...)` */
|
|
15
|
+
const SUPABASE_QUERY_RE = /\bsupabase\.from\s*\(\s*['"](\w+)['"]\s*\)\s*\.(select|insert|update|delete|upsert)\s*\(/g;
|
|
16
|
+
// ── Extraction function ───────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Extract ORM query calls from file content using regex.
|
|
19
|
+
*
|
|
20
|
+
* Fast-path: skips files that don't contain `prisma.` or `supabase.from`.
|
|
21
|
+
* Results are appended to the `out` array (push pattern avoids allocation).
|
|
22
|
+
*
|
|
23
|
+
* @param filePath Relative path of the source file
|
|
24
|
+
* @param content File content string
|
|
25
|
+
* @param out Output array to append extracted queries to
|
|
26
|
+
*/
|
|
27
|
+
export function extractORMQueriesInline(filePath, content, out) {
|
|
28
|
+
const hasPrisma = content.includes('prisma.');
|
|
29
|
+
const hasSupabase = content.includes('supabase.from');
|
|
30
|
+
if (!hasPrisma && !hasSupabase)
|
|
31
|
+
return;
|
|
32
|
+
// Pre-compute line number offsets to avoid O(n²) substring+split per match
|
|
33
|
+
const lineOffsets = buildLineOffsets(content);
|
|
34
|
+
if (hasPrisma) {
|
|
35
|
+
PRISMA_QUERY_RE.lastIndex = 0;
|
|
36
|
+
let m;
|
|
37
|
+
while ((m = PRISMA_QUERY_RE.exec(content)) !== null) {
|
|
38
|
+
const model = m[1];
|
|
39
|
+
if (model.startsWith('$'))
|
|
40
|
+
continue;
|
|
41
|
+
out.push({
|
|
42
|
+
filePath,
|
|
43
|
+
orm: 'prisma',
|
|
44
|
+
model,
|
|
45
|
+
method: m[2],
|
|
46
|
+
lineNumber: lineNumberAtOffset(lineOffsets, m.index),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (hasSupabase) {
|
|
51
|
+
SUPABASE_QUERY_RE.lastIndex = 0;
|
|
52
|
+
let m;
|
|
53
|
+
while ((m = SUPABASE_QUERY_RE.exec(content)) !== null) {
|
|
54
|
+
out.push({
|
|
55
|
+
filePath,
|
|
56
|
+
orm: 'supabase',
|
|
57
|
+
model: m[1],
|
|
58
|
+
method: m[2],
|
|
59
|
+
lineNumber: lineNumberAtOffset(lineOffsets, m.index),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ── Line offset helpers ───────────────────────────────────────────────────
|
|
65
|
+
/** Build an array of byte offsets where each newline occurs (O(n) once). */
|
|
66
|
+
function buildLineOffsets(content) {
|
|
67
|
+
const offsets = [];
|
|
68
|
+
for (let i = 0; i < content.length; i++) {
|
|
69
|
+
if (content[i] === '\n')
|
|
70
|
+
offsets.push(i);
|
|
71
|
+
}
|
|
72
|
+
return offsets;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Binary search for 0-based line number at a given character offset.
|
|
76
|
+
*
|
|
77
|
+
* Returns the number of newlines that occur before `offset` in the content,
|
|
78
|
+
* which is the 0-based line number. When `offset` is beyond the last newline,
|
|
79
|
+
* returns `lineOffsets.length` (i.e., the last line index).
|
|
80
|
+
*/
|
|
81
|
+
function lineNumberAtOffset(lineOffsets, offset) {
|
|
82
|
+
let lo = 0;
|
|
83
|
+
let hi = lineOffsets.length;
|
|
84
|
+
while (lo < hi) {
|
|
85
|
+
const mid = (lo + hi) >>> 1;
|
|
86
|
+
if (lineOffsets[mid] < offset)
|
|
87
|
+
lo = mid + 1;
|
|
88
|
+
else
|
|
89
|
+
hi = mid;
|
|
90
|
+
}
|
|
91
|
+
return lo;
|
|
92
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: orm
|
|
3
|
+
*
|
|
4
|
+
* Processes ORM queries (Prisma + Supabase) and creates QUERIES edges.
|
|
5
|
+
*
|
|
6
|
+
* @deps parse
|
|
7
|
+
* @reads allORMQueries (from parse)
|
|
8
|
+
* @writes graph (CodeElement nodes, QUERIES edges)
|
|
9
|
+
*/
|
|
10
|
+
import type { PipelinePhase } from './types.js';
|
|
11
|
+
export interface ORMOutput {
|
|
12
|
+
edgesCreated: number;
|
|
13
|
+
modelCount: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const ormPhase: PipelinePhase<ORMOutput>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase: orm
|
|
3
|
+
*
|
|
4
|
+
* Processes ORM queries (Prisma + Supabase) and creates QUERIES edges.
|
|
5
|
+
*
|
|
6
|
+
* @deps parse
|
|
7
|
+
* @reads allORMQueries (from parse)
|
|
8
|
+
* @writes graph (CodeElement nodes, QUERIES edges)
|
|
9
|
+
*/
|
|
10
|
+
import { getPhaseOutput } from './types.js';
|
|
11
|
+
import { generateId } from '../../../lib/utils.js';
|
|
12
|
+
import { isDev } from '../utils/env.js';
|
|
13
|
+
export const ormPhase = {
|
|
14
|
+
name: 'orm',
|
|
15
|
+
deps: ['parse'],
|
|
16
|
+
async execute(ctx, deps) {
|
|
17
|
+
const { allORMQueries } = getPhaseOutput(deps, 'parse');
|
|
18
|
+
if (allORMQueries.length === 0) {
|
|
19
|
+
return { edgesCreated: 0, modelCount: 0 };
|
|
20
|
+
}
|
|
21
|
+
return processORMQueries(ctx.graph, allORMQueries);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
function processORMQueries(graph, queries) {
|
|
25
|
+
const modelNodes = new Map();
|
|
26
|
+
const seenEdges = new Set();
|
|
27
|
+
let edgesCreated = 0;
|
|
28
|
+
for (const q of queries) {
|
|
29
|
+
const modelKey = `${q.orm}:${q.model}`;
|
|
30
|
+
let modelNodeId = modelNodes.get(modelKey);
|
|
31
|
+
if (!modelNodeId) {
|
|
32
|
+
const candidateIds = [
|
|
33
|
+
generateId('Class', `${q.model}`),
|
|
34
|
+
generateId('Interface', `${q.model}`),
|
|
35
|
+
generateId('CodeElement', `${q.model}`),
|
|
36
|
+
];
|
|
37
|
+
const existing = candidateIds.find((id) => graph.getNode(id));
|
|
38
|
+
if (existing) {
|
|
39
|
+
modelNodeId = existing;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
modelNodeId = generateId('CodeElement', `${q.orm}:${q.model}`);
|
|
43
|
+
graph.addNode({
|
|
44
|
+
id: modelNodeId,
|
|
45
|
+
label: 'CodeElement',
|
|
46
|
+
properties: {
|
|
47
|
+
name: q.model,
|
|
48
|
+
filePath: '',
|
|
49
|
+
description: `${q.orm} model/table: ${q.model}`,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
modelNodes.set(modelKey, modelNodeId);
|
|
54
|
+
}
|
|
55
|
+
const fileId = generateId('File', q.filePath);
|
|
56
|
+
const edgeKey = `${fileId}->${modelNodeId}:${q.method}`;
|
|
57
|
+
if (seenEdges.has(edgeKey))
|
|
58
|
+
continue;
|
|
59
|
+
seenEdges.add(edgeKey);
|
|
60
|
+
graph.addRelationship({
|
|
61
|
+
id: generateId('QUERIES', edgeKey),
|
|
62
|
+
sourceId: fileId,
|
|
63
|
+
targetId: modelNodeId,
|
|
64
|
+
type: 'QUERIES',
|
|
65
|
+
confidence: 0.9,
|
|
66
|
+
reason: `${q.orm}-${q.method}`,
|
|
67
|
+
});
|
|
68
|
+
edgesCreated++;
|
|
69
|
+
}
|
|
70
|
+
if (isDev) {
|
|
71
|
+
console.log(`ORM dataflow: ${edgesCreated} QUERIES edges, ${modelNodes.size} models (${queries.length} total calls)`);
|
|
72
|
+
}
|
|
73
|
+
return { edgesCreated, modelCount: modelNodes.size };
|
|
74
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse implementation — chunked parse + resolve loop.
|
|
3
|
+
*
|
|
4
|
+
* This is the core parsing engine of the ingestion pipeline. It reads
|
|
5
|
+
* source files in byte-budget chunks (~20MB each), parses via worker
|
|
6
|
+
* pool (or sequential fallback), resolves imports/calls/heritage per
|
|
7
|
+
* chunk, and synthesizes wildcard import bindings.
|
|
8
|
+
*
|
|
9
|
+
* Consumed by the parse phase (`parse.ts`) — the phase file handles
|
|
10
|
+
* dependency wiring while the heavy implementation lives here.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { BindingAccumulator } from '../binding-accumulator.js';
|
|
15
|
+
import { type ExportedTypeMap } from '../call-processor.js';
|
|
16
|
+
import { createResolutionContext } from '../model/resolution-context.js';
|
|
17
|
+
import { type PipelineProgress } from '../../../_shared/index.js';
|
|
18
|
+
import type { ExtractedDecoratorRoute, ExtractedFetchCall, ExtractedORMQuery, ExtractedRoute, ExtractedToolDef } from '../workers/parse-worker.js';
|
|
19
|
+
import type { KnowledgeGraph } from '../../graph/types.js';
|
|
20
|
+
import type { PipelineOptions } from '../pipeline.js';
|
|
21
|
+
type ScannedFile = {
|
|
22
|
+
path: string;
|
|
23
|
+
size: number;
|
|
24
|
+
};
|
|
25
|
+
type ProgressFn = (progress: PipelineProgress) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Chunked parse + resolve loop.
|
|
28
|
+
*
|
|
29
|
+
* Reads source in byte-budget chunks (~20MB each). For each chunk:
|
|
30
|
+
* 1. Parse via worker pool (or sequential fallback)
|
|
31
|
+
* 2. Resolve imports from extracted data
|
|
32
|
+
* 3. Synthesize wildcard import bindings (Go/Ruby/C++/Swift/Python)
|
|
33
|
+
* 4. Resolve heritage + routes per chunk; defer worker CALLS until all chunks
|
|
34
|
+
* have contributed heritage so interface-dispatch implementor map is complete
|
|
35
|
+
* 5. Collect TypeEnv bindings for cross-file propagation
|
|
36
|
+
*/
|
|
37
|
+
export declare function runChunkedParseAndResolve(graph: KnowledgeGraph, scannedFiles: ScannedFile[], allPaths: string[], totalFiles: number, repoPath: string, pipelineStart: number, onProgress: ProgressFn, options?: PipelineOptions): Promise<{
|
|
38
|
+
exportedTypeMap: ExportedTypeMap;
|
|
39
|
+
allFetchCalls: ExtractedFetchCall[];
|
|
40
|
+
allExtractedRoutes: ExtractedRoute[];
|
|
41
|
+
allDecoratorRoutes: ExtractedDecoratorRoute[];
|
|
42
|
+
allToolDefs: ExtractedToolDef[];
|
|
43
|
+
allORMQueries: ExtractedORMQuery[];
|
|
44
|
+
bindingAccumulator: BindingAccumulator;
|
|
45
|
+
resolutionContext: ReturnType<typeof createResolutionContext>;
|
|
46
|
+
}>;
|
|
47
|
+
export {};
|