qora 0.3.0
qora: ^0.3.0 copied to clipboard
A powerful async state management library for Dart, inspired by TanStack Query
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.
Unreleased #
0.3.0 - 2026-02-25 #
Added #
MutationController<TData, TVariables, TContext>— standalone controller managing the full mutation lifecycle:MutationIdle → MutationPending → MutationSuccess | MutationFailureMutationState<TData, TVariables>— sealed class hierarchy with four variants:MutationIdle,MutationPending,MutationSuccess,MutationFailure; each carries typedvariablesfor full traceabilityMutationOptions<TData, TVariables, TContext>— per-mutation configuration with lifecycle callbacks:onMutate(variables)— called before the mutator; return value becomesTContext(snapshot for rollback)onSuccess(data, variables, context)— called on successonError(error, variables, context)— called on failure; usecontextto roll back optimistic updates viarestoreQueryDataonSettled(data, error, variables, context)— called after either outcomeretryCount/retryDelay— optional retry with exponential backoff (default: 0 retries)
MutatorFunction<TData, TVariables>typedef — mirrorsQueryFunction<T>for consistency (mutatorparameter naming mirrorsfetcher)MutationTracker— abstract interface implemented byQoraClient; decouplesMutationControllerfrom the client to prevent circular importsMutationEvent— type-erased event emitted on every mutation state transition:mutatorId— stablemutation_Nidentifier correlating events to a specific controllerstatus/isIdle/isPending/isSuccess/isError/isFinished— coarse-grained status helpersdata,error,variables— type-erased payloadmetadata— optionalMap<String, Object?>forwarded fromMutationController.metadata; attach domain context (e.g.{'category': 'auth'}) without modifying the core schematimestamp— emission time
QoraClientimplementsMutationTracker— global DevTools observability for all tracked mutations:mutationEvents—Stream<MutationEvent>of every state transition from all tracked controllersactiveMutations— snapshotMap<String, MutationEvent>of currently running (pending) mutations only; finished entries are auto-purged onSuccess/Failure, preventing memory accumulation of "ghost" entriesdebugInfo()now includesactive_mutationscount
MutationController.metadata— optionalMap<String, Object?>?forwarded verbatim to everyMutationEvent; enables DevTools labelling without schema changesMutationController.id— uniquemutation_Nidentifier (monotonically increasing counter)MutationStateExtensions—fold<R>()exhaustive mapper andstatusgetter returningMutationStatusenumMutationStatusenum — coarse-grainedidle | pending | success | errorvalues with boolean gettersMutationStateStreamExtensions—whereSuccess(),whereError(),dataOrNull()stream operators
Changed #
MutationFunctionrenamed toMutatorFunction— aligns naming with thefetcher/mutatorparameter convention used throughout the API
Fixed #
MutationController.streamrace condition — the previousasync*implementation started the generator in the next microtask, causing events emitted before the first microtask (e.g. callingmutatesynchronously afterlisten) to be lost on the broadcast stream. The stream getter now uses aStreamControllerwhoseonListencallback runs synchronously, capturing the current state and subscribing to the broadcast stream with no timing gap
0.2.0 - 2026-02-22 #
Added #
watchState<T>(Object key)— observe-only stream that subscribes to a query's state without triggering any fetch; ideal for derived UI components (e.g. badges, avatar widgets)prefetch<T>()— pre-warm the cache before navigation without blocking the UI; no-op if data is already freshrestoreQueryData<T>(key, snapshot)— roll back an optimistic update; removes the entry from cache if snapshot isnullremoveQuery(key)— evict a single query from cache and cancel any pending request for itclear()— evict all cached queries and cancel all in-flight requests (e.g. on user logout)cachedKeysgetter — returns all currently cached normalised query keys for debugging or bulk operationsdebugInfo()— returns a map snapshot of cache and pending-request counts
Changed #
QoraState<T>rewritten as a sealed class — four exhaustive variants:Initial | Loading | Success | Failure.LoadingandFailurenow carrypreviousDatafor graceful degradation (stale data shown during refetch or on error)- Polymorphic key system —
fetchQuery,watchQuery,watchState,prefetch,setQueryData,restoreQueryData,invalidate,getQueryData, andgetQueryStatenow acceptObject(plainList<dynamic>orQoraKey); keys are normalised and compared with deep structural equality KeyCacheMap— custom map implementation with deep recursive equality and order-independent map-key comparison; eliminates reference-equality bugsinvalidate(key)replaces the oldinvalidateQuery(key)invalidateWhere(predicate)replaces the oldinvalidateQueries(predicate); predicate receives the normalisedList<dynamic>key- Package structure reorganised — source files split into
cache/,config/,client/,key/,state/, andutils/subdirectories for clarity
Fixed #
- Defensive immutability: normalised key lists are wrapped in
List.unmodifiable()to prevent accidental mutation by callers