gitnexus 1.6.3-rc.6 → 1.6.3-rc.7

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.
@@ -6,7 +6,7 @@ export { getLanguageFromFilename, getSyntaxLanguageFromFilename } from './langua
6
6
  export type { MroStrategy } from './mro-strategy.js';
7
7
  export type { PipelinePhase, PipelineProgress } from './pipeline.js';
8
8
  export type { SymbolDefinition } from './scope-resolution/symbol-definition.js';
9
- export type { ScopeId, DefId, ScopeKind, Range, Capture, CaptureMatch, BindingRef, ImportEdge, TypeRef, Scope, ResolutionEvidence, Resolution, Reference, ReferenceIndex, LookupParams, RegistryContributor, ParsedImport, ParsedTypeBinding, WorkspaceIndex, ScopeTree, Callsite, } from './scope-resolution/types.js';
9
+ export type { ScopeId, DefId, ScopeKind, Range, Capture, CaptureMatch, BindingRef, ImportEdge, TypeRef, Scope, ResolutionEvidence, Resolution, Reference, ReferenceIndex, LookupParams, RegistryContributor, ParsedImport, ParsedTypeBinding, WorkspaceIndex, Callsite, } from './scope-resolution/types.js';
10
10
  export { EvidenceWeights, typeBindingWeightAtDepth } from './scope-resolution/evidence-weights.js';
11
11
  export { ORIGIN_PRIORITY } from './scope-resolution/origin-priority.js';
12
12
  export type { OriginForTieBreak } from './scope-resolution/origin-priority.js';
@@ -22,6 +22,12 @@ export { resolveTypeRef } from './scope-resolution/resolve-type-ref.js';
22
22
  export type { ResolveTypeRefContext, ScopeLookup } from './scope-resolution/resolve-type-ref.js';
23
23
  export { buildMethodDispatchIndex } from './scope-resolution/method-dispatch-index.js';
24
24
  export type { MethodDispatchIndex, MethodDispatchInput, } from './scope-resolution/method-dispatch-index.js';
25
+ export { makeScopeId, clearScopeIdInternPool } from './scope-resolution/scope-id.js';
26
+ export type { ScopeIdInput } from './scope-resolution/scope-id.js';
27
+ export { buildScopeTree, ScopeTreeInvariantError } from './scope-resolution/scope-tree.js';
28
+ export type { ScopeTree } from './scope-resolution/scope-tree.js';
29
+ export { buildPositionIndex } from './scope-resolution/position-index.js';
30
+ export type { PositionIndex } from './scope-resolution/position-index.js';
25
31
  export { diffResolutions } from './scope-resolution/shadow/diff.js';
26
32
  export type { ShadowAgreement, ShadowCallsite, ShadowDiff, } from './scope-resolution/shadow/diff.js';
27
33
  export { aggregateDiffs } from './scope-resolution/shadow/aggregate.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAGzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AACjG,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAIrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAChF,YAAY,EACV,OAAO,EACP,KAAK,EACL,SAAS,EACT,KAAK,EACL,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,OAAO,EACP,KAAK,EACL,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,QAAQ,GACT,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG/E,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAG5F,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACnG,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,YAAY,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AAGjG,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AACvF,YAAY,EACV,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,YAAY,EACV,eAAe,EACf,cAAc,EACd,UAAU,GACX,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAGzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AACjG,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAIrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAChF,YAAY,EACV,OAAO,EACP,KAAK,EACL,SAAS,EACT,KAAK,EACL,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,OAAO,EACP,KAAK,EACL,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,QAAQ,GACT,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG/E,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAG5F,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACnG,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,YAAY,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AAGjG,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AACvF,YAAY,EACV,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACrF,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3F,YAAY,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,YAAY,EACV,eAAe,EACf,cAAc,EACd,UAAU,GACX,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC"}
@@ -16,6 +16,10 @@ export { buildQualifiedNameIndex } from './scope-resolution/qualified-name-index
16
16
  export { resolveTypeRef } from './scope-resolution/resolve-type-ref.js';
17
17
  // Method-dispatch materialized view over HeritageMap (RFC §3.1; Ring 2 SHARED #914)
18
18
  export { buildMethodDispatchIndex } from './scope-resolution/method-dispatch-index.js';
19
+ // Scope tree spine + position lookup (RFC §2.2 + §3.1; Ring 2 SHARED #912)
20
+ export { makeScopeId, clearScopeIdInternPool } from './scope-resolution/scope-id.js';
21
+ export { buildScopeTree, ScopeTreeInvariantError } from './scope-resolution/scope-tree.js';
22
+ export { buildPositionIndex } from './scope-resolution/position-index.js';
19
23
  // Shadow-mode diff + aggregation (RFC §6.3; Ring 2 SHARED #918)
20
24
  export { diffResolutions } from './scope-resolution/shadow/diff.js';
21
25
  export { aggregateDiffs } from './scope-resolution/shadow/aggregate.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAiCjG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAGxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAgCjG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAGxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAE3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `PositionIndex` — O(log N_file) scope-at-position lookup
