simple_network_handler 2.0.1 copy "simple_network_handler: ^2.0.1" to clipboard
simple_network_handler: ^2.0.1 copied to clipboard

A package for handling network errors with error registry and interceptors. Supports Dio and Supabase.

2.0.1 #

Performance #

  • perf: registry declarations are read once per handler/interceptor instead of on every error. ErrorRegistry/SupabaseErrorRegistry implementations return map literals, so each lookup previously rebuilt the whole map and every closure in it. Declare registry getters as literals — they are now cached.
  • perf: endpoint keys are compiled once into a lookup index — hashed exact keys and pre-split {param} templates — replacing two linear scans with per-key string splitting on every mapped response. Resolution order (exact → template → *, first declaration wins) is unchanged.
  • perf: ErrorMappingInterceptor skips endpoint resolution entirely for statuses no registry key mentions (new ErrorRegistry.hasMappingForStatus) — the happy path of every request is now a single set lookup instead of a full registry walk.
  • perf: RefreshTokenInterceptor parses excludedPaths once at construction and short-circuits when nothing is excluded, instead of re-splitting every pattern on every request.
  • perf: RetryInterceptor compiles its Retry-After HTTP-date pattern once instead of per parse.
  • perf: NetworkLogInterceptor no longer stringifies large bodies before truncating them — binary bodies log as <N bytes>, FormData as a field/file count, streams as <stream>; curl export omits binary and streamed bodies.

Added #

  • feat: installNetworkHandling returns the NetworkHandler wired to the same registry instance (with enableDebugLogging/onFailure parameters), so the interceptors and safeCall can no longer be given two different registries. The cascade form still works.
  • feat: DefaultErrorRegistry — batteries-included mappings for 401/403/404/5xx, timeouts and connection errors, with LocalizedFailure copy and correct TransportFailure typing (DefaultOfflineFailure, DefaultTimeoutFailure, DefaultUnauthorizedFailure, DefaultForbiddenFailure, DefaultNotFoundFailure, DefaultServerFailure, DefaultGenericFailure). Extend it to add your own mappings.
  • feat: ErrorRegistry.endpointRegistry, dioRegistry and generalRegistry now default to empty — only genericError must be implemented.
  • feat: Result gains getOrDefault, getOrElse, onOk, onErr, mapAsync and flatMapAsync.
  • feat: ErrorRegistry.debugUnmatchedKeys / debugValidate — feed them your API path constants to catch registry keys that can never fire (typos, renamed endpoints).
  • feat: NetworkLogInterceptor.enabled (defaults to kDebugMode) — release builds no longer log URLs, timings or bodies unless you opt in.
  • feat: RefreshTokenInterceptor.close() closes the internally created replay/refresh client; a caller-supplied httpClient is left untouched.
  • feat: FakeNetworkHandler gains enqueueOkAll, remaining, reset() and verifyDrained(); a wrong-typed queued success now throws a StateError naming both types instead of a raw cast error.

2.0.0 #

Breaking release — modernized API. See the "Migrating from 1.x" section of the README for the full old→new mapping.

  • BREAKING — static facades removed: SimpleNetworkHandler and SupabaseNetworkHandler are gone. Construct NetworkHandler(registry) / SupabaseHandler(registry) once at startup and inject them; debug logging and the failure observer are constructor parameters (enableDebugLogging:, onFailure:). No global mutable state remains.

  • feat: RetryInterceptor retries 429 by default — the status that most commonly carries the Retry-After header it honors.

  • feat: connectivity-aware retry — RetryInterceptor.waitForConnectivity: for offline-shaped failures (no response, connectivity exception type) the retry waits for your network gate (e.g. connectivity_plus) instead of backing off blindly; capped by maxDelay so a dead gate can never hang a request.

  • feat: consumer test kits — package:simple_network_handler/testing.dart (FakeNetworkHandler) and package:simple_network_handler_supabase/testing.dart (FakeSupabaseHandler): queue Ok/Err results and unit-test repositories/cubits with no Dio, adapters, or registries; empty queue throws a descriptive error unless defaultResult is set.

  • feat: NetworkLogInterceptor — structured, sanitized traffic logging (one line per request and per outcome with duration; authorization/cookie/set-cookie/x-api-key redacted by default), optional header/body logging with truncation, and a redacted copy-pasteable curl export (logCurl / NetworkLogInterceptor.curl). installNetworkHandling(logging: ...) places it after refresh (final headers) and before retry (every attempt logged).

  • BREAKING — Result<T> replaces dartz's Either: safeCall returns Future<Result<T>> with sealed Ok/Err variants (exhaustive switch support). fold(onFailure, onSuccess) is kept with the same argument order, so fold-based call sites migrate by renaming types only. The dartz dependency is gone.

  • BREAKING — registry factories return Failure directly: (json) => Left(MyFailure()) becomes (json) => MyFailure() (typedef FailureFactory; Supabase: SupabaseFailureFactory). Mapping a response to a substitute success via the registry was removed — perform such recoveries in the repository via onEndpointError. This closes the Either<Failure, dynamic> / success as T type holes entirely. parsedEitherKeyparsedFailureKey.

  • BREAKING — Failure is pure data: getTitle/getSubtitle(BuildContext) moved to the new LocalizedFailure mixin. Migrate each failure with extends Failure with LocalizedFailure (bodies unchanged); generic error UI branches once on failure is LocalizedFailure with a fallback. FailureAbstract removed; the silently-empty default strings are gone.

  • BREAKING — Supabase support moved to simple_network_handler_supabase: import package:simple_network_handler_supabase/simple_network_handler_supabase.dart instead of the old root barrel. The main package no longer depends on supabase_flutter — Dio-only consumers drop the entire Supabase SDK from their dependency tree. Shared types live in simple_network_handler_core, re-exported by both.

  • feat: instance-based NetworkHandler(registry) and SupabaseHandler(registry) as the primary API — DI-friendly, multiple independently configured backends, isolated tests.

  • feat: Result gains map, flatMap, mapErr, valueOrNull, failureOrNull, and mapBusiness (moved from the removed map_business_extension.dart, same transport-pass-through semantics).

  • feat: Dio-side transport classification — ErrorRegistry gains isTransportError(DioException) (default: connectionError/connectionTimeout/sendTimeout/receiveTimeout) and transportError (default genericError), mirroring the Supabase registry so Result.mapBusiness works uniformly across both backends. Explicit dioRegistry mappings always win.

  • feat: onFailure observer (FailureObserver) on NetworkHandler and SupabaseHandler — invoked with every handler-produced failure, its original exception and stack trace; one-line Sentry/Crashlytics wiring. A throwing observer never breaks error propagation.

  • feat: RetryInterceptor — exponential backoff with full jitter, Retry-After support (seconds and HTTP-date, capped at maxDelay), transient-only conditions (connectivity-shaped exception types + 502/503/504 by default), safe/idempotent methods only by default (opt in per method), cancellation-aware, onRetry observability hook. Place after RefreshTokenInterceptor, before ErrorMappingInterceptor.

  • feat: proactive token refresh — RefreshTokenInterceptor.isTokenExpired runs a single-flight refresh BEFORE sending when the stored token is known-expired, saving the 401 round-trip; failing proactive refreshes fall back to the reactive path. Bundled JwtExpiry.isExpired/expiryOf helpers (30s default leeway; opaque tokens never assumed expired).

  • feat: RefreshTokenInterceptor.excludedPaths now supports {param} templates, mid-path * (one segment) and trailing * (any depth); query strings are stripped before matching.

  • feat: first-class cancellation — CancelledFailure marker + built-in RequestCancelledFailure (core); safeCall maps DioExceptionType.cancel to ErrorRegistry.cancellationError (observer skipped — cancellation is not an error) and Result.mapBusiness lets cancellations pass through like transport failures; CancellationScope + installNetworkHandling(cancellation: scope) enrolls token-less requests so scope.cancelAll() aborts everything in flight (sign-out), reusable immediately, per-call tokens take precedence. Per-page cancellation via ambient scopes: pageScope.bind(() => ...) enrolls every request started inside the block (zone-propagated through the whole await chain — no token plumbing); the scope-enrollment interceptor is always installed by installNetworkHandling, so bind works without a Dio-wide scope. Precedence: per-call token → ambient scope → Dio-wide scope. New simple_network_handler_bloc package: CancellableRequests mixin ties cancellation to the bloc/cubit lifecycle (cancellable(() async {...}) + automatic cancelAll on close).

  • feat: dio.installNetworkHandling(errorRegistry: ..., refresh: ..., retry: ...) — one-call setup that owns the interceptor ordering (refresh → retry → mapping) so it can't be wired wrong, and injects the retry client. RetryInterceptor.dio is now optional at construction (asserted when hand-wiring without one).

  • chore: GitHub Actions CI — analyze + test for all three packages and the example app.

1.4.0 #

  • fix: endpoint registry keys with {param} templates (e.g. /api/users/{id}) now match real request paths (/api/users/5). Previously template keys NEVER matched (Retrofit substitutes params before the request), so those mappings silently fell through to */genericError. Lookup precedence: exact path (query string stripped) → template match (declaration order) → *. Behavioral note: registries already containing template keys will start firing their specific mappings — this is the documented intent, but review yours if you relied on the fall-through.
  • fix: calling safeCall without a configured registry now throws a descriptive StateError in ALL build modes. Previously this was an assert, so release builds crashed on a bare null-check instead.
  • fix: a registered factory producing a success value of the wrong type no longer throws a TypeError out of safeCall (breaking the Either contract) — it is reported via onMappingError and falls back to genericError.
  • feat: ErrorRegistry.onMappingError(error, stackTrace, response) — invoked when an endpoint factory throws (e.g. a broken fromJson) or produces a mis-typed success. Default implementation logs a prominent warning in debug builds (previously these were swallowed silently); override to report to your error tracker.
  • feat: predicate-based exception matching via ExceptionMatcher and the new generalMatchers list on both ErrorRegistry and SupabaseErrorRegistry. The existing generalRegistry maps match only the exact runtimeType (subclasses never match); matchers support is-checks: ExceptionMatcher.whenType<MyBaseException>((e) => MyFailure()).
  • feat: SimpleNetworkHandler.reset() and SupabaseNetworkHandler.reset() (@visibleForTesting) so tests no longer share registry state.
  • docs: ErrorMappingInterceptor now documents that mapped 2xx statuses produce a synthetic rejection that intentionally bypasses other interceptors' onError.

1.3.0 #

  • feat: add TransportFailure marker mixin on the shared Failure base. Mix it onto any failure representing a transport-level problem (offline / timeout / unreachable) so UIs and observers can special-case connectivity once, regardless of feature. Because it lives on the shared base, it is available to both the Dio (REST) and Supabase variants.
  • feat: SupabaseErrorRegistry gains first-class transport classification — bool isTransportError(Object error) (default covers TimeoutException and SocketException; SocketException matched by runtime type name to avoid a dart:io import so web builds keep compiling) and Failure get transportError (defaults to genericError; override to return your localized offline failure). Wired into safeCall's general-exception path.
  • feat: add Either<Failure, T>.mapBusiness(ifBusinessError, onData) extension — a transport-aware fold that lets TransportFailures pass through unchanged while substituting a feature-specific failure for any other Left. Replaces the fold((_) => Left(FeatureFailure()), ...) pattern that discarded transport classification.
  • note: fully backward compatible. Existing consumers compile and behave identically without opting in — the default transportError is genericError, and the new transport check runs only AFTER the existing handleRealtimeError consultation, so registries that classify transport errors via the realtime hook keep working unchanged.
  • migration hint: replace result.fold((_) => Left(FeatureFailure()), (data) => Right(...)) with result.mapBusiness(const FeatureFailure(), (data) => Right(...)), and override transportError on your registry to return a failure that mixes in TransportFailure.

1.2.0 #

  • feat: add RefreshTokenInterceptor with automatic token refresh, single-flight queuing of concurrent 401s and request replay
  • feat: add TokenStore interface and RefreshRequest spec executed on a separate bare Dio instance
  • docs: document the required interceptor order with ErrorMappingInterceptor

1.1.1 #

  • fix: SupabaseNetworkHandler now resolves AuthExceptions against the auth registry by semantic code (e.g. email_not_confirmed, invalid_credentials), falling back to statusCode and then message. Previously only statusCode/message were checked, so registries keyed by error codes never matched.

1.1.0 #

  • feat: add Supabase API support with SupabaseNetworkHandler and SupabaseErrorRegistry
  • feat: add Supabase-specific failure classes (AuthFailure, PostgrestFailure, StorageFailure, etc.)
  • feat: add separate import for Supabase: import 'package:simple_network_handler/simple_network_handler_supabase.dart'
  • feat: add example Supabase error registry and repository implementation

1.0.4 #

  • feat: change general error to accept types and provide access to the exception itself
  • feat: add logging functionality

1.0.3 #

  • feat: add handling of custom exceptions

1.0.2 #

  • chore: update readme

1.0.1 #

  • chore: update readme
  • feat: add example app that showcases an advanced use case

1.0.0 #

  • feat: initial release of Simple Network Handler
2
likes
160
points
156
downloads

Documentation

API reference

Publisher

verified publisherkeep-it-simple.dev

Weekly Downloads

A package for handling network errors with error registry and interceptors. Supports Dio and Supabase.

Repository (GitHub)
View/report issues

Topics

#dio #network #error-handling #supabase

License

MIT (license)

Dependencies

dio, flutter, simple_network_handler_core

More

Packages that depend on simple_network_handler