Compare commits

..

7 Commits

Author SHA1 Message Date
Sergey Penkovsky
9baf6f8d33 chore(release): publish packages
- cherrypick@3.0.0-dev.1
 - cherrypick_flutter@1.1.3-dev.1
2025-07-30 13:17:19 +03:00
Sergey Penkovsky
4e97a39501 docs: add quick guide for circular dependency detection to README 2025-07-30 13:16:23 +03:00
Sergey Penkovsky
58daf668c5 chore(release): publish packages
- cherrypick@3.0.0-dev.0
 - cherrypick_flutter@1.1.3-dev.0
2025-07-30 13:06:09 +03:00
Sergey Penkovsky
b57ca797e1 Merge pull request #12 from pese-git/cycle-detector
feat: implement comprehensive circular dependency detection system
2025-07-30 12:37:40 +03:00
Sergey Penkovsky
38fd356ec3 Remove dead code: _createDependencyKey (no longer used, cycle detection not affected) 2025-07-30 08:17:49 +03:00
Sergey Penkovsky
8fd18df811 feat: enable CherryPick cycle detection in debug mode and use safe root scope 2025-07-29 17:16:22 +03:00
Sergey Penkovsky
06c0dd60c0 feat: implement comprehensive circular dependency detection system
- Add two-level circular dependency detection (local and global)
- Implement CycleDetector for local scope cycle detection
- Implement GlobalCycleDetector for cross-scope cycle detection
- Add CircularDependencyException with detailed dependency chain info
- Integrate cycle detection into Scope class with unique scope IDs
- Extend CherryPick helper with cycle detection management API
- Add safe scope creation methods with automatic detection
- Support both synchronous and asynchronous dependency resolution
- Include comprehensive test coverage (72+ tests)
- Add bilingual documentation (English and Russian)
- Provide usage examples and architectural best practices
- Add performance recommendations and debug tools

BREAKING CHANGE: Scope constructor now generates unique IDs for global detection

fix: remove tmp files

update examples

update examples
2025-07-29 11:20:13 +03:00
27 changed files with 228 additions and 761 deletions

View File

