Merge remote-tracking branch 'origin/master' into iterablex
diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
index 439e796..bf6b38a 100644
--- a/.github/dependabot.yaml
+++ b/.github/dependabot.yaml
@@ -8,3 +8,7 @@
       interval: monthly
     labels:
       - autosubmit
+    groups:
+      github-actions:
+        patterns:
+          - "*"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ac62fe0..e21d2e9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,8 +21,8 @@
       matrix:
         sdk: [dev]
     steps:
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
-      - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d
+      - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
+      - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30
         with:
           sdk: ${{ matrix.sdk }}
       - id: install
@@ -48,8 +48,8 @@
         os: [ubuntu-latest]
         sdk: [3.1.0, dev]
     steps:
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
-      - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d
+      - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
+      - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30
         with:
           sdk: ${{ matrix.sdk }}
       - id: install
@@ -61,3 +61,6 @@
       - name: Run Chrome tests
         run: dart test --platform chrome --test-randomize-ordering-seed=random
         if: always() && steps.install.outcome == 'success'
+      - name: Run Chrome tests - wasm
+        run: dart test --platform chrome --compiler dart2wasm
+        if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f97cba..332686b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,11 +4,16 @@
 - Shuffle `IterableExtension.sample` results.
 - Fix `mergeSort` when the runtime iterable generic is a subtype of the static
   generic.
+- `CanonicalizedMap`: added constructor `fromEntries`.
+- Mark "mixin" classes as `mixin`.
+- Deprecate `transitiveClosure`. Consider using `package:graphs`.
 - Remove `firstOrNull`, `lastOrNull`, `singleOrNull` and `elementAtOrNull()`
   from `IterableExtensions`. Since Dart 3.0, exact equivalents to these are
   available in Dart core, so 'package:collection' would only be shadowing these.
+- Deprecate `whereNotNull()` from `IterableNullableExtension`. Use `nonNulls`
+  instead - this is an equivalent extension available in Dart core since
+  version 3.0.
 - Require Dart `^3.1.0`
-- Mark "mixin" classes as `mixin`.
 
 ## 1.18.0
 
diff --git a/lib/algorithms.dart b/lib/algorithms.dart
index 71a9f67..ac43242 100644
--- a/lib/algorithms.dart
+++ b/lib/algorithms.dart
@@ -4,7 +4,7 @@
 
 /// Import `collection.dart` instead.
 @Deprecated('Will be removed in collection 2.0.0.')
-library dart.pkg.collection.algorithms;
+library;
 
 export 'src/algorithms.dart'
     show binarySearch, insertionSort, lowerBound, mergeSort, reverse, shuffle;
diff --git a/lib/equality.dart b/lib/equality.dart
index 021430b..5dc158c 100644
--- a/lib/equality.dart
+++ b/lib/equality.dart
@@ -4,6 +4,6 @@
 
 /// Import `collection.dart` instead.
 @Deprecated('Will be removed in collection 2.0.0.')
-library dart.pkg.collection.equality;
+library;
 
 export 'src/equality.dart';
diff --git a/lib/iterable_zip.dart b/lib/iterable_zip.dart
index 1ef5595..bd0b1ef 100644
--- a/lib/iterable_zip.dart
+++ b/lib/iterable_zip.dart
@@ -4,6 +4,6 @@
 
 /// Import `collection.dart` instead.
 @Deprecated('Will be removed in collection 2.0.0.')
-library dart.pkg.collection.iterable_zip;
+library;
 
 export 'src/iterable_zip.dart';
diff --git a/lib/priority_queue.dart b/lib/priority_queue.dart
index 9ed8be8..7505ce4 100644
--- a/lib/priority_queue.dart
+++ b/lib/priority_queue.dart
@@ -4,6 +4,6 @@
 
 /// Import `collection.dart` instead.
 @Deprecated('Will be removed in collection 2.0.0.')
-library dart.pkg.collection.priority_queue;
+library;
 
 export 'src/priority_queue.dart';
diff --git a/lib/src/algorithms.dart b/lib/src/algorithms.dart
index f5ea8d3..bb5843c 100644
--- a/lib/src/algorithms.dart
+++ b/lib/src/algorithms.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// A selection of data manipulation algorithms.
-library pkg.collection.algorithms;
+library;
 
 import 'dart:math' show Random;
 
