Merge branch 'master' into add-docs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d73828..8932188 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
## 2.1.2-dev
* Require Dart 2.19
+* Add an example.
## 2.1.1
diff --git a/example/example.dart b/example/example.dart
new file mode 100644
index 0000000..963182d
--- /dev/null
+++ b/example/example.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:isolate';
+
+import 'package:stream_channel/isolate_channel.dart';
+import 'package:stream_channel/stream_channel.dart';
+
+Future<void> main() async {
+ // A StreamChannel<T>, is in simplest terms, a wrapper around a Stream<T> and
+ // a StreamSink<T>. For example, you can create a channel that wraps standard
+ // IO:
+ var stdioChannel = StreamChannel(stdin, stdout);
+ stdioChannel.sink.add('Hello!\n'.codeUnits);
+
+ // Like a Stream<T> can be transformed with a StreamTransformer<T>, a
+ // StreamChannel<T> can be transformed with a StreamChannelTransformer<T>.
+ // For example, we can handle standard input as strings:
+ var stringChannel = stdioChannel
+ .transform(StreamChannelTransformer.fromCodec(utf8))
+ .transformStream(LineSplitter());
+ stringChannel.sink.add('world!\n');
+
+ // You can implement StreamChannel<T> by extending StreamChannelMixin<T>, but
+ // it's much easier to use a StreamChannelController<T>. A controller has two
+ // StreamChannel<T> members: `local` and `foreign`. The creator of a
+ // controller should work with the `local` channel, while the recipient should
+ // work with the `foreign` channel, and usually will not have direct access to
+ // the underlying controller.
+ var ctrl = StreamChannelController<String>();
+ ctrl.local.stream.listen((event) {
+ // Do something useful here...
+ });
+
+ // You can also pipe events from one channel to another.
+ ctrl
+ ..foreign.pipe(stringChannel)
+ ..local.sink.add('Piped!\n');
+ await ctrl.local.sink.close();
+
+ // The StreamChannel<T> interface provides several guarantees, which can be
+ // found here:
+ // https://pub.dev/documentation/stream_channel/latest/stream_channel/StreamChannel-class.html
+ //
+ // By calling `StreamChannel<T>.withGuarantees()`, you can create a
+ // StreamChannel<T> that provides all guarantees.
+ var dummyCtrl0 = StreamChannelController<String>();
+ var guaranteedChannel = StreamChannel.withGuarantees(
+ dummyCtrl0.foreign.stream, dummyCtrl0.foreign.sink);
+
+ // To close a StreamChannel, use `sink.close()`.
+ await guaranteedChannel.sink.close();
+
+ // A MultiChannel<T> multiplexes multiple virtual channels across a single
+ // underlying transport layer. For example, an application listening over
+ // standard I/O can still support multiple clients if it has a mechanism to
+ // separate events from different clients.
+ //
+ // A MultiChannel<T> splits events into numbered channels, which are
+ // instances of VirtualChannel<T>.
+ var dummyCtrl1 = StreamChannelController<String>();
+ var multiChannel = MultiChannel<String>(dummyCtrl1.foreign);
+ var channel1 = multiChannel.virtualChannel();
+ await multiChannel.sink.close();
+
+ // The client/peer should also create its own MultiChannel<T>, connected to
+ // the underlying transport, use the corresponding ID's to handle events in
+ // their respective channels. It is up to you how to communicate channel ID's
+ // across different endpoints.
+ var dummyCtrl2 = StreamChannelController<String>();
+ var multiChannel2 = MultiChannel<String>(dummyCtrl2.foreign);
+ var channel2 = multiChannel2.virtualChannel(channel1.id);
+ await channel2.sink.close();
+ await multiChannel2.sink.close();
+
+ // Multiple instances of a Dart application can communicate easily across
+ // `SendPort`/`ReceivePort` pairs by means of the `IsolateChannel<T>` class.
+ // Typically, one endpoint will create a `ReceivePort`, and call the
+ // `IsolateChannel.connectReceive` constructor. The other endpoint will be
+ // given the corresponding `SendPort`, and then call
+ // `IsolateChannel.connectSend`.
+ var recv = ReceivePort();
+ var recvChannel = IsolateChannel.connectReceive(recv);
+ var sendChannel = IsolateChannel.connectSend(recv.sendPort);
+
+ // You must manually close `IsolateChannel<T>` sinks, however.
+ await recvChannel.sink.close();
+ await sendChannel.sink.close();
+
+ // You can use the `Disconnector` transformer to cause a channel to act as
+ // though the remote end of its transport had disconnected.
+ var disconnector = Disconnector<String>();
+ var disconnectable = stringChannel.transform(disconnector);
+ disconnectable.sink.add('Still connected!');
+ disconnector.disconnect();
+
+ // Additionally:
+ // * The `DelegatingStreamController<T>` class can be extended to build a
+ // basis for wrapping other `StreamChannel<T>` objects.
+ // * The `jsonDocument` transformer converts events to/from JSON, using
+ // the `json` codec from `dart:convert`.
+ // * `package:json_rpc_2` directly builds on top of
+ // `package:stream_channel`, so any compatible transport can be used to
+ // create interactive client/server or peer-to-peer applications (i.e.
+ // language servers, microservices, etc.
+}