@soda-gql/lsp 0.14.1 → 0.14.2
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/README.md +45 -0
- package/dist/bin.cjs +1 -1
- package/dist/bin.mjs +1 -1
- package/dist/index.cjs +298 -2
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +42 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +44 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +297 -2
- package/dist/index.mjs.map +1 -0
- package/dist/{server-DsJ1bZ7i.cjs → server-C1MSX490.cjs} +1101 -410
- package/dist/server-C1MSX490.cjs.map +1 -0
- package/dist/{server-CqOUHwDk.mjs → server-wPCHK04O.mjs} +1087 -414
- package/dist/server-wPCHK04O.mjs.map +1 -0
- package/package.json +6 -6
- package/dist/server-CqOUHwDk.mjs.map +0 -1
- package/dist/server-DsJ1bZ7i.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -52,8 +52,53 @@ const server = createLspServer({ connection });
|
|
|
52
52
|
server.start();
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
## Claude Code Integration
|
|
56
|
+
|
|
57
|
+
soda-gql LSP integrates with [Claude Code](https://claude.com/claude-code) as a plugin, providing GraphQL diagnostics, completion, and code intelligence directly in the AI coding assistant.
|
|
58
|
+
|
|
59
|
+
### How it works
|
|
60
|
+
|
|
61
|
+
The integration uses a separate **proxy package** ([`@soda-gql/protocol-proxy`](../protocol-proxy)) that resolves the project-local `@soda-gql/lsp` at runtime. This ensures version consistency between the LSP server and the project's soda-gql dependencies — similar to how `typescript-language-server` delegates to the project-local TypeScript.
|
|
62
|
+
|
|
63
|
+
### Setup for users
|
|
64
|
+
|
|
65
|
+
1. Install the proxy binary globally:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install -g @soda-gql/protocol-proxy
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. Ensure `@soda-gql/lsp` is a project dependency (the proxy resolves it from `node_modules`):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bun add -D @soda-gql/lsp
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. The Claude Code plugin is defined in the project's `.claude-plugin/marketplace.json`. Install it via `/plugin` in Claude Code and restart the session.
|
|
78
|
+
|
|
79
|
+
### Setup for monorepo contributors
|
|
80
|
+
|
|
81
|
+
1. Build the packages:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bun run build
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
2. Link the proxy package globally:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cd packages/protocol-proxy && bun link
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
3. Install the plugin via `/plugin` in Claude Code, then restart.
|
|
94
|
+
|
|
95
|
+
### Coexistence with TypeScript LSP
|
|
96
|
+
|
|
97
|
+
Both soda-gql LSP and the TypeScript LSP plugin can be active simultaneously on `.ts`/`.tsx` files. The TypeScript LSP provides type checking, while soda-gql LSP provides GraphQL-specific intelligence within tagged templates and callback builders.
|
|
98
|
+
|
|
55
99
|
## Supported Editors
|
|
56
100
|
|
|
101
|
+
- **Claude Code** — Via the soda-gql plugin (see above)
|
|
57
102
|
- **VS Code / Cursor** — Via the [soda-gql VS Code extension](../vscode-extension)
|
|
58
103
|
- **Any LSP-compatible editor** — Neovim, Emacs, Sublime Text, etc. using the standalone CLI
|
|
59
104
|
|
package/dist/bin.cjs
CHANGED
package/dist/bin.mjs
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,304 @@
|
|
|
1
|
-
const require_server = require('./server-
|
|
1
|
+
const require_server = require('./server-C1MSX490.cjs');
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let __soda_gql_builder = require("@soda-gql/builder");
|
|
5
|
+
let __soda_gql_config = require("@soda-gql/config");
|
|
6
|
+
let graphql = require("graphql");
|
|
2
7
|
|
|
8
|
+
//#region packages/lsp/src/cli-utils.ts
|
|
9
|
+
/**
|
|
10
|
+
* Shared utility functions for CLI.
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/** Extract variable declaration from template content (e.g., "($id: ID!)"). */
|
|
14
|
+
const extractVariablesFromContent = (content) => {
|
|
15
|
+
const match = content.match(/^\s*\(([^)]+)\)/);
|
|
16
|
+
return match ? `(${match[1]})` : undefined;
|
|
17
|
+
};
|
|
18
|
+
/** Compute 1-based line number from a byte offset in source. */
|
|
19
|
+
const computeLineFromOffset = (source, offset) => {
|
|
20
|
+
let line = 1;
|
|
21
|
+
for (let i = 0; i < offset && i < source.length; i++) {
|
|
22
|
+
if (source[i] === "\n") line++;
|
|
23
|
+
}
|
|
24
|
+
return line;
|
|
25
|
+
};
|
|
26
|
+
const collectDiagnostics = (state, ctx) => {
|
|
27
|
+
const diagnostics = require_server.collectRawDiagnostics(state, ctx);
|
|
28
|
+
return [...diagnostics].map((d) => ({
|
|
29
|
+
message: d.message,
|
|
30
|
+
line: d.range.start.line + 1,
|
|
31
|
+
column: d.range.start.character + 1,
|
|
32
|
+
severity: diagnosticSeverityToString(d.severity)
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
35
|
+
const diagnosticSeverityToString = (severity) => {
|
|
36
|
+
switch (severity) {
|
|
37
|
+
case 1: return "Error";
|
|
38
|
+
case 2: return "Warning";
|
|
39
|
+
case 3: return "Information";
|
|
40
|
+
case 4: return "Hint";
|
|
41
|
+
default: return "Error";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/** Introspect a single GraphQL type (depth-1). */
|
|
45
|
+
const introspectType = (schema, typeName) => {
|
|
46
|
+
const type = schema.getType(typeName);
|
|
47
|
+
if (!type) return undefined;
|
|
48
|
+
if ((0, graphql.isObjectType)(type) || (0, graphql.isInterfaceType)(type)) {
|
|
49
|
+
const fields = Object.values(type.getFields()).map((f) => ({
|
|
50
|
+
name: f.name,
|
|
51
|
+
type: f.type.toString(),
|
|
52
|
+
args: f.args.map((a) => ({
|
|
53
|
+
name: a.name,
|
|
54
|
+
type: a.type.toString()
|
|
55
|
+
}))
|
|
56
|
+
}));
|
|
57
|
+
return {
|
|
58
|
+
name: type.name,
|
|
59
|
+
kind: (0, graphql.isObjectType)(type) ? "OBJECT" : "INTERFACE",
|
|
60
|
+
fields
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if ((0, graphql.isUnionType)(type)) {
|
|
64
|
+
return {
|
|
65
|
+
name: type.name,
|
|
66
|
+
kind: "UNION",
|
|
67
|
+
members: type.getTypes().map((t) => ({ name: t.name }))
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if ((0, graphql.isEnumType)(type)) {
|
|
71
|
+
return {
|
|
72
|
+
name: type.name,
|
|
73
|
+
kind: "ENUM",
|
|
74
|
+
values: type.getValues().map((v) => ({ name: v.name }))
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if ((0, graphql.isInputObjectType)(type)) {
|
|
78
|
+
const fields = Object.values(type.getFields()).map((f) => ({
|
|
79
|
+
name: f.name,
|
|
80
|
+
type: f.type.toString()
|
|
81
|
+
}));
|
|
82
|
+
return {
|
|
83
|
+
name: type.name,
|
|
84
|
+
kind: "INPUT_OBJECT",
|
|
85
|
+
fields
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if ((0, graphql.isScalarType)(type)) {
|
|
89
|
+
return {
|
|
90
|
+
name: type.name,
|
|
91
|
+
kind: "SCALAR"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
};
|
|
96
|
+
/** List all user-defined types in a schema. */
|
|
97
|
+
const listTypes = (schema) => {
|
|
98
|
+
const typeMap = schema.getTypeMap();
|
|
99
|
+
const types = Object.values(typeMap).filter((t) => !t.name.startsWith("__")).map((t) => ({
|
|
100
|
+
name: t.name,
|
|
101
|
+
kind: getTypeKind(t)
|
|
102
|
+
}));
|
|
103
|
+
return { types };
|
|
104
|
+
};
|
|
105
|
+
const getTypeKind = (type) => {
|
|
106
|
+
if ((0, graphql.isObjectType)(type)) return "OBJECT";
|
|
107
|
+
if ((0, graphql.isInterfaceType)(type)) return "INTERFACE";
|
|
108
|
+
if ((0, graphql.isUnionType)(type)) return "UNION";
|
|
109
|
+
if ((0, graphql.isEnumType)(type)) return "ENUM";
|
|
110
|
+
if ((0, graphql.isInputObjectType)(type)) return "INPUT_OBJECT";
|
|
111
|
+
if ((0, graphql.isScalarType)(type)) return "SCALAR";
|
|
112
|
+
return "UNKNOWN";
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region packages/lsp/src/cli.ts
|
|
117
|
+
/**
|
|
118
|
+
* One-shot CLI runner: exposes soda-gql GraphQL intelligence as CLI subcommands.
|
|
119
|
+
* Provides a stable, daemon-free interface for Claude Code skills.
|
|
120
|
+
* @module
|
|
121
|
+
*/
|
|
122
|
+
const USAGE = `Usage: soda-gql-lsp-cli <subcommand> [options]
|
|
123
|
+
|
|
124
|
+
Subcommands:
|
|
125
|
+
diagnostics <file> Validate GraphQL templates against the schema
|
|
126
|
+
schema [typeName] Introspect schema types (omit typeName for full list)
|
|
127
|
+
symbols <file> List fragments and operations in a file
|
|
128
|
+
|
|
129
|
+
Options:
|
|
130
|
+
--workspace Index all workspace files (for cross-file fragment resolution)
|
|
131
|
+
--schema <name> Target schema name (multi-schema projects)
|
|
132
|
+
--config <path> Config context file path (multi-config projects)
|
|
133
|
+
--help Show this help message`;
|
|
134
|
+
const parseCliArgs = (args) => {
|
|
135
|
+
const subcommand = args[0];
|
|
136
|
+
if (!subcommand || subcommand === "--help" || ![
|
|
137
|
+
"diagnostics",
|
|
138
|
+
"schema",
|
|
139
|
+
"symbols"
|
|
140
|
+
].includes(subcommand)) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
let filePath;
|
|
144
|
+
let typeName;
|
|
145
|
+
let workspace = false;
|
|
146
|
+
let schemaName;
|
|
147
|
+
let configPath;
|
|
148
|
+
let i = 1;
|
|
149
|
+
const firstArg = args[i];
|
|
150
|
+
if (firstArg !== undefined && !firstArg.startsWith("--")) {
|
|
151
|
+
if (subcommand === "schema") {
|
|
152
|
+
typeName = firstArg;
|
|
153
|
+
} else {
|
|
154
|
+
filePath = (0, node_path.resolve)(firstArg);
|
|
155
|
+
}
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
for (; i < args.length; i++) {
|
|
159
|
+
const arg = args[i];
|
|
160
|
+
if (arg === undefined) continue;
|
|
161
|
+
if (arg === "--workspace") {
|
|
162
|
+
workspace = true;
|
|
163
|
+
} else if (arg === "--schema" && i + 1 < args.length) {
|
|
164
|
+
schemaName = args[++i];
|
|
165
|
+
} else if (arg === "--config" && i + 1 < args.length) {
|
|
166
|
+
const nextArg = args[++i];
|
|
167
|
+
if (nextArg !== undefined) configPath = (0, node_path.resolve)(nextArg);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
subcommand,
|
|
172
|
+
filePath,
|
|
173
|
+
typeName,
|
|
174
|
+
workspace,
|
|
175
|
+
schemaName,
|
|
176
|
+
configPath
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const output = (data) => {
|
|
180
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
181
|
+
};
|
|
182
|
+
var CliError = class extends Error {};
|
|
183
|
+
const cliError = (message) => {
|
|
184
|
+
process.stderr.write(`${JSON.stringify({ error: message })}\n`);
|
|
185
|
+
return new CliError(message);
|
|
186
|
+
};
|
|
187
|
+
const initRegistry = () => {
|
|
188
|
+
const cwd = process.cwd();
|
|
189
|
+
const configPaths = (0, __soda_gql_config.findAllConfigFiles)(cwd);
|
|
190
|
+
if (configPaths.length === 0) {
|
|
191
|
+
throw cliError(`No soda-gql config found in ${cwd}`);
|
|
192
|
+
}
|
|
193
|
+
const result = require_server.createConfigRegistry(configPaths);
|
|
194
|
+
if (result.isErr()) {
|
|
195
|
+
throw cliError(result.error.message);
|
|
196
|
+
}
|
|
197
|
+
return result.value;
|
|
198
|
+
};
|
|
199
|
+
const indexWorkspace = (registry) => {
|
|
200
|
+
for (const ctx of registry.getAllContexts()) {
|
|
201
|
+
const filesResult = (0, __soda_gql_builder.resolveEntryPaths)(ctx.config.include, ctx.config.exclude);
|
|
202
|
+
if (filesResult.isErr()) continue;
|
|
203
|
+
for (const fp of filesResult.value) {
|
|
204
|
+
if (ctx.documentManager.get(fp)) continue;
|
|
205
|
+
try {
|
|
206
|
+
const source = (0, node_fs.readFileSync)(fp, "utf-8");
|
|
207
|
+
ctx.documentManager.update(fp, 1, source);
|
|
208
|
+
} catch {}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const resolveContext = (registry, filePath) => {
|
|
213
|
+
const ctx = filePath ? registry.resolveForUri(filePath) : registry.getAllContexts()[0];
|
|
214
|
+
if (!ctx) {
|
|
215
|
+
throw cliError(filePath ? `No soda-gql config covers ${filePath}` : "No soda-gql config context found");
|
|
216
|
+
}
|
|
217
|
+
return ctx;
|
|
218
|
+
};
|
|
219
|
+
const readSource = (filePath) => {
|
|
220
|
+
try {
|
|
221
|
+
return (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
222
|
+
} catch (e) {
|
|
223
|
+
throw cliError(`Failed to read file: ${e instanceof Error ? e.message : String(e)}`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const requireFilePath = (args, subcommand) => {
|
|
227
|
+
const { filePath } = args;
|
|
228
|
+
if (!filePath) throw cliError(`${subcommand} requires a file path argument`);
|
|
229
|
+
return filePath;
|
|
230
|
+
};
|
|
231
|
+
const handleDiagnostics = (registry, args) => {
|
|
232
|
+
const filePath = requireFilePath(args, "diagnostics");
|
|
233
|
+
const ctx = resolveContext(registry, filePath);
|
|
234
|
+
const source = readSource(filePath);
|
|
235
|
+
const state = ctx.documentManager.update(filePath, 1, source);
|
|
236
|
+
output(collectDiagnostics(state, ctx));
|
|
237
|
+
};
|
|
238
|
+
const handleSchema = (registry, args) => {
|
|
239
|
+
const ctx = resolveContext(registry, args.configPath);
|
|
240
|
+
const targetSchemaName = args.schemaName ?? ctx.schemaResolver.getSchemaNames()[0];
|
|
241
|
+
if (!targetSchemaName) throw cliError("No schema available");
|
|
242
|
+
const entry = ctx.schemaResolver.getSchema(targetSchemaName);
|
|
243
|
+
if (!entry) throw cliError(`Schema '${targetSchemaName}' not found`);
|
|
244
|
+
if (args.typeName) {
|
|
245
|
+
const result = introspectType(entry.schema, args.typeName);
|
|
246
|
+
if (!result) throw cliError(`Type '${args.typeName}' not found in schema`);
|
|
247
|
+
output(result);
|
|
248
|
+
} else {
|
|
249
|
+
output(listTypes(entry.schema));
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const handleSymbols = (registry, args) => {
|
|
253
|
+
const filePath = requireFilePath(args, "symbols");
|
|
254
|
+
const ctx = resolveContext(registry, filePath);
|
|
255
|
+
const source = readSource(filePath);
|
|
256
|
+
const state = ctx.documentManager.update(filePath, 1, source);
|
|
257
|
+
const symbols = state.templates.filter((t) => t.elementName !== undefined).map((t) => ({
|
|
258
|
+
name: t.elementName,
|
|
259
|
+
kind: t.kind,
|
|
260
|
+
typeName: t.typeName,
|
|
261
|
+
schemaName: t.schemaName,
|
|
262
|
+
variables: extractVariablesFromContent(t.content),
|
|
263
|
+
line: computeLineFromOffset(source, t.contentRange.start)
|
|
264
|
+
}));
|
|
265
|
+
output(symbols);
|
|
266
|
+
};
|
|
267
|
+
/** Run the soda-gql LSP CLI with the given arguments. */
|
|
268
|
+
const runLspCli = async (args) => {
|
|
269
|
+
const parsed = parseCliArgs(args);
|
|
270
|
+
if (!parsed) {
|
|
271
|
+
process.stderr.write(`${USAGE}\n`);
|
|
272
|
+
process.exit(args.includes("--help") ? 0 : 1);
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const registry = initRegistry();
|
|
276
|
+
if (parsed.workspace) {
|
|
277
|
+
indexWorkspace(registry);
|
|
278
|
+
}
|
|
279
|
+
switch (parsed.subcommand) {
|
|
280
|
+
case "diagnostics":
|
|
281
|
+
handleDiagnostics(registry, parsed);
|
|
282
|
+
break;
|
|
283
|
+
case "schema":
|
|
284
|
+
handleSchema(registry, parsed);
|
|
285
|
+
break;
|
|
286
|
+
case "symbols":
|
|
287
|
+
handleSymbols(registry, parsed);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
} catch (e) {
|
|
291
|
+
if (e instanceof CliError) process.exit(1);
|
|
292
|
+
throw e;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
3
297
|
exports.createDocumentManager = require_server.createDocumentManager;
|
|
4
298
|
exports.createLspServer = require_server.createLspServer;
|
|
5
299
|
exports.createPositionMapper = require_server.createPositionMapper;
|
|
6
300
|
exports.createSchemaResolver = require_server.createSchemaResolver;
|
|
7
301
|
exports.lspErrors = require_server.lspErrors;
|
|
8
|
-
exports.preprocessFragmentArgs = require_server.preprocessFragmentArgs;
|
|
302
|
+
exports.preprocessFragmentArgs = require_server.preprocessFragmentArgs;
|
|
303
|
+
exports.runLspCli = runLspCli;
|
|
304
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["collectRawDiagnostics","filePath: string | undefined","typeName: string | undefined","schemaName: string | undefined","configPath: string | undefined","createConfigRegistry"],"sources":["../src/cli-utils.ts","../src/cli.ts"],"sourcesContent":["/**\n * Shared utility functions for CLI.\n * @module\n */\n\nimport {\n type GraphQLNamedType,\n type GraphQLSchema,\n isEnumType,\n isInputObjectType,\n isInterfaceType,\n isObjectType,\n isScalarType,\n isUnionType,\n} from \"graphql\";\nimport type { ConfigContext } from \"./config-registry\";\nimport { collectRawDiagnostics } from \"./diagnostics-collector\";\nimport type { DocumentState } from \"./types\";\n\n/** Extract variable declaration from template content (e.g., \"($id: ID!)\"). */\nexport const extractVariablesFromContent = (content: string): string | undefined => {\n const match = content.match(/^\\s*\\(([^)]+)\\)/);\n return match ? `(${match[1]})` : undefined;\n};\n\n/** Compute 1-based line number from a byte offset in source. */\nexport const computeLineFromOffset = (source: string, offset: number): number => {\n let line = 1;\n for (let i = 0; i < offset && i < source.length; i++) {\n if (source[i] === \"\\n\") line++;\n }\n return line;\n};\n\n/** Collect diagnostics and map to JSON-serializable format. */\nexport type DiagnosticResult = { message: string; line: number; column: number; severity: string };\n\nexport const collectDiagnostics = (state: DocumentState, ctx: ConfigContext): DiagnosticResult[] => {\n const diagnostics = collectRawDiagnostics(state, ctx);\n return [...diagnostics].map((d) => ({\n message: d.message,\n line: d.range.start.line + 1,\n column: d.range.start.character + 1,\n severity: diagnosticSeverityToString(d.severity),\n }));\n};\n\nexport const diagnosticSeverityToString = (severity: number | undefined): string => {\n switch (severity) {\n case 1:\n return \"Error\";\n case 2:\n return \"Warning\";\n case 3:\n return \"Information\";\n case 4:\n return \"Hint\";\n default:\n return \"Error\";\n }\n};\n\n/** Introspect a single GraphQL type (depth-1). */\nexport const introspectType = (schema: GraphQLSchema, typeName: string) => {\n const type = schema.getType(typeName);\n if (!type) return undefined;\n\n if (isObjectType(type) || isInterfaceType(type)) {\n const fields = Object.values(type.getFields()).map((f) => ({\n name: f.name,\n type: f.type.toString(),\n args: f.args.map((a) => ({ name: a.name, type: a.type.toString() })),\n }));\n return { name: type.name, kind: isObjectType(type) ? \"OBJECT\" : \"INTERFACE\", fields };\n }\n if (isUnionType(type)) {\n return { name: type.name, kind: \"UNION\", members: type.getTypes().map((t) => ({ name: t.name })) };\n }\n if (isEnumType(type)) {\n return { name: type.name, kind: \"ENUM\", values: type.getValues().map((v) => ({ name: v.name })) };\n }\n if (isInputObjectType(type)) {\n const fields = Object.values(type.getFields()).map((f) => ({\n name: f.name,\n type: f.type.toString(),\n }));\n return { name: type.name, kind: \"INPUT_OBJECT\", fields };\n }\n if (isScalarType(type)) {\n return { name: type.name, kind: \"SCALAR\" };\n }\n return undefined;\n};\n\n/** List all user-defined types in a schema. */\nexport const listTypes = (schema: GraphQLSchema) => {\n const typeMap = schema.getTypeMap();\n const types = Object.values(typeMap)\n .filter((t) => !t.name.startsWith(\"__\"))\n .map((t) => ({ name: t.name, kind: getTypeKind(t) }));\n return { types };\n};\n\nconst getTypeKind = (type: GraphQLNamedType): string => {\n if (isObjectType(type)) return \"OBJECT\";\n if (isInterfaceType(type)) return \"INTERFACE\";\n if (isUnionType(type)) return \"UNION\";\n if (isEnumType(type)) return \"ENUM\";\n if (isInputObjectType(type)) return \"INPUT_OBJECT\";\n if (isScalarType(type)) return \"SCALAR\";\n return \"UNKNOWN\";\n};\n","/**\n * One-shot CLI runner: exposes soda-gql GraphQL intelligence as CLI subcommands.\n * Provides a stable, daemon-free interface for Claude Code skills.\n * @module\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { resolveEntryPaths } from \"@soda-gql/builder\";\nimport { findAllConfigFiles } from \"@soda-gql/config\";\nimport { collectDiagnostics, computeLineFromOffset, extractVariablesFromContent, introspectType, listTypes } from \"./cli-utils\";\nimport type { ConfigContext, ConfigRegistry } from \"./config-registry\";\nimport { createConfigRegistry } from \"./config-registry\";\n\ninterface CliArgs {\n readonly subcommand: \"diagnostics\" | \"schema\" | \"symbols\";\n readonly filePath?: string;\n readonly typeName?: string;\n readonly workspace: boolean;\n readonly schemaName?: string;\n readonly configPath?: string;\n}\n\nconst USAGE = `Usage: soda-gql-lsp-cli <subcommand> [options]\n\nSubcommands:\n diagnostics <file> Validate GraphQL templates against the schema\n schema [typeName] Introspect schema types (omit typeName for full list)\n symbols <file> List fragments and operations in a file\n\nOptions:\n --workspace Index all workspace files (for cross-file fragment resolution)\n --schema <name> Target schema name (multi-schema projects)\n --config <path> Config context file path (multi-config projects)\n --help Show this help message`;\n\nexport const parseCliArgs = (args: readonly string[]): CliArgs | undefined => {\n const subcommand = args[0];\n if (!subcommand || subcommand === \"--help\" || ![\"diagnostics\", \"schema\", \"symbols\"].includes(subcommand)) {\n return undefined;\n }\n\n let filePath: string | undefined;\n let typeName: string | undefined;\n let workspace = false;\n let schemaName: string | undefined;\n let configPath: string | undefined;\n\n let i = 1;\n // Collect positional arg first (before any flags)\n const firstArg = args[i];\n if (firstArg !== undefined && !firstArg.startsWith(\"--\")) {\n if (subcommand === \"schema\") {\n typeName = firstArg;\n } else {\n filePath = resolve(firstArg);\n }\n i++;\n }\n\n // Collect flags\n for (; i < args.length; i++) {\n const arg = args[i];\n if (arg === undefined) continue;\n if (arg === \"--workspace\") {\n workspace = true;\n } else if (arg === \"--schema\" && i + 1 < args.length) {\n schemaName = args[++i];\n } else if (arg === \"--config\" && i + 1 < args.length) {\n const nextArg = args[++i];\n if (nextArg !== undefined) configPath = resolve(nextArg);\n }\n }\n\n return { subcommand: subcommand as CliArgs[\"subcommand\"], filePath, typeName, workspace, schemaName, configPath };\n};\n\nconst output = (data: unknown): void => {\n process.stdout.write(`${JSON.stringify(data, null, 2)}\\n`);\n};\n\nclass CliError extends Error {}\n\nconst cliError = (message: string): CliError => {\n process.stderr.write(`${JSON.stringify({ error: message })}\\n`);\n return new CliError(message);\n};\n\nconst initRegistry = (): ConfigRegistry => {\n const cwd = process.cwd();\n const configPaths = findAllConfigFiles(cwd);\n if (configPaths.length === 0) {\n throw cliError(`No soda-gql config found in ${cwd}`);\n }\n const result = createConfigRegistry(configPaths);\n if (result.isErr()) {\n throw cliError(result.error.message);\n }\n return result.value;\n};\n\nconst indexWorkspace = (registry: ConfigRegistry): void => {\n for (const ctx of registry.getAllContexts()) {\n const filesResult = resolveEntryPaths(ctx.config.include, ctx.config.exclude);\n if (filesResult.isErr()) continue;\n for (const fp of filesResult.value) {\n if (ctx.documentManager.get(fp)) continue;\n try {\n const source = readFileSync(fp, \"utf-8\");\n ctx.documentManager.update(fp, 1, source);\n } catch {\n /* skip unreadable files */\n }\n }\n }\n};\n\nconst resolveContext = (registry: ConfigRegistry, filePath?: string): ConfigContext => {\n const ctx = filePath ? registry.resolveForUri(filePath) : registry.getAllContexts()[0];\n if (!ctx) {\n throw cliError(filePath ? `No soda-gql config covers ${filePath}` : \"No soda-gql config context found\");\n }\n return ctx;\n};\n\nconst readSource = (filePath: string): string => {\n try {\n return readFileSync(filePath, \"utf-8\");\n } catch (e) {\n throw cliError(`Failed to read file: ${e instanceof Error ? e.message : String(e)}`);\n }\n};\n\nconst requireFilePath = (args: CliArgs, subcommand: string): string => {\n const { filePath } = args;\n if (!filePath) throw cliError(`${subcommand} requires a file path argument`);\n return filePath;\n};\n\nconst handleDiagnostics = (registry: ConfigRegistry, args: CliArgs): void => {\n const filePath = requireFilePath(args, \"diagnostics\");\n const ctx = resolveContext(registry, filePath);\n const source = readSource(filePath);\n const state = ctx.documentManager.update(filePath, 1, source);\n output(collectDiagnostics(state, ctx));\n};\n\nconst handleSchema = (registry: ConfigRegistry, args: CliArgs): void => {\n const ctx = resolveContext(registry, args.configPath);\n const targetSchemaName = args.schemaName ?? ctx.schemaResolver.getSchemaNames()[0];\n if (!targetSchemaName) throw cliError(\"No schema available\");\n\n const entry = ctx.schemaResolver.getSchema(targetSchemaName);\n if (!entry) throw cliError(`Schema '${targetSchemaName}' not found`);\n\n if (args.typeName) {\n const result = introspectType(entry.schema, args.typeName);\n if (!result) throw cliError(`Type '${args.typeName}' not found in schema`);\n output(result);\n } else {\n output(listTypes(entry.schema));\n }\n};\n\nconst handleSymbols = (registry: ConfigRegistry, args: CliArgs): void => {\n const filePath = requireFilePath(args, \"symbols\");\n const ctx = resolveContext(registry, filePath);\n const source = readSource(filePath);\n const state = ctx.documentManager.update(filePath, 1, source);\n const symbols = state.templates\n .filter((t): t is typeof t & { elementName: string } => t.elementName !== undefined)\n .map((t) => ({\n name: t.elementName,\n kind: t.kind,\n typeName: t.typeName,\n schemaName: t.schemaName,\n variables: extractVariablesFromContent(t.content),\n line: computeLineFromOffset(source, t.contentRange.start),\n }));\n output(symbols);\n};\n\n/** Run the soda-gql LSP CLI with the given arguments. */\nexport const runLspCli = async (args: readonly string[]): Promise<void> => {\n const parsed = parseCliArgs(args);\n if (!parsed) {\n process.stderr.write(`${USAGE}\\n`);\n process.exit(args.includes(\"--help\") ? 0 : 1);\n }\n\n try {\n const registry = initRegistry();\n if (parsed.workspace) {\n indexWorkspace(registry);\n }\n\n switch (parsed.subcommand) {\n case \"diagnostics\":\n handleDiagnostics(registry, parsed);\n break;\n case \"schema\":\n handleSchema(registry, parsed);\n break;\n case \"symbols\":\n handleSymbols(registry, parsed);\n break;\n }\n } catch (e) {\n if (e instanceof CliError) process.exit(1);\n throw e;\n }\n};\n"],"mappings":";;;;;;;;;;;;;AAoBA,MAAa,+BAA+B,YAAwC;CAClF,MAAM,QAAQ,QAAQ,MAAM,kBAAkB;AAC9C,QAAO,QAAQ,IAAI,MAAM,GAAG,KAAK;;;AAInC,MAAa,yBAAyB,QAAgB,WAA2B;CAC/E,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,IAAI,OAAO,QAAQ,KAAK;AACpD,MAAI,OAAO,OAAO,KAAM;;AAE1B,QAAO;;AAMT,MAAa,sBAAsB,OAAsB,QAA2C;CAClG,MAAM,cAAcA,qCAAsB,OAAO,IAAI;AACrD,QAAO,CAAC,GAAG,YAAY,CAAC,KAAK,OAAO;EAClC,SAAS,EAAE;EACX,MAAM,EAAE,MAAM,MAAM,OAAO;EAC3B,QAAQ,EAAE,MAAM,MAAM,YAAY;EAClC,UAAU,2BAA2B,EAAE,SAAS;EACjD,EAAE;;AAGL,MAAa,8BAA8B,aAAyC;AAClF,SAAQ,UAAR;EACE,KAAK,EACH,QAAO;EACT,KAAK,EACH,QAAO;EACT,KAAK,EACH,QAAO;EACT,KAAK,EACH,QAAO;EACT,QACE,QAAO;;;;AAKb,MAAa,kBAAkB,QAAuB,aAAqB;CACzE,MAAM,OAAO,OAAO,QAAQ,SAAS;AACrC,KAAI,CAAC,KAAM,QAAO;AAElB,+BAAiB,KAAK,iCAAoB,KAAK,EAAE;EAC/C,MAAM,SAAS,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,KAAK,OAAO;GACzD,MAAM,EAAE;GACR,MAAM,EAAE,KAAK,UAAU;GACvB,MAAM,EAAE,KAAK,KAAK,OAAO;IAAE,MAAM,EAAE;IAAM,MAAM,EAAE,KAAK,UAAU;IAAE,EAAE;GACrE,EAAE;AACH,SAAO;GAAE,MAAM,KAAK;GAAM,gCAAmB,KAAK,GAAG,WAAW;GAAa;GAAQ;;AAEvF,8BAAgB,KAAK,EAAE;AACrB,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM;GAAS,SAAS,KAAK,UAAU,CAAC,KAAK,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;GAAE;;AAEpG,6BAAe,KAAK,EAAE;AACpB,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM;GAAQ,QAAQ,KAAK,WAAW,CAAC,KAAK,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;GAAE;;AAEnG,oCAAsB,KAAK,EAAE;EAC3B,MAAM,SAAS,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,KAAK,OAAO;GACzD,MAAM,EAAE;GACR,MAAM,EAAE,KAAK,UAAU;GACxB,EAAE;AACH,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM;GAAgB;GAAQ;;AAE1D,+BAAiB,KAAK,EAAE;AACtB,SAAO;GAAE,MAAM,KAAK;GAAM,MAAM;GAAU;;AAE5C,QAAO;;;AAIT,MAAa,aAAa,WAA0B;CAClD,MAAM,UAAU,OAAO,YAAY;CACnC,MAAM,QAAQ,OAAO,OAAO,QAAQ,CACjC,QAAQ,MAAM,CAAC,EAAE,KAAK,WAAW,KAAK,CAAC,CACvC,KAAK,OAAO;EAAE,MAAM,EAAE;EAAM,MAAM,YAAY,EAAE;EAAE,EAAE;AACvD,QAAO,EAAE,OAAO;;AAGlB,MAAM,eAAe,SAAmC;AACtD,+BAAiB,KAAK,CAAE,QAAO;AAC/B,kCAAoB,KAAK,CAAE,QAAO;AAClC,8BAAgB,KAAK,CAAE,QAAO;AAC9B,6BAAe,KAAK,CAAE,QAAO;AAC7B,oCAAsB,KAAK,CAAE,QAAO;AACpC,+BAAiB,KAAK,CAAE,QAAO;AAC/B,QAAO;;;;;;;;;;ACvFT,MAAM,QAAQ;;;;;;;;;;;;AAad,MAAa,gBAAgB,SAAiD;CAC5E,MAAM,aAAa,KAAK;AACxB,KAAI,CAAC,cAAc,eAAe,YAAY,CAAC;EAAC;EAAe;EAAU;EAAU,CAAC,SAAS,WAAW,EAAE;AACxG,SAAO;;CAGT,IAAIC;CACJ,IAAIC;CACJ,IAAI,YAAY;CAChB,IAAIC;CACJ,IAAIC;CAEJ,IAAI,IAAI;CAER,MAAM,WAAW,KAAK;AACtB,KAAI,aAAa,aAAa,CAAC,SAAS,WAAW,KAAK,EAAE;AACxD,MAAI,eAAe,UAAU;AAC3B,cAAW;SACN;AACL,qCAAmB,SAAS;;AAE9B;;AAIF,QAAO,IAAI,KAAK,QAAQ,KAAK;EAC3B,MAAM,MAAM,KAAK;AACjB,MAAI,QAAQ,UAAW;AACvB,MAAI,QAAQ,eAAe;AACzB,eAAY;aACH,QAAQ,cAAc,IAAI,IAAI,KAAK,QAAQ;AACpD,gBAAa,KAAK,EAAE;aACX,QAAQ,cAAc,IAAI,IAAI,KAAK,QAAQ;GACpD,MAAM,UAAU,KAAK,EAAE;AACvB,OAAI,YAAY,UAAW,qCAAqB,QAAQ;;;AAI5D,QAAO;EAAc;EAAqC;EAAU;EAAU;EAAW;EAAY;EAAY;;AAGnH,MAAM,UAAU,SAAwB;AACtC,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC,IAAI;;AAG5D,IAAM,WAAN,cAAuB,MAAM;AAE7B,MAAM,YAAY,YAA8B;AAC9C,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC,CAAC,IAAI;AAC/D,QAAO,IAAI,SAAS,QAAQ;;AAG9B,MAAM,qBAAqC;CACzC,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,wDAAiC,IAAI;AAC3C,KAAI,YAAY,WAAW,GAAG;AAC5B,QAAM,SAAS,+BAA+B,MAAM;;CAEtD,MAAM,SAASC,oCAAqB,YAAY;AAChD,KAAI,OAAO,OAAO,EAAE;AAClB,QAAM,SAAS,OAAO,MAAM,QAAQ;;AAEtC,QAAO,OAAO;;AAGhB,MAAM,kBAAkB,aAAmC;AACzD,MAAK,MAAM,OAAO,SAAS,gBAAgB,EAAE;EAC3C,MAAM,wDAAgC,IAAI,OAAO,SAAS,IAAI,OAAO,QAAQ;AAC7E,MAAI,YAAY,OAAO,CAAE;AACzB,OAAK,MAAM,MAAM,YAAY,OAAO;AAClC,OAAI,IAAI,gBAAgB,IAAI,GAAG,CAAE;AACjC,OAAI;IACF,MAAM,mCAAsB,IAAI,QAAQ;AACxC,QAAI,gBAAgB,OAAO,IAAI,GAAG,OAAO;WACnC;;;;AAOd,MAAM,kBAAkB,UAA0B,aAAqC;CACrF,MAAM,MAAM,WAAW,SAAS,cAAc,SAAS,GAAG,SAAS,gBAAgB,CAAC;AACpF,KAAI,CAAC,KAAK;AACR,QAAM,SAAS,WAAW,6BAA6B,aAAa,mCAAmC;;AAEzG,QAAO;;AAGT,MAAM,cAAc,aAA6B;AAC/C,KAAI;AACF,mCAAoB,UAAU,QAAQ;UAC/B,GAAG;AACV,QAAM,SAAS,wBAAwB,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,GAAG;;;AAIxF,MAAM,mBAAmB,MAAe,eAA+B;CACrE,MAAM,EAAE,aAAa;AACrB,KAAI,CAAC,SAAU,OAAM,SAAS,GAAG,WAAW,gCAAgC;AAC5E,QAAO;;AAGT,MAAM,qBAAqB,UAA0B,SAAwB;CAC3E,MAAM,WAAW,gBAAgB,MAAM,cAAc;CACrD,MAAM,MAAM,eAAe,UAAU,SAAS;CAC9C,MAAM,SAAS,WAAW,SAAS;CACnC,MAAM,QAAQ,IAAI,gBAAgB,OAAO,UAAU,GAAG,OAAO;AAC7D,QAAO,mBAAmB,OAAO,IAAI,CAAC;;AAGxC,MAAM,gBAAgB,UAA0B,SAAwB;CACtE,MAAM,MAAM,eAAe,UAAU,KAAK,WAAW;CACrD,MAAM,mBAAmB,KAAK,cAAc,IAAI,eAAe,gBAAgB,CAAC;AAChF,KAAI,CAAC,iBAAkB,OAAM,SAAS,sBAAsB;CAE5D,MAAM,QAAQ,IAAI,eAAe,UAAU,iBAAiB;AAC5D,KAAI,CAAC,MAAO,OAAM,SAAS,WAAW,iBAAiB,aAAa;AAEpE,KAAI,KAAK,UAAU;EACjB,MAAM,SAAS,eAAe,MAAM,QAAQ,KAAK,SAAS;AAC1D,MAAI,CAAC,OAAQ,OAAM,SAAS,SAAS,KAAK,SAAS,uBAAuB;AAC1E,SAAO,OAAO;QACT;AACL,SAAO,UAAU,MAAM,OAAO,CAAC;;;AAInC,MAAM,iBAAiB,UAA0B,SAAwB;CACvE,MAAM,WAAW,gBAAgB,MAAM,UAAU;CACjD,MAAM,MAAM,eAAe,UAAU,SAAS;CAC9C,MAAM,SAAS,WAAW,SAAS;CACnC,MAAM,QAAQ,IAAI,gBAAgB,OAAO,UAAU,GAAG,OAAO;CAC7D,MAAM,UAAU,MAAM,UACnB,QAAQ,MAA+C,EAAE,gBAAgB,UAAU,CACnF,KAAK,OAAO;EACX,MAAM,EAAE;EACR,MAAM,EAAE;EACR,UAAU,EAAE;EACZ,YAAY,EAAE;EACd,WAAW,4BAA4B,EAAE,QAAQ;EACjD,MAAM,sBAAsB,QAAQ,EAAE,aAAa,MAAM;EAC1D,EAAE;AACL,QAAO,QAAQ;;;AAIjB,MAAa,YAAY,OAAO,SAA2C;CACzE,MAAM,SAAS,aAAa,KAAK;AACjC,KAAI,CAAC,QAAQ;AACX,UAAQ,OAAO,MAAM,GAAG,MAAM,IAAI;AAClC,UAAQ,KAAK,KAAK,SAAS,SAAS,GAAG,IAAI,EAAE;;AAG/C,KAAI;EACF,MAAM,WAAW,cAAc;AAC/B,MAAI,OAAO,WAAW;AACpB,kBAAe,SAAS;;AAG1B,UAAQ,OAAO,YAAf;GACE,KAAK;AACH,sBAAkB,UAAU,OAAO;AACnC;GACF,KAAK;AACH,iBAAa,UAAU,OAAO;AAC9B;GACF,KAAK;AACH,kBAAc,UAAU,OAAO;AAC/B;;UAEG,GAAG;AACV,MAAI,aAAa,SAAU,SAAQ,KAAK,EAAE;AAC1C,QAAM"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import * as _swc_core0 from "@swc/core";
|
|
2
2
|
import { GraphqlSystemIdentifyHelper } from "@soda-gql/builder";
|
|
3
|
-
import { ExtractedTemplateWithPosition, OperationKind } from "@soda-gql/common/template-extraction";
|
|
3
|
+
import { ExtractedFieldTree, ExtractedTemplateWithPosition, OperationKind } from "@soda-gql/common/template-extraction";
|
|
4
4
|
import * as graphql0 from "graphql";
|
|
5
5
|
import { DocumentNode, FragmentDefinitionNode } from "graphql";
|
|
6
6
|
import { Result } from "neverthrow";
|
|
7
7
|
import { ResolvedSodaGqlConfig } from "@soda-gql/config";
|
|
8
|
-
import { Connection } from "vscode-languageserver/node";
|
|
8
|
+
import { Connection, InitializeParams, InitializeResult } from "vscode-languageserver/node";
|
|
9
9
|
|
|
10
|
+
//#region packages/lsp/src/cli.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* One-shot CLI runner: exposes soda-gql GraphQL intelligence as CLI subcommands.
|
|
13
|
+
* Provides a stable, daemon-free interface for Claude Code skills.
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
interface CliArgs {
|
|
17
|
+
readonly subcommand: "diagnostics" | "schema" | "symbols";
|
|
18
|
+
readonly filePath?: string;
|
|
19
|
+
readonly typeName?: string;
|
|
20
|
+
readonly workspace: boolean;
|
|
21
|
+
readonly schemaName?: string;
|
|
22
|
+
readonly configPath?: string;
|
|
23
|
+
}
|
|
24
|
+
declare const parseCliArgs: (args: readonly string[]) => CliArgs | undefined;
|
|
25
|
+
/** Run the soda-gql LSP CLI with the given arguments. */
|
|
26
|
+
declare const runLspCli: (args: readonly string[]) => Promise<void>;
|
|
27
|
+
//#endregion
|
|
10
28
|
//#region packages/lsp/src/types.d.ts
|
|
11
29
|
/** A single tagged template extracted from a TypeScript file (with guaranteed position info). */
|
|
12
30
|
type ExtractedTemplate = ExtractedTemplateWithPosition;
|
|
@@ -16,6 +34,8 @@ type DocumentState = {
|
|
|
16
34
|
readonly version: number;
|
|
17
35
|
readonly source: string;
|
|
18
36
|
readonly templates: readonly ExtractedTemplate[];
|
|
37
|
+
/** Callback builder field call trees (schema-independent, resolved lazily). */
|
|
38
|
+
readonly fieldTrees: readonly ExtractedFieldTree[];
|
|
19
39
|
/** Set when @swc/core could not be loaded; template extraction is skipped. */
|
|
20
40
|
readonly swcUnavailable?: true;
|
|
21
41
|
};
|
|
@@ -51,6 +71,10 @@ type DocumentManager = {
|
|
|
51
71
|
readonly get: (uri: string) => DocumentState | undefined;
|
|
52
72
|
readonly remove: (uri: string) => void;
|
|
53
73
|
readonly findTemplateAtOffset: (uri: string, offset: number) => ExtractedTemplate | undefined;
|
|
74
|
+
/** Find the field call tree containing the given offset. */
|
|
75
|
+
readonly findFieldTreeAtOffset: (uri: string, offset: number) => ExtractedFieldTree | undefined;
|
|
76
|
+
/** Find a template whose typeNameSpan contains the given offset. */
|
|
77
|
+
readonly findTemplateByTypeNameOffset: (uri: string, offset: number) => ExtractedTemplate | undefined;
|
|
54
78
|
/** Get fragments from other documents for a given schema, excluding the specified URI. */
|
|
55
79
|
readonly getExternalFragments: (uri: string, schemaName: string) => readonly IndexedFragment[];
|
|
56
80
|
/** Get ALL indexed fragments (including self) for a given schema. */
|
|
@@ -71,8 +95,21 @@ type SwcLoaderOptions = {
|
|
|
71
95
|
/**
|
|
72
96
|
* Reconstruct full GraphQL source from an extracted template.
|
|
73
97
|
* Prepends the definition header from curried tag call arguments.
|
|
98
|
+
*
|
|
99
|
+
* For callback-variables templates (source === "callback-variables"), wraps the
|
|
100
|
+
* partial variables string in a dummy operation to produce valid GraphQL that
|
|
101
|
+
* graphql-language-service can parse.
|
|
74
102
|
*/
|
|
75
103
|
declare const reconstructGraphql: (template: ExtractedTemplate) => string;
|
|
104
|
+
/**
|
|
105
|
+
* Compute the length of the synthesized prefix before the template content
|
|
106
|
+
* in the reconstructed GraphQL string.
|
|
107
|
+
*
|
|
108
|
+
* For tagged templates, headerLen = reconstructed.length - content.length (content is at the end).
|
|
109
|
+
* For callback-variables, content is in the MIDDLE (prefix + content + suffix), so we must
|
|
110
|
+
* compute the prefix length explicitly.
|
|
111
|
+
*/
|
|
112
|
+
declare const computeHeaderLen: (template: ExtractedTemplate, reconstructed: string) => number;
|
|
76
113
|
/** Create a document manager that tracks open documents and extracts templates. */
|
|
77
114
|
declare const createDocumentManager: (helper: GraphqlSystemIdentifyHelper, swcOptions?: SwcLoaderOptions) => DocumentManager;
|
|
78
115
|
//#endregion
|
|
@@ -222,9 +259,11 @@ declare const createSchemaResolver: (config: ResolvedSodaGqlConfig) => Result<Sc
|
|
|
222
259
|
//#region packages/lsp/src/server.d.ts
|
|
223
260
|
type LspServerOptions = {
|
|
224
261
|
readonly connection?: Connection;
|
|
262
|
+
readonly initializeParams?: InitializeParams;
|
|
225
263
|
};
|
|
226
264
|
declare const createLspServer: (options?: LspServerOptions) => {
|
|
227
265
|
start: () => void;
|
|
266
|
+
initializeResult: InitializeResult<any> | undefined;
|
|
228
267
|
};
|
|
229
268
|
/** One-time SWC unavailable notification state. */
|
|
230
269
|
type SwcNotificationState = {
|
|
@@ -233,5 +272,5 @@ type SwcNotificationState = {
|
|
|
233
272
|
/** Check if SWC is unavailable and show a one-time error notification. */
|
|
234
273
|
declare const checkSwcUnavailable: (swcUnavailable: boolean | undefined, state: SwcNotificationState, showError: (message: string) => void) => void;
|
|
235
274
|
//#endregion
|
|
236
|
-
export { type DocumentManager, type DocumentState, type ExtractedTemplate, type FragmentSpreadLocation, type IndexedFragment, type LspError, type LspErrorCode, type LspResult, type LspServerOptions, type OperationKind, type PositionMapper, type SchemaEntry, type SchemaResolver, createDocumentManager, createLspServer, createPositionMapper, createSchemaResolver, lspErrors, preprocessFragmentArgs };
|
|
275
|
+
export { type DocumentManager, type DocumentState, type ExtractedTemplate, type FragmentSpreadLocation, type IndexedFragment, type LspError, type LspErrorCode, type LspResult, type LspServerOptions, type OperationKind, type PositionMapper, type SchemaEntry, type SchemaResolver, createDocumentManager, createLspServer, createPositionMapper, createSchemaResolver, lspErrors, preprocessFragmentArgs, runLspCli };
|
|
237
276
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/document-manager.ts","../src/errors.ts","../src/fragment-args-preprocessor.ts","../src/position-mapping.ts","../src/schema-resolver.ts","../src/server.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/cli.ts","../src/types.ts","../src/document-manager.ts","../src/errors.ts","../src/fragment-args-preprocessor.ts","../src/position-mapping.ts","../src/schema-resolver.ts","../src/server.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GraphqlSystemIdentifyHelper } from "@soda-gql/builder";
|
|
2
|
+
import { ResolvedSodaGqlConfig } from "@soda-gql/config";
|
|
2
3
|
import * as graphql0 from "graphql";
|
|
3
4
|
import { DocumentNode, FragmentDefinitionNode } from "graphql";
|
|
5
|
+
import { ExtractedFieldTree, ExtractedTemplateWithPosition, OperationKind } from "@soda-gql/common/template-extraction";
|
|
4
6
|
import { Result } from "neverthrow";
|
|
5
|
-
import {
|
|
6
|
-
import { Connection } from "vscode-languageserver/node";
|
|
7
|
-
import { GraphqlSystemIdentifyHelper } from "@soda-gql/builder";
|
|
7
|
+
import { Connection, InitializeParams, InitializeResult } from "vscode-languageserver/node";
|
|
8
8
|
import * as _swc_core0 from "@swc/core";
|
|
9
9
|
|
|
10
|
+
//#region packages/lsp/src/cli.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* One-shot CLI runner: exposes soda-gql GraphQL intelligence as CLI subcommands.
|
|
13
|
+
* Provides a stable, daemon-free interface for Claude Code skills.
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
interface CliArgs {
|
|
17
|
+
readonly subcommand: "diagnostics" | "schema" | "symbols";
|
|
18
|
+
readonly filePath?: string;
|
|
19
|
+
readonly typeName?: string;
|
|
20
|
+
readonly workspace: boolean;
|
|
21
|
+
readonly schemaName?: string;
|
|
22
|
+
readonly configPath?: string;
|
|
23
|
+
}
|
|
24
|
+
declare const parseCliArgs: (args: readonly string[]) => CliArgs | undefined;
|
|
25
|
+
/** Run the soda-gql LSP CLI with the given arguments. */
|
|
26
|
+
declare const runLspCli: (args: readonly string[]) => Promise<void>;
|
|
27
|
+
//#endregion
|
|
10
28
|
//#region packages/lsp/src/types.d.ts
|
|
11
29
|
/** A single tagged template extracted from a TypeScript file (with guaranteed position info). */
|
|
12
30
|
type ExtractedTemplate = ExtractedTemplateWithPosition;
|
|
@@ -16,6 +34,8 @@ type DocumentState = {
|
|
|
16
34
|
readonly version: number;
|
|
17
35
|
readonly source: string;
|
|
18
36
|
readonly templates: readonly ExtractedTemplate[];
|
|
37
|
+
/** Callback builder field call trees (schema-independent, resolved lazily). */
|
|
38
|
+
readonly fieldTrees: readonly ExtractedFieldTree[];
|
|
19
39
|
/** Set when @swc/core could not be loaded; template extraction is skipped. */
|
|
20
40
|
readonly swcUnavailable?: true;
|
|
21
41
|
};
|
|
@@ -51,6 +71,10 @@ type DocumentManager = {
|
|
|
51
71
|
readonly get: (uri: string) => DocumentState | undefined;
|
|
52
72
|
readonly remove: (uri: string) => void;
|
|
53
73
|
readonly findTemplateAtOffset: (uri: string, offset: number) => ExtractedTemplate | undefined;
|
|
74
|
+
/** Find the field call tree containing the given offset. */
|
|
75
|
+
readonly findFieldTreeAtOffset: (uri: string, offset: number) => ExtractedFieldTree | undefined;
|
|
76
|
+
/** Find a template whose typeNameSpan contains the given offset. */
|
|
77
|
+
readonly findTemplateByTypeNameOffset: (uri: string, offset: number) => ExtractedTemplate | undefined;
|
|
54
78
|
/** Get fragments from other documents for a given schema, excluding the specified URI. */
|
|
55
79
|
readonly getExternalFragments: (uri: string, schemaName: string) => readonly IndexedFragment[];
|
|
56
80
|
/** Get ALL indexed fragments (including self) for a given schema. */
|
|
@@ -71,8 +95,21 @@ type SwcLoaderOptions = {
|
|
|
71
95
|
/**
|
|
72
96
|
* Reconstruct full GraphQL source from an extracted template.
|
|
73
97
|
* Prepends the definition header from curried tag call arguments.
|
|
98
|
+
*
|
|
99
|
+
* For callback-variables templates (source === "callback-variables"), wraps the
|
|
100
|
+
* partial variables string in a dummy operation to produce valid GraphQL that
|
|
101
|
+
* graphql-language-service can parse.
|
|
74
102
|
*/
|
|
75
103
|
declare const reconstructGraphql: (template: ExtractedTemplate) => string;
|
|
104
|
+
/**
|
|
105
|
+
* Compute the length of the synthesized prefix before the template content
|
|
106
|
+
* in the reconstructed GraphQL string.
|
|
107
|
+
*
|
|
108
|
+
* For tagged templates, headerLen = reconstructed.length - content.length (content is at the end).
|
|
109
|
+
* For callback-variables, content is in the MIDDLE (prefix + content + suffix), so we must
|
|
110
|
+
* compute the prefix length explicitly.
|
|
111
|
+
*/
|
|
112
|
+
declare const computeHeaderLen: (template: ExtractedTemplate, reconstructed: string) => number;
|
|
76
113
|
/** Create a document manager that tracks open documents and extracts templates. */
|
|
77
114
|
declare const createDocumentManager: (helper: GraphqlSystemIdentifyHelper, swcOptions?: SwcLoaderOptions) => DocumentManager;
|
|
78
115
|
//#endregion
|
|
@@ -222,9 +259,11 @@ declare const createSchemaResolver: (config: ResolvedSodaGqlConfig) => Result<Sc
|
|
|
222
259
|
//#region packages/lsp/src/server.d.ts
|
|
223
260
|
type LspServerOptions = {
|
|
224
261
|
readonly connection?: Connection;
|
|
262
|
+
readonly initializeParams?: InitializeParams;
|
|
225
263
|
};
|
|
226
264
|
declare const createLspServer: (options?: LspServerOptions) => {
|
|
227
265
|
start: () => void;
|
|
266
|
+
initializeResult: InitializeResult<any> | undefined;
|
|
228
267
|
};
|
|
229
268
|
/** One-time SWC unavailable notification state. */
|
|
230
269
|
type SwcNotificationState = {
|
|
@@ -233,5 +272,5 @@ type SwcNotificationState = {
|
|
|
233
272
|
/** Check if SWC is unavailable and show a one-time error notification. */
|
|
234
273
|
declare const checkSwcUnavailable: (swcUnavailable: boolean | undefined, state: SwcNotificationState, showError: (message: string) => void) => void;
|
|
235
274
|
//#endregion
|
|
236
|
-
export { type DocumentManager, type DocumentState, type ExtractedTemplate, type FragmentSpreadLocation, type IndexedFragment, type LspError, type LspErrorCode, type LspResult, type LspServerOptions, type OperationKind, type PositionMapper, type SchemaEntry, type SchemaResolver, createDocumentManager, createLspServer, createPositionMapper, createSchemaResolver, lspErrors, preprocessFragmentArgs };
|
|
275
|
+
export { type DocumentManager, type DocumentState, type ExtractedTemplate, type FragmentSpreadLocation, type IndexedFragment, type LspError, type LspErrorCode, type LspResult, type LspServerOptions, type OperationKind, type PositionMapper, type SchemaEntry, type SchemaResolver, createDocumentManager, createLspServer, createPositionMapper, createSchemaResolver, lspErrors, preprocessFragmentArgs, runLspCli };
|
|
237
276
|
//# sourceMappingURL=index.d.mts.map
|