diff --git a/lib/src/canonicalized_map.dart b/lib/src/canonicalized_map.dart
index 8490924..4103843 100644
--- a/lib/src/canonicalized_map.dart
+++ b/lib/src/canonicalized_map.dart
@@ -46,6 +46,23 @@
     addAll(other);
   }
 
+  /// Creates a canonicalized map that is initialized with the key/value pairs
+  /// of [entries].
+  ///
+  /// The [canonicalize] function should return the canonical value for the
+  /// given key. Keys with the same canonical value are considered equivalent.
+  ///
+  /// The [isValidKey] function is called before calling [canonicalize] for
+  /// methods that take arbitrary objects. It can be used to filter out keys
+  /// that can't be canonicalized.
+  CanonicalizedMap.fromEntries(
+      Iterable<MapEntry<K, V>> entries, C Function(K key) canonicalize,
+      {bool Function(K key)? isValidKey})
+      : _canonicalize = canonicalize,
+        _isValidKeyFn = isValidKey {
+    addEntries(entries);
+  }
+
   CanonicalizedMap._(
       this._canonicalize, this._isValidKeyFn, Map<C, MapEntry<K, V>> base) {
     _base.addAll(base);
diff --git a/lib/src/functions.dart b/lib/src/functions.dart
index 8f60b26..fb67c9f 100644
--- a/lib/src/functions.dart
+++ b/lib/src/functions.dart
@@ -120,6 +120,7 @@
 /// that vertex has no outgoing edges. This isn't checked, but if it's not
 /// satisfied, the function may crash or provide unexpected output. For example,
 /// `{"a": ["b"]}` is not valid, but `{"a": ["b"], "b": []}` is.
+@Deprecated('This method will be removed. Consider using package:graphs.')
 Map<T, Set<T>> transitiveClosure<T>(Map<T, Iterable<T>> graph) {
   // This uses [Warshall's algorithm][], modified not to add a vertex from each
   // node to itself.
diff --git a/lib/src/iterable_extensions.dart b/lib/src/iterable_extensions.dart
index 5def483..02d73a7 100644
--- a/lib/src/iterable_extensions.dart
+++ b/lib/src/iterable_extensions.dart
@@ -572,6 +572,7 @@
   /// of this iterable, in their original iteration order.
   ///
   /// For an `Iterable<X?>`, this method is equivalent to `.whereType<X>()`.
+  @Deprecated('Use .nonNulls instead.')
   Iterable<T> whereNotNull() sync* {
     for (var element in this) {
       if (element != null) yield element;
diff --git a/lib/wrappers.dart b/lib/wrappers.dart
index d3a2ff6..be529ca 100644
--- a/lib/wrappers.dart
+++ b/lib/wrappers.dart
@@ -4,7 +4,7 @@
 
 /// Import `collection.dart` instead.
 @Deprecated('Will be removed in collection 2.0.0.')
-library dart.pkg.collection.wrappers;
+library;
 
 export 'src/canonicalized_map.dart';
 export 'src/unmodifiable_wrappers.dart';
diff --git a/pubspec.yaml b/pubspec.yaml
index 99e423f..d242250 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -12,5 +12,5 @@
   sdk: ^3.1.0
 
 dev_dependencies:
-  dart_flutter_team_lints: ^2.0.0
+  dart_flutter_team_lints: ^3.0.0
   test: ^1.16.0
diff --git a/test/canonicalized_map_test.dart b/test/canonicalized_map_test.dart
index 9ae4657..aadb734 100644
--- a/test/canonicalized_map_test.dart
+++ b/test/canonicalized_map_test.dart
@@ -205,6 +205,24 @@
     });
   });
 
