gitnexus 1.6.9-rc.30 → 1.6.9-rc.32

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.
@@ -24,6 +24,8 @@ import type { ExtractedContract, RepoHandle } from '../types.js';
24
24
  * widen `HTTP_SCAN_GLOB` if needed.
25
25
  */
26
26
  export declare const HANDLES_ROUTE_QUERY = "\nMATCH (handlerFile:File)-[r:CodeRelation {type: 'HANDLES_ROUTE'}]->(route:Route)\nRETURN handlerFile.id AS fileId, handlerFile.filePath AS filePath,\n route.name AS routePath, route.id AS routeId,\n route.method AS routeMethod,\n route.handlerSymbolId AS handlerSymbolId,\n route.responseKeys AS responseKeys,\n r.reason AS routeSource";
27
+ export declare const RESOLVE_BY_NAME_QUERY = "\nMATCH (n) WHERE labels(n) IN ['Function','Method','CodeElement']\n AND n.name = $name AND n.filePath <> ''\nRETURN n.id AS uid, n.name AS name, n.filePath AS filePath\nLIMIT 2";
28
+ export declare const RESOLVE_IN_MODULE_QUERY = "\nMATCH (n) WHERE labels(n) IN ['Function','Method','CodeElement']\n AND n.name = $name AND (n.filePath STARTS WITH $fileDot OR n.filePath STARTS WITH $fileSlash)\nRETURN n.id AS uid, n.name AS name, n.filePath AS filePath\nLIMIT 2";
27
29
  /**
28
30
  * Canonicalize a provider-side HTTP path for contract-id generation:
29
31
  * - strip query string
@@ -67,10 +67,21 @@ MATCH (sym:CodeElement)
67
67
  WHERE sym.filePath = $filePath AND sym.startLine IS NOT NULL AND sym.endLine IS NOT NULL
68
68
  RETURN sym.id AS uid, sym.name AS name, sym.filePath AS filePath,
69
69
  sym.startLine AS startLine, sym.endLine AS endLine, labels(sym) AS labels`;
70
- // Repo-wide lookup of a symbol by exact name (label-union, as in
71
- // manifest-extractor.ts). Used to resolve a provider's named handler when it is
72
- // defined in a file OTHER than its route registration — and only honored when
73
- // the result is unique (see resolveSymbolByNameUnique).
70
+ // Repo-wide lookup of a symbol by exact name. Used to resolve a provider's
71
+ // named handler when it is defined in a file OTHER than its route registration —
72
+ // and only honored when the result is unique (see resolveSymbolByNameUnique).
73
+ //
74
+ // Label filtering uses `labels(n) IN [...]` rather than the openCypher
75
+ // disjunction `MATCH (n:A|B|C)`. NOTE: this 3-label set (Function/Method/
76
+ // CodeElement) actually PARSES — LadybugDB only rejects a disjunction that
77
+ // names a reserved keyword (e.g. `Macro`, `Union`) or a missing node table,
78
+ // neither of which applies here. So this query was NOT broken by #2325; it
79
+ // uses the `labels(n) IN` form for consistency with the manifest custom-branch
80
+ // fix (which WAS broken) and to stay immune if a reserved-keyword label is
81
+ // added later. `labels(n)` returns the node's single label as a string here, so
82
+ // `IN [...]` is an exact allowlist. (Exported so integration tests can run the
83
+ // exact production query against a real LadybugDB — the bug shipped because no
84
+ // test ran these strings against the real parser.)
74
85
  //
75
86
  // `n.filePath <> ''` excludes synthetic non-source `CodeElement` nodes that
76
87
  // carry no real file — ORM model/table nodes (orm.ts emits `filePath: ''`) and
@@ -78,9 +89,9 @@ RETURN sym.id AS uid, sym.name AS name, sym.filePath AS filePath,
78
89
  // degenerate edge-less node NOR inflates the uniqueness count and masks the real
79
90
  // handler. `LIMIT 2` bounds materialization: distinguishing unique (1) from
80
91
  // ambiguous (>=2) never needs more than two rows (the count guard stays exact).
81
- const RESOLVE_BY_NAME_QUERY = `
82
- MATCH (n:Function|Method|CodeElement)
83
- WHERE n.name = $name AND n.filePath <> ''
92
+ export const RESOLVE_BY_NAME_QUERY = `
93
+ MATCH (n) WHERE labels(n) IN ['Function','Method','CodeElement']
94
+ AND n.name = $name AND n.filePath <> ''
84
95
  RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
85
96
  LIMIT 2`;
86
97
  // Resolve an IMPORTED handler by pinning it to the import's target module: the
@@ -89,9 +100,9 @@ LIMIT 2`;
89
100
  // the precise rung — it survives aliases and local same-name collisions that a
90
101
  // repo-wide name lookup cannot, and only resolves on a unique match within that
91
102
  // module. `LIMIT 2` keeps the uniqueness count exact (see RESOLVE_BY_NAME_QUERY).
92
- const RESOLVE_IN_MODULE_QUERY = `
93
- MATCH (n:Function|Method|CodeElement)
94
- WHERE n.name = $name AND (n.filePath STARTS WITH $fileDot OR n.filePath STARTS WITH $fileSlash)
103
+ export const RESOLVE_IN_MODULE_QUERY = `
104
+ MATCH (n) WHERE labels(n) IN ['Function','Method','CodeElement']
105
+ AND n.name = $name AND (n.filePath STARTS WITH $fileDot OR n.filePath STARTS WITH $fileSlash)
95
106
  RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
96
107
  LIMIT 2`;
97
108
  // Source-file extensions an import specifier may resolve to (stripped before
@@ -4,6 +4,7 @@ export interface ManifestExtractResult {
4
4
  contracts: StoredContract[];
5
5
  crossLinks: CrossLink[];
6
6
  }
7
+ export declare const CUSTOM_CONTRACT_RESOLVE_QUERY = "MATCH (n)\n WHERE labels(n) IN ['Function','Method','Class','Interface','Struct','Enum','Trait','Constructor','TypeAlias','Impl','Macro','Union','Typedef','Property','Record','Delegate','Annotation','Template','Const','Static','CodeElement']\n AND n.name = $symbolName\n RETURN n.id AS uid, n.name AS name, n.filePath AS filePath\n ORDER BY n.filePath ASC\n LIMIT 1";
7
8
  /**
8
9
  * Stable synthetic symbolUid for a manifest-declared contract whose target
9
10
  * symbol could not be resolved against the per-repo graph (resolveSymbol
@@ -1,4 +1,18 @@
1
1
  import { logger } from '../../logger.js';
2
+ // Repo-wide symbol lookup for `custom` workspace contracts. Exported so the
3
+ // #2325 integration test can run the EXACT production query against a real
4
+ // LadybugDB — a hand-copied query string in the test would silently drift
5
+ // from this allowlist. Uses the `labels(n) IN [...]` allowlist form rather
6
+ // than a `MATCH (n:A|B)` disjunction: this 21-label list contains the
7
+ // reserved-keyword labels `Macro` and `Union`, and LadybugDB's parser rejects
8
+ // a disjunction that names a reserved keyword (#2325) — which the resolver's
9
+ // try/catch then swallowed. `labels(n) IN` has no such collision.
10
+ export const CUSTOM_CONTRACT_RESOLVE_QUERY = `MATCH (n)
11
+ WHERE labels(n) IN ['Function','Method','Class','Interface','Struct','Enum','Trait','Constructor','TypeAlias','Impl','Macro','Union','Typedef','Property','Record','Delegate','Annotation','Template','Const','Static','CodeElement']
12
+ AND n.name = $symbolName
13
+ RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
14
+ ORDER BY n.filePath ASC
15
+ LIMIT 1`;
2
16
  /**
3
17
  * Canonicalize an HTTP path for matching against Route.name in the graph.
4
18
  * Mirrors core/ingestion/pipeline.ts ensureSlash semantics:
@@ -148,6 +162,18 @@ export class ManifestExtractor {
148
162
  // Cross-impact still works: the bridge query joins on the synthetic
149
163
  // uid, and the local impact engine derives the same uid for the
150
164
  // unresolved symbol — name-based hints are the additional safety net.
165
+ //
166
+ // Label filtering uses `MATCH (n) WHERE labels(n) IN [...]`, NOT the
167
+ // openCypher disjunction `MATCH (n:A|B|C)`. LadybugDB's parser rejects a
168
+ // disjunction that names a reserved keyword (`Macro` and `Union` both are)
169
+ // OR a label with no node table (e.g. the old `lib` branch's `Package`).
170
+ // The `custom` branch (reserved keywords in its list) and `lib` branch
171
+ // (missing `Package` table) genuinely threw (#2325) and the whole try/catch
172
+ // below swallowed it; the other branches parsed but use the same form for
173
+ // consistency and future-proofing. `labels(n)` returns the node's single
174
+ // label as a string here, so `IN [...]` is an exact allowlist that includes
175
+ // listed labels and excludes everything else — and is immune to both
176
+ // failure modes (no keyword collision; an unknown label is just a non-match).
151
177
  try {
152
178
  let rows;
153
179
  if (link.type === 'http') {
@@ -178,7 +204,7 @@ export class ManifestExtractor {
178
204
  // listeners, publishers). Restrict to symbol-like labels to
179
205
  // avoid cross-matching Files/Variables/Imports that happen to
180
206
  // share the topic name.
181
- rows = await executor(`MATCH (n:Function|Method|Class|Interface) WHERE n.name = $contract
207
+ rows = await executor(`MATCH (n) WHERE labels(n) IN ['Function','Method','Class','Interface'] AND n.name = $contract
182
208
  RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
183
209
  ORDER BY n.filePath ASC
184
210
  LIMIT 1`, { contract: link.contract });
@@ -199,13 +225,13 @@ export class ManifestExtractor {
199
225
  const serviceName = link.type === 'thrift' ? (rawServiceName.split('.').pop() ?? '') : rawServiceName;
200
226
  const methodName = parts[1]?.trim() ?? '';
201
227
  if (methodName) {
202
- rows = await executor(`MATCH (n:Function|Method) WHERE n.name = $methodName
228
+ rows = await executor(`MATCH (n) WHERE labels(n) IN ['Function','Method'] AND n.name = $methodName
203
229
  RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
204
230
  ORDER BY n.filePath ASC
205
231
  LIMIT 1`, { methodName });
206
232
  }
207
233
  else if (serviceName) {
208
- rows = await executor(`MATCH (n:Class|Interface) WHERE n.name = $serviceName
234
+ rows = await executor(`MATCH (n) WHERE labels(n) IN ['Class','Interface'] AND n.name = $serviceName
209
235
  RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
210
236
  ORDER BY n.filePath ASC
211
237
  LIMIT 1`, { serviceName });
@@ -217,10 +243,11 @@ export class ManifestExtractor {
217
243
  else if (link.type === 'lib') {
218
244
  // Only exact match on the symbol's name. Previous fallback to
219
245
  // CONTAINS on n.filePath would promote "react" to "react-native"
220
- // or "@types/react" — silent wrong attribution. Restrict to
221
- // package-level labels so we don't return arbitrary symbols
222
- // named after a library.
223
- rows = await executor(`MATCH (n:Package|Module) WHERE n.name = $contract
246
+ // or "@types/react" — silent wrong attribution. Restrict to the
247
+ // package-level `Module` label so we don't return arbitrary symbols
248
+ // named after a library. (There is no `Package` node table — see
249
+ // NODE_TABLES so a `Package` entry only ever matched nothing.)
250
+ rows = await executor(`MATCH (n) WHERE labels(n) IN ['Module'] AND n.name = $contract
224
251
  RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
225
252
  ORDER BY n.filePath ASC
226
253
  LIMIT 1`, { contract: link.contract });
@@ -238,11 +265,7 @@ export class ManifestExtractor {
238
265
  const symbolName = link.contract.includes('::')
239
266
  ? link.contract.split('::').pop()
240
267
  : link.contract;
241
- rows = await executor(`MATCH (n:Function|Method|Class|Interface|Struct|Enum|Trait|Constructor|TypeAlias|Impl|Macro|Union|Typedef|Property|Record|Delegate|Annotation|Template|Const|Static|CodeElement)
242
- WHERE n.name = $symbolName
243
- RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
244
- ORDER BY n.filePath ASC
245
- LIMIT 1`, { symbolName });
268
+ rows = await executor(CUSTOM_CONTRACT_RESOLVE_QUERY, { symbolName });
246
269
  }
247
270
  else {
248
271
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.9-rc.30",
3
+ "version": "1.6.9-rc.32",
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",
@@ -61,7 +61,7 @@
61
61
  "@scarf/scarf": "^1.4.0",
62
62
  "busboy": "^1.6.0",
63
63
  "cli-progress": "^3.12.0",
64
- "commander": "^14.0.3",
64
+ "commander": "^15.0.0",
65
65
  "cors": "^2.8.5",
66
66
  "express": "^5.2.1",
67
67
  "express-rate-limit": "^8.4.1",