stdio
File-descriptor-level (dup2) capture and redirection of stdout/stderr, including native/FFI and child-process output invisible to Dart streams. POSIX only.
0.4.0
2.7K downloads/mo
#4825 most downloaded on pub.dev
danReynolds/stdio
What this package is like to depend on
Last release 1 months ago
20 Jul 2026
Too new to tell
only 2 dated releases
Nearly every release is documented
notes for 2 of 2 stable releases
Nothing withdrawn
no release was ever pulled
1 months old
2 releases · first in 2026
2 releases in the last 12 months
see the full history below
Release timeline
2 releases · Jul 2026 to Jul 2026Releases
latest 2-
0.4.020 Jul 2026Release notes
Open source →Pre-publish consolidation: correctness fixes at the fd layer plus a final API finalization pass (breaking — 0.4.0 was never published).
Correctness
stop()now restores the saved descriptors' original file-status flags. The mirror-to-original path setsO_NONBLOCKon the saved fds, and the flag lives on the open file description — shared with the fd 1/2 handed back on stop (and with the parent shell when the target is the tty) — so post-stop writes couldEAGAIN, and the leak outlived the process.maxLineBytesis now enforced on newline-terminated lines too: a long line arriving within one read chunk previously bypassed the cap entirely. Runs are split byte-exactly into cap-sized pieces (an exactly-cap-sized line stays whole); the README's split guarantee is now unconditionally true.- A reader-isolate death mid-session now restores fd 1/2 immediately
(previously they stayed pointed at pipes nobody drained, and the app's next
~64 KiB of prints wedged the main isolate in a blocking
write()).pause()/resume()/startProcess/adoptthrowStateErroron a degraded session;readerError+ closed streams remain the signal. - Saved-fd mirror overflow drops are now counted on the new dedicated
mirrorDroppedBytescounter (they were silent; they are a separate channel from the line counters, which they deliberately do not pollute). droppedLines/droppedBytesnow count exactly the lines missing fromhistory: reader-side backpressure drops PLUS history-ring evictions (evictions were previously uncounted, making the documented totals wrong).close()is no longer retried onEINTR(on Linux/macOS the fd is deallocated regardless, so the retry was a double-close that could hit an unrelated reallocated fd).stop()'s drain-timeout path no longer closes fds a wedged-but-alive reader may still be blocked inread()/poll()on (deliberate bounded leak instead of fd-reuse byte theft), and the backstopIsolate.kill(immediate)is now issued only on that path — it can no longer race a healthy reader's cleanup.startProcess()/adopt()afterstop()now throwStateErrorinstead of silently discarding the child's output.- A throwing
classifycallback no longer burns reader send credits (which permanently stalled the live feed after ~8 throws): the line is treated as unclassified and the first error is surfaced once onreaderError. open(2)is bound throughVarArgslike the other variadic syscalls.StdioRedirect.stop()releases the process-wide slot in afinally, so a (nearly impossible) restore failure can no longer wedge the package with no way to start a fresh session.
Breaking API changes
- Renamed the main class
StdioCapture→Stdio, so the entry points read asStdio.capture/Stdio.start/Stdio.redirectToFile(matching the package name). Capturedis nowCaptured<T>with avaluefield:Stdio.capture<T>()returns the body's result alongside the transcript, andstop()returnsFuture<Captured<void>>.Capturedalso gaineddroppedLines/droppedBytes(lines missing fromlines).Stdio.capture()acceptshistoryLines/maxLineBytes/mirrorToFile/classify, forwarded tostart().startProcess()returnsCapturedProcess(theprocessplus adrainedfuture that completes on full delivery);adopt()now returnsCapturedProcesstoo (it was the bare drain future — ambiguous at the call site) and throws a clearStateErrorwhen the child's streams were already claimed (adopted twice / listened before adopt).- Renamed
mirrorToSavedFds:→mirrorToOriginal:(introduced unpublished after 0.3.0), with real documentation and, new in this release, drop accounting (mirrorDroppedBytes) and end-to-end tests. terminalis now typedStdoutTerminalSink— anIOSinkwith the terminal accessors and a concreteStdout— and the redundantterminalStdoutis gone (it was a second mutable-encoding handle to the same fd).terminalStderr(introduced unpublished after 0.3.0) stays, as the saved fd 2 counterpart. The abstractTerminalSinkinterface is gone too: it had no consumers and both implementations are final, soFdTerminalSinkis now the documented base type (nullable-safecolumns/rows;StdoutTerminalSinkadds the throwing Stdout-contractterminalColumns/terminalLinesview of the same data).CapturedLinegainedseq, a monotonic per-session sequence number stamped as each line enters the transcript — the robust history→stream stitch acrossawaits (the constructor now requires it).CapturedLine.textstrips one trailing\r(CRLF output reads clean);bytesstays byte-exact.StdoutTerminalSink.writelnhonorslineTerminator(payload\ns still pass through untranslated).StdioExceptionmoved to its own library and is now exported (the 0.4.0 pre-release changelog wrongly called the non-export deliberate: callers could neither catch it by type nor readreaderErrorusefully).- New
Stdio.anyActivestatic — advisory "is any capture/redirect holding fd 1/2" probe for coordination; the uncoordinated secondstart()still throwsStateError. - pubspec:
platforms: linux, macosdeclared explicitly (POSIX-only).
Docs
- The README and library summary are general-purpose — capturing native and child output is the headline; the terminal handle is one use among several.
- Honest transcripts:
stop()/capture()return the retained transcript (the lasthistoryLineslines; seedroppedLines) — the docs no longer claim everything is guaranteed present, and note that a throwingcapture()body loses the transcript (usestart()/stop()for post-mortems). - Documented: main-isolate-only entry points, the double-start
StateErroron every entry point,redirectToFilemoving BOTH descriptors, the FIFO-mirror caveat,readerErrorcarrying the stringified isolate error, the gap-free history→subscribe recipe (andseqstitching), and the sinkflush/closeno-op semantics on the sink type itself. dart run stdio:verifyis mentioned as the platform self-check.
-
0.3.019 Jul 2026Release notes
Open source →pause()/resume()/isPaused: temporarily point fd 1/2 back at the real terminal without tearing the session down — the terminal-handoff primitive for TUIs (spawn$EDITOR/a pager withinheritStdiomid-session; the child inherits the real descriptors). Buffered writers are flushed at both edges so bytes land on the side of the boundary they were written on.stop()remains safe while paused.