@prestyj/cli 4.6.3 → 4.10.1
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/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +11 -10
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/mcp.js +1 -1
- package/dist/cli/shared.d.ts +12 -0
- package/dist/cli/shared.d.ts.map +1 -1
- package/dist/cli/shared.js +31 -6
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +63 -62
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +7 -14
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -17
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +1 -1
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -58
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -254
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/auto-update.d.ts +4 -26
- package/dist/core/auto-update.d.ts.map +1 -1
- package/dist/core/auto-update.js +12 -237
- package/dist/core/auto-update.js.map +1 -1
- package/dist/core/claude-code-version.d.ts +1 -9
- package/dist/core/claude-code-version.d.ts.map +1 -1
- package/dist/core/claude-code-version.js +2 -105
- package/dist/core/claude-code-version.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +2 -2
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/file-lock.d.ts +1 -5
- package/dist/core/file-lock.d.ts.map +1 -1
- package/dist/core/file-lock.js +2 -75
- package/dist/core/file-lock.js.map +1 -1
- package/dist/core/logger.d.ts +4 -17
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +21 -110
- package/dist/core/logger.js.map +1 -1
- package/dist/core/lsp/client.d.ts +63 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +215 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/format.d.ts +12 -0
- package/dist/core/lsp/format.d.ts.map +1 -0
- package/dist/core/lsp/format.js +30 -0
- package/dist/core/lsp/format.js.map +1 -0
- package/dist/core/lsp/format.test.d.ts +2 -0
- package/dist/core/lsp/format.test.d.ts.map +1 -0
- package/dist/core/lsp/format.test.js +56 -0
- package/dist/core/lsp/format.test.js.map +1 -0
- package/dist/core/lsp/integration.test.d.ts +2 -0
- package/dist/core/lsp/integration.test.d.ts.map +1 -0
- package/dist/core/lsp/integration.test.js +60 -0
- package/dist/core/lsp/integration.test.js.map +1 -0
- package/dist/core/lsp/jsonrpc.d.ts +32 -0
- package/dist/core/lsp/jsonrpc.d.ts.map +1 -0
- package/dist/core/lsp/jsonrpc.js +141 -0
- package/dist/core/lsp/jsonrpc.js.map +1 -0
- package/dist/core/lsp/jsonrpc.test.d.ts +2 -0
- package/dist/core/lsp/jsonrpc.test.d.ts.map +1 -0
- package/dist/core/lsp/jsonrpc.test.js +127 -0
- package/dist/core/lsp/jsonrpc.test.js.map +1 -0
- package/dist/core/lsp/manager.d.ts +40 -0
- package/dist/core/lsp/manager.d.ts.map +1 -0
- package/dist/core/lsp/manager.js +135 -0
- package/dist/core/lsp/manager.js.map +1 -0
- package/dist/core/lsp/manager.test.d.ts +2 -0
- package/dist/core/lsp/manager.test.d.ts.map +1 -0
- package/dist/core/lsp/manager.test.js +119 -0
- package/dist/core/lsp/manager.test.js.map +1 -0
- package/dist/core/lsp/servers.d.ts +45 -0
- package/dist/core/lsp/servers.d.ts.map +1 -0
- package/dist/core/lsp/servers.js +247 -0
- package/dist/core/lsp/servers.js.map +1 -0
- package/dist/core/mcp/defaults.d.ts +1 -1
- package/dist/core/mcp/defaults.js +1 -1
- package/dist/core/mcp/store.d.ts +1 -1
- package/dist/core/mcp/store.test.js +1 -1
- package/dist/core/mcp/store.test.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -75
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +4 -344
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/oauth/anthropic.d.ts +1 -3
- package/dist/core/oauth/anthropic.d.ts.map +1 -1
- package/dist/core/oauth/anthropic.js +2 -96
- package/dist/core/oauth/anthropic.js.map +1 -1
- package/dist/core/oauth/gemini.d.ts +1 -3
- package/dist/core/oauth/gemini.d.ts.map +1 -1
- package/dist/core/oauth/gemini.js +2 -385
- package/dist/core/oauth/gemini.js.map +1 -1
- package/dist/core/oauth/kimi.d.ts +1 -45
- package/dist/core/oauth/kimi.d.ts.map +1 -1
- package/dist/core/oauth/kimi.js +2 -277
- package/dist/core/oauth/kimi.js.map +1 -1
- package/dist/core/oauth/openai.d.ts +1 -3
- package/dist/core/oauth/openai.d.ts.map +1 -1
- package/dist/core/oauth/openai.js +2 -187
- package/dist/core/oauth/openai.js.map +1 -1
- package/dist/core/oauth/pkce.d.ts +1 -4
- package/dist/core/oauth/pkce.d.ts.map +1 -1
- package/dist/core/oauth/pkce.js +2 -16
- package/dist/core/oauth/pkce.js.map +1 -1
- package/dist/core/oauth/types.d.ts +1 -13
- package/dist/core/oauth/types.d.ts.map +1 -1
- package/dist/core/process-manager.d.ts +10 -0
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +45 -1
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +132 -7
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +20 -0
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/session-manager.d.ts +26 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +99 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.d.ts +2 -0
- package/dist/core/session-manager.test.d.ts.map +1 -0
- package/dist/core/session-manager.test.js +113 -0
- package/dist/core/session-manager.test.js.map +1 -0
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +6 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/telegram.d.ts +1 -112
- package/dist/core/telegram.d.ts.map +1 -1
- package/dist/core/telegram.js +2 -251
- package/dist/core/telegram.js.map +1 -1
- package/dist/core/thinking-level.d.ts +1 -4
- package/dist/core/thinking-level.d.ts.map +1 -1
- package/dist/core/thinking-level.js +3 -58
- package/dist/core/thinking-level.js.map +1 -1
- package/dist/core/voice-transcriber.d.ts +1 -32
- package/dist/core/voice-transcriber.d.ts.map +1 -1
- package/dist/core/voice-transcriber.js +3 -112
- package/dist/core/voice-transcriber.js.map +1 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +5 -2
- package/dist/interactive.js.map +1 -1
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +10 -38
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +11 -39
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +13 -6
- package/dist/session.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +6 -3
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +3 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +15 -3
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/edit.test.js +70 -0
- package/dist/tools/edit.test.js.map +1 -1
- package/dist/tools/goals.d.ts +1 -1
- package/dist/tools/index.d.ts +15 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/task-send.d.ts +12 -0
- package/dist/tools/task-send.d.ts.map +1 -0
- package/dist/tools/task-send.js +34 -0
- package/dist/tools/task-send.js.map +1 -0
- package/dist/tools/task-send.test.d.ts +2 -0
- package/dist/tools/task-send.test.d.ts.map +1 -0
- package/dist/tools/task-send.test.js +95 -0
- package/dist/tools/task-send.test.js.map +1 -0
- package/dist/tools/write.d.ts +3 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +13 -2
- package/dist/tools/write.js.map +1 -1
- package/dist/tools/write.test.js +33 -0
- package/dist/tools/write.test.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts +16 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +127 -12
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/anchor-pad-reclaim.test.d.ts +2 -0
- package/dist/ui/anchor-pad-reclaim.test.d.ts.map +1 -0
- package/dist/ui/anchor-pad-reclaim.test.js +146 -0
- package/dist/ui/anchor-pad-reclaim.test.js.map +1 -0
- package/dist/ui/app-items.d.ts +14 -0
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js +13 -0
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/app-items.test.js +17 -1
- package/dist/ui/app-items.test.js.map +1 -1
- package/dist/ui/atomic-flush-regression.test.d.ts +2 -0
- package/dist/ui/atomic-flush-regression.test.d.ts.map +1 -0
- package/dist/ui/atomic-flush-regression.test.js +218 -0
- package/dist/ui/atomic-flush-regression.test.js.map +1 -0
- package/dist/ui/components/AssistantMessage.test.js +32 -1
- package/dist/ui/components/AssistantMessage.test.js.map +1 -1
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +1 -0
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/InputArea.test.js +11 -5
- package/dist/ui/components/InputArea.test.js.map +1 -1
- package/dist/ui/components/Markdown.d.ts.map +1 -1
- package/dist/ui/components/Markdown.js +9 -1
- package/dist/ui/components/Markdown.js.map +1 -1
- package/dist/ui/footer-jump-regression.test.js +56 -9
- package/dist/ui/footer-jump-regression.test.js.map +1 -1
- package/dist/ui/frame-clip-regression.test.d.ts +2 -0
- package/dist/ui/frame-clip-regression.test.d.ts.map +1 -0
- package/dist/ui/frame-clip-regression.test.js +215 -0
- package/dist/ui/frame-clip-regression.test.js.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts +9 -1
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.js +51 -19
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -1
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts +2 -0
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.d.ts.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js +81 -0
- package/dist/ui/hooks/useTranscriptHistory.sessionstore.test.js.map +1 -0
- package/dist/ui/live-area-clamp.test.js +37 -0
- package/dist/ui/live-area-clamp.test.js.map +1 -1
- package/dist/ui/live-area-height.d.ts.map +1 -1
- package/dist/ui/live-area-height.js +13 -6
- package/dist/ui/live-area-height.js.map +1 -1
- package/dist/ui/live-area-height.test.js +9 -0
- package/dist/ui/live-area-height.test.js.map +1 -1
- package/dist/ui/live-item-flush.d.ts +28 -0
- package/dist/ui/live-item-flush.d.ts.map +1 -1
- package/dist/ui/live-item-flush.js +44 -0
- package/dist/ui/live-item-flush.js.map +1 -1
- package/dist/ui/live-item-flush.test.js +83 -1
- package/dist/ui/live-item-flush.test.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +12 -30
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/mcp.d.ts +1 -2
- package/dist/ui/mcp.d.ts.map +1 -1
- package/dist/ui/mcp.js +8 -43
- package/dist/ui/mcp.js.map +1 -1
- package/dist/ui/pixel.d.ts.map +1 -1
- package/dist/ui/pixel.js +10 -38
- package/dist/ui/pixel.js.map +1 -1
- package/dist/ui/queued-message.test.js +7 -1
- package/dist/ui/queued-message.test.js.map +1 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +154 -5
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/sessions.d.ts.map +1 -1
- package/dist/ui/sessions.js +10 -42
- package/dist/ui/sessions.js.map +1 -1
- package/dist/ui/startup-banner-order.test.d.ts +2 -0
- package/dist/ui/startup-banner-order.test.d.ts.map +1 -0
- package/dist/ui/startup-banner-order.test.js +105 -0
- package/dist/ui/startup-banner-order.test.js.map +1 -0
- package/dist/ui/streaming-flush-bounce.test.js +41 -20
- package/dist/ui/streaming-flush-bounce.test.js.map +1 -1
- package/dist/ui/submit-boundary-gap.test.d.ts +2 -0
- package/dist/ui/submit-boundary-gap.test.d.ts.map +1 -0
- package/dist/ui/submit-boundary-gap.test.js +180 -0
- package/dist/ui/submit-boundary-gap.test.js.map +1 -0
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +28 -9
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +10 -3
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/testing/screen-recorder.d.ts.map +1 -1
- package/dist/ui/testing/screen-recorder.js +11 -1
- package/dist/ui/testing/screen-recorder.js.map +1 -1
- package/dist/ui/tui-history-parity.test.js +4 -1
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/ui/tui-simulation.test.js +7 -1
- package/dist/ui/tui-simulation.test.js.map +1 -1
- package/dist/ui/utils/assistant-stream-split.d.ts +7 -0
- package/dist/ui/utils/assistant-stream-split.d.ts.map +1 -1
- package/dist/ui/utils/assistant-stream-split.js +22 -1
- package/dist/ui/utils/assistant-stream-split.js.map +1 -1
- package/dist/ui/utils/assistant-stream-split.test.js +14 -0
- package/dist/ui/utils/assistant-stream-split.test.js.map +1 -1
- package/dist/ui/utils/markdown-renderer.d.ts.map +1 -1
- package/dist/ui/utils/markdown-renderer.js +46 -3
- package/dist/ui/utils/markdown-renderer.js.map +1 -1
- package/dist/ui/utils/markdown-renderer.test.d.ts +2 -0
- package/dist/ui/utils/markdown-renderer.test.d.ts.map +1 -0
- package/dist/ui/utils/markdown-renderer.test.js +63 -0
- package/dist/ui/utils/markdown-renderer.test.js.map +1 -0
- package/dist/ui/utils/terminal-graphics.d.ts +28 -1
- package/dist/ui/utils/terminal-graphics.d.ts.map +1 -1
- package/dist/ui/utils/terminal-graphics.js +44 -3
- package/dist/ui/utils/terminal-graphics.js.map +1 -1
- package/dist/ui/utils/terminal-graphics.test.js +40 -1
- package/dist/ui/utils/terminal-graphics.test.js.map +1 -1
- package/dist/utils/session-title.js +1 -1
- package/dist/utils/session-title.js.map +1 -1
- package/package.json +6 -5
- package/dist/core/model-registry.test.d.ts +0 -2
- package/dist/core/model-registry.test.d.ts.map +0 -1
- package/dist/core/model-registry.test.js +0 -95
- package/dist/core/model-registry.test.js.map +0 -1
- package/dist/core/oauth/gemini.test.d.ts +0 -2
- package/dist/core/oauth/gemini.test.d.ts.map +0 -1
- package/dist/core/oauth/gemini.test.js +0 -154
- package/dist/core/oauth/gemini.test.js.map +0 -1
- package/dist/core/thinking-level.test.d.ts +0 -2
- package/dist/core/thinking-level.test.d.ts.map +0 -1
- package/dist/core/thinking-level.test.js +0 -51
- package/dist/core/thinking-level.test.js.map +0 -1
- package/dist/core/voice-transcriber.test.d.ts +0 -2
- package/dist/core/voice-transcriber.test.d.ts.map +0 -1
- package/dist/core/voice-transcriber.test.js +0 -88
- package/dist/core/voice-transcriber.test.js.map +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export class JsonRpcRequestError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(method, code, message) {
|
|
4
|
+
super(`${method}: ${message} (code ${code})`);
|
|
5
|
+
this.name = "JsonRpcRequestError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const HEADER_SEPARATOR = "\r\n\r\n";
|
|
10
|
+
/**
|
|
11
|
+
* JSON-RPC connection speaking Content-Length framed messages.
|
|
12
|
+
*
|
|
13
|
+
* Server→client REQUESTS are auto-replied with safe defaults so language
|
|
14
|
+
* servers never stall waiting on us: `workspace/configuration` gets one null
|
|
15
|
+
* per requested item; everything else (`client/registerCapability`,
|
|
16
|
+
* `window/workDoneProgress/create`, …) gets a plain null result.
|
|
17
|
+
*/
|
|
18
|
+
export class JsonRpcConnection {
|
|
19
|
+
output;
|
|
20
|
+
buffer = Buffer.alloc(0);
|
|
21
|
+
nextId = 1;
|
|
22
|
+
pending = new Map();
|
|
23
|
+
notificationHandlers = new Map();
|
|
24
|
+
disposed = false;
|
|
25
|
+
constructor(input, output) {
|
|
26
|
+
this.output = output;
|
|
27
|
+
input.on("data", (chunk) => this.onData(chunk));
|
|
28
|
+
// Writing to a dead server's stdin must never crash the host process.
|
|
29
|
+
output.on("error", () => { });
|
|
30
|
+
}
|
|
31
|
+
onNotification(method, handler) {
|
|
32
|
+
const handlers = this.notificationHandlers.get(method) ?? [];
|
|
33
|
+
handlers.push(handler);
|
|
34
|
+
this.notificationHandlers.set(method, handlers);
|
|
35
|
+
}
|
|
36
|
+
request(method, params, timeoutMs) {
|
|
37
|
+
if (this.disposed) {
|
|
38
|
+
return Promise.reject(new JsonRpcRequestError(method, -32099, "connection disposed"));
|
|
39
|
+
}
|
|
40
|
+
const id = this.nextId++;
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const timer = setTimeout(() => {
|
|
43
|
+
this.pending.delete(id);
|
|
44
|
+
reject(new JsonRpcRequestError(method, -32803, `timed out after ${timeoutMs}ms`));
|
|
45
|
+
}, timeoutMs);
|
|
46
|
+
timer.unref();
|
|
47
|
+
this.pending.set(id, (msg) => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
if (msg.error) {
|
|
50
|
+
reject(new JsonRpcRequestError(method, msg.error.code, msg.error.message));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
resolve(msg.result);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.send({ jsonrpc: "2.0", id, method, params });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
notify(method, params) {
|
|
60
|
+
this.send({ jsonrpc: "2.0", method, params });
|
|
61
|
+
}
|
|
62
|
+
/** Reject all in-flight requests and stop accepting new work. */
|
|
63
|
+
dispose() {
|
|
64
|
+
if (this.disposed)
|
|
65
|
+
return;
|
|
66
|
+
this.disposed = true;
|
|
67
|
+
for (const [id, settle] of this.pending) {
|
|
68
|
+
this.pending.delete(id);
|
|
69
|
+
settle({ error: { code: -32099, message: "connection disposed" } });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
send(msg) {
|
|
73
|
+
if (this.disposed)
|
|
74
|
+
return;
|
|
75
|
+
const body = JSON.stringify(msg);
|
|
76
|
+
try {
|
|
77
|
+
this.output.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Server stdin already closed — degradation is handled by the caller.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
onData(chunk) {
|
|
84
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
85
|
+
for (;;) {
|
|
86
|
+
const headerEnd = this.buffer.indexOf(HEADER_SEPARATOR);
|
|
87
|
+
if (headerEnd === -1)
|
|
88
|
+
return;
|
|
89
|
+
const header = this.buffer.subarray(0, headerEnd).toString();
|
|
90
|
+
const match = /Content-Length: (\d+)/i.exec(header);
|
|
91
|
+
if (!match) {
|
|
92
|
+
this.buffer = this.buffer.subarray(headerEnd + HEADER_SEPARATOR.length);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const length = Number(match[1]);
|
|
96
|
+
const bodyStart = headerEnd + HEADER_SEPARATOR.length;
|
|
97
|
+
if (this.buffer.length < bodyStart + length)
|
|
98
|
+
return;
|
|
99
|
+
const body = this.buffer.subarray(bodyStart, bodyStart + length).toString();
|
|
100
|
+
this.buffer = this.buffer.subarray(bodyStart + length);
|
|
101
|
+
let msg;
|
|
102
|
+
try {
|
|
103
|
+
msg = JSON.parse(body);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
this.onMessage(msg);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
onMessage(msg) {
|
|
112
|
+
// Response to one of our requests.
|
|
113
|
+
if (msg.id !== undefined && msg.method === undefined) {
|
|
114
|
+
const settle = typeof msg.id === "number" ? this.pending.get(msg.id) : undefined;
|
|
115
|
+
if (settle && typeof msg.id === "number") {
|
|
116
|
+
this.pending.delete(msg.id);
|
|
117
|
+
settle(msg);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Server→client request: auto-reply with safe defaults.
|
|
122
|
+
if (msg.id !== undefined && msg.method !== undefined) {
|
|
123
|
+
let result = null;
|
|
124
|
+
if (msg.method === "workspace/configuration") {
|
|
125
|
+
const items = msg.params?.items ?? [];
|
|
126
|
+
result = items.map(() => null);
|
|
127
|
+
}
|
|
128
|
+
this.send({ jsonrpc: "2.0", id: msg.id, result });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Notification.
|
|
132
|
+
if (msg.method !== undefined) {
|
|
133
|
+
const handlers = this.notificationHandlers.get(msg.method);
|
|
134
|
+
if (handlers) {
|
|
135
|
+
for (const handler of handlers)
|
|
136
|
+
handler(msg.params);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=jsonrpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../../../src/core/lsp/jsonrpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.test.d.ts","sourceRoot":"","sources":["../../../src/core/lsp/jsonrpc.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { PassThrough } from "node:stream";
|
|
3
|
+
import { JsonRpcConnection, JsonRpcRequestError } from "./jsonrpc.js";
|
|
4
|
+
function frame(msg) {
|
|
5
|
+
const body = JSON.stringify(msg);
|
|
6
|
+
return `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`;
|
|
7
|
+
}
|
|
8
|
+
/** Collects framed messages the connection writes to its output stream. */
|
|
9
|
+
function captureOutput(output) {
|
|
10
|
+
const messages = [];
|
|
11
|
+
let buffer = Buffer.alloc(0);
|
|
12
|
+
output.on("data", (chunk) => {
|
|
13
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
14
|
+
for (;;) {
|
|
15
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
16
|
+
if (headerEnd === -1)
|
|
17
|
+
return;
|
|
18
|
+
const match = /Content-Length: (\d+)/i.exec(buffer.subarray(0, headerEnd).toString());
|
|
19
|
+
if (!match)
|
|
20
|
+
return;
|
|
21
|
+
const length = Number(match[1]);
|
|
22
|
+
const start = headerEnd + 4;
|
|
23
|
+
if (buffer.length < start + length)
|
|
24
|
+
return;
|
|
25
|
+
messages.push(JSON.parse(buffer.subarray(start, start + length).toString()));
|
|
26
|
+
buffer = buffer.subarray(start + length);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return messages;
|
|
30
|
+
}
|
|
31
|
+
function makeConnection() {
|
|
32
|
+
const serverIn = new PassThrough(); // server → client bytes
|
|
33
|
+
const clientOut = new PassThrough(); // client → server bytes
|
|
34
|
+
const sent = captureOutput(clientOut);
|
|
35
|
+
const conn = new JsonRpcConnection(serverIn, clientOut);
|
|
36
|
+
return { conn, serverIn, sent };
|
|
37
|
+
}
|
|
38
|
+
async function tick() {
|
|
39
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
40
|
+
}
|
|
41
|
+
describe("JsonRpcConnection", () => {
|
|
42
|
+
it("round-trips a request and its framed response", async () => {
|
|
43
|
+
const { conn, serverIn, sent } = makeConnection();
|
|
44
|
+
const pending = conn.request("initialize", { rootUri: "file:///x" }, 1000);
|
|
45
|
+
await tick();
|
|
46
|
+
expect(sent).toHaveLength(1);
|
|
47
|
+
expect(sent[0].method).toBe("initialize");
|
|
48
|
+
expect(sent[0].params).toEqual({ rootUri: "file:///x" });
|
|
49
|
+
serverIn.write(frame({ jsonrpc: "2.0", id: sent[0].id, result: { capabilities: {} } }));
|
|
50
|
+
await expect(pending).resolves.toEqual({ capabilities: {} });
|
|
51
|
+
});
|
|
52
|
+
it("parses messages split across arbitrary chunk boundaries", async () => {
|
|
53
|
+
const { conn, serverIn } = makeConnection();
|
|
54
|
+
const received = [];
|
|
55
|
+
conn.onNotification("textDocument/publishDiagnostics", (params) => received.push(params));
|
|
56
|
+
const params = { uri: "file:///a.ts", diagnostics: [{ message: "boom" }] };
|
|
57
|
+
const bytes = Buffer.from(frame({ jsonrpc: "2.0", method: "textDocument/publishDiagnostics", params }));
|
|
58
|
+
// Drip-feed one byte at a time — worst-case TCP/pipe fragmentation.
|
|
59
|
+
for (const byte of bytes) {
|
|
60
|
+
serverIn.write(Buffer.from([byte]));
|
|
61
|
+
}
|
|
62
|
+
await tick();
|
|
63
|
+
expect(received).toEqual([params]);
|
|
64
|
+
});
|
|
65
|
+
it("parses multiple messages arriving in a single chunk", async () => {
|
|
66
|
+
const { conn, serverIn } = makeConnection();
|
|
67
|
+
const received = [];
|
|
68
|
+
conn.onNotification("note", (params) => received.push(params));
|
|
69
|
+
serverIn.write(frame({ jsonrpc: "2.0", method: "note", params: { n: 1 } }) +
|
|
70
|
+
frame({ jsonrpc: "2.0", method: "note", params: { n: 2 } }));
|
|
71
|
+
await tick();
|
|
72
|
+
expect(received).toEqual([{ n: 1 }, { n: 2 }]);
|
|
73
|
+
});
|
|
74
|
+
it("auto-replies to workspace/configuration with one null per item", async () => {
|
|
75
|
+
const { serverIn, sent } = makeConnection();
|
|
76
|
+
serverIn.write(frame({
|
|
77
|
+
jsonrpc: "2.0",
|
|
78
|
+
id: 99,
|
|
79
|
+
method: "workspace/configuration",
|
|
80
|
+
params: { items: [{ section: "a" }, { section: "b" }] },
|
|
81
|
+
}));
|
|
82
|
+
await tick();
|
|
83
|
+
expect(sent).toEqual([{ jsonrpc: "2.0", id: 99, result: [null, null] }]);
|
|
84
|
+
});
|
|
85
|
+
it("auto-replies null to other server→client requests", async () => {
|
|
86
|
+
const { serverIn, sent } = makeConnection();
|
|
87
|
+
serverIn.write(frame({ jsonrpc: "2.0", id: 7, method: "client/registerCapability", params: {} }));
|
|
88
|
+
serverIn.write(frame({ jsonrpc: "2.0", id: 8, method: "window/workDoneProgress/create", params: {} }));
|
|
89
|
+
await tick();
|
|
90
|
+
expect(sent).toEqual([
|
|
91
|
+
{ jsonrpc: "2.0", id: 7, result: null },
|
|
92
|
+
{ jsonrpc: "2.0", id: 8, result: null },
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
it("rejects a request after its timeout elapses", async () => {
|
|
96
|
+
const { conn } = makeConnection();
|
|
97
|
+
await expect(conn.request("slow/method", null, 20)).rejects.toThrow(JsonRpcRequestError);
|
|
98
|
+
});
|
|
99
|
+
it("rejects with the server error payload", async () => {
|
|
100
|
+
const { conn, serverIn, sent } = makeConnection();
|
|
101
|
+
const pending = conn.request("textDocument/diagnostic", {}, 1000);
|
|
102
|
+
await tick();
|
|
103
|
+
serverIn.write(frame({
|
|
104
|
+
jsonrpc: "2.0",
|
|
105
|
+
id: sent[0].id,
|
|
106
|
+
error: { code: -32802, message: "server cancelled" },
|
|
107
|
+
}));
|
|
108
|
+
await expect(pending).rejects.toMatchObject({ code: -32802 });
|
|
109
|
+
});
|
|
110
|
+
it("skips malformed JSON bodies and keeps parsing subsequent messages", async () => {
|
|
111
|
+
const { conn, serverIn } = makeConnection();
|
|
112
|
+
const received = [];
|
|
113
|
+
conn.onNotification("ok", (params) => received.push(params));
|
|
114
|
+
const garbage = "{not json";
|
|
115
|
+
serverIn.write(`Content-Length: ${Buffer.byteLength(garbage)}\r\n\r\n${garbage}`);
|
|
116
|
+
serverIn.write(frame({ jsonrpc: "2.0", method: "ok", params: { fine: true } }));
|
|
117
|
+
await tick();
|
|
118
|
+
expect(received).toEqual([{ fine: true }]);
|
|
119
|
+
});
|
|
120
|
+
it("rejects all in-flight requests on dispose", async () => {
|
|
121
|
+
const { conn } = makeConnection();
|
|
122
|
+
const pending = conn.request("initialize", {}, 60_000);
|
|
123
|
+
conn.dispose();
|
|
124
|
+
await expect(pending).rejects.toMatchObject({ code: -32099 });
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=jsonrpc.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonrpc.test.js","sourceRoot":"","sources":["../../../src/core/lsp/jsonrpc.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type LspServerSpec } from "./servers.js";
|
|
2
|
+
export interface LspManagerOptions {
|
|
3
|
+
/** Server catalog override — tests inject a fake-server spec here. */
|
|
4
|
+
catalog?: readonly LspServerSpec[];
|
|
5
|
+
/** Hard diagnostics budget once a client has served at least one file. */
|
|
6
|
+
warmBudgetMs?: number;
|
|
7
|
+
/** Hard budget for a client's very first file (spawn + init + indexing). */
|
|
8
|
+
firstBudgetMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Lazily spawns and pools language servers keyed by (serverId, projectRoot).
|
|
12
|
+
* Nothing runs until the first edit/write of a matching file. Every failure
|
|
13
|
+
* path degrades to an empty string — diagnostics are an enhancement, never a
|
|
14
|
+
* tool-breaking dependency. A failed spawn/init marks the (server, root) pair
|
|
15
|
+
* broken for the session so a missing binary costs one attempt, not one per
|
|
16
|
+
* edit.
|
|
17
|
+
*/
|
|
18
|
+
export declare class LspManager {
|
|
19
|
+
private readonly cwd;
|
|
20
|
+
private readonly catalog;
|
|
21
|
+
private readonly warmBudgetMs;
|
|
22
|
+
private readonly firstBudgetMs;
|
|
23
|
+
/** (serverId\0root) → in-flight or settled client; null = broken for session. */
|
|
24
|
+
private readonly clients;
|
|
25
|
+
/** Keys that have completed at least one diagnostics pass (warm). */
|
|
26
|
+
private readonly warmKeys;
|
|
27
|
+
private shutDown;
|
|
28
|
+
constructor(cwd: string, options?: LspManagerOptions);
|
|
29
|
+
/**
|
|
30
|
+
* Diagnostics for `filePath` after its content became `content` on disk.
|
|
31
|
+
* Returns a formatted informational block, or "" when the file is clean,
|
|
32
|
+
* unsupported, or anything at all goes wrong (silent graceful degradation).
|
|
33
|
+
*/
|
|
34
|
+
diagnosticsAfterWrite(filePath: string, content: string): Promise<string>;
|
|
35
|
+
/** Shut down every pooled server. Safe in process exit handlers. */
|
|
36
|
+
shutdownAll(): void;
|
|
37
|
+
private collect;
|
|
38
|
+
private ensureClient;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/lsp/manager.ts"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;;;GAOG;AACH,qBAAa,UAAU;IAWnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAVtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,iFAAiF;IACjF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgD;IACxE,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAS;gBAGN,GAAG,EAAE,MAAM,EAC5B,OAAO,CAAC,EAAE,iBAAiB;IAO7B;;;;OAIG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqB/E,oEAAoE;IACpE,WAAW,IAAI,IAAI;YASL,OAAO;IA0BrB,OAAO,CAAC,YAAY;CA+BrB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { log } from "../logger.js";
|
|
3
|
+
import { LspClient } from "./client.js";
|
|
4
|
+
import { formatDiagnostics } from "./format.js";
|
|
5
|
+
import { LSP_SERVER_CATALOG, findProjectRoot, serverForFile, } from "./servers.js";
|
|
6
|
+
const DEFAULT_WARM_BUDGET_MS = 3000;
|
|
7
|
+
const DEFAULT_FIRST_BUDGET_MS = 8000;
|
|
8
|
+
const INIT_TIMEOUT_MS = 10_000;
|
|
9
|
+
/**
|
|
10
|
+
* Lazily spawns and pools language servers keyed by (serverId, projectRoot).
|
|
11
|
+
* Nothing runs until the first edit/write of a matching file. Every failure
|
|
12
|
+
* path degrades to an empty string — diagnostics are an enhancement, never a
|
|
13
|
+
* tool-breaking dependency. A failed spawn/init marks the (server, root) pair
|
|
14
|
+
* broken for the session so a missing binary costs one attempt, not one per
|
|
15
|
+
* edit.
|
|
16
|
+
*/
|
|
17
|
+
export class LspManager {
|
|
18
|
+
cwd;
|
|
19
|
+
catalog;
|
|
20
|
+
warmBudgetMs;
|
|
21
|
+
firstBudgetMs;
|
|
22
|
+
/** (serverId\0root) → in-flight or settled client; null = broken for session. */
|
|
23
|
+
clients = new Map();
|
|
24
|
+
/** Keys that have completed at least one diagnostics pass (warm). */
|
|
25
|
+
warmKeys = new Set();
|
|
26
|
+
shutDown = false;
|
|
27
|
+
constructor(cwd, options) {
|
|
28
|
+
this.cwd = cwd;
|
|
29
|
+
this.catalog = options?.catalog ?? LSP_SERVER_CATALOG;
|
|
30
|
+
this.warmBudgetMs = options?.warmBudgetMs ?? DEFAULT_WARM_BUDGET_MS;
|
|
31
|
+
this.firstBudgetMs = options?.firstBudgetMs ?? DEFAULT_FIRST_BUDGET_MS;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Diagnostics for `filePath` after its content became `content` on disk.
|
|
35
|
+
* Returns a formatted informational block, or "" when the file is clean,
|
|
36
|
+
* unsupported, or anything at all goes wrong (silent graceful degradation).
|
|
37
|
+
*/
|
|
38
|
+
async diagnosticsAfterWrite(filePath, content) {
|
|
39
|
+
if (this.shutDown)
|
|
40
|
+
return "";
|
|
41
|
+
try {
|
|
42
|
+
const spec = serverForFile(filePath, this.catalog);
|
|
43
|
+
if (!spec)
|
|
44
|
+
return "";
|
|
45
|
+
const root = findProjectRoot(filePath, spec.rootMarkers, this.cwd);
|
|
46
|
+
const key = `${spec.id}\u0000${root}`;
|
|
47
|
+
const budgetMs = this.warmKeys.has(key) ? this.warmBudgetMs : this.firstBudgetMs;
|
|
48
|
+
const work = this.collect(key, spec, root, filePath, content, budgetMs);
|
|
49
|
+
// On budget overrun the work keeps running in the background so the
|
|
50
|
+
// now-warm server can serve the next edit; swallow its eventual outcome.
|
|
51
|
+
return await withBudget(work, budgetMs);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
log("WARN", "lsp", `diagnostics failed for ${filePath}`, {
|
|
55
|
+
error: error instanceof Error ? error.message : String(error),
|
|
56
|
+
});
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Shut down every pooled server. Safe in process exit handlers. */
|
|
61
|
+
shutdownAll() {
|
|
62
|
+
this.shutDown = true;
|
|
63
|
+
for (const pending of this.clients.values()) {
|
|
64
|
+
void pending.then((client) => client?.shutdown()).catch(() => { });
|
|
65
|
+
}
|
|
66
|
+
this.clients.clear();
|
|
67
|
+
this.warmKeys.clear();
|
|
68
|
+
}
|
|
69
|
+
async collect(key, spec, root, filePath, content, budgetMs) {
|
|
70
|
+
const client = await this.ensureClient(key, spec, root);
|
|
71
|
+
if (!client)
|
|
72
|
+
return "";
|
|
73
|
+
if (!client.isAlive) {
|
|
74
|
+
// Crashed after init — mark broken for the session, never respawn loops.
|
|
75
|
+
this.clients.set(key, Promise.resolve(null));
|
|
76
|
+
log("WARN", "lsp", `${spec.id} server died`, { root });
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
const uri = client.syncDocument(path.resolve(this.cwd, filePath), content);
|
|
80
|
+
const diagnostics = await client.collectDiagnostics(uri, budgetMs);
|
|
81
|
+
this.warmKeys.add(key);
|
|
82
|
+
if (diagnostics === null)
|
|
83
|
+
return "";
|
|
84
|
+
const relPath = path.relative(this.cwd, path.resolve(this.cwd, filePath));
|
|
85
|
+
return formatDiagnostics(relPath, diagnostics);
|
|
86
|
+
}
|
|
87
|
+
ensureClient(key, spec, root) {
|
|
88
|
+
const existing = this.clients.get(key);
|
|
89
|
+
if (existing)
|
|
90
|
+
return existing;
|
|
91
|
+
const pending = (async () => {
|
|
92
|
+
const command = spec.resolveCommand(root);
|
|
93
|
+
if (!command) {
|
|
94
|
+
log("INFO", "lsp", `${spec.id} language server not available`, { root });
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const startedAt = Date.now();
|
|
99
|
+
const client = new LspClient(spec, root, command);
|
|
100
|
+
await client.initialize(INIT_TIMEOUT_MS);
|
|
101
|
+
log("INFO", "lsp", `${spec.id} server initialized`, {
|
|
102
|
+
root,
|
|
103
|
+
ms: String(Date.now() - startedAt),
|
|
104
|
+
});
|
|
105
|
+
return client;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
log("WARN", "lsp", `${spec.id} server failed to start`, {
|
|
109
|
+
root,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
});
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
this.clients.set(key, pending);
|
|
116
|
+
return pending;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Race `work` against the budget; overrun resolves "" and lets work finish. */
|
|
120
|
+
function withBudget(work, budgetMs) {
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
const timer = setTimeout(() => resolve(""), budgetMs);
|
|
123
|
+
timer.unref();
|
|
124
|
+
work
|
|
125
|
+
.then((result) => {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
resolve(result);
|
|
128
|
+
})
|
|
129
|
+
.catch(() => {
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
resolve("");
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/lsp/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.test.d.ts","sourceRoot":"","sources":["../../../src/core/lsp/manager.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { LspManager } from "./manager.js";
|
|
7
|
+
const FIXTURE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../tools/__fixtures__/fake-lsp-server.mjs");
|
|
8
|
+
function fakeSpec(serverArgs = [], overrides) {
|
|
9
|
+
return {
|
|
10
|
+
id: "fake",
|
|
11
|
+
extensions: [".fake"],
|
|
12
|
+
rootMarkers: ["fake-root.json"],
|
|
13
|
+
languageIdFor: () => "fake",
|
|
14
|
+
resolveCommand: () => ({ command: process.execPath, args: [FIXTURE, ...serverArgs] }),
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe("LspManager", () => {
|
|
19
|
+
let tmpDir;
|
|
20
|
+
let managers;
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "lsp-manager-test-"));
|
|
23
|
+
await fs.writeFile(path.join(tmpDir, "fake-root.json"), "{}");
|
|
24
|
+
managers = [];
|
|
25
|
+
});
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
for (const manager of managers)
|
|
28
|
+
manager.shutdownAll();
|
|
29
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
30
|
+
});
|
|
31
|
+
function makeManager(spec, budgets) {
|
|
32
|
+
const manager = new LspManager(tmpDir, {
|
|
33
|
+
catalog: [spec],
|
|
34
|
+
warmBudgetMs: budgets?.warm ?? 5000,
|
|
35
|
+
firstBudgetMs: budgets?.first ?? 5000,
|
|
36
|
+
});
|
|
37
|
+
managers.push(manager);
|
|
38
|
+
return manager;
|
|
39
|
+
}
|
|
40
|
+
it("returns formatted diagnostics for broken content", async () => {
|
|
41
|
+
const manager = makeManager(fakeSpec());
|
|
42
|
+
const filePath = path.join(tmpDir, "broken.fake");
|
|
43
|
+
const result = await manager.diagnosticsAfterWrite(filePath, "ok line\nhas ERROR here\n");
|
|
44
|
+
expect(result).toContain("Diagnostics in broken.fake");
|
|
45
|
+
expect(result).toContain("L2:5 fake error on line 2 (fake)");
|
|
46
|
+
});
|
|
47
|
+
it("returns empty string once a follow-up edit fixes the file", async () => {
|
|
48
|
+
const manager = makeManager(fakeSpec());
|
|
49
|
+
const filePath = path.join(tmpDir, "cycle.fake");
|
|
50
|
+
const broken = await manager.diagnosticsAfterWrite(filePath, "ERROR\n");
|
|
51
|
+
expect(broken).toContain("fake error on line 1");
|
|
52
|
+
const fixed = await manager.diagnosticsAfterWrite(filePath, "all good\n");
|
|
53
|
+
expect(fixed).toBe("");
|
|
54
|
+
const rebroken = await manager.diagnosticsAfterWrite(filePath, "fine\nERROR again\n");
|
|
55
|
+
expect(rebroken).toContain("fake error on line 2");
|
|
56
|
+
});
|
|
57
|
+
it("works with pull-diagnostics servers", async () => {
|
|
58
|
+
const manager = makeManager(fakeSpec(["--pull"]));
|
|
59
|
+
const filePath = path.join(tmpDir, "pull.fake");
|
|
60
|
+
const result = await manager.diagnosticsAfterWrite(filePath, "ERROR\n");
|
|
61
|
+
expect(result).toContain("fake error on line 1");
|
|
62
|
+
});
|
|
63
|
+
it("returns empty string for unsupported extensions without spawning", async () => {
|
|
64
|
+
const manager = makeManager(fakeSpec());
|
|
65
|
+
const result = await manager.diagnosticsAfterWrite(path.join(tmpDir, "readme.md"), "# hi");
|
|
66
|
+
expect(result).toBe("");
|
|
67
|
+
});
|
|
68
|
+
it("returns empty string when the time budget is exceeded", async () => {
|
|
69
|
+
const manager = makeManager(fakeSpec(["--delay-ms=2000"]), { warm: 300, first: 300 });
|
|
70
|
+
const filePath = path.join(tmpDir, "slow.fake");
|
|
71
|
+
const started = Date.now();
|
|
72
|
+
const result = await manager.diagnosticsAfterWrite(filePath, "ERROR\n");
|
|
73
|
+
expect(result).toBe("");
|
|
74
|
+
expect(Date.now() - started).toBeLessThan(1500);
|
|
75
|
+
});
|
|
76
|
+
it("marks a server broken after spawn failure and never retries", async () => {
|
|
77
|
+
let resolveCalls = 0;
|
|
78
|
+
const spec = fakeSpec([], {
|
|
79
|
+
resolveCommand: () => {
|
|
80
|
+
resolveCalls++;
|
|
81
|
+
return { command: path.join(tmpDir, "does-not-exist-binary"), args: [] };
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
const manager = makeManager(spec);
|
|
85
|
+
const filePath = path.join(tmpDir, "broken-server.fake");
|
|
86
|
+
expect(await manager.diagnosticsAfterWrite(filePath, "ERROR\n")).toBe("");
|
|
87
|
+
expect(await manager.diagnosticsAfterWrite(filePath, "ERROR\n")).toBe("");
|
|
88
|
+
expect(resolveCalls).toBe(1);
|
|
89
|
+
});
|
|
90
|
+
it("returns empty string when no server command resolves", async () => {
|
|
91
|
+
const spec = fakeSpec([], { resolveCommand: () => null });
|
|
92
|
+
const manager = makeManager(spec);
|
|
93
|
+
const result = await manager.diagnosticsAfterWrite(path.join(tmpDir, "a.fake"), "ERROR\n");
|
|
94
|
+
expect(result).toBe("");
|
|
95
|
+
});
|
|
96
|
+
it("performs the shutdown handshake on shutdownAll", async () => {
|
|
97
|
+
const shutdownFile = path.join(tmpDir, "shutdown-marker");
|
|
98
|
+
const manager = makeManager(fakeSpec([`--shutdown-file=${shutdownFile}`]));
|
|
99
|
+
const filePath = path.join(tmpDir, "bye.fake");
|
|
100
|
+
await manager.diagnosticsAfterWrite(filePath, "ok\n");
|
|
101
|
+
manager.shutdownAll();
|
|
102
|
+
// Poll for the marker the fixture writes when it receives `shutdown`.
|
|
103
|
+
const deadline = Date.now() + 3000;
|
|
104
|
+
let seen = false;
|
|
105
|
+
while (Date.now() < deadline && !seen) {
|
|
106
|
+
seen = await fs.access(shutdownFile).then(() => true, () => false);
|
|
107
|
+
if (!seen)
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
109
|
+
}
|
|
110
|
+
expect(seen).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it("returns empty string after shutdownAll", async () => {
|
|
113
|
+
const manager = makeManager(fakeSpec());
|
|
114
|
+
manager.shutdownAll();
|
|
115
|
+
const result = await manager.diagnosticsAfterWrite(path.join(tmpDir, "a.fake"), "ERROR\n");
|
|
116
|
+
expect(result).toBe("");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=manager.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.test.js","sourceRoot":"","sources":["../../../src/core/lsp/manager.test.ts"],"names":[],"mappings":"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"}
|