@grafema/api 0.2.5-beta
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/LICENSE +190 -0
- package/README.md +219 -0
- package/dist/context.d.ts +22 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +18 -0
- package/dist/context.js.map +1 -0
- package/dist/dataloaders/index.d.ts +18 -0
- package/dist/dataloaders/index.d.ts.map +1 -0
- package/dist/dataloaders/index.js +17 -0
- package/dist/dataloaders/index.js.map +1 -0
- package/dist/dataloaders/nodeLoader.d.ts +19 -0
- package/dist/dataloaders/nodeLoader.d.ts.map +1 -0
- package/dist/dataloaders/nodeLoader.js +31 -0
- package/dist/dataloaders/nodeLoader.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/pagination.d.ts +50 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +71 -0
- package/dist/pagination.js.map +1 -0
- package/dist/resolvers/edge.d.ts +22 -0
- package/dist/resolvers/edge.d.ts.map +1 -0
- package/dist/resolvers/edge.js +36 -0
- package/dist/resolvers/edge.js.map +1 -0
- package/dist/resolvers/index.d.ts +159 -0
- package/dist/resolvers/index.d.ts.map +1 -0
- package/dist/resolvers/index.js +21 -0
- package/dist/resolvers/index.js.map +1 -0
- package/dist/resolvers/mutation.d.ts +69 -0
- package/dist/resolvers/mutation.d.ts.map +1 -0
- package/dist/resolvers/mutation.js +82 -0
- package/dist/resolvers/mutation.js.map +1 -0
- package/dist/resolvers/node.d.ts +50 -0
- package/dist/resolvers/node.d.ts.map +1 -0
- package/dist/resolvers/node.js +69 -0
- package/dist/resolvers/node.js.map +1 -0
- package/dist/resolvers/query.d.ts +169 -0
- package/dist/resolvers/query.d.ts.map +1 -0
- package/dist/resolvers/query.js +188 -0
- package/dist/resolvers/query.js.map +1 -0
- package/dist/schema/enums.graphql +27 -0
- package/dist/schema/mutations.graphql +53 -0
- package/dist/schema/queries.graphql +213 -0
- package/dist/schema/scalars.graphql +2 -0
- package/dist/schema/subscriptions.graphql +84 -0
- package/dist/schema/types.graphql +440 -0
- package/dist/server.d.ts +31 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +109 -0
- package/dist/server.js.map +1 -0
- package/package.json +51 -0
- package/src/context.ts +33 -0
- package/src/dataloaders/index.ts +24 -0
- package/src/dataloaders/nodeLoader.ts +41 -0
- package/src/index.ts +11 -0
- package/src/pagination.ts +109 -0
- package/src/resolvers/edge.ts +39 -0
- package/src/resolvers/index.ts +24 -0
- package/src/resolvers/mutation.ts +108 -0
- package/src/resolvers/node.ts +118 -0
- package/src/resolvers/query.ts +307 -0
- package/src/schema/enums.graphql +27 -0
- package/src/schema/mutations.graphql +53 -0
- package/src/schema/queries.graphql +213 -0
- package/src/schema/scalars.graphql +2 -0
- package/src/schema/subscriptions.graphql +84 -0
- package/src/schema/types.graphql +440 -0
- package/src/server.ts +140 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Resolvers
|
|
3
|
+
*
|
|
4
|
+
* Implements all Query type fields.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { BaseNodeRecord } from '@grafema/types';
|
|
8
|
+
import type { GraphQLContext } from '../context.js';
|
|
9
|
+
import { paginateArray } from '../pagination.js';
|
|
10
|
+
|
|
11
|
+
export interface NodeFilter {
|
|
12
|
+
type?: string | null;
|
|
13
|
+
name?: string | null;
|
|
14
|
+
file?: string | null;
|
|
15
|
+
exported?: boolean | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const queryResolvers = {
|
|
19
|
+
/**
|
|
20
|
+
* Get node by ID.
|
|
21
|
+
*
|
|
22
|
+
* Complexity: O(1)
|
|
23
|
+
*/
|
|
24
|
+
async node(
|
|
25
|
+
_: unknown,
|
|
26
|
+
args: { id: string },
|
|
27
|
+
context: GraphQLContext
|
|
28
|
+
) {
|
|
29
|
+
return context.loaders.node.load(args.id);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Find nodes matching criteria with cursor-based pagination.
|
|
34
|
+
*
|
|
35
|
+
* Complexity: O(n) where n = nodes matching type filter
|
|
36
|
+
* This is acceptable because:
|
|
37
|
+
* - We filter by type first (uses RFDB's type index)
|
|
38
|
+
* - Results are paginated
|
|
39
|
+
*/
|
|
40
|
+
async nodes(
|
|
41
|
+
_: unknown,
|
|
42
|
+
args: {
|
|
43
|
+
filter?: NodeFilter | null;
|
|
44
|
+
first?: number | null;
|
|
45
|
+
after?: string | null;
|
|
46
|
+
},
|
|
47
|
+
context: GraphQLContext
|
|
48
|
+
) {
|
|
49
|
+
const filter = args.filter || {};
|
|
50
|
+
|
|
51
|
+
// Build query for backend
|
|
52
|
+
const query: Record<string, unknown> = {};
|
|
53
|
+
if (filter.type) query.type = filter.type;
|
|
54
|
+
if (filter.name) query.name = filter.name;
|
|
55
|
+
if (filter.file) query.file = filter.file;
|
|
56
|
+
|
|
57
|
+
// Get all matching nodes
|
|
58
|
+
const nodes = await context.backend.getAllNodes(query);
|
|
59
|
+
|
|
60
|
+
// Apply exported filter (not supported by backend query)
|
|
61
|
+
let filteredNodes = nodes;
|
|
62
|
+
if (filter.exported !== null && filter.exported !== undefined) {
|
|
63
|
+
filteredNodes = nodes.filter((n) => n.exported === filter.exported);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return paginateArray(
|
|
67
|
+
filteredNodes,
|
|
68
|
+
args.first,
|
|
69
|
+
args.after,
|
|
70
|
+
(n: BaseNodeRecord) => n.id
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* BFS traversal.
|
|
76
|
+
*
|
|
77
|
+
* Complexity: O(V + E) for reachable subgraph
|
|
78
|
+
* Bounded by maxDepth parameter.
|
|
79
|
+
*/
|
|
80
|
+
async bfs(
|
|
81
|
+
_: unknown,
|
|
82
|
+
args: { startIds: string[]; maxDepth: number; edgeTypes: string[] },
|
|
83
|
+
context: GraphQLContext
|
|
84
|
+
) {
|
|
85
|
+
return context.backend.bfs(args.startIds, args.maxDepth, args.edgeTypes);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* DFS traversal.
|
|
90
|
+
*
|
|
91
|
+
* Complexity: O(V + E) for reachable subgraph
|
|
92
|
+
*/
|
|
93
|
+
async dfs(
|
|
94
|
+
_: unknown,
|
|
95
|
+
args: { startIds: string[]; maxDepth: number; edgeTypes?: string[] | null },
|
|
96
|
+
context: GraphQLContext
|
|
97
|
+
) {
|
|
98
|
+
return context.backend.dfs(
|
|
99
|
+
args.startIds,
|
|
100
|
+
args.maxDepth,
|
|
101
|
+
args.edgeTypes || []
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Reachability check.
|
|
107
|
+
*
|
|
108
|
+
* Complexity: O(V + E) worst case, often O(d) with early termination
|
|
109
|
+
*/
|
|
110
|
+
async reachability(
|
|
111
|
+
_: unknown,
|
|
112
|
+
args: {
|
|
113
|
+
from: string;
|
|
114
|
+
to: string;
|
|
115
|
+
edgeTypes?: string[] | null;
|
|
116
|
+
maxDepth?: number | null;
|
|
117
|
+
},
|
|
118
|
+
context: GraphQLContext
|
|
119
|
+
) {
|
|
120
|
+
const maxDepth = args.maxDepth ?? 10;
|
|
121
|
+
const reachable = await context.backend.bfs(
|
|
122
|
+
[args.from],
|
|
123
|
+
maxDepth,
|
|
124
|
+
args.edgeTypes || []
|
|
125
|
+
);
|
|
126
|
+
return reachable.includes(args.to);
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Execute Datalog query.
|
|
131
|
+
*
|
|
132
|
+
* Complexity: Depends on query, bounded by RFDB's timeout
|
|
133
|
+
*/
|
|
134
|
+
async datalog(
|
|
135
|
+
_: unknown,
|
|
136
|
+
args: { query: string; limit?: number | null; offset?: number | null },
|
|
137
|
+
context: GraphQLContext
|
|
138
|
+
) {
|
|
139
|
+
const limit = args.limit ?? 50;
|
|
140
|
+
const offset = args.offset ?? 0;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const results = await context.backend.checkGuarantee(args.query);
|
|
144
|
+
const total = results.length;
|
|
145
|
+
const paginatedResults = results.slice(offset, offset + limit);
|
|
146
|
+
|
|
147
|
+
// Enrich with node data
|
|
148
|
+
const enrichedResults = await Promise.all(
|
|
149
|
+
paginatedResults.map(async (r) => {
|
|
150
|
+
const bindings = r.bindings || [];
|
|
151
|
+
const xBinding = bindings.find((b) => b.name === 'X');
|
|
152
|
+
const nodeId = xBinding?.value;
|
|
153
|
+
const node = nodeId
|
|
154
|
+
? await context.loaders.node.load(String(nodeId))
|
|
155
|
+
: null;
|
|
156
|
+
return {
|
|
157
|
+
bindings: Object.fromEntries(
|
|
158
|
+
bindings.map((b) => [b.name, b.value])
|
|
159
|
+
),
|
|
160
|
+
node,
|
|
161
|
+
};
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
success: true,
|
|
167
|
+
count: total,
|
|
168
|
+
results: enrichedResults,
|
|
169
|
+
error: null,
|
|
170
|
+
};
|
|
171
|
+
} catch (error) {
|
|
172
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
173
|
+
return {
|
|
174
|
+
success: false,
|
|
175
|
+
count: 0,
|
|
176
|
+
results: [],
|
|
177
|
+
error: message,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get graph statistics.
|
|
184
|
+
*
|
|
185
|
+
* Complexity: O(1) - cached in backend
|
|
186
|
+
*/
|
|
187
|
+
async stats(_: unknown, _args: unknown, context: GraphQLContext) {
|
|
188
|
+
return context.backend.getStats();
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get analysis status.
|
|
193
|
+
* Placeholder - actual implementation depends on analysis state tracking.
|
|
194
|
+
*/
|
|
195
|
+
async analysisStatus(_: unknown, _args: unknown, _context: GraphQLContext) {
|
|
196
|
+
// Placeholder - would need to track analysis state
|
|
197
|
+
return {
|
|
198
|
+
running: false,
|
|
199
|
+
phase: null,
|
|
200
|
+
message: null,
|
|
201
|
+
servicesDiscovered: 0,
|
|
202
|
+
servicesAnalyzed: 0,
|
|
203
|
+
error: null,
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* List all guarantees.
|
|
209
|
+
* Placeholder - actual implementation depends on GuaranteeManager.
|
|
210
|
+
*/
|
|
211
|
+
async guarantees(_: unknown, _args: unknown, _context: GraphQLContext) {
|
|
212
|
+
// Placeholder - would need GuaranteeManager integration
|
|
213
|
+
return [];
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get guarantee by ID.
|
|
218
|
+
* Placeholder.
|
|
219
|
+
*/
|
|
220
|
+
async guarantee(
|
|
221
|
+
_: unknown,
|
|
222
|
+
_args: { id: string },
|
|
223
|
+
_context: GraphQLContext
|
|
224
|
+
) {
|
|
225
|
+
return null;
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Find calls to a function/method.
|
|
230
|
+
* Placeholder - would reuse MCP handler logic.
|
|
231
|
+
*/
|
|
232
|
+
async findCalls(
|
|
233
|
+
_: unknown,
|
|
234
|
+
_args: {
|
|
235
|
+
target: string;
|
|
236
|
+
className?: string | null;
|
|
237
|
+
limit?: number | null;
|
|
238
|
+
offset?: number | null;
|
|
239
|
+
},
|
|
240
|
+
_context: GraphQLContext
|
|
241
|
+
) {
|
|
242
|
+
// Placeholder - would reuse MCP find_calls handler
|
|
243
|
+
return [];
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get function details.
|
|
248
|
+
* Placeholder - would reuse MCP handler logic.
|
|
249
|
+
*/
|
|
250
|
+
async getFunctionDetails(
|
|
251
|
+
_: unknown,
|
|
252
|
+
_args: {
|
|
253
|
+
name: string;
|
|
254
|
+
file?: string | null;
|
|
255
|
+
transitive?: boolean | null;
|
|
256
|
+
},
|
|
257
|
+
_context: GraphQLContext
|
|
258
|
+
) {
|
|
259
|
+
// Placeholder - would reuse MCP get_function_details handler
|
|
260
|
+
return null;
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Find guards protecting a node.
|
|
265
|
+
* Placeholder.
|
|
266
|
+
*/
|
|
267
|
+
async findGuards(
|
|
268
|
+
_: unknown,
|
|
269
|
+
_args: { nodeId: string },
|
|
270
|
+
_context: GraphQLContext
|
|
271
|
+
) {
|
|
272
|
+
return [];
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Trace alias chain.
|
|
277
|
+
* Placeholder.
|
|
278
|
+
*/
|
|
279
|
+
async traceAlias(
|
|
280
|
+
_: unknown,
|
|
281
|
+
_args: {
|
|
282
|
+
variableName: string;
|
|
283
|
+
file: string;
|
|
284
|
+
maxDepth?: number | null;
|
|
285
|
+
},
|
|
286
|
+
_context: GraphQLContext
|
|
287
|
+
) {
|
|
288
|
+
return [];
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Trace data flow.
|
|
293
|
+
* Placeholder.
|
|
294
|
+
*/
|
|
295
|
+
async traceDataFlow(
|
|
296
|
+
_: unknown,
|
|
297
|
+
_args: {
|
|
298
|
+
source: string;
|
|
299
|
+
file?: string | null;
|
|
300
|
+
direction?: string | null;
|
|
301
|
+
maxDepth?: number | null;
|
|
302
|
+
},
|
|
303
|
+
_context: GraphQLContext
|
|
304
|
+
) {
|
|
305
|
+
return [];
|
|
306
|
+
},
|
|
307
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Direction for graph traversal.
|
|
3
|
+
"""
|
|
4
|
+
enum TraversalDirection {
|
|
5
|
+
FORWARD
|
|
6
|
+
BACKWARD
|
|
7
|
+
BOTH
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
Severity levels for guarantees.
|
|
12
|
+
"""
|
|
13
|
+
enum Severity {
|
|
14
|
+
ERROR
|
|
15
|
+
WARNING
|
|
16
|
+
INFO
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
Priority levels for contract guarantees.
|
|
21
|
+
"""
|
|
22
|
+
enum Priority {
|
|
23
|
+
CRITICAL
|
|
24
|
+
IMPORTANT
|
|
25
|
+
OBSERVED
|
|
26
|
+
TRACKED
|
|
27
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type Mutation {
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# Analysis
|
|
4
|
+
# ============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Run project analysis.
|
|
8
|
+
Blocks until complete (with timeout).
|
|
9
|
+
"""
|
|
10
|
+
analyzeProject(
|
|
11
|
+
"""Optional: analyze only this service"""
|
|
12
|
+
service: String
|
|
13
|
+
|
|
14
|
+
"""Force re-analysis even if already analyzed"""
|
|
15
|
+
force: Boolean
|
|
16
|
+
): AnalysisResult!
|
|
17
|
+
|
|
18
|
+
# ============================================================================
|
|
19
|
+
# Guarantees
|
|
20
|
+
# ============================================================================
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
Create a new guarantee.
|
|
24
|
+
|
|
25
|
+
For Datalog-based: provide name + rule
|
|
26
|
+
For contract-based: provide name + type + priority
|
|
27
|
+
"""
|
|
28
|
+
createGuarantee(input: CreateGuaranteeInput!): Guarantee!
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
Delete a guarantee by name.
|
|
32
|
+
"""
|
|
33
|
+
deleteGuarantee(name: String!): Boolean!
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
Check all guarantees or specific ones.
|
|
37
|
+
"""
|
|
38
|
+
checkGuarantees(
|
|
39
|
+
"""Specific guarantee names to check (null = all)"""
|
|
40
|
+
names: [String!]
|
|
41
|
+
): GuaranteeCheckResult!
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
Check a single ad-hoc invariant without persisting.
|
|
45
|
+
"""
|
|
46
|
+
checkInvariant(
|
|
47
|
+
"""Datalog rule"""
|
|
48
|
+
rule: String!
|
|
49
|
+
|
|
50
|
+
"""Description for error messages"""
|
|
51
|
+
description: String
|
|
52
|
+
): GuaranteeResult!
|
|
53
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
type Query {
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# Node Lookups
|
|
4
|
+
# ============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Get a single node by ID.
|
|
8
|
+
Returns null if not found.
|
|
9
|
+
"""
|
|
10
|
+
node(id: ID!): Node
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
Find nodes matching filter criteria.
|
|
14
|
+
Uses cursor-based pagination (Relay spec).
|
|
15
|
+
"""
|
|
16
|
+
nodes(
|
|
17
|
+
"""Filter criteria"""
|
|
18
|
+
filter: NodeFilter
|
|
19
|
+
|
|
20
|
+
"""Number of items to return (default: 50, max: 250)"""
|
|
21
|
+
first: Int
|
|
22
|
+
|
|
23
|
+
"""Cursor to start after"""
|
|
24
|
+
after: String
|
|
25
|
+
): NodeConnection!
|
|
26
|
+
|
|
27
|
+
# ============================================================================
|
|
28
|
+
# Graph Traversal
|
|
29
|
+
# ============================================================================
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
BFS traversal from starting nodes.
|
|
33
|
+
Returns all node IDs reachable within maxDepth.
|
|
34
|
+
|
|
35
|
+
Complexity: O(V + E) where V = reachable nodes, E = traversed edges
|
|
36
|
+
"""
|
|
37
|
+
bfs(
|
|
38
|
+
"""Starting node IDs"""
|
|
39
|
+
startIds: [ID!]!
|
|
40
|
+
|
|
41
|
+
"""Maximum traversal depth"""
|
|
42
|
+
maxDepth: Int!
|
|
43
|
+
|
|
44
|
+
"""Edge types to traverse (empty = all)"""
|
|
45
|
+
edgeTypes: [String!]!
|
|
46
|
+
): [ID!]!
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
DFS traversal from starting nodes.
|
|
50
|
+
Returns all node IDs reachable within maxDepth.
|
|
51
|
+
|
|
52
|
+
Complexity: O(V + E) where V = reachable nodes, E = traversed edges
|
|
53
|
+
"""
|
|
54
|
+
dfs(
|
|
55
|
+
"""Starting node IDs"""
|
|
56
|
+
startIds: [ID!]!
|
|
57
|
+
|
|
58
|
+
"""Maximum traversal depth"""
|
|
59
|
+
maxDepth: Int!
|
|
60
|
+
|
|
61
|
+
"""Edge types to traverse (null = all)"""
|
|
62
|
+
edgeTypes: [String!]
|
|
63
|
+
): [ID!]!
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
Check if a path exists between two nodes.
|
|
67
|
+
Uses BFS internally with early termination.
|
|
68
|
+
|
|
69
|
+
Complexity: O(V + E) worst case, often much faster with early termination
|
|
70
|
+
"""
|
|
71
|
+
reachability(
|
|
72
|
+
"""Source node ID"""
|
|
73
|
+
from: ID!
|
|
74
|
+
|
|
75
|
+
"""Target node ID"""
|
|
76
|
+
to: ID!
|
|
77
|
+
|
|
78
|
+
"""Edge types to traverse (null = all)"""
|
|
79
|
+
edgeTypes: [String!]
|
|
80
|
+
|
|
81
|
+
"""Maximum depth to search (default: 10)"""
|
|
82
|
+
maxDepth: Int
|
|
83
|
+
): Boolean!
|
|
84
|
+
|
|
85
|
+
# ============================================================================
|
|
86
|
+
# Datalog Queries
|
|
87
|
+
# ============================================================================
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
Execute a Datalog query on the code graph.
|
|
91
|
+
|
|
92
|
+
The query must define a violation/1 predicate.
|
|
93
|
+
|
|
94
|
+
Available predicates:
|
|
95
|
+
- node(Id, Type) - match nodes by type
|
|
96
|
+
- edge(Src, Dst, Type) - match edges
|
|
97
|
+
- attr(Id, Name, Value) - match node attributes
|
|
98
|
+
|
|
99
|
+
Example: violation(X) :- node(X, "FUNCTION"), attr(X, "async", true).
|
|
100
|
+
"""
|
|
101
|
+
datalog(
|
|
102
|
+
"""Datalog query defining violation/1"""
|
|
103
|
+
query: String!
|
|
104
|
+
|
|
105
|
+
"""Maximum results (default: 50)"""
|
|
106
|
+
limit: Int
|
|
107
|
+
|
|
108
|
+
"""Results to skip (for simple pagination)"""
|
|
109
|
+
offset: Int
|
|
110
|
+
): DatalogResult!
|
|
111
|
+
|
|
112
|
+
# ============================================================================
|
|
113
|
+
# High-Level Queries (from MCP handlers)
|
|
114
|
+
# ============================================================================
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
Find all calls to a function/method.
|
|
118
|
+
"""
|
|
119
|
+
findCalls(
|
|
120
|
+
"""Function or method name"""
|
|
121
|
+
target: String!
|
|
122
|
+
|
|
123
|
+
"""Optional class name for method calls"""
|
|
124
|
+
className: String
|
|
125
|
+
|
|
126
|
+
"""Maximum results"""
|
|
127
|
+
limit: Int
|
|
128
|
+
|
|
129
|
+
"""Results to skip"""
|
|
130
|
+
offset: Int
|
|
131
|
+
): [CallInfo!]!
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
Get comprehensive function details including calls and callers.
|
|
135
|
+
"""
|
|
136
|
+
getFunctionDetails(
|
|
137
|
+
"""Function name"""
|
|
138
|
+
name: String!
|
|
139
|
+
|
|
140
|
+
"""File path for disambiguation"""
|
|
141
|
+
file: String
|
|
142
|
+
|
|
143
|
+
"""Follow transitive call chains"""
|
|
144
|
+
transitive: Boolean
|
|
145
|
+
): FunctionDetails
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
Find guards (conditional scopes) protecting a node.
|
|
149
|
+
"""
|
|
150
|
+
findGuards(
|
|
151
|
+
"""Node ID to find guards for"""
|
|
152
|
+
nodeId: ID!
|
|
153
|
+
): [GuardInfo!]!
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
Trace variable alias chain to original source.
|
|
157
|
+
"""
|
|
158
|
+
traceAlias(
|
|
159
|
+
"""Variable name"""
|
|
160
|
+
variableName: String!
|
|
161
|
+
|
|
162
|
+
"""File where variable is defined"""
|
|
163
|
+
file: String!
|
|
164
|
+
|
|
165
|
+
"""Maximum trace depth (default: 20)"""
|
|
166
|
+
maxDepth: Int
|
|
167
|
+
): [Node!]!
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
Trace data flow from/to a node.
|
|
171
|
+
"""
|
|
172
|
+
traceDataFlow(
|
|
173
|
+
"""Source node ID or variable name"""
|
|
174
|
+
source: String!
|
|
175
|
+
|
|
176
|
+
"""File path"""
|
|
177
|
+
file: String
|
|
178
|
+
|
|
179
|
+
"""Direction of trace"""
|
|
180
|
+
direction: TraversalDirection
|
|
181
|
+
|
|
182
|
+
"""Maximum depth (default: 10)"""
|
|
183
|
+
maxDepth: Int
|
|
184
|
+
): [[String!]!]!
|
|
185
|
+
|
|
186
|
+
# ============================================================================
|
|
187
|
+
# Guarantees
|
|
188
|
+
# ============================================================================
|
|
189
|
+
|
|
190
|
+
"""
|
|
191
|
+
List all defined guarantees.
|
|
192
|
+
"""
|
|
193
|
+
guarantees: [Guarantee!]!
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
Get a specific guarantee by ID.
|
|
197
|
+
"""
|
|
198
|
+
guarantee(id: ID!): Guarantee
|
|
199
|
+
|
|
200
|
+
# ============================================================================
|
|
201
|
+
# Statistics
|
|
202
|
+
# ============================================================================
|
|
203
|
+
|
|
204
|
+
"""
|
|
205
|
+
Get graph statistics.
|
|
206
|
+
"""
|
|
207
|
+
stats: GraphStats!
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
Get current analysis status.
|
|
211
|
+
"""
|
|
212
|
+
analysisStatus: AnalysisStatus!
|
|
213
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
type Subscription {
|
|
2
|
+
"""
|
|
3
|
+
Stream nodes matching filter as they're found.
|
|
4
|
+
Useful for UI visualization of large datasets.
|
|
5
|
+
"""
|
|
6
|
+
nodesStream(
|
|
7
|
+
"""Filter criteria"""
|
|
8
|
+
filter: NodeFilter
|
|
9
|
+
|
|
10
|
+
"""Batch size for streaming (default: 100)"""
|
|
11
|
+
batchSize: Int
|
|
12
|
+
): NodeBatch!
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
Stream BFS traversal results level by level.
|
|
16
|
+
Each batch contains all nodes at one depth level.
|
|
17
|
+
"""
|
|
18
|
+
bfsStream(
|
|
19
|
+
"""Starting node IDs"""
|
|
20
|
+
startIds: [ID!]!
|
|
21
|
+
|
|
22
|
+
"""Maximum traversal depth"""
|
|
23
|
+
maxDepth: Int!
|
|
24
|
+
|
|
25
|
+
"""Edge types to traverse"""
|
|
26
|
+
edgeTypes: [String!]!
|
|
27
|
+
): TraversalBatch!
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
Stream analysis progress events.
|
|
31
|
+
"""
|
|
32
|
+
analysisProgress(
|
|
33
|
+
"""Optional: filter to specific service"""
|
|
34
|
+
service: String
|
|
35
|
+
): AnalysisEvent!
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
Batch of nodes for streaming responses.
|
|
40
|
+
"""
|
|
41
|
+
type NodeBatch {
|
|
42
|
+
"""Nodes in this batch"""
|
|
43
|
+
nodes: [Node!]!
|
|
44
|
+
|
|
45
|
+
"""Progress from 0.0 to 1.0"""
|
|
46
|
+
progress: Float!
|
|
47
|
+
|
|
48
|
+
"""Whether streaming is complete"""
|
|
49
|
+
done: Boolean!
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
Batch of traversal results at a specific depth.
|
|
54
|
+
"""
|
|
55
|
+
type TraversalBatch {
|
|
56
|
+
"""Current depth level"""
|
|
57
|
+
depth: Int!
|
|
58
|
+
|
|
59
|
+
"""Node IDs at this depth"""
|
|
60
|
+
nodeIds: [ID!]!
|
|
61
|
+
|
|
62
|
+
"""Whether traversal is complete"""
|
|
63
|
+
done: Boolean!
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
Analysis progress event.
|
|
68
|
+
"""
|
|
69
|
+
type AnalysisEvent {
|
|
70
|
+
"""Current phase name"""
|
|
71
|
+
phase: String!
|
|
72
|
+
|
|
73
|
+
"""Human-readable message"""
|
|
74
|
+
message: String!
|
|
75
|
+
|
|
76
|
+
"""Progress from 0.0 to 1.0"""
|
|
77
|
+
progress: Float!
|
|
78
|
+
|
|
79
|
+
"""Number of services completed"""
|
|
80
|
+
servicesCompleted: Int!
|
|
81
|
+
|
|
82
|
+
"""Total number of services"""
|
|
83
|
+
servicesTotal: Int!
|
|
84
|
+
}
|