@supermodeltools/mcp-server 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/graph-cache.js +328 -16
- package/dist/cache/graph-types.js +2 -2
- package/dist/constants.js +8 -4
- package/dist/index.js +167 -9
- package/dist/server.js +61 -98
- package/dist/tools/overview.js +144 -0
- package/dist/tools/symbol-context.js +285 -0
- package/dist/utils/api-helpers.js +8 -22
- package/package.json +2 -2
- package/dist/cache/index.js +0 -21
- package/dist/filtering.js +0 -45
- package/dist/queries/discovery.js +0 -163
- package/dist/queries/index.js +0 -252
- package/dist/queries/summary.js +0 -36
- package/dist/queries/traversal.js +0 -445
- package/dist/queries/types.js +0 -38
- package/dist/tools/create-supermodel-graph.js +0 -691
- package/dist/tools/feature-request.js +0 -84
- package/dist/tools/find-call-sites.js +0 -140
- package/dist/tools/find-definition.js +0 -160
- package/dist/tools/graph-tools.js +0 -277
- package/dist/tools/report-bug.js +0 -133
- package/dist/tools/task-query-tools.js +0 -81
- package/dist/tools/trace-call-chain.js +0 -178
- package/dist/tools/trace-data-flow.js +0 -232
- package/dist/utils/github.js +0 -253
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Traversal queries: function_calls_in, function_calls_out, definitions_in_file
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.functionCallsIn = functionCallsIn;
|
|
7
|
-
exports.functionCallsOut = functionCallsOut;
|
|
8
|
-
exports.definitionsInFile = definitionsInFile;
|
|
9
|
-
exports.fileImports = fileImports;
|
|
10
|
-
exports.domainMap = domainMap;
|
|
11
|
-
exports.domainMembership = domainMembership;
|
|
12
|
-
exports.neighborhood = neighborhood;
|
|
13
|
-
const graph_cache_1 = require("../cache/graph-cache");
|
|
14
|
-
const types_1 = require("./types");
|
|
15
|
-
const constants_1 = require("../constants");
|
|
16
|
-
const DEFAULT_LIMIT = constants_1.DEFAULT_QUERY_LIMIT;
|
|
17
|
-
/**
|
|
18
|
-
* function_calls_in - Find all callers of a function
|
|
19
|
-
*/
|
|
20
|
-
function functionCallsIn(params, graph, source) {
|
|
21
|
-
if (!params.targetId) {
|
|
22
|
-
console.error('[ERROR] function_calls_in called without targetId');
|
|
23
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
|
|
24
|
-
detail: 'Use search or list_nodes with labels=["Function"] to find function IDs',
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
// Verify target exists and is a function
|
|
28
|
-
const targetNode = graph.nodeById.get(params.targetId);
|
|
29
|
-
if (!targetNode) {
|
|
30
|
-
console.error('[ERROR] Node not found:', params.targetId);
|
|
31
|
-
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
32
|
-
detail: 'Use search or list_nodes with labels=["Function"] to discover function IDs',
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
if (targetNode.labels?.[0] !== 'Function') {
|
|
36
|
-
const actualLabel = targetNode.labels?.[0] || 'unknown';
|
|
37
|
-
console.error('[ERROR] Node is not a Function:', params.targetId, 'is', actualLabel);
|
|
38
|
-
return (0, types_1.createError)('INVALID_PARAMS', `Node is not a Function (found ${actualLabel})`, {
|
|
39
|
-
detail: 'This query only works on Function nodes. Use search with labels=["Function"] to find functions',
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
const adj = graph.callAdj.get(params.targetId);
|
|
43
|
-
if (!adj) {
|
|
44
|
-
return (0, types_1.createResponse)('function_calls_in', graph.cacheKey, source, graph.cachedAt, { nodes: [], edges: [] });
|
|
45
|
-
}
|
|
46
|
-
const limit = params.limit || DEFAULT_LIMIT;
|
|
47
|
-
const callerIds = adj.in.slice(0, limit);
|
|
48
|
-
const nodes = [];
|
|
49
|
-
const edges = [];
|
|
50
|
-
for (const callerId of callerIds) {
|
|
51
|
-
const callerNode = graph.nodeById.get(callerId);
|
|
52
|
-
if (callerNode) {
|
|
53
|
-
nodes.push((0, graph_cache_1.toNodeDescriptor)(callerNode));
|
|
54
|
-
edges.push({
|
|
55
|
-
type: 'calls',
|
|
56
|
-
from: callerId,
|
|
57
|
-
to: params.targetId,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
const hasMore = adj.in.length > limit;
|
|
62
|
-
return (0, types_1.createResponse)('function_calls_in', graph.cacheKey, source, graph.cachedAt, { nodes, edges }, {
|
|
63
|
-
page: { limit, hasMore },
|
|
64
|
-
warnings: hasMore ? [`${adj.in.length - limit} more callers not shown. Increase limit to see more.`] : undefined,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* function_calls_out - Find all functions called by a function
|
|
69
|
-
*/
|
|
70
|
-
function functionCallsOut(params, graph, source) {
|
|
71
|
-
if (!params.targetId) {
|
|
72
|
-
console.error('[ERROR] function_calls_out called without targetId');
|
|
73
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
|
|
74
|
-
detail: 'Use search or list_nodes with labels=["Function"] to find function IDs',
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
// Verify target exists and is a function
|
|
78
|
-
const targetNode = graph.nodeById.get(params.targetId);
|
|
79
|
-
if (!targetNode) {
|
|
80
|
-
console.error('[ERROR] Node not found:', params.targetId);
|
|
81
|
-
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
82
|
-
detail: 'Use search or list_nodes with labels=["Function"] to discover function IDs',
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
if (targetNode.labels?.[0] !== 'Function') {
|
|
86
|
-
const actualLabel = targetNode.labels?.[0] || 'unknown';
|
|
87
|
-
console.error('[ERROR] Node is not a Function:', params.targetId, 'is', actualLabel);
|
|
88
|
-
return (0, types_1.createError)('INVALID_PARAMS', `Node is not a Function (found ${actualLabel})`, {
|
|
89
|
-
detail: 'This query only works on Function nodes. Use search with labels=["Function"] to find functions',
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
const adj = graph.callAdj.get(params.targetId);
|
|
93
|
-
if (!adj) {
|
|
94
|
-
return (0, types_1.createResponse)('function_calls_out', graph.cacheKey, source, graph.cachedAt, { nodes: [], edges: [] });
|
|
95
|
-
}
|
|
96
|
-
const limit = params.limit || DEFAULT_LIMIT;
|
|
97
|
-
const calleeIds = adj.out.slice(0, limit);
|
|
98
|
-
const nodes = [];
|
|
99
|
-
const edges = [];
|
|
100
|
-
for (const calleeId of calleeIds) {
|
|
101
|
-
const calleeNode = graph.nodeById.get(calleeId);
|
|
102
|
-
if (calleeNode) {
|
|
103
|
-
nodes.push((0, graph_cache_1.toNodeDescriptor)(calleeNode));
|
|
104
|
-
edges.push({
|
|
105
|
-
type: 'calls',
|
|
106
|
-
from: params.targetId,
|
|
107
|
-
to: calleeId,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
const hasMore = adj.out.length > limit;
|
|
112
|
-
return (0, types_1.createResponse)('function_calls_out', graph.cacheKey, source, graph.cachedAt, { nodes, edges }, {
|
|
113
|
-
page: { limit, hasMore },
|
|
114
|
-
warnings: hasMore ? [`${adj.out.length - limit} more callees not shown. Increase limit to see more.`] : undefined,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* definitions_in_file - Get all classes, functions, types defined in a file
|
|
119
|
-
*/
|
|
120
|
-
function definitionsInFile(params, graph, source) {
|
|
121
|
-
// Accept either targetId (file node ID) or filePathPrefix (file path)
|
|
122
|
-
let filePath = null;
|
|
123
|
-
if (params.targetId) {
|
|
124
|
-
const targetNode = graph.nodeById.get(params.targetId);
|
|
125
|
-
if (!targetNode) {
|
|
126
|
-
console.error('[ERROR] Node not found:', params.targetId);
|
|
127
|
-
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
128
|
-
detail: 'Use search or list_nodes with labels=["File"] to find file nodes',
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
if (targetNode.labels?.[0] !== 'File') {
|
|
132
|
-
const actualLabel = targetNode.labels?.[0] || 'unknown';
|
|
133
|
-
console.error('[ERROR] Node is not a File:', params.targetId, 'is', actualLabel);
|
|
134
|
-
return (0, types_1.createError)('INVALID_PARAMS', `Node is not a File (found ${actualLabel})`, {
|
|
135
|
-
detail: 'This query requires a File node. Use search with labels=["File"] to find files',
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
filePath = targetNode.properties?.filePath || targetNode.properties?.path;
|
|
139
|
-
}
|
|
140
|
-
else if (params.filePathPrefix) {
|
|
141
|
-
filePath = params.filePathPrefix;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
console.error('[ERROR] definitions_in_file called without targetId or filePathPrefix');
|
|
145
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId or filePathPrefix', {
|
|
146
|
-
detail: 'Provide either a file node ID (targetId) or a file path (filePathPrefix)',
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
if (!filePath) {
|
|
150
|
-
console.error('[ERROR] Could not determine file path from node');
|
|
151
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Could not determine file path from the provided node', {
|
|
152
|
-
detail: 'The node is missing filePath or path properties',
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
const normalizedPath = (0, graph_cache_1.normalizePath)(filePath);
|
|
156
|
-
const pathEntry = graph.pathIndex.get(normalizedPath);
|
|
157
|
-
let resolvedPath = normalizedPath;
|
|
158
|
-
if (!pathEntry) {
|
|
159
|
-
// Try to find by partial match
|
|
160
|
-
for (const [path] of graph.pathIndex) {
|
|
161
|
-
if (path.endsWith(normalizedPath) || normalizedPath.endsWith(path)) {
|
|
162
|
-
resolvedPath = path;
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (resolvedPath === normalizedPath) {
|
|
167
|
-
console.error('[ERROR] File not found in codebase:', filePath);
|
|
168
|
-
return (0, types_1.createError)('NOT_FOUND', `File not found in analyzed codebase: ${filePath}`, {
|
|
169
|
-
detail: 'The file may not exist, may be excluded by .gitignore, or has no definitions',
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
const entry = pathEntry || graph.pathIndex.get(resolvedPath);
|
|
174
|
-
if (!entry) {
|
|
175
|
-
return (0, types_1.createResponse)('definitions_in_file', graph.cacheKey, source, graph.cachedAt, { file: null, definitions: { nodes: [] } });
|
|
176
|
-
}
|
|
177
|
-
const limit = params.limit || DEFAULT_LIMIT;
|
|
178
|
-
const nodes = [];
|
|
179
|
-
// Get file node
|
|
180
|
-
let fileNode = null;
|
|
181
|
-
if (entry.fileId) {
|
|
182
|
-
const file = graph.nodeById.get(entry.fileId);
|
|
183
|
-
if (file) {
|
|
184
|
-
fileNode = (0, graph_cache_1.toNodeDescriptor)(file);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
// Collect all definitions
|
|
188
|
-
const allIds = [...entry.classIds, ...entry.functionIds, ...entry.typeIds];
|
|
189
|
-
for (const id of allIds.slice(0, limit)) {
|
|
190
|
-
const node = graph.nodeById.get(id);
|
|
191
|
-
if (node) {
|
|
192
|
-
nodes.push((0, graph_cache_1.toNodeDescriptor)(node));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
const hasMore = allIds.length > limit;
|
|
196
|
-
return (0, types_1.createResponse)('definitions_in_file', graph.cacheKey, source, graph.cachedAt, { file: fileNode, definitions: { nodes } }, {
|
|
197
|
-
page: { limit, hasMore },
|
|
198
|
-
warnings: hasMore ? [`${allIds.length - limit} more definitions not shown.`] : undefined,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* file_imports - Get imports for a file (both outgoing and incoming)
|
|
203
|
-
* v1.1 query
|
|
204
|
-
*/
|
|
205
|
-
function fileImports(params, graph, source) {
|
|
206
|
-
if (!params.targetId) {
|
|
207
|
-
console.error('[ERROR] file_imports called without targetId');
|
|
208
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
|
|
209
|
-
detail: 'Use search or list_nodes with labels=["File"] to find file nodes',
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
const targetNode = graph.nodeById.get(params.targetId);
|
|
213
|
-
if (!targetNode) {
|
|
214
|
-
console.error('[ERROR] Node not found:', params.targetId);
|
|
215
|
-
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
216
|
-
detail: 'Use search or list_nodes to discover valid node IDs',
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
const label = targetNode.labels?.[0];
|
|
220
|
-
if (label !== 'File' && label !== 'LocalModule' && label !== 'ExternalModule') {
|
|
221
|
-
console.error('[ERROR] Node is not a File/Module:', params.targetId, 'is', label);
|
|
222
|
-
return (0, types_1.createError)('INVALID_PARAMS', `Node is not a File/Module (found ${label})`, {
|
|
223
|
-
detail: 'This query requires a File, LocalModule, or ExternalModule node',
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
const limit = params.limit || DEFAULT_LIMIT;
|
|
227
|
-
const outIds = new Set();
|
|
228
|
-
const addOutgoingImports = (nodeId) => {
|
|
229
|
-
const adj = graph.importAdj.get(nodeId);
|
|
230
|
-
if (!adj)
|
|
231
|
-
return;
|
|
232
|
-
for (const outId of adj.out) {
|
|
233
|
-
outIds.add(outId);
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
// Always include imports directly attached to the target node.
|
|
237
|
-
addOutgoingImports(params.targetId);
|
|
238
|
-
// If the target is a File node, also aggregate IMPORTS edges attached to
|
|
239
|
-
// definitions within the file. This handles graphs where IMPORTS edges
|
|
240
|
-
// are modeled as Function -> Module rather than File -> Module.
|
|
241
|
-
if (label === 'File') {
|
|
242
|
-
const filePathRaw = targetNode.properties?.filePath ||
|
|
243
|
-
targetNode.properties?.path ||
|
|
244
|
-
'';
|
|
245
|
-
const filePath = (0, graph_cache_1.normalizePath)(filePathRaw);
|
|
246
|
-
const entry = graph.pathIndex.get(filePath);
|
|
247
|
-
if (entry) {
|
|
248
|
-
for (const id of entry.functionIds)
|
|
249
|
-
addOutgoingImports(id);
|
|
250
|
-
for (const id of entry.classIds)
|
|
251
|
-
addOutgoingImports(id);
|
|
252
|
-
for (const id of entry.typeIds)
|
|
253
|
-
addOutgoingImports(id);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
const imports = [];
|
|
257
|
-
for (const id of outIds) {
|
|
258
|
-
if (imports.length >= limit)
|
|
259
|
-
break;
|
|
260
|
-
const node = graph.nodeById.get(id);
|
|
261
|
-
if (node) {
|
|
262
|
-
imports.push((0, graph_cache_1.toNodeDescriptor)(node));
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
const hasMore = outIds.size > limit;
|
|
266
|
-
return (0, types_1.createResponse)('file_imports', graph.cacheKey, source, graph.cachedAt, { imports: { nodes: imports } }, {
|
|
267
|
-
page: { limit, hasMore },
|
|
268
|
-
warnings: label === 'File' && outIds.size === 0
|
|
269
|
-
? [
|
|
270
|
-
'No IMPORTS edges found directly on the File node or on its contained definitions. This may reflect graph modeling choices for this repository.',
|
|
271
|
-
]
|
|
272
|
-
: undefined,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* domain_map - List all domains with their relationships
|
|
277
|
-
* v1.1 query
|
|
278
|
-
*/
|
|
279
|
-
function domainMap(params, graph, source) {
|
|
280
|
-
const domains = [];
|
|
281
|
-
const relationships = [];
|
|
282
|
-
// Get domain nodes
|
|
283
|
-
const domainIds = graph.labelIndex.get('Domain') || [];
|
|
284
|
-
for (const id of domainIds) {
|
|
285
|
-
const node = graph.nodeById.get(id);
|
|
286
|
-
if (node) {
|
|
287
|
-
const name = node.properties?.name;
|
|
288
|
-
const description = node.properties?.description;
|
|
289
|
-
const domainEntry = graph.domainIndex.get(name);
|
|
290
|
-
domains.push({
|
|
291
|
-
name,
|
|
292
|
-
description,
|
|
293
|
-
memberCount: domainEntry?.memberIds.length || 0,
|
|
294
|
-
});
|
|
295
|
-
// Collect relationships
|
|
296
|
-
if (domainEntry) {
|
|
297
|
-
for (const rel of domainEntry.relationships) {
|
|
298
|
-
const targetNode = graph.nodeById.get(rel.endNode);
|
|
299
|
-
if (targetNode) {
|
|
300
|
-
relationships.push({
|
|
301
|
-
from: name,
|
|
302
|
-
to: targetNode.properties?.name,
|
|
303
|
-
type: rel.type,
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return (0, types_1.createResponse)('domain_map', graph.cacheKey, source, graph.cachedAt, { domains, relationships });
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* domain_membership - Get members of a domain
|
|
314
|
-
* v1.1 query
|
|
315
|
-
*/
|
|
316
|
-
function domainMembership(params, graph, source) {
|
|
317
|
-
if (!params.searchText && !params.targetId) {
|
|
318
|
-
console.error('[ERROR] domain_membership called without searchText or targetId');
|
|
319
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: searchText or targetId', {
|
|
320
|
-
detail: 'Provide either a domain name (searchText) or domain node ID (targetId)',
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
let domainName;
|
|
324
|
-
if (params.targetId) {
|
|
325
|
-
const node = graph.nodeById.get(params.targetId);
|
|
326
|
-
if (!node) {
|
|
327
|
-
console.error('[ERROR] Node not found:', params.targetId);
|
|
328
|
-
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
329
|
-
detail: 'Use domain_map to discover domain nodes',
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
domainName = node.properties?.name;
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
domainName = params.searchText;
|
|
336
|
-
}
|
|
337
|
-
const domainEntry = graph.domainIndex.get(domainName);
|
|
338
|
-
if (!domainEntry) {
|
|
339
|
-
console.error('[ERROR] Domain not found:', domainName);
|
|
340
|
-
return (0, types_1.createError)('NOT_FOUND', `Domain not found: ${domainName}`, {
|
|
341
|
-
detail: 'Use domain_map to list available domains',
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
const limit = params.limit || DEFAULT_LIMIT;
|
|
345
|
-
const members = [];
|
|
346
|
-
for (const id of domainEntry.memberIds.slice(0, limit)) {
|
|
347
|
-
const node = graph.nodeById.get(id);
|
|
348
|
-
if (node)
|
|
349
|
-
members.push((0, graph_cache_1.toNodeDescriptor)(node));
|
|
350
|
-
}
|
|
351
|
-
const hasMore = domainEntry.memberIds.length > limit;
|
|
352
|
-
return (0, types_1.createResponse)('domain_membership', graph.cacheKey, source, graph.cachedAt, { domain: domainName, members: { nodes: members } }, {
|
|
353
|
-
page: { limit, hasMore },
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* neighborhood - Get ego graph around a node
|
|
358
|
-
* v1.1 query
|
|
359
|
-
*/
|
|
360
|
-
function neighborhood(params, graph, source) {
|
|
361
|
-
if (!params.targetId) {
|
|
362
|
-
console.error('[ERROR] neighborhood called without targetId');
|
|
363
|
-
return (0, types_1.createError)('INVALID_PARAMS', 'Missing required parameter: targetId', {
|
|
364
|
-
detail: 'Use search or list_nodes to find a node, then explore its neighborhood',
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
const targetNode = graph.nodeById.get(params.targetId);
|
|
368
|
-
if (!targetNode) {
|
|
369
|
-
console.error('[ERROR] Node not found:', params.targetId);
|
|
370
|
-
return (0, types_1.createError)('NOT_FOUND', `Node not found: ${params.targetId}`, {
|
|
371
|
-
detail: 'Use search or list_nodes to discover valid node IDs',
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
const depth = Math.min(params.depth || 1, constants_1.MAX_NEIGHBORHOOD_DEPTH);
|
|
375
|
-
const limit = params.limit || 100;
|
|
376
|
-
// Only include relationship types we actually implement
|
|
377
|
-
const relationshipTypes = params.relationshipTypes || ['calls', 'IMPORTS'];
|
|
378
|
-
const visited = new Set();
|
|
379
|
-
const nodes = [];
|
|
380
|
-
const edges = [];
|
|
381
|
-
// BFS
|
|
382
|
-
let frontier = [params.targetId];
|
|
383
|
-
visited.add(params.targetId);
|
|
384
|
-
nodes.push((0, graph_cache_1.toNodeDescriptor)(targetNode));
|
|
385
|
-
for (let d = 0; d < depth && nodes.length < limit; d++) {
|
|
386
|
-
const nextFrontier = [];
|
|
387
|
-
for (const nodeId of frontier) {
|
|
388
|
-
if (nodes.length >= limit)
|
|
389
|
-
break;
|
|
390
|
-
// Get adjacent nodes via call graph
|
|
391
|
-
if (relationshipTypes.includes('calls')) {
|
|
392
|
-
const callAdj = graph.callAdj.get(nodeId);
|
|
393
|
-
if (callAdj) {
|
|
394
|
-
for (const adjId of [...callAdj.out, ...callAdj.in]) {
|
|
395
|
-
if (!visited.has(adjId) && nodes.length < limit) {
|
|
396
|
-
visited.add(adjId);
|
|
397
|
-
const adjNode = graph.nodeById.get(adjId);
|
|
398
|
-
if (adjNode) {
|
|
399
|
-
nodes.push((0, graph_cache_1.toNodeDescriptor)(adjNode));
|
|
400
|
-
nextFrontier.push(adjId);
|
|
401
|
-
// Add edge
|
|
402
|
-
if (callAdj.out.includes(adjId)) {
|
|
403
|
-
edges.push({ type: 'calls', from: nodeId, to: adjId });
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
edges.push({ type: 'calls', from: adjId, to: nodeId });
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// Get adjacent nodes via import graph
|
|
414
|
-
if (relationshipTypes.includes('IMPORTS')) {
|
|
415
|
-
const importAdj = graph.importAdj.get(nodeId);
|
|
416
|
-
if (importAdj) {
|
|
417
|
-
for (const adjId of [...importAdj.out, ...importAdj.in]) {
|
|
418
|
-
if (!visited.has(adjId) && nodes.length < limit) {
|
|
419
|
-
visited.add(adjId);
|
|
420
|
-
const adjNode = graph.nodeById.get(adjId);
|
|
421
|
-
if (adjNode) {
|
|
422
|
-
nodes.push((0, graph_cache_1.toNodeDescriptor)(adjNode));
|
|
423
|
-
nextFrontier.push(adjId);
|
|
424
|
-
if (importAdj.out.includes(adjId)) {
|
|
425
|
-
edges.push({ type: 'IMPORTS', from: nodeId, to: adjId });
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
edges.push({ type: 'IMPORTS', from: adjId, to: nodeId });
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
frontier = nextFrontier;
|
|
437
|
-
if (frontier.length === 0)
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
const hasMore = nodes.length >= limit;
|
|
441
|
-
return (0, types_1.createResponse)('neighborhood', graph.cacheKey, source, graph.cachedAt, { nodes, edges }, {
|
|
442
|
-
page: { limit, hasMore },
|
|
443
|
-
warnings: hasMore ? [`Neighborhood truncated at ${limit} nodes. Decrease depth or increase limit.`] : undefined,
|
|
444
|
-
});
|
|
445
|
-
}
|
package/dist/queries/types.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Query types and result shapes for the query engine
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isQueryError = isQueryError;
|
|
7
|
-
exports.createError = createError;
|
|
8
|
-
exports.createResponse = createResponse;
|
|
9
|
-
// Type guard for error responses
|
|
10
|
-
function isQueryError(result) {
|
|
11
|
-
return (typeof result === 'object' &&
|
|
12
|
-
result !== null &&
|
|
13
|
-
'error' in result &&
|
|
14
|
-
typeof result.error === 'object');
|
|
15
|
-
}
|
|
16
|
-
// Create error response
|
|
17
|
-
function createError(code, message, options) {
|
|
18
|
-
return {
|
|
19
|
-
error: {
|
|
20
|
-
code,
|
|
21
|
-
message,
|
|
22
|
-
retryable: options?.retryable,
|
|
23
|
-
detail: options?.detail,
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
// Create success response
|
|
28
|
-
function createResponse(query, cacheKey, source, cachedAt, result, options) {
|
|
29
|
-
return {
|
|
30
|
-
query,
|
|
31
|
-
cacheKey,
|
|
32
|
-
source,
|
|
33
|
-
cachedAt,
|
|
34
|
-
result,
|
|
35
|
-
page: options?.page,
|
|
36
|
-
warnings: options?.warnings,
|
|
37
|
-
};
|
|
38
|
-
}
|