Merge branch 'main' into visible-shared-stdin
diff --git a/pkgs/io/CHANGELOG.md b/pkgs/io/CHANGELOG.md
index c47aee9..fccc57d 100644
--- a/pkgs/io/CHANGELOG.md
+++ b/pkgs/io/CHANGELOG.md
@@ -1,6 +1,11 @@
 ## 1.1.0-wip
 
 * Add a `deepCopyLinks` argument to `copyPath` and `copyPathSync`.
+* Remove the `@visibleForTesting` annotation from `SharedStdIn`.
+* **Potentially Breaking** Make the stream parameter required for
+  `SharedStdIn.new`.
+  * This is treated as non-breaking because the class was marked as visible
+    for testing only.
 * **Potentially Breaking** `AnsiCode` and `AnsiCodeType` marked final. These
   were never intended to support subclasses and were already closed for
   extension with private generative constructors. They are now marked `final`
diff --git a/pkgs/io/lib/src/shared_stdin.dart b/pkgs/io/lib/src/shared_stdin.dart
index 72bb50c..3bb0d27 100644
--- a/pkgs/io/lib/src/shared_stdin.dart
+++ b/pkgs/io/lib/src/shared_stdin.dart
@@ -6,8 +6,6 @@
 import 'dart:convert';
 import 'dart:io';
 
-import 'package:meta/meta.dart';
-
 /// A shared singleton instance of `dart:io`'s [stdin] stream.
 ///
 /// _Unlike_ the normal [stdin] stream, [sharedStdIn] may switch subscribers
@@ -19,18 +17,29 @@
 /// hanging.
 final SharedStdIn sharedStdIn = SharedStdIn(stdin);
 
-/// A singleton wrapper around `stdin` that allows new subscribers.
+/// A wrapper around a stream that allows new subscribers, intended for use
+/// with [stdin] or other input streams that usually only allow one subscriber.
 ///
-/// This class is visible in order to be used as a test harness for mock
-/// implementations of `stdin`. In normal programs, [sharedStdIn] should be
-/// used directly.
-@visibleForTesting
+/// If you only use this class with [stdin], you should use the [sharedStdIn]
+/// singleton.
+///
+/// You may still only have one listening [StreamSubscription] at a time.
 class SharedStdIn extends Stream<List<int>> {
   StreamController<List<int>>? _current;
   StreamSubscription<List<int>>? _sub;
 
-  SharedStdIn([Stream<List<int>>? stream]) {
-    _sub = (stream ??= stdin).listen(_onInput);
+  /// Creates a new [SharedStdIn] sharing the [stream].
+  ///
+  /// If you only use this class with [stdin], you should use the [sharedStdIn]
+  /// singleton.
+  ///
+  /// Calling this constructor more than once with the same source [stream]
+  /// will likely result in an error.
+  factory SharedStdIn(Stream<List<int>> stream) => SharedStdIn._(stream);
+
+  /// Actual constructor is private to prevent subclassing.
+  SharedStdIn._(Stream<List<int>> stream) {
+    _sub = stream.listen(_onInput);
   }
 
   /// Returns a future that completes with the next line.