sverklo 0.3.0 → 0.4.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/bin/sverklo.js +38 -5
- package/dist/bin/sverklo.js.map +1 -1
- package/dist/src/server/tools/impact.d.ts +4 -0
- package/dist/src/server/tools/impact.js +77 -1
- package/dist/src/server/tools/impact.js.map +1 -1
- package/dist/src/workspace/cli.d.ts +17 -0
- package/dist/src/workspace/cli.js +205 -0
- package/dist/src/workspace/cli.js.map +1 -0
- package/dist/src/workspace/cross-db.d.ts +49 -0
- package/dist/src/workspace/cross-db.js +192 -0
- package/dist/src/workspace/cross-db.js.map +1 -0
- package/dist/src/workspace/cross-indexer.d.ts +16 -0
- package/dist/src/workspace/cross-indexer.js +288 -0
- package/dist/src/workspace/cross-indexer.js.map +1 -0
- package/dist/src/workspace/graphql-consumer.d.ts +13 -0
- package/dist/src/workspace/graphql-consumer.js +293 -0
- package/dist/src/workspace/graphql-consumer.js.map +1 -0
- package/dist/src/workspace/graphql-extractor.d.ts +13 -0
- package/dist/src/workspace/graphql-extractor.js +172 -0
- package/dist/src/workspace/graphql-extractor.js.map +1 -0
- package/dist/src/workspace/workspace-config.d.ts +32 -0
- package/dist/src/workspace/workspace-config.js +91 -0
- package/dist/src/workspace/workspace-config.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Scan a TypeScript/JavaScript file for GraphQL operations (gql tagged templates,
|
|
5
|
+
* graphql() calls, .graphql imports) and resolve field references against known contracts.
|
|
6
|
+
*/
|
|
7
|
+
export function detectGraphQLConsumers(filePath, content, knownContracts) {
|
|
8
|
+
const matches = [];
|
|
9
|
+
// Build lookup maps from known contracts
|
|
10
|
+
const endpointContracts = new Map();
|
|
11
|
+
const fieldContracts = new Set();
|
|
12
|
+
const typeReturnMap = new Map(); // "Query.user" -> "User"
|
|
13
|
+
for (const c of knownContracts) {
|
|
14
|
+
if (c.symbolKind === "endpoint") {
|
|
15
|
+
endpointContracts.set(c.symbolName, c);
|
|
16
|
+
// Extract return type from signature, e.g. "user(id: ID!): User" -> "User"
|
|
17
|
+
const returnType = extractReturnType(c.signature ?? "");
|
|
18
|
+
if (returnType) {
|
|
19
|
+
typeReturnMap.set(c.symbolName, returnType);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (c.symbolKind === "field" || c.symbolKind === "endpoint") {
|
|
23
|
+
fieldContracts.add(c.symbolName);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Collect all GraphQL operation strings with their source locations
|
|
27
|
+
const operations = extractOperations(filePath, content);
|
|
28
|
+
for (const op of operations) {
|
|
29
|
+
const parsed = parseGraphQLOperation(op.graphql);
|
|
30
|
+
if (!parsed)
|
|
31
|
+
continue;
|
|
32
|
+
const referencedFields = [];
|
|
33
|
+
// Resolve root fields against endpoints
|
|
34
|
+
for (const rootField of parsed.rootFields) {
|
|
35
|
+
const endpointName = `${capitalize(parsed.operationType)}.${rootField.name}`;
|
|
36
|
+
const returnType = typeReturnMap.get(endpointName);
|
|
37
|
+
if (endpointContracts.has(endpointName)) {
|
|
38
|
+
referencedFields.push(endpointName);
|
|
39
|
+
}
|
|
40
|
+
// Resolve nested selections
|
|
41
|
+
if (rootField.selections.length > 0) {
|
|
42
|
+
const parentType = returnType ?? capitalize(rootField.name);
|
|
43
|
+
resolveSelections(rootField.selections, parentType, fieldContracts, typeReturnMap, referencedFields, returnType != null);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (referencedFields.length > 0) {
|
|
47
|
+
matches.push({
|
|
48
|
+
file: filePath,
|
|
49
|
+
symbol: op.enclosingSymbol,
|
|
50
|
+
line: op.line,
|
|
51
|
+
referencedFields: [...new Set(referencedFields)],
|
|
52
|
+
edgeType: parsed.operationType,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return matches;
|
|
57
|
+
}
|
|
58
|
+
function extractOperations(filePath, content) {
|
|
59
|
+
const ops = [];
|
|
60
|
+
// Pattern 1: gql`...` or graphql`...` tagged template literals
|
|
61
|
+
const taggedTemplateRe = /\b(?:gql|graphql)\s*`([\s\S]*?)`/g;
|
|
62
|
+
let m;
|
|
63
|
+
while ((m = taggedTemplateRe.exec(content)) !== null) {
|
|
64
|
+
const line = lineAt(content, m.index);
|
|
65
|
+
ops.push({
|
|
66
|
+
graphql: m[1],
|
|
67
|
+
line,
|
|
68
|
+
enclosingSymbol: findEnclosingSymbol(content, m.index),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Pattern 2: graphql("...") or graphql('...') function call with a string literal
|
|
72
|
+
const graphqlCallRe = /\bgraphql\s*\(\s*(['"`])([\s\S]*?)\1\s*\)/g;
|
|
73
|
+
while ((m = graphqlCallRe.exec(content)) !== null) {
|
|
74
|
+
const line = lineAt(content, m.index);
|
|
75
|
+
ops.push({
|
|
76
|
+
graphql: m[2],
|
|
77
|
+
line,
|
|
78
|
+
enclosingSymbol: findEnclosingSymbol(content, m.index),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Pattern 3: import from .graphql file
|
|
82
|
+
const graphqlImportRe = /import\s+.*?\s+from\s+['"]([^'"]+\.graphql)['"]/g;
|
|
83
|
+
while ((m = graphqlImportRe.exec(content)) !== null) {
|
|
84
|
+
const importPath = m[1];
|
|
85
|
+
const resolved = resolve(dirname(filePath), importPath);
|
|
86
|
+
try {
|
|
87
|
+
const graphqlContent = readFileSync(resolved, "utf-8");
|
|
88
|
+
const line = lineAt(content, m.index);
|
|
89
|
+
ops.push({
|
|
90
|
+
graphql: graphqlContent,
|
|
91
|
+
line,
|
|
92
|
+
enclosingSymbol: findEnclosingSymbol(content, m.index),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// File not found — skip silently
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return ops;
|
|
100
|
+
}
|
|
101
|
+
function parseGraphQLOperation(graphql) {
|
|
102
|
+
// Strip comments
|
|
103
|
+
const cleaned = graphql.replace(/#[^\n]*/g, "");
|
|
104
|
+
// Find operation keyword
|
|
105
|
+
const opMatch = cleaned.match(/\b(query|mutation|subscription)\b\s*([A-Za-z_]\w*)?\s*(?:\([^)]*\))?\s*\{/);
|
|
106
|
+
// If no explicit operation keyword, assume it's a query if we see { at top level
|
|
107
|
+
let operationType = "query";
|
|
108
|
+
let operationName = null;
|
|
109
|
+
let bodyStart;
|
|
110
|
+
if (opMatch) {
|
|
111
|
+
operationType = opMatch[1];
|
|
112
|
+
operationName = opMatch[2] ?? null;
|
|
113
|
+
bodyStart = opMatch.index + opMatch[0].length - 1; // position of {
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const braceIdx = cleaned.indexOf("{");
|
|
117
|
+
if (braceIdx === -1)
|
|
118
|
+
return null;
|
|
119
|
+
bodyStart = braceIdx;
|
|
120
|
+
}
|
|
121
|
+
const rootFields = parseSelectionSet(cleaned, bodyStart);
|
|
122
|
+
if (rootFields.length === 0)
|
|
123
|
+
return null;
|
|
124
|
+
return { operationType, operationName, rootFields };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Parse a selection set starting at the opening brace.
|
|
128
|
+
* Returns the field selections inside.
|
|
129
|
+
*/
|
|
130
|
+
function parseSelectionSet(src, bracePos) {
|
|
131
|
+
const fields = [];
|
|
132
|
+
// Find the matching closing brace
|
|
133
|
+
let depth = 0;
|
|
134
|
+
let i = bracePos;
|
|
135
|
+
if (src[i] !== "{")
|
|
136
|
+
return fields;
|
|
137
|
+
depth = 1;
|
|
138
|
+
i++;
|
|
139
|
+
while (i < src.length && depth > 0) {
|
|
140
|
+
const ch = src[i];
|
|
141
|
+
if (ch === "{") {
|
|
142
|
+
depth++;
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
else if (ch === "}") {
|
|
146
|
+
depth--;
|
|
147
|
+
i++;
|
|
148
|
+
}
|
|
149
|
+
else if (depth === 1) {
|
|
150
|
+
// At the top-level of this selection set — look for a field name
|
|
151
|
+
const fieldMatch = src.slice(i).match(/^(\s*(?:\.\.\.\s+on\s+\w+\s*\{[\s\S]*?\}|\.\.\.\w+)\s*)/);
|
|
152
|
+
if (fieldMatch) {
|
|
153
|
+
// Fragment spread or inline fragment — skip for now
|
|
154
|
+
i += fieldMatch[1].length;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const nameMatch = src.slice(i).match(/^\s*([A-Za-z_]\w*)\s*(?:\([^)]*\))?\s*/);
|
|
158
|
+
if (nameMatch) {
|
|
159
|
+
const fieldName = nameMatch[1];
|
|
160
|
+
// Skip GraphQL keywords that aren't field names
|
|
161
|
+
if (fieldName === "on" || fieldName === "fragment") {
|
|
162
|
+
i += nameMatch[0].length;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
// Check for alias: `alias: fieldName`
|
|
166
|
+
const afterName = i + nameMatch[0].length;
|
|
167
|
+
let actualName = fieldName;
|
|
168
|
+
// Handle alias pattern: aliasName: actualFieldName
|
|
169
|
+
const aliasCheck = src.slice(afterName).match(/^:\s*([A-Za-z_]\w*)\s*(?:\([^)]*\))?\s*/);
|
|
170
|
+
if (aliasCheck) {
|
|
171
|
+
actualName = aliasCheck[1];
|
|
172
|
+
const subStart = afterName + aliasCheck[0].length;
|
|
173
|
+
if (src[subStart] === "{") {
|
|
174
|
+
const subFields = parseSelectionSet(src, subStart);
|
|
175
|
+
fields.push({ name: actualName, selections: subFields });
|
|
176
|
+
// Advance past the sub-selection
|
|
177
|
+
i = skipSelectionSet(src, subStart);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
fields.push({ name: actualName, selections: [] });
|
|
181
|
+
i = subStart;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else if (src[afterName] === "{") {
|
|
185
|
+
const subFields = parseSelectionSet(src, afterName);
|
|
186
|
+
fields.push({ name: actualName, selections: subFields });
|
|
187
|
+
i = skipSelectionSet(src, afterName);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
fields.push({ name: actualName, selections: [] });
|
|
191
|
+
i = afterName;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
i++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
i++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return fields;
|
|
203
|
+
}
|
|
204
|
+
/** Skip past a balanced { ... } block, returning the position after the closing brace. */
|
|
205
|
+
function skipSelectionSet(src, bracePos) {
|
|
206
|
+
let depth = 0;
|
|
207
|
+
let i = bracePos;
|
|
208
|
+
while (i < src.length) {
|
|
209
|
+
if (src[i] === "{")
|
|
210
|
+
depth++;
|
|
211
|
+
else if (src[i] === "}") {
|
|
212
|
+
depth--;
|
|
213
|
+
if (depth === 0)
|
|
214
|
+
return i + 1;
|
|
215
|
+
}
|
|
216
|
+
i++;
|
|
217
|
+
}
|
|
218
|
+
return i;
|
|
219
|
+
}
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Field resolution against known contracts
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
function resolveSelections(selections, parentType, fieldContracts, typeReturnMap, referencedFields, exact) {
|
|
224
|
+
for (const sel of selections) {
|
|
225
|
+
const qualifiedName = `${parentType}.${sel.name}`;
|
|
226
|
+
if (fieldContracts.has(qualifiedName)) {
|
|
227
|
+
referencedFields.push(qualifiedName);
|
|
228
|
+
}
|
|
229
|
+
else if (!exact) {
|
|
230
|
+
// Inferred match — still include it but it will get 0.8 confidence
|
|
231
|
+
// at the edge level (handled by the caller/cross-indexer)
|
|
232
|
+
referencedFields.push(qualifiedName);
|
|
233
|
+
}
|
|
234
|
+
// Recurse into nested selections
|
|
235
|
+
if (sel.selections.length > 0) {
|
|
236
|
+
// Try to find the return type for this field from endpoint map
|
|
237
|
+
const returnType = typeReturnMap.get(qualifiedName);
|
|
238
|
+
const childType = returnType ?? capitalize(sel.name);
|
|
239
|
+
const childExact = returnType != null;
|
|
240
|
+
resolveSelections(sel.selections, childType, fieldContracts, typeReturnMap, referencedFields, childExact);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Helpers
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
/** Extract the return type from a GraphQL field signature like "user(id: ID!): User!" */
|
|
248
|
+
function extractReturnType(signature) {
|
|
249
|
+
const m = signature.match(/:\s*\[?\s*([A-Za-z_]\w*)/);
|
|
250
|
+
return m ? m[1] : null;
|
|
251
|
+
}
|
|
252
|
+
/** Capitalize the first letter of a string. */
|
|
253
|
+
function capitalize(s) {
|
|
254
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
255
|
+
}
|
|
256
|
+
/** Compute 1-based line number at a character offset within content. */
|
|
257
|
+
function lineAt(content, offset) {
|
|
258
|
+
let line = 1;
|
|
259
|
+
for (let i = 0; i < offset && i < content.length; i++) {
|
|
260
|
+
if (content[i] === "\n")
|
|
261
|
+
line++;
|
|
262
|
+
}
|
|
263
|
+
return line;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Find the name of the enclosing function/component at a given offset.
|
|
267
|
+
* Looks backwards for: function X, const X =, export function X, etc.
|
|
268
|
+
*/
|
|
269
|
+
function findEnclosingSymbol(content, offset) {
|
|
270
|
+
// Get the content before the offset
|
|
271
|
+
const before = content.slice(0, offset);
|
|
272
|
+
// Try to find the nearest function/const/class declaration
|
|
273
|
+
// Search backwards through the lines
|
|
274
|
+
const lines = before.split("\n");
|
|
275
|
+
for (let i = lines.length - 1; i >= 0 && i >= lines.length - 30; i--) {
|
|
276
|
+
const line = lines[i];
|
|
277
|
+
// function declaration
|
|
278
|
+
const funcMatch = line.match(/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_]\w*)/);
|
|
279
|
+
if (funcMatch)
|
|
280
|
+
return funcMatch[1];
|
|
281
|
+
// arrow function / const assignment
|
|
282
|
+
const constMatch = line.match(/(?:export\s+)?(?:const|let|var)\s+([A-Za-z_]\w*)\s*=/);
|
|
283
|
+
if (constMatch)
|
|
284
|
+
return constMatch[1];
|
|
285
|
+
// class method
|
|
286
|
+
const methodMatch = line.match(/^\s*(?:async\s+)?([A-Za-z_]\w*)\s*\(/);
|
|
287
|
+
if (methodMatch && methodMatch[1] !== "if" && methodMatch[1] !== "for" && methodMatch[1] !== "while") {
|
|
288
|
+
return methodMatch[1];
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return "<module>";
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=graphql-consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-consumer.js","sourceRoot":"","sources":["../../../src/workspace/graphql-consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { InterfaceContract } from "./cross-db.js";
|
|
2
|
+
export interface GraphQLExtractionResult {
|
|
3
|
+
contracts: Omit<InterfaceContract, "id" | "projectId">[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Extract type/field contracts from GraphQL schema SDL files.
|
|
7
|
+
*
|
|
8
|
+
* Produces a flat list of contracts:
|
|
9
|
+
* - One per type/input/interface/enum (symbolKind = "type")
|
|
10
|
+
* - One per field within a type (symbolKind = "field" or "endpoint" for root types)
|
|
11
|
+
* - One per scalar declaration (symbolKind = "type")
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractGraphQLContracts(filePath: string, content: string): GraphQLExtractionResult;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
// Root types whose fields are exposed as "endpoint" kind
|
|
3
|
+
const ROOT_TYPES = new Set(["Query", "Mutation", "Subscription"]);
|
|
4
|
+
/**
|
|
5
|
+
* Extract type/field contracts from GraphQL schema SDL files.
|
|
6
|
+
*
|
|
7
|
+
* Produces a flat list of contracts:
|
|
8
|
+
* - One per type/input/interface/enum (symbolKind = "type")
|
|
9
|
+
* - One per field within a type (symbolKind = "field" or "endpoint" for root types)
|
|
10
|
+
* - One per scalar declaration (symbolKind = "type")
|
|
11
|
+
*/
|
|
12
|
+
export function extractGraphQLContracts(filePath, content) {
|
|
13
|
+
const contracts = [];
|
|
14
|
+
const lines = content.split("\n");
|
|
15
|
+
// Pre-process: strip comments and block descriptions to simplify parsing.
|
|
16
|
+
// We keep the original lines for line-number tracking and hashing,
|
|
17
|
+
// but work on a cleaned version for regex matching.
|
|
18
|
+
const cleaned = stripCommentsAndDescriptions(lines);
|
|
19
|
+
// Extract scalar declarations: scalar DateTime
|
|
20
|
+
for (let i = 0; i < cleaned.length; i++) {
|
|
21
|
+
const scalarMatch = cleaned[i].match(/^\s*scalar\s+([A-Za-z_]\w*)/);
|
|
22
|
+
if (scalarMatch) {
|
|
23
|
+
contracts.push({
|
|
24
|
+
symbolName: scalarMatch[1],
|
|
25
|
+
symbolKind: "type",
|
|
26
|
+
sourceFile: filePath,
|
|
27
|
+
fileLine: i + 1,
|
|
28
|
+
interfaceType: "graphql",
|
|
29
|
+
signature: `scalar ${scalarMatch[1]}`,
|
|
30
|
+
contentHash: sha256(lines[i]),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Extract type / input / interface / enum blocks
|
|
35
|
+
// Handles: type Foo { | type Foo implements Bar { | extend type Foo {
|
|
36
|
+
// input Foo { | interface Foo { | enum Foo {
|
|
37
|
+
const blockStartRe = /^\s*(extend\s+)?(type|input|interface|enum)\s+([A-Za-z_]\w*)(?:\s+implements\s+[A-Za-z_][\w\s&,]*)?\s*\{/;
|
|
38
|
+
let i = 0;
|
|
39
|
+
while (i < cleaned.length) {
|
|
40
|
+
const m = cleaned[i].match(blockStartRe);
|
|
41
|
+
if (!m) {
|
|
42
|
+
i++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const isExtend = !!m[1];
|
|
46
|
+
const blockKind = m[2]; // type | input | interface | enum
|
|
47
|
+
const typeName = m[3];
|
|
48
|
+
const blockStartLine = i;
|
|
49
|
+
// Emit a contract for the type itself (skip for extend blocks — the base
|
|
50
|
+
// type was already declared elsewhere)
|
|
51
|
+
if (!isExtend) {
|
|
52
|
+
contracts.push({
|
|
53
|
+
symbolName: typeName,
|
|
54
|
+
symbolKind: "type",
|
|
55
|
+
sourceFile: filePath,
|
|
56
|
+
fileLine: blockStartLine + 1,
|
|
57
|
+
interfaceType: "graphql",
|
|
58
|
+
signature: cleaned[i].trim(),
|
|
59
|
+
contentHash: sha256(lines[i]),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Find the matching closing brace, accounting for nesting (rare in SDL
|
|
63
|
+
// but possible with nested input types etc.)
|
|
64
|
+
let depth = 1;
|
|
65
|
+
i++;
|
|
66
|
+
while (i < cleaned.length && depth > 0) {
|
|
67
|
+
for (const ch of cleaned[i]) {
|
|
68
|
+
if (ch === "{")
|
|
69
|
+
depth++;
|
|
70
|
+
else if (ch === "}")
|
|
71
|
+
depth--;
|
|
72
|
+
}
|
|
73
|
+
if (depth > 0) {
|
|
74
|
+
// This line is inside the block — try to extract a field
|
|
75
|
+
const field = parseField(cleaned[i], typeName, blockKind);
|
|
76
|
+
if (field) {
|
|
77
|
+
const symbolKind = ROOT_TYPES.has(typeName) ? "endpoint" : "field";
|
|
78
|
+
contracts.push({
|
|
79
|
+
symbolName: `${typeName}.${field.name}`,
|
|
80
|
+
symbolKind,
|
|
81
|
+
sourceFile: filePath,
|
|
82
|
+
fileLine: i + 1,
|
|
83
|
+
interfaceType: "graphql",
|
|
84
|
+
signature: field.signature,
|
|
85
|
+
contentHash: sha256(lines[i]),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { contracts };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse a single field line inside a type block.
|
|
96
|
+
* Handles:
|
|
97
|
+
* fieldName: Type
|
|
98
|
+
* fieldName(arg: Type, arg2: Type): Type
|
|
99
|
+
* ENUM_VALUE (inside enum blocks)
|
|
100
|
+
*/
|
|
101
|
+
function parseField(line, _typeName, blockKind) {
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
if (!trimmed || trimmed === "{" || trimmed === "}")
|
|
104
|
+
return null;
|
|
105
|
+
if (blockKind === "enum") {
|
|
106
|
+
// Enum values are simple identifiers, possibly followed by @directives
|
|
107
|
+
const enumMatch = trimmed.match(/^([A-Za-z_]\w*)/);
|
|
108
|
+
if (enumMatch) {
|
|
109
|
+
return { name: enumMatch[1], signature: trimmed };
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
// Field: name(args): ReturnType or name: ReturnType
|
|
114
|
+
const fieldMatch = trimmed.match(/^([A-Za-z_]\w*)\s*(\([^)]*\))?\s*:\s*(.+)/);
|
|
115
|
+
if (fieldMatch) {
|
|
116
|
+
return { name: fieldMatch[1], signature: trimmed };
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Strip # line comments and """ block descriptions from SDL lines.
|
|
122
|
+
* Returns a new array of the same length, with comment/description lines blanked out.
|
|
123
|
+
*/
|
|
124
|
+
function stripCommentsAndDescriptions(lines) {
|
|
125
|
+
const result = new Array(lines.length);
|
|
126
|
+
let inBlockDesc = false;
|
|
127
|
+
for (let i = 0; i < lines.length; i++) {
|
|
128
|
+
if (inBlockDesc) {
|
|
129
|
+
// Look for the closing """
|
|
130
|
+
const closeIdx = lines[i].indexOf('"""');
|
|
131
|
+
if (closeIdx !== -1) {
|
|
132
|
+
inBlockDesc = false;
|
|
133
|
+
// Blank out up to and including the closing """
|
|
134
|
+
result[i] = lines[i].substring(closeIdx + 3);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
result[i] = "";
|
|
138
|
+
}
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
let line = lines[i];
|
|
142
|
+
// Handle opening """ — could be single-line """ ... """ or start a block
|
|
143
|
+
const openIdx = line.indexOf('"""');
|
|
144
|
+
if (openIdx !== -1) {
|
|
145
|
+
const afterOpen = line.substring(openIdx + 3);
|
|
146
|
+
const closeIdx = afterOpen.indexOf('"""');
|
|
147
|
+
if (closeIdx !== -1) {
|
|
148
|
+
// Single-line block description: """some text"""
|
|
149
|
+
line = line.substring(0, openIdx) + afterOpen.substring(closeIdx + 3);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// Multi-line block description starts
|
|
153
|
+
inBlockDesc = true;
|
|
154
|
+
line = line.substring(0, openIdx);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Strip # comments (but not inside strings — SDL doesn't have inline strings
|
|
158
|
+
// outside of descriptions, so this is safe)
|
|
159
|
+
const hashIdx = line.indexOf("#");
|
|
160
|
+
if (hashIdx !== -1) {
|
|
161
|
+
line = line.substring(0, hashIdx);
|
|
162
|
+
}
|
|
163
|
+
// Strip single-line "description" strings that precede fields
|
|
164
|
+
line = line.replace(/^\s*"[^"]*"\s*$/, "");
|
|
165
|
+
result[i] = line;
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
function sha256(content) {
|
|
170
|
+
return createHash("sha256").update(content).digest("hex");
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=graphql-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-extractor.js","sourceRoot":"","sources":["../../../src/workspace/graphql-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface WorkspaceProject {
|
|
2
|
+
path: string;
|
|
3
|
+
role: "provider" | "consumer" | "both";
|
|
4
|
+
interfaces?: Array<{
|
|
5
|
+
type: "graphql" | "openapi" | "protobuf" | "npm" | "trpc";
|
|
6
|
+
schema?: string;
|
|
7
|
+
spec?: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkspaceConfig {
|
|
11
|
+
workspace: string;
|
|
12
|
+
projects: WorkspaceProject[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load a workspace config from ~/.sverklo/workspaces/<name>.yaml.
|
|
16
|
+
* Returns null if the file doesn't exist or can't be parsed.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadWorkspaceConfig(name: string): WorkspaceConfig | null;
|
|
19
|
+
/**
|
|
20
|
+
* Scan ~/.sverklo/workspaces/ for .yaml files and return their names.
|
|
21
|
+
*/
|
|
22
|
+
export declare function listWorkspaces(): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Find which workspace contains this project path (exact match on
|
|
25
|
+
* any project's path). Returns the first match or null.
|
|
26
|
+
*/
|
|
27
|
+
export declare function findWorkspaceForProject(projectPath: string): WorkspaceConfig | null;
|
|
28
|
+
/**
|
|
29
|
+
* Get the path for the cross-repo SQLite database for a workspace.
|
|
30
|
+
* Creates the parent directory if it doesn't exist.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getWorkspaceDbPath(name: string): string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
const WORKSPACES_DIR = join(homedir(), ".sverklo", "workspaces");
|
|
6
|
+
/**
|
|
7
|
+
* Load a workspace config from ~/.sverklo/workspaces/<name>.yaml.
|
|
8
|
+
* Returns null if the file doesn't exist or can't be parsed.
|
|
9
|
+
*/
|
|
10
|
+
export function loadWorkspaceConfig(name) {
|
|
11
|
+
const filePath = join(WORKSPACES_DIR, `${name}.yaml`);
|
|
12
|
+
if (!existsSync(filePath))
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
16
|
+
const parsed = parseYaml(raw);
|
|
17
|
+
if (!parsed || typeof parsed !== "object")
|
|
18
|
+
return null;
|
|
19
|
+
const workspace = String(parsed.workspace ?? name);
|
|
20
|
+
const rawProjects = Array.isArray(parsed.projects) ? parsed.projects : [];
|
|
21
|
+
const projects = rawProjects
|
|
22
|
+
.filter((p) => typeof p === "object" && p !== null && typeof p.path === "string")
|
|
23
|
+
.map((p) => {
|
|
24
|
+
const role = ["provider", "consumer", "both"].includes(p.role)
|
|
25
|
+
? p.role
|
|
26
|
+
: "both";
|
|
27
|
+
const interfaces = Array.isArray(p.interfaces)
|
|
28
|
+
? p.interfaces
|
|
29
|
+
.filter((i) => typeof i === "object" &&
|
|
30
|
+
i !== null &&
|
|
31
|
+
typeof i.type === "string")
|
|
32
|
+
.map((i) => ({
|
|
33
|
+
type: i.type,
|
|
34
|
+
...(typeof i.schema === "string" ? { schema: i.schema } : {}),
|
|
35
|
+
...(typeof i.spec === "string" ? { spec: i.spec } : {}),
|
|
36
|
+
}))
|
|
37
|
+
: undefined;
|
|
38
|
+
return {
|
|
39
|
+
path: p.path,
|
|
40
|
+
role,
|
|
41
|
+
...(interfaces && interfaces.length > 0 ? { interfaces } : {}),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
return { workspace, projects };
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (process.env.SVERKLO_DEBUG) {
|
|
48
|
+
process.stderr.write(`[sverklo] Failed to parse workspace '${name}': ${err}\n`);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Scan ~/.sverklo/workspaces/ for .yaml files and return their names.
|
|
55
|
+
*/
|
|
56
|
+
export function listWorkspaces() {
|
|
57
|
+
if (!existsSync(WORKSPACES_DIR))
|
|
58
|
+
return [];
|
|
59
|
+
try {
|
|
60
|
+
return readdirSync(WORKSPACES_DIR)
|
|
61
|
+
.filter((f) => f.endsWith(".yaml"))
|
|
62
|
+
.map((f) => f.replace(/\.yaml$/, ""));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Find which workspace contains this project path (exact match on
|
|
70
|
+
* any project's path). Returns the first match or null.
|
|
71
|
+
*/
|
|
72
|
+
export function findWorkspaceForProject(projectPath) {
|
|
73
|
+
const names = listWorkspaces();
|
|
74
|
+
for (const name of names) {
|
|
75
|
+
const config = loadWorkspaceConfig(name);
|
|
76
|
+
if (config && config.projects.some((p) => p.path === projectPath)) {
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the path for the cross-repo SQLite database for a workspace.
|
|
84
|
+
* Creates the parent directory if it doesn't exist.
|
|
85
|
+
*/
|
|
86
|
+
export function getWorkspaceDbPath(name) {
|
|
87
|
+
const dir = join(WORKSPACES_DIR, name);
|
|
88
|
+
mkdirSync(dir, { recursive: true });
|
|
89
|
+
return join(dir, "cross.db");
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=workspace-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-config.js","sourceRoot":"","sources":["../../../src/workspace/workspace-config.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sverklo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Sverklo — local-first code intelligence MCP server. Diff-aware MR review, risk scoring, hybrid semantic search, PageRank ranking, persistent memory. Zero config.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|