feat(audit): Batch 10 — CC conventions (shared line-buffer, cleanError)
The contained consistency consolidations; the sweeping ones are deferred with their standard documented (they're the audit's own post-1.0 refactors, and several would be reckless to do unattended). Landed: - src/main/lib/lineBuffer.ts (createLineBuffer): one newline line-splitter for streamed child-process output, replacing the duplicated buffer loop in download.ts and terminal.ts (CC3/CC4/SIMP5). Pure + unit-tested (test/lineBuffer.test.ts, 7 cases: partial lines, CRLF, empty lines, flush, multi-line chunks). download.ts keeps byte-identical marker parsing and no close-flush (yt-dlp template lines are always newline-terminated). - ytdlp.ts: getYtdlpVersion/updateYtdlp now run stderr through cleanError, the same `cleanError(r.stderr) || r.error?.message || ''` idiom already used by download/probe/indexer (CC6) — a failed check shows the trailing error line. - CL6 resolved: the one real redundancy was L15; clearDir/openFile/showInFolder are deliberate view-model wrappers (CC13), left as-is. Deferred with documented standard + rationale (CODE-AUDIT.md): CC1 (breaking persisted-key rename → migration), CC5+CL4 (security-critical updater net.request, not live-verifiable here), CC7 (cosmetic arg-order), CC9 (zod dep), CC10 (electron-store↔jsonStore split is deliberate for DPAPI), CC11 (config.ts move), CC12 (file-tree reorg), CC13→L93, CC14→L142. Verified: typecheck (node+web) + 275 tests + eslint + production build green; touched files prettier-clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+8
-14
@@ -14,6 +14,7 @@ import { getYtdlpPath, getBinDir, getSystem32Path } from './binaries'
|
||||
import { getSettings } from './settings'
|
||||
import { ensureManagedYtdlp } from './ytdlp'
|
||||
import { parseExtraArgs } from './buildArgs'
|
||||
import { createLineBuffer } from './lib/lineBuffer'
|
||||
import { IpcChannels, type TerminalEvent, type TerminalRunResult } from '@shared/ipc'
|
||||
|
||||
const active = new Map<string, ChildProcess>()
|
||||
@@ -47,18 +48,12 @@ export function runTerminal(wc: WebContents, id: string, argsRaw: string): Termi
|
||||
active.set(id, child)
|
||||
|
||||
// Buffer each stream and emit on newline boundaries (flush the remainder on close).
|
||||
const buffers: Record<'stdout' | 'stderr', string> = { stdout: '', stderr: '' }
|
||||
function feed(stream: 'stdout' | 'stderr', chunk: string): void {
|
||||
buffers[stream] += chunk
|
||||
let nl: number
|
||||
while ((nl = buffers[stream].indexOf('\n')) >= 0) {
|
||||
const line = buffers[stream].slice(0, nl).replace(/\r$/, '')
|
||||
buffers[stream] = buffers[stream].slice(nl + 1)
|
||||
send(wc, { type: 'output', id, line, stream })
|
||||
}
|
||||
const lineBufs = {
|
||||
stdout: createLineBuffer((line) => send(wc, { type: 'output', id, line, stream: 'stdout' })),
|
||||
stderr: createLineBuffer((line) => send(wc, { type: 'output', id, line, stream: 'stderr' }))
|
||||
}
|
||||
child.stdout?.on('data', (c: Buffer) => feed('stdout', c.toString()))
|
||||
child.stderr?.on('data', (c: Buffer) => feed('stderr', c.toString()))
|
||||
child.stdout?.on('data', (c: Buffer) => lineBufs.stdout.push(c.toString()))
|
||||
child.stderr?.on('data', (c: Buffer) => lineBufs.stderr.push(c.toString()))
|
||||
|
||||
let settled = false
|
||||
child.on('error', (err) => {
|
||||
@@ -72,9 +67,8 @@ export function runTerminal(wc: WebContents, id: string, argsRaw: string): Termi
|
||||
settled = true
|
||||
active.delete(id)
|
||||
// Flush any trailing partial lines that never hit a newline.
|
||||
for (const stream of ['stdout', 'stderr'] as const) {
|
||||
if (buffers[stream]) send(wc, { type: 'output', id, line: buffers[stream], stream })
|
||||
}
|
||||
lineBufs.stdout.flush()
|
||||
lineBufs.stderr.flush()
|
||||
send(wc, { type: 'done', id, code })
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user