cotx-engine 0.1.0
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/LICENSE +94 -0
- package/README.md +103 -0
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.js +93 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/context.d.ts +1 -0
- package/dist/commands/context.js +98 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/diff.d.ts +19 -0
- package/dist/commands/diff.js +127 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/impact.d.ts +3 -0
- package/dist/commands/impact.js +91 -0
- package/dist/commands/impact.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +13 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +13 -0
- package/dist/commands/lint.js +290 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/map.d.ts +6 -0
- package/dist/commands/map.js +409 -0
- package/dist/commands/map.js.map +1 -0
- package/dist/commands/migrate.d.ts +16 -0
- package/dist/commands/migrate.js +150 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/query.d.ts +3 -0
- package/dist/commands/query.js +47 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/rename.d.ts +5 -0
- package/dist/commands/rename.js +163 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/snapshot.d.ts +6 -0
- package/dist/commands/snapshot.js +48 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +72 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +163 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/write.d.ts +6 -0
- package/dist/commands/write.js +221 -0
- package/dist/commands/write.js.map +1 -0
- package/dist/compiler/auto-describe.d.ts +13 -0
- package/dist/compiler/auto-describe.js +91 -0
- package/dist/compiler/auto-describe.js.map +1 -0
- package/dist/compiler/concept-compiler.d.ts +21 -0
- package/dist/compiler/concept-compiler.js +125 -0
- package/dist/compiler/concept-compiler.js.map +1 -0
- package/dist/compiler/contract-compiler.d.ts +16 -0
- package/dist/compiler/contract-compiler.js +90 -0
- package/dist/compiler/contract-compiler.js.map +1 -0
- package/dist/compiler/delta-detector.d.ts +8 -0
- package/dist/compiler/delta-detector.js +34 -0
- package/dist/compiler/delta-detector.js.map +1 -0
- package/dist/compiler/flow-compiler.d.ts +18 -0
- package/dist/compiler/flow-compiler.js +69 -0
- package/dist/compiler/flow-compiler.js.map +1 -0
- package/dist/compiler/module-compiler.d.ts +18 -0
- package/dist/compiler/module-compiler.js +420 -0
- package/dist/compiler/module-compiler.js.map +1 -0
- package/dist/compiler/stale-detector.d.ts +22 -0
- package/dist/compiler/stale-detector.js +79 -0
- package/dist/compiler/stale-detector.js.map +1 -0
- package/dist/config/ignore-service.d.ts +26 -0
- package/dist/config/ignore-service.js +366 -0
- package/dist/config/ignore-service.js.map +1 -0
- package/dist/core/analysis/cluster-enricher.d.ts +38 -0
- package/dist/core/analysis/cluster-enricher.js +169 -0
- package/dist/core/analysis/cluster-enricher.js.map +1 -0
- package/dist/core/analysis/community-processor.d.ts +39 -0
- package/dist/core/analysis/community-processor.js +319 -0
- package/dist/core/analysis/community-processor.js.map +1 -0
- package/dist/core/analysis/process-processor.d.ts +51 -0
- package/dist/core/analysis/process-processor.js +318 -0
- package/dist/core/analysis/process-processor.js.map +1 -0
- package/dist/core/bridge.d.ts +15 -0
- package/dist/core/bridge.js +63 -0
- package/dist/core/bridge.js.map +1 -0
- package/dist/core/export/json-exporter.d.ts +43 -0
- package/dist/core/export/json-exporter.js +13 -0
- package/dist/core/export/json-exporter.js.map +1 -0
- package/dist/core/graph/graph.d.ts +2 -0
- package/dist/core/graph/graph.js +79 -0
- package/dist/core/graph/graph.js.map +1 -0
- package/dist/core/graph/types.d.ts +25 -0
- package/dist/core/graph/types.js +2 -0
- package/dist/core/graph/types.js.map +1 -0
- package/dist/core/parser/ast-cache.d.ts +11 -0
- package/dist/core/parser/ast-cache.js +36 -0
- package/dist/core/parser/ast-cache.js.map +1 -0
- package/dist/core/parser/call-processor.d.ts +105 -0
- package/dist/core/parser/call-processor.js +1807 -0
- package/dist/core/parser/call-processor.js.map +1 -0
- package/dist/core/parser/call-routing.d.ts +55 -0
- package/dist/core/parser/call-routing.js +113 -0
- package/dist/core/parser/call-routing.js.map +1 -0
- package/dist/core/parser/call-sites/extract-language-call-site.d.ts +10 -0
- package/dist/core/parser/call-sites/extract-language-call-site.js +23 -0
- package/dist/core/parser/call-sites/extract-language-call-site.js.map +1 -0
- package/dist/core/parser/call-sites/java.d.ts +9 -0
- package/dist/core/parser/call-sites/java.js +31 -0
- package/dist/core/parser/call-sites/java.js.map +1 -0
- package/dist/core/parser/cluster-enricher.d.ts +38 -0
- package/dist/core/parser/cluster-enricher.js +169 -0
- package/dist/core/parser/cluster-enricher.js.map +1 -0
- package/dist/core/parser/community-processor.d.ts +39 -0
- package/dist/core/parser/community-processor.js +321 -0
- package/dist/core/parser/community-processor.js.map +1 -0
- package/dist/core/parser/constants.d.ts +16 -0
- package/dist/core/parser/constants.js +17 -0
- package/dist/core/parser/constants.js.map +1 -0
- package/dist/core/parser/entry-point-scoring.d.ts +57 -0
- package/dist/core/parser/entry-point-scoring.js +377 -0
- package/dist/core/parser/entry-point-scoring.js.map +1 -0
- package/dist/core/parser/export-detection.d.ts +57 -0
- package/dist/core/parser/export-detection.js +234 -0
- package/dist/core/parser/export-detection.js.map +1 -0
- package/dist/core/parser/field-extractor.d.ts +34 -0
- package/dist/core/parser/field-extractor.js +33 -0
- package/dist/core/parser/field-extractor.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/c-cpp.d.ts +16 -0
- package/dist/core/parser/field-extractors/configs/c-cpp.js +129 -0
- package/dist/core/parser/field-extractors/configs/c-cpp.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/csharp.d.ts +15 -0
- package/dist/core/parser/field-extractors/configs/csharp.js +129 -0
- package/dist/core/parser/field-extractors/configs/csharp.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/dart.d.ts +12 -0
- package/dist/core/parser/field-extractors/configs/dart.js +93 -0
- package/dist/core/parser/field-extractors/configs/dart.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/go.d.ts +12 -0
- package/dist/core/parser/field-extractors/configs/go.js +66 -0
- package/dist/core/parser/field-extractors/configs/go.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/helpers.d.ts +40 -0
- package/dist/core/parser/field-extractors/configs/helpers.js +118 -0
- package/dist/core/parser/field-extractors/configs/helpers.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/jvm.d.ts +17 -0
- package/dist/core/parser/field-extractors/configs/jvm.js +139 -0
- package/dist/core/parser/field-extractors/configs/jvm.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/php.d.ts +12 -0
- package/dist/core/parser/field-extractors/configs/php.js +69 -0
- package/dist/core/parser/field-extractors/configs/php.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/python.d.ts +15 -0
- package/dist/core/parser/field-extractors/configs/python.js +92 -0
- package/dist/core/parser/field-extractors/configs/python.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/ruby.d.ts +15 -0
- package/dist/core/parser/field-extractors/configs/ruby.js +68 -0
- package/dist/core/parser/field-extractors/configs/ruby.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/rust.d.ts +12 -0
- package/dist/core/parser/field-extractors/configs/rust.js +58 -0
- package/dist/core/parser/field-extractors/configs/rust.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/swift.d.ts +15 -0
- package/dist/core/parser/field-extractors/configs/swift.js +75 -0
- package/dist/core/parser/field-extractors/configs/swift.js.map +1 -0
- package/dist/core/parser/field-extractors/configs/typescript-javascript.d.ts +14 -0
- package/dist/core/parser/field-extractors/configs/typescript-javascript.js +72 -0
- package/dist/core/parser/field-extractors/configs/typescript-javascript.js.map +1 -0
- package/dist/core/parser/field-extractors/generic.d.ts +61 -0
- package/dist/core/parser/field-extractors/generic.js +170 -0
- package/dist/core/parser/field-extractors/generic.js.map +1 -0
- package/dist/core/parser/field-extractors/typescript.d.ts +16 -0
- package/dist/core/parser/field-extractors/typescript.js +167 -0
- package/dist/core/parser/field-extractors/typescript.js.map +1 -0
- package/dist/core/parser/field-types.d.ts +46 -0
- package/dist/core/parser/field-types.js +2 -0
- package/dist/core/parser/field-types.js.map +1 -0
- package/dist/core/parser/filesystem-walker.d.ts +28 -0
- package/dist/core/parser/filesystem-walker.js +82 -0
- package/dist/core/parser/filesystem-walker.js.map +1 -0
- package/dist/core/parser/framework-detection.d.ts +149 -0
- package/dist/core/parser/framework-detection.js +782 -0
- package/dist/core/parser/framework-detection.js.map +1 -0
- package/dist/core/parser/heritage-processor.d.ts +52 -0
- package/dist/core/parser/heritage-processor.js +339 -0
- package/dist/core/parser/heritage-processor.js.map +1 -0
- package/dist/core/parser/import-processor.d.ts +33 -0
- package/dist/core/parser/import-processor.js +382 -0
- package/dist/core/parser/import-processor.js.map +1 -0
- package/dist/core/parser/import-resolvers/csharp.d.ts +19 -0
- package/dist/core/parser/import-resolvers/csharp.js +132 -0
- package/dist/core/parser/import-resolvers/csharp.js.map +1 -0
- package/dist/core/parser/import-resolvers/dart.d.ts +7 -0
- package/dist/core/parser/import-resolvers/dart.js +45 -0
- package/dist/core/parser/import-resolvers/dart.js.map +1 -0
- package/dist/core/parser/import-resolvers/go.d.ts +18 -0
- package/dist/core/parser/import-resolvers/go.js +62 -0
- package/dist/core/parser/import-resolvers/go.js.map +1 -0
- package/dist/core/parser/import-resolvers/jvm.d.ts +32 -0
- package/dist/core/parser/import-resolvers/jvm.js +160 -0
- package/dist/core/parser/import-resolvers/jvm.js.map +1 -0
- package/dist/core/parser/import-resolvers/php.d.ts +25 -0
- package/dist/core/parser/import-resolvers/php.js +81 -0
- package/dist/core/parser/import-resolvers/php.js.map +1 -0
- package/dist/core/parser/import-resolvers/python.d.ts +25 -0
- package/dist/core/parser/import-resolvers/python.js +85 -0
- package/dist/core/parser/import-resolvers/python.js.map +1 -0
- package/dist/core/parser/import-resolvers/ruby.d.ts +15 -0
- package/dist/core/parser/import-resolvers/ruby.js +21 -0
- package/dist/core/parser/import-resolvers/ruby.js.map +1 -0
- package/dist/core/parser/import-resolvers/rust.d.ts +18 -0
- package/dist/core/parser/import-resolvers/rust.js +119 -0
- package/dist/core/parser/import-resolvers/rust.js.map +1 -0
- package/dist/core/parser/import-resolvers/standard.d.ts +36 -0
- package/dist/core/parser/import-resolvers/standard.js +144 -0
- package/dist/core/parser/import-resolvers/standard.js.map +1 -0
- package/dist/core/parser/import-resolvers/swift.d.ts +7 -0
- package/dist/core/parser/import-resolvers/swift.js +25 -0
- package/dist/core/parser/import-resolvers/swift.js.map +1 -0
- package/dist/core/parser/import-resolvers/types.d.ts +44 -0
- package/dist/core/parser/import-resolvers/types.js +7 -0
- package/dist/core/parser/import-resolvers/types.js.map +1 -0
- package/dist/core/parser/import-resolvers/utils.d.ts +35 -0
- package/dist/core/parser/import-resolvers/utils.js +150 -0
- package/dist/core/parser/import-resolvers/utils.js.map +1 -0
- package/dist/core/parser/import-resolvers/vue.d.ts +8 -0
- package/dist/core/parser/import-resolvers/vue.js +10 -0
- package/dist/core/parser/import-resolvers/vue.js.map +1 -0
- package/dist/core/parser/language-config.d.ts +52 -0
- package/dist/core/parser/language-config.js +182 -0
- package/dist/core/parser/language-config.js.map +1 -0
- package/dist/core/parser/language-provider.d.ts +126 -0
- package/dist/core/parser/language-provider.js +25 -0
- package/dist/core/parser/language-provider.js.map +1 -0
- package/dist/core/parser/languages/c-cpp.d.ts +12 -0
- package/dist/core/parser/languages/c-cpp.js +312 -0
- package/dist/core/parser/languages/c-cpp.js.map +1 -0
- package/dist/core/parser/languages/csharp.d.ts +8 -0
- package/dist/core/parser/languages/csharp.js +127 -0
- package/dist/core/parser/languages/csharp.js.map +1 -0
- package/dist/core/parser/languages/dart.d.ts +12 -0
- package/dist/core/parser/languages/dart.js +91 -0
- package/dist/core/parser/languages/dart.js.map +1 -0
- package/dist/core/parser/languages/go.d.ts +11 -0
- package/dist/core/parser/languages/go.js +32 -0
- package/dist/core/parser/languages/go.js.map +1 -0
- package/dist/core/parser/languages/index.d.ts +38 -0
- package/dist/core/parser/languages/index.js +63 -0
- package/dist/core/parser/languages/index.js.map +1 -0
- package/dist/core/parser/languages/java.d.ts +9 -0
- package/dist/core/parser/languages/java.js +33 -0
- package/dist/core/parser/languages/java.js.map +1 -0
- package/dist/core/parser/languages/kotlin.d.ts +9 -0
- package/dist/core/parser/languages/kotlin.js +112 -0
- package/dist/core/parser/languages/kotlin.js.map +1 -0
- package/dist/core/parser/languages/php.d.ts +8 -0
- package/dist/core/parser/languages/php.js +226 -0
- package/dist/core/parser/languages/php.js.map +1 -0
- package/dist/core/parser/languages/python.d.ts +12 -0
- package/dist/core/parser/languages/python.js +66 -0
- package/dist/core/parser/languages/python.js.map +1 -0
- package/dist/core/parser/languages/ruby.d.ts +9 -0
- package/dist/core/parser/languages/ruby.js +109 -0
- package/dist/core/parser/languages/ruby.js.map +1 -0
- package/dist/core/parser/languages/rust.d.ts +12 -0
- package/dist/core/parser/languages/rust.js +121 -0
- package/dist/core/parser/languages/rust.js.map +1 -0
- package/dist/core/parser/languages/swift.d.ts +12 -0
- package/dist/core/parser/languages/swift.js +233 -0
- package/dist/core/parser/languages/swift.js.map +1 -0
- package/dist/core/parser/languages/typescript.d.ts +11 -0
- package/dist/core/parser/languages/typescript.js +169 -0
- package/dist/core/parser/languages/typescript.js.map +1 -0
- package/dist/core/parser/languages/vue.d.ts +13 -0
- package/dist/core/parser/languages/vue.js +65 -0
- package/dist/core/parser/languages/vue.js.map +1 -0
- package/dist/core/parser/markdown-processor.d.ts +17 -0
- package/dist/core/parser/markdown-processor.js +125 -0
- package/dist/core/parser/markdown-processor.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/parser/method-extractors/configs/c-cpp.js +276 -0
- package/dist/core/parser/method-extractors/configs/c-cpp.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/csharp.js +243 -0
- package/dist/core/parser/method-extractors/configs/csharp.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/dart.js +263 -0
- package/dist/core/parser/method-extractors/configs/dart.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/go.js +120 -0
- package/dist/core/parser/method-extractors/configs/go.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/parser/method-extractors/configs/jvm.js +309 -0
- package/dist/core/parser/method-extractors/configs/jvm.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/php.js +243 -0
- package/dist/core/parser/method-extractors/configs/php.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/python.js +219 -0
- package/dist/core/parser/method-extractors/configs/python.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/ruby.js +201 -0
- package/dist/core/parser/method-extractors/configs/ruby.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/rust.js +120 -0
- package/dist/core/parser/method-extractors/configs/rust.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/parser/method-extractors/configs/swift.js +191 -0
- package/dist/core/parser/method-extractors/configs/swift.js.map +1 -0
- package/dist/core/parser/method-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/parser/method-extractors/configs/typescript-javascript.js +231 -0
- package/dist/core/parser/method-extractors/configs/typescript-javascript.js.map +1 -0
- package/dist/core/parser/method-extractors/generic.d.ts +11 -0
- package/dist/core/parser/method-extractors/generic.js +162 -0
- package/dist/core/parser/method-extractors/generic.js.map +1 -0
- package/dist/core/parser/method-types.d.ts +110 -0
- package/dist/core/parser/method-types.js +2 -0
- package/dist/core/parser/method-types.js.map +1 -0
- package/dist/core/parser/mro-processor.d.ts +46 -0
- package/dist/core/parser/mro-processor.js +677 -0
- package/dist/core/parser/mro-processor.js.map +1 -0
- package/dist/core/parser/named-binding-processor.d.ts +18 -0
- package/dist/core/parser/named-binding-processor.js +43 -0
- package/dist/core/parser/named-binding-processor.js.map +1 -0
- package/dist/core/parser/named-bindings/csharp.d.ts +3 -0
- package/dist/core/parser/named-bindings/csharp.js +38 -0
- package/dist/core/parser/named-bindings/csharp.js.map +1 -0
- package/dist/core/parser/named-bindings/java.d.ts +3 -0
- package/dist/core/parser/named-bindings/java.js +30 -0
- package/dist/core/parser/named-bindings/java.js.map +1 -0
- package/dist/core/parser/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/parser/named-bindings/kotlin.js +37 -0
- package/dist/core/parser/named-bindings/kotlin.js.map +1 -0
- package/dist/core/parser/named-bindings/php.d.ts +3 -0
- package/dist/core/parser/named-bindings/php.js +62 -0
- package/dist/core/parser/named-bindings/php.js.map +1 -0
- package/dist/core/parser/named-bindings/python.d.ts +3 -0
- package/dist/core/parser/named-bindings/python.js +50 -0
- package/dist/core/parser/named-bindings/python.js.map +1 -0
- package/dist/core/parser/named-bindings/rust.d.ts +3 -0
- package/dist/core/parser/named-bindings/rust.js +67 -0
- package/dist/core/parser/named-bindings/rust.js.map +1 -0
- package/dist/core/parser/named-bindings/types.d.ts +16 -0
- package/dist/core/parser/named-bindings/types.js +7 -0
- package/dist/core/parser/named-bindings/types.js.map +1 -0
- package/dist/core/parser/named-bindings/typescript.d.ts +3 -0
- package/dist/core/parser/named-bindings/typescript.js +59 -0
- package/dist/core/parser/named-bindings/typescript.js.map +1 -0
- package/dist/core/parser/parsing-processor.d.ts +23 -0
- package/dist/core/parser/parsing-processor.js +464 -0
- package/dist/core/parser/parsing-processor.js.map +1 -0
- package/dist/core/parser/pipeline.d.ts +17 -0
- package/dist/core/parser/pipeline.js +1405 -0
- package/dist/core/parser/pipeline.js.map +1 -0
- package/dist/core/parser/process-processor.d.ts +51 -0
- package/dist/core/parser/process-processor.js +318 -0
- package/dist/core/parser/process-processor.js.map +1 -0
- package/dist/core/parser/resolution-context.d.ts +58 -0
- package/dist/core/parser/resolution-context.js +136 -0
- package/dist/core/parser/resolution-context.js.map +1 -0
- package/dist/core/parser/route-extractors/expo.d.ts +1 -0
- package/dist/core/parser/route-extractors/expo.js +37 -0
- package/dist/core/parser/route-extractors/expo.js.map +1 -0
- package/dist/core/parser/route-extractors/middleware.d.ts +47 -0
- package/dist/core/parser/route-extractors/middleware.js +168 -0
- package/dist/core/parser/route-extractors/middleware.js.map +1 -0
- package/dist/core/parser/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/parser/route-extractors/nextjs.js +77 -0
- package/dist/core/parser/route-extractors/nextjs.js.map +1 -0
- package/dist/core/parser/route-extractors/php.d.ts +7 -0
- package/dist/core/parser/route-extractors/php.js +23 -0
- package/dist/core/parser/route-extractors/php.js.map +1 -0
- package/dist/core/parser/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/parser/route-extractors/response-shapes.js +295 -0
- package/dist/core/parser/route-extractors/response-shapes.js.map +1 -0
- package/dist/core/parser/structure-processor.d.ts +2 -0
- package/dist/core/parser/structure-processor.js +37 -0
- package/dist/core/parser/structure-processor.js.map +1 -0
- package/dist/core/parser/symbol-table.d.ts +79 -0
- package/dist/core/parser/symbol-table.js +116 -0
- package/dist/core/parser/symbol-table.js.map +1 -0
- package/dist/core/parser/tree-sitter-queries.d.ts +16 -0
- package/dist/core/parser/tree-sitter-queries.js +1180 -0
- package/dist/core/parser/tree-sitter-queries.js.map +1 -0
- package/dist/core/parser/type-env.d.ts +81 -0
- package/dist/core/parser/type-env.js +1048 -0
- package/dist/core/parser/type-env.js.map +1 -0
- package/dist/core/parser/type-extractors/c-cpp.d.ts +7 -0
- package/dist/core/parser/type-extractors/c-cpp.js +533 -0
- package/dist/core/parser/type-extractors/c-cpp.js.map +1 -0
- package/dist/core/parser/type-extractors/csharp.d.ts +2 -0
- package/dist/core/parser/type-extractors/csharp.js +584 -0
- package/dist/core/parser/type-extractors/csharp.js.map +1 -0
- package/dist/core/parser/type-extractors/dart.d.ts +15 -0
- package/dist/core/parser/type-extractors/dart.js +370 -0
- package/dist/core/parser/type-extractors/dart.js.map +1 -0
- package/dist/core/parser/type-extractors/go.d.ts +2 -0
- package/dist/core/parser/type-extractors/go.js +514 -0
- package/dist/core/parser/type-extractors/go.js.map +1 -0
- package/dist/core/parser/type-extractors/jvm.d.ts +3 -0
- package/dist/core/parser/type-extractors/jvm.js +857 -0
- package/dist/core/parser/type-extractors/jvm.js.map +1 -0
- package/dist/core/parser/type-extractors/php.d.ts +2 -0
- package/dist/core/parser/type-extractors/php.js +535 -0
- package/dist/core/parser/type-extractors/php.js.map +1 -0
- package/dist/core/parser/type-extractors/python.d.ts +2 -0
- package/dist/core/parser/type-extractors/python.js +475 -0
- package/dist/core/parser/type-extractors/python.js.map +1 -0
- package/dist/core/parser/type-extractors/ruby.d.ts +2 -0
- package/dist/core/parser/type-extractors/ruby.js +378 -0
- package/dist/core/parser/type-extractors/ruby.js.map +1 -0
- package/dist/core/parser/type-extractors/rust.d.ts +2 -0
- package/dist/core/parser/type-extractors/rust.js +516 -0
- package/dist/core/parser/type-extractors/rust.js.map +1 -0
- package/dist/core/parser/type-extractors/shared.d.ts +131 -0
- package/dist/core/parser/type-extractors/shared.js +797 -0
- package/dist/core/parser/type-extractors/shared.js.map +1 -0
- package/dist/core/parser/type-extractors/swift.d.ts +2 -0
- package/dist/core/parser/type-extractors/swift.js +485 -0
- package/dist/core/parser/type-extractors/swift.js.map +1 -0
- package/dist/core/parser/type-extractors/types.d.ts +172 -0
- package/dist/core/parser/type-extractors/types.js +2 -0
- package/dist/core/parser/type-extractors/types.js.map +1 -0
- package/dist/core/parser/type-extractors/typescript.d.ts +2 -0
- package/dist/core/parser/type-extractors/typescript.js +662 -0
- package/dist/core/parser/type-extractors/typescript.js.map +1 -0
- package/dist/core/parser/utils/ast-helpers.d.ts +73 -0
- package/dist/core/parser/utils/ast-helpers.js +415 -0
- package/dist/core/parser/utils/ast-helpers.js.map +1 -0
- package/dist/core/parser/utils/call-analysis.d.ts +75 -0
- package/dist/core/parser/utils/call-analysis.js +575 -0
- package/dist/core/parser/utils/call-analysis.js.map +1 -0
- package/dist/core/parser/utils/event-loop.d.ts +5 -0
- package/dist/core/parser/utils/event-loop.js +6 -0
- package/dist/core/parser/utils/event-loop.js.map +1 -0
- package/dist/core/parser/utils/method-props.d.ts +8 -0
- package/dist/core/parser/utils/method-props.js +39 -0
- package/dist/core/parser/utils/method-props.js.map +1 -0
- package/dist/core/parser/utils/verbose.d.ts +1 -0
- package/dist/core/parser/utils/verbose.js +8 -0
- package/dist/core/parser/utils/verbose.js.map +1 -0
- package/dist/core/parser/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/parser/vue-sfc-extractor.js +95 -0
- package/dist/core/parser/vue-sfc-extractor.js.map +1 -0
- package/dist/core/parser/workers/parse-worker.d.ts +171 -0
- package/dist/core/parser/workers/parse-worker.js +1724 -0
- package/dist/core/parser/workers/parse-worker.js.map +1 -0
- package/dist/core/parser/workers/worker-pool.d.ts +16 -0
- package/dist/core/parser/workers/worker-pool.js +124 -0
- package/dist/core/parser/workers/worker-pool.js.map +1 -0
- package/dist/core/shared/graph-types.d.ts +61 -0
- package/dist/core/shared/graph-types.js +5 -0
- package/dist/core/shared/graph-types.js.map +1 -0
- package/dist/core/shared/index.d.ts +4 -0
- package/dist/core/shared/index.js +4 -0
- package/dist/core/shared/index.js.map +1 -0
- package/dist/core/shared/language-detection.d.ts +22 -0
- package/dist/core/shared/language-detection.js +137 -0
- package/dist/core/shared/language-detection.js.map +1 -0
- package/dist/core/shared/languages.d.ts +23 -0
- package/dist/core/shared/languages.js +25 -0
- package/dist/core/shared/languages.js.map +1 -0
- package/dist/core/shared/pipeline.d.ts +15 -0
- package/dist/core/shared/pipeline.js +5 -0
- package/dist/core/shared/pipeline.js.map +1 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +5 -0
- package/dist/core/tree-sitter/parser-loader.js +71 -0
- package/dist/core/tree-sitter/parser-loader.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/hash.d.ts +1 -0
- package/dist/lib/hash.js +6 -0
- package/dist/lib/hash.js.map +1 -0
- package/dist/lib/naming.d.ts +12 -0
- package/dist/lib/naming.js +28 -0
- package/dist/lib/naming.js.map +1 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +4 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/mcp/server.d.ts +26 -0
- package/dist/mcp/server.js +282 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +37 -0
- package/dist/mcp/tools.js +650 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/query/bm25.d.ts +19 -0
- package/dist/query/bm25.js +60 -0
- package/dist/query/bm25.js.map +1 -0
- package/dist/query/graph-index.d.ts +40 -0
- package/dist/query/graph-index.js +178 -0
- package/dist/query/graph-index.js.map +1 -0
- package/dist/store/derived-index.d.ts +4 -0
- package/dist/store/derived-index.js +68 -0
- package/dist/store/derived-index.js.map +1 -0
- package/dist/store/meta.d.ts +1 -0
- package/dist/store/meta.js +3 -0
- package/dist/store/meta.js.map +1 -0
- package/dist/store/schema.d.ts +135 -0
- package/dist/store/schema.js +2 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/store.d.ts +49 -0
- package/dist/store/store.js +254 -0
- package/dist/store/store.js.map +1 -0
- package/dist/types/pipeline.d.ts +12 -0
- package/dist/types/pipeline.js +2 -0
- package/dist/types/pipeline.js.map +1 -0
- package/package.json +69 -0
- package/skills/cotx-enrich/SKILL.md +59 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,1724 @@
|
|
|
1
|
+
import { parentPort } from 'node:worker_threads';
|
|
2
|
+
import Parser from 'tree-sitter';
|
|
3
|
+
import JavaScript from 'tree-sitter-javascript';
|
|
4
|
+
import TypeScript from 'tree-sitter-typescript';
|
|
5
|
+
import Python from 'tree-sitter-python';
|
|
6
|
+
import Java from 'tree-sitter-java';
|
|
7
|
+
import C from 'tree-sitter-c';
|
|
8
|
+
import CPP from 'tree-sitter-cpp';
|
|
9
|
+
import CSharp from 'tree-sitter-c-sharp';
|
|
10
|
+
import Go from 'tree-sitter-go';
|
|
11
|
+
import Rust from 'tree-sitter-rust';
|
|
12
|
+
import PHP from 'tree-sitter-php';
|
|
13
|
+
import Ruby from 'tree-sitter-ruby';
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
15
|
+
import { SupportedLanguages } from '../../shared/index.js';
|
|
16
|
+
import { getProvider } from '../languages/index.js';
|
|
17
|
+
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
18
|
+
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
19
|
+
const _require = createRequire(import.meta.url);
|
|
20
|
+
let Swift = null;
|
|
21
|
+
try {
|
|
22
|
+
Swift = _require('tree-sitter-swift');
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
// tree-sitter-dart is an optionalDependency — may not be installed
|
|
26
|
+
let Dart = null;
|
|
27
|
+
try {
|
|
28
|
+
Dart = _require('tree-sitter-dart');
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
// tree-sitter-kotlin is an optionalDependency — may not be installed
|
|
32
|
+
let Kotlin = null;
|
|
33
|
+
try {
|
|
34
|
+
Kotlin = _require('tree-sitter-kotlin');
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
import { getLanguageFromFilename } from '../../shared/index.js';
|
|
38
|
+
import { FUNCTION_NODE_TYPES, getDefinitionNodeFromCaptures, findEnclosingClassInfo, getLabelFromCaptures, findDescendant, extractStringContent, genericFuncName, inferFunctionLabel, CLASS_CONTAINER_TYPES, } from '../utils/ast-helpers.js';
|
|
39
|
+
import { countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, extractCallArgTypes, } from '../utils/call-analysis.js';
|
|
40
|
+
import { extractParsedCallSite } from '../call-sites/extract-language-call-site.js';
|
|
41
|
+
import { buildTypeEnv } from '../type-env.js';
|
|
42
|
+
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
43
|
+
import { generateId } from '../../../lib/utils.js';
|
|
44
|
+
import { preprocessImportPath } from '../import-processor.js';
|
|
45
|
+
import { extractVueScript, extractTemplateComponents, isVueSetupTopLevel, } from '../vue-sfc-extractor.js';
|
|
46
|
+
import { buildMethodProps, arityForIdFromInfo } from '../utils/method-props.js';
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Worker-local parser + language map
|
|
49
|
+
// ============================================================================
|
|
50
|
+
const parser = new Parser();
|
|
51
|
+
const languageMap = {
|
|
52
|
+
[SupportedLanguages.JavaScript]: JavaScript,
|
|
53
|
+
[SupportedLanguages.TypeScript]: TypeScript.typescript,
|
|
54
|
+
[`${SupportedLanguages.TypeScript}:tsx`]: TypeScript.tsx,
|
|
55
|
+
[SupportedLanguages.Python]: Python,
|
|
56
|
+
[SupportedLanguages.Java]: Java,
|
|
57
|
+
[SupportedLanguages.C]: C,
|
|
58
|
+
[SupportedLanguages.CPlusPlus]: CPP,
|
|
59
|
+
[SupportedLanguages.CSharp]: CSharp,
|
|
60
|
+
[SupportedLanguages.Go]: Go,
|
|
61
|
+
[SupportedLanguages.Rust]: Rust,
|
|
62
|
+
...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
63
|
+
[SupportedLanguages.PHP]: PHP.php_only,
|
|
64
|
+
[SupportedLanguages.Ruby]: Ruby,
|
|
65
|
+
[SupportedLanguages.Vue]: TypeScript.typescript,
|
|
66
|
+
...(Dart ? { [SupportedLanguages.Dart]: Dart } : {}),
|
|
67
|
+
...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Check if a language grammar is available in this worker.
|
|
71
|
+
* Duplicated from parser-loader.ts because workers can't import from the main thread.
|
|
72
|
+
* Extra filePath parameter needed to distinguish .tsx from .ts (different grammars
|
|
73
|
+
* under the same SupportedLanguages.TypeScript key).
|
|
74
|
+
*/
|
|
75
|
+
const isLanguageAvailable = (language, filePath) => {
|
|
76
|
+
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
77
|
+
? `${language}:tsx`
|
|
78
|
+
: language;
|
|
79
|
+
return key in languageMap && languageMap[key] != null;
|
|
80
|
+
};
|
|
81
|
+
const setLanguage = (language, filePath) => {
|
|
82
|
+
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
83
|
+
? `${language}:tsx`
|
|
84
|
+
: language;
|
|
85
|
+
const lang = languageMap[key];
|
|
86
|
+
if (!lang)
|
|
87
|
+
throw new Error(`Unsupported language: ${language}`);
|
|
88
|
+
parser.setLanguage(lang);
|
|
89
|
+
};
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Per-file O(1) memoization — avoids repeated parent-chain walks per symbol.
|
|
92
|
+
// Three bare Maps cleared at file boundaries. Map.get() returns undefined for
|
|
93
|
+
// missing keys, so `cached !== undefined` distinguishes "not computed" from
|
|
94
|
+
// a stored null (enclosing class/function not found = top-level).
|
|
95
|
+
// ============================================================================
|
|
96
|
+
const classIdCache = new Map();
|
|
97
|
+
const functionIdCache = new Map();
|
|
98
|
+
const exportCache = new Map();
|
|
99
|
+
const clearCaches = () => {
|
|
100
|
+
classIdCache.clear();
|
|
101
|
+
functionIdCache.clear();
|
|
102
|
+
exportCache.clear();
|
|
103
|
+
fieldInfoCache.clear();
|
|
104
|
+
methodInfoCache.clear();
|
|
105
|
+
};
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// FieldExtractor cache — extract field metadata once per class, reuse for each property.
|
|
108
|
+
// Keyed by class node startIndex (unique per AST node within a file).
|
|
109
|
+
// ============================================================================
|
|
110
|
+
const fieldInfoCache = new Map();
|
|
111
|
+
/**
|
|
112
|
+
* Walk up from a definition node to find the nearest enclosing class/struct/interface
|
|
113
|
+
* AST node. Returns the SyntaxNode itself (not an ID) for passing to FieldExtractor.
|
|
114
|
+
*/
|
|
115
|
+
function findEnclosingClassNode(node) {
|
|
116
|
+
let current = node.parent;
|
|
117
|
+
while (current) {
|
|
118
|
+
if (CLASS_CONTAINER_TYPES.has(current.type)) {
|
|
119
|
+
// Ruby singleton_class (class << self) has no name field — walk up to
|
|
120
|
+
// the enclosing class/module so the caller gets a node with a findable name.
|
|
121
|
+
if (current.type === 'singleton_class') {
|
|
122
|
+
current = current.parent;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
return current;
|
|
126
|
+
}
|
|
127
|
+
current = current.parent;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* For C++ out-of-class method definitions (e.g. `void Foo::bar() {}`), extract the
|
|
133
|
+
* class name from the qualified_identifier scope and find the class declaration in the
|
|
134
|
+
* file's AST. Returns the class SyntaxNode or null if not found.
|
|
135
|
+
*
|
|
136
|
+
* Handles pointer/reference return types where function_declarator is nested inside
|
|
137
|
+
* pointer_declarator or reference_declarator.
|
|
138
|
+
*/
|
|
139
|
+
function findClassNodeByQualifiedName(node) {
|
|
140
|
+
const declarator = node.childForFieldName('declarator');
|
|
141
|
+
if (!declarator)
|
|
142
|
+
return null;
|
|
143
|
+
// Find the function_declarator, recursively unwrapping pointer_declarator /
|
|
144
|
+
// reference_declarator chains (e.g. int** Foo::bar() has
|
|
145
|
+
// pointer_declarator → pointer_declarator → function_declarator).
|
|
146
|
+
let funcDecl = null;
|
|
147
|
+
if (declarator.type === 'function_declarator') {
|
|
148
|
+
funcDecl = declarator;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
let current = declarator;
|
|
152
|
+
while (current && !funcDecl) {
|
|
153
|
+
for (let i = 0; i < current.namedChildCount; i++) {
|
|
154
|
+
const child = current.namedChild(i);
|
|
155
|
+
if (child?.type === 'function_declarator') {
|
|
156
|
+
funcDecl = child;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!funcDecl) {
|
|
161
|
+
const next = current.namedChildren.find((c) => c.type === 'pointer_declarator' || c.type === 'reference_declarator');
|
|
162
|
+
current = next ?? null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!funcDecl)
|
|
167
|
+
return null;
|
|
168
|
+
// Check if the inner declarator is a qualified_identifier (Foo::bar)
|
|
169
|
+
const innerDecl = funcDecl.childForFieldName('declarator');
|
|
170
|
+
if (!innerDecl || innerDecl.type !== 'qualified_identifier')
|
|
171
|
+
return null;
|
|
172
|
+
const scope = innerDecl.childForFieldName('scope');
|
|
173
|
+
if (!scope)
|
|
174
|
+
return null;
|
|
175
|
+
const className = scope.text;
|
|
176
|
+
// Search the file for a matching class/struct specifier, including inside
|
|
177
|
+
// namespace_definition blocks (the majority of production C++ uses namespaces).
|
|
178
|
+
const root = node.tree.rootNode;
|
|
179
|
+
const classTypes = new Set(['class_specifier', 'struct_specifier']);
|
|
180
|
+
const searchIn = (parent) => {
|
|
181
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
182
|
+
const child = parent.namedChild(i);
|
|
183
|
+
if (!child)
|
|
184
|
+
continue;
|
|
185
|
+
if (classTypes.has(child.type)) {
|
|
186
|
+
const nameNode = child.childForFieldName('name');
|
|
187
|
+
if (nameNode?.text === className)
|
|
188
|
+
return child;
|
|
189
|
+
}
|
|
190
|
+
// Recurse into namespace blocks
|
|
191
|
+
if (child.type === 'namespace_definition') {
|
|
192
|
+
const found = searchIn(child);
|
|
193
|
+
if (found)
|
|
194
|
+
return found;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
};
|
|
199
|
+
return searchIn(root);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Minimal no-op SymbolTable stub for FieldExtractorContext in the worker.
|
|
203
|
+
* Field extraction only uses symbolTable.lookupExactAll for optional type resolution —
|
|
204
|
+
* returning [] causes the extractor to use the raw type string, which is fine for us.
|
|
205
|
+
*/
|
|
206
|
+
const NOOP_SYMBOL_TABLE = {
|
|
207
|
+
lookupExactAll: () => [],
|
|
208
|
+
lookupExact: () => undefined,
|
|
209
|
+
lookupExactFull: () => undefined,
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Get (or extract and cache) field info for a class node.
|
|
213
|
+
* Returns a name→FieldInfo map, or undefined if the provider has no field extractor
|
|
214
|
+
* or the class yielded no fields.
|
|
215
|
+
*/
|
|
216
|
+
function getFieldInfo(classNode, provider, context) {
|
|
217
|
+
if (!provider.fieldExtractor)
|
|
218
|
+
return undefined;
|
|
219
|
+
const cacheKey = classNode.startIndex;
|
|
220
|
+
let cached = fieldInfoCache.get(cacheKey);
|
|
221
|
+
if (cached)
|
|
222
|
+
return cached;
|
|
223
|
+
const result = provider.fieldExtractor.extract(classNode, context);
|
|
224
|
+
if (!result?.fields?.length)
|
|
225
|
+
return undefined;
|
|
226
|
+
cached = new Map();
|
|
227
|
+
for (const field of result.fields) {
|
|
228
|
+
cached.set(field.name, field);
|
|
229
|
+
}
|
|
230
|
+
fieldInfoCache.set(cacheKey, cached);
|
|
231
|
+
return cached;
|
|
232
|
+
}
|
|
233
|
+
// ============================================================================
|
|
234
|
+
// MethodExtractor cache — extract method metadata once per class, reuse for each method.
|
|
235
|
+
// Keyed by class node startIndex (unique per AST node within a file).
|
|
236
|
+
// ============================================================================
|
|
237
|
+
const methodInfoCache = new Map();
|
|
238
|
+
/**
|
|
239
|
+
* Get (or extract and cache) method info for a class node.
|
|
240
|
+
* Returns a "name:line" → MethodInfo map, or undefined if the provider has no method extractor
|
|
241
|
+
* or the class yielded no methods.
|
|
242
|
+
* Keyed by name:line (not name alone) to support overloaded methods in Java/Kotlin.
|
|
243
|
+
*/
|
|
244
|
+
function getMethodInfo(classNode, provider, context) {
|
|
245
|
+
if (!provider.methodExtractor)
|
|
246
|
+
return undefined;
|
|
247
|
+
const cacheKey = classNode.startIndex;
|
|
248
|
+
let cached = methodInfoCache.get(cacheKey);
|
|
249
|
+
if (cached)
|
|
250
|
+
return cached;
|
|
251
|
+
const result = provider.methodExtractor.extract(classNode, context);
|
|
252
|
+
if (!result?.methods?.length)
|
|
253
|
+
return undefined;
|
|
254
|
+
cached = new Map();
|
|
255
|
+
for (const method of result.methods) {
|
|
256
|
+
cached.set(`${method.name}:${method.line}`, method);
|
|
257
|
+
}
|
|
258
|
+
methodInfoCache.set(cacheKey, cached);
|
|
259
|
+
return cached;
|
|
260
|
+
}
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Enclosing function detection (for call extraction) — cached
|
|
263
|
+
// ============================================================================
|
|
264
|
+
/** Walk up AST to find enclosing function, return its generateId or null for top-level.
|
|
265
|
+
* Applies provider.labelOverride so the label matches the definition phase (single source of truth). */
|
|
266
|
+
const findEnclosingFunctionId = (node, filePath, provider) => {
|
|
267
|
+
const cached = functionIdCache.get(node);
|
|
268
|
+
if (cached !== undefined)
|
|
269
|
+
return cached;
|
|
270
|
+
let current = node.parent;
|
|
271
|
+
while (current) {
|
|
272
|
+
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
273
|
+
const efnResult = provider.methodExtractor?.extractFunctionName?.(current);
|
|
274
|
+
const funcName = efnResult?.funcName ?? genericFuncName(current);
|
|
275
|
+
const label = efnResult?.label ?? inferFunctionLabel(current.type);
|
|
276
|
+
if (funcName) {
|
|
277
|
+
// Apply labelOverride so label matches definition phase (e.g., Kotlin Function→Method).
|
|
278
|
+
// null means "skip as definition" — keep original label for scope identification.
|
|
279
|
+
let finalLabel = label;
|
|
280
|
+
if (provider.labelOverride) {
|
|
281
|
+
const override = provider.labelOverride(current, label);
|
|
282
|
+
if (override !== null)
|
|
283
|
+
finalLabel = override;
|
|
284
|
+
}
|
|
285
|
+
// Qualify with enclosing class to match definition-phase node IDs
|
|
286
|
+
const classInfo = cachedFindEnclosingClassInfo(current, filePath);
|
|
287
|
+
const qualifiedName = classInfo ? `${classInfo.className}.${funcName}` : funcName;
|
|
288
|
+
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
|
289
|
+
// Use the same MethodExtractor (getMethodInfo) as the definition phase.
|
|
290
|
+
let arity;
|
|
291
|
+
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
|
292
|
+
const classNode = findEnclosingClassNode(current) ?? findClassNodeByQualifiedName(current);
|
|
293
|
+
if (classNode) {
|
|
294
|
+
const methodMap = getMethodInfo(classNode, provider, {
|
|
295
|
+
filePath,
|
|
296
|
+
language: getLanguageFromFilename(filePath),
|
|
297
|
+
});
|
|
298
|
+
const defLine = current.startPosition.row + 1;
|
|
299
|
+
const info = methodMap?.get(`${funcName}:${defLine}`);
|
|
300
|
+
if (info) {
|
|
301
|
+
arity = info.parameters.some((p) => p.isVariadic)
|
|
302
|
+
? undefined
|
|
303
|
+
: info.parameters.length;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const arityTag = arity !== undefined ? `#${arity}` : '';
|
|
308
|
+
const result = generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag}`);
|
|
309
|
+
functionIdCache.set(node, result);
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Language-specific enclosing function resolution (e.g., Dart where
|
|
314
|
+
// function_body is a sibling of function_signature, not a child).
|
|
315
|
+
if (provider.enclosingFunctionFinder) {
|
|
316
|
+
const customResult = provider.enclosingFunctionFinder(current);
|
|
317
|
+
if (customResult) {
|
|
318
|
+
let finalLabel = customResult.label;
|
|
319
|
+
if (provider.labelOverride) {
|
|
320
|
+
const override = provider.labelOverride(current.previousSibling, finalLabel);
|
|
321
|
+
if (override !== null)
|
|
322
|
+
finalLabel = override;
|
|
323
|
+
}
|
|
324
|
+
// Qualify custom result with enclosing class
|
|
325
|
+
const classInfo = cachedFindEnclosingClassInfo(current.previousSibling ?? current, filePath);
|
|
326
|
+
const qualifiedName = classInfo
|
|
327
|
+
? `${classInfo.className}.${customResult.funcName}`
|
|
328
|
+
: customResult.funcName;
|
|
329
|
+
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
|
330
|
+
const sigNode = current.previousSibling ?? current;
|
|
331
|
+
let arity2;
|
|
332
|
+
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
|
333
|
+
const classNode2 = findEnclosingClassNode(sigNode) ?? findClassNodeByQualifiedName(sigNode);
|
|
334
|
+
if (classNode2) {
|
|
335
|
+
const methodMap2 = getMethodInfo(classNode2, provider, {
|
|
336
|
+
filePath,
|
|
337
|
+
language: getLanguageFromFilename(filePath),
|
|
338
|
+
});
|
|
339
|
+
const defLine2 = sigNode.startPosition.row + 1;
|
|
340
|
+
const info2 = methodMap2?.get(`${customResult.funcName}:${defLine2}`);
|
|
341
|
+
if (info2) {
|
|
342
|
+
arity2 = info2.parameters.some((p) => p.isVariadic)
|
|
343
|
+
? undefined
|
|
344
|
+
: info2.parameters.length;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const arityTag2 = arity2 !== undefined ? `#${arity2}` : '';
|
|
349
|
+
const result = generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag2}`);
|
|
350
|
+
functionIdCache.set(node, result);
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
current = current.parent;
|
|
355
|
+
}
|
|
356
|
+
functionIdCache.set(node, null);
|
|
357
|
+
return null;
|
|
358
|
+
};
|
|
359
|
+
/** Cached wrapper for findEnclosingClassInfo — avoids repeated parent walks. */
|
|
360
|
+
const cachedFindEnclosingClassInfo = (node, filePath) => {
|
|
361
|
+
const cached = classIdCache.get(node);
|
|
362
|
+
if (cached !== undefined)
|
|
363
|
+
return cached;
|
|
364
|
+
const result = findEnclosingClassInfo(node, filePath);
|
|
365
|
+
classIdCache.set(node, result);
|
|
366
|
+
return result;
|
|
367
|
+
};
|
|
368
|
+
/** Cached wrapper for export checking — avoids repeated parent walks per symbol. */
|
|
369
|
+
const cachedExportCheck = (checker, node, name) => {
|
|
370
|
+
const cached = exportCache.get(node);
|
|
371
|
+
if (cached !== undefined)
|
|
372
|
+
return cached;
|
|
373
|
+
const result = checker(node, name);
|
|
374
|
+
exportCache.set(node, result);
|
|
375
|
+
return result;
|
|
376
|
+
};
|
|
377
|
+
// Label detection moved to shared getLabelFromCaptures in utils.ts
|
|
378
|
+
// DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
|
|
379
|
+
// ============================================================================
|
|
380
|
+
// Process a batch of files
|
|
381
|
+
// ============================================================================
|
|
382
|
+
const processBatch = (files, onProgress) => {
|
|
383
|
+
const result = {
|
|
384
|
+
nodes: [],
|
|
385
|
+
relationships: [],
|
|
386
|
+
symbols: [],
|
|
387
|
+
imports: [],
|
|
388
|
+
calls: [],
|
|
389
|
+
assignments: [],
|
|
390
|
+
heritage: [],
|
|
391
|
+
routes: [],
|
|
392
|
+
fetchCalls: [],
|
|
393
|
+
decoratorRoutes: [],
|
|
394
|
+
toolDefs: [],
|
|
395
|
+
ormQueries: [],
|
|
396
|
+
constructorBindings: [],
|
|
397
|
+
typeEnvBindings: [],
|
|
398
|
+
skippedLanguages: {},
|
|
399
|
+
fileCount: 0,
|
|
400
|
+
};
|
|
401
|
+
// Group by language to minimize setLanguage calls
|
|
402
|
+
const byLanguage = new Map();
|
|
403
|
+
for (const file of files) {
|
|
404
|
+
const lang = getLanguageFromFilename(file.path);
|
|
405
|
+
if (!lang)
|
|
406
|
+
continue;
|
|
407
|
+
let list = byLanguage.get(lang);
|
|
408
|
+
if (!list) {
|
|
409
|
+
list = [];
|
|
410
|
+
byLanguage.set(lang, list);
|
|
411
|
+
}
|
|
412
|
+
list.push(file);
|
|
413
|
+
}
|
|
414
|
+
let totalProcessed = 0;
|
|
415
|
+
let lastReported = 0;
|
|
416
|
+
const PROGRESS_INTERVAL = 100; // report every 100 files
|
|
417
|
+
const onFileProcessed = onProgress
|
|
418
|
+
? () => {
|
|
419
|
+
totalProcessed++;
|
|
420
|
+
if (totalProcessed - lastReported >= PROGRESS_INTERVAL) {
|
|
421
|
+
lastReported = totalProcessed;
|
|
422
|
+
onProgress(totalProcessed);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
: undefined;
|
|
426
|
+
for (const [language, langFiles] of byLanguage) {
|
|
427
|
+
const provider = getProvider(language);
|
|
428
|
+
const queryString = provider.treeSitterQueries;
|
|
429
|
+
if (!queryString)
|
|
430
|
+
continue;
|
|
431
|
+
// Track if we need to handle tsx separately
|
|
432
|
+
const tsxFiles = [];
|
|
433
|
+
const regularFiles = [];
|
|
434
|
+
if (language === SupportedLanguages.TypeScript) {
|
|
435
|
+
for (const f of langFiles) {
|
|
436
|
+
if (f.path.endsWith('.tsx')) {
|
|
437
|
+
tsxFiles.push(f);
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
regularFiles.push(f);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
regularFiles.push(...langFiles);
|
|
446
|
+
}
|
|
447
|
+
// Process regular files for this language
|
|
448
|
+
if (regularFiles.length > 0) {
|
|
449
|
+
if (isLanguageAvailable(language, regularFiles[0].path)) {
|
|
450
|
+
try {
|
|
451
|
+
setLanguage(language, regularFiles[0].path);
|
|
452
|
+
processFileGroup(regularFiles, language, queryString, result, onFileProcessed);
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
// parser unavailable — skip this language group
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
result.skippedLanguages[language] =
|
|
460
|
+
(result.skippedLanguages[language] || 0) + regularFiles.length;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// Process tsx files separately (different grammar)
|
|
464
|
+
if (tsxFiles.length > 0) {
|
|
465
|
+
if (isLanguageAvailable(language, tsxFiles[0].path)) {
|
|
466
|
+
try {
|
|
467
|
+
setLanguage(language, tsxFiles[0].path);
|
|
468
|
+
processFileGroup(tsxFiles, language, queryString, result, onFileProcessed);
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
// parser unavailable — skip this language group
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
result.skippedLanguages[language] =
|
|
476
|
+
(result.skippedLanguages[language] || 0) + tsxFiles.length;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return result;
|
|
481
|
+
};
|
|
482
|
+
const ROUTE_HTTP_METHODS = new Set([
|
|
483
|
+
'get',
|
|
484
|
+
'post',
|
|
485
|
+
'put',
|
|
486
|
+
'patch',
|
|
487
|
+
'delete',
|
|
488
|
+
'options',
|
|
489
|
+
'any',
|
|
490
|
+
'match',
|
|
491
|
+
]);
|
|
492
|
+
const ROUTE_RESOURCE_METHODS = new Set(['resource', 'apiResource']);
|
|
493
|
+
// Express/Hono method names that register routes
|
|
494
|
+
const EXPRESS_ROUTE_METHODS = new Set([
|
|
495
|
+
'get',
|
|
496
|
+
'post',
|
|
497
|
+
'put',
|
|
498
|
+
'delete',
|
|
499
|
+
'patch',
|
|
500
|
+
'all',
|
|
501
|
+
'use',
|
|
502
|
+
'route',
|
|
503
|
+
]);
|
|
504
|
+
// HTTP client methods that are ONLY used by clients, not Express route registration.
|
|
505
|
+
// Methods like get/post/put/delete/patch overlap with Express — those are captured by
|
|
506
|
+
// the express_route handler as route definitions, not consumers. The fetch() global
|
|
507
|
+
// function is captured separately by the route.fetch query.
|
|
508
|
+
const HTTP_CLIENT_ONLY_METHODS = new Set(['head', 'options', 'request', 'ajax']);
|
|
509
|
+
// Decorator names that indicate HTTP route handlers (NestJS, Flask, FastAPI, Spring)
|
|
510
|
+
const ROUTE_DECORATOR_NAMES = new Set([
|
|
511
|
+
'Get',
|
|
512
|
+
'Post',
|
|
513
|
+
'Put',
|
|
514
|
+
'Delete',
|
|
515
|
+
'Patch',
|
|
516
|
+
'Route',
|
|
517
|
+
'get',
|
|
518
|
+
'post',
|
|
519
|
+
'put',
|
|
520
|
+
'delete',
|
|
521
|
+
'patch',
|
|
522
|
+
'route',
|
|
523
|
+
'RequestMapping',
|
|
524
|
+
'GetMapping',
|
|
525
|
+
'PostMapping',
|
|
526
|
+
'PutMapping',
|
|
527
|
+
'DeleteMapping',
|
|
528
|
+
]);
|
|
529
|
+
const RESOURCE_ACTIONS = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy'];
|
|
530
|
+
const API_RESOURCE_ACTIONS = ['index', 'store', 'show', 'update', 'destroy'];
|
|
531
|
+
/** Check if node is a scoped_call_expression with object 'Route' */
|
|
532
|
+
function isRouteStaticCall(node) {
|
|
533
|
+
if (node.type !== 'scoped_call_expression')
|
|
534
|
+
return false;
|
|
535
|
+
const obj = node.childForFieldName?.('object') ?? node.children?.[0];
|
|
536
|
+
return obj?.text === 'Route';
|
|
537
|
+
}
|
|
538
|
+
/** Get the method name from a scoped_call_expression or member_call_expression */
|
|
539
|
+
function getCallMethodName(node) {
|
|
540
|
+
const nameNode = node.childForFieldName?.('name') ?? node.children?.find((c) => c.type === 'name');
|
|
541
|
+
return nameNode?.text ?? null;
|
|
542
|
+
}
|
|
543
|
+
/** Get the arguments node from a call expression */
|
|
544
|
+
function getArguments(node) {
|
|
545
|
+
return node.children?.find((c) => c.type === 'arguments') ?? null;
|
|
546
|
+
}
|
|
547
|
+
/** Find the closure body inside arguments */
|
|
548
|
+
function findClosureBody(argsNode) {
|
|
549
|
+
if (!argsNode)
|
|
550
|
+
return null;
|
|
551
|
+
for (const child of argsNode.children ?? []) {
|
|
552
|
+
if (child.type === 'argument') {
|
|
553
|
+
for (const inner of child.children ?? []) {
|
|
554
|
+
if (inner.type === 'anonymous_function' || inner.type === 'arrow_function') {
|
|
555
|
+
return (inner.childForFieldName?.('body') ??
|
|
556
|
+
inner.children?.find((c) => c.type === 'compound_statement') ??
|
|
557
|
+
null);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (child.type === 'anonymous_function' || child.type === 'arrow_function') {
|
|
562
|
+
return (child.childForFieldName?.('body') ??
|
|
563
|
+
child.children?.find((c) => c.type === 'compound_statement') ??
|
|
564
|
+
null);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
/** Extract first string argument from arguments node */
|
|
570
|
+
function extractFirstStringArg(argsNode) {
|
|
571
|
+
if (!argsNode)
|
|
572
|
+
return null;
|
|
573
|
+
for (const child of argsNode.children ?? []) {
|
|
574
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
575
|
+
if (!target)
|
|
576
|
+
continue;
|
|
577
|
+
if (target.type === 'string' || target.type === 'encapsed_string') {
|
|
578
|
+
return extractStringContent(target);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
/** Extract middleware from arguments — handles string or array */
|
|
584
|
+
function extractMiddlewareArg(argsNode) {
|
|
585
|
+
if (!argsNode)
|
|
586
|
+
return [];
|
|
587
|
+
for (const child of argsNode.children ?? []) {
|
|
588
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
589
|
+
if (!target)
|
|
590
|
+
continue;
|
|
591
|
+
if (target.type === 'string' || target.type === 'encapsed_string') {
|
|
592
|
+
const val = extractStringContent(target);
|
|
593
|
+
return val ? [val] : [];
|
|
594
|
+
}
|
|
595
|
+
if (target.type === 'array_creation_expression') {
|
|
596
|
+
const items = [];
|
|
597
|
+
for (const el of target.children ?? []) {
|
|
598
|
+
if (el.type === 'array_element_initializer') {
|
|
599
|
+
const str = el.children?.find((c) => c.type === 'string' || c.type === 'encapsed_string');
|
|
600
|
+
const val = str ? extractStringContent(str) : null;
|
|
601
|
+
if (val)
|
|
602
|
+
items.push(val);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return items;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return [];
|
|
609
|
+
}
|
|
610
|
+
/** Extract Controller::class from arguments */
|
|
611
|
+
function extractClassArg(argsNode) {
|
|
612
|
+
if (!argsNode)
|
|
613
|
+
return null;
|
|
614
|
+
for (const child of argsNode.children ?? []) {
|
|
615
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
616
|
+
if (target?.type === 'class_constant_access_expression') {
|
|
617
|
+
return target.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
/** Extract controller class name from arguments: [Controller::class, 'method'] or 'Controller@method' */
|
|
623
|
+
function extractControllerTarget(argsNode) {
|
|
624
|
+
if (!argsNode)
|
|
625
|
+
return { controller: null, method: null };
|
|
626
|
+
const args = [];
|
|
627
|
+
for (const child of argsNode.children ?? []) {
|
|
628
|
+
if (child.type === 'argument')
|
|
629
|
+
args.push(child.children?.[0]);
|
|
630
|
+
else if (child.type !== '(' && child.type !== ')' && child.type !== ',')
|
|
631
|
+
args.push(child);
|
|
632
|
+
}
|
|
633
|
+
// Second arg is the handler
|
|
634
|
+
const handlerNode = args[1];
|
|
635
|
+
if (!handlerNode)
|
|
636
|
+
return { controller: null, method: null };
|
|
637
|
+
// Array syntax: [UserController::class, 'index']
|
|
638
|
+
if (handlerNode.type === 'array_creation_expression') {
|
|
639
|
+
let controller = null;
|
|
640
|
+
let method = null;
|
|
641
|
+
const elements = [];
|
|
642
|
+
for (const el of handlerNode.children ?? []) {
|
|
643
|
+
if (el.type === 'array_element_initializer')
|
|
644
|
+
elements.push(el);
|
|
645
|
+
}
|
|
646
|
+
if (elements[0]) {
|
|
647
|
+
const classAccess = findDescendant(elements[0], 'class_constant_access_expression');
|
|
648
|
+
if (classAccess) {
|
|
649
|
+
controller = classAccess.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (elements[1]) {
|
|
653
|
+
const str = findDescendant(elements[1], 'string');
|
|
654
|
+
method = str ? extractStringContent(str) : null;
|
|
655
|
+
}
|
|
656
|
+
return { controller, method };
|
|
657
|
+
}
|
|
658
|
+
// String syntax: 'UserController@index'
|
|
659
|
+
if (handlerNode.type === 'string' || handlerNode.type === 'encapsed_string') {
|
|
660
|
+
const text = extractStringContent(handlerNode);
|
|
661
|
+
if (text?.includes('@')) {
|
|
662
|
+
const [controller, method] = text.split('@');
|
|
663
|
+
return { controller, method };
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
// Class reference: UserController::class (invokable controller)
|
|
667
|
+
if (handlerNode.type === 'class_constant_access_expression') {
|
|
668
|
+
const controller = handlerNode.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
669
|
+
return { controller, method: '__invoke' };
|
|
670
|
+
}
|
|
671
|
+
return { controller: null, method: null };
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Unwrap a chained call like Route::middleware('auth')->prefix('api')->group(fn)
|
|
675
|
+
*/
|
|
676
|
+
function unwrapRouteChain(node) {
|
|
677
|
+
if (node.type !== 'member_call_expression')
|
|
678
|
+
return null;
|
|
679
|
+
const terminalMethod = getCallMethodName(node);
|
|
680
|
+
if (!terminalMethod)
|
|
681
|
+
return null;
|
|
682
|
+
const terminalArgs = getArguments(node);
|
|
683
|
+
const attributes = [];
|
|
684
|
+
let current = node.children?.[0];
|
|
685
|
+
while (current) {
|
|
686
|
+
if (current.type === 'member_call_expression') {
|
|
687
|
+
const method = getCallMethodName(current);
|
|
688
|
+
const args = getArguments(current);
|
|
689
|
+
if (method)
|
|
690
|
+
attributes.unshift({ method, argsNode: args });
|
|
691
|
+
current = current.children?.[0];
|
|
692
|
+
}
|
|
693
|
+
else if (current.type === 'scoped_call_expression') {
|
|
694
|
+
const obj = current.childForFieldName?.('object') ?? current.children?.[0];
|
|
695
|
+
if (obj?.text !== 'Route')
|
|
696
|
+
return null;
|
|
697
|
+
const method = getCallMethodName(current);
|
|
698
|
+
const args = getArguments(current);
|
|
699
|
+
if (method)
|
|
700
|
+
attributes.unshift({ method, argsNode: args });
|
|
701
|
+
return { isRouteFacade: true, terminalMethod, attributes, terminalArgs, node };
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
/** Parse Route::group(['middleware' => ..., 'prefix' => ...], fn) array syntax */
|
|
710
|
+
function parseArrayGroupArgs(argsNode) {
|
|
711
|
+
const ctx = { middleware: [], prefix: null, controller: null };
|
|
712
|
+
if (!argsNode)
|
|
713
|
+
return ctx;
|
|
714
|
+
for (const child of argsNode.children ?? []) {
|
|
715
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
716
|
+
if (target?.type === 'array_creation_expression') {
|
|
717
|
+
for (const el of target.children ?? []) {
|
|
718
|
+
if (el.type !== 'array_element_initializer')
|
|
719
|
+
continue;
|
|
720
|
+
const children = el.children ?? [];
|
|
721
|
+
const arrowIdx = children.findIndex((c) => c.type === '=>');
|
|
722
|
+
if (arrowIdx === -1)
|
|
723
|
+
continue;
|
|
724
|
+
const key = extractStringContent(children[arrowIdx - 1]);
|
|
725
|
+
const val = children[arrowIdx + 1];
|
|
726
|
+
if (key === 'middleware') {
|
|
727
|
+
if (val?.type === 'string') {
|
|
728
|
+
const s = extractStringContent(val);
|
|
729
|
+
if (s)
|
|
730
|
+
ctx.middleware.push(s);
|
|
731
|
+
}
|
|
732
|
+
else if (val?.type === 'array_creation_expression') {
|
|
733
|
+
for (const item of val.children ?? []) {
|
|
734
|
+
if (item.type === 'array_element_initializer') {
|
|
735
|
+
const str = item.children?.find((c) => c.type === 'string');
|
|
736
|
+
const s = str ? extractStringContent(str) : null;
|
|
737
|
+
if (s)
|
|
738
|
+
ctx.middleware.push(s);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else if (key === 'prefix') {
|
|
744
|
+
ctx.prefix = extractStringContent(val) ?? null;
|
|
745
|
+
}
|
|
746
|
+
else if (key === 'controller') {
|
|
747
|
+
if (val?.type === 'class_constant_access_expression') {
|
|
748
|
+
ctx.controller = val.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return ctx;
|
|
755
|
+
}
|
|
756
|
+
function extractLaravelRoutes(tree, filePath) {
|
|
757
|
+
const routes = [];
|
|
758
|
+
function resolveStack(stack) {
|
|
759
|
+
const middleware = [];
|
|
760
|
+
let prefix = null;
|
|
761
|
+
let controller = null;
|
|
762
|
+
for (const ctx of stack) {
|
|
763
|
+
middleware.push(...ctx.middleware);
|
|
764
|
+
if (ctx.prefix)
|
|
765
|
+
prefix = prefix ? `${prefix}/${ctx.prefix}`.replace(/\/+/g, '/') : ctx.prefix;
|
|
766
|
+
if (ctx.controller)
|
|
767
|
+
controller = ctx.controller;
|
|
768
|
+
}
|
|
769
|
+
return { middleware, prefix, controller };
|
|
770
|
+
}
|
|
771
|
+
function emitRoute(httpMethod, argsNode, lineNumber, groupStack, chainAttrs) {
|
|
772
|
+
const effective = resolveStack(groupStack);
|
|
773
|
+
for (const attr of chainAttrs) {
|
|
774
|
+
if (attr.method === 'middleware')
|
|
775
|
+
effective.middleware.push(...extractMiddlewareArg(attr.argsNode));
|
|
776
|
+
if (attr.method === 'prefix') {
|
|
777
|
+
const p = extractFirstStringArg(attr.argsNode);
|
|
778
|
+
if (p)
|
|
779
|
+
effective.prefix = effective.prefix ? `${effective.prefix}/${p}` : p;
|
|
780
|
+
}
|
|
781
|
+
if (attr.method === 'controller') {
|
|
782
|
+
const cls = extractClassArg(attr.argsNode);
|
|
783
|
+
if (cls)
|
|
784
|
+
effective.controller = cls;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
const routePath = extractFirstStringArg(argsNode);
|
|
788
|
+
if (ROUTE_RESOURCE_METHODS.has(httpMethod)) {
|
|
789
|
+
const target = extractControllerTarget(argsNode);
|
|
790
|
+
const actions = httpMethod === 'apiResource' ? API_RESOURCE_ACTIONS : RESOURCE_ACTIONS;
|
|
791
|
+
for (const action of actions) {
|
|
792
|
+
routes.push({
|
|
793
|
+
filePath,
|
|
794
|
+
httpMethod,
|
|
795
|
+
routePath,
|
|
796
|
+
controllerName: target.controller ?? effective.controller,
|
|
797
|
+
methodName: action,
|
|
798
|
+
middleware: [...effective.middleware],
|
|
799
|
+
prefix: effective.prefix,
|
|
800
|
+
lineNumber,
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
const target = extractControllerTarget(argsNode);
|
|
806
|
+
routes.push({
|
|
807
|
+
filePath,
|
|
808
|
+
httpMethod,
|
|
809
|
+
routePath,
|
|
810
|
+
controllerName: target.controller ?? effective.controller,
|
|
811
|
+
methodName: target.method,
|
|
812
|
+
middleware: [...effective.middleware],
|
|
813
|
+
prefix: effective.prefix,
|
|
814
|
+
lineNumber,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function walk(node, groupStack) {
|
|
819
|
+
// Case 1: Simple Route::get(...), Route::post(...), etc.
|
|
820
|
+
if (isRouteStaticCall(node)) {
|
|
821
|
+
const method = getCallMethodName(node);
|
|
822
|
+
if (method && (ROUTE_HTTP_METHODS.has(method) || ROUTE_RESOURCE_METHODS.has(method))) {
|
|
823
|
+
emitRoute(method, getArguments(node), node.startPosition.row, groupStack, []);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (method === 'group') {
|
|
827
|
+
const argsNode = getArguments(node);
|
|
828
|
+
const groupCtx = parseArrayGroupArgs(argsNode);
|
|
829
|
+
const body = findClosureBody(argsNode);
|
|
830
|
+
if (body) {
|
|
831
|
+
groupStack.push(groupCtx);
|
|
832
|
+
walkChildren(body, groupStack);
|
|
833
|
+
groupStack.pop();
|
|
834
|
+
}
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
// Case 2: Fluent chain — Route::middleware(...)->group(...) or Route::middleware(...)->get(...)
|
|
839
|
+
const chain = unwrapRouteChain(node);
|
|
840
|
+
if (chain) {
|
|
841
|
+
if (chain.terminalMethod === 'group') {
|
|
842
|
+
const groupCtx = { middleware: [], prefix: null, controller: null };
|
|
843
|
+
for (const attr of chain.attributes) {
|
|
844
|
+
if (attr.method === 'middleware')
|
|
845
|
+
groupCtx.middleware.push(...extractMiddlewareArg(attr.argsNode));
|
|
846
|
+
if (attr.method === 'prefix')
|
|
847
|
+
groupCtx.prefix = extractFirstStringArg(attr.argsNode);
|
|
848
|
+
if (attr.method === 'controller')
|
|
849
|
+
groupCtx.controller = extractClassArg(attr.argsNode);
|
|
850
|
+
}
|
|
851
|
+
const body = findClosureBody(chain.terminalArgs);
|
|
852
|
+
if (body) {
|
|
853
|
+
groupStack.push(groupCtx);
|
|
854
|
+
walkChildren(body, groupStack);
|
|
855
|
+
groupStack.pop();
|
|
856
|
+
}
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (ROUTE_HTTP_METHODS.has(chain.terminalMethod) ||
|
|
860
|
+
ROUTE_RESOURCE_METHODS.has(chain.terminalMethod)) {
|
|
861
|
+
emitRoute(chain.terminalMethod, chain.terminalArgs, node.startPosition.row, groupStack, chain.attributes);
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// Default: recurse into children
|
|
866
|
+
walkChildren(node, groupStack);
|
|
867
|
+
}
|
|
868
|
+
function walkChildren(node, groupStack) {
|
|
869
|
+
for (const child of node.children ?? []) {
|
|
870
|
+
walk(child, groupStack);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
walk(tree.rootNode, []);
|
|
874
|
+
return routes;
|
|
875
|
+
}
|
|
876
|
+
// ============================================================================
|
|
877
|
+
// ORM Query Detection (Prisma + Supabase)
|
|
878
|
+
// ============================================================================
|
|
879
|
+
const PRISMA_QUERY_RE = /\bprisma\.(\w+)\.(findMany|findFirst|findUnique|findUniqueOrThrow|findFirstOrThrow|create|createMany|update|updateMany|delete|deleteMany|upsert|count|aggregate|groupBy)\s*\(/g;
|
|
880
|
+
const SUPABASE_QUERY_RE = /\bsupabase\.from\s*\(\s*['"](\w+)['"]\s*\)\s*\.(select|insert|update|delete|upsert)\s*\(/g;
|
|
881
|
+
/**
|
|
882
|
+
* Extract ORM query calls from file content via regex.
|
|
883
|
+
* Appends results to the provided array (avoids allocation when no matches).
|
|
884
|
+
*/
|
|
885
|
+
export function extractORMQueries(filePath, content, out) {
|
|
886
|
+
const hasPrisma = content.includes('prisma.');
|
|
887
|
+
const hasSupabase = content.includes('supabase.from');
|
|
888
|
+
if (!hasPrisma && !hasSupabase)
|
|
889
|
+
return;
|
|
890
|
+
if (hasPrisma) {
|
|
891
|
+
PRISMA_QUERY_RE.lastIndex = 0;
|
|
892
|
+
let m;
|
|
893
|
+
while ((m = PRISMA_QUERY_RE.exec(content)) !== null) {
|
|
894
|
+
const model = m[1];
|
|
895
|
+
if (model.startsWith('$'))
|
|
896
|
+
continue;
|
|
897
|
+
out.push({
|
|
898
|
+
filePath,
|
|
899
|
+
orm: 'prisma',
|
|
900
|
+
model,
|
|
901
|
+
method: m[2],
|
|
902
|
+
lineNumber: content.substring(0, m.index).split('\n').length - 1,
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (hasSupabase) {
|
|
907
|
+
SUPABASE_QUERY_RE.lastIndex = 0;
|
|
908
|
+
let m;
|
|
909
|
+
while ((m = SUPABASE_QUERY_RE.exec(content)) !== null) {
|
|
910
|
+
out.push({
|
|
911
|
+
filePath,
|
|
912
|
+
orm: 'supabase',
|
|
913
|
+
model: m[1],
|
|
914
|
+
method: m[2],
|
|
915
|
+
lineNumber: content.substring(0, m.index).split('\n').length - 1,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
const processFileGroup = (files, language, queryString, result, onFileProcessed) => {
|
|
921
|
+
let query;
|
|
922
|
+
try {
|
|
923
|
+
const lang = parser.getLanguage();
|
|
924
|
+
query = new Parser.Query(lang, queryString);
|
|
925
|
+
}
|
|
926
|
+
catch (err) {
|
|
927
|
+
const message = `Query compilation failed for ${language}: ${err instanceof Error ? err.message : String(err)}`;
|
|
928
|
+
if (parentPort) {
|
|
929
|
+
parentPort.postMessage({ type: 'warning', message });
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
console.warn(message);
|
|
933
|
+
}
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
for (const file of files) {
|
|
937
|
+
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
938
|
+
if (file.content.length > TREE_SITTER_MAX_BUFFER)
|
|
939
|
+
continue;
|
|
940
|
+
// Vue SFC preprocessing: extract <script> block content
|
|
941
|
+
let parseContent = file.content;
|
|
942
|
+
let lineOffset = 0;
|
|
943
|
+
let isVueSetup = false;
|
|
944
|
+
if (language === SupportedLanguages.Vue) {
|
|
945
|
+
const extracted = extractVueScript(file.content);
|
|
946
|
+
if (!extracted)
|
|
947
|
+
continue; // skip .vue files with no script block
|
|
948
|
+
parseContent = extracted.scriptContent;
|
|
949
|
+
lineOffset = extracted.lineOffset;
|
|
950
|
+
isVueSetup = extracted.isSetup;
|
|
951
|
+
}
|
|
952
|
+
clearCaches(); // Reset memoization before each new file
|
|
953
|
+
let tree;
|
|
954
|
+
try {
|
|
955
|
+
tree = parser.parse(parseContent, undefined, {
|
|
956
|
+
bufferSize: getTreeSitterBufferSize(parseContent.length),
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
catch (err) {
|
|
960
|
+
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
result.fileCount++;
|
|
964
|
+
onFileProcessed?.();
|
|
965
|
+
let matches;
|
|
966
|
+
try {
|
|
967
|
+
matches = query.matches(tree.rootNode);
|
|
968
|
+
}
|
|
969
|
+
catch (err) {
|
|
970
|
+
console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
// Pre-pass: extract heritage from query matches to build parentMap for buildTypeEnv.
|
|
974
|
+
// Heritage edges (EXTENDS/IMPLEMENTS) are created by heritage-processor which runs
|
|
975
|
+
// in PARALLEL with call-processor, so the graph edges don't exist when buildTypeEnv
|
|
976
|
+
// runs. This pre-pass makes parent class information available for type resolution.
|
|
977
|
+
const fileParentMap = new Map();
|
|
978
|
+
for (const match of matches) {
|
|
979
|
+
const captureMap = {};
|
|
980
|
+
for (const c of match.captures) {
|
|
981
|
+
captureMap[c.name] = c.node;
|
|
982
|
+
}
|
|
983
|
+
if (captureMap['heritage.class'] && captureMap['heritage.extends']) {
|
|
984
|
+
const className = captureMap['heritage.class'].text;
|
|
985
|
+
const parentName = captureMap['heritage.extends'].text;
|
|
986
|
+
// Skip Go named fields (only anonymous fields are struct embedding)
|
|
987
|
+
const extendsNode = captureMap['heritage.extends'];
|
|
988
|
+
const fieldDecl = extendsNode.parent;
|
|
989
|
+
if (fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name'))
|
|
990
|
+
continue;
|
|
991
|
+
let parents = fileParentMap.get(className);
|
|
992
|
+
if (!parents) {
|
|
993
|
+
parents = [];
|
|
994
|
+
fileParentMap.set(className, parents);
|
|
995
|
+
}
|
|
996
|
+
if (!parents.includes(parentName))
|
|
997
|
+
parents.push(parentName);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
// Build per-file type environment + constructor bindings in a single AST walk.
|
|
1001
|
+
// Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
|
|
1002
|
+
const parentMap = fileParentMap;
|
|
1003
|
+
const provider = getProvider(language);
|
|
1004
|
+
const typeEnv = buildTypeEnv(tree, language, {
|
|
1005
|
+
parentMap,
|
|
1006
|
+
enclosingFunctionFinder: provider?.enclosingFunctionFinder,
|
|
1007
|
+
extractFunctionName: provider?.methodExtractor?.extractFunctionName,
|
|
1008
|
+
});
|
|
1009
|
+
const callRouter = provider.callRouter;
|
|
1010
|
+
if (typeEnv.constructorBindings.length > 0) {
|
|
1011
|
+
result.constructorBindings.push({
|
|
1012
|
+
filePath: file.path,
|
|
1013
|
+
bindings: [...typeEnv.constructorBindings],
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
// Extract file-scope bindings for ExportedTypeMap (closes worker/sequential quality gap).
|
|
1017
|
+
// Sequential path uses collectExportedBindings(typeEnv) directly; worker path serializes
|
|
1018
|
+
// these bindings so the main thread can merge them into ExportedTypeMap.
|
|
1019
|
+
const fileScope = typeEnv.fileScope();
|
|
1020
|
+
if (fileScope.size > 0) {
|
|
1021
|
+
const bindings = [];
|
|
1022
|
+
for (const [name, type] of fileScope)
|
|
1023
|
+
bindings.push([name, type]);
|
|
1024
|
+
result.typeEnvBindings.push({ filePath: file.path, bindings });
|
|
1025
|
+
}
|
|
1026
|
+
// Per-file map: decorator end-line → decorator info, for associating with definitions
|
|
1027
|
+
const fileDecorators = new Map();
|
|
1028
|
+
for (const match of matches) {
|
|
1029
|
+
const captureMap = {};
|
|
1030
|
+
for (const c of match.captures) {
|
|
1031
|
+
captureMap[c.name] = c.node;
|
|
1032
|
+
}
|
|
1033
|
+
// Extract import paths before skipping
|
|
1034
|
+
if (captureMap['import'] && captureMap['import.source']) {
|
|
1035
|
+
const rawImportPath = preprocessImportPath(captureMap['import.source'].text, captureMap['import'], provider);
|
|
1036
|
+
if (!rawImportPath)
|
|
1037
|
+
continue;
|
|
1038
|
+
const extractor = provider.namedBindingExtractor;
|
|
1039
|
+
const namedBindings = extractor ? extractor(captureMap['import']) : undefined;
|
|
1040
|
+
result.imports.push({
|
|
1041
|
+
filePath: file.path,
|
|
1042
|
+
rawImportPath,
|
|
1043
|
+
language: language,
|
|
1044
|
+
...(namedBindings ? { namedBindings } : {}),
|
|
1045
|
+
});
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
// Extract assignment sites (field write access)
|
|
1049
|
+
if (captureMap['assignment'] &&
|
|
1050
|
+
captureMap['assignment.receiver'] &&
|
|
1051
|
+
captureMap['assignment.property']) {
|
|
1052
|
+
const receiverText = captureMap['assignment.receiver'].text;
|
|
1053
|
+
const propertyName = captureMap['assignment.property'].text;
|
|
1054
|
+
if (receiverText && propertyName) {
|
|
1055
|
+
const srcId = findEnclosingFunctionId(captureMap['assignment'], file.path, provider) ||
|
|
1056
|
+
generateId('File', file.path);
|
|
1057
|
+
let receiverTypeName;
|
|
1058
|
+
if (typeEnv) {
|
|
1059
|
+
receiverTypeName = typeEnv.lookup(receiverText, captureMap['assignment']) ?? undefined;
|
|
1060
|
+
}
|
|
1061
|
+
result.assignments.push({
|
|
1062
|
+
filePath: file.path,
|
|
1063
|
+
sourceId: srcId,
|
|
1064
|
+
receiverText,
|
|
1065
|
+
propertyName,
|
|
1066
|
+
...(receiverTypeName ? { receiverTypeName } : {}),
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
if (!captureMap['call'])
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
// Store decorator metadata for later association with definitions
|
|
1073
|
+
if (captureMap['decorator'] && captureMap['decorator.name']) {
|
|
1074
|
+
const decoratorName = captureMap['decorator.name'].text;
|
|
1075
|
+
const decoratorArg = captureMap['decorator.arg']?.text;
|
|
1076
|
+
const decoratorNode = captureMap['decorator'];
|
|
1077
|
+
// Store by the decorator's end line — the definition follows immediately after
|
|
1078
|
+
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
1079
|
+
name: decoratorName,
|
|
1080
|
+
arg: decoratorArg,
|
|
1081
|
+
});
|
|
1082
|
+
if (ROUTE_DECORATOR_NAMES.has(decoratorName)) {
|
|
1083
|
+
const routePath = decoratorArg || '';
|
|
1084
|
+
const method = decoratorName.replace('Mapping', '').toUpperCase();
|
|
1085
|
+
const httpMethod = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(method)
|
|
1086
|
+
? method
|
|
1087
|
+
: 'GET';
|
|
1088
|
+
result.decoratorRoutes.push({
|
|
1089
|
+
filePath: file.path,
|
|
1090
|
+
routePath,
|
|
1091
|
+
httpMethod,
|
|
1092
|
+
decoratorName,
|
|
1093
|
+
lineNumber: decoratorNode.startPosition.row + lineOffset,
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
// MCP/RPC tool detection: @mcp.tool(), @app.tool(), @server.tool()
|
|
1097
|
+
if (decoratorName === 'tool') {
|
|
1098
|
+
// Re-store with isTool flag for the definition handler
|
|
1099
|
+
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
1100
|
+
name: decoratorName,
|
|
1101
|
+
arg: decoratorArg,
|
|
1102
|
+
isTool: true,
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
continue;
|
|
1106
|
+
}
|
|
1107
|
+
// Extract HTTP consumer URLs: fetch(), axios.get(), $.get(), requests.get(), etc.
|
|
1108
|
+
if (captureMap['route.fetch']) {
|
|
1109
|
+
const urlNode = captureMap['route.url'] ?? captureMap['route.template_url'];
|
|
1110
|
+
if (urlNode) {
|
|
1111
|
+
result.fetchCalls.push({
|
|
1112
|
+
filePath: file.path,
|
|
1113
|
+
fetchURL: urlNode.text,
|
|
1114
|
+
lineNumber: captureMap['route.fetch'].startPosition.row + lineOffset,
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
// HTTP client calls: axios.get('/path'), $.post('/path'), requests.get('/path')
|
|
1120
|
+
// Skip methods also in EXPRESS_ROUTE_METHODS to avoid double-registering Express
|
|
1121
|
+
// routes as both route definitions AND consumers (both queries match same AST node)
|
|
1122
|
+
if (captureMap['http_client'] && captureMap['http_client.url']) {
|
|
1123
|
+
const method = captureMap['http_client.method']?.text;
|
|
1124
|
+
const url = captureMap['http_client.url'].text;
|
|
1125
|
+
if (method && HTTP_CLIENT_ONLY_METHODS.has(method) && url.startsWith('/')) {
|
|
1126
|
+
result.fetchCalls.push({
|
|
1127
|
+
filePath: file.path,
|
|
1128
|
+
fetchURL: url,
|
|
1129
|
+
lineNumber: captureMap['http_client'].startPosition.row + lineOffset,
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
// Express/Hono route registration: app.get('/path', handler)
|
|
1135
|
+
if (captureMap['express_route'] &&
|
|
1136
|
+
captureMap['express_route.method'] &&
|
|
1137
|
+
captureMap['express_route.path']) {
|
|
1138
|
+
const method = captureMap['express_route.method'].text;
|
|
1139
|
+
const routePath = captureMap['express_route.path'].text;
|
|
1140
|
+
if (EXPRESS_ROUTE_METHODS.has(method) && routePath.startsWith('/')) {
|
|
1141
|
+
const httpMethod = method === 'all' || method === 'use' || method === 'route'
|
|
1142
|
+
? 'GET'
|
|
1143
|
+
: method.toUpperCase();
|
|
1144
|
+
result.decoratorRoutes.push({
|
|
1145
|
+
filePath: file.path,
|
|
1146
|
+
routePath,
|
|
1147
|
+
httpMethod,
|
|
1148
|
+
decoratorName: `express.${method}`,
|
|
1149
|
+
lineNumber: captureMap['express_route'].startPosition.row + lineOffset,
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
// Extract call sites
|
|
1155
|
+
if (captureMap['call']) {
|
|
1156
|
+
const callNode0 = captureMap['call'];
|
|
1157
|
+
const languageSeed = extractParsedCallSite(language, callNode0);
|
|
1158
|
+
if (languageSeed) {
|
|
1159
|
+
if (!provider.isBuiltInName(languageSeed.calledName)) {
|
|
1160
|
+
const sourceId = findEnclosingFunctionId(callNode0, file.path, provider) ||
|
|
1161
|
+
generateId('File', file.path);
|
|
1162
|
+
const receiverName = languageSeed.callForm === 'member' ? languageSeed.receiverName : undefined;
|
|
1163
|
+
let receiverTypeName = receiverName
|
|
1164
|
+
? typeEnv.lookup(receiverName, callNode0)
|
|
1165
|
+
: undefined;
|
|
1166
|
+
// Type-as-receiver (e.g. Java `User::getName`): no TypeEnv binding for the class name
|
|
1167
|
+
if (receiverName !== undefined &&
|
|
1168
|
+
receiverTypeName === undefined &&
|
|
1169
|
+
languageSeed.callForm === 'member' &&
|
|
1170
|
+
(language === SupportedLanguages.Java ||
|
|
1171
|
+
language === SupportedLanguages.CSharp ||
|
|
1172
|
+
language === SupportedLanguages.Kotlin)) {
|
|
1173
|
+
const c0 = receiverName.charCodeAt(0);
|
|
1174
|
+
if (c0 >= 65 && c0 <= 90)
|
|
1175
|
+
receiverTypeName = receiverName;
|
|
1176
|
+
}
|
|
1177
|
+
result.calls.push({
|
|
1178
|
+
filePath: file.path,
|
|
1179
|
+
calledName: languageSeed.calledName,
|
|
1180
|
+
sourceId,
|
|
1181
|
+
callForm: languageSeed.callForm,
|
|
1182
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1183
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
const callNameNode = captureMap['call.name'];
|
|
1189
|
+
if (callNameNode) {
|
|
1190
|
+
const calledName = callNameNode.text;
|
|
1191
|
+
// Dispatch: route language-specific calls (heritage, properties, imports)
|
|
1192
|
+
const routed = callRouter?.(calledName, captureMap['call']);
|
|
1193
|
+
if (routed) {
|
|
1194
|
+
if (routed.kind === 'skip')
|
|
1195
|
+
continue;
|
|
1196
|
+
if (routed.kind === 'import') {
|
|
1197
|
+
result.imports.push({
|
|
1198
|
+
filePath: file.path,
|
|
1199
|
+
rawImportPath: routed.importPath,
|
|
1200
|
+
language,
|
|
1201
|
+
});
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
if (routed.kind === 'heritage') {
|
|
1205
|
+
for (const item of routed.items) {
|
|
1206
|
+
result.heritage.push({
|
|
1207
|
+
filePath: file.path,
|
|
1208
|
+
className: item.enclosingClass,
|
|
1209
|
+
parentName: item.mixinName,
|
|
1210
|
+
kind: item.heritageKind,
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
if (routed.kind === 'properties') {
|
|
1216
|
+
const propEnclosingInfo = cachedFindEnclosingClassInfo(captureMap['call'], file.path);
|
|
1217
|
+
const propEnclosingClassId = propEnclosingInfo?.classId ?? null;
|
|
1218
|
+
// Enrich routed properties with FieldExtractor metadata
|
|
1219
|
+
let routedFieldMap;
|
|
1220
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
1221
|
+
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
1222
|
+
if (classNode) {
|
|
1223
|
+
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
1224
|
+
typeEnv,
|
|
1225
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1226
|
+
filePath: file.path,
|
|
1227
|
+
language,
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
for (const item of routed.items) {
|
|
1232
|
+
const routedFieldInfo = routedFieldMap?.get(item.propName);
|
|
1233
|
+
const propQualifiedName = propEnclosingInfo
|
|
1234
|
+
? `${propEnclosingInfo.className}.${item.propName}`
|
|
1235
|
+
: item.propName;
|
|
1236
|
+
const nodeId = generateId('Property', `${file.path}:${propQualifiedName}`);
|
|
1237
|
+
result.nodes.push({
|
|
1238
|
+
id: nodeId,
|
|
1239
|
+
label: 'Property',
|
|
1240
|
+
properties: {
|
|
1241
|
+
name: item.propName,
|
|
1242
|
+
filePath: file.path,
|
|
1243
|
+
startLine: item.startLine,
|
|
1244
|
+
endLine: item.endLine,
|
|
1245
|
+
language,
|
|
1246
|
+
isExported: true,
|
|
1247
|
+
description: item.accessorType,
|
|
1248
|
+
...(item.declaredType
|
|
1249
|
+
? { declaredType: item.declaredType }
|
|
1250
|
+
: routedFieldInfo?.type
|
|
1251
|
+
? { declaredType: routedFieldInfo.type }
|
|
1252
|
+
: {}),
|
|
1253
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1254
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1255
|
+
: {}),
|
|
1256
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1257
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1258
|
+
: {}),
|
|
1259
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1260
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1261
|
+
: {}),
|
|
1262
|
+
},
|
|
1263
|
+
});
|
|
1264
|
+
result.symbols.push({
|
|
1265
|
+
filePath: file.path,
|
|
1266
|
+
name: item.propName,
|
|
1267
|
+
nodeId,
|
|
1268
|
+
type: 'Property',
|
|
1269
|
+
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
1270
|
+
...(item.declaredType
|
|
1271
|
+
? { declaredType: item.declaredType }
|
|
1272
|
+
: routedFieldInfo?.type
|
|
1273
|
+
? { declaredType: routedFieldInfo.type }
|
|
1274
|
+
: {}),
|
|
1275
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1276
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1277
|
+
: {}),
|
|
1278
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1279
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1280
|
+
: {}),
|
|
1281
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1282
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1283
|
+
: {}),
|
|
1284
|
+
});
|
|
1285
|
+
const fileId = generateId('File', file.path);
|
|
1286
|
+
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
1287
|
+
result.relationships.push({
|
|
1288
|
+
id: relId,
|
|
1289
|
+
sourceId: fileId,
|
|
1290
|
+
targetId: nodeId,
|
|
1291
|
+
type: 'DEFINES',
|
|
1292
|
+
confidence: 1.0,
|
|
1293
|
+
reason: '',
|
|
1294
|
+
});
|
|
1295
|
+
if (propEnclosingClassId) {
|
|
1296
|
+
result.relationships.push({
|
|
1297
|
+
id: generateId('HAS_PROPERTY', `${propEnclosingClassId}->${nodeId}`),
|
|
1298
|
+
sourceId: propEnclosingClassId,
|
|
1299
|
+
targetId: nodeId,
|
|
1300
|
+
type: 'HAS_PROPERTY',
|
|
1301
|
+
confidence: 1.0,
|
|
1302
|
+
reason: '',
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
continue;
|
|
1307
|
+
}
|
|
1308
|
+
// kind === 'call' — fall through to normal call processing below
|
|
1309
|
+
}
|
|
1310
|
+
if (!provider.isBuiltInName(calledName)) {
|
|
1311
|
+
const callNode = captureMap['call'];
|
|
1312
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider) ||
|
|
1313
|
+
generateId('File', file.path);
|
|
1314
|
+
const callForm = inferCallForm(callNode, callNameNode);
|
|
1315
|
+
let receiverName = callForm === 'member' ? extractReceiverName(callNameNode) : undefined;
|
|
1316
|
+
let receiverTypeName = receiverName
|
|
1317
|
+
? typeEnv.lookup(receiverName, callNode)
|
|
1318
|
+
: undefined;
|
|
1319
|
+
let receiverMixedChain;
|
|
1320
|
+
// When the receiver is a complex expression (call chain, field chain, or mixed),
|
|
1321
|
+
// extractReceiverName returns undefined. Walk the receiver node to build a unified
|
|
1322
|
+
// mixed chain for deferred resolution in processCallsFromExtracted.
|
|
1323
|
+
if (callForm === 'member' && receiverName === undefined && !receiverTypeName) {
|
|
1324
|
+
const receiverNode = extractReceiverNode(callNameNode);
|
|
1325
|
+
if (receiverNode) {
|
|
1326
|
+
const extracted = extractMixedChain(receiverNode);
|
|
1327
|
+
if (extracted && extracted.chain.length > 0) {
|
|
1328
|
+
receiverMixedChain = extracted.chain;
|
|
1329
|
+
receiverName = extracted.baseReceiverName;
|
|
1330
|
+
// Try the type environment immediately for the base receiver
|
|
1331
|
+
// (covers explicitly-typed locals and annotated parameters).
|
|
1332
|
+
if (receiverName) {
|
|
1333
|
+
receiverTypeName = typeEnv.lookup(receiverName, callNode);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
const inferLiteralType = provider.typeConfig?.inferLiteralType;
|
|
1339
|
+
const argCountForOverloadHints = countCallArguments(callNode);
|
|
1340
|
+
// Skip when no arg list / zero args: nothing to infer for overload typing; saves AST walks + payload size.
|
|
1341
|
+
const argTypes = inferLiteralType &&
|
|
1342
|
+
argCountForOverloadHints !== undefined &&
|
|
1343
|
+
argCountForOverloadHints > 0
|
|
1344
|
+
? extractCallArgTypes(callNode, inferLiteralType, (varName, cn) => typeEnv.lookup(varName, cn))
|
|
1345
|
+
: undefined;
|
|
1346
|
+
result.calls.push({
|
|
1347
|
+
filePath: file.path,
|
|
1348
|
+
calledName,
|
|
1349
|
+
sourceId,
|
|
1350
|
+
argCount: countCallArguments(callNode),
|
|
1351
|
+
...(callForm !== undefined ? { callForm } : {}),
|
|
1352
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1353
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1354
|
+
...(receiverMixedChain !== undefined ? { receiverMixedChain } : {}),
|
|
1355
|
+
...(argTypes !== undefined ? { argTypes } : {}),
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
// Extract heritage (extends/implements)
|
|
1362
|
+
if (captureMap['heritage.class']) {
|
|
1363
|
+
if (captureMap['heritage.extends']) {
|
|
1364
|
+
// Go struct embedding: the query matches ALL field_declarations with
|
|
1365
|
+
// type_identifier, but only anonymous fields (no name) are embedded.
|
|
1366
|
+
// Named fields like `Breed string` also match — skip them.
|
|
1367
|
+
const extendsNode = captureMap['heritage.extends'];
|
|
1368
|
+
const fieldDecl = extendsNode.parent;
|
|
1369
|
+
const isNamedField = fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name');
|
|
1370
|
+
if (!isNamedField) {
|
|
1371
|
+
result.heritage.push({
|
|
1372
|
+
filePath: file.path,
|
|
1373
|
+
className: captureMap['heritage.class'].text,
|
|
1374
|
+
parentName: captureMap['heritage.extends'].text,
|
|
1375
|
+
kind: 'extends',
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (captureMap['heritage.implements']) {
|
|
1380
|
+
result.heritage.push({
|
|
1381
|
+
filePath: file.path,
|
|
1382
|
+
className: captureMap['heritage.class'].text,
|
|
1383
|
+
parentName: captureMap['heritage.implements'].text,
|
|
1384
|
+
kind: 'implements',
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
if (captureMap['heritage.trait']) {
|
|
1388
|
+
result.heritage.push({
|
|
1389
|
+
filePath: file.path,
|
|
1390
|
+
className: captureMap['heritage.class'].text,
|
|
1391
|
+
parentName: captureMap['heritage.trait'].text,
|
|
1392
|
+
kind: 'trait-impl',
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
if (captureMap['heritage.extends'] ||
|
|
1396
|
+
captureMap['heritage.implements'] ||
|
|
1397
|
+
captureMap['heritage.trait']) {
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
const nodeLabel = getLabelFromCaptures(captureMap, provider);
|
|
1402
|
+
if (!nodeLabel)
|
|
1403
|
+
continue;
|
|
1404
|
+
const nameNode = captureMap['name'];
|
|
1405
|
+
// Synthesize name for constructors without explicit @name capture (e.g. Swift init)
|
|
1406
|
+
if (!nameNode && nodeLabel !== 'Constructor')
|
|
1407
|
+
continue;
|
|
1408
|
+
const nodeName = nameNode ? nameNode.text : 'init';
|
|
1409
|
+
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
1410
|
+
const startLine = definitionNode
|
|
1411
|
+
? definitionNode.startPosition.row + lineOffset
|
|
1412
|
+
: nameNode
|
|
1413
|
+
? nameNode.startPosition.row + lineOffset
|
|
1414
|
+
: lineOffset;
|
|
1415
|
+
// Compute enclosing class BEFORE node ID — needed to qualify method IDs
|
|
1416
|
+
const needsOwner = nodeLabel === 'Method' ||
|
|
1417
|
+
nodeLabel === 'Constructor' ||
|
|
1418
|
+
nodeLabel === 'Property' ||
|
|
1419
|
+
nodeLabel === 'Function';
|
|
1420
|
+
const enclosingClassInfo = needsOwner
|
|
1421
|
+
? cachedFindEnclosingClassInfo(nameNode || definitionNode, file.path)
|
|
1422
|
+
: null;
|
|
1423
|
+
const enclosingClassId = enclosingClassInfo?.classId ?? null;
|
|
1424
|
+
// Qualify method/property IDs with enclosing class name to avoid collisions
|
|
1425
|
+
const qualifiedName = enclosingClassInfo
|
|
1426
|
+
? `${enclosingClassInfo.className}.${nodeName}`
|
|
1427
|
+
: nodeName;
|
|
1428
|
+
// Extract method metadata BEFORE generating node ID — parameterCount is needed
|
|
1429
|
+
// to disambiguate overloaded methods via #<arity> suffix in the ID.
|
|
1430
|
+
let declaredType;
|
|
1431
|
+
let methodProps = {};
|
|
1432
|
+
let arityForId; // raw param count for ID, even for variadic
|
|
1433
|
+
if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
|
|
1434
|
+
// Use MethodExtractor for method metadata — provides parameterCount, parameterTypes,
|
|
1435
|
+
// returnType, isAbstract/isFinal/annotations, visibility, and more.
|
|
1436
|
+
let enrichedByMethodExtractor = false;
|
|
1437
|
+
if (provider.methodExtractor && definitionNode) {
|
|
1438
|
+
const classNode = findEnclosingClassNode(definitionNode) ?? findClassNodeByQualifiedName(definitionNode);
|
|
1439
|
+
if (classNode) {
|
|
1440
|
+
const methodMap = getMethodInfo(classNode, provider, {
|
|
1441
|
+
filePath: file.path,
|
|
1442
|
+
language,
|
|
1443
|
+
});
|
|
1444
|
+
const defLine = definitionNode.startPosition.row + 1;
|
|
1445
|
+
const info = methodMap?.get(`${nodeName}:${defLine}`);
|
|
1446
|
+
if (info) {
|
|
1447
|
+
enrichedByMethodExtractor = true;
|
|
1448
|
+
arityForId = arityForIdFromInfo(info);
|
|
1449
|
+
methodProps = buildMethodProps(info);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
// For top-level methods (e.g. Go method_declaration), try extractFromNode
|
|
1454
|
+
if (!enrichedByMethodExtractor &&
|
|
1455
|
+
provider.methodExtractor?.extractFromNode &&
|
|
1456
|
+
definitionNode) {
|
|
1457
|
+
const info = provider.methodExtractor.extractFromNode(definitionNode, {
|
|
1458
|
+
filePath: file.path,
|
|
1459
|
+
language,
|
|
1460
|
+
});
|
|
1461
|
+
if (info) {
|
|
1462
|
+
enrichedByMethodExtractor = true;
|
|
1463
|
+
arityForId = arityForIdFromInfo(info);
|
|
1464
|
+
methodProps = buildMethodProps(info);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
// Append #<paramCount> to Method/Constructor IDs to disambiguate overloads.
|
|
1469
|
+
// Functions are not suffixed — they don't overload by name in the same scope.
|
|
1470
|
+
const needsAritySuffix = nodeLabel === 'Method' || nodeLabel === 'Constructor';
|
|
1471
|
+
const arityTag = needsAritySuffix && arityForId !== undefined ? `#${arityForId}` : '';
|
|
1472
|
+
const nodeId = generateId(nodeLabel, `${file.path}:${qualifiedName}${arityTag}`);
|
|
1473
|
+
const description = provider.descriptionExtractor?.(nodeLabel, nodeName, captureMap);
|
|
1474
|
+
let frameworkHint = definitionNode
|
|
1475
|
+
? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
|
|
1476
|
+
: null;
|
|
1477
|
+
// Decorators appear on lines immediately before their definition; allow up to
|
|
1478
|
+
// MAX_DECORATOR_SCAN_LINES gap for blank lines / multi-line decorator stacks.
|
|
1479
|
+
const MAX_DECORATOR_SCAN_LINES = 5;
|
|
1480
|
+
if (definitionNode) {
|
|
1481
|
+
const defStartLine = definitionNode.startPosition.row;
|
|
1482
|
+
for (let checkLine = defStartLine - 1; checkLine >= Math.max(0, defStartLine - MAX_DECORATOR_SCAN_LINES); checkLine--) {
|
|
1483
|
+
const dec = fileDecorators.get(checkLine);
|
|
1484
|
+
if (dec) {
|
|
1485
|
+
// Use first (closest) decorator found for framework hint
|
|
1486
|
+
if (!frameworkHint) {
|
|
1487
|
+
frameworkHint = {
|
|
1488
|
+
framework: 'decorator',
|
|
1489
|
+
entryPointMultiplier: 1.2,
|
|
1490
|
+
reason: `@${dec.name}${dec.arg ? `("${dec.arg}")` : ''}`,
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
// Emit tool definition if this is a @tool decorator
|
|
1494
|
+
if (dec.isTool) {
|
|
1495
|
+
result.toolDefs.push({
|
|
1496
|
+
filePath: file.path,
|
|
1497
|
+
toolName: nodeName,
|
|
1498
|
+
description: dec.arg || '',
|
|
1499
|
+
lineNumber: definitionNode.startPosition.row + lineOffset,
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
fileDecorators.delete(checkLine);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
// Property metadata extraction (not needed before nodeId — Properties don't overload)
|
|
1507
|
+
if (nodeLabel === 'Property' && definitionNode) {
|
|
1508
|
+
// FieldExtractor is the single source of truth when available
|
|
1509
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
1510
|
+
const classNode = findEnclosingClassNode(definitionNode);
|
|
1511
|
+
if (classNode) {
|
|
1512
|
+
const fieldMap = getFieldInfo(classNode, provider, {
|
|
1513
|
+
typeEnv,
|
|
1514
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1515
|
+
filePath: file.path,
|
|
1516
|
+
language,
|
|
1517
|
+
});
|
|
1518
|
+
const info = fieldMap?.get(nodeName);
|
|
1519
|
+
if (info) {
|
|
1520
|
+
declaredType = info.type ?? undefined;
|
|
1521
|
+
methodProps.visibility = info.visibility;
|
|
1522
|
+
methodProps.isStatic = info.isStatic;
|
|
1523
|
+
methodProps.isReadonly = info.isReadonly;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
result.nodes.push({
|
|
1529
|
+
id: nodeId,
|
|
1530
|
+
label: nodeLabel,
|
|
1531
|
+
properties: {
|
|
1532
|
+
name: nodeName,
|
|
1533
|
+
filePath: file.path,
|
|
1534
|
+
startLine: definitionNode ? definitionNode.startPosition.row + lineOffset : startLine,
|
|
1535
|
+
endLine: definitionNode ? definitionNode.endPosition.row + lineOffset : startLine,
|
|
1536
|
+
language: language,
|
|
1537
|
+
isExported: language === SupportedLanguages.Vue && isVueSetup
|
|
1538
|
+
? isVueSetupTopLevel(nameNode || definitionNode)
|
|
1539
|
+
: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
|
|
1540
|
+
...(frameworkHint
|
|
1541
|
+
? {
|
|
1542
|
+
astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
|
|
1543
|
+
astFrameworkReason: frameworkHint.reason,
|
|
1544
|
+
}
|
|
1545
|
+
: {}),
|
|
1546
|
+
...(description !== undefined ? { description } : {}),
|
|
1547
|
+
...methodProps,
|
|
1548
|
+
...(declaredType !== undefined ? { declaredType } : {}),
|
|
1549
|
+
},
|
|
1550
|
+
});
|
|
1551
|
+
// enclosingClassId already computed above (before nodeId generation)
|
|
1552
|
+
result.symbols.push({
|
|
1553
|
+
filePath: file.path,
|
|
1554
|
+
name: nodeName,
|
|
1555
|
+
nodeId,
|
|
1556
|
+
type: nodeLabel,
|
|
1557
|
+
parameterCount: methodProps.parameterCount,
|
|
1558
|
+
requiredParameterCount: methodProps.requiredParameterCount,
|
|
1559
|
+
parameterTypes: methodProps.parameterTypes,
|
|
1560
|
+
returnType: methodProps.returnType,
|
|
1561
|
+
...(declaredType !== undefined ? { declaredType } : {}),
|
|
1562
|
+
...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
|
|
1563
|
+
visibility: methodProps.visibility,
|
|
1564
|
+
isStatic: methodProps.isStatic,
|
|
1565
|
+
isReadonly: methodProps.isReadonly,
|
|
1566
|
+
isAbstract: methodProps.isAbstract,
|
|
1567
|
+
isFinal: methodProps.isFinal,
|
|
1568
|
+
...(methodProps.isVirtual !== undefined
|
|
1569
|
+
? { isVirtual: methodProps.isVirtual }
|
|
1570
|
+
: {}),
|
|
1571
|
+
...(methodProps.isOverride !== undefined
|
|
1572
|
+
? { isOverride: methodProps.isOverride }
|
|
1573
|
+
: {}),
|
|
1574
|
+
...(methodProps.isAsync !== undefined ? { isAsync: methodProps.isAsync } : {}),
|
|
1575
|
+
...(methodProps.isPartial !== undefined
|
|
1576
|
+
? { isPartial: methodProps.isPartial }
|
|
1577
|
+
: {}),
|
|
1578
|
+
...(methodProps.annotations !== undefined
|
|
1579
|
+
? { annotations: methodProps.annotations }
|
|
1580
|
+
: {}),
|
|
1581
|
+
});
|
|
1582
|
+
const fileId = generateId('File', file.path);
|
|
1583
|
+
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
1584
|
+
result.relationships.push({
|
|
1585
|
+
id: relId,
|
|
1586
|
+
sourceId: fileId,
|
|
1587
|
+
targetId: nodeId,
|
|
1588
|
+
type: 'DEFINES',
|
|
1589
|
+
confidence: 1.0,
|
|
1590
|
+
reason: '',
|
|
1591
|
+
});
|
|
1592
|
+
// ── HAS_METHOD / HAS_PROPERTY: link member to enclosing class ──
|
|
1593
|
+
if (enclosingClassId) {
|
|
1594
|
+
const memberEdgeType = nodeLabel === 'Property' ? 'HAS_PROPERTY' : 'HAS_METHOD';
|
|
1595
|
+
result.relationships.push({
|
|
1596
|
+
id: generateId(memberEdgeType, `${enclosingClassId}->${nodeId}`),
|
|
1597
|
+
sourceId: enclosingClassId,
|
|
1598
|
+
targetId: nodeId,
|
|
1599
|
+
type: memberEdgeType,
|
|
1600
|
+
confidence: 1.0,
|
|
1601
|
+
reason: '',
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
// Extract framework routes via provider detection (e.g., Laravel routes.php)
|
|
1606
|
+
if (provider.isRouteFile?.(file.path)) {
|
|
1607
|
+
const extractedRoutes = extractLaravelRoutes(tree, file.path);
|
|
1608
|
+
result.routes.push(...extractedRoutes);
|
|
1609
|
+
}
|
|
1610
|
+
// Extract ORM queries (Prisma, Supabase)
|
|
1611
|
+
extractORMQueries(file.path, parseContent, result.ormQueries);
|
|
1612
|
+
// Vue: emit CALLS edges for components used in <template>
|
|
1613
|
+
if (language === SupportedLanguages.Vue) {
|
|
1614
|
+
const templateComponents = extractTemplateComponents(file.content);
|
|
1615
|
+
for (const componentName of templateComponents) {
|
|
1616
|
+
result.calls.push({
|
|
1617
|
+
filePath: file.path,
|
|
1618
|
+
calledName: componentName,
|
|
1619
|
+
sourceId: generateId('File', file.path),
|
|
1620
|
+
callForm: 'free',
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
// ============================================================================
|
|
1627
|
+
// Worker message handler — supports sub-batch streaming
|
|
1628
|
+
// ============================================================================
|
|
1629
|
+
/** Accumulated result across sub-batches */
|
|
1630
|
+
let accumulated = {
|
|
1631
|
+
nodes: [],
|
|
1632
|
+
relationships: [],
|
|
1633
|
+
symbols: [],
|
|
1634
|
+
imports: [],
|
|
1635
|
+
calls: [],
|
|
1636
|
+
assignments: [],
|
|
1637
|
+
heritage: [],
|
|
1638
|
+
routes: [],
|
|
1639
|
+
fetchCalls: [],
|
|
1640
|
+
decoratorRoutes: [],
|
|
1641
|
+
toolDefs: [],
|
|
1642
|
+
ormQueries: [],
|
|
1643
|
+
constructorBindings: [],
|
|
1644
|
+
typeEnvBindings: [],
|
|
1645
|
+
skippedLanguages: {},
|
|
1646
|
+
fileCount: 0,
|
|
1647
|
+
};
|
|
1648
|
+
let cumulativeProcessed = 0;
|
|
1649
|
+
const mergeResult = (target, src) => {
|
|
1650
|
+
target.nodes.push(...src.nodes);
|
|
1651
|
+
target.relationships.push(...src.relationships);
|
|
1652
|
+
target.symbols.push(...src.symbols);
|
|
1653
|
+
target.imports.push(...src.imports);
|
|
1654
|
+
target.calls.push(...src.calls);
|
|
1655
|
+
target.assignments.push(...src.assignments);
|
|
1656
|
+
target.heritage.push(...src.heritage);
|
|
1657
|
+
target.routes.push(...src.routes);
|
|
1658
|
+
target.fetchCalls.push(...src.fetchCalls);
|
|
1659
|
+
target.decoratorRoutes.push(...src.decoratorRoutes);
|
|
1660
|
+
target.toolDefs.push(...src.toolDefs);
|
|
1661
|
+
target.ormQueries.push(...src.ormQueries);
|
|
1662
|
+
target.constructorBindings.push(...src.constructorBindings);
|
|
1663
|
+
target.typeEnvBindings.push(...src.typeEnvBindings);
|
|
1664
|
+
for (const [lang, count] of Object.entries(src.skippedLanguages)) {
|
|
1665
|
+
target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
|
|
1666
|
+
}
|
|
1667
|
+
target.fileCount += src.fileCount;
|
|
1668
|
+
};
|
|
1669
|
+
parentPort.on('message', (msg) => {
|
|
1670
|
+
try {
|
|
1671
|
+
// Legacy single-message mode (backward compat): array of files
|
|
1672
|
+
if (Array.isArray(msg)) {
|
|
1673
|
+
const result = processBatch(msg, (filesProcessed) => {
|
|
1674
|
+
parentPort.postMessage({ type: 'progress', filesProcessed });
|
|
1675
|
+
});
|
|
1676
|
+
parentPort.postMessage({ type: 'result', data: result });
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
// Sub-batch mode: { type: 'sub-batch', files: [...] }
|
|
1680
|
+
if (msg.type === 'sub-batch') {
|
|
1681
|
+
const result = processBatch(msg.files, (filesProcessed) => {
|
|
1682
|
+
parentPort.postMessage({
|
|
1683
|
+
type: 'progress',
|
|
1684
|
+
filesProcessed: cumulativeProcessed + filesProcessed,
|
|
1685
|
+
});
|
|
1686
|
+
});
|
|
1687
|
+
cumulativeProcessed += result.fileCount;
|
|
1688
|
+
mergeResult(accumulated, result);
|
|
1689
|
+
// Signal ready for next sub-batch
|
|
1690
|
+
parentPort.postMessage({ type: 'sub-batch-done' });
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
// Flush: send accumulated results
|
|
1694
|
+
if (msg.type === 'flush') {
|
|
1695
|
+
parentPort.postMessage({ type: 'result', data: accumulated });
|
|
1696
|
+
// Reset for potential reuse
|
|
1697
|
+
accumulated = {
|
|
1698
|
+
nodes: [],
|
|
1699
|
+
relationships: [],
|
|
1700
|
+
symbols: [],
|
|
1701
|
+
imports: [],
|
|
1702
|
+
calls: [],
|
|
1703
|
+
assignments: [],
|
|
1704
|
+
heritage: [],
|
|
1705
|
+
routes: [],
|
|
1706
|
+
fetchCalls: [],
|
|
1707
|
+
decoratorRoutes: [],
|
|
1708
|
+
toolDefs: [],
|
|
1709
|
+
ormQueries: [],
|
|
1710
|
+
constructorBindings: [],
|
|
1711
|
+
typeEnvBindings: [],
|
|
1712
|
+
skippedLanguages: {},
|
|
1713
|
+
fileCount: 0,
|
|
1714
|
+
};
|
|
1715
|
+
cumulativeProcessed = 0;
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
catch (err) {
|
|
1720
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1721
|
+
parentPort.postMessage({ type: 'error', error: message });
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
//# sourceMappingURL=parse-worker.js.map
|