| 334 | * ending must not bring down the whole daemon. |
| 335 | */ |
| 336 | export class SocketTransport extends LineBasedJsonRpcTransport { |
| 337 | private buffer = ''; |
| 338 | private closeHandlers: Array<() => void> = []; |
| 339 | |
| 340 | constructor(private socket: Socket, private prefix: string = 'cg-sock') { |
| 341 | super(); |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * Register a callback fired exactly once when the socket closes (from either |
| 346 | * side). Used by the daemon to decrement its connected-clients refcount. |
| 347 | */ |
| 348 | onClose(handler: () => void): void { |
| 349 | this.closeHandlers.push(handler); |
| 350 | } |
| 351 | |
| 352 | start(handler: MessageHandler): void { |
| 353 | this.messageHandler = handler; |
| 354 | |
| 355 | this.socket.setEncoding('utf8'); |
| 356 | this.socket.on('data', (chunk: string) => { |
| 357 | this.buffer += chunk; |
| 358 | let idx; |
| 359 | // Drain every complete line; tail-fragment stays in the buffer for the |
| 360 | // next chunk. The handler is async but we don't await it here — JSON-RPC |
| 361 | // permits out-of-order responses, and serializing here would deadlock if |
| 362 | // a handler issued a server-initiated request that needed a *later* line |
| 363 | // to arrive (e.g. roots/list mid-tools-call). |
| 364 | while ((idx = this.buffer.indexOf('\n')) !== -1) { |
| 365 | const line = this.buffer.slice(0, idx); |
| 366 | this.buffer = this.buffer.slice(idx + 1); |
| 367 | void this.handleLine(line); |
| 368 | } |
| 369 | }); |
| 370 | |
| 371 | this.socket.on('close', () => this.handleSocketClose()); |
| 372 | this.socket.on('error', (err) => { |
| 373 | // Don't crash the daemon over a broken pipe; just shut this connection. |
| 374 | process.stderr.write(`[CodeGraph daemon] socket error: ${err.message}\n`); |
| 375 | this.handleSocketClose(); |
| 376 | }); |
| 377 | } |
| 378 | |
| 379 | stop(): void { |
| 380 | if (this.stopped) return; |
| 381 | this.stopped = true; |
| 382 | this.rejectPending('Transport stopped'); |
| 383 | if (!this.socket.destroyed) { |
| 384 | this.socket.end(); |
| 385 | this.socket.destroy(); |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | /** |
| 390 | * Write a one-shot line directly to the socket (no JSON-RPC framing applied |
| 391 | * by this class — caller produces the line). The daemon uses this for the |
| 392 | * hello/handshake line that precedes the JSON-RPC stream. |
| 393 | */ |
nothing calls this directly
no outgoing calls
no test coverage detected