resilify 1.1.1
resilify: ^1.1.1 copied to clipboard
Unified Result-based API handling for Dart & Flutter — works with http, Dio, Retrofit, Chopper, and WebSocket. No exceptions. Just results.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.1.1 #
Added #
-
ResultX.recover()/ResultX.recoverWith()(sync) — synchronous counterparts to the existingFutureResultX.recover/recoverWith.recoversubstitutes a fallback success value computed from the [Failure];recoverWithdelegates to a callback that returns its own [Result] (which may itself be an [Error]).final user = cachedResult.recover((f) => User.guest()); -
ResultX.ensure()— post-success validation that turns a [Success] into an [Error] when a predicate fails, leaving an existing [Error] untouched. Useful when an HTTP 200 envelope still encodes a logical failure (empty payload, server-side flag, etc.).final valid = orderResult.ensure( (o) => o.items.isNotEmpty, (o) => const Failure.badResponse(message: 'Empty order'), ); -
ResultCache.keys— unmodifiable snapshot of the cache key set for diagnostics and ad-hoc inspection. -
ResultCache.invalidateWhere()— bulk invalidation by predicate, returning the number of entries removed. Enables tag-style eviction when keys encode resource type or user scope:cache.invalidateWhere((k) => k.startsWith('user:'));
1.1.0 #
Added #
-
Circuit breaker (
CircuitBreaker) — a closed/open/half-open state machine forResult-returning operations. AfterfailureThresholdconsecutive trip-eligible failures the breaker opens and fast-fails calls forresetTimeout, then admits a single probe. ConfigurableshouldTrippredicate (defaults tofailure.isRetryable) andonStateChangeobserver. Pluggable clock for tests. -
FailureKindenum +Failure.kindfield — categorical tag that discriminates failures independently ofcode/message. Enables accurate retry decisions for code-less failures (anetworkfailure is retryable; aparsingfailure is not). NewFailureKind.circuitOpenmarks fast-fail responses fromCircuitBreaker.execute. -
FailureTypeenum — everyFailurenow carries atypefield set automatically by its named constructor. Switch onfailure.typeinstead of inspectingfailure.codeto route failures to the right UI state or analytics bucket without fragile integer comparisons:switch (failure.type) { FailureType.network => showOfflineBanner(), FailureType.unauthorized => pushLoginScreen(), _ => showGenericError(failure.message), } -
Failure.validation()— new named constructor for HTTP 422 (Unprocessable Entity) withtype = FailureType.validation. Also handled byFailure.fromStatusCode(422). -
RetryHelper.withTimeout— standalone helper that wraps a single attempt in a timeout, returningError(Failure.timeout())instead of throwing. -
Future<T>.asResult()extension — terser bridge from a throwingFuture<T>into aFuture<Result<T>>(vs the longerResult.tryRunAsync(() => future)). NamedasResultrather thantoResultto avoid colliding with the retrofit / chopper integrations' transport-specific.toResult()methods. -
Result.collectAsync()— async counterpart toResult.collect(). Awaits anIterable<Future<Result<T>>>in order and short-circuits on the first error, returning aResult<List<T>>:final r = await Result.collectAsync([ api.fetchUser('u1'), api.fetchUser('u2'), ]); -
Result.asyncMap()/Result.asyncFlatMap()— async transform methods on plainResult<T>(not a future). ComplementFutureResultX.mapAsync/flatMapAsyncfor cases where you already hold a resolved result and want to start an async pipeline:final result = await cachedResult.asyncMap((user) => enrichUser(user)); -
FutureResultX.onSuccessAsync()/onErrorAsync()— async side-effect hooks onFuture<Result<T>>. Mirror the synchronousonSuccess/onErrorbut acceptasynccallbacks, useful for async logging, cache writes, or analytics calls:await api.fetchUser(id) .onSuccessAsync((u) => cache.put(id, u)) .onErrorAsync((f) => analytics.logError(f)); -
ResultCache<K, V>— in-memory cache forResultvalues with optional TTL.getOrFetchserves cached successes instantly and fetches on miss; errors are never cached so callers can retry immediately:final cache = ResultCache<String, User>(ttl: const Duration(minutes: 5)); final user = await cache.getOrFetch('u1', () => api.fetchUser('u1')); -
ResultDeduplicator<K, V>— collapses concurrent calls for the same key into a single in-flight future. All concurrent callers receive the same result; once the future completes the key is evicted so the next call triggers a fresh fetch:final dedup = ResultDeduplicator<String, User>(); // Three concurrent callers → one network request. final results = await Future.wait([ dedup.run('u1', () => api.fetchUser('u1')), dedup.run('u1', () => api.fetchUser('u1')), dedup.run('u1', () => api.fetchUser('u1')), ]); -
LogCallback— thetypedef LogCallback = void Function(String line)is now exported from the coreresilify.dartbarrel so you can type callback parameters without importing the Dio barrel.
Changed #
Failure.isRetryableis now decided primarily byFailureKind, soFailure.network()and similar code-less transient failures are now reported as retryable. Failures built with the unstructured generic constructor still fall back tocode-based heuristics. Existingcode-based retry decisions are unchanged.Failure.toString()now includes bothkind:andtype:for easier diagnostics. The==/hashCodecontract is unchanged (stilltype+code+message+cause+retryAfter) so existing equality-based assertions keep working.
1.0.6 #
Fixed #
- Resolved pub.dev analysis failure caused by
dart pub outdated --jsoncrashing during dependency advisory parsing. - Stabilized dependency resolution to ensure compatibility with pub.dev's pana analysis pipeline.
- Fixed documentation generation issues that prevented dartdoc from running successfully.
- Corrected
documentationURL inpubspec.yamlto point to a valid and accessible resource.
Changed #
- Adjusted dependency constraints (notably
dio) to avoid triggering advisory parsing issues on pub.dev. - Simplified dev dependency graph to improve analysis reliability and prevent toolchain conflicts.
- Improved overall package compatibility with pub.dev scoring system and analysis environment.
Documentation #
- Added missing dartdoc comments across public APIs to improve documentation coverage and pub score.
1.0.5 #
Added #
-
Result.tap()— execute side effects (logging, debugging) on successful results without transforming the value. Great for inspecting data in the middle of a result chain:result.tap(print).tap(logToAnalytics).map(transformData); -
Enhanced code quality standards with stricter linting rules (upgraded
lintsto ^6.1.0) — the package now adheres to the latest Dart conventions and best practices for code style and correctness.
Fixed #
- Trailing-comma lint warnings in
lib/src/integrations/chopper_result.dartandlib/src/result.dart— package now passesdart analyze --fatal-warningswith zero issues. - Removed deprecated transitive dependencies (
build_resolvers,build_runner_core) that were flagged by pub.dev.
Changed #
- Updated dev dependencies to latest versions:
lints: ^4.0.0 → ^6.1.0 for stricter code quality checksretrofit_generator: ^8.0.0 → ^10.2.5build_runner: ^2.4.0 → ^2.15.0chopper_generator: ^8.0.0 → ^8.6.1json_serializable: ^6.7.0 → ^6.13.2test: ^1.25.0 → ^1.31.1- All transitive dependencies updated to latest compatible versions.
1.0.4 #
Added #
-
Failure.retryAfter— aDuration?field onFailurethat surfaces the server's back-off hint from aRetry-AfterHTTP header. Populated automatically for 429 / 5xx responses by bothHttpResultHandlerand the Dio integration'smapDioException. Pair withRetryHelper.retry'sdelayparameter to honor the server's wait time exactly:result.errorOrNull?.retryAfter // Duration(seconds: 30) when present -
Failure.parseRetryAfter(String?)— static helper that converts the seconds form of an HTTPRetry-Afterheader into aDuration. Returnsnullfor null / blank / non-numeric input; clamps negatives to zero. The HTTP-date form is intentionally left to callers so the core library stays free ofdart:io. -
platforms:declaration inpubspec.yaml(Android, iOS, Linux, macOS, Windows) so pub.dev surfaces verified platform support on the package page. -
documentation:link inpubspec.yamlpointing at the published dartdoc. -
Smoke tests for
HttpResultHandlercovering JSON GET/POST round-trips, query parameter merging, default-header propagation, 404 / 429 / 5xx mapping,Retry-Afterextraction, and parsing failures — closing the integration test gap flagged in the 1.0.3 audit.
Fixed #
- Trailing-comma lint warnings in
dio_result.dartandlogger.dartso the package now ships with a cleandart analyze.
1.0.3 #
Added #
Result.fromNullable<T>(T?, {Failure Function()? onNull})— bridge nullable APIs (cache lookups,firstWhereOrNull, etc.) into aResult<T>in one call.Result.zip2andResult.zip3— combine multiple results into a record (Result<(A, B)>/Result<(A, B, C)>), short-circuiting on the first failure left-to-right. Handy for parallel fetches withFuture.wait.Result.collect<T>(Iterable<Result<T>>)— fold a list of results into a singleResult<List<T>>, returning the first failure encountered.recoverWithextension onFuture<Result<T>>— likerecover, but the fallback callback may itself return aResult(so a fallback network call that also fails is surfaced as the final error).mapErrorAsyncextension onFuture<Result<T>>— async counterpart to the synchronousResult.mapError.
Changed #
RetryHelper.retrynow acceptsattemptTimeout. When set, each attempt is wrapped inFuture.timeout; an exceeded timeout is converted into anError(Failure.timeout())and goes through the normalretryIf/maxAttemptsmachinery.
1.0.2 #
Added #
- New
Failurenamed constructors:Failure.forbidden(403),Failure.conflict(409), andFailure.rateLimit(429). Failure.fromStatusCode(int)— picks the most specific named constructor for a given HTTP status, falling back tobadResponsefor other 4xx andserverErrorfor other 5xx.Failure.is4xx,Failure.is5xx, andFailure.isRetryablegetters — drop-in predicates forRetryHelper.retryIf.onCompleteextension onResult<T>— finally-style hook that fires for bothSuccessandError.flatten()extension onResult<Result<T>>— collapses one layer of nesting thatflatMapchains often produce.
Changed #
RetryHelper.retrynow acceptsmaxDelayto cap the wait between attempts andjitter(with an optionalRandom) to spread retries and prevent thundering-herd retry storms. Both default to no-op behavior, so existing call sites are unaffected.
1.0.1 #
Added #
Result.tryRunandResult.tryRunAsync— bridge throwing code into aResult<T>without writing try/catch at call sites. Both accept an optionalonErrorto translate the caught object into a domain-specificFailure.mapErroronResult<T>— transform the wrappedFailurewithout touching the success path. Useful for translating low-level transport failures into domain failures.errorOrThrowextension — symmetric counterpart togetOrThrow, returning the wrappedFailureor throwing aStateErroronSuccess.
1.0.0 — Initial release #
Added #
-
Core
- Sealed
Result<T>withSuccess<T>andError<T>variants (Dart 3). - Structured
Failurevalue type with named constructors:network,timeout,badResponse,parsing,unauthorized,notFound,serverError,cancelled,unknown. - Synchronous extensions:
isSuccess,isError,dataOrNull,errorOrNull,getOrElse,getOrThrow,onSuccess,onError. when,fold,map,flatMaponResult<T>.- Async helpers on
Future<Result<T>>:mapAsync,flatMapAsync,recover. - Stream helpers on
Stream<Result<T>>:mapStream,whereSuccess,whereError,dataStream,listenResult. - List helpers on
Result<List<T>>:mapList,filter,whereResult,firstOrError. RetryHelper.retrywith exponential backoff, predicate-based retry, and per-attempt observer.
- Sealed
-
Integrations (each opt-in via its own barrel file)
resilify_http.dart—HttpResultHandlerforpackage:http.resilify_dio.dart—DioResultHandler(incl.upload/downloadwith progress) andResultLoggerInterceptor.resilify_retrofit.dart—.toResult()on Retrofit-generated futures.resilify_chopper.dart—.toResult()on ChopperResponse<T>futures with pluggable failure mappers.resilify_websocket.dart—WebSocketResultHandler<T>with auto-reconnect and exponential backoff.