()
| 277 | // ─── Internal ────────────────────────────────────────────────────────── |
| 278 | |
| 279 | _drain() { |
| 280 | // Iterative — avoids recursion stack growth when many sentinels arrive |
| 281 | // back-to-back (e.g. fast-running queued commands). |
| 282 | while (this.queue.length > 0) { |
| 283 | const head = this.queue[0]; |
| 284 | const re = new RegExp(`${head.sentinel}_(-?\\d+)_`); |
| 285 | const match = this.buffer.match(re); |
| 286 | if (!match) return; |
| 287 | |
| 288 | const sentinelStart = match.index; |
| 289 | let stdout = this.buffer.slice(0, sentinelStart); |
| 290 | stdout = stdout.replace(/\r?\n$/, ''); |
| 291 | const exitCode = parseInt(match[1], 10); |
| 292 | |
| 293 | const sentinelEnd = sentinelStart + match[0].length; |
| 294 | this.buffer = this.buffer.slice(sentinelEnd).replace(/^\r?\n/, ''); |
| 295 | |
| 296 | const cleanOutput = sanitizeToolOutput(stdout); |
| 297 | |
| 298 | clearTimeout(head.timer); |
| 299 | this.queue.shift(); |
| 300 | head.resolve({ |
| 301 | stdout: cleanOutput, |
| 302 | exitCode, |
| 303 | timedOut: head.timedOut(), |
| 304 | }); |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | _failPending(reason) { |
| 309 | while (this.queue.length > 0) { |
no test coverage detected