netanchor 5.0.0 copy "netanchor: ^5.0.0" to clipboard
netanchor: ^5.0.0 copied to clipboard

A swappable, SOLID-compliant networking layer for Flutter & Dart. Adapter pattern over Dio with refresh-token, retry, cache, pagination, and DataKey unwrapping. Zero UI coupling.

Changelog #

All notable changes to netanchor are documented here.

The format follows Keep a Changelog, and this project adheres to Semantic Versioning.

4.1.0 — 2026-07-21 #

Added #

  • Added Result.mapAsync, accepting a synchronous or asynchronous success transform through FutureOr while preserving failures unchanged.
  • Added Result.flatMapAsync for composing synchronous or asynchronous Result-returning repository operations without rebuilding the failure branch.
  • Added widenFailure() for safely reifying Result<T, F> as Result<T, Failure> before a chain may introduce a different application failure subtype. It preserves the exact failure instance.
  • Added tests for synchronous and asynchronous callbacks, success/failure composition, callback skipping, instance preservation, exception propagation, and NetworkFailureFailure widening before chaining an application-owned cache failure.

Documentation #

  • Added a repository example that composes remote login and asynchronous session persistence without manual branching.
  • Documented that transform exceptions propagate to the caller because a generic Result<S, F> cannot construct an arbitrary F safely.
  • Documented why a covariant assignment alone does not change Dart's reified runtime generic type before heterogeneous flatMapAsync composition.

This release is additive. Existing map, flatMap, when, result variants, and failure contracts are unchanged.

4.0.0 — 2026-07-21 #

This major release lets network and application-specific failures share one open typed contract without repository-level failure mappers.

Breaking changes #

  • Renamed the failed result variant from Failure<S, F> to FailureResult<S, F> so the clearer Failure name can represent the common failure contract. Pattern matches and direct constructor calls must use the new variant name.
  • Result.failure(...) is unchanged and remains the recommended construction API.

Added #

  • Added the open Failure interface with a message contract. Applications can implement it for cache, persistence, or business-rule failures without adding application types to netanchor.
  • NetworkFailure now implements Failure, while NetworkResult<T> remains Result<T, NetworkFailure>.
  • Added covariance and repository tests proving that Result<T, NetworkFailure> can widen to Result<T, Failure> without casts, mapping, branching, or loss of the original failure instance and metadata.

Migration #

// 3.x
case Failure(:final failure):
return Failure<User, NetworkFailure>(failure);

// 4.0
case FailureResult(:final failure):
return FailureResult<User, NetworkFailure>(failure);

// Unchanged
return Result<User, NetworkFailure>.failure(failure);

3.0.0 — 2026-07-21 #

This major release replaces ambiguous response parser callbacks with explicit, composable payload decoders.

Breaking changes #

  • Replaced every request's parser: parameter with decoder: and the ResponseParser<T> typedef with PayloadDecoder<T>.
  • getPaginated now accepts itemDecoder: instead of parser: and custom PaginationParser implementations receive a PayloadDecoder<T>.
  • sendVoid and head now return NetworkResult<void>; VoidResponse and voidParser were removed.

Added #

  • Decoders.jsonObject, jsonList, list, nullable, raw, empty, and custom cover conventional JSON models, primitives, nullable responses, empty bodies, and application-specific wire formats.
  • PayloadDecodeException reports the expected shape, actual runtime type, and JSONPath-like failure location without storing or printing the raw payload.
  • List decoders preserve order, never skip invalid items, and report nested item paths such as $[2].

Changed #

  • Successful live, cached, retried, and authentication-replayed responses now share one decoding pipeline: optional DataKey unwrapping happens first, then the selected decoder runs exactly once for the returned response.
  • Decode errors remain inside NetworkResult as ParseFailure; callback exceptions are retained as diagnostic causes without leaking payload data.

2.0.1 — 2026-07-20 #

Fixed #

  • Mixed multipart maps now detect and convert List<UploadFile> values to Dio FormData, matching the documented multi-file upload example.
  • NetAnchorListener.onError and onNoInternet now represent terminal request outcomes. Temporary failures followed by a successful retry no longer trigger misleading UI events.
  • A successful networkFirst cache fallback no longer emits the offline failure that caused the fallback. Background stale-while-revalidate failures also stay internal while the caller receives cached data.

2.0.0 — 2026-07-20 #

This major release makes retry decisions request-aware, hardens the execution pipeline, and turns secure logging into the default.