3
+ * (RFC §3.1; Ring 2 SHARED #912).
4
+ *
5
+ * Per-file sorted array of `(range, scopeId)` entries, sorted by start
6
+ * position ASC (`startLine`, then `startCol`). `atPosition(filePath, line,
7
+ * col)` binary-searches for the last entry whose start ≤ (line, col), then
8
+ * scans backward through the sorted prefix and returns the first entry
9
+ * whose range contains the query position.
10
+ *
11
+ * **Why this works.** `ScopeTree`'s invariants (parent strictly contains
12
+ * child; siblings don't overlap) guarantee that the scopes containing a
13
+ * given point form an **ancestor chain**. When scanning backward through
14
+ * entries sorted by start position ASC, the first scope we find that
15
+ * contains the query is the innermost one — any deeper-starting scope
16
+ * that also contained the query would appear *later* in the sorted array,
17
+ * but we're only scanning entries with start ≤ query, so anything later
18
+ * necessarily starts after the query and can't contain it.
19
+ *
20
+ * Expected complexity: `O(log N_file + D)` where `D` is the lexical depth
21
+ * at the query position (typically ≤ 10). Worst-case degrades to `O(N_file)`
22
+ * only under pathological inputs (many scopes starting at the same line).
23
+ *
24
+ * **Line/column conventions.** Matches `Range` in `types.ts`: lines are
25
+ * 1-based, columns are 0-based. Ranges are **inclusive on both ends** —
26
+ * a scope whose `endLine:endCol` equals the query position still contains
27
+ * it. That matches how tree-sitter captures bodies (closing brace
28
+ * included) and how closed PR #902's `enclosingFunctions` behaved.
29
+ */
30
+ import type { Scope, ScopeId } from './types.js';
31
+ export interface PositionIndex {
32
+ /** Total scope entries indexed across all files. */
33
+ readonly size: number;
34
+ /**
35
+ * Innermost scope containing `(line, col)` in `filePath`, or `undefined`
36
+ * when nothing contains it (position before file start, after file end,
37
+ * or filePath not indexed).
38
+ */
39
+ atPosition(filePath: string, line: number, col: number): ScopeId | undefined;
40
+ }
41
+ /**
42
+ * Build a `PositionIndex` from a flat list of `Scope` records.
43
+ *
44
+ * Duplicate `id`s are tolerated and deduplicated — the caller's
45
+ * `ScopeTree.buildScopeTree` is the authoritative validator of scope
46
+ * identity, and the position index does not need to re-check that
47
+ * invariant.
48
+ */
49
+ export declare function buildPositionIndex(scopes: readonly Scope[]): PositionIndex;
50
+ //# sourceMappingURL=position-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-index.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/position-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAS,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,aAAa,CAqB1E"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * `PositionIndex` — O(log N_file) scope-at-position lookup
3
+ * (RFC §3.1; Ring 2 SHARED #912).
4
+ *
5
+ * Per-file sorted array of `(range, scopeId)` entries, sorted by start
6
+ * position ASC (`startLine`, then `startCol`). `atPosition(filePath, line,
7
+ * col)` binary-searches for the last entry whose start ≤ (line, col), then
8
+ * scans backward through the sorted prefix and returns the first entry
9
+ * whose range contains the query position.
10
+ *
11
+ * **Why this works.** `ScopeTree`'s invariants (parent strictly contains
12
+ * child; siblings don't overlap) guarantee that the scopes containing a
13
+ * given point form an **ancestor chain**. When scanning backward through
14
+ * entries sorted by start position ASC, the first scope we find that
15
+ * contains the query is the innermost one — any deeper-starting scope
16
+ * that also contained the query would appear *later* in the sorted array,
17
+ * but we're only scanning entries with start ≤ query, so anything later
18
+ * necessarily starts after the query and can't contain it.
19
+ *
20
+ * Expected complexity: `O(log N_file + D)` where `D` is the lexical depth
21
+ * at the query position (typically ≤ 10). Worst-case degrades to `O(N_file)`
22
+ * only under pathological inputs (many scopes starting at the same line).
23
+ *
24
+ * **Line/column conventions.** Matches `Range` in `types.ts`: lines are
25
+ * 1-based, columns are 0-based. Ranges are **inclusive on both ends** —
26
+ * a scope whose `endLine:endCol` equals the query position still contains
27
+ * it. That matches how tree-sitter captures bodies (closing brace
28
+ * included) and how closed PR #902's `enclosingFunctions` behaved.
29
+ */
30
+ /**
31
+ * Build a `PositionIndex` from a flat list of `Scope` records.
32
+ *
33
+ * Duplicate `id`s are tolerated and deduplicated — the caller's
34
+ * `ScopeTree.buildScopeTree` is the authoritative validator of scope
35
+ * identity, and the position index does not need to re-check that
36
+ * invariant.
37
+ */
38
+ export function buildPositionIndex(scopes) {
39
+ const entriesByFile = new Map();
40
+ const seen = new Set();
41
+ for (const scope of scopes) {
42
+ if (seen.has(scope.id))
43
+ continue;
44
+ seen.add(scope.id);
45
+ let bucket = entriesByFile.get(scope.filePath);
46
+ if (bucket === undefined) {
47
+ bucket = [];
48
+ entriesByFile.set(scope.filePath, bucket);
49
+ }
50
+ bucket.push({ id: scope.id, range: scope.range });
51
+ }
52
+ for (const bucket of entriesByFile.values()) {
53
+ bucket.sort(compareEntry);
54
+ }
55
+ return freezeIndex(entriesByFile, seen.size);
56
+ }
57
+ /**
58
+ * Sort by start position ASC, breaking ties by end position DESC so that
59
+ * larger (outer) scopes appear before their smaller (inner) co-starting
60
+ * siblings in the array. Makes the backward-scan contract crisp: the
61
+ * first containing hit from the end of the scanned prefix is the
62
+ * innermost scope.
63
+ */
64
+ function compareEntry(a, b) {
65
+ if (a.range.startLine !== b.range.startLine)
66
+ return a.range.startLine - b.range.startLine;
67
+ if (a.range.startCol !== b.range.startCol)
68
+ return a.range.startCol - b.range.startCol;
69
+ if (a.range.endLine !== b.range.endLine)
70
+ return b.range.endLine - a.range.endLine;
71
+ return b.range.endCol - a.range.endCol;
72
+ }
73
+ /** Whether `(line, col)` is at or after `range`'s start. */
74
+ function startIsAtOrBefore(range, line, col) {
75
+ if (range.startLine < line)
76
+ return true;
77
+ if (range.startLine > line)
78
+ return false;
79
+ return range.startCol <= col;
80
+ }
81
+ /** Whether `(line, col)` is at or before `range`'s end (inclusive). */
82
+ function endIsAtOrAfter(range, line, col) {
83
+ if (range.endLine > line)
84
+ return true;
85
+ if (range.endLine < line)
86
+ return false;
87
+ return range.endCol >= col;
88
+ }
89
+ /**
90
+ * Return the largest index `i` in `arr` where `arr[i].range` starts at or
91
+ * before `(line, col)`. Returns `-1` if no entry starts ≤ the query.
92
+ *
93
+ * Classic "upper bound - 1" binary search: find the first entry that
94
+ * starts *after* the query, then step back one.
95
+ */
96
+ function findLastStartLteIndex(arr, line, col) {
97
+ let lo = 0;
98
+ let hi = arr.length;
99
+ while (lo < hi) {
100
+ const mid = (lo + hi) >>> 1;
101
+ if (startIsAtOrBefore(arr[mid].range, line, col)) {
102
+ lo = mid + 1;
103
+ }
104
+ else {
105
+ hi = mid;
106
+ }
107
+ }
108
+ return lo - 1;
109
+ }
110
+ function freezeIndex(entriesByFile, size) {
111
+ return {
112
+ get size() {
113
+ return size;
114
+ },
115
+ atPosition(filePath, line, col) {
116
+ const bucket = entriesByFile.get(filePath);
117
+ if (bucket === undefined || bucket.length === 0)
118
+ return undefined;
119
+ const endIdx = findLastStartLteIndex(bucket, line, col);
120
+ if (endIdx < 0)
121
+ return undefined;
122
+ // Scan backward; first containing hit is innermost (see file header).
123
+ for (let i = endIdx; i >= 0; i--) {
124
+ const entry = bucket[i];
125
+ if (endIsAtOrAfter(entry.range, line, col)) {
126
+ // `startIsAtOrBefore` is guaranteed true by the binary search.
127
+ return entry.id;
128
+ }
129
+ }
130
+ return undefined;
131
+ },
132
+ };
133
+ }
134
+ //# sourceMappingURL=position-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"position-index.js","sourceRoot":"","sources":["../../src/scope-resolution/position-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `ScopeId` canonical constructor + string intern pool
3
+ * (RFC §2.2; Ring 2 SHARED #912).
4
+ *
5
+ * `ScopeId` is a deterministic string derived from the scope's file path,
6
+ * byte range, and kind:
7
+ *
8
+ * scope:{filePath}#{startLine}:{startCol}-{endLine}:{endCol}:{kind}
9
+ *
10
+ * Two scopes produced by reparsing the same file at the same positions are
11
+ * `===`-equal as strings. Beyond the canonical shape, `makeScopeId` also
12
+ * **interns** the string through a process-local pool, so repeated calls
13
+ * with structurally identical inputs return the same string reference —
14
+ * making `Map<ScopeId, ...>` lookups and cache keys identity-fast.
15
+ *
16
+ * The intern pool is unbounded. The number of distinct `ScopeId`s across a
17
+ * single indexing run is O(total scopes in workspace), which is bounded by
18
+ * source-text size and already in memory; interning adds no asymptotic
19
+ * pressure. `clearScopeIdInternPool` is exported for test isolation.
20
+ */
21
+ import type { Range } from './types.js';
22
+ import type { ScopeId, ScopeKind } from './types.js';
23
+ /** Inputs required to construct a canonical `ScopeId`. */
24
+ export interface ScopeIdInput {
25
+ readonly filePath: string;
26
+ readonly range: Range;
27
+ readonly kind: ScopeKind;
28
+ }
29
+ /**
30
+ * Build a canonical `ScopeId` from its structural parts and intern it.
31
+ *
32
+ * Pure + referentially transparent: given the same input shape, always
33
+ * returns the same string reference for the lifetime of the pool.
34
+ */
35
+ export declare function makeScopeId(input: ScopeIdInput): ScopeId;
36
+ /**
37
+ * Drop the intern pool. Intended for test setup/teardown — production code
38
+ * should not need this, since the pool's memory usage is bounded by the
39
+ * number of live scopes and cleaning it mid-run would break identity
40
+ * equality for existing scope ids.
41
+ */
42
+ export declare function clearScopeIdInternPool(): void;
43
+ //# sourceMappingURL=scope-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-id.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/scope-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErD,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAMxD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `ScopeId` canonical constructor + string intern pool
3
+ * (RFC §2.2; Ring 2 SHARED #912).
4
+ *
5
+ * `ScopeId` is a deterministic string derived from the scope's file path,
6
+ * byte range, and kind:
7
+ *
8
+ * scope:{filePath}#{startLine}:{startCol}-{endLine}:{endCol}:{kind}
9
+ *
10
+ * Two scopes produced by reparsing the same file at the same positions are
11
+ * `===`-equal as strings. Beyond the canonical shape, `makeScopeId` also
12
+ * **interns** the string through a process-local pool, so repeated calls
13
+ * with structurally identical inputs return the same string reference —
14
+ * making `Map<ScopeId, ...>` lookups and cache keys identity-fast.
15
+ *
16
+ * The intern pool is unbounded. The number of distinct `ScopeId`s across a
17
+ * single indexing run is O(total scopes in workspace), which is bounded by
18
+ * source-text size and already in memory; interning adds no asymptotic
19
+ * pressure. `clearScopeIdInternPool` is exported for test isolation.
20
+ */
21
+ /**
22
+ * Build a canonical `ScopeId` from its structural parts and intern it.
23
+ *
24
+ * Pure + referentially transparent: given the same input shape, always
25
+ * returns the same string reference for the lifetime of the pool.
26
+ */
27
+ export function makeScopeId(input) {
28
+ const raw = `scope:${input.filePath}#${input.range.startLine}:${input.range.startCol}-${input.range.endLine}:${input.range.endCol}:${input.kind}`;
29
+ const existing = INTERN_POOL.get(raw);
30
+ if (existing !== undefined)
31
+ return existing;
32
+ INTERN_POOL.set(raw, raw);
33
+ return raw;
34
+ }
35
+ /**
36
+ * Drop the intern pool. Intended for test setup/teardown — production code
37
+ * should not need this, since the pool's memory usage is bounded by the
38
+ * number of live scopes and cleaning it mid-run would break identity
39
+ * equality for existing scope ids.
40
+ */
41
+ export function clearScopeIdInternPool() {
42
+ INTERN_POOL.clear();
43
+ }
44
+ /** Internal: shared intern pool (process-local). */
45
+ const INTERN_POOL = new Map();
46
+ //# sourceMappingURL=scope-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-id.js","sourceRoot":"","sources":["../../src/scope-resolution/scope-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAYH;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAmB;IAC7C,MAAM,GAAG,GAAG,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;IAClJ,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB;IACpC,WAAW,CAAC,KAAK,EAAE,CAAC;AACtB,CAAC;AAED,oDAAoD;AACpD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `ScopeTree` — the lexical-scope spine of the `SemanticModel`
3
+ * (RFC §2.2 + §3.1; Ring 2 SHARED #912).
4
+ *
5
+ * Generalizes the `enclosingFunctions` pattern from closed PR #902 to
6
+ * arbitrary `ScopeKind`s. Owns the (parent ↔ children) relationship
7
+ * derived from each `Scope.parent` pointer, and validates the structural
8
+ * invariants a well-formed scope tree must satisfy.
9
+ *
10
+ * Invariants enforced at build time (throw on violation):
11
+ *
12
+ * - Every non-`Module` scope has a non-null parent.
13
+ * - Every parent pointer references a scope that was also supplied to
14
+ * `buildScopeTree`.
15
+ * - Parent range **strictly contains** child range.
16
+ * - Sibling ranges under the same parent do not overlap.
17
+ * - Parent and child live in the same `filePath`. (Cross-file parent
18
+ * pointers would be a category error — a `File` scope is not the
19
+ * parent of another file's scopes; imports do that job.)
20
+ *
21
+ * Satisfies the `ScopeLookup` contract from #916 (`resolve-type-ref`), so
22
+ * `resolveTypeRef` can take a `ScopeTree` directly without adapters.
23
+ *
24
+ * Immutable surface: `byId` is a `ReadonlyMap`; children arrays are
25
+ * `Object.freeze`d; miss lookups return a shared frozen empty array.
26
+ */
27
+ import type { Scope, ScopeId } from './types.js';
28
+ import type { ScopeLookup } from './resolve-type-ref.js';
29
+ export interface ScopeTree extends ScopeLookup {
30
+ readonly size: number;
31
+ readonly byId: ReadonlyMap<ScopeId, Scope>;
32
+ getScope(id: ScopeId): Scope | undefined;
33
+ getParent(id: ScopeId): Scope | undefined;
34
+ /** Child `ScopeId`s of `id`, in input order. Frozen empty array on miss. */
35
+ getChildren(id: ScopeId): readonly ScopeId[];
36
+ /**
37
+ * Ancestor chain from the immediate parent up to (and including) the
38
+ * root module scope. Excludes the starting scope itself. Frozen empty
39
+ * array on miss / for a root scope.
40
+ */
41
+ getAncestors(id: ScopeId): readonly ScopeId[];
42
+ has(id: ScopeId): boolean;
43
+ }
44
+ /**
45
+ * Thrown by `buildScopeTree` when the input violates a structural
46
+ * invariant. Carries the offending ids + the invariant name so failed
47
+ * extraction pipelines can report actionable diagnostics.
48
+ */
49
+ export declare class ScopeTreeInvariantError extends Error {
50
+ readonly invariant: 'non-module-requires-parent' | 'parent-not-found' | 'parent-must-contain-child' | 'sibling-ranges-overlap' | 'parent-must-share-filepath' | 'duplicate-scope-id';
51
+ constructor(invariant: 'non-module-requires-parent' | 'parent-not-found' | 'parent-must-contain-child' | 'sibling-ranges-overlap' | 'parent-must-share-filepath' | 'duplicate-scope-id', message: string);
52
+ }
53
+ /**
54
+ * Build an immutable `ScopeTree` from a flat list of `Scope` records.
55
+ *
56
+ * Throws `ScopeTreeInvariantError` on the first invariant violation; a
57
+ * malformed tree is a bug in the extraction pipeline, not a data case for
58
+ * consumers to handle, so fail-fast is the correct posture.
59
+ */
60
+ export declare function buildScopeTree(scopes: readonly Scope[]): ScopeTree;
61
+ //# sourceMappingURL=scope-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-tree.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/scope-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAS,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE3C,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAC1C,4EAA4E;IAC5E,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAAC;IAC9C,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3B;AAID;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,SAAS,EACd,4BAA4B,GAC5B,kBAAkB,GAClB,2BAA2B,GAC3B,wBAAwB,GACxB,4BAA4B,GAC5B,oBAAoB;gBANf,SAAS,EACd,4BAA4B,GAC5B,kBAAkB,GAClB,2BAA2B,GAC3B,wBAAwB,GACxB,4BAA4B,GAC5B,oBAAoB,EACxB,OAAO,EAAE,MAAM;CAKlB;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,SAAS,CAiFlE"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * `ScopeTree` — the lexical-scope spine of the `SemanticModel`
3
+ * (RFC §2.2 + §3.1; Ring 2 SHARED #912).
4
+ *
5
+ * Generalizes the `enclosingFunctions` pattern from closed PR #902 to
6
+ * arbitrary `ScopeKind`s. Owns the (parent ↔ children) relationship
7
+ * derived from each `Scope.parent` pointer, and validates the structural
8
+ * invariants a well-formed scope tree must satisfy.
9
+ *
10
+ * Invariants enforced at build time (throw on violation):
11
+ *
12
+ * - Every non-`Module` scope has a non-null parent.
13
+ * - Every parent pointer references a scope that was also supplied to
14
+ * `buildScopeTree`.
15
+ * - Parent range **strictly contains** child range.
16
+ * - Sibling ranges under the same parent do not overlap.
17
+ * - Parent and child live in the same `filePath`. (Cross-file parent
18
+ * pointers would be a category error — a `File` scope is not the
19
+ * parent of another file's scopes; imports do that job.)
20
+ *
21
+ * Satisfies the `ScopeLookup` contract from #916 (`resolve-type-ref`), so
22
+ * `resolveTypeRef` can take a `ScopeTree` directly without adapters.
23
+ *
24
+ * Immutable surface: `byId` is a `ReadonlyMap`; children arrays are
25
+ * `Object.freeze`d; miss lookups return a shared frozen empty array.
26
+ */
27
+ // ─── Build errors ───────────────────────────────────────────────────────────
28
+ /**
29
+ * Thrown by `buildScopeTree` when the input violates a structural
30
+ * invariant. Carries the offending ids + the invariant name so failed
31
+ * extraction pipelines can report actionable diagnostics.
32
+ */
33
+ export class ScopeTreeInvariantError extends Error {
34
+ invariant;
35
+ constructor(invariant, message) {
36
+ super(message);
37
+ this.invariant = invariant;
38
+ this.name = 'ScopeTreeInvariantError';
39
+ }
40
+ }
41
+ // ─── Builder ───────────────────────────────────────────────────────────────
42
+ /**
43
+ * Build an immutable `ScopeTree` from a flat list of `Scope` records.
44
+ *
45
+ * Throws `ScopeTreeInvariantError` on the first invariant violation; a
46
+ * malformed tree is a bug in the extraction pipeline, not a data case for
47
+ * consumers to handle, so fail-fast is the correct posture.
48
+ */
49
+ export function buildScopeTree(scopes) {
50
+ const byId = new Map();
51
+ const childrenById = new Map();
52
+ // ── Pass 1: collect by id + duplicate check ───────────────────────────
53
+ for (const scope of scopes) {
54
+ if (byId.has(scope.id)) {
55
+ throw new ScopeTreeInvariantError('duplicate-scope-id', `Two scopes share id '${scope.id}'. Scope ids must be unique per tree.`);
56
+ }
57
+ byId.set(scope.id, scope);
58
+ }
59
+ // ── Pass 2: validate parent pointers + build children buckets ─────────
60
+ for (const scope of scopes) {
61
+ if (scope.parent === null) {
62
+ if (scope.kind !== 'Module') {
63
+ throw new ScopeTreeInvariantError('non-module-requires-parent', `Scope '${scope.id}' has kind '${scope.kind}' but no parent. Only 'Module' scopes may be root-level.`);
64
+ }
65
+ continue;
66
+ }
67
+ const parent = byId.get(scope.parent);
68
+ if (parent === undefined) {
69
+ throw new ScopeTreeInvariantError('parent-not-found', `Scope '${scope.id}' references parent '${scope.parent}' which is not part of this tree.`);
70
+ }
71
+ if (parent.filePath !== scope.filePath) {
72
+ throw new ScopeTreeInvariantError('parent-must-share-filepath', `Scope '${scope.id}' (${scope.filePath}) has parent '${parent.id}' in a different file (${parent.filePath}). Parent/child scopes must share filePath.`);
73
+ }
74
+ if (!rangeStrictlyContains(parent.range, scope.range)) {
75
+ throw new ScopeTreeInvariantError('parent-must-contain-child', `Parent scope '${parent.id}' at ${formatRange(parent.range)} does not strictly contain child '${scope.id}' at ${formatRange(scope.range)}.`);
76
+ }
77
+ let bucket = childrenById.get(parent.id);
78
+ if (bucket === undefined) {
79
+ bucket = [];
80
+ childrenById.set(parent.id, bucket);
81
+ }
82
+ bucket.push(scope.id);
83
+ }
84
+ // ── Pass 3: sibling-overlap check ─────────────────────────────────────
85
+ for (const [parentId, childIds] of childrenById) {
86
+ if (childIds.length < 2)
87
+ continue;
88
+ // Sort siblings by (startLine, startCol) for an O(n log n) pairwise
89
+ // scan instead of O(n²) all-pairs.
90
+ const children = childIds.map((id) => byId.get(id)).slice();
91
+ children.sort((a, b) => comparePosition(a.range, b.range));
92
+ for (let i = 1; i < children.length; i++) {
93
+ const prev = children[i - 1];
94
+ const curr = children[i];
95
+ if (rangesOverlap(prev.range, curr.range)) {
96
+ throw new ScopeTreeInvariantError('sibling-ranges-overlap', `Sibling scopes under parent '${parentId}' overlap: '${prev.id}' ${formatRange(prev.range)} and '${curr.id}' ${formatRange(curr.range)}.`);
97
+ }
98
+ }
99
+ }
100
+ // Freeze children arrays so the surface is truly read-only.
101
+ const frozenChildren = new Map();
102
+ for (const [parentId, childIds] of childrenById) {
103
+ frozenChildren.set(parentId, Object.freeze(childIds.slice()));
104
+ }
105
+ return freezeTree(byId, frozenChildren);
106
+ }
107
+ // ─── Internals ──────────────────────────────────────────────────────────────
108
+ const EMPTY_CHILDREN = Object.freeze([]);
109
+ function freezeTree(byId, childrenById) {
110
+ return {
111
+ byId,
112
+ get size() {
113
+ return byId.size;
114
+ },
115
+ getScope(id) {
116
+ return byId.get(id);
117
+ },
118
+ getParent(id) {
119
+ const scope = byId.get(id);
120
+ if (scope === undefined || scope.parent === null)
121
+ return undefined;
122
+ return byId.get(scope.parent);
123
+ },
124
+ getChildren(id) {
125
+ return childrenById.get(id) ?? EMPTY_CHILDREN;
126
+ },
127
+ getAncestors(id) {
128
+ const start = byId.get(id);
129
+ if (start === undefined || start.parent === null)
130
+ return EMPTY_CHILDREN;
131
+ const out = [];
132
+ const visited = new Set([id]);
133
+ let cursor = start.parent;
134
+ while (cursor !== null && !visited.has(cursor)) {
135
+ visited.add(cursor);
136
+ out.push(cursor);
137
+ const next = byId.get(cursor);
138
+ cursor = next === undefined ? null : next.parent;
139
+ }
140
+ return Object.freeze(out);
141
+ },
142
+ has(id) {
143
+ return byId.has(id);
144
+ },
145
+ };
146
+ }
147
+ /**
148
+ * `outer` strictly contains `inner` when `outer`'s start is at or before
149
+ * `inner`'s start, `outer`'s end is at or after `inner`'s end, and they are
150
+ * not the exact same range. Equal ranges are rejected — a child cannot
151
+ * occupy the exact same span as its parent.
152
+ */
153
+ function rangeStrictlyContains(outer, inner) {
154
+ if (outer.startLine === inner.startLine &&
155
+ outer.startCol === inner.startCol &&
156
+ outer.endLine === inner.endLine &&
157
+ outer.endCol === inner.endCol) {
158
+ return false;
159
+ }
160
+ const outerStartsAtOrBefore = outer.startLine < inner.startLine ||
161
+ (outer.startLine === inner.startLine && outer.startCol <= inner.startCol);
162
+ const outerEndsAtOrAfter = outer.endLine > inner.endLine ||
163
+ (outer.endLine === inner.endLine && outer.endCol >= inner.endCol);
164
+ return outerStartsAtOrBefore && outerEndsAtOrAfter;
165
+ }
166
+ /**
167
+ * Two ranges overlap when neither finishes before the other begins. Ranges
168
+ * that merely touch at a single boundary point (`a.end === b.start`) do
169
+ * NOT overlap — this matches tree-sitter's half-open-like range semantics
170
+ * and the typical "sibling blocks meet but don't overlap" pattern.
171
+ */
172
+ function rangesOverlap(a, b) {
173
+ const aEndsBeforeB = a.endLine < b.startLine || (a.endLine === b.startLine && a.endCol <= b.startCol);
174
+ const bEndsBeforeA = b.endLine < a.startLine || (b.endLine === a.startLine && b.endCol <= a.startCol);
175
+ return !(aEndsBeforeB || bEndsBeforeA);
176
+ }
177
+ function comparePosition(a, b) {
178
+ if (a.startLine !== b.startLine)
179
+ return a.startLine - b.startLine;
180
+ return a.startCol - b.startCol;
181
+ }
182
+ function formatRange(r) {
183
+ return `${r.startLine}:${r.startCol}-${r.endLine}:${r.endCol}`;
184
+ }
185
+ //# sourceMappingURL=scope-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-tree.js","sourceRoot":"","sources":["../../src/scope-resolution/scope-tree.ts"],"names":[],"mappings":"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"}
@@ -167,12 +167,6 @@ export interface ParsedTypeBinding {
167
167
  * concretely typed in Ring 2 SHARED (#915).
168
168
  */
169
169
  export type WorkspaceIndex = unknown;
170
- /**
171
- * Scope tree handle consumed by parse-phase hooks (`bindingScopeFor`,
172
- * `importOwningScope`) to navigate the in-progress scope tree. Opaque
173
- * placeholder in Ring 1; concretely typed in Ring 2 SHARED (#912).
174
- */
175
- export type ScopeTree = unknown;
176
170
  /** Call-site description passed to `arityCompatibility`. */
177
171
  export interface Callsite {
178
172
  /** Number of arguments at the call site. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Per-phase wall-clock timing for the search pipeline and similar
3
+ * multi-stage flows. Designed to be called from query() with minimal
4
+ * ceremony and negligible overhead (< 0.1 ms per phase recorded).
5
+ *
6
+ * ### Sequential usage
7
+ *
8
+ * ```ts
9
+ * const t = new PhaseTimer();
10
+ * t.start('bm25'); await bm25Search(...); t.stop();
11
+ * t.start('merge'); doMerge(); t.stop();
12
+ * const phases = t.summary(); // { bm25: 42, merge: 3 }
13
+ * ```
14
+ *
15
+ * ### Concurrent usage (Promise.all)
16
+ *
17
+ * `start`/`stop` assume a single active phase at a time, which is wrong
18
+ * for concurrent work inside `Promise.all` — the second `start` would
19
+ * auto-stop the first and only one of the two would get timed. Use
20
+ * {@link PhaseTimer.time} to wrap each concurrent promise instead:
21
+ *
22
+ * ```ts
23
+ * const [a, b] = await Promise.all([
24
+ * t.time('bm25', bm25Search(...)),
25
+ * t.time('vector', semanticSearch(...)),
26
+ * ]);
27
+ * ```
28
+ *
29
+ * ### Pre-measured durations
30
+ *
31
+ * ```ts
32
+ * t.mark('inherited', 12.5);
33
+ * ```
34
+ */
35
+ export declare class PhaseTimer {
36
+ private phases;
37
+ private current;
38
+ private t0;
39
+ /** Start a new phase. Implicitly stops the previous one, if any. */
40
+ start(phase: string): void;
41
+ /** Stop the current phase. No-op if no phase is active. */
42
+ stop(): void;
43
+ /**
44
+ * Record a pre-measured duration without touching the active phase.
45
+ * Use for concurrent operations inside `Promise.all` where
46
+ * `start`/`stop` would step on each other, or for durations imported
47
+ * from sub-systems. Additive across repeated calls with the same
48
+ * phase name. Ignores negative / non-finite inputs.
49
+ */
50
+ mark(phase: string, durationMs: number): void;
51
+ /**
52
+ * Wrap a promise with automatic timing. Records wall time via
53
+ * {@link PhaseTimer.mark} regardless of which other phases are
54
+ * active — safe to use inside `Promise.all`.
55
+ */
56
+ time<T>(phase: string, promise: Promise<T>): Promise<T>;
57
+ /**
58
+ * Snapshot of accumulated durations rounded to 0.1 ms. Stops the
59
+ * current phase if one is still running.
60
+ */
61
+ summary(): Record<string, number>;
62
+ /**
63
+ * Sum of every recorded phase duration.
64
+ *
65
+ * Note: for phases recorded via {@link PhaseTimer.time} or
66
+ * {@link PhaseTimer.mark} this is the *sum*, not the wall time —
67
+ * concurrent work overlaps and the sum can exceed the end-to-end
68
+ * wall time. Record wall time separately with `mark('wall', …)` if
69
+ * that distinction matters.
70
+ */
71
+ totalMs(): number;
72
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Per-phase wall-clock timing for the search pipeline and similar
3
+ * multi-stage flows. Designed to be called from query() with minimal
4
+ * ceremony and negligible overhead (< 0.1 ms per phase recorded).
5
+ *
6
+ * ### Sequential usage
7
+ *
8
+ * ```ts
9
+ * const t = new PhaseTimer();
10
+ * t.start('bm25'); await bm25Search(...); t.stop();
11
+ * t.start('merge'); doMerge(); t.stop();
12
+ * const phases = t.summary(); // { bm25: 42, merge: 3 }
13
+ * ```
14
+ *
15
+ * ### Concurrent usage (Promise.all)
16
+ *
17
+ * `start`/`stop` assume a single active phase at a time, which is wrong
18
+ * for concurrent work inside `Promise.all` — the second `start` would
19
+ * auto-stop the first and only one of the two would get timed. Use
20
+ * {@link PhaseTimer.time} to wrap each concurrent promise instead:
21
+ *
22
+ * ```ts
23
+ * const [a, b] = await Promise.all([
24
+ * t.time('bm25', bm25Search(...)),
25
+ * t.time('vector', semanticSearch(...)),
26
+ * ]);
27
+ * ```
28
+ *
29
+ * ### Pre-measured durations
30
+ *
31
+ * ```ts
32
+ * t.mark('inherited', 12.5);
33
+ * ```
34
+ */
35
+ export class PhaseTimer {
36
+ phases = new Map();
37
+ current = null;
38
+ t0 = 0;
39
+ /** Start a new phase. Implicitly stops the previous one, if any. */
40
+ start(phase) {
41
+ this.stop();
42
+ this.current = phase;
43
+ this.t0 = performance.now();
44
+ }
45
+ /** Stop the current phase. No-op if no phase is active. */
46
+ stop() {
47
+ if (this.current !== null) {
48
+ const elapsed = performance.now() - this.t0;
49
+ this.phases.set(this.current, (this.phases.get(this.current) ?? 0) + elapsed);
50
+ this.current = null;
51
+ }
52
+ }
53
+ /**
54
+ * Record a pre-measured duration without touching the active phase.
55
+ * Use for concurrent operations inside `Promise.all` where
56
+ * `start`/`stop` would step on each other, or for durations imported
57
+ * from sub-systems. Additive across repeated calls with the same
58
+ * phase name. Ignores negative / non-finite inputs.
59
+ */
60
+ mark(phase, durationMs) {
61
+ if (!Number.isFinite(durationMs) || durationMs < 0)
62
+ return;
63
+ this.phases.set(phase, (this.phases.get(phase) ?? 0) + durationMs);
64
+ }
65
+ /**
66
+ * Wrap a promise with automatic timing. Records wall time via
67
+ * {@link PhaseTimer.mark} regardless of which other phases are
68
+ * active — safe to use inside `Promise.all`.
69
+ */
70
+ async time(phase, promise) {
71
+ const t0 = performance.now();
72
+ try {
73
+ return await promise;
74
+ }
75
+ finally {
76
+ this.mark(phase, performance.now() - t0);
77
+ }
78
+ }
79
+ /**
80
+ * Snapshot of accumulated durations rounded to 0.1 ms. Stops the
81
+ * current phase if one is still running.
82
+ */
83
+ summary() {
84
+ this.stop();
85
+ const out = {};
86
+ for (const [k, v] of this.phases)
87
+ out[k] = Math.round(v * 10) / 10;
88
+ return out;
89
+ }
90
+ /**
91
+ * Sum of every recorded phase duration.
92
+ *
93
+ * Note: for phases recorded via {@link PhaseTimer.time} or
94
+ * {@link PhaseTimer.mark} this is the *sum*, not the wall time —
95
+ * concurrent work overlaps and the sum can exceed the end-to-end
96
+ * wall time. Record wall time separately with `mark('wall', …)` if
97
+ * that distinction matters.
98
+ */
99
+ totalMs() {
100
+ this.stop();
101
+ let t = 0;
102
+ for (const v of this.phases.values())
103
+ t += v;
104
+ return Math.round(t * 10) / 10;
105
+ }
106
+ }
@@ -18,6 +18,7 @@ import { listRegisteredRepos, cleanupOldKuzuFiles, } from '../../storage/repo-ma
18
18
  import { GroupService } from '../../core/group/service.js';
19
19
  import { collectBestChunks } from '../../core/embeddings/types.js';
20
20
  import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME } from '../../core/lbug/schema.js';
21
+ import { PhaseTimer } from '../../core/search/phase-timer.js';
21
22
  // AI context generation is CLI-only (gitnexus analyze)
22
23
  // import { generateAIContextFiles } from '../../cli/ai-context.js';
23
24
  /**
@@ -134,6 +135,25 @@ function logQueryError(context, err) {
134
135
  const msg = err instanceof Error ? err.message : String(err);
135
136
  console.error(`GitNexus [${context}]: ${msg}`);
136
137
  }
138
+ /**
139
+ * Structured per-query latency log for production aggregation (#553).
140
+ *
141
+ * Emitted on stderr — NOT stdout — because the MCP stdio transport uses
142
+ * stdout exclusively for JSON-RPC responses (#324), and the CLI e2e test
143
+ * `tool output goes to stdout via fd 1` asserts that stdout parses cleanly
144
+ * as JSON. Any `console.log` from inside a tool handler would corrupt the
145
+ * protocol. Matches the existing `logQueryError` convention above, which
146
+ * uses stderr for the same reason.
147
+ *
148
+ * The `GitNexus [query:timing] …` prefix keeps lines greppable; the
149
+ * `phases` payload is JSON so log-scraping pipelines can parse it
150
+ * without custom format knowledge.
151
+ */
152
+ function logQueryTiming(query, phases) {
153
+ const totalMs = phases.wall ?? Object.values(phases).reduce((a, b) => a + b, 0);
154
+ const truncated = query.length > 80 ? `${query.slice(0, 80)}…` : query;
155
+ console.error(`GitNexus [query:timing] query=${JSON.stringify(truncated)} totalMs=${totalMs} phases=${JSON.stringify(phases)}`);
156
+ }
137
157
  export class LocalBackend {
138
158
  repos = new Map();
139
159
  contextCache = new Map();
@@ -449,15 +469,26 @@ export class LocalBackend {
449
469
  const maxSymbolsPerProcess = params.max_symbols || 10;
450
470
  const includeContent = params.include_content ?? false;
451
471
  const searchQuery = params.query.trim();
452
- // Step 1: Run hybrid search to get matching symbols
472
+ // Per-phase timing instrumentation (#553). Records wall time for each
473
+ // observable sub-step of the search pipeline so production latency can
474
+ // be aggregated offline for Pareto analysis and bottleneck detection.
475
+ // Overhead is <0.1 ms per phase; the timer is passive and never alters
476
+ // query behaviour.
477
+ const timer = new PhaseTimer();
478
+ const wallStart = performance.now();
479
+ // Step 1: Run hybrid search to get matching symbols. BM25 and vector
480
+ // search run concurrently via Promise.all — use `timer.time()` for
481
+ // each so both get independent wall-time records without fighting
482
+ // over a single `current` phase slot.
453
483
  const searchLimit = processLimit * maxSymbolsPerProcess; // fetch enough raw results
454
484
  const [bm25SearchResult, semanticResults] = await Promise.all([
455
- this.bm25Search(repo, searchQuery, searchLimit),
456
- this.semanticSearch(repo, searchQuery, searchLimit),
485
+ timer.time('bm25', this.bm25Search(repo, searchQuery, searchLimit)),
486
+ timer.time('vector', this.semanticSearch(repo, searchQuery, searchLimit)),
457
487
  ]);
458
488
  const bm25Results = bm25SearchResult.results;
459
489
  const ftsUsed = bm25SearchResult.ftsUsed;
460
490
  // Merge via reciprocal rank fusion
491
+ timer.start('merge');
461
492
  const scoreMap = new Map();
462
493
  for (let i = 0; i < bm25Results.length; i++) {
463
494
  const result = bm25Results[i];
@@ -486,7 +517,9 @@ export class LocalBackend {
486
517
  const merged = Array.from(scoreMap.entries())
487
518
  .sort((a, b) => b[1].score - a[1].score)
488
519
  .slice(0, searchLimit);
520
+ timer.stop(); // merge
489
521
  // Step 2: For each match with a nodeId, trace to process(es)
522
+ timer.start('symbol_lookup');
490
523
  const processMap = new Map();
491
524
  const definitions = []; // standalone symbols not in any process
492
525
  for (const [_, item] of merged) {
@@ -590,7 +623,9 @@ export class LocalBackend {
590
623
  }
591
624
  }
592
625
  }
626
+ timer.stop(); // symbol_lookup
593
627
  // Step 3: Rank processes by aggregate score + internal cohesion boost
628
+ timer.start('ranking');
594
629
  const rankedProcesses = Array.from(processMap.values())
595
630
  .map((p) => ({
596
631
  ...p,
@@ -598,7 +633,9 @@ export class LocalBackend {
598
633
  }))
599
634
  .sort((a, b) => b.priority - a.priority)
600
635
  .slice(0, processLimit);
636
+ timer.stop(); // ranking
601
637
  // Step 4: Build response
638
+ timer.start('formatting');
602
639
  const processes = rankedProcesses.map((p) => ({
603
640
  id: p.id,
604
641
  summary: p.heuristicLabel || p.label,
@@ -619,10 +656,18 @@ export class LocalBackend {
619
656
  seen.add(s.id);
620
657
  return true;
621
658
  });
659
+ timer.stop(); // formatting
660
+ // End-to-end wall time — deliberately a separate mark so callers can
661
+ // compare sum(phases) vs wall to see how much Promise.all concurrency
662
+ // saved. Must come before summary() so it's included.
663
+ timer.mark('wall', performance.now() - wallStart);
664
+ const timing = timer.summary();
665
+ logQueryTiming(searchQuery, timing);
622
666
  return {
623
667
  processes,
624
668
  process_symbols: dedupedSymbols,
625
669
  definitions: definitions.slice(0, 20), // cap standalone definitions
670
+ timing,
626
671
  ...(!ftsUsed && {
627
672
  warning: 'FTS extension unavailable - keyword search degraded. Run: gitnexus analyze --force to rebuild indexes.',
628
673
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.3-rc.6",
3
+ "version": "1.6.3-rc.7",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",