gitnexus 1.6.4-rc.2 → 1.6.4-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/_shared/index.d.ts +1 -1
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +1 -1
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +22 -14
- package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/finalize-algorithm.js +298 -37
- package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +23 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +36 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +47 -3
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/types.js +10 -2
- package/dist/_shared/scope-resolution/types.js.map +1 -1
- package/dist/cli/analyze.d.ts +6 -0
- package/dist/cli/analyze.js +35 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +31 -0
- package/dist/cli/index.js +13 -0
- package/dist/cli/setup.js +2 -2
- package/dist/core/embeddings/config.d.ts +2 -0
- package/dist/core/embeddings/config.js +36 -0
- package/dist/core/embeddings/embedder.js +11 -6
- package/dist/core/embeddings/embedding-pipeline.d.ts +7 -1
- package/dist/core/embeddings/embedding-pipeline.js +93 -29
- package/dist/core/embeddings/exact-search.d.ts +15 -0
- package/dist/core/embeddings/exact-search.js +27 -0
- package/dist/core/embeddings/types.d.ts +4 -0
- package/dist/core/embeddings/types.js +2 -0
- package/dist/core/group/config-parser.js +2 -0
- package/dist/core/group/matching.d.ts +3 -3
- package/dist/core/group/matching.js +46 -6
- package/dist/core/group/storage.js +2 -0
- package/dist/core/group/sync.js +1 -1
- package/dist/core/group/types.d.ts +18 -0
- package/dist/core/ingestion/call-processor.d.ts +3 -3
- package/dist/core/ingestion/call-processor.js +58 -65
- package/dist/core/ingestion/constants.d.ts +4 -3
- package/dist/core/ingestion/constants.js +8 -3
- package/dist/core/ingestion/finalize-orchestrator.js +6 -3
- package/dist/core/ingestion/heritage-processor.js +2 -2
- package/dist/core/ingestion/import-processor.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp/captures.js +4 -1
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +14 -13
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +62 -50
- package/dist/core/ingestion/languages/python/captures.js +9 -1
- package/dist/core/ingestion/languages/python/index.d.ts +1 -1
- package/dist/core/ingestion/languages/python/index.js +1 -1
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +3 -1
- package/dist/core/ingestion/languages/python/simple-hooks.js +8 -0
- package/dist/core/ingestion/languages/python.js +28 -1
- package/dist/core/ingestion/languages/swift.js +14 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/arity-metadata.js +103 -0
- package/dist/core/ingestion/languages/typescript/arity.d.ts +37 -0
- package/dist/core/ingestion/languages/typescript/arity.js +54 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/typescript/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/typescript/captures.d.ts +28 -0
- package/dist/core/ingestion/languages/typescript/captures.js +451 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.d.ts +49 -0
- package/dist/core/ingestion/languages/typescript/import-decomposer.js +371 -0
- package/dist/core/ingestion/languages/typescript/import-target.d.ts +50 -0
- package/dist/core/ingestion/languages/typescript/import-target.js +61 -0
- package/dist/core/ingestion/languages/typescript/index.d.ts +94 -0
- package/dist/core/ingestion/languages/typescript/index.js +94 -0
- package/dist/core/ingestion/languages/typescript/interpret.d.ts +35 -0
- package/dist/core/ingestion/languages/typescript/interpret.js +317 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.d.ts +62 -0
- package/dist/core/ingestion/languages/typescript/merge-bindings.js +158 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +77 -0
- package/dist/core/ingestion/languages/typescript/query.js +778 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.d.ts +59 -0
- package/dist/core/ingestion/languages/typescript/receiver-binding.js +171 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/typescript/scope-resolver.js +113 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +71 -0
- package/dist/core/ingestion/languages/typescript/simple-hooks.js +131 -0
- package/dist/core/ingestion/languages/typescript.js +19 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +3 -4
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +14 -1
- package/dist/core/ingestion/parsing-processor.js +20 -9
- package/dist/core/ingestion/pipeline-phases/processes.js +9 -4
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +10 -4
- package/dist/core/ingestion/registry-primary-flag.d.ts +3 -1
- package/dist/core/ingestion/registry-primary-flag.js +4 -1
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +5 -2
- package/dist/core/ingestion/scope-extractor-bridge.js +7 -2
- package/dist/core/ingestion/scope-extractor.js +19 -18
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +73 -11
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +48 -10
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +283 -14
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +23 -2
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +109 -37
- package/dist/core/ingestion/scope-resolution/passes/mro.js +3 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +13 -5
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +11 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +8 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +21 -5
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
- package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +54 -11
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +105 -30
- package/dist/core/ingestion/type-extractors/swift.js +7 -4
- package/dist/core/ingestion/utils/ast-helpers.d.ts +2 -0
- package/dist/core/ingestion/utils/ast-helpers.js +12 -0
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +14 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
- package/dist/core/ingestion/workers/parse-worker.js +15 -9
- package/dist/core/ingestion/workers/worker-pool.d.ts +11 -4
- package/dist/core/ingestion/workers/worker-pool.js +244 -48
- package/dist/core/lbug/extension-loader.d.ts +86 -0
- package/dist/core/lbug/extension-loader.js +184 -0
- package/dist/core/lbug/lbug-adapter.d.ts +18 -17
- package/dist/core/lbug/lbug-adapter.js +45 -73
- package/dist/core/lbug/pool-adapter.js +10 -28
- package/dist/core/platform/capabilities.d.ts +24 -0
- package/dist/core/platform/capabilities.js +54 -0
- package/dist/core/run-analyze.js +36 -9
- package/dist/core/search/bm25-index.d.ts +0 -17
- package/dist/core/search/bm25-index.js +10 -118
- package/dist/core/search/fts-indexes.d.ts +1 -0
- package/dist/core/search/fts-indexes.js +7 -0
- package/dist/core/search/fts-schema.d.ts +6 -0
- package/dist/core/search/fts-schema.js +7 -0
- package/dist/mcp/core/embedder.js +11 -4
- package/dist/mcp/local/local-backend.js +50 -15
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +113 -0
- package/hooks/claude/gitnexus-hook.cjs +11 -1
- package/package.json +6 -5
- package/scripts/build-tree-sitter-dart.cjs +42 -0
- package/scripts/build-tree-sitter-proto.cjs +1 -1
- package/scripts/build.js +22 -2
- package/scripts/install-duckdb-extension.mjs +37 -0
- package/vendor/tree-sitter-dart/README.md +18 -0
- package/vendor/tree-sitter-dart/binding.gyp +31 -0
- package/vendor/tree-sitter-dart/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-dart/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-dart/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-dart/grammar.js +2895 -0
- package/vendor/tree-sitter-dart/package.json +18 -0
- package/vendor/tree-sitter-dart/queries/highlights.scm +246 -0
- package/vendor/tree-sitter-dart/queries/tags.scm +92 -0
- package/vendor/tree-sitter-dart/queries/test.scm +1 -0
- package/vendor/tree-sitter-dart/src/grammar.json +12459 -0
- package/vendor/tree-sitter-dart/src/node-types.json +15055 -0
- package/vendor/tree-sitter-dart/src/parser.c +196127 -0
- package/vendor/tree-sitter-dart/src/scanner.c +130 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/array.h +290 -0
- package/vendor/tree-sitter-dart/src/tree_sitter/parser.h +265 -0
- package/vendor/tree-sitter-swift/LICENSE +21 -0
- package/vendor/tree-sitter-swift/README.md +139 -0
- package/vendor/tree-sitter-swift/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-swift/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-swift/package.json +28 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
- package/vendor/tree-sitter-swift/src/node-types.json +30694 -0
- package/web/assets/agent-DaprsFSX.js +597 -0
- package/web/assets/architecture-YZFGNWBL-S5CXDPWN-DEdGaPg2.js +1 -0
- package/web/assets/architectureDiagram-EMZXCZ2Q-Domyk_gO.js +36 -0
- package/web/assets/blockDiagram-IGV67L2C-B_2kD7tM.js +132 -0
- package/web/assets/c4Diagram-DFAF54RM-BhJJW8Gg.js +10 -0
- package/web/assets/chunk-3GS5O3IE-jlWIjPsl.js +231 -0
- package/web/assets/chunk-3YCYZ6SJ-Blq_IzZs.js +1 -0
- package/web/assets/chunk-6NTNNK5N-DyPc58pp.js +1 -0
- package/web/assets/chunk-7RZVMHOQ-BdIU-RGO.js +321 -0
- package/web/assets/chunk-A34GCYZU-BI2i_LdU.js +1 -0
- package/web/assets/chunk-AEOMTBSW-D7qjBMHW.js +1 -0
- package/web/assets/chunk-CilyBKbf.js +1 -0
- package/web/assets/chunk-DJ7UZH7F-i11ywiBl.js +1 -0
- package/web/assets/chunk-DKKBVRCY-1SffGI1N.js +4 -0
- package/web/assets/chunk-DU5LTGQ6-DaPeiwD5.js +1 -0
- package/web/assets/chunk-FXACKDTF-uhhi2PC2.js +159 -0
- package/web/assets/chunk-H3VCZNTA-IchcISDt.js +1 -0
- package/web/assets/chunk-HN6EAY2L-D7ZFMNrB.js +1 -0
- package/web/assets/chunk-KSICW3F5-C2tZmXwv.js +15 -0
- package/web/assets/chunk-O5ABG6QK-Bt-Km84H.js +1 -0
- package/web/assets/chunk-PK6DOVAG-ChlWY0BQ.js +206 -0
- package/web/assets/chunk-RNJOYNJ4-B724K7cW.js +1 -0
- package/web/assets/chunk-RWUO3TPN-DYn1XriD.js +1 -0
- package/web/assets/chunk-TBF5ZNIQ-DKtDz6ae.js +1 -0
- package/web/assets/chunk-TU3PZOEN-DE5Qhc0N.js +1 -0
- package/web/assets/chunk-TYMNRAUI-g1h33cq-.js +1 -0
- package/web/assets/chunk-VELTKBKT-C9dVN39o.js +1 -0
- package/web/assets/chunk-W7ZLLLMY-Du-Hb9yb.js +1 -0
- package/web/assets/chunk-WSB5WSVC-B123clsZ.js +1 -0
- package/web/assets/chunk-XGPFEOL4-BR7Eue38.js +1 -0
- package/web/assets/classDiagram-PPOCWD7C-BglfKSs_.js +1 -0
- package/web/assets/classDiagram-v2-23LJLIIU-BSzTM28O.js +1 -0
- package/web/assets/context-builder-CqQNhRj1.js +15 -0
- package/web/assets/cose-bilkent-PNC4W37J-DCfErU-A.js +1 -0
- package/web/assets/dagre-E77IOHMT-tDRRhDoN.js +4 -0
- package/web/assets/diagram-H7BISOXX-CUVHlmAh.js +43 -0
- package/web/assets/diagram-JC5VWROH-BoyOxulB.js +24 -0
- package/web/assets/diagram-LXUTUG65-osr9hb7N.js +10 -0
- package/web/assets/diagram-WEHSV5V5-d8nUqS39.js +24 -0
- package/web/assets/erDiagram-GCSMX5X6-b-IwOhPS.js +85 -0
- package/web/assets/flowDiagram-OTCZ4VVT-Ott2Q0AP.js +162 -0
- package/web/assets/ganttDiagram-MUNLMDZQ-BYtgN_5s.js +292 -0
- package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-CFyBIGZq.js +1 -0
- package/web/assets/gitGraphDiagram-3HKGZ4G3-CsVD2gn4.js +106 -0
- package/web/assets/index-BleGLU8S.css +2 -0
- package/web/assets/index-C_xK08EW.js +885 -0
- package/web/assets/info-OMHHGYJF-BF2H5H6G-yjAxKEzh.js +1 -0
- package/web/assets/infoDiagram-MN7RKWGX-DXK0Unn5.js +2 -0
- package/web/assets/ishikawaDiagram-YMYX4NHK-CXsnC2FA.js +70 -0
- package/web/assets/journeyDiagram-SO5T7YLQ-BzZ07B-X.js +139 -0
- package/web/assets/kanban-definition-LJHFXRCJ-C6_EpAd9.js +89 -0
- package/web/assets/katex-GD7MH7QM-CJiOjBBJ.js +261 -0
- package/web/assets/mindmap-definition-2EUWGEK5-CCYGWZ1m.js +96 -0
- package/web/assets/packet-4T2RLAQJ-EV4IVRXR-B8k4E3IT.js +1 -0
- package/web/assets/pie-ZZUOXDRM-N23DN5KN-DdvfY118.js +1 -0
- package/web/assets/pieDiagram-3IATQBI2-RyvRlQb4.js +30 -0
- package/web/assets/quadrantDiagram-E256RVCF-Bfb6sxCx.js +7 -0
- package/web/assets/radar-PYXPWWZC-P6TP7ZYP-1EEDC_yU.js +1 -0
- package/web/assets/requirementDiagram-M5DCFWZL-DjvHDyvN.js +84 -0
- package/web/assets/sankeyDiagram-L3NBLAOT-CBCbbl8s.js +10 -0
- package/web/assets/sequenceDiagram-ZOUHS735-BscU8TUR.js +157 -0
- package/web/assets/stateDiagram-MLPALWAM-CJusEK2D.js +1 -0
- package/web/assets/stateDiagram-v2-B5LQ5ZB2-DImJ3PXD.js +1 -0
- package/web/assets/timeline-definition-5SPVSISX-DigPA1X8.js +120 -0
- package/web/assets/treeView-SZITEDCU-5DXDK3XO-CzPDt3aG.js +1 -0
- package/web/assets/treemap-W4RFUUIX-WYLRDWKO-B9Iqiorr.js +1 -0
- package/web/assets/vennDiagram-IE5QUKF5-C91UkZIf.js +34 -0
- package/web/assets/wardley-RL74JXVD-BCRCBASE-x42Qw7hp.js +1 -0
- package/web/assets/wardleyDiagram-XU3VSMPF-DloBhI0U.js +20 -0
- package/web/assets/xychartDiagram-ZHJ5623Y-BGWJvgwI.js +7 -0
- package/web/index.html +21 -0
- package/scripts/patch-tree-sitter-swift.cjs +0 -78
|
@@ -13,10 +13,21 @@ import { initEmbedder, embedBatch, embedText, embeddingToArray, isEmbedderReady,
|
|
|
13
13
|
import { generateEmbeddingText } from './text-generator.js';
|
|
14
14
|
import { chunkNode, characterChunk } from './chunker.js';
|
|
15
15
|
import { extractStructuralNames } from './structural-extractor.js';
|
|
16
|
-
import {
|
|
16
|
+
import { EMBEDDABLE_LABELS, isShortLabel, LABEL_METHOD, LABELS_WITH_EXPORTED, STRUCTURAL_LABELS, collectBestChunks, } from './types.js';
|
|
17
|
+
import { resolveEmbeddingConfig } from './config.js';
|
|
18
|
+
import { rankExactEmbeddingRows } from './exact-search.js';
|
|
17
19
|
import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME, CREATE_VECTOR_INDEX_QUERY, STALE_HASH_SENTINEL, } from '../lbug/schema.js';
|
|
18
20
|
import { loadVectorExtension } from '../lbug/lbug-adapter.js';
|
|
21
|
+
import { getExactScanLimit } from '../platform/capabilities.js';
|
|
19
22
|
const isDev = process.env.NODE_ENV === 'development';
|
|
23
|
+
const vectorUnavailableMessage = 'VECTOR extension is unavailable for this LadybugDB runtime; semantic search will use exact scan when embeddings exist.';
|
|
24
|
+
const ensureVectorExtensionAvailable = async () => {
|
|
25
|
+
const vectorReady = await loadVectorExtension();
|
|
26
|
+
if (!vectorReady) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
20
31
|
/**
|
|
21
32
|
* Bump this when the embedding text template changes in a way that should
|
|
22
33
|
* invalidate existing vectors, such as metadata/header shape changes,
|
|
@@ -132,15 +143,17 @@ export const batchInsertEmbeddings = async (executeWithReusedStatement, updates)
|
|
|
132
143
|
|
|
133
144
|
*/
|
|
134
145
|
const createVectorIndex = async (executeQuery) => {
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
if (!(await ensureVectorExtensionAvailable()))
|
|
147
|
+
return false;
|
|
137
148
|
try {
|
|
138
149
|
await executeQuery(CREATE_VECTOR_INDEX_QUERY);
|
|
150
|
+
return true;
|
|
139
151
|
}
|
|
140
152
|
catch (error) {
|
|
141
153
|
if (isDev) {
|
|
142
154
|
console.warn('Vector index creation warning:', error);
|
|
143
155
|
}
|
|
156
|
+
return false;
|
|
144
157
|
}
|
|
145
158
|
};
|
|
146
159
|
/**
|
|
@@ -158,8 +171,12 @@ const createVectorIndex = async (executeQuery) => {
|
|
|
158
171
|
|
|
159
172
|
*/
|
|
160
173
|
export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatement, onProgress, config = {}, skipNodeIds, context, existingEmbeddings) => {
|
|
161
|
-
const finalConfig =
|
|
174
|
+
const finalConfig = resolveEmbeddingConfig(config);
|
|
175
|
+
let totalChunks = 0;
|
|
162
176
|
try {
|
|
177
|
+
const vectorAvailable = await ensureVectorExtensionAvailable();
|
|
178
|
+
if (!vectorAvailable && isDev)
|
|
179
|
+
console.warn(vectorUnavailableMessage);
|
|
163
180
|
// Phase 1: Load embedding model
|
|
164
181
|
onProgress({
|
|
165
182
|
phase: 'loading-model',
|
|
@@ -247,21 +264,25 @@ export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatem
|
|
|
247
264
|
// Ensure the vector index exists even when no new nodes need embedding.
|
|
248
265
|
// A prior crash or first-time incremental run may have left CodeEmbedding
|
|
249
266
|
// rows without ever reaching index creation.
|
|
250
|
-
await createVectorIndex(executeQuery);
|
|
267
|
+
const vectorIndexReady = await createVectorIndex(executeQuery);
|
|
251
268
|
onProgress({
|
|
252
269
|
phase: 'ready',
|
|
253
270
|
percent: 100,
|
|
254
271
|
nodesProcessed: 0,
|
|
255
272
|
totalNodes: 0,
|
|
256
273
|
});
|
|
257
|
-
return
|
|
274
|
+
return {
|
|
275
|
+
nodesProcessed: 0,
|
|
276
|
+
chunksProcessed: 0,
|
|
277
|
+
vectorIndexReady,
|
|
278
|
+
semanticMode: vectorIndexReady ? 'vector-index' : 'exact-scan',
|
|
279
|
+
};
|
|
258
280
|
}
|
|
259
281
|
// Phase 3: Chunk + embed nodes
|
|
260
282
|
const batchSize = finalConfig.batchSize;
|
|
261
283
|
const chunkSize = finalConfig.chunkSize;
|
|
262
284
|
const overlap = finalConfig.overlap;
|
|
263
285
|
let processedNodes = 0;
|
|
264
|
-
let totalChunks = 0;
|
|
265
286
|
onProgress({
|
|
266
287
|
phase: 'embedding',
|
|
267
288
|
percent: 20,
|
|
@@ -323,7 +344,7 @@ export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatem
|
|
|
323
344
|
}
|
|
324
345
|
}
|
|
325
346
|
// Embed chunk texts in sub-batches to control memory
|
|
326
|
-
const EMBED_SUB_BATCH =
|
|
347
|
+
const EMBED_SUB_BATCH = finalConfig.subBatchSize;
|
|
327
348
|
for (let si = 0; si < allTexts.length; si += EMBED_SUB_BATCH) {
|
|
328
349
|
const subTexts = allTexts.slice(si, si + EMBED_SUB_BATCH);
|
|
329
350
|
const subUpdates = allUpdates.slice(si, si + EMBED_SUB_BATCH);
|
|
@@ -363,7 +384,7 @@ export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatem
|
|
|
363
384
|
if (isDev) {
|
|
364
385
|
console.log('📇 Creating vector index...');
|
|
365
386
|
}
|
|
366
|
-
await createVectorIndex(executeQuery);
|
|
387
|
+
const vectorIndexReady = await createVectorIndex(executeQuery);
|
|
367
388
|
onProgress({
|
|
368
389
|
phase: 'ready',
|
|
369
390
|
percent: 100,
|
|
@@ -373,6 +394,12 @@ export const runEmbeddingPipeline = async (executeQuery, executeWithReusedStatem
|
|
|
373
394
|
if (isDev) {
|
|
374
395
|
console.log(`✅ Embedding pipeline complete! (${totalChunks} chunks from ${totalNodes} nodes)`);
|
|
375
396
|
}
|
|
397
|
+
return {
|
|
398
|
+
nodesProcessed: totalNodes,
|
|
399
|
+
chunksProcessed: totalChunks,
|
|
400
|
+
vectorIndexReady,
|
|
401
|
+
semanticMode: vectorIndexReady ? 'vector-index' : 'exact-scan',
|
|
402
|
+
};
|
|
376
403
|
}
|
|
377
404
|
catch (error) {
|
|
378
405
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -397,26 +424,63 @@ export const semanticSearch = async (executeQuery, query, k = 10, maxDistance =
|
|
|
397
424
|
const queryEmbedding = await embedText(query);
|
|
398
425
|
const queryVec = embeddingToArray(queryEmbedding);
|
|
399
426
|
const queryVecStr = `[${queryVec.join(',')}]`;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
427
|
+
let bestChunks = new Map();
|
|
428
|
+
if (await loadVectorExtension()) {
|
|
429
|
+
try {
|
|
430
|
+
bestChunks = await collectBestChunks(k, async (fetchLimit) => {
|
|
431
|
+
const vectorQuery = `
|
|
432
|
+
CALL QUERY_VECTOR_INDEX('${EMBEDDING_TABLE_NAME}', '${EMBEDDING_INDEX_NAME}',
|
|
433
|
+
CAST(${queryVecStr} AS FLOAT[${queryVec.length}]), ${fetchLimit})
|
|
434
|
+
YIELD node AS emb, distance
|
|
435
|
+
WITH emb, distance
|
|
436
|
+
WHERE distance < ${maxDistance}
|
|
437
|
+
RETURN emb.nodeId AS nodeId, emb.chunkIndex AS chunkIndex,
|
|
438
|
+
emb.startLine AS startLine, emb.endLine AS endLine, distance
|
|
439
|
+
ORDER BY distance
|
|
440
|
+
`;
|
|
441
|
+
const embResults = await executeQuery(vectorQuery);
|
|
442
|
+
return embResults.map((row) => ({
|
|
443
|
+
nodeId: row.nodeId ?? row[0],
|
|
444
|
+
chunkIndex: row.chunkIndex ?? row[1] ?? 0,
|
|
445
|
+
startLine: row.startLine ?? row[2] ?? 0,
|
|
446
|
+
endLine: row.endLine ?? row[3] ?? 0,
|
|
447
|
+
distance: row.distance ?? row[4],
|
|
448
|
+
}));
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
bestChunks = new Map();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (bestChunks.size === 0) {
|
|
456
|
+
const countRows = await executeQuery(`MATCH (e:${EMBEDDING_TABLE_NAME}) RETURN count(e) AS cnt`);
|
|
457
|
+
const countRow = countRows[0];
|
|
458
|
+
const embeddingCount = Number(countRow?.cnt ?? countRow?.[0] ?? 0);
|
|
459
|
+
const exactLimit = getExactScanLimit();
|
|
460
|
+
if (embeddingCount > 0 && embeddingCount <= exactLimit) {
|
|
461
|
+
const rows = await executeQuery(`
|
|
462
|
+
MATCH (e:${EMBEDDING_TABLE_NAME})
|
|
463
|
+
RETURN e.nodeId AS nodeId, e.chunkIndex AS chunkIndex,
|
|
464
|
+
e.startLine AS startLine, e.endLine AS endLine, e.embedding AS embedding
|
|
465
|
+
`);
|
|
466
|
+
const exactRows = rows.map((row) => ({
|
|
467
|
+
nodeId: row.nodeId ?? row[0],
|
|
468
|
+
chunkIndex: row.chunkIndex ?? row[1] ?? 0,
|
|
469
|
+
startLine: row.startLine ?? row[2] ?? 0,
|
|
470
|
+
endLine: row.endLine ?? row[3] ?? 0,
|
|
471
|
+
embedding: row.embedding ?? row[4] ?? [],
|
|
472
|
+
}));
|
|
473
|
+
bestChunks = new Map(rankExactEmbeddingRows(exactRows, queryVec, k, maxDistance).map((row) => [
|
|
474
|
+
row.nodeId,
|
|
475
|
+
{
|
|
476
|
+
distance: row.distance,
|
|
477
|
+
chunkIndex: row.chunkIndex,
|
|
478
|
+
startLine: row.startLine,
|
|
479
|
+
endLine: row.endLine,
|
|
480
|
+
},
|
|
481
|
+
]));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
420
484
|
if (bestChunks.size === 0) {
|
|
421
485
|
return [];
|
|
422
486
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ExactEmbeddingRow {
|
|
2
|
+
nodeId: string;
|
|
3
|
+
chunkIndex: number;
|
|
4
|
+
startLine: number;
|
|
5
|
+
endLine: number;
|
|
6
|
+
embedding: readonly number[];
|
|
7
|
+
}
|
|
8
|
+
export interface ExactSearchChunk {
|
|
9
|
+
nodeId: string;
|
|
10
|
+
chunkIndex: number;
|
|
11
|
+
startLine: number;
|
|
12
|
+
endLine: number;
|
|
13
|
+
distance: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const rankExactEmbeddingRows: (rows: readonly ExactEmbeddingRow[], queryEmbedding: readonly number[], limit: number, maxDistance: number) => ExactSearchChunk[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const cosineDistance = (a, b) => {
|
|
2
|
+
let dot = 0;
|
|
3
|
+
let aNorm = 0;
|
|
4
|
+
let bNorm = 0;
|
|
5
|
+
const len = Math.min(a.length, b.length);
|
|
6
|
+
for (let i = 0; i < len; i++) {
|
|
7
|
+
const av = a[i] ?? 0;
|
|
8
|
+
const bv = b[i] ?? 0;
|
|
9
|
+
dot += av * bv;
|
|
10
|
+
aNorm += av * av;
|
|
11
|
+
bNorm += bv * bv;
|
|
12
|
+
}
|
|
13
|
+
if (aNorm === 0 || bNorm === 0)
|
|
14
|
+
return 1;
|
|
15
|
+
return 1 - dot / (Math.sqrt(aNorm) * Math.sqrt(bNorm));
|
|
16
|
+
};
|
|
17
|
+
export const rankExactEmbeddingRows = (rows, queryEmbedding, limit, maxDistance) => rows
|
|
18
|
+
.map((row) => ({
|
|
19
|
+
nodeId: row.nodeId,
|
|
20
|
+
chunkIndex: row.chunkIndex,
|
|
21
|
+
startLine: row.startLine,
|
|
22
|
+
endLine: row.endLine,
|
|
23
|
+
distance: cosineDistance(row.embedding, queryEmbedding),
|
|
24
|
+
}))
|
|
25
|
+
.filter((row) => row.distance < maxDistance)
|
|
26
|
+
.sort((a, b) => a.distance - b.distance)
|
|
27
|
+
.slice(0, limit);
|
|
@@ -102,6 +102,10 @@ export interface EmbeddingConfig {
|
|
|
102
102
|
modelId: string;
|
|
103
103
|
/** Number of nodes to embed in each batch */
|
|
104
104
|
batchSize: number;
|
|
105
|
+
/** Number of chunks passed to one local/HTTP embedding call */
|
|
106
|
+
subBatchSize: number;
|
|
107
|
+
/** Maximum ONNX Runtime CPU threads for local inference */
|
|
108
|
+
threads: number;
|
|
105
109
|
/** Embedding vector dimensions */
|
|
106
110
|
dimensions: number;
|
|
107
111
|
/** Device to use for inference: 'auto' tries GPU first (DirectML on Windows, CUDA on Linux), falls back to CPU */
|
|
@@ -147,6 +147,8 @@ export const CHUNKING_RULES = {
|
|
|
147
147
|
export const DEFAULT_EMBEDDING_CONFIG = {
|
|
148
148
|
modelId: 'Snowflake/snowflake-arctic-embed-xs',
|
|
149
149
|
batchSize: 16,
|
|
150
|
+
subBatchSize: 8,
|
|
151
|
+
threads: 2,
|
|
150
152
|
dimensions: 384,
|
|
151
153
|
device: 'auto',
|
|
152
154
|
maxSnippetLength: 500,
|
|
@@ -14,6 +14,8 @@ const DEFAULT_MATCHING = {
|
|
|
14
14
|
bm25_threshold: 0.7,
|
|
15
15
|
embedding_threshold: 0.65,
|
|
16
16
|
max_candidates_per_step: 3,
|
|
17
|
+
exclude_links_paths: [],
|
|
18
|
+
exclude_links_param_only_paths: false,
|
|
17
19
|
};
|
|
18
20
|
export function parseGroupConfig(yamlContent) {
|
|
19
21
|
const raw = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StoredContract, CrossLink } from './types.js';
|
|
1
|
+
import type { StoredContract, CrossLink, MatchingConfig } from './types.js';
|
|
2
2
|
export interface MatchResult {
|
|
3
3
|
matched: CrossLink[];
|
|
4
4
|
unmatched: StoredContract[];
|
|
@@ -8,6 +8,6 @@ export interface WildcardMatchResult {
|
|
|
8
8
|
remaining: StoredContract[];
|
|
9
9
|
}
|
|
10
10
|
export declare function normalizeContractId(id: string): string;
|
|
11
|
-
export declare function buildProviderIndex(contracts: StoredContract[]): Map<string, StoredContract[]>;
|
|
12
|
-
export declare function runExactMatch(contracts: StoredContract[], providerIndex?: Map<string, StoredContract[]
|
|
11
|
+
export declare function buildProviderIndex(contracts: StoredContract[], matchingConfig?: MatchingConfig): Map<string, StoredContract[]>;
|
|
12
|
+
export declare function runExactMatch(contracts: StoredContract[], providerIndex?: Map<string, StoredContract[]>, matchingConfig?: MatchingConfig): MatchResult;
|
|
13
13
|
export declare function runWildcardMatch(unmatched: StoredContract[], providerIndex: Map<string, StoredContract[]>): WildcardMatchResult;
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
function isGrpcWildcard(cid) {
|
|
2
2
|
return cid.startsWith('grpc::') && cid.endsWith('/*');
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* Detect HTTP contracts that are too generic or infrastructure-level to
|
|
6
|
+
* produce meaningful cross-repo links. These are still extracted (useful
|
|
7
|
+
* for documentation / route maps) but excluded from cross-link matching.
|
|
8
|
+
*
|
|
9
|
+
* Two categories:
|
|
10
|
+
* 1. Health-check / readiness endpoints — every service has one, matching
|
|
11
|
+
* them produces N×M false links.
|
|
12
|
+
* 2. Param-only paths — routes like `/{param}` or `/{param}/{param}` that
|
|
13
|
+
* collapse to a single catch-all after normalization. These match any
|
|
14
|
+
* service with a similar shape, producing false positives.
|
|
15
|
+
*
|
|
16
|
+
* Both are configurable via matching.exclude_links_paths and
|
|
17
|
+
* matching.exclude_links_param_only_paths in group.yaml.
|
|
18
|
+
*/
|
|
19
|
+
function buildNoisyContractFilter(matchingConfig) {
|
|
20
|
+
const excludePaths = matchingConfig?.exclude_links_paths?.length
|
|
21
|
+
? new Set(matchingConfig.exclude_links_paths.map((p) => p.replace(/\/+$/, '')))
|
|
22
|
+
: new Set();
|
|
23
|
+
const excludeParamOnly = matchingConfig?.exclude_links_param_only_paths === true;
|
|
24
|
+
return function isNoisyHttpContract(contractId) {
|
|
25
|
+
if (!contractId.startsWith('http::'))
|
|
26
|
+
return false;
|
|
27
|
+
const parts = contractId.split('::');
|
|
28
|
+
if (parts.length < 3)
|
|
29
|
+
return false;
|
|
30
|
+
const pathPart = parts.slice(2).join('::').replace(/\/+$/, '');
|
|
31
|
+
if (excludePaths.has(pathPart))
|
|
32
|
+
return true;
|
|
33
|
+
if (excludeParamOnly) {
|
|
34
|
+
const segments = pathPart.split('/').filter(Boolean);
|
|
35
|
+
if (segments.length > 0 && segments.every((s) => s === '{param}'))
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
4
41
|
export function normalizeContractId(id) {
|
|
5
42
|
const colonIdx = id.indexOf('::');
|
|
6
43
|
if (colonIdx === -1)
|
|
@@ -74,8 +111,9 @@ function findMatchingKeys(contractId, index) {
|
|
|
74
111
|
}
|
|
75
112
|
return [];
|
|
76
113
|
}
|
|
77
|
-
export function buildProviderIndex(contracts) {
|
|
78
|
-
const
|
|
114
|
+
export function buildProviderIndex(contracts, matchingConfig) {
|
|
115
|
+
const isNoisy = buildNoisyContractFilter(matchingConfig);
|
|
116
|
+
const providers = contracts.filter((c) => c.role === 'provider' && !isNoisy(c.contractId));
|
|
79
117
|
const index = new Map();
|
|
80
118
|
for (const p of providers) {
|
|
81
119
|
const key = normalizeContractId(p.contractId);
|
|
@@ -85,10 +123,10 @@ export function buildProviderIndex(contracts) {
|
|
|
85
123
|
}
|
|
86
124
|
return index;
|
|
87
125
|
}
|
|
88
|
-
export function runExactMatch(contracts, providerIndex) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const consumers = contracts.filter((c) => c.role === 'consumer' && !isGrpcWildcard(c.contractId));
|
|
126
|
+
export function runExactMatch(contracts, providerIndex, matchingConfig) {
|
|
127
|
+
const isNoisy = buildNoisyContractFilter(matchingConfig);
|
|
128
|
+
const index = providerIndex ?? buildProviderIndex(contracts, matchingConfig);
|
|
129
|
+
const consumers = contracts.filter((c) => c.role === 'consumer' && !isGrpcWildcard(c.contractId) && !isNoisy(c.contractId));
|
|
92
130
|
const matched = [];
|
|
93
131
|
const matchedConsumerIds = new Set();
|
|
94
132
|
const matchedProviderIds = new Set();
|
|
@@ -129,6 +167,8 @@ export function runExactMatch(contracts, providerIndex) {
|
|
|
129
167
|
const normalUnmatched = contracts.filter((c) => {
|
|
130
168
|
if (isGrpcWildcard(c.contractId))
|
|
131
169
|
return false; // excluded from exact, handled separately
|
|
170
|
+
if (isNoisy(c.contractId))
|
|
171
|
+
return false; // excluded from matching — don't surface as unmatched
|
|
132
172
|
const id = `${c.repo}::${c.contractId}`;
|
|
133
173
|
return c.role === 'provider' ? !matchedProviderIds.has(id) : !matchedConsumerIds.has(id);
|
|
134
174
|
});
|
|
@@ -85,6 +85,8 @@ matching:
|
|
|
85
85
|
bm25_threshold: 0.7
|
|
86
86
|
embedding_threshold: 0.65
|
|
87
87
|
max_candidates_per_step: 3
|
|
88
|
+
# exclude_links_paths: [/ping, /health, /healthcheck]
|
|
89
|
+
# exclude_links_param_only_paths: false
|
|
88
90
|
`;
|
|
89
91
|
await fsp.writeFile(path.join(groupDir, 'group.yaml'), template, 'utf-8');
|
|
90
92
|
return groupDir;
|
package/dist/core/group/sync.js
CHANGED
|
@@ -168,7 +168,7 @@ export async function syncGroup(config, opts) {
|
|
|
168
168
|
console.log(` manifest: ${manifestCrossLinks.length} cross-links from ${config.links.length} declared links`);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
const { matched, unmatched } = runExactMatch(autoContracts);
|
|
171
|
+
const { matched, unmatched } = runExactMatch(autoContracts, undefined, config.matching);
|
|
172
172
|
// Dedupe cross-links. Manifest contracts participate in runExactMatch, so a
|
|
173
173
|
// manifest-declared link can also emit a matchType:'exact' CrossLink with the
|
|
174
174
|
// same endpoints. Prefer the manifest version — it reflects operator intent
|
|
@@ -29,6 +29,24 @@ export interface MatchingConfig {
|
|
|
29
29
|
bm25_threshold: number;
|
|
30
30
|
embedding_threshold: number;
|
|
31
31
|
max_candidates_per_step: number;
|
|
32
|
+
/**
|
|
33
|
+
* HTTP paths to exclude from cross-link matching. Contracts at these paths
|
|
34
|
+
* are still extracted and visible in the registry, but they don't produce
|
|
35
|
+
* cross-repo links. Useful for health-check endpoints (`/ping`, `/health`)
|
|
36
|
+
* that every service exposes and would otherwise create N×M false links.
|
|
37
|
+
* Trailing slashes are normalized before comparison.
|
|
38
|
+
* @default []
|
|
39
|
+
*/
|
|
40
|
+
exclude_links_paths?: string[];
|
|
41
|
+
/**
|
|
42
|
+
* When `true`, exclude HTTP routes where every path segment is `{param}`
|
|
43
|
+
* (e.g. `/{param}`, `/{param}/{param}`) from cross-link matching. Mixed
|
|
44
|
+
* routes like `/users/{param}` are not affected. These param-only routes
|
|
45
|
+
* collapse to a single catch-all after normalization and produce false
|
|
46
|
+
* positives across unrelated services.
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
exclude_links_param_only_paths?: boolean;
|
|
32
50
|
}
|
|
33
51
|
export interface SymbolRef {
|
|
34
52
|
filePath: string;
|
|
@@ -130,9 +130,9 @@ export declare const resolveMemberCall: (ownerType: string, methodName: string,
|
|
|
130
130
|
* resolution via `ctx.resolve()`.
|
|
131
131
|
*
|
|
132
132
|
* Used for `foo()`, `doStuff()` — unqualified calls with no receiver.
|
|
133
|
-
* Also handles
|
|
134
|
-
*
|
|
135
|
-
*
|
|
133
|
+
* Also handles implicit constructors (`User()` without `new`) by delegating
|
|
134
|
+
* to {@link resolveStaticCall} when the tiered pool contains class-like
|
|
135
|
+
* targets.
|
|
136
136
|
*
|
|
137
137
|
* {@link resolveCallTarget} delegates here for `callForm === 'free'`.
|
|
138
138
|
*
|
|
@@ -3,7 +3,7 @@ import { CLASS_TYPES, CALL_TARGET_TYPES, lookupMethodByOwnerWithMRO } from './mo
|
|
|
3
3
|
* DAG stage 4 fallback: used when `selectDispatch` is absent or returns null.
|
|
4
4
|
* Preserves pre-DAG dispatch semantics:
|
|
5
5
|
* - 'constructor' → constructor branch
|
|
6
|
-
* - 'free' → free branch (admits
|
|
6
|
+
* - 'free' → free branch (admits class-target fast path)
|
|
7
7
|
* - 'member' or undefined → owner-scoped branch
|
|
8
8
|
*
|
|
9
9
|
* `undefined` callForm MUST route through owner-scoped (not free) so bare
|
|
@@ -613,7 +613,7 @@ importedRawReturnTypesMap, heritageMap, bindingAccumulator) => {
|
|
|
613
613
|
if (!tree) {
|
|
614
614
|
try {
|
|
615
615
|
tree = parser.parse(file.content, undefined, {
|
|
616
|
-
bufferSize: getTreeSitterBufferSize(file.content
|
|
616
|
+
bufferSize: getTreeSitterBufferSize(file.content),
|
|
617
617
|
});
|
|
618
618
|
}
|
|
619
619
|
catch (parseError) {
|
|
@@ -1275,41 +1275,19 @@ const disambiguateByOverloadOrArgTypes = (pool, overloadHints, preComputedArgTyp
|
|
|
1275
1275
|
return matchCandidatesByArgTypes(pool, preComputedArgTypes);
|
|
1276
1276
|
return null;
|
|
1277
1277
|
};
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
*
|
|
1282
|
-
* Swift extensions (`extension User { ... }` in a separate file) create
|
|
1283
|
-
* multiple `Class` nodes sharing the same symbol name — one for the primary
|
|
1284
|
-
* declaration and one per extension file. When overload disambiguation and
|
|
1285
|
-
* receiver narrowing both fail to converge on a single candidate, this
|
|
1286
|
-
* heuristic picks the primary definition based on the assumption that it
|
|
1287
|
-
* lives at the shortest file path (e.g. `User.swift` over `UserExtensions.swift`).
|
|
1288
|
-
*
|
|
1289
|
-
* Intentionally narrower than {@link INSTANTIABLE_CLASS_TYPES}: only `Class`
|
|
1290
|
-
* and `Struct` are considered, not `Record`. Swift extensions only produce
|
|
1291
|
-
* `Class` duplicates in practice, and C#/Kotlin records do not exhibit the
|
|
1292
|
-
* same multi-file-definition pattern, so widening this set risks accidental
|
|
1293
|
-
* dedup of legitimately distinct record types.
|
|
1294
|
-
*
|
|
1295
|
-
* Returns a `ResolveResult` when the heuristic fires, `null` when the
|
|
1296
|
-
* candidate pool does not match the shape (mixed types, non-Class/Struct
|
|
1297
|
-
* kinds, or `length <= 1`). Callers should fall through to their own null
|
|
1298
|
-
* return when this helper returns `null`.
|
|
1299
|
-
*
|
|
1300
|
-
* Used by `resolveFreeCall`. Having a single source of truth prevents
|
|
1301
|
-
* duplication if the heuristic is ever tuned.
|
|
1302
|
-
*/
|
|
1303
|
-
const dedupSwiftExtensionCandidates = (candidates, tier) => {
|
|
1304
|
-
if (candidates.length <= 1)
|
|
1305
|
-
return null;
|
|
1306
|
-
const allSameType = candidates.every((c) => c.type === candidates[0].type);
|
|
1307
|
-
if (!allSameType)
|
|
1308
|
-
return null;
|
|
1309
|
-
if (candidates[0].type !== 'Class' && candidates[0].type !== 'Struct')
|
|
1278
|
+
const orderProviderSameNameTypeCandidates = (candidates, typeName, filePath) => {
|
|
1279
|
+
const language = getLanguageFromFilename(filePath);
|
|
1280
|
+
if (language == null)
|
|
1310
1281
|
return null;
|
|
1311
|
-
|
|
1312
|
-
|
|
1282
|
+
return (getProvider(language).orderSameNameTypeCandidates?.({
|
|
1283
|
+
typeName,
|
|
1284
|
+
callSiteFilePath: filePath,
|
|
1285
|
+
candidates,
|
|
1286
|
+
}) ?? null);
|
|
1287
|
+
};
|
|
1288
|
+
const resolveProviderPrimaryTypeCandidate = (candidates, tier, typeName, filePath) => {
|
|
1289
|
+
const ordered = orderProviderSameNameTypeCandidates(candidates, typeName, filePath);
|
|
1290
|
+
return ordered && ordered.length > 0 ? toResolveResult(ordered[0], tier) : null;
|
|
1313
1291
|
};
|
|
1314
1292
|
/**
|
|
1315
1293
|
* Thin dispatcher that routes a call to the appropriate specialized resolver.
|
|
@@ -1742,6 +1720,25 @@ ancestryView) => {
|
|
|
1742
1720
|
break;
|
|
1743
1721
|
}
|
|
1744
1722
|
}
|
|
1723
|
+
if (!firstDef && !ambiguous) {
|
|
1724
|
+
const orderedTypeCandidates = orderProviderSameNameTypeCandidates(ctx.model.types.lookupClassByName(receiverTypeName), receiverTypeName, filePath);
|
|
1725
|
+
if (orderedTypeCandidates) {
|
|
1726
|
+
for (const candidate of orderedTypeCandidates) {
|
|
1727
|
+
const def = canWalkMRO
|
|
1728
|
+
? lookupMethodByOwnerWithMRO(candidate.nodeId, methodName, heritageMap, ctx.model, mroStrategy, argCount)
|
|
1729
|
+
: ctx.model.methods.lookupMethodByOwner(candidate.nodeId, methodName, argCount);
|
|
1730
|
+
if (!def)
|
|
1731
|
+
continue;
|
|
1732
|
+
if (!firstDef) {
|
|
1733
|
+
firstDef = def;
|
|
1734
|
+
}
|
|
1735
|
+
else if (def.nodeId !== firstDef.nodeId) {
|
|
1736
|
+
ambiguous = true;
|
|
1737
|
+
break;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1745
1742
|
if (!firstDef || ambiguous)
|
|
1746
1743
|
return undefined;
|
|
1747
1744
|
return { def: firstDef, tier: typeResolved.tier };
|
|
@@ -1791,9 +1788,9 @@ export const resolveMemberCall = (ownerType, methodName, currentFile, ctx, herit
|
|
|
1791
1788
|
* resolution via `ctx.resolve()`.
|
|
1792
1789
|
*
|
|
1793
1790
|
* Used for `foo()`, `doStuff()` — unqualified calls with no receiver.
|
|
1794
|
-
* Also handles
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1791
|
+
* Also handles implicit constructors (`User()` without `new`) by delegating
|
|
1792
|
+
* to {@link resolveStaticCall} when the tiered pool contains class-like
|
|
1793
|
+
* targets.
|
|
1797
1794
|
*
|
|
1798
1795
|
* {@link resolveCallTarget} delegates here for `callForm === 'free'`.
|
|
1799
1796
|
*
|
|
@@ -1816,33 +1813,30 @@ export const resolveFreeCall = (calledName, filePath, ctx, argCount, tieredOverr
|
|
|
1816
1813
|
if (!tiered)
|
|
1817
1814
|
return null;
|
|
1818
1815
|
let filteredCandidates = filterCallableCandidates(tiered.candidates, argCount, 'free');
|
|
1819
|
-
// Class-target fast path:
|
|
1820
|
-
//
|
|
1816
|
+
// Class-target fast path: free-form call targeting a class. Delegates to
|
|
1817
|
+
// resolveStaticCall for O(1) class + constructor lookup.
|
|
1821
1818
|
// The `.some()` trigger must stay aligned with `INSTANTIABLE_CLASS_TYPES` —
|
|
1822
1819
|
// any type admitted here that is not in that set will cause resolveStaticCall
|
|
1823
1820
|
// to return null, wasting two lookup passes per call. `Enum` is deliberately
|
|
1824
|
-
// excluded; `Record` is included so
|
|
1825
|
-
//
|
|
1821
|
+
// excluded; `Record` is included so record-like class targets reach the fast
|
|
1822
|
+
// path.
|
|
1826
1823
|
// Align with INSTANTIABLE_CLASS_TYPES by reusing the set directly rather
|
|
1827
1824
|
// than enumerating literal strings. This converts an invariant that was
|
|
1828
1825
|
// previously enforced by a comment ("keep this list aligned with
|
|
1829
1826
|
// INSTANTIABLE_CLASS_TYPES") into one enforced structurally — any future
|
|
1830
|
-
// extension of the set
|
|
1831
|
-
//
|
|
1832
|
-
//
|
|
1833
|
-
//
|
|
1834
|
-
// is excluded there by design. Do not collapse that helper into
|
|
1835
|
-
// INSTANTIABLE_CLASS_TYPES.
|
|
1827
|
+
// extension of the set propagates here automatically.
|
|
1828
|
+
// Language providers can still choose a primary same-name type candidate in
|
|
1829
|
+
// the tail of this function when their grammars index one logical type
|
|
1830
|
+
// multiple times.
|
|
1836
1831
|
const hasClassTarget = filteredCandidates.length === 0 &&
|
|
1837
1832
|
tiered.candidates.some((c) => INSTANTIABLE_CLASS_TYPES.has(c.type));
|
|
1838
1833
|
if (hasClassTarget) {
|
|
1839
1834
|
const staticResult = resolveStaticCall(calledName, filePath, ctx, argCount, tiered);
|
|
1840
1835
|
if (staticResult)
|
|
1841
1836
|
return staticResult;
|
|
1842
|
-
// Retry with constructor form
|
|
1843
|
-
// free function calls
|
|
1844
|
-
//
|
|
1845
|
-
// applies.
|
|
1837
|
+
// Retry with constructor form for languages whose constructor calls look
|
|
1838
|
+
// like free function calls. If resolveStaticCall didn't match, re-filter
|
|
1839
|
+
// with constructor form so CONSTRUCTOR_TARGET_TYPES applies.
|
|
1846
1840
|
//
|
|
1847
1841
|
// The retry fires for every null return from `resolveStaticCall`, which
|
|
1848
1842
|
// can happen for three distinct reasons — all three are handled below:
|
|
@@ -1856,9 +1850,8 @@ export const resolveFreeCall = (calledName, filePath, ctx, argCount, tieredOverr
|
|
|
1856
1850
|
// (b) Homonym ambiguity — two or more instantiable class candidates
|
|
1857
1851
|
// share the name (e.g. `User` in two files, same tier). The
|
|
1858
1852
|
// retry repopulates `filteredCandidates` with both Classes and
|
|
1859
|
-
// they flow into
|
|
1860
|
-
//
|
|
1861
|
-
// Covered by the R7 Swift-extension dedup test.
|
|
1853
|
+
// they flow into the provider same-name candidate hook below, which
|
|
1854
|
+
// can pick a primary definition or null-route.
|
|
1862
1855
|
//
|
|
1863
1856
|
// (c) `resolveStaticCall` step 4 bailed because the tiered pool
|
|
1864
1857
|
// contains ownerless `Constructor` nodes (some extractors emit
|
|
@@ -1882,11 +1875,9 @@ export const resolveFreeCall = (calledName, filePath, ctx, argCount, tieredOverr
|
|
|
1882
1875
|
return toResolveResult(disambiguated, tiered.tier);
|
|
1883
1876
|
}
|
|
1884
1877
|
if (filteredCandidates.length !== 1) {
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
if (deduped)
|
|
1889
|
-
return deduped;
|
|
1878
|
+
const primary = resolveProviderPrimaryTypeCandidate(filteredCandidates, tiered.tier, calledName, filePath);
|
|
1879
|
+
if (primary)
|
|
1880
|
+
return primary;
|
|
1890
1881
|
return null;
|
|
1891
1882
|
}
|
|
1892
1883
|
return toResolveResult(filteredCandidates[0], tiered.tier);
|
|
@@ -2034,9 +2025,11 @@ export const resolveStaticCall = (className, currentFile, ctx, argCount, tieredO
|
|
|
2034
2025
|
// Interface / Trait / Impl). Null-route via the fall-through `return
|
|
2035
2026
|
// null` — this is the dominant Codex-fix case.
|
|
2036
2027
|
// length === 1 → a single instantiable candidate remains, return it.
|
|
2037
|
-
// length > 1 →
|
|
2038
|
-
//
|
|
2039
|
-
|
|
2028
|
+
// length > 1 → let the call-site provider choose a primary when it can
|
|
2029
|
+
// prove the candidates are one logical type; otherwise null-route.
|
|
2030
|
+
const primary = resolveProviderPrimaryTypeCandidate(instantiableCandidates, typeResolved.tier, className, currentFile);
|
|
2031
|
+
if (primary)
|
|
2032
|
+
return primary;
|
|
2040
2033
|
if (instantiableCandidates.length === 1) {
|
|
2041
2034
|
return toResolveResult(instantiableCandidates[0], typeResolved.tier);
|
|
2042
2035
|
}
|
|
@@ -2591,7 +2584,7 @@ export const extractFetchCallsFromFiles = async (files, astCache) => {
|
|
|
2591
2584
|
if (!tree) {
|
|
2592
2585
|
try {
|
|
2593
2586
|
tree = parser.parse(file.content, undefined, {
|
|
2594
|
-
bufferSize: getTreeSitterBufferSize(file.content
|
|
2587
|
+
bufferSize: getTreeSitterBufferSize(file.content),
|
|
2595
2588
|
});
|
|
2596
2589
|
}
|
|
2597
2590
|
catch {
|
|
@@ -10,7 +10,8 @@ export declare const TREE_SITTER_BUFFER_SIZE: number;
|
|
|
10
10
|
export declare const TREE_SITTER_MAX_BUFFER: number;
|
|
11
11
|
/**
|
|
12
12
|
* Compute adaptive buffer size for tree-sitter parsing.
|
|
13
|
-
* Uses
|
|
14
|
-
*
|
|
13
|
+
* Uses 2x UTF-8 byte size, clamped between 512 KB and 32 MB.
|
|
14
|
+
* Keeps tree-sitter's byte-sized buffer above large ASCII and multibyte sources.
|
|
15
15
|
*/
|
|
16
|
-
export declare const
|
|
16
|
+
export declare const getTreeSitterContentByteLength: (sourceText: string) => number;
|
|
17
|
+
export declare const getTreeSitterBufferSize: (sourceText: string) => number;
|