gitnexus 1.6.6-rc.2 → 1.6.6-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.
Files changed (112) hide show
  1. package/dist/_shared/index.d.ts +1 -1
  2. package/dist/_shared/index.d.ts.map +1 -1
  3. package/dist/_shared/index.js.map +1 -1
  4. package/dist/_shared/scope-resolution/reference-site.d.ts +8 -0
  5. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
  6. package/dist/_shared/scope-resolution/registries/context.d.ts +18 -1
  7. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
  8. package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
  9. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +4 -2
  10. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -1
  11. package/dist/_shared/scope-resolution/registries/lookup-core.js +8 -23
  12. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
  13. package/dist/cli/analyze.js +65 -3
  14. package/dist/cli/eval-server.d.ts +14 -3
  15. package/dist/cli/eval-server.js +79 -9
  16. package/dist/cli/index.js +3 -1
  17. package/dist/cli/serve.js +6 -1
  18. package/dist/cli/setup.js +6 -4
  19. package/dist/cli/wiki.d.ts +1 -0
  20. package/dist/cli/wiki.js +32 -8
  21. package/dist/core/ingestion/languages/cpp/captures.js +165 -9
  22. package/dist/core/ingestion/languages/cpp/constraint-filter.js +102 -32
  23. package/dist/core/ingestion/languages/cpp/type-classifier.d.ts +9 -9
  24. package/dist/core/ingestion/languages/cpp/type-classifier.js +12 -8
  25. package/dist/core/ingestion/languages/java.js +35 -0
  26. package/dist/core/ingestion/languages/javascript/arity.d.ts +11 -0
  27. package/dist/core/ingestion/languages/javascript/arity.js +11 -0
  28. package/dist/core/ingestion/languages/javascript/captures.d.ts +30 -0
  29. package/dist/core/ingestion/languages/javascript/captures.js +655 -0
  30. package/dist/core/ingestion/languages/javascript/import-target.d.ts +29 -0
  31. package/dist/core/ingestion/languages/javascript/import-target.js +53 -0
  32. package/dist/core/ingestion/languages/javascript/index.d.ts +48 -0
  33. package/dist/core/ingestion/languages/javascript/index.js +48 -0
  34. package/dist/core/ingestion/languages/javascript/interpret.d.ts +29 -0
  35. package/dist/core/ingestion/languages/javascript/interpret.js +41 -0
  36. package/dist/core/ingestion/languages/javascript/merge-bindings.d.ts +16 -0
  37. package/dist/core/ingestion/languages/javascript/merge-bindings.js +18 -0
  38. package/dist/core/ingestion/languages/javascript/query.d.ts +52 -0
  39. package/dist/core/ingestion/languages/javascript/query.js +413 -0
  40. package/dist/core/ingestion/languages/javascript/scope-resolver.d.ts +37 -0
  41. package/dist/core/ingestion/languages/javascript/scope-resolver.js +73 -0
  42. package/dist/core/ingestion/languages/javascript/simple-hooks.d.ts +32 -0
  43. package/dist/core/ingestion/languages/javascript/simple-hooks.js +37 -0
  44. package/dist/core/ingestion/languages/typescript/simple-hooks.d.ts +10 -0
  45. package/dist/core/ingestion/languages/typescript/simple-hooks.js +4 -1
  46. package/dist/core/ingestion/languages/typescript.js +15 -0
  47. package/dist/core/ingestion/model/field-registry.d.ts +17 -3
  48. package/dist/core/ingestion/model/field-registry.js +19 -4
  49. package/dist/core/ingestion/model/owned-members-lookup.d.ts +19 -0
  50. package/dist/core/ingestion/model/owned-members-lookup.js +43 -0
  51. package/dist/core/ingestion/model/type-registry.d.ts +9 -0
  52. package/dist/core/ingestion/model/type-registry.js +18 -0
  53. package/dist/core/ingestion/registry-primary-flag.js +1 -0
  54. package/dist/core/ingestion/resolve-references.d.ts +3 -1
  55. package/dist/core/ingestion/resolve-references.js +5 -1
  56. package/dist/core/ingestion/scope-extractor.js +4 -0
  57. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +1 -0
  58. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +37 -7
  59. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +2 -0
  60. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +8 -1
  61. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +2 -0
  62. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +4 -2
  63. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +48 -9
  64. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  65. package/dist/core/ingestion/scope-resolution/pipeline/run.js +2 -0
  66. package/dist/core/lbug/lbug-adapter.d.ts +3 -1
  67. package/dist/core/lbug/lbug-adapter.js +33 -19
  68. package/dist/core/lbug/lbug-config.d.ts +1 -1
  69. package/dist/core/lbug/lbug-config.js +1 -1
  70. package/dist/core/lbug/pool-adapter.d.ts +19 -7
  71. package/dist/core/lbug/pool-adapter.js +70 -44
  72. package/dist/core/lbug/query-params.d.ts +1 -0
  73. package/dist/core/lbug/query-params.js +21 -0
  74. package/dist/core/search/bm25-index.js +4 -6
  75. package/dist/core/wiki/generator.d.ts +13 -0
  76. package/dist/core/wiki/generator.js +45 -4
  77. package/dist/core/wiki/llm-client.d.ts +1 -1
  78. package/dist/core/wiki/llm-client.js +23 -6
  79. package/dist/mcp/local/local-backend.d.ts +18 -3
  80. package/dist/mcp/local/local-backend.js +127 -11
  81. package/dist/mcp/tools.js +10 -0
  82. package/dist/server/api.d.ts +3 -0
  83. package/dist/server/api.js +58 -30
  84. package/package.json +3 -3
  85. package/web/assets/agent-SmDhIVj9.js +608 -0
  86. package/web/assets/{architectureDiagram-UL44E2DR-DvIkEGkp.js → architectureDiagram-UL44E2DR-ChS6kfgC.js} +1 -1
  87. package/web/assets/{chunk-LCXTWHL2-CqJ0oo1x.js → chunk-LCXTWHL2-CikfiTEP.js} +1 -1
  88. package/web/assets/{chunk-RG4AUYOV-D0M6vdq9.js → chunk-RG4AUYOV-D0pH0sP0.js} +1 -1
  89. package/web/assets/{classDiagram-KGZ6W3CR-BB-saqC1.js → classDiagram-KGZ6W3CR-VJjLBrQ3.js} +1 -1
  90. package/web/assets/{classDiagram-v2-72OJOZXJ-Bp2bAlS5.js → classDiagram-v2-72OJOZXJ-CL_v399n.js} +1 -1
  91. package/web/assets/{diagram-3NCE3AQN-BUxnDPOY.js → diagram-3NCE3AQN-DTTuOnDj.js} +1 -1
  92. package/web/assets/{diagram-GF46GFSD-BJTyqqvI.js → diagram-GF46GFSD-Di3e_png.js} +1 -1
  93. package/web/assets/{diagram-QXG6HAR7-DilMHamP.js → diagram-QXG6HAR7-CVQY1hhk.js} +1 -1
  94. package/web/assets/{diagram-WEQXMOUZ-8KUrarg1.js → diagram-WEQXMOUZ-BoxS7CN3.js} +1 -1
  95. package/web/assets/{erDiagram-L5TCEMPS-Doj9mSiW.js → erDiagram-L5TCEMPS-CLTuHusW.js} +1 -1
  96. package/web/assets/{flowDiagram-H6V6AXG4-BOSR-i3p.js → flowDiagram-H6V6AXG4-CQfVb2C4.js} +1 -1
  97. package/web/assets/{index-j4-NuleT.js → index-BuC2I_Cj.js} +5 -5
  98. package/web/assets/{infoDiagram-3YFTVSEB-Ce_TvtaV.js → infoDiagram-3YFTVSEB-BOk6LlXV.js} +1 -1
  99. package/web/assets/{ishikawaDiagram-BNXS4ZKH-4MXIDCSF.js → ishikawaDiagram-BNXS4ZKH-C0sASGB6.js} +1 -1
  100. package/web/assets/{kanban-definition-75IXJCU3-yYdfm8Kk.js → kanban-definition-75IXJCU3-DT9Lizpz.js} +1 -1
  101. package/web/assets/{mindmap-definition-2TDM6QVE-DFNEKM77.js → mindmap-definition-2TDM6QVE-BEYaB7hb.js} +1 -1
  102. package/web/assets/{pieDiagram-CU6KROY3-iZAqpvhd.js → pieDiagram-CU6KROY3-DCgHNkyY.js} +1 -1
  103. package/web/assets/{requirementDiagram-JXO7QTGE-CtDijhnf.js → requirementDiagram-JXO7QTGE-D6vDU9M2.js} +1 -1
  104. package/web/assets/{sequenceDiagram-VS2MUI6T-BdjvyCHM.js → sequenceDiagram-VS2MUI6T-CJrGCTXy.js} +1 -1
  105. package/web/assets/{stateDiagram-7D4R322I-e5odN9fG.js → stateDiagram-7D4R322I-BvxQnQyL.js} +1 -1
  106. package/web/assets/{stateDiagram-v2-36443NZ5-vPsvjR2D.js → stateDiagram-v2-36443NZ5-D8ziO9W2.js} +1 -1
  107. package/web/assets/{timeline-definition-O6YCAMPW-DPAksNQp.js → timeline-definition-O6YCAMPW-B45j2q3m.js} +1 -1
  108. package/web/assets/{vennDiagram-MWXL3ELB-Dtlfdt7J.js → vennDiagram-MWXL3ELB-CRkZRDhi.js} +1 -1
  109. package/web/assets/{wardleyDiagram-CUQ6CDDI-B23uRMUC.js → wardleyDiagram-CUQ6CDDI-BekUytZJ.js} +1 -1
  110. package/web/assets/{xychartDiagram-N2JHSOCM-DxwHeksW.js → xychartDiagram-N2JHSOCM-BeMqwSgr.js} +1 -1
  111. package/web/index.html +1 -1
  112. package/web/assets/agent-D2kLt6Dl.js +0 -597
