stdio 0.4.0
stdio: ^0.4.0 copied to clipboard
File-descriptor-level (dup2) capture and redirection of stdout/stderr, including native/FFI and child-process output invisible to Dart streams. POSIX only.
// The three entry points of package:stdio.
//
// Run: dart run example/main.dart
// (Everything here uses Dart writes for simplicity — the whole point of the
// package is that native/FFI `write(1|2)` and child-process output are
// captured exactly the same way.)
import 'dart:io';
import 'package:stdio/stdio.dart';
Future<void> main() async {
// ── 1. Scoped: capture exactly one body, get the transcript + its value ──
final cap = await Stdio.capture<int>(() {
print('hello from stdout');
stderr.writeln('and from stderr');
return 6 * 7; // whatever the body returns comes back on `.value`
});
print('captured ${cap.lines.length} lines (body returned ${cap.value})');
print('stdout side: ${cap.out}');
print('stderr side: ${cap.err}');
// ── 2. Session: live streams + history + the real terminal ───────────────
final capture = await Stdio.start(
classify: (l) => l.text.startsWith('[db]') ? 'db' : null,
);
final seen = <String>[];
capture.output.listen(
(l) => seen.add('${l.stream.name}${l.source == null ? '' : '/${l.source}'}: ${l.text}'));
print('[db] opened'); // tagged 'db' by the classifier
stderr.writeln('plain stderr'); // untagged
// The saved terminal writes THROUGH the capture, to the real screen:
capture.terminal.writeln('(this line skips the capture entirely)');
final result = await capture.stop(); // restore fd 1/2 + full transcript
print('live listener saw: $seen');
print('history kept ${result.lines.length} lines');
// ── 3. Redirect: no capture — point fd 1/2 at a file and walk away ───────
final log = File('${Directory.systemTemp.path}/stdio_example.log');
final redirect = await Stdio.redirectToFile(log);
print('this goes to the file, not the screen');
stderr.writeln('this too, in exact write order');
await stdout.flush();
await redirect.stop();
print('log file contents:\n${log.readAsStringSync()}');
log.deleteSync();
}