@514labs/moose-lsp 1.0.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.
Files changed (84) hide show
  1. package/dist/clickhouseData.d.ts +78 -0
  2. package/dist/clickhouseData.js +112 -0
  3. package/dist/clickhouseData.js.map +1 -0
  4. package/dist/clickhouseData.test.d.ts +2 -0
  5. package/dist/clickhouseData.test.js +93 -0
  6. package/dist/clickhouseData.test.js.map +1 -0
  7. package/dist/clickhouseVersion.d.ts +49 -0
  8. package/dist/clickhouseVersion.js +149 -0
  9. package/dist/clickhouseVersion.js.map +1 -0
  10. package/dist/clickhouseVersion.test.d.ts +2 -0
  11. package/dist/clickhouseVersion.test.js +114 -0
  12. package/dist/clickhouseVersion.test.js.map +1 -0
  13. package/dist/codeActions.d.ts +27 -0
  14. package/dist/codeActions.js +173 -0
  15. package/dist/codeActions.js.map +1 -0
  16. package/dist/codeActions.test.d.ts +2 -0
  17. package/dist/codeActions.test.js +207 -0
  18. package/dist/codeActions.test.js.map +1 -0
  19. package/dist/completions.d.ts +22 -0
  20. package/dist/completions.js +228 -0
  21. package/dist/completions.js.map +1 -0
  22. package/dist/completions.test.d.ts +2 -0
  23. package/dist/completions.test.js +283 -0
  24. package/dist/completions.test.js.map +1 -0
  25. package/dist/data/clickhouse-25.6.json +30772 -0
  26. package/dist/data/clickhouse-25.8.json +31872 -0
  27. package/dist/diagnostics.d.ts +18 -0
  28. package/dist/diagnostics.js +54 -0
  29. package/dist/diagnostics.js.map +1 -0
  30. package/dist/diagnostics.test.d.ts +2 -0
  31. package/dist/diagnostics.test.js +110 -0
  32. package/dist/diagnostics.test.js.map +1 -0
  33. package/dist/formatting.d.ts +36 -0
  34. package/dist/formatting.js +70 -0
  35. package/dist/formatting.js.map +1 -0
  36. package/dist/formatting.test.d.ts +2 -0
  37. package/dist/formatting.test.js +93 -0
  38. package/dist/formatting.test.js.map +1 -0
  39. package/dist/hover.d.ts +43 -0
  40. package/dist/hover.js +198 -0
  41. package/dist/hover.js.map +1 -0
  42. package/dist/hover.test.d.ts +2 -0
  43. package/dist/hover.test.js +319 -0
  44. package/dist/hover.test.js.map +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +26 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/projectDetector.d.ts +7 -0
  49. package/dist/projectDetector.js +66 -0
  50. package/dist/projectDetector.js.map +1 -0
  51. package/dist/projectDetector.test.d.ts +2 -0
  52. package/dist/projectDetector.test.js +114 -0
  53. package/dist/projectDetector.test.js.map +1 -0
  54. package/dist/server.d.ts +2 -0
  55. package/dist/server.integration.test.d.ts +2 -0
  56. package/dist/server.integration.test.js +190 -0
  57. package/dist/server.integration.test.js.map +1 -0
  58. package/dist/server.js +426 -0
  59. package/dist/server.js.map +1 -0
  60. package/dist/serverLogic.d.ts +32 -0
  61. package/dist/serverLogic.js +51 -0
  62. package/dist/serverLogic.js.map +1 -0
  63. package/dist/serverLogic.test.d.ts +2 -0
  64. package/dist/serverLogic.test.js +264 -0
  65. package/dist/serverLogic.test.js.map +1 -0
  66. package/dist/sqlExtractor.d.ts +15 -0
  67. package/dist/sqlExtractor.js +106 -0
  68. package/dist/sqlExtractor.js.map +1 -0
  69. package/dist/sqlExtractor.test.d.ts +2 -0
  70. package/dist/sqlExtractor.test.js +268 -0
  71. package/dist/sqlExtractor.test.js.map +1 -0
  72. package/dist/sqlLocations.d.ts +31 -0
  73. package/dist/sqlLocations.js +52 -0
  74. package/dist/sqlLocations.js.map +1 -0
  75. package/dist/sqlLocations.test.d.ts +2 -0
  76. package/dist/sqlLocations.test.js +95 -0
  77. package/dist/sqlLocations.test.js.map +1 -0
  78. package/dist/typescriptService.d.ts +29 -0
  79. package/dist/typescriptService.js +138 -0
  80. package/dist/typescriptService.js.map +1 -0
  81. package/dist/typescriptService.test.d.ts +2 -0
  82. package/dist/typescriptService.test.js +201 -0
  83. package/dist/typescriptService.test.js.map +1 -0
  84. package/package.json +47 -0