@@ -3,37 +3,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2025-08-04
### Changes
---
Packages with breaking changes:
- [`cherrypick` - `v3.0.0-dev.2`](#cherrypick---v300-dev2)
Packages with other changes:
- [`cherrypick_flutter` - `v1.1.3-dev.2`](#cherrypick_flutter---v113-dev2)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `cherrypick_flutter` - `v1.1.3-dev.2`
---
#### `cherrypick` - `v3.0.0-dev.2`
- **FEAT**(binding): add deprecated proxy async methods for backward compatibility and highlight transition to modern API.
- **DOCS**: add quick guide for circular dependency detection to README.
- **DOCS**: add quick guide for circular dependency detection to README.
- **BREAKING** **FEAT**: implement comprehensive circular dependency detection system.
- **BREAKING** **FEAT**: implement comprehensive circular dependency detection system.
## 2025-07-30 ## 2025-07-30
### Changes ### Changes

View File

@@ -1,42 +0,0 @@
# benchmark_cherrypick
Benchmarks for performance and features of the cherrypick (core) DI container.
All scenarios use the public API capabilities of cherrypick (scope, module, binding, scoping, and async support).
## Scenarios
- **RegisterAndResolve**: basic registration and resolution of a dependency.
- **ChainSingleton (A->B->C, singleton)**: dependency chain, all as singletons.
- **ChainFactory (A->B->C, factory)**: dependency chain with factory bindings (new instance on each request).
- **NamedResolve (by name)**: resolving a named dependency among multiple implementations.
- **AsyncChain (A->B->C, async)**: asynchronous dependency chain.
- **ScopeOverride (child overrides parent)**: overriding a dependency in a child scope over a parent.
## Benchmark results
| Scenario | RunTime (μs) |
|----------------------------------------------------|---------------|
| RegisterAndResolve | 0.4574 |
| ChainSingleton (A->B->C, singleton) | 0.3759 |
| ChainFactory (A->B->C, factory) | 1.3783 |
| NamedResolve (by name) | 0.5193 |
| AsyncChain (A->B->C, async) | 0.5985 |
| ScopeOverride (child overrides parent) | 0.3611 |
## How to run
1. Get dependencies:
```shell
dart pub get
```
2. Run the benchmarks:
```shell
dart run bin/main.dart
```
A text report with all metrics will be displayed in the console.
---
To add your custom scenario — just create a new Dart file and declare a class extending BenchmarkBase or AsyncBenchmarkBase, then add its invocation to main.dart.

View File

@@ -1,42 +0,0 @@
# benchmark_cherrypick
Бенчмарки производительности и функциональности DI-контейнера cherrypick (core).
Все сценарии используют реальные возможности public API cherrypick (scope, module, binding, scoping и асинхронность).
## Сценарии
- **RegisterAndResolve**: базовая операция регистрации и разрешения зависимости.
- **ChainSingleton (A->B->C, singleton)**: цепочка зависимостей, все singletons.
- **ChainFactory (A->B->C, factory)**: цепочка зависимостей с factory биндингами, новые объекты на каждый запрос.
- **NamedResolve (by name)**: разрешение именованной зависимости среди нескольких реализаций.
- **AsyncChain (A->B->C, async)**: асинхронная цепочка зависимостей.
- **ScopeOverride (child overrides parent)**: переопределение зависимости в дочернем scope над родительским.
## Результаты исследования
| Сценарий | RunTime (мкс) |
|----------------------------------------------------|--------------|
| RegisterAndResolve | 0.4574 |
| ChainSingleton (A->B->C, singleton) | 0.3759 |
| ChainFactory (A->B->C, factory) | 1.3783 |
| NamedResolve (by name) | 0.5193 |
| AsyncChain (A->B->C, async) | 0.5985 |
| ScopeOverride (child overrides parent) | 0.3611 |
## Как запускать
1. Получить зависимости:
```shell
dart pub get
```
2. Запустить бенчмарк:
```shell
dart run bin/main.dart
```
Будет показан текстовый отчёт по всем метрикам.
---
Если хотите добавить свой сценарий — создайте отдельный Dart-файл и объявите новый BenchmarkBase/AsyncBenchmarkBase, не забудьте вставить его вызов в main.

View File

@@ -1,33 +0,0 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
analyzer:
errors:
deprecated_member_use: ignore
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@@ -1,17 +0,0 @@
import 'package:benchmark_cherrypick/cherrypick_benchmark.dart';
import 'package:benchmark_cherrypick/complex_bindings_benchmark.dart';
import 'package:benchmark_cherrypick/async_chain_benchmark.dart';
import 'package:benchmark_cherrypick/scope_override_benchmark.dart';
void main(List<String> args) async {
// Синхронные бенчмарки
RegisterAndResolveBenchmark().report();
ChainSingletonBenchmark().report();
ChainFactoryBenchmark().report();
NamedResolveBenchmark().report();
// Асинхронный бенчмарк
await AsyncChainBenchmark().report();
ScopeOverrideBenchmark().report();
}

View File

@@ -1,43 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
class AsyncA {}
class AsyncB {
final AsyncA a;
AsyncB(this.a);
}
class AsyncC {
final AsyncB b;
AsyncC(this.b);
}
class AsyncChainModule extends Module {
@override
void builder(Scope currentScope) {
bind<AsyncA>().toProvideAsync(() async => AsyncA()).singleton();
bind<AsyncB>().toProvideAsync(() async => AsyncB(await currentScope.resolveAsync<AsyncA>())).singleton();
bind<AsyncC>().toProvideAsync(() async => AsyncC(await currentScope.resolveAsync<AsyncB>())).singleton();
}
}
class AsyncChainBenchmark extends AsyncBenchmarkBase {
AsyncChainBenchmark() : super('AsyncChain (A->B->C, async)');
late Scope scope;
@override
Future<void> setup() async {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
scope = CherryPick.openRootScope();
scope.installModules([AsyncChainModule()]);
}
@override
Future<void> teardown() async {
CherryPick.closeRootScope();
}
@override
Future<void> run() async {
await scope.resolveAsync<AsyncC>();
}
}

View File

@@ -1,40 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
class AppModule extends Module {
@override
void builder(Scope currentScope) {
bind<FooService>().toProvide(() => FooService());
}
}
// Dummy service for DI
class FooService {}
class RegisterAndResolveBenchmark extends BenchmarkBase {
RegisterAndResolveBenchmark() : super('RegisterAndResolve');
late final Scope scope;
@override
void setup() {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
scope = CherryPick.openRootScope();
scope.installModules([AppModule()]);
}
@override
void run() {
scope.resolve<FooService>();
}
@override
void teardown() => CherryPick.closeRootScope();
}
void main() {
RegisterAndResolveBenchmark().report();
}

View File

@@ -1,95 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
// === DI graph: A -> B -> C (singleton) ===
class ServiceA {}
class ServiceB {
final ServiceA a;
ServiceB(this.a);
}
class ServiceC {
final ServiceB b;
ServiceC(this.b);
}
class ChainSingletonModule extends Module {
@override
void builder(Scope currentScope) {
bind<ServiceA>().toProvide(() => ServiceA()).singleton();
bind<ServiceB>().toProvide((() => ServiceB(currentScope.resolve<ServiceA>()))).singleton();
bind<ServiceC>().toProvide((() => ServiceC(currentScope.resolve<ServiceB>()))).singleton();
}
}
class ChainSingletonBenchmark extends BenchmarkBase {
ChainSingletonBenchmark() : super('ChainSingleton (A->B->C, singleton)');
late Scope scope;
@override
void setup() {
scope = CherryPick.openRootScope();
scope.installModules([ChainSingletonModule()]);
}
@override
void teardown() => CherryPick.closeRootScope();
@override
void run() {
scope.resolve<ServiceC>();
}
}
// === DI graph: A -> B -> C (factory/no singleton) ===
class ChainFactoryModule extends Module {
@override
void builder(Scope currentScope) {
bind<ServiceA>().toProvide(() => ServiceA());
bind<ServiceB>().toProvide((() => ServiceB(currentScope.resolve<ServiceA>())));
bind<ServiceC>().toProvide((() => ServiceC(currentScope.resolve<ServiceB>())));
}
}
class ChainFactoryBenchmark extends BenchmarkBase {
ChainFactoryBenchmark() : super('ChainFactory (A->B->C, factory)');
late Scope scope;
@override
void setup() {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
scope = CherryPick.openRootScope();
scope.installModules([ChainFactoryModule()]);
}
@override
void teardown() => CherryPick.closeRootScope();
@override
void run() {
scope.resolve<ServiceC>();
}
}
// === Named bindings: Multiple implementations ===
class Impl1 {}
class Impl2 {}
class NamedModule extends Module {
@override
void builder(Scope currentScope) {
bind<Object>().toProvide(() => Impl1()).withName('impl1');
bind<Object>().toProvide(() => Impl2()).withName('impl2');
}
}
class NamedResolveBenchmark extends BenchmarkBase {
NamedResolveBenchmark() : super('NamedResolve (by name)');
late Scope scope;
@override
void setup() {
scope = CherryPick.openRootScope();
scope.installModules([NamedModule()]);
}
@override
void teardown() => CherryPick.closeRootScope();
@override
void run() {
// Switch name for comparison
scope.resolve<Object>(named: 'impl2');
}
}

View File

@@ -1,46 +0,0 @@
// ignore: depend_on_referenced_packages
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:cherrypick/cherrypick.dart';
class Shared {}
class ParentImpl extends Shared {}
class ChildImpl extends Shared {}
class ParentModule extends Module {
@override
void builder(Scope currentScope) {
bind<Shared>().toProvide(() => ParentImpl()).singleton();
}
}
class ChildOverrideModule extends Module {
@override
void builder(Scope currentScope) {
bind<Shared>().toProvide(() => ChildImpl()).singleton();
}
}
class ScopeOverrideBenchmark extends BenchmarkBase {
ScopeOverrideBenchmark() : super('ScopeOverride (child overrides parent)');
late Scope parent;
late Scope child;
@override
void setup() {
CherryPick.disableGlobalCycleDetection();
CherryPick.disableGlobalCrossScopeCycleDetection();
parent = CherryPick.openRootScope();
parent.installModules([ParentModule()]);
child = parent.openSubScope('child');
child.installModules([ChildOverrideModule()]);
}
@override
void teardown() {
CherryPick.closeRootScope();
}
@override
void run() {
// Должен возвращать ChildImpl, а не ParentImpl
final resolved = child.resolve<Shared>();
assert(resolved is ChildImpl);
}
}

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@@ -1,68 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
ansi_modifier:
dependency: transitive
description:
name: ansi_modifier
sha256: "4b97c241f345e49c929bd56d0198b567b7dfcca7ec8d4f798745c9ced998684c"
url: "https://pub.dev"
source: hosted
version: "0.1.4"
benchmark_harness:
dependency: "direct dev"
description:
name: benchmark_harness
sha256: "83f65107165883ba8623eb822daacb23dcf9f795c66841de758c9dd7c5a0cf28"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
benchmark_runner:
dependency: "direct dev"
description:
name: benchmark_runner
sha256: "7de181228eb74cb34507ded2260fe88b3b71e0aacfe0dfa794df49edaf041ca3"
url: "https://pub.dev"
source: hosted
version: "0.0.4"
cherrypick:
dependency: "direct main"
description:
path: "../cherrypick"
relative: true
source: path
version: "3.0.0-dev.1"
exception_templates:
dependency: transitive
description:
name: exception_templates
sha256: "517f7c770da690073663f867ee2057ae2f4ffb28edae9da9faa624aa29ac76eb"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
lazy_memo:
dependency: transitive
description:
name: lazy_memo
sha256: dcb30b4184a6d767e1d779d74ce784d752d38313b8fb4bad6b659ae7af4bb34d
url: "https://pub.dev"
source: hosted
version: "0.2.3"
lints:
dependency: "direct dev"
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "5.1.1"
meta:
dependency: transitive
description:
name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.17.0"
sdks:
dart: ">=3.6.0 <4.0.0"

View File

@@ -1,16 +0,0 @@
name: benchmark_cherrypick
version: 0.1.0
publish_to: none
description: Benchmark for cherrypick core DI library
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
cherrypick:
path: ../cherrypick
dev_dependencies:
lints: ^5.0.0
benchmark_harness: ^2.2.2
benchmark_runner: ^0.0.2

View File

@@ -1,13 +1,3 @@
## 3.0.0-dev.2
> Note: This release has breaking changes.
- **FEAT**(binding): add deprecated proxy async methods for backward compatibility and highlight transition to modern API.
- **DOCS**: add quick guide for circular dependency detection to README.
- **DOCS**: add quick guide for circular dependency detection to README.
- **BREAKING** **FEAT**: implement comprehensive circular dependency detection system.
- **BREAKING** **FEAT**: implement comprehensive circular dependency detection system.
## 3.0.0-dev.1 ## 3.0.0-dev.1
- **DOCS**: add quick guide for circular dependency detection to README. - **DOCS**: add quick guide for circular dependency detection to README.

View File

@@ -17,28 +17,19 @@ class FeatureModule extends Module {
@override @override
void builder(Scope currentScope) { void builder(Scope currentScope) {
// Using toProvideAsync for async initialization // Using toProvideAsync for async initialization
bind<DataRepository>() bind<DataRepository>().withName("networkRepo").toProvideAsync(() async {
.withName("networkRepo")
.toProvideWithParams((params) async {
print('REPO PARAMS: $params');
final client = await Future.delayed( final client = await Future.delayed(
Duration(milliseconds: 1000), Duration(milliseconds: 100),
() => currentScope.resolve<ApiClient>( () => currentScope.resolve<ApiClient>(
named: isMock ? "apiClientMock" : "apiClientImpl", named: isMock ? "apiClientMock" : "apiClientImpl"));
),
);
return NetworkDataRepository(client); return NetworkDataRepository(client);
}).singleton(); }).singleton();
// Asynchronous initialization of DataBloc // Asynchronous initialization of DataBloc
bind<DataBloc>().toProvide( bind<DataBloc>().toProvideAsync(
() async { () async {
final repo = await currentScope.resolveAsync<DataRepository>( final repo = await currentScope.resolveAsync<DataRepository>(
named: "networkRepo", named: "networkRepo");
params: 'Some params',
);
return DataBloc(repo); return DataBloc(repo);
}, },
); );
@@ -47,7 +38,9 @@ class FeatureModule extends Module {
Future<void> main() async { Future<void> main() async {
try { try {
final scope = openRootScope().installModules([AppModule()]); final scope = openRootScope().installModules([
AppModule(),
]);
final subScope = scope final subScope = scope
.openSubScope("featureScope") .openSubScope("featureScope")
@@ -55,11 +48,8 @@ Future<void> main() async {
// Asynchronous instance resolution // Asynchronous instance resolution
final dataBloc = await subScope.resolveAsync<DataBloc>(); final dataBloc = await subScope.resolveAsync<DataBloc>();
dataBloc.data.listen( dataBloc.data.listen((d) => print('Received data: $d'),
(d) => print('Received data: $d'), onError: (e) => print('Error: $e'), onDone: () => print('DONE'));
onError: (e) => print('Error: $e'),
onDone: () => print('DONE'),
);
await dataBloc.fetchData(); await dataBloc.fetchData();
} catch (e) { } catch (e) {

View File

@@ -13,7 +13,6 @@ library;
// limitations under the License. // limitations under the License.
// //
export 'package:cherrypick/src/binding_resolver.dart';
export 'package:cherrypick/src/binding.dart'; export 'package:cherrypick/src/binding.dart';
export 'package:cherrypick/src/cycle_detector.dart'; export 'package:cherrypick/src/cycle_detector.dart';
export 'package:cherrypick/src/global_cycle_detector.dart'; export 'package:cherrypick/src/global_cycle_detector.dart';

View File

@@ -11,21 +11,45 @@
// limitations under the License. // limitations under the License.
// //
import 'package:cherrypick/src/binding_resolver.dart'; enum Mode { simple, instance, providerInstance, providerInstanceWithParams }
typedef Provider<T> = T? Function();
typedef ProviderWithParams<T> = T Function(dynamic params);
typedef AsyncProvider<T> = Future<T> Function();
typedef AsyncProviderWithParams<T> = Future<T> Function(dynamic params);
/// RU: Класс Binding<T> настраивает параметры экземпляра. /// RU: Класс Binding<T> настраивает параметры экземпляра.
/// ENG: The Binding<T> class configures the settings for the instance. /// ENG: The Binding<T> class configures the settings for the instance.
/// ///
class Binding<T> { class Binding<T> {
late Mode _mode;
late Type _key; late Type _key;
String? _name; late String _name;
T? _instance;
Future<T>? _instanceAsync;
Provider<T>? _provider;
ProviderWithParams<T>? _providerWithParams;
BindingResolver<T>? _resolver; AsyncProvider<T>? asyncProvider;
AsyncProviderWithParams<T>? asyncProviderWithParams;
late bool _isSingleton = false;
late bool _isNamed = false;
Binding() { Binding() {
_mode = Mode.simple;
_key = T; _key = T;
} }
/// RU: Метод возвращает [Mode] экземпляра.
/// ENG: The method returns the [Mode] of the instance.
///
/// return [Mode]
Mode get mode => _mode;
/// RU: Метод возвращает тип экземпляра. /// RU: Метод возвращает тип экземпляра.
/// ENG: The method returns the type of the instance. /// ENG: The method returns the type of the instance.
/// ///
@@ -36,21 +60,19 @@ class Binding<T> {
/// ENG: The method returns the name of the instance. /// ENG: The method returns the name of the instance.
/// ///
/// return [String] /// return [String]
String? get name => _name; String get name => _name;
/// RU: Метод проверяет именован экземпляр или нет.
/// ENG: The method checks whether the instance is named or not.
///
/// return [bool]
bool get isNamed => _name != null;
/// RU: Метод проверяет сингелтон экземпляр или нет. /// RU: Метод проверяет сингелтон экземпляр или нет.
/// ENG: The method checks the singleton instance or not. /// ENG: The method checks the singleton instance or not.
/// ///
/// return [bool] /// return [bool]
bool get isSingleton => _resolver?.isSingleton ?? false; bool get isSingleton => _isSingleton;
BindingResolver<T>? get resolver => _resolver; /// RU: Метод проверяет именован экземпляр или нет.
/// ENG: The method checks whether the instance is named or not.
///
/// return [bool]
bool get isNamed => _isNamed;
/// RU: Добавляет имя для экземляпя [value]. /// RU: Добавляет имя для экземляпя [value].
/// ENG: Added name for instance [value]. /// ENG: Added name for instance [value].
@@ -58,6 +80,7 @@ class Binding<T> {
/// return [Binding] /// return [Binding]
Binding<T> withName(String name) { Binding<T> withName(String name) {
_name = name; _name = name;
_isNamed = true;
return this; return this;
} }
@@ -65,9 +88,21 @@ class Binding<T> {
/// ENG: Initialization instance [value]. /// ENG: Initialization instance [value].
/// ///
/// return [Binding] /// return [Binding]
Binding<T> toInstance(Instance<T> value) { Binding<T> toInstance(T value) {
_resolver = InstanceResolver<T>(value); _mode = Mode.instance;
_instance = value;
_isSingleton = true;
return this;
}
/// RU: Инициализация экземляпяра [value].
/// ENG: Initialization instance [value].
///
/// return [Binding]
Binding<T> toInstanceAsync(Future<T> value) {
_mode = Mode.instance;
_instanceAsync = value;
_isSingleton = true;
return this; return this;
} }
@@ -76,8 +111,18 @@ class Binding<T> {
/// ///
/// return [Binding] /// return [Binding]
Binding<T> toProvide(Provider<T> value) { Binding<T> toProvide(Provider<T> value) {
_resolver = ProviderResolver<T>((_) => value.call(), withParams: false); _mode = Mode.providerInstance;
_provider = value;
return this;
}
/// RU: Инициализация экземляпяра  через провайдер [value].
/// ENG: Initialization instance via provider [value].
///
/// return [Binding]
Binding<T> toProvideAsync(AsyncProvider<T> provider) {
_mode = Mode.providerInstance;
asyncProvider = provider;
return this; return this;
} }
@@ -86,24 +131,19 @@ class Binding<T> {
/// ///
/// return [Binding] /// return [Binding]
Binding<T> toProvideWithParams(ProviderWithParams<T> value) { Binding<T> toProvideWithParams(ProviderWithParams<T> value) {
_resolver = ProviderResolver<T>(value, withParams: true); _mode = Mode.providerInstanceWithParams;
_providerWithParams = value;
return this; return this;
} }
@Deprecated('Use toInstance instead of toInstanceAsync') /// RU: Инициализация экземляра через асинхронный провайдер [value] с динамическим параметром.
Binding<T> toInstanceAsync(Instance<T> value) { /// ENG: Initializes the instance via async provider [value] with a dynamic param.
return this.toInstance(value); ///
} /// return [Binding]
Binding<T> toProvideAsyncWithParams(AsyncProviderWithParams<T> provider) {
@Deprecated('Use toProvide instead of toProvideAsync') _mode = Mode.providerInstanceWithParams;
Binding<T> toProvideAsync(Provider<T> value) { asyncProviderWithParams = provider;
return this.toProvide(value); return this;
}
@Deprecated('Use toProvideWithParams instead of toProvideAsyncWithParams')
Binding<T> toProvideAsyncWithParams(ProviderWithParams<T> value) {
return this.toProvideWithParams(value);
} }
/// RU: Инициализация экземляпяра  как сингелтон [value]. /// RU: Инициализация экземляпяра  как сингелтон [value].
@@ -111,16 +151,40 @@ class Binding<T> {
/// ///
/// return [Binding] /// return [Binding]
Binding<T> singleton() { Binding<T> singleton() {
_resolver?.toSingleton(); _isSingleton = true;
return this; return this;
} }
T? resolveSync([dynamic params]) { /// RU: Поиск экземпляра.
return resolver?.resolveSync(params); /// ENG: Resolve instance.
///
/// return [T]
T? get instance => _instance;
/// RU: Поиск экземпляра.
/// ENG: Resolve instance.
///
/// return [T]
Future<T>? get instanceAsync => _instanceAsync;
/// RU: Поиск экземпляра.
/// ENG: Resolve instance.
///
/// return [T]
T? get provider {
if (_isSingleton) {
_instance ??= _provider?.call();
return _instance;
}
return _provider?.call();
} }
Future<T>? resolveAsync([dynamic params]) { /// RU: Поиск экземпляра с параметром.
return resolver?.resolveAsync(params); ///
/// ENG: Resolve instance with [params].
///
/// return [T]
T? providerWithParams(dynamic params) {
return _providerWithParams?.call(params);
} }
} }

View File

@@ -1,129 +0,0 @@
import 'dart:async';
typedef Instance<T> = FutureOr<T>;
/// RU: Синхронный или асинхронный провайдер без параметров, возвращающий [T] или [Future<T>].
/// ENG: Synchronous or asynchronous provider without parameters, returning [T] or [Future<T>].
typedef Provider<T> = FutureOr<T> Function();
/// RU: Провайдер с динамическим параметром, возвращающий [T] или [Future<T>] в зависимости от реализации.
/// ENG: Provider with dynamic parameter, returning [T] or [Future<T>] depending on implementation.
typedef ProviderWithParams<T> = FutureOr<T> Function(dynamic);
/// RU: Абстрактный интерфейс для классов, которые разрешают зависимости типа [T].
/// ENG: Abstract interface for classes that resolve dependencies of type [T].
abstract class BindingResolver<T> {
/// RU: Синхронное разрешение зависимости с параметром [params].
/// ENG: Synchronous resolution of the dependency with [params].
T? resolveSync([dynamic params]);
/// RU: Асинхронное разрешение зависимости с параметром [params].
/// ENG: Asynchronous resolution of the dependency with [params].
Future<T>? resolveAsync([dynamic params]);
/// RU: Помечает текущий резолвер как синглтон — результат будет закеширован.
/// ENG: Marks this resolver as singleton — result will be cached.
void toSingleton();
bool get isSingleton;
}
/// RU: Резолвер, оборачивающий конкретный экземпляр [T] (или Future<T>), без вызова провайдера.
/// ENG: Resolver that wraps a concrete instance of [T] (or Future<T>), without provider invocation.
class InstanceResolver<T> implements BindingResolver<T> {
final Instance<T> _instance;
/// RU: Создаёт резолвер, оборачивающий значение [instance].
/// ENG: Creates a resolver that wraps the given [instance].
InstanceResolver(this._instance);
@override
T resolveSync([_]) {
if (_instance is T) return _instance;
throw StateError(
'Instance $_instance is Future; '
'use resolveAsync() instead',
);
}
@override
Future<T> resolveAsync([_]) {
if (_instance is Future<T>) return _instance;
return Future.value(_instance);
}
@override
void toSingleton() {}
@override
bool get isSingleton => true;
}
/// RU: Резолвер, оборачивающий провайдер, с возможностью синглтон-кеширования.
/// ENG: Resolver that wraps a provider, with optional singleton caching.
class ProviderResolver<T> implements BindingResolver<T> {
final ProviderWithParams<T> _provider;
final bool _withParams;
FutureOr<T>? _cache;
bool _singleton = false;
/// RU: Создаёт резолвер из произвольной функции [raw], поддерживающей ноль или один параметр.
/// ENG: Creates a resolver from arbitrary function [raw], supporting zero or one parameter.
ProviderResolver(
ProviderWithParams<T> provider, {
required bool withParams,
}) : _provider = provider,
_withParams = withParams;
@override
T resolveSync([dynamic params]) {
_checkParams(params);
final result = _cache ?? _provider(params);
if (result is T) {
if (_singleton) {
_cache ??= result;
}
return result;
}
throw StateError(
'Provider [$_provider] return Future<$T>. Use resolveAsync() instead.',
);
}
@override
Future<T> resolveAsync([dynamic params]) {
_checkParams(params);
final result = _cache ?? _provider(params);
final target = result is Future<T> ? result : Future<T>.value(result);
if (_singleton) {
_cache ??= target;
}
return target;
}
@override
void toSingleton() {
_singleton = true;
}
@override
bool get isSingleton => _singleton;
/// RU: Проверяет, был ли передан параметр, если провайдер требует его.
/// ENG: Checks if parameter is passed when the provider expects it.
void _checkParams(dynamic params) {
if (_withParams && params == null) {
throw StateError(
'[$T] Params is null. Maybe you forgot to pass it?',
);
}
}
}

View File

@@ -13,9 +13,9 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:math'; import 'dart:math';
import 'package:cherrypick/src/binding.dart';
import 'package:cherrypick/src/cycle_detector.dart'; import 'package:cherrypick/src/cycle_detector.dart';
import 'package:cherrypick/src/global_cycle_detector.dart'; import 'package:cherrypick/src/global_cycle_detector.dart';
import 'package:cherrypick/src/binding_resolver.dart';
import 'package:cherrypick/src/module.dart'; import 'package:cherrypick/src/module.dart';
Scope openRootScope() => Scope(null); Scope openRootScope() => Scope(null);
@@ -176,12 +176,33 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
/// RU: Внутренний метод для разрешения зависимостей без проверки циклических зависимостей. /// RU: Внутренний метод для разрешения зависимостей без проверки циклических зависимостей.
/// ENG: Internal method for dependency resolution without circular dependency checking. /// ENG: Internal method for dependency resolution without circular dependency checking.
T? _tryResolveInternal<T>({String? named, dynamic params}) { T? _tryResolveInternal<T>({String? named, dynamic params}) {
final resolver = _findBindingResolver<T>(named);
// 1 Поиск зависимости по всем модулям текущего скоупа // 1 Поиск зависимости по всем модулям текущего скоупа
return resolver?.resolveSync(params) ?? if (_modulesList.isNotEmpty) {
for (var module in _modulesList) {
for (var binding in module.bindingSet) {
if (binding.key == T &&
((!binding.isNamed && named == null) ||
(binding.isNamed && named == binding.name))) {
switch (binding.mode) {
case Mode.instance:
return binding.instance;
case Mode.providerInstance:
return binding.provider;
case Mode.providerInstanceWithParams:
if (params == null) {
throw StateError('Param is null. Maybe you forget pass it');
}
return binding.providerWithParams(params);
default:
return null;
}
}
}
}
}
// 2 Поиск зависимостей в родительском скоупе // 2 Поиск зависимостей в родительском скоупе
_parentScope?.tryResolve(named: named, params: params); return _parentScope?._tryResolveInternal(named: named, params: params);
} }
/// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T]. /// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T].
@@ -245,25 +266,30 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
/// RU: Внутренний метод для асинхронного разрешения зависимостей без проверки циклических зависимостей. /// RU: Внутренний метод для асинхронного разрешения зависимостей без проверки циклических зависимостей.
/// ENG: Internal method for async dependency resolution without circular dependency checking. /// ENG: Internal method for async dependency resolution without circular dependency checking.
Future<T?> _tryResolveAsyncInternal<T>({String? named, dynamic params}) async { Future<T?> _tryResolveAsyncInternal<T>({String? named, dynamic params}) async {
final resolver = _findBindingResolver<T>(named); if (_modulesList.isNotEmpty) {
// 1 Поиск зависимости по всем модулям текущего скоупа
return resolver?.resolveAsync(params) ??
// 2 Поиск зависимостей в родительском скоупе
_parentScope?.tryResolveAsync(named: named, params: params);
}
BindingResolver<T>? _findBindingResolver<T>(String? named) {
for (var module in _modulesList) { for (var module in _modulesList) {
for (var binding in module.bindingSet) { for (var binding in module.bindingSet) {
if (binding.key == T && if (binding.key == T &&
((!binding.isNamed && named == null) || ((!binding.isNamed && named == null) ||
(binding.isNamed && named == binding.name))) { (binding.isNamed && named == binding.name))) {
return binding.resolver as BindingResolver<T>?; if (binding.instanceAsync != null) {
} return await binding.instanceAsync;
}
} }
return null; if (binding.asyncProvider != null) {
return await binding.asyncProvider?.call();
}
if (binding.asyncProviderWithParams != null) {
if (params == null) {
throw StateError('Param is null. Maybe you forget pass it');
}
return await binding.asyncProviderWithParams!(params);
}
}
}
}
}
return _parentScope?._tryResolveAsyncInternal(named: named, params: params);
} }
} }

View File

@@ -1,6 +1,6 @@
name: cherrypick name: cherrypick
description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects. description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects.
version: 3.0.0-dev.2 version: 3.0.0-dev.1
homepage: https://pese-git.github.io/cherrypick-site/ homepage: https://pese-git.github.io/cherrypick-site/
documentation: https://github.com/pese-git/cherrypick/wiki documentation: https://github.com/pese-git/cherrypick/wiki
repository: https://github.com/pese-git/cherrypick repository: https://github.com/pese-git/cherrypick

View File

@@ -1,4 +1,4 @@
import 'package:cherrypick/cherrypick.dart'; import 'package:cherrypick/src/binding.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
@@ -7,12 +7,12 @@ void main() {
group('Without name', () { group('Without name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>(); final binding = Binding<int>();
expect(binding.resolver, null); expect(binding.instance, null);
}); });
test('Sets mode to instance', () { test('Sets mode to instance', () {
final binding = Binding<int>().toInstance(5); final binding = Binding<int>().toInstance(5);
expect(binding.resolver, isA<InstanceResolver<int>>()); expect(binding.mode, Mode.instance);
}); });
test('isSingleton is true', () { test('isSingleton is true', () {
@@ -22,19 +22,19 @@ void main() {
test('Stores value', () { test('Stores value', () {
final binding = Binding<int>().toInstance(5); final binding = Binding<int>().toInstance(5);
expect(binding.resolver?.resolveSync(), 5); expect(binding.instance, 5);
}); });
}); });
group('With name', () { group('With name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>().withName('n'); final binding = Binding<int>().withName('n');
expect(binding.resolver, null); expect(binding.instance, null);
}); });
test('Sets mode to instance', () { test('Sets mode to instance', () {
final binding = Binding<int>().withName('n').toInstance(5); final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.resolver, isA<InstanceResolver<int>>()); expect(binding.mode, Mode.instance);
}); });
test('Sets key', () { test('Sets key', () {
@@ -49,7 +49,7 @@ void main() {
test('Stores value', () { test('Stores value', () {
final binding = Binding<int>().withName('n').toInstance(5); final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.resolver?.resolveSync(), 5); expect(binding.instance, 5);
}); });
test('Sets name', () { test('Sets name', () {
@@ -60,39 +60,45 @@ void main() {
test('Multiple toInstance calls change value', () { test('Multiple toInstance calls change value', () {
final binding = Binding<int>().toInstance(1).toInstance(2); final binding = Binding<int>().toInstance(1).toInstance(2);
expect(binding.resolver?.resolveSync(), 2); expect(binding.instance, 2);
}); });
}); });
// --- Instance binding (asynchronous) --- // --- Instance binding (asynchronous) ---
group('Async Instance Binding (toInstanceAsync)', () { group('Async Instance Binding (toInstanceAsync)', () {
test('Resolves instanceAsync with expected value', () async { test('Resolves instanceAsync with expected value', () async {
final binding = Binding<int>().toInstance(Future.value(42)); final binding = Binding<int>().toInstanceAsync(Future.value(42));
expect(await binding.resolveAsync(), 42); expect(await binding.instanceAsync, 42);
});
test('Does not affect instance', () {
final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.instance, null);
}); });
test('Sets mode to instance', () { test('Sets mode to instance', () {
final binding = Binding<int>().toInstance(Future.value(5)); final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.resolver, isA<InstanceResolver<int>>()); expect(binding.mode, Mode.instance);
}); });
test('isSingleton is true after toInstanceAsync', () { test('isSingleton is true after toInstanceAsync', () {
final binding = Binding<int>().toInstance(Future.value(5)); final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.isSingleton, isTrue); expect(binding.isSingleton, isTrue);
}); });
test('Composes with withName', () async { test('Composes with withName', () async {
final binding = final binding = Binding<int>()
Binding<int>().withName('asyncValue').toInstance(Future.value(7)); .withName('asyncValue')
.toInstanceAsync(Future.value(7));
expect(binding.isNamed, isTrue); expect(binding.isNamed, isTrue);
expect(binding.name, 'asyncValue'); expect(binding.name, 'asyncValue');
expect(await binding.resolveAsync(), 7); expect(await binding.instanceAsync, 7);
}); });
test('Keeps value after multiple awaits', () async { test('Keeps value after multiple awaits', () async {
final binding = Binding<int>().toInstance(Future.value(123)); final binding = Binding<int>().toInstanceAsync(Future.value(123));
final result1 = await binding.resolveAsync(); final result1 = await binding.instanceAsync;
final result2 = await binding.resolveAsync(); final result2 = await binding.instanceAsync;
expect(result1, equals(result2)); expect(result1, equals(result2));
}); });
}); });
@@ -102,12 +108,12 @@ void main() {
group('Without name', () { group('Without name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>(); final binding = Binding<int>();
expect(binding.resolver, null); expect(binding.provider, null);
}); });
test('Sets mode to providerInstance', () { test('Sets mode to providerInstance', () {
final binding = Binding<int>().toProvide(() => 5); final binding = Binding<int>().toProvide(() => 5);
expect(binding.resolver, isA<ProviderResolver<int>>()); expect(binding.mode, Mode.providerInstance);
}); });
test('isSingleton is false by default', () { test('isSingleton is false by default', () {
@@ -117,19 +123,19 @@ void main() {
test('Returns provided value', () { test('Returns provided value', () {
final binding = Binding<int>().toProvide(() => 5); final binding = Binding<int>().toProvide(() => 5);
expect(binding.resolveSync(), 5); expect(binding.provider, 5);
}); });
}); });
group('With name', () { group('With name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>().withName('n'); final binding = Binding<int>().withName('n');
expect(binding.resolver, null); expect(binding.provider, null);
}); });
test('Sets mode to providerInstance', () { test('Sets mode to providerInstance', () {
final binding = Binding<int>().withName('n').toProvide(() => 5); final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.resolver, isA<ProviderResolver<int>>()); expect(binding.mode, Mode.providerInstance);
}); });
test('Sets key', () { test('Sets key', () {
@@ -144,7 +150,7 @@ void main() {
test('Returns provided value', () { test('Returns provided value', () {
final binding = Binding<int>().withName('n').toProvide(() => 5); final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.resolveSync(), 5); expect(binding.provider, 5);
}); });
test('Sets name', () { test('Sets name', () {
@@ -157,14 +163,14 @@ void main() {
// --- Async provider binding --- // --- Async provider binding ---
group('Async Provider Binding', () { group('Async Provider Binding', () {
test('Resolves asyncProvider value', () async { test('Resolves asyncProvider value', () async {
final binding = Binding<int>().toProvide(() async => 5); final binding = Binding<int>().toProvideAsync(() async => 5);
expect(await binding.resolveAsync(), 5); expect(await binding.asyncProvider?.call(), 5);
}); });
test('Resolves asyncProviderWithParams value', () async { test('Resolves asyncProviderWithParams value', () async {
final binding = Binding<int>() final binding = Binding<int>()
.toProvideWithParams((param) async => 5 + (param as int)); .toProvideAsyncWithParams((param) async => 5 + (param as int));
expect(await binding.resolveAsync(3), 8); expect(await binding.asyncProviderWithParams?.call(3), 8);
}); });
}); });
@@ -173,7 +179,12 @@ void main() {
group('Without name', () { group('Without name', () {
test('Returns null if no provider set', () { test('Returns null if no provider set', () {
final binding = Binding<int>().singleton(); final binding = Binding<int>().singleton();
expect(binding.resolver, null); expect(binding.provider, null);
});
test('Sets mode to providerInstance', () {
final binding = Binding<int>().toProvide(() => 5).singleton();
expect(binding.mode, Mode.providerInstance);
}); });
test('isSingleton is true', () { test('isSingleton is true', () {
@@ -183,7 +194,7 @@ void main() {
test('Returns singleton value', () { test('Returns singleton value', () {
final binding = Binding<int>().toProvide(() => 5).singleton(); final binding = Binding<int>().toProvide(() => 5).singleton();
expect(binding.resolveSync(), 5); expect(binding.provider, 5);
}); });
test('Returns same value each call and provider only called once', () { test('Returns same value each call and provider only called once', () {
@@ -193,8 +204,8 @@ void main() {
return counter; return counter;
}).singleton(); }).singleton();
final first = binding.resolveSync(); final first = binding.provider;
final second = binding.resolveSync(); final second = binding.provider;
expect(first, equals(second)); expect(first, equals(second));
expect(counter, 1); expect(counter, 1);
}); });
@@ -203,7 +214,13 @@ void main() {
group('With name', () { group('With name', () {
test('Returns null if no provider set', () { test('Returns null if no provider set', () {
final binding = Binding<int>().withName('n').singleton(); final binding = Binding<int>().withName('n').singleton();
expect(binding.resolver, null); expect(binding.provider, null);
});
test('Sets mode to providerInstance', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.mode, Mode.providerInstance);
}); });
test('Sets key', () { test('Sets key', () {
@@ -221,7 +238,7 @@ void main() {
test('Returns singleton value', () { test('Returns singleton value', () {
final binding = final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton(); Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.resolveSync(), 5); expect(binding.provider, 5);
}); });
test('Sets name', () { test('Sets name', () {
@@ -230,6 +247,12 @@ void main() {
expect(binding.name, 'n'); expect(binding.name, 'n');
}); });
}); });
test('Chained withName and singleton preserves mode', () {
final binding =
Binding<int>().toProvide(() => 3).withName("named").singleton();
expect(binding.mode, Mode.providerInstance);
});
}); });
// --- WithName / Named binding, isNamed, edge-cases --- // --- WithName / Named binding, isNamed, edge-cases ---
@@ -242,7 +265,7 @@ void main() {
test('providerWithParams returns null if not set', () { test('providerWithParams returns null if not set', () {
final binding = Binding<int>(); final binding = Binding<int>();
expect(binding.resolveSync(123), null); expect(binding.providerWithParams(123), null);
}); });
}); });
} }

View File

@@ -200,13 +200,11 @@ class AsyncServiceB {
class AsyncCircularModule extends Module { class AsyncCircularModule extends Module {
@override @override
void builder(Scope currentScope) { void builder(Scope currentScope) {
// ignore: deprecated_member_use_from_same_package
bind<AsyncServiceA>().toProvideAsync(() async { bind<AsyncServiceA>().toProvideAsync(() async {
final serviceB = await currentScope.resolveAsync<AsyncServiceB>(); final serviceB = await currentScope.resolveAsync<AsyncServiceB>();
return AsyncServiceA(serviceB); return AsyncServiceA(serviceB);
}); });
// ignore: deprecated_member_use_from_same_package
bind<AsyncServiceB>().toProvideAsync(() async { bind<AsyncServiceB>().toProvideAsync(() async {
final serviceA = await currentScope.resolveAsync<AsyncServiceA>(); final serviceA = await currentScope.resolveAsync<AsyncServiceA>();
return AsyncServiceB(serviceA); return AsyncServiceB(serviceA);

View File

@@ -127,7 +127,7 @@ void main() {
final scope = Scope(null) final scope = Scope(null)
..installModules([ ..installModules([
_InlineModule((m, s) { _InlineModule((m, s) {
m.bind<String>().toInstance(Future.value('async value')); m.bind<String>().toInstanceAsync(Future.value('async value'));
}), }),
]); ]);
expect(await scope.resolveAsync<String>(), "async value"); expect(await scope.resolveAsync<String>(), "async value");
@@ -137,7 +137,7 @@ void main() {
final scope = Scope(null) final scope = Scope(null)
..installModules([ ..installModules([
_InlineModule((m, s) { _InlineModule((m, s) {
m.bind<int>().toProvide(() async => 7); m.bind<int>().toProvideAsync(() async => 7);
}), }),
]); ]);
expect(await scope.resolveAsync<int>(), 7); expect(await scope.resolveAsync<int>(), 7);
@@ -147,7 +147,7 @@ void main() {
final scope = Scope(null) final scope = Scope(null)
..installModules([ ..installModules([
_InlineModule((m, s) { _InlineModule((m, s) {
m.bind<int>().toProvideWithParams((x) async => (x as int) * 3); m.bind<int>().toProvideAsyncWithParams((x) async => (x as int) * 3);
}), }),
]); ]);
expect(await scope.resolveAsync<int>(params: 2), 6); expect(await scope.resolveAsync<int>(params: 2), 6);

View File

@@ -1,7 +1,3 @@
## 1.1.3-dev.2
- Update a dependency to the latest release.
## 1.1.3-dev.1 ## 1.1.3-dev.1
- Update a dependency to the latest release. - Update a dependency to the latest release.

View File

@@ -1,6 +1,6 @@
name: cherrypick_flutter name: cherrypick_flutter
description: "Flutter library that allows access to the root scope through the context using `CherryPickProvider`." description: "Flutter library that allows access to the root scope through the context using `CherryPickProvider`."
version: 1.1.3-dev.2 version: 1.1.3-dev.1
homepage: https://pese-git.github.io/cherrypick-site/ homepage: https://pese-git.github.io/cherrypick-site/
documentation: https://github.com/pese-git/cherrypick/wiki documentation: https://github.com/pese-git/cherrypick/wiki
repository: https://github.com/pese-git/cherrypick repository: https://github.com/pese-git/cherrypick
@@ -13,7 +13,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
cherrypick: ^3.0.0-dev.2 cherrypick: ^3.0.0-dev.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -127,7 +127,7 @@ packages:
path: "../../cherrypick" path: "../../cherrypick"
relative: true relative: true
source: path source: path
version: "3.0.0-dev.1" version: "2.2.0"
cherrypick_annotations: cherrypick_annotations:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -141,7 +141,7 @@ packages:
path: "../../cherrypick_flutter" path: "../../cherrypick_flutter"
relative: true relative: true
source: path source: path
version: "1.1.3-dev.1" version: "1.1.2"
cherrypick_generator: cherrypick_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:

View File

@@ -151,7 +151,7 @@ packages:
path: "../../cherrypick" path: "../../cherrypick"
relative: true relative: true
source: path source: path
version: "3.0.0-dev.1" version: "2.2.0"
cherrypick_annotations: cherrypick_annotations:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -3,7 +3,6 @@ name: cherrypick_workspace
sdkPath: .fvm/flutter_sdk sdkPath: .fvm/flutter_sdk
packages: packages:
- benchmark_cherrypick
- cherrypick - cherrypick
- cherrypick_flutter - cherrypick_flutter
- cherrypick_annotations - cherrypick_annotations