| Index: lib/src/utils.dart
 | 
| diff --git a/lib/src/utils.dart b/lib/src/utils.dart
 | 
| index 7ce6724e17c31d67239f2754e61c1757c2532b1e..c9975e5f9ae769a030c2dfa729dcd03a21c44863 100644
 | 
| --- a/lib/src/utils.dart
 | 
| +++ b/lib/src/utils.dart
 | 
| @@ -7,6 +7,7 @@ library barback.utils;
 | 
|  import 'dart:async';
 | 
|  import 'dart:typed_data';
 | 
|  
 | 
| +import 'package:async/async.dart';
 | 
|  import 'package:stack_trace/stack_trace.dart';
 | 
|  
 | 
|  /// A class that represents a value or an error.
 | 
| @@ -129,8 +130,9 @@ Uint8List toUint8List(List<int> input) {
 | 
|  ///
 | 
|  /// This returns a map whose keys are the return values of [fn] and whose values
 | 
|  /// are lists of each element in [iter] for which [fn] returned that key.
 | 
| -Map<Object, List> groupBy(Iterable iter, fn(element)) {
 | 
| -  var map = {};
 | 
| +Map<Object/*=T*/, List/*<S>*/> groupBy/*<S, T>*/(Iterable/*<S>*/ iter,
 | 
| +    /*=T*/ fn(/*=S*/ element)) {
 | 
| +  var map = /*<T, List<S>>*/{};
 | 
|    for (var element in iter) {
 | 
|      var list = map.putIfAbsent(fn(element), () => []);
 | 
|      list.add(element);
 | 
| @@ -156,27 +158,35 @@ List flatten(Iterable nested) {
 | 
|  }
 | 
|  
 | 
|  /// Returns the union of all elements in each set in [sets].
 | 
| -Set unionAll(Iterable<Set> sets) =>
 | 
| +Set/*<T>*/ unionAll/*<T>*/(Iterable<Set/*<T>*/> sets) =>
 | 
|    sets.fold(new Set(), (union, set) => union.union(set));
 | 
|  
 | 
|  /// Creates a new map from [map] with new keys and values.
 | 
|  ///
 | 
|  /// The return values of [keyFn] are used as the keys and the return values of
 | 
|  /// [valueFn] are used as the values for the new map.
 | 
| -Map mapMap(Map map, keyFn(key, value), valueFn(key, value)) =>
 | 
| +Map/*<K2, V2>*/ mapMap/*<K1, V1, K2, V2>*/(Map/*<K1, V1>*/ map,
 | 
| +    /*=K2*/ keyFn(/*=K1*/ key, /*=V1*/ value),
 | 
| +    /*=V2*/ valueFn(/*=K1*/ key, /*=V1*/ value)) =>
 | 
|    new Map.fromIterable(map.keys,
 | 
| -      key: (key) => keyFn(key, map[key]),
 | 
| -      value: (key) => valueFn(key, map[key]));
 | 
| +      key: (key) => keyFn(key as dynamic/*=K1*/, map[key]),
 | 
| +      value: (key) => valueFn(key as dynamic/*=K1*/, map[key]));
 | 
|  
 | 
|  /// Creates a new map from [map] with the same keys.
 | 
|  ///
 | 
|  /// The return values of [fn] are used as the values for the new map.
 | 
| -Map mapMapValues(Map map, fn(key, value)) => mapMap(map, (key, _) => key, fn);
 | 
| +Map/*<K, V2>*/ mapMapValues/*<K, V1, V2>*/(Map/*<K, V1>*/ map,
 | 
| +    /*=V2*/ fn(/*=K*/ key, /*=V1*/ value)) =>
 | 
| +  // TODO(nweiz): Don't explicitly type [key] when sdk#25490 is fixed.
 | 
| +  mapMap(map, (/*=K*/ key, _) => key, fn);
 | 
|  
 | 
|  /// Creates a new map from [map] with the same keys.
 | 
|  ///
 | 
|  /// The return values of [fn] are used as the keys for the new map.
 | 
| -Map mapMapKeys(Map map, fn(key, value)) => mapMap(map, fn, (_, value) => value);
 | 
| +Map/*<K2, V>*/ mapMapKeys/*<K1, V, K2>*/(Map/*<K1, V>*/ map,
 | 
| +    /*=K2*/ fn(/*=K1*/ key, /*=V*/ value)) =>
 | 
| +  // TODO(nweiz): Don't explicitly type [value] when sdk#25490 is fixed.
 | 
| +  mapMap(map, fn, (_, /*=V*/ value) => value);
 | 
|  
 | 
|  /// Returns whether [set1] has exactly the same elements as [set2].
 | 
|  bool setEquals(Set set1, Set set2) =>
 | 
| @@ -186,15 +196,17 @@ bool setEquals(Set set1, Set set2) =>
 | 
|  ///
 | 
|  /// If [broadcast] is true, this will return a broadcast stream; otherwise, it
 | 
|  /// will return a buffered stream.
 | 
| -Stream mergeStreams(Iterable<Stream> streams, {bool broadcast: false}) {
 | 
| +Stream/*<T>*/ mergeStreams/*<T>*/(Iterable<Stream/*<T>*/> streams,
 | 
| +    {bool broadcast: false}) {
 | 
|    streams = streams.toList();
 | 
|    var doneCount = 0;
 | 
|    // Use a sync stream to preserve the synchrony behavior of the input streams.
 | 
|    // If the inputs are sync, then this will be sync as well; if the inputs are
 | 
|    // async, then the events we receive will also be async, and forwarding them
 | 
|    // sync won't change that.
 | 
| -  var controller = broadcast ? new StreamController.broadcast(sync: true)
 | 
| -      : new StreamController(sync: true);
 | 
| +  var controller = broadcast
 | 
| +      ? new StreamController/*<T>*/.broadcast(sync: true)
 | 
| +      : new StreamController/*<T>*/(sync: true);
 | 
|  
 | 
|    for (var stream in streams) {
 | 
|      stream.listen(
 | 
| @@ -238,10 +250,7 @@ Future pumpEventQueue([int times=20]) {
 | 
|  /// Like `new Future`, but avoids issue 11911 by using `new Future.value` under
 | 
|  /// the covers.
 | 
|  // TODO(jmesserly): doc comment changed to due 14601.
 | 
| -Future newFuture(callback()) => new Future.value().then((_) => callback());
 | 
| -
 | 
| -/// Like [Future.sync], but wraps the Future in [Chain.track] as well.
 | 
| -Future syncFuture(callback()) => Chain.track(new Future.sync(callback));
 | 
| +Future/*<T>*/ newFuture/*<T>*/(/*=T*/ callback()) async => await callback();
 | 
|  
 | 
|  /// Returns a buffered stream that will emit the same values as the stream
 | 
|  /// returned by [future] once [future] completes.
 | 
| @@ -252,17 +261,18 @@ Future syncFuture(callback()) => Chain.track(new Future.sync(callback));
 | 
|  /// If [broadcast] is true, a broadcast stream is returned. This assumes that
 | 
|  /// the stream returned by [future] will be a broadcast stream as well.
 | 
|  /// [broadcast] defaults to false.
 | 
| -Stream futureStream(Future<Stream> future, {bool broadcast: false}) {
 | 
| -  var subscription;
 | 
| -  var controller;
 | 
| +Stream/*<T>*/ futureStream/*<T>*/(Future<Stream/*<T>*/> future,
 | 
| +    {bool broadcast: false}) {
 | 
| +  StreamSubscription/*<T>*/ subscription;
 | 
| +  StreamController/*<T>*/ controller;
 | 
|  
 | 
| -  future = future.catchError((e, stackTrace) {
 | 
| +  future = DelegatingFuture.typed(future.catchError((e, stackTrace) {
 | 
|      // Since [controller] is synchronous, it's likely that emitting an error
 | 
|      // will cause it to be cancelled before we call close.
 | 
|      if (controller != null) controller.addError(e, stackTrace);
 | 
|      if (controller != null) controller.close();
 | 
|      controller = null;
 | 
| -  });
 | 
| +  }));
 | 
|  
 | 
|    onListen() {
 | 
|      future.then((stream) {
 | 
| @@ -281,10 +291,10 @@ Stream futureStream(Future<Stream> future, {bool broadcast: false}) {
 | 
|    }
 | 
|  
 | 
|    if (broadcast) {
 | 
| -    controller = new StreamController.broadcast(
 | 
| +    controller = new StreamController/*<T>*/.broadcast(
 | 
|          sync: true, onListen: onListen, onCancel: onCancel);
 | 
|    } else {
 | 
| -    controller = new StreamController(
 | 
| +    controller = new StreamController/*<T>*/(
 | 
|          sync: true, onListen: onListen, onCancel: onCancel);
 | 
|    }
 | 
|    return controller.stream;
 | 
| @@ -294,10 +304,10 @@ Stream futureStream(Future<Stream> future, {bool broadcast: false}) {
 | 
|  /// [callback].
 | 
|  ///
 | 
|  /// [callback] will only be called when the returned [Stream] gets a subscriber.
 | 
| -Stream callbackStream(Stream callback()) {
 | 
| -  var subscription;
 | 
| -  var controller;
 | 
| -  controller = new StreamController(onListen: () {
 | 
| +Stream/*<T>*/ callbackStream/*<T>*/(Stream/*<T>*/ callback()) {
 | 
| +  StreamSubscription/*<T>*/ subscription;
 | 
| +  StreamController/*<T>*/ controller;
 | 
| +  controller = new StreamController/*<T>*/(onListen: () {
 | 
|      subscription = callback().listen(controller.add,
 | 
|          onError: controller.addError,
 | 
|          onDone: controller.close);
 | 
| @@ -313,13 +323,14 @@ Stream callbackStream(Stream callback()) {
 | 
|  ///
 | 
|  /// The returned stream will enqueue events from [broadcast] until a listener is
 | 
|  /// attached, then pipe events to that listener.
 | 
| -Stream broadcastToSingleSubscription(Stream broadcast) {
 | 
| +Stream/*<T>*/ broadcastToSingleSubscription/*<T>*/(Stream/*<T>*/ broadcast) {
 | 
|    if (!broadcast.isBroadcast) return broadcast;
 | 
|  
 | 
|    // TODO(nweiz): Implement this using a transformer when issues 18588 and 18586
 | 
|    // are fixed.
 | 
|    var subscription;
 | 
| -  var controller = new StreamController(onCancel: () => subscription.cancel());
 | 
| +  var controller = new StreamController/*<T>*/(
 | 
| +      onCancel: () => subscription.cancel());
 | 
|    subscription = broadcast.listen(controller.add,
 | 
|        onError: controller.addError,
 | 
|        onDone: controller.close);
 | 
| 
 |