package/dist/server.js ADDED
@@ -0,0 +1,426 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+ var path = __toESM(require("node:path"));
26
+ var import_moose_sql_validator_wasm = require("@514labs/moose-sql-validator-wasm");
27
+ var import_node = require("vscode-languageserver/node");
28
+ var import_vscode_languageserver_textdocument = require("vscode-languageserver-textdocument");
29
+ var import_clickhouseData = require("./clickhouseData");
30
+ var import_clickhouseVersion = require("./clickhouseVersion");
31
+ var import_codeActions = require("./codeActions");
32
+ var import_diagnostics = require("./diagnostics");
33
+ var import_hover = require("./hover");
34
+ var import_projectDetector = require("./projectDetector");
35
+ var import_serverLogic = require("./serverLogic");
36
+ var import_sqlExtractor = require("./sqlExtractor");
37
+ var import_typescriptService = require("./typescriptService");
38
+ const connection = (0, import_node.createConnection)(import_node.ProposedFeatures.all);
39
+ const documents = new import_node.TextDocuments(import_vscode_languageserver_textdocument.TextDocument);
40
+ let mooseProjectRoot = null;
41
+ let tsService = null;
42
+ let clickhouseData = null;
43
+ let clientSupportsSnippets = false;
44
+ const validationTimers = /* @__PURE__ */ new Map();
45
+ const DEBOUNCE_MS = 300;
46
+ function validateDocument(document) {
47
+ if (!tsService?.isHealthy() || !mooseProjectRoot) return;
48
+ const filePath = new URL(document.uri).pathname;
49
+ if (!(0, import_serverLogic.shouldValidateFile)(filePath, mooseProjectRoot)) return;
50
+ connection.console.log(`Validating file: ${filePath}`);
51
+ try {
52
+ tsService.updateFile(filePath, document.getText());
53
+ const sourceFile = tsService.getSourceFile(filePath);
54
+ if (!sourceFile) {
55
+ connection.console.log(`Could not get source file for: ${filePath}`);
56
+ return;
57
+ }
58
+ const sqlLocations = (0, import_sqlExtractor.extractSqlLocations)(
59
+ sourceFile,
60
+ tsService.getTypeChecker()
61
+ );
62
+ connection.console.log(
63
+ `Found ${sqlLocations.length} SQL templates in ${filePath}`
64
+ );
65
+ const diagnosticsMap = (0, import_serverLogic.validateSqlLocations)(
66
+ sqlLocations,
67
+ import_moose_sql_validator_wasm.validateSql,
68
+ import_diagnostics.createLocationDiagnostic
69
+ );
70
+ const diagnostics = diagnosticsMap.get(document.uri) || [];
71
+ connection.sendDiagnostics({ uri: document.uri, diagnostics });
72
+ if (diagnostics.length > 0) {
73
+ connection.console.log(
74
+ `Published ${diagnostics.length} diagnostic(s) for ${filePath}`
75
+ );
76
+ }
77
+ } catch (error) {
78
+ if (error instanceof Error) {
79
+ connection.console.error(`Error validating SQL: ${error.message}`);
80
+ } else {
81
+ connection.console.error(`Unknown error validating SQL: ${error}`);
82
+ }
83
+ }
84
+ }
85
+ function performInitialScan() {
86
+ if (!tsService?.isHealthy() || !mooseProjectRoot) return;
87
+ connection.console.log("Running initial SQL validation scan...");
88
+ try {
89
+ const sourceFiles = tsService.getSourceFiles();
90
+ const typeChecker = tsService.getTypeChecker();
91
+ const allLocations = (0, import_sqlExtractor.extractAllSqlLocations)(sourceFiles, typeChecker);
92
+ connection.console.log(
93
+ `Found ${allLocations.length} SQL templates in ${sourceFiles.length} files`
94
+ );
95
+ if (allLocations.length === 0) return;
96
+ const diagnosticsMap = (0, import_serverLogic.validateSqlLocations)(
97
+ allLocations,
98
+ import_moose_sql_validator_wasm.validateSql,
99
+ import_diagnostics.createLocationDiagnostic
100
+ );
101
+ const allFileUris = /* @__PURE__ */ new Set();
102
+ for (const location of allLocations) {
103
+ allFileUris.add(`file://${location.file}`);
104
+ }
105
+ for (const uri of allFileUris) {
106
+ const diagnostics = diagnosticsMap.get(uri) || [];
107
+ connection.sendDiagnostics({ uri, diagnostics });
108
+ }
109
+ connection.console.log(
110
+ `Published diagnostics for ${allFileUris.size} files (${diagnosticsMap.size} with errors)`
111
+ );
112
+ } catch (error) {
113
+ if (error instanceof Error) {
114
+ connection.console.error(`Error in initial scan: ${error.message}`);
115
+ } else {
116
+ connection.console.error(`Unknown error in initial scan: ${error}`);
117
+ }
118
+ }
119
+ }
120
+ async function loadClickHouseCompletionData(projectRoot) {
121
+ try {
122
+ let version = await (0, import_clickhouseVersion.detectClickHouseVersion)(projectRoot);
123
+ if (version) {
124
+ connection.console.log(`Detected ClickHouse version: ${version}`);
125
+ } else {
126
+ const available = (0, import_clickhouseData.getAvailableVersions)();
127
+ if (available.length > 0) {
128
+ version = available[0];
129
+ connection.console.log(
130
+ `No ClickHouse version detected, using latest: ${version}`
131
+ );
132
+ } else {
133
+ connection.console.warn("No ClickHouse data files available");
134
+ return;
135
+ }
136
+ }
137
+ clickhouseData = await (0, import_clickhouseData.loadClickHouseData)(version);
138
+ if (clickhouseData.warning) {
139
+ connection.console.warn(clickhouseData.warning);
140
+ }
141
+ const jsonData = JSON.stringify(clickhouseData);
142
+ const initResult = (0, import_moose_sql_validator_wasm.initCompletionData)(jsonData);
143
+ if (!initResult.success) {
144
+ connection.console.error(
145
+ `Failed to init completion data: ${initResult.error}`
146
+ );
147
+ return;
148
+ }
149
+ connection.console.log(
150
+ `Loaded ClickHouse data: ${clickhouseData.functions.length} functions, ${clickhouseData.keywords.length} keywords`
151
+ );
152
+ } catch (error) {
153
+ if (error instanceof Error) {
154
+ connection.console.error(
155
+ `Failed to load ClickHouse data: ${error.message}`
156
+ );
157
+ } else {
158
+ connection.console.error(`Failed to load ClickHouse data: ${error}`);
159
+ }
160
+ }
161
+ }
162
+ connection.onInitialize(
163
+ async (params) => {
164
+ clientSupportsSnippets = params.capabilities.textDocument?.completion?.completionItem?.snippetSupport ?? false;
165
+ connection.console.log(`Client snippet support: ${clientSupportsSnippets}`);
166
+ const workspaceRoot = params.rootUri ? new URL(params.rootUri).pathname : null;
167
+ if (workspaceRoot) {
168
+ try {
169
+ mooseProjectRoot = await (0, import_projectDetector.detectMooseProject)(workspaceRoot);
170
+ if (mooseProjectRoot) {
171
+ connection.console.log(
172
+ `Moose project detected at: ${mooseProjectRoot}`
173
+ );
174
+ await (0, import_moose_sql_validator_wasm.initValidator)();
175
+ const tsconfigPath = path.join(mooseProjectRoot, "tsconfig.json");
176
+ tsService = (0, import_typescriptService.createTypeScriptService)();
177
+ tsService.initialize(tsconfigPath);
178
+ if (!tsService.isHealthy()) {
179
+ connection.console.error(
180
+ `Failed to initialize TypeScript: ${tsService.getError()}`
181
+ );
182
+ tsService = null;
183
+ } else {
184
+ connection.console.log("TypeScript service initialized");
185
+ performInitialScan();
186
+ }
187
+ await loadClickHouseCompletionData(mooseProjectRoot);
188
+ } else {
189
+ connection.console.log("No Moose project detected in workspace");
190
+ }
191
+ } catch (error) {
192
+ connection.console.error(`Error detecting Moose project: ${error}`);
193
+ }
194
+ }
195
+ return {
196
+ capabilities: {
197
+ textDocumentSync: {
198
+ openClose: true,
199
+ change: import_node.TextDocumentSyncKind.Full,
200
+ // Full sync for as-you-type validation
201
+ save: {
202
+ includeText: true
203
+ }
204
+ },
205
+ codeActionProvider: {
206
+ codeActionKinds: ["source.formatSql"]
207
+ },
208
+ completionProvider: {
209
+ triggerCharacters: [".", "(", " "],
210
+ resolveProvider: false
211
+ },
212
+ hoverProvider: true
213
+ }
214
+ };
215
+ }
216
+ );
217
+ function scheduleValidation(document) {
218
+ const uri = document.uri;
219
+ const existingTimer = validationTimers.get(uri);
220
+ if (existingTimer) {
221
+ clearTimeout(existingTimer);
222
+ }
223
+ const timer = setTimeout(() => {
224
+ validationTimers.delete(uri);
225
+ validateDocument(document);
226
+ }, DEBOUNCE_MS);
227
+ validationTimers.set(uri, timer);
228
+ }
229
+ documents.onDidChangeContent((event) => {
230
+ scheduleValidation(event.document);
231
+ });
232
+ documents.onDidSave((event) => {
233
+ const uri = event.document.uri;
234
+ const existingTimer = validationTimers.get(uri);
235
+ if (existingTimer) {
236
+ clearTimeout(existingTimer);
237
+ validationTimers.delete(uri);
238
+ }
239
+ connection.console.log(`didSave received for: ${uri}`);
240
+ validateDocument(event.document);
241
+ });
242
+ documents.onDidOpen((event) => {
243
+ connection.console.log(`didOpen received for: ${event.document.uri}`);
244
+ validateDocument(event.document);
245
+ });
246
+ documents.onDidClose((event) => {
247
+ const uri = event.document.uri;
248
+ const existingTimer = validationTimers.get(uri);
249
+ if (existingTimer) {
250
+ clearTimeout(existingTimer);
251
+ validationTimers.delete(uri);
252
+ }
253
+ });
254
+ connection.onCodeAction((params) => {
255
+ if (!tsService?.isHealthy() || !mooseProjectRoot) return [];
256
+ const document = documents.get(params.textDocument.uri);
257
+ if (!document) return [];
258
+ const filePath = new URL(params.textDocument.uri).pathname;
259
+ if (!(0, import_serverLogic.shouldValidateFile)(filePath, mooseProjectRoot)) return [];
260
+ try {
261
+ const sourceFile = tsService.getSourceFile(filePath);
262
+ if (!sourceFile) return [];
263
+ const sqlLocations = (0, import_sqlExtractor.extractSqlLocations)(
264
+ sourceFile,
265
+ tsService.getTypeChecker()
266
+ );
267
+ const location = (0, import_codeActions.findSqlTemplateAtPosition)(
268
+ sqlLocations,
269
+ params.range.start.line,
270
+ params.range.start.character
271
+ );
272
+ if (!location) return [];
273
+ const node = (0, import_codeActions.findTemplateNodeById)(sourceFile, location.id);
274
+ if (!node) return [];
275
+ const edit = (0, import_codeActions.createFormatSqlEdit)(sourceFile, node);
276
+ if (!edit) return [];
277
+ return [
278
+ {
279
+ title: "Format SQL",
280
+ kind: `${import_node.CodeActionKind.Source}.formatSql`,
281
+ edit: {
282
+ changes: {
283
+ [params.textDocument.uri]: [edit]
284
+ }
285
+ }
286
+ }
287
+ ];
288
+ } catch {
289
+ return [];
290
+ }
291
+ });
292
+ function mapCompletionItemKind(kind) {
293
+ switch (kind) {
294
+ case "function":
295
+ return import_node.CompletionItemKind.Function;
296
+ case "aggregate_function":
297
+ return import_node.CompletionItemKind.Method;
298
+ case "table_function":
299
+ return import_node.CompletionItemKind.Function;
300
+ case "keyword":
301
+ return import_node.CompletionItemKind.Keyword;
302
+ case "data_type":
303
+ return import_node.CompletionItemKind.TypeParameter;
304
+ case "table_engine":
305
+ return import_node.CompletionItemKind.Class;
306
+ case "format":
307
+ return import_node.CompletionItemKind.Constant;
308
+ case "setting":
309
+ return import_node.CompletionItemKind.Property;
310
+ default:
311
+ return import_node.CompletionItemKind.Text;
312
+ }
313
+ }
314
+ function toRustCompletionItem(rustItem, useSnippets) {
315
+ const kind = mapCompletionItemKind(rustItem.kind);
316
+ let insertText;
317
+ let insertTextFormat;
318
+ if (rustItem.hasParams) {
319
+ if (useSnippets) {
320
+ insertText = `${rustItem.label}($1)$0`;
321
+ insertTextFormat = import_node.InsertTextFormat.Snippet;
322
+ } else {
323
+ insertText = `${rustItem.label}()`;
324
+ insertTextFormat = import_node.InsertTextFormat.PlainText;
325
+ }
326
+ }
327
+ return {
328
+ label: rustItem.label,
329
+ kind,
330
+ detail: rustItem.detail,
331
+ documentation: rustItem.documentation ? { kind: "markdown", value: rustItem.documentation.value } : void 0,
332
+ insertText,
333
+ insertTextFormat,
334
+ sortText: rustItem.sortText
335
+ };
336
+ }
337
+ connection.onCompletion((params) => {
338
+ if (!tsService?.isHealthy() || !mooseProjectRoot || !clickhouseData) {
339
+ return [];
340
+ }
341
+ const document = documents.get(params.textDocument.uri);
342
+ if (!document) return [];
343
+ const filePath = new URL(params.textDocument.uri).pathname;
344
+ if (!(0, import_serverLogic.shouldValidateFile)(filePath, mooseProjectRoot)) return [];
345
+ try {
346
+ const sourceFile = tsService.getSourceFile(filePath);
347
+ if (!sourceFile) return [];
348
+ const sqlLocations = (0, import_sqlExtractor.extractSqlLocations)(
349
+ sourceFile,
350
+ tsService.getTypeChecker()
351
+ );
352
+ const location = (0, import_codeActions.findSqlTemplateAtPosition)(
353
+ sqlLocations,
354
+ params.position.line,
355
+ params.position.character
356
+ );
357
+ if (!location) return [];
358
+ const cursorLine = params.position.line;
359
+ const cursorChar = params.position.character;
360
+ const templateStartLine = location.line - 1;
361
+ const templateStartChar = location.column - 1;
362
+ const sqlText = location.templateText;
363
+ let cursorOffset = 0;
364
+ const lines = sqlText.split("\n");
365
+ const relativeLine = cursorLine - templateStartLine;
366
+ for (let i = 0; i < relativeLine && i < lines.length; i++) {
367
+ cursorOffset += lines[i].length + 1;
368
+ }
369
+ if (relativeLine === 0) {
370
+ cursorOffset += cursorChar - templateStartChar;
371
+ } else if (relativeLine < lines.length) {
372
+ cursorOffset += cursorChar;
373
+ }
374
+ cursorOffset = Math.max(0, Math.min(cursorOffset, sqlText.length));
375
+ const rustCompletions = (0, import_moose_sql_validator_wasm.getCompletions)(sqlText, cursorOffset);
376
+ const lineText = document.getText({
377
+ start: { line: params.position.line, character: 0 },
378
+ end: params.position
379
+ });
380
+ const wordMatch = lineText.match(/[\w]+$/);
381
+ const prefix = wordMatch ? wordMatch[0].toLowerCase() : "";
382
+ const completions = rustCompletions.filter((c) => !prefix || c.label.toLowerCase().startsWith(prefix)).map((c) => toRustCompletionItem(c, clientSupportsSnippets));
383
+ return completions;
384
+ } catch {
385
+ return [];
386
+ }
387
+ });
388
+ connection.onHover((params) => {
389
+ if (!tsService?.isHealthy() || !mooseProjectRoot || !clickhouseData) {
390
+ return null;
391
+ }
392
+ const document = documents.get(params.textDocument.uri);
393
+ if (!document) return null;
394
+ const filePath = new URL(params.textDocument.uri).pathname;
395
+ if (!(0, import_serverLogic.shouldValidateFile)(filePath, mooseProjectRoot)) return null;
396
+ try {
397
+ const sourceFile = tsService.getSourceFile(filePath);
398
+ if (!sourceFile) return null;
399
+ const sqlLocations = (0, import_sqlExtractor.extractSqlLocations)(
400
+ sourceFile,
401
+ tsService.getTypeChecker()
402
+ );
403
+ const location = (0, import_codeActions.findSqlTemplateAtPosition)(
404
+ sqlLocations,
405
+ params.position.line,
406
+ params.position.character
407
+ );
408
+ if (!location) return null;
409
+ const lineText = document.getText({
410
+ start: { line: params.position.line, character: 0 },
411
+ end: { line: params.position.line + 1, character: 0 }
412
+ });
413
+ const word = (0, import_hover.getWordAtPosition)(lineText, params.position.character);
414
+ if (!word) return null;
415
+ const hoverInfo = (0, import_hover.findHoverInfo)(word, clickhouseData);
416
+ if (!hoverInfo) return null;
417
+ return {
418
+ contents: (0, import_hover.createHoverContent)(hoverInfo)
419
+ };
420
+ } catch {
421
+ return null;
422
+ }
423
+ });
424
+ documents.listen(connection);
425
+ connection.listen();
426
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import * as path from 'node:path';\nimport {\n getCompletions,\n initCompletionData,\n initValidator,\n type CompletionItem as RustCompletionItem,\n validateSql,\n} from '@514labs/moose-sql-validator-wasm';\nimport {\n type CodeAction,\n CodeActionKind,\n type CodeActionParams,\n type CompletionItem,\n CompletionItemKind,\n type CompletionParams,\n createConnection,\n type Hover,\n type HoverParams,\n type InitializeParams,\n type InitializeResult,\n InsertTextFormat,\n ProposedFeatures,\n TextDocumentSyncKind,\n TextDocuments,\n} from 'vscode-languageserver/node';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport {\n type ClickHouseData,\n getAvailableVersions,\n loadClickHouseData,\n} from './clickhouseData';\nimport { detectClickHouseVersion } from './clickhouseVersion';\nimport {\n createFormatSqlEdit,\n findSqlTemplateAtPosition,\n findTemplateNodeById,\n} from './codeActions';\n// completions.ts still used by tests but server uses Rust completions\nimport { createLocationDiagnostic } from './diagnostics';\nimport { createHoverContent, findHoverInfo, getWordAtPosition } from './hover';\nimport { detectMooseProject } from './projectDetector';\nimport { shouldValidateFile, validateSqlLocations } from './serverLogic';\nimport { extractAllSqlLocations, extractSqlLocations } from './sqlExtractor';\nimport {\n createTypeScriptService,\n type TypeScriptService,\n} from './typescriptService';\n\nconst connection = createConnection(ProposedFeatures.all);\nconst documents = new TextDocuments(TextDocument);\n\nlet mooseProjectRoot: string | null = null;\nlet tsService: TypeScriptService | null = null;\nlet clickhouseData: ClickHouseData | null = null;\nlet clientSupportsSnippets = false;\n\n// Debounce timers for as-you-type validation\nconst validationTimers = new Map<string, ReturnType<typeof setTimeout>>();\nconst DEBOUNCE_MS = 300;\n\n/**\n * Validates a document and publishes diagnostics.\n * Uses the TypeScript service to extract SQL locations and validate them.\n */\nfunction validateDocument(document: TextDocument): void {\n if (!tsService?.isHealthy() || !mooseProjectRoot) return;\n\n const filePath = new URL(document.uri).pathname;\n if (!shouldValidateFile(filePath, mooseProjectRoot)) return;\n\n connection.console.log(`Validating file: ${filePath}`);\n\n try {\n // Update TypeScript program with latest content\n tsService.updateFile(filePath, document.getText());\n\n const sourceFile = tsService.getSourceFile(filePath);\n if (!sourceFile) {\n connection.console.log(`Could not get source file for: ${filePath}`);\n return;\n }\n\n // Extract SQL locations from this file\n const sqlLocations = extractSqlLocations(\n sourceFile,\n tsService.getTypeChecker(),\n );\n\n connection.console.log(\n `Found ${sqlLocations.length} SQL templates in ${filePath}`,\n );\n\n // Validate and collect diagnostics\n const diagnosticsMap = validateSqlLocations(\n sqlLocations,\n validateSql,\n createLocationDiagnostic,\n );\n\n // Publish diagnostics for this file (empty array clears old diagnostics)\n const diagnostics = diagnosticsMap.get(document.uri) || [];\n connection.sendDiagnostics({ uri: document.uri, diagnostics });\n\n if (diagnostics.length > 0) {\n connection.console.log(\n `Published ${diagnostics.length} diagnostic(s) for ${filePath}`,\n );\n }\n } catch (error) {\n if (error instanceof Error) {\n connection.console.error(`Error validating SQL: ${error.message}`);\n } else {\n connection.console.error(`Unknown error validating SQL: ${error}`);\n }\n }\n}\n\n/**\n * Performs initial full-project scan for SQL templates.\n * Called after TypeScript service is initialized.\n */\nfunction performInitialScan(): void {\n if (!tsService?.isHealthy() || !mooseProjectRoot) return;\n\n connection.console.log('Running initial SQL validation scan...');\n\n try {\n const sourceFiles = tsService.getSourceFiles();\n const typeChecker = tsService.getTypeChecker();\n\n const allLocations = extractAllSqlLocations(sourceFiles, typeChecker);\n connection.console.log(\n `Found ${allLocations.length} SQL templates in ${sourceFiles.length} files`,\n );\n\n if (allLocations.length === 0) return;\n\n // Validate all locations\n const diagnosticsMap = validateSqlLocations(\n allLocations,\n validateSql,\n createLocationDiagnostic,\n );\n\n // Collect all unique file URIs\n const allFileUris = new Set<string>();\n for (const location of allLocations) {\n allFileUris.add(`file://${location.file}`);\n }\n\n // Publish diagnostics for all files (empty arrays clear old diagnostics)\n for (const uri of allFileUris) {\n const diagnostics = diagnosticsMap.get(uri) || [];\n connection.sendDiagnostics({ uri, diagnostics });\n }\n\n connection.console.log(\n `Published diagnostics for ${allFileUris.size} files (${diagnosticsMap.size} with errors)`,\n );\n } catch (error) {\n if (error instanceof Error) {\n connection.console.error(`Error in initial scan: ${error.message}`);\n } else {\n connection.console.error(`Unknown error in initial scan: ${error}`);\n }\n }\n}\n\n/**\n * Loads ClickHouse completion data and initializes Rust completion engine.\n * Falls back to latest available version if detection fails.\n */\nasync function loadClickHouseCompletionData(\n projectRoot: string,\n): Promise<void> {\n try {\n // Try to detect version from docker-compose\n let version = await detectClickHouseVersion(projectRoot);\n\n if (version) {\n connection.console.log(`Detected ClickHouse version: ${version}`);\n } else {\n // Fall back to latest available version\n const available = getAvailableVersions();\n if (available.length > 0) {\n version = available[0]; // Already sorted descending\n connection.console.log(\n `No ClickHouse version detected, using latest: ${version}`,\n );\n } else {\n connection.console.warn('No ClickHouse data files available');\n return;\n }\n }\n\n clickhouseData = await loadClickHouseData(version);\n\n if (clickhouseData.warning) {\n connection.console.warn(clickhouseData.warning);\n }\n\n // Initialize Rust completion engine with the data\n const jsonData = JSON.stringify(clickhouseData);\n const initResult = initCompletionData(jsonData);\n\n if (!initResult.success) {\n connection.console.error(\n `Failed to init completion data: ${initResult.error}`,\n );\n return;\n }\n\n connection.console.log(\n `Loaded ClickHouse data: ${clickhouseData.functions.length} functions, ${clickhouseData.keywords.length} keywords`,\n );\n } catch (error) {\n if (error instanceof Error) {\n connection.console.error(\n `Failed to load ClickHouse data: ${error.message}`,\n );\n } else {\n connection.console.error(`Failed to load ClickHouse data: ${error}`);\n }\n }\n}\n\nconnection.onInitialize(\n async (params: InitializeParams): Promise<InitializeResult> => {\n // Check if client supports snippet completions\n clientSupportsSnippets =\n params.capabilities.textDocument?.completion?.completionItem\n ?.snippetSupport ?? false;\n\n connection.console.log(`Client snippet support: ${clientSupportsSnippets}`);\n\n const workspaceRoot = params.rootUri\n ? new URL(params.rootUri).pathname\n : null;\n\n if (workspaceRoot) {\n try {\n mooseProjectRoot = await detectMooseProject(workspaceRoot);\n if (mooseProjectRoot) {\n connection.console.log(\n `Moose project detected at: ${mooseProjectRoot}`,\n );\n\n // Initialize WASM SQL validator\n await initValidator();\n\n // Initialize TypeScript service\n const tsconfigPath = path.join(mooseProjectRoot, 'tsconfig.json');\n tsService = createTypeScriptService();\n tsService.initialize(tsconfigPath);\n\n if (!tsService.isHealthy()) {\n connection.console.error(\n `Failed to initialize TypeScript: ${tsService.getError()}`,\n );\n tsService = null;\n } else {\n connection.console.log('TypeScript service initialized');\n // Perform initial full-project scan\n performInitialScan();\n }\n\n // Load ClickHouse completion data\n await loadClickHouseCompletionData(mooseProjectRoot);\n } else {\n connection.console.log('No Moose project detected in workspace');\n }\n } catch (error) {\n connection.console.error(`Error detecting Moose project: ${error}`);\n }\n }\n\n return {\n capabilities: {\n textDocumentSync: {\n openClose: true,\n change: TextDocumentSyncKind.Full, // Full sync for as-you-type validation\n save: {\n includeText: true,\n },\n },\n codeActionProvider: {\n codeActionKinds: ['source.formatSql'],\n },\n completionProvider: {\n triggerCharacters: ['.', '(', ' '],\n resolveProvider: false,\n },\n hoverProvider: true,\n },\n };\n },\n);\n\n/**\n * Schedules a debounced validation for the given document.\n * Cancels any pending validation for the same document.\n */\nfunction scheduleValidation(document: TextDocument): void {\n const uri = document.uri;\n\n // Cancel any pending validation for this document\n const existingTimer = validationTimers.get(uri);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n\n // Schedule new validation\n const timer = setTimeout(() => {\n validationTimers.delete(uri);\n validateDocument(document);\n }, DEBOUNCE_MS);\n\n validationTimers.set(uri, timer);\n}\n\n// Validate on content change (as-you-type with debouncing)\ndocuments.onDidChangeContent((event) => {\n scheduleValidation(event.document);\n});\n\n// Validate immediately on save (cancel any pending debounced validation)\ndocuments.onDidSave((event) => {\n const uri = event.document.uri;\n const existingTimer = validationTimers.get(uri);\n if (existingTimer) {\n clearTimeout(existingTimer);\n validationTimers.delete(uri);\n }\n connection.console.log(`didSave received for: ${uri}`);\n validateDocument(event.document);\n});\n\n// Validate on open\ndocuments.onDidOpen((event) => {\n connection.console.log(`didOpen received for: ${event.document.uri}`);\n validateDocument(event.document);\n});\n\n// Clean up timers when document is closed\ndocuments.onDidClose((event) => {\n const uri = event.document.uri;\n const existingTimer = validationTimers.get(uri);\n if (existingTimer) {\n clearTimeout(existingTimer);\n validationTimers.delete(uri);\n }\n});\n\n// Code action handler - returns available code actions for a given range\nconnection.onCodeAction((params: CodeActionParams): CodeAction[] => {\n if (!tsService?.isHealthy() || !mooseProjectRoot) return [];\n\n const document = documents.get(params.textDocument.uri);\n if (!document) return [];\n\n const filePath = new URL(params.textDocument.uri).pathname;\n if (!shouldValidateFile(filePath, mooseProjectRoot)) return [];\n\n try {\n const sourceFile = tsService.getSourceFile(filePath);\n if (!sourceFile) return [];\n\n const sqlLocations = extractSqlLocations(\n sourceFile,\n tsService.getTypeChecker(),\n );\n\n // Find if cursor is inside any SQL template\n const location = findSqlTemplateAtPosition(\n sqlLocations,\n params.range.start.line,\n params.range.start.character,\n );\n\n if (!location) return [];\n\n // Find the AST node to compute the edit\n const node = findTemplateNodeById(sourceFile, location.id);\n if (!node) return [];\n\n // Compute the edit directly (don't defer to resolve)\n const edit = createFormatSqlEdit(sourceFile, node);\n if (!edit) return [];\n\n return [\n {\n title: 'Format SQL',\n kind: `${CodeActionKind.Source}.formatSql`,\n edit: {\n changes: {\n [params.textDocument.uri]: [edit],\n },\n },\n },\n ];\n } catch {\n return [];\n }\n});\n\n/**\n * Maps domain-level completion kind from Rust to LSP CompletionItemKind.\n * This maintains the architectural separation: Rust handles SQL domain logic,\n * TypeScript handles LSP protocol mapping.\n */\nfunction mapCompletionItemKind(\n kind: RustCompletionItem['kind'],\n): CompletionItemKind {\n switch (kind) {\n case 'function':\n return CompletionItemKind.Function;\n case 'aggregate_function':\n return CompletionItemKind.Method;\n case 'table_function':\n return CompletionItemKind.Function;\n case 'keyword':\n return CompletionItemKind.Keyword;\n case 'data_type':\n return CompletionItemKind.TypeParameter;\n case 'table_engine':\n return CompletionItemKind.Class;\n case 'format':\n return CompletionItemKind.Constant;\n case 'setting':\n return CompletionItemKind.Property;\n default:\n return CompletionItemKind.Text;\n }\n}\n\n/**\n * Converts a Rust CompletionItem to an LSP CompletionItem.\n * Handles snippet generation based on hasParams and client capabilities.\n */\nfunction toRustCompletionItem(\n rustItem: RustCompletionItem,\n useSnippets: boolean,\n): CompletionItem {\n const kind = mapCompletionItemKind(rustItem.kind);\n\n // Generate insertText based on hasParams and snippet support\n let insertText: string | undefined;\n let insertTextFormat: InsertTextFormat | undefined;\n\n if (rustItem.hasParams) {\n if (useSnippets) {\n insertText = `${rustItem.label}($1)$0`;\n insertTextFormat = InsertTextFormat.Snippet;\n } else {\n insertText = `${rustItem.label}()`;\n insertTextFormat = InsertTextFormat.PlainText;\n }\n }\n\n return {\n label: rustItem.label,\n kind,\n detail: rustItem.detail,\n documentation: rustItem.documentation\n ? { kind: 'markdown' as const, value: rustItem.documentation.value }\n : undefined,\n insertText,\n insertTextFormat,\n sortText: rustItem.sortText,\n };\n}\n\n// Completion handler - provides context-aware SQL completions inside sql template literals\nconnection.onCompletion((params: CompletionParams): CompletionItem[] => {\n if (!tsService?.isHealthy() || !mooseProjectRoot || !clickhouseData) {\n return [];\n }\n\n const document = documents.get(params.textDocument.uri);\n if (!document) return [];\n\n const filePath = new URL(params.textDocument.uri).pathname;\n if (!shouldValidateFile(filePath, mooseProjectRoot)) return [];\n\n try {\n const sourceFile = tsService.getSourceFile(filePath);\n if (!sourceFile) return [];\n\n const sqlLocations = extractSqlLocations(\n sourceFile,\n tsService.getTypeChecker(),\n );\n\n // Check if cursor is inside any SQL template\n const location = findSqlTemplateAtPosition(\n sqlLocations,\n params.position.line,\n params.position.character,\n );\n\n if (!location) return [];\n\n // Calculate cursor offset within the SQL template\n const cursorLine = params.position.line;\n const cursorChar = params.position.character;\n const templateStartLine = location.line - 1; // Convert 1-indexed to 0-indexed\n const templateStartChar = location.column - 1;\n\n // Get the SQL text and calculate offset\n const sqlText = location.templateText;\n let cursorOffset = 0;\n\n // Count characters from start of template to cursor position\n const lines = sqlText.split('\\n');\n const relativeLine = cursorLine - templateStartLine;\n\n for (let i = 0; i < relativeLine && i < lines.length; i++) {\n cursorOffset += lines[i].length + 1; // +1 for newline\n }\n\n if (relativeLine === 0) {\n cursorOffset += cursorChar - templateStartChar;\n } else if (relativeLine < lines.length) {\n cursorOffset += cursorChar;\n }\n\n // Clamp to valid range\n cursorOffset = Math.max(0, Math.min(cursorOffset, sqlText.length));\n\n // Get context-aware completions from Rust\n const rustCompletions = getCompletions(sqlText, cursorOffset);\n\n // Get prefix for filtering\n const lineText = document.getText({\n start: { line: params.position.line, character: 0 },\n end: params.position,\n });\n const wordMatch = lineText.match(/[\\w]+$/);\n const prefix = wordMatch ? wordMatch[0].toLowerCase() : '';\n\n // Convert to LSP CompletionItem format and filter by prefix\n const completions: CompletionItem[] = rustCompletions\n .filter((c) => !prefix || c.label.toLowerCase().startsWith(prefix))\n .map((c) => toRustCompletionItem(c, clientSupportsSnippets));\n\n return completions;\n } catch {\n return [];\n }\n});\n\n// Hover handler - provides documentation on hover inside sql template literals\nconnection.onHover((params: HoverParams): Hover | null => {\n if (!tsService?.isHealthy() || !mooseProjectRoot || !clickhouseData) {\n return null;\n }\n\n const document = documents.get(params.textDocument.uri);\n if (!document) return null;\n\n const filePath = new URL(params.textDocument.uri).pathname;\n if (!shouldValidateFile(filePath, mooseProjectRoot)) return null;\n\n try {\n const sourceFile = tsService.getSourceFile(filePath);\n if (!sourceFile) return null;\n\n const sqlLocations = extractSqlLocations(\n sourceFile,\n tsService.getTypeChecker(),\n );\n\n // Check if cursor is inside any SQL template\n const location = findSqlTemplateAtPosition(\n sqlLocations,\n params.position.line,\n params.position.character,\n );\n\n if (!location) return null;\n\n // Get the full line text to extract word at cursor\n const lineText = document.getText({\n start: { line: params.position.line, character: 0 },\n end: { line: params.position.line + 1, character: 0 },\n });\n\n const word = getWordAtPosition(lineText, params.position.character);\n if (!word) return null;\n\n // Look up hover info\n const hoverInfo = findHoverInfo(word, clickhouseData);\n if (!hoverInfo) return null;\n\n return {\n contents: createHoverContent(hoverInfo),\n };\n } catch {\n return null;\n }\n});\n\ndocuments.listen(connection);\nconnection.listen();\n"],"mappings":"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","names":[]}
@@ -0,0 +1,32 @@
1
+ import { ValidationResult } from '@514labs/moose-sql-validator-wasm';
2
+ import { Diagnostic } from 'vscode-languageserver/node';
3
+ import { SqlLocation } from './sqlLocations.js';
4
+
5
+ /**
6
+ * Function type for SQL validation
7
+ */
8
+ type ValidateSqlFn = (sql: string) => ValidationResult;
9
+ /**
10
+ * Function type for creating diagnostics from SqlLocation
11
+ */
12
+ type CreateLocationDiagnosticFn = (location: SqlLocation, error: NonNullable<ValidationResult['error']>) => {
13
+ uri: string;
14
+ diagnostic: Diagnostic;
15
+ };
16
+ /**
17
+ * Determines if a file should be validated based on path and project root
18
+ * @param filePath - The absolute path to the file
19
+ * @param mooseProjectRoot - The root of the Moose project (or null if not detected)
20
+ * @returns true if the file should be validated
21
+ */
22
+ declare function shouldValidateFile(filePath: string, mooseProjectRoot: string | null): boolean;
23
+ /**
24
+ * Validates SQL from template locations and returns a map of URI -> diagnostics
25
+ * @param sqlLocations - Array of SQL template locations
26
+ * @param validateSql - Function to validate SQL strings
27
+ * @param createDiagnostic - Function to create LSP diagnostics from validation errors
28
+ * @returns Map of file URIs to their diagnostics
29
+ */
30
+ declare function validateSqlLocations(sqlLocations: SqlLocation[], validateSql: ValidateSqlFn, createDiagnostic: CreateLocationDiagnosticFn): Map<string, Diagnostic[]>;
31
+
32
+ export { type CreateLocationDiagnosticFn, type ValidateSqlFn, shouldValidateFile, validateSqlLocations };
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var serverLogic_exports = {};
21
+ __export(serverLogic_exports, {
22
+ shouldValidateFile: () => shouldValidateFile,
23
+ validateSqlLocations: () => validateSqlLocations
24
+ });
25
+ module.exports = __toCommonJS(serverLogic_exports);
26
+ var import_sqlLocations = require("./sqlLocations");
27
+ function shouldValidateFile(filePath, mooseProjectRoot) {
28
+ if (!mooseProjectRoot) return false;
29
+ return filePath.startsWith(mooseProjectRoot) && filePath.endsWith(".ts");
30
+ }
31
+ function validateSqlLocations(sqlLocations, validateSql, createDiagnostic) {
32
+ const diagnosticsMap = /* @__PURE__ */ new Map();
33
+ for (const location of sqlLocations) {
34
+ const preparedSql = (0, import_sqlLocations.prepareSqlForValidation)(location.templateText);
35
+ const result = validateSql(preparedSql);
36
+ if (!result.valid && result.error) {
37
+ const { uri, diagnostic } = createDiagnostic(location, result.error);
38
+ if (!diagnosticsMap.has(uri)) {
39
+ diagnosticsMap.set(uri, []);
40
+ }
41
+ diagnosticsMap.get(uri)?.push(diagnostic);
42
+ }
43
+ }
44
+ return diagnosticsMap;
45
+ }
46
+ // Annotate the CommonJS export names for ESM import in node:
47
+ 0 && (module.exports = {
48
+ shouldValidateFile,
49
+ validateSqlLocations
50
+ });
51
+ //# sourceMappingURL=serverLogic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/serverLogic.ts"],"sourcesContent":["import type { ValidationResult } from '@514labs/moose-sql-validator-wasm';\nimport type { Diagnostic } from 'vscode-languageserver/node';\nimport { prepareSqlForValidation, type SqlLocation } from './sqlLocations';\n\n/**\n * Function type for SQL validation\n */\nexport type ValidateSqlFn = (sql: string) => ValidationResult;\n\n/**\n * Function type for creating diagnostics from SqlLocation\n */\nexport type CreateLocationDiagnosticFn = (\n location: SqlLocation,\n error: NonNullable<ValidationResult['error']>,\n) => { uri: string; diagnostic: Diagnostic };\n\n/**\n * Determines if a file should be validated based on path and project root\n * @param filePath - The absolute path to the file\n * @param mooseProjectRoot - The root of the Moose project (or null if not detected)\n * @returns true if the file should be validated\n */\nexport function shouldValidateFile(\n filePath: string,\n mooseProjectRoot: string | null,\n): boolean {\n if (!mooseProjectRoot) return false;\n return filePath.startsWith(mooseProjectRoot) && filePath.endsWith('.ts');\n}\n\n/**\n * Validates SQL from template locations and returns a map of URI -> diagnostics\n * @param sqlLocations - Array of SQL template locations\n * @param validateSql - Function to validate SQL strings\n * @param createDiagnostic - Function to create LSP diagnostics from validation errors\n * @returns Map of file URIs to their diagnostics\n */\nexport function validateSqlLocations(\n sqlLocations: SqlLocation[],\n validateSql: ValidateSqlFn,\n createDiagnostic: CreateLocationDiagnosticFn,\n): Map<string, Diagnostic[]> {\n const diagnosticsMap = new Map<string, Diagnostic[]>();\n\n for (const location of sqlLocations) {\n // Replace ${...} placeholders with valid SQL identifiers before validation\n const preparedSql = prepareSqlForValidation(location.templateText);\n const result = validateSql(preparedSql);\n\n if (!result.valid && result.error) {\n const { uri, diagnostic } = createDiagnostic(location, result.error);\n\n if (!diagnosticsMap.has(uri)) {\n diagnosticsMap.set(uri, []);\n }\n diagnosticsMap.get(uri)?.push(diagnostic);\n }\n }\n\n return diagnosticsMap;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,0BAA0D;AAqBnD,SAAS,mBACd,UACA,kBACS;AACT,MAAI,CAAC,iBAAkB,QAAO;AAC9B,SAAO,SAAS,WAAW,gBAAgB,KAAK,SAAS,SAAS,KAAK;AACzE;AASO,SAAS,qBACd,cACA,aACA,kBAC2B;AAC3B,QAAM,iBAAiB,oBAAI,IAA0B;AAErD,aAAW,YAAY,cAAc;AAEnC,UAAM,kBAAc,6CAAwB,SAAS,YAAY;AACjE,UAAM,SAAS,YAAY,WAAW;AAEtC,QAAI,CAAC,OAAO,SAAS,OAAO,OAAO;AACjC,YAAM,EAAE,KAAK,WAAW,IAAI,iBAAiB,UAAU,OAAO,KAAK;AAEnE,UAAI,CAAC,eAAe,IAAI,GAAG,GAAG;AAC5B,uBAAe,IAAI,KAAK,CAAC,CAAC;AAAA,MAC5B;AACA,qBAAe,IAAI,GAAG,GAAG,KAAK,UAAU;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }