swrly 0.3.0-dev.2
swrly: ^0.3.0-dev.2 copied to clipboard
Server-state cache for Flutter — dedupe, cache by query key, stale-while-revalidate. A TanStack Query for Flutter.
0.3.0-dev.2 #
Documentation hygiene — no API changes.
- The README no longer carries version numbers. Every release meant editing
a status line, two "New in X" lists, an Install pin, an inline
(0.2.0)marker and a "Shipped in" recap — and it rotted twice (0.2.0 shipped while the README still called it a prerelease and pinned^0.1.0). Nowpubspec.yamlis the version,CHANGELOG.mdis the history, the pub.dev badge renders the current number, and the README only describes what the library does. The two "New in X" lists are replaced by one version-free What you get; Install isflutter pub add swrly. - New:
test/docs_freshness_test.dart. Three guards that fail the suite rather than relying on remembering: the README must contain no version literal,CHANGELOG.md's newest entry must matchpubspec.yaml, and every public type must have an entry indoc/API.md. Together withreadme_snippets_test.dart(which compile-checks the samples), the docs now break the build when they drift instead of going stale quietly. - The public-type guard immediately found one gap:
QueryKeyHashhad no entry indoc/API.md. Documented, along withqueryKeyStartsWith— both are useful inside aninvalidateQueriesWhere/removeQueriesWherepredicate.
0.3.0-dev.1 #
First 0.3.0 feature, on the dev prerelease track for real-world verification.
- New:
Query<T>/QueryFamily<T, A>— query definition objects. Declare a query's key, fetch function and options once, then consume the same definition three ways: imperatively (postsQuery.fetch()/refetch()), declaratively (QueryBuilder.of(postsQuery, builder: ...)), or for cache control (data/state/stream/setData/invalidate/remove/copyWith). Previously the(key, fn)pair had to be retyped at every call site, where a mistyped key is a silent cache miss rather than a compile error. AQueryis a stateless value object — the cache still lives inQueryClient, and two definitions with the same key address the same entry. See SPEC §10. QueryFamilycovers parameterised queries. Keys are always[...prefix, ...argKey(arg)](default[arg]), soinvalidateAll()/removeAll()are correct by construction; supplyargKeywhen the argument is a record or custom object so the key is built from primitives instead oftoString().Query.invalidate()/Query.remove()are exact, not prefix-scoped — a definition names one entry, so invalidating['posts']leaves['posts', 'page', 2]alone.invalidateQueries(prefix)/removeQueries(prefix)keep their prefix semantics.- New:
QueryBuilder.of(query, builder: ...)— build a widget straight from a definition. Because a definition'sfnis a stable field rather than an inline closure, thequeryFnre-captured for invalidation refetches is identical across builds. - New:
QueryClient.removeQueriesWhere(test)— predicate form ofremoveQueries, mirroringinvalidateQueriesWhere. - Docs: README "Define a query once" section, SPEC §10, API reference for
both types; the SPEC's out-of-scope list no longer claims 0.2.0 features are
missing, and ROADMAP's duplicate
v0.2section is folded intov0.4. - Tests: 15 new tests for the definition objects, plus a new
test/readme_snippets_test.dartthat compile-checks every Dart snippet in the README anddoc/API.mdagainst the real API, so a doc example can't drift from the code without the suite going red. 67 tests total;lib/srcstays at 100% line coverage (330/330).
Backward compatible with 0.2.x — purely additive.
0.2.1 #
-
Fix:
observe()/stateOf()no longer disarm garbage collection. Both go through the internal entry lookup, which cancels a pending GC timer so an entry can't be disposed out from under a caller — but neither re-armed it, so observing (or synchronously reading) a key with no subscribers left the entry resident forever, andstateOfon an unknown key leaked the idle entry it created. Both now re-arm GC, matchingfetchQuery/setQueryData. Entries with a live subscriber (a mountedQueryBuilder) are unaffected. Found while documenting the non-widget path below. -
README: "Using swrly without widgets".
QueryClientis a complete API on its own; the README now documents the imperative path (fetchQuery) and the observe path (observe/stateOf) alongsideQueryBuilder, with a table of the three ways to read a query and patterns for prefetch-before-navigate, synchronous peeks and non-widget subscriptions. -
Documented that
observe()does not register a subscriber, so it does not hold an entry againstcacheTimeGC (README +doc/API.md). -
README status/install corrected for the released
0.2.0(they still described0.2.0as a prerelease and pinned^0.1.0). -
Example: don't notify instrumentation
ValueNotifiers during build. -
Tests: 50 (3 new, covering the GC re-arm);
lib/srcstays at 100% line coverage.
0.2.0 #
Stable release of the 0.2.0 line (the 0.2.0-dev.* notes below are the full
history). Headline additions since 0.1.x:
- Retry + backoff —
retry/retryDelayonQueryBuilder/fetchQuery, exponential default (1s→30s). - Optimistic updates with automatic rollback —
MutationBuilder.onMutateapplies an optimistic write and returns a rollback closure run on error. keepPreviousData/placeholderData— no loading flash on key changes (search / pagination); newQueryState.isPlaceholderData.- Example: a new Stress test screen — an in-app performance harness with
a live FPS / build / raster / jank readout, a cache-ops micro-benchmark, and
hundreds of live
QueryBuilders under continuous invalidation. - Docs: the README now has a measured Performance section.
Backward compatible with 0.1.x.
0.2.0-dev.4 #
- Example: rebuilt as a verification harness for all 0.2.0 features —
pagination with
keepPreviousData(dimmed previous page while loading), a "Fail next" retry demo, and a "make next create fail" toggle that shows the optimistic insert being rolled back automatically. - Tests: 100% line coverage on
lib/src(addedrefetch(),refetchOnResume, custom-keytoStringfallback, idle getters, singleton). - Docs: added
doc/VERIFICATION.md(status, remaining TODOs, how to verify the prerelease). README updated for the 0.2.0-dev feature set.
0.2.0-dev.3 #
- New:
keepPreviousData/placeholderData.QueryBuilder.keepPreviousDatakeeps rendering the previous key's data (flaggedstate.isPlaceholderData) while a new key loads, instead of flashing to a spinner — ideal for search / pagination.QueryBuilder.placeholderDatashows a static stand-in until the first real value arrives. Neither is cached or affects freshness. NewQueryState.isPlaceholderDataflag. See SPEC §9.
0.2.0-dev.2 #
- New: optimistic updates with automatic rollback.
MutationBuilder.onMutateruns beforemutationFn, applies your optimistic write, and returns a rollback closure; swrly runs it automatically on error (beforeonError) and keeps the optimistic value on success. Backward compatible — existingonSuccess/onError/onSettledsignatures are unchanged. See SPEC §9.
0.2.0-dev.1 #
First 0.2.0 feature, on the dev prerelease track for real-world verification.
- New: retry + backoff. A
queryFnthat throws is retried up toretrytimes with aretryDelay(attempt)backoff. Configure per query (QueryBuilder.retry/retryDelay,fetchQuery(retry:, retryDelay:)) or globally (QueryClient(defaultRetry:, defaultRetryDelay:)). Default is0retries (unchanged 0.1.x behaviour);defaultRetryDelayFnprovides an exponential 1s→30s backoff. While retrying the query staysisFetchingand only surfaces anerrorafter retries are exhausted; retries respect the generation guard (a supersede/dispose stops them). See SPEC §8.1.
0.1.1-dev.1 #
Prerelease of the 0.1.1 correctness patch, published for verification before the
stable 0.1.1 release. Everything below plus the review fix noted here.
- Fix (review):
QueryBuilderonly re-captures the refetcher (primeRefetcher) on a same-key rebuild whenenabledis true. Previously a disabled query that rebuilt could get a refetcher installed, so a laterinvalidateQuerieswould fetch a query the caller setenabled: false— violating SPEC §9. Added a regression test plus tests foronError/onSettledon unmount,hasDataretention across errors, andcopyWithfield clearing (31 tests total).
0.1.1 #
Correctness patch — behaviour fixes found in review, all backward compatible.
- Fix:
QueryState.hasDatais now based on a stored "has a successful value" flag instead ofdata != null. A query that legitimately resolves tonullnow reportsisSuccess && hasDataat the widget layer, matching the cache's own freshness contract (SPEC §2/§5.1). - Fix:
QueryState.copyWithcan now cleardata/error/stackTrace(via a sentinel). Entering a fetch and a successful refetch clear a stale error/stackTrace, so an old failure no longer leaks into a later loading/success state. - Fix:
MutationBuildernow runsonSuccess/onError/onSettledregardless ofmounted— a widget that disposes mid-flight no longer silently skips a cache invalidation done inonSuccess. OnlysetStateis guarded. - Fix:
QueryBuilderre-captures the currentqueryFn/staleTimeon a same-key rebuild (newQueryClient.primeRefetcher), so a laterinvalidateQueriesrefetch uses the current closure rather than a stale one. It still does not refetch on a plainqueryFnidentity change. - New:
QueryClient.invalidateQueriesWhere((key) => bool)— predicate form ofinvalidateQueriesfor sets a prefix can't express. - Docs: documented the last-writer-wins semantics for an entry's captured
queryFnwhen subscribers share a key (SPEC §6). Added 6 tests.
0.1.0 #
- Example: rebuilt around a real dio client hitting a public API, with a
live request counter + event log so the cache is observable — cache hits
show as "0 requests", keyed detail queries (
['post', id]) demonstrate per-key caching, and mutations show optimisticsetQueryData. Runs on web. - Docs: README overhauled — "why server state", how-it-works flow, and
comparison tables (vs
FutureBuilder, vs Riverpod/Bloc, vs a dio cache interceptor).docs/renamed todoc/(pub convention); added pubspec topics. - Bundles all the
0.0.2cache-correctness fixes below.
0.0.2 #
- Fix:
invalidateQueries(refetch: true)now actually refetches active subscribers (the lastqueryFnis captured per entry) instead of only marking data stale. - Fix: stale responses can no longer overwrite fresher data — each fetch carries a generation token and only the newest may write back.
- Fix: a successful
nullvalue is treated as fresh; nullable-data queries no longer refetch on every call. - Fix: freshness uses a monotonic clock, immune to wall-clock jumps.
- Fix: garbage collection can no longer dispose an entry that is being reused imperatively; idle imperatively-created entries are now collected too.
- Fix:
QueryBuilderkicks off the fetch whenenabledflips false → true. - Docs: added
docs/SPEC.md; expanded test coverage (dedup, race, GC, nullable, invalidate-refetch, widget lifecycle).
0.0.1 #
- Initial scaffold:
QueryClient,QueryBuilder,MutationBuilder, prefix invalidation, refetch-on-resume.