Breaking changes #

  • Request-aware retry APIRetryPolicy.shouldRetry now receives a RetryContext containing the request, failure, policy attempt, and RetryCause. Custom policies must update their method signature.
  • Explicit retry modes — requests use RetryMode.automatic, .always, or .never. Automatic mode permits only GET and HEAD; mutations require .always and should carry a server-supported idempotency key.
  • Canonical immutable headers — request and response header names are stored lower-case, and request maps / response header collections are unmodifiable snapshots.
  • Opaque cache keys — automatic cache keys are now canonical SHA-256 fingerprints and include authentication identity. Existing 1.x automatic entries are intentionally not reused after upgrade. Explicit CacheConfig.key values retain their exact-key behavior.

Added #

  • Safe per-request retry control — automatic retries now default to GET/HEAD only. Mutating requests require RetryMode.always.
  • Complete retry context — policies can inspect request headers, metadata, method, failure origin, and an auth-independent attempt counter.
  • Refresh token extractorAuthConfig.refreshTokenExtractor can parse tokens from any successful refresh response shape while the existing root JSON keys remain the default.
  • Cache scopeCacheConfig.scope partitions tenant-, company-, and locale-specific responses while automatic authentication isolation remains active.
  • Per-request metadata — every facade helper now exposes the existing immutable HttpRequest.metadata channel to interceptors and retry policies.
  • Pure Dart runtime — removed the unused Flutter SDK runtime dependency; Flutter apps remain fully supported by the same package.

Security #

  • Traffic-log redactionLoggingInterceptor now recursively masks common credential fields in URIs, query parameters, headers, request bodies, and response bodies by default. LogRedactor supports application-specific body formats and keys.
  • Failure logs redact URI credentials and omit server-controlled messages; refresh errors and cache diagnostics no longer print response bodies or raw cache keys.

Fixed #

  • Connectivity failures now participate in the retry policy.
  • Cancellation interrupts retry backoff immediately.
  • Interceptor and connectivity-checker exceptions remain inside the typed NetworkResult contract.
  • Auth refresh retries no longer consume failure retry attempts.
  • Post-refresh callbacks cannot invalidate already-persisted tokens, and terminal 401 responses still reach response interceptors.
  • Cache backend errors degrade gracefully instead of hiding successful network responses.
  • CachePolicy.noCache now performs neither reads nor writes, while networkOnly and successful networkFirst requests skip unnecessary reads.
  • Automatic cache entries are isolated across authenticated users and use canonical request encoding independent of map insertion order. Global default headers also participate in automatic key partitioning.
  • Retry-policy exceptions remain inside the typed NetworkResult contract.
  • Reusing a NetAnchorBuilder no longer duplicates logging interceptors or retains an automatically-managed interceptor from an earlier build.
  • Custom refresh extractors are validated before tokens reach storage.
  • DioAdapter no longer mutates a caller-owned Dio client's global validateStatus, honors UploadFile.contentType, and no longer imports dart:io, restoring Web compatibility.

1.3.0 — 2026-07-20 #

Adds per-request auth control and makes failure messages consistent. This release is backward compatible: existing requests keep their current auth behavior unless they explicitly opt out.

Added #

  • Per-request requiresAuth flag — every request helper now accepts requiresAuth: false for public endpoints. These requests skip automatic access-token attachment and do not enter the refresh / unauthorized flow on a 401. The default remains true, preserving existing behavior.

Fixed #

  • Consistent failure messages — terminal 401 responses now preserve the server's message or error value in UnauthorizedFailure.message, just like other HTTP failures. Plain-text response bodies are also preserved. localizedKey remains the stable, type-based localization contract.
  • Latest Dio compatibility — exception mapping remains exhaustive when Dio adds a new DioExceptionType; transformation timeouts map to HttpAdapterErrorKind.timeout without forcing consumers onto a newer Dio minimum version.

1.2.0 — 2026-06-24 #

Adds a sixth cache strategy. Additive and backward compatible — existing policies and call sites are unchanged.

Added #

  • CachePolicy.networkFirst — the "online → fresh, offline → last known" strategy. Always tries the network first and returns the fresh response (writing it to the cache) when the server is reachable. Only a connectivity or timeout failure makes it fall back to the most recent cached entry — even if that entry is expired — so the caller still gets data offline. Any other failure (4xx/5xx, parse) is returned untouched and never masked by stale cache.

    await netAnchor.get<List<Ad>>(
      '/ads',
      parser: Ad.fromList,
      cache: const CacheConfig(policy: CachePolicy.networkFirst, ttl: Duration(days: 7)),
    );
    

    Previously this behaviour had to be hand-composed at the call site (networkOnly, then a second cacheFirst read on a no-internet failure). It is now a first-class policy.

Notes #

  • networkFirst never proactively evicts an expired entry on the pre-read pass — the stale copy is deliberately retained as the offline fallback.
  • README caching section updated (six strategies; new row + example).

1.1.3 — Branding #

