* Single backward pass: count all newlines (for totalLines) and extract * the last few lines as flat copies (for the CircularBuffer / progress). * Only used in pipe mode (hooks). File mode uses the shared poller.
(data: string)
| 205 | * Only used in pipe mode (hooks). File mode uses the shared poller. |
| 206 | */ |
| 207 | #updateProgress(data: string): void { |
| 208 | const MAX_PROGRESS_BYTES = 4096 |
| 209 | const MAX_PROGRESS_LINES = 100 |
| 210 | |
| 211 | let lineCount = 0 |
| 212 | const lines: string[] = [] |
| 213 | let extractedBytes = 0 |
| 214 | let pos = data.length |
| 215 | |
| 216 | while (pos > 0) { |
| 217 | const prev = data.lastIndexOf('\n', pos - 1) |
| 218 | if (prev === -1) { |
| 219 | break |
| 220 | } |
| 221 | lineCount++ |
| 222 | if ( |
| 223 | lines.length < MAX_PROGRESS_LINES && |
| 224 | extractedBytes < MAX_PROGRESS_BYTES |
| 225 | ) { |
| 226 | const lineLen = pos - prev - 1 |
| 227 | if (lineLen > 0 && lineLen <= MAX_PROGRESS_BYTES - extractedBytes) { |
| 228 | const line = data.slice(prev + 1, pos) |
| 229 | if (line.trim()) { |
| 230 | lines.push(Buffer.from(line).toString()) |
| 231 | extractedBytes += lineLen |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | pos = prev |
| 236 | } |
| 237 | |
| 238 | this.#totalLines += lineCount |
| 239 | |
| 240 | for (let i = lines.length - 1; i >= 0; i--) { |
| 241 | this.#recentLines.add(lines[i]!) |
| 242 | } |
| 243 | |
| 244 | if (this.#onProgress && lines.length > 0) { |
| 245 | const recent = this.#recentLines.getRecent(5) |
| 246 | this.#onProgress( |
| 247 | safeJoinLines(recent, '\n'), |
| 248 | safeJoinLines(this.#recentLines.getRecent(100), '\n'), |
| 249 | this.#totalLines, |
| 250 | this.#totalBytes, |
| 251 | this.#disk !== null, |
| 252 | ) |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | #spillToDisk(stderrChunk: string | null, stdoutChunk: string | null): void { |
| 257 | this.#disk = new DiskTaskOutput(this.taskId) |
no test coverage detected