+  group('CanonicalizedMap.fromEntries', () {
+    test('canonicalizes its keys', () {
+      var map = CanonicalizedMap.fromEntries(
+          {'1': 'value 1', '2': 'value 2', '3': 'value 3'}.entries, int.parse);
+      expect(map['01'], equals('value 1'));
+      expect(map['02'], equals('value 2'));
+      expect(map['03'], equals('value 3'));
+    });
+
+    test('uses the final value for collisions', () {
+      var map = CanonicalizedMap.fromEntries(
+          {'1': 'value 1', '01': 'value 2', '001': 'value 3'}.entries,
+          int.parse);
+      expect(map.length, equals(1));
+      expect(map['0001'], equals('value 3'));
+    });
+  });
+
   group('CanonicalizedMap.toMapOfCanonicalKeys', () {
     test('convert to a `Map<C,V>`', () {
       var map = CanonicalizedMap.from(
diff --git a/test/extensions_test.dart b/test/extensions_test.dart
index e235f5e..30002fc 100644
--- a/test/extensions_test.dart
+++ b/test/extensions_test.dart
@@ -836,17 +836,47 @@
     group('of nullable', () {
       group('.whereNotNull', () {
         test('empty', () {
-          expect(iterable(<int?>[]).whereNotNull(), isEmpty);
+          expect(
+              iterable(<int?>[])
+                  .whereNotNull(), // ignore: deprecated_member_use_from_same_package
+              isEmpty);
         });
         test('single', () {
-          expect(iterable(<int?>[null]).whereNotNull(), isEmpty);
-          expect(iterable(<int?>[1]).whereNotNull(), [1]);
+          expect(
+              iterable(<int?>[
+                null
+              ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package
+              isEmpty);
+          expect(
+              iterable(<int?>[
+                1
+              ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package
+              [1]);
         });
         test('multiple', () {
-          expect(iterable(<int?>[1, 3, 5]).whereNotNull(), [1, 3, 5]);
-          expect(iterable(<int?>[null, null, null]).whereNotNull(), isEmpty);
           expect(
-              iterable(<int?>[1, null, 3, null, 5]).whereNotNull(), [1, 3, 5]);
+              iterable(<int?>[
+                1,
+                3,
+                5
+              ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package
+              [1, 3, 5]);
+          expect(
+              iterable(<int?>[
+                null,
+                null,
+                null
+              ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package
+              isEmpty);
+          expect(
+              iterable(<int?>[
+                1,
+                null,
+                3,
+                null,
+                5
+              ]).whereNotNull(), // ignore: deprecated_member_use_from_same_package
+              [1, 3, 5]);
         });
       });
     });
diff --git a/test/functions_test.dart b/test/functions_test.dart
index cc97327..f602303 100644
--- a/test/functions_test.dart
+++ b/test/functions_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// ignore_for_file: deprecated_member_use_from_same_package
+
 import 'package:collection/collection.dart';
 import 'package:test/test.dart';
 
@@ -9,7 +11,6 @@
   group('mapMap()', () {
     test('with an empty map returns an empty map', () {
       expect(
-          // ignore: deprecated_member_use_from_same_package
           mapMap({},
               key: expectAsync2((_, __) {}, count: 0),
               value: expectAsync2((_, __) {}, count: 0)),
@@ -18,7 +19,6 @@
 
     test('with no callbacks, returns a copy of the map', () {
       var map = {'foo': 1, 'bar': 2};
-      // ignore: deprecated_member_use_from_same_package
       var result = mapMap<String, int, String, int>(map);
       expect(result, equals({'foo': 1, 'bar': 2}));
 
@@ -29,7 +29,6 @@
 
     test("maps the map's keys", () {
       expect(
-          // ignore: deprecated_member_use_from_same_package
           mapMap<String, int, dynamic, int>({'foo': 1, 'bar': 2},
               key: (dynamic key, dynamic value) => key[value]),
           equals({'o': 1, 'r': 2}));
@@ -37,7 +36,6 @@
 
     test("maps the map's values", () {
       expect(
-          // ignore: deprecated_member_use_from_same_package
           mapMap<String, int, String, dynamic>({'foo': 1, 'bar': 2},
               value: (dynamic key, dynamic value) => key[value]),
           equals({'foo': 'o', 'bar': 'r'}));
@@ -45,7 +43,6 @@
 
     test("maps both the map's keys and values", () {
       expect(
-          // ignore: deprecated_member_use_from_same_package
           mapMap({'foo': 1, 'bar': 2},
               key: (dynamic key, dynamic value) => '$key$value',
               value: (dynamic key, dynamic value) => key[value]),