Docs / packaging patch. No code or behavior changed in lib/.

  • README header now leads with the brand. An animated logo at the top, followed by a row of pub-version / pub-points / MIT-license badges in the brand palette (#0B2D4D background, #19B5FE foreground). Served from the GitHub raw URL so pub.dev renders them correctly — pub.dev does not resolve relative paths or render SVG in README.
  • assets/ added at the repo root with the four files the README references: netanchor-animated.gif, netanchor-logo-horizontal-clean.png, netanchor-mark.svg, netanchor-mark-mono.svg. Repo-only — they're served from GitHub, not bundled into consumer apps.
  • .pubignore lists assets/, netanchor-logo/, and netanchor-logo.zip. Brand source files stay in the GitHub repo but are kept out of the tarball uploaded to pub.dev.
  • Installation snippet bumped from ^1.1.2 to ^1.1.3.

1.1.2 — Republish #

Version-only bump; no source changes from 1.1.1. Published manually to refresh the pub.dev rendering after the README author section landed.

1.1.1 — README fixes #

Docs-only patch. Two README cleanups:

  • Installation snippet bumped from netanchor: ^0.4.0 (a stale reference still showing the very first pub.dev release) to ^1.1.0, so the example matches the actual minimum version that exposes PrettyNetAnchorLogger / LogSeverity / log().
  • Contributing section closed for now. The project isn't taking external pull requests yet — the README now says so explicitly instead of inviting them. Bug reports as issues are still welcome.

No code or behavior changed.

1.1.0 — Pretty logger #

Backwards-compatible release. New traffic-log renderer that boxes every request / response / failure with the visual style of package:logger, plus a richer logger contract so loggers can opt in without breaking the existing info / warn / error API.

Added #

  • PrettyNetAnchorLogger — bordered, color-coded box renderer matching the visual style of package:logger's PrettyPrinter. Each entry is wrapped in a ┌─ TITLE ────┐ … └────┘ box with ANSI colors, level emoji (💡 / ⚠️ / ⛔), optional timestamp row, indented JSON for Map / Iterable payloads, and a filtered caller-stack section (at Symbol (package:foo/bar.dart:42:5)) whose paths are clickable in most IDE consoles.
    final netAnchor = NetAnchor.builder()
        .baseUrl('https://api.example.com')
        .logger(const PrettyNetAnchorLogger(printTime: true))
        .enableTrafficLog()
        .build();
    
  • LogSeverity enum (info / warn / error) — severity tag for the new NetAnchorLogger.log method. Named LogSeverity (not LogLevel) to avoid colliding with the LogLevel types that consumer apps commonly define for their own UI log panels.
  • NetAnchorLogger.log({level, title, message, error, stackTrace}) — titled, leveled log entry that pretty loggers render on the box border. The abstract base provides a default implementation that falls back to info / warn / error with the title inlined into the message, so existing custom loggers keep working unchanged.
  • LoggingInterceptor.pretty(...) factory — one-call constructor that wires a PrettyNetAnchorLogger with sensible defaults (colors: true, printTime: false, lineLength: 120).
  • Caller-stack section in pretty boxes — the LoggingInterceptor now captures StackTrace.current at each hook and passes it to logger.log. Framework frames (package:netanchor/, package:dio/, package:flutter/, dart:async, …) are filtered out by default; the remaining user frames (capped at callerMethodCount, default 3) are rendered inside the box. Customize via the excludeFromCallerTrace knob.

Changed #

  • LoggingInterceptor.onResponse routes by status code. 2xx still logs at info (cyan), but 4xx now logs at warn (yellow) and 5xx at error (red), so HTTP failures stand out visually instead of disappearing in the success stream. Pure behavior change for users who rely on log severity — output is otherwise unchanged.
  • Request / response / failure bodies are JSON-pretty-printed when they're a Map or Iterable, with one key per line. Plain strings and other types still go through toString.

1.0.0 — Stable API #

First stable release. The public API is now considered settled; future breaking changes will follow semver with a deprecation path.

Added #

  • Result.success / Result.failure const factory constructors — build a result without naming the variant class. Pairs nicely with the NetworkResult<T> alias:
    NetworkResult<User>.success(user);
    NetworkResult<User>.failure(const UnauthorizedFailure());
    
    Success<T, F>(...) / Failure<T, F>(...) still work — the factories are purely additive.
  • NetAnchorBuilder.enableTrafficLog({logHeaders, logBody}) — a one-call switch that wires a LoggingInterceptor using the builder's configured logger. Previously you had to know LoggingInterceptor existed, construct it yourself, and hand it the logger via addInterceptor(...). The builder dartdoc now also spells out that .logger() is the sink and .enableTrafficLog() is the switch.

Changed #

  • LoggingInterceptor.onFailure no longer logs failure.rawError. For an HTTP failure rawError is the response body, which onResponse already printed on the line — so the old behaviour logged the body twice. onFailure now logs only the typed failure summary (status code + message), which is the new information that line carries. Transport-level failures (timeout, no-internet, parse) were never double-logged and are unaffected.

0.4.0 — Initial public release #

First public release on pub.dev. A swappable, SOLID-compliant networking layer for Flutter & Dart with zero UI coupling.

Added #

  • CoreNetAnchor, NetAnchorBuilder, and NetAnchorConfig for configuring a network client with a fluent builder API.
  • Adapter pattern — pluggable HttpAdapter abstraction with a built-in DioAdapter (over dio ^5.7.0) and a MockAdapter for tests.
  • Result typesResult<S, F> sealed type plus NetworkFailure and network_failure_x extensions so callers branch on typed failures instead of catching exceptions.
  • InterceptorsInterceptor interface with built-in AuthInterceptor, HeaderInterceptor, and LoggingInterceptor.
  • Auth & refresh tokensTokenRefresher / HttpTokenRefresher, TokenStorage, and AuthConfig with an internal single-flight refresh coordinator that queues concurrent 401s and replays them once.
  • Retry — configurable RetryPolicy with exponential backoff and per-request opt-in/opt-out.
  • CacheCachePolicy and pluggable CacheStore (in-memory by default).
  • PaginationPaginated<T> envelope and PaginationParser for cursor- and page-based APIs.
  • Response parsingResponseParser, DataKey unwrapping for APIs that wrap payloads under data/result/custom keys, and VoidResponse for 204-style endpoints.
  • File uploadUploadFile with progress callbacks.
  • Binary downloads — first-class support via HttpResponseType.
  • CancellationCancellationToken for cooperative request cancellation.
  • ListenersNetAnchorListener + NetworkEvent for decoupling cross-cutting UI concerns (toasts, navigation, analytics) from repositories.
  • ConnectivityConnectivityChecker hook for offline-aware flows.
  • LoggerNetAnchorLogger abstraction.
  • Example app — runnable Flutter example under example/ showing NetAnchor.builder(), listeners, and an auth module wired through a Bloc/repository structure.
  • Tests — coverage for pagination, retry, cache, auth refresh, cancellation, DataKey, MockAdapter, and the core builder.

5.0.0 - 2026-07-22 #

This major release simplifies the normal request → repository → UI path and hardens caching without moving application presentation policy into core.

Breaking #

  • Result<S, F> is now Result<T> and always carries the open Failure contract. NetworkResult, widenFailure, and the second variant generic were removed.
  • Result composition is explicit: mapSuccess, mapSuccessAsync, thenResult, and thenResultAsync. The same concise helpers are available directly on Future<Result<T>>.
  • Normal request parsing uses fromJson:; getList and getJson cover common collection/raw-object cases.
  • NetAnchorListener became diagnostics-only NetworkObserver; silent was removed so transport callbacks cannot accidentally drive UI.
  • Failure implementations now expose stable code plus safe message.
  • UnknownFailure was renamed to UnknownNetworkFailure; arbitrary HTTP statuses use HttpFailure.
  • Cache setup and policy were redesigned around .cache(...), stable identity, CacheOptions, and CacheStrategy.
  • MockAdapter moved to package:netanchor/netanchor_testing.dart.

Added #

  • ResponseFailureMapper for application-specific non-2xx failures without a repository-wide network mapper.
  • Async model factories through FutureOr.
  • Opt-in private/public response caching with namespace, schema, scope, explicitly varied headers, tag invalidation, and logout partition clearing.
  • Cache-first, network-first, stale-while-revalidate, and refresh strategies.
  • Cache race protection: newest-write wins, invalidation/logout generations, no-store ordering, corrupt-entry recovery, and single-flight background refresh.
  • TokenRefreshException for expected refresh protocol failures.
  • A complete app-owned UnexpectedErrorDetails runner/presenter example.

Security and behavior #

  • Private cache is bypassed when stable identity is unavailable; access-token rotation never changes cache identity.
  • Cache-Control: no-store removes current cached data and never allows an old response to delete a newer value.
  • Mutations remain non-retryable unless explicitly opted in with RetryMode.always.
  • Sensitive traffic redaction remains enabled independently of build mode.
4
likes
160
points
51
downloads

Documentation

API reference

Publisher

verified publisherbasuony.com

Weekly Downloads

A swappable, SOLID-compliant networking layer for Flutter & Dart. Adapter pattern over Dio with refresh-token, retry, cache, pagination, and DataKey unwrapping. Zero UI coupling.

Repository (GitHub)
View/report issues

Topics

#networking #http #dio #rest #api

License

MIT (license)

Dependencies

crypto, dio, http_parser

More

Packages that depend on netanchor