diff --git a/bin/matrixoidc.dart b/bin/matrixoidc.dart index e405b09..edf5181 100644 --- a/bin/matrixoidc.dart +++ b/bin/matrixoidc.dart @@ -1,5 +1,5 @@ import "dart:io"; -import "package:args/args.dart"; +import 'package:cli_tools/config.dart'; import "package:matrixoidc/controllers/settings_controller.dart"; import "package:matrixoidc/helpers/api_helper.dart"; import "package:riverpod/riverpod.dart"; @@ -8,18 +8,18 @@ import "package:shelf/shelf_io.dart"; import "package:shelf_router/shelf_router.dart"; void main(List argsRaw) async { - final parser = ArgParser() + final parser = ConfigParser() + ..addFlag("help", abbr: "h") ..addOption("socket", abbr: "s") ..addOption("serviceDomain", abbr: "d") ..addOption("port", abbr: "p", defaultsTo: "8080") ..addOption("address", abbr: "a", defaultsTo: "127.0.0.1") ..addOption("issuer", abbr: "i", mandatory: true) - ..addOption("homeserver", abbr: "h", mandatory: true) + ..addOption("homeserver", abbr: "u", mandatory: true) ..addOption("jwtSecretFile", abbr: "j", mandatory: true) ..addOption("authorizeEndpoint", abbr: "e", mandatory: true); final container = ProviderContainer(); - container .read(SettingsController.provider.notifier) .set(parser.parse(argsRaw)); diff --git a/flake.nix b/flake.nix index ae04dfa..bcbb13c 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,7 @@ flake-parts.lib.mkFlake {inherit inputs;} { systems = ["x86_64-linux" "aarch64-linux"]; perSystem = { + lib, pkgs, system, ... @@ -22,6 +23,23 @@ devShells.default = pkgs.mkShell { buildInputs = with pkgs; [just dart oauth2c watchexec]; }; + + packages.default = pkgs.buildDartApplication { + pname = "matrixoidc"; + version = "1.0.0"; + src = ./.; + + dartConfigHook = "packageRun build_runner build"; + autoPubspecLock = ./pubspec.lock; + + meta = { + homepage = "https://git.federated.nexus/Henry-Hiles/matrixoidc"; + description = "An attempt to make an OIDC provider that authenticates with matrix OAuth."; + mainProgram = "matrixoidc"; + license = lib.licenses.gpl3Plus; + maintainers = [lib.maintainers.quadradical]; + }; + }; }; }; } diff --git a/lib/models/matrix_user.freezed.dart b/lib/models/matrix_user.freezed.dart index 94504e8..0dd35c7 100644 --- a/lib/models/matrix_user.freezed.dart +++ b/lib/models/matrix_user.freezed.dart @@ -4,7 +4,7 @@ // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark -part of "matrix_user.dart"; +part of 'matrix_user.dart'; // ************************************************************************** // FreezedGenerator @@ -20,7 +20,7 @@ mixin _$MatrixUser { /// Create a copy of MatrixUser /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) -@pragma("vm:prefer-inline") +@pragma('vm:prefer-inline') $MatrixUserCopyWith get copyWith => _$MatrixUserCopyWithImpl(this as MatrixUser, _$identity); /// Serializes this MatrixUser to a JSON map. @@ -38,7 +38,7 @@ int get hashCode => Object.hash(runtimeType,userId,matrixToken); @override String toString() { - return "MatrixUser(userId: $userId, matrixToken: $matrixToken)"; + return 'MatrixUser(userId: $userId, matrixToken: $matrixToken)'; } @@ -66,7 +66,7 @@ class _$MatrixUserCopyWithImpl<$Res> /// Create a copy of MatrixUser /// with the given fields replaced by the non-null parameter values. -@pragma("vm:prefer-inline") @override $Res call({Object? userId = null,Object? matrixToken = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? userId = null,Object? matrixToken = null,}) { return _then(_self.copyWith( userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable as String,matrixToken: null == matrixToken ? _self.matrixToken : matrixToken // ignore: cast_nullable_to_non_nullable @@ -90,7 +90,7 @@ class _MatrixUser implements MatrixUser { /// Create a copy of MatrixUser /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) -@pragma("vm:prefer-inline") +@pragma('vm:prefer-inline') _$MatrixUserCopyWith<_MatrixUser> get copyWith => __$MatrixUserCopyWithImpl<_MatrixUser>(this, _$identity); @override @@ -109,7 +109,7 @@ int get hashCode => Object.hash(runtimeType,userId,matrixToken); @override String toString() { - return "MatrixUser(userId: $userId, matrixToken: $matrixToken)"; + return 'MatrixUser(userId: $userId, matrixToken: $matrixToken)'; } @@ -137,7 +137,7 @@ class __$MatrixUserCopyWithImpl<$Res> /// Create a copy of MatrixUser /// with the given fields replaced by the non-null parameter values. -@override @pragma("vm:prefer-inline") $Res call({Object? userId = null,Object? matrixToken = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? userId = null,Object? matrixToken = null,}) { return _then(_MatrixUser( userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable as String,matrixToken: null == matrixToken ? _self.matrixToken : matrixToken // ignore: cast_nullable_to_non_nullable diff --git a/lib/models/matrix_user.g.dart b/lib/models/matrix_user.g.dart index 95e855f..e3aa600 100644 --- a/lib/models/matrix_user.g.dart +++ b/lib/models/matrix_user.g.dart @@ -1,18 +1,18 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of "matrix_user.dart"; +part of 'matrix_user.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** _MatrixUser _$MatrixUserFromJson(Map json) => _MatrixUser( - userId: json["userId"] as String, - matrixToken: json["matrixToken"] as String, + userId: json['userId'] as String, + matrixToken: json['matrixToken'] as String, ); Map _$MatrixUserToJson(_MatrixUser instance) => { - "userId": instance.userId, - "matrixToken": instance.matrixToken, + 'userId': instance.userId, + 'matrixToken': instance.matrixToken, }; diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 6d7a895..dc6cf54 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -7,11 +7,11 @@ part "settings.g.dart"; abstract class Settings with _$Settings { const factory Settings({ required String? socket, + required String? serviceDomain, required String address, required String port, required String homeserver, required String issuer, - required String serviceDomain, required String jwtSecretFile, required String authorizeEndpoint, }) = _Settings; diff --git a/lib/models/settings.freezed.dart b/lib/models/settings.freezed.dart index 8376cab..3af7771 100644 --- a/lib/models/settings.freezed.dart +++ b/lib/models/settings.freezed.dart @@ -4,7 +4,7 @@ // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark -part of "settings.dart"; +part of 'settings.dart'; // ************************************************************************** // FreezedGenerator @@ -16,11 +16,11 @@ T _$identity(T value) => value; /// @nodoc mixin _$Settings { - String? get socket; String get address; String get port; String get homeserver; String get issuer; String get serviceDomain; String get jwtSecretFile; String get authorizeEndpoint; + String? get socket; String? get serviceDomain; String get address; String get port; String get homeserver; String get issuer; String get jwtSecretFile; String get authorizeEndpoint; /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) -@pragma("vm:prefer-inline") +@pragma('vm:prefer-inline') $SettingsCopyWith get copyWith => _$SettingsCopyWithImpl(this as Settings, _$identity); /// Serializes this Settings to a JSON map. @@ -29,16 +29,16 @@ $SettingsCopyWith get copyWith => _$SettingsCopyWithImpl(thi @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.address, address) || other.address == address)&&(identical(other.port, port) || other.port == port)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.issuer, issuer) || other.issuer == issuer)&&(identical(other.serviceDomain, serviceDomain) || other.serviceDomain == serviceDomain)&&(identical(other.jwtSecretFile, jwtSecretFile) || other.jwtSecretFile == jwtSecretFile)&&(identical(other.authorizeEndpoint, authorizeEndpoint) || other.authorizeEndpoint == authorizeEndpoint)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.serviceDomain, serviceDomain) || other.serviceDomain == serviceDomain)&&(identical(other.address, address) || other.address == address)&&(identical(other.port, port) || other.port == port)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.issuer, issuer) || other.issuer == issuer)&&(identical(other.jwtSecretFile, jwtSecretFile) || other.jwtSecretFile == jwtSecretFile)&&(identical(other.authorizeEndpoint, authorizeEndpoint) || other.authorizeEndpoint == authorizeEndpoint)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,socket,address,port,homeserver,issuer,serviceDomain,jwtSecretFile,authorizeEndpoint); +int get hashCode => Object.hash(runtimeType,socket,serviceDomain,address,port,homeserver,issuer,jwtSecretFile,authorizeEndpoint); @override String toString() { - return "Settings(socket: $socket, address: $address, port: $port, homeserver: $homeserver, issuer: $issuer, serviceDomain: $serviceDomain, jwtSecretFile: $jwtSecretFile, authorizeEndpoint: $authorizeEndpoint)"; + return 'Settings(socket: $socket, serviceDomain: $serviceDomain, address: $address, port: $port, homeserver: $homeserver, issuer: $issuer, jwtSecretFile: $jwtSecretFile, authorizeEndpoint: $authorizeEndpoint)'; } @@ -49,7 +49,7 @@ abstract mixin class $SettingsCopyWith<$Res> { factory $SettingsCopyWith(Settings value, $Res Function(Settings) _then) = _$SettingsCopyWithImpl; @useResult $Res call({ - String? socket, String address, String port, String homeserver, String issuer, String serviceDomain, String jwtSecretFile, String authorizeEndpoint + String? socket, String? serviceDomain, String address, String port, String homeserver, String issuer, String jwtSecretFile, String authorizeEndpoint }); @@ -66,14 +66,14 @@ class _$SettingsCopyWithImpl<$Res> /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. -@pragma("vm:prefer-inline") @override $Res call({Object? socket = freezed,Object? address = null,Object? port = null,Object? homeserver = null,Object? issuer = null,Object? serviceDomain = null,Object? jwtSecretFile = null,Object? authorizeEndpoint = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? socket = freezed,Object? serviceDomain = freezed,Object? address = null,Object? port = null,Object? homeserver = null,Object? issuer = null,Object? jwtSecretFile = null,Object? authorizeEndpoint = null,}) { return _then(_self.copyWith( socket: freezed == socket ? _self.socket : socket // ignore: cast_nullable_to_non_nullable +as String?,serviceDomain: freezed == serviceDomain ? _self.serviceDomain : serviceDomain // ignore: cast_nullable_to_non_nullable as String?,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as String,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable as String,homeserver: null == homeserver ? _self.homeserver : homeserver // ignore: cast_nullable_to_non_nullable as String,issuer: null == issuer ? _self.issuer : issuer // ignore: cast_nullable_to_non_nullable -as String,serviceDomain: null == serviceDomain ? _self.serviceDomain : serviceDomain // ignore: cast_nullable_to_non_nullable as String,jwtSecretFile: null == jwtSecretFile ? _self.jwtSecretFile : jwtSecretFile // ignore: cast_nullable_to_non_nullable as String,authorizeEndpoint: null == authorizeEndpoint ? _self.authorizeEndpoint : authorizeEndpoint // ignore: cast_nullable_to_non_nullable as String, @@ -87,22 +87,22 @@ as String, @JsonSerializable() class _Settings implements Settings { - const _Settings({required this.socket, required this.address, required this.port, required this.homeserver, required this.issuer, required this.serviceDomain, required this.jwtSecretFile, required this.authorizeEndpoint}); + const _Settings({required this.socket, required this.serviceDomain, required this.address, required this.port, required this.homeserver, required this.issuer, required this.jwtSecretFile, required this.authorizeEndpoint}); factory _Settings.fromJson(Map json) => _$SettingsFromJson(json); @override final String? socket; +@override final String? serviceDomain; @override final String address; @override final String port; @override final String homeserver; @override final String issuer; -@override final String serviceDomain; @override final String jwtSecretFile; @override final String authorizeEndpoint; /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) -@pragma("vm:prefer-inline") +@pragma('vm:prefer-inline') _$SettingsCopyWith<_Settings> get copyWith => __$SettingsCopyWithImpl<_Settings>(this, _$identity); @override @@ -112,16 +112,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.address, address) || other.address == address)&&(identical(other.port, port) || other.port == port)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.issuer, issuer) || other.issuer == issuer)&&(identical(other.serviceDomain, serviceDomain) || other.serviceDomain == serviceDomain)&&(identical(other.jwtSecretFile, jwtSecretFile) || other.jwtSecretFile == jwtSecretFile)&&(identical(other.authorizeEndpoint, authorizeEndpoint) || other.authorizeEndpoint == authorizeEndpoint)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Settings&&(identical(other.socket, socket) || other.socket == socket)&&(identical(other.serviceDomain, serviceDomain) || other.serviceDomain == serviceDomain)&&(identical(other.address, address) || other.address == address)&&(identical(other.port, port) || other.port == port)&&(identical(other.homeserver, homeserver) || other.homeserver == homeserver)&&(identical(other.issuer, issuer) || other.issuer == issuer)&&(identical(other.jwtSecretFile, jwtSecretFile) || other.jwtSecretFile == jwtSecretFile)&&(identical(other.authorizeEndpoint, authorizeEndpoint) || other.authorizeEndpoint == authorizeEndpoint)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,socket,address,port,homeserver,issuer,serviceDomain,jwtSecretFile,authorizeEndpoint); +int get hashCode => Object.hash(runtimeType,socket,serviceDomain,address,port,homeserver,issuer,jwtSecretFile,authorizeEndpoint); @override String toString() { - return "Settings(socket: $socket, address: $address, port: $port, homeserver: $homeserver, issuer: $issuer, serviceDomain: $serviceDomain, jwtSecretFile: $jwtSecretFile, authorizeEndpoint: $authorizeEndpoint)"; + return 'Settings(socket: $socket, serviceDomain: $serviceDomain, address: $address, port: $port, homeserver: $homeserver, issuer: $issuer, jwtSecretFile: $jwtSecretFile, authorizeEndpoint: $authorizeEndpoint)'; } @@ -132,7 +132,7 @@ abstract mixin class _$SettingsCopyWith<$Res> implements $SettingsCopyWith<$Res> factory _$SettingsCopyWith(_Settings value, $Res Function(_Settings) _then) = __$SettingsCopyWithImpl; @override @useResult $Res call({ - String? socket, String address, String port, String homeserver, String issuer, String serviceDomain, String jwtSecretFile, String authorizeEndpoint + String? socket, String? serviceDomain, String address, String port, String homeserver, String issuer, String jwtSecretFile, String authorizeEndpoint }); @@ -149,14 +149,14 @@ class __$SettingsCopyWithImpl<$Res> /// Create a copy of Settings /// with the given fields replaced by the non-null parameter values. -@override @pragma("vm:prefer-inline") $Res call({Object? socket = freezed,Object? address = null,Object? port = null,Object? homeserver = null,Object? issuer = null,Object? serviceDomain = null,Object? jwtSecretFile = null,Object? authorizeEndpoint = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? socket = freezed,Object? serviceDomain = freezed,Object? address = null,Object? port = null,Object? homeserver = null,Object? issuer = null,Object? jwtSecretFile = null,Object? authorizeEndpoint = null,}) { return _then(_Settings( socket: freezed == socket ? _self.socket : socket // ignore: cast_nullable_to_non_nullable +as String?,serviceDomain: freezed == serviceDomain ? _self.serviceDomain : serviceDomain // ignore: cast_nullable_to_non_nullable as String?,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as String,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable as String,homeserver: null == homeserver ? _self.homeserver : homeserver // ignore: cast_nullable_to_non_nullable as String,issuer: null == issuer ? _self.issuer : issuer // ignore: cast_nullable_to_non_nullable -as String,serviceDomain: null == serviceDomain ? _self.serviceDomain : serviceDomain // ignore: cast_nullable_to_non_nullable as String,jwtSecretFile: null == jwtSecretFile ? _self.jwtSecretFile : jwtSecretFile // ignore: cast_nullable_to_non_nullable as String,authorizeEndpoint: null == authorizeEndpoint ? _self.authorizeEndpoint : authorizeEndpoint // ignore: cast_nullable_to_non_nullable as String, diff --git a/lib/models/settings.g.dart b/lib/models/settings.g.dart index 11a29e1..e9172f1 100644 --- a/lib/models/settings.g.dart +++ b/lib/models/settings.g.dart @@ -1,29 +1,29 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of "settings.dart"; +part of 'settings.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** _Settings _$SettingsFromJson(Map json) => _Settings( - socket: json["socket"] as String?, - address: json["address"] as String, - port: json["port"] as String, - homeserver: json["homeserver"] as String, - issuer: json["issuer"] as String, - serviceDomain: json["serviceDomain"] as String, - jwtSecretFile: json["jwtSecretFile"] as String, - authorizeEndpoint: json["authorizeEndpoint"] as String, + socket: json['socket'] as String?, + serviceDomain: json['serviceDomain'] as String?, + address: json['address'] as String, + port: json['port'] as String, + homeserver: json['homeserver'] as String, + issuer: json['issuer'] as String, + jwtSecretFile: json['jwtSecretFile'] as String, + authorizeEndpoint: json['authorizeEndpoint'] as String, ); Map _$SettingsToJson(_Settings instance) => { - "socket": instance.socket, - "address": instance.address, - "port": instance.port, - "homeserver": instance.homeserver, - "issuer": instance.issuer, - "serviceDomain": instance.serviceDomain, - "jwtSecretFile": instance.jwtSecretFile, - "authorizeEndpoint": instance.authorizeEndpoint, + 'socket': instance.socket, + 'serviceDomain': instance.serviceDomain, + 'address': instance.address, + 'port': instance.port, + 'homeserver': instance.homeserver, + 'issuer': instance.issuer, + 'jwtSecretFile': instance.jwtSecretFile, + 'authorizeEndpoint': instance.authorizeEndpoint, }; diff --git a/pubspec.lock b/pubspec.lock index 84ee519..52f7e2d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -26,7 +26,7 @@ packages: source: hosted version: "7.4.5" args: - dependency: "direct main" + dependency: transitive description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 @@ -121,6 +121,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.4" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + cli_tools: + dependency: "direct main" + description: + name: cli_tools + sha256: ed6d1cef6e34ff3d68e7c798c3d18f497a311ac8082533340a4a914b12344717 + url: "https://pub.dev" + source: hosted + version: "0.6.0" clock: dependency: transitive description: @@ -169,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + dart_mappable: + dependency: transitive + description: + name: dart_mappable + sha256: "2255b2c00e328a65fef5a8df2dabfc0dc9c2e518c33a50051a4519b1c7a28c48" + url: "https://pub.dev" + source: hosted + version: "4.5.0" dart_style: dependency: transitive description: @@ -353,6 +377,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + oauth2: + dependency: transitive + description: + name: oauth2 + sha256: c84470642cbb2bec450ccab2f8520c079cd1ca546a76ffd5c40589e07f4e8bf4 + url: "https://pub.dev" + source: hosted + version: "2.0.3" package_config: dependency: transitive description: @@ -385,6 +417,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + pub_api_client: + dependency: transitive + description: + name: pub_api_client + sha256: b9c0184ce4a562d8cf2ebd7be235a25aa158b7c01bdef863cccadc5894644e26 + url: "https://pub.dev" + source: hosted + version: "3.1.1" pub_semver: dependency: transitive description: @@ -401,6 +441,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + rfc_6901: + dependency: transitive + description: + name: rfc_6901 + sha256: df1bbfa3d023009598f19636d6114c6ac1e0b7bb7bf6a260f0e6e6ce91416820 + url: "https://pub.dev" + source: hosted + version: "0.2.0" riverpod: dependency: "direct main" description: @@ -497,6 +545,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + super_string: + dependency: transitive + description: + name: super_string + sha256: ba41acf9fbb318b3fc0d57c9235779100394d85d83f45ab533615df1f3146ea7 + url: "https://pub.dev" + source: hosted + version: "1.0.3" term_glyph: dependency: transitive description: @@ -521,6 +577,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + type_plus: + dependency: transitive + description: + name: type_plus + sha256: d5d1019471f0d38b91603adb9b5fd4ce7ab903c879d2fbf1a3f80a630a03fcc9 + url: "https://pub.dev" + source: hosted + version: "2.1.1" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1daf022..98f5105 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,8 +14,8 @@ dependencies: freezed_annotation: ^3.0.0 json_annotation: ^4.9.0 shelf_router: ^1.1.4 - args: ^2.7.0 fast_immutable_collections: ^11.0.4 + cli_tools: ^0.6.0 dev_dependencies: build_runner: ^2.4.6