@@ -2,15 +2,29 @@
2
2
  * Field Registry
3
3
  *
4
4
  * Owner-scoped field/property index extracted from SymbolTable.
5
- * Stores Property symbols keyed by `ownerNodeId\0fieldName` for O(1) lookup.
5
+ * Stores Property / Variable / Const / Static symbols keyed by
6
+ * `ownerNodeId\0fieldName` for O(1) lookup. Supports multiple defs
7
+ * under the same (owner, name) — e.g. legacy Property plus a
8
+ * scope-resolution Variable reconciliation entry.
6
9
  */
7
10
  import type { SymbolDefinition } from '../../../_shared/index.js';
8
11
  export interface FieldRegistry {
9
- /** Look up a field/property by its owning class nodeId and field name. */
12
+ /**
13
+ * First field registered under `(ownerNodeId, fieldName)`, if any.
14
+ * Registration order is first-wins: when a Property and a Variable share
15
+ * an `(owner, simpleName)` key, the earlier `register(...)` call's def is
16
+ * returned. Prefer `lookupAllByOwner` when overloads or duplicate-kind
17
+ * entries under the same name must all be visible.
18
+ */
10
19
  lookupFieldByOwner(ownerNodeId: string, fieldName: string): SymbolDefinition | undefined;
20
+ /**
21
+ * Every field registered under `(ownerNodeId, fieldName)` in registration
22
+ * order. Returns `[]` on miss.
23
+ */
24
+ lookupAllByOwner(ownerNodeId: string, fieldName: string): readonly SymbolDefinition[];
11
25
  }
12
26
  export interface MutableFieldRegistry extends FieldRegistry {
13
- /** Register a field/property under its owner. */
27
+ /** Register a field under its owner. Appends when the key already exists. */
14
28
  register(ownerNodeId: string, fieldName: string, def: SymbolDefinition): void;
15
29
  /** Clear all entries. */
16
30
  clear(): void;
@@ -2,21 +2,36 @@
2
2
  * Field Registry
3
3
  *
4
4
  * Owner-scoped field/property index extracted from SymbolTable.
5
- * Stores Property symbols keyed by `ownerNodeId\0fieldName` for O(1) lookup.
5
+ * Stores Property / Variable / Const / Static symbols keyed by
6
+ * `ownerNodeId\0fieldName` for O(1) lookup. Supports multiple defs
7
+ * under the same (owner, name) — e.g. legacy Property plus a
8
+ * scope-resolution Variable reconciliation entry.
6
9
  */
10
+ const EMPTY = Object.freeze([]);
7
11
  // ---------------------------------------------------------------------------
8
12
  // Factory
9
13
  // ---------------------------------------------------------------------------
10
14
  export const createFieldRegistry = () => {
11
15
  const fieldByOwner = new Map();
16
+ const lookupAllByOwner = (ownerNodeId, fieldName) => {
17
+ return fieldByOwner.get(`${ownerNodeId}\0${fieldName}`) ?? EMPTY;
18
+ };
12
19
  const lookupFieldByOwner = (ownerNodeId, fieldName) => {
13
- return fieldByOwner.get(`${ownerNodeId}\0${fieldName}`);
20
+ const pool = lookupAllByOwner(ownerNodeId, fieldName);
21
+ return pool.length === 0 ? undefined : pool[0];
14
22
  };
15
23
  const register = (ownerNodeId, fieldName, def) => {
16
- fieldByOwner.set(`${ownerNodeId}\0${fieldName}`, def);
24
+ const key = `${ownerNodeId}\0${fieldName}`;
25
+ const existing = fieldByOwner.get(key);
26
+ if (existing) {
27
+ existing.push(def);
28
+ }
29
+ else {
30
+ fieldByOwner.set(key, [def]);
31
+ }
17
32
  };
18
33
  const clear = () => {
19
34
  fieldByOwner.clear();
20
35
  };
21
- return { lookupFieldByOwner, register, clear };
36
+ return { lookupFieldByOwner, lookupAllByOwner, register, clear };
22
37
  };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Owner-keyed member lookup for Step 2 (RFC #909 / PR #1656).
3
+ *
4
+ * Merges MethodRegistry + FieldRegistry hits for `(ownerDefId, memberName)`
5
+ * in O(1) map time per registry — no `defs.byId` scan. Callers that omit
6
+ * this helper and leave `ownedMembersByOwner` unset fall back to an O(|defs|)
7
+ * compatibility scan inside `lookupCore.collectOwnedMembers`.
8
+ */
9
+ import type { DefId, SymbolDefinition } from '../../../_shared/index.js';
10
+ import type { SemanticModel } from './semantic-model.js';
11
+ /**
12
+ * Production hook for `RegistryContext.ownedMembersByOwner`.
13
+ * Returns `[]` on miss (authoritative indexed empty) — never `undefined`.
14
+ *
15
+ * Merges hits from all three owner-keyed registries (methods, fields,
16
+ * nested types) under the same `(ownerDefId, memberName)` key. The
17
+ * caller's `acceptedKinds` filter in `lookupCore` picks the right subset.
18
+ */
19
+ export declare function lookupOwnedMembersByOwner(model: Pick<SemanticModel, 'methods' | 'fields' | 'types'>, ownerDefId: DefId, memberName: string): readonly SymbolDefinition[];
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Owner-keyed member lookup for Step 2 (RFC #909 / PR #1656).
3
+ *
4
+ * Merges MethodRegistry + FieldRegistry hits for `(ownerDefId, memberName)`
5
+ * in O(1) map time per registry — no `defs.byId` scan. Callers that omit
6
+ * this helper and leave `ownedMembersByOwner` unset fall back to an O(|defs|)
7
+ * compatibility scan inside `lookupCore.collectOwnedMembers`.
8
+ */
9
+ const EMPTY = Object.freeze([]);
10
+ /**
11
+ * Production hook for `RegistryContext.ownedMembersByOwner`.
12
+ * Returns `[]` on miss (authoritative indexed empty) — never `undefined`.
13
+ *
14
+ * Merges hits from all three owner-keyed registries (methods, fields,
15
+ * nested types) under the same `(ownerDefId, memberName)` key. The
16
+ * caller's `acceptedKinds` filter in `lookupCore` picks the right subset.
17
+ */
18
+ export function lookupOwnedMembersByOwner(model, ownerDefId, memberName) {
19
+ const methods = model.methods.lookupAllByOwner(ownerDefId, memberName);
20
+ const fields = model.fields.lookupAllByOwner(ownerDefId, memberName);
21
+ const nestedTypes = model.types.lookupAllByOwner(ownerDefId, memberName);
22
+ const methodCount = methods.length;
23
+ const fieldCount = fields.length;
24
+ const typeCount = nestedTypes.length;
25
+ const total = methodCount + fieldCount + typeCount;
26
+ if (total === 0)
27
+ return EMPTY;
28
+ if (methodCount === total)
29
+ return methods;
30
+ if (fieldCount === total)
31
+ return fields;
32
+ if (typeCount === total)
33
+ return nestedTypes;
34
+ const merged = new Array(total);
35
+ let i = 0;
36
+ for (let j = 0; j < methodCount; j++)
37
+ merged[i++] = methods[j];
38
+ for (let j = 0; j < fieldCount; j++)
39
+ merged[i++] = fields[j];
40
+ for (let j = 0; j < typeCount; j++)
41
+ merged[i++] = nestedTypes[j];
42
+ return merged;
43
+ }
@@ -27,12 +27,21 @@ export interface TypeRegistry {
27
27
  * Returned array is a view into the live index — do not mutate.
28
28
  */
29
29
  lookupImplByName(name: string): readonly SymbolDefinition[];
30
+ /**
31
+ * Look up nested-type defs registered under `(ownerNodeId, simpleName)`
32
+ * in registration order. Returns `[]` on miss. Used by Step 2 Receiver/MRO
33
+ * resolution when the receiver's owner declares nested classes/structs/
34
+ * enums/typedefs/etc. that the caller's `acceptedKinds` includes.
35
+ */
36
+ lookupAllByOwner(ownerNodeId: string, simpleName: string): readonly SymbolDefinition[];
30
37
  }
31
38
  export interface MutableTypeRegistry extends TypeRegistry {
32
39
  /** Register a class-like type by name and qualified name. */
33
40
  registerClass(name: string, qualifiedName: string, def: SymbolDefinition): void;
34
41
  /** Register a Rust Impl block by name. */
35
42
  registerImpl(name: string, def: SymbolDefinition): void;
43
+ /** Register a nested type under its owner. Appends when the key already exists. */
44
+ registerByOwner(ownerNodeId: string, simpleName: string, def: SymbolDefinition): void;
36
45
  /** Clear all entries. */
37
46
  clear(): void;
38
47
  }
@@ -5,6 +5,7 @@
5
5
  * Eagerly-populated indexes keyed by symbol name and qualified name.
6
6
  * Also includes a separate index for Rust Impl blocks.
7
7
  */
8
+ const EMPTY = Object.freeze([]);
8
9
  // ---------------------------------------------------------------------------
9
10
  // Factory
10
11
  // ---------------------------------------------------------------------------
@@ -12,6 +13,7 @@ export const createTypeRegistry = () => {
12
13
  const classByName = new Map();
13
14
  const classByQualifiedName = new Map();
14
15
  const implByName = new Map();
16
+ const nestedByOwner = new Map();
15
17
  const lookupClassByName = (name) => {
16
18
  return classByName.get(name) ?? [];
17
19
  };
@@ -21,6 +23,9 @@ export const createTypeRegistry = () => {
21
23
  const lookupImplByName = (name) => {
22
24
  return implByName.get(name) ?? [];
23
25
  };
26
+ const lookupAllByOwner = (ownerNodeId, simpleName) => {
27
+ return nestedByOwner.get(`${ownerNodeId}\0${simpleName}`) ?? EMPTY;
28
+ };
24
29
  const registerClass = (name, qualifiedName, def) => {
25
30
  const existing = classByName.get(name);
26
31
  if (existing) {
@@ -46,17 +51,30 @@ export const createTypeRegistry = () => {
46
51
  implByName.set(name, [def]);
47
52
  }
48
53
  };
54
+ const registerByOwner = (ownerNodeId, simpleName, def) => {
55
+ const key = `${ownerNodeId}\0${simpleName}`;
56
+ const existing = nestedByOwner.get(key);
57
+ if (existing) {
58
+ existing.push(def);
59
+ }
60
+ else {
61
+ nestedByOwner.set(key, [def]);
62
+ }
63
+ };
49
64
  const clear = () => {
50
65
  classByName.clear();
51
66
  classByQualifiedName.clear();
52
67
  implByName.clear();
68
+ nestedByOwner.clear();
53
69
  };
54
70
  return {
55
71
  lookupClassByName,
56
72
  lookupClassByQualifiedName,
57
73
  lookupImplByName,
74
+ lookupAllByOwner,
58
75
  registerClass,
59
76
  registerImpl,
77
+ registerByOwner,
60
78
  clear,
61
79
  };
62
80
  };
@@ -72,6 +72,7 @@ export const MIGRATED_LANGUAGES = new Set([
72
72
  SupportedLanguages.C,
73
73
  SupportedLanguages.CPlusPlus,
74
74
  SupportedLanguages.PHP,
75
+ SupportedLanguages.JavaScript,
75
76
  ]);
76
77
  /**
77
78
  * Return the env-var name that controls a given language's registry-
@@ -37,12 +37,14 @@
37
37
  * resolution but not emitted as multiple edges; callers that want
38
38
  * branch-on-ambiguity behavior should consume the registries directly.
39
39
  */
40
- import { CLASS_KINDS, FIELD_KINDS, METHOD_KINDS, type ReferenceIndex, type RegistryProviders } from '../../_shared/index.js';
40
+ import { CLASS_KINDS, FIELD_KINDS, METHOD_KINDS, type ReferenceIndex, type RegistryContext, type RegistryProviders } from '../../_shared/index.js';
41
41
  import type { ScopeResolutionIndexes } from './model/scope-resolution-indexes.js';
42
42
  export interface ResolveReferencesInput {
43
43
  readonly scopes: ScopeResolutionIndexes;
44
44
  /** Provider hooks consumed by the registries (e.g. `arityCompatibility`). */
45
45
  readonly providers?: RegistryProviders;
46
+ /** Required owner-keyed member lookup used by Step 2 receiver/MRO walks. */
47
+ readonly ownedMembersByOwner: RegistryContext['ownedMembersByOwner'];
46
48
  }
47
49
  export interface ResolveStats {
48
50
  readonly sitesProcessed: number;
@@ -51,6 +51,7 @@ export function resolveReferenceSites(input) {
51
51
  defs: scopes.defs,
52
52
  qualifiedNames: scopes.qualifiedNames,
53
53
  moduleScopes: scopes.moduleScopes,
54
+ ownedMembersByOwner: input.ownedMembersByOwner,
54
55
  methodDispatch: scopes.methodDispatch,
55
56
  providers,
56
57
  };
@@ -135,7 +136,10 @@ function lookupForSite(site, classRegistry, methodRegistry, fieldRegistry) {
135
136
  case 'write': {
136
137
  // Try field first; fall through to method then class so bare-name
137
138
  // reads of a function (e.g. `cb = save`) still resolve.
138
- const fieldHits = fieldRegistry.lookup(site.name, site.inScope);
139
+ const fieldOpts = {
140
+ ...(site.explicitReceiver !== undefined ? { explicitReceiver: site.explicitReceiver } : {}),
141
+ };
142
+ const fieldHits = fieldRegistry.lookup(site.name, site.inScope, fieldOpts);
139
143
  if (fieldHits.length > 0)
140
144
  return fieldHits;
141
145
  const methodHits = methodRegistry.lookup(site.name, site.inScope);
@@ -692,6 +692,7 @@ function pass5CollectReferences(matches, positionIndex, filePath, referenceSites
692
692
  const explicitReceiver = extractExplicitReceiver(match);
693
693
  const arity = extractArity(match);
694
694
  const argumentTypes = extractArgumentTypes(match);
695
+ const argumentTypeClasses = parseJsonParameterTypeClassesCapture(match['@reference.parameter-type-classes']);
695
696
  const site = {
696
697
  name: nameCap.text,
697
698
  atRange: anchor.range,
@@ -701,6 +702,7 @@ function pass5CollectReferences(matches, positionIndex, filePath, referenceSites
701
702
  ...(explicitReceiver !== undefined ? { explicitReceiver } : {}),
702
703
  ...(arity !== undefined ? { arity } : {}),
703
704
  ...(argumentTypes !== undefined ? { argumentTypes } : {}),
705
+ ...(argumentTypeClasses !== undefined ? { argumentTypeClasses } : {}),
704
706
  };
705
707
  referenceSites.push(site);
706
708
  }
@@ -806,9 +808,11 @@ const KNOWN_SUB_TAGS = new Set([
806
808
  '@reference.receiver',
807
809
  '@reference.arity',
808
810
  '@reference.parameter-types',
811
+ '@reference.parameter-type-classes',
809
812
  '@declaration.parameter-count',
810
813
  '@declaration.required-parameter-count',
811
814
  '@declaration.parameter-types',
815
+ '@declaration.parameter-type-classes',
812
816
  '@declaration.template-constraints',
813
817
  ]);
814
818
  /**
@@ -70,6 +70,7 @@ export declare function pickImplicitThisOverload(site: {
70
70
  readonly name: string;
71
71
  readonly arity?: number;
72
72
  readonly argumentTypes?: readonly string[];
73
+ readonly argumentTypeClasses?: readonly import('../../../../_shared/index.js').ParameterTypeClass[];
73
74
  }, scopes: ScopeResolutionIndexes, workspaceIndex: WorkspaceResolutionIndex, model: SemanticModel, hookCtx?: {
74
75
  readonly conversionRankFn?: ConversionRankFn;
75
76
  readonly constraintCompatibility?: ScopeResolver['constraintCompatibility'];
@@ -22,6 +22,11 @@ import { isOverloadAmbiguousAfterNormalization, narrowOverloadCandidates, } from
22
22
  export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _referenceIndex, handledSites, model, workspaceIndex, options = {}) {
23
23
  let emitted = 0;
24
24
  const seen = new Set();
25
+ // Build an O(1) simple-name -> callable defs index over scopes.defs once
26
+ // per pass so pickUniqueGlobalCallable doesn't re-scan defs.byId.values()
27
+ // per call site. Same name + callable-kind filter that the previous scan
28
+ // applied (see pickUniqueGlobalCallable JSDoc). Cost: O(|defs|) once.
29
+ const globalCallablesBySimpleName = buildGlobalCallableIndex(scopes);
25
30
  for (const parsed of parsedFiles) {
26
31
  for (const site of parsed.referenceSites) {
27
32
  if (site.kind !== 'call')
@@ -67,6 +72,7 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
67
72
  const allCallables = findAllCallableBindingsInScope(site.inScope, site.name, scopes);
68
73
  if (allCallables.length > 1) {
69
74
  const narrowed = narrowOverloadCandidates(allCallables, site.arity, site.argumentTypes, {
75
+ argumentTypeClasses: site.argumentTypeClasses,
70
76
  conversionRankFn: options.conversionRankFn,
71
77
  constraintCompatibility: options.constraintCompatibility,
72
78
  });
@@ -121,6 +127,7 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
121
127
  }
122
128
  else {
123
129
  const narrowed = narrowOverloadCandidates(ordinary, site.arity, site.argumentTypes, {
130
+ argumentTypeClasses: site.argumentTypeClasses,
124
131
  conversionRankFn: options.conversionRankFn,
125
132
  constraintCompatibility: options.constraintCompatibility,
126
133
  });
@@ -159,6 +166,7 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
159
166
  push(ordinary);
160
167
  push(adl);
161
168
  const narrowed = narrowOverloadCandidates(merged, site.arity, site.argumentTypes, {
169
+ argumentTypeClasses: site.argumentTypeClasses,
162
170
  conversionRankFn: options.conversionRankFn,
163
171
  constraintCompatibility: options.constraintCompatibility,
164
172
  });
@@ -187,7 +195,7 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
187
195
  // the caller does not import the target package. Same-package calls are
188
196
  // usually caught by nearest-scope lookup before reaching here.
189
197
  if (fnDef === undefined && options.allowGlobalFallback === true) {
190
- fnDef = pickUniqueGlobalCallable(site.name, model, scopes, parsed.filePath, options.isFileLocalDef, site.arity, options.isCallableVisibleFromCaller !== undefined
198
+ fnDef = pickUniqueGlobalCallable(site.name, model, globalCallablesBySimpleName, parsed.filePath, options.isFileLocalDef, site.arity, options.isCallableVisibleFromCaller !== undefined
191
199
  ? (candidate) => options.isCallableVisibleFromCaller({
192
200
  callerParsed: parsed,
193
201
  candidate,
@@ -227,15 +235,36 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
227
235
  }
228
236
  return emitted;
229
237
  }
230
- function pickUniqueGlobalCallable(name, model, scopes, callerFilePath, isFileLocalDef, callArity, isCallerVisible, callArgTypes, conversionRankFn) {
231
- const scopeDefs = [];
232
- const scopeSeen = new Set();
238
+ /**
239
+ * Build a `simpleName -> callable defs` index from `scopes.defs` once per
240
+ * pass. Mirrors the filter the old per-site scan applied: Function /
241
+ * Method / Constructor, keyed by the last `.`-segment of `qualifiedName`
242
+ * (falling back to the qualifiedName itself when undotted). Used by
243
+ * `pickUniqueGlobalCallable` so every free-call fallback site is O(1)
244
+ * instead of O(|defs|).
245
+ */
246
+ function buildGlobalCallableIndex(scopes) {
247
+ const out = new Map();
233
248
  for (const def of scopes.defs.byId.values()) {
234
- const simple = def.qualifiedName?.split('.').pop() ?? def.qualifiedName;
235
- if (simple !== name)
236
- continue;
237
249
  if (def.type !== 'Function' && def.type !== 'Method' && def.type !== 'Constructor')
238
250
  continue;
251
+ const qualified = def.qualifiedName;
252
+ if (qualified === undefined || qualified.length === 0)
253
+ continue;
254
+ const dot = qualified.lastIndexOf('.');
255
+ const simple = dot === -1 ? qualified : qualified.slice(dot + 1);
256
+ const bucket = out.get(simple);
257
+ if (bucket)
258
+ bucket.push(def);
259
+ else
260
+ out.set(simple, [def]);
261
+ }
262
+ return out;
263
+ }
264
+ function pickUniqueGlobalCallable(name, model, globalCallablesBySimpleName, callerFilePath, isFileLocalDef, callArity, isCallerVisible, callArgTypes, conversionRankFn) {
265
+ const scopeDefs = [];
266
+ const scopeSeen = new Set();
267
+ for (const def of globalCallablesBySimpleName.get(name) ?? []) {
239
268
  // Skip file-local defs (e.g. C `static` functions) that live in a
240
269
  // different file from the caller — they are logically invisible.
241
270
  if (isFileLocalDef !== undefined && def.filePath !== callerFilePath && isFileLocalDef(def)) {
@@ -401,6 +430,7 @@ export function pickImplicitThisOverload(site, scopes, workspaceIndex, model, ho
401
430
  // disambiguating signal) leaves the call unresolved rather than
402
431
  // routing to an arbitrary first overload by registration order.
403
432
  const candidates = narrowOverloadCandidates(overloads, site.arity, site.argumentTypes, {
433
+ argumentTypeClasses: site.argumentTypeClasses,
404
434
  conversionRankFn: hookCtx?.conversionRankFn,
405
435
  constraintCompatibility: hookCtx?.constraintCompatibility,
406
436
  });
@@ -59,6 +59,8 @@ export type ConversionRankFn = (argType: string, paramType: string) => number;
59
59
  * undefined preserves the legacy arity + exact-type behavior.
60
60
  */
61
61
  export interface OverloadNarrowingHookCtx {
62
+ /** Shape-preserving per-argument sidecar aligned with `argTypes`. */
63
+ readonly argumentTypeClasses?: ConstraintContext['argumentTypeClasses'];
62
64
  /** Conversion-rank scoring fallback (step 4b). Engages when the
63
65
  * exact-type filter rejects every candidate. */
64
66
  readonly conversionRankFn?: ConversionRankFn;
@@ -118,7 +118,14 @@ export function narrowOverloadCandidates(overloads, argCount, argTypes, hookCtx)
118
118
  // than emitting a wrong edge.
119
119
  if (hookCtx?.constraintCompatibility !== undefined && argCount !== undefined) {
120
120
  const callsite = { arity: argCount };
121
- const ctx = argTypes !== undefined ? { argumentTypes: argTypes } : {};
121
+ const ctx = argTypes !== undefined
122
+ ? {
123
+ argumentTypes: argTypes,
124
+ ...(hookCtx.argumentTypeClasses !== undefined
125
+ ? { argumentTypeClasses: hookCtx.argumentTypeClasses }
126
+ : {}),
127
+ }
128
+ : {};
122
129
  result = result.filter((def) => {
123
130
  if (def.templateConstraints === undefined)
124
131
  return true;
@@ -250,6 +250,7 @@ export function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, h
250
250
  const methodOverloads = model.methods.lookupAllByOwner(ownerId, memberName);
251
251
  if (methodOverloads.length > 0) {
252
252
  const narrowed = narrowOverloadCandidates(methodOverloads, site.arity, site.argumentTypes, {
253
+ argumentTypeClasses: site.argumentTypeClasses,
253
254
  conversionRankFn: provider.conversionRankFn,
254
255
  constraintCompatibility: provider.constraintCompatibility,
255
256
  });
@@ -532,6 +533,7 @@ function pickOverload(ownerId, memberName, site, model, provider) {
532
533
  if (overloads.length === 1)
533
534
  return overloads[0];
534
535
  const candidates = narrowOverloadCandidates(overloads, site.arity, site.argumentTypes, {
536
+ argumentTypeClasses: site.argumentTypeClasses,
535
537
  conversionRankFn: provider.conversionRankFn,
536
538
  constraintCompatibility: provider.constraintCompatibility,
537
539
  });
@@ -18,8 +18,8 @@
18
18
  * undefined `ownerId` is reachable via either:
19
19
  * - `model.methods.lookupAllByOwner(ownerId, simpleName)` — if the
20
20
  * def is a Method / Function / Constructor, OR
21
- * - `model.fields.lookupFieldByOwner(ownerId, simpleName)` — if the
22
- * def is a Property / Variable.
21
+ * - `model.fields.lookupAllByOwner(ownerId, simpleName)` — if the
22
+ * def is a Property / Variable / Const / Static.
23
23
  *
24
24
  * This invariant is the foundation of Contract Invariant I9
25
25
  * (`contract/scope-resolver.ts`): scope-resolution passes MUST read
@@ -47,6 +47,8 @@ export interface ReconcileStats {
47
47
  readonly methodsRegistered: number;
48
48
  /** Property/Variable defs registered into FieldRegistry. */
49
49
  readonly fieldsRegistered: number;
50
+ /** Class-like nested type defs registered into TypeRegistry by owner. */
51
+ readonly nestedTypesRegistered: number;
50
52
  /** Defs already present (idempotent skip). */
51
53
  readonly skippedAlreadyPresent: number;
52
54
  }
@@ -18,8 +18,8 @@
18
18
  * undefined `ownerId` is reachable via either:
19
19
  * - `model.methods.lookupAllByOwner(ownerId, simpleName)` — if the
20
20
  * def is a Method / Function / Constructor, OR
21
- * - `model.fields.lookupFieldByOwner(ownerId, simpleName)` — if the
22
- * def is a Property / Variable.
21
+ * - `model.fields.lookupAllByOwner(ownerId, simpleName)` — if the
22
+ * def is a Property / Variable / Const / Static.
23
23
  *
24
24
  * This invariant is the foundation of Contract Invariant I9
25
25
  * (`contract/scope-resolver.ts`): scope-resolution passes MUST read
@@ -41,9 +41,25 @@
41
41
  * follow-up plan.
42
42
  */
43
43
  import { simpleQualifiedName } from '../graph-bridge/ids.js';
44
+ const NESTED_TYPE_KINDS = new Set([
45
+ 'Class',
46
+ 'Interface',
47
+ 'Enum',
48
+ 'Struct',
49
+ 'Union',
50
+ 'Trait',
51
+ 'TypeAlias',
52
+ 'Typedef',
53
+ 'Record',
54
+ 'Delegate',
55
+ 'Annotation',
56
+ 'Template',
57
+ 'Namespace',
58
+ ]);
44
59
  export function reconcileOwnership(parsedFiles, model) {
45
60
  let methodsRegistered = 0;
46
61
  let fieldsRegistered = 0;
62
+ let nestedTypesRegistered = 0;
47
63
  let skippedAlreadyPresent = 0;
48
64
  for (const parsed of parsedFiles) {
49
65
  for (const def of parsed.localDefs) {
@@ -62,18 +78,30 @@ export function reconcileOwnership(parsedFiles, model) {
62
78
  model.methods.register(ownerId, simple, def);
63
79
  methodsRegistered++;
64
80
  }
65
- else if (def.type === 'Property' || def.type === 'Variable') {
66
- const existing = model.fields.lookupFieldByOwner(ownerId, simple);
67
- if (existing !== undefined && existing.nodeId === def.nodeId) {
81
+ else if (def.type === 'Property' ||
82
+ def.type === 'Variable' ||
83
+ def.type === 'Const' ||
84
+ def.type === 'Static') {
85
+ const existing = model.fields.lookupAllByOwner(ownerId, simple);
86
+ if (existing.some((e) => e.nodeId === def.nodeId)) {
68
87
  skippedAlreadyPresent++;
69
88
  continue;
70
89
  }
71
90
  model.fields.register(ownerId, simple, def);
72
91
  fieldsRegistered++;
73
92
  }
93
+ else if (NESTED_TYPE_KINDS.has(def.type)) {
94
+ const existing = model.types.lookupAllByOwner(ownerId, simple);
95
+ if (existing.some((e) => e.nodeId === def.nodeId)) {
96
+ skippedAlreadyPresent++;
97
+ continue;
98
+ }
99
+ model.types.registerByOwner(ownerId, simple, def);
100
+ nestedTypesRegistered++;
101
+ }
74
102
  }
75
103
  }
76
- return { methodsRegistered, fieldsRegistered, skippedAlreadyPresent };
104
+ return { methodsRegistered, fieldsRegistered, nestedTypesRegistered, skippedAlreadyPresent };
77
105
  }
78
106
  /**
79
107
  * Debug-mode parity validator. Runs only when
@@ -111,14 +139,25 @@ export function validateOwnershipParity(parsedFiles, model, onWarn) {
111
139
  mismatches++;
112
140
  }
113
141
  }
114
- else if (def.type === 'Property' || def.type === 'Variable') {
115
- const found = model.fields.lookupFieldByOwner(ownerId, simple);
116
- if (found === undefined || found.nodeId !== def.nodeId) {
142
+ else if (def.type === 'Property' ||
143
+ def.type === 'Variable' ||
144
+ def.type === 'Const' ||
145
+ def.type === 'Static') {
146
+ const found = model.fields.lookupAllByOwner(ownerId, simple);
147
+ if (!found.some((d) => d.nodeId === def.nodeId)) {
117
148
  onWarn(`semantic-model parity: ${def.type} ${def.nodeId} (${parsed.filePath}) ` +
118
149
  `owned by ${ownerId} as "${simple}" not in FieldRegistry`);
119
150
  mismatches++;
120
151
  }
121
152
  }
153
+ else if (NESTED_TYPE_KINDS.has(def.type)) {
154
+ const found = model.types.lookupAllByOwner(ownerId, simple);
155
+ if (!found.some((d) => d.nodeId === def.nodeId)) {
156
+ onWarn(`semantic-model parity: ${def.type} ${def.nodeId} (${parsed.filePath}) ` +
157
+ `owned by ${ownerId} as "${simple}" not in TypeRegistry owner index`);
158
+ mismatches++;
159
+ }
160
+ }
122
161
  }
123
162
  }
124
163
  return mismatches;
@@ -17,6 +17,7 @@ import { javaScopeResolver } from '../../languages/java/scope-resolver.js';
17
17
  import { cScopeResolver } from '../../languages/c/scope-resolver.js';
18
18
  import { cppScopeResolver } from '../../languages/cpp/scope-resolver.js';
19
19
  import { phpScopeResolver } from '../../languages/php/scope-resolver.js';
20
+ import { javascriptScopeResolver } from '../../languages/javascript/scope-resolver.js';
20
21
  /** Map of `SupportedLanguages` → `ScopeResolver`. The phase iterates
21
22
  * this map intersected with `MIGRATED_LANGUAGES` (the per-language
22
23
  * flag set) so adding a resolver here without flipping the flag is
@@ -30,4 +31,5 @@ export const SCOPE_RESOLVERS = new Map([
30
31
  [SupportedLanguages.C, cScopeResolver],
31
32
  [SupportedLanguages.CPlusPlus, cppScopeResolver],
32
33
  [SupportedLanguages.PHP, phpScopeResolver],
34
+ [SupportedLanguages.JavaScript, javascriptScopeResolver],
33
35
  ]);
@@ -22,6 +22,7 @@
22
22
  *
23
23
  * Plan: `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
24
24
  */
25
+ import { lookupOwnedMembersByOwner } from '../../model/owned-members-lookup.js';
25
26
  import { reconcileOwnership, validateOwnershipParity } from './reconcile-ownership.js';
26
27
  import { validateBindingsImmutability } from './validate-bindings-immutability.js';
27
28
  import { extractParsedFile } from '../../scope-extractor-bridge.js';
@@ -236,6 +237,7 @@ export function runScopeResolution(input, provider) {
236
237
  const { referenceIndex, stats: resolveStats } = resolveReferenceSites({
237
238
  scopes: indexes,
238
239
  providers: registryProviders,
240
+ ownedMembersByOwner: (ownerDefId, memberName) => lookupOwnedMembersByOwner(readonlyModel, ownerDefId, memberName),
239
241
  });
240
242
  const tResolve = PROF ? process.hrtime.bigint() : 0n;
241
243
  // ── Phase 4: emit graph edges (LOAD-BEARING ORDER — see I1) ────────────
@@ -66,7 +66,9 @@ export declare const initLbug: (dbPath: string) => Promise<{
66
66
  * database is busy (e.g. `gitnexus analyze` holds the write lock).
67
67
  * Each retry waits DB_LOCK_RETRY_DELAY_MS * attempt milliseconds.
68
68
  */
69
- export declare const withLbugDb: <T>(dbPath: string, operation: () => Promise<T>) => Promise<T>;
69
+ export declare const withLbugDb: <T>(dbPath: string, operation: () => Promise<T>, options?: {
70
+ readOnly?: boolean;
71
+ }) => Promise<T>;
70
72
  export type LbugProgressCallback = (message: string) => void;
71
73
  export declare const loadGraphToLbug: (graph: KnowledgeGraph, repoPath: string, storagePath: string, onProgress?: LbugProgressCallback) => Promise<{
72
74
  success: boolean;