Release 0.3.0: Query/Mutation race fixes + offline support #81

Merged
Thilawyn merged 12 commits from next into master 2026-10-11 22:52:47 +02:00
Owner

Summary

Merges next → master (12 commits, 22 files) for the effect-view 0.3.0 release: offline support (networkMode) for Query/Mutation, an operation-based rework that fixes Query/Mutation race conditions, and Stream-based request APIs. Full entry: packages/effect-view/CHANGELOG.md.

Offline support (networkMode)

  • Query.networkMode (new Operation.NetworkMode). "online" (default): a request made offline waits for a connection before running — its state has paused: true meanwhile, with result.waiting still true. "offlineFirst" runs right away and, if it fails while offline, pauses until the connection returns, then runs again. "always" ignores the connection.
  • refreshOnReconnect (defaults true, except in "always" mode): refreshes the current key when the connection returns, subject to staleTime.
  • refreshOnFocus / refreshOnReconnect accept "always" to refresh even a fresh result (Query.RefreshOn).
  • Focus/reconnect refreshes no longer restart a request that is already running, and paused requests resume by themselves.
  • New AppConnectivity and AppFocus services, each providing layerBrowser (navigator.onLine + online/offline events; page visibility), layerDummy (e.g. for tests) and make(view) (from any View<boolean>, e.g. on React Native).
  • Mutation.networkMode (defaults to the client's new defaultMutationNetworkMode, "online"): mutations called offline are paused until the connection returns.
  • Query.withScheduledRefresh now skips refreshes while the app is not focused; a skipped refresh still counts toward the schedule. Pass refreshInBackground: true to keep the previous behavior.
  • QueryClient gains defaultNetworkMode, defaultRefreshOnReconnect and defaultMutationNetworkMode.

Query/Mutation race fixes (new Operation module)

Each run of a Query or Mutation is now an Operation, and only the current one updates state / latestFinalState. Fixes, most visible on React Native:

  • a request's final state could be lost, leaving state stuck on waiting: true and the result out of the cache;
  • a superseded request could overwrite the state of a newer one, or be recorded in latestFinalState while still waiting;
  • concurrent fetch/refresh calls superseding a running request could leave an extra request running that was never interrupted.

Also: serving a fresh result from the cache no longer resets that cache entry's staleness timer.

Breaking changes

  • fetchView / refreshView / mutateView → fetchStream / refreshStream / mutateStream: same request, but returns a Stream of that request's states. It emits the current state first, so subscribing late still yields the final state, then ends once the request settles or is interrupted. Consume with Stream.use.
  • A fetch/refresh superseded by a newer request is now interrupted instead of returning its unfinished state typed as final; its state goes back to the previous result.
  • fiber removed from Query / Mutation; use the new operation View (key, state, stream, fiber, interrupt, await).
  • Overlapping mutations: mutation.state / latestFinalState follow only the most recently started call — earlier calls still run to completion but no longer update them. Use mutateStream to follow each call separately.
  • Renames: FinalQueryState → QueryFinalState, FinalMutationState → MutationFinalState, LatestMutationState → MutationLatestState; removed QueryStateLens, makeQueryStateLens, MutationStateLens and makeMutationStateLens.
  • QueryClient.layer is no longer a function: it's now a plain Layer with the default options, and QueryClient.layerWithOptions takes options. Both now require the AppConnectivity and AppFocus services; in a browser use QueryClient.layerBrowser / layerBrowserWithOptions (which provide both), elsewhere provide the services (e.g. the layerDummys).
  • Mutation.make and MutationForm.make now require a QueryClient.
  • refreshOnWindowFocus → refreshOnFocus, defaultRefreshOnWindowFocus → defaultRefreshOnFocus (focus now comes from the AppFocus service).

Docs & tests

  • New CHANGELOG.md; docs (query.md, mutation.md, getting-started.md), ai-docs and the example app updated.
  • New Query.network.test.ts (network modes); Query.test.ts and Mutation.test.ts expanded.
## Summary Merges `next` → `master` (12 commits, 22 files) for the effect-view **0.3.0** release: offline support (`networkMode`) for `Query`/`Mutation`, an operation-based rework that fixes `Query`/`Mutation` race conditions, and Stream-based request APIs. Full entry: `packages/effect-view/CHANGELOG.md`. ### Offline support (`networkMode`) - `Query.networkMode` (new `Operation.NetworkMode`). `"online"` (default): a request made offline waits for a connection before running — its state has `paused: true` meanwhile, with `result.waiting` still `true`. `"offlineFirst"` runs right away and, if it fails while offline, pauses until the connection returns, then runs again. `"always"` ignores the connection. - `refreshOnReconnect` (defaults `true`, except in `"always"` mode): refreshes the current key when the connection returns, subject to `staleTime`. - `refreshOnFocus` / `refreshOnReconnect` accept `"always"` to refresh even a fresh result (`Query.RefreshOn`). - Focus/reconnect refreshes no longer restart a request that is already running, and paused requests resume by themselves. - New `AppConnectivity` and `AppFocus` services, each providing `layerBrowser` (`navigator.onLine` + `online`/`offline` events; page visibility), `layerDummy` (e.g. for tests) and `make(view)` (from any `View<boolean>`, e.g. on React Native). - `Mutation.networkMode` (defaults to the client's new `defaultMutationNetworkMode`, `"online"`): mutations called offline are paused until the connection returns. - `Query.withScheduledRefresh` now skips refreshes while the app is not focused; a skipped refresh still counts toward the schedule. Pass `refreshInBackground: true` to keep the previous behavior. - `QueryClient` gains `defaultNetworkMode`, `defaultRefreshOnReconnect` and `defaultMutationNetworkMode`. ### Query/Mutation race fixes (new `Operation` module) Each run of a Query or Mutation is now an `Operation`, and only the current one updates `state` / `latestFinalState`. Fixes, most visible on React Native: - a request's final state could be lost, leaving `state` stuck on `waiting: true` and the result out of the cache; - a superseded request could overwrite the state of a newer one, or be recorded in `latestFinalState` while still waiting; - concurrent `fetch`/`refresh` calls superseding a running request could leave an extra request running that was never interrupted. Also: serving a fresh result from the cache no longer resets that cache entry's staleness timer. ### Breaking changes - `fetchView` / `refreshView` / `mutateView` → `fetchStream` / `refreshStream` / `mutateStream`: same request, but returns a `Stream` of that request's states. It emits the current state first, so subscribing late still yields the final state, then ends once the request settles or is interrupted. Consume with `Stream.use`. - A `fetch`/`refresh` superseded by a newer request is now interrupted instead of returning its unfinished state typed as final; its state goes back to the previous result. - `fiber` removed from `Query` / `Mutation`; use the new `operation` View (`key`, `state`, `stream`, `fiber`, `interrupt`, `await`). - Overlapping mutations: `mutation.state` / `latestFinalState` follow only the most recently started call — earlier calls still run to completion but no longer update them. Use `mutateStream` to follow each call separately. - Renames: `FinalQueryState` → `QueryFinalState`, `FinalMutationState` → `MutationFinalState`, `LatestMutationState` → `MutationLatestState`; removed `QueryStateLens`, `makeQueryStateLens`, `MutationStateLens` and `makeMutationStateLens`. - `QueryClient.layer` is no longer a function: it's now a plain `Layer` with the default options, and `QueryClient.layerWithOptions` takes options. Both now require the `AppConnectivity` and `AppFocus` services; in a browser use `QueryClient.layerBrowser` / `layerBrowserWithOptions` (which provide both), elsewhere provide the services (e.g. the `layerDummy`s). - `Mutation.make` and `MutationForm.make` now require a `QueryClient`. - `refreshOnWindowFocus` → `refreshOnFocus`, `defaultRefreshOnWindowFocus` → `defaultRefreshOnFocus` (focus now comes from the `AppFocus` service). ### Docs & tests - New `CHANGELOG.md`; docs (`query.md`, `mutation.md`, `getting-started.md`), ai-docs and the example app updated. - New `Query.network.test.ts` (network modes); `Query.test.ts` and `Mutation.test.ts` expanded.
Thilawyn added 12 commits 2026-10-11 22:45:46 +02:00
Refactor Query/Mutation
Lint / lint (push) Successful in 1m3s
3875bc4f69
Refactor QueryClient
Lint / lint (push) Successful in 26s
acb05fce44
Version bump + add changelog
Lint / lint (push) Successful in 24s
e45903cbdc
Update docs
Lint / lint (push) Failing after 1m6s
e48f3a6176
Update changelog
Lint / lint (push) Failing after 20s
0b636e0eaf
Improve connectivity
Lint / lint (push) Failing after 18s
43bc165a1c
Update Query
Lint / lint (push) Successful in 25s
Test build / test-build (pull_request) Successful in 1m16s
63b2c62fa3
Thilawyn changed title from Release 0.3.0: Query/Mutation refactor + connectivity support to Release 0.3.0: Query/Mutation race fixes + offline support 2026-10-11 22:52:13 +02:00
Thilawyn merged commit 663f536fa3 into master 2026-10-11 22:52:47 +02:00
Thilawyn deleted branch next 2026-10-11 22:52:47 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Thilawyn/effect-view#81