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
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
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
|
|
83
|
-
|
|
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
|
|
94
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
222
|
-
// named after a library.
|
|
223
|
-
|
|
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(
|
|
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.
|
|
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": "^
|
